Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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