static void bq27520_hw_config(struct work_struct *work)
{
	int ret = 0, flags = 0, type = 0, fw_ver = 0, status = 0;
	struct bq27520_device_info *di;

	di  = container_of(work, struct bq27520_device_info, hw_config.work);

	pr_debug(KERN_INFO "Enter bq27520_hw_config\n");
	ret = bq27520_chip_config(di);
	if (ret) {
		dev_err(di->dev, "Failed to config Bq27520 ret = %d\n", ret);
		return;
	}
	/* bq27520 is ready for access, update current_battery_status by reading
	 * from hardware
	 */
	if_notify_msm_charger(&status);
	update_current_battery_status(status);
	msm_battery_gauge_register(&bq27520_batt_gauge);
	msm_charger_notify_event(NULL, CHG_BATT_STATUS_CHANGE);

	enable_irq(di->irq);

	/* poll battery status every 3 seconds, if charging status changes,
	 * notify msm_charger
	 */
	schedule_delayed_work(&current_battery_status.poller,
				BQ27520_POLLING_STATUS);

	if (di->pdata->enable_dlog) {
		schedule_work(&di->counter);
		init_timer(&timer);
		timer.function = &bq27520_every_30secs;
		timer.data = (unsigned long)di;
		timer.expires = jiffies + BQ27520_COULOMB_POLL;
		add_timer(&timer);
	}

	bq27520_cntl_cmd(di, BQ27520_SUBCMD_CTNL_STATUS);
	udelay(66);
	bq27520_read(BQ27520_REG_CNTL, &flags, 0, di);
	bq27520_cntl_cmd(di, BQ27520_SUBCMD_DEVCIE_TYPE);
	udelay(66);
	bq27520_read(BQ27520_REG_CNTL, &type, 0, di);
	bq27520_cntl_cmd(di, BQ27520_SUBCMD_FW_VER);
	udelay(66);
	bq27520_read(BQ27520_REG_CNTL, &fw_ver, 0, di);

	dev_info(di->dev, "DEVICE_TYPE is 0x%02X, FIRMWARE_VERSION\
		is 0x%02X\n", type, fw_ver);
	dev_info(di->dev, "Complete bq27520 configuration 0x%02X\n", flags);
}
static ssize_t bq27520_read_subcmd(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	int ret, temp = 0;
	struct platform_device *client;
	struct bq27520_device_info *di;

	client = to_platform_device(dev);
	di = platform_get_drvdata(client);

	if (subcmd == BQ27520_SUBCMD_DEVCIE_TYPE ||
		 subcmd == BQ27520_SUBCMD_FW_VER ||
		 subcmd == BQ27520_SUBCMD_HW_VER ||
		 subcmd == BQ27520_SUBCMD_CHEM_ID) {

		bq27520_cntl_cmd(di, subcmd);/* Retrieve Chip status */
		udelay(66);
		ret = bq27520_read(BQ27520_REG_CNTL, &temp, 0, di);

		if (ret)
			ret = snprintf(buf, PAGE_SIZE, "Read Error!\n");
		else
			ret = snprintf(buf, PAGE_SIZE, "0x%02x\n", temp);
	} else
		ret = snprintf(buf, PAGE_SIZE, "Register Error!\n");

	return ret;
}
/* only if battery charging satus changes then notify msm_charger. otherwise
 * only refresh current_batter_status
 */
static int if_notify_msm_charger(int *data)
{
	int ret = 0, flags = 0, status = 0;
	unsigned long flag;

	ret = bq27520_read(BQ27520_REG_FLAGS, &flags, 0, bq27520_di);
	if (ret < 0) {
		dev_err(bq27520_di->dev, "error %d reading register %02x\n",
			ret, BQ27520_REG_FLAGS);
		status = POWER_SUPPLY_STATUS_UNKNOWN;
	} else {
		if (flags & BQ27520_FLAG_FC)
			status = POWER_SUPPLY_STATUS_FULL;
		else if (flags & BQ27520_FLAG_DSC)
			status = POWER_SUPPLY_STATUS_DISCHARGING;
		else
			status = POWER_SUPPLY_STATUS_CHARGING;
	}

	*data = status;
	spin_lock_irqsave(&current_battery_status.lock, flag);
	ret = (status != current_battery_status.status[GET_BATTERY_STATUS]);
	spin_unlock_irqrestore(&current_battery_status.lock, flag);
	return ret;
}
int bq27520_send_subcmd(struct i2c_client *client, int *rt_value, u16 sub_cmd)
{
    int ret, tmp_buf = 0;
    int retry_count = RETRY_COUNT;

    do
    {
    ret = bq27520_cntl_cmd(client, sub_cmd);
    if (ret) {
        dev_err(&client->dev, "Send subcommand 0x%04X error.\n", sub_cmd);
        retry_count--;
        msleep(I2C_RETRY_DELAY);
    }
    } while (ret && retry_count > 0);

    if (!rt_value) return ret;

    retry_count = RETRY_COUNT;
    udelay(800);
    do
    {
    /* need read data to rt_value */
    ret = bq27520_read(client, BQ27520_REG_CNTL, &tmp_buf, 0);
    if (ret)
    {
        dev_err(&client->dev, "Error!! %s subcommand %04X\n",
                         __func__, sub_cmd);
        retry_count--;
        msleep(I2C_RETRY_DELAY);
    }
    } while (ret && retry_count > 0);
    *rt_value = tmp_buf;
    return ret;
}
/*
 * Return the battery Voltage in milivolts
 * Or < 0 if something fails.
 */
static int bq27520_battery_voltage(struct bq27520_device_info *di)
{
	int ret, volt = 0;

	ret = bq27520_read(BQ27520_REG_VOLT, &volt, 0, di);
	if (ret) {
		dev_err(di->dev, "error %d reading voltage\n", ret);
		return ret;
	}

	return volt;
}
/*
 * Return the battery temperature in tenths of degree Celsius
 * Or < 0 if something fails.
 */
static int bq27520_battery_temperature(struct bq27520_device_info *di)
{
	int ret, temp = 0;

	ret = bq27520_read(BQ27520_REG_TEMP, &temp, 0, di);
	if (ret) {
		dev_err(di->dev, "error %d reading temperature\n", ret);
		return ret;
	}

	return temp + ZERO_DEGREE_CELSIUS_IN_TENTH_KELVIN;
}
/*
 * Return the battery Relative State-of-Charge
 * Or < 0 if something fails.
 */
static int bq27520_battery_rsoc(struct bq27520_device_info *di)
{
	int ret, rsoc = 0;

	ret = bq27520_read(BQ27520_REG_SOC, &rsoc, 0, di);

	if (ret) {
		dev_err(di->dev,
			"error %d reading relative State-of-Charge\n", ret);
		return ret;
	}

	return rsoc;
}
static void bq27520_coulomb_counter_work(struct work_struct *work)
{
	int value = 0, temp = 0, index = 0, ret = 0, count = 0;
	struct bq27520_device_info *di;
	unsigned long flags;

	di = container_of(work, struct bq27520_device_info, counter);

	/* retrieve 30 values from FIFO of coulomb data logging buffer
	 * and average over time
	 */
	do {
		ret = bq27520_read(BQ27520_REG_LOGBUF, &temp, 0, di);
		if (ret < 0)
			break;
		if (temp != 0x7FFF) {
			++count;
			value += temp;
		}
		udelay(66);
		ret = bq27520_read(BQ27520_REG_LOGIDX, &index, 0, di);
		if (ret < 0)
			break;
		udelay(66);
	} while (index != 0 || temp != 0x7FFF);

	if (ret < 0) {
		dev_err(di->dev, "Error %d reading datalog register\n", ret);
		return;
	}

	if (count) {
		spin_lock_irqsave(&lock, flags);
		coulomb_counter = value/count;
		spin_unlock_irqrestore(&lock, flags);
	}
}
static ssize_t bq27520_read_stdcmd(struct device *dev,
	struct device_attribute *attr, char *buf)
{
	int ret;
	int temp = 0;
	struct platform_device *client;
	struct bq27520_device_info *di;

	client = to_platform_device(dev);
	di = platform_get_drvdata(client);

	if (reg <= BQ27520_REG_ICR && reg > 0x00) {
		ret = bq27520_read(reg, &temp, 0, di);
		if (ret)
			ret = snprintf(buf, PAGE_SIZE, "Read Error!\n");
		else
			ret = snprintf(buf, PAGE_SIZE, "0x%02x\n", temp);
	} else
		ret = snprintf(buf, PAGE_SIZE, "Register Error!\n");

	return ret;
}
static int bq27520_chip_config(struct bq27520_device_info *di)
{
	int flags = 0, ret = 0;

	bq27520_cntl_cmd(di, BQ27520_SUBCMD_CTNL_STATUS);
	udelay(66);
	ret = bq27520_read(BQ27520_REG_CNTL, &flags, 0, di);
	if (ret < 0) {
		dev_err(di->dev, "error %d reading register %02x\n",
			 ret, BQ27520_REG_CNTL);
		return ret;
	}
	udelay(66);

	bq27520_cntl_cmd(di, BQ27520_SUBCMD_ENABLE_IT);
	udelay(66);

	if (di->pdata->enable_dlog && !(flags & BQ27520_CS_DLOGEN)) {
		bq27520_cntl_cmd(di, BQ27520_SUBCMD_ENABLE_DLOG);
		udelay(66);
	}

	return 0;
}