linux/drivers/power/supply/gpio-charger.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *  Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
   4 *  Driver for chargers which report their online status through a GPIO pin
   5 */
   6
   7#include <linux/device.h>
   8#include <linux/gpio.h> /* For legacy platform data */
   9#include <linux/init.h>
  10#include <linux/interrupt.h>
  11#include <linux/kernel.h>
  12#include <linux/module.h>
  13#include <linux/platform_device.h>
  14#include <linux/power_supply.h>
  15#include <linux/slab.h>
  16#include <linux/of.h>
  17#include <linux/gpio/consumer.h>
  18
  19#include <linux/power/gpio-charger.h>
  20
  21struct gpio_charger {
  22        unsigned int irq;
  23        unsigned int charge_status_irq;
  24        bool wakeup_enabled;
  25
  26        struct power_supply *charger;
  27        struct power_supply_desc charger_desc;
  28        struct gpio_desc *gpiod;
  29        struct gpio_desc *charge_status;
  30};
  31
  32static irqreturn_t gpio_charger_irq(int irq, void *devid)
  33{
  34        struct power_supply *charger = devid;
  35
  36        power_supply_changed(charger);
  37
  38        return IRQ_HANDLED;
  39}
  40
  41static inline struct gpio_charger *psy_to_gpio_charger(struct power_supply *psy)
  42{
  43        return power_supply_get_drvdata(psy);
  44}
  45
  46static int gpio_charger_get_property(struct power_supply *psy,
  47                enum power_supply_property psp, union power_supply_propval *val)
  48{
  49        struct gpio_charger *gpio_charger = psy_to_gpio_charger(psy);
  50
  51        switch (psp) {
  52        case POWER_SUPPLY_PROP_ONLINE:
  53                val->intval = gpiod_get_value_cansleep(gpio_charger->gpiod);
  54                break;
  55        case POWER_SUPPLY_PROP_STATUS:
  56                if (gpiod_get_value_cansleep(gpio_charger->charge_status))
  57                        val->intval = POWER_SUPPLY_STATUS_CHARGING;
  58                else
  59                        val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
  60                break;
  61        default:
  62                return -EINVAL;
  63        }
  64
  65        return 0;
  66}
  67
  68static enum power_supply_type gpio_charger_get_type(struct device *dev)
  69{
  70        const char *chargetype;
  71
  72        if (!device_property_read_string(dev, "charger-type", &chargetype)) {
  73                if (!strcmp("unknown", chargetype))
  74                        return POWER_SUPPLY_TYPE_UNKNOWN;
  75                if (!strcmp("battery", chargetype))
  76                        return POWER_SUPPLY_TYPE_BATTERY;
  77                if (!strcmp("ups", chargetype))
  78                        return POWER_SUPPLY_TYPE_UPS;
  79                if (!strcmp("mains", chargetype))
  80                        return POWER_SUPPLY_TYPE_MAINS;
  81                if (!strcmp("usb-sdp", chargetype))
  82                        return POWER_SUPPLY_TYPE_USB;
  83                if (!strcmp("usb-dcp", chargetype))
  84                        return POWER_SUPPLY_TYPE_USB;
  85                if (!strcmp("usb-cdp", chargetype))
  86                        return POWER_SUPPLY_TYPE_USB;
  87                if (!strcmp("usb-aca", chargetype))
  88                        return POWER_SUPPLY_TYPE_USB;
  89        }
  90        dev_warn(dev, "unknown charger type %s\n", chargetype);
  91
  92        return POWER_SUPPLY_TYPE_UNKNOWN;
  93}
  94
  95static int gpio_charger_get_irq(struct device *dev, void *dev_id,
  96                                struct gpio_desc *gpio)
  97{
  98        int ret, irq = gpiod_to_irq(gpio);
  99
 100        if (irq > 0) {
 101                ret = devm_request_any_context_irq(dev, irq, gpio_charger_irq,
 102                                                   IRQF_TRIGGER_RISING |
 103                                                   IRQF_TRIGGER_FALLING,
 104                                                   dev_name(dev),
 105                                                   dev_id);
 106                if (ret < 0) {
 107                        dev_warn(dev, "Failed to request irq: %d\n", ret);
 108                        irq = 0;
 109                }
 110        }
 111
 112        return irq;
 113}
 114
 115static enum power_supply_property gpio_charger_properties[] = {
 116        POWER_SUPPLY_PROP_ONLINE,
 117        POWER_SUPPLY_PROP_STATUS /* Must always be last in the array. */
 118};
 119
 120static int gpio_charger_probe(struct platform_device *pdev)
 121{
 122        struct device *dev = &pdev->dev;
 123        const struct gpio_charger_platform_data *pdata = dev->platform_data;
 124        struct power_supply_config psy_cfg = {};
 125        struct gpio_charger *gpio_charger;
 126        struct power_supply_desc *charger_desc;
 127        struct gpio_desc *charge_status;
 128        int charge_status_irq;
 129        unsigned long flags;
 130        int ret;
 131
 132        if (!pdata && !dev->of_node) {
 133                dev_err(dev, "No platform data\n");
 134                return -ENOENT;
 135        }
 136
 137        gpio_charger = devm_kzalloc(dev, sizeof(*gpio_charger), GFP_KERNEL);
 138        if (!gpio_charger)
 139                return -ENOMEM;
 140
 141        /*
 142         * This will fetch a GPIO descriptor from device tree, ACPI or
 143         * boardfile descriptor tables. It's good to try this first.
 144         */
 145        gpio_charger->gpiod = devm_gpiod_get(dev, NULL, GPIOD_IN);
 146
 147        /*
 148         * If this fails and we're not using device tree, try the
 149         * legacy platform data method.
 150         */
 151        if (IS_ERR(gpio_charger->gpiod) && !dev->of_node) {
 152                /* Non-DT: use legacy GPIO numbers */
 153                if (!gpio_is_valid(pdata->gpio)) {
 154                        dev_err(dev, "Invalid gpio pin in pdata\n");
 155                        return -EINVAL;
 156                }
 157                flags = GPIOF_IN;
 158                if (pdata->gpio_active_low)
 159                        flags |= GPIOF_ACTIVE_LOW;
 160                ret = devm_gpio_request_one(dev, pdata->gpio, flags,
 161                                            dev_name(dev));
 162                if (ret) {
 163                        dev_err(dev, "Failed to request gpio pin: %d\n", ret);
 164                        return ret;
 165                }
 166                /* Then convert this to gpiod for now */
 167                gpio_charger->gpiod = gpio_to_desc(pdata->gpio);
 168        } else if (IS_ERR(gpio_charger->gpiod)) {
 169                /* Just try again if this happens */
 170                if (PTR_ERR(gpio_charger->gpiod) == -EPROBE_DEFER)
 171                        return -EPROBE_DEFER;
 172                dev_err(dev, "error getting GPIO descriptor\n");
 173                return PTR_ERR(gpio_charger->gpiod);
 174        }
 175
 176        charge_status = devm_gpiod_get_optional(dev, "charge-status", GPIOD_IN);
 177        gpio_charger->charge_status = charge_status;
 178        if (IS_ERR(gpio_charger->charge_status))
 179                return PTR_ERR(gpio_charger->charge_status);
 180
 181        charger_desc = &gpio_charger->charger_desc;
 182        charger_desc->properties = gpio_charger_properties;
 183        charger_desc->num_properties = ARRAY_SIZE(gpio_charger_properties);
 184        /* Remove POWER_SUPPLY_PROP_STATUS from the supported properties. */
 185        if (!gpio_charger->charge_status)
 186                charger_desc->num_properties -= 1;
 187        charger_desc->get_property = gpio_charger_get_property;
 188
 189        psy_cfg.of_node = dev->of_node;
 190        psy_cfg.drv_data = gpio_charger;
 191
 192        if (pdata) {
 193                charger_desc->name = pdata->name;
 194                charger_desc->type = pdata->type;
 195                psy_cfg.supplied_to = pdata->supplied_to;
 196                psy_cfg.num_supplicants = pdata->num_supplicants;
 197        } else {
 198                charger_desc->name = dev->of_node->name;
 199                charger_desc->type = gpio_charger_get_type(dev);
 200        }
 201
 202        if (!charger_desc->name)
 203                charger_desc->name = pdev->name;
 204
 205        gpio_charger->charger = devm_power_supply_register(dev, charger_desc,
 206                                                           &psy_cfg);
 207        if (IS_ERR(gpio_charger->charger)) {
 208                ret = PTR_ERR(gpio_charger->charger);
 209                dev_err(dev, "Failed to register power supply: %d\n", ret);
 210                return ret;
 211        }
 212
 213        gpio_charger->irq = gpio_charger_get_irq(dev, gpio_charger->charger,
 214                                                 gpio_charger->gpiod);
 215
 216        charge_status_irq = gpio_charger_get_irq(dev, gpio_charger->charger,
 217                                                 gpio_charger->charge_status);
 218        gpio_charger->charge_status_irq = charge_status_irq;
 219
 220        platform_set_drvdata(pdev, gpio_charger);
 221
 222        device_init_wakeup(dev, 1);
 223
 224        return 0;
 225}
 226
 227#ifdef CONFIG_PM_SLEEP
 228static int gpio_charger_suspend(struct device *dev)
 229{
 230        struct gpio_charger *gpio_charger = dev_get_drvdata(dev);
 231
 232        if (device_may_wakeup(dev))
 233                gpio_charger->wakeup_enabled =
 234                        !enable_irq_wake(gpio_charger->irq);
 235
 236        return 0;
 237}
 238
 239static int gpio_charger_resume(struct device *dev)
 240{
 241        struct gpio_charger *gpio_charger = dev_get_drvdata(dev);
 242
 243        if (device_may_wakeup(dev) && gpio_charger->wakeup_enabled)
 244                disable_irq_wake(gpio_charger->irq);
 245        power_supply_changed(gpio_charger->charger);
 246
 247        return 0;
 248}
 249#endif
 250
 251static SIMPLE_DEV_PM_OPS(gpio_charger_pm_ops,
 252                gpio_charger_suspend, gpio_charger_resume);
 253
 254static const struct of_device_id gpio_charger_match[] = {
 255        { .compatible = "gpio-charger" },
 256        { }
 257};
 258MODULE_DEVICE_TABLE(of, gpio_charger_match);
 259
 260static struct platform_driver gpio_charger_driver = {
 261        .probe = gpio_charger_probe,
 262        .driver = {
 263                .name = "gpio-charger",
 264                .pm = &gpio_charger_pm_ops,
 265                .of_match_table = gpio_charger_match,
 266        },
 267};
 268
 269module_platform_driver(gpio_charger_driver);
 270
 271MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
 272MODULE_DESCRIPTION("Driver for chargers which report their online status through a GPIO");
 273MODULE_LICENSE("GPL");
 274MODULE_ALIAS("platform:gpio-charger");
 275