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

Commit 5cfddbc8 authored by qctecmdr Service's avatar qctecmdr Service Committed by Gerrit - the friendly Code Review server
Browse files

Merge "power: qpnp-fg-gen4: Support profile loading based on battery age level"

parents 83d7cf07 899898dc
Loading
Loading
Loading
Loading
+8 −0
Original line number Original line Diff line number Diff line
@@ -397,6 +397,14 @@ First Level Node - FG Gen4 device
		    five pin battery is used. Based on this, time to full
		    five pin battery is used. Based on this, time to full
		    calculations would use the Rbatt calculated properly.
		    calculations would use the Rbatt calculated properly.


- qcom,multi-profile-load
	Usage:      optional
	Value type: <empty>
	Definition: A boolean property that when specified indicates that
		    multiple profile loading needs to be enabled. This requires
		    multiple battery profiles to be specified for a battery for
		    proper functionality.

==========================================================
==========================================================
Second Level Nodes - Peripherals managed by FG Gen4 driver
Second Level Nodes - Peripherals managed by FG Gen4 driver
==========================================================
==========================================================
+150 −60
Original line number Original line Diff line number Diff line
@@ -116,6 +116,8 @@
#define RCONN_OFFSET			0
#define RCONN_OFFSET			0
#define ACT_BATT_CAP_WORD		285
#define ACT_BATT_CAP_WORD		285
#define ACT_BATT_CAP_OFFSET		0
#define ACT_BATT_CAP_OFFSET		0
#define BATT_AGE_LEVEL_WORD		288
#define BATT_AGE_LEVEL_OFFSET		0
#define CYCLE_COUNT_WORD		291
#define CYCLE_COUNT_WORD		291
#define CYCLE_COUNT_OFFSET		0
#define CYCLE_COUNT_OFFSET		0
#define PROFILE_INTEGRITY_WORD		299
#define PROFILE_INTEGRITY_WORD		299
@@ -190,6 +192,7 @@ struct fg_dt_props {
	bool	linearize_soc;
	bool	linearize_soc;
	bool	rapid_soc_dec_en;
	bool	rapid_soc_dec_en;
	bool	five_pin_battery;
	bool	five_pin_battery;
	bool	multi_profile_load;
	int	cutoff_volt_mv;
	int	cutoff_volt_mv;
	int	empty_volt_mv;
	int	empty_volt_mv;
	int	cutoff_curr_ma;
	int	cutoff_curr_ma;
@@ -249,6 +252,8 @@ struct fg_gen4_chip {
	int			esr_nominal;
	int			esr_nominal;
	int			soh;
	int			soh;
	int			esr_soh_cycle_count;
	int			esr_soh_cycle_count;
	int			batt_age_level;
	int			last_batt_age_level;
	bool			first_profile_load;
	bool			first_profile_load;
	bool			ki_coeff_dischg_en;
	bool			ki_coeff_dischg_en;
	bool			slope_limit_en;
	bool			slope_limit_en;
@@ -1323,7 +1328,7 @@ static int fg_gen4_get_batt_profile(struct fg_dev *fg)
	struct device_node *node = fg->dev->of_node;
	struct device_node *node = fg->dev->of_node;
	struct device_node *batt_node, *profile_node;
	struct device_node *batt_node, *profile_node;
	const char *data;
	const char *data;
	int rc, len, i, tuple_len;
	int rc, len, i, tuple_len, avail_age_level = 0;


	batt_node = of_find_node_by_name(node, "qcom,battery-data");
	batt_node = of_find_node_by_name(node, "qcom,battery-data");
	if (!batt_node) {
	if (!batt_node) {
@@ -1331,6 +1336,11 @@ static int fg_gen4_get_batt_profile(struct fg_dev *fg)
		return -ENXIO;
		return -ENXIO;
	}
	}


	if (chip->dt.multi_profile_load)
		profile_node = of_batterydata_get_best_aged_profile(batt_node,
					fg->batt_id_ohms / 1000,
					chip->batt_age_level, &avail_age_level);
	else
		profile_node = of_batterydata_get_best_profile(batt_node,
		profile_node = of_batterydata_get_best_profile(batt_node,
					fg->batt_id_ohms / 1000, NULL);
					fg->batt_id_ohms / 1000, NULL);
	if (IS_ERR(profile_node))
	if (IS_ERR(profile_node))
@@ -1341,6 +1351,13 @@ static int fg_gen4_get_batt_profile(struct fg_dev *fg)
		return -ENODATA;
		return -ENODATA;
	}
	}


	if (chip->dt.multi_profile_load &&
		chip->batt_age_level != avail_age_level) {
		fg_dbg(fg, FG_STATUS, "Batt_age_level %d doesn't exist, using %d\n",
			chip->batt_age_level, avail_age_level);
		chip->batt_age_level = avail_age_level;
	}

	rc = of_property_read_string(profile_node, "qcom,battery-type",
	rc = of_property_read_string(profile_node, "qcom,battery-type",
			&fg->bp.batt_type_str);
			&fg->bp.batt_type_str);
	if (rc < 0) {
	if (rc < 0) {
@@ -1687,6 +1704,10 @@ static bool is_profile_load_required(struct fg_gen4_chip *chip)
			FIRST_PROFILE_LOAD_BIT,
			FIRST_PROFILE_LOAD_BIT,
	};
	};


	if (chip->dt.multi_profile_load &&
		(chip->batt_age_level != chip->last_batt_age_level))
		return true;

	rc = fg_sram_read(fg, PROFILE_INTEGRITY_WORD,
	rc = fg_sram_read(fg, PROFILE_INTEGRITY_WORD,
			PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT);
			PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT);
	if (rc < 0) {
	if (rc < 0) {
@@ -1756,47 +1777,29 @@ static bool is_profile_load_required(struct fg_gen4_chip *chip)
}
}


#define SOC_READY_WAIT_TIME_MS	1000
#define SOC_READY_WAIT_TIME_MS	1000
static void profile_load_work(struct work_struct *work)
static int qpnp_fg_gen4_load_profile(struct fg_gen4_chip *chip)
{
{
	struct fg_dev *fg = container_of(work,
	struct fg_dev *fg = &chip->fg;
				struct fg_dev,
				profile_load_work.work);
	struct fg_gen4_chip *chip = container_of(fg,
				struct fg_gen4_chip, fg);
	int64_t nom_cap_uah;
	u8 val, mask, buf[2];
	u8 val, mask, buf[2];
	int rc;
	int rc;
	bool normal_profile_load = false;


	vote(fg->awake_votable, PROFILE_LOAD, true, 0);
	/*

	 * This is used to determine if the profile is loaded normally i.e.
	rc = fg_gen4_get_batt_id(chip);
	 * either multi profile loading is disabled OR if it is enabled, then
	if (rc < 0) {
	 * only loading the profile with battery age level 0 is considered.
		pr_err("Error in getting battery id, rc:%d\n", rc);
	 */
		goto out;
	normal_profile_load = !chip->dt.multi_profile_load ||
	}
				(chip->dt.multi_profile_load &&

					chip->batt_age_level == 0);
	rc = fg_gen4_get_batt_profile(fg);
	if (normal_profile_load) {
	if (rc < 0) {
		rc = fg_masked_write(fg, BATT_SOC_RESTART(fg), RESTART_GO_BIT,
		fg->profile_load_status = PROFILE_MISSING;
					0);
		pr_warn("profile for batt_id=%dKOhms not found..using OTP, rc:%d\n",
			fg->batt_id_ohms / 1000, rc);
		goto out;
	}

	if (!fg->profile_available)
		goto out;

	if (!is_profile_load_required(chip))
		goto done;

	clear_cycle_count(chip->counter);

	fg_dbg(fg, FG_STATUS, "profile loading started\n");
	rc = fg_masked_write(fg, BATT_SOC_RESTART(fg), RESTART_GO_BIT, 0);
		if (rc < 0) {
		if (rc < 0) {
			pr_err("Error in writing to %04x, rc=%d\n",
			pr_err("Error in writing to %04x, rc=%d\n",
				BATT_SOC_RESTART(fg), rc);
				BATT_SOC_RESTART(fg), rc);
		goto out;
			return rc;
		}
	}
	}


	/* load battery profile */
	/* load battery profile */
@@ -1804,25 +1807,30 @@ static void profile_load_work(struct work_struct *work)
			chip->batt_profile, PROFILE_LEN, FG_IMA_ATOMIC);
			chip->batt_profile, PROFILE_LEN, FG_IMA_ATOMIC);
	if (rc < 0) {
	if (rc < 0) {
		pr_err("Error in writing battery profile, rc:%d\n", rc);
		pr_err("Error in writing battery profile, rc:%d\n", rc);
		goto out;
		return rc;
	}
	}


	if (normal_profile_load) {
		/* Enable side loading for voltage and current */
		/* Enable side loading for voltage and current */
		val = mask = BIT(0);
		val = mask = BIT(0);
		rc = fg_sram_masked_write(fg, SYS_CONFIG_WORD,
		rc = fg_sram_masked_write(fg, SYS_CONFIG_WORD,
				SYS_CONFIG_OFFSET, mask, val, FG_IMA_DEFAULT);
				SYS_CONFIG_OFFSET, mask, val, FG_IMA_DEFAULT);
		if (rc < 0) {
		if (rc < 0) {
		pr_err("Error in setting SYS_CONFIG_WORD[0], rc=%d\n", rc);
			pr_err("Error in setting SYS_CONFIG_WORD[0], rc=%d\n",
		goto out;
				rc);
			return rc;
		}
		}


		/* Clear first logged main current ADC values */
		/* Clear first logged main current ADC values */
		buf[0] = buf[1] = 0;
		buf[0] = buf[1] = 0;
		rc = fg_sram_write(fg, FIRST_LOG_CURRENT_v2_WORD,
		rc = fg_sram_write(fg, FIRST_LOG_CURRENT_v2_WORD,
			FIRST_LOG_CURRENT_v2_OFFSET, buf, 2, FG_IMA_DEFAULT);
				FIRST_LOG_CURRENT_v2_OFFSET, buf, 2,
				FG_IMA_DEFAULT);
		if (rc < 0) {
		if (rc < 0) {
		pr_err("Error in clearing FIRST_LOG_CURRENT rc=%d\n", rc);
			pr_err("Error in clearing FIRST_LOG_CURRENT rc=%d\n",
		goto out;
				rc);
			return rc;
		}
	}
	}


	/* Set the profile integrity bit */
	/* Set the profile integrity bit */
@@ -1831,13 +1839,24 @@ static void profile_load_work(struct work_struct *work)
			PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT);
			PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT);
	if (rc < 0) {
	if (rc < 0) {
		pr_err("failed to write profile integrity rc=%d\n", rc);
		pr_err("failed to write profile integrity rc=%d\n", rc);
		goto out;
		return rc;
	}

	if (chip->dt.multi_profile_load && chip->batt_age_level >= 0) {
		val = (u8)chip->batt_age_level;
		rc = fg_sram_write(fg, BATT_AGE_LEVEL_WORD,
				BATT_AGE_LEVEL_OFFSET, &val, 1, FG_IMA_ATOMIC);
		if (rc < 0) {
			pr_err("Error in writing batt_age_level, rc:%d\n", rc);
			return rc;
		}
	}
	}


	if (normal_profile_load) {
		rc = fg_restart(fg, SOC_READY_WAIT_TIME_MS);
		rc = fg_restart(fg, SOC_READY_WAIT_TIME_MS);
		if (rc < 0) {
		if (rc < 0) {
			pr_err("Error in restarting FG, rc=%d\n", rc);
			pr_err("Error in restarting FG, rc=%d\n", rc);
		goto out;
			return rc;
		}
		}


		/* Clear side loading for voltage and current */
		/* Clear side loading for voltage and current */
@@ -1846,10 +1865,57 @@ static void profile_load_work(struct work_struct *work)
		rc = fg_sram_masked_write(fg, SYS_CONFIG_WORD,
		rc = fg_sram_masked_write(fg, SYS_CONFIG_WORD,
				SYS_CONFIG_OFFSET, mask, val, FG_IMA_DEFAULT);
				SYS_CONFIG_OFFSET, mask, val, FG_IMA_DEFAULT);
		if (rc < 0) {
		if (rc < 0) {
		pr_err("Error in clearing SYS_CONFIG_WORD[0], rc=%d\n", rc);
			pr_err("Error in clearing SYS_CONFIG_WORD[0], rc=%d\n",
				rc);
			return rc;
		}
	}

	return 0;
}

static void profile_load_work(struct work_struct *work)
{
	struct fg_dev *fg = container_of(work,
				struct fg_dev,
				profile_load_work.work);
	struct fg_gen4_chip *chip = container_of(fg,
				struct fg_gen4_chip, fg);
	int64_t nom_cap_uah;
	u8 val, buf[2];
	int rc;

	vote(fg->awake_votable, PROFILE_LOAD, true, 0);

	rc = fg_gen4_get_batt_id(chip);
	if (rc < 0) {
		pr_err("Error in getting battery id, rc:%d\n", rc);
		goto out;
	}

	rc = fg_gen4_get_batt_profile(fg);
	if (rc < 0) {
		fg->profile_load_status = PROFILE_MISSING;
		pr_warn("profile for batt_id=%dKOhms not found..using OTP, rc:%d\n",
			fg->batt_id_ohms / 1000, rc);
		goto out;
		goto out;
	}
	}


	if (!fg->profile_available)
		goto out;

	if (!is_profile_load_required(chip))
		goto done;

	if (!chip->dt.multi_profile_load)
		clear_cycle_count(chip->counter);

	fg_dbg(fg, FG_STATUS, "profile loading started\n");

	rc = qpnp_fg_gen4_load_profile(chip);
	if (rc < 0)
		goto out;

	fg_dbg(fg, FG_STATUS, "SOC is ready\n");
	fg_dbg(fg, FG_STATUS, "SOC is ready\n");
	fg->profile_load_status = PROFILE_LOADED;
	fg->profile_load_status = PROFILE_LOADED;


@@ -1900,6 +1966,8 @@ static void profile_load_work(struct work_struct *work)
	schedule_delayed_work(&chip->ttf->ttf_work, msecs_to_jiffies(10000));
	schedule_delayed_work(&chip->ttf->ttf_work, msecs_to_jiffies(10000));
	fg_dbg(fg, FG_STATUS, "profile loaded successfully");
	fg_dbg(fg, FG_STATUS, "profile loaded successfully");
out:
out:
	if (chip->dt.multi_profile_load && rc < 0)
		chip->batt_age_level = chip->last_batt_age_level;
	fg->soc_reporting_ready = true;
	fg->soc_reporting_ready = true;
	vote(fg->awake_votable, ESR_FCC_VOTER, true, 0);
	vote(fg->awake_votable, ESR_FCC_VOTER, true, 0);
	schedule_delayed_work(&chip->pl_enable_work, msecs_to_jiffies(5000));
	schedule_delayed_work(&chip->pl_enable_work, msecs_to_jiffies(5000));
@@ -3511,6 +3579,9 @@ static int fg_psy_get_property(struct power_supply *psy,
	case POWER_SUPPLY_PROP_CC_STEP_SEL:
	case POWER_SUPPLY_PROP_CC_STEP_SEL:
		pval->intval = chip->ttf->cc_step.sel;
		pval->intval = chip->ttf->cc_step.sel;
		break;
		break;
	case POWER_SUPPLY_PROP_BATT_AGE_LEVEL:
		pval->intval = chip->batt_age_level;
		break;
	default:
	default:
		pr_err("unsupported property %d\n", psp);
		pr_err("unsupported property %d\n", psp);
		rc = -EINVAL;
		rc = -EINVAL;
@@ -3592,6 +3663,14 @@ static int fg_psy_set_property(struct power_supply *psy,
				chip->first_profile_load = false;
				chip->first_profile_load = false;
		}
		}
		break;
		break;
	case POWER_SUPPLY_PROP_BATT_AGE_LEVEL:
		if (!chip->dt.multi_profile_load || pval->intval < 0 ||
			chip->batt_age_level == pval->intval)
			return -EINVAL;
		chip->last_batt_age_level = chip->batt_age_level;
		chip->batt_age_level = pval->intval;
		schedule_delayed_work(&fg->profile_load_work, 0);
		break;
	default:
	default:
		break;
		break;
	}
	}
@@ -3610,6 +3689,7 @@ static int fg_property_is_writeable(struct power_supply *psy,
	case POWER_SUPPLY_PROP_ESR_NOMINAL:
	case POWER_SUPPLY_PROP_ESR_NOMINAL:
	case POWER_SUPPLY_PROP_SOH:
	case POWER_SUPPLY_PROP_SOH:
	case POWER_SUPPLY_PROP_CLEAR_SOH:
	case POWER_SUPPLY_PROP_CLEAR_SOH:
	case POWER_SUPPLY_PROP_BATT_AGE_LEVEL:
		return 1;
		return 1;
	default:
	default:
		break;
		break;
@@ -3646,6 +3726,7 @@ static enum power_supply_property fg_psy_props[] = {
	POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
	POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
	POWER_SUPPLY_PROP_CC_STEP,
	POWER_SUPPLY_PROP_CC_STEP,
	POWER_SUPPLY_PROP_CC_STEP_SEL,
	POWER_SUPPLY_PROP_CC_STEP_SEL,
	POWER_SUPPLY_PROP_BATT_AGE_LEVEL,
};
};


static const struct power_supply_desc fg_psy_desc = {
static const struct power_supply_desc fg_psy_desc = {
@@ -4220,6 +4301,13 @@ static int fg_gen4_hw_init(struct fg_gen4_chip *chip)
		return rc;
		return rc;
	}
	}


	chip->batt_age_level = chip->last_batt_age_level = -EINVAL;
	if (chip->dt.multi_profile_load) {
		rc = fg_sram_read(fg, BATT_AGE_LEVEL_WORD,
			BATT_AGE_LEVEL_OFFSET, &val, 1, FG_IMA_DEFAULT);
		if (!rc)
			chip->batt_age_level = chip->last_batt_age_level = val;
	}
	return 0;
	return 0;
}
}


@@ -4701,6 +4789,8 @@ static int fg_gen4_parse_dt(struct fg_gen4_chip *chip)


	chip->dt.five_pin_battery = of_property_read_bool(node,
	chip->dt.five_pin_battery = of_property_read_bool(node,
					"qcom,five-pin-battery");
					"qcom,five-pin-battery");
	chip->dt.multi_profile_load = of_property_read_bool(node,
					"qcom,multi-profile-load");
	return 0;
	return 0;
}
}