1 // SPDX-License-Identifier: GPL-2.0-only
3 * Power supply driver for the Active-semi ACT8945A PMIC
5 * Copyright (C) 2015 Atmel Corporation
7 * Author: Wenyou Yang <wenyou.yang@atmel.com>
9 #include <linux/interrupt.h>
10 #include <linux/module.h>
12 #include <linux/of_irq.h>
13 #include <linux/platform_device.h>
14 #include <linux/power_supply.h>
15 #include <linux/regmap.h>
16 #include <linux/gpio/consumer.h>
18 static const char *act8945a_charger_model
= "ACT8945A";
19 static const char *act8945a_charger_manufacturer
= "Active-semi";
22 * ACT8945A Charger Register Map
26 #define ACT8945A_APCH_CFG 0x71
27 #define ACT8945A_APCH_STATUS 0x78
28 #define ACT8945A_APCH_CTRL 0x79
29 #define ACT8945A_APCH_STATE 0x7A
31 /* ACT8945A_APCH_CFG */
32 #define APCH_CFG_OVPSET (0x3 << 0)
33 #define APCH_CFG_OVPSET_6V6 (0x0 << 0)
34 #define APCH_CFG_OVPSET_7V (0x1 << 0)
35 #define APCH_CFG_OVPSET_7V5 (0x2 << 0)
36 #define APCH_CFG_OVPSET_8V (0x3 << 0)
37 #define APCH_CFG_PRETIMO (0x3 << 2)
38 #define APCH_CFG_PRETIMO_40_MIN (0x0 << 2)
39 #define APCH_CFG_PRETIMO_60_MIN (0x1 << 2)
40 #define APCH_CFG_PRETIMO_80_MIN (0x2 << 2)
41 #define APCH_CFG_PRETIMO_DISABLED (0x3 << 2)
42 #define APCH_CFG_TOTTIMO (0x3 << 4)
43 #define APCH_CFG_TOTTIMO_3_HOUR (0x0 << 4)
44 #define APCH_CFG_TOTTIMO_4_HOUR (0x1 << 4)
45 #define APCH_CFG_TOTTIMO_5_HOUR (0x2 << 4)
46 #define APCH_CFG_TOTTIMO_DISABLED (0x3 << 4)
47 #define APCH_CFG_SUSCHG (0x1 << 7)
49 #define APCH_STATUS_CHGDAT BIT(0)
50 #define APCH_STATUS_INDAT BIT(1)
51 #define APCH_STATUS_TEMPDAT BIT(2)
52 #define APCH_STATUS_TIMRDAT BIT(3)
53 #define APCH_STATUS_CHGSTAT BIT(4)
54 #define APCH_STATUS_INSTAT BIT(5)
55 #define APCH_STATUS_TEMPSTAT BIT(6)
56 #define APCH_STATUS_TIMRSTAT BIT(7)
58 #define APCH_CTRL_CHGEOCOUT BIT(0)
59 #define APCH_CTRL_INDIS BIT(1)
60 #define APCH_CTRL_TEMPOUT BIT(2)
61 #define APCH_CTRL_TIMRPRE BIT(3)
62 #define APCH_CTRL_CHGEOCIN BIT(4)
63 #define APCH_CTRL_INCON BIT(5)
64 #define APCH_CTRL_TEMPIN BIT(6)
65 #define APCH_CTRL_TIMRTOT BIT(7)
67 #define APCH_STATE_ACINSTAT (0x1 << 1)
68 #define APCH_STATE_CSTATE (0x3 << 4)
69 #define APCH_STATE_CSTATE_SHIFT 4
70 #define APCH_STATE_CSTATE_DISABLED 0x00
71 #define APCH_STATE_CSTATE_EOC 0x01
72 #define APCH_STATE_CSTATE_FAST 0x02
73 #define APCH_STATE_CSTATE_PRE 0x03
75 struct act8945a_charger
{
76 struct power_supply
*psy
;
77 struct power_supply_desc desc
;
78 struct regmap
*regmap
;
79 struct work_struct work
;
82 struct gpio_desc
*lbo_gpio
;
83 struct gpio_desc
*chglev_gpio
;
86 static int act8945a_get_charger_state(struct regmap
*regmap
, int *val
)
89 unsigned int status
, state
;
91 ret
= regmap_read(regmap
, ACT8945A_APCH_STATUS
, &status
);
95 ret
= regmap_read(regmap
, ACT8945A_APCH_STATE
, &state
);
99 state
&= APCH_STATE_CSTATE
;
100 state
>>= APCH_STATE_CSTATE_SHIFT
;
103 case APCH_STATE_CSTATE_PRE
:
104 case APCH_STATE_CSTATE_FAST
:
105 *val
= POWER_SUPPLY_STATUS_CHARGING
;
107 case APCH_STATE_CSTATE_EOC
:
108 if (status
& APCH_STATUS_CHGDAT
)
109 *val
= POWER_SUPPLY_STATUS_FULL
;
111 *val
= POWER_SUPPLY_STATUS_CHARGING
;
113 case APCH_STATE_CSTATE_DISABLED
:
115 if (!(status
& APCH_STATUS_INDAT
))
116 *val
= POWER_SUPPLY_STATUS_DISCHARGING
;
118 *val
= POWER_SUPPLY_STATUS_NOT_CHARGING
;
125 static int act8945a_get_charge_type(struct regmap
*regmap
, int *val
)
128 unsigned int status
, state
;
130 ret
= regmap_read(regmap
, ACT8945A_APCH_STATUS
, &status
);
134 ret
= regmap_read(regmap
, ACT8945A_APCH_STATE
, &state
);
138 state
&= APCH_STATE_CSTATE
;
139 state
>>= APCH_STATE_CSTATE_SHIFT
;
142 case APCH_STATE_CSTATE_PRE
:
143 *val
= POWER_SUPPLY_CHARGE_TYPE_TRICKLE
;
145 case APCH_STATE_CSTATE_FAST
:
146 *val
= POWER_SUPPLY_CHARGE_TYPE_FAST
;
148 case APCH_STATE_CSTATE_EOC
:
149 *val
= POWER_SUPPLY_CHARGE_TYPE_NONE
;
151 case APCH_STATE_CSTATE_DISABLED
:
153 if (!(status
& APCH_STATUS_INDAT
))
154 *val
= POWER_SUPPLY_CHARGE_TYPE_NONE
;
156 *val
= POWER_SUPPLY_CHARGE_TYPE_UNKNOWN
;
163 static int act8945a_get_battery_health(struct regmap
*regmap
, int *val
)
166 unsigned int status
, state
, config
;
168 ret
= regmap_read(regmap
, ACT8945A_APCH_STATUS
, &status
);
172 ret
= regmap_read(regmap
, ACT8945A_APCH_CFG
, &config
);
176 ret
= regmap_read(regmap
, ACT8945A_APCH_STATE
, &state
);
180 state
&= APCH_STATE_CSTATE
;
181 state
>>= APCH_STATE_CSTATE_SHIFT
;
184 case APCH_STATE_CSTATE_DISABLED
:
185 if (config
& APCH_CFG_SUSCHG
) {
186 *val
= POWER_SUPPLY_HEALTH_UNKNOWN
;
187 } else if (status
& APCH_STATUS_INDAT
) {
188 if (!(status
& APCH_STATUS_TEMPDAT
))
189 *val
= POWER_SUPPLY_HEALTH_OVERHEAT
;
190 else if (status
& APCH_STATUS_TIMRDAT
)
191 *val
= POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE
;
193 *val
= POWER_SUPPLY_HEALTH_OVERVOLTAGE
;
195 *val
= POWER_SUPPLY_HEALTH_GOOD
;
198 case APCH_STATE_CSTATE_PRE
:
199 case APCH_STATE_CSTATE_FAST
:
200 case APCH_STATE_CSTATE_EOC
:
202 *val
= POWER_SUPPLY_HEALTH_GOOD
;
209 static int act8945a_get_capacity_level(struct act8945a_charger
*charger
,
210 struct regmap
*regmap
, int *val
)
213 unsigned int status
, state
, config
;
214 int lbo_level
= gpiod_get_value(charger
->lbo_gpio
);
216 ret
= regmap_read(regmap
, ACT8945A_APCH_STATUS
, &status
);
220 ret
= regmap_read(regmap
, ACT8945A_APCH_CFG
, &config
);
224 ret
= regmap_read(regmap
, ACT8945A_APCH_STATE
, &state
);
228 state
&= APCH_STATE_CSTATE
;
229 state
>>= APCH_STATE_CSTATE_SHIFT
;
232 case APCH_STATE_CSTATE_PRE
:
233 *val
= POWER_SUPPLY_CAPACITY_LEVEL_LOW
;
235 case APCH_STATE_CSTATE_FAST
:
237 *val
= POWER_SUPPLY_CAPACITY_LEVEL_HIGH
;
239 *val
= POWER_SUPPLY_CAPACITY_LEVEL_LOW
;
241 case APCH_STATE_CSTATE_EOC
:
242 if (status
& APCH_STATUS_CHGDAT
)
243 *val
= POWER_SUPPLY_CAPACITY_LEVEL_FULL
;
245 *val
= POWER_SUPPLY_CAPACITY_LEVEL_NORMAL
;
247 case APCH_STATE_CSTATE_DISABLED
:
249 if (config
& APCH_CFG_SUSCHG
) {
250 *val
= POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN
;
252 *val
= POWER_SUPPLY_CAPACITY_LEVEL_NORMAL
;
253 if (!(status
& APCH_STATUS_INDAT
)) {
255 *val
= POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL
;
264 #define MAX_CURRENT_USB_HIGH 450000
265 #define MAX_CURRENT_USB_LOW 90000
266 #define MAX_CURRENT_USB_PRE 45000
268 * Riset(K) = 2336 * (1V/Ichg(mA)) - 0.205
271 #define MAX_CURRENT_AC_HIGH 886527
272 #define MAX_CURRENT_AC_LOW 117305
273 #define MAX_CURRENT_AC_HIGH_PRE 88653
274 #define MAX_CURRENT_AC_LOW_PRE 11731
276 static int act8945a_get_current_max(struct act8945a_charger
*charger
,
277 struct regmap
*regmap
, int *val
)
280 unsigned int status
, state
;
281 unsigned int acin_state
;
282 int chgin_level
= gpiod_get_value(charger
->chglev_gpio
);
284 ret
= regmap_read(regmap
, ACT8945A_APCH_STATUS
, &status
);
288 ret
= regmap_read(regmap
, ACT8945A_APCH_STATE
, &state
);
292 acin_state
= (state
& APCH_STATE_ACINSTAT
) >> 1;
294 state
&= APCH_STATE_CSTATE
;
295 state
>>= APCH_STATE_CSTATE_SHIFT
;
298 case APCH_STATE_CSTATE_PRE
:
301 *val
= MAX_CURRENT_AC_HIGH_PRE
;
303 *val
= MAX_CURRENT_AC_LOW_PRE
;
305 *val
= MAX_CURRENT_USB_PRE
;
308 case APCH_STATE_CSTATE_FAST
:
311 *val
= MAX_CURRENT_AC_HIGH
;
313 *val
= MAX_CURRENT_AC_LOW
;
316 *val
= MAX_CURRENT_USB_HIGH
;
318 *val
= MAX_CURRENT_USB_LOW
;
321 case APCH_STATE_CSTATE_EOC
:
322 case APCH_STATE_CSTATE_DISABLED
:
331 static enum power_supply_property act8945a_charger_props
[] = {
332 POWER_SUPPLY_PROP_STATUS
,
333 POWER_SUPPLY_PROP_CHARGE_TYPE
,
334 POWER_SUPPLY_PROP_TECHNOLOGY
,
335 POWER_SUPPLY_PROP_HEALTH
,
336 POWER_SUPPLY_PROP_CAPACITY_LEVEL
,
337 POWER_SUPPLY_PROP_CURRENT_MAX
,
338 POWER_SUPPLY_PROP_MODEL_NAME
,
339 POWER_SUPPLY_PROP_MANUFACTURER
342 static int act8945a_charger_get_property(struct power_supply
*psy
,
343 enum power_supply_property prop
,
344 union power_supply_propval
*val
)
346 struct act8945a_charger
*charger
= power_supply_get_drvdata(psy
);
347 struct regmap
*regmap
= charger
->regmap
;
351 case POWER_SUPPLY_PROP_STATUS
:
352 ret
= act8945a_get_charger_state(regmap
, &val
->intval
);
354 case POWER_SUPPLY_PROP_CHARGE_TYPE
:
355 ret
= act8945a_get_charge_type(regmap
, &val
->intval
);
357 case POWER_SUPPLY_PROP_TECHNOLOGY
:
358 val
->intval
= POWER_SUPPLY_TECHNOLOGY_LION
;
360 case POWER_SUPPLY_PROP_HEALTH
:
361 ret
= act8945a_get_battery_health(regmap
, &val
->intval
);
363 case POWER_SUPPLY_PROP_CAPACITY_LEVEL
:
364 ret
= act8945a_get_capacity_level(charger
,
365 regmap
, &val
->intval
);
367 case POWER_SUPPLY_PROP_CURRENT_MAX
:
368 ret
= act8945a_get_current_max(charger
,
369 regmap
, &val
->intval
);
371 case POWER_SUPPLY_PROP_MODEL_NAME
:
372 val
->strval
= act8945a_charger_model
;
374 case POWER_SUPPLY_PROP_MANUFACTURER
:
375 val
->strval
= act8945a_charger_manufacturer
;
384 static int act8945a_enable_interrupt(struct act8945a_charger
*charger
)
386 struct regmap
*regmap
= charger
->regmap
;
390 ctrl
= APCH_CTRL_CHGEOCOUT
| APCH_CTRL_CHGEOCIN
|
391 APCH_CTRL_INDIS
| APCH_CTRL_INCON
|
392 APCH_CTRL_TEMPOUT
| APCH_CTRL_TEMPIN
|
393 APCH_CTRL_TIMRPRE
| APCH_CTRL_TIMRTOT
;
394 ret
= regmap_write(regmap
, ACT8945A_APCH_CTRL
, ctrl
);
398 ctrl
= APCH_STATUS_CHGSTAT
| APCH_STATUS_INSTAT
|
399 APCH_STATUS_TEMPSTAT
| APCH_STATUS_TIMRSTAT
;
400 ret
= regmap_write(regmap
, ACT8945A_APCH_STATUS
, ctrl
);
407 static unsigned int act8945a_set_supply_type(struct act8945a_charger
*charger
,
410 unsigned int status
, state
;
413 ret
= regmap_read(charger
->regmap
, ACT8945A_APCH_STATUS
, &status
);
417 ret
= regmap_read(charger
->regmap
, ACT8945A_APCH_STATE
, &state
);
421 if (status
& APCH_STATUS_INDAT
) {
422 if (state
& APCH_STATE_ACINSTAT
)
423 *type
= POWER_SUPPLY_TYPE_MAINS
;
425 *type
= POWER_SUPPLY_TYPE_USB
;
427 *type
= POWER_SUPPLY_TYPE_BATTERY
;
433 static void act8945a_work(struct work_struct
*work
)
435 struct act8945a_charger
*charger
=
436 container_of(work
, struct act8945a_charger
, work
);
438 act8945a_set_supply_type(charger
, &charger
->desc
.type
);
440 power_supply_changed(charger
->psy
);
443 static irqreturn_t
act8945a_status_changed(int irq
, void *dev_id
)
445 struct act8945a_charger
*charger
= dev_id
;
447 if (charger
->init_done
)
448 schedule_work(&charger
->work
);
453 #define DEFAULT_TOTAL_TIME_OUT 3
454 #define DEFAULT_PRE_TIME_OUT 40
455 #define DEFAULT_INPUT_OVP_THRESHOLD 6600
457 static int act8945a_charger_config(struct device
*dev
,
458 struct act8945a_charger
*charger
)
460 struct device_node
*np
= dev
->of_node
;
461 struct regmap
*regmap
= charger
->regmap
;
465 u32 input_voltage_threshold
;
469 unsigned int value
= 0;
472 dev_err(dev
, "no charger of node\n");
476 ret
= regmap_read(regmap
, ACT8945A_APCH_CFG
, &tmp
);
480 if (tmp
& APCH_CFG_SUSCHG
) {
481 value
|= APCH_CFG_SUSCHG
;
482 dev_info(dev
, "have been suspended\n");
485 charger
->lbo_gpio
= devm_gpiod_get_optional(dev
, "active-semi,lbo",
487 if (IS_ERR(charger
->lbo_gpio
)) {
488 err
= PTR_ERR(charger
->lbo_gpio
);
489 dev_err(dev
, "unable to claim gpio \"lbo\": %d\n", err
);
493 ret
= devm_request_irq(dev
, gpiod_to_irq(charger
->lbo_gpio
),
494 act8945a_status_changed
,
495 (IRQF_TRIGGER_FALLING
| IRQF_TRIGGER_RISING
),
496 "act8945a_lbo_detect", charger
);
498 dev_info(dev
, "failed to request gpio \"lbo\" IRQ\n");
500 charger
->chglev_gpio
= devm_gpiod_get_optional(dev
,
501 "active-semi,chglev",
503 if (IS_ERR(charger
->chglev_gpio
)) {
504 err
= PTR_ERR(charger
->chglev_gpio
);
505 dev_err(dev
, "unable to claim gpio \"chglev\": %d\n", err
);
509 if (of_property_read_u32(np
,
510 "active-semi,input-voltage-threshold-microvolt",
511 &input_voltage_threshold
))
512 input_voltage_threshold
= DEFAULT_INPUT_OVP_THRESHOLD
;
514 if (of_property_read_u32(np
,
515 "active-semi,precondition-timeout",
517 pre_time_out
= DEFAULT_PRE_TIME_OUT
;
519 if (of_property_read_u32(np
, "active-semi,total-timeout",
521 total_time_out
= DEFAULT_TOTAL_TIME_OUT
;
523 switch (input_voltage_threshold
) {
525 value
|= APCH_CFG_OVPSET_8V
;
528 value
|= APCH_CFG_OVPSET_7V5
;
531 value
|= APCH_CFG_OVPSET_7V
;
535 value
|= APCH_CFG_OVPSET_6V6
;
539 switch (pre_time_out
) {
541 value
|= APCH_CFG_PRETIMO_60_MIN
;
544 value
|= APCH_CFG_PRETIMO_80_MIN
;
547 value
|= APCH_CFG_PRETIMO_DISABLED
;
551 value
|= APCH_CFG_PRETIMO_40_MIN
;
555 switch (total_time_out
) {
557 value
|= APCH_CFG_TOTTIMO_4_HOUR
;
560 value
|= APCH_CFG_TOTTIMO_5_HOUR
;
563 value
|= APCH_CFG_TOTTIMO_DISABLED
;
567 value
|= APCH_CFG_TOTTIMO_3_HOUR
;
571 return regmap_write(regmap
, ACT8945A_APCH_CFG
, value
);
574 static int act8945a_charger_probe(struct platform_device
*pdev
)
576 struct act8945a_charger
*charger
;
577 struct power_supply_config psy_cfg
= {};
580 charger
= devm_kzalloc(&pdev
->dev
, sizeof(*charger
), GFP_KERNEL
);
584 charger
->regmap
= dev_get_regmap(pdev
->dev
.parent
, NULL
);
585 if (!charger
->regmap
) {
586 dev_err(&pdev
->dev
, "Parent did not provide regmap\n");
590 ret
= act8945a_charger_config(&pdev
->dev
, charger
);
594 irq
= of_irq_get(pdev
->dev
.of_node
, 0);
596 dev_err(&pdev
->dev
, "failed to find IRQ number\n");
597 return irq
?: -ENXIO
;
600 ret
= devm_request_irq(&pdev
->dev
, irq
, act8945a_status_changed
,
601 IRQF_TRIGGER_FALLING
, "act8945a_interrupt",
604 dev_err(&pdev
->dev
, "failed to request nIRQ pin IRQ\n");
608 charger
->desc
.name
= "act8945a-charger";
609 charger
->desc
.get_property
= act8945a_charger_get_property
;
610 charger
->desc
.properties
= act8945a_charger_props
;
611 charger
->desc
.num_properties
= ARRAY_SIZE(act8945a_charger_props
);
613 ret
= act8945a_set_supply_type(charger
, &charger
->desc
.type
);
617 psy_cfg
.of_node
= pdev
->dev
.of_node
;
618 psy_cfg
.drv_data
= charger
;
620 charger
->psy
= devm_power_supply_register(&pdev
->dev
,
623 if (IS_ERR(charger
->psy
)) {
624 dev_err(&pdev
->dev
, "failed to register power supply\n");
625 return PTR_ERR(charger
->psy
);
628 platform_set_drvdata(pdev
, charger
);
630 INIT_WORK(&charger
->work
, act8945a_work
);
632 ret
= act8945a_enable_interrupt(charger
);
636 charger
->init_done
= true;
641 static int act8945a_charger_remove(struct platform_device
*pdev
)
643 struct act8945a_charger
*charger
= platform_get_drvdata(pdev
);
645 charger
->init_done
= false;
646 cancel_work_sync(&charger
->work
);
651 static struct platform_driver act8945a_charger_driver
= {
653 .name
= "act8945a-charger",
655 .probe
= act8945a_charger_probe
,
656 .remove
= act8945a_charger_remove
,
658 module_platform_driver(act8945a_charger_driver
);
660 MODULE_DESCRIPTION("Active-semi ACT8945A ActivePath charger driver");
661 MODULE_AUTHOR("Wenyou Yang <wenyou.yang@atmel.com>");
662 MODULE_LICENSE("GPL");