root/drivers/power/supply/tps65090-charger.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. tps65090_low_chrg_current
  2. tps65090_enable_charging
  3. tps65090_config_charger
  4. tps65090_ac_get_property
  5. tps65090_charger_isr
  6. tps65090_parse_dt_charger_data
  7. tps65090_charger_poll_task
  8. tps65090_charger_probe
  9. tps65090_charger_remove

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

/* [<][>][^][v][top][bottom][index][help] */