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

Commit ec7e8b08 authored by Subbaraman Narayanamurthy's avatar Subbaraman Narayanamurthy
Browse files

power: qpnp-fg: validate cc_soc in case of a FG lockup



When there is a FG lockup, there is a chance of CC_SOC value to
go beyond the limits. Validate cc_soc while handling every delta
soc interrupt. If it is out of range (e.g. > 100%), then trigger
the IMA error recovery sequence. At the end of it, restore a
valid cc_soc back to SRAM.

Device tree property "qcom,fg-cc-soc-limit-pct" is added to
specify the CC_SOC limit as a percentage.

CRs-Fixed: 962694
Change-Id: I9bc064d5d890d734e7806da03e5c068fde011986
Signed-off-by: default avatarSubbaraman Narayanamurthy <subbaram@codeaurora.org>
parent 75111a55
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -271,6 +271,8 @@ Parent node optional properties:
					will be notified to userspace. If this
					limit is not specified, then the
					default limit would be 150C.
- qcom,fg-cc-soc-limit-pct:		Percentage of CC_SOC before resetting
					FG and restore the full CC_SOC value.

qcom,fg-soc node required properties:
- reg : offset and length of the PMIC peripheral register map.
+80 −2
Original line number Diff line number Diff line
@@ -592,9 +592,16 @@ struct fg_chip {
	bool			irqs_enabled;
	bool			use_last_soc;
	int			last_soc;
	/* Validating temperature */
	int			last_good_temp;
	int			batt_temp_low_limit;
	int			batt_temp_high_limit;
	/* Validating CC_SOC */
	struct work_struct	cc_soc_store_work;
	struct fg_wakeup_source	cc_soc_wakeup_source;
	int			cc_soc_limit_pct;
	bool			use_last_cc_soc;
	int64_t			last_cc_soc;
};

/* FG_MEMIF DEBUGFS structures */
@@ -1334,6 +1341,7 @@ static void fg_check_ima_error_handling(struct fg_chip *chip)
	}
	mutex_lock(&chip->ima_recovery_lock);
	fg_enable_irqs(chip, false);
	chip->use_last_cc_soc = true;
	chip->ima_error_handling = true;
	if (!work_pending(&chip->ima_error_recovery_work))
		schedule_work(&chip->ima_error_recovery_work);
@@ -2499,10 +2507,15 @@ static int update_sram_data(struct fg_chip *chip, int *resched_ms)
				pr_err("Couldn't save sram registers\n");
				goto out;
			}
			if (!chip->use_last_soc)
			if (!chip->use_last_soc) {
				chip->last_soc = get_monotonic_soc_raw(chip);
				chip->last_cc_soc = div64_s64(
					(int64_t)chip->last_soc *
					FULL_PERCENT_28BIT, FULL_SOC_RAW);
			}
			if (fg_debug_mask & FG_STATUS)
				pr_info("last_soc: %d\n", chip->last_soc);
				pr_info("last_soc: %d last_cc_soc: %lld\n",
					chip->last_soc, chip->last_cc_soc);
		} else {
			pr_err("update_sram failed\n");
			goto out;
@@ -4432,6 +4445,37 @@ done:
	fg_relax(&chip->gain_comp_wakeup_source);
}

static void cc_soc_store_work(struct work_struct *work)
{
	struct fg_chip *chip = container_of(work, struct fg_chip,
					cc_soc_store_work);
	int cc_soc_pct;

	if (!chip->nom_cap_uah) {
		pr_err("nom_cap_uah zero!\n");
		fg_relax(&chip->cc_soc_wakeup_source);
		return;
	}

	cc_soc_pct = get_sram_prop_now(chip, FG_DATA_CC_CHARGE);
	cc_soc_pct = div64_s64(cc_soc_pct * 100,
				chip->nom_cap_uah);
	chip->last_cc_soc = div64_s64((int64_t)chip->last_soc *
				FULL_PERCENT_28BIT, FULL_SOC_RAW);

	if (fg_debug_mask & FG_STATUS)
		pr_info("cc_soc_pct: %d last_cc_soc: %lld\n", cc_soc_pct,
			chip->last_cc_soc);

	if (fg_reset_on_lockup && (chip->cc_soc_limit_pct > 0 &&
			cc_soc_pct >= chip->cc_soc_limit_pct)) {
		pr_err("CC_SOC out of range\n");
		fg_check_ima_error_handling(chip);
	}

	fg_relax(&chip->cc_soc_wakeup_source);
}

#define SOC_FIRST_EST_DONE	BIT(5)
static bool is_first_est_done(struct fg_chip *chip)
{
@@ -4592,6 +4636,9 @@ static irqreturn_t fg_soc_irq_handler(int irq, void *_chip)
			chip->last_soc = get_monotonic_soc_raw(chip);
		if (fg_debug_mask & FG_STATUS)
			pr_info("last_soc: %d\n", chip->last_soc);

		fg_stay_awake(&chip->cc_soc_wakeup_source);
		schedule_work(&chip->cc_soc_store_work);
	}

	if (chip->use_vbat_low_empty_soc) {
@@ -6446,6 +6493,11 @@ static int fg_of_init(struct fg_chip *chip)
		pr_info("batt-temp-low_limit: %d batt-temp-high_limit: %d\n",
			chip->batt_temp_low_limit, chip->batt_temp_high_limit);

	OF_READ_PROPERTY(chip->cc_soc_limit_pct, "fg-cc-soc-limit-pct", rc, 0);

	if (fg_debug_mask & FG_STATUS)
		pr_info("cc-soc-limit-pct: %d\n", chip->cc_soc_limit_pct);

	return rc;
}

@@ -6665,6 +6717,7 @@ static void fg_cancel_all_works(struct fg_chip *chip)
	cancel_work_sync(&chip->esr_extract_config_work);
	cancel_work_sync(&chip->slope_limiter_work);
	cancel_work_sync(&chip->dischg_gain_work);
	cancel_work_sync(&chip->cc_soc_store_work);
}

static void fg_cleanup(struct fg_chip *chip)
@@ -6689,6 +6742,7 @@ static void fg_cleanup(struct fg_chip *chip)
	wakeup_source_trash(&chip->slope_limit_wakeup_source.source);
	wakeup_source_trash(&chip->dischg_gain_wakeup_source.source);
	wakeup_source_trash(&chip->fg_reset_wakeup_source.source);
	wakeup_source_trash(&chip->cc_soc_wakeup_source.source);
}

static int fg_remove(struct spmi_device *spmi)
@@ -7492,6 +7546,25 @@ static int fg_init_iadc_config(struct fg_chip *chip)
	return 0;
}

static void fg_restore_cc_soc(struct fg_chip *chip)
{
	int rc;

	if (!chip->use_last_cc_soc || !chip->last_cc_soc)
		return;

	if (fg_debug_mask & FG_STATUS)
		pr_info("Restoring cc_soc: %lld\n", chip->last_cc_soc);

	rc = fg_mem_write(chip, (u8 *)&chip->last_cc_soc,
			fg_data[FG_DATA_CC_CHARGE].address, 4,
			fg_data[FG_DATA_CC_CHARGE].offset, 0);
	if (rc)
		pr_err("failed to update CC_SOC rc=%d\n", rc);
	else
		chip->use_last_cc_soc = false;
}

static void fg_restore_soc(struct fg_chip *chip)
{
	int rc;
@@ -7651,6 +7724,7 @@ wait:
		pr_info("IMA error recovery done...\n");
out:
	fg_restore_soc(chip);
	fg_restore_cc_soc(chip);
	fg_enable_irqs(chip, true);
	update_sram_data_work(&chip->update_sram_data.work);
	update_temp_data(&chip->update_temp_work.work);
@@ -7860,6 +7934,8 @@ static int fg_probe(struct spmi_device *spmi)
			"qpnp_fg_dischg_gain");
	wakeup_source_init(&chip->fg_reset_wakeup_source.source,
			"qpnp_fg_reset");
	wakeup_source_init(&chip->cc_soc_wakeup_source.source,
			"qpnp_fg_cc_soc");
	spin_lock_init(&chip->sec_access_lock);
	mutex_init(&chip->rw_lock);
	mutex_init(&chip->cyc_ctr.lock);
@@ -7889,6 +7965,7 @@ static int fg_probe(struct spmi_device *spmi)
	INIT_WORK(&chip->esr_extract_config_work, esr_extract_config_work);
	INIT_WORK(&chip->slope_limiter_work, slope_limiter_work);
	INIT_WORK(&chip->dischg_gain_work, discharge_gain_work);
	INIT_WORK(&chip->cc_soc_store_work, cc_soc_store_work);
	alarm_init(&chip->fg_cap_learning_alarm, ALARM_BOOTTIME,
			fg_cap_learning_alarm_cb);
	init_completion(&chip->sram_access_granted);
@@ -8062,6 +8139,7 @@ of_init_fail:
	wakeup_source_trash(&chip->slope_limit_wakeup_source.source);
	wakeup_source_trash(&chip->dischg_gain_wakeup_source.source);
	wakeup_source_trash(&chip->fg_reset_wakeup_source.source);
	wakeup_source_trash(&chip->cc_soc_wakeup_source.source);
	return rc;
}