13bc6d790SSaravanan Sekar // SPDX-License-Identifier: GPL-2.0-or-later
23bc6d790SSaravanan Sekar /*
33bc6d790SSaravanan Sekar  * MP2629 battery charger driver
43bc6d790SSaravanan Sekar  *
53bc6d790SSaravanan Sekar  * Copyright 2020 Monolithic Power Systems, Inc
63bc6d790SSaravanan Sekar  *
73bc6d790SSaravanan Sekar  * Author: Saravanan Sekar <sravanhome@gmail.com>
83bc6d790SSaravanan Sekar  */
93bc6d790SSaravanan Sekar 
103bc6d790SSaravanan Sekar #include <linux/bits.h>
113bc6d790SSaravanan Sekar #include <linux/iio/consumer.h>
123bc6d790SSaravanan Sekar #include <linux/iio/types.h>
133bc6d790SSaravanan Sekar #include <linux/interrupt.h>
143bc6d790SSaravanan Sekar #include <linux/mfd/mp2629.h>
153bc6d790SSaravanan Sekar #include <linux/module.h>
163bc6d790SSaravanan Sekar #include <linux/mod_devicetable.h>
173bc6d790SSaravanan Sekar #include <linux/platform_device.h>
183bc6d790SSaravanan Sekar #include <linux/power_supply.h>
193bc6d790SSaravanan Sekar #include <linux/regmap.h>
203bc6d790SSaravanan Sekar 
213bc6d790SSaravanan Sekar #define MP2629_REG_INPUT_ILIM		0x00
223bc6d790SSaravanan Sekar #define MP2629_REG_INPUT_VLIM		0x01
233bc6d790SSaravanan Sekar #define MP2629_REG_CHARGE_CTRL		0x04
243bc6d790SSaravanan Sekar #define MP2629_REG_CHARGE_ILIM		0x05
253bc6d790SSaravanan Sekar #define MP2629_REG_PRECHARGE		0x06
263bc6d790SSaravanan Sekar #define MP2629_REG_TERM_CURRENT		0x06
273bc6d790SSaravanan Sekar #define MP2629_REG_CHARGE_VLIM		0x07
283bc6d790SSaravanan Sekar #define MP2629_REG_TIMER_CTRL		0x08
293bc6d790SSaravanan Sekar #define MP2629_REG_IMPEDANCE_COMP	0x09
303bc6d790SSaravanan Sekar #define MP2629_REG_INTERRUPT		0x0b
313bc6d790SSaravanan Sekar #define MP2629_REG_STATUS		0x0c
323bc6d790SSaravanan Sekar #define MP2629_REG_FAULT		0x0d
333bc6d790SSaravanan Sekar 
343bc6d790SSaravanan Sekar #define MP2629_MASK_INPUT_TYPE		GENMASK(7, 5)
353bc6d790SSaravanan Sekar #define MP2629_MASK_CHARGE_TYPE		GENMASK(4, 3)
363bc6d790SSaravanan Sekar #define MP2629_MASK_CHARGE_CTRL		GENMASK(5, 4)
373bc6d790SSaravanan Sekar #define MP2629_MASK_WDOG_CTRL		GENMASK(5, 4)
383bc6d790SSaravanan Sekar #define MP2629_MASK_IMPEDANCE		GENMASK(7, 4)
393bc6d790SSaravanan Sekar 
403bc6d790SSaravanan Sekar #define MP2629_INPUTSOURCE_CHANGE	GENMASK(7, 5)
413bc6d790SSaravanan Sekar #define MP2629_CHARGING_CHANGE		GENMASK(4, 3)
423bc6d790SSaravanan Sekar #define MP2629_FAULT_BATTERY		BIT(3)
433bc6d790SSaravanan Sekar #define MP2629_FAULT_THERMAL		BIT(4)
443bc6d790SSaravanan Sekar #define MP2629_FAULT_INPUT		BIT(5)
453bc6d790SSaravanan Sekar #define MP2629_FAULT_OTG		BIT(6)
463bc6d790SSaravanan Sekar 
473bc6d790SSaravanan Sekar #define MP2629_MAX_BATT_CAPACITY	100
483bc6d790SSaravanan Sekar 
493bc6d790SSaravanan Sekar #define MP2629_PROPS(_idx, _min, _max, _step)		\
503bc6d790SSaravanan Sekar 	[_idx] = {					\
513bc6d790SSaravanan Sekar 		.min	= _min,				\
523bc6d790SSaravanan Sekar 		.max	= _max,				\
533bc6d790SSaravanan Sekar 		.step	= _step,			\
543bc6d790SSaravanan Sekar }
553bc6d790SSaravanan Sekar 
563bc6d790SSaravanan Sekar enum mp2629_source_type {
573bc6d790SSaravanan Sekar 	MP2629_SOURCE_TYPE_NO_INPUT,
583bc6d790SSaravanan Sekar 	MP2629_SOURCE_TYPE_NON_STD,
593bc6d790SSaravanan Sekar 	MP2629_SOURCE_TYPE_SDP,
603bc6d790SSaravanan Sekar 	MP2629_SOURCE_TYPE_CDP,
613bc6d790SSaravanan Sekar 	MP2629_SOURCE_TYPE_DCP,
623bc6d790SSaravanan Sekar 	MP2629_SOURCE_TYPE_OTG = 7,
633bc6d790SSaravanan Sekar };
643bc6d790SSaravanan Sekar 
653bc6d790SSaravanan Sekar enum mp2629_field {
663bc6d790SSaravanan Sekar 	INPUT_ILIM,
673bc6d790SSaravanan Sekar 	INPUT_VLIM,
683bc6d790SSaravanan Sekar 	CHARGE_ILIM,
693bc6d790SSaravanan Sekar 	CHARGE_VLIM,
703bc6d790SSaravanan Sekar 	PRECHARGE,
713bc6d790SSaravanan Sekar 	TERM_CURRENT,
723bc6d790SSaravanan Sekar 	MP2629_MAX_FIELD
733bc6d790SSaravanan Sekar };
743bc6d790SSaravanan Sekar 
753bc6d790SSaravanan Sekar struct mp2629_charger {
763bc6d790SSaravanan Sekar 	struct device *dev;
773bc6d790SSaravanan Sekar 	int status;
783bc6d790SSaravanan Sekar 	int fault;
793bc6d790SSaravanan Sekar 
803bc6d790SSaravanan Sekar 	struct regmap *regmap;
813bc6d790SSaravanan Sekar 	struct regmap_field *regmap_fields[MP2629_MAX_FIELD];
823bc6d790SSaravanan Sekar 	struct mutex lock;
833bc6d790SSaravanan Sekar 	struct power_supply *usb;
843bc6d790SSaravanan Sekar 	struct power_supply *battery;
853bc6d790SSaravanan Sekar 	struct iio_channel *iiochan[MP2629_ADC_CHAN_END];
863bc6d790SSaravanan Sekar };
873bc6d790SSaravanan Sekar 
883bc6d790SSaravanan Sekar struct mp2629_prop {
893bc6d790SSaravanan Sekar 	int reg;
903bc6d790SSaravanan Sekar 	int mask;
913bc6d790SSaravanan Sekar 	int min;
923bc6d790SSaravanan Sekar 	int max;
933bc6d790SSaravanan Sekar 	int step;
943bc6d790SSaravanan Sekar 	int shift;
953bc6d790SSaravanan Sekar };
963bc6d790SSaravanan Sekar 
973bc6d790SSaravanan Sekar static enum power_supply_usb_type mp2629_usb_types[] = {
983bc6d790SSaravanan Sekar 	POWER_SUPPLY_USB_TYPE_SDP,
993bc6d790SSaravanan Sekar 	POWER_SUPPLY_USB_TYPE_DCP,
1003bc6d790SSaravanan Sekar 	POWER_SUPPLY_USB_TYPE_CDP,
1013bc6d790SSaravanan Sekar 	POWER_SUPPLY_USB_TYPE_PD_DRP,
1023bc6d790SSaravanan Sekar 	POWER_SUPPLY_USB_TYPE_UNKNOWN
1033bc6d790SSaravanan Sekar };
1043bc6d790SSaravanan Sekar 
1053bc6d790SSaravanan Sekar static enum power_supply_property mp2629_charger_usb_props[] = {
1063bc6d790SSaravanan Sekar 	POWER_SUPPLY_PROP_ONLINE,
1073bc6d790SSaravanan Sekar 	POWER_SUPPLY_PROP_USB_TYPE,
1083bc6d790SSaravanan Sekar 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
1093bc6d790SSaravanan Sekar 	POWER_SUPPLY_PROP_CURRENT_NOW,
1103bc6d790SSaravanan Sekar 	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
1113bc6d790SSaravanan Sekar 	POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT,
1123bc6d790SSaravanan Sekar };
1133bc6d790SSaravanan Sekar 
1143bc6d790SSaravanan Sekar static enum power_supply_property mp2629_charger_bat_props[] = {
1153bc6d790SSaravanan Sekar 	POWER_SUPPLY_PROP_STATUS,
1163bc6d790SSaravanan Sekar 	POWER_SUPPLY_PROP_HEALTH,
1173bc6d790SSaravanan Sekar 	POWER_SUPPLY_PROP_CHARGE_TYPE,
1183bc6d790SSaravanan Sekar 	POWER_SUPPLY_PROP_VOLTAGE_NOW,
1193bc6d790SSaravanan Sekar 	POWER_SUPPLY_PROP_CURRENT_NOW,
1203bc6d790SSaravanan Sekar 	POWER_SUPPLY_PROP_CAPACITY,
1213bc6d790SSaravanan Sekar 	POWER_SUPPLY_PROP_PRECHARGE_CURRENT,
1223bc6d790SSaravanan Sekar 	POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT,
1233bc6d790SSaravanan Sekar 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
1243bc6d790SSaravanan Sekar 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
1253bc6d790SSaravanan Sekar 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
1263bc6d790SSaravanan Sekar 	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
1273bc6d790SSaravanan Sekar };
1283bc6d790SSaravanan Sekar 
1293bc6d790SSaravanan Sekar static struct mp2629_prop props[] = {
1303bc6d790SSaravanan Sekar 	MP2629_PROPS(INPUT_ILIM, 100000, 3250000, 50000),
1313bc6d790SSaravanan Sekar 	MP2629_PROPS(INPUT_VLIM, 3800000, 5300000, 100000),
1323bc6d790SSaravanan Sekar 	MP2629_PROPS(CHARGE_ILIM, 320000, 4520000, 40000),
1333bc6d790SSaravanan Sekar 	MP2629_PROPS(CHARGE_VLIM, 3400000, 4670000, 10000),
1343bc6d790SSaravanan Sekar 	MP2629_PROPS(PRECHARGE, 120000, 720000, 40000),
1353bc6d790SSaravanan Sekar 	MP2629_PROPS(TERM_CURRENT, 80000, 680000, 40000),
1363bc6d790SSaravanan Sekar };
1373bc6d790SSaravanan Sekar 
1383bc6d790SSaravanan Sekar static const struct reg_field mp2629_reg_fields[] = {
1393bc6d790SSaravanan Sekar 	[INPUT_ILIM]	= REG_FIELD(MP2629_REG_INPUT_ILIM, 0, 5),
1403bc6d790SSaravanan Sekar 	[INPUT_VLIM]	= REG_FIELD(MP2629_REG_INPUT_VLIM, 0, 3),
1413bc6d790SSaravanan Sekar 	[CHARGE_ILIM]	= REG_FIELD(MP2629_REG_CHARGE_ILIM, 0, 6),
1423bc6d790SSaravanan Sekar 	[CHARGE_VLIM]	= REG_FIELD(MP2629_REG_CHARGE_VLIM, 1, 7),
1433bc6d790SSaravanan Sekar 	[PRECHARGE]	= REG_FIELD(MP2629_REG_PRECHARGE, 4, 7),
1443bc6d790SSaravanan Sekar 	[TERM_CURRENT]	= REG_FIELD(MP2629_REG_TERM_CURRENT, 0, 3),
1453bc6d790SSaravanan Sekar };
1463bc6d790SSaravanan Sekar 
1473bc6d790SSaravanan Sekar static char *adc_chan_name[] = {
1483bc6d790SSaravanan Sekar 	"mp2629-batt-volt",
1493bc6d790SSaravanan Sekar 	"mp2629-system-volt",
1503bc6d790SSaravanan Sekar 	"mp2629-input-volt",
1513bc6d790SSaravanan Sekar 	"mp2629-batt-current",
1523bc6d790SSaravanan Sekar 	"mp2629-input-current",
1533bc6d790SSaravanan Sekar };
1543bc6d790SSaravanan Sekar 
mp2629_read_adc(struct mp2629_charger * charger,enum mp2629_adc_chan ch,union power_supply_propval * val)1553bc6d790SSaravanan Sekar static int mp2629_read_adc(struct mp2629_charger *charger,
1563bc6d790SSaravanan Sekar 			   enum mp2629_adc_chan ch,
1573bc6d790SSaravanan Sekar 			   union power_supply_propval *val)
1583bc6d790SSaravanan Sekar {
1593bc6d790SSaravanan Sekar 	int ret;
1603bc6d790SSaravanan Sekar 	int chval;
1613bc6d790SSaravanan Sekar 
1623bc6d790SSaravanan Sekar 	ret = iio_read_channel_processed(charger->iiochan[ch], &chval);
1633bc6d790SSaravanan Sekar 	if (ret)
1643bc6d790SSaravanan Sekar 		return ret;
1653bc6d790SSaravanan Sekar 
1663bc6d790SSaravanan Sekar 	val->intval = chval * 1000;
1673bc6d790SSaravanan Sekar 
1683bc6d790SSaravanan Sekar 	return 0;
1693bc6d790SSaravanan Sekar }
1703bc6d790SSaravanan Sekar 
mp2629_get_prop(struct mp2629_charger * charger,enum mp2629_field fld,union power_supply_propval * val)1713bc6d790SSaravanan Sekar static int mp2629_get_prop(struct mp2629_charger *charger,
1723bc6d790SSaravanan Sekar 			   enum mp2629_field fld,
1733bc6d790SSaravanan Sekar 			   union power_supply_propval *val)
1743bc6d790SSaravanan Sekar {
1753bc6d790SSaravanan Sekar 	int ret;
1763bc6d790SSaravanan Sekar 	unsigned int rval;
1773bc6d790SSaravanan Sekar 
1783bc6d790SSaravanan Sekar 	ret = regmap_field_read(charger->regmap_fields[fld], &rval);
1793bc6d790SSaravanan Sekar 	if (ret)
1803bc6d790SSaravanan Sekar 		return ret;
1813bc6d790SSaravanan Sekar 
1823bc6d790SSaravanan Sekar 	val->intval = rval * props[fld].step + props[fld].min;
1833bc6d790SSaravanan Sekar 
1843bc6d790SSaravanan Sekar 	return 0;
1853bc6d790SSaravanan Sekar }
1863bc6d790SSaravanan Sekar 
mp2629_set_prop(struct mp2629_charger * charger,enum mp2629_field fld,const union power_supply_propval * val)1873bc6d790SSaravanan Sekar static int mp2629_set_prop(struct mp2629_charger *charger,
1883bc6d790SSaravanan Sekar 			   enum mp2629_field fld,
1893bc6d790SSaravanan Sekar 			   const union power_supply_propval *val)
1903bc6d790SSaravanan Sekar {
1913bc6d790SSaravanan Sekar 	unsigned int rval;
1923bc6d790SSaravanan Sekar 
1933bc6d790SSaravanan Sekar 	if (val->intval < props[fld].min || val->intval > props[fld].max)
1943bc6d790SSaravanan Sekar 		return -EINVAL;
1953bc6d790SSaravanan Sekar 
1963bc6d790SSaravanan Sekar 	rval = (val->intval - props[fld].min) / props[fld].step;
1973bc6d790SSaravanan Sekar 	return regmap_field_write(charger->regmap_fields[fld], rval);
1983bc6d790SSaravanan Sekar }
1993bc6d790SSaravanan Sekar 
mp2629_get_battery_capacity(struct mp2629_charger * charger,union power_supply_propval * val)2003bc6d790SSaravanan Sekar static int mp2629_get_battery_capacity(struct mp2629_charger *charger,
2013bc6d790SSaravanan Sekar 				       union power_supply_propval *val)
2023bc6d790SSaravanan Sekar {
2033bc6d790SSaravanan Sekar 	union power_supply_propval vnow, vlim;
2043bc6d790SSaravanan Sekar 	int ret;
2053bc6d790SSaravanan Sekar 
2063bc6d790SSaravanan Sekar 	ret = mp2629_read_adc(charger, MP2629_BATT_VOLT, &vnow);
2073bc6d790SSaravanan Sekar 	if (ret)
2083bc6d790SSaravanan Sekar 		return ret;
2093bc6d790SSaravanan Sekar 
2103bc6d790SSaravanan Sekar 	ret = mp2629_get_prop(charger, CHARGE_VLIM, &vlim);
2113bc6d790SSaravanan Sekar 	if (ret)
2123bc6d790SSaravanan Sekar 		return ret;
2133bc6d790SSaravanan Sekar 
2143bc6d790SSaravanan Sekar 	val->intval = (vnow.intval * 100) / vlim.intval;
2153bc6d790SSaravanan Sekar 	val->intval = min(val->intval, MP2629_MAX_BATT_CAPACITY);
2163bc6d790SSaravanan Sekar 
2173bc6d790SSaravanan Sekar 	return 0;
2183bc6d790SSaravanan Sekar }
2193bc6d790SSaravanan Sekar 
mp2629_charger_battery_get_prop(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)2203bc6d790SSaravanan Sekar static int mp2629_charger_battery_get_prop(struct power_supply *psy,
2213bc6d790SSaravanan Sekar 					enum power_supply_property psp,
2223bc6d790SSaravanan Sekar 					union power_supply_propval *val)
2233bc6d790SSaravanan Sekar {
2243bc6d790SSaravanan Sekar 	struct mp2629_charger *charger = dev_get_drvdata(psy->dev.parent);
2253bc6d790SSaravanan Sekar 	unsigned int rval;
2263bc6d790SSaravanan Sekar 	int ret = 0;
2273bc6d790SSaravanan Sekar 
2283bc6d790SSaravanan Sekar 	switch (psp) {
2293bc6d790SSaravanan Sekar 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
2303bc6d790SSaravanan Sekar 		ret = mp2629_read_adc(charger, MP2629_BATT_VOLT, val);
2313bc6d790SSaravanan Sekar 		break;
2323bc6d790SSaravanan Sekar 
2333bc6d790SSaravanan Sekar 	case POWER_SUPPLY_PROP_CURRENT_NOW:
2343bc6d790SSaravanan Sekar 		ret = mp2629_read_adc(charger, MP2629_BATT_CURRENT, val);
2353bc6d790SSaravanan Sekar 		break;
2363bc6d790SSaravanan Sekar 
2373bc6d790SSaravanan Sekar 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
2383bc6d790SSaravanan Sekar 		val->intval = 4520000;
2393bc6d790SSaravanan Sekar 		break;
2403bc6d790SSaravanan Sekar 
2413bc6d790SSaravanan Sekar 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
2423bc6d790SSaravanan Sekar 		val->intval = 4670000;
2433bc6d790SSaravanan Sekar 		break;
2443bc6d790SSaravanan Sekar 
2453bc6d790SSaravanan Sekar 	case POWER_SUPPLY_PROP_CAPACITY:
2463bc6d790SSaravanan Sekar 		ret = mp2629_get_battery_capacity(charger, val);
2473bc6d790SSaravanan Sekar 		break;
2483bc6d790SSaravanan Sekar 
2493bc6d790SSaravanan Sekar 	case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
2503bc6d790SSaravanan Sekar 		ret = mp2629_get_prop(charger, TERM_CURRENT, val);
2513bc6d790SSaravanan Sekar 		break;
2523bc6d790SSaravanan Sekar 
2533bc6d790SSaravanan Sekar 	case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
2543bc6d790SSaravanan Sekar 		ret = mp2629_get_prop(charger, PRECHARGE, val);
2553bc6d790SSaravanan Sekar 		break;
2563bc6d790SSaravanan Sekar 
2573bc6d790SSaravanan Sekar 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
2583bc6d790SSaravanan Sekar 		ret = mp2629_get_prop(charger, CHARGE_VLIM, val);
2593bc6d790SSaravanan Sekar 		break;
2603bc6d790SSaravanan Sekar 
2613bc6d790SSaravanan Sekar 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
2623bc6d790SSaravanan Sekar 		ret = mp2629_get_prop(charger, CHARGE_ILIM, val);
2633bc6d790SSaravanan Sekar 		break;
2643bc6d790SSaravanan Sekar 
2653bc6d790SSaravanan Sekar 	case POWER_SUPPLY_PROP_HEALTH:
2663bc6d790SSaravanan Sekar 		if (!charger->fault)
2673bc6d790SSaravanan Sekar 			val->intval = POWER_SUPPLY_HEALTH_GOOD;
2683bc6d790SSaravanan Sekar 		if (MP2629_FAULT_BATTERY & charger->fault)
2693bc6d790SSaravanan Sekar 			val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
2703bc6d790SSaravanan Sekar 		else if (MP2629_FAULT_THERMAL & charger->fault)
2713bc6d790SSaravanan Sekar 			val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
2723bc6d790SSaravanan Sekar 		else if (MP2629_FAULT_INPUT & charger->fault)
2733bc6d790SSaravanan Sekar 			val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
2743bc6d790SSaravanan Sekar 		break;
2753bc6d790SSaravanan Sekar 
2763bc6d790SSaravanan Sekar 	case POWER_SUPPLY_PROP_STATUS:
2773bc6d790SSaravanan Sekar 		ret = regmap_read(charger->regmap, MP2629_REG_STATUS, &rval);
2783bc6d790SSaravanan Sekar 		if (ret)
2793bc6d790SSaravanan Sekar 			break;
2803bc6d790SSaravanan Sekar 
2813bc6d790SSaravanan Sekar 		rval = (rval & MP2629_MASK_CHARGE_TYPE) >> 3;
2823bc6d790SSaravanan Sekar 		switch (rval) {
2833bc6d790SSaravanan Sekar 		case 0x00:
2843bc6d790SSaravanan Sekar 			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
2853bc6d790SSaravanan Sekar 			break;
2863bc6d790SSaravanan Sekar 		case 0x01:
2873bc6d790SSaravanan Sekar 		case 0x10:
2883bc6d790SSaravanan Sekar 			val->intval = POWER_SUPPLY_STATUS_CHARGING;
2893bc6d790SSaravanan Sekar 			break;
2903bc6d790SSaravanan Sekar 		case 0x11:
2913bc6d790SSaravanan Sekar 			val->intval = POWER_SUPPLY_STATUS_FULL;
2923bc6d790SSaravanan Sekar 		}
2933bc6d790SSaravanan Sekar 		break;
2943bc6d790SSaravanan Sekar 
2953bc6d790SSaravanan Sekar 	case POWER_SUPPLY_PROP_CHARGE_TYPE:
2963bc6d790SSaravanan Sekar 		ret = regmap_read(charger->regmap, MP2629_REG_STATUS, &rval);
2973bc6d790SSaravanan Sekar 		if (ret)
2983bc6d790SSaravanan Sekar 			break;
2993bc6d790SSaravanan Sekar 
3003bc6d790SSaravanan Sekar 		rval = (rval & MP2629_MASK_CHARGE_TYPE) >> 3;
3013bc6d790SSaravanan Sekar 		switch (rval) {
3023bc6d790SSaravanan Sekar 		case 0x00:
3033bc6d790SSaravanan Sekar 			val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
3043bc6d790SSaravanan Sekar 			break;
3053bc6d790SSaravanan Sekar 		case 0x01:
3063bc6d790SSaravanan Sekar 			val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
3073bc6d790SSaravanan Sekar 			break;
3083bc6d790SSaravanan Sekar 		case 0x10:
3093bc6d790SSaravanan Sekar 			val->intval = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
3103bc6d790SSaravanan Sekar 			break;
3113bc6d790SSaravanan Sekar 		default:
3123bc6d790SSaravanan Sekar 			val->intval = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
3133bc6d790SSaravanan Sekar 		}
3143bc6d790SSaravanan Sekar 		break;
3153bc6d790SSaravanan Sekar 
3163bc6d790SSaravanan Sekar 	default:
3173bc6d790SSaravanan Sekar 		return -EINVAL;
3183bc6d790SSaravanan Sekar 	}
3193bc6d790SSaravanan Sekar 
3203bc6d790SSaravanan Sekar 	return ret;
3213bc6d790SSaravanan Sekar }
3223bc6d790SSaravanan Sekar 
mp2629_charger_battery_set_prop(struct power_supply * psy,enum power_supply_property psp,const union power_supply_propval * val)3233bc6d790SSaravanan Sekar static int mp2629_charger_battery_set_prop(struct power_supply *psy,
3243bc6d790SSaravanan Sekar 					enum power_supply_property psp,
3253bc6d790SSaravanan Sekar 					const union power_supply_propval *val)
3263bc6d790SSaravanan Sekar {
3273bc6d790SSaravanan Sekar 	struct mp2629_charger *charger = dev_get_drvdata(psy->dev.parent);
3283bc6d790SSaravanan Sekar 
3293bc6d790SSaravanan Sekar 	switch (psp) {
3303bc6d790SSaravanan Sekar 	case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
3313bc6d790SSaravanan Sekar 		return mp2629_set_prop(charger, TERM_CURRENT, val);
3323bc6d790SSaravanan Sekar 
3333bc6d790SSaravanan Sekar 	case POWER_SUPPLY_PROP_PRECHARGE_CURRENT:
3343bc6d790SSaravanan Sekar 		return mp2629_set_prop(charger, PRECHARGE, val);
3353bc6d790SSaravanan Sekar 
3363bc6d790SSaravanan Sekar 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
3373bc6d790SSaravanan Sekar 		return mp2629_set_prop(charger, CHARGE_VLIM, val);
3383bc6d790SSaravanan Sekar 
3393bc6d790SSaravanan Sekar 	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
3403bc6d790SSaravanan Sekar 		return mp2629_set_prop(charger, CHARGE_ILIM, val);
3413bc6d790SSaravanan Sekar 
3423bc6d790SSaravanan Sekar 	default:
3433bc6d790SSaravanan Sekar 		return -EINVAL;
3443bc6d790SSaravanan Sekar 	}
3453bc6d790SSaravanan Sekar }
3463bc6d790SSaravanan Sekar 
mp2629_charger_usb_get_prop(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)3473bc6d790SSaravanan Sekar static int mp2629_charger_usb_get_prop(struct power_supply *psy,
3483bc6d790SSaravanan Sekar 				enum power_supply_property psp,
3493bc6d790SSaravanan Sekar 				union power_supply_propval *val)
3503bc6d790SSaravanan Sekar {
3513bc6d790SSaravanan Sekar 	struct mp2629_charger *charger = dev_get_drvdata(psy->dev.parent);
3523bc6d790SSaravanan Sekar 	unsigned int rval;
3533bc6d790SSaravanan Sekar 	int ret;
3543bc6d790SSaravanan Sekar 
3553bc6d790SSaravanan Sekar 	switch (psp) {
3563bc6d790SSaravanan Sekar 	case POWER_SUPPLY_PROP_ONLINE:
3573bc6d790SSaravanan Sekar 		ret = regmap_read(charger->regmap, MP2629_REG_STATUS, &rval);
3583bc6d790SSaravanan Sekar 		if (ret)
3593bc6d790SSaravanan Sekar 			break;
3603bc6d790SSaravanan Sekar 
3613bc6d790SSaravanan Sekar 		val->intval = !!(rval & MP2629_MASK_INPUT_TYPE);
3623bc6d790SSaravanan Sekar 		break;
3633bc6d790SSaravanan Sekar 
3643bc6d790SSaravanan Sekar 	case POWER_SUPPLY_PROP_USB_TYPE:
3653bc6d790SSaravanan Sekar 		ret = regmap_read(charger->regmap, MP2629_REG_STATUS, &rval);
3663bc6d790SSaravanan Sekar 		if (ret)
3673bc6d790SSaravanan Sekar 			break;
3683bc6d790SSaravanan Sekar 
3693bc6d790SSaravanan Sekar 		rval = (rval & MP2629_MASK_INPUT_TYPE) >> 5;
3703bc6d790SSaravanan Sekar 		switch (rval) {
3713bc6d790SSaravanan Sekar 		case MP2629_SOURCE_TYPE_SDP:
3723bc6d790SSaravanan Sekar 			val->intval = POWER_SUPPLY_USB_TYPE_SDP;
3733bc6d790SSaravanan Sekar 			break;
3743bc6d790SSaravanan Sekar 		case MP2629_SOURCE_TYPE_CDP:
3753bc6d790SSaravanan Sekar 			val->intval = POWER_SUPPLY_USB_TYPE_CDP;
3763bc6d790SSaravanan Sekar 			break;
3773bc6d790SSaravanan Sekar 		case MP2629_SOURCE_TYPE_DCP:
3783bc6d790SSaravanan Sekar 			val->intval = POWER_SUPPLY_USB_TYPE_DCP;
3793bc6d790SSaravanan Sekar 			break;
3803bc6d790SSaravanan Sekar 		case MP2629_SOURCE_TYPE_OTG:
3813bc6d790SSaravanan Sekar 			val->intval = POWER_SUPPLY_USB_TYPE_PD_DRP;
3823bc6d790SSaravanan Sekar 			break;
3833bc6d790SSaravanan Sekar 		default:
3843bc6d790SSaravanan Sekar 			val->intval = POWER_SUPPLY_USB_TYPE_UNKNOWN;
3853bc6d790SSaravanan Sekar 			break;
3863bc6d790SSaravanan Sekar 		}
3873bc6d790SSaravanan Sekar 		break;
3883bc6d790SSaravanan Sekar 
3893bc6d790SSaravanan Sekar 	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
3903bc6d790SSaravanan Sekar 		ret = mp2629_read_adc(charger, MP2629_INPUT_VOLT, val);
3913bc6d790SSaravanan Sekar 		break;
3923bc6d790SSaravanan Sekar 
3933bc6d790SSaravanan Sekar 	case POWER_SUPPLY_PROP_CURRENT_NOW:
3943bc6d790SSaravanan Sekar 		ret = mp2629_read_adc(charger, MP2629_INPUT_CURRENT, val);
3953bc6d790SSaravanan Sekar 		break;
3963bc6d790SSaravanan Sekar 
3973bc6d790SSaravanan Sekar 	case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
3983bc6d790SSaravanan Sekar 		ret = mp2629_get_prop(charger, INPUT_VLIM, val);
3993bc6d790SSaravanan Sekar 		break;
4003bc6d790SSaravanan Sekar 
4013bc6d790SSaravanan Sekar 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
4023bc6d790SSaravanan Sekar 		ret = mp2629_get_prop(charger, INPUT_ILIM, val);
4033bc6d790SSaravanan Sekar 		break;
4043bc6d790SSaravanan Sekar 
4053bc6d790SSaravanan Sekar 	default:
4063bc6d790SSaravanan Sekar 		return -EINVAL;
4073bc6d790SSaravanan Sekar 	}
4083bc6d790SSaravanan Sekar 
4093bc6d790SSaravanan Sekar 	return ret;
4103bc6d790SSaravanan Sekar }
4113bc6d790SSaravanan Sekar 
mp2629_charger_usb_set_prop(struct power_supply * psy,enum power_supply_property psp,const union power_supply_propval * val)4123bc6d790SSaravanan Sekar static int mp2629_charger_usb_set_prop(struct power_supply *psy,
4133bc6d790SSaravanan Sekar 				enum power_supply_property psp,
4143bc6d790SSaravanan Sekar 				const union power_supply_propval *val)
4153bc6d790SSaravanan Sekar {
4163bc6d790SSaravanan Sekar 	struct mp2629_charger *charger = dev_get_drvdata(psy->dev.parent);
4173bc6d790SSaravanan Sekar 
4183bc6d790SSaravanan Sekar 	switch (psp) {
4193bc6d790SSaravanan Sekar 	case POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT:
4203bc6d790SSaravanan Sekar 		return mp2629_set_prop(charger, INPUT_VLIM, val);
4213bc6d790SSaravanan Sekar 
4223bc6d790SSaravanan Sekar 	case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
4233bc6d790SSaravanan Sekar 		return mp2629_set_prop(charger, INPUT_ILIM, val);
4243bc6d790SSaravanan Sekar 
4253bc6d790SSaravanan Sekar 	default:
4263bc6d790SSaravanan Sekar 		return -EINVAL;
4273bc6d790SSaravanan Sekar 	}
4283bc6d790SSaravanan Sekar }
4293bc6d790SSaravanan Sekar 
mp2629_charger_battery_prop_writeable(struct power_supply * psy,enum power_supply_property psp)4303bc6d790SSaravanan Sekar static int mp2629_charger_battery_prop_writeable(struct power_supply *psy,
4313bc6d790SSaravanan Sekar 				     enum power_supply_property psp)
4323bc6d790SSaravanan Sekar {
4333bc6d790SSaravanan Sekar 	return (psp == POWER_SUPPLY_PROP_PRECHARGE_CURRENT) ||
4343bc6d790SSaravanan Sekar 	       (psp == POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT) ||
4353bc6d790SSaravanan Sekar 	       (psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT) ||
4363bc6d790SSaravanan Sekar 	       (psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE);
4373bc6d790SSaravanan Sekar }
4383bc6d790SSaravanan Sekar 
mp2629_charger_usb_prop_writeable(struct power_supply * psy,enum power_supply_property psp)4393bc6d790SSaravanan Sekar static int mp2629_charger_usb_prop_writeable(struct power_supply *psy,
4403bc6d790SSaravanan Sekar 				     enum power_supply_property psp)
4413bc6d790SSaravanan Sekar {
4423bc6d790SSaravanan Sekar 	return (psp == POWER_SUPPLY_PROP_INPUT_VOLTAGE_LIMIT) ||
4433bc6d790SSaravanan Sekar 	       (psp == POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT);
4443bc6d790SSaravanan Sekar }
4453bc6d790SSaravanan Sekar 
mp2629_irq_handler(int irq,void * dev_id)4463bc6d790SSaravanan Sekar static irqreturn_t mp2629_irq_handler(int irq, void *dev_id)
4473bc6d790SSaravanan Sekar {
4483bc6d790SSaravanan Sekar 	struct mp2629_charger *charger = dev_id;
4493bc6d790SSaravanan Sekar 	unsigned int rval;
4503bc6d790SSaravanan Sekar 	int ret;
4513bc6d790SSaravanan Sekar 
4523bc6d790SSaravanan Sekar 	mutex_lock(&charger->lock);
4533bc6d790SSaravanan Sekar 
4543bc6d790SSaravanan Sekar 	ret = regmap_read(charger->regmap, MP2629_REG_FAULT, &rval);
4553bc6d790SSaravanan Sekar 	if (ret)
4563bc6d790SSaravanan Sekar 		goto unlock;
4573bc6d790SSaravanan Sekar 
4583bc6d790SSaravanan Sekar 	if (rval) {
4593bc6d790SSaravanan Sekar 		charger->fault = rval;
4603bc6d790SSaravanan Sekar 		if (MP2629_FAULT_BATTERY & rval)
4613bc6d790SSaravanan Sekar 			dev_err(charger->dev, "Battery fault OVP\n");
4623bc6d790SSaravanan Sekar 		else if (MP2629_FAULT_THERMAL & rval)
4633bc6d790SSaravanan Sekar 			dev_err(charger->dev, "Thermal shutdown fault\n");
4643bc6d790SSaravanan Sekar 		else if (MP2629_FAULT_INPUT & rval)
4653bc6d790SSaravanan Sekar 			dev_err(charger->dev, "no input or input OVP\n");
4663bc6d790SSaravanan Sekar 		else if (MP2629_FAULT_OTG & rval)
4673bc6d790SSaravanan Sekar 			dev_err(charger->dev, "VIN overloaded\n");
4683bc6d790SSaravanan Sekar 
4693bc6d790SSaravanan Sekar 		goto unlock;
4703bc6d790SSaravanan Sekar 	}
4713bc6d790SSaravanan Sekar 
4723bc6d790SSaravanan Sekar 	ret = regmap_read(charger->regmap, MP2629_REG_STATUS, &rval);
4733bc6d790SSaravanan Sekar 	if (ret)
4743bc6d790SSaravanan Sekar 		goto unlock;
4753bc6d790SSaravanan Sekar 
4763bc6d790SSaravanan Sekar 	if (rval & MP2629_INPUTSOURCE_CHANGE)
4773bc6d790SSaravanan Sekar 		power_supply_changed(charger->usb);
4783bc6d790SSaravanan Sekar 	else if (rval & MP2629_CHARGING_CHANGE)
4793bc6d790SSaravanan Sekar 		power_supply_changed(charger->battery);
4803bc6d790SSaravanan Sekar 
4813bc6d790SSaravanan Sekar unlock:
4823bc6d790SSaravanan Sekar 	mutex_unlock(&charger->lock);
4833bc6d790SSaravanan Sekar 
4843bc6d790SSaravanan Sekar 	return IRQ_HANDLED;
4853bc6d790SSaravanan Sekar }
4863bc6d790SSaravanan Sekar 
4873bc6d790SSaravanan Sekar static const struct power_supply_desc mp2629_usb_desc = {
4883bc6d790SSaravanan Sekar 	.name		= "mp2629_usb",
4893bc6d790SSaravanan Sekar 	.type		= POWER_SUPPLY_TYPE_USB,
4903bc6d790SSaravanan Sekar 	.usb_types      = mp2629_usb_types,
4913bc6d790SSaravanan Sekar 	.num_usb_types  = ARRAY_SIZE(mp2629_usb_types),
4923bc6d790SSaravanan Sekar 	.properties	= mp2629_charger_usb_props,
4933bc6d790SSaravanan Sekar 	.num_properties	= ARRAY_SIZE(mp2629_charger_usb_props),
4943bc6d790SSaravanan Sekar 	.get_property	= mp2629_charger_usb_get_prop,
4953bc6d790SSaravanan Sekar 	.set_property	= mp2629_charger_usb_set_prop,
4963bc6d790SSaravanan Sekar 	.property_is_writeable = mp2629_charger_usb_prop_writeable,
4973bc6d790SSaravanan Sekar };
4983bc6d790SSaravanan Sekar 
4993bc6d790SSaravanan Sekar static const struct power_supply_desc mp2629_battery_desc = {
5003bc6d790SSaravanan Sekar 	.name		= "mp2629_battery",
5013bc6d790SSaravanan Sekar 	.type		= POWER_SUPPLY_TYPE_BATTERY,
5023bc6d790SSaravanan Sekar 	.properties	= mp2629_charger_bat_props,
5033bc6d790SSaravanan Sekar 	.num_properties	= ARRAY_SIZE(mp2629_charger_bat_props),
5043bc6d790SSaravanan Sekar 	.get_property	= mp2629_charger_battery_get_prop,
5053bc6d790SSaravanan Sekar 	.set_property	= mp2629_charger_battery_set_prop,
5063bc6d790SSaravanan Sekar 	.property_is_writeable = mp2629_charger_battery_prop_writeable,
5073bc6d790SSaravanan Sekar };
5083bc6d790SSaravanan Sekar 
batt_impedance_compensation_show(struct device * dev,struct device_attribute * attr,char * buf)5093bc6d790SSaravanan Sekar static ssize_t batt_impedance_compensation_show(struct device *dev,
5103bc6d790SSaravanan Sekar 					   struct device_attribute *attr,
5113bc6d790SSaravanan Sekar 					   char *buf)
5123bc6d790SSaravanan Sekar {
5133bc6d790SSaravanan Sekar 	struct mp2629_charger *charger = dev_get_drvdata(dev->parent);
5143bc6d790SSaravanan Sekar 	unsigned int rval;
5153bc6d790SSaravanan Sekar 	int ret;
5163bc6d790SSaravanan Sekar 
5173bc6d790SSaravanan Sekar 	ret = regmap_read(charger->regmap, MP2629_REG_IMPEDANCE_COMP, &rval);
5183bc6d790SSaravanan Sekar 	if (ret)
5193bc6d790SSaravanan Sekar 		return ret;
5203bc6d790SSaravanan Sekar 
5213bc6d790SSaravanan Sekar 	rval = (rval >> 4) * 10;
522*a441f3b9Sye xingchen 	return sysfs_emit(buf, "%d mohm\n", rval);
5233bc6d790SSaravanan Sekar }
5243bc6d790SSaravanan Sekar 
batt_impedance_compensation_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)5253bc6d790SSaravanan Sekar static ssize_t batt_impedance_compensation_store(struct device *dev,
5263bc6d790SSaravanan Sekar 					    struct device_attribute *attr,
5273bc6d790SSaravanan Sekar 					    const char *buf,
5283bc6d790SSaravanan Sekar 					    size_t count)
5293bc6d790SSaravanan Sekar {
5303bc6d790SSaravanan Sekar 	struct mp2629_charger *charger = dev_get_drvdata(dev->parent);
5313bc6d790SSaravanan Sekar 	unsigned int val;
5323bc6d790SSaravanan Sekar 	int ret;
5333bc6d790SSaravanan Sekar 
5343bc6d790SSaravanan Sekar 	ret = kstrtouint(buf, 10, &val);
5353bc6d790SSaravanan Sekar 	if (ret)
5363bc6d790SSaravanan Sekar 		return ret;
5373bc6d790SSaravanan Sekar 
5383bc6d790SSaravanan Sekar 	if (val > 140)
5393bc6d790SSaravanan Sekar 		return -ERANGE;
5403bc6d790SSaravanan Sekar 
5413bc6d790SSaravanan Sekar 	/* multiples of 10 mohm so round off */
5423bc6d790SSaravanan Sekar 	val = val / 10;
5433bc6d790SSaravanan Sekar 	ret = regmap_update_bits(charger->regmap, MP2629_REG_IMPEDANCE_COMP,
5443bc6d790SSaravanan Sekar 					MP2629_MASK_IMPEDANCE, val << 4);
5453bc6d790SSaravanan Sekar 	if (ret)
5463bc6d790SSaravanan Sekar 		return ret;
5473bc6d790SSaravanan Sekar 
5483bc6d790SSaravanan Sekar 	return count;
5493bc6d790SSaravanan Sekar }
5503bc6d790SSaravanan Sekar 
5513bc6d790SSaravanan Sekar static DEVICE_ATTR_RW(batt_impedance_compensation);
5523bc6d790SSaravanan Sekar 
5533bc6d790SSaravanan Sekar static struct attribute *mp2629_charger_sysfs_attrs[] = {
5543bc6d790SSaravanan Sekar 	&dev_attr_batt_impedance_compensation.attr,
5553bc6d790SSaravanan Sekar 	NULL
5563bc6d790SSaravanan Sekar };
5573bc6d790SSaravanan Sekar ATTRIBUTE_GROUPS(mp2629_charger_sysfs);
5583bc6d790SSaravanan Sekar 
mp2629_charger_disable(void * data)5593bc6d790SSaravanan Sekar static void mp2629_charger_disable(void *data)
5603bc6d790SSaravanan Sekar {
5613bc6d790SSaravanan Sekar 	struct mp2629_charger *charger = data;
5623bc6d790SSaravanan Sekar 
5633bc6d790SSaravanan Sekar 	regmap_update_bits(charger->regmap, MP2629_REG_CHARGE_CTRL,
5643bc6d790SSaravanan Sekar 					MP2629_MASK_CHARGE_CTRL, 0);
5653bc6d790SSaravanan Sekar }
5663bc6d790SSaravanan Sekar 
mp2629_charger_probe(struct platform_device * pdev)5673bc6d790SSaravanan Sekar static int mp2629_charger_probe(struct platform_device *pdev)
5683bc6d790SSaravanan Sekar {
5693bc6d790SSaravanan Sekar 	struct device *dev = &pdev->dev;
5703bc6d790SSaravanan Sekar 	struct mp2629_data *ddata = dev_get_drvdata(dev->parent);
5713bc6d790SSaravanan Sekar 	struct mp2629_charger *charger;
5723bc6d790SSaravanan Sekar 	struct power_supply_config psy_cfg = {};
5733bc6d790SSaravanan Sekar 	int ret, i, irq;
5743bc6d790SSaravanan Sekar 
5753bc6d790SSaravanan Sekar 	charger = devm_kzalloc(dev, sizeof(*charger), GFP_KERNEL);
5763bc6d790SSaravanan Sekar 	if (!charger)
5773bc6d790SSaravanan Sekar 		return -ENOMEM;
5783bc6d790SSaravanan Sekar 
5793bc6d790SSaravanan Sekar 	charger->regmap = ddata->regmap;
5803bc6d790SSaravanan Sekar 	charger->dev = dev;
5813bc6d790SSaravanan Sekar 	platform_set_drvdata(pdev, charger);
5823bc6d790SSaravanan Sekar 
5832b7950c7SSergey Shtylyov 	irq = platform_get_irq(to_platform_device(dev->parent), 0);
5842b7950c7SSergey Shtylyov 	if (irq < 0)
5853bc6d790SSaravanan Sekar 		return irq;
5863bc6d790SSaravanan Sekar 
5873bc6d790SSaravanan Sekar 	for (i = 0; i < MP2629_MAX_FIELD; i++) {
5883bc6d790SSaravanan Sekar 		charger->regmap_fields[i] = devm_regmap_field_alloc(dev,
5893bc6d790SSaravanan Sekar 					charger->regmap, mp2629_reg_fields[i]);
5903bc6d790SSaravanan Sekar 		if (IS_ERR(charger->regmap_fields[i])) {
5913bc6d790SSaravanan Sekar 			dev_err(dev, "regmap field alloc fail %d\n", i);
5923bc6d790SSaravanan Sekar 			return PTR_ERR(charger->regmap_fields[i]);
5933bc6d790SSaravanan Sekar 		}
5943bc6d790SSaravanan Sekar 	}
5953bc6d790SSaravanan Sekar 
5963bc6d790SSaravanan Sekar 	for (i = 0; i < MP2629_ADC_CHAN_END; i++) {
5973bc6d790SSaravanan Sekar 		charger->iiochan[i] = devm_iio_channel_get(dev,
5983bc6d790SSaravanan Sekar 							adc_chan_name[i]);
5993bc6d790SSaravanan Sekar 		if (IS_ERR(charger->iiochan[i])) {
6003bc6d790SSaravanan Sekar 			dev_err(dev, "iio chan get %s err\n", adc_chan_name[i]);
6013bc6d790SSaravanan Sekar 			return PTR_ERR(charger->iiochan[i]);
6023bc6d790SSaravanan Sekar 		}
6033bc6d790SSaravanan Sekar 	}
6043bc6d790SSaravanan Sekar 
6053bc6d790SSaravanan Sekar 	ret = devm_add_action_or_reset(dev, mp2629_charger_disable, charger);
6063bc6d790SSaravanan Sekar 	if (ret)
6073bc6d790SSaravanan Sekar 		return ret;
6083bc6d790SSaravanan Sekar 
6093bc6d790SSaravanan Sekar 	charger->usb = devm_power_supply_register(dev, &mp2629_usb_desc, NULL);
6103bc6d790SSaravanan Sekar 	if (IS_ERR(charger->usb)) {
6113bc6d790SSaravanan Sekar 		dev_err(dev, "power supply register usb failed\n");
6123bc6d790SSaravanan Sekar 		return PTR_ERR(charger->usb);
6133bc6d790SSaravanan Sekar 	}
6143bc6d790SSaravanan Sekar 
6153bc6d790SSaravanan Sekar 	psy_cfg.drv_data = charger;
6163bc6d790SSaravanan Sekar 	psy_cfg.attr_grp = mp2629_charger_sysfs_groups;
6173bc6d790SSaravanan Sekar 	charger->battery = devm_power_supply_register(dev,
6183bc6d790SSaravanan Sekar 					 &mp2629_battery_desc, &psy_cfg);
6193bc6d790SSaravanan Sekar 	if (IS_ERR(charger->battery)) {
6203bc6d790SSaravanan Sekar 		dev_err(dev, "power supply register battery failed\n");
6213bc6d790SSaravanan Sekar 		return PTR_ERR(charger->battery);
6223bc6d790SSaravanan Sekar 	}
6233bc6d790SSaravanan Sekar 
6243bc6d790SSaravanan Sekar 	ret = regmap_update_bits(charger->regmap, MP2629_REG_CHARGE_CTRL,
6253bc6d790SSaravanan Sekar 					MP2629_MASK_CHARGE_CTRL, BIT(4));
6263bc6d790SSaravanan Sekar 	if (ret) {
6273bc6d790SSaravanan Sekar 		dev_err(dev, "enable charge fail: %d\n", ret);
6283bc6d790SSaravanan Sekar 		return ret;
6293bc6d790SSaravanan Sekar 	}
6303bc6d790SSaravanan Sekar 
6313bc6d790SSaravanan Sekar 	regmap_update_bits(charger->regmap, MP2629_REG_TIMER_CTRL,
6323bc6d790SSaravanan Sekar 					MP2629_MASK_WDOG_CTRL, 0);
6333bc6d790SSaravanan Sekar 
6343bc6d790SSaravanan Sekar 	mutex_init(&charger->lock);
6353bc6d790SSaravanan Sekar 
6363bc6d790SSaravanan Sekar 	ret = devm_request_threaded_irq(dev, irq, NULL,	mp2629_irq_handler,
6373bc6d790SSaravanan Sekar 					IRQF_ONESHOT | IRQF_TRIGGER_RISING,
6383bc6d790SSaravanan Sekar 					"mp2629-charger", charger);
6393bc6d790SSaravanan Sekar 	if (ret) {
6403bc6d790SSaravanan Sekar 		dev_err(dev, "failed to request gpio IRQ\n");
6413bc6d790SSaravanan Sekar 		return ret;
6423bc6d790SSaravanan Sekar 	}
6433bc6d790SSaravanan Sekar 
6443bc6d790SSaravanan Sekar 	regmap_update_bits(charger->regmap, MP2629_REG_INTERRUPT,
6453bc6d790SSaravanan Sekar 				GENMASK(6, 5), BIT(6) | BIT(5));
6463bc6d790SSaravanan Sekar 
6473bc6d790SSaravanan Sekar 	return 0;
6483bc6d790SSaravanan Sekar }
6493bc6d790SSaravanan Sekar 
6503bc6d790SSaravanan Sekar static const struct of_device_id mp2629_charger_of_match[] = {
6513bc6d790SSaravanan Sekar 	{ .compatible = "mps,mp2629_charger"},
6523bc6d790SSaravanan Sekar 	{}
6533bc6d790SSaravanan Sekar };
6543bc6d790SSaravanan Sekar MODULE_DEVICE_TABLE(of, mp2629_charger_of_match);
6553bc6d790SSaravanan Sekar 
6563bc6d790SSaravanan Sekar static struct platform_driver mp2629_charger_driver = {
6573bc6d790SSaravanan Sekar 	.driver = {
6583bc6d790SSaravanan Sekar 		.name = "mp2629_charger",
6593bc6d790SSaravanan Sekar 		.of_match_table = mp2629_charger_of_match,
6603bc6d790SSaravanan Sekar 	},
6613bc6d790SSaravanan Sekar 	.probe		= mp2629_charger_probe,
6623bc6d790SSaravanan Sekar };
6633bc6d790SSaravanan Sekar module_platform_driver(mp2629_charger_driver);
6643bc6d790SSaravanan Sekar 
6653bc6d790SSaravanan Sekar MODULE_AUTHOR("Saravanan Sekar <sravanhome@gmail.com>");
6663bc6d790SSaravanan Sekar MODULE_DESCRIPTION("MP2629 Charger driver");
6673bc6d790SSaravanan Sekar MODULE_LICENSE("GPL");
668