int smb345_volatile_writes(struct i2c_client *client, uint8_t value)
{
	int ret = 0;

	if (value == smb345_ENABLE_WRITE) {
		/* Enable volatile write to config registers */
		ret = smb345_update_reg(client, smb345_CMD_REG,
						ENABLE_WRT_ACCESS);
		if (ret < 0) {
			dev_err(&client->dev, "%s(): Failed in writing"
				"register 0x%02x\n", __func__, smb345_CMD_REG);
			return ret;
		}
	} else {
		ret = smb345_read(client, smb345_CMD_REG);
		if (ret < 0) {
			dev_err(&client->dev, "%s: err %d\n", __func__, ret);
			return ret;
		}

		ret = smb345_write(client, smb345_CMD_REG, ret & (~(1<<7)));
		if (ret < 0) {
			dev_err(&client->dev, "%s: err %d\n", __func__, ret);
			return ret;
		}
	}
	return ret;
}
static int smb345_clear_reg(struct i2c_client *client, int reg, u8 value)
{
	int ret, retval;

	retval = smb345_read(client, reg);
	if (retval < 0) {
		dev_err(&client->dev, "%s: err %d\n", __func__, retval);
		return retval;
	}

	ret = smb345_write(client, reg, retval & (~value));
	if (ret < 0) {
		dev_err(&client->dev, "%s: err %d\n", __func__, ret);
		return ret;
	}

	return ret;
}
int smb345_float_volt_set(unsigned int val)
{
	struct i2c_client *client = charger->client;
	int ret = 0, retval;

	if (val > 4500 || val < 3500) {
		SMB_ERR("%s(): val=%d is out of range !\n",__func__, val);
	}

	printk("%s(): val=%d\n",__func__, val);

	ret = smb345_volatile_writes(client, smb345_ENABLE_WRITE);
	if (ret < 0) {
		dev_err(&client->dev, "%s() charger enable write error..\n", __func__);
		goto fault;
	}

	retval = smb345_read(client, smb345_FLOAT_VLTG);
	if (retval < 0) {
		dev_err(&client->dev, "%s(): Failed in reading 0x%02x",
				__func__, smb345_FLOAT_VLTG);
		goto fault;
	}
	retval = retval & (~FLOAT_VOLT_MASK);
	val = clamp_val(val, 3500, 4500) - 3500;
	val /= 20;
	retval |= val;
	ret = smb345_write(client, smb345_FLOAT_VLTG, retval);
	if (ret < 0) {
		dev_err(&client->dev, "%s(): Failed in writing"
			"register 0x%02x\n", __func__, smb345_FLOAT_VLTG);
		goto fault;
       }

	ret = smb345_volatile_writes(client, smb345_DISABLE_WRITE);
	if (ret < 0) {
		dev_err(&client->dev, "%s() charger disable write error..\n", __func__);
	}
	return 0;
fault:
	return ret;
}
void reconfig_AICL(void)
{
	struct i2c_client *client = charger->client;

	if (ac_on && !gpio_get_value(GPIO_AC_OK)) {
		int retval;
		retval = smb345_read(client, smb345_STS_REG_E);
		if (retval < 0)
			dev_err(&client->dev, "%s(): Failed in reading 0x%02x",
			__func__, smb345_STS_REG_E);
		else {
			SMB_NOTICE("Status Reg E=0x%02x\n", retval);

			if ((retval & 0xF) <= 0x1) {
				SMB_NOTICE("reconfig input current limit\n");
				smb345_set_InputCurrentlimit(client, 1200);
			}
		}
	}
}
int smb345_config_thermal_limit(void)
{
	struct i2c_client *client = charger->client;
	int ret = 0, retval, setting = 0;

	ret = smb345_volatile_writes(client, smb345_ENABLE_WRITE);
	if (ret < 0) {
		dev_err(&client->dev, "%s() charger enable write error..\n", __func__);
		goto error;
	}

	retval = smb345_read(client, smb345_HRD_SFT_TEMP);
	if (retval < 0) {
		dev_err(&client->dev, "%s(): Failed in reading 0x%02x",
				__func__, smb345_HRD_SFT_TEMP);
		goto error;
	}

	setting = retval & HOT_LIMIT_MASK;
	if (setting != 0x33) {
		setting = retval & (~HOT_LIMIT_MASK);
		setting |= 0x33;
		SMB_NOTICE("Set HRD SFT limit, retval=%x setting=%x\n", retval, setting);
		ret = smb345_write(client, smb345_HRD_SFT_TEMP, setting);
		if (ret < 0) {
			dev_err(&client->dev, "%s(): Failed in writing 0x%02x to register"
				"0x%02x\n", __func__, setting, smb345_HRD_SFT_TEMP);
			goto error;
		}
	} else
		SMB_NOTICE("Bypass set HRD SFT limit=%x\n", retval);

	ret = smb345_volatile_writes(client, smb345_DISABLE_WRITE);
	if (ret < 0) {
		dev_err(&client->dev, "%s() charger enable write error..\n", __func__);
		goto error;
	}
error:
	return ret;
}
int smb345_vflt_setting(void)
{
	struct i2c_client *client = charger->client;
	u8 ret = 0, setting;

	ret = smb345_volatile_writes(client, smb345_ENABLE_WRITE);
	if (ret < 0) {
		dev_err(&client->dev, "%s() error in smb345 volatile writes \n", __func__);
		goto error;
	}

	ret = smb345_read(client, smb345_FLOAT_VLTG);
	if (ret < 0) {
		dev_err(&client->dev, "%s() error in smb345 read \n", __func__);
		goto error;
	}

	setting = ret & FLOAT_VOLT_MASK;
	if (setting != FLOAT_VOLT_43V) {
		setting = ret & (~FLOAT_VOLT_MASK);
		setting |= FLOAT_VOLT_43V;
		SMB_NOTICE("Set Float Volt, retval=%x setting=%x\n", ret, setting);
		ret = smb345_write(client, smb345_FLOAT_VLTG, setting);
		if (ret < 0) {
			dev_err(&client->dev, "%s() error in smb345 write \n", __func__);
			goto error;
		}
	} else
		SMB_NOTICE("Bypass set Float Volt=%x\n", ret);

	ret = smb345_volatile_writes(client, smb345_DISABLE_WRITE);
	if (ret < 0) {
		dev_err(&client->dev, "%s() error in smb345 volatile writes \n", __func__);
		goto error;
	}

error:
	return ret;
}
static void cable_det_work_function(struct work_struct *dat)
{
	struct i2c_client *client = charger->client;

	if (delayed_work_pending(&charger->cable_det_work))
		cancel_delayed_work(&charger->cable_det_work);

	if (ac_on && !gpio_get_value(GPIO_AC_OK)) {
		int retval;
		retval = smb345_read(client, smb345_STS_REG_E);
		if (retval < 0)
			dev_err(&client->dev, "%s(): Failed in reading 0x%02x",
			__func__, smb345_STS_REG_E);
		else {
			SMB_NOTICE("Status Reg E=0x02%x\n", retval);

			if ((retval & 0xF) <= 0x1) {
				SMB_NOTICE("reconfig input current limit\n");
				smb345_set_InputCurrentlimit(client, 1200);
			}
		}
	}
}
int
smb345_set_WCInputCurrentlimit(struct i2c_client *client, u32 current_setting)
{
	int ret = 0, retval;
	u8 setting;

	charger->wpc_curr_limit_count++;

	ret = smb345_volatile_writes(client, smb345_ENABLE_WRITE);
	if (ret < 0) {
		dev_err(&client->dev, "%s() error in configuring charger..\n",
								__func__);
		goto error;
	}

	if (current_setting != 0) {
		retval = smb345_read(client, smb345_CHRG_CRNTS);
		if (retval < 0) {
			dev_err(&client->dev, "%s(): Failed in reading 0x%02x",
				__func__, smb345_CHRG_CRNTS);
			goto error;
		}

		setting = retval & 0x0F;
		if (current_setting == 2000)
			setting |= 0x70;
		else if (current_setting == 1800)
			setting |= 0x60;
		else if (current_setting == 1200)
			setting |= 0x40;
		else if (current_setting == 900)
			setting |= 0x30;
		else if (current_setting == 700)
			setting |= 0x20;
		else if (current_setting == 500)
			setting |= 0x10;
		else if (current_setting == 300)
			setting |= 0x00;
		else
			setting |= 0x20;

		SMB_NOTICE("Set ICL=%u retval =%x setting=%x\n",
			current_setting, retval, setting);

		ret = smb345_write(client, smb345_CHRG_CRNTS, setting);
		if (ret < 0) {
			dev_err(&client->dev, "%s(): Failed in writing 0x%02x to register"
				"0x%02x\n", __func__, setting, smb345_CHRG_CRNTS);
			goto error;
		}

		charger->wpc_curr_limit = current_setting;
	}

	if (current_setting == 300) {
		retval = smb345_read(client, smb345_VRS_FUNC);
		if (retval < 0) {
			dev_err(&client->dev, "%s(): Failed in reading 0x%02x",
					__func__, smb345_VRS_FUNC);
			goto error;
		}

		setting = retval & (~(BIT(4)));
		SMB_NOTICE("Disable AICL, retval=%x setting=%x\n", retval, setting);
		ret = smb345_write(client, smb345_VRS_FUNC, setting);
		if (ret < 0) {
			dev_err(&client->dev, "%s(): Failed in writing 0x%02x to register"
				"0x%02x\n", __func__, setting, smb345_VRS_FUNC);
			goto error;
		}
	}

	ret = smb345_volatile_writes(client, smb345_DISABLE_WRITE);
	if (ret < 0) {
		dev_err(&client->dev, "%s() error in configuring charger..\n",
								__func__);
		goto error;
	}

error:
	return ret;
}
int
smb345_set_InputCurrentlimit(struct i2c_client *client, u32 current_setting)
{
	int ret = 0, retval;
	u8 setting = 0;

	wake_lock(&charger_wakelock);

	ret = smb345_volatile_writes(client, smb345_ENABLE_WRITE);
	if (ret < 0) {
		dev_err(&client->dev, "%s() error in configuring charger..\n",
								__func__);
		goto error;
	}

	if (charge_en_flag)
		smb345_pin_control(0);

	retval = smb345_read(client, smb345_VRS_FUNC);
	if (retval < 0) {
		dev_err(&client->dev, "%s(): Failed in reading 0x%02x",
				__func__, smb345_VRS_FUNC);
		goto error;
	}

	setting = retval & (~(BIT(4)));
	SMB_NOTICE("Disable AICL, retval=%x setting=%x\n", retval, setting);
	ret = smb345_write(client, smb345_VRS_FUNC, setting);
	if (ret < 0) {
		dev_err(&client->dev, "%s(): Failed in writing 0x%02x to register"
			"0x%02x\n", __func__, setting, smb345_VRS_FUNC);
		goto error;
	}

	retval = smb345_read(client, smb345_CHRG_CRNTS);
	if (retval < 0) {
		dev_err(&client->dev, "%s(): Failed in reading 0x%02x",
			__func__, smb345_CHRG_CRNTS);
		goto error;
	}
	setting = retval & 0xF0;
	if(current_setting == 2000)
		setting |= 0x07;
	else if(current_setting == 1800)
		setting |= 0x06;
	else if (current_setting == 1200)
		setting |= 0x04;
	else if(current_setting == 900)
		setting |= 0x03;
	else if(current_setting == 500)
		setting |= 0x01;
	else
		setting |= 0x07;

	SMB_NOTICE("Set ICL=%u retval =%x setting=%x\n", current_setting, retval, setting);

	ret = smb345_write(client, smb345_CHRG_CRNTS, setting);
	if (ret < 0) {
		dev_err(&client->dev, "%s(): Failed in writing 0x%02x to register"
			"0x%02x\n", __func__, setting, smb345_CHRG_CRNTS);
		goto error;
	}

	if(current_setting == 2000)
		charger->curr_limit = 2000;
	else if(current_setting == 1800)
		charger->curr_limit = 1800;
	else if (current_setting == 1200)
		charger->curr_limit = 1200;
	else if(current_setting == 900)
		charger->curr_limit = 900;
	else if(current_setting == 500)
		charger->curr_limit = 500;
	else
		charger->curr_limit = 2000;

	if (current_setting > 900) {
		charger->time_of_1800mA_limit = jiffies;
	} else{
		charger->time_of_1800mA_limit = 0;
	}

	retval = smb345_read(client, smb345_VRS_FUNC);
	if (retval < 0) {
		dev_err(&client->dev, "%s(): Failed in reading 0x%02x",
				__func__, smb345_VRS_FUNC);
		goto error;
	}

	setting = retval | BIT(4);
	SMB_NOTICE("Re-enable AICL, setting=%x\n", setting);
	msleep(20);
	ret = smb345_write(client, smb345_VRS_FUNC, setting);
	if (ret < 0) {
		dev_err(&client->dev, "%s(): Failed in writing 0x%02x to register"
			"0x%02x\n", __func__, setting, smb345_VRS_FUNC);
			goto error;
	}

	if (charge_en_flag)
		smb345_pin_control(1);

	ret = smb345_volatile_writes(client, smb345_DISABLE_WRITE);
	if (ret < 0) {
		dev_err(&client->dev, "%s() error in configuring charger..\n",
								__func__);
		goto error;
	}

error:
	wake_unlock(&charger_wakelock);
	return ret;
}
int smb345_config_thermal_charging(int temp, int volt, int rule)
{
	struct i2c_client *client = charger->client;
	int ret = 0, retval, setting = 0;
	int BAT_Mid_Temp = BAT_Mid_Temp_Wired;

	if (rule == THERMAL_RULE1)
		BAT_Mid_Temp = BAT_Mid_Temp_Wired;
	else if (rule == THERMAL_RULE2)
		BAT_Mid_Temp = BAT_Mid_Temp_Wireless;

	smb345_config_thermal_limit();

	SMB_NOTICE("temp=%d, volt=%d\n", temp, volt);

	ret = smb345_volatile_writes(client, smb345_ENABLE_WRITE);
	if (ret < 0) {
		dev_err(&client->dev, "%s() charger enable write error..\n", __func__);
		goto error;
	}

	/*control float voltage*/
	retval = smb345_read(client, smb345_FLOAT_VLTG);
	if (retval < 0) {
		dev_err(&client->dev, "%s(): Failed in reading 0x%02x",
				__func__, smb345_FLOAT_VLTG);
		goto error;
	}

	setting = retval & FLOAT_VOLT_MASK;
	if (temp <= BAT_Mid_Temp
		|| (temp > BAT_Mid_Temp && volt > FLOAT_VOLT_LOW_DECIMAL)
		|| temp > BAT_Hot_Limit) {
		if (setting != FLOAT_VOLT_43V) {
			setting = retval & (~FLOAT_VOLT_MASK);
			setting |= FLOAT_VOLT_43V;
			SMB_NOTICE("Set Float Volt, retval=%x setting=%x\n", retval, setting);
			ret = smb345_write(client, smb345_FLOAT_VLTG, setting);
			if (ret < 0) {
				dev_err(&client->dev, "%s(): Failed in writing 0x%02x to register"
					"0x%02x\n", __func__, setting, smb345_FLOAT_VLTG);
				goto error;
			}
		} else
			SMB_NOTICE("Bypass set Float Volt=%x\n", retval);
	} else {
		if (setting != FLOAT_VOLT_LOW) {
			setting = retval & (~FLOAT_VOLT_MASK);
			setting |= FLOAT_VOLT_LOW;
			SMB_NOTICE("Set Float Volt, retval=%x setting=%x\n", retval, setting);
			ret = smb345_write(client, smb345_FLOAT_VLTG, setting);
			if (ret < 0) {
				dev_err(&client->dev, "%s(): Failed in writing 0x%02x to register"
					"0x%02x\n", __func__, setting, smb345_FLOAT_VLTG);
				goto error;
			}
		} else
			SMB_NOTICE("Bypass set Float Volt=%x\n", retval);
	}

	/*charger enable/disable*/
	retval = smb345_read(client, smb345_PIN_CTRL);
	if (retval < 0) {
		dev_err(&client->dev, "%s(): Failed in reading 0x%02x",
				__func__, smb345_PIN_CTRL);
		goto error;
	}

	setting = retval & ENABLE_PIN_CTRL_MASK;
	if (temp < BAT_Cold_Limit || temp > BAT_Hot_Limit ||
		(temp > BAT_Mid_Temp && volt > FLOAT_VOLT_LOW_DECIMAL)) {
		if (setting != 0x40) {
			SMB_NOTICE("Charger disable\n");
			smb345_charger_enable(false);
		} else
			SMB_NOTICE("Bypass charger disable\n");
	} else {
		if (setting != 0x60) {
			SMB_NOTICE("Charger enable\n");
			smb345_charger_enable(true);
		} else {
			/*interrupt status*/
			retval = smb345_read(client, smb345_INTR_STS_B);
			if (retval < 0) {
				dev_err(&client->dev, "%s(): Failed in reading 0x%02x",
					__func__, smb345_INTR_STS_B);
				goto error;
			}
			if ((retval & BAT_OVER_VOLT_MASK) == 0x40) {
				SMB_NOTICE("disable then enable charger to recover bat over-volt\n");
				smb345_charger_enable(false);
				smb345_charger_enable(true);
			} else
				SMB_NOTICE("Bypass charger enable\n");
		}
	}

	ret = smb345_volatile_writes(client, smb345_DISABLE_WRITE);
	if (ret < 0) {
		dev_err(&client->dev, "%s() charger enable write error..\n", __func__);
		goto error;
	}
error:
	return ret;
}
int smb345_config_thermal_charging(int temp, int volt, int rule)
{
	struct i2c_client *client = charger->client;
	int ret = 0, retval, setting = 0;
	int BAT_Mid_Temp = BAT_Mid_Temp_Wired;
	/*Charger float voltage for normal temperature conditions. Default 4.3V.*/
	int flt_volt_43 = FLOAT_VOLT_43V;
	/*Charger float voltage for high temperature conditions. Default 4.1V.*/
	int flt_volt_low = FLOAT_VOLT_LOW;

	flt_volt_43 = (float_volt_setting - 3500) / 20;

	if(flt_volt_43 < 0 || flt_volt_43 > FLOAT_VOLT_43V) {
		SMB_NOTICE("BUG: Invalid float voltage setting calculated: %d\n", flt_volt_43);
		flt_volt_43 = FLOAT_VOLT_43V;
	}
	if(flt_volt_low > flt_volt_43) flt_volt_low = flt_volt_43;

	if (rule == THERMAL_RULE1)
		BAT_Mid_Temp = BAT_Mid_Temp_Wired;
	else if (rule == THERMAL_RULE2)
		BAT_Mid_Temp = BAT_Mid_Temp_Wireless;

	mdelay(100);
	smb345_config_thermal_limit();

	SMB_NOTICE("temp=%d, volt=%d\n", temp, volt);

	ret = smb345_volatile_writes(client, smb345_ENABLE_WRITE);
	if (ret < 0) {
		dev_err(&client->dev, "%s() charger enable write error..\n", __func__);
		goto error;
	}

	/*control float voltage*/
	retval = smb345_read(client, smb345_FLOAT_VLTG);
	if (retval < 0) {
		dev_err(&client->dev, "%s(): Failed in reading 0x%02x",
				__func__, smb345_FLOAT_VLTG);
		goto error;
	}

	setting = retval & FLOAT_VOLT_MASK;
	if (temp <= BAT_Mid_Temp
		|| (temp > BAT_Mid_Temp && volt > FLOAT_VOLT_LOW_DECIMAL)
		|| temp > BAT_Hot_Limit) {
		if (setting != flt_volt_43) {
			setting = retval & (~FLOAT_VOLT_MASK);
			setting |= flt_volt_43;
			SMB_NOTICE("Set Float Volt, retval=%x setting=%x V=%dmV\n", retval, setting, float_volt_setting);
			ret = smb345_write(client, smb345_FLOAT_VLTG, setting);
			if (ret < 0) {
				dev_err(&client->dev, "%s(): Failed in writing 0x%02x to register"
					"0x%02x\n", __func__, setting, smb345_FLOAT_VLTG);
				goto error;
			}
		} else
			SMB_NOTICE("Bypass set Float Volt setting=%x V=%dmV\n", retval, float_volt_setting);
	} else {
		if (setting != flt_volt_low) {
			setting = retval & (~FLOAT_VOLT_MASK);
			setting |= flt_volt_low;
			SMB_NOTICE("Set Float Volt, retval=%x setting=%x\n", retval, setting);
			ret = smb345_write(client, smb345_FLOAT_VLTG, setting);
			if (ret < 0) {
				dev_err(&client->dev, "%s(): Failed in writing 0x%02x to register"
					"0x%02x\n", __func__, setting, smb345_FLOAT_VLTG);
				goto error;
			}
		} else
			SMB_NOTICE("Bypass set Float Volt=%x\n", retval);
	}

	/*charger enable/disable*/
	retval = smb345_read(client, smb345_PIN_CTRL);
	if (retval < 0) {
		dev_err(&client->dev, "%s(): Failed in reading 0x%02x",
				__func__, smb345_PIN_CTRL);
		goto error;
	}

	setting = retval & ENABLE_PIN_CTRL_MASK;
	if (temp < BAT_Cold_Limit || temp > BAT_Hot_Limit ||
		(temp > BAT_Mid_Temp && volt > FLOAT_VOLT_LOW_DECIMAL)) {
		if (setting != 0x40) {
			SMB_NOTICE("Charger disable\n");
			smb345_charger_enable(false);
		} else
			SMB_NOTICE("Bypass charger disable\n");
	} else {
		if (setting != 0x60) {
			SMB_NOTICE("Charger enable\n");
			smb345_charger_enable(true);
		} else {
			/*interrupt status*/
			retval = smb345_read(client, smb345_INTR_STS_B);
			if (retval < 0) {
				dev_err(&client->dev, "%s(): Failed in reading 0x%02x",
					__func__, smb345_INTR_STS_B);
				goto error;
			}
			if ((retval & BAT_OVER_VOLT_MASK) == 0x40) {
				SMB_NOTICE("disable then enable charger to recover bat over-volt\n");
				smb345_charger_enable(false);
				smb345_charger_enable(true);
			} else
				SMB_NOTICE("Bypass charger enable\n");
		}
	}

	ret = smb345_volatile_writes(client, smb345_DISABLE_WRITE);
	if (ret < 0) {
		dev_err(&client->dev, "%s() charger enable write error..\n", __func__);
		goto error;
	}
error:
	return ret;
}