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

power: twl4030_charger: detect battery presence prior to enabling charger

TWL4030's Battery Charger seems to be designed for non-hotpluggable
batteries.

If battery is not present in the system, BATSTS is always set with the
expectation that software will take actions to move to a required safe
state (could be power down or disable various charger paths).

It does not seem possible even by manipulating the edge detection
of the event (using BCIEDR2 register) to have a consistent hotplug
handling. This seems to be the result of BATSTS interrupt generated
when the thermistor of the battery pack is disconnected from the
dedicated ADIN1 pin. Clearing the status just results in the status
being regenerated by the monitoring ADC(MADC) and disabling the
edges of event just makes hotplug no longer function. The only
other option is to disable the detection of the MADC by disabling
BCIMFEN4::BATSTSMCHGEN (battery presence detector) - but then, we can
never again detect battery reconnection.

So, detect battery presence based on precharge(which is hardware
automatic state) or default main charger configuration at the time of
probe and enable charger logic only if battery was present.

Reported-by: Russell King <linux@arm.linux.org.uk>
Tested-by: Tony Lindgren <tony@atomide.com>
Signed-off-by: Nishanth Menon <nm@ti.com>
Signed-off-by: Sebastian Reichel <sre@kernel.org>

authored by

Nishanth Menon and committed by
Sebastian Reichel
61a7784e 030494e7

+43 -1
+43 -1
drivers/power/twl4030_charger.c
··· 28 28 #define TWL4030_BCIICHG 0x08 29 29 #define TWL4030_BCIVAC 0x0a 30 30 #define TWL4030_BCIVBUS 0x0c 31 + #define TWL4030_BCIMFSTS3 0x0F 31 32 #define TWL4030_BCIMFSTS4 0x10 32 33 #define TWL4030_BCICTL1 0x23 33 34 #define TWL4030_BB_CFG 0x12 35 + 36 + #define TWL4030_BCIMFSTS1 0x01 34 37 35 38 #define TWL4030_BCIAUTOWEN BIT(5) 36 39 #define TWL4030_CONFIG_DONE BIT(4) ··· 54 51 #define TWL4030_BBISEL_150uA 0x01 55 52 #define TWL4030_BBISEL_500uA 0x02 56 53 #define TWL4030_BBISEL_1000uA 0x03 54 + 55 + #define TWL4030_BATSTSPCHG BIT(2) 56 + #define TWL4030_BATSTSMCHG BIT(6) 57 57 58 58 /* BCI interrupts */ 59 59 #define TWL4030_WOVF BIT(0) /* Watchdog overflow */ ··· 148 142 return ret; 149 143 150 144 return temp | val; 145 + } 146 + 147 + /* 148 + * Check if Battery Pack was present 149 + */ 150 + static int twl4030_is_battery_present(struct twl4030_bci *bci) 151 + { 152 + int ret; 153 + u8 val = 0; 154 + 155 + /* Battery presence in Main charge? */ 156 + ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val, TWL4030_BCIMFSTS3); 157 + if (ret) 158 + return ret; 159 + if (val & TWL4030_BATSTSMCHG) 160 + return 0; 161 + 162 + /* 163 + * OK, It could be that bootloader did not enable main charger, 164 + * pre-charge is h/w auto. So, Battery presence in Pre-charge? 165 + */ 166 + ret = twl_i2c_read_u8(TWL4030_MODULE_PRECHARGE, &val, 167 + TWL4030_BCIMFSTS1); 168 + if (ret) 169 + return ret; 170 + if (val & TWL4030_BATSTSPCHG) 171 + return 0; 172 + 173 + return -ENODEV; 151 174 } 152 175 153 176 /* ··· 576 541 bci->irq_chg = platform_get_irq(pdev, 0); 577 542 bci->irq_bci = platform_get_irq(pdev, 1); 578 543 579 - platform_set_drvdata(pdev, bci); 544 + /* Only proceed further *IF* battery is physically present */ 545 + ret = twl4030_is_battery_present(bci); 546 + if (ret) { 547 + dev_crit(&pdev->dev, "Battery was not detected:%d\n", ret); 548 + goto fail_no_battery; 549 + } 580 550 551 + platform_set_drvdata(pdev, bci); 581 552 bci->ac.name = "twl4030_ac"; 582 553 bci->ac.type = POWER_SUPPLY_TYPE_MAINS; 583 554 bci->ac.properties = twl4030_charger_props; ··· 674 633 fail_register_usb: 675 634 power_supply_unregister(&bci->ac); 676 635 fail_register_ac: 636 + fail_no_battery: 677 637 kfree(bci); 678 638 679 639 return ret;