1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (c) 2022-2024, Linaro Ltd
8 #include <linux/auxiliary_bus.h>
9 #include <linux/bits.h>
10 #include <linux/cleanup.h>
11 #include <linux/delay.h>
12 #include <linux/jiffies.h>
13 #include <linux/module.h>
14 #include <linux/mutex.h>
15 #include <linux/notifier.h>
16 #include <linux/power_supply.h>
17 #include <linux/platform_data/lenovo-yoga-c630.h>
19 struct yoga_c630_psy
{
20 struct yoga_c630_ec
*ec
;
22 struct fwnode_handle
*fwnode
;
23 struct notifier_block nb
;
25 /* guards all battery properties and registration of power supplies */
28 struct power_supply
*adp_psy
;
29 struct power_supply
*bat_psy
;
31 unsigned long last_status_update
;
38 unsigned int bat_status
;
39 unsigned int design_capacity
;
40 unsigned int design_voltage
;
41 unsigned int full_charge_capacity
;
43 unsigned int capacity_now
;
44 unsigned int voltage_now
;
50 #define LENOVO_EC_CACHE_TIME (10 * HZ)
52 #define LENOVO_EC_ADPT_STATUS 0xa3
53 #define LENOVO_EC_ADPT_STATUS_PRESENT BIT(7)
54 #define LENOVO_EC_BAT_ATTRIBUTES 0xc0
55 #define LENOVO_EC_BAT_ATTRIBUTES_UNIT_IS_MA BIT(1)
56 #define LENOVO_EC_BAT_STATUS 0xc1
57 #define LENOVO_EC_BAT_STATUS_DISCHARGING BIT(0)
58 #define LENOVO_EC_BAT_STATUS_CHARGING BIT(1)
59 #define LENOVO_EC_BAT_REMAIN_CAPACITY 0xc2
60 #define LENOVO_EC_BAT_VOLTAGE 0xc6
61 #define LENOVO_EC_BAT_DESIGN_VOLTAGE 0xc8
62 #define LENOVO_EC_BAT_DESIGN_CAPACITY 0xca
63 #define LENOVO_EC_BAT_FULL_CAPACITY 0xcc
64 #define LENOVO_EC_BAT_CURRENT 0xd2
65 #define LENOVO_EC_BAT_FULL_FACTORY 0xd6
66 #define LENOVO_EC_BAT_PRESENT 0xda
67 #define LENOVO_EC_BAT_PRESENT_IS_PRESENT BIT(0)
68 #define LENOVO_EC_BAT_FULL_REGISTER 0xdb
69 #define LENOVO_EC_BAT_FULL_REGISTER_IS_FACTORY BIT(0)
71 static int yoga_c630_psy_update_bat_info(struct yoga_c630_psy
*ecbat
)
73 struct yoga_c630_ec
*ec
= ecbat
->ec
;
76 lockdep_assert_held(&ecbat
->lock
);
78 val
= yoga_c630_ec_read8(ec
, LENOVO_EC_BAT_PRESENT
);
81 ecbat
->bat_present
= !!(val
& LENOVO_EC_BAT_PRESENT_IS_PRESENT
);
82 if (!ecbat
->bat_present
)
85 val
= yoga_c630_ec_read8(ec
, LENOVO_EC_BAT_ATTRIBUTES
);
88 ecbat
->unit_mA
= val
& LENOVO_EC_BAT_ATTRIBUTES_UNIT_IS_MA
;
90 val
= yoga_c630_ec_read16(ec
, LENOVO_EC_BAT_DESIGN_CAPACITY
);
93 ecbat
->design_capacity
= val
* 1000;
96 * DSDT has delays after most of EC reads in these methods.
97 * Having no documentation for the EC we have to follow and sleep here.
101 val
= yoga_c630_ec_read16(ec
, LENOVO_EC_BAT_DESIGN_VOLTAGE
);
104 ecbat
->design_voltage
= val
;
108 val
= yoga_c630_ec_read8(ec
, LENOVO_EC_BAT_FULL_REGISTER
);
111 val
= yoga_c630_ec_read16(ec
,
112 val
& LENOVO_EC_BAT_FULL_REGISTER_IS_FACTORY
?
113 LENOVO_EC_BAT_FULL_FACTORY
:
114 LENOVO_EC_BAT_FULL_CAPACITY
);
118 ecbat
->full_charge_capacity
= val
* 1000;
120 if (!ecbat
->unit_mA
) {
121 ecbat
->design_capacity
*= 10;
122 ecbat
->full_charge_capacity
*= 10;
128 static int yoga_c630_psy_maybe_update_bat_status(struct yoga_c630_psy
*ecbat
)
130 struct yoga_c630_ec
*ec
= ecbat
->ec
;
134 guard(mutex
)(&ecbat
->lock
);
135 if (time_before(jiffies
, ecbat
->last_status_update
+ LENOVO_EC_CACHE_TIME
))
138 val
= yoga_c630_ec_read8(ec
, LENOVO_EC_BAT_STATUS
);
141 ecbat
->bat_status
= val
;
145 val
= yoga_c630_ec_read16(ec
, LENOVO_EC_BAT_REMAIN_CAPACITY
);
148 ecbat
->capacity_now
= val
* 1000;
152 val
= yoga_c630_ec_read16(ec
, LENOVO_EC_BAT_VOLTAGE
);
155 ecbat
->voltage_now
= val
* 1000;
159 val
= yoga_c630_ec_read16(ec
, LENOVO_EC_BAT_CURRENT
);
162 current_mA
= sign_extend32(val
, 15);
163 ecbat
->current_now
= current_mA
* 1000;
164 ecbat
->rate_now
= current_mA
* (ecbat
->voltage_now
/ 1000);
169 ecbat
->capacity_now
*= 10;
171 ecbat
->last_status_update
= jiffies
;
176 static int yoga_c630_psy_update_adapter_status(struct yoga_c630_psy
*ecbat
)
178 struct yoga_c630_ec
*ec
= ecbat
->ec
;
181 guard(mutex
)(&ecbat
->lock
);
183 val
= yoga_c630_ec_read8(ec
, LENOVO_EC_ADPT_STATUS
);
187 ecbat
->adapter_online
= !!(val
& LENOVO_EC_ADPT_STATUS_PRESENT
);
192 static bool yoga_c630_psy_is_charged(struct yoga_c630_psy
*ecbat
)
194 if (ecbat
->bat_status
!= 0)
197 if (ecbat
->full_charge_capacity
<= ecbat
->capacity_now
)
200 if (ecbat
->design_capacity
<= ecbat
->capacity_now
)
206 static int yoga_c630_psy_bat_get_property(struct power_supply
*psy
,
207 enum power_supply_property psp
,
208 union power_supply_propval
*val
)
210 struct yoga_c630_psy
*ecbat
= power_supply_get_drvdata(psy
);
213 if (!ecbat
->bat_present
&& psp
!= POWER_SUPPLY_PROP_PRESENT
)
216 rc
= yoga_c630_psy_maybe_update_bat_status(ecbat
);
221 case POWER_SUPPLY_PROP_STATUS
:
222 if (ecbat
->bat_status
& LENOVO_EC_BAT_STATUS_DISCHARGING
)
223 val
->intval
= POWER_SUPPLY_STATUS_DISCHARGING
;
224 else if (ecbat
->bat_status
& LENOVO_EC_BAT_STATUS_CHARGING
)
225 val
->intval
= POWER_SUPPLY_STATUS_CHARGING
;
226 else if (yoga_c630_psy_is_charged(ecbat
))
227 val
->intval
= POWER_SUPPLY_STATUS_FULL
;
229 val
->intval
= POWER_SUPPLY_STATUS_NOT_CHARGING
;
231 case POWER_SUPPLY_PROP_PRESENT
:
232 val
->intval
= ecbat
->bat_present
;
234 case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN
:
235 val
->intval
= ecbat
->design_voltage
;
237 case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN
:
238 case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN
:
239 val
->intval
= ecbat
->design_capacity
;
241 case POWER_SUPPLY_PROP_CHARGE_FULL
:
242 case POWER_SUPPLY_PROP_ENERGY_FULL
:
243 val
->intval
= ecbat
->full_charge_capacity
;
245 case POWER_SUPPLY_PROP_CHARGE_NOW
:
246 case POWER_SUPPLY_PROP_ENERGY_NOW
:
247 val
->intval
= ecbat
->capacity_now
;
249 case POWER_SUPPLY_PROP_CURRENT_NOW
:
250 val
->intval
= ecbat
->current_now
;
252 case POWER_SUPPLY_PROP_POWER_NOW
:
253 val
->intval
= ecbat
->rate_now
;
255 case POWER_SUPPLY_PROP_VOLTAGE_NOW
:
256 val
->intval
= ecbat
->voltage_now
;
258 case POWER_SUPPLY_PROP_TECHNOLOGY
:
259 val
->intval
= POWER_SUPPLY_TECHNOLOGY_LION
;
261 case POWER_SUPPLY_PROP_MODEL_NAME
:
262 val
->strval
= "PABAS0241231";
264 case POWER_SUPPLY_PROP_MANUFACTURER
:
265 val
->strval
= "Compal";
267 case POWER_SUPPLY_PROP_SCOPE
:
268 val
->intval
= POWER_SUPPLY_SCOPE_SYSTEM
;
278 static enum power_supply_property yoga_c630_psy_bat_mA_properties
[] = {
279 POWER_SUPPLY_PROP_STATUS
,
280 POWER_SUPPLY_PROP_PRESENT
,
281 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN
,
282 POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN
,
283 POWER_SUPPLY_PROP_CHARGE_FULL
,
284 POWER_SUPPLY_PROP_CHARGE_NOW
,
285 POWER_SUPPLY_PROP_CURRENT_NOW
,
286 POWER_SUPPLY_PROP_POWER_NOW
,
287 POWER_SUPPLY_PROP_VOLTAGE_NOW
,
288 POWER_SUPPLY_PROP_TECHNOLOGY
,
289 POWER_SUPPLY_PROP_MODEL_NAME
,
290 POWER_SUPPLY_PROP_MANUFACTURER
,
291 POWER_SUPPLY_PROP_SCOPE
,
294 static enum power_supply_property yoga_c630_psy_bat_mWh_properties
[] = {
295 POWER_SUPPLY_PROP_STATUS
,
296 POWER_SUPPLY_PROP_PRESENT
,
297 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN
,
298 POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN
,
299 POWER_SUPPLY_PROP_ENERGY_FULL
,
300 POWER_SUPPLY_PROP_ENERGY_NOW
,
301 POWER_SUPPLY_PROP_CURRENT_NOW
,
302 POWER_SUPPLY_PROP_POWER_NOW
,
303 POWER_SUPPLY_PROP_VOLTAGE_NOW
,
304 POWER_SUPPLY_PROP_TECHNOLOGY
,
305 POWER_SUPPLY_PROP_MODEL_NAME
,
306 POWER_SUPPLY_PROP_MANUFACTURER
,
307 POWER_SUPPLY_PROP_SCOPE
,
310 static const struct power_supply_desc yoga_c630_psy_bat_psy_desc_mA
= {
311 .name
= "yoga-c630-battery",
312 .type
= POWER_SUPPLY_TYPE_BATTERY
,
313 .properties
= yoga_c630_psy_bat_mA_properties
,
314 .num_properties
= ARRAY_SIZE(yoga_c630_psy_bat_mA_properties
),
315 .get_property
= yoga_c630_psy_bat_get_property
,
318 static const struct power_supply_desc yoga_c630_psy_bat_psy_desc_mWh
= {
319 .name
= "yoga-c630-battery",
320 .type
= POWER_SUPPLY_TYPE_BATTERY
,
321 .properties
= yoga_c630_psy_bat_mWh_properties
,
322 .num_properties
= ARRAY_SIZE(yoga_c630_psy_bat_mWh_properties
),
323 .get_property
= yoga_c630_psy_bat_get_property
,
326 static int yoga_c630_psy_adpt_get_property(struct power_supply
*psy
,
327 enum power_supply_property psp
,
328 union power_supply_propval
*val
)
330 struct yoga_c630_psy
*ecbat
= power_supply_get_drvdata(psy
);
333 ret
= yoga_c630_psy_update_adapter_status(ecbat
);
338 case POWER_SUPPLY_PROP_ONLINE
:
339 val
->intval
= ecbat
->adapter_online
;
341 case POWER_SUPPLY_PROP_USB_TYPE
:
342 val
->intval
= POWER_SUPPLY_USB_TYPE_C
;
351 static enum power_supply_property yoga_c630_psy_adpt_properties
[] = {
352 POWER_SUPPLY_PROP_ONLINE
,
353 POWER_SUPPLY_PROP_USB_TYPE
,
356 static const struct power_supply_desc yoga_c630_psy_adpt_psy_desc
= {
357 .name
= "yoga-c630-adapter",
358 .type
= POWER_SUPPLY_TYPE_USB
,
359 .usb_types
= BIT(POWER_SUPPLY_USB_TYPE_C
),
360 .properties
= yoga_c630_psy_adpt_properties
,
361 .num_properties
= ARRAY_SIZE(yoga_c630_psy_adpt_properties
),
362 .get_property
= yoga_c630_psy_adpt_get_property
,
365 static int yoga_c630_psy_register_bat_psy(struct yoga_c630_psy
*ecbat
)
367 struct power_supply_config bat_cfg
= {};
369 bat_cfg
.drv_data
= ecbat
;
370 bat_cfg
.fwnode
= ecbat
->fwnode
;
371 bat_cfg
.no_wakeup_source
= true;
372 ecbat
->bat_psy
= power_supply_register(ecbat
->dev
,
374 &yoga_c630_psy_bat_psy_desc_mA
:
375 &yoga_c630_psy_bat_psy_desc_mWh
,
377 if (IS_ERR(ecbat
->bat_psy
)) {
378 dev_err(ecbat
->dev
, "failed to register battery supply\n");
379 return PTR_ERR(ecbat
->bat_psy
);
385 static void yoga_c630_ec_refresh_bat_info(struct yoga_c630_psy
*ecbat
)
389 guard(mutex
)(&ecbat
->lock
);
391 current_unit
= ecbat
->unit_mA
;
393 yoga_c630_psy_update_bat_info(ecbat
);
395 if (current_unit
!= ecbat
->unit_mA
) {
396 power_supply_unregister(ecbat
->bat_psy
);
397 yoga_c630_psy_register_bat_psy(ecbat
);
401 static int yoga_c630_psy_notify(struct notifier_block
*nb
,
402 unsigned long action
, void *data
)
404 struct yoga_c630_psy
*ecbat
= container_of(nb
, struct yoga_c630_psy
, nb
);
407 case LENOVO_EC_EVENT_BAT_INFO
:
408 yoga_c630_ec_refresh_bat_info(ecbat
);
410 case LENOVO_EC_EVENT_BAT_ADPT_STATUS
:
411 power_supply_changed(ecbat
->adp_psy
);
413 case LENOVO_EC_EVENT_BAT_STATUS
:
414 power_supply_changed(ecbat
->bat_psy
);
421 static int yoga_c630_psy_probe(struct auxiliary_device
*adev
,
422 const struct auxiliary_device_id
*id
)
424 struct yoga_c630_ec
*ec
= adev
->dev
.platform_data
;
425 struct power_supply_config adp_cfg
= {};
426 struct device
*dev
= &adev
->dev
;
427 struct yoga_c630_psy
*ecbat
;
430 ecbat
= devm_kzalloc(&adev
->dev
, sizeof(*ecbat
), GFP_KERNEL
);
436 mutex_init(&ecbat
->lock
);
437 ecbat
->fwnode
= adev
->dev
.parent
->fwnode
;
438 ecbat
->nb
.notifier_call
= yoga_c630_psy_notify
;
440 auxiliary_set_drvdata(adev
, ecbat
);
442 adp_cfg
.drv_data
= ecbat
;
443 adp_cfg
.fwnode
= ecbat
->fwnode
;
444 adp_cfg
.supplied_to
= (char **)&yoga_c630_psy_bat_psy_desc_mA
.name
;
445 adp_cfg
.num_supplicants
= 1;
446 adp_cfg
.no_wakeup_source
= true;
447 ecbat
->adp_psy
= devm_power_supply_register(dev
, &yoga_c630_psy_adpt_psy_desc
, &adp_cfg
);
448 if (IS_ERR(ecbat
->adp_psy
)) {
449 dev_err(dev
, "failed to register AC adapter supply\n");
450 return PTR_ERR(ecbat
->adp_psy
);
453 scoped_guard(mutex
, &ecbat
->lock
) {
454 ret
= yoga_c630_psy_update_bat_info(ecbat
);
458 ret
= yoga_c630_psy_register_bat_psy(ecbat
);
463 ret
= yoga_c630_ec_register_notify(ecbat
->ec
, &ecbat
->nb
);
470 power_supply_unregister(ecbat
->bat_psy
);
474 static void yoga_c630_psy_remove(struct auxiliary_device
*adev
)
476 struct yoga_c630_psy
*ecbat
= auxiliary_get_drvdata(adev
);
478 yoga_c630_ec_unregister_notify(ecbat
->ec
, &ecbat
->nb
);
479 power_supply_unregister(ecbat
->bat_psy
);
482 static const struct auxiliary_device_id yoga_c630_psy_id_table
[] = {
483 { .name
= YOGA_C630_MOD_NAME
"." YOGA_C630_DEV_PSY
, },
486 MODULE_DEVICE_TABLE(auxiliary
, yoga_c630_psy_id_table
);
488 static struct auxiliary_driver yoga_c630_psy_driver
= {
489 .name
= YOGA_C630_DEV_PSY
,
490 .id_table
= yoga_c630_psy_id_table
,
491 .probe
= yoga_c630_psy_probe
,
492 .remove
= yoga_c630_psy_remove
,
495 module_auxiliary_driver(yoga_c630_psy_driver
);
497 MODULE_DESCRIPTION("Lenovo Yoga C630 psy");
498 MODULE_LICENSE("GPL");