diff --git a/drivers/power/supply/sc27xx_fuel_gauge.c b/drivers/power/supply/sc27xx_fuel_gauge.c index 096da951f465..6071671caaa9 100644 --- a/drivers/power/supply/sc27xx_fuel_gauge.c +++ b/drivers/power/supply/sc27xx_fuel_gauge.c @@ -109,6 +109,8 @@ struct sc27xx_fgu_data { }; static int sc27xx_fgu_cap_to_clbcnt(struct sc27xx_fgu_data *data, int capacity); +static void sc27xx_fgu_capacity_calibration(struct sc27xx_fgu_data *data, + int cap, bool int_mode); static const char * const sc27xx_charger_supply_name[] = { "sc2731_charger", @@ -389,6 +391,9 @@ static int sc27xx_fgu_get_capacity(struct sc27xx_fgu_data *data, int *cap) delta_cap = DIV_ROUND_CLOSEST(temp * 100, data->total_cap); *cap = delta_cap + data->init_cap; + /* Calibrate the battery capacity in a normal range. */ + sc27xx_fgu_capacity_calibration(data, *cap, false); + return 0; } @@ -661,14 +666,108 @@ static const struct power_supply_desc sc27xx_fgu_desc = { static void sc27xx_fgu_adjust_cap(struct sc27xx_fgu_data *data, int cap) { + int ret; + data->init_cap = cap; - data->init_clbcnt = sc27xx_fgu_cap_to_clbcnt(data, data->init_cap); + ret = sc27xx_fgu_get_clbcnt(data, &data->init_clbcnt); + if (ret) + dev_err(data->dev, "failed to get init coulomb counter\n"); +} + +static void sc27xx_fgu_capacity_calibration(struct sc27xx_fgu_data *data, + int cap, bool int_mode) +{ + int ret, ocv, chg_sts, adc; + + ret = sc27xx_fgu_get_vbat_ocv(data, &ocv); + if (ret) { + dev_err(data->dev, "get battery ocv error.\n"); + return; + } + + ret = sc27xx_fgu_get_status(data, &chg_sts); + if (ret) { + dev_err(data->dev, "get charger status error.\n"); + return; + } + + /* + * If we are in charging mode, then we do not need to calibrate the + * lower capacity. + */ + if (chg_sts == POWER_SUPPLY_STATUS_CHARGING) + return; + + if ((ocv > data->cap_table[0].ocv && cap < 100) || cap > 100) { + /* + * If current OCV value is larger than the max OCV value in + * OCV table, or the current capacity is larger than 100, + * we should force the inititial capacity to 100. + */ + sc27xx_fgu_adjust_cap(data, 100); + } else if (ocv <= data->cap_table[data->table_len - 1].ocv) { + /* + * If current OCV value is leass than the minimum OCV value in + * OCV table, we should force the inititial capacity to 0. + */ + sc27xx_fgu_adjust_cap(data, 0); + } else if ((ocv > data->cap_table[data->table_len - 1].ocv && cap <= 0) || + (ocv > data->min_volt && cap <= data->alarm_cap)) { + /* + * If current OCV value is not matchable with current capacity, + * we should re-calculate current capacity by looking up the + * OCV table. + */ + int cur_cap = power_supply_ocv2cap_simple(data->cap_table, + data->table_len, ocv); + + sc27xx_fgu_adjust_cap(data, cur_cap); + } else if (ocv <= data->min_volt) { + /* + * If current OCV value is less than the low alarm voltage, but + * current capacity is larger than the alarm capacity, we should + * adjust the inititial capacity to alarm capacity. + */ + if (cap > data->alarm_cap) { + sc27xx_fgu_adjust_cap(data, data->alarm_cap); + } else { + int cur_cap; + + /* + * If current capacity is equal with 0 or less than 0 + * (some error occurs), we should adjust inititial + * capacity to the capacity corresponding to current OCV + * value. + */ + cur_cap = power_supply_ocv2cap_simple(data->cap_table, + data->table_len, + ocv); + sc27xx_fgu_adjust_cap(data, cur_cap); + } + + if (!int_mode) + return; + + /* + * After adjusting the battery capacity, we should set the + * lowest alarm voltage instead. + */ + data->min_volt = data->cap_table[data->table_len - 1].ocv; + data->alarm_cap = power_supply_ocv2cap_simple(data->cap_table, + data->table_len, + data->min_volt); + + adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000); + regmap_update_bits(data->regmap, + data->base + SC27XX_FGU_LOW_OVERLOAD, + SC27XX_FGU_LOW_OVERLOAD_MASK, adc); + } } static irqreturn_t sc27xx_fgu_interrupt(int irq, void *dev_id) { struct sc27xx_fgu_data *data = dev_id; - int ret, cap, ocv, adc; + int ret, cap; u32 status; mutex_lock(&data->lock); @@ -694,49 +793,7 @@ static irqreturn_t sc27xx_fgu_interrupt(int irq, void *dev_id) if (ret) goto out; - ret = sc27xx_fgu_get_vbat_ocv(data, &ocv); - if (ret) - goto out; - - /* - * If current OCV value is less than the minimum OCV value in OCV table, - * which means now battery capacity is 0%, and we should adjust the - * inititial capacity to 0. - */ - if (ocv <= data->cap_table[data->table_len - 1].ocv) { - sc27xx_fgu_adjust_cap(data, 0); - } else if (ocv <= data->min_volt) { - /* - * If current OCV value is less than the low alarm voltage, but - * current capacity is larger than the alarm capacity, we should - * adjust the inititial capacity to alarm capacity. - */ - if (cap > data->alarm_cap) { - sc27xx_fgu_adjust_cap(data, data->alarm_cap); - } else if (cap <= 0) { - int cur_cap; - - /* - * If current capacity is equal with 0 or less than 0 - * (some error occurs), we should adjust inititial - * capacity to the capacity corresponding to current OCV - * value. - */ - cur_cap = power_supply_ocv2cap_simple(data->cap_table, - data->table_len, - ocv); - sc27xx_fgu_adjust_cap(data, cur_cap); - } - - /* - * After adjusting the battery capacity, we should set the - * lowest alarm voltage instead. - */ - data->min_volt = data->cap_table[data->table_len - 1].ocv; - adc = sc27xx_fgu_voltage_to_adc(data, data->min_volt / 1000); - regmap_update_bits(data->regmap, data->base + SC27XX_FGU_LOW_OVERLOAD, - SC27XX_FGU_LOW_OVERLOAD_MASK, adc); - } + sc27xx_fgu_capacity_calibration(data, cap, true); out: mutex_unlock(&data->lock);