Skip to content

Commit 97a68e4

Browse files
committed
power: supply: macsmc_power: Add more props, rework others
Signed-off-by: Hector Martin <[email protected]>
1 parent 215762b commit 97a68e4

1 file changed

Lines changed: 202 additions & 22 deletions

File tree

drivers/power/supply/macsmc_power.c

Lines changed: 202 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* Copyright The Asahi Linux Contributors
55
*/
66

7+
#include <linux/ctype.h>
78
#include <linux/module.h>
89
#include <linux/of.h>
910
#include <linux/platform_device.h>
@@ -19,50 +20,178 @@ struct macsmc_power {
1920
struct power_supply *psy;
2021
char model_name[MAX_STRING_LENGTH];
2122
char serial_number[MAX_STRING_LENGTH];
23+
char mfg_date[MAX_STRING_LENGTH];
2224

2325
struct notifier_block nb;
2426
};
2527

28+
#define CHNC_BATTERY_FULL BIT(0)
29+
#define CHNC_NO_CHARGER BIT(7)
30+
#define CHNC_NOCHG_CH0C BIT(14)
31+
#define CHNC_NOCHG_CH0B_CH0K BIT(15)
32+
#define CHNC_BATTERY_FULL_2 BIT(18)
33+
#define CHNC_BMS_BUSY BIT(23)
34+
#define CHNC_NOAC_CH0J BIT(53)
35+
#define CHNC_NOAC_CH0I BIT(54)
36+
37+
#define CH0R_LOWER_FLAGS GENMASK(15, 0)
38+
#define CH0R_NOAC_CH0I BIT(0)
39+
#define CH0R_NOAC_CH0J BIT(5)
40+
#define CH0R_BMS_BUSY BIT(8)
41+
#define CH0R_NOAC_CH0K BIT(9)
42+
43+
#define CH0X_CH0C BIT(0)
44+
#define CH0X_CH0B BIT(1)
45+
2646
static int macsmc_battery_get_status(struct macsmc_power *power)
2747
{
28-
u8 val;
48+
u64 nocharge_flags;
49+
u32 nopower_flags;
50+
u16 ac_current;
2951
int ret;
3052

31-
ret = apple_smc_read_u8(power->smc, SMC_KEY(BSFC), &val);
32-
if (ret)
53+
/*
54+
* Note: there are fallbacks in case some of these SMC keys disappear in the future
55+
* or are not present on some machines. We treat the absence of the CHCE/CHCC/BSFC/CHSC
56+
* flags as an error, since they are quite fundamental and simple booleans.
57+
*/
58+
59+
/*
60+
* If power input is inhibited, we are definitely discharging.
61+
* However, if the only reason is the BMS is doing a balancing cycle,
62+
* go ahead and ignore that one to avoid spooking users.
63+
*/
64+
ret = apple_smc_read_u32(power->smc, SMC_KEY(CH0R), &nopower_flags);
65+
if (!ret && (nopower_flags & CH0R_LOWER_FLAGS & ~CH0R_BMS_BUSY))
66+
return POWER_SUPPLY_STATUS_DISCHARGING;
67+
68+
/* If no charger is present, we are definitely discharging. */
69+
ret = apple_smc_read_flag(power->smc, SMC_KEY(CHCE));
70+
if (ret < 0)
71+
return ret;
72+
else if (!ret)
73+
return POWER_SUPPLY_STATUS_DISCHARGING;
74+
75+
/* If AC is not charge capable, we are definitely discharging. */
76+
ret = apple_smc_read_flag(power->smc, SMC_KEY(CHCC));
77+
if (ret < 0)
3378
return ret;
34-
if (val == 1)
79+
else if (!ret)
80+
return POWER_SUPPLY_STATUS_DISCHARGING;
81+
82+
/*
83+
* If the AC input current limit is tiny or 0, we are discharging no matter
84+
* how much the BMS believes it can charge.
85+
*/
86+
ret = apple_smc_read_u16(power->smc, SMC_KEY(AC-i), &ac_current);
87+
if (!ret && ac_current < 100)
88+
return POWER_SUPPLY_STATUS_DISCHARGING;
89+
90+
/* If the battery is full, report it as such. */
91+
ret = apple_smc_read_flag(power->smc, SMC_KEY(BSFC));
92+
if (ret < 0)
93+
return ret;
94+
else if (ret)
3595
return POWER_SUPPLY_STATUS_FULL;
3696

37-
ret = apple_smc_read_u8(power->smc, SMC_KEY(CHSC), &val);
38-
if (ret)
97+
/* If there are reasons we aren't charging... */
98+
ret = apple_smc_read_u64(power->smc, SMC_KEY(CHNC), &nocharge_flags);
99+
if (!ret) {
100+
/* Perhaps the battery is full after all */
101+
if (nocharge_flags & CHNC_BATTERY_FULL)
102+
return POWER_SUPPLY_STATUS_FULL;
103+
/* Or maybe the BMS is just busy doing something, if so call it charging anyway */
104+
else if (nocharge_flags == CHNC_BMS_BUSY)
105+
return POWER_SUPPLY_STATUS_CHARGING;
106+
/* If we have other reasons we aren't charging, say we aren't */
107+
else if (nocharge_flags)
108+
return POWER_SUPPLY_STATUS_NOT_CHARGING;
109+
/* Else we're either charging or about to charge */
110+
else
111+
return POWER_SUPPLY_STATUS_CHARGING;
112+
}
113+
114+
/* As a fallback, use the system charging flag. */
115+
ret = apple_smc_read_flag(power->smc, SMC_KEY(CHSC));
116+
if (ret < 0)
39117
return ret;
40-
if (val == 1)
118+
if (!ret)
119+
return POWER_SUPPLY_STATUS_NOT_CHARGING;
120+
else
41121
return POWER_SUPPLY_STATUS_CHARGING;
122+
}
42123

43-
ret = apple_smc_read_u8(power->smc, SMC_KEY(CHCC), &val);
124+
static int macsmc_battery_get_charge_behaviour(struct macsmc_power *power)
125+
{
126+
int ret;
127+
u8 val;
128+
129+
/* CH0I returns a bitmask like the low byte of CH0R */
130+
ret = apple_smc_read_u8(power->smc, SMC_KEY(CH0I), &val);
44131
if (ret)
45132
return ret;
46-
if (val == 0)
47-
return POWER_SUPPLY_STATUS_DISCHARGING;
133+
if (val & CH0R_NOAC_CH0I)
134+
return POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE;
48135

49-
ret = apple_smc_read_u8(power->smc, SMC_KEY(CHCE), &val);
136+
/* CH0C returns a bitmask containing CH0B/CH0C flags */
137+
ret = apple_smc_read_u8(power->smc, SMC_KEY(CH0C), &val);
50138
if (ret)
51139
return ret;
52-
if (val == 0)
53-
return POWER_SUPPLY_STATUS_DISCHARGING;
140+
if (val & CH0X_CH0C)
141+
return POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE;
54142
else
55-
return POWER_SUPPLY_STATUS_NOT_CHARGING;
143+
return POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO;
144+
}
56145

57-
146+
static int macsmc_battery_set_charge_behaviour(struct macsmc_power *power, int val)
147+
{
148+
u8 ch0i, ch0c;
149+
int ret;
150+
151+
/*
152+
* CH0I/CH0C are "hard" controls that will allow the battery to run down to 0.
153+
* CH0K/CH0B are "soft" controls that are reset to 0 when SOC drops below 50%;
154+
* we don't expose these yet.
155+
*/
156+
157+
switch (val) {
158+
case POWER_SUPPLY_CHARGE_BEHAVIOUR_AUTO:
159+
ch0i = ch0c = 0;
160+
break;
161+
case POWER_SUPPLY_CHARGE_BEHAVIOUR_INHIBIT_CHARGE:
162+
ch0i = 0;
163+
ch0c = 1;
164+
break;
165+
case POWER_SUPPLY_CHARGE_BEHAVIOUR_FORCE_DISCHARGE:
166+
ch0i = 1;
167+
ch0c = 0;
168+
break;
169+
default:
170+
return -EINVAL;
171+
}
172+
173+
ret = apple_smc_write_u8(power->smc, SMC_KEY(CH0I), ch0i);
174+
if (ret)
175+
return ret;
176+
return apple_smc_write_u8(power->smc, SMC_KEY(CH0C), ch0c);
177+
}
178+
179+
static int macsmc_battery_get_date(const char *s, int *out)
180+
{
181+
if (!isdigit(s[0]) || !isdigit(s[1]))
182+
return -ENOTSUPP;
183+
184+
*out = (s[0] - '0') * 10 + s[1] - '0';
185+
return 0;
58186
}
59187

60188
static int macsmc_battery_get_property(struct power_supply *psy,
61-
enum power_supply_property psp,
62-
union power_supply_propval *val)
189+
enum power_supply_property psp,
190+
union power_supply_propval *val)
63191
{
64192
struct macsmc_power *power = power_supply_get_drvdata(psy);
65193
int ret = 0;
194+
u8 vu8;
66195
u16 vu16;
67196
u32 vu32;
68197
s16 vs16;
@@ -77,6 +206,10 @@ static int macsmc_battery_get_property(struct power_supply *psy,
77206
case POWER_SUPPLY_PROP_PRESENT:
78207
val->intval = 1;
79208
break;
209+
case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
210+
val->intval = macsmc_battery_get_charge_behaviour(power);
211+
ret = val->intval < 0 ? val->intval : 0;
212+
break;
80213
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
81214
ret = apple_smc_read_u16(power->smc, SMC_KEY(B0TE), &vu16);
82215
val->intval = vu16 == 0xffff ? 0 : vu16 * 60;
@@ -145,6 +278,9 @@ static int macsmc_battery_get_property(struct power_supply *psy,
145278
ret = apple_smc_read_u16(power->smc, SMC_KEY(B0CT), &vu16);
146279
val->intval = vu16;
147280
break;
281+
case POWER_SUPPLY_PROP_SCOPE:
282+
val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
283+
break;
148284
case POWER_SUPPLY_PROP_HEALTH:
149285
ret = apple_smc_read_flag(power->smc, SMC_KEY(BBAD));
150286
val->intval = ret == 1 ? POWER_SUPPLY_HEALTH_DEAD : POWER_SUPPLY_HEALTH_GOOD;
@@ -156,16 +292,53 @@ static int macsmc_battery_get_property(struct power_supply *psy,
156292
case POWER_SUPPLY_PROP_SERIAL_NUMBER:
157293
val->strval = power->serial_number;
158294
break;
295+
case POWER_SUPPLY_PROP_MANUFACTURE_YEAR:
296+
ret = macsmc_battery_get_date(&power->mfg_date[0], &val->intval);
297+
val->intval += 2000 - 8; /* -8 is a fixup for a firmware bug... */
298+
break;
299+
case POWER_SUPPLY_PROP_MANUFACTURE_MONTH:
300+
ret = macsmc_battery_get_date(&power->mfg_date[2], &val->intval);
301+
break;
302+
case POWER_SUPPLY_PROP_MANUFACTURE_DAY:
303+
ret = macsmc_battery_get_date(&power->mfg_date[4], &val->intval);
304+
break;
159305
default:
160306
return -EINVAL;
161307
}
162308

163309
return ret;
164310
}
165311

312+
static int macsmc_battery_set_property(struct power_supply *psy,
313+
enum power_supply_property psp,
314+
const union power_supply_propval *val)
315+
{
316+
struct macsmc_power *power = power_supply_get_drvdata(psy);
317+
318+
switch (psp) {
319+
case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
320+
return macsmc_battery_set_charge_behaviour(power, val->intval);
321+
default:
322+
return -EINVAL;
323+
}
324+
}
325+
326+
static int macsmc_battery_property_is_writeable(struct power_supply *psy,
327+
enum power_supply_property psp)
328+
{
329+
switch (psp) {
330+
case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
331+
return true;
332+
default:
333+
return false;
334+
}
335+
}
336+
337+
166338
static enum power_supply_property macsmc_battery_props[] = {
167339
POWER_SUPPLY_PROP_STATUS,
168340
POWER_SUPPLY_PROP_PRESENT,
341+
POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR,
169342
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
170343
POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
171344
POWER_SUPPLY_PROP_CAPACITY,
@@ -183,17 +356,23 @@ static enum power_supply_property macsmc_battery_props[] = {
183356
POWER_SUPPLY_PROP_TEMP,
184357
POWER_SUPPLY_PROP_CHARGE_COUNTER,
185358
POWER_SUPPLY_PROP_CYCLE_COUNT,
359+
POWER_SUPPLY_PROP_SCOPE,
186360
POWER_SUPPLY_PROP_HEALTH,
187361
POWER_SUPPLY_PROP_MODEL_NAME,
188362
POWER_SUPPLY_PROP_SERIAL_NUMBER,
363+
POWER_SUPPLY_PROP_MANUFACTURE_YEAR,
364+
POWER_SUPPLY_PROP_MANUFACTURE_MONTH,
365+
POWER_SUPPLY_PROP_MANUFACTURE_DAY,
189366
};
190367

191368
static const struct power_supply_desc macsmc_battery_desc = {
192-
.name = "macsmc-battery",
193-
.type = POWER_SUPPLY_TYPE_BATTERY,
194-
.get_property = macsmc_battery_get_property,
195-
.properties = macsmc_battery_props,
196-
.num_properties = ARRAY_SIZE(macsmc_battery_props),
369+
.name = "macsmc-battery",
370+
.type = POWER_SUPPLY_TYPE_BATTERY,
371+
.get_property = macsmc_battery_get_property,
372+
.set_property = macsmc_battery_set_property,
373+
.property_is_writeable = macsmc_battery_property_is_writeable,
374+
.properties = macsmc_battery_props,
375+
.num_properties = ARRAY_SIZE(macsmc_battery_props),
197376
};
198377

199378
static int macsmc_power_event(struct notifier_block *nb, unsigned long event, void *data)
@@ -234,6 +413,7 @@ static int macsmc_power_probe(struct platform_device *pdev)
234413
/* Fetch string properties */
235414
apple_smc_read(smc, SMC_KEY(BMDN), power->model_name, sizeof(power->model_name) - 1);
236415
apple_smc_read(smc, SMC_KEY(BMSN), power->serial_number, sizeof(power->serial_number) - 1);
416+
apple_smc_read(smc, SMC_KEY(BMDT), power->mfg_date, sizeof(power->mfg_date) - 1);
237417

238418
psy_cfg.drv_data = power;
239419
power->psy = devm_power_supply_register(&pdev->dev, &macsmc_battery_desc, &psy_cfg);

0 commit comments

Comments
 (0)