summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorSubbaraman Narayanamurthy <subbaram@codeaurora.org>2016-01-11 12:07:41 -0800
committerDavid Keitel <dkeitel@codeaurora.org>2016-03-23 21:23:02 -0700
commit3ad8f9a8786bbf40dd1c3246f29eb4a32f7f7caa (patch)
tree60544988312aeeb6cdcc5899a96c361b0732ded1 /drivers
parent473c773d5417c5a0974cfb12dd3ef461ed4ace3a (diff)
power: qpnp-fg: disable charging during battery profile loading
Currently, a battery profile is loaded when the profile integrity bit is not present, a new battery is inserted, or battery voltage is not in range across reboot. When the battery profile loading happens after the device has booted up, the charger driver is notified to disable battery charging. However, in cases like offline mode charging, the device can boot up again with charging enabled causing battery parameters to be modified even before the charger driver can probe. This causes a jump in state of charge (SOC) across reboot and causes the FG to restart. To help with this scenario, make sure the charger driver is ready, and charging is kept disabled before the battery profile loading process start. Enable charging back when there is no need to reload the battery profile, or the FG has restarted and the first SOC estimate is done. CRs-Fixed: 953448 Change-Id: I90b1c81ce502c0f6e96feab3b23cff61054422cb Signed-off-by: Subbaraman Narayanamurthy <subbaram@codeaurora.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/power/qpnp-fg.c130
1 files changed, 107 insertions, 23 deletions
diff --git a/drivers/power/qpnp-fg.c b/drivers/power/qpnp-fg.c
index 07f1803c724e..d91286dd4f31 100644
--- a/drivers/power/qpnp-fg.c
+++ b/drivers/power/qpnp-fg.c
@@ -466,6 +466,7 @@ struct fg_chip {
bool safety_timer_expired;
bool bad_batt_detection_en;
bool bcl_lpm_disabled;
+ bool charging_disabled;
struct delayed_work update_jeita_setting;
struct delayed_work update_sram_data;
struct delayed_work update_temp_work;
@@ -3269,22 +3270,42 @@ static bool is_otg_present(struct fg_chip *chip)
return prop.intval != 0;
}
+static bool is_charger_available(struct fg_chip *chip)
+{
+ if (!chip->batt_psy_name)
+ return false;
+
+ if (!chip->batt_psy)
+ chip->batt_psy = power_supply_get_by_name(chip->batt_psy_name);
+
+ if (!chip->batt_psy)
+ return false;
+
+ return true;
+}
+
static int set_prop_enable_charging(struct fg_chip *chip, bool enable)
{
int rc = 0;
union power_supply_propval ret = {enable, };
- if (!chip->batt_psy)
- chip->batt_psy = power_supply_get_by_name("battery");
+ if (!is_charger_available(chip)) {
+ pr_err("Charger not available yet!\n");
+ return -EINVAL;
+ }
- if (chip->batt_psy) {
- rc = power_supply_set_property(chip->batt_psy,
- POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED,
- &ret);
- if (rc)
- pr_err("couldn't configure batt chg %d\n", rc);
+ rc = power_supply_set_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_BATTERY_CHARGING_ENABLED,
+ &ret);
+ if (rc) {
+ pr_err("couldn't configure batt chg %d\n", rc);
+ return rc;
}
+ chip->charging_disabled = !enable;
+ if (fg_debug_mask & FG_STATUS)
+ pr_info("%sabling charging\n", enable ? "en" : "dis");
+
return rc;
}
@@ -3653,15 +3674,12 @@ static void update_esr_value(struct work_struct *work)
struct fg_chip,
update_esr_work);
- if (!chip->batt_psy && chip->batt_psy_name)
- chip->batt_psy = power_supply_get_by_name(chip->batt_psy_name);
-
- if (chip->batt_psy)
- power_supply_get_property(chip->batt_psy,
- POWER_SUPPLY_PROP_CHARGE_TYPE, &prop);
- else
+ if (!is_charger_available(chip))
return;
+ power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_CHARGE_TYPE, &prop);
+
if (!chip->esr_strict_filter) {
if ((prop.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER &&
chip->status == POWER_SUPPLY_STATUS_CHARGING) ||
@@ -4343,13 +4361,41 @@ static void update_cc_cv_setpoint(struct fg_chip *chip)
#define PROFILE_LOAD_TIMEOUT_MS 5000
static int fg_do_restart(struct fg_chip *chip, bool write_profile)
{
- int rc;
+ int rc, ibat_ua;
u8 reg = 0;
u8 buf[2];
+ bool tried_once = false;
if (fg_debug_mask & FG_STATUS)
pr_info("restarting fuel gauge...\n");
+try_again:
+ if (write_profile) {
+ if (!chip->charging_disabled) {
+ pr_err("Charging not yet disabled!\n");
+ return -EINVAL;
+ }
+
+ ibat_ua = get_sram_prop_now(chip, FG_DATA_CURRENT);
+ if (ibat_ua == -EINVAL) {
+ pr_err("SRAM not updated yet!\n");
+ return ibat_ua;
+ }
+
+ if (ibat_ua < 0) {
+ pr_warn("Charging enabled?, ibat_ua: %d\n", ibat_ua);
+
+ if (!tried_once) {
+ cancel_delayed_work(&chip->update_sram_data);
+ schedule_delayed_work(&chip->update_sram_data,
+ msecs_to_jiffies(0));
+ msleep(1000);
+ tried_once = true;
+ goto try_again;
+ }
+ }
+ }
+
chip->fg_restarting = true;
/*
* save the temperature if the sw rbias control is active so that there
@@ -4526,6 +4572,16 @@ static int fg_do_restart(struct fg_chip *chip, bool write_profile)
goto fail;
}
}
+
+ /* Enable charging now as the first estimate is done now */
+ if (chip->charging_disabled) {
+ rc = set_prop_enable_charging(chip, true);
+ if (rc)
+ pr_err("Failed to enable charging, rc=%d\n", rc);
+ else
+ chip->charging_disabled = false;
+ }
+
chip->fg_restarting = false;
if (fg_debug_mask & FG_STATUS)
@@ -4682,6 +4738,19 @@ wait:
goto no_profile;
}
+ /* Check whether the charger is ready */
+ if (!is_charger_available(chip))
+ goto reschedule;
+
+ /* Disable charging for a FG cycle before calculating vbat_in_range */
+ if (!chip->charging_disabled) {
+ rc = set_prop_enable_charging(chip, false);
+ if (rc)
+ pr_err("Failed to disable charging, rc=%d\n", rc);
+
+ goto reschedule;
+ }
+
vbat_in_range = get_vbat_est_diff(chip)
< settings[FG_MEM_VBAT_EST_DIFF].value * 1000;
profiles_same = memcmp(chip->batt_profile, data,
@@ -4700,30 +4769,28 @@ wait:
clear_cycle_counter(chip);
chip->learning_data.learned_cc_uah = 0;
}
+
if (fg_est_dump)
dump_sram(&chip->dump_sram);
+
if ((fg_debug_mask & FG_STATUS) && !vbat_in_range)
pr_info("Vbat out of range: v_current_pred: %d, v:%d\n",
fg_data[FG_DATA_CPRED_VOLTAGE].value,
fg_data[FG_DATA_VOLTAGE].value);
+
if ((fg_debug_mask & FG_STATUS) && fg_is_batt_empty(chip))
pr_info("battery empty\n");
+
if ((fg_debug_mask & FG_STATUS) && !profiles_same)
pr_info("profiles differ\n");
+
if (fg_debug_mask & FG_STATUS) {
pr_info("Using new profile\n");
print_hex_dump(KERN_INFO, "FG: loaded profile: ",
DUMP_PREFIX_NONE, 16, 1,
chip->batt_profile, len, false);
}
- if (!chip->batt_psy && chip->batt_psy_name)
- chip->batt_psy = power_supply_get_by_name(chip->batt_psy_name);
- if (!chip->batt_psy) {
- if (fg_debug_mask & FG_STATUS)
- pr_info("batt psy not registered\n");
- goto reschedule;
- }
if (chip->power_supply_registered)
power_supply_changed(chip->bms_psy);
@@ -4764,6 +4831,14 @@ wait:
}
done:
+ if (chip->charging_disabled) {
+ rc = set_prop_enable_charging(chip, true);
+ if (rc)
+ pr_err("Failed to enable charging, rc=%d\n", rc);
+ else
+ chip->charging_disabled = false;
+ }
+
if (fg_batt_type)
chip->batt_type = fg_batt_type;
else
@@ -4787,6 +4862,14 @@ done:
fg_data[FG_DATA_VOLTAGE].value);
return rc;
no_profile:
+ if (chip->charging_disabled) {
+ rc = set_prop_enable_charging(chip, true);
+ if (rc)
+ pr_err("Failed to enable charging, rc=%d\n", rc);
+ else
+ chip->charging_disabled = false;
+ }
+
if (chip->power_supply_registered)
power_supply_changed(chip->bms_psy);
fg_relax(&chip->profile_wakeup_source);
@@ -5401,6 +5484,7 @@ static void fg_cleanup(struct fg_chip *chip)
cancel_work_sync(&chip->gain_comp_work);
cancel_work_sync(&chip->init_work);
cancel_work_sync(&chip->charge_full_work);
+ cancel_work_sync(&chip->esr_extract_config_work);
mutex_destroy(&chip->rslow_comp.lock);
mutex_destroy(&chip->rw_lock);
mutex_destroy(&chip->cyc_ctr.lock);