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