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 = container_of(psy,
 139                                        struct tps65090_charger, ac);
 140
 141        if (psp == POWER_SUPPLY_PROP_ONLINE) {
 142                val->intval = charger->ac_online;
 143                charger->prev_ac_online = charger->ac_online;
 144                return 0;
 145        }
 146        return -EINVAL;
 147}
 148
 149static irqreturn_t tps65090_charger_isr(int irq, void *dev_id)
 150{
 151        struct tps65090_charger *charger = dev_id;
 152        int ret;
 153        uint8_t status1 = 0;
 154        uint8_t intrsts = 0;
 155
 156        ret = tps65090_read(charger->dev->parent, TPS65090_REG_CG_STATUS1,
 157                            &status1);
 158        if (ret < 0) {
 159                dev_err(charger->dev, "%s(): Error in reading reg 0x%x\n",
 160                                __func__, TPS65090_REG_CG_STATUS1);
 161                return IRQ_HANDLED;
 162        }
 163        msleep(75);
 164        ret = tps65090_read(charger->dev->parent, TPS65090_REG_INTR_STS,
 165                            &intrsts);
 166        if (ret < 0) {
 167                dev_err(charger->dev, "%s(): Error in reading reg 0x%x\n",
 168                                __func__, TPS65090_REG_INTR_STS);
 169                return IRQ_HANDLED;
 170        }
 171
 172        if (intrsts & TPS65090_VACG) {
 173                ret = tps65090_enable_charging(charger);
 174                if (ret < 0)
 175                        return IRQ_HANDLED;
 176                charger->ac_online = 1;
 177        } else {
 178                charger->ac_online = 0;
 179        }
 180
 181        /* Clear interrupts. */
 182        if (!charger->passive_mode) {
 183                ret = tps65090_write(charger->dev->parent,
 184                                     TPS65090_REG_INTR_STS, 0x00);
 185                if (ret < 0) {
 186                        dev_err(charger->dev,
 187                                "%s(): Error in writing reg 0x%x\n",
 188                                __func__, TPS65090_REG_INTR_STS);
 189                }
 190        }
 191
 192        if (charger->prev_ac_online != charger->ac_online)
 193                power_supply_changed(&charger->ac);
 194
 195        return IRQ_HANDLED;
 196}
 197
 198static struct tps65090_platform_data *
 199                tps65090_parse_dt_charger_data(struct platform_device *pdev)
 200{
 201        struct tps65090_platform_data *pdata;
 202        struct device_node *np = pdev->dev.of_node;
 203        unsigned int prop;
 204
 205        pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
 206        if (!pdata) {
 207                dev_err(&pdev->dev, "Memory alloc for tps65090_pdata failed\n");
 208                return NULL;
 209        }
 210
 211        prop = of_property_read_bool(np, "ti,enable-low-current-chrg");
 212        pdata->enable_low_current_chrg = prop;
 213
 214        pdata->irq_base = -1;
 215
 216        return pdata;
 217
 218}
 219
 220static int tps65090_charger_poll_task(void *data)
 221{
 222        set_freezable();
 223
 224        while (!kthread_should_stop()) {
 225                schedule_timeout_interruptible(POLL_INTERVAL);
 226                try_to_freeze();
 227                tps65090_charger_isr(-1, data);
 228        }
 229        return 0;
 230}
 231
 232static int tps65090_charger_probe(struct platform_device *pdev)
 233{
 234        struct tps65090_charger *cdata;
 235        struct tps65090_platform_data *pdata;
 236        uint8_t status1 = 0;
 237        int ret;
 238        int irq;
 239
 240        pdata = dev_get_platdata(pdev->dev.parent);
 241
 242        if (IS_ENABLED(CONFIG_OF) && !pdata && pdev->dev.of_node)
 243                pdata = tps65090_parse_dt_charger_data(pdev);
 244
 245        if (!pdata) {
 246                dev_err(&pdev->dev, "%s():no platform data available\n",
 247                                __func__);
 248                return -ENODEV;
 249        }
 250
 251        cdata = devm_kzalloc(&pdev->dev, sizeof(*cdata), GFP_KERNEL);
 252        if (!cdata) {
 253                dev_err(&pdev->dev, "failed to allocate memory status\n");
 254                return -ENOMEM;
 255        }
 256
 257        platform_set_drvdata(pdev, cdata);
 258
 259        cdata->dev                      = &pdev->dev;
 260        cdata->pdata                    = pdata;
 261
 262        cdata->ac.name                  = "tps65090-ac";
 263        cdata->ac.type                  = POWER_SUPPLY_TYPE_MAINS;
 264        cdata->ac.get_property          = tps65090_ac_get_property;
 265        cdata->ac.properties            = tps65090_ac_props;
 266        cdata->ac.num_properties        = ARRAY_SIZE(tps65090_ac_props);
 267        cdata->ac.supplied_to           = pdata->supplied_to;
 268        cdata->ac.num_supplicants       = pdata->num_supplicants;
 269        cdata->ac.of_node               = pdev->dev.of_node;
 270
 271        ret = power_supply_register(&pdev->dev, &cdata->ac);
 272        if (ret) {
 273                dev_err(&pdev->dev, "failed: power supply register\n");
 274                return ret;
 275        }
 276
 277        irq = platform_get_irq(pdev, 0);
 278        if (irq < 0)
 279                irq = -ENXIO;
 280        cdata->irq = irq;
 281
 282        ret = tps65090_config_charger(cdata);
 283        if (ret < 0) {
 284                dev_err(&pdev->dev, "charger config failed, err %d\n", ret);
 285                goto fail_unregister_supply;
 286        }
 287
 288        /* Check for charger presence */
 289        ret = tps65090_read(cdata->dev->parent, TPS65090_REG_CG_STATUS1,
 290                        &status1);
 291        if (ret < 0) {
 292                dev_err(cdata->dev, "%s(): Error in reading reg 0x%x", __func__,
 293                        TPS65090_REG_CG_STATUS1);
 294                goto fail_unregister_supply;
 295        }
 296
 297        if (status1 != 0) {
 298                ret = tps65090_enable_charging(cdata);
 299                if (ret < 0) {
 300                        dev_err(cdata->dev, "error enabling charger\n");
 301                        goto fail_unregister_supply;
 302                }
 303                cdata->ac_online = 1;
 304                power_supply_changed(&cdata->ac);
 305        }
 306
 307        if (irq != -ENXIO) {
 308                ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
 309                        tps65090_charger_isr, 0, "tps65090-charger", cdata);
 310                if (ret) {
 311                        dev_err(cdata->dev,
 312                                "Unable to register irq %d err %d\n", irq,
 313                                ret);
 314                        goto fail_unregister_supply;
 315                }
 316        } else {
 317                cdata->poll_task = kthread_run(tps65090_charger_poll_task,
 318                                              cdata, "ktps65090charger");
 319                cdata->passive_mode = true;
 320                if (IS_ERR(cdata->poll_task)) {
 321                        ret = PTR_ERR(cdata->poll_task);
 322                        dev_err(cdata->dev,
 323                                "Unable to run kthread err %d\n", ret);
 324                        goto fail_unregister_supply;
 325                }
 326        }
 327
 328        return 0;
 329
 330fail_unregister_supply:
 331        power_supply_unregister(&cdata->ac);
 332
 333        return ret;
 334}
 335
 336static int tps65090_charger_remove(struct platform_device *pdev)
 337{
 338        struct tps65090_charger *cdata = platform_get_drvdata(pdev);
 339
 340        if (cdata->irq == -ENXIO)
 341                kthread_stop(cdata->poll_task);
 342        power_supply_unregister(&cdata->ac);
 343
 344        return 0;
 345}
 346
 347static struct of_device_id of_tps65090_charger_match[] = {
 348        { .compatible = "ti,tps65090-charger", },
 349        { /* end */ }
 350};
 351
 352static struct platform_driver tps65090_charger_driver = {
 353        .driver = {
 354                .name   = "tps65090-charger",
 355                .of_match_table = of_tps65090_charger_match,
 356        },
 357        .probe  = tps65090_charger_probe,
 358        .remove = tps65090_charger_remove,
 359};
 360module_platform_driver(tps65090_charger_driver);
 361
 362MODULE_LICENSE("GPL v2");
 363MODULE_AUTHOR("Syed Rafiuddin <srafiuddin@nvidia.com>");
 364MODULE_DESCRIPTION("tps65090 battery charger driver");
 365