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