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 v2.6.37-rc7 565 lines 14 kB view raw
1/* 2 * TWL4030/TPS65950 BCI (Battery Charger Interface) driver 3 * 4 * Copyright (C) 2010 Gražvydas Ignotas <notasas@gmail.com> 5 * 6 * based on twl4030_bci_battery.c by TI 7 * Copyright (C) 2008 Texas Instruments, Inc. 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 */ 14 15#include <linux/init.h> 16#include <linux/module.h> 17#include <linux/slab.h> 18#include <linux/platform_device.h> 19#include <linux/interrupt.h> 20#include <linux/i2c/twl.h> 21#include <linux/power_supply.h> 22#include <linux/notifier.h> 23#include <linux/usb/otg.h> 24 25#define TWL4030_BCIMSTATEC 0x02 26#define TWL4030_BCIICHG 0x08 27#define TWL4030_BCIVAC 0x0a 28#define TWL4030_BCIVBUS 0x0c 29#define TWL4030_BCIMFSTS4 0x10 30#define TWL4030_BCICTL1 0x23 31 32#define TWL4030_BCIAUTOWEN BIT(5) 33#define TWL4030_CONFIG_DONE BIT(4) 34#define TWL4030_BCIAUTOUSB BIT(1) 35#define TWL4030_BCIAUTOAC BIT(0) 36#define TWL4030_CGAIN BIT(5) 37#define TWL4030_USBFASTMCHG BIT(2) 38#define TWL4030_STS_VBUS BIT(7) 39#define TWL4030_STS_USB_ID BIT(2) 40 41/* BCI interrupts */ 42#define TWL4030_WOVF BIT(0) /* Watchdog overflow */ 43#define TWL4030_TMOVF BIT(1) /* Timer overflow */ 44#define TWL4030_ICHGHIGH BIT(2) /* Battery charge current high */ 45#define TWL4030_ICHGLOW BIT(3) /* Battery cc. low / FSM state change */ 46#define TWL4030_ICHGEOC BIT(4) /* Battery current end-of-charge */ 47#define TWL4030_TBATOR2 BIT(5) /* Battery temperature out of range 2 */ 48#define TWL4030_TBATOR1 BIT(6) /* Battery temperature out of range 1 */ 49#define TWL4030_BATSTS BIT(7) /* Battery status */ 50 51#define TWL4030_VBATLVL BIT(0) /* VBAT level */ 52#define TWL4030_VBATOV BIT(1) /* VBAT overvoltage */ 53#define TWL4030_VBUSOV BIT(2) /* VBUS overvoltage */ 54#define TWL4030_ACCHGOV BIT(3) /* Ac charger overvoltage */ 55 56#define TWL4030_MSTATEC_USB BIT(4) 57#define TWL4030_MSTATEC_AC BIT(5) 58#define TWL4030_MSTATEC_MASK 0x0f 59#define TWL4030_MSTATEC_QUICK1 0x02 60#define TWL4030_MSTATEC_QUICK7 0x07 61#define TWL4030_MSTATEC_COMPLETE1 0x0b 62#define TWL4030_MSTATEC_COMPLETE4 0x0e 63 64static bool allow_usb; 65module_param(allow_usb, bool, 1); 66MODULE_PARM_DESC(allow_usb, "Allow USB charge drawing default current"); 67 68struct twl4030_bci { 69 struct device *dev; 70 struct power_supply ac; 71 struct power_supply usb; 72 struct otg_transceiver *transceiver; 73 struct notifier_block otg_nb; 74 int irq_chg; 75 int irq_bci; 76}; 77 78/* 79 * clear and set bits on an given register on a given module 80 */ 81static int twl4030_clear_set(u8 mod_no, u8 clear, u8 set, u8 reg) 82{ 83 u8 val = 0; 84 int ret; 85 86 ret = twl_i2c_read_u8(mod_no, &val, reg); 87 if (ret) 88 return ret; 89 90 val &= ~clear; 91 val |= set; 92 93 return twl_i2c_write_u8(mod_no, val, reg); 94} 95 96static int twl4030_bci_read(u8 reg, u8 *val) 97{ 98 return twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, val, reg); 99} 100 101static int twl4030_clear_set_boot_bci(u8 clear, u8 set) 102{ 103 return twl4030_clear_set(TWL4030_MODULE_PM_MASTER, 0, 104 TWL4030_CONFIG_DONE | TWL4030_BCIAUTOWEN | set, 105 TWL4030_PM_MASTER_BOOT_BCI); 106} 107 108static int twl4030bci_read_adc_val(u8 reg) 109{ 110 int ret, temp; 111 u8 val; 112 113 /* read MSB */ 114 ret = twl4030_bci_read(reg + 1, &val); 115 if (ret) 116 return ret; 117 118 temp = (int)(val & 0x03) << 8; 119 120 /* read LSB */ 121 ret = twl4030_bci_read(reg, &val); 122 if (ret) 123 return ret; 124 125 return temp | val; 126} 127 128/* 129 * Check if VBUS power is present 130 */ 131static int twl4030_bci_have_vbus(struct twl4030_bci *bci) 132{ 133 int ret; 134 u8 hwsts; 135 136 ret = twl_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &hwsts, 137 TWL4030_PM_MASTER_STS_HW_CONDITIONS); 138 if (ret < 0) 139 return 0; 140 141 dev_dbg(bci->dev, "check_vbus: HW_CONDITIONS %02x\n", hwsts); 142 143 /* in case we also have STS_USB_ID, VBUS is driven by TWL itself */ 144 if ((hwsts & TWL4030_STS_VBUS) && !(hwsts & TWL4030_STS_USB_ID)) 145 return 1; 146 147 return 0; 148} 149 150/* 151 * Enable/Disable USB Charge funtionality. 152 */ 153static int twl4030_charger_enable_usb(struct twl4030_bci *bci, bool enable) 154{ 155 int ret; 156 157 if (enable) { 158 /* Check for USB charger conneted */ 159 if (!twl4030_bci_have_vbus(bci)) 160 return -ENODEV; 161 162 /* 163 * Until we can find out what current the device can provide, 164 * require a module param to enable USB charging. 165 */ 166 if (!allow_usb) { 167 dev_warn(bci->dev, "USB charging is disabled.\n"); 168 return -EACCES; 169 } 170 171 /* forcing the field BCIAUTOUSB (BOOT_BCI[1]) to 1 */ 172 ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOUSB); 173 if (ret < 0) 174 return ret; 175 176 /* forcing USBFASTMCHG(BCIMFSTS4[2]) to 1 */ 177 ret = twl4030_clear_set(TWL4030_MODULE_MAIN_CHARGE, 0, 178 TWL4030_USBFASTMCHG, TWL4030_BCIMFSTS4); 179 } else { 180 ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOUSB, 0); 181 } 182 183 return ret; 184} 185 186/* 187 * Enable/Disable AC Charge funtionality. 188 */ 189static int twl4030_charger_enable_ac(bool enable) 190{ 191 int ret; 192 193 if (enable) 194 ret = twl4030_clear_set_boot_bci(0, TWL4030_BCIAUTOAC); 195 else 196 ret = twl4030_clear_set_boot_bci(TWL4030_BCIAUTOAC, 0); 197 198 return ret; 199} 200 201/* 202 * TWL4030 CHG_PRES (AC charger presence) events 203 */ 204static irqreturn_t twl4030_charger_interrupt(int irq, void *arg) 205{ 206 struct twl4030_bci *bci = arg; 207 208 dev_dbg(bci->dev, "CHG_PRES irq\n"); 209 power_supply_changed(&bci->ac); 210 power_supply_changed(&bci->usb); 211 212 return IRQ_HANDLED; 213} 214 215/* 216 * TWL4030 BCI monitoring events 217 */ 218static irqreturn_t twl4030_bci_interrupt(int irq, void *arg) 219{ 220 struct twl4030_bci *bci = arg; 221 u8 irqs1, irqs2; 222 int ret; 223 224 ret = twl_i2c_read_u8(TWL4030_MODULE_INTERRUPTS, &irqs1, 225 TWL4030_INTERRUPTS_BCIISR1A); 226 if (ret < 0) 227 return IRQ_HANDLED; 228 229 ret = twl_i2c_read_u8(TWL4030_MODULE_INTERRUPTS, &irqs2, 230 TWL4030_INTERRUPTS_BCIISR2A); 231 if (ret < 0) 232 return IRQ_HANDLED; 233 234 dev_dbg(bci->dev, "BCI irq %02x %02x\n", irqs2, irqs1); 235 236 if (irqs1 & (TWL4030_ICHGLOW | TWL4030_ICHGEOC)) { 237 /* charger state change, inform the core */ 238 power_supply_changed(&bci->ac); 239 power_supply_changed(&bci->usb); 240 } 241 242 /* various monitoring events, for now we just log them here */ 243 if (irqs1 & (TWL4030_TBATOR2 | TWL4030_TBATOR1)) 244 dev_warn(bci->dev, "battery temperature out of range\n"); 245 246 if (irqs1 & TWL4030_BATSTS) 247 dev_crit(bci->dev, "battery disconnected\n"); 248 249 if (irqs2 & TWL4030_VBATOV) 250 dev_crit(bci->dev, "VBAT overvoltage\n"); 251 252 if (irqs2 & TWL4030_VBUSOV) 253 dev_crit(bci->dev, "VBUS overvoltage\n"); 254 255 if (irqs2 & TWL4030_ACCHGOV) 256 dev_crit(bci->dev, "Ac charger overvoltage\n"); 257 258 return IRQ_HANDLED; 259} 260 261static int twl4030_bci_usb_ncb(struct notifier_block *nb, unsigned long val, 262 void *priv) 263{ 264 struct twl4030_bci *bci = container_of(nb, struct twl4030_bci, otg_nb); 265 266 dev_dbg(bci->dev, "OTG notify %lu\n", val); 267 268 switch (val) { 269 case USB_EVENT_VBUS: 270 case USB_EVENT_CHARGER: 271 twl4030_charger_enable_usb(bci, true); 272 break; 273 case USB_EVENT_NONE: 274 twl4030_charger_enable_usb(bci, false); 275 break; 276 } 277 278 return NOTIFY_OK; 279} 280 281/* 282 * TI provided formulas: 283 * CGAIN == 0: ICHG = (BCIICHG * 1.7) / (2^10 - 1) - 0.85 284 * CGAIN == 1: ICHG = (BCIICHG * 3.4) / (2^10 - 1) - 1.7 285 * Here we use integer approximation of: 286 * CGAIN == 0: val * 1.6618 - 0.85 287 * CGAIN == 1: (val * 1.6618 - 0.85) * 2 288 */ 289static int twl4030_charger_get_current(void) 290{ 291 int curr; 292 int ret; 293 u8 bcictl1; 294 295 curr = twl4030bci_read_adc_val(TWL4030_BCIICHG); 296 if (curr < 0) 297 return curr; 298 299 ret = twl4030_bci_read(TWL4030_BCICTL1, &bcictl1); 300 if (ret) 301 return ret; 302 303 ret = (curr * 16618 - 850 * 10000) / 10; 304 if (bcictl1 & TWL4030_CGAIN) 305 ret *= 2; 306 307 return ret; 308} 309 310/* 311 * Returns the main charge FSM state 312 * Or < 0 on failure. 313 */ 314static int twl4030bci_state(struct twl4030_bci *bci) 315{ 316 int ret; 317 u8 state; 318 319 ret = twl4030_bci_read(TWL4030_BCIMSTATEC, &state); 320 if (ret) { 321 pr_err("twl4030_bci: error reading BCIMSTATEC\n"); 322 return ret; 323 } 324 325 dev_dbg(bci->dev, "state: %02x\n", state); 326 327 return state; 328} 329 330static int twl4030_bci_state_to_status(int state) 331{ 332 state &= TWL4030_MSTATEC_MASK; 333 if (TWL4030_MSTATEC_QUICK1 <= state && state <= TWL4030_MSTATEC_QUICK7) 334 return POWER_SUPPLY_STATUS_CHARGING; 335 else if (TWL4030_MSTATEC_COMPLETE1 <= state && 336 state <= TWL4030_MSTATEC_COMPLETE4) 337 return POWER_SUPPLY_STATUS_FULL; 338 else 339 return POWER_SUPPLY_STATUS_NOT_CHARGING; 340} 341 342static int twl4030_bci_get_property(struct power_supply *psy, 343 enum power_supply_property psp, 344 union power_supply_propval *val) 345{ 346 struct twl4030_bci *bci = dev_get_drvdata(psy->dev->parent); 347 int is_charging; 348 int state; 349 int ret; 350 351 state = twl4030bci_state(bci); 352 if (state < 0) 353 return state; 354 355 if (psy->type == POWER_SUPPLY_TYPE_USB) 356 is_charging = state & TWL4030_MSTATEC_USB; 357 else 358 is_charging = state & TWL4030_MSTATEC_AC; 359 360 switch (psp) { 361 case POWER_SUPPLY_PROP_STATUS: 362 if (is_charging) 363 val->intval = twl4030_bci_state_to_status(state); 364 else 365 val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 366 break; 367 case POWER_SUPPLY_PROP_VOLTAGE_NOW: 368 /* charging must be active for meaningful result */ 369 if (!is_charging) 370 return -ENODATA; 371 if (psy->type == POWER_SUPPLY_TYPE_USB) { 372 ret = twl4030bci_read_adc_val(TWL4030_BCIVBUS); 373 if (ret < 0) 374 return ret; 375 /* BCIVBUS uses ADCIN8, 7/1023 V/step */ 376 val->intval = ret * 6843; 377 } else { 378 ret = twl4030bci_read_adc_val(TWL4030_BCIVAC); 379 if (ret < 0) 380 return ret; 381 /* BCIVAC uses ADCIN11, 10/1023 V/step */ 382 val->intval = ret * 9775; 383 } 384 break; 385 case POWER_SUPPLY_PROP_CURRENT_NOW: 386 if (!is_charging) 387 return -ENODATA; 388 /* current measurement is shared between AC and USB */ 389 ret = twl4030_charger_get_current(); 390 if (ret < 0) 391 return ret; 392 val->intval = ret; 393 break; 394 case POWER_SUPPLY_PROP_ONLINE: 395 val->intval = is_charging && 396 twl4030_bci_state_to_status(state) != 397 POWER_SUPPLY_STATUS_NOT_CHARGING; 398 break; 399 default: 400 return -EINVAL; 401 } 402 403 return 0; 404} 405 406static enum power_supply_property twl4030_charger_props[] = { 407 POWER_SUPPLY_PROP_STATUS, 408 POWER_SUPPLY_PROP_ONLINE, 409 POWER_SUPPLY_PROP_VOLTAGE_NOW, 410 POWER_SUPPLY_PROP_CURRENT_NOW, 411}; 412 413static int __init twl4030_bci_probe(struct platform_device *pdev) 414{ 415 struct twl4030_bci *bci; 416 int ret; 417 int reg; 418 419 bci = kzalloc(sizeof(*bci), GFP_KERNEL); 420 if (bci == NULL) 421 return -ENOMEM; 422 423 bci->dev = &pdev->dev; 424 bci->irq_chg = platform_get_irq(pdev, 0); 425 bci->irq_bci = platform_get_irq(pdev, 1); 426 427 platform_set_drvdata(pdev, bci); 428 429 bci->ac.name = "twl4030_ac"; 430 bci->ac.type = POWER_SUPPLY_TYPE_MAINS; 431 bci->ac.properties = twl4030_charger_props; 432 bci->ac.num_properties = ARRAY_SIZE(twl4030_charger_props); 433 bci->ac.get_property = twl4030_bci_get_property; 434 435 ret = power_supply_register(&pdev->dev, &bci->ac); 436 if (ret) { 437 dev_err(&pdev->dev, "failed to register ac: %d\n", ret); 438 goto fail_register_ac; 439 } 440 441 bci->usb.name = "twl4030_usb"; 442 bci->usb.type = POWER_SUPPLY_TYPE_USB; 443 bci->usb.properties = twl4030_charger_props; 444 bci->usb.num_properties = ARRAY_SIZE(twl4030_charger_props); 445 bci->usb.get_property = twl4030_bci_get_property; 446 447 ret = power_supply_register(&pdev->dev, &bci->usb); 448 if (ret) { 449 dev_err(&pdev->dev, "failed to register usb: %d\n", ret); 450 goto fail_register_usb; 451 } 452 453 ret = request_threaded_irq(bci->irq_chg, NULL, 454 twl4030_charger_interrupt, 0, pdev->name, bci); 455 if (ret < 0) { 456 dev_err(&pdev->dev, "could not request irq %d, status %d\n", 457 bci->irq_chg, ret); 458 goto fail_chg_irq; 459 } 460 461 ret = request_threaded_irq(bci->irq_bci, NULL, 462 twl4030_bci_interrupt, 0, pdev->name, bci); 463 if (ret < 0) { 464 dev_err(&pdev->dev, "could not request irq %d, status %d\n", 465 bci->irq_bci, ret); 466 goto fail_bci_irq; 467 } 468 469 bci->transceiver = otg_get_transceiver(); 470 if (bci->transceiver != NULL) { 471 bci->otg_nb.notifier_call = twl4030_bci_usb_ncb; 472 otg_register_notifier(bci->transceiver, &bci->otg_nb); 473 } 474 475 /* Enable interrupts now. */ 476 reg = ~(TWL4030_ICHGLOW | TWL4030_ICHGEOC | TWL4030_TBATOR2 | 477 TWL4030_TBATOR1 | TWL4030_BATSTS); 478 ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg, 479 TWL4030_INTERRUPTS_BCIIMR1A); 480 if (ret < 0) { 481 dev_err(&pdev->dev, "failed to unmask interrupts: %d\n", ret); 482 goto fail_unmask_interrupts; 483 } 484 485 reg = ~(TWL4030_VBATOV | TWL4030_VBUSOV | TWL4030_ACCHGOV); 486 ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg, 487 TWL4030_INTERRUPTS_BCIIMR2A); 488 if (ret < 0) 489 dev_warn(&pdev->dev, "failed to unmask interrupts: %d\n", ret); 490 491 twl4030_charger_enable_ac(true); 492 twl4030_charger_enable_usb(bci, true); 493 494 return 0; 495 496fail_unmask_interrupts: 497 if (bci->transceiver != NULL) { 498 otg_unregister_notifier(bci->transceiver, &bci->otg_nb); 499 otg_put_transceiver(bci->transceiver); 500 } 501 free_irq(bci->irq_bci, bci); 502fail_bci_irq: 503 free_irq(bci->irq_chg, bci); 504fail_chg_irq: 505 power_supply_unregister(&bci->usb); 506fail_register_usb: 507 power_supply_unregister(&bci->ac); 508fail_register_ac: 509 platform_set_drvdata(pdev, NULL); 510 kfree(bci); 511 512 return ret; 513} 514 515static int __exit twl4030_bci_remove(struct platform_device *pdev) 516{ 517 struct twl4030_bci *bci = platform_get_drvdata(pdev); 518 519 twl4030_charger_enable_ac(false); 520 twl4030_charger_enable_usb(bci, false); 521 522 /* mask interrupts */ 523 twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff, 524 TWL4030_INTERRUPTS_BCIIMR1A); 525 twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff, 526 TWL4030_INTERRUPTS_BCIIMR2A); 527 528 if (bci->transceiver != NULL) { 529 otg_unregister_notifier(bci->transceiver, &bci->otg_nb); 530 otg_put_transceiver(bci->transceiver); 531 } 532 free_irq(bci->irq_bci, bci); 533 free_irq(bci->irq_chg, bci); 534 power_supply_unregister(&bci->usb); 535 power_supply_unregister(&bci->ac); 536 platform_set_drvdata(pdev, NULL); 537 kfree(bci); 538 539 return 0; 540} 541 542static struct platform_driver twl4030_bci_driver = { 543 .driver = { 544 .name = "twl4030_bci", 545 .owner = THIS_MODULE, 546 }, 547 .remove = __exit_p(twl4030_bci_remove), 548}; 549 550static int __init twl4030_bci_init(void) 551{ 552 return platform_driver_probe(&twl4030_bci_driver, twl4030_bci_probe); 553} 554module_init(twl4030_bci_init); 555 556static void __exit twl4030_bci_exit(void) 557{ 558 platform_driver_unregister(&twl4030_bci_driver); 559} 560module_exit(twl4030_bci_exit); 561 562MODULE_AUTHOR("Gražydas Ignotas"); 563MODULE_DESCRIPTION("TWL4030 Battery Charger Interface driver"); 564MODULE_LICENSE("GPL"); 565MODULE_ALIAS("platform:twl4030_bci");