Skip to content

Commit bc7d93e

Browse files
marcanjannau
authored andcommitted
power: supply: macsmc_power: Add more props, rework others
Signed-off-by: Hector Martin <marcan@marcan.st>
1 parent dfd71ff commit bc7d93e

1 file changed

Lines changed: 202 additions & 21 deletions

File tree

drivers/power/supply/macsmc_power.c

Lines changed: 202 additions & 21 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,48 +20,177 @@ 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+
}
145+
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+
ret = apple_smc_write_u8(power->smc, SMC_KEY(CH0I), ch0i);
173+
if (ret)
174+
return ret;
175+
return apple_smc_write_u8(power->smc, SMC_KEY(CH0C), ch0c);
176+
}
177+
178+
static int macsmc_battery_get_date(const char *s, int *out)
179+
{
180+
if (!isdigit(s[0]) || !isdigit(s[1]))
181+
return -ENOTSUPP;
182+
183+
*out = (s[0] - '0') * 10 + s[1] - '0';
184+
return 0;
56185
}
57186

58187
static int macsmc_battery_get_property(struct power_supply *psy,
59-
enum power_supply_property psp,
60-
union power_supply_propval *val)
188+
enum power_supply_property psp,
189+
union power_supply_propval *val)
61190
{
62191
struct macsmc_power *power = power_supply_get_drvdata(psy);
63192
int ret = 0;
193+
u8 vu8;
64194
u16 vu16;
65195
u32 vu32;
66196
s16 vs16;
@@ -75,6 +205,10 @@ static int macsmc_battery_get_property(struct power_supply *psy,
75205
case POWER_SUPPLY_PROP_PRESENT:
76206
val->intval = 1;
77207
break;
208+
case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
209+
val->intval = macsmc_battery_get_charge_behaviour(power);
210+
ret = val->intval < 0 ? val->intval : 0;
211+
break;
78212
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
79213
ret = apple_smc_read_u16(power->smc, SMC_KEY(B0TE), &vu16);
80214
val->intval = vu16 == 0xffff ? 0 : vu16 * 60;
@@ -143,6 +277,9 @@ static int macsmc_battery_get_property(struct power_supply *psy,
143277
ret = apple_smc_read_u16(power->smc, SMC_KEY(B0CT), &vu16);
144278
val->intval = vu16;
145279
break;
280+
case POWER_SUPPLY_PROP_SCOPE:
281+
val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
282+
break;
146283
case POWER_SUPPLY_PROP_HEALTH:
147284
ret = apple_smc_read_flag(power->smc, SMC_KEY(BBAD));
148285
val->intval = ret == 1 ? POWER_SUPPLY_HEALTH_DEAD : POWER_SUPPLY_HEALTH_GOOD;
@@ -154,16 +291,53 @@ static int macsmc_battery_get_property(struct power_supply *psy,
154291
case POWER_SUPPLY_PROP_SERIAL_NUMBER:
155292
val->strval = power->serial_number;
156293
break;
294+
case POWER_SUPPLY_PROP_MANUFACTURE_YEAR:
295+
ret = macsmc_battery_get_date(&power->mfg_date[0], &val->intval);
296+
val->intval += 2000 - 8; /* -8 is a fixup for a firmware bug... */
297+
break;
298+
case POWER_SUPPLY_PROP_MANUFACTURE_MONTH:
299+
ret = macsmc_battery_get_date(&power->mfg_date[2], &val->intval);
300+
break;
301+
case POWER_SUPPLY_PROP_MANUFACTURE_DAY:
302+
ret = macsmc_battery_get_date(&power->mfg_date[4], &val->intval);
303+
break;
157304
default:
158305
return -EINVAL;
159306
}
160307

161308
return ret;
162309
}
163310

311+
static int macsmc_battery_set_property(struct power_supply *psy,
312+
enum power_supply_property psp,
313+
const union power_supply_propval *val)
314+
{
315+
struct macsmc_power *power = power_supply_get_drvdata(psy);
316+
317+
switch (psp) {
318+
case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
319+
return macsmc_battery_set_charge_behaviour(power, val->intval);
320+
default:
321+
return -EINVAL;
322+
}
323+
}
324+
325+
static int macsmc_battery_property_is_writeable(struct power_supply *psy,
326+
enum power_supply_property psp)
327+
{
328+
switch (psp) {
329+
case POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR:
330+
return true;
331+
default:
332+
return false;
333+
}
334+
}
335+
336+
164337
static enum power_supply_property macsmc_battery_props[] = {
165338
POWER_SUPPLY_PROP_STATUS,
166339
POWER_SUPPLY_PROP_PRESENT,
340+
POWER_SUPPLY_PROP_CHARGE_BEHAVIOUR,
167341
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
168342
POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
169343
POWER_SUPPLY_PROP_CAPACITY,
@@ -181,17 +355,23 @@ static enum power_supply_property macsmc_battery_props[] = {
181355
POWER_SUPPLY_PROP_TEMP,
182356
POWER_SUPPLY_PROP_CHARGE_COUNTER,
183357
POWER_SUPPLY_PROP_CYCLE_COUNT,
358+
POWER_SUPPLY_PROP_SCOPE,
184359
POWER_SUPPLY_PROP_HEALTH,
185360
POWER_SUPPLY_PROP_MODEL_NAME,
186361
POWER_SUPPLY_PROP_SERIAL_NUMBER,
362+
POWER_SUPPLY_PROP_MANUFACTURE_YEAR,
363+
POWER_SUPPLY_PROP_MANUFACTURE_MONTH,
364+
POWER_SUPPLY_PROP_MANUFACTURE_DAY,
187365
};
188366

189367
static const struct power_supply_desc macsmc_battery_desc = {
190-
.name = "macsmc-battery",
191-
.type = POWER_SUPPLY_TYPE_BATTERY,
192-
.get_property = macsmc_battery_get_property,
193-
.properties = macsmc_battery_props,
194-
.num_properties = ARRAY_SIZE(macsmc_battery_props),
368+
.name = "macsmc-battery",
369+
.type = POWER_SUPPLY_TYPE_BATTERY,
370+
.get_property = macsmc_battery_get_property,
371+
.set_property = macsmc_battery_set_property,
372+
.property_is_writeable = macsmc_battery_property_is_writeable,
373+
.properties = macsmc_battery_props,
374+
.num_properties = ARRAY_SIZE(macsmc_battery_props),
195375
};
196376

197377
static int macsmc_power_event(struct notifier_block *nb, unsigned long event, void *data)
@@ -232,6 +412,7 @@ static int macsmc_power_probe(struct platform_device *pdev)
232412
/* Fetch string properties */
233413
apple_smc_read(smc, SMC_KEY(BMDN), power->model_name, sizeof(power->model_name) - 1);
234414
apple_smc_read(smc, SMC_KEY(BMSN), power->serial_number, sizeof(power->serial_number) - 1);
415+
apple_smc_read(smc, SMC_KEY(BMDT), power->mfg_date, sizeof(power->mfg_date) - 1);
235416

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

0 commit comments

Comments
 (0)