1 // SPDX-License-Identifier: GPL-2.0-only
3 * Battery driver for Marvell 88PM860x PMIC
5 * Copyright (c) 2012 Marvell International Ltd.
6 * Author: Jett Zhou <jtzhou@marvell.com>
7 * Haojian Zhuang <haojian.zhuang@marvell.com>
10 #include <linux/kernel.h>
11 #include <linux/module.h>
12 #include <linux/platform_device.h>
13 #include <linux/slab.h>
14 #include <linux/power_supply.h>
15 #include <linux/mfd/88pm860x.h>
16 #include <linux/delay.h>
17 #include <linux/uaccess.h>
18 #include <asm/div64.h>
20 /* bit definitions of Status Query Interface 2 */
21 #define STATUS2_CHG (1 << 2)
23 /* bit definitions of Reset Out Register */
24 #define RESET_SW_PD (1 << 7)
26 /* bit definitions of PreReg 1 */
27 #define PREREG1_90MA (0x0)
28 #define PREREG1_180MA (0x1)
29 #define PREREG1_450MA (0x4)
30 #define PREREG1_540MA (0x5)
31 #define PREREG1_1350MA (0xE)
32 #define PREREG1_VSYS_4_5V (3 << 4)
34 /* bit definitions of Charger Control 1 Register */
35 #define CC1_MODE_OFF (0)
36 #define CC1_MODE_PRECHARGE (1)
37 #define CC1_MODE_FASTCHARGE (2)
38 #define CC1_MODE_PULSECHARGE (3)
39 #define CC1_ITERM_20MA (0 << 2)
40 #define CC1_ITERM_60MA (2 << 2)
41 #define CC1_VFCHG_4_2V (9 << 4)
43 /* bit definitions of Charger Control 2 Register */
44 #define CC2_ICHG_100MA (0x1)
45 #define CC2_ICHG_500MA (0x9)
46 #define CC2_ICHG_1000MA (0x13)
48 /* bit definitions of Charger Control 3 Register */
49 #define CC3_180MIN_TIMEOUT (0x6 << 4)
50 #define CC3_270MIN_TIMEOUT (0x7 << 4)
51 #define CC3_360MIN_TIMEOUT (0xA << 4)
52 #define CC3_DISABLE_TIMEOUT (0xF << 4)
54 /* bit definitions of Charger Control 4 Register */
55 #define CC4_IPRE_40MA (7)
56 #define CC4_VPCHG_3_2V (3 << 4)
57 #define CC4_IFCHG_MON_EN (1 << 6)
58 #define CC4_BTEMP_MON_EN (1 << 7)
60 /* bit definitions of Charger Control 6 Register */
61 #define CC6_BAT_OV_EN (1 << 2)
62 #define CC6_BAT_UV_EN (1 << 3)
63 #define CC6_UV_VBAT_SET (0x3 << 6) /* 2.8v */
65 /* bit definitions of Charger Control 7 Register */
66 #define CC7_BAT_REM_EN (1 << 3)
67 #define CC7_IFSM_EN (1 << 7)
69 /* bit definitions of Measurement Enable 1 Register */
70 #define MEAS1_VBAT (1 << 0)
72 /* bit definitions of Measurement Enable 3 Register */
73 #define MEAS3_IBAT_EN (1 << 0)
74 #define MEAS3_CC_EN (1 << 2)
77 #define FSM_DISCHARGE 1
78 #define FSM_PRECHARGE 2
79 #define FSM_FASTCHARGE 3
81 #define PRECHARGE_THRESHOLD 3100
82 #define POWEROFF_THRESHOLD 3400
83 #define CHARGE_THRESHOLD 4000
84 #define DISCHARGE_THRESHOLD 4180
86 /* over-temperature on PM8606 setting */
87 #define OVER_TEMP_FLAG (1 << 6)
88 #define OVTEMP_AUTORECOVER (1 << 3)
90 /* over-voltage protect on vchg setting mv */
91 #define VCHG_NORMAL_LOW 4200
92 #define VCHG_NORMAL_CHECK 5800
93 #define VCHG_NORMAL_HIGH 6000
94 #define VCHG_OVP_LOW 5500
96 struct pm860x_charger_info
{
97 struct pm860x_chip
*chip
;
98 struct i2c_client
*i2c
;
99 struct i2c_client
*i2c_8606
;
102 struct power_supply
*usb
;
106 unsigned state
:3; /* fsm state */
107 unsigned online
:1; /* usb charger */
108 unsigned present
:1; /* battery present */
112 static char *pm860x_supplied_to
[] = {
116 static int measure_vchg(struct pm860x_charger_info
*info
, int *data
)
118 unsigned char buf
[2];
121 ret
= pm860x_bulk_read(info
->i2c
, PM8607_VCHG_MEAS1
, 2, buf
);
125 *data
= ((buf
[0] & 0xff) << 4) | (buf
[1] & 0x0f);
126 /* V_BATT_MEAS(mV) = value * 5 * 1.8 * 1000 / (2^12) */
127 *data
= ((*data
& 0xfff) * 9 * 125) >> 9;
129 dev_dbg(info
->dev
, "%s, vchg: %d mv\n", __func__
, *data
);
134 static void set_vchg_threshold(struct pm860x_charger_info
*info
,
139 /* (tmp << 8) * / 5 / 1800 */
143 data
= (min
<< 5) / 1125;
144 pm860x_reg_write(info
->i2c
, PM8607_VCHG_LOWTH
, data
);
145 dev_dbg(info
->dev
, "VCHG_LOWTH:%dmv, 0x%x\n", min
, data
);
150 data
= (max
<< 5) / 1125;
151 pm860x_reg_write(info
->i2c
, PM8607_VCHG_HIGHTH
, data
);
152 dev_dbg(info
->dev
, "VCHG_HIGHTH:%dmv, 0x%x\n", max
, data
);
156 static void set_vbatt_threshold(struct pm860x_charger_info
*info
,
161 /* (tmp << 8) * 3 / 1800 */
165 data
= (min
<< 5) / 675;
166 pm860x_reg_write(info
->i2c
, PM8607_VBAT_LOWTH
, data
);
167 dev_dbg(info
->dev
, "VBAT Min:%dmv, LOWTH:0x%x\n", min
, data
);
172 data
= (max
<< 5) / 675;
173 pm860x_reg_write(info
->i2c
, PM8607_VBAT_HIGHTH
, data
);
174 dev_dbg(info
->dev
, "VBAT Max:%dmv, HIGHTH:0x%x\n", max
, data
);
179 static int start_precharge(struct pm860x_charger_info
*info
)
183 dev_dbg(info
->dev
, "Start Pre-charging!\n");
184 set_vbatt_threshold(info
, 0, 0);
186 ret
= pm860x_reg_write(info
->i2c_8606
, PM8606_PREREGULATORA
,
187 PREREG1_1350MA
| PREREG1_VSYS_4_5V
);
191 ret
= pm860x_set_bits(info
->i2c
, PM8607_CHG_CTRL1
, 3,
195 /* set 270 minutes timeout */
196 ret
= pm860x_set_bits(info
->i2c
, PM8607_CHG_CTRL3
, (0xf << 4),
200 /* set precharge current, termination voltage, IBAT & TBAT monitor */
201 ret
= pm860x_reg_write(info
->i2c
, PM8607_CHG_CTRL4
,
202 CC4_IPRE_40MA
| CC4_VPCHG_3_2V
|
203 CC4_IFCHG_MON_EN
| CC4_BTEMP_MON_EN
);
206 ret
= pm860x_set_bits(info
->i2c
, PM8607_CHG_CTRL7
,
207 CC7_BAT_REM_EN
| CC7_IFSM_EN
,
208 CC7_BAT_REM_EN
| CC7_IFSM_EN
);
211 /* trigger precharge */
212 ret
= pm860x_set_bits(info
->i2c
, PM8607_CHG_CTRL1
, 3,
218 static int start_fastcharge(struct pm860x_charger_info
*info
)
222 dev_dbg(info
->dev
, "Start Fast-charging!\n");
224 /* set fastcharge termination current & voltage, disable charging */
225 ret
= pm860x_reg_write(info
->i2c
, PM8607_CHG_CTRL1
,
226 CC1_MODE_OFF
| CC1_ITERM_60MA
|
230 ret
= pm860x_reg_write(info
->i2c_8606
, PM8606_PREREGULATORA
,
231 PREREG1_540MA
| PREREG1_VSYS_4_5V
);
234 ret
= pm860x_set_bits(info
->i2c
, PM8607_CHG_CTRL2
, 0x1f,
238 /* set 270 minutes timeout */
239 ret
= pm860x_set_bits(info
->i2c
, PM8607_CHG_CTRL3
, (0xf << 4),
243 /* set IBAT & TBAT monitor */
244 ret
= pm860x_set_bits(info
->i2c
, PM8607_CHG_CTRL4
,
245 CC4_IFCHG_MON_EN
| CC4_BTEMP_MON_EN
,
246 CC4_IFCHG_MON_EN
| CC4_BTEMP_MON_EN
);
249 ret
= pm860x_set_bits(info
->i2c
, PM8607_CHG_CTRL6
,
250 CC6_BAT_OV_EN
| CC6_BAT_UV_EN
|
252 CC6_BAT_OV_EN
| CC6_BAT_UV_EN
|
256 ret
= pm860x_set_bits(info
->i2c
, PM8607_CHG_CTRL7
,
257 CC7_BAT_REM_EN
| CC7_IFSM_EN
,
258 CC7_BAT_REM_EN
| CC7_IFSM_EN
);
261 /* launch fast-charge */
262 ret
= pm860x_set_bits(info
->i2c
, PM8607_CHG_CTRL1
, 3,
263 CC1_MODE_FASTCHARGE
);
264 /* vchg threshold setting */
265 set_vchg_threshold(info
, VCHG_NORMAL_LOW
, VCHG_NORMAL_HIGH
);
270 static void stop_charge(struct pm860x_charger_info
*info
, int vbatt
)
272 dev_dbg(info
->dev
, "Stop charging!\n");
273 pm860x_set_bits(info
->i2c
, PM8607_CHG_CTRL1
, 3, CC1_MODE_OFF
);
274 if (vbatt
> CHARGE_THRESHOLD
&& info
->online
)
275 set_vbatt_threshold(info
, CHARGE_THRESHOLD
, 0);
278 static void power_off_notification(struct pm860x_charger_info
*info
)
280 dev_dbg(info
->dev
, "Power-off notification!\n");
283 static int set_charging_fsm(struct pm860x_charger_info
*info
)
285 struct power_supply
*psy
;
286 union power_supply_propval data
;
287 unsigned char fsm_state
[][16] = { "init", "discharge", "precharge",
293 psy
= power_supply_get_by_name(pm860x_supplied_to
[0]);
296 ret
= power_supply_get_property(psy
, POWER_SUPPLY_PROP_VOLTAGE_NOW
,
299 power_supply_put(psy
);
302 vbatt
= data
.intval
/ 1000;
304 ret
= power_supply_get_property(psy
, POWER_SUPPLY_PROP_PRESENT
, &data
);
306 power_supply_put(psy
);
309 power_supply_put(psy
);
311 mutex_lock(&info
->lock
);
312 info
->present
= data
.intval
;
314 dev_dbg(info
->dev
, "Entering FSM:%s, Charger:%s, Battery:%s, "
316 &fsm_state
[info
->state
][0],
317 (info
->online
) ? "online" : "N/A",
318 (info
->present
) ? "present" : "N/A", info
->allowed
);
319 dev_dbg(info
->dev
, "set_charging_fsm:vbatt:%d(mV)\n", vbatt
);
321 switch (info
->state
) {
323 if (info
->online
&& info
->present
&& info
->allowed
) {
324 if (vbatt
< PRECHARGE_THRESHOLD
) {
325 info
->state
= FSM_PRECHARGE
;
326 start_precharge(info
);
327 } else if (vbatt
> DISCHARGE_THRESHOLD
) {
328 info
->state
= FSM_DISCHARGE
;
329 stop_charge(info
, vbatt
);
330 } else if (vbatt
< DISCHARGE_THRESHOLD
) {
331 info
->state
= FSM_FASTCHARGE
;
332 start_fastcharge(info
);
335 if (vbatt
< POWEROFF_THRESHOLD
) {
336 power_off_notification(info
);
338 info
->state
= FSM_DISCHARGE
;
339 stop_charge(info
, vbatt
);
344 if (info
->online
&& info
->present
&& info
->allowed
) {
345 if (vbatt
> PRECHARGE_THRESHOLD
) {
346 info
->state
= FSM_FASTCHARGE
;
347 start_fastcharge(info
);
350 info
->state
= FSM_DISCHARGE
;
351 stop_charge(info
, vbatt
);
355 if (info
->online
&& info
->present
&& info
->allowed
) {
356 if (vbatt
< PRECHARGE_THRESHOLD
) {
357 info
->state
= FSM_PRECHARGE
;
358 start_precharge(info
);
361 info
->state
= FSM_DISCHARGE
;
362 stop_charge(info
, vbatt
);
366 if (info
->online
&& info
->present
&& info
->allowed
) {
367 if (vbatt
< PRECHARGE_THRESHOLD
) {
368 info
->state
= FSM_PRECHARGE
;
369 start_precharge(info
);
370 } else if (vbatt
< DISCHARGE_THRESHOLD
) {
371 info
->state
= FSM_FASTCHARGE
;
372 start_fastcharge(info
);
375 if (vbatt
< POWEROFF_THRESHOLD
)
376 power_off_notification(info
);
377 else if (vbatt
> CHARGE_THRESHOLD
&& info
->online
)
378 set_vbatt_threshold(info
, CHARGE_THRESHOLD
, 0);
382 dev_warn(info
->dev
, "FSM meets wrong state:%d\n",
387 "Out FSM:%s, Charger:%s, Battery:%s, Allowed:%d\n",
388 &fsm_state
[info
->state
][0],
389 (info
->online
) ? "online" : "N/A",
390 (info
->present
) ? "present" : "N/A", info
->allowed
);
391 mutex_unlock(&info
->lock
);
396 static irqreturn_t
pm860x_charger_handler(int irq
, void *data
)
398 struct pm860x_charger_info
*info
= data
;
401 mutex_lock(&info
->lock
);
402 ret
= pm860x_reg_read(info
->i2c
, PM8607_STATUS_2
);
404 mutex_unlock(&info
->lock
);
407 if (ret
& STATUS2_CHG
) {
414 mutex_unlock(&info
->lock
);
415 dev_dbg(info
->dev
, "%s, Charger:%s, Allowed:%d\n", __func__
,
416 (info
->online
) ? "online" : "N/A", info
->allowed
);
418 set_charging_fsm(info
);
420 power_supply_changed(info
->usb
);
425 static irqreturn_t
pm860x_temp_handler(int irq
, void *data
)
427 struct power_supply
*psy
;
428 struct pm860x_charger_info
*info
= data
;
429 union power_supply_propval temp
;
433 psy
= power_supply_get_by_name(pm860x_supplied_to
[0]);
436 ret
= power_supply_get_property(psy
, POWER_SUPPLY_PROP_TEMP
, &temp
);
439 value
= temp
.intval
/ 10;
441 mutex_lock(&info
->lock
);
442 /* Temperature < -10 C or >40 C, Will not allow charge */
443 if (value
< -10 || value
> 40)
447 dev_dbg(info
->dev
, "%s, Allowed: %d\n", __func__
, info
->allowed
);
448 mutex_unlock(&info
->lock
);
450 set_charging_fsm(info
);
452 power_supply_put(psy
);
456 static irqreturn_t
pm860x_exception_handler(int irq
, void *data
)
458 struct pm860x_charger_info
*info
= data
;
460 mutex_lock(&info
->lock
);
462 mutex_unlock(&info
->lock
);
463 dev_dbg(info
->dev
, "%s, irq: %d\n", __func__
, irq
);
465 set_charging_fsm(info
);
469 static irqreturn_t
pm860x_done_handler(int irq
, void *data
)
471 struct pm860x_charger_info
*info
= data
;
472 struct power_supply
*psy
;
473 union power_supply_propval val
;
477 mutex_lock(&info
->lock
);
478 /* pre-charge done, will transimit to fast-charge stage */
479 if (info
->state
== FSM_PRECHARGE
) {
484 * Fast charge done, delay to read
485 * the correct status of CHG_DET.
489 psy
= power_supply_get_by_name(pm860x_supplied_to
[0]);
492 ret
= power_supply_get_property(psy
, POWER_SUPPLY_PROP_VOLTAGE_NOW
,
496 vbatt
= val
.intval
/ 1000;
498 * CHG_DONE interrupt is faster than CHG_DET interrupt when
499 * plug in/out usb, So we can not rely on info->online, we
500 * need check pm8607 status register to check usb is online
501 * or not, then we can decide it is real charge done
502 * automatically or it is triggered by usb plug out;
504 ret
= pm860x_reg_read(info
->i2c
, PM8607_STATUS_2
);
507 if (vbatt
> CHARGE_THRESHOLD
&& ret
& STATUS2_CHG
)
508 power_supply_set_property(psy
, POWER_SUPPLY_PROP_CHARGE_FULL
,
512 power_supply_put(psy
);
514 mutex_unlock(&info
->lock
);
515 dev_dbg(info
->dev
, "%s, Allowed: %d\n", __func__
, info
->allowed
);
516 set_charging_fsm(info
);
521 static irqreturn_t
pm860x_vbattery_handler(int irq
, void *data
)
523 struct pm860x_charger_info
*info
= data
;
525 mutex_lock(&info
->lock
);
527 set_vbatt_threshold(info
, 0, 0);
529 if (info
->present
&& info
->online
)
533 mutex_unlock(&info
->lock
);
534 dev_dbg(info
->dev
, "%s, Allowed: %d\n", __func__
, info
->allowed
);
536 set_charging_fsm(info
);
541 static irqreturn_t
pm860x_vchg_handler(int irq
, void *data
)
543 struct pm860x_charger_info
*info
= data
;
549 measure_vchg(info
, &vchg
);
551 mutex_lock(&info
->lock
);
554 /* check if over-temp on pm8606 or not */
555 status
= pm860x_reg_read(info
->i2c_8606
, PM8606_FLAGS
);
556 if (status
& OVER_TEMP_FLAG
) {
557 /* clear over temp flag and set auto recover */
558 pm860x_set_bits(info
->i2c_8606
, PM8606_FLAGS
,
559 OVER_TEMP_FLAG
, OVER_TEMP_FLAG
);
560 pm860x_set_bits(info
->i2c_8606
,
565 "%s, pm8606 over-temp occurred\n", __func__
);
569 if (vchg
> VCHG_NORMAL_CHECK
) {
570 set_vchg_threshold(info
, VCHG_OVP_LOW
, 0);
573 "%s,pm8607 over-vchg occurred,vchg = %dmv\n",
575 } else if (vchg
< VCHG_OVP_LOW
) {
576 set_vchg_threshold(info
, VCHG_NORMAL_LOW
,
580 "%s,pm8607 over-vchg recover,vchg = %dmv\n",
583 mutex_unlock(&info
->lock
);
585 dev_dbg(info
->dev
, "%s, Allowed: %d\n", __func__
, info
->allowed
);
586 set_charging_fsm(info
);
591 static int pm860x_usb_get_prop(struct power_supply
*psy
,
592 enum power_supply_property psp
,
593 union power_supply_propval
*val
)
595 struct pm860x_charger_info
*info
= power_supply_get_drvdata(psy
);
598 case POWER_SUPPLY_PROP_STATUS
:
599 if (info
->state
== FSM_FASTCHARGE
||
600 info
->state
== FSM_PRECHARGE
)
601 val
->intval
= POWER_SUPPLY_STATUS_CHARGING
;
603 val
->intval
= POWER_SUPPLY_STATUS_DISCHARGING
;
605 case POWER_SUPPLY_PROP_ONLINE
:
606 val
->intval
= info
->online
;
614 static enum power_supply_property pm860x_usb_props
[] = {
615 POWER_SUPPLY_PROP_STATUS
,
616 POWER_SUPPLY_PROP_ONLINE
,
619 static int pm860x_init_charger(struct pm860x_charger_info
*info
)
623 ret
= pm860x_reg_read(info
->i2c
, PM8607_STATUS_2
);
627 mutex_lock(&info
->lock
);
628 info
->state
= FSM_INIT
;
629 if (ret
& STATUS2_CHG
) {
636 mutex_unlock(&info
->lock
);
638 set_charging_fsm(info
);
642 static struct pm860x_irq_desc
{
644 irqreturn_t (*handler
)(int irq
, void *data
);
645 } pm860x_irq_descs
[] = {
646 { "usb supply detect", pm860x_charger_handler
},
647 { "charge done", pm860x_done_handler
},
648 { "charge timeout", pm860x_exception_handler
},
649 { "charge fault", pm860x_exception_handler
},
650 { "temperature", pm860x_temp_handler
},
651 { "vbatt", pm860x_vbattery_handler
},
652 { "vchg", pm860x_vchg_handler
},
655 static const struct power_supply_desc pm860x_charger_desc
= {
657 .type
= POWER_SUPPLY_TYPE_USB
,
658 .properties
= pm860x_usb_props
,
659 .num_properties
= ARRAY_SIZE(pm860x_usb_props
),
660 .get_property
= pm860x_usb_get_prop
,
663 static int pm860x_charger_probe(struct platform_device
*pdev
)
665 struct pm860x_chip
*chip
= dev_get_drvdata(pdev
->dev
.parent
);
666 struct power_supply_config psy_cfg
= {};
667 struct pm860x_charger_info
*info
;
673 info
= devm_kzalloc(&pdev
->dev
, sizeof(*info
), GFP_KERNEL
);
677 count
= pdev
->num_resources
;
678 for (i
= 0, j
= 0; i
< count
; i
++) {
679 info
->irq
[j
] = platform_get_irq(pdev
, i
);
680 if (info
->irq
[j
] < 0)
688 (chip
->id
== CHIP_PM8607
) ? chip
->client
: chip
->companion
;
690 (chip
->id
== CHIP_PM8607
) ? chip
->companion
: chip
->client
;
691 if (!info
->i2c_8606
) {
692 dev_err(&pdev
->dev
, "Missed I2C address of 88PM8606!\n");
696 info
->dev
= &pdev
->dev
;
698 /* set init value for the case we are not using battery */
699 set_vchg_threshold(info
, VCHG_NORMAL_LOW
, VCHG_OVP_LOW
);
701 mutex_init(&info
->lock
);
702 platform_set_drvdata(pdev
, info
);
704 psy_cfg
.drv_data
= info
;
705 psy_cfg
.supplied_to
= pm860x_supplied_to
;
706 psy_cfg
.num_supplicants
= ARRAY_SIZE(pm860x_supplied_to
);
707 info
->usb
= power_supply_register(&pdev
->dev
, &pm860x_charger_desc
,
709 if (IS_ERR(info
->usb
)) {
710 ret
= PTR_ERR(info
->usb
);
714 pm860x_init_charger(info
);
716 for (i
= 0; i
< ARRAY_SIZE(info
->irq
); i
++) {
717 ret
= request_threaded_irq(info
->irq
[i
], NULL
,
718 pm860x_irq_descs
[i
].handler
,
719 IRQF_ONESHOT
, pm860x_irq_descs
[i
].name
, info
);
721 dev_err(chip
->dev
, "Failed to request IRQ: #%d: %d\n",
729 power_supply_unregister(info
->usb
);
731 free_irq(info
->irq
[i
], info
);
736 static int pm860x_charger_remove(struct platform_device
*pdev
)
738 struct pm860x_charger_info
*info
= platform_get_drvdata(pdev
);
741 power_supply_unregister(info
->usb
);
742 for (i
= 0; i
< info
->irq_nums
; i
++)
743 free_irq(info
->irq
[i
], info
);
747 static struct platform_driver pm860x_charger_driver
= {
749 .name
= "88pm860x-charger",
751 .probe
= pm860x_charger_probe
,
752 .remove
= pm860x_charger_remove
,
754 module_platform_driver(pm860x_charger_driver
);
756 MODULE_DESCRIPTION("Marvell 88PM860x Charger driver");
757 MODULE_LICENSE("GPL");