11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
28c0984e5SSebastian Reichel /*
38c0984e5SSebastian Reichel  * max8903_charger.c - Maxim 8903 USB/Adapter Charger Driver
48c0984e5SSebastian Reichel  *
58c0984e5SSebastian Reichel  * Copyright (C) 2011 Samsung Electronics
68c0984e5SSebastian Reichel  * MyungJoo Ham <myungjoo.ham@samsung.com>
78c0984e5SSebastian Reichel  */
88c0984e5SSebastian Reichel 
9*50da8d04SLinus Walleij #include <linux/gpio/consumer.h>
108c0984e5SSebastian Reichel #include <linux/interrupt.h>
118c0984e5SSebastian Reichel #include <linux/module.h>
128c0984e5SSebastian Reichel #include <linux/of.h>
138c0984e5SSebastian Reichel #include <linux/slab.h>
148c0984e5SSebastian Reichel #include <linux/power_supply.h>
158c0984e5SSebastian Reichel #include <linux/platform_device.h>
163188677dSLinus Walleij 
178c0984e5SSebastian Reichel struct max8903_data {
188c0984e5SSebastian Reichel 	struct device *dev;
198c0984e5SSebastian Reichel 	struct power_supply *psy;
208c0984e5SSebastian Reichel 	struct power_supply_desc psy_desc;
21*50da8d04SLinus Walleij 	/*
22*50da8d04SLinus Walleij 	 * GPIOs
23*50da8d04SLinus Walleij 	 * chg, flt, dcm and usus are optional.
24*50da8d04SLinus Walleij 	 * dok or uok must be present.
25*50da8d04SLinus Walleij 	 * If dok is present, cen must be present.
26*50da8d04SLinus Walleij 	 */
27*50da8d04SLinus Walleij 	struct gpio_desc *cen; /* Charger Enable input */
28*50da8d04SLinus Walleij 	struct gpio_desc *dok; /* DC (Adapter) Power OK output */
29*50da8d04SLinus Walleij 	struct gpio_desc *uok; /* USB Power OK output */
30*50da8d04SLinus Walleij 	struct gpio_desc *chg; /* Charger status output */
31*50da8d04SLinus Walleij 	struct gpio_desc *flt; /* Fault output */
32*50da8d04SLinus Walleij 	struct gpio_desc *dcm; /* Current-Limit Mode input (1: DC, 2: USB) */
33*50da8d04SLinus Walleij 	struct gpio_desc *usus; /* USB Suspend Input (1: suspended) */
348c0984e5SSebastian Reichel 	bool fault;
358c0984e5SSebastian Reichel 	bool usb_in;
368c0984e5SSebastian Reichel 	bool ta_in;
378c0984e5SSebastian Reichel };
388c0984e5SSebastian Reichel 
398c0984e5SSebastian Reichel static enum power_supply_property max8903_charger_props[] = {
408c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_STATUS, /* Charger status output */
418c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_ONLINE, /* External power source */
428c0984e5SSebastian Reichel 	POWER_SUPPLY_PROP_HEALTH, /* Fault or OK */
438c0984e5SSebastian Reichel };
448c0984e5SSebastian Reichel 
max8903_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)458c0984e5SSebastian Reichel static int max8903_get_property(struct power_supply *psy,
468c0984e5SSebastian Reichel 		enum power_supply_property psp,
478c0984e5SSebastian Reichel 		union power_supply_propval *val)
488c0984e5SSebastian Reichel {
498c0984e5SSebastian Reichel 	struct max8903_data *data = power_supply_get_drvdata(psy);
508c0984e5SSebastian Reichel 
518c0984e5SSebastian Reichel 	switch (psp) {
528c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_STATUS:
538c0984e5SSebastian Reichel 		val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
54*50da8d04SLinus Walleij 		if (data->chg) {
55*50da8d04SLinus Walleij 			if (gpiod_get_value(data->chg))
56*50da8d04SLinus Walleij 				/* CHG asserted */
578c0984e5SSebastian Reichel 				val->intval = POWER_SUPPLY_STATUS_CHARGING;
588c0984e5SSebastian Reichel 			else if (data->usb_in || data->ta_in)
598c0984e5SSebastian Reichel 				val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
608c0984e5SSebastian Reichel 			else
618c0984e5SSebastian Reichel 				val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
628c0984e5SSebastian Reichel 		}
638c0984e5SSebastian Reichel 		break;
648c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_ONLINE:
658c0984e5SSebastian Reichel 		val->intval = 0;
668c0984e5SSebastian Reichel 		if (data->usb_in || data->ta_in)
678c0984e5SSebastian Reichel 			val->intval = 1;
688c0984e5SSebastian Reichel 		break;
698c0984e5SSebastian Reichel 	case POWER_SUPPLY_PROP_HEALTH:
708c0984e5SSebastian Reichel 		val->intval = POWER_SUPPLY_HEALTH_GOOD;
718c0984e5SSebastian Reichel 		if (data->fault)
728c0984e5SSebastian Reichel 			val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
738c0984e5SSebastian Reichel 		break;
748c0984e5SSebastian Reichel 	default:
758c0984e5SSebastian Reichel 		return -EINVAL;
768c0984e5SSebastian Reichel 	}
778c0984e5SSebastian Reichel 
788c0984e5SSebastian Reichel 	return 0;
798c0984e5SSebastian Reichel }
808c0984e5SSebastian Reichel 
max8903_dcin(int irq,void * _data)818c0984e5SSebastian Reichel static irqreturn_t max8903_dcin(int irq, void *_data)
828c0984e5SSebastian Reichel {
838c0984e5SSebastian Reichel 	struct max8903_data *data = _data;
848c0984e5SSebastian Reichel 	bool ta_in;
858c0984e5SSebastian Reichel 	enum power_supply_type old_type;
868c0984e5SSebastian Reichel 
87*50da8d04SLinus Walleij 	/*
88*50da8d04SLinus Walleij 	 * This means the line is asserted.
89*50da8d04SLinus Walleij 	 *
90*50da8d04SLinus Walleij 	 * The signal is active low, but the inversion is handled in the GPIO
91*50da8d04SLinus Walleij 	 * library as the line should be flagged GPIO_ACTIVE_LOW in the device
92*50da8d04SLinus Walleij 	 * tree.
93*50da8d04SLinus Walleij 	 */
94*50da8d04SLinus Walleij 	ta_in = gpiod_get_value(data->dok);
958c0984e5SSebastian Reichel 
968c0984e5SSebastian Reichel 	if (ta_in == data->ta_in)
978c0984e5SSebastian Reichel 		return IRQ_HANDLED;
988c0984e5SSebastian Reichel 
998c0984e5SSebastian Reichel 	data->ta_in = ta_in;
1008c0984e5SSebastian Reichel 
1018c0984e5SSebastian Reichel 	/* Set Current-Limit-Mode 1:DC 0:USB */
102*50da8d04SLinus Walleij 	if (data->dcm)
103*50da8d04SLinus Walleij 		gpiod_set_value(data->dcm, ta_in);
1048c0984e5SSebastian Reichel 
105*50da8d04SLinus Walleij 	/* Charger Enable / Disable */
106*50da8d04SLinus Walleij 	if (data->cen) {
107*50da8d04SLinus Walleij 		int val;
108*50da8d04SLinus Walleij 
109*50da8d04SLinus Walleij 		if (ta_in)
110*50da8d04SLinus Walleij 			/* Certainly enable if DOK is asserted */
111*50da8d04SLinus Walleij 			val = 1;
112*50da8d04SLinus Walleij 		else if (data->usb_in)
113*50da8d04SLinus Walleij 			/* Enable if the USB charger is enabled */
114*50da8d04SLinus Walleij 			val = 1;
115*50da8d04SLinus Walleij 		else
116*50da8d04SLinus Walleij 			/* Else default-disable */
117*50da8d04SLinus Walleij 			val = 0;
118*50da8d04SLinus Walleij 
119*50da8d04SLinus Walleij 		gpiod_set_value(data->cen, val);
120*50da8d04SLinus Walleij 	}
1218c0984e5SSebastian Reichel 
1228c0984e5SSebastian Reichel 	dev_dbg(data->dev, "TA(DC-IN) Charger %s.\n", ta_in ?
1238c0984e5SSebastian Reichel 			"Connected" : "Disconnected");
1248c0984e5SSebastian Reichel 
1258c0984e5SSebastian Reichel 	old_type = data->psy_desc.type;
1268c0984e5SSebastian Reichel 
1278c0984e5SSebastian Reichel 	if (data->ta_in)
1288c0984e5SSebastian Reichel 		data->psy_desc.type = POWER_SUPPLY_TYPE_MAINS;
1298c0984e5SSebastian Reichel 	else if (data->usb_in)
1308c0984e5SSebastian Reichel 		data->psy_desc.type = POWER_SUPPLY_TYPE_USB;
1318c0984e5SSebastian Reichel 	else
1328c0984e5SSebastian Reichel 		data->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY;
1338c0984e5SSebastian Reichel 
1348c0984e5SSebastian Reichel 	if (old_type != data->psy_desc.type)
1358c0984e5SSebastian Reichel 		power_supply_changed(data->psy);
1368c0984e5SSebastian Reichel 
1378c0984e5SSebastian Reichel 	return IRQ_HANDLED;
1388c0984e5SSebastian Reichel }
1398c0984e5SSebastian Reichel 
max8903_usbin(int irq,void * _data)1408c0984e5SSebastian Reichel static irqreturn_t max8903_usbin(int irq, void *_data)
1418c0984e5SSebastian Reichel {
1428c0984e5SSebastian Reichel 	struct max8903_data *data = _data;
1438c0984e5SSebastian Reichel 	bool usb_in;
1448c0984e5SSebastian Reichel 	enum power_supply_type old_type;
1458c0984e5SSebastian Reichel 
146*50da8d04SLinus Walleij 	/*
147*50da8d04SLinus Walleij 	 * This means the line is asserted.
148*50da8d04SLinus Walleij 	 *
149*50da8d04SLinus Walleij 	 * The signal is active low, but the inversion is handled in the GPIO
150*50da8d04SLinus Walleij 	 * library as the line should be flagged GPIO_ACTIVE_LOW in the device
151*50da8d04SLinus Walleij 	 * tree.
152*50da8d04SLinus Walleij 	 */
153*50da8d04SLinus Walleij 	usb_in = gpiod_get_value(data->uok);
1548c0984e5SSebastian Reichel 
1558c0984e5SSebastian Reichel 	if (usb_in == data->usb_in)
1568c0984e5SSebastian Reichel 		return IRQ_HANDLED;
1578c0984e5SSebastian Reichel 
1588c0984e5SSebastian Reichel 	data->usb_in = usb_in;
1598c0984e5SSebastian Reichel 
1608c0984e5SSebastian Reichel 	/* Do not touch Current-Limit-Mode */
1618c0984e5SSebastian Reichel 
162*50da8d04SLinus Walleij 	/* Charger Enable / Disable */
163*50da8d04SLinus Walleij 	if (data->cen) {
164*50da8d04SLinus Walleij 		int val;
165*50da8d04SLinus Walleij 
166*50da8d04SLinus Walleij 		if (usb_in)
167*50da8d04SLinus Walleij 			/* Certainly enable if UOK is asserted */
168*50da8d04SLinus Walleij 			val = 1;
169*50da8d04SLinus Walleij 		else if (data->ta_in)
170*50da8d04SLinus Walleij 			/* Enable if the DC charger is enabled */
171*50da8d04SLinus Walleij 			val = 1;
172*50da8d04SLinus Walleij 		else
173*50da8d04SLinus Walleij 			/* Else default-disable */
174*50da8d04SLinus Walleij 			val = 0;
175*50da8d04SLinus Walleij 
176*50da8d04SLinus Walleij 		gpiod_set_value(data->cen, val);
177*50da8d04SLinus Walleij 	}
1788c0984e5SSebastian Reichel 
1798c0984e5SSebastian Reichel 	dev_dbg(data->dev, "USB Charger %s.\n", usb_in ?
1808c0984e5SSebastian Reichel 			"Connected" : "Disconnected");
1818c0984e5SSebastian Reichel 
1828c0984e5SSebastian Reichel 	old_type = data->psy_desc.type;
1838c0984e5SSebastian Reichel 
1848c0984e5SSebastian Reichel 	if (data->ta_in)
1858c0984e5SSebastian Reichel 		data->psy_desc.type = POWER_SUPPLY_TYPE_MAINS;
1868c0984e5SSebastian Reichel 	else if (data->usb_in)
1878c0984e5SSebastian Reichel 		data->psy_desc.type = POWER_SUPPLY_TYPE_USB;
1888c0984e5SSebastian Reichel 	else
1898c0984e5SSebastian Reichel 		data->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY;
1908c0984e5SSebastian Reichel 
1918c0984e5SSebastian Reichel 	if (old_type != data->psy_desc.type)
1928c0984e5SSebastian Reichel 		power_supply_changed(data->psy);
1938c0984e5SSebastian Reichel 
1948c0984e5SSebastian Reichel 	return IRQ_HANDLED;
1958c0984e5SSebastian Reichel }
1968c0984e5SSebastian Reichel 
max8903_fault(int irq,void * _data)1978c0984e5SSebastian Reichel static irqreturn_t max8903_fault(int irq, void *_data)
1988c0984e5SSebastian Reichel {
1998c0984e5SSebastian Reichel 	struct max8903_data *data = _data;
2008c0984e5SSebastian Reichel 	bool fault;
2018c0984e5SSebastian Reichel 
202*50da8d04SLinus Walleij 	/*
203*50da8d04SLinus Walleij 	 * This means the line is asserted.
204*50da8d04SLinus Walleij 	 *
205*50da8d04SLinus Walleij 	 * The signal is active low, but the inversion is handled in the GPIO
206*50da8d04SLinus Walleij 	 * library as the line should be flagged GPIO_ACTIVE_LOW in the device
207*50da8d04SLinus Walleij 	 * tree.
208*50da8d04SLinus Walleij 	 */
209*50da8d04SLinus Walleij 	fault = gpiod_get_value(data->flt);
2108c0984e5SSebastian Reichel 
2118c0984e5SSebastian Reichel 	if (fault == data->fault)
2128c0984e5SSebastian Reichel 		return IRQ_HANDLED;
2138c0984e5SSebastian Reichel 
2148c0984e5SSebastian Reichel 	data->fault = fault;
2158c0984e5SSebastian Reichel 
2168c0984e5SSebastian Reichel 	if (fault)
2178c0984e5SSebastian Reichel 		dev_err(data->dev, "Charger suffers a fault and stops.\n");
2188c0984e5SSebastian Reichel 	else
2198c0984e5SSebastian Reichel 		dev_err(data->dev, "Charger recovered from a fault.\n");
2208c0984e5SSebastian Reichel 
2218c0984e5SSebastian Reichel 	return IRQ_HANDLED;
2228c0984e5SSebastian Reichel }
2238c0984e5SSebastian Reichel 
max8903_setup_gpios(struct platform_device * pdev)2248c0984e5SSebastian Reichel static int max8903_setup_gpios(struct platform_device *pdev)
2258c0984e5SSebastian Reichel {
2268c0984e5SSebastian Reichel 	struct max8903_data *data = platform_get_drvdata(pdev);
2278c0984e5SSebastian Reichel 	struct device *dev = &pdev->dev;
228*50da8d04SLinus Walleij 	bool ta_in = false;
229*50da8d04SLinus Walleij 	bool usb_in = false;
230*50da8d04SLinus Walleij 	enum gpiod_flags flags;
2318c0984e5SSebastian Reichel 
232*50da8d04SLinus Walleij 	data->dok = devm_gpiod_get_optional(dev, "dok", GPIOD_IN);
233*50da8d04SLinus Walleij 	if (IS_ERR(data->dok))
234*50da8d04SLinus Walleij 		return dev_err_probe(dev, PTR_ERR(data->dok),
235*50da8d04SLinus Walleij 				     "failed to get DOK GPIO");
236*50da8d04SLinus Walleij 	if (data->dok) {
237*50da8d04SLinus Walleij 		gpiod_set_consumer_name(data->dok, data->psy_desc.name);
238*50da8d04SLinus Walleij 		/*
239*50da8d04SLinus Walleij 		 * The DC OK is pulled up to 1 and goes low when a charger
240*50da8d04SLinus Walleij 		 * is plugged in (active low) but in the device tree the
241*50da8d04SLinus Walleij 		 * line is marked as GPIO_ACTIVE_LOW so we get a 1 (asserted)
242*50da8d04SLinus Walleij 		 * here if the DC charger is plugged in.
243*50da8d04SLinus Walleij 		 */
244*50da8d04SLinus Walleij 		ta_in = gpiod_get_value(data->dok);
2458c0984e5SSebastian Reichel 	}
2468c0984e5SSebastian Reichel 
247*50da8d04SLinus Walleij 	data->uok = devm_gpiod_get_optional(dev, "uok", GPIOD_IN);
248*50da8d04SLinus Walleij 	if (IS_ERR(data->uok))
249*50da8d04SLinus Walleij 		return dev_err_probe(dev, PTR_ERR(data->uok),
250*50da8d04SLinus Walleij 				     "failed to get UOK GPIO");
251*50da8d04SLinus Walleij 	if (data->uok) {
252*50da8d04SLinus Walleij 		gpiod_set_consumer_name(data->uok, data->psy_desc.name);
253*50da8d04SLinus Walleij 		/*
254*50da8d04SLinus Walleij 		 * The USB OK is pulled up to 1 and goes low when a USB charger
255*50da8d04SLinus Walleij 		 * is plugged in (active low) but in the device tree the
256*50da8d04SLinus Walleij 		 * line is marked as GPIO_ACTIVE_LOW so we get a 1 (asserted)
257*50da8d04SLinus Walleij 		 * here if the USB charger is plugged in.
258*50da8d04SLinus Walleij 		 */
259*50da8d04SLinus Walleij 		usb_in = gpiod_get_value(data->uok);
260*50da8d04SLinus Walleij 	}
261*50da8d04SLinus Walleij 
262*50da8d04SLinus Walleij 	/* Either DC OK or USB OK must be provided */
263*50da8d04SLinus Walleij 	if (!data->dok && !data->uok) {
264*50da8d04SLinus Walleij 		dev_err(dev, "no valid power source\n");
2658c0984e5SSebastian Reichel 		return -EINVAL;
2668c0984e5SSebastian Reichel 	}
2678c0984e5SSebastian Reichel 
268*50da8d04SLinus Walleij 	/*
269*50da8d04SLinus Walleij 	 * If either charger is already connected at this point,
270*50da8d04SLinus Walleij 	 * assert the CEN line and enable charging from the start.
271*50da8d04SLinus Walleij 	 *
272*50da8d04SLinus Walleij 	 * The line is active low but also marked with GPIO_ACTIVE_LOW
273*50da8d04SLinus Walleij 	 * in the device tree, so when we assert the line with
274*50da8d04SLinus Walleij 	 * GPIOD_OUT_HIGH the line will be driven low.
275*50da8d04SLinus Walleij 	 */
276*50da8d04SLinus Walleij 	flags = (ta_in || usb_in) ? GPIOD_OUT_HIGH : GPIOD_OUT_LOW;
277*50da8d04SLinus Walleij 	/*
278*50da8d04SLinus Walleij 	 * If DC OK is provided, Charger Enable CEN is compulsory
279*50da8d04SLinus Walleij 	 * so this is not optional here.
280*50da8d04SLinus Walleij 	 */
281*50da8d04SLinus Walleij 	data->cen = devm_gpiod_get(dev, "cen", flags);
282*50da8d04SLinus Walleij 	if (IS_ERR(data->cen))
283*50da8d04SLinus Walleij 		return dev_err_probe(dev, PTR_ERR(data->cen),
284*50da8d04SLinus Walleij 				     "failed to get CEN GPIO");
285*50da8d04SLinus Walleij 	gpiod_set_consumer_name(data->cen, data->psy_desc.name);
2868c0984e5SSebastian Reichel 
287*50da8d04SLinus Walleij 	/*
288*50da8d04SLinus Walleij 	 * If the DC charger is connected, then select it.
289*50da8d04SLinus Walleij 	 *
290*50da8d04SLinus Walleij 	 * The DCM line should be marked GPIO_ACTIVE_HIGH in the
291*50da8d04SLinus Walleij 	 * device tree. Driving it high will enable the DC charger
292*50da8d04SLinus Walleij 	 * input over the USB charger input.
293*50da8d04SLinus Walleij 	 */
294*50da8d04SLinus Walleij 	flags = ta_in ? GPIOD_OUT_HIGH : GPIOD_OUT_LOW;
295*50da8d04SLinus Walleij 	data->dcm = devm_gpiod_get_optional(dev, "dcm", flags);
296*50da8d04SLinus Walleij 	if (IS_ERR(data->dcm))
297*50da8d04SLinus Walleij 		return dev_err_probe(dev, PTR_ERR(data->dcm),
298*50da8d04SLinus Walleij 				     "failed to get DCM GPIO");
299*50da8d04SLinus Walleij 	gpiod_set_consumer_name(data->dcm, data->psy_desc.name);
3008c0984e5SSebastian Reichel 
301*50da8d04SLinus Walleij 	data->chg = devm_gpiod_get_optional(dev, "chg", GPIOD_IN);
302*50da8d04SLinus Walleij 	if (IS_ERR(data->chg))
303*50da8d04SLinus Walleij 		return dev_err_probe(dev, PTR_ERR(data->chg),
304*50da8d04SLinus Walleij 				     "failed to get CHG GPIO");
305*50da8d04SLinus Walleij 	gpiod_set_consumer_name(data->chg, data->psy_desc.name);
3068c0984e5SSebastian Reichel 
307*50da8d04SLinus Walleij 	data->flt = devm_gpiod_get_optional(dev, "flt", GPIOD_IN);
308*50da8d04SLinus Walleij 	if (IS_ERR(data->flt))
309*50da8d04SLinus Walleij 		return dev_err_probe(dev, PTR_ERR(data->flt),
310*50da8d04SLinus Walleij 				     "failed to get FLT GPIO");
311*50da8d04SLinus Walleij 	gpiod_set_consumer_name(data->flt, data->psy_desc.name);
3128c0984e5SSebastian Reichel 
313*50da8d04SLinus Walleij 	data->usus = devm_gpiod_get_optional(dev, "usus", GPIOD_IN);
314*50da8d04SLinus Walleij 	if (IS_ERR(data->usus))
315*50da8d04SLinus Walleij 		return dev_err_probe(dev, PTR_ERR(data->usus),
316*50da8d04SLinus Walleij 				     "failed to get USUS GPIO");
317*50da8d04SLinus Walleij 	gpiod_set_consumer_name(data->usus, data->psy_desc.name);
3188c0984e5SSebastian Reichel 
3198c0984e5SSebastian Reichel 	data->fault = false;
3208c0984e5SSebastian Reichel 	data->ta_in = ta_in;
3218c0984e5SSebastian Reichel 	data->usb_in = usb_in;
3228c0984e5SSebastian Reichel 
3238c0984e5SSebastian Reichel 	return 0;
3248c0984e5SSebastian Reichel }
3258c0984e5SSebastian Reichel 
max8903_probe(struct platform_device * pdev)3268c0984e5SSebastian Reichel static int max8903_probe(struct platform_device *pdev)
3278c0984e5SSebastian Reichel {
3288c0984e5SSebastian Reichel 	struct max8903_data *data;
3298c0984e5SSebastian Reichel 	struct device *dev = &pdev->dev;
3308c0984e5SSebastian Reichel 	struct power_supply_config psy_cfg = {};
3318c0984e5SSebastian Reichel 	int ret = 0;
3328c0984e5SSebastian Reichel 
3338c0984e5SSebastian Reichel 	data = devm_kzalloc(dev, sizeof(struct max8903_data), GFP_KERNEL);
3348c0984e5SSebastian Reichel 	if (!data)
3358c0984e5SSebastian Reichel 		return -ENOMEM;
3368c0984e5SSebastian Reichel 
3378c0984e5SSebastian Reichel 	data->dev = dev;
3388c0984e5SSebastian Reichel 	platform_set_drvdata(pdev, data);
3398c0984e5SSebastian Reichel 
3408c0984e5SSebastian Reichel 	ret = max8903_setup_gpios(pdev);
3418c0984e5SSebastian Reichel 	if (ret)
3428c0984e5SSebastian Reichel 		return ret;
3438c0984e5SSebastian Reichel 
3448c0984e5SSebastian Reichel 	data->psy_desc.name = "max8903_charger";
3458c0984e5SSebastian Reichel 	data->psy_desc.type = (data->ta_in) ? POWER_SUPPLY_TYPE_MAINS :
3468c0984e5SSebastian Reichel 			((data->usb_in) ? POWER_SUPPLY_TYPE_USB :
3478c0984e5SSebastian Reichel 			 POWER_SUPPLY_TYPE_BATTERY);
3488c0984e5SSebastian Reichel 	data->psy_desc.get_property = max8903_get_property;
3498c0984e5SSebastian Reichel 	data->psy_desc.properties = max8903_charger_props;
3508c0984e5SSebastian Reichel 	data->psy_desc.num_properties = ARRAY_SIZE(max8903_charger_props);
3518c0984e5SSebastian Reichel 
3528c0984e5SSebastian Reichel 	psy_cfg.of_node = dev->of_node;
3538c0984e5SSebastian Reichel 	psy_cfg.drv_data = data;
3548c0984e5SSebastian Reichel 
3558c0984e5SSebastian Reichel 	data->psy = devm_power_supply_register(dev, &data->psy_desc, &psy_cfg);
3568c0984e5SSebastian Reichel 	if (IS_ERR(data->psy)) {
3578c0984e5SSebastian Reichel 		dev_err(dev, "failed: power supply register.\n");
3588c0984e5SSebastian Reichel 		return PTR_ERR(data->psy);
3598c0984e5SSebastian Reichel 	}
3608c0984e5SSebastian Reichel 
361*50da8d04SLinus Walleij 	if (data->dok) {
362*50da8d04SLinus Walleij 		ret = devm_request_threaded_irq(dev, gpiod_to_irq(data->dok),
3638c0984e5SSebastian Reichel 					NULL, max8903_dcin,
3648c0984e5SSebastian Reichel 					IRQF_TRIGGER_FALLING |
3658c0984e5SSebastian Reichel 					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
3668c0984e5SSebastian Reichel 					"MAX8903 DC IN", data);
3678c0984e5SSebastian Reichel 		if (ret) {
3688c0984e5SSebastian Reichel 			dev_err(dev, "Cannot request irq %d for DC (%d)\n",
369*50da8d04SLinus Walleij 					gpiod_to_irq(data->dok), ret);
3708c0984e5SSebastian Reichel 			return ret;
3718c0984e5SSebastian Reichel 		}
3728c0984e5SSebastian Reichel 	}
3738c0984e5SSebastian Reichel 
374*50da8d04SLinus Walleij 	if (data->uok) {
375*50da8d04SLinus Walleij 		ret = devm_request_threaded_irq(dev, gpiod_to_irq(data->uok),
3768c0984e5SSebastian Reichel 					NULL, max8903_usbin,
3778c0984e5SSebastian Reichel 					IRQF_TRIGGER_FALLING |
3788c0984e5SSebastian Reichel 					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
3798c0984e5SSebastian Reichel 					"MAX8903 USB IN", data);
3808c0984e5SSebastian Reichel 		if (ret) {
3818c0984e5SSebastian Reichel 			dev_err(dev, "Cannot request irq %d for USB (%d)\n",
382*50da8d04SLinus Walleij 					gpiod_to_irq(data->uok), ret);
3838c0984e5SSebastian Reichel 			return ret;
3848c0984e5SSebastian Reichel 		}
3858c0984e5SSebastian Reichel 	}
3868c0984e5SSebastian Reichel 
387*50da8d04SLinus Walleij 	if (data->flt) {
388*50da8d04SLinus Walleij 		ret = devm_request_threaded_irq(dev, gpiod_to_irq(data->flt),
3898c0984e5SSebastian Reichel 					NULL, max8903_fault,
3908c0984e5SSebastian Reichel 					IRQF_TRIGGER_FALLING |
3918c0984e5SSebastian Reichel 					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
3928c0984e5SSebastian Reichel 					"MAX8903 Fault", data);
3938c0984e5SSebastian Reichel 		if (ret) {
3948c0984e5SSebastian Reichel 			dev_err(dev, "Cannot request irq %d for Fault (%d)\n",
395*50da8d04SLinus Walleij 					gpiod_to_irq(data->flt), ret);
3968c0984e5SSebastian Reichel 			return ret;
3978c0984e5SSebastian Reichel 		}
3988c0984e5SSebastian Reichel 	}
3998c0984e5SSebastian Reichel 
4008c0984e5SSebastian Reichel 	return 0;
4018c0984e5SSebastian Reichel }
4028c0984e5SSebastian Reichel 
4038c0984e5SSebastian Reichel static const struct of_device_id max8903_match_ids[] = {
4048c0984e5SSebastian Reichel 	{ .compatible = "maxim,max8903", },
4058c0984e5SSebastian Reichel 	{ /* sentinel */ }
4068c0984e5SSebastian Reichel };
4078c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(of, max8903_match_ids);
4088c0984e5SSebastian Reichel 
4098c0984e5SSebastian Reichel static struct platform_driver max8903_driver = {
4108c0984e5SSebastian Reichel 	.probe	= max8903_probe,
4118c0984e5SSebastian Reichel 	.driver = {
4128c0984e5SSebastian Reichel 		.name	= "max8903-charger",
4138c0984e5SSebastian Reichel 		.of_match_table = max8903_match_ids
4148c0984e5SSebastian Reichel 	},
4158c0984e5SSebastian Reichel };
4168c0984e5SSebastian Reichel 
4178c0984e5SSebastian Reichel module_platform_driver(max8903_driver);
4188c0984e5SSebastian Reichel 
4198c0984e5SSebastian Reichel MODULE_LICENSE("GPL");
4208c0984e5SSebastian Reichel MODULE_DESCRIPTION("MAX8903 Charger Driver");
4218c0984e5SSebastian Reichel MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
4228c0984e5SSebastian Reichel MODULE_ALIAS("platform:max8903-charger");
423