in supply/ab8500_charger.c [1570:1717]
static int ab8500_charger_usb_en(struct ux500_charger *charger,
int enable, int vset_uv, int ich_out_ua)
{
int ret;
int volt_index;
int curr_index;
u8 overshoot = 0;
struct ab8500_charger *di = to_ab8500_charger_usb_device_info(charger);
if (enable) {
/* Check if USB is connected */
if (!di->usb.charger_connected) {
dev_err(di->dev, "USB charger not connected\n");
return -ENXIO;
}
/*
* 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_usb) {
ret = regulator_enable(di->regu);
if (ret)
dev_warn(di->dev,
"Failed to enable regulator\n");
else
di->vddadc_en_usb = true;
}
/* Enable USB charging */
dev_dbg(di->dev, "Enable USB: %d uV %d uA\n", vset_uv, ich_out_ua);
/* Check if the requested voltage or current is valid */
volt_index = ab8500_voltage_to_regval(vset_uv);
curr_index = ab8500_current_to_regval(di, ich_out_ua);
if (volt_index < 0 || curr_index < 0) {
dev_err(di->dev,
"Charger voltage or current too high, "
"charging not started\n");
return -ENXIO;
}
/*
* ChVoltLevel: max voltage up to which battery can be
* charged
*/
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;
}
/* Check if VBAT overshoot control should be enabled */
if (!di->bm->enable_overshoot)
overshoot = USB_CHG_NO_OVERSHOOT_ENA_N;
/* Enable USB Charger */
dev_dbg(di->dev,
"Enabling USB with write to AB8500_USBCH_CTRL1_REG\n");
ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
AB8500_USBCH_CTRL1_REG, USB_CH_ENA | overshoot);
if (ret) {
dev_err(di->dev, "%s write failed\n", __func__);
return ret;
}
/* If success 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->usb.charger_online = 1;
/* USBChInputCurr: current that can be drawn from the usb */
ret = ab8500_charger_set_vbus_in_curr(di,
di->max_usb_in_curr.usb_type_max_ua);
if (ret) {
dev_err(di->dev, "setting USBChInputCurr failed\n");
return ret;
}
/* ChOutputCurentLevel: protected output current */
ret = ab8500_charger_set_output_curr(di, ich_out_ua);
if (ret) {
dev_err(di->dev, "%s "
"Failed to set ChOutputCurentLevel\n",
__func__);
return ret;
}
queue_delayed_work(di->charger_wq, &di->check_vbat_work, HZ);
} else {
/* Disable USB charging */
dev_dbg(di->dev, "%s Disabled USB charging\n", __func__);
ret = abx500_set_register_interruptible(di->dev,
AB8500_CHARGER,
AB8500_USBCH_CTRL1_REG, 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");
/* USBChInputCurr: current that can be drawn from the usb */
ret = ab8500_charger_set_vbus_in_curr(di, 0);
if (ret) {
dev_err(di->dev, "setting USBChInputCurr failed\n");
return ret;
}
/* ChOutputCurentLevel: protected output current */
ret = ab8500_charger_set_output_curr(di, 0);
if (ret) {
dev_err(di->dev, "%s "
"Failed to reset ChOutputCurentLevel\n",
__func__);
return ret;
}
di->usb.charger_online = 0;
di->usb.wd_expired = false;
/* Disable regulator if enabled */
if (di->vddadc_en_usb) {
regulator_disable(di->regu);
di->vddadc_en_usb = false;
}
dev_dbg(di->dev, "%s Disabled USB charging\n", __func__);
/* Cancel any pending Vbat check work */
cancel_delayed_work(&di->check_vbat_work);
}
ab8500_power_supply_changed(di, di->usb_chg.psy);
return ret;
}