linux/drivers/power/tps65217_charger.c
<<
>>
Prefs
   1/*
   2 * Battery charger driver for TI's tps65217
   3 *
   4 * Copyright (c) 2015, Collabora Ltd.
   5
   6 * This program is free software; you can redistribute it and/or modify it
   7 * under the terms and conditions of the GNU General Public License,
   8 * version 2, as published by the Free Software Foundation.
   9
  10 * This program is distributed in the hope it will be useful, but WITHOUT
  11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  13 * more details.
  14
  15 * You should have received a copy of the GNU General Public License
  16 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17 */
  18
  19/*
  20 * Battery charger driver for TI's tps65217
  21 */
  22#include <linux/kernel.h>
  23#include <linux/kthread.h>
  24#include <linux/device.h>
  25#include <linux/module.h>
  26#include <linux/platform_device.h>
  27#include <linux/init.h>
  28#include <linux/interrupt.h>
  29#include <linux/slab.h>
  30#include <linux/err.h>
  31#include <linux/of.h>
  32#include <linux/of_device.h>
  33#include <linux/power_supply.h>
  34
  35#include <linux/mfd/core.h>
  36#include <linux/mfd/tps65217.h>
  37
  38#define POLL_INTERVAL           (HZ * 2)
  39
  40struct tps65217_charger {
  41        struct tps65217 *tps;
  42        struct device *dev;
  43        struct power_supply *ac;
  44
  45        int     ac_online;
  46        int     prev_ac_online;
  47
  48        struct task_struct      *poll_task;
  49};
  50
  51static enum power_supply_property tps65217_ac_props[] = {
  52        POWER_SUPPLY_PROP_ONLINE,
  53};
  54
  55static int tps65217_config_charger(struct tps65217_charger *charger)
  56{
  57        int ret;
  58
  59        dev_dbg(charger->dev, "%s\n", __func__);
  60
  61        /*
  62         * tps65217 rev. G, p. 31 (see p. 32 for NTC schematic)
  63         *
  64         * The device can be configured to support a 100k NTC (B = 3960) by
  65         * setting the the NTC_TYPE bit in register CHGCONFIG1 to 1. However it
  66         * is not recommended to do so. In sleep mode, the charger continues
  67         * charging the battery, but all register values are reset to default
  68         * values. Therefore, the charger would get the wrong temperature
  69         * information. If 100k NTC setting is required, please contact the
  70         * factory.
  71         *
  72         * ATTENTION, conflicting information, from p. 46
  73         *
  74         * NTC TYPE (for battery temperature measurement)
  75         *   0 – 100k (curve 1, B = 3960)
  76         *   1 – 10k  (curve 2, B = 3480) (default on reset)
  77         *
  78         */
  79        ret = tps65217_clear_bits(charger->tps, TPS65217_REG_CHGCONFIG1,
  80                                  TPS65217_CHGCONFIG1_NTC_TYPE,
  81                                  TPS65217_PROTECT_NONE);
  82        if (ret) {
  83                dev_err(charger->dev,
  84                        "failed to set 100k NTC setting: %d\n", ret);
  85                return ret;
  86        }
  87
  88        return 0;
  89}
  90
  91static int tps65217_enable_charging(struct tps65217_charger *charger)
  92{
  93        int ret;
  94
  95        /* charger already enabled */
  96        if (charger->ac_online)
  97                return 0;
  98
  99        dev_dbg(charger->dev, "%s: enable charging\n", __func__);
 100        ret = tps65217_set_bits(charger->tps, TPS65217_REG_CHGCONFIG1,
 101                                TPS65217_CHGCONFIG1_CHG_EN,
 102                                TPS65217_CHGCONFIG1_CHG_EN,
 103                                TPS65217_PROTECT_NONE);
 104        if (ret) {
 105                dev_err(charger->dev,
 106                        "%s: Error in writing CHG_EN in reg 0x%x: %d\n",
 107                        __func__, TPS65217_REG_CHGCONFIG1, ret);
 108                return ret;
 109        }
 110
 111        charger->ac_online = 1;
 112
 113        return 0;
 114}
 115
 116static int tps65217_ac_get_property(struct power_supply *psy,
 117                        enum power_supply_property psp,
 118                        union power_supply_propval *val)
 119{
 120        struct tps65217_charger *charger = power_supply_get_drvdata(psy);
 121
 122        if (psp == POWER_SUPPLY_PROP_ONLINE) {
 123                val->intval = charger->ac_online;
 124                return 0;
 125        }
 126        return -EINVAL;
 127}
 128
 129static irqreturn_t tps65217_charger_irq(int irq, void *dev)
 130{
 131        int ret, val;
 132        struct tps65217_charger *charger = dev;
 133
 134        charger->prev_ac_online = charger->ac_online;
 135
 136        ret = tps65217_reg_read(charger->tps, TPS65217_REG_STATUS, &val);
 137        if (ret < 0) {
 138                dev_err(charger->dev, "%s: Error in reading reg 0x%x\n",
 139                        __func__, TPS65217_REG_STATUS);
 140                return IRQ_HANDLED;
 141        }
 142
 143        dev_dbg(charger->dev, "%s: 0x%x\n", __func__, val);
 144
 145        /* check for AC status bit */
 146        if (val & TPS65217_STATUS_ACPWR) {
 147                ret = tps65217_enable_charging(charger);
 148                if (ret) {
 149                        dev_err(charger->dev,
 150                                "failed to enable charger: %d\n", ret);
 151                        return IRQ_HANDLED;
 152                }
 153        } else {
 154                charger->ac_online = 0;
 155        }
 156
 157        if (charger->prev_ac_online != charger->ac_online)
 158                power_supply_changed(charger->ac);
 159
 160        ret = tps65217_reg_read(charger->tps, TPS65217_REG_CHGCONFIG0, &val);
 161        if (ret < 0) {
 162                dev_err(charger->dev, "%s: Error in reading reg 0x%x\n",
 163                        __func__, TPS65217_REG_CHGCONFIG0);
 164                return IRQ_HANDLED;
 165        }
 166
 167        if (val & TPS65217_CHGCONFIG0_ACTIVE)
 168                dev_dbg(charger->dev, "%s: charger is charging\n", __func__);
 169        else
 170                dev_dbg(charger->dev,
 171                        "%s: charger is NOT charging\n", __func__);
 172
 173        return IRQ_HANDLED;
 174}
 175
 176static int tps65217_charger_poll_task(void *data)
 177{
 178        set_freezable();
 179
 180        while (!kthread_should_stop()) {
 181                schedule_timeout_interruptible(POLL_INTERVAL);
 182                try_to_freeze();
 183                tps65217_charger_irq(-1, data);
 184        }
 185        return 0;
 186}
 187
 188static const struct power_supply_desc tps65217_charger_desc = {
 189        .name                   = "tps65217-ac",
 190        .type                   = POWER_SUPPLY_TYPE_MAINS,
 191        .get_property           = tps65217_ac_get_property,
 192        .properties             = tps65217_ac_props,
 193        .num_properties         = ARRAY_SIZE(tps65217_ac_props),
 194};
 195
 196static int tps65217_charger_probe(struct platform_device *pdev)
 197{
 198        struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
 199        struct tps65217_charger *charger;
 200        int ret;
 201
 202        dev_dbg(&pdev->dev, "%s\n", __func__);
 203
 204        charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL);
 205        if (!charger)
 206                return -ENOMEM;
 207
 208        charger->tps = tps;
 209        charger->dev = &pdev->dev;
 210
 211        charger->ac = devm_power_supply_register(&pdev->dev,
 212                                                 &tps65217_charger_desc,
 213                                                 NULL);
 214        if (IS_ERR(charger->ac)) {
 215                dev_err(&pdev->dev, "failed: power supply register\n");
 216                return PTR_ERR(charger->ac);
 217        }
 218
 219        ret = tps65217_config_charger(charger);
 220        if (ret < 0) {
 221                dev_err(charger->dev, "charger config failed, err %d\n", ret);
 222                return ret;
 223        }
 224
 225        charger->poll_task = kthread_run(tps65217_charger_poll_task,
 226                                      charger, "ktps65217charger");
 227        if (IS_ERR(charger->poll_task)) {
 228                ret = PTR_ERR(charger->poll_task);
 229                dev_err(charger->dev, "Unable to run kthread err %d\n", ret);
 230                return ret;
 231        }
 232
 233        return 0;
 234}
 235
 236static int tps65217_charger_remove(struct platform_device *pdev)
 237{
 238        struct tps65217_charger *charger = platform_get_drvdata(pdev);
 239
 240        kthread_stop(charger->poll_task);
 241
 242        return 0;
 243}
 244
 245static const struct of_device_id tps65217_charger_match_table[] = {
 246        { .compatible = "ti,tps65217-charger", },
 247        { /* sentinel */ }
 248};
 249MODULE_DEVICE_TABLE(of, tps65217_charger_match_table);
 250
 251static struct platform_driver tps65217_charger_driver = {
 252        .probe  = tps65217_charger_probe,
 253        .remove = tps65217_charger_remove,
 254        .driver = {
 255                .name   = "tps65217-charger",
 256                .of_match_table = of_match_ptr(tps65217_charger_match_table),
 257        },
 258
 259};
 260module_platform_driver(tps65217_charger_driver);
 261
 262MODULE_LICENSE("GPL v2");
 263MODULE_AUTHOR("Enric Balletbo Serra <enric.balletbo@collabora.com>");
 264MODULE_DESCRIPTION("TPS65217 battery charger driver");
 265