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