linux/drivers/power/tps65090-charger.c
<<
>>
Prefs
   1/*
   2 * Battery charger driver for TI's tps65090
   3 *
   4 * Copyright (c) 2013, NVIDIA CORPORATION.  All rights reserved.
   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#include <linux/err.h>
  19#include <linux/init.h>
  20#include <linux/interrupt.h>
  21#include <linux/kernel.h>
  22#include <linux/module.h>
  23#include <linux/slab.h>
  24#include <linux/delay.h>
  25#include <linux/platform_device.h>
  26#include <linux/power_supply.h>
  27#include <linux/mfd/tps65090.h>
  28
  29#define TPS65090_REG_INTR_STS   0x00
  30#define TPS65090_REG_INTR_MASK  0x02
  31#define TPS65090_REG_CG_CTRL0   0x04
  32#define TPS65090_REG_CG_CTRL1   0x05
  33#define TPS65090_REG_CG_CTRL2   0x06
  34#define TPS65090_REG_CG_CTRL3   0x07
  35#define TPS65090_REG_CG_CTRL4   0x08
  36#define TPS65090_REG_CG_CTRL5   0x09
  37#define TPS65090_REG_CG_STATUS1 0x0a
  38#define TPS65090_REG_CG_STATUS2 0x0b
  39
  40#define TPS65090_CHARGER_ENABLE BIT(0)
  41#define TPS65090_VACG           BIT(1)
  42#define TPS65090_NOITERM        BIT(5)
  43
  44struct tps65090_charger {
  45        struct  device  *dev;
  46        int     ac_online;
  47        int     prev_ac_online;
  48        int     irq;
  49        struct power_supply     ac;
  50        struct tps65090_platform_data *pdata;
  51};
  52
  53static enum power_supply_property tps65090_ac_props[] = {
  54        POWER_SUPPLY_PROP_ONLINE,
  55};
  56
  57static int tps65090_low_chrg_current(struct tps65090_charger *charger)
  58{
  59        int ret;
  60
  61        ret = tps65090_write(charger->dev->parent, TPS65090_REG_CG_CTRL5,
  62                        TPS65090_NOITERM);
  63        if (ret < 0) {
  64                dev_err(charger->dev, "%s(): error reading in register 0x%x\n",
  65                        __func__, TPS65090_REG_CG_CTRL5);
  66                return ret;
  67        }
  68        return 0;
  69}
  70
  71static int tps65090_enable_charging(struct tps65090_charger *charger)
  72{
  73        int ret;
  74        uint8_t ctrl0 = 0;
  75
  76        ret = tps65090_read(charger->dev->parent, TPS65090_REG_CG_CTRL0,
  77                            &ctrl0);
  78        if (ret < 0) {
  79                dev_err(charger->dev, "%s(): error reading in register 0x%x\n",
  80                                __func__, TPS65090_REG_CG_CTRL0);
  81                return ret;
  82        }
  83
  84        ret = tps65090_write(charger->dev->parent, TPS65090_REG_CG_CTRL0,
  85                                (ctrl0 | TPS65090_CHARGER_ENABLE));
  86        if (ret < 0) {
  87                dev_err(charger->dev, "%s(): error writing in register 0x%x\n",
  88                                __func__, TPS65090_REG_CG_CTRL0);
  89                return ret;
  90        }
  91        return 0;
  92}
  93
  94static int tps65090_config_charger(struct tps65090_charger *charger)
  95{
  96        uint8_t intrmask = 0;
  97        int ret;
  98
  99        if (charger->pdata->enable_low_current_chrg) {
 100                ret = tps65090_low_chrg_current(charger);
 101                if (ret < 0) {
 102                        dev_err(charger->dev,
 103                                "error configuring low charge current\n");
 104                        return ret;
 105                }
 106        }
 107
 108        /* Enable the VACG interrupt for AC power detect */
 109        ret = tps65090_read(charger->dev->parent, TPS65090_REG_INTR_MASK,
 110                            &intrmask);
 111        if (ret < 0) {
 112                dev_err(charger->dev, "%s(): error reading in register 0x%x\n",
 113                        __func__, TPS65090_REG_INTR_MASK);
 114                return ret;
 115        }
 116
 117        ret = tps65090_write(charger->dev->parent, TPS65090_REG_INTR_MASK,
 118                             (intrmask | TPS65090_VACG));
 119        if (ret < 0) {
 120                dev_err(charger->dev, "%s(): error writing in register 0x%x\n",
 121                        __func__, TPS65090_REG_CG_CTRL0);
 122                return ret;
 123        }
 124
 125        return 0;
 126}
 127
 128static int tps65090_ac_get_property(struct power_supply *psy,
 129                        enum power_supply_property psp,
 130                        union power_supply_propval *val)
 131{
 132        struct tps65090_charger *charger = container_of(psy,
 133                                        struct tps65090_charger, ac);
 134
 135        if (psp == POWER_SUPPLY_PROP_ONLINE) {
 136                val->intval = charger->ac_online;
 137                charger->prev_ac_online = charger->ac_online;
 138                return 0;
 139        }
 140        return -EINVAL;
 141}
 142
 143static irqreturn_t tps65090_charger_isr(int irq, void *dev_id)
 144{
 145        struct tps65090_charger *charger = dev_id;
 146        int ret;
 147        uint8_t status1 = 0;
 148        uint8_t intrsts = 0;
 149
 150        ret = tps65090_read(charger->dev->parent, TPS65090_REG_CG_STATUS1,
 151                            &status1);
 152        if (ret < 0) {
 153                dev_err(charger->dev, "%s(): Error in reading reg 0x%x\n",
 154                                __func__, TPS65090_REG_CG_STATUS1);
 155                return IRQ_HANDLED;
 156        }
 157        msleep(75);
 158        ret = tps65090_read(charger->dev->parent, TPS65090_REG_INTR_STS,
 159                            &intrsts);
 160        if (ret < 0) {
 161                dev_err(charger->dev, "%s(): Error in reading reg 0x%x\n",
 162                                __func__, TPS65090_REG_INTR_STS);
 163                return IRQ_HANDLED;
 164        }
 165
 166        if (intrsts & TPS65090_VACG) {
 167                ret = tps65090_enable_charging(charger);
 168                if (ret < 0)
 169                        return IRQ_HANDLED;
 170                charger->ac_online = 1;
 171        } else {
 172                charger->ac_online = 0;
 173        }
 174
 175        /* Clear interrupts. */
 176        ret = tps65090_write(charger->dev->parent, TPS65090_REG_INTR_STS, 0x00);
 177        if (ret < 0) {
 178                dev_err(charger->dev, "%s(): Error in writing reg 0x%x\n",
 179                                __func__, TPS65090_REG_INTR_STS);
 180        }
 181
 182        if (charger->prev_ac_online != charger->ac_online)
 183                power_supply_changed(&charger->ac);
 184
 185        return IRQ_HANDLED;
 186}
 187
 188#if defined(CONFIG_OF)
 189
 190#include <linux/of_device.h>
 191
 192static struct tps65090_platform_data *
 193                tps65090_parse_dt_charger_data(struct platform_device *pdev)
 194{
 195        struct tps65090_platform_data *pdata;
 196        struct device_node *np = pdev->dev.of_node;
 197        unsigned int prop;
 198
 199        pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
 200        if (!pdata) {
 201                dev_err(&pdev->dev, "Memory alloc for tps65090_pdata failed\n");
 202                return NULL;
 203        }
 204
 205        prop = of_property_read_bool(np, "ti,enable-low-current-chrg");
 206        pdata->enable_low_current_chrg = prop;
 207
 208        pdata->irq_base = -1;
 209
 210        return pdata;
 211
 212}
 213#else
 214static struct tps65090_platform_data *
 215                tps65090_parse_dt_charger_data(struct platform_device *pdev)
 216{
 217        return NULL;
 218}
 219#endif
 220
 221static int tps65090_charger_probe(struct platform_device *pdev)
 222{
 223        struct tps65090_charger *cdata;
 224        struct tps65090_platform_data *pdata;
 225        uint8_t status1 = 0;
 226        int ret;
 227        int irq;
 228
 229        pdata = dev_get_platdata(pdev->dev.parent);
 230
 231        if (!pdata && pdev->dev.of_node)
 232                pdata = tps65090_parse_dt_charger_data(pdev);
 233
 234        if (!pdata) {
 235                dev_err(&pdev->dev, "%s():no platform data available\n",
 236                                __func__);
 237                return -ENODEV;
 238        }
 239
 240        cdata = devm_kzalloc(&pdev->dev, sizeof(*cdata), GFP_KERNEL);
 241        if (!cdata) {
 242                dev_err(&pdev->dev, "failed to allocate memory status\n");
 243                return -ENOMEM;
 244        }
 245
 246        platform_set_drvdata(pdev, cdata);
 247
 248        cdata->dev                      = &pdev->dev;
 249        cdata->pdata                    = pdata;
 250
 251        cdata->ac.name                  = "tps65090-ac";
 252        cdata->ac.type                  = POWER_SUPPLY_TYPE_MAINS;
 253        cdata->ac.get_property          = tps65090_ac_get_property;
 254        cdata->ac.properties            = tps65090_ac_props;
 255        cdata->ac.num_properties        = ARRAY_SIZE(tps65090_ac_props);
 256        cdata->ac.supplied_to           = pdata->supplied_to;
 257        cdata->ac.num_supplicants       = pdata->num_supplicants;
 258        cdata->ac.of_node               = pdev->dev.of_node;
 259
 260        ret = power_supply_register(&pdev->dev, &cdata->ac);
 261        if (ret) {
 262                dev_err(&pdev->dev, "failed: power supply register\n");
 263                return ret;
 264        }
 265
 266        irq = platform_get_irq(pdev, 0);
 267        if (irq <= 0) {
 268                dev_warn(&pdev->dev, "Unable to get charger irq = %d\n", irq);
 269                ret = irq;
 270                goto fail_unregister_supply;
 271        }
 272
 273        cdata->irq = irq;
 274
 275        ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
 276                tps65090_charger_isr, 0, "tps65090-charger", cdata);
 277        if (ret) {
 278                dev_err(cdata->dev, "Unable to register irq %d err %d\n", irq,
 279                        ret);
 280                goto fail_free_irq;
 281        }
 282
 283        ret = tps65090_config_charger(cdata);
 284        if (ret < 0) {
 285                dev_err(&pdev->dev, "charger config failed, err %d\n", ret);
 286                goto fail_free_irq;
 287        }
 288
 289        /* Check for charger presence */
 290        ret = tps65090_read(cdata->dev->parent, TPS65090_REG_CG_STATUS1,
 291                        &status1);
 292        if (ret < 0) {
 293                dev_err(cdata->dev, "%s(): Error in reading reg 0x%x", __func__,
 294                        TPS65090_REG_CG_STATUS1);
 295                goto fail_free_irq;
 296        }
 297
 298        if (status1 != 0) {
 299                ret = tps65090_enable_charging(cdata);
 300                if (ret < 0) {
 301                        dev_err(cdata->dev, "error enabling charger\n");
 302                        goto fail_free_irq;
 303                }
 304                cdata->ac_online = 1;
 305                power_supply_changed(&cdata->ac);
 306        }
 307
 308        return 0;
 309
 310fail_free_irq:
 311        devm_free_irq(cdata->dev, irq, cdata);
 312fail_unregister_supply:
 313        power_supply_unregister(&cdata->ac);
 314
 315        return ret;
 316}
 317
 318static int tps65090_charger_remove(struct platform_device *pdev)
 319{
 320        struct tps65090_charger *cdata = platform_get_drvdata(pdev);
 321
 322        devm_free_irq(cdata->dev, cdata->irq, cdata);
 323        power_supply_unregister(&cdata->ac);
 324
 325        return 0;
 326}
 327
 328static struct of_device_id of_tps65090_charger_match[] = {
 329        { .compatible = "ti,tps65090-charger", },
 330        { /* end */ }
 331};
 332
 333static struct platform_driver tps65090_charger_driver = {
 334        .driver = {
 335                .name   = "tps65090-charger",
 336                .of_match_table = of_tps65090_charger_match,
 337                .owner  = THIS_MODULE,
 338        },
 339        .probe  = tps65090_charger_probe,
 340        .remove = tps65090_charger_remove,
 341};
 342module_platform_driver(tps65090_charger_driver);
 343
 344MODULE_LICENSE("GPL v2");
 345MODULE_AUTHOR("Syed Rafiuddin <srafiuddin@nvidia.com>");
 346MODULE_DESCRIPTION("tps65090 battery charger driver");
 347
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.