static int ab8500_charger_set_current()

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, &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;
}