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