ab8500-bm: Add usb power path support
[linux-2.6-block.git] / drivers / power / abx500_chargalg.c
index f043c0851a7600d1417711b7f1ec4364e9331985..a9b8efdafb8f8ae81de0769bae2fcfc724c60667 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/mfd/abx500.h>
 #include <linux/mfd/abx500/ux500_chargalg.h>
 #include <linux/mfd/abx500/ab8500-bm.h>
+#include <linux/notifier.h>
 
 /* Watchdog kick interval */
 #define CHG_WD_INTERVAL                        (6 * HZ)
@@ -33,6 +34,9 @@
 /* End-of-charge criteria counter */
 #define EOC_COND_CNT                   10
 
+/* Plus margin for the low battery threshold */
+#define BAT_PLUS_MARGIN                (100)
+
 #define to_abx500_chargalg_device_info(x) container_of((x), \
        struct abx500_chargalg, chargalg_psy);
 
@@ -82,6 +86,7 @@ enum abx500_chargalg_states {
        STATE_HW_TEMP_PROTECT_INIT,
        STATE_HW_TEMP_PROTECT,
        STATE_NORMAL_INIT,
+       STATE_USB_PP_PRE_CHARGE,
        STATE_NORMAL,
        STATE_WAIT_FOR_RECHARGE_INIT,
        STATE_WAIT_FOR_RECHARGE,
@@ -113,6 +118,7 @@ static const char *states[] = {
        "HW_TEMP_PROTECT_INIT",
        "HW_TEMP_PROTECT",
        "NORMAL_INIT",
+       "USB_PP_PRE_CHARGE",
        "NORMAL",
        "WAIT_FOR_RECHARGE_INIT",
        "WAIT_FOR_RECHARGE",
@@ -204,6 +210,7 @@ enum maxim_ret {
  * @batt_data:         data of the battery
  * @susp_status:       current charger suspension status
  * @bm:                Platform specific battery management information
+ * @parent:            pointer to the struct abx500
  * @chargalg_psy:      structure that holds the battery properties exposed by
  *                     the charging algorithm
  * @events:            structure for information about events triggered
@@ -227,6 +234,7 @@ struct abx500_chargalg {
        struct abx500_chargalg_charger_info chg_info;
        struct abx500_chargalg_battery_data batt_data;
        struct abx500_chargalg_suspension_status susp_status;
+       struct ab8500 *parent;
        struct abx500_bm_data *bm;
        struct power_supply chargalg_psy;
        struct ux500_charger *ac_chg;
@@ -241,6 +249,9 @@ struct abx500_chargalg {
        struct kobject chargalg_kobject;
 };
 
+/*External charger prepare notifier*/
+BLOCKING_NOTIFIER_HEAD(charger_notifier_list);
+
 /* Main battery properties */
 static enum power_supply_property abx500_chargalg_props[] = {
        POWER_SUPPLY_PROP_STATUS,
@@ -303,6 +314,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
@@ -477,6 +512,8 @@ static int abx500_chargalg_kick_watchdog(struct abx500_chargalg *di)
 static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable,
        int vset, int iset)
 {
+       static int abx500_chargalg_ex_ac_enable_toggle;
+
        if (!di->ac_chg || !di->ac_chg->ops.enable)
                return -ENXIO;
 
@@ -489,6 +526,14 @@ static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable,
        di->chg_info.ac_iset = iset;
        di->chg_info.ac_vset = vset;
 
+       /* Enable external charger */
+       if (enable && di->ac_chg->external &&
+           !abx500_chargalg_ex_ac_enable_toggle) {
+               blocking_notifier_call_chain(&charger_notifier_list,
+                                            0, di->dev);
+               abx500_chargalg_ex_ac_enable_toggle++;
+       }
+
        return di->ac_chg->ops.enable(di->ac_chg, enable, vset, iset);
 }
 
@@ -520,6 +565,37 @@ static int abx500_chargalg_usb_en(struct abx500_chargalg *di, int enable,
        return di->usb_chg->ops.enable(di->usb_chg, enable, vset, iset);
 }
 
+ /**
+ * ab8540_chargalg_usb_pp_en() - Enable/ disable USB power path
+ * @di:                pointer to the abx500_chargalg structure
+ * @enable:    power path enable/disable
+ *
+ * The USB power path will be enable/ disable
+ */
+static int ab8540_chargalg_usb_pp_en(struct abx500_chargalg *di, bool enable)
+{
+       if (!di->usb_chg || !di->usb_chg->ops.pp_enable)
+               return -ENXIO;
+
+       return di->usb_chg->ops.pp_enable(di->usb_chg, enable);
+}
+
+/**
+ * ab8540_chargalg_usb_pre_chg_en() - Enable/ disable USB pre-charge
+ * @di:                pointer to the abx500_chargalg structure
+ * @enable:    USB pre-charge enable/disable
+ *
+ * The USB USB pre-charge will be enable/ disable
+ */
+static int ab8540_chargalg_usb_pre_chg_en(struct abx500_chargalg *di,
+                                         bool enable)
+{
+       if (!di->usb_chg || !di->usb_chg->ops.pre_chg_enable)
+               return -ENXIO;
+
+       return di->usb_chg->ops.pre_chg_enable(di->usb_chg, enable);
+}
+
 /**
  * abx500_chargalg_update_chg_curr() - Update charger current
  * @di:                pointer to the abx500_chargalg structure
@@ -725,6 +801,9 @@ static void abx500_chargalg_end_of_charge(struct abx500_chargalg *di)
                di->batt_data.avg_curr > 0) {
                if (++di->eoc_cnt >= EOC_COND_CNT) {
                        di->eoc_cnt = 0;
+                       if ((di->chg_info.charger_type & USB_CHG) &&
+                          (di->usb_chg->power_path))
+                               ab8540_chargalg_usb_pp_en(di, true);
                        di->charge_status = POWER_SUPPLY_STATUS_FULL;
                        di->maintenance_chg = true;
                        dev_dbg(di->dev, "EOC reached!\n");
@@ -1217,6 +1296,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,
@@ -1227,6 +1307,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
@@ -1416,6 +1504,22 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
                break;
 
        case STATE_NORMAL_INIT:
+               if ((di->chg_info.charger_type & USB_CHG) &&
+                               di->usb_chg->power_path) {
+                       if (di->batt_data.volt >
+                           (di->bm->fg_params->lowbat_threshold +
+                            BAT_PLUS_MARGIN)) {
+                               ab8540_chargalg_usb_pre_chg_en(di, false);
+                               ab8540_chargalg_usb_pp_en(di, false);
+                       } else {
+                               ab8540_chargalg_usb_pp_en(di, true);
+                               ab8540_chargalg_usb_pre_chg_en(di, true);
+                               abx500_chargalg_state_to(di,
+                                       STATE_USB_PP_PRE_CHARGE);
+                               break;
+                       }
+               }
+
                abx500_chargalg_start_charging(di,
                        di->bm->bat_type[di->bm->batt_id].normal_vol_lvl,
                        di->bm->bat_type[di->bm->batt_id].normal_cur_lvl);
@@ -1430,6 +1534,13 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
 
                break;
 
+       case STATE_USB_PP_PRE_CHARGE:
+               if (di->batt_data.volt >
+                       (di->bm->fg_params->lowbat_threshold +
+                       BAT_PLUS_MARGIN))
+                       abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+               break;
+
        case STATE_NORMAL:
                handle_maxim_chg_curr(di);
                if (di->charge_status == POWER_SUPPLY_STATUS_FULL &&
@@ -1873,8 +1984,9 @@ static int abx500_chargalg_probe(struct platform_device *pdev)
                }
        }
 
-       /* get device struct */
+       /* get device struct and parent */
        di->dev = &pdev->dev;
+       di->parent = dev_get_drvdata(pdev->dev.parent);
 
        /* chargalg supply */
        di->chargalg_psy.name = "abx500_chargalg";