power: bms: Report zero SOC if voltage is below cutoff

If the battery voltage remains below the cutoff threshold
for a specific period (determined by a platform data -
vbatt_cutoff_retries) report the SOC as 0. This allows
the userspace to perform a graceful shutdown. Also, provide
a platform data entry (low_voltage_detect) to enable this
functionality.
Enable 'low_voltage_detect' and set 'vbatt_cutoff_retries'
as required to enable the full functionality.

CRs-Fixed: 450387
Change-Id: I668d391cab3d0424fe5f370df0c1169124c34f5e
Signed-off-by: Anirudh Ghayal <aghayal@codeaurora.org>
This commit is contained in:
Anirudh Ghayal
2013-03-24 22:04:25 -07:00
committed by Gerrit - the friendly Code Review server
parent 8e9aa3235d
commit 0c7c2bcff1
2 changed files with 63 additions and 10 deletions
+59 -10
View File
@@ -59,6 +59,8 @@
#define PON_CNTRL_6 0x018
#define WD_BIT BIT(7)
#define BATT_ALARM_ACCURACY 50 /* 50mV */
enum pmic_bms_interrupts {
PM8921_BMS_SBI_WRITE_OK,
PM8921_BMS_CC_THR,
@@ -159,7 +161,6 @@ struct pm8921_bms_chip {
int soc_at_cv;
int prev_chg_soc;
struct power_supply *batt_psy;
bool low_voltage_wake_lock_held;
struct wake_lock low_voltage_wake_lock;
int soc_calc_period;
int normal_voltage_calc_ms;
@@ -170,6 +171,9 @@ struct pm8921_bms_chip {
int ocv_dis_high_soc;
int ocv_dis_low_soc;
int prev_vbat_batt_terminal_uv;
int vbatt_cutoff_count;
int low_voltage_detect;
int vbatt_cutoff_retries;
};
/*
@@ -391,6 +395,20 @@ static int usb_chg_plugged_in(struct pm8921_bms_chip *chip)
return val;
}
static void pm8921_bms_low_voltage_config(struct pm8921_bms_chip *chip,
int time_ms)
{
int ms = 0;
/* if work was pending and was cancelled, calculate SOC immediately */
if (!cancel_delayed_work_sync(&chip->calculate_soc_delayed_work))
ms = time_ms;
chip->soc_calc_period = time_ms;
schedule_delayed_work(&chip->calculate_soc_delayed_work,
msecs_to_jiffies(ms));
}
static int pm8921_bms_enable_batt_alarm(struct pm8921_bms_chip *chip)
{
int rc = 0;
@@ -479,8 +497,12 @@ static int pm8921_battery_gauge_alarm_notify(struct notifier_block *nb,
* hold the low voltage wakelock until the soc
* work finds it appropriate to release it.
*/
wake_lock(&the_chip->low_voltage_wake_lock);
the_chip->low_voltage_wake_lock_held = 1;
if (!wake_lock_active(&the_chip->low_voltage_wake_lock)) {
pr_debug("Holding low voltage wakelock\n");
wake_lock(&the_chip->low_voltage_wake_lock);
pm8921_bms_low_voltage_config(the_chip,
the_chip->low_voltage_calc_ms);
}
rc = pm8xxx_batt_alarm_disable(
PM8XXX_BATT_ALARM_LOWER_COMPARATOR);
@@ -1788,26 +1810,42 @@ static void very_low_voltage_check(struct pm8921_bms_chip *chip,
* if battery is very low (v_cutoff voltage + 20mv) hold
* a wakelock untill soc = 0%
*/
if (vbat_uv <= (chip->v_cutoff + 20) * 1000
&& !chip->low_voltage_wake_lock_held) {
if (vbat_uv <= (chip->alarm_low_mv + 20) * 1000 &&
!wake_lock_active(&the_chip->low_voltage_wake_lock)) {
pr_debug("voltage = %d low holding wakelock\n", vbat_uv);
wake_lock(&chip->low_voltage_wake_lock);
chip->low_voltage_wake_lock_held = 1;
chip->soc_calc_period = chip->low_voltage_calc_ms;
}
if (vbat_uv > (chip->v_cutoff + 20) * 1000
&& chip->low_voltage_wake_lock_held) {
if (vbat_uv > (chip->alarm_low_mv + 20 + BATT_ALARM_ACCURACY) * 1000
&& wake_lock_active(&the_chip->low_voltage_wake_lock)) {
pr_debug("voltage = %d releasing wakelock\n", vbat_uv);
chip->low_voltage_wake_lock_held = 0;
wake_unlock(&chip->low_voltage_wake_lock);
chip->vbatt_cutoff_count = 0;
chip->soc_calc_period = chip->normal_voltage_calc_ms;
rc = pm8921_bms_enable_batt_alarm(chip);
if (rc)
pr_err("Unable to enable batt alarm\n");
wake_unlock(&chip->low_voltage_wake_lock);
}
}
static bool is_voltage_below_cutoff_window(struct pm8921_bms_chip *chip,
int ibat_ua, int vbat_uv)
{
if (vbat_uv < (chip->v_cutoff * 1000) && ibat_ua > 0) {
chip->vbatt_cutoff_count++;
if (chip->vbatt_cutoff_count >= chip->vbatt_cutoff_retries) {
pr_debug("cutoff_count >= %d\n",
chip->vbatt_cutoff_retries);
return true;
}
} else {
chip->vbatt_cutoff_count = 0;
}
return false;
}
static int last_soc_est = -EINVAL;
static int adjust_soc(struct pm8921_bms_chip *chip, int soc,
int batt_temp, int chargecycles,
@@ -1834,6 +1872,15 @@ static int adjust_soc(struct pm8921_bms_chip *chip, int soc,
very_low_voltage_check(chip, ibat_ua, vbat_uv);
if (chip->low_voltage_detect &&
wake_lock_active(&chip->low_voltage_wake_lock)) {
if (is_voltage_below_cutoff_window(chip, ibat_ua, vbat_uv)) {
soc = 0;
pr_info("Voltage below cutoff, setting soc to 0\n");
goto out;
}
}
delta_ocv_uv_limit = DIV_ROUND_CLOSEST(ibat_ua, 1000);
ocv_est_uv = vbat_uv + (ibat_ua * rbatt)/1000;
@@ -3327,6 +3374,8 @@ static int __devinit pm8921_bms_probe(struct platform_device *pdev)
chip->alarm_low_mv = pdata->alarm_low_mv;
chip->alarm_high_mv = pdata->alarm_high_mv;
chip->low_voltage_detect = pdata->low_voltage_detect;
chip->vbatt_cutoff_retries = pdata->vbatt_cutoff_retries;
mutex_init(&chip->calib_mutex);
INIT_WORK(&chip->calib_hkadc_work, calibrate_hkadc_work);
+4
View File
@@ -44,6 +44,8 @@ struct pm8xxx_bms_core_data {
* @disable_flat_portion_ocv: feature to disable ocv updates while in sleep
* @ocv_dis_high_soc: the high soc percent when ocv should be disabled
* @ocv_dis_low_soc: the low soc percent when ocv should be enabled
* @low_voltage_detect: feature to enable 0 SOC reporting on low volatge
* @vbatt_cutoff_retries: number of tries before we report a 0 SOC
*/
struct pm8921_bms_platform_data {
struct pm8xxx_bms_core_data bms_cdata;
@@ -65,6 +67,8 @@ struct pm8921_bms_platform_data {
int disable_flat_portion_ocv;
int ocv_dis_high_soc;
int ocv_dis_low_soc;
int low_voltage_detect;
int vbatt_cutoff_retries;
};
#if defined(CONFIG_PM8921_BMS) || defined(CONFIG_PM8921_BMS_MODULE)