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 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
		    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
==========================================================
+150 −60
Original line number Diff line number Diff line
@@ -116,6 +116,8 @@
#define RCONN_OFFSET			0
#define ACT_BATT_CAP_WORD		285
#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_OFFSET		0
#define PROFILE_INTEGRITY_WORD		299
@@ -190,6 +192,7 @@ struct fg_dt_props {
	bool	linearize_soc;
	bool	rapid_soc_dec_en;
	bool	five_pin_battery;
	bool	multi_profile_load;
	int	cutoff_volt_mv;
	int	empty_volt_mv;
	int	cutoff_curr_ma;
@@ -249,6 +252,8 @@ struct fg_gen4_chip {
	int			esr_nominal;
	int			soh;
	int			esr_soh_cycle_count;
	int			batt_age_level;
	int			last_batt_age_level;
	bool			first_profile_load;
	bool			ki_coeff_dischg_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 *batt_node, *profile_node;
	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");
	if (!batt_node) {
@@ -1331,6 +1336,11 @@ static int fg_gen4_get_batt_profile(struct fg_dev *fg)
		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,
					fg->batt_id_ohms / 1000, NULL);
	if (IS_ERR(profile_node))
@@ -1341,6 +1351,13 @@ static int fg_gen4_get_batt_profile(struct fg_dev *fg)
		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",
			&fg->bp.batt_type_str);
	if (rc < 0) {
@@ -1687,6 +1704,10 @@ static bool is_profile_load_required(struct fg_gen4_chip *chip)
			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,
			PROFILE_INTEGRITY_OFFSET, &val, 1, FG_IMA_DEFAULT);
	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
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,
				profile_load_work.work);
	struct fg_gen4_chip *chip = container_of(fg,
				struct fg_gen4_chip, fg);
	int64_t nom_cap_uah;
	struct fg_dev *fg = &chip->fg;
	u8 val, mask, buf[2];
	int rc;
	bool normal_profile_load = false;

	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;
	}

	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);
	/*
	 * This is used to determine if the profile is loaded normally i.e.
	 * either multi profile loading is disabled OR if it is enabled, then
	 * only loading the profile with battery age level 0 is considered.
	 */
	normal_profile_load = !chip->dt.multi_profile_load ||
				(chip->dt.multi_profile_load &&
					chip->batt_age_level == 0);
	if (normal_profile_load) {
		rc = fg_masked_write(fg, BATT_SOC_RESTART(fg), RESTART_GO_BIT,
					0);
		if (rc < 0) {
			pr_err("Error in writing to %04x, rc=%d\n",
				BATT_SOC_RESTART(fg), rc);
		goto out;
			return rc;
		}
	}

	/* load battery profile */
@@ -1804,25 +1807,30 @@ static void profile_load_work(struct work_struct *work)
			chip->batt_profile, PROFILE_LEN, FG_IMA_ATOMIC);
	if (rc < 0) {
		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 */
		val = mask = BIT(0);
		rc = fg_sram_masked_write(fg, SYS_CONFIG_WORD,
				SYS_CONFIG_OFFSET, mask, val, FG_IMA_DEFAULT);
		if (rc < 0) {
		pr_err("Error in setting SYS_CONFIG_WORD[0], rc=%d\n", rc);
		goto out;
			pr_err("Error in setting SYS_CONFIG_WORD[0], rc=%d\n",
				rc);
			return rc;
		}

		/* Clear first logged main current ADC values */
		buf[0] = buf[1] = 0;
		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) {
		pr_err("Error in clearing FIRST_LOG_CURRENT rc=%d\n", rc);
		goto out;
			pr_err("Error in clearing FIRST_LOG_CURRENT rc=%d\n",
				rc);
			return rc;
		}
	}

	/* 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);
	if (rc < 0) {
		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);
		if (rc < 0) {
			pr_err("Error in restarting FG, rc=%d\n", rc);
		goto out;
			return rc;
		}

		/* 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,
				SYS_CONFIG_OFFSET, mask, val, FG_IMA_DEFAULT);
		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;
	}

	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->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));
	fg_dbg(fg, FG_STATUS, "profile loaded successfully");
out:
	if (chip->dt.multi_profile_load && rc < 0)
		chip->batt_age_level = chip->last_batt_age_level;
	fg->soc_reporting_ready = true;
	vote(fg->awake_votable, ESR_FCC_VOTER, true, 0);
	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:
		pval->intval = chip->ttf->cc_step.sel;
		break;
	case POWER_SUPPLY_PROP_BATT_AGE_LEVEL:
		pval->intval = chip->batt_age_level;
		break;
	default:
		pr_err("unsupported property %d\n", psp);
		rc = -EINVAL;
@@ -3592,6 +3663,14 @@ static int fg_psy_set_property(struct power_supply *psy,
				chip->first_profile_load = false;
		}
		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:
		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_SOH:
	case POWER_SUPPLY_PROP_CLEAR_SOH:
	case POWER_SUPPLY_PROP_BATT_AGE_LEVEL:
		return 1;
	default:
		break;
@@ -3646,6 +3726,7 @@ static enum power_supply_property fg_psy_props[] = {
	POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
	POWER_SUPPLY_PROP_CC_STEP,
	POWER_SUPPLY_PROP_CC_STEP_SEL,
	POWER_SUPPLY_PROP_BATT_AGE_LEVEL,
};

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;
	}

	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;
}

@@ -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,
					"qcom,five-pin-battery");
	chip->dt.multi_profile_load = of_property_read_bool(node,
					"qcom,multi-profile-load");
	return 0;
}