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+
2646static 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
58187static 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+
164337static 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
189367static 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
197377static 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