• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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,
63 };
64 
65 enum 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 
75 struct 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 
88 struct mp2629_prop {
89 	int reg;
90 	int mask;
91 	int min;
92 	int max;
93 	int step;
94 	int shift;
95 };
96 
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
103 };
104 
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,
112 };
113 
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,
127 };
128 
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),
136 };
137 
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),
145 };
146 
147 static 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 
mp2629_read_adc(struct mp2629_charger * charger,enum mp2629_adc_chan ch,union power_supply_propval * val)155 static 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(charger->iiochan[ch], &chval);
163 	if (ret)
164 		return ret;
165 
166 	val->intval = chval * 1000;
167 
168 	return 0;
169 }
170 
mp2629_get_prop(struct mp2629_charger * charger,enum mp2629_field fld,union power_supply_propval * val)171 static 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(charger->regmap_fields[fld], &rval);
179 	if (ret)
180 		return ret;
181 
182 	val->intval = rval * props[fld].step + props[fld].min;
183 
184 	return 0;
185 }
186 
mp2629_set_prop(struct mp2629_charger * charger,enum mp2629_field fld,const union power_supply_propval * val)187 static 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(charger->regmap_fields[fld], rval);
198 }
199 
mp2629_get_battery_capacity(struct mp2629_charger * charger,union power_supply_propval * val)200 static 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, MP2629_BATT_VOLT, &vnow);
207 	if (ret)
208 		return ret;
209 
210 	ret = mp2629_get_prop(charger, CHARGE_VLIM, &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 
mp2629_charger_battery_get_prop(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)220 static 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(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, MP2629_BATT_VOLT, val);
231 		break;
232 
233 	case POWER_SUPPLY_PROP_CURRENT_NOW:
234 		ret = mp2629_read_adc(charger, 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, TERM_CURRENT, val);
251 		break;
252 
253 	case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
254 		ret = mp2629_get_prop(charger, PRECHARGE, val);
255 		break;
256 
257 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
258 		ret = mp2629_get_prop(charger, CHARGE_VLIM, val);
259 		break;
260 
261 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
262 		ret = mp2629_get_prop(charger, 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(charger->regmap, MP2629_REG_STATUS, &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(charger->regmap, MP2629_REG_STATUS, &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 
mp2629_charger_battery_set_prop(struct power_supply * psy,enum power_supply_property psp,const union power_supply_propval * val)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)
326 {
327 	struct mp2629_charger *charger = dev_get_drvdata(psy->dev.parent);
328 
329 	switch (psp) {
330 	case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
331 		return mp2629_set_prop(charger, TERM_CURRENT, val);
332 
333 	case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
334 		return mp2629_set_prop(charger, PRECHARGE, val);
335 
336 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
337 		return mp2629_set_prop(charger, CHARGE_VLIM, val);
338 
339 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
340 		return mp2629_set_prop(charger, CHARGE_ILIM, val);
341 
342 	default:
343 		return -EINVAL;
344 	}
345 }
346 
mp2629_charger_usb_get_prop(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * 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)
350 {
351 	struct mp2629_charger *charger = dev_get_drvdata(psy->dev.parent);
352 	unsigned int rval;
353 	int ret;
354 
355 	switch (psp) {
356 	case POWER_SUPPLY_PROP_ONLINE:
357 		ret = regmap_read(charger->regmap, MP2629_REG_STATUS, &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(charger->regmap, MP2629_REG_STATUS, &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, MP2629_INPUT_VOLT, val);
391 		break;
392 
393 	case POWER_SUPPLY_PROP_CURRENT_NOW:
394 		ret = mp2629_read_adc(charger, MP2629_INPUT_CURRENT, val);
395 		break;
396 
397 	case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
398 		ret = mp2629_get_prop(charger, INPUT_VLIM, val);
399 		break;
400 
401 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
402 		ret = mp2629_get_prop(charger, INPUT_ILIM, val);
403 		break;
404 
405 	default:
406 		return -EINVAL;
407 	}
408 
409 	return ret;
410 }
411 
mp2629_charger_usb_set_prop(struct power_supply * psy,enum power_supply_property psp,const union power_supply_propval * 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)
415 {
416 	struct mp2629_charger *charger = dev_get_drvdata(psy->dev.parent);
417 
418 	switch (psp) {
419 	case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
420 		return mp2629_set_prop(charger, INPUT_VLIM, val);
421 
422 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
423 		return mp2629_set_prop(charger, INPUT_ILIM, val);
424 
425 	default:
426 		return -EINVAL;
427 	}
428 }
429 
mp2629_charger_battery_prop_writeable(struct power_supply * psy,enum power_supply_property psp)430 static 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 
mp2629_charger_usb_prop_writeable(struct power_supply * psy,enum power_supply_property psp)439 static 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 
mp2629_irq_handler(int irq,void * dev_id)446 static 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(charger->regmap, MP2629_REG_FAULT, &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(charger->regmap, MP2629_REG_STATUS, &rval);
473 	if (ret)
474 		goto unlock;
475 
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);
480 
481 unlock:
482 	mutex_unlock(&charger->lock);
483 
484 	return IRQ_HANDLED;
485 }
486 
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,
497 };
498 
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,
507 };
508 
batt_impedance_compensation_show(struct device * dev,struct device_attribute * attr,char * buf)509 static 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->parent);
514 	unsigned int rval;
515 	int ret;
516 
517 	ret = regmap_read(charger->regmap, MP2629_REG_IMPEDANCE_COMP, &rval);
518 	if (ret)
519 		return ret;
520 
521 	rval = (rval >> 4) * 10;
522 	return sysfs_emit(buf, "%d mohm\n", rval);
523 }
524 
batt_impedance_compensation_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)525 static 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->parent);
531 	unsigned int val;
532 	int ret;
533 
534 	ret = kstrtouint(buf, 10, &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(charger->regmap, MP2629_REG_IMPEDANCE_COMP,
544 					MP2629_MASK_IMPEDANCE, val << 4);
545 	if (ret)
546 		return ret;
547 
548 	return count;
549 }
550 
551 static DEVICE_ATTR_RW(batt_impedance_compensation);
552 
553 static struct attribute *mp2629_charger_sysfs_attrs[] = {
554 	&dev_attr_batt_impedance_compensation.attr,
555 	NULL
556 };
557 ATTRIBUTE_GROUPS(mp2629_charger_sysfs);
558 
mp2629_charger_disable(void * data)559 static void mp2629_charger_disable(void *data)
560 {
561 	struct mp2629_charger *charger = data;
562 
563 	regmap_update_bits(charger->regmap, MP2629_REG_CHARGE_CTRL,
564 					MP2629_MASK_CHARGE_CTRL, 0);
565 }
566 
mp2629_charger_probe(struct platform_device * pdev)567 static int mp2629_charger_probe(struct platform_device *pdev)
568 {
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 = {};
573 	int ret, i, irq;
574 
575 	charger = devm_kzalloc(dev, 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, 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 					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]);
593 		}
594 	}
595 
596 	for (i = 0; i < MP2629_ADC_CHAN_END; i++) {
597 		charger->iiochan[i] = devm_iio_channel_get(dev,
598 							adc_chan_name[i]);
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]);
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(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);
613 	}
614 
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);
622 	}
623 
624 	ret = regmap_update_bits(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(charger->regmap, MP2629_REG_TIMER_CTRL,
632 					MP2629_MASK_WDOG_CTRL, 0);
633 
634 	mutex_init(&charger->lock);
635 
636 	ret = devm_request_threaded_irq(dev, irq, NULL,	mp2629_irq_handler,
637 					IRQF_ONESHOT | IRQF_TRIGGER_RISING,
638 					"mp2629-charger", charger);
639 	if (ret) {
640 		dev_err(dev, "failed to request gpio IRQ\n");
641 		return ret;
642 	}
643 
644 	regmap_update_bits(charger->regmap, MP2629_REG_INTERRUPT,
645 				GENMASK(6, 5), BIT(6) | BIT(5));
646 
647 	return 0;
648 }
649 
650 static const struct of_device_id mp2629_charger_of_match[] = {
651 	{ .compatible = "mps,mp2629_charger"},
652 	{}
653 };
654 MODULE_DEVICE_TABLE(of, mp2629_charger_of_match);
655 
656 static 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 };
663 module_platform_driver(mp2629_charger_driver);
664 
665 MODULE_AUTHOR("Saravanan Sekar <sravanhome@gmail.com>");
666 MODULE_DESCRIPTION("MP2629 Charger driver");
667 MODULE_LICENSE("GPL");
668