summaryrefslogtreecommitdiff
path: root/drivers/power
diff options
context:
space:
mode:
authorJohan Palsson <johan.palsson@stericsson.com>2011-03-04 11:09:57 +0100
committerUlf Hansson <ulf.hansson@stericsson.com>2011-09-19 15:14:54 +0200
commitf4d79c68d460600e5ad1ab5a4833e2768e2c2017 (patch)
tree82f5017023c76b8711e06ce6d14bc9d35388813f /drivers/power
parente2ebbfea7393917ad7c876b88336c7376a7f6178 (diff)
power: ab8500_bm: Allow USB charger voltage to collapse
When the requested USB charger current is more than the charger can deliver, the output voltage will drop and AB8500 will detect it as a not-ok charger. In this case we will try to lower the current and try charging again ST-Ericsson Linux next: - ST-Ericsson ID: ER327042 ST-Ericsson FOSS-OUT ID: Trivial Change-Id: I620663643d08b214b38a7498d891f30e3e4342ca Signed-off-by: Johan Palsson <johan.palsson@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/17544 Reviewed-by: Karl KOMIEROWSKI <karl.komierowski@stericsson.com> Reviewed-by: Jonas ABERG <jonas.aberg@stericsson.com>
Diffstat (limited to 'drivers/power')
-rw-r--r--drivers/power/ab8500_chargalg.c120
-rw-r--r--drivers/power/ab8500_charger.c46
2 files changed, 127 insertions, 39 deletions
diff --git a/drivers/power/ab8500_chargalg.c b/drivers/power/ab8500_chargalg.c
index 9a1efd7c078..f4e4528b15c 100644
--- a/drivers/power/ab8500_chargalg.c
+++ b/drivers/power/ab8500_chargalg.c
@@ -51,6 +51,10 @@ struct ab8500_chargalg_charger_info {
int usb_curr;
int ac_volt;
int ac_curr;
+ int usb_vset;
+ int usb_iset;
+ int ac_vset;
+ int ac_iset;
};
struct ab8500_chargalg_suspension_status {
@@ -143,6 +147,7 @@ struct ab8500_chargalg_events {
bool usb_wd_expired;
bool ac_cv_active;
bool usb_cv_active;
+ bool vbus_collapsed;
};
/**
@@ -154,6 +159,9 @@ struct ab8500_chargalg_events {
* @condition_cnt: number of iterations needed before a new charger current
is set
* @max_current: maximum charger current
+ * @wait_cnt: to avoid too fast current step down in case of charger
+ * voltage collapse, we insert this delay between step
+ * down
* @level: tells in how many steps the charging current has been
increased
*/
@@ -163,6 +171,7 @@ struct ab8500_charge_curr_maximization {
int test_delta_i;
int condition_cnt;
int max_current;
+ int wait_cnt;
u8 level;
};
@@ -464,6 +473,9 @@ static int ab8500_chargalg_ac_en(struct ab8500_chargalg *di, int enable,
if (di->ac_chg->max_out_curr)
iset = min(iset, di->ac_chg->max_out_curr);
+ di->chg_info.ac_iset = iset;
+ di->chg_info.ac_vset = vset;
+
return di->ac_chg->ops.enable(di->ac_chg, enable, vset, iset);
}
@@ -489,6 +501,9 @@ static int ab8500_chargalg_usb_en(struct ab8500_chargalg *di, int enable,
if (di->usb_chg->max_out_curr)
iset = min(iset, di->usb_chg->max_out_curr);
+ di->chg_info.usb_iset = iset;
+ di->chg_info.usb_vset = vset;
+
return di->usb_chg->ops.enable(di->usb_chg, enable, vset, iset);
}
@@ -505,11 +520,30 @@ static int ab8500_chargalg_update_chg_curr(struct ab8500_chargalg *di,
{
/* Check if charger exists and update current if charging */
if (di->ac_chg && di->ac_chg->ops.update_curr &&
- di->chg_info.charger_type & AC_CHG)
+ di->chg_info.charger_type & AC_CHG) {
+ /*
+ * Select maximum of what both the charger
+ * and the battery supports
+ */
+ if (di->ac_chg->max_out_curr)
+ iset = min(iset, di->ac_chg->max_out_curr);
+
+ di->chg_info.ac_iset = iset;
+
return di->ac_chg->ops.update_curr(di->ac_chg, iset);
- else if (di->usb_chg && di->usb_chg->ops.update_curr &&
- di->chg_info.charger_type & USB_CHG)
+ } else if (di->usb_chg && di->usb_chg->ops.update_curr &&
+ di->chg_info.charger_type & USB_CHG) {
+ /*
+ * Select maximum of what both the charger
+ * and the battery supports
+ */
+ if (di->usb_chg->max_out_curr)
+ iset = min(iset, di->usb_chg->max_out_curr);
+
+ di->chg_info.usb_iset = iset;
+
return di->usb_chg->ops.update_curr(di->usb_chg, iset);
+ }
return -ENXIO;
}
@@ -697,22 +731,26 @@ static enum maxim_ret ab8500_chargalg_chg_curr_maxim(struct ab8500_chargalg *di)
return MAXIM_RET_NOACTION;
delta_i = di->ccm.original_iset - di->batt_data.inst_curr;
- dev_dbg(di->dev, "[CHARGALG_OPTI_DATA], Ucv ,%d, Dcv ,%d, cnt ,%d,"
- " cur iset ,%d, avg i ,%d, Ibat inst: ,%d,mA, Orig iset: ,%d, "
- "delta_i ,%d, Max ,%d,mA"
- " AC chg curr: ,%d,mA"
- " USB chg curr: ,%d,mA\n",
- di->events.usb_cv_active,
- di->events.ac_cv_active,
- di->ccm.condition_cnt,
- di->ccm.current_iset,
- di->batt_data.avg_curr,
- di->batt_data.inst_curr,
- di->ccm.original_iset,
- delta_i,
- di->ccm.max_current,
- di->chg_info.ac_curr,
- di->chg_info.usb_curr);
+
+ if (di->events.vbus_collapsed) {
+ dev_dbg(di->dev, "Charger voltage has collapsed\n");
+ if (di->ccm.wait_cnt++ == 0) {
+ dev_dbg(di->dev, "lowering current\n");
+ di->ccm.condition_cnt = di->bat->maxi->wait_cycles;
+ di->ccm.max_current =
+ di->ccm.current_iset - di->ccm.test_delta_i;
+ di->ccm.current_iset = di->ccm.max_current;
+ di->ccm.level--;
+ return MAXIM_RET_CHANGE;
+ } else {
+ dev_dbg(di->dev, "waiting\n");
+ /* Let's go in here twice before lowering curr again */
+ di->ccm.wait_cnt = (di->ccm.wait_cnt + 1) % 4;
+ return MAXIM_RET_NOACTION;
+ }
+ }
+
+ di->ccm.wait_cnt = 0;
if ((di->batt_data.inst_curr > di->ccm.original_iset)) {
dev_dbg(di->dev, " Maximization Ibat (%dmA) too high"
@@ -730,15 +768,6 @@ static enum maxim_ret ab8500_chargalg_chg_curr_maxim(struct ab8500_chargalg *di)
return MAXIM_RET_IBAT_TOO_HIGH;
}
- /* Make sure first we are actually charging.. */
- if (di->charge_status != POWER_SUPPLY_STATUS_CHARGING ||
- (di->events.usb_cv_active || di->events.ac_cv_active)) {
- di->ccm.condition_cnt = di->bat->maxi->wait_cycles;
- di->ccm.level = 0;
- di->ccm.current_iset = di->ccm.original_iset;
- return MAXIM_RET_NOACTION;
- }
-
if (delta_i > di->ccm.test_delta_i &&
(di->ccm.current_iset + di->ccm.test_delta_i) <
di->ccm.max_current) {
@@ -756,8 +785,6 @@ static enum maxim_ret ab8500_chargalg_chg_curr_maxim(struct ab8500_chargalg *di)
di->ccm.level);
return MAXIM_RET_CHANGE;
} else {
- dev_dbg(di->dev, " Maximization, wait for"
- " counter, to go %d\n", di->ccm.condition_cnt);
return MAXIM_RET_NOACTION;
}
} else {
@@ -1085,7 +1112,19 @@ static int ab8500_chargalg_get_ext_psy_data(struct device *dev, void *data)
break;
case POWER_SUPPLY_PROP_CURRENT_AVG:
- di->batt_data.avg_curr = ret.intval / 1000;
+ switch (ext->type) {
+ case POWER_SUPPLY_TYPE_BATTERY:
+ di->batt_data.avg_curr = ret.intval / 1000;
+ break;
+ case POWER_SUPPLY_TYPE_USB:
+ if (ret.intval)
+ di->events.vbus_collapsed = true;
+ else
+ di->events.vbus_collapsed = false;
+ break;
+ default:
+ break;
+ }
break;
case POWER_SUPPLY_PROP_CAPACITY:
di->batt_data.percent = ret.intval;
@@ -1175,7 +1214,12 @@ static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di)
}
/* Main or USB charger not ok. */
else if (di->events.mainextchnotok || di->events.usbchargernotok) {
- if (di->charge_state != STATE_CHG_NOT_OK)
+ /*
+ * If vbus_collapsed is set, we have to lower the charger
+ * current, which is done in the normal state below
+ */
+ if (di->charge_state != STATE_CHG_NOT_OK &&
+ !di->events.vbus_collapsed)
ab8500_chargalg_state_to(di, STATE_CHG_NOT_OK_INIT);
}
/* VBUS, Main or VBAT OVV. */
@@ -1215,8 +1259,8 @@ static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di)
dev_dbg(di->dev,
"[CHARGALG] Vb %d Ib_avg %d Ib_inst %d Tb %d Cap %d Maint %d "
"State %s Active_chg %d Chg_status %d AC %d USB %d "
- "AC_online %d USB_online %d "
- "AC_CV %d USB_CV %d\n",
+ "AC_online %d USB_online %d AC_CV %d USB_CV %d AC_I %d "
+ "USB_I %d AC_Vset %d AC_Iset %d USB_Vset %d USB_Iset %d\n",
di->batt_data.volt,
di->batt_data.avg_curr,
di->batt_data.inst_curr,
@@ -1231,7 +1275,13 @@ static void ab8500_chargalg_algorithm(struct ab8500_chargalg *di)
di->chg_info.online_chg & AC_CHG,
di->chg_info.online_chg & USB_CHG,
di->events.ac_cv_active,
- di->events.usb_cv_active);
+ di->events.usb_cv_active,
+ di->chg_info.ac_curr,
+ di->chg_info.usb_curr,
+ di->chg_info.ac_vset,
+ di->chg_info.ac_iset,
+ di->chg_info.usb_vset,
+ di->chg_info.usb_iset);
switch (di->charge_state) {
case STATE_HANDHELD_INIT:
diff --git a/drivers/power/ab8500_charger.c b/drivers/power/ab8500_charger.c
index 4aff8d03eae..6c0b8af2f7f 100644
--- a/drivers/power/ab8500_charger.c
+++ b/drivers/power/ab8500_charger.c
@@ -151,6 +151,7 @@ struct ab8500_charger_event_flags {
bool vbus_ovv;
bool usbchargernotok;
bool chgwdexp;
+ bool vbus_collapse;
};
struct ab8500_charger_usb_state {
@@ -236,6 +237,7 @@ static enum power_supply_property ab8500_charger_ac_props[] = {
/* USB properties */
static enum power_supply_property ab8500_charger_usb_props[] = {
POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_CURRENT_AVG,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
@@ -486,9 +488,22 @@ static int ab8500_charger_max_usb_curr(struct ab8500_charger *di,
case USB_STAT_ACA_RID_C_NM:
di->max_usb_in_curr = USB_CH_IP_CUR_LVL_1P5;
break;
+ case USB_STAT_RESERVED:
+ /*
+ * This state is used to indicate that VBUS has dropped below
+ * the detection level 4 times in a row. This is due to the
+ * charger output current is set to high making the charger
+ * voltage collapse. This have to be propagated through to
+ * chargalg. This is done using the property
+ * POWER_SUPPLY_PROP_CURRENT_AVG = 1
+ */
+ di->flags.vbus_collapse = true;
+ dev_dbg(di->dev, "USB Type - USB_STAT_RESERVED "
+ "VBUS has collapsed\n");
+ ret = -1;
+ break;
case USB_STAT_HM_IDGND:
case USB_STAT_NOT_CONFIGURED:
- case USB_STAT_RESERVED:
case USB_STAT_NOT_VALID_LINK:
dev_err(di->dev, "USB Type - Charging not allowed\n");
di->max_usb_in_curr = USB_CH_IP_CUR_LVL_0P05;
@@ -1209,8 +1224,19 @@ static int ab8500_charger_update_charger_current(struct ux500_charger *charger,
ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
AB8500_CH_OPT_CRNTLVL_REG, (u8) curr_index);
- if (ret)
+ if (ret) {
dev_err(di->dev, "%s write failed\n", __func__);
+ return ret;
+ }
+
+ /* Reset the main and usb drop input current measurement counter */
+ ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
+ AB8500_CHARGER_CTRL,
+ 0x1);
+ if (ret) {
+ dev_err(di->dev, "%s write failed\n", __func__);
+ return ret;
+ }
return ret;
}
@@ -1513,10 +1539,12 @@ static void ab8500_charger_check_usbchargernotok_work(struct work_struct *work)
dev_err(di->dev, "%s ab8500 read failed\n", __func__);
return;
}
- if (reg_value & VBUS_CH_NOK)
+ if (reg_value & VBUS_CH_NOK) {
di->flags.usbchargernotok = true;
- else
+ } else {
di->flags.usbchargernotok = false;
+ di->flags.vbus_collapse = false;
+ }
power_supply_changed(&di->usb_chg.psy);
}
@@ -1965,6 +1993,16 @@ static int ab8500_charger_usb_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_CURRENT_NOW:
val->intval = ab8500_charger_get_usb_current(di) * 1000;
break;
+ case POWER_SUPPLY_PROP_CURRENT_AVG:
+ /*
+ * This property is used to indicate when VBUS has collapsed
+ * due to too high output current from the USB charger
+ */
+ if (di->flags.vbus_collapse)
+ val->intval = 1;
+ else
+ val->intval = 0;
+ break;
default:
return -EINVAL;
}