static u8 smb327_set_termination_current_limit(struct i2c_client *client,
				int termination_current)
{
	u8 reg_data, term_data;

	term_data = termination_current < 25 ? 0x0 :
		termination_current > 200 ? 0x7 :
		(termination_current - 25) / 25;

	/* Charge completion termination current */
	smb327_i2c_read(client, SMB327_CHARGE_CURRENT, &reg_data);
	reg_data &= ~CFG_TERMINATION_CURRENT_MASK;
	reg_data |= term_data << CFG_TERMINATION_CURRENT_SHIFT;

	smb327_i2c_write(client, SMB327_CHARGE_CURRENT, &reg_data);

	/* set STAT assertion termination current */
	smb327_i2c_read(client, SMB327_VARIOUS_FUNCTIONS, &reg_data);
	reg_data &= ~CFG_STAT_ASSETION_TERM_MASK;
	reg_data |= term_data << CFG_STAT_ASSETION_TERM_SHIFT;

	smb327_i2c_write(client, SMB327_VARIOUS_FUNCTIONS, &reg_data);

	return reg_data;
}
static void smb327_i2c_write_array(struct i2c_client *client,
				u8 *buf, int size)
{
	int i;
	for (i = 0; i < size; i += 3)
		smb327_i2c_write(client, (u8) (*(buf + i)), (buf + i) + 1);
}
static void smb327_irq_disable(struct i2c_client *client)
{
	u8 data;

	smb327_set_writable(client, 1);
	data = 0x6E;
	smb327_i2c_write(client, 0x04, &data);

	smb327_i2c_read(client,
			SMB327_INTERRUPT_SIGNAL_SELECTION, &data);
	data &= 0xfe;
	smb327_i2c_write(client,
			SMB327_INTERRUPT_SIGNAL_SELECTION, &data);

	smb327_set_writable(client, 0);
}
static u8 smb327_set_charge_enable(struct i2c_client *client, int enable)
{
	u8 chg_en;
	u8 reg_data;

	reg_data = 0x00;

	smb327_i2c_write(client, SMB327_FUNCTION_CONTROL_C, &reg_data);

	smb327_i2c_read(client, SMB327_COMMAND, &chg_en);
	if (!enable)
		chg_en |= CMD_CHARGE_EN;
	else
		chg_en &= ~CMD_CHARGE_EN;

	smb327_i2c_write(client, SMB327_COMMAND, &chg_en);

	return chg_en;
}
static void smb327_irq_enable(struct i2c_client *client)
{
	u8 data;

	smb327_set_writable(client, 1);

	if (system_rev < 10)
		data = 0x4D;
	else
		data = 0x6D;

	smb327_i2c_write(client, 0x04, &data);

	smb327_i2c_read(client,
			SMB327_INTERRUPT_SIGNAL_SELECTION, &data);
	data |= 0x1;
	smb327_i2c_write(client,
			SMB327_INTERRUPT_SIGNAL_SELECTION, &data);

	smb327_set_writable(client, 0);
}
static void smb327_set_writable(struct i2c_client *client, int writable)
{
	u8 reg_data;

	smb327_i2c_read(client, SMB327_COMMAND, &reg_data);

	if (writable)
		reg_data |= CMD_A_ALLOW_WRITE;
	else
		reg_data &= ~CMD_A_ALLOW_WRITE;

	smb327_i2c_write(client, SMB327_COMMAND, &reg_data);
}
static void smb327_charger_otg_control(struct i2c_client *client, int enable)
{
	u8 reg_data;

	smb327_i2c_read(client, SMB327_OTG_POWER_AND_LDO, &reg_data);

	if (!enable)
		reg_data |= CFG_OTG_ENABLE;
	else
		reg_data &= ~CFG_OTG_ENABLE;

	smb327_set_writable(client, 1);
	smb327_i2c_write(client, SMB327_OTG_POWER_AND_LDO, &reg_data);
	smb327_set_writable(client, 0);
}
static void smb327_set_command(struct i2c_client *client,
				int reg, int datum)
{
	int val;
	u8 data = 0;
	val = smb327_i2c_read(client, reg, &data);
	if (val >= 0) {
		pr_debug("%s : reg(0x%02x): 0x%02x", __func__, reg, data);
		if (data != datum) {
			data = datum;
			if (smb327_i2c_write(client, reg, &data) < 0)
				pr_err("%s : error!\n", __func__);
			val = smb327_i2c_read(client, reg, &data);
			if (val >= 0)
				pr_debug(" => 0x%02x\n", data);
		}
	}
}
static u8 smb327_set_fast_charging_current(struct i2c_client *client,
				int fast_charging_current)
{
	u8 reg_data, chg_data;

	chg_data = fast_charging_current < 500 ? 0x0 :
		fast_charging_current > 1200 ? 0x7 :
		(fast_charging_current - 500) / 100;

	smb327_i2c_read(client, SMB327_CHARGE_CURRENT, &reg_data);
	reg_data &= ~CFG_CHARGE_CURRENT_MASK;
	reg_data |= chg_data << CFG_CHARGE_CURRENT_SHIFT;

	smb327_i2c_write(client, SMB327_CHARGE_CURRENT, &reg_data);

	pr_info("%s: Charge Current : 0x%x\n", __func__, reg_data);

	return reg_data;
}
static u8 smb327_set_input_current_limit(struct i2c_client *client,
				int input_current)
{
	u8 curr_data, reg_data;

	curr_data = input_current < 500 ? 0x0 :
		input_current > 1200 ? 0x7 :
		(input_current - 500) / 100;

	smb327_i2c_read(client, SMB327_INPUT_CURRENTLIMIT, &reg_data);
	reg_data &= ~CFG_INPUT_CURRENT_MASK;
	reg_data |= curr_data << CFG_INPUT_CURRENT_SHIFT;

	smb327_i2c_write(client, SMB327_INPUT_CURRENTLIMIT, &reg_data);

	pr_info("%s: set current limit : 0x%x\n", __func__, reg_data);

	return reg_data;
}
static int smb327_get_charge_now(struct i2c_client *client)
{
	u8 chg_now, data, addr;

	smb327_i2c_read(client, SMB327_BATTERY_CHARGING_STATUS_A, &chg_now);

	for (addr = 0; addr < 0x0c; addr++) {
		smb327_i2c_read(client, addr, &data);
		pr_info("smb327 addr : 0x%02x data : 0x%02x\n", addr, data);
	}
	for (addr = 0x31; addr <= 0x3D; addr++) {
		smb327_i2c_read(client, addr, &data);
		pr_info("smb327 addr : 0x%02x data : 0x%02x\n", addr, data);
	}

	/* Clear IRQ */
	data = 0xFF;
	smb327_i2c_write(client, 0x30, &data);

	return (chg_now & 0x2);
}
static u8 smb327_set_float_voltage(struct i2c_client *client, int float_voltage)
{
	u8 reg_data, float_data;

	if (float_voltage < 3460)
		float_data = 0;
	else if (float_voltage <= 4340)
		float_data = (float_voltage - 3500) / 20 + 2;
	else if ((float_voltage == 4350) || (float_voltage == 4360))
		float_data = 0x2D; /* (4340 -3500)/20 + 1 */
	else
		float_data = 0x3F;

	smb327_i2c_read(client,	SMB327_FLOAT_VOLTAGE, &reg_data);
	reg_data &= ~CFG_FLOAT_VOLTAGE_MASK;
	reg_data |= float_data << CFG_FLOAT_VOLTAGE_SHIFT;

	smb327_i2c_write(client, SMB327_FLOAT_VOLTAGE, &reg_data);

	return reg_data;
}
static int smb327_get_charging_health(struct i2c_client *client)
{
	int health = POWER_SUPPLY_HEALTH_GOOD;
	u8 status_reg, data;

	smb327_i2c_read(client, SMB327_INTERRUPT_STATUS_C, &status_reg);
	pr_info("%s : Interrupt status C(0x%02x)\n", __func__, status_reg);

	/* Is enabled ? */
	if (status_reg & 0x2) {
		health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
	} else if (status_reg & 0x4) {
		health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
	} else if (status_reg & 0x80) { /* watchdog enable workaround */
		/* clear IRQ */
		data = 0xFF;
		smb327_i2c_write(client, 0x30, &data);
		/* reset function control */
		smb327_charger_function_control(client);
	}

	return (int)health;
}
static void smb327_charger_function_control(struct i2c_client *client)
{
	struct sec_charger_info *charger = i2c_get_clientdata(client);
	u8 reg_data, charge_mode;

	smb327_set_writable(client, 1);

	if (system_rev < 10)
		reg_data = 0x4E;
	else
		reg_data = 0x6E;

	smb327_i2c_write(client, SMB327_FUNCTION_CONTROL_B, &reg_data);

	smb327_i2c_read(client,
			SMB327_INTERRUPT_SIGNAL_SELECTION, &reg_data);
	reg_data |= 0x1;
	smb327_i2c_write(client,
			SMB327_INTERRUPT_SIGNAL_SELECTION, &reg_data);

	if (charger->cable_type == POWER_SUPPLY_TYPE_BATTERY) {
		/* turn off charger */
		smb327_set_charge_enable(client, 0);
	} else {
		pr_info("%s: Input : %d, Charge : %d\n", __func__,
			charger->pdata->charging_current[charger->cable_type].input_current_limit,
			charger->charging_current);
		/* AICL enable */
		smb327_i2c_read(client, SMB327_INPUT_CURRENTLIMIT, &reg_data);
		reg_data &= ~CFG_AICL_ENABLE;
		reg_data &= ~CFG_AICL_VOLTAGE; /* AICL enable voltage 4.25V */
		smb327_i2c_write(client, SMB327_INPUT_CURRENTLIMIT, &reg_data);

		/* Function control A */
		reg_data = 0xDA;
		smb327_i2c_write(client, SMB327_FUNCTION_CONTROL_A, &reg_data);

		/* 4.2V float voltage */
		smb327_set_float_voltage(client,
			charger->pdata->chg_float_voltage);

		/* Set termination current */
		smb327_set_termination_current_limit(client,
			charger->pdata->charging_current[charger->
			cable_type].full_check_current_1st);

		smb327_set_input_current_limit(client,
			charger->pdata->charging_current
                        [charger->cable_type].input_current_limit);

		smb327_set_fast_charging_current(client,
			charger->charging_current);

		/* SET USB5/1, AC/USB Mode */
		charge_mode = (charger->cable_type == POWER_SUPPLY_TYPE_MAINS) ||
					(charger->cable_type == POWER_SUPPLY_TYPE_UARTOFF) ?
					0x3 : 0x2;
		smb327_i2c_read(client, SMB327_COMMAND, &reg_data);
		reg_data &= ~0x0C;
		reg_data |= charge_mode << 2;
		smb327_i2c_write(client, SMB327_COMMAND, &reg_data);

		smb327_set_charge_enable(client, 1);
	}
	smb327_test_read(client);
	smb327_set_writable(client, 0);
}