162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * max8903_charger.c - Maxim 8903 USB/Adapter Charger Driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2011 Samsung Electronics 662306a36Sopenharmony_ci * MyungJoo Ham <myungjoo.ham@samsung.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1062306a36Sopenharmony_ci#include <linux/interrupt.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/of.h> 1362306a36Sopenharmony_ci#include <linux/slab.h> 1462306a36Sopenharmony_ci#include <linux/power_supply.h> 1562306a36Sopenharmony_ci#include <linux/platform_device.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistruct max8903_data { 1862306a36Sopenharmony_ci struct device *dev; 1962306a36Sopenharmony_ci struct power_supply *psy; 2062306a36Sopenharmony_ci struct power_supply_desc psy_desc; 2162306a36Sopenharmony_ci /* 2262306a36Sopenharmony_ci * GPIOs 2362306a36Sopenharmony_ci * chg, flt, dcm and usus are optional. 2462306a36Sopenharmony_ci * dok or uok must be present. 2562306a36Sopenharmony_ci * If dok is present, cen must be present. 2662306a36Sopenharmony_ci */ 2762306a36Sopenharmony_ci struct gpio_desc *cen; /* Charger Enable input */ 2862306a36Sopenharmony_ci struct gpio_desc *dok; /* DC (Adapter) Power OK output */ 2962306a36Sopenharmony_ci struct gpio_desc *uok; /* USB Power OK output */ 3062306a36Sopenharmony_ci struct gpio_desc *chg; /* Charger status output */ 3162306a36Sopenharmony_ci struct gpio_desc *flt; /* Fault output */ 3262306a36Sopenharmony_ci struct gpio_desc *dcm; /* Current-Limit Mode input (1: DC, 2: USB) */ 3362306a36Sopenharmony_ci struct gpio_desc *usus; /* USB Suspend Input (1: suspended) */ 3462306a36Sopenharmony_ci bool fault; 3562306a36Sopenharmony_ci bool usb_in; 3662306a36Sopenharmony_ci bool ta_in; 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic enum power_supply_property max8903_charger_props[] = { 4062306a36Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, /* Charger status output */ 4162306a36Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, /* External power source */ 4262306a36Sopenharmony_ci POWER_SUPPLY_PROP_HEALTH, /* Fault or OK */ 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic int max8903_get_property(struct power_supply *psy, 4662306a36Sopenharmony_ci enum power_supply_property psp, 4762306a36Sopenharmony_ci union power_supply_propval *val) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct max8903_data *data = power_supply_get_drvdata(psy); 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci switch (psp) { 5262306a36Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 5362306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_UNKNOWN; 5462306a36Sopenharmony_ci if (data->chg) { 5562306a36Sopenharmony_ci if (gpiod_get_value(data->chg)) 5662306a36Sopenharmony_ci /* CHG asserted */ 5762306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_CHARGING; 5862306a36Sopenharmony_ci else if (data->usb_in || data->ta_in) 5962306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 6062306a36Sopenharmony_ci else 6162306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci break; 6462306a36Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 6562306a36Sopenharmony_ci val->intval = 0; 6662306a36Sopenharmony_ci if (data->usb_in || data->ta_in) 6762306a36Sopenharmony_ci val->intval = 1; 6862306a36Sopenharmony_ci break; 6962306a36Sopenharmony_ci case POWER_SUPPLY_PROP_HEALTH: 7062306a36Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_GOOD; 7162306a36Sopenharmony_ci if (data->fault) 7262306a36Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; 7362306a36Sopenharmony_ci break; 7462306a36Sopenharmony_ci default: 7562306a36Sopenharmony_ci return -EINVAL; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci return 0; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic irqreturn_t max8903_dcin(int irq, void *_data) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci struct max8903_data *data = _data; 8462306a36Sopenharmony_ci bool ta_in; 8562306a36Sopenharmony_ci enum power_supply_type old_type; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci /* 8862306a36Sopenharmony_ci * This means the line is asserted. 8962306a36Sopenharmony_ci * 9062306a36Sopenharmony_ci * The signal is active low, but the inversion is handled in the GPIO 9162306a36Sopenharmony_ci * library as the line should be flagged GPIO_ACTIVE_LOW in the device 9262306a36Sopenharmony_ci * tree. 9362306a36Sopenharmony_ci */ 9462306a36Sopenharmony_ci ta_in = gpiod_get_value(data->dok); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci if (ta_in == data->ta_in) 9762306a36Sopenharmony_ci return IRQ_HANDLED; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci data->ta_in = ta_in; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci /* Set Current-Limit-Mode 1:DC 0:USB */ 10262306a36Sopenharmony_ci if (data->dcm) 10362306a36Sopenharmony_ci gpiod_set_value(data->dcm, ta_in); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci /* Charger Enable / Disable */ 10662306a36Sopenharmony_ci if (data->cen) { 10762306a36Sopenharmony_ci int val; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (ta_in) 11062306a36Sopenharmony_ci /* Certainly enable if DOK is asserted */ 11162306a36Sopenharmony_ci val = 1; 11262306a36Sopenharmony_ci else if (data->usb_in) 11362306a36Sopenharmony_ci /* Enable if the USB charger is enabled */ 11462306a36Sopenharmony_ci val = 1; 11562306a36Sopenharmony_ci else 11662306a36Sopenharmony_ci /* Else default-disable */ 11762306a36Sopenharmony_ci val = 0; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci gpiod_set_value(data->cen, val); 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci dev_dbg(data->dev, "TA(DC-IN) Charger %s.\n", ta_in ? 12362306a36Sopenharmony_ci "Connected" : "Disconnected"); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci old_type = data->psy_desc.type; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (data->ta_in) 12862306a36Sopenharmony_ci data->psy_desc.type = POWER_SUPPLY_TYPE_MAINS; 12962306a36Sopenharmony_ci else if (data->usb_in) 13062306a36Sopenharmony_ci data->psy_desc.type = POWER_SUPPLY_TYPE_USB; 13162306a36Sopenharmony_ci else 13262306a36Sopenharmony_ci data->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci if (old_type != data->psy_desc.type) 13562306a36Sopenharmony_ci power_supply_changed(data->psy); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci return IRQ_HANDLED; 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic irqreturn_t max8903_usbin(int irq, void *_data) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci struct max8903_data *data = _data; 14362306a36Sopenharmony_ci bool usb_in; 14462306a36Sopenharmony_ci enum power_supply_type old_type; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* 14762306a36Sopenharmony_ci * This means the line is asserted. 14862306a36Sopenharmony_ci * 14962306a36Sopenharmony_ci * The signal is active low, but the inversion is handled in the GPIO 15062306a36Sopenharmony_ci * library as the line should be flagged GPIO_ACTIVE_LOW in the device 15162306a36Sopenharmony_ci * tree. 15262306a36Sopenharmony_ci */ 15362306a36Sopenharmony_ci usb_in = gpiod_get_value(data->uok); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci if (usb_in == data->usb_in) 15662306a36Sopenharmony_ci return IRQ_HANDLED; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci data->usb_in = usb_in; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* Do not touch Current-Limit-Mode */ 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* Charger Enable / Disable */ 16362306a36Sopenharmony_ci if (data->cen) { 16462306a36Sopenharmony_ci int val; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (usb_in) 16762306a36Sopenharmony_ci /* Certainly enable if UOK is asserted */ 16862306a36Sopenharmony_ci val = 1; 16962306a36Sopenharmony_ci else if (data->ta_in) 17062306a36Sopenharmony_ci /* Enable if the DC charger is enabled */ 17162306a36Sopenharmony_ci val = 1; 17262306a36Sopenharmony_ci else 17362306a36Sopenharmony_ci /* Else default-disable */ 17462306a36Sopenharmony_ci val = 0; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci gpiod_set_value(data->cen, val); 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci dev_dbg(data->dev, "USB Charger %s.\n", usb_in ? 18062306a36Sopenharmony_ci "Connected" : "Disconnected"); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci old_type = data->psy_desc.type; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (data->ta_in) 18562306a36Sopenharmony_ci data->psy_desc.type = POWER_SUPPLY_TYPE_MAINS; 18662306a36Sopenharmony_ci else if (data->usb_in) 18762306a36Sopenharmony_ci data->psy_desc.type = POWER_SUPPLY_TYPE_USB; 18862306a36Sopenharmony_ci else 18962306a36Sopenharmony_ci data->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci if (old_type != data->psy_desc.type) 19262306a36Sopenharmony_ci power_supply_changed(data->psy); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci return IRQ_HANDLED; 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic irqreturn_t max8903_fault(int irq, void *_data) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci struct max8903_data *data = _data; 20062306a36Sopenharmony_ci bool fault; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* 20362306a36Sopenharmony_ci * This means the line is asserted. 20462306a36Sopenharmony_ci * 20562306a36Sopenharmony_ci * The signal is active low, but the inversion is handled in the GPIO 20662306a36Sopenharmony_ci * library as the line should be flagged GPIO_ACTIVE_LOW in the device 20762306a36Sopenharmony_ci * tree. 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci fault = gpiod_get_value(data->flt); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (fault == data->fault) 21262306a36Sopenharmony_ci return IRQ_HANDLED; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci data->fault = fault; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci if (fault) 21762306a36Sopenharmony_ci dev_err(data->dev, "Charger suffers a fault and stops.\n"); 21862306a36Sopenharmony_ci else 21962306a36Sopenharmony_ci dev_err(data->dev, "Charger recovered from a fault.\n"); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci return IRQ_HANDLED; 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cistatic int max8903_setup_gpios(struct platform_device *pdev) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci struct max8903_data *data = platform_get_drvdata(pdev); 22762306a36Sopenharmony_ci struct device *dev = &pdev->dev; 22862306a36Sopenharmony_ci bool ta_in = false; 22962306a36Sopenharmony_ci bool usb_in = false; 23062306a36Sopenharmony_ci enum gpiod_flags flags; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci data->dok = devm_gpiod_get_optional(dev, "dok", GPIOD_IN); 23362306a36Sopenharmony_ci if (IS_ERR(data->dok)) 23462306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(data->dok), 23562306a36Sopenharmony_ci "failed to get DOK GPIO"); 23662306a36Sopenharmony_ci if (data->dok) { 23762306a36Sopenharmony_ci gpiod_set_consumer_name(data->dok, data->psy_desc.name); 23862306a36Sopenharmony_ci /* 23962306a36Sopenharmony_ci * The DC OK is pulled up to 1 and goes low when a charger 24062306a36Sopenharmony_ci * is plugged in (active low) but in the device tree the 24162306a36Sopenharmony_ci * line is marked as GPIO_ACTIVE_LOW so we get a 1 (asserted) 24262306a36Sopenharmony_ci * here if the DC charger is plugged in. 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_ci ta_in = gpiod_get_value(data->dok); 24562306a36Sopenharmony_ci } 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci data->uok = devm_gpiod_get_optional(dev, "uok", GPIOD_IN); 24862306a36Sopenharmony_ci if (IS_ERR(data->uok)) 24962306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(data->uok), 25062306a36Sopenharmony_ci "failed to get UOK GPIO"); 25162306a36Sopenharmony_ci if (data->uok) { 25262306a36Sopenharmony_ci gpiod_set_consumer_name(data->uok, data->psy_desc.name); 25362306a36Sopenharmony_ci /* 25462306a36Sopenharmony_ci * The USB OK is pulled up to 1 and goes low when a USB charger 25562306a36Sopenharmony_ci * is plugged in (active low) but in the device tree the 25662306a36Sopenharmony_ci * line is marked as GPIO_ACTIVE_LOW so we get a 1 (asserted) 25762306a36Sopenharmony_ci * here if the USB charger is plugged in. 25862306a36Sopenharmony_ci */ 25962306a36Sopenharmony_ci usb_in = gpiod_get_value(data->uok); 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci /* Either DC OK or USB OK must be provided */ 26362306a36Sopenharmony_ci if (!data->dok && !data->uok) { 26462306a36Sopenharmony_ci dev_err(dev, "no valid power source\n"); 26562306a36Sopenharmony_ci return -EINVAL; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* 26962306a36Sopenharmony_ci * If either charger is already connected at this point, 27062306a36Sopenharmony_ci * assert the CEN line and enable charging from the start. 27162306a36Sopenharmony_ci * 27262306a36Sopenharmony_ci * The line is active low but also marked with GPIO_ACTIVE_LOW 27362306a36Sopenharmony_ci * in the device tree, so when we assert the line with 27462306a36Sopenharmony_ci * GPIOD_OUT_HIGH the line will be driven low. 27562306a36Sopenharmony_ci */ 27662306a36Sopenharmony_ci flags = (ta_in || usb_in) ? GPIOD_OUT_HIGH : GPIOD_OUT_LOW; 27762306a36Sopenharmony_ci /* 27862306a36Sopenharmony_ci * If DC OK is provided, Charger Enable CEN is compulsory 27962306a36Sopenharmony_ci * so this is not optional here. 28062306a36Sopenharmony_ci */ 28162306a36Sopenharmony_ci data->cen = devm_gpiod_get(dev, "cen", flags); 28262306a36Sopenharmony_ci if (IS_ERR(data->cen)) 28362306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(data->cen), 28462306a36Sopenharmony_ci "failed to get CEN GPIO"); 28562306a36Sopenharmony_ci gpiod_set_consumer_name(data->cen, data->psy_desc.name); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci /* 28862306a36Sopenharmony_ci * If the DC charger is connected, then select it. 28962306a36Sopenharmony_ci * 29062306a36Sopenharmony_ci * The DCM line should be marked GPIO_ACTIVE_HIGH in the 29162306a36Sopenharmony_ci * device tree. Driving it high will enable the DC charger 29262306a36Sopenharmony_ci * input over the USB charger input. 29362306a36Sopenharmony_ci */ 29462306a36Sopenharmony_ci flags = ta_in ? GPIOD_OUT_HIGH : GPIOD_OUT_LOW; 29562306a36Sopenharmony_ci data->dcm = devm_gpiod_get_optional(dev, "dcm", flags); 29662306a36Sopenharmony_ci if (IS_ERR(data->dcm)) 29762306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(data->dcm), 29862306a36Sopenharmony_ci "failed to get DCM GPIO"); 29962306a36Sopenharmony_ci gpiod_set_consumer_name(data->dcm, data->psy_desc.name); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci data->chg = devm_gpiod_get_optional(dev, "chg", GPIOD_IN); 30262306a36Sopenharmony_ci if (IS_ERR(data->chg)) 30362306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(data->chg), 30462306a36Sopenharmony_ci "failed to get CHG GPIO"); 30562306a36Sopenharmony_ci gpiod_set_consumer_name(data->chg, data->psy_desc.name); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci data->flt = devm_gpiod_get_optional(dev, "flt", GPIOD_IN); 30862306a36Sopenharmony_ci if (IS_ERR(data->flt)) 30962306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(data->flt), 31062306a36Sopenharmony_ci "failed to get FLT GPIO"); 31162306a36Sopenharmony_ci gpiod_set_consumer_name(data->flt, data->psy_desc.name); 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci data->usus = devm_gpiod_get_optional(dev, "usus", GPIOD_IN); 31462306a36Sopenharmony_ci if (IS_ERR(data->usus)) 31562306a36Sopenharmony_ci return dev_err_probe(dev, PTR_ERR(data->usus), 31662306a36Sopenharmony_ci "failed to get USUS GPIO"); 31762306a36Sopenharmony_ci gpiod_set_consumer_name(data->usus, data->psy_desc.name); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci data->fault = false; 32062306a36Sopenharmony_ci data->ta_in = ta_in; 32162306a36Sopenharmony_ci data->usb_in = usb_in; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci return 0; 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic int max8903_probe(struct platform_device *pdev) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci struct max8903_data *data; 32962306a36Sopenharmony_ci struct device *dev = &pdev->dev; 33062306a36Sopenharmony_ci struct power_supply_config psy_cfg = {}; 33162306a36Sopenharmony_ci int ret = 0; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci data = devm_kzalloc(dev, sizeof(struct max8903_data), GFP_KERNEL); 33462306a36Sopenharmony_ci if (!data) 33562306a36Sopenharmony_ci return -ENOMEM; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci data->dev = dev; 33862306a36Sopenharmony_ci platform_set_drvdata(pdev, data); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci ret = max8903_setup_gpios(pdev); 34162306a36Sopenharmony_ci if (ret) 34262306a36Sopenharmony_ci return ret; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci data->psy_desc.name = "max8903_charger"; 34562306a36Sopenharmony_ci data->psy_desc.type = (data->ta_in) ? POWER_SUPPLY_TYPE_MAINS : 34662306a36Sopenharmony_ci ((data->usb_in) ? POWER_SUPPLY_TYPE_USB : 34762306a36Sopenharmony_ci POWER_SUPPLY_TYPE_BATTERY); 34862306a36Sopenharmony_ci data->psy_desc.get_property = max8903_get_property; 34962306a36Sopenharmony_ci data->psy_desc.properties = max8903_charger_props; 35062306a36Sopenharmony_ci data->psy_desc.num_properties = ARRAY_SIZE(max8903_charger_props); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci psy_cfg.of_node = dev->of_node; 35362306a36Sopenharmony_ci psy_cfg.drv_data = data; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci data->psy = devm_power_supply_register(dev, &data->psy_desc, &psy_cfg); 35662306a36Sopenharmony_ci if (IS_ERR(data->psy)) { 35762306a36Sopenharmony_ci dev_err(dev, "failed: power supply register.\n"); 35862306a36Sopenharmony_ci return PTR_ERR(data->psy); 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (data->dok) { 36262306a36Sopenharmony_ci ret = devm_request_threaded_irq(dev, gpiod_to_irq(data->dok), 36362306a36Sopenharmony_ci NULL, max8903_dcin, 36462306a36Sopenharmony_ci IRQF_TRIGGER_FALLING | 36562306a36Sopenharmony_ci IRQF_TRIGGER_RISING | IRQF_ONESHOT, 36662306a36Sopenharmony_ci "MAX8903 DC IN", data); 36762306a36Sopenharmony_ci if (ret) { 36862306a36Sopenharmony_ci dev_err(dev, "Cannot request irq %d for DC (%d)\n", 36962306a36Sopenharmony_ci gpiod_to_irq(data->dok), ret); 37062306a36Sopenharmony_ci return ret; 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci if (data->uok) { 37562306a36Sopenharmony_ci ret = devm_request_threaded_irq(dev, gpiod_to_irq(data->uok), 37662306a36Sopenharmony_ci NULL, max8903_usbin, 37762306a36Sopenharmony_ci IRQF_TRIGGER_FALLING | 37862306a36Sopenharmony_ci IRQF_TRIGGER_RISING | IRQF_ONESHOT, 37962306a36Sopenharmony_ci "MAX8903 USB IN", data); 38062306a36Sopenharmony_ci if (ret) { 38162306a36Sopenharmony_ci dev_err(dev, "Cannot request irq %d for USB (%d)\n", 38262306a36Sopenharmony_ci gpiod_to_irq(data->uok), ret); 38362306a36Sopenharmony_ci return ret; 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci if (data->flt) { 38862306a36Sopenharmony_ci ret = devm_request_threaded_irq(dev, gpiod_to_irq(data->flt), 38962306a36Sopenharmony_ci NULL, max8903_fault, 39062306a36Sopenharmony_ci IRQF_TRIGGER_FALLING | 39162306a36Sopenharmony_ci IRQF_TRIGGER_RISING | IRQF_ONESHOT, 39262306a36Sopenharmony_ci "MAX8903 Fault", data); 39362306a36Sopenharmony_ci if (ret) { 39462306a36Sopenharmony_ci dev_err(dev, "Cannot request irq %d for Fault (%d)\n", 39562306a36Sopenharmony_ci gpiod_to_irq(data->flt), ret); 39662306a36Sopenharmony_ci return ret; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci return 0; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic const struct of_device_id max8903_match_ids[] = { 40462306a36Sopenharmony_ci { .compatible = "maxim,max8903", }, 40562306a36Sopenharmony_ci { /* sentinel */ } 40662306a36Sopenharmony_ci}; 40762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, max8903_match_ids); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic struct platform_driver max8903_driver = { 41062306a36Sopenharmony_ci .probe = max8903_probe, 41162306a36Sopenharmony_ci .driver = { 41262306a36Sopenharmony_ci .name = "max8903-charger", 41362306a36Sopenharmony_ci .of_match_table = max8903_match_ids 41462306a36Sopenharmony_ci }, 41562306a36Sopenharmony_ci}; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cimodule_platform_driver(max8903_driver); 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 42062306a36Sopenharmony_ciMODULE_DESCRIPTION("MAX8903 Charger Driver"); 42162306a36Sopenharmony_ciMODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); 42262306a36Sopenharmony_ciMODULE_ALIAS("platform:max8903-charger"); 423