]> git.karo-electronics.de Git - linux-beck.git/commitdiff
ab8500-bm: Quick re-attach charging behaviour
authorLee Jones <lee.jones@linaro.org>
Thu, 14 Feb 2013 09:24:10 +0000 (09:24 +0000)
committerLee Jones <lee.jones@linaro.org>
Thu, 7 Mar 2013 04:35:38 +0000 (12:35 +0800)
Due to a bug in some AB8500 ASICs charger removal cannot always
be detected if the removal and reinsertion is done to close in time.
This patch detects above described case and handles the situation
so that charging will be kept turned on.

Signed-off-by: Lee Jones <lee.jones@linaro.org>
drivers/power/ab8500_charger.c
drivers/power/abx500_chargalg.c
include/linux/mfd/abx500/ux500_chargalg.h

index dcd3c6feca97400eb88da8d12752002c3c77890a..3eb23cf9ff476b63d3eb74c22918e6c029fc4969 100644 (file)
@@ -52,6 +52,7 @@
 #define VBUS_DET_DBNC100               0x02
 #define VBUS_DET_DBNC1                 0x01
 #define OTP_ENABLE_WD                  0x01
+#define DROP_COUNT_RESET               0x01
 
 #define MAIN_CH_INPUT_CURR_SHIFT       4
 #define VBUS_IN_CURR_LIM_SHIFT         4
@@ -1677,6 +1678,105 @@ static int ab8500_charger_usb_en(struct ux500_charger *charger,
        return ret;
 }
 
+/**
+ * ab8500_charger_usb_check_enable() - enable usb charging
+ * @charger:   pointer to the ux500_charger structure
+ * @vset:      charging voltage
+ * @iset:      charger output current
+ *
+ * Check if the VBUS charger has been disconnected and reconnected without
+ * AB8500 rising an interrupt. Returns 0 on success.
+ */
+static int ab8500_charger_usb_check_enable(struct ux500_charger *charger,
+       int vset, int iset)
+{
+       u8 usbch_ctrl1 = 0;
+       int ret = 0;
+
+       struct ab8500_charger *di = to_ab8500_charger_usb_device_info(charger);
+
+       if (!di->usb.charger_connected)
+               return ret;
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+                               AB8500_USBCH_CTRL1_REG, &usbch_ctrl1);
+       if (ret < 0) {
+               dev_err(di->dev, "ab8500 read failed %d\n", __LINE__);
+               return ret;
+       }
+       dev_dbg(di->dev, "USB charger ctrl: 0x%02x\n", usbch_ctrl1);
+
+       if (!(usbch_ctrl1 & USB_CH_ENA)) {
+               dev_info(di->dev, "Charging has been disabled abnormally and will be re-enabled\n");
+
+               ret = abx500_mask_and_set_register_interruptible(di->dev,
+                                       AB8500_CHARGER, AB8500_CHARGER_CTRL,
+                                       DROP_COUNT_RESET, DROP_COUNT_RESET);
+               if (ret < 0) {
+                       dev_err(di->dev, "ab8500 write failed %d\n", __LINE__);
+                       return ret;
+               }
+
+               ret = ab8500_charger_usb_en(&di->usb_chg, true, vset, iset);
+               if (ret < 0) {
+                       dev_err(di->dev, "Failed to enable VBUS charger %d\n",
+                                       __LINE__);
+                       return ret;
+               }
+       }
+       return ret;
+}
+
+/**
+ * ab8500_charger_ac_check_enable() - enable usb charging
+ * @charger:   pointer to the ux500_charger structure
+ * @vset:      charging voltage
+ * @iset:      charger output current
+ *
+ * Check if the AC charger has been disconnected and reconnected without
+ * AB8500 rising an interrupt. Returns 0 on success.
+ */
+static int ab8500_charger_ac_check_enable(struct ux500_charger *charger,
+       int vset, int iset)
+{
+       u8 mainch_ctrl1 = 0;
+       int ret = 0;
+
+       struct ab8500_charger *di = to_ab8500_charger_ac_device_info(charger);
+
+       if (!di->ac.charger_connected)
+               return ret;
+
+       ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
+                               AB8500_MCH_CTRL1, &mainch_ctrl1);
+       if (ret < 0) {
+               dev_err(di->dev, "ab8500 read failed %d\n", __LINE__);
+               return ret;
+       }
+       dev_dbg(di->dev, "AC charger ctrl: 0x%02x\n", mainch_ctrl1);
+
+       if (!(mainch_ctrl1 & MAIN_CH_ENA)) {
+               dev_info(di->dev, "Charging has been disabled abnormally and will be re-enabled\n");
+
+               ret = abx500_mask_and_set_register_interruptible(di->dev,
+                                       AB8500_CHARGER, AB8500_CHARGER_CTRL,
+                                       DROP_COUNT_RESET, DROP_COUNT_RESET);
+
+               if (ret < 0) {
+                       dev_err(di->dev, "ab8500 write failed %d\n", __LINE__);
+                       return ret;
+               }
+
+               ret = ab8500_charger_ac_en(&di->usb_chg, true, vset, iset);
+               if (ret < 0) {
+                       dev_err(di->dev, "failed to enable AC charger %d\n",
+                               __LINE__);
+                       return ret;
+               }
+       }
+       return ret;
+}
+
 /**
  * ab8500_charger_watchdog_kick() - kick charger watchdog
  * @di:                pointer to the ab8500_charger structure
@@ -1734,8 +1834,7 @@ static int ab8500_charger_update_charger_current(struct ux500_charger *charger,
 
        /* Reset the main and usb drop input current measurement counter */
        ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
-                               AB8500_CHARGER_CTRL,
-                               0x1);
+                               AB8500_CHARGER_CTRL, DROP_COUNT_RESET);
        if (ret) {
                dev_err(di->dev, "%s write failed\n", __func__);
                return ret;
@@ -3221,6 +3320,7 @@ static int ab8500_charger_probe(struct platform_device *pdev)
        di->ac_chg.psy.num_supplicants = ARRAY_SIZE(supply_interface),
        /* ux500_charger sub-class */
        di->ac_chg.ops.enable = &ab8500_charger_ac_en;
+       di->ac_chg.ops.check_enable = &ab8500_charger_ac_check_enable;
        di->ac_chg.ops.kick_wd = &ab8500_charger_watchdog_kick;
        di->ac_chg.ops.update_curr = &ab8500_charger_update_charger_current;
        di->ac_chg.max_out_volt = ab8500_charger_voltage_map[
@@ -3242,6 +3342,7 @@ static int ab8500_charger_probe(struct platform_device *pdev)
        di->usb_chg.psy.num_supplicants = ARRAY_SIZE(supply_interface),
        /* ux500_charger sub-class */
        di->usb_chg.ops.enable = &ab8500_charger_usb_en;
+       di->usb_chg.ops.check_enable = &ab8500_charger_usb_check_enable;
        di->usb_chg.ops.kick_wd = &ab8500_charger_watchdog_kick;
        di->usb_chg.ops.update_curr = &ab8500_charger_update_charger_current;
        di->usb_chg.max_out_volt = ab8500_charger_voltage_map[
index 31507bfe549c403fffb02744e338622bde122d24..8ab65a3a81900431fa74441850c8ccec663549a7 100644 (file)
@@ -305,6 +305,30 @@ static void abx500_chargalg_state_to(struct abx500_chargalg *di,
        di->charge_state = state;
 }
 
+static int abx500_chargalg_check_charger_enable(struct abx500_chargalg *di)
+{
+       switch (di->charge_state) {
+       case STATE_NORMAL:
+       case STATE_MAINTENANCE_A:
+       case STATE_MAINTENANCE_B:
+               break;
+       default:
+               return 0;
+       }
+
+       if (di->chg_info.charger_type & USB_CHG) {
+               return di->usb_chg->ops.check_enable(di->usb_chg,
+                         di->bm->bat_type[di->bm->batt_id].normal_vol_lvl,
+                         di->bm->bat_type[di->bm->batt_id].normal_cur_lvl);
+       } else if ((di->chg_info.charger_type & AC_CHG) &&
+                  !(di->ac_chg->external)) {
+               return di->ac_chg->ops.check_enable(di->ac_chg,
+                         di->bm->bat_type[di->bm->batt_id].normal_vol_lvl,
+                         di->bm->bat_type[di->bm->batt_id].normal_cur_lvl);
+       }
+       return 0;
+}
+
 /**
  * abx500_chargalg_check_charger_connection() - Check charger connection change
  * @di:                pointer to the abx500_chargalg structure
@@ -1219,6 +1243,7 @@ static void abx500_chargalg_external_power_changed(struct power_supply *psy)
 static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
 {
        int charger_status;
+       int ret;
 
        /* Collect data from all power_supply class devices */
        class_for_each_device(power_supply_class, NULL,
@@ -1229,6 +1254,14 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
        abx500_chargalg_check_charger_voltage(di);
 
        charger_status = abx500_chargalg_check_charger_connection(di);
+
+       if (is_ab8500(di->parent)) {
+               ret = abx500_chargalg_check_charger_enable(di);
+               if (ret < 0)
+                       dev_err(di->dev, "Checking charger is enabled error"
+                                       ": Returned Value %d\n", ret);
+       }
+
        /*
         * First check if we have a charger connected.
         * Also we don't allow charging of unknown batteries if configured
index d43ac0f355263e7ac188d09616fef59476feec03..110d12f09548e2083e67a90f8fb30851ba378692 100644 (file)
@@ -17,6 +17,7 @@ struct ux500_charger;
 
 struct ux500_charger_ops {
        int (*enable) (struct ux500_charger *, int, int, int);
+       int (*check_enable) (struct ux500_charger *, int, int);
        int (*kick_wd) (struct ux500_charger *);
        int (*update_curr) (struct ux500_charger *, int);
 };