1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * MP2629 battery charger driver
5 * Copyright 2020 Monolithic Power Systems, Inc
7 * Author: Saravanan Sekar <sravanhome@gmail.com>
10 #include <linux/bits.h>
11 #include <linux/iio/consumer.h>
12 #include <linux/iio/types.h>
13 #include <linux/interrupt.h>
14 #include <linux/mfd/mp2629.h>
15 #include <linux/module.h>
16 #include <linux/mod_devicetable.h>
17 #include <linux/platform_device.h>
18 #include <linux/power_supply.h>
19 #include <linux/regmap.h>
21 #define MP2629_REG_INPUT_ILIM 0x00
22 #define MP2629_REG_INPUT_VLIM 0x01
23 #define MP2629_REG_CHARGE_CTRL 0x04
24 #define MP2629_REG_CHARGE_ILIM 0x05
25 #define MP2629_REG_PRECHARGE 0x06
26 #define MP2629_REG_TERM_CURRENT 0x06
27 #define MP2629_REG_CHARGE_VLIM 0x07
28 #define MP2629_REG_TIMER_CTRL 0x08
29 #define MP2629_REG_IMPEDANCE_COMP 0x09
30 #define MP2629_REG_INTERRUPT 0x0b
31 #define MP2629_REG_STATUS 0x0c
32 #define MP2629_REG_FAULT 0x0d
34 #define MP2629_MASK_INPUT_TYPE GENMASK(7, 5)
35 #define MP2629_MASK_CHARGE_TYPE GENMASK(4, 3)
36 #define MP2629_MASK_CHARGE_CTRL GENMASK(5, 4)
37 #define MP2629_MASK_WDOG_CTRL GENMASK(5, 4)
38 #define MP2629_MASK_IMPEDANCE GENMASK(7, 4)
40 #define MP2629_INPUTSOURCE_CHANGE GENMASK(7, 5)
41 #define MP2629_CHARGING_CHANGE GENMASK(4, 3)
42 #define MP2629_FAULT_BATTERY BIT(3)
43 #define MP2629_FAULT_THERMAL BIT(4)
44 #define MP2629_FAULT_INPUT BIT(5)
45 #define MP2629_FAULT_OTG BIT(6)
47 #define MP2629_MAX_BATT_CAPACITY 100
49 #define MP2629_PROPS(_idx, _min, _max, _step) \
56 enum mp2629_source_type
{
57 MP2629_SOURCE_TYPE_NO_INPUT
,
58 MP2629_SOURCE_TYPE_NON_STD
,
59 MP2629_SOURCE_TYPE_SDP
,
60 MP2629_SOURCE_TYPE_CDP
,
61 MP2629_SOURCE_TYPE_DCP
,
62 MP2629_SOURCE_TYPE_OTG
= 7,
75 struct mp2629_charger
{
80 struct regmap
*regmap
;
81 struct regmap_field
*regmap_fields
[MP2629_MAX_FIELD
];
83 struct power_supply
*usb
;
84 struct power_supply
*battery
;
85 struct iio_channel
*iiochan
[MP2629_ADC_CHAN_END
];
97 static enum power_supply_property mp2629_charger_usb_props
[] = {
98 POWER_SUPPLY_PROP_ONLINE
,
99 POWER_SUPPLY_PROP_USB_TYPE
,
100 POWER_SUPPLY_PROP_VOLTAGE_NOW
,
101 POWER_SUPPLY_PROP_CURRENT_NOW
,
102 POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT
,
103 POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT
,
106 static enum power_supply_property mp2629_charger_bat_props
[] = {
107 POWER_SUPPLY_PROP_STATUS
,
108 POWER_SUPPLY_PROP_HEALTH
,
109 POWER_SUPPLY_PROP_CHARGE_TYPE
,
110 POWER_SUPPLY_PROP_VOLTAGE_NOW
,
111 POWER_SUPPLY_PROP_CURRENT_NOW
,
112 POWER_SUPPLY_PROP_CAPACITY
,
113 POWER_SUPPLY_PROP_PRECHARGE_CURRENT
,
114 POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT
,
115 POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT
,
116 POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE
,
117 POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX
,
118 POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX
,
121 static struct mp2629_prop props
[] = {
122 MP2629_PROPS(INPUT_ILIM
, 100000, 3250000, 50000),
123 MP2629_PROPS(INPUT_VLIM
, 3800000, 5300000, 100000),
124 MP2629_PROPS(CHARGE_ILIM
, 320000, 4520000, 40000),
125 MP2629_PROPS(CHARGE_VLIM
, 3400000, 4670000, 10000),
126 MP2629_PROPS(PRECHARGE
, 120000, 720000, 40000),
127 MP2629_PROPS(TERM_CURRENT
, 80000, 680000, 40000),
130 static const struct reg_field mp2629_reg_fields
[] = {
131 [INPUT_ILIM
] = REG_FIELD(MP2629_REG_INPUT_ILIM
, 0, 5),
132 [INPUT_VLIM
] = REG_FIELD(MP2629_REG_INPUT_VLIM
, 0, 3),
133 [CHARGE_ILIM
] = REG_FIELD(MP2629_REG_CHARGE_ILIM
, 0, 6),
134 [CHARGE_VLIM
] = REG_FIELD(MP2629_REG_CHARGE_VLIM
, 1, 7),
135 [PRECHARGE
] = REG_FIELD(MP2629_REG_PRECHARGE
, 4, 7),
136 [TERM_CURRENT
] = REG_FIELD(MP2629_REG_TERM_CURRENT
, 0, 3),
139 static char *adc_chan_name
[] = {
141 "mp2629-system-volt",
143 "mp2629-batt-current",
144 "mp2629-input-current",
147 static int mp2629_read_adc(struct mp2629_charger
*charger
,
148 enum mp2629_adc_chan ch
,
149 union power_supply_propval
*val
)
154 ret
= iio_read_channel_processed(charger
->iiochan
[ch
], &chval
);
158 val
->intval
= chval
* 1000;
163 static int mp2629_get_prop(struct mp2629_charger
*charger
,
164 enum mp2629_field fld
,
165 union power_supply_propval
*val
)
170 ret
= regmap_field_read(charger
->regmap_fields
[fld
], &rval
);
174 val
->intval
= rval
* props
[fld
].step
+ props
[fld
].min
;
179 static int mp2629_set_prop(struct mp2629_charger
*charger
,
180 enum mp2629_field fld
,
181 const union power_supply_propval
*val
)
185 if (val
->intval
< props
[fld
].min
|| val
->intval
> props
[fld
].max
)
188 rval
= (val
->intval
- props
[fld
].min
) / props
[fld
].step
;
189 return regmap_field_write(charger
->regmap_fields
[fld
], rval
);
192 static int mp2629_get_battery_capacity(struct mp2629_charger
*charger
,
193 union power_supply_propval
*val
)
195 union power_supply_propval vnow
, vlim
;
198 ret
= mp2629_read_adc(charger
, MP2629_BATT_VOLT
, &vnow
);
202 ret
= mp2629_get_prop(charger
, CHARGE_VLIM
, &vlim
);
206 val
->intval
= (vnow
.intval
* 100) / vlim
.intval
;
207 val
->intval
= min(val
->intval
, MP2629_MAX_BATT_CAPACITY
);
212 static int mp2629_charger_battery_get_prop(struct power_supply
*psy
,
213 enum power_supply_property psp
,
214 union power_supply_propval
*val
)
216 struct mp2629_charger
*charger
= dev_get_drvdata(psy
->dev
.parent
);
221 case POWER_SUPPLY_PROP_VOLTAGE_NOW
:
222 ret
= mp2629_read_adc(charger
, MP2629_BATT_VOLT
, val
);
225 case POWER_SUPPLY_PROP_CURRENT_NOW
:
226 ret
= mp2629_read_adc(charger
, MP2629_BATT_CURRENT
, val
);
229 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX
:
230 val
->intval
= 4520000;
233 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX
:
234 val
->intval
= 4670000;
237 case POWER_SUPPLY_PROP_CAPACITY
:
238 ret
= mp2629_get_battery_capacity(charger
, val
);
241 case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT
:
242 ret
= mp2629_get_prop(charger
, TERM_CURRENT
, val
);
245 case POWER_SUPPLY_PROP_PRECHARGE_CURRENT
:
246 ret
= mp2629_get_prop(charger
, PRECHARGE
, val
);
249 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE
:
250 ret
= mp2629_get_prop(charger
, CHARGE_VLIM
, val
);
253 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT
:
254 ret
= mp2629_get_prop(charger
, CHARGE_ILIM
, val
);
257 case POWER_SUPPLY_PROP_HEALTH
:
259 val
->intval
= POWER_SUPPLY_HEALTH_GOOD
;
260 if (MP2629_FAULT_BATTERY
& charger
->fault
)
261 val
->intval
= POWER_SUPPLY_HEALTH_OVERVOLTAGE
;
262 else if (MP2629_FAULT_THERMAL
& charger
->fault
)
263 val
->intval
= POWER_SUPPLY_HEALTH_OVERHEAT
;
264 else if (MP2629_FAULT_INPUT
& charger
->fault
)
265 val
->intval
= POWER_SUPPLY_HEALTH_OVERVOLTAGE
;
268 case POWER_SUPPLY_PROP_STATUS
:
269 ret
= regmap_read(charger
->regmap
, MP2629_REG_STATUS
, &rval
);
273 rval
= (rval
& MP2629_MASK_CHARGE_TYPE
) >> 3;
276 val
->intval
= POWER_SUPPLY_STATUS_DISCHARGING
;
280 val
->intval
= POWER_SUPPLY_STATUS_CHARGING
;
283 val
->intval
= POWER_SUPPLY_STATUS_FULL
;
287 case POWER_SUPPLY_PROP_CHARGE_TYPE
:
288 ret
= regmap_read(charger
->regmap
, MP2629_REG_STATUS
, &rval
);
292 rval
= (rval
& MP2629_MASK_CHARGE_TYPE
) >> 3;
295 val
->intval
= POWER_SUPPLY_CHARGE_TYPE_NONE
;
298 val
->intval
= POWER_SUPPLY_CHARGE_TYPE_TRICKLE
;
301 val
->intval
= POWER_SUPPLY_CHARGE_TYPE_STANDARD
;
304 val
->intval
= POWER_SUPPLY_CHARGE_TYPE_UNKNOWN
;
315 static int mp2629_charger_battery_set_prop(struct power_supply
*psy
,
316 enum power_supply_property psp
,
317 const union power_supply_propval
*val
)
319 struct mp2629_charger
*charger
= dev_get_drvdata(psy
->dev
.parent
);
322 case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT
:
323 return mp2629_set_prop(charger
, TERM_CURRENT
, val
);
325 case POWER_SUPPLY_PROP_PRECHARGE_CURRENT
:
326 return mp2629_set_prop(charger
, PRECHARGE
, val
);
328 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE
:
329 return mp2629_set_prop(charger
, CHARGE_VLIM
, val
);
331 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT
:
332 return mp2629_set_prop(charger
, CHARGE_ILIM
, val
);
339 static int mp2629_charger_usb_get_prop(struct power_supply
*psy
,
340 enum power_supply_property psp
,
341 union power_supply_propval
*val
)
343 struct mp2629_charger
*charger
= dev_get_drvdata(psy
->dev
.parent
);
348 case POWER_SUPPLY_PROP_ONLINE
:
349 ret
= regmap_read(charger
->regmap
, MP2629_REG_STATUS
, &rval
);
353 val
->intval
= !!(rval
& MP2629_MASK_INPUT_TYPE
);
356 case POWER_SUPPLY_PROP_USB_TYPE
:
357 ret
= regmap_read(charger
->regmap
, MP2629_REG_STATUS
, &rval
);
361 rval
= (rval
& MP2629_MASK_INPUT_TYPE
) >> 5;
363 case MP2629_SOURCE_TYPE_SDP
:
364 val
->intval
= POWER_SUPPLY_USB_TYPE_SDP
;
366 case MP2629_SOURCE_TYPE_CDP
:
367 val
->intval
= POWER_SUPPLY_USB_TYPE_CDP
;
369 case MP2629_SOURCE_TYPE_DCP
:
370 val
->intval
= POWER_SUPPLY_USB_TYPE_DCP
;
372 case MP2629_SOURCE_TYPE_OTG
:
373 val
->intval
= POWER_SUPPLY_USB_TYPE_PD_DRP
;
376 val
->intval
= POWER_SUPPLY_USB_TYPE_UNKNOWN
;
381 case POWER_SUPPLY_PROP_VOLTAGE_NOW
:
382 ret
= mp2629_read_adc(charger
, MP2629_INPUT_VOLT
, val
);
385 case POWER_SUPPLY_PROP_CURRENT_NOW
:
386 ret
= mp2629_read_adc(charger
, MP2629_INPUT_CURRENT
, val
);
389 case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT
:
390 ret
= mp2629_get_prop(charger
, INPUT_VLIM
, val
);
393 case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT
:
394 ret
= mp2629_get_prop(charger
, INPUT_ILIM
, val
);
404 static int mp2629_charger_usb_set_prop(struct power_supply
*psy
,
405 enum power_supply_property psp
,
406 const union power_supply_propval
*val
)
408 struct mp2629_charger
*charger
= dev_get_drvdata(psy
->dev
.parent
);
411 case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT
:
412 return mp2629_set_prop(charger
, INPUT_VLIM
, val
);
414 case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT
:
415 return mp2629_set_prop(charger
, INPUT_ILIM
, val
);
422 static int mp2629_charger_battery_prop_writeable(struct power_supply
*psy
,
423 enum power_supply_property psp
)
425 return (psp
== POWER_SUPPLY_PROP_PRECHARGE_CURRENT
) ||
426 (psp
== POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT
) ||
427 (psp
== POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT
) ||
428 (psp
== POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE
);
431 static int mp2629_charger_usb_prop_writeable(struct power_supply
*psy
,
432 enum power_supply_property psp
)
434 return (psp
== POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT
) ||
435 (psp
== POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT
);
438 static irqreturn_t
mp2629_irq_handler(int irq
, void *dev_id
)
440 struct mp2629_charger
*charger
= dev_id
;
444 mutex_lock(&charger
->lock
);
446 ret
= regmap_read(charger
->regmap
, MP2629_REG_FAULT
, &rval
);
451 charger
->fault
= rval
;
452 if (MP2629_FAULT_BATTERY
& rval
)
453 dev_err(charger
->dev
, "Battery fault OVP\n");
454 else if (MP2629_FAULT_THERMAL
& rval
)
455 dev_err(charger
->dev
, "Thermal shutdown fault\n");
456 else if (MP2629_FAULT_INPUT
& rval
)
457 dev_err(charger
->dev
, "no input or input OVP\n");
458 else if (MP2629_FAULT_OTG
& rval
)
459 dev_err(charger
->dev
, "VIN overloaded\n");
464 ret
= regmap_read(charger
->regmap
, MP2629_REG_STATUS
, &rval
);
468 if (rval
& MP2629_INPUTSOURCE_CHANGE
)
469 power_supply_changed(charger
->usb
);
470 else if (rval
& MP2629_CHARGING_CHANGE
)
471 power_supply_changed(charger
->battery
);
474 mutex_unlock(&charger
->lock
);
479 static const struct power_supply_desc mp2629_usb_desc
= {
480 .name
= "mp2629_usb",
481 .type
= POWER_SUPPLY_TYPE_USB
,
482 .usb_types
= BIT(POWER_SUPPLY_USB_TYPE_SDP
) |
483 BIT(POWER_SUPPLY_USB_TYPE_CDP
) |
484 BIT(POWER_SUPPLY_USB_TYPE_DCP
) |
485 BIT(POWER_SUPPLY_USB_TYPE_PD_DRP
) |
486 BIT(POWER_SUPPLY_USB_TYPE_UNKNOWN
),
487 .properties
= mp2629_charger_usb_props
,
488 .num_properties
= ARRAY_SIZE(mp2629_charger_usb_props
),
489 .get_property
= mp2629_charger_usb_get_prop
,
490 .set_property
= mp2629_charger_usb_set_prop
,
491 .property_is_writeable
= mp2629_charger_usb_prop_writeable
,
494 static const struct power_supply_desc mp2629_battery_desc
= {
495 .name
= "mp2629_battery",
496 .type
= POWER_SUPPLY_TYPE_BATTERY
,
497 .properties
= mp2629_charger_bat_props
,
498 .num_properties
= ARRAY_SIZE(mp2629_charger_bat_props
),
499 .get_property
= mp2629_charger_battery_get_prop
,
500 .set_property
= mp2629_charger_battery_set_prop
,
501 .property_is_writeable
= mp2629_charger_battery_prop_writeable
,
504 static ssize_t
batt_impedance_compensation_show(struct device
*dev
,
505 struct device_attribute
*attr
,
508 struct mp2629_charger
*charger
= dev_get_drvdata(dev
->parent
);
512 ret
= regmap_read(charger
->regmap
, MP2629_REG_IMPEDANCE_COMP
, &rval
);
516 rval
= (rval
>> 4) * 10;
517 return sysfs_emit(buf
, "%d mohm\n", rval
);
520 static ssize_t
batt_impedance_compensation_store(struct device
*dev
,
521 struct device_attribute
*attr
,
525 struct mp2629_charger
*charger
= dev_get_drvdata(dev
->parent
);
529 ret
= kstrtouint(buf
, 10, &val
);
536 /* multiples of 10 mohm so round off */
538 ret
= regmap_update_bits(charger
->regmap
, MP2629_REG_IMPEDANCE_COMP
,
539 MP2629_MASK_IMPEDANCE
, val
<< 4);
546 static DEVICE_ATTR_RW(batt_impedance_compensation
);
548 static struct attribute
*mp2629_charger_sysfs_attrs
[] = {
549 &dev_attr_batt_impedance_compensation
.attr
,
552 ATTRIBUTE_GROUPS(mp2629_charger_sysfs
);
554 static void mp2629_charger_disable(void *data
)
556 struct mp2629_charger
*charger
= data
;
558 regmap_update_bits(charger
->regmap
, MP2629_REG_CHARGE_CTRL
,
559 MP2629_MASK_CHARGE_CTRL
, 0);
562 static int mp2629_charger_probe(struct platform_device
*pdev
)
564 struct device
*dev
= &pdev
->dev
;
565 struct mp2629_data
*ddata
= dev_get_drvdata(dev
->parent
);
566 struct mp2629_charger
*charger
;
567 struct power_supply_config psy_cfg
= {};
570 charger
= devm_kzalloc(dev
, sizeof(*charger
), GFP_KERNEL
);
574 charger
->regmap
= ddata
->regmap
;
576 platform_set_drvdata(pdev
, charger
);
578 irq
= platform_get_irq(to_platform_device(dev
->parent
), 0);
582 for (i
= 0; i
< MP2629_MAX_FIELD
; i
++) {
583 charger
->regmap_fields
[i
] = devm_regmap_field_alloc(dev
,
584 charger
->regmap
, mp2629_reg_fields
[i
]);
585 if (IS_ERR(charger
->regmap_fields
[i
])) {
586 dev_err(dev
, "regmap field alloc fail %d\n", i
);
587 return PTR_ERR(charger
->regmap_fields
[i
]);
591 for (i
= 0; i
< MP2629_ADC_CHAN_END
; i
++) {
592 charger
->iiochan
[i
] = devm_iio_channel_get(dev
,
594 if (IS_ERR(charger
->iiochan
[i
])) {
595 dev_err(dev
, "iio chan get %s err\n", adc_chan_name
[i
]);
596 return PTR_ERR(charger
->iiochan
[i
]);
600 ret
= devm_add_action_or_reset(dev
, mp2629_charger_disable
, charger
);
604 charger
->usb
= devm_power_supply_register(dev
, &mp2629_usb_desc
, NULL
);
605 if (IS_ERR(charger
->usb
)) {
606 dev_err(dev
, "power supply register usb failed\n");
607 return PTR_ERR(charger
->usb
);
610 psy_cfg
.drv_data
= charger
;
611 psy_cfg
.attr_grp
= mp2629_charger_sysfs_groups
;
612 charger
->battery
= devm_power_supply_register(dev
,
613 &mp2629_battery_desc
, &psy_cfg
);
614 if (IS_ERR(charger
->battery
)) {
615 dev_err(dev
, "power supply register battery failed\n");
616 return PTR_ERR(charger
->battery
);
619 ret
= regmap_update_bits(charger
->regmap
, MP2629_REG_CHARGE_CTRL
,
620 MP2629_MASK_CHARGE_CTRL
, BIT(4));
622 dev_err(dev
, "enable charge fail: %d\n", ret
);
626 regmap_update_bits(charger
->regmap
, MP2629_REG_TIMER_CTRL
,
627 MP2629_MASK_WDOG_CTRL
, 0);
629 mutex_init(&charger
->lock
);
631 ret
= devm_request_threaded_irq(dev
, irq
, NULL
, mp2629_irq_handler
,
632 IRQF_ONESHOT
| IRQF_TRIGGER_RISING
,
633 "mp2629-charger", charger
);
635 dev_err(dev
, "failed to request gpio IRQ\n");
639 regmap_update_bits(charger
->regmap
, MP2629_REG_INTERRUPT
,
640 GENMASK(6, 5), BIT(6) | BIT(5));
645 static const struct of_device_id mp2629_charger_of_match
[] = {
646 { .compatible
= "mps,mp2629_charger"},
649 MODULE_DEVICE_TABLE(of
, mp2629_charger_of_match
);
651 static struct platform_driver mp2629_charger_driver
= {
653 .name
= "mp2629_charger",
654 .of_match_table
= mp2629_charger_of_match
,
656 .probe
= mp2629_charger_probe
,
658 module_platform_driver(mp2629_charger_driver
);
660 MODULE_AUTHOR("Saravanan Sekar <sravanhome@gmail.com>");
661 MODULE_DESCRIPTION("MP2629 Charger driver");
662 MODULE_LICENSE("GPL");