tps65090-charger.c (8767B)
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 29struct 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 40static enum power_supply_property tps65090_ac_props[] = { 41 POWER_SUPPLY_PROP_ONLINE, 42}; 43 44static 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 61static 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 87static 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 124static 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 138static 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 187static 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 209static 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 221static 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 229static 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, IRQF_ONESHOT, "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 325fail_unregister_supply: 326 power_supply_unregister(cdata->ac); 327 328 return ret; 329} 330 331static 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 342static const struct of_device_id of_tps65090_charger_match[] = { 343 { .compatible = "ti,tps65090-charger", }, 344 { /* end */ } 345}; 346MODULE_DEVICE_TABLE(of, of_tps65090_charger_match); 347 348static 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}; 356module_platform_driver(tps65090_charger_driver); 357 358MODULE_LICENSE("GPL v2"); 359MODULE_AUTHOR("Syed Rafiuddin <srafiuddin@nvidia.com>"); 360MODULE_DESCRIPTION("tps65090 battery charger driver");