in supply/ab8500_charger.c [1393:1558]
static int ab8500_charger_ac_en(struct ux500_charger *charger,
int enable, int vset_uv, int iset_ua)
{
int ret;
int volt_index;
int curr_index;
int input_curr_index;
u8 overshoot = 0;
struct ab8500_charger *di = to_ab8500_charger_ac_device_info(charger);
if (enable) {
/* Check if AC is connected */
if (!di->ac.charger_connected) {
dev_err(di->dev, "AC charger not connected\n");
return -ENXIO;
}
/* Enable AC charging */
dev_dbg(di->dev, "Enable AC: %duV %duA\n", vset_uv, iset_ua);
/*
* Due to a bug in AB8500, BTEMP_HIGH/LOW interrupts
* will be triggered every time we enable the VDD ADC supply.
* This will turn off charging for a short while.
* It can be avoided by having the supply on when
* there is a charger enabled. Normally the VDD ADC supply
* is enabled every time a GPADC conversion is triggered.
* We will force it to be enabled from this driver to have
* the GPADC module independent of the AB8500 chargers
*/
if (!di->vddadc_en_ac) {
ret = regulator_enable(di->regu);
if (ret)
dev_warn(di->dev,
"Failed to enable regulator\n");
else
di->vddadc_en_ac = true;
}
/* Check if the requested voltage or current is valid */
volt_index = ab8500_voltage_to_regval(vset_uv);
curr_index = ab8500_current_to_regval(di, iset_ua);
input_curr_index = ab8500_current_to_regval(di,
di->bm->chg_params->ac_curr_max_ua);
if (volt_index < 0 || curr_index < 0 || input_curr_index < 0) {
dev_err(di->dev,
"Charger voltage or current too high, "
"charging not started\n");
return -ENXIO;
}
/* ChVoltLevel: maximum battery charging voltage */
ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
AB8500_CH_VOLT_LVL_REG, (u8) volt_index);
if (ret) {
dev_err(di->dev, "%s write failed\n", __func__);
return ret;
}
/* MainChInputCurr: current that can be drawn from the charger*/
ret = ab8500_charger_set_main_in_curr(di,
di->bm->chg_params->ac_curr_max_ua);
if (ret) {
dev_err(di->dev, "%s Failed to set MainChInputCurr\n",
__func__);
return ret;
}
/* ChOutputCurentLevel: protected output current */
ret = ab8500_charger_set_output_curr(di, iset_ua);
if (ret) {
dev_err(di->dev, "%s "
"Failed to set ChOutputCurentLevel\n",
__func__);
return ret;
}
/* Check if VBAT overshoot control should be enabled */
if (!di->bm->enable_overshoot)
overshoot = MAIN_CH_NO_OVERSHOOT_ENA_N;
/* Enable Main Charger */
ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
AB8500_MCH_CTRL1, MAIN_CH_ENA | overshoot);
if (ret) {
dev_err(di->dev, "%s write failed\n", __func__);
return ret;
}
/* Power on charging LED indication */
ret = ab8500_charger_led_en(di, true);
if (ret < 0)
dev_err(di->dev, "failed to enable LED\n");
di->ac.charger_online = 1;
} else {
/* Disable AC charging */
if (is_ab8500_1p1_or_earlier(di->parent)) {
/*
* For ABB revision 1.0 and 1.1 there is a bug in the
* watchdog logic. That means we have to continuously
* kick the charger watchdog even when no charger is
* connected. This is only valid once the AC charger
* has been enabled. This is a bug that is not handled
* by the algorithm and the watchdog have to be kicked
* by the charger driver when the AC charger
* is disabled
*/
if (di->ac_conn) {
queue_delayed_work(di->charger_wq,
&di->kick_wd_work,
round_jiffies(WD_KICK_INTERVAL));
}
/*
* We can't turn off charging completely
* due to a bug in AB8500 cut1.
* If we do, charging will not start again.
* That is why we set the lowest voltage
* and current possible
*/
ret = abx500_set_register_interruptible(di->dev,
AB8500_CHARGER,
AB8500_CH_VOLT_LVL_REG, CH_VOL_LVL_3P5);
if (ret) {
dev_err(di->dev,
"%s write failed\n", __func__);
return ret;
}
ret = ab8500_charger_set_output_curr(di, 0);
if (ret) {
dev_err(di->dev, "%s "
"Failed to set ChOutputCurentLevel\n",
__func__);
return ret;
}
} else {
ret = abx500_set_register_interruptible(di->dev,
AB8500_CHARGER,
AB8500_MCH_CTRL1, 0);
if (ret) {
dev_err(di->dev,
"%s write failed\n", __func__);
return ret;
}
}
ret = ab8500_charger_led_en(di, false);
if (ret < 0)
dev_err(di->dev, "failed to disable LED\n");
di->ac.charger_online = 0;
di->ac.wd_expired = false;
/* Disable regulator if enabled */
if (di->vddadc_en_ac) {
regulator_disable(di->regu);
di->vddadc_en_ac = false;
}
dev_dbg(di->dev, "%s Disabled AC charging\n", __func__);
}
ab8500_power_supply_changed(di, di->ac_chg.psy);
return ret;
}