in supply/ab8500_charger.c [1152:1263]
static int ab8500_charger_set_current(struct ab8500_charger *di,
int ich_ua, int reg)
{
int ret = 0;
int curr_index, prev_curr_index, shift_value, i;
u8 reg_value;
u32 step_udelay;
bool no_stepping = false;
atomic_inc(&di->current_stepping_sessions);
ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER,
reg, ®_value);
if (ret < 0) {
dev_err(di->dev, "%s read failed\n", __func__);
goto exit_set_current;
}
switch (reg) {
case AB8500_MCH_IPT_CURLVL_REG:
shift_value = MAIN_CH_INPUT_CURR_SHIFT;
prev_curr_index = (reg_value >> shift_value);
curr_index = ab8500_current_to_regval(di, ich_ua);
step_udelay = STEP_UDELAY;
if (!di->ac.charger_connected)
no_stepping = true;
break;
case AB8500_USBCH_IPT_CRNTLVL_REG:
shift_value = VBUS_IN_CURR_LIM_SHIFT;
prev_curr_index = (reg_value >> shift_value);
curr_index = ab8500_vbus_in_curr_to_regval(di, ich_ua);
step_udelay = STEP_UDELAY * 100;
if (!di->usb.charger_connected)
no_stepping = true;
break;
case AB8500_CH_OPT_CRNTLVL_REG:
shift_value = 0;
prev_curr_index = (reg_value >> shift_value);
curr_index = ab8500_current_to_regval(di, ich_ua);
step_udelay = STEP_UDELAY;
if (curr_index && (curr_index - prev_curr_index) > 1)
step_udelay *= 100;
if (!di->usb.charger_connected && !di->ac.charger_connected)
no_stepping = true;
break;
default:
dev_err(di->dev, "%s current register not valid\n", __func__);
ret = -ENXIO;
goto exit_set_current;
}
if (curr_index < 0) {
dev_err(di->dev, "requested current limit out-of-range\n");
ret = -ENXIO;
goto exit_set_current;
}
/* only update current if it's been changed */
if (prev_curr_index == curr_index) {
dev_dbg(di->dev, "%s current not changed for reg: 0x%02x\n",
__func__, reg);
ret = 0;
goto exit_set_current;
}
dev_dbg(di->dev, "%s set charger current: %d uA for reg: 0x%02x\n",
__func__, ich_ua, reg);
if (no_stepping) {
ret = abx500_set_register_interruptible(di->dev, AB8500_CHARGER,
reg, (u8)curr_index << shift_value);
if (ret)
dev_err(di->dev, "%s write failed\n", __func__);
} else if (prev_curr_index > curr_index) {
for (i = prev_curr_index - 1; i >= curr_index; i--) {
dev_dbg(di->dev, "curr change_1 to: %x for 0x%02x\n",
(u8) i << shift_value, reg);
ret = abx500_set_register_interruptible(di->dev,
AB8500_CHARGER, reg, (u8)i << shift_value);
if (ret) {
dev_err(di->dev, "%s write failed\n", __func__);
goto exit_set_current;
}
if (i != curr_index)
usleep_range(step_udelay, step_udelay * 2);
}
} else {
bool allow = true;
for (i = prev_curr_index + 1; i <= curr_index && allow; i++) {
dev_dbg(di->dev, "curr change_2 to: %x for 0x%02x\n",
(u8)i << shift_value, reg);
ret = abx500_set_register_interruptible(di->dev,
AB8500_CHARGER, reg, (u8)i << shift_value);
if (ret) {
dev_err(di->dev, "%s write failed\n", __func__);
goto exit_set_current;
}
if (i != curr_index)
usleep_range(step_udelay, step_udelay * 2);
allow = ab8500_charger_check_continue_stepping(di, reg);
}
}
exit_set_current:
atomic_dec(&di->current_stepping_sessions);
return ret;
}