power: twl4030_charger: detect battery presence prior to enabling charger
authorNishanth Menon <nm@ti.com>
Wed, 28 May 2014 21:46:49 +0000 (16:46 -0500)
committerSebastian Reichel <sre@kernel.org>
Wed, 23 Jul 2014 11:58:33 +0000 (13:58 +0200)
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>
drivers/power/twl4030_charger.c

index f14108844e1a100b8ae61d6bbd9f7124653218f0..2598c588006e7784f6d989ea7da9f5f2783a9304 100644 (file)
 #define TWL4030_BCIICHG                0x08
 #define TWL4030_BCIVAC         0x0a
 #define TWL4030_BCIVBUS                0x0c
+#define TWL4030_BCIMFSTS3      0x0F
 #define TWL4030_BCIMFSTS4      0x10
 #define TWL4030_BCICTL1                0x23
 #define TWL4030_BB_CFG         0x12
 
+#define TWL4030_BCIMFSTS1      0x01
+
 #define TWL4030_BCIAUTOWEN     BIT(5)
 #define TWL4030_CONFIG_DONE    BIT(4)
 #define TWL4030_BCIAUTOUSB     BIT(1)
@@ -52,6 +55,9 @@
 #define TWL4030_BBISEL_500uA   0x02
 #define TWL4030_BBISEL_1000uA  0x03
 
+#define TWL4030_BATSTSPCHG     BIT(2)
+#define TWL4030_BATSTSMCHG     BIT(6)
+
 /* BCI interrupts */
 #define TWL4030_WOVF           BIT(0) /* Watchdog overflow */
 #define TWL4030_TMOVF          BIT(1) /* Timer overflow */
@@ -144,6 +150,35 @@ static int twl4030bci_read_adc_val(u8 reg)
        return temp | val;
 }
 
+/*
+ * Check if Battery Pack was present
+ */
+static int twl4030_is_battery_present(struct twl4030_bci *bci)
+{
+       int ret;
+       u8 val = 0;
+
+       /* Battery presence in Main charge? */
+       ret = twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &val, TWL4030_BCIMFSTS3);
+       if (ret)
+               return ret;
+       if (val & TWL4030_BATSTSMCHG)
+               return 0;
+
+       /*
+        * OK, It could be that bootloader did not enable main charger,
+        * pre-charge is h/w auto. So, Battery presence in Pre-charge?
+        */
+       ret = twl_i2c_read_u8(TWL4030_MODULE_PRECHARGE, &val,
+                             TWL4030_BCIMFSTS1);
+       if (ret)
+               return ret;
+       if (val & TWL4030_BATSTSPCHG)
+               return 0;
+
+       return -ENODEV;
+}
+
 /*
  * Check if VBUS power is present
  */
@@ -541,8 +576,14 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
        bci->irq_chg = platform_get_irq(pdev, 0);
        bci->irq_bci = platform_get_irq(pdev, 1);
 
-       platform_set_drvdata(pdev, bci);
+       /* Only proceed further *IF* battery is physically present */
+       ret = twl4030_is_battery_present(bci);
+       if  (ret) {
+               dev_crit(&pdev->dev, "Battery was not detected:%d\n", ret);
+               goto fail_no_battery;
+       }
 
+       platform_set_drvdata(pdev, bci);
        bci->ac.name = "twl4030_ac";
        bci->ac.type = POWER_SUPPLY_TYPE_MAINS;
        bci->ac.properties = twl4030_charger_props;
@@ -633,6 +674,7 @@ fail_chg_irq:
 fail_register_usb:
        power_supply_unregister(&bci->ac);
 fail_register_ac:
+fail_no_battery:
        kfree(bci);
 
        return ret;