From 580665279fb6a16eb119ec80a5be1b3ec7a641a0 Mon Sep 17 00:00:00 2001 From: Yuanjiang Yu Date: Wed, 31 Jul 2019 18:00:26 +0800 Subject: [PATCH] power: supply: sc27xx: Optimize the battery capacity calibration This patch factors out the capacity calibration into one single function to calibrate the battery capacity, and adding more abnormal cases to calibrate the capacity when the OCV value is not matchable with current capacity. Moreover we also allow to calibrate the capacity when charger magager tries to get current capacity to make sure we give a correct capacity for userspace. Signed-off-by: Yuanjiang Yu Signed-off-by: Baolin Wang Signed-off-by: Sebastian Reichel --- drivers/power/supply/sc27xx_fuel_gauge.c | 147 ++++++++++++++++------- 1 file changed, 102 insertions(+), 45 deletions(-) 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);