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