18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * max8903_charger.c - Maxim 8903 USB/Adapter Charger Driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2011 Samsung Electronics
68c2ecf20Sopenharmony_ci * MyungJoo Ham <myungjoo.ham@samsung.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/gpio.h>
108c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/of.h>
138c2ecf20Sopenharmony_ci#include <linux/of_device.h>
148c2ecf20Sopenharmony_ci#include <linux/of_gpio.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/power_supply.h>
178c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
188c2ecf20Sopenharmony_ci#include <linux/power/max8903_charger.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistruct max8903_data {
218c2ecf20Sopenharmony_ci	struct max8903_pdata *pdata;
228c2ecf20Sopenharmony_ci	struct device *dev;
238c2ecf20Sopenharmony_ci	struct power_supply *psy;
248c2ecf20Sopenharmony_ci	struct power_supply_desc psy_desc;
258c2ecf20Sopenharmony_ci	bool fault;
268c2ecf20Sopenharmony_ci	bool usb_in;
278c2ecf20Sopenharmony_ci	bool ta_in;
288c2ecf20Sopenharmony_ci};
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic enum power_supply_property max8903_charger_props[] = {
318c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_STATUS, /* Charger status output */
328c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_ONLINE, /* External power source */
338c2ecf20Sopenharmony_ci	POWER_SUPPLY_PROP_HEALTH, /* Fault or OK */
348c2ecf20Sopenharmony_ci};
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic int max8903_get_property(struct power_supply *psy,
378c2ecf20Sopenharmony_ci		enum power_supply_property psp,
388c2ecf20Sopenharmony_ci		union power_supply_propval *val)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	struct max8903_data *data = power_supply_get_drvdata(psy);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	switch (psp) {
438c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_STATUS:
448c2ecf20Sopenharmony_ci		val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
458c2ecf20Sopenharmony_ci		if (gpio_is_valid(data->pdata->chg)) {
468c2ecf20Sopenharmony_ci			if (gpio_get_value(data->pdata->chg) == 0)
478c2ecf20Sopenharmony_ci				val->intval = POWER_SUPPLY_STATUS_CHARGING;
488c2ecf20Sopenharmony_ci			else if (data->usb_in || data->ta_in)
498c2ecf20Sopenharmony_ci				val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
508c2ecf20Sopenharmony_ci			else
518c2ecf20Sopenharmony_ci				val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
528c2ecf20Sopenharmony_ci		}
538c2ecf20Sopenharmony_ci		break;
548c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_ONLINE:
558c2ecf20Sopenharmony_ci		val->intval = 0;
568c2ecf20Sopenharmony_ci		if (data->usb_in || data->ta_in)
578c2ecf20Sopenharmony_ci			val->intval = 1;
588c2ecf20Sopenharmony_ci		break;
598c2ecf20Sopenharmony_ci	case POWER_SUPPLY_PROP_HEALTH:
608c2ecf20Sopenharmony_ci		val->intval = POWER_SUPPLY_HEALTH_GOOD;
618c2ecf20Sopenharmony_ci		if (data->fault)
628c2ecf20Sopenharmony_ci			val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
638c2ecf20Sopenharmony_ci		break;
648c2ecf20Sopenharmony_ci	default:
658c2ecf20Sopenharmony_ci		return -EINVAL;
668c2ecf20Sopenharmony_ci	}
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	return 0;
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic irqreturn_t max8903_dcin(int irq, void *_data)
728c2ecf20Sopenharmony_ci{
738c2ecf20Sopenharmony_ci	struct max8903_data *data = _data;
748c2ecf20Sopenharmony_ci	struct max8903_pdata *pdata = data->pdata;
758c2ecf20Sopenharmony_ci	bool ta_in;
768c2ecf20Sopenharmony_ci	enum power_supply_type old_type;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	ta_in = gpio_get_value(pdata->dok) ? false : true;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	if (ta_in == data->ta_in)
818c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	data->ta_in = ta_in;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	/* Set Current-Limit-Mode 1:DC 0:USB */
868c2ecf20Sopenharmony_ci	if (gpio_is_valid(pdata->dcm))
878c2ecf20Sopenharmony_ci		gpio_set_value(pdata->dcm, ta_in ? 1 : 0);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	/* Charger Enable / Disable (cen is negated) */
908c2ecf20Sopenharmony_ci	if (gpio_is_valid(pdata->cen))
918c2ecf20Sopenharmony_ci		gpio_set_value(pdata->cen, ta_in ? 0 :
928c2ecf20Sopenharmony_ci				(data->usb_in ? 0 : 1));
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	dev_dbg(data->dev, "TA(DC-IN) Charger %s.\n", ta_in ?
958c2ecf20Sopenharmony_ci			"Connected" : "Disconnected");
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	old_type = data->psy_desc.type;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	if (data->ta_in)
1008c2ecf20Sopenharmony_ci		data->psy_desc.type = POWER_SUPPLY_TYPE_MAINS;
1018c2ecf20Sopenharmony_ci	else if (data->usb_in)
1028c2ecf20Sopenharmony_ci		data->psy_desc.type = POWER_SUPPLY_TYPE_USB;
1038c2ecf20Sopenharmony_ci	else
1048c2ecf20Sopenharmony_ci		data->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	if (old_type != data->psy_desc.type)
1078c2ecf20Sopenharmony_ci		power_supply_changed(data->psy);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic irqreturn_t max8903_usbin(int irq, void *_data)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	struct max8903_data *data = _data;
1158c2ecf20Sopenharmony_ci	struct max8903_pdata *pdata = data->pdata;
1168c2ecf20Sopenharmony_ci	bool usb_in;
1178c2ecf20Sopenharmony_ci	enum power_supply_type old_type;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	usb_in = gpio_get_value(pdata->uok) ? false : true;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	if (usb_in == data->usb_in)
1228c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	data->usb_in = usb_in;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	/* Do not touch Current-Limit-Mode */
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	/* Charger Enable / Disable (cen is negated) */
1298c2ecf20Sopenharmony_ci	if (gpio_is_valid(pdata->cen))
1308c2ecf20Sopenharmony_ci		gpio_set_value(pdata->cen, usb_in ? 0 :
1318c2ecf20Sopenharmony_ci				(data->ta_in ? 0 : 1));
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	dev_dbg(data->dev, "USB Charger %s.\n", usb_in ?
1348c2ecf20Sopenharmony_ci			"Connected" : "Disconnected");
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	old_type = data->psy_desc.type;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	if (data->ta_in)
1398c2ecf20Sopenharmony_ci		data->psy_desc.type = POWER_SUPPLY_TYPE_MAINS;
1408c2ecf20Sopenharmony_ci	else if (data->usb_in)
1418c2ecf20Sopenharmony_ci		data->psy_desc.type = POWER_SUPPLY_TYPE_USB;
1428c2ecf20Sopenharmony_ci	else
1438c2ecf20Sopenharmony_ci		data->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	if (old_type != data->psy_desc.type)
1468c2ecf20Sopenharmony_ci		power_supply_changed(data->psy);
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic irqreturn_t max8903_fault(int irq, void *_data)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	struct max8903_data *data = _data;
1548c2ecf20Sopenharmony_ci	struct max8903_pdata *pdata = data->pdata;
1558c2ecf20Sopenharmony_ci	bool fault;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	fault = gpio_get_value(pdata->flt) ? false : true;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if (fault == data->fault)
1608c2ecf20Sopenharmony_ci		return IRQ_HANDLED;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	data->fault = fault;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	if (fault)
1658c2ecf20Sopenharmony_ci		dev_err(data->dev, "Charger suffers a fault and stops.\n");
1668c2ecf20Sopenharmony_ci	else
1678c2ecf20Sopenharmony_ci		dev_err(data->dev, "Charger recovered from a fault.\n");
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1708c2ecf20Sopenharmony_ci}
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cistatic struct max8903_pdata *max8903_parse_dt_data(struct device *dev)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	struct device_node *np = dev->of_node;
1758c2ecf20Sopenharmony_ci	struct max8903_pdata *pdata = NULL;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	if (!np)
1788c2ecf20Sopenharmony_ci		return NULL;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
1818c2ecf20Sopenharmony_ci	if (!pdata)
1828c2ecf20Sopenharmony_ci		return NULL;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	pdata->dc_valid = false;
1858c2ecf20Sopenharmony_ci	pdata->usb_valid = false;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	pdata->cen = of_get_named_gpio(np, "cen-gpios", 0);
1888c2ecf20Sopenharmony_ci	if (!gpio_is_valid(pdata->cen))
1898c2ecf20Sopenharmony_ci		pdata->cen = -EINVAL;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	pdata->chg = of_get_named_gpio(np, "chg-gpios", 0);
1928c2ecf20Sopenharmony_ci	if (!gpio_is_valid(pdata->chg))
1938c2ecf20Sopenharmony_ci		pdata->chg = -EINVAL;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	pdata->flt = of_get_named_gpio(np, "flt-gpios", 0);
1968c2ecf20Sopenharmony_ci	if (!gpio_is_valid(pdata->flt))
1978c2ecf20Sopenharmony_ci		pdata->flt = -EINVAL;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	pdata->usus = of_get_named_gpio(np, "usus-gpios", 0);
2008c2ecf20Sopenharmony_ci	if (!gpio_is_valid(pdata->usus))
2018c2ecf20Sopenharmony_ci		pdata->usus = -EINVAL;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	pdata->dcm = of_get_named_gpio(np, "dcm-gpios", 0);
2048c2ecf20Sopenharmony_ci	if (!gpio_is_valid(pdata->dcm))
2058c2ecf20Sopenharmony_ci		pdata->dcm = -EINVAL;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	pdata->dok = of_get_named_gpio(np, "dok-gpios", 0);
2088c2ecf20Sopenharmony_ci	if (!gpio_is_valid(pdata->dok))
2098c2ecf20Sopenharmony_ci		pdata->dok = -EINVAL;
2108c2ecf20Sopenharmony_ci	else
2118c2ecf20Sopenharmony_ci		pdata->dc_valid = true;
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	pdata->uok = of_get_named_gpio(np, "uok-gpios", 0);
2148c2ecf20Sopenharmony_ci	if (!gpio_is_valid(pdata->uok))
2158c2ecf20Sopenharmony_ci		pdata->uok = -EINVAL;
2168c2ecf20Sopenharmony_ci	else
2178c2ecf20Sopenharmony_ci		pdata->usb_valid = true;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	return pdata;
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_cistatic int max8903_setup_gpios(struct platform_device *pdev)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	struct max8903_data *data = platform_get_drvdata(pdev);
2258c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
2268c2ecf20Sopenharmony_ci	struct max8903_pdata *pdata = pdev->dev.platform_data;
2278c2ecf20Sopenharmony_ci	int ret = 0;
2288c2ecf20Sopenharmony_ci	int gpio;
2298c2ecf20Sopenharmony_ci	int ta_in = 0;
2308c2ecf20Sopenharmony_ci	int usb_in = 0;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	if (pdata->dc_valid) {
2338c2ecf20Sopenharmony_ci		if (gpio_is_valid(pdata->dok)) {
2348c2ecf20Sopenharmony_ci			ret = devm_gpio_request(dev, pdata->dok,
2358c2ecf20Sopenharmony_ci						data->psy_desc.name);
2368c2ecf20Sopenharmony_ci			if (ret) {
2378c2ecf20Sopenharmony_ci				dev_err(dev,
2388c2ecf20Sopenharmony_ci					"Failed GPIO request for dok: %d err %d\n",
2398c2ecf20Sopenharmony_ci					pdata->dok, ret);
2408c2ecf20Sopenharmony_ci				return ret;
2418c2ecf20Sopenharmony_ci			}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci			gpio = pdata->dok; /* PULL_UPed Interrupt */
2448c2ecf20Sopenharmony_ci			ta_in = gpio_get_value(gpio) ? 0 : 1;
2458c2ecf20Sopenharmony_ci		} else {
2468c2ecf20Sopenharmony_ci			dev_err(dev, "When DC is wired, DOK should be wired as well.\n");
2478c2ecf20Sopenharmony_ci			return -EINVAL;
2488c2ecf20Sopenharmony_ci		}
2498c2ecf20Sopenharmony_ci	}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	if (gpio_is_valid(pdata->dcm)) {
2528c2ecf20Sopenharmony_ci		ret = devm_gpio_request(dev, pdata->dcm, data->psy_desc.name);
2538c2ecf20Sopenharmony_ci		if (ret) {
2548c2ecf20Sopenharmony_ci			dev_err(dev,
2558c2ecf20Sopenharmony_ci				"Failed GPIO request for dcm: %d err %d\n",
2568c2ecf20Sopenharmony_ci				pdata->dcm, ret);
2578c2ecf20Sopenharmony_ci			return ret;
2588c2ecf20Sopenharmony_ci		}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci		gpio = pdata->dcm; /* Output */
2618c2ecf20Sopenharmony_ci		gpio_set_value(gpio, ta_in);
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	if (pdata->usb_valid) {
2658c2ecf20Sopenharmony_ci		if (gpio_is_valid(pdata->uok)) {
2668c2ecf20Sopenharmony_ci			ret = devm_gpio_request(dev, pdata->uok,
2678c2ecf20Sopenharmony_ci						data->psy_desc.name);
2688c2ecf20Sopenharmony_ci			if (ret) {
2698c2ecf20Sopenharmony_ci				dev_err(dev,
2708c2ecf20Sopenharmony_ci					"Failed GPIO request for uok: %d err %d\n",
2718c2ecf20Sopenharmony_ci					pdata->uok, ret);
2728c2ecf20Sopenharmony_ci				return ret;
2738c2ecf20Sopenharmony_ci			}
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci			gpio = pdata->uok;
2768c2ecf20Sopenharmony_ci			usb_in = gpio_get_value(gpio) ? 0 : 1;
2778c2ecf20Sopenharmony_ci		} else {
2788c2ecf20Sopenharmony_ci			dev_err(dev, "When USB is wired, UOK should be wired."
2798c2ecf20Sopenharmony_ci					"as well.\n");
2808c2ecf20Sopenharmony_ci			return -EINVAL;
2818c2ecf20Sopenharmony_ci		}
2828c2ecf20Sopenharmony_ci	}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	if (gpio_is_valid(pdata->cen)) {
2858c2ecf20Sopenharmony_ci		ret = devm_gpio_request(dev, pdata->cen, data->psy_desc.name);
2868c2ecf20Sopenharmony_ci		if (ret) {
2878c2ecf20Sopenharmony_ci			dev_err(dev,
2888c2ecf20Sopenharmony_ci				"Failed GPIO request for cen: %d err %d\n",
2898c2ecf20Sopenharmony_ci				pdata->cen, ret);
2908c2ecf20Sopenharmony_ci			return ret;
2918c2ecf20Sopenharmony_ci		}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci		gpio_set_value(pdata->cen, (ta_in || usb_in) ? 0 : 1);
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	if (gpio_is_valid(pdata->chg)) {
2978c2ecf20Sopenharmony_ci		ret = devm_gpio_request(dev, pdata->chg, data->psy_desc.name);
2988c2ecf20Sopenharmony_ci		if (ret) {
2998c2ecf20Sopenharmony_ci			dev_err(dev,
3008c2ecf20Sopenharmony_ci				"Failed GPIO request for chg: %d err %d\n",
3018c2ecf20Sopenharmony_ci				pdata->chg, ret);
3028c2ecf20Sopenharmony_ci			return ret;
3038c2ecf20Sopenharmony_ci		}
3048c2ecf20Sopenharmony_ci	}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	if (gpio_is_valid(pdata->flt)) {
3078c2ecf20Sopenharmony_ci		ret = devm_gpio_request(dev, pdata->flt, data->psy_desc.name);
3088c2ecf20Sopenharmony_ci		if (ret) {
3098c2ecf20Sopenharmony_ci			dev_err(dev,
3108c2ecf20Sopenharmony_ci				"Failed GPIO request for flt: %d err %d\n",
3118c2ecf20Sopenharmony_ci				pdata->flt, ret);
3128c2ecf20Sopenharmony_ci			return ret;
3138c2ecf20Sopenharmony_ci		}
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	if (gpio_is_valid(pdata->usus)) {
3178c2ecf20Sopenharmony_ci		ret = devm_gpio_request(dev, pdata->usus, data->psy_desc.name);
3188c2ecf20Sopenharmony_ci		if (ret) {
3198c2ecf20Sopenharmony_ci			dev_err(dev,
3208c2ecf20Sopenharmony_ci				"Failed GPIO request for usus: %d err %d\n",
3218c2ecf20Sopenharmony_ci				pdata->usus, ret);
3228c2ecf20Sopenharmony_ci			return ret;
3238c2ecf20Sopenharmony_ci		}
3248c2ecf20Sopenharmony_ci	}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	data->fault = false;
3278c2ecf20Sopenharmony_ci	data->ta_in = ta_in;
3288c2ecf20Sopenharmony_ci	data->usb_in = usb_in;
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	return 0;
3318c2ecf20Sopenharmony_ci}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_cistatic int max8903_probe(struct platform_device *pdev)
3348c2ecf20Sopenharmony_ci{
3358c2ecf20Sopenharmony_ci	struct max8903_data *data;
3368c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
3378c2ecf20Sopenharmony_ci	struct max8903_pdata *pdata = pdev->dev.platform_data;
3388c2ecf20Sopenharmony_ci	struct power_supply_config psy_cfg = {};
3398c2ecf20Sopenharmony_ci	int ret = 0;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	data = devm_kzalloc(dev, sizeof(struct max8903_data), GFP_KERNEL);
3428c2ecf20Sopenharmony_ci	if (!data)
3438c2ecf20Sopenharmony_ci		return -ENOMEM;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	if (IS_ENABLED(CONFIG_OF) && !pdata && dev->of_node)
3468c2ecf20Sopenharmony_ci		pdata = max8903_parse_dt_data(dev);
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	if (!pdata) {
3498c2ecf20Sopenharmony_ci		dev_err(dev, "No platform data.\n");
3508c2ecf20Sopenharmony_ci		return -EINVAL;
3518c2ecf20Sopenharmony_ci	}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	pdev->dev.platform_data = pdata;
3548c2ecf20Sopenharmony_ci	data->pdata = pdata;
3558c2ecf20Sopenharmony_ci	data->dev = dev;
3568c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, data);
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	if (pdata->dc_valid == false && pdata->usb_valid == false) {
3598c2ecf20Sopenharmony_ci		dev_err(dev, "No valid power sources.\n");
3608c2ecf20Sopenharmony_ci		return -EINVAL;
3618c2ecf20Sopenharmony_ci	}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	ret = max8903_setup_gpios(pdev);
3648c2ecf20Sopenharmony_ci	if (ret)
3658c2ecf20Sopenharmony_ci		return ret;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	data->psy_desc.name = "max8903_charger";
3688c2ecf20Sopenharmony_ci	data->psy_desc.type = (data->ta_in) ? POWER_SUPPLY_TYPE_MAINS :
3698c2ecf20Sopenharmony_ci			((data->usb_in) ? POWER_SUPPLY_TYPE_USB :
3708c2ecf20Sopenharmony_ci			 POWER_SUPPLY_TYPE_BATTERY);
3718c2ecf20Sopenharmony_ci	data->psy_desc.get_property = max8903_get_property;
3728c2ecf20Sopenharmony_ci	data->psy_desc.properties = max8903_charger_props;
3738c2ecf20Sopenharmony_ci	data->psy_desc.num_properties = ARRAY_SIZE(max8903_charger_props);
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	psy_cfg.of_node = dev->of_node;
3768c2ecf20Sopenharmony_ci	psy_cfg.drv_data = data;
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	data->psy = devm_power_supply_register(dev, &data->psy_desc, &psy_cfg);
3798c2ecf20Sopenharmony_ci	if (IS_ERR(data->psy)) {
3808c2ecf20Sopenharmony_ci		dev_err(dev, "failed: power supply register.\n");
3818c2ecf20Sopenharmony_ci		return PTR_ERR(data->psy);
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	if (pdata->dc_valid) {
3858c2ecf20Sopenharmony_ci		ret = devm_request_threaded_irq(dev, gpio_to_irq(pdata->dok),
3868c2ecf20Sopenharmony_ci					NULL, max8903_dcin,
3878c2ecf20Sopenharmony_ci					IRQF_TRIGGER_FALLING |
3888c2ecf20Sopenharmony_ci					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
3898c2ecf20Sopenharmony_ci					"MAX8903 DC IN", data);
3908c2ecf20Sopenharmony_ci		if (ret) {
3918c2ecf20Sopenharmony_ci			dev_err(dev, "Cannot request irq %d for DC (%d)\n",
3928c2ecf20Sopenharmony_ci					gpio_to_irq(pdata->dok), ret);
3938c2ecf20Sopenharmony_ci			return ret;
3948c2ecf20Sopenharmony_ci		}
3958c2ecf20Sopenharmony_ci	}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	if (pdata->usb_valid) {
3988c2ecf20Sopenharmony_ci		ret = devm_request_threaded_irq(dev, gpio_to_irq(pdata->uok),
3998c2ecf20Sopenharmony_ci					NULL, max8903_usbin,
4008c2ecf20Sopenharmony_ci					IRQF_TRIGGER_FALLING |
4018c2ecf20Sopenharmony_ci					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
4028c2ecf20Sopenharmony_ci					"MAX8903 USB IN", data);
4038c2ecf20Sopenharmony_ci		if (ret) {
4048c2ecf20Sopenharmony_ci			dev_err(dev, "Cannot request irq %d for USB (%d)\n",
4058c2ecf20Sopenharmony_ci					gpio_to_irq(pdata->uok), ret);
4068c2ecf20Sopenharmony_ci			return ret;
4078c2ecf20Sopenharmony_ci		}
4088c2ecf20Sopenharmony_ci	}
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	if (gpio_is_valid(pdata->flt)) {
4118c2ecf20Sopenharmony_ci		ret = devm_request_threaded_irq(dev, gpio_to_irq(pdata->flt),
4128c2ecf20Sopenharmony_ci					NULL, max8903_fault,
4138c2ecf20Sopenharmony_ci					IRQF_TRIGGER_FALLING |
4148c2ecf20Sopenharmony_ci					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
4158c2ecf20Sopenharmony_ci					"MAX8903 Fault", data);
4168c2ecf20Sopenharmony_ci		if (ret) {
4178c2ecf20Sopenharmony_ci			dev_err(dev, "Cannot request irq %d for Fault (%d)\n",
4188c2ecf20Sopenharmony_ci					gpio_to_irq(pdata->flt), ret);
4198c2ecf20Sopenharmony_ci			return ret;
4208c2ecf20Sopenharmony_ci		}
4218c2ecf20Sopenharmony_ci	}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	return 0;
4248c2ecf20Sopenharmony_ci}
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_cistatic const struct of_device_id max8903_match_ids[] = {
4278c2ecf20Sopenharmony_ci	{ .compatible = "maxim,max8903", },
4288c2ecf20Sopenharmony_ci	{ /* sentinel */ }
4298c2ecf20Sopenharmony_ci};
4308c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, max8903_match_ids);
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_cistatic struct platform_driver max8903_driver = {
4338c2ecf20Sopenharmony_ci	.probe	= max8903_probe,
4348c2ecf20Sopenharmony_ci	.driver = {
4358c2ecf20Sopenharmony_ci		.name	= "max8903-charger",
4368c2ecf20Sopenharmony_ci		.of_match_table = max8903_match_ids
4378c2ecf20Sopenharmony_ci	},
4388c2ecf20Sopenharmony_ci};
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_cimodule_platform_driver(max8903_driver);
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
4438c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MAX8903 Charger Driver");
4448c2ecf20Sopenharmony_ciMODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
4458c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:max8903-charger");
446