Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6ed51a55 authored by Xiaozhe Shi's avatar Xiaozhe Shi Committed by Nicholas Troast
Browse files

power: qpnp-fg: fix peripheral restart code



Currently the do_restart code does not give the same guarantee as profile
initialization. During a manual fuel gauge peripheral restart, the
driver could display the incorrect battery type, and cause SRAM
read/writes to fail silently.

Specifically, there's a period of time between putting in the battery
and the battery being loaded where the battery is considered missing,
even when it's not. The missing battery type will not change until the
battery profile is loaded. In the worst case, if the new battery is
unrecognized, it will just stay missing all the time. During this time,
SRAM writes and reads will also fail to read the correct values, which
can cause the driver to report incorrect values.

Fix this by changing the driver so that once the battery is removed,
the battery type is set to the default battery type. If the battery
is missing, report the type as missing through the power supply
framework. Also, disallow SRAM updates during this time. Finally, use
a completion instead of polling in order to make the restart routine
finish faster and avoid the period of time where delayed or incorrect
SRAM reads and writes can happen.

CRs-Fixed: 867716
Change-Id: I31f654546b6209b6a39cdbc08bd778e53040c5c0
Signed-off-by: default avatarXiaozhe Shi <xiaozhes@codeaurora.org>
parent 83033633
Loading
Loading
Loading
Loading
+43 −27
Original line number Diff line number Diff line
@@ -400,6 +400,7 @@ struct fg_chip {
	struct completion	sram_access_granted;
	struct completion	sram_access_revoked;
	struct completion	batt_id_avail;
	struct completion	first_soc_done;
	struct power_supply	bms_psy;
	struct mutex		rw_lock;
	struct mutex		sysfs_restart_lock;
@@ -427,6 +428,7 @@ struct fg_chip {
	bool			first_profile_loaded;
	struct fg_wakeup_source	update_temp_wakeup_source;
	struct fg_wakeup_source	update_sram_wakeup_source;
	bool			fg_restarting;
	bool			profile_loaded;
	bool			use_otp_profile;
	bool			battery_missing;
@@ -1926,6 +1928,9 @@ static void update_sram_data(struct fg_chip *chip, int *resched_ms)
	int battid_valid = fg_is_batt_id_valid(chip);

	fg_stay_awake(&chip->update_sram_wakeup_source);
	if (chip->fg_restarting)
		goto resched;

	fg_mem_lock(chip);
	for (i = 1; i < FG_DATA_MAX; i++) {
		if (chip->profile_loaded && i >= FG_DATA_BATT_ID)
@@ -1994,6 +1999,7 @@ static void update_sram_data(struct fg_chip *chip, int *resched_ms)
	if (!rc)
		get_current_time(&chip->last_sram_update_time);

resched:
	if (battid_valid) {
		complete_all(&chip->batt_id_avail);
		*resched_ms = fg_sram_update_period_ms;
@@ -2055,6 +2061,9 @@ static void update_temp_data(struct work_struct *work)
				struct fg_chip,
				update_temp_work.work);

	if (chip->fg_restarting)
		goto resched;

	fg_stay_awake(&chip->update_temp_wakeup_source);
	if (chip->sw_rbias_ctrl) {
		rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT,
@@ -2110,10 +2119,12 @@ out:
		if (rc)
			pr_err("failed to write BATT_TEMP_OFF rc=%d\n", rc);
	}
	fg_relax(&chip->update_temp_wakeup_source);

resched:
	schedule_delayed_work(
		&chip->update_temp_work,
		msecs_to_jiffies(TEMP_PERIOD_UPDATE_MS));
	fg_relax(&chip->update_temp_wakeup_source);
}

static void update_jeita_setting(struct work_struct *work)
@@ -2555,6 +2566,11 @@ static int fg_power_get_property(struct power_supply *psy,

	switch (psp) {
	case POWER_SUPPLY_PROP_BATTERY_TYPE:
		if (chip->battery_missing)
			val->strval = missing_batt_type;
		else if (chip->fg_restarting)
			val->strval = loading_batt_type;
		else
			val->strval = chip->batt_type;
		break;
	case POWER_SUPPLY_PROP_CAPACITY:
@@ -3660,7 +3676,7 @@ static irqreturn_t fg_batt_missing_irq_handler(int irq, void *_chip)
	if (batt_missing) {
		chip->battery_missing = true;
		chip->profile_loaded = false;
		chip->batt_type = missing_batt_type;
		chip->batt_type = default_batt_type;
		mutex_lock(&chip->cyc_ctr.lock);
		if (fg_debug_mask & FG_IRQS)
			pr_info("battery missing, clearing cycle counters\n");
@@ -3685,6 +3701,7 @@ static irqreturn_t fg_batt_missing_irq_handler(int irq, void *_chip)

	if (chip->power_supply_registered)
		power_supply_changed(&chip->bms_psy);
	complete_all(&chip->first_soc_done);
	return IRQ_HANDLED;
}

@@ -4140,16 +4157,17 @@ static void update_cc_cv_setpoint(struct fg_chip *chip)
#define FIRST_EST_DONE_BIT		BIT(5)
#define MAX_TRIES_FIRST_EST		3
#define FIRST_EST_WAIT_MS		2000
#define PROFILE_LOAD_TIMEOUT_MS		5000
static int fg_do_restart(struct fg_chip *chip, bool write_profile)
{
	int rc;
	int tries = 0;
	u8 reg = 0;
	u8 buf[2];

	if (fg_debug_mask & FG_STATUS)
		pr_info("restarting fuel gauge...\n");

	chip->fg_restarting = true;
	/*
	 * save the temperature if the sw rbias control is active so that there
	 * is no gap of time when there is no valid temperature read after the
@@ -4261,18 +4279,18 @@ static int fg_do_restart(struct fg_chip *chip, bool write_profile)
	}

	/* wait for the first estimate to complete */
	for (tries = 0; tries < MAX_TRIES_FIRST_EST; tries++) {
		msleep(FIRST_EST_WAIT_MS);

	rc = wait_for_completion_interruptible_timeout(&chip->first_soc_done,
			msecs_to_jiffies(PROFILE_LOAD_TIMEOUT_MS));
	if (rc <= 0) {
		pr_err("transaction timed out rc=%d\n", rc);
		rc = -ETIMEDOUT;
		goto fail;
	}
	rc = fg_read(chip, &reg, INT_RT_STS(chip->soc_base), 1);
	if (rc) {
		pr_err("spmi read failed: addr=%03X, rc=%d\n",
				INT_RT_STS(chip->soc_base), rc);
		}
		if (reg & FIRST_EST_DONE_BIT)
			break;
		if (fg_debug_mask & FG_STATUS)
			pr_info("waiting for est, tries = %d\n", tries);
		goto fail;
	}
	if ((reg & FIRST_EST_DONE_BIT) == 0)
		pr_err("Battery profile reloading failed, no first estimate\n");
@@ -4306,6 +4324,7 @@ static int fg_do_restart(struct fg_chip *chip, bool write_profile)
			goto fail;
		}
	}
	chip->fg_restarting = false;

	if (fg_debug_mask & FG_STATUS)
		pr_info("done!\n");
@@ -4318,10 +4337,10 @@ sub_and_fail:
	fg_release_access_if_necessary(chip);
	goto fail;
fail:
	chip->fg_restarting = false;
	return -EINVAL;
}

#define PROFILE_LOAD_TIMEOUT_MS		5000
#define FG_PROFILE_LEN			128
#define PROFILE_COMPARE_LEN		32
#define THERMAL_COEFF_ADDR		0x444
@@ -4332,7 +4351,7 @@ static int fg_batt_profile_init(struct fg_chip *chip)
	int len;
	struct device_node *node = chip->spmi->dev.of_node;
	struct device_node *batt_node, *profile_node;
	const char *data, *batt_type_str, *old_batt_type;
	const char *data, *batt_type_str;
	bool tried_again = false, vbat_in_range, profiles_same;
	u8 reg = 0;

@@ -4362,9 +4381,8 @@ wait:
							fg_batt_type);
	if (!profile_node) {
		pr_err("couldn't find profile handle\n");
		old_batt_type = default_batt_type;
		rc = -ENODATA;
		goto fail;
		goto no_profile;
	}

	/* read rslow compensation values if they're available */
@@ -4424,7 +4442,7 @@ wait:
	if (len != FG_PROFILE_LEN) {
		pr_err("battery profile incorrect size: %d\n", len);
		rc = -EINVAL;
		goto fail;
		goto no_profile;
	}

	rc = of_property_read_string(profile_node, "qcom,battery-type",
@@ -4491,8 +4509,6 @@ wait:
				DUMP_PREFIX_NONE, 16, 1,
				chip->batt_profile, len, false);
	}
	old_batt_type = chip->batt_type;
	chip->batt_type = loading_batt_type;
	if (chip->power_supply_registered)
		power_supply_changed(&chip->bms_psy);

@@ -4508,7 +4524,7 @@ wait:
	rc = fg_do_restart(chip, true);
	if (rc) {
		pr_err("restart failed: %d\n", rc);
		goto fail;
		goto no_profile;
	}

	/*
@@ -4554,11 +4570,9 @@ done:
	fg_relax(&chip->profile_wakeup_source);
	pr_info("Battery SOC: %d\n", get_prop_capacity(chip));
	return rc;
fail:
	chip->batt_type = old_batt_type;
no_profile:
	if (chip->power_supply_registered)
		power_supply_changed(&chip->bms_psy);
no_profile:
	fg_relax(&chip->profile_wakeup_source);
	return rc;
}
@@ -6047,6 +6061,8 @@ static int fg_probe(struct spmi_device *spmi)
	init_completion(&chip->sram_access_revoked);
	complete_all(&chip->sram_access_revoked);
	init_completion(&chip->batt_id_avail);
	init_completion(&chip->first_soc_done);
	complete_all(&chip->first_soc_done);
	dev_set_drvdata(&spmi->dev, chip);

	spmi_for_each_container_dev(spmi_resource, spmi) {