1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * MP2629 battery charger driver
4 *
5 * Copyright 2020 Monolithic Power Systems, Inc
6 *
7 * Author: Saravanan Sekar <sravanhome@gmail.com>
8 */
9
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>
20
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
33
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)
39
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)
46
47#define MP2629_MAX_BATT_CAPACITY 100
48
49#define MP2629_PROPS(_idx, _min, _max, _step) \
50 [_idx] = { \
51 .min = _min, \
52 .max = _max, \
53 .step = _step, \
54}
55
56enum 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,
63};
64
65enum mp2629_field {
66 INPUT_ILIM,
67 INPUT_VLIM,
68 CHARGE_ILIM,
69 CHARGE_VLIM,
70 PRECHARGE,
71 TERM_CURRENT,
72 MP2629_MAX_FIELD
73};
74
75struct mp2629_charger {
76 struct device *dev;
77 int status;
78 int fault;
79
80 struct regmap *regmap;
81 struct regmap_field *regmap_fields[MP2629_MAX_FIELD];
82 struct mutex lock;
83 struct power_supply *usb;
84 struct power_supply *battery;
85 struct iio_channel *iiochan[MP2629_ADC_CHAN_END];
86};
87
88struct mp2629_prop {
89 int reg;
90 int mask;
91 int min;
92 int max;
93 int step;
94 int shift;
95};
96
97static 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
103};
104
105static 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,
112};
113
114static 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,
127};
128
129static 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),
136};
137
138static 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),
145};
146
147static char *adc_chan_name[] = {
148 "mp2629-batt-volt",
149 "mp2629-system-volt",
150 "mp2629-input-volt",
151 "mp2629-batt-current",
152 "mp2629-input-current",
153};
154
155static int mp2629_read_adc(struct mp2629_charger *charger,
156 enum mp2629_adc_chan ch,
157 union power_supply_propval *val)
158{
159 int ret;
160 int chval;
161
162 ret = iio_read_channel_processed(chan: charger->iiochan[ch], val: &chval);
163 if (ret)
164 return ret;
165
166 val->intval = chval * 1000;
167
168 return 0;
169}
170
171static int mp2629_get_prop(struct mp2629_charger *charger,
172 enum mp2629_field fld,
173 union power_supply_propval *val)
174{
175 int ret;
176 unsigned int rval;
177
178 ret = regmap_field_read(field: charger->regmap_fields[fld], val: &rval);
179 if (ret)
180 return ret;
181
182 val->intval = rval * props[fld].step + props[fld].min;
183
184 return 0;
185}
186
187static int mp2629_set_prop(struct mp2629_charger *charger,
188 enum mp2629_field fld,
189 const union power_supply_propval *val)
190{
191 unsigned int rval;
192
193 if (val->intval < props[fld].min || val->intval > props[fld].max)
194 return -EINVAL;
195
196 rval = (val->intval - props[fld].min) / props[fld].step;
197 return regmap_field_write(field: charger->regmap_fields[fld], val: rval);
198}
199
200static int mp2629_get_battery_capacity(struct mp2629_charger *charger,
201 union power_supply_propval *val)
202{
203 union power_supply_propval vnow, vlim;
204 int ret;
205
206 ret = mp2629_read_adc(charger, ch: MP2629_BATT_VOLT, val: &vnow);
207 if (ret)
208 return ret;
209
210 ret = mp2629_get_prop(charger, fld: CHARGE_VLIM, val: &vlim);
211 if (ret)
212 return ret;
213
214 val->intval = (vnow.intval * 100) / vlim.intval;
215 val->intval = min(val->intval, MP2629_MAX_BATT_CAPACITY);
216
217 return 0;
218}
219
220static int mp2629_charger_battery_get_prop(struct power_supply *psy,
221 enum power_supply_property psp,
222 union power_supply_propval *val)
223{
224 struct mp2629_charger *charger = dev_get_drvdata(dev: psy->dev.parent);
225 unsigned int rval;
226 int ret = 0;
227
228 switch (psp) {
229 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
230 ret = mp2629_read_adc(charger, ch: MP2629_BATT_VOLT, val);
231 break;
232
233 case POWER_SUPPLY_PROP_CURRENT_NOW:
234 ret = mp2629_read_adc(charger, ch: MP2629_BATT_CURRENT, val);
235 break;
236
237 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
238 val->intval = 4520000;
239 break;
240
241 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
242 val->intval = 4670000;
243 break;
244
245 case POWER_SUPPLY_PROP_CAPACITY:
246 ret = mp2629_get_battery_capacity(charger, val);
247 break;
248
249 case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
250 ret = mp2629_get_prop(charger, fld: TERM_CURRENT, val);
251 break;
252
253 case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
254 ret = mp2629_get_prop(charger, fld: PRECHARGE, val);
255 break;
256
257 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
258 ret = mp2629_get_prop(charger, fld: CHARGE_VLIM, val);
259 break;
260
261 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
262 ret = mp2629_get_prop(charger, fld: CHARGE_ILIM, val);
263 break;
264
265 case POWER_SUPPLY_PROP_HEALTH:
266 if (!charger->fault)
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;
274 break;
275
276 case POWER_SUPPLY_PROP_STATUS:
277 ret = regmap_read(map: charger->regmap, MP2629_REG_STATUS, val: &rval);
278 if (ret)
279 break;
280
281 rval = (rval & MP2629_MASK_CHARGE_TYPE) >> 3;
282 switch (rval) {
283 case 0x00:
284 val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
285 break;
286 case 0x01:
287 case 0x10:
288 val->intval = POWER_SUPPLY_STATUS_CHARGING;
289 break;
290 case 0x11:
291 val->intval = POWER_SUPPLY_STATUS_FULL;
292 }
293 break;
294
295 case POWER_SUPPLY_PROP_CHARGE_TYPE:
296 ret = regmap_read(map: charger->regmap, MP2629_REG_STATUS, val: &rval);
297 if (ret)
298 break;
299
300 rval = (rval & MP2629_MASK_CHARGE_TYPE) >> 3;
301 switch (rval) {
302 case 0x00:
303 val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
304 break;
305 case 0x01:
306 val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
307 break;
308 case 0x10:
309 val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
310 break;
311 default:
312 val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
313 }
314 break;
315
316 default:
317 return -EINVAL;
318 }
319
320 return ret;
321}
322
323static int mp2629_charger_battery_set_prop(struct power_supply *psy,
324 enum power_supply_property psp,
325 const union power_supply_propval *val)
326{
327 struct mp2629_charger *charger = dev_get_drvdata(dev: psy->dev.parent);
328
329 switch (psp) {
330 case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
331 return mp2629_set_prop(charger, fld: TERM_CURRENT, val);
332
333 case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
334 return mp2629_set_prop(charger, fld: PRECHARGE, val);
335
336 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
337 return mp2629_set_prop(charger, fld: CHARGE_VLIM, val);
338
339 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
340 return mp2629_set_prop(charger, fld: CHARGE_ILIM, val);
341
342 default:
343 return -EINVAL;
344 }
345}
346
347static int mp2629_charger_usb_get_prop(struct power_supply *psy,
348 enum power_supply_property psp,
349 union power_supply_propval *val)
350{
351 struct mp2629_charger *charger = dev_get_drvdata(dev: psy->dev.parent);
352 unsigned int rval;
353 int ret;
354
355 switch (psp) {
356 case POWER_SUPPLY_PROP_ONLINE:
357 ret = regmap_read(map: charger->regmap, MP2629_REG_STATUS, val: &rval);
358 if (ret)
359 break;
360
361 val->intval = !!(rval & MP2629_MASK_INPUT_TYPE);
362 break;
363
364 case POWER_SUPPLY_PROP_USB_TYPE:
365 ret = regmap_read(map: charger->regmap, MP2629_REG_STATUS, val: &rval);
366 if (ret)
367 break;
368
369 rval = (rval & MP2629_MASK_INPUT_TYPE) >> 5;
370 switch (rval) {
371 case MP2629_SOURCE_TYPE_SDP:
372 val->intval = POWER_SUPPLY_USB_TYPE_SDP;
373 break;
374 case MP2629_SOURCE_TYPE_CDP:
375 val->intval = POWER_SUPPLY_USB_TYPE_CDP;
376 break;
377 case MP2629_SOURCE_TYPE_DCP:
378 val->intval = POWER_SUPPLY_USB_TYPE_DCP;
379 break;
380 case MP2629_SOURCE_TYPE_OTG:
381 val->intval = POWER_SUPPLY_USB_TYPE_PD_DRP;
382 break;
383 default:
384 val->intval = POWER_SUPPLY_USB_TYPE_UNKNOWN;
385 break;
386 }
387 break;
388
389 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
390 ret = mp2629_read_adc(charger, ch: MP2629_INPUT_VOLT, val);
391 break;
392
393 case POWER_SUPPLY_PROP_CURRENT_NOW:
394 ret = mp2629_read_adc(charger, ch: MP2629_INPUT_CURRENT, val);
395 break;
396
397 case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
398 ret = mp2629_get_prop(charger, fld: INPUT_VLIM, val);
399 break;
400
401 case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
402 ret = mp2629_get_prop(charger, fld: INPUT_ILIM, val);
403 break;
404
405 default:
406 return -EINVAL;
407 }
408
409 return ret;
410}
411
412static int mp2629_charger_usb_set_prop(struct power_supply *psy,
413 enum power_supply_property psp,
414 const union power_supply_propval *val)
415{
416 struct mp2629_charger *charger = dev_get_drvdata(dev: psy->dev.parent);
417
418 switch (psp) {
419 case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
420 return mp2629_set_prop(charger, fld: INPUT_VLIM, val);
421
422 case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
423 return mp2629_set_prop(charger, fld: INPUT_ILIM, val);
424
425 default:
426 return -EINVAL;
427 }
428}
429
430static int mp2629_charger_battery_prop_writeable(struct power_supply *psy,
431 enum power_supply_property psp)
432{
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);
437}
438
439static int mp2629_charger_usb_prop_writeable(struct power_supply *psy,
440 enum power_supply_property psp)
441{
442 return (psp == POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT) ||
443 (psp == POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT);
444}
445
446static irqreturn_t mp2629_irq_handler(int irq, void *dev_id)
447{
448 struct mp2629_charger *charger = dev_id;
449 unsigned int rval;
450 int ret;
451
452 mutex_lock(&charger->lock);
453
454 ret = regmap_read(map: charger->regmap, MP2629_REG_FAULT, val: &rval);
455 if (ret)
456 goto unlock;
457
458 if (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");
468
469 goto unlock;
470 }
471
472 ret = regmap_read(map: charger->regmap, MP2629_REG_STATUS, val: &rval);
473 if (ret)
474 goto unlock;
475
476 if (rval & MP2629_INPUTSOURCE_CHANGE)
477 power_supply_changed(psy: charger->usb);
478 else if (rval & MP2629_CHARGING_CHANGE)
479 power_supply_changed(psy: charger->battery);
480
481unlock:
482 mutex_unlock(lock: &charger->lock);
483
484 return IRQ_HANDLED;
485}
486
487static 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,
497};
498
499static 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,
507};
508
509static ssize_t batt_impedance_compensation_show(struct device *dev,
510 struct device_attribute *attr,
511 char *buf)
512{
513 struct mp2629_charger *charger = dev_get_drvdata(dev: dev->parent);
514 unsigned int rval;
515 int ret;
516
517 ret = regmap_read(map: charger->regmap, MP2629_REG_IMPEDANCE_COMP, val: &rval);
518 if (ret)
519 return ret;
520
521 rval = (rval >> 4) * 10;
522 return sysfs_emit(buf, fmt: "%d mohm\n", rval);
523}
524
525static ssize_t batt_impedance_compensation_store(struct device *dev,
526 struct device_attribute *attr,
527 const char *buf,
528 size_t count)
529{
530 struct mp2629_charger *charger = dev_get_drvdata(dev: dev->parent);
531 unsigned int val;
532 int ret;
533
534 ret = kstrtouint(s: buf, base: 10, res: &val);
535 if (ret)
536 return ret;
537
538 if (val > 140)
539 return -ERANGE;
540
541 /* multiples of 10 mohm so round off */
542 val = val / 10;
543 ret = regmap_update_bits(map: charger->regmap, MP2629_REG_IMPEDANCE_COMP,
544 MP2629_MASK_IMPEDANCE, val: val << 4);
545 if (ret)
546 return ret;
547
548 return count;
549}
550
551static DEVICE_ATTR_RW(batt_impedance_compensation);
552
553static struct attribute *mp2629_charger_sysfs_attrs[] = {
554 &dev_attr_batt_impedance_compensation.attr,
555 NULL
556};
557ATTRIBUTE_GROUPS(mp2629_charger_sysfs);
558
559static void mp2629_charger_disable(void *data)
560{
561 struct mp2629_charger *charger = data;
562
563 regmap_update_bits(map: charger->regmap, MP2629_REG_CHARGE_CTRL,
564 MP2629_MASK_CHARGE_CTRL, val: 0);
565}
566
567static int mp2629_charger_probe(struct platform_device *pdev)
568{
569 struct device *dev = &pdev->dev;
570 struct mp2629_data *ddata = dev_get_drvdata(dev: dev->parent);
571 struct mp2629_charger *charger;
572 struct power_supply_config psy_cfg = {};
573 int ret, i, irq;
574
575 charger = devm_kzalloc(dev, size: sizeof(*charger), GFP_KERNEL);
576 if (!charger)
577 return -ENOMEM;
578
579 charger->regmap = ddata->regmap;
580 charger->dev = dev;
581 platform_set_drvdata(pdev, data: charger);
582
583 irq = platform_get_irq(to_platform_device(dev->parent), 0);
584 if (irq < 0)
585 return irq;
586
587 for (i = 0; i < MP2629_MAX_FIELD; i++) {
588 charger->regmap_fields[i] = devm_regmap_field_alloc(dev,
589 regmap: charger->regmap, reg_field: mp2629_reg_fields[i]);
590 if (IS_ERR(ptr: charger->regmap_fields[i])) {
591 dev_err(dev, "regmap field alloc fail %d\n", i);
592 return PTR_ERR(ptr: charger->regmap_fields[i]);
593 }
594 }
595
596 for (i = 0; i < MP2629_ADC_CHAN_END; i++) {
597 charger->iiochan[i] = devm_iio_channel_get(dev,
598 consumer_channel: adc_chan_name[i]);
599 if (IS_ERR(ptr: charger->iiochan[i])) {
600 dev_err(dev, "iio chan get %s err\n", adc_chan_name[i]);
601 return PTR_ERR(ptr: charger->iiochan[i]);
602 }
603 }
604
605 ret = devm_add_action_or_reset(dev, mp2629_charger_disable, charger);
606 if (ret)
607 return ret;
608
609 charger->usb = devm_power_supply_register(parent: dev, desc: &mp2629_usb_desc, NULL);
610 if (IS_ERR(ptr: charger->usb)) {
611 dev_err(dev, "power supply register usb failed\n");
612 return PTR_ERR(ptr: charger->usb);
613 }
614
615 psy_cfg.drv_data = charger;
616 psy_cfg.attr_grp = mp2629_charger_sysfs_groups;
617 charger->battery = devm_power_supply_register(parent: dev,
618 desc: &mp2629_battery_desc, cfg: &psy_cfg);
619 if (IS_ERR(ptr: charger->battery)) {
620 dev_err(dev, "power supply register battery failed\n");
621 return PTR_ERR(ptr: charger->battery);
622 }
623
624 ret = regmap_update_bits(map: charger->regmap, MP2629_REG_CHARGE_CTRL,
625 MP2629_MASK_CHARGE_CTRL, BIT(4));
626 if (ret) {
627 dev_err(dev, "enable charge fail: %d\n", ret);
628 return ret;
629 }
630
631 regmap_update_bits(map: charger->regmap, MP2629_REG_TIMER_CTRL,
632 MP2629_MASK_WDOG_CTRL, val: 0);
633
634 mutex_init(&charger->lock);
635
636 ret = devm_request_threaded_irq(dev, irq, NULL, thread_fn: mp2629_irq_handler,
637 IRQF_ONESHOT | IRQF_TRIGGER_RISING,
638 devname: "mp2629-charger", dev_id: charger);
639 if (ret) {
640 dev_err(dev, "failed to request gpio IRQ\n");
641 return ret;
642 }
643
644 regmap_update_bits(map: charger->regmap, MP2629_REG_INTERRUPT,
645 GENMASK(6, 5), BIT(6) | BIT(5));
646
647 return 0;
648}
649
650static const struct of_device_id mp2629_charger_of_match[] = {
651 { .compatible = "mps,mp2629_charger"},
652 {}
653};
654MODULE_DEVICE_TABLE(of, mp2629_charger_of_match);
655
656static struct platform_driver mp2629_charger_driver = {
657 .driver = {
658 .name = "mp2629_charger",
659 .of_match_table = mp2629_charger_of_match,
660 },
661 .probe = mp2629_charger_probe,
662};
663module_platform_driver(mp2629_charger_driver);
664
665MODULE_AUTHOR("Saravanan Sekar <sravanhome@gmail.com>");
666MODULE_DESCRIPTION("MP2629 Charger driver");
667MODULE_LICENSE("GPL");
668

source code of linux/drivers/power/supply/mp2629_charger.c