linux/drivers/power/supply/ltc3651-charger.c
<<
>>
Prefs
   1/*
   2 *  Copyright (C) 2017, Topic Embedded Products
   3 *  Driver for LTC3651 charger IC.
   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
  11#include <linux/device.h>
  12#include <linux/gpio/consumer.h>
  13#include <linux/init.h>
  14#include <linux/interrupt.h>
  15#include <linux/kernel.h>
  16#include <linux/module.h>
  17#include <linux/platform_device.h>
  18#include <linux/power_supply.h>
  19#include <linux/slab.h>
  20#include <linux/of.h>
  21
  22struct ltc3651_charger {
  23        struct power_supply *charger;
  24        struct power_supply_desc charger_desc;
  25        struct gpio_desc *acpr_gpio;
  26        struct gpio_desc *fault_gpio;
  27        struct gpio_desc *chrg_gpio;
  28};
  29
  30static irqreturn_t ltc3651_charger_irq(int irq, void *devid)
  31{
  32        struct power_supply *charger = devid;
  33
  34        power_supply_changed(charger);
  35
  36        return IRQ_HANDLED;
  37}
  38
  39static inline struct ltc3651_charger *psy_to_ltc3651_charger(
  40        struct power_supply *psy)
  41{
  42        return power_supply_get_drvdata(psy);
  43}
  44
  45static int ltc3651_charger_get_property(struct power_supply *psy,
  46                enum power_supply_property psp, union power_supply_propval *val)
  47{
  48        struct ltc3651_charger *ltc3651_charger = psy_to_ltc3651_charger(psy);
  49
  50        switch (psp) {
  51        case POWER_SUPPLY_PROP_STATUS:
  52                if (!ltc3651_charger->chrg_gpio) {
  53                        val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
  54                        break;
  55                }
  56                if (gpiod_get_value(ltc3651_charger->chrg_gpio))
  57                        val->intval = POWER_SUPPLY_STATUS_CHARGING;
  58                else
  59                        val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
  60                break;
  61        case POWER_SUPPLY_PROP_ONLINE:
  62                val->intval = gpiod_get_value(ltc3651_charger->acpr_gpio);
  63                break;
  64        case POWER_SUPPLY_PROP_HEALTH:
  65                if (!ltc3651_charger->fault_gpio) {
  66                        val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
  67                        break;
  68                }
  69                if (!gpiod_get_value(ltc3651_charger->fault_gpio)) {
  70                        val->intval = POWER_SUPPLY_HEALTH_GOOD;
  71                        break;
  72                }
  73                /*
  74                 * If the fault pin is active, the chrg pin explains the type
  75                 * of failure.
  76                 */
  77                if (!ltc3651_charger->chrg_gpio) {
  78                        val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
  79                        break;
  80                }
  81                val->intval = gpiod_get_value(ltc3651_charger->chrg_gpio) ?
  82                                POWER_SUPPLY_HEALTH_OVERHEAT :
  83                                POWER_SUPPLY_HEALTH_DEAD;
  84                break;
  85        default:
  86                return -EINVAL;
  87        }
  88
  89        return 0;
  90}
  91
  92static enum power_supply_property ltc3651_charger_properties[] = {
  93        POWER_SUPPLY_PROP_STATUS,
  94        POWER_SUPPLY_PROP_ONLINE,
  95        POWER_SUPPLY_PROP_HEALTH,
  96};
  97
  98static int ltc3651_charger_probe(struct platform_device *pdev)
  99{
 100        struct power_supply_config psy_cfg = {};
 101        struct ltc3651_charger *ltc3651_charger;
 102        struct power_supply_desc *charger_desc;
 103        int ret;
 104
 105        ltc3651_charger = devm_kzalloc(&pdev->dev, sizeof(*ltc3651_charger),
 106                                        GFP_KERNEL);
 107        if (!ltc3651_charger)
 108                return -ENOMEM;
 109
 110        ltc3651_charger->acpr_gpio = devm_gpiod_get(&pdev->dev,
 111                                        "lltc,acpr", GPIOD_IN);
 112        if (IS_ERR(ltc3651_charger->acpr_gpio)) {
 113                ret = PTR_ERR(ltc3651_charger->acpr_gpio);
 114                dev_err(&pdev->dev, "Failed to acquire acpr GPIO: %d\n", ret);
 115                return ret;
 116        }
 117        ltc3651_charger->fault_gpio = devm_gpiod_get_optional(&pdev->dev,
 118                                        "lltc,fault", GPIOD_IN);
 119        if (IS_ERR(ltc3651_charger->fault_gpio)) {
 120                ret = PTR_ERR(ltc3651_charger->fault_gpio);
 121                dev_err(&pdev->dev, "Failed to acquire fault GPIO: %d\n", ret);
 122                return ret;
 123        }
 124        ltc3651_charger->chrg_gpio = devm_gpiod_get_optional(&pdev->dev,
 125                                        "lltc,chrg", GPIOD_IN);
 126        if (IS_ERR(ltc3651_charger->chrg_gpio)) {
 127                ret = PTR_ERR(ltc3651_charger->chrg_gpio);
 128                dev_err(&pdev->dev, "Failed to acquire chrg GPIO: %d\n", ret);
 129                return ret;
 130        }
 131
 132        charger_desc = &ltc3651_charger->charger_desc;
 133        charger_desc->name = pdev->dev.of_node->name;
 134        charger_desc->type = POWER_SUPPLY_TYPE_MAINS;
 135        charger_desc->properties = ltc3651_charger_properties;
 136        charger_desc->num_properties = ARRAY_SIZE(ltc3651_charger_properties);
 137        charger_desc->get_property = ltc3651_charger_get_property;
 138        psy_cfg.of_node = pdev->dev.of_node;
 139        psy_cfg.drv_data = ltc3651_charger;
 140
 141        ltc3651_charger->charger = devm_power_supply_register(&pdev->dev,
 142                                                      charger_desc, &psy_cfg);
 143        if (IS_ERR(ltc3651_charger->charger)) {
 144                ret = PTR_ERR(ltc3651_charger->charger);
 145                dev_err(&pdev->dev, "Failed to register power supply: %d\n",
 146                        ret);
 147                return ret;
 148        }
 149
 150        /*
 151         * Acquire IRQs for the GPIO pins if possible. If the system does not
 152         * support IRQs on these pins, userspace will have to poll the sysfs
 153         * files manually.
 154         */
 155        if (ltc3651_charger->acpr_gpio) {
 156                ret = gpiod_to_irq(ltc3651_charger->acpr_gpio);
 157                if (ret >= 0)
 158                        ret = devm_request_any_context_irq(&pdev->dev, ret,
 159                                ltc3651_charger_irq,
 160                                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
 161                                dev_name(&pdev->dev), ltc3651_charger->charger);
 162                if (ret < 0)
 163                        dev_warn(&pdev->dev, "Failed to request acpr irq\n");
 164        }
 165        if (ltc3651_charger->fault_gpio) {
 166                ret = gpiod_to_irq(ltc3651_charger->fault_gpio);
 167                if (ret >= 0)
 168                        ret = devm_request_any_context_irq(&pdev->dev, ret,
 169                                ltc3651_charger_irq,
 170                                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
 171                                dev_name(&pdev->dev), ltc3651_charger->charger);
 172                if (ret < 0)
 173                        dev_warn(&pdev->dev, "Failed to request fault irq\n");
 174        }
 175        if (ltc3651_charger->chrg_gpio) {
 176                ret = gpiod_to_irq(ltc3651_charger->chrg_gpio);
 177                if (ret >= 0)
 178                        ret = devm_request_any_context_irq(&pdev->dev, ret,
 179                                ltc3651_charger_irq,
 180                                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
 181                                dev_name(&pdev->dev), ltc3651_charger->charger);
 182                if (ret < 0)
 183                        dev_warn(&pdev->dev, "Failed to request chrg irq\n");
 184        }
 185
 186        platform_set_drvdata(pdev, ltc3651_charger);
 187
 188        return 0;
 189}
 190
 191static const struct of_device_id ltc3651_charger_match[] = {
 192        { .compatible = "lltc,ltc3651-charger" },
 193        { }
 194};
 195MODULE_DEVICE_TABLE(of, ltc3651_charger_match);
 196
 197static struct platform_driver ltc3651_charger_driver = {
 198        .probe = ltc3651_charger_probe,
 199        .driver = {
 200                .name = "ltc3651-charger",
 201                .of_match_table = ltc3651_charger_match,
 202        },
 203};
 204
 205module_platform_driver(ltc3651_charger_driver);
 206
 207MODULE_AUTHOR("Mike Looijmans <mike.looijmans@topic.nl>");
 208MODULE_DESCRIPTION("Driver for LTC3651 charger");
 209MODULE_LICENSE("GPL");
 210MODULE_ALIAS("platform:ltc3651-charger");
 211