static int pmic_fg_get_charge_full(struct pmic_fg_info *info, int *value)
{
	int ret;

	ret = pmic_fg_reg_readb(info, DC_FG_DES_CAP1_REG);
	if (ret < 0)
		goto pmic_fg_cfull_err;
	*value = (ret & FG_DES_CAP1_VAL_MASK) << 8;

	/*
	 * higher byte and lower byte reads should be
	 * back to back to get successful lower byte result.
	 */
	pmic_fg_reg_readb(info, DC_FG_DES_CAP1_REG);
	ret = pmic_fg_reg_readb(info, DC_FG_DES_CAP0_REG);
	if (ret < 0)
		goto pmic_fg_cfull_err;
	*value |= (ret & FG_DES_CAP0_VAL_MASK);
	*value *= FG_DES_CAP_RES_LSB;

	return 0;

pmic_fg_cfull_err:
	return ret;
}
static int pmic_fg_program_rdc_vals(struct pmic_fg_info *info)
{
	int ret;
	int rdc1, rdc0;


       rdc1 = pmic_fg_reg_readb(info, DC_FG_RDC1_REG);
       if (rdc1 < 0) {
				dev_warn(&info->pdev->dev, "RDC1 reg read err!!\n");
				return ret;
       }
       rdc0 = pmic_fg_reg_readb(info, DC_FG_RDC1_REG);

       if (rdc1 == info->cfg->rdc1 && rdc0 == info->cfg->rdc0) {
				dev_info(&info->pdev->dev, "RDC is already initialized\n");
				return 0;
       } else {
				dev_info(&info->pdev->dev, "RDC need to be initialized\n");
       }

       ret = pmic_fg_reg_writeb(info, DC_FG_RDC1_REG, info->cfg->rdc1);

	ret = pmic_fg_reg_writeb(info, DC_FG_RDC0_REG, info->cfg->rdc0);


	ret = pmic_fg_reg_clearb(info, DC_FG_TUNING_CNTL4, (1<<3));
	ret = pmic_fg_reg_setb(info, DC_FG_TUNING_CNTL4, (1<<4));

	return ret;
}
static int pmic_fg_program_design_cap(struct pmic_fg_info *info)
{
	int ret;
	int cap1, cap0;
/*
	ret = pmic_fg_reg_writeb(info, DC_FG_DES_CAP1_REG, info->pdata->cap1);
	if (ret < 0)
		goto fg_prog_descap_fail;
*/
       cap1 = pmic_fg_reg_readb(info, DC_FG_DES_CAP1_REG);
       if (cap1 < 0) {
				dev_warn(&info->pdev->dev, "CAP1 reg read err!!\n");
				return ret;
      }
       cap0 = pmic_fg_reg_readb(info, DC_FG_DES_CAP0_REG);

       if (cap1 == info->cfg->cap1 && cap0 == info->cfg->cap0) {
				dev_info(&info->pdev->dev, "FG data is already initialized\n");
				return 0;
       } else {
				dev_info(&info->pdev->dev, "FG data need to be initialized\n");
       }

       /*Disable coulomb meter*/
       ret = pmic_fg_reg_clearb(info, DC_FG_CNTL_REG, FG_CNTL_CC_EN);

       ret = pmic_fg_reg_writeb(info, DC_FG_DES_CAP1_REG, info->cfg->cap1);


		ret = pmic_fg_reg_writeb(info, DC_FG_DES_CAP0_REG, info->cfg->cap0);

	ret = pmic_fg_reg_setb(info, DC_FG_CNTL_REG, FG_CNTL_CC_EN);

	return 0;
}
static int pmic_fg_set_lowbatt_thresholds(struct pmic_fg_info *info)
{
	int ret;
	u8 reg_val, reg_thr;

	ret = pmic_fg_reg_readb(info, DC_FG_REP_CAP_REG);
	if (ret < 0) {
		dev_err(&info->pdev->dev, "%s:read err:%d\n", __func__, ret);
		return ret;
	}
	ret = (ret & FG_REP_CAP_VAL_MASK);

	if (ret > FG_LOW_CAP_WARN1_THR)
		reg_val = FG_LOW_CAP_WARN1_THR;
	else if (ret > FG_LOW_CAP_WARN2_THR)
		reg_val = FG_LOW_CAP_WARN2_THR;
	else if (ret > FG_LOW_CAP_CRIT_THR)
		reg_val = FG_LOW_CAP_CRIT_THR;
	/*set low battery alert, which is 14% for the first level,
	 * 4% and 0% for the sencond level, the value will impact PMIC auto calibration
	 * this default value is recommended by PMIC vendor*/
	if (ret > FG_LOW_CAP_CRIT_THR) {
		ret = pmic_fg_reg_readb(info, DC_FG_TUNING_CNTL4);
		if (ret < 0) {
			dev_err(&info->pdev->dev, "%s:read err:%d\n", __func__, ret);
			return ret;
		}
		reg_thr = ret & 0xF8;
		reg_thr |= 0x04;
		ret = pmic_fg_reg_writeb(info, DC_FG_TUNING_CNTL4, reg_thr);
		if (ret < 0) {
			dev_err(&info->pdev->dev, "%s:write err:%d\n", __func__, ret);
			return ret;
		}
	} else {
		reg_val = FG_LOW_CAP_SHDN_THR;
		ret = pmic_fg_reg_readb(info, DC_FG_TUNING_CNTL4);
		if (ret < 0) {
			dev_err(&info->pdev->dev, "%s:read err:%d\n", __func__, ret);
			return ret;
		}
		reg_thr = ret & 0xF8;
		ret = pmic_fg_reg_writeb(info, DC_FG_TUNING_CNTL4, reg_thr);
		if (ret < 0) {
			dev_err(&info->pdev->dev, "%s:write err:%d\n", __func__, ret);
			return ret;
		}
	}

	reg_val |= 0x90;
	ret = pmic_fg_reg_writeb(info, DC_FG_LOW_CAP_REG, reg_val);
	if (ret < 0)
		dev_err(&info->pdev->dev, "%s:write err:%d\n", __func__, ret);

	return ret;
}
static int pmic_fg_program_vbatt_full(struct pmic_fg_info *info)
{
	int ret;
	u8 val;

	ret = pmic_fg_reg_readb(info, DC_CHRG_CCCV_REG);
	if (ret < 0)
		goto fg_prog_ocv_fail;
	else
		val = (ret & ~CHRG_CCCV_CV_MASK);

	switch (info->pdata->design_max_volt) {
	case CV_4100:
		val |= (CHRG_CCCV_CV_4100MV << CHRG_CCCV_CV_BIT_POS);
		break;
	case CV_4150:
		val |= (CHRG_CCCV_CV_4150MV << CHRG_CCCV_CV_BIT_POS);
		break;
	case CV_4200:
		val |= (CHRG_CCCV_CV_4200MV << CHRG_CCCV_CV_BIT_POS);
		break;
	case CV_4350:
		val |= (CHRG_CCCV_CV_4350MV << CHRG_CCCV_CV_BIT_POS);
		break;
	default:
		val |= (CHRG_CCCV_CV_4200MV << CHRG_CCCV_CV_BIT_POS);
		break;
	}

	ret = pmic_fg_reg_writeb(info, DC_CHRG_CCCV_REG, val);
fg_prog_ocv_fail:
	return ret;
}
static int pmic_fg_update_config_params(struct pmic_fg_info *info)
{
	int ret, i;
	u8 buf[2];

	ret = pmic_fg_reg_readmul(info, DC_FG_DES_CAP1_REG, 2, buf);
	if (ret < 0)
		goto fg_save_cfg_fail;
	else {
		info->cfg->cap1 = buf[0];
		info->cfg->cap0 = buf[1];
	}

	ret = pmic_fg_reg_readmul(info, DC_FG_RDC1_REG, 2, buf);
	if (ret < 0)
		goto fg_save_cfg_fail;
	else {
		info->cfg->rdc1 = buf[0];
		info->cfg->rdc0 = buf[1];
	}

	for (i = 0; i < BAT_CURVE_SIZE; i++) {
		ret = pmic_fg_reg_readb(info, DC_FG_OCV_CURVE_REG + i);
		if (ret < 0)
			goto fg_save_cfg_fail;
		else
			info->cfg->bat_curve[i] = ret;
	}

	return 0;

fg_save_cfg_fail:
	return ret;
}
static int pmic_fg_get_current(struct pmic_fg_info *info, int *cur)
{
	int ret, raw_val, sign;
	int pwr_stat;

	pwr_stat = pmic_fg_reg_readb(info, DC_PS_STAT_REG);
	if (pwr_stat < 0) {
		dev_err(&info->pdev->dev, "PWR STAT read failed:%d\n", pwr_stat);
		return pwr_stat;
	}

	if (pwr_stat & PS_STAT_BAT_CHRG_DIR) {
		sign = 1;
		ret = pmic_read_adc_val("CURRENT", "BATCCUR", &raw_val, info);
	} else {
		sign = -1;
		ret = pmic_read_adc_val("CURRENT", "BATDCUR", &raw_val, info);
	}
	if (ret < 0)
		goto cur_read_fail;

	*cur = raw_val * sign;
cur_read_fail:
	return ret;
}
static void pmic_fg_init_config_regs(struct pmic_fg_info *info)
{
	int ret;


	/*
	 * check if the config data is already
	 * programmed and if so just return.
	 */
	ret = pmic_fg_reg_readb(info, DC_FG_CNTL_REG);
	if (ret < 0) {
		dev_warn(&info->pdev->dev, "FG CNTL reg read err!!\n");
	} else if ((ret & FG_CNTL_OCV_ADJ_EN) && (ret & FG_CNTL_CAP_ADJ_EN)) {
		dev_info(&info->pdev->dev,
			"FG data except the OCV curve is initialized\n");
		/*
		 * ocv curve will be set to default values
		 * at every boot, so it is needed to explicitly write
		 * the ocv curve data for each boot
		 */
		ret = pmic_fg_program_ocv_curve(info);
		if (ret < 0)
			dev_err(&info->pdev->dev,
				"set ocv curve fail:%d\n", ret);
		/* comment the following for FW may have touched the regsiters */
		/*info->fg_init_done = true;*/
		/*pmic_fg_dump_init_regs(info);*/
		/*return;*/
	} else {
		dev_info(&info->pdev->dev, "FG data need to be initialized\n");
	}


	ret = pmic_fg_program_ocv_curve(info);
	if (ret < 0)
		dev_err(&info->pdev->dev, "set ocv curve fail:%d\n", ret);

	ret = pmic_fg_program_rdc_vals(info);
	if (ret < 0)
		dev_err(&info->pdev->dev, "set rdc fail:%d\n", ret);

	ret = pmic_fg_program_design_cap(info);
	if (ret < 0)
		dev_err(&info->pdev->dev, "set design cap fail:%d\n", ret);

	ret = pmic_fg_program_vbatt_full(info);
	if (ret < 0)
		dev_err(&info->pdev->dev, "set vbatt full fail:%d\n", ret);

	ret = pmic_fg_set_lowbatt_thresholds(info);
	if (ret < 0)
		dev_err(&info->pdev->dev, "lowbatt thr set fail:%d\n", ret);

	ret = pmic_fg_reg_writeb(info, DC_FG_CNTL_REG, 0xff);
	if (ret < 0)
		dev_err(&info->pdev->dev, "gauge cntl set fail:%d\n", ret);

	info->fg_init_done = true;
	pmic_fg_dump_init_regs(info);
}
static void pmic_fg_dump_init_regs(struct pmic_fg_info *info)
{
	int i;

	dev_info(&info->pdev->dev, "reg:%x, val:%x\n",
			DC_CHRG_CCCV_REG,
			pmic_fg_reg_readb(info, DC_CHRG_CCCV_REG));

	for (i = 0; i < BAT_CURVE_SIZE; i++) {
		dev_info(&info->pdev->dev, "reg:%x, val:%x\n",
			DC_FG_OCV_CURVE_REG + i,
			pmic_fg_reg_readb(info, DC_FG_OCV_CURVE_REG + i));
	}

	dev_info(&info->pdev->dev, "reg:%x, val:%x\n",
				DC_FG_CNTL_REG,
				pmic_fg_reg_readb(info, DC_FG_CNTL_REG));
	dev_info(&info->pdev->dev, "reg:%x, val:%x\n",
				DC_FG_DES_CAP1_REG,
				pmic_fg_reg_readb(info, DC_FG_DES_CAP1_REG));
	dev_info(&info->pdev->dev, "reg:%x, val:%x\n",
				DC_FG_DES_CAP0_REG,
				pmic_fg_reg_readb(info, DC_FG_DES_CAP0_REG));
	dev_info(&info->pdev->dev, "reg:%x, val:%x\n",
				DC_FG_RDC1_REG,
				pmic_fg_reg_readb(info, DC_FG_RDC1_REG));
	dev_info(&info->pdev->dev, "reg:%x, val:%x\n",
				DC_FG_RDC0_REG,
				pmic_fg_reg_readb(info, DC_FG_RDC0_REG));
}
static void pmic_fg_init_config_regs(struct pmic_fg_info *info)
{
	int ret;


	/*
	 * check if the config data is already
	 * programmed and if so just return.
	 */
	ret = pmic_fg_reg_readb(info, DC_FG_CNTL_REG);
	if (ret < 0) {
		dev_warn(&info->pdev->dev, "FG CNTL reg read err!!\n");
	} else if ((ret & FG_CNTL_OCV_ADJ_EN) && (ret & FG_CNTL_CAP_ADJ_EN)) {
		dev_dbg(&info->pdev->dev, "FG data is already initialized\n");
		/* comment the following for FW may have touched the regsiters */
		/* info->fg_init_done = true; */
		/* pmic_fg_dump_init_regs(info); */
		/* return; */
	} else {
		dev_dbg(&info->pdev->dev, "FG data need to be initialized\n");
	}


	ret = pmic_fg_program_ocv_curve(info);
	if (ret < 0)
		dev_err(&info->pdev->dev, "set ocv curve fail:%d\n", ret);

	ret = pmic_fg_program_rdc_vals(info);
	if (ret < 0)
		dev_err(&info->pdev->dev, "set rdc fail:%d\n", ret);

	ret = pmic_fg_program_design_cap(info);
	if (ret < 0)
		dev_err(&info->pdev->dev, "set design cap fail:%d\n", ret);

	ret = pmic_fg_program_vbatt_full(info);
	if (ret < 0)
		dev_err(&info->pdev->dev, "set vbatt full fail:%d\n", ret);

	ret = pmic_fg_set_lowbatt_thresholds(info);
	if (ret < 0)
		dev_err(&info->pdev->dev, "lowbatt thr set fail:%d\n", ret);

	ret = pmic_fg_reg_writeb(info, DC_FG_CNTL_REG, 0xf7);
	if (ret < 0)
		dev_err(&info->pdev->dev, "gauge cntl set fail:%d\n", ret);

	info->fg_init_done = true;
	/* no need to dump registers except in debug cases
	pmic_fg_dump_init_regs(info); */
}
static int pmic_fg_get_vocv(struct pmic_fg_info *info, int *vocv)
{
	int ret, value;

	/*
	 * OCV readings are 12-bit length. So Read
	 * the MSB first left-shift by 4 bits and
	 * read the lower nibble.
	 */
	ret = pmic_fg_reg_readb(info, DC_FG_OCVH_REG);
	if (ret < 0)
		goto vocv_read_fail;
	value = ret << 4;

	ret = pmic_fg_reg_readb(info, DC_FG_OCVL_REG);
	if (ret < 0)
		goto vocv_read_fail;
	value |= (ret & 0xf);

	*vocv = ADC_TO_VBATT(value);
vocv_read_fail:
	return ret;
}
static int pmic_fg_get_capacity(struct pmic_fg_info *info)
{
	int ret, value, cap, cc_cap;

	ret = pmic_fg_get_vocv(info, &value);
	if (ret < 0)
		return ret;

	/* do Vocv min threshold check */
	if (value < info->pdata->design_min_volt)
		return 0;


	ret = pmic_fg_reg_readb(info, DC_FG_REP_CAP_REG);
	if (ret < 0)
		return ret;

	if (!(ret & FG_REP_CAP_VALID))
		dev_err(&info->pdev->dev,
				"capacity measurement not valid\n");

	/*
	 * read the coulomb meter capacity to report SOC
	 * when the FULL is detected, as RepSoC is not
	 * hitting 100% in some cases.
	 */
	if (info->status == POWER_SUPPLY_STATUS_FULL) {
		cc_cap = pmic_fg_reg_readb(info, DC_FG_CC_CAP_REG);
		if (cc_cap < 0)
			return cc_cap;
		cap = (cc_cap & FG_CC_CAP_VAL_MASK);
	} else {
		cap = (ret & FG_REP_CAP_VAL_MASK);
	}
	return cap;
}
static int pmic_fg_update_config_params(struct pmic_fg_info *info)
{
	int ret, i;

	ret = pmic_fg_reg_readb(info, DC_FG_DES_CAP1_REG);
	if (ret < 0)
		goto fg_svae_cfg_fail;
	info->cfg->cap1 = ret;

	/*
	 * higher byte and lower byte reads should be
	 * back to back to get successful lower byte result.
	 */
	pmic_fg_reg_readb(info, DC_FG_DES_CAP1_REG);
	ret = pmic_fg_reg_readb(info, DC_FG_DES_CAP0_REG);
	if (ret < 0)
		goto fg_svae_cfg_fail;
	else
		info->cfg->cap0 = ret;

	ret = pmic_fg_reg_readb(info, DC_FG_RDC1_REG);
	if (ret < 0)
		goto fg_svae_cfg_fail;
	else
		info->cfg->rdc1 = ret;

	/*
	 * higher byte and lower byte reads should be
	 * back to back to get successful lower byte result.
	 */
	pmic_fg_reg_readb(info, DC_FG_RDC1_REG);
	ret = pmic_fg_reg_readb(info, DC_FG_RDC0_REG);
	if (ret < 0)
		goto fg_svae_cfg_fail;
	else
		info->cfg->rdc0 = ret;

	for (i = 0; i < BAT_CURVE_SIZE; i++) {
		ret = pmic_fg_reg_readb(info, DC_FG_OCV_CURVE_REG + i);
		if (ret < 0)
			goto fg_svae_cfg_fail;
		else
			info->cfg->bat_curve[i] = ret;
	}

	return 0;

fg_svae_cfg_fail:
	return ret;
}
static int pmic_fg_get_battery_property(struct power_supply *psy,
					enum power_supply_property psp,
					union power_supply_propval *val)
{
	struct pmic_fg_info *info = container_of(psy,
				struct pmic_fg_info, bat);
	int ret = 0, value, cc_cap;

	mutex_lock(&info->lock);
	switch (psp) {
	case POWER_SUPPLY_PROP_STATUS:
		val->intval = info->status;
		break;
	case POWER_SUPPLY_PROP_HEALTH:
		val->intval = pmic_fg_battery_health(info);
		info->health = val->intval;
		break;
	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
		ret = pmic_fg_get_vbatt(info, &value);
		if (ret < 0)
			goto pmic_fg_read_err;
		val->intval = value * 1000;
		break;
	case POWER_SUPPLY_PROP_VOLTAGE_OCV:
		ret = pmic_fg_get_vocv(info, &value);
		if (ret < 0)
			goto pmic_fg_read_err;

		val->intval = value * 1000;
		break;
	case POWER_SUPPLY_PROP_CURRENT_NOW:
	case POWER_SUPPLY_PROP_CURRENT_AVG:
		ret = pmic_fg_get_current(info, &value);
		if (ret < 0)
			goto pmic_fg_read_err;
		val->intval = value * 1000;
		break;
	case POWER_SUPPLY_PROP_PRESENT:
		ret = pmic_fg_reg_readb(info, DC_CHRG_STAT_REG);
		if (ret < 0)
			goto pmic_fg_read_err;

		if (ret & CHRG_STAT_BAT_PRESENT)
			val->intval = 1;
		else
			val->intval = 0;
		break;
	case POWER_SUPPLY_PROP_CAPACITY:
		if (info->status == POWER_SUPPLY_STATUS_FULL)
			val->intval = 100;
		else {
			ret = pmic_fg_get_capacity(info);
			if (ret < 0)
				goto pmic_fg_read_err;
			val->intval = ret;
		}
		break;
	case POWER_SUPPLY_PROP_TEMP:
		ret = pmic_fg_get_btemp(info, &value);
		if (ret < 0)
			goto pmic_fg_read_err;
		val->intval = value * 10;
		break;
	case POWER_SUPPLY_PROP_TECHNOLOGY:
		val->intval = info->pdata->technology;
		break;
	case POWER_SUPPLY_PROP_CHARGE_NOW:
		ret = pmic_fg_get_charge_now(info, &value);
		if (ret < 0)
			goto pmic_fg_read_err;
		val->intval = value;
		break;
	case POWER_SUPPLY_PROP_CHARGE_FULL:
		ret = pmic_fg_get_charge_full(info, &value);
		if (ret < 0)
			goto pmic_fg_read_err;
		val->intval = value;
		break;
	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
		val->intval = info->pdata->design_cap * 1000;
		break;
	case POWER_SUPPLY_PROP_MODEL_NAME:
		val->strval = info->pdata->battid;
		break;
	default:
		mutex_unlock(&info->lock);
		return -EINVAL;
	}

	mutex_unlock(&info->lock);
	return 0;

pmic_fg_read_err:
	mutex_unlock(&info->lock);
	return ret;
}