From fbb8dc031a2e5fef161bbddbee679a066c272e97 Mon Sep 17 00:00:00 2001 From: Abhijeet Dharmapurikar Date: Wed, 1 Aug 2012 21:33:09 -0700 Subject: [PATCH] power: pm8921-bms: limit voltage correction The "adjust_soc" algorithm where we change the open circuit voltage (ocv) in steps so that the state of charge (soc) starts approaching the estimated soc, causes nonlinearity in the soc curves if an incorrect resistance value is fed to the algorithm. As battery ages and temperature changes, it is hard to estimate the exact battery resistance. So to fix the nonlinearity, limit the amount by which the ocv is changed. Make it proportional to the current, i.e. change ocv by small amounts in light load and let it change by proportionally large amounts in heavy load situations. Also, make the point where the soc is adjusted configurable via platform data instead of forcing it to 25%. Change-Id: Idc141e6bf3172dab278afe1900f5a1f9cdd624dd Signed-off-by: Abhijeet Dharmapurikar --- arch/arm/mach-msm/board-8064-pmic.c | 1 + arch/arm/mach-msm/board-8930-pmic.c | 1 + arch/arm/mach-msm/board-8960-pmic.c | 1 + drivers/power/pm8921-bms.c | 24 +++++++++++++++++++++++- include/linux/mfd/pm8xxx/pm8921-bms.h | 1 + 5 files changed, 27 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-msm/board-8064-pmic.c b/arch/arm/mach-msm/board-8064-pmic.c index 518579a986c..1f39d3f2cbf 100644 --- a/arch/arm/mach-msm/board-8064-pmic.c +++ b/arch/arm/mach-msm/board-8064-pmic.c @@ -394,6 +394,7 @@ apq8064_pm8921_bms_pdata __devinitdata = { .v_cutoff = 3400, .max_voltage_uv = MAX_VOLTAGE_MV * 1000, .shutdown_soc_valid_limit = 20, + .adjust_soc_low_threshold = 25, }; static struct pm8921_platform_data diff --git a/arch/arm/mach-msm/board-8930-pmic.c b/arch/arm/mach-msm/board-8930-pmic.c index 384b1cf3ec7..c16f55dc8c5 100644 --- a/arch/arm/mach-msm/board-8930-pmic.c +++ b/arch/arm/mach-msm/board-8930-pmic.c @@ -345,6 +345,7 @@ static struct pm8921_bms_platform_data pm8921_bms_pdata __devinitdata = { .v_cutoff = 3400, .max_voltage_uv = MAX_VOLTAGE_MV * 1000, .shutdown_soc_valid_limit = 20, + .adjust_soc_low_threshold = 25, }; static struct pm8038_platform_data pm8038_platform_data __devinitdata = { diff --git a/arch/arm/mach-msm/board-8960-pmic.c b/arch/arm/mach-msm/board-8960-pmic.c index 98140b4e043..46d317b6234 100644 --- a/arch/arm/mach-msm/board-8960-pmic.c +++ b/arch/arm/mach-msm/board-8960-pmic.c @@ -426,6 +426,7 @@ static struct pm8921_bms_platform_data pm8921_bms_pdata __devinitdata = { .max_voltage_uv = MAX_VOLTAGE_MV * 1000, .rconn_mohm = 18, .shutdown_soc_valid_limit = 20, + .adjust_soc_low_threshold = 25, }; #define PM8921_LC_LED_MAX_CURRENT 4 /* I = 4mA */ diff --git a/drivers/power/pm8921-bms.c b/drivers/power/pm8921-bms.c index f26ecd9114f..5ddca3e5a5c 100644 --- a/drivers/power/pm8921-bms.c +++ b/drivers/power/pm8921-bms.c @@ -142,6 +142,7 @@ struct pm8921_bms_chip { int ignore_shutdown_soc; int prev_iavg_ua; int prev_uuc_iavg_ma; + int adjust_soc_low_threshold; }; /* @@ -1440,6 +1441,7 @@ static int adjust_soc(struct pm8921_bms_chip *chip, int soc, int batt_temp, int soc_new = 0; int m = 0; int rc = 0; + int delta_ocv_uv_limit = 0; rc = pm8921_bms_get_simultaneous_battery_voltage_and_current( &ibat_ua, @@ -1452,6 +1454,9 @@ static int adjust_soc(struct pm8921_bms_chip *chip, int soc, int batt_temp, if (ibat_ua < 0) goto out; + + delta_ocv_uv_limit = DIV_ROUND_CLOSEST(ibat_ua, 1000); + ocv_est_uv = vbat_uv + (ibat_ua * rbatt)/1000; pc_est = calculate_pc(chip, ocv_est_uv, batt_temp, last_chargecycles); soc_est = div_s64((s64)fcc_uah * pc_est - uuc_uah*100, @@ -1469,7 +1474,8 @@ static int adjust_soc(struct pm8921_bms_chip *chip, int soc, int batt_temp, * and cause a bad user experience */ if (soc_est == soc - || (is_between(45, 25, soc_est) && is_between(50, 20, soc)) + || (is_between(45, chip->adjust_soc_low_threshold, soc_est) + && is_between(50, chip->adjust_soc_low_threshold - 5, soc)) || soc >= 90) goto out; @@ -1504,6 +1510,18 @@ static int adjust_soc(struct pm8921_bms_chip *chip, int soc, int batt_temp, delta_ocv_uv = div_s64((soc - soc_est) * (s64)m * 1000, n * (pc - pc_new)); + + if (abs(delta_ocv_uv) > delta_ocv_uv_limit) { + pr_debug("limiting delta ocv %d limit = %d\n", delta_ocv_uv, + delta_ocv_uv_limit); + + if (delta_ocv_uv > 0) + delta_ocv_uv = delta_ocv_uv_limit; + else + delta_ocv_uv = -1 * delta_ocv_uv_limit; + pr_debug("new delta ocv = %d\n", delta_ocv_uv); + } + chip->last_ocv_uv -= delta_ocv_uv; if (chip->last_ocv_uv >= chip->max_voltage_uv) @@ -3038,6 +3056,10 @@ static int __devinit pm8921_bms_probe(struct platform_device *pdev) chip->start_percent = -EINVAL; chip->end_percent = -EINVAL; chip->shutdown_soc_valid_limit = pdata->shutdown_soc_valid_limit; + chip->adjust_soc_low_threshold = pdata->adjust_soc_low_threshold; + if (chip->adjust_soc_low_threshold >= 45) + chip->adjust_soc_low_threshold = 45; + chip->ignore_shutdown_soc = pdata->ignore_shutdown_soc; rc = set_battery_data(chip); if (rc) { diff --git a/include/linux/mfd/pm8xxx/pm8921-bms.h b/include/linux/mfd/pm8xxx/pm8921-bms.h index 070b4a9a9fe..d1438d1c2b3 100644 --- a/include/linux/mfd/pm8xxx/pm8921-bms.h +++ b/include/linux/mfd/pm8xxx/pm8921-bms.h @@ -132,6 +132,7 @@ struct pm8921_bms_platform_data { int enable_fcc_learning; int shutdown_soc_valid_limit; int ignore_shutdown_soc; + int adjust_soc_low_threshold; }; #if defined(CONFIG_PM8921_BMS) || defined(CONFIG_PM8921_BMS_MODULE)