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

Commit d8342857 authored by Sahil Chandna's avatar Sahil Chandna
Browse files

power: qpnp-fg-gen4: Add support for SW based SOC scaling



On aged batteries, SOC calculated by FG can remain high for low Vbatt.
Add support for SW based SOC scaling using filtered battery voltage so
that SOC can be shown to the user in a better way. SW based SOC scaling
is enabled based on DT property and once the Vbatt goes below the specified
threshold.

Change-Id: Idd94fdebab4def6ab53b317efbc67d5a7464a99e
Signed-off-by: default avatarSahil Chandna <chandna@codeaurora.org>
parent f74ff626
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
 * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
 */

#ifndef __FG_CORE_H__
@@ -167,6 +167,8 @@ enum fg_sram_param_id {
	FG_SRAM_MONOTONIC_SOC,
	FG_SRAM_VOLTAGE_PRED,
	FG_SRAM_OCV,
	FG_SRAM_VBAT_FLT,
	FG_SRAM_VBAT_TAU,
	FG_SRAM_VBAT_FINAL,
	FG_SRAM_IBAT_FINAL,
	FG_SRAM_ESR,
@@ -485,6 +487,8 @@ struct fg_dbgfs {
	u32				addr;
};

extern int fg_decode_voltage_24b(struct fg_sram_param *sp,
	enum fg_sram_param_id id, int val);
extern int fg_decode_voltage_15b(struct fg_sram_param *sp,
	enum fg_sram_param_id id, int val);
extern int fg_decode_current_16b(struct fg_sram_param *sp,
+18 −2
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2016-2018 The Linux Foundation. All rights reserved.
 * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved.
 */

#include <linux/of.h>
@@ -18,9 +18,25 @@
#define MAX_LINE_LENGTH			(ADDR_LEN + (ITEMS_PER_LINE *	\
					CHARS_PER_ITEM) + 1)		\

#define VOLTAGE_15BIT_MASK	GENMASK(14, 0)
#define MAX_READ_TRIES		5

#define VOLTAGE_24BIT_MSB_MASK	GENMASK(27, 16)
#define VOLTAGE_24BIT_LSB_MASK	GENMASK(11, 0)
int fg_decode_voltage_24b(struct fg_sram_param *sp,
	enum fg_sram_param_id id, int value)
{
	int msb, lsb, val;

	msb = value & VOLTAGE_24BIT_MSB_MASK;
	lsb = value & VOLTAGE_24BIT_LSB_MASK;
	val = (msb >> 4) | lsb;
	sp[id].value = div_s64((s64)val * sp[id].denmtr, sp[id].numrtr);
	pr_debug("id: %d raw value: %x decoded value: %x\n", id, value,
			sp[id].value);
	return sp[id].value;
}

#define VOLTAGE_15BIT_MASK	GENMASK(14, 0)
int fg_decode_voltage_15b(struct fg_sram_param *sp,
				enum fg_sram_param_id id, int value)
{
+421 −33
Original line number Diff line number Diff line
@@ -160,12 +160,16 @@
#define MONOTONIC_SOC_OFFSET		0

/* v2 SRAM address and offset in ascending order */
#define LOW_PASS_VBATT_WORD		3
#define LOW_PASS_VBATT_OFFSET		0
#define RSLOW_SCALE_FN_DISCHG_V2_WORD	281
#define RSLOW_SCALE_FN_DISCHG_V2_OFFSET	0
#define RSLOW_SCALE_FN_CHG_V2_WORD	285
#define RSLOW_SCALE_FN_CHG_V2_OFFSET	0
#define ACT_BATT_CAP_v2_WORD		287
#define ACT_BATT_CAP_v2_OFFSET		0
#define VBAT_FLT_WORD			326
#define VBAT_FLT_OFFSET			0
#define RSLOW_v2_WORD			371
#define RSLOW_v2_OFFSET			0
#define OCV_v2_WORD			425
@@ -197,12 +201,15 @@ struct fg_dt_props {
	bool	multi_profile_load;
	bool	esr_calib_dischg;
	bool	soc_hi_res;
	bool	soc_scale_mode;
	int	cutoff_volt_mv;
	int	empty_volt_mv;
	int	sys_min_volt_mv;
	int	cutoff_curr_ma;
	int	sys_term_curr_ma;
	int	delta_soc_thr;
	int	vbatt_scale_thr_mv;
	int	scale_timer_ms;
	int	esr_timer_chg_fast[NUM_ESR_TIMERS];
	int	esr_timer_chg_slow[NUM_ESR_TIMERS];
	int	esr_timer_dischg_fast[NUM_ESR_TIMERS];
@@ -245,10 +252,13 @@ struct fg_gen4_chip {
	struct votable		*parallel_current_en_votable;
	struct votable		*mem_attn_irq_en_votable;
	struct work_struct	esr_calib_work;
	struct work_struct	soc_scale_work;
	struct alarm		esr_fast_cal_timer;
	struct alarm		soc_scale_alarm_timer;
	struct delayed_work	pl_enable_work;
	struct work_struct	pl_current_en_work;
	struct completion	mem_attn;
	struct mutex		soc_scale_lock;
	char			batt_profile[PROFILE_LEN];
	enum slope_limit_status	slope_limit_sts;
	int			ki_coeff_full_soc[2];
@@ -260,6 +270,14 @@ struct fg_gen4_chip {
	int			esr_soh_cycle_count;
	int			batt_age_level;
	int			last_batt_age_level;
	int			soc_scale_msoc;
	int			prev_soc_scale_msoc;
	int			soc_scale_slope;
	int			vbatt_avg;
	int			vbatt_now;
	int			vbatt_res;
	int			scale_timer;
	int			current_now;
	bool			first_profile_load;
	bool			ki_coeff_dischg_en;
	bool			slope_limit_en;
@@ -273,6 +291,7 @@ struct fg_gen4_chip {
	bool			rapid_soc_dec_en;
	bool			vbatt_low;
	bool			chg_term_good;
	bool			soc_scale_mode;
};

struct bias_config {
@@ -329,6 +348,8 @@ static int fg_restart_mp;
static bool fg_sram_dump;
static bool fg_esr_fast_cal_en;

static int fg_gen4_validate_soc_scale_mode(struct fg_gen4_chip *chip);

static struct fg_sram_param pm8150b_v1_sram_params[] = {
	PARAM(BATT_SOC, BATT_SOC_WORD, BATT_SOC_OFFSET, 4, 1, 1, 0, NULL,
		fg_decode_default),
@@ -422,6 +443,8 @@ static struct fg_sram_param pm8150b_v1_sram_params[] = {
};

static struct fg_sram_param pm8150b_v2_sram_params[] = {
	PARAM(VBAT_TAU, LOW_PASS_VBATT_WORD, LOW_PASS_VBATT_OFFSET, 1, 1, 1, 0,
		NULL, NULL),
	PARAM(BATT_SOC, BATT_SOC_v2_WORD, BATT_SOC_v2_OFFSET, 4, 1, 1, 0, NULL,
		fg_decode_default),
	PARAM(FULL_SOC, FULL_SOC_v2_WORD, FULL_SOC_v2_OFFSET, 2, 1, 1, 0,
@@ -432,6 +455,8 @@ static struct fg_sram_param pm8150b_v2_sram_params[] = {
		1000, 244141, 0, NULL, fg_decode_voltage_15b),
	PARAM(OCV, OCV_v2_WORD, OCV_v2_OFFSET, 2, 1000, 244141, 0, NULL,
		fg_decode_voltage_15b),
	PARAM(VBAT_FLT, VBAT_FLT_WORD, VBAT_FLT_OFFSET, 4, 10000, 19073, 0,
		NULL, fg_decode_voltage_24b),
	PARAM(VBAT_FINAL, VBAT_FINAL_WORD, VBAT_FINAL_OFFSET, 2, 1000, 244141,
		0, NULL, fg_decode_voltage_15b),
	PARAM(IBAT_FINAL, IBAT_FINAL_WORD, IBAT_FINAL_OFFSET, 2, 1000, 488282,
@@ -859,14 +884,19 @@ static int fg_gen4_get_prop_capacity(struct fg_dev *fg, int *val)
		return 0;
	}

	if (chip->soc_scale_mode) {
		mutex_lock(&chip->soc_scale_lock);
		*val = chip->soc_scale_msoc;
		mutex_unlock(&chip->soc_scale_lock);
	} else {
		rc = fg_get_msoc(fg, &msoc);
		if (rc < 0)
			return rc;

		if (chip->dt.linearize_soc && fg->delta_soc > 0)
			*val = fg->maint_soc;
		else
			*val = msoc;
	}

	return 0;
}
@@ -960,6 +990,39 @@ static int fg_gen4_get_power(struct fg_gen4_chip *chip, int *val, bool average)
	return 0;
}

static int fg_gen4_get_prop_soc_scale(struct fg_gen4_chip *chip)
{
	struct fg_dev *fg = &chip->fg;
	int rc;

	rc = fg_get_sram_prop(fg, FG_SRAM_VBAT_FLT, &chip->vbatt_avg);
	if (rc < 0) {
		pr_err("Failed to get filtered battery voltage, rc = %d\n",
			rc);
		return rc;
	}

	rc = fg_get_battery_voltage(fg, &chip->vbatt_now);
	if (rc < 0) {
		pr_err("Failed to get battery voltage, rc =%d\n", rc);
		return rc;
	}

	rc = fg_get_battery_current(fg, &chip->current_now);
	if (rc < 0) {
		pr_err("Failed to get battery current rc=%d\n", rc);
		return rc;
	}

	chip->vbatt_now = DIV_ROUND_CLOSEST(chip->vbatt_now, 1000);
	chip->vbatt_avg = DIV_ROUND_CLOSEST(chip->vbatt_avg, 1000);
	chip->vbatt_res = chip->vbatt_avg - chip->dt.cutoff_volt_mv;
	pr_debug("FVSS: Vbatt now=%d Vbatt avg=%d Vbatt res=%d\n",
		chip->vbatt_now, chip->vbatt_avg, chip->vbatt_res);

	return rc;
}

/* ALG callback functions below */

static int fg_gen4_get_ttf_param(void *data, enum ttf_param param, int *val)
@@ -2231,6 +2294,10 @@ static void profile_load_work(struct work_struct *work)
		pm_stay_awake(fg->dev);
		schedule_work(&fg->status_change_work);
	}

	rc = fg_gen4_validate_soc_scale_mode(chip);
	if (rc < 0)
		pr_err("Failed to validate SOC scale mode, rc=%d\n", rc);
}

static void get_batt_psy_props(struct fg_dev *fg)
@@ -2870,6 +2937,193 @@ static int fg_gen4_esr_fast_calib_config(struct fg_gen4_chip *chip, bool en)
	return 0;
}

#define IBATT_TAU_MASK	GENMASK(3, 0)
static int fg_gen4_set_vbatt_tau(struct fg_gen4_chip *chip, u8 vbatt_tau)
{
	struct fg_dev *fg = &chip->fg;
	int rc;
	u8 buf;

	rc = fg_sram_read(fg, fg->sp[FG_SRAM_VBAT_TAU].addr_word,
			fg->sp[FG_SRAM_VBAT_TAU].addr_byte,
			&buf, fg->sp[FG_SRAM_VBAT_TAU].len,
			FG_IMA_DEFAULT);
	if (rc < 0) {
		pr_err("Error in reading Vbatt_tau, rc=%d\n", rc);
		return rc;
	}

	buf &= IBATT_TAU_MASK;
	buf |= vbatt_tau << 4;
	rc = fg_sram_write(fg,
			fg->sp[FG_SRAM_VBAT_TAU].addr_word,
			fg->sp[FG_SRAM_VBAT_TAU].addr_byte,
			&buf, fg->sp[FG_SRAM_VBAT_TAU].len,
			FG_IMA_DEFAULT);
	if (rc < 0)
		pr_err("Error in writing Vbatt_tau, rc=%d\n", rc);

	return rc;
}

static int fg_gen4_enter_soc_scale(struct fg_gen4_chip *chip)
{
	struct fg_dev *fg = &chip->fg;
	int rc, soc;

	rc = fg_gen4_get_prop_capacity(fg, &soc);
	if (rc < 0) {
		pr_err("Failed to get capacity, rc =%d\n", rc);
		return rc;
	}

	/* Set entry FVS SOC equal to current H/W reported SOC */
	chip->soc_scale_msoc = chip->prev_soc_scale_msoc = soc;
	chip->scale_timer = chip->dt.scale_timer_ms;
	/*
	 * Calculate the FVS slope to linearly calculate SOC
	 * based on filtered battery voltage.
	 */
	chip->soc_scale_slope =
			DIV_ROUND_CLOSEST(chip->vbatt_res,
					chip->soc_scale_msoc);
	if (chip->soc_scale_slope <= 0) {
		pr_err("Error in slope calculated = %d\n",
			chip->soc_scale_slope);
		return -EINVAL;
	}

	chip->soc_scale_mode = true;
	pr_debug("FVSS: Enter FVSS mode, SOC=%d slope=%d timer=%d\n", soc,
		chip->soc_scale_slope, chip->scale_timer);
	alarm_start_relative(&chip->soc_scale_alarm_timer,
				ms_to_ktime(chip->scale_timer));

	return 0;
}

static void fg_gen4_write_scale_msoc(struct fg_gen4_chip *chip)
{
	struct fg_dev *fg = &chip->fg;
	int soc_raw, rc;

	if (!fg->charge_full) {
		soc_raw = DIV_ROUND_CLOSEST(chip->soc_scale_msoc * 0xFFFF,
						100);
		rc = fg_sram_write(fg, fg->sp[FG_SRAM_MONOTONIC_SOC].addr_word,
				fg->sp[FG_SRAM_MONOTONIC_SOC].addr_byte,
				(u8 *)&soc_raw,
				fg->sp[FG_SRAM_MONOTONIC_SOC].len,
				FG_IMA_ATOMIC);
		if (rc < 0) {
			pr_err("failed to write monotonic_soc rc=%d\n", rc);
			chip->soc_scale_mode = false;
		}
	}
}

static void fg_gen4_exit_soc_scale(struct fg_gen4_chip *chip)
{
	if (chip->soc_scale_mode) {
		alarm_cancel(&chip->soc_scale_alarm_timer);
		cancel_work_sync(&chip->soc_scale_work);
		/* While exiting soc_scale_mode, Update MSOC register */
		fg_gen4_write_scale_msoc(chip);
	}

	chip->soc_scale_mode = false;
	pr_debug("FVSS: Exit FVSS mode\n");
}

static int fg_gen4_validate_soc_scale_mode(struct fg_gen4_chip *chip)
{
	struct fg_dev *fg = &chip->fg;
	int rc, msoc_actual;

	if (!chip->dt.soc_scale_mode)
		return 0;

	rc = fg_gen4_get_prop_soc_scale(chip);
	if (rc < 0) {
		pr_err("Failed to get soc scale props\n");
		goto fail_soc_scale;
	}

	rc = fg_get_msoc(fg, &msoc_actual);
	if (rc < 0) {
		pr_err("Failed to get msoc rc=%d\n", rc);
		goto fail_soc_scale;
	}

	if (!chip->soc_scale_mode && fg->charge_status ==
		POWER_SUPPLY_STATUS_DISCHARGING &&
		chip->vbatt_avg < chip->dt.vbatt_scale_thr_mv) {
		rc = fg_gen4_enter_soc_scale(chip);
		if (rc < 0) {
			pr_err("Failed to enter SOC scale mode\n");
			goto fail_soc_scale;
		}
	} else if (chip->soc_scale_mode && chip->current_now < 0) {
		/*
		 * Stay in SOC scale mode till H/W SOC catch scaled SOC
		 * while charging.
		 */
		if (msoc_actual >= chip->soc_scale_msoc)
			fg_gen4_exit_soc_scale(chip);
	}

	return 0;
fail_soc_scale:
	fg_gen4_exit_soc_scale(chip);
	return rc;
}

static int fg_gen4_set_vbatt_low(struct fg_gen4_chip *chip)
{
	struct fg_dev *fg = &chip->fg;
	int rc, vbatt_flt;

	if (chip->soc_scale_mode) {
		rc = fg_get_sram_prop(fg, FG_SRAM_VBAT_FLT,
					&vbatt_flt);
		if (rc < 0) {
			pr_err("failed to get filtered battery voltage, rc=%d\n",
				rc);
			/*
			 * If we fail here, exit FVSS mode
			 * and set Vbatt low flag true to report
			 * 0 SOC
			 */
			fg_gen4_exit_soc_scale(chip);
			chip->vbatt_low = true;
			return 0;
		}

		vbatt_flt /= 1000;
		if (vbatt_flt < chip->dt.empty_volt_mv ||
		    vbatt_flt > (fg->bp.float_volt_uv/1000)) {
			pr_err("Filtered Vbatt is not in range %d\n",
			       vbatt_flt);
			/*
			 * If we fail here, exit FVSS mode
			 * and set Vbatt low flag true to report
			 * 0 SOC
			 */
			fg_gen4_exit_soc_scale(chip);
			chip->vbatt_low = true;
			return 0;
		}

		if (vbatt_flt <= chip->dt.cutoff_volt_mv)
			chip->vbatt_low = true;
	} else {
		/* Set the flag to show 0% */
		chip->vbatt_low = true;
	}

	return 0;
}

/* All irq handlers below this */

static irqreturn_t fg_mem_attn_irq_handler(int irq, void *data)
@@ -2964,8 +3218,7 @@ static irqreturn_t fg_vbatt_low_irq_handler(int irq, void *data)
				pr_err("Error in configuring for rapid SOC reduction rc:%d\n",
					rc);
		} else {
			/* Set the flag to show 0% */
			chip->vbatt_low = true;
			fg_gen4_set_vbatt_low(chip);
		}
	}

@@ -3169,6 +3422,10 @@ static irqreturn_t fg_delta_msoc_irq_handler(int irq, void *data)
			chip->esr_fast_calib_retry = true;
	}

	rc = fg_gen4_validate_soc_scale_mode(chip);
	if (rc < 0)
		pr_err("Failed to validate SOC scale mode, rc=%d\n", rc);

	if (batt_psy_initialized(fg))
		power_supply_changed(fg->batt_psy);

@@ -3437,6 +3694,79 @@ static void esr_calib_work(struct work_struct *work)
	vote(fg->awake_votable, ESR_CALIB, false, 0);
}

static enum alarmtimer_restart fg_soc_scale_timer(struct alarm *alarm,
							ktime_t time)
{
	struct fg_gen4_chip *chip = container_of(alarm, struct fg_gen4_chip,
							soc_scale_alarm_timer);

	schedule_work(&chip->soc_scale_work);
	return ALARMTIMER_NORESTART;
}

static void soc_scale_work(struct work_struct *work)
{
	struct fg_gen4_chip *chip = container_of(work, struct fg_gen4_chip,
						soc_scale_work);
	struct fg_dev *fg = &chip->fg;
	int soc, soc_thr_percent, rc;

	if (!chip->soc_scale_mode)
		return;

	soc_thr_percent = chip->dt.delta_soc_thr / 10;
	if (soc_thr_percent == 0) {
		/* Set minimum SOC change that can be reported = 1% */
		soc_thr_percent = 1;
	}

	rc = fg_gen4_validate_soc_scale_mode(chip);
	if (rc < 0)
		pr_err("Failed to validate SOC scale mode, rc=%d\n", rc);

	if (chip->vbatt_res <= 0)
		chip->vbatt_res = 0;

	mutex_lock(&chip->soc_scale_lock);
	soc = DIV_ROUND_CLOSEST(chip->vbatt_res,
				chip->soc_scale_slope);
	/* If calculated SOC is higher than current SOC, report current SOC */
	if (soc > chip->prev_soc_scale_msoc) {
		chip->soc_scale_msoc = chip->prev_soc_scale_msoc;
		chip->scale_timer = chip->dt.scale_timer_ms;
	} else if ((chip->prev_soc_scale_msoc - soc) > soc_thr_percent) {
		/*
		 * If difference b/w current SOC and calculated SOC
		 * is higher than SOC threshold then handle this by
		 * showing current SOC - SOC threshold and decrease
		 * timer resolution to catch up the rate of decrement
		 * of SOC.
		 */
		chip->soc_scale_msoc = chip->prev_soc_scale_msoc -
					soc_thr_percent;
		chip->scale_timer = chip->dt.scale_timer_ms /
				(chip->prev_soc_scale_msoc - soc);
	} else {
		chip->soc_scale_msoc = soc;
		chip->scale_timer = chip->dt.scale_timer_ms;
	}

	if (chip->soc_scale_msoc < 0)
		chip->soc_scale_msoc = 0;

	mutex_unlock(&chip->soc_scale_lock);
	if (chip->prev_soc_scale_msoc != chip->soc_scale_msoc) {
		if (batt_psy_initialized(fg))
			power_supply_changed(fg->batt_psy);
	}

	chip->prev_soc_scale_msoc = chip->soc_scale_msoc;
	pr_debug("FVSS: Calculated SOC=%d SOC reported=%d timer resolution=%d\n",
		soc, chip->soc_scale_msoc, chip->scale_timer);
	alarm_start_relative(&chip->soc_scale_alarm_timer,
				ms_to_ktime(chip->scale_timer));
}

static void pl_current_en_work(struct work_struct *work)
{
	struct fg_gen4_chip *chip = container_of(work,
@@ -3549,6 +3879,10 @@ static void status_change_work(struct work_struct *work)
		schedule_work(&chip->pl_current_en_work);
	}

	rc = fg_gen4_validate_soc_scale_mode(chip);
	if (rc < 0)
		pr_err("Failed to validate SOC scale mode, rc=%d\n", rc);

	ttf_update(chip->ttf, input_present);
	fg->prev_charge_status = fg->charge_status;
out:
@@ -3797,6 +4131,9 @@ static int fg_psy_get_property(struct power_supply *psy,
	case POWER_SUPPLY_PROP_VOLTAGE_OCV:
		rc = fg_get_sram_prop(fg, FG_SRAM_OCV, &pval->intval);
		break;
	case POWER_SUPPLY_PROP_VOLTAGE_AVG:
		rc = fg_get_sram_prop(fg, FG_SRAM_VBAT_FLT, &pval->intval);
		break;
	case POWER_SUPPLY_PROP_RESISTANCE_ID:
		pval->intval = fg->batt_id_ohms;
		break;
@@ -3874,6 +4211,9 @@ static int fg_psy_get_property(struct power_supply *psy,
	case POWER_SUPPLY_PROP_BATT_AGE_LEVEL:
		pval->intval = chip->batt_age_level;
		break;
	case POWER_SUPPLY_PROP_SCALE_MODE_EN:
		pval->intval = chip->soc_scale_mode;
		break;
	case POWER_SUPPLY_PROP_POWER_NOW:
		rc = fg_gen4_get_power(chip, &pval->intval, false);
		break;
@@ -4003,6 +4343,7 @@ static enum power_supply_property fg_psy_props[] = {
	POWER_SUPPLY_PROP_TEMP,
	POWER_SUPPLY_PROP_VOLTAGE_NOW,
	POWER_SUPPLY_PROP_VOLTAGE_OCV,
	POWER_SUPPLY_PROP_VOLTAGE_AVG,
	POWER_SUPPLY_PROP_CURRENT_NOW,
	POWER_SUPPLY_PROP_RESISTANCE_ID,
	POWER_SUPPLY_PROP_RESISTANCE,
@@ -4029,6 +4370,7 @@ static enum power_supply_property fg_psy_props[] = {
	POWER_SUPPLY_PROP_BATT_AGE_LEVEL,
	POWER_SUPPLY_PROP_POWER_NOW,
	POWER_SUPPLY_PROP_POWER_AVG,
	POWER_SUPPLY_PROP_SCALE_MODE_EN,
};

static const struct power_supply_desc fg_psy_desc = {
@@ -4465,6 +4807,7 @@ static int fg_gen4_batt_temp_config(struct fg_gen4_chip *chip)
	return rc;
}

#define VBATT_TAU_DEFAULT	3
static int fg_gen4_hw_init(struct fg_gen4_chip *chip)
{
	struct fg_dev *fg = &chip->fg;
@@ -4520,7 +4863,6 @@ static int fg_gen4_hw_init(struct fg_gen4_chip *chip)
		}
	}

	if (chip->dt.delta_soc_thr > 0 && chip->dt.delta_soc_thr < 125) {
	fg_encode(fg->sp, FG_SRAM_DELTA_MSOC_THR,
		chip->dt.delta_soc_thr, buf);
	rc = fg_sram_write(fg,
@@ -4544,7 +4886,6 @@ static int fg_gen4_hw_init(struct fg_gen4_chip *chip)
		pr_err("Error in writing delta_bsoc_thr, rc=%d\n", rc);
		return rc;
	}
	}

	rc = fg_gen4_batt_temp_config(chip);
	if (rc < 0)
@@ -4661,6 +5002,15 @@ static int fg_gen4_hw_init(struct fg_gen4_chip *chip)
		if (!rc)
			chip->batt_age_level = chip->last_batt_age_level = val;
	}

	if (chip->dt.soc_scale_mode) {
		rc = fg_gen4_set_vbatt_tau(chip, VBATT_TAU_DEFAULT);
		if (rc < 0) {
			fg_gen4_exit_soc_scale(chip);
			return rc;
		}
	}

	return 0;
}

@@ -5075,6 +5425,8 @@ static int fg_gen4_parse_child_nodes_dt(struct fg_gen4_chip *chip)
#define DEFAULT_DELTA_SOC_THR		5	/* 0.5 % */
#define DEFAULT_ESR_PULSE_THRESH_MA	47
#define DEFAULT_ESR_MEAS_CURR_MA	120
#define DEFAULT_SCALE_VBATT_THR_MV	3400
#define DEFAULT_SCALE_ALARM_TIMER_MS	10000

static int fg_gen4_parse_dt(struct fg_gen4_chip *chip)
{
@@ -5121,6 +5473,12 @@ static int fg_gen4_parse_dt(struct fg_gen4_chip *chip)
	of_property_read_u32(node, "qcom,fg-delta-soc-thr",
				&chip->dt.delta_soc_thr);

	if (chip->dt.delta_soc_thr < 0 || chip->dt.delta_soc_thr >= 125) {
		pr_err("Invalid delta SOC threshold=%d\n",
		       chip->dt.delta_soc_thr);
		return -EINVAL;
	}

	chip->dt.esr_timer_chg_fast[TIMER_RETRY] = -EINVAL;
	chip->dt.esr_timer_chg_fast[TIMER_MAX] = -EINVAL;
	rc = fg_parse_dt_property_u32_array(node, "qcom,fg-esr-timer-chg-fast",
@@ -5163,6 +5521,17 @@ static int fg_gen4_parse_dt(struct fg_gen4_chip *chip)
	chip->dt.linearize_soc = of_property_read_bool(node,
					"qcom,linearize-soc");

	chip->dt.soc_scale_mode = of_property_read_bool(node,
						"qcom,soc-scale-mode-en");
	if (chip->dt.soc_scale_mode) {
		chip->dt.vbatt_scale_thr_mv = DEFAULT_SCALE_VBATT_THR_MV;
		of_property_read_u32(node, "qcom,soc-scale-vbatt-mv",
					&chip->dt.vbatt_scale_thr_mv);
		chip->dt.scale_timer_ms = DEFAULT_SCALE_ALARM_TIMER_MS;
		of_property_read_u32(node, "qcom,soc-scale-time-ms",
					&chip->dt.scale_timer_ms);
	}

	rc = fg_parse_ki_coefficients(fg);
	if (rc < 0)
		pr_err("Error in parsing Ki coefficients, rc=%d\n", rc);
@@ -5216,6 +5585,9 @@ static void fg_gen4_cleanup(struct fg_gen4_chip *chip)
	fg_unregister_interrupts(fg, chip, FG_GEN4_IRQ_MAX);

	cancel_work_sync(&fg->status_change_work);
	if (chip->soc_scale_mode)
		fg_gen4_exit_soc_scale(chip);

	cancel_delayed_work_sync(&fg->profile_load_work);
	cancel_delayed_work_sync(&fg->sram_dump_work);
	cancel_work_sync(&chip->pl_current_en_work);
@@ -5273,11 +5645,13 @@ static int fg_gen4_probe(struct platform_device *pdev)
	mutex_init(&fg->bus_lock);
	mutex_init(&fg->sram_rw_lock);
	mutex_init(&fg->charge_full_lock);
	mutex_init(&chip->soc_scale_lock);
	init_completion(&fg->soc_update);
	init_completion(&fg->soc_ready);
	init_completion(&chip->mem_attn);
	INIT_WORK(&fg->status_change_work, status_change_work);
	INIT_WORK(&chip->esr_calib_work, esr_calib_work);
	INIT_WORK(&chip->soc_scale_work, soc_scale_work);
	INIT_DELAYED_WORK(&fg->profile_load_work, profile_load_work);
	INIT_DELAYED_WORK(&fg->sram_dump_work, sram_dump_work);
	INIT_DELAYED_WORK(&chip->pl_enable_work, pl_enable_work);
@@ -5353,6 +5727,17 @@ static int fg_gen4_probe(struct platform_device *pdev)
		}
	}

	if (chip->dt.soc_scale_mode) {
		if (alarmtimer_get_rtcdev()) {
			alarm_init(&chip->soc_scale_alarm_timer,
				ALARM_BOOTTIME, fg_soc_scale_timer);
		} else {
			dev_err(fg->dev, "Failed to initialize SOC scale timer\n");
			rc = -EPROBE_DEFER;
			goto exit;
		}
	}

	rc = fg_memif_init(fg);
	if (rc < 0) {
		dev_err(fg->dev, "Error in initializing FG_MEMIF, rc:%d\n",
@@ -5475,6 +5860,9 @@ static void fg_gen4_shutdown(struct platform_device *pdev)

	fg_unregister_interrupts(fg, chip, FG_GEN4_IRQ_MAX);

	if (chip->soc_scale_mode)
		fg_gen4_exit_soc_scale(chip);

	if (chip->rapid_soc_dec_en) {
		rc = fg_gen4_rapid_soc_config(chip, false);
		if (rc < 0)