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+
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+ }
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
60188static 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+
166338static 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
191368static 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
199378static 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