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_usb_type mp2629_usb_types[] = {
98 POWER_SUPPLY_USB_TYPE_SDP,
99 POWER_SUPPLY_USB_TYPE_DCP,
100 POWER_SUPPLY_USB_TYPE_CDP,
101 POWER_SUPPLY_USB_TYPE_PD_DRP,
102 POWER_SUPPLY_USB_TYPE_UNKNOWN
105 static enum power_supply_property mp2629_charger_usb_props[] = {
106 POWER_SUPPLY_PROP_ONLINE,
107 POWER_SUPPLY_PROP_USB_TYPE,
108 POWER_SUPPLY_PROP_VOLTAGE_NOW,
109 POWER_SUPPLY_PROP_CURRENT_NOW,
110 POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
111 POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT,
114 static enum power_supply_property mp2629_charger_bat_props[] = {
115 POWER_SUPPLY_PROP_STATUS,
116 POWER_SUPPLY_PROP_HEALTH,
117 POWER_SUPPLY_PROP_CHARGE_TYPE,
118 POWER_SUPPLY_PROP_VOLTAGE_NOW,
119 POWER_SUPPLY_PROP_CURRENT_NOW,
120 POWER_SUPPLY_PROP_CAPACITY,
121 POWER_SUPPLY_PROP_PRECHARGE_CURRENT,
122 POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
123 POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
124 POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
125 POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
126 POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
129 static struct mp2629_prop props[] = {
130 MP2629_PROPS(INPUT_ILIM, 100000, 3250000, 50000),
131 MP2629_PROPS(INPUT_VLIM, 3800000, 5300000, 100000),
132 MP2629_PROPS(CHARGE_ILIM, 320000, 4520000, 40000),
133 MP2629_PROPS(CHARGE_VLIM, 3400000, 4670000, 10000),
134 MP2629_PROPS(PRECHARGE, 120000, 720000, 40000),
135 MP2629_PROPS(TERM_CURRENT, 80000, 680000, 40000),
138 static const struct reg_field mp2629_reg_fields[] = {
139 [INPUT_ILIM] = REG_FIELD(MP2629_REG_INPUT_ILIM, 0, 5),
140 [INPUT_VLIM] = REG_FIELD(MP2629_REG_INPUT_VLIM, 0, 3),
141 [CHARGE_ILIM] = REG_FIELD(MP2629_REG_CHARGE_ILIM, 0, 6),
142 [CHARGE_VLIM] = REG_FIELD(MP2629_REG_CHARGE_VLIM, 1, 7),
143 [PRECHARGE] = REG_FIELD(MP2629_REG_PRECHARGE, 4, 7),
144 [TERM_CURRENT] = REG_FIELD(MP2629_REG_TERM_CURRENT, 0, 3),
147 static char *adc_chan_name[] = {
149 "mp2629-system-volt",
151 "mp2629-batt-current",
152 "mp2629-input-current",
155 static int mp2629_read_adc(struct mp2629_charger *charger,
156 enum mp2629_adc_chan ch,
157 union power_supply_propval *val)
162 ret = iio_read_channel_processed(charger->iiochan[ch], &chval);
166 val->intval = chval * 1000;
171 static int mp2629_get_prop(struct mp2629_charger *charger,
172 enum mp2629_field fld,
173 union power_supply_propval *val)
178 ret = regmap_field_read(charger->regmap_fields[fld], &rval);
182 val->intval = rval * props[fld].step + props[fld].min;
187 static int mp2629_set_prop(struct mp2629_charger *charger,
188 enum mp2629_field fld,
189 const union power_supply_propval *val)
193 if (val->intval < props[fld].min || val->intval > props[fld].max)
196 rval = (val->intval - props[fld].min) / props[fld].step;
197 return regmap_field_write(charger->regmap_fields[fld], rval);
200 static int mp2629_get_battery_capacity(struct mp2629_charger *charger,
201 union power_supply_propval *val)
203 union power_supply_propval vnow, vlim;
206 ret = mp2629_read_adc(charger, MP2629_BATT_VOLT, &vnow);
210 ret = mp2629_get_prop(charger, CHARGE_VLIM, &vlim);
214 val->intval = (vnow.intval * 100) / vlim.intval;
215 val->intval = min(val->intval, MP2629_MAX_BATT_CAPACITY);
220 static int mp2629_charger_battery_get_prop(struct power_supply *psy,
221 enum power_supply_property psp,
222 union power_supply_propval *val)
224 struct mp2629_charger *charger = dev_get_drvdata(psy->dev.parent);
229 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
230 ret = mp2629_read_adc(charger, MP2629_BATT_VOLT, val);
233 case POWER_SUPPLY_PROP_CURRENT_NOW:
234 ret = mp2629_read_adc(charger, MP2629_BATT_CURRENT, val);
237 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
238 val->intval = 4520000;
241 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
242 val->intval = 4670000;
245 case POWER_SUPPLY_PROP_CAPACITY:
246 ret = mp2629_get_battery_capacity(charger, val);
249 case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
250 ret = mp2629_get_prop(charger, TERM_CURRENT, val);
253 case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
254 ret = mp2629_get_prop(charger, PRECHARGE, val);
257 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
258 ret = mp2629_get_prop(charger, CHARGE_VLIM, val);
261 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
262 ret = mp2629_get_prop(charger, CHARGE_ILIM, val);
265 case POWER_SUPPLY_PROP_HEALTH:
267 val->intval = POWER_SUPPLY_HEALTH_GOOD;
268 if (MP2629_FAULT_BATTERY & charger->fault)
269 val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
270 else if (MP2629_FAULT_THERMAL & charger->fault)
271 val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
272 else if (MP2629_FAULT_INPUT & charger->fault)
273 val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
276 case POWER_SUPPLY_PROP_STATUS:
277 ret = regmap_read(charger->regmap, MP2629_REG_STATUS, &rval);
281 rval = (rval & MP2629_MASK_CHARGE_TYPE) >> 3;
284 val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
288 val->intval = POWER_SUPPLY_STATUS_CHARGING;
291 val->intval = POWER_SUPPLY_STATUS_FULL;
295 case POWER_SUPPLY_PROP_CHARGE_TYPE:
296 ret = regmap_read(charger->regmap, MP2629_REG_STATUS, &rval);
300 rval = (rval & MP2629_MASK_CHARGE_TYPE) >> 3;
303 val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
306 val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
309 val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
312 val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
323 static int mp2629_charger_battery_set_prop(struct power_supply *psy,
324 enum power_supply_property psp,
325 const union power_supply_propval *val)
327 struct mp2629_charger *charger = dev_get_drvdata(psy->dev.parent);
330 case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
331 return mp2629_set_prop(charger, TERM_CURRENT, val);
333 case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
334 return mp2629_set_prop(charger, PRECHARGE, val);
336 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
337 return mp2629_set_prop(charger, CHARGE_VLIM, val);
339 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
340 return mp2629_set_prop(charger, CHARGE_ILIM, val);
347 static int mp2629_charger_usb_get_prop(struct power_supply *psy,
348 enum power_supply_property psp,
349 union power_supply_propval *val)
351 struct mp2629_charger *charger = dev_get_drvdata(psy->dev.parent);
356 case POWER_SUPPLY_PROP_ONLINE:
357 ret = regmap_read(charger->regmap, MP2629_REG_STATUS, &rval);
361 val->intval = !!(rval & MP2629_MASK_INPUT_TYPE);
364 case POWER_SUPPLY_PROP_USB_TYPE:
365 ret = regmap_read(charger->regmap, MP2629_REG_STATUS, &rval);
369 rval = (rval & MP2629_MASK_INPUT_TYPE) >> 5;
371 case MP2629_SOURCE_TYPE_SDP:
372 val->intval = POWER_SUPPLY_USB_TYPE_SDP;
374 case MP2629_SOURCE_TYPE_CDP:
375 val->intval = POWER_SUPPLY_USB_TYPE_CDP;
377 case MP2629_SOURCE_TYPE_DCP:
378 val->intval = POWER_SUPPLY_USB_TYPE_DCP;
380 case MP2629_SOURCE_TYPE_OTG:
381 val->intval = POWER_SUPPLY_USB_TYPE_PD_DRP;
384 val->intval = POWER_SUPPLY_USB_TYPE_UNKNOWN;
389 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
390 ret = mp2629_read_adc(charger, MP2629_INPUT_VOLT, val);
393 case POWER_SUPPLY_PROP_CURRENT_NOW:
394 ret = mp2629_read_adc(charger, MP2629_INPUT_CURRENT, val);
397 case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
398 ret = mp2629_get_prop(charger, INPUT_VLIM, val);
401 case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
402 ret = mp2629_get_prop(charger, INPUT_ILIM, val);
412 static int mp2629_charger_usb_set_prop(struct power_supply *psy,
413 enum power_supply_property psp,
414 const union power_supply_propval *val)
416 struct mp2629_charger *charger = dev_get_drvdata(psy->dev.parent);
419 case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
420 return mp2629_set_prop(charger, INPUT_VLIM, val);
422 case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
423 return mp2629_set_prop(charger, INPUT_ILIM, val);
430 static int mp2629_charger_battery_prop_writeable(struct power_supply *psy,
431 enum power_supply_property psp)
433 return (psp == POWER_SUPPLY_PROP_PRECHARGE_CURRENT) ||
434 (psp == POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT) ||
435 (psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT) ||
436 (psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE);
439 static int mp2629_charger_usb_prop_writeable(struct power_supply *psy,
440 enum power_supply_property psp)
442 return (psp == POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT) ||
443 (psp == POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT);
446 static irqreturn_t mp2629_irq_handler(int irq, void *dev_id)
448 struct mp2629_charger *charger = dev_id;
452 mutex_lock(&charger->lock);
454 ret = regmap_read(charger->regmap, MP2629_REG_FAULT, &rval);
459 charger->fault = rval;
460 if (MP2629_FAULT_BATTERY & rval)
461 dev_err(charger->dev, "Battery fault OVP\n");
462 else if (MP2629_FAULT_THERMAL & rval)
463 dev_err(charger->dev, "Thermal shutdown fault\n");
464 else if (MP2629_FAULT_INPUT & rval)
465 dev_err(charger->dev, "no input or input OVP\n");
466 else if (MP2629_FAULT_OTG & rval)
467 dev_err(charger->dev, "VIN overloaded\n");
472 ret = regmap_read(charger->regmap, MP2629_REG_STATUS, &rval);
476 if (rval & MP2629_INPUTSOURCE_CHANGE)
477 power_supply_changed(charger->usb);
478 else if (rval & MP2629_CHARGING_CHANGE)
479 power_supply_changed(charger->battery);
482 mutex_unlock(&charger->lock);
487 static const struct power_supply_desc mp2629_usb_desc = {
488 .name = "mp2629_usb",
489 .type = POWER_SUPPLY_TYPE_USB,
490 .usb_types = mp2629_usb_types,
491 .num_usb_types = ARRAY_SIZE(mp2629_usb_types),
492 .properties = mp2629_charger_usb_props,
493 .num_properties = ARRAY_SIZE(mp2629_charger_usb_props),
494 .get_property = mp2629_charger_usb_get_prop,
495 .set_property = mp2629_charger_usb_set_prop,
496 .property_is_writeable = mp2629_charger_usb_prop_writeable,
499 static const struct power_supply_desc mp2629_battery_desc = {
500 .name = "mp2629_battery",
501 .type = POWER_SUPPLY_TYPE_BATTERY,
502 .properties = mp2629_charger_bat_props,
503 .num_properties = ARRAY_SIZE(mp2629_charger_bat_props),
504 .get_property = mp2629_charger_battery_get_prop,
505 .set_property = mp2629_charger_battery_set_prop,
506 .property_is_writeable = mp2629_charger_battery_prop_writeable,
509 static ssize_t batt_impedance_compensation_show(struct device *dev,
510 struct device_attribute *attr,
513 struct mp2629_charger *charger = dev_get_drvdata(dev->parent);
517 ret = regmap_read(charger->regmap, MP2629_REG_IMPEDANCE_COMP, &rval);
521 rval = (rval >> 4) * 10;
522 return sysfs_emit(buf, "%d mohm\n", rval);
525 static ssize_t batt_impedance_compensation_store(struct device *dev,
526 struct device_attribute *attr,
530 struct mp2629_charger *charger = dev_get_drvdata(dev->parent);
534 ret = kstrtouint(buf, 10, &val);
541 /* multiples of 10 mohm so round off */
543 ret = regmap_update_bits(charger->regmap, MP2629_REG_IMPEDANCE_COMP,
544 MP2629_MASK_IMPEDANCE, val << 4);
551 static DEVICE_ATTR_RW(batt_impedance_compensation);
553 static struct attribute *mp2629_charger_sysfs_attrs[] = {
554 &dev_attr_batt_impedance_compensation.attr,
557 ATTRIBUTE_GROUPS(mp2629_charger_sysfs);
559 static void mp2629_charger_disable(void *data)
561 struct mp2629_charger *charger = data;
563 regmap_update_bits(charger->regmap, MP2629_REG_CHARGE_CTRL,
564 MP2629_MASK_CHARGE_CTRL, 0);
567 static int mp2629_charger_probe(struct platform_device *pdev)
569 struct device *dev = &pdev->dev;
570 struct mp2629_data *ddata = dev_get_drvdata(dev->parent);
571 struct mp2629_charger *charger;
572 struct power_supply_config psy_cfg = {};
575 charger = devm_kzalloc(dev, sizeof(*charger), GFP_KERNEL);
579 charger->regmap = ddata->regmap;
581 platform_set_drvdata(pdev, charger);
583 irq = platform_get_irq(to_platform_device(dev->parent), 0);
587 for (i = 0; i < MP2629_MAX_FIELD; i++) {
588 charger->regmap_fields[i] = devm_regmap_field_alloc(dev,
589 charger->regmap, mp2629_reg_fields[i]);
590 if (IS_ERR(charger->regmap_fields[i])) {
591 dev_err(dev, "regmap field alloc fail %d\n", i);
592 return PTR_ERR(charger->regmap_fields[i]);
596 for (i = 0; i < MP2629_ADC_CHAN_END; i++) {
597 charger->iiochan[i] = devm_iio_channel_get(dev,
599 if (IS_ERR(charger->iiochan[i])) {
600 dev_err(dev, "iio chan get %s err\n", adc_chan_name[i]);
601 return PTR_ERR(charger->iiochan[i]);
605 ret = devm_add_action_or_reset(dev, mp2629_charger_disable, charger);
609 charger->usb = devm_power_supply_register(dev, &mp2629_usb_desc, NULL);
610 if (IS_ERR(charger->usb)) {
611 dev_err(dev, "power supply register usb failed\n");
612 return PTR_ERR(charger->usb);
615 psy_cfg.drv_data = charger;
616 psy_cfg.attr_grp = mp2629_charger_sysfs_groups;
617 charger->battery = devm_power_supply_register(dev,
618 &mp2629_battery_desc, &psy_cfg);
619 if (IS_ERR(charger->battery)) {
620 dev_err(dev, "power supply register battery failed\n");
621 return PTR_ERR(charger->battery);
624 ret = regmap_update_bits(charger->regmap, MP2629_REG_CHARGE_CTRL,
625 MP2629_MASK_CHARGE_CTRL, BIT(4));
627 dev_err(dev, "enable charge fail: %d\n", ret);
631 regmap_update_bits(charger->regmap, MP2629_REG_TIMER_CTRL,
632 MP2629_MASK_WDOG_CTRL, 0);
634 mutex_init(&charger->lock);
636 ret = devm_request_threaded_irq(dev, irq, NULL, mp2629_irq_handler,
637 IRQF_ONESHOT | IRQF_TRIGGER_RISING,
638 "mp2629-charger", charger);
640 dev_err(dev, "failed to request gpio IRQ\n");
644 regmap_update_bits(charger->regmap, MP2629_REG_INTERRUPT,
645 GENMASK(6, 5), BIT(6) | BIT(5));
650 static const struct of_device_id mp2629_charger_of_match[] = {
651 { .compatible = "mps,mp2629_charger"},
654 MODULE_DEVICE_TABLE(of, mp2629_charger_of_match);
656 static struct platform_driver mp2629_charger_driver = {
658 .name = "mp2629_charger",
659 .of_match_table = mp2629_charger_of_match,
661 .probe = mp2629_charger_probe,
663 module_platform_driver(mp2629_charger_driver);
665 MODULE_AUTHOR("Saravanan Sekar <sravanhome@gmail.com>");
666 MODULE_DESCRIPTION("MP2629 Charger driver");
667 MODULE_LICENSE("GPL");