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

Commit ca652b3d authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "power: qcom-step-chg: Add temperature based step-charging"

parents 8b13ff94 95e05ba5
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -178,6 +178,10 @@ Charger specific properties:
  Definition: WD bark-timeout in seconds. The possible values are
		16, 32, 64, 128. If not defined it defaults to 64.

- qcom,sw-jeita-enable
  Usage:      optional
  Value type: bool
  Definition: Boolean flag which when present enables sw compensation for jeita

=============================================
Second Level Nodes - SMB2 Charger Peripherals
+3 −0
Original line number Diff line number Diff line
@@ -210,6 +210,9 @@ static int smb2_parse_dt(struct smb2 *chip)
	chg->step_chg_enabled = of_property_read_bool(node,
				"qcom,step-charging-enable");

	chg->sw_jeita_enabled = of_property_read_bool(node,
				"qcom,sw-jeita-enable");

	rc = of_property_read_u32(node, "qcom,wd-bark-time-secs",
					&chip->dt.wd_bark_time);
	if (rc < 0 || chip->dt.wd_bark_time < MIN_WD_BARK_TIME)
+3 −2
Original line number Diff line number Diff line
@@ -4070,7 +4070,7 @@ irqreturn_t smblib_handle_wdog_bark(int irq, void *data)
	if (rc < 0)
		smblib_err(chg, "Couldn't pet the dog rc=%d\n", rc);

	if (chg->step_chg_enabled)
	if (chg->step_chg_enabled || chg->sw_jeita_enabled)
		power_supply_changed(chg->batt_psy);

	return IRQ_HANDLED;
@@ -4708,7 +4708,8 @@ int smblib_init(struct smb_charger *chg)
			return rc;
		}

		rc = qcom_step_chg_init(chg->step_chg_enabled);
		rc = qcom_step_chg_init(chg->step_chg_enabled,
						chg->sw_jeita_enabled);
		if (rc < 0) {
			smblib_err(chg, "Couldn't init qcom_step_chg_init rc=%d\n",
				rc);
+1 −0
Original line number Diff line number Diff line
@@ -306,6 +306,7 @@ struct smb_charger {
	int			dcp_icl_ua;
	int			fake_capacity;
	bool			step_chg_enabled;
	bool			sw_jeita_enabled;
	bool			is_hdc;
	bool			chg_done;
	bool			micro_usb_mode;
+270 −51
Original line number Diff line number Diff line
@@ -20,7 +20,7 @@

#define MAX_STEP_CHG_ENTRIES	8
#define STEP_CHG_VOTER		"STEP_CHG_VOTER"
#define STATUS_CHANGE_VOTER	"STATUS_CHANGE_VOTER"
#define JEITA_VOTER		"JEITA_VOTER"

#define is_between(left, right, value) \
		(((left) >= (right) && (left) >= (value) \
@@ -28,23 +28,44 @@
		|| ((left) <= (right) && (left) <= (value) \
			&& (value) <= (right)))

struct step_chg_data {
	u32 vbatt_soc_low;
	u32 vbatt_soc_high;
	u32 fcc_ua;
struct range_data {
	u32 low_threshold;
	u32 high_threshold;
	u32 value;
};

struct step_chg_cfg {
	u32			psy_prop;
	char			*prop_name;
	struct step_chg_data cfg[MAX_STEP_CHG_ENTRIES];
	int			hysteresis;
	struct range_data	fcc_cfg[MAX_STEP_CHG_ENTRIES];
};

struct jeita_fcc_cfg {
	u32			psy_prop;
	char			*prop_name;
	int			hysteresis;
	struct range_data	fcc_cfg[MAX_STEP_CHG_ENTRIES];
};

struct jeita_fv_cfg {
	u32			psy_prop;
	char			*prop_name;
	int			hysteresis;
	struct range_data	fv_cfg[MAX_STEP_CHG_ENTRIES];
};

struct step_chg_info {
	ktime_t			last_update_time;
	ktime_t			step_last_update_time;
	ktime_t			jeita_last_update_time;
	bool			step_chg_enable;
	bool			sw_jeita_enable;
	int			jeita_fcc_index;
	int			jeita_fv_index;
	int			step_index;

	struct votable		*fcc_votable;
	struct votable		*fv_votable;
	struct wakeup_source	*step_chg_ws;
	struct power_supply	*batt_psy;
	struct delayed_work	status_change_work;
@@ -53,26 +74,30 @@ struct step_chg_info {

static struct step_chg_info *the_chip;

#define STEP_CHG_HYSTERISIS_DELAY_US		5000000 /* 5 secs */

/*
 * Step Charging Configuration
 * Update the table based on the battery profile
 * Supports VBATT and SOC based source
 * range data must be in increasing ranges and shouldn't overlap
 */
static struct step_chg_cfg step_chg_config = {
	.psy_prop	= POWER_SUPPLY_PROP_VOLTAGE_NOW,
	.prop_name	= "VBATT",
	.cfg	 = {
	.hysteresis	= 100000, /* 100mV */
	.fcc_cfg	= {
		/* VBAT_LOW	VBAT_HIGH	FCC */
		{3600000,	4000000,	3000000},
		{4000000,	4200000,	2800000},
		{4200000,	4400000,	2000000},
		{4001000,	4200000,	2800000},
		{4201000,	4400000,	2000000},
	},
	/*
	 *	SOC STEP-CHG configuration example.
	 *
	 *	.psy_prop = POWER_SUPPLY_PROP_CAPACITY,
	 *	.prop_name = "SOC",
 *	.cfg	= {
	 *	.fcc_cfg	= {
	 *		//SOC_LOW	SOC_HIGH	FCC
	 *		{20,		70,		3000000},
	 *		{70,		90,		2750000},
@@ -81,6 +106,40 @@ static struct step_chg_cfg step_chg_config = {
	 */
};

/*
 * Jeita Charging Configuration
 * Update the table based on the battery profile
 * Please ensure that the TEMP ranges are programmed in the hw so that
 * an interrupt is issued and a consequent psy changed will cause us to
 * react immediately.
 * range data must be in increasing ranges and shouldn't overlap.
 * Gaps are okay
 */
static struct jeita_fcc_cfg jeita_fcc_config = {
	.psy_prop	= POWER_SUPPLY_PROP_TEMP,
	.prop_name	= "BATT_TEMP",
	.hysteresis	= 10, /* 1degC hysteresis */
	.fcc_cfg	= {
		/* TEMP_LOW	TEMP_HIGH	FCC */
		{0,		100,		600000},
		{101,		200,		2000000},
		{201,		450,		3000000},
		{451,		550,		600000},
	},
};

static struct jeita_fv_cfg jeita_fv_config = {
	.psy_prop	= POWER_SUPPLY_PROP_TEMP,
	.prop_name	= "BATT_TEMP",
	.hysteresis	= 10, /* 1degC hysteresis */
	.fv_cfg		= {
		/* TEMP_LOW	TEMP_HIGH	FCC */
		{0,		100,		4200000},
		{101,		450,		4400000},
		{451,		550,		4200000},
	},
};

static bool is_batt_available(struct step_chg_info *chip)
{
	if (!chip->batt_psy)
@@ -92,22 +151,67 @@ static bool is_batt_available(struct step_chg_info *chip)
	return true;
}

static int get_fcc(int threshold)
static int get_val(struct range_data *range, int hysteresis, int current_index,
		int threshold,
		int *new_index, int *val)
{
	int i;

	*new_index = -EINVAL;
	/* first find the matching index without hysteresis */
	for (i = 0; i < MAX_STEP_CHG_ENTRIES; i++)
		if (is_between(step_chg_config.cfg[i].vbatt_soc_low,
			step_chg_config.cfg[i].vbatt_soc_high, threshold))
			return step_chg_config.cfg[i].fcc_ua;
		if (is_between(range[i].low_threshold,
			range[i].high_threshold, threshold)) {
			*new_index = i;
			*val = range[i].value;
		}

	/* if nothing was found, return -ENODATA */
	if (*new_index == -EINVAL)
		return -ENODATA;
	/*
	 * If we don't have a current_index return this
	 * newfound value. There is no hysterisis from out of range
	 * to in range transition
	 */
	if (current_index == -EINVAL)
		return 0;

	/*
	 * Check for hysteresis if it in the neighbourhood
	 * of our current index.
	 */
	if (*new_index == current_index + 1) {
		if (threshold < range[*new_index].low_threshold + hysteresis) {
			/*
			 * Stay in the current index, threshold is not higher
			 * by hysteresis amount
			 */
			*new_index = current_index;
			*val = range[current_index].value;
		}
	} else if (*new_index == current_index - 1) {
		if (threshold > range[*new_index].high_threshold - hysteresis) {
			/*
			 * stay in the current index, threshold is not lower
			 * by hysteresis amount
			 */
			*new_index = current_index;
			*val = range[current_index].value;
		}
	}
	return 0;
}

static int handle_step_chg_config(struct step_chg_info *chip)
{
	union power_supply_propval pval = {0, };
	int rc = 0, fcc_ua = 0;
	u64 elapsed_us;

	elapsed_us = ktime_us_delta(ktime_get(), chip->step_last_update_time);
	if (elapsed_us < STEP_CHG_HYSTERISIS_DELAY_US)
		goto reschedule;

	rc = power_supply_get_property(chip->batt_psy,
		POWER_SUPPLY_PROP_STEP_CHARGING_ENABLED, &pval);
@@ -119,7 +223,7 @@ static int handle_step_chg_config(struct step_chg_info *chip)
	if (!chip->step_chg_enable) {
		if (chip->fcc_votable)
			vote(chip->fcc_votable, STEP_CHG_VOTER, false, 0);
		return 0;
		goto update_time;
	}

	rc = power_supply_get_property(chip->batt_psy,
@@ -130,48 +234,144 @@ static int handle_step_chg_config(struct step_chg_info *chip)
		return rc;
	}

	chip->fcc_votable = find_votable("FCC");
	if (!chip->fcc_votable)
		return -EINVAL;

	fcc_ua = get_fcc(pval.intval);
	if (fcc_ua < 0) {
	rc = get_val(step_chg_config.fcc_cfg, step_chg_config.hysteresis,
			chip->step_index,
			pval.intval,
			&chip->step_index,
			&fcc_ua);
	if (rc < 0) {
		/* remove the vote if no step-based fcc is found */
		if (chip->fcc_votable)
			vote(chip->fcc_votable, STEP_CHG_VOTER, false, 0);
		return 0;
		goto update_time;
	}

	if (!chip->fcc_votable)
		chip->fcc_votable = find_votable("FCC");
	if (!chip->fcc_votable)
		return -EINVAL;

	vote(chip->fcc_votable, STEP_CHG_VOTER, true, fcc_ua);

	pr_debug("%s = %d Step-FCC = %duA\n",
		step_chg_config.prop_name, pval.intval, fcc_ua);

update_time:
	chip->step_last_update_time = ktime_get();
	return 0;

reschedule:
	/* reschedule 1000uS after the remaining time */
	return (STEP_CHG_HYSTERISIS_DELAY_US - elapsed_us + 1000);
}

static int handle_jeita(struct step_chg_info *chip)
{
	union power_supply_propval pval = {0, };
	int rc = 0, fcc_ua = 0, fv_uv = 0;
	u64 elapsed_us;

	if (!chip->sw_jeita_enable) {
		if (chip->fcc_votable)
			vote(chip->fcc_votable, JEITA_VOTER, false, 0);
		if (chip->fv_votable)
			vote(chip->fv_votable, JEITA_VOTER, false, 0);
		return 0;
	}

	elapsed_us = ktime_us_delta(ktime_get(), chip->jeita_last_update_time);
	if (elapsed_us < STEP_CHG_HYSTERISIS_DELAY_US)
		goto reschedule;

	rc = power_supply_get_property(chip->batt_psy,
				jeita_fcc_config.psy_prop, &pval);
	if (rc < 0) {
		pr_err("Couldn't read %s property rc=%d\n",
				step_chg_config.prop_name, rc);
		return rc;
	}

	rc = get_val(jeita_fcc_config.fcc_cfg, jeita_fcc_config.hysteresis,
			chip->jeita_fcc_index,
			pval.intval,
			&chip->jeita_fcc_index,
			&fcc_ua);
	if (rc < 0) {
		/* remove the vote if no step-based fcc is found */
		if (chip->fcc_votable)
			vote(chip->fcc_votable, JEITA_VOTER, false, 0);
		goto update_time;
	}

	if (!chip->fcc_votable)
		chip->fcc_votable = find_votable("FCC");
	if (!chip->fcc_votable)
		/* changing FCC is a must */
		return -EINVAL;

	vote(chip->fcc_votable, JEITA_VOTER, true, fcc_ua);

	rc = get_val(jeita_fv_config.fv_cfg, jeita_fv_config.hysteresis,
			chip->jeita_fv_index,
			pval.intval,
			&chip->jeita_fv_index,
			&fv_uv);
	if (rc < 0) {
		/* remove the vote if no step-based fcc is found */
		if (chip->fv_votable)
			vote(chip->fv_votable, JEITA_VOTER, false, 0);
		goto update_time;
	}

	chip->fv_votable = find_votable("FV");
	if (!chip->fv_votable)
		goto update_time;

	vote(chip->fv_votable, JEITA_VOTER, true, fv_uv);

	pr_debug("%s = %d FCC = %duA FV = %duV\n",
		step_chg_config.prop_name, pval.intval, fcc_ua, fv_uv);

update_time:
	chip->jeita_last_update_time = ktime_get();
	return 0;

reschedule:
	/* reschedule 1000uS after the remaining time */
	return (STEP_CHG_HYSTERISIS_DELAY_US - elapsed_us + 1000);
}

#define STEP_CHG_HYSTERISIS_DELAY_US		5000000 /* 5 secs */
static void status_change_work(struct work_struct *work)
{
	struct step_chg_info *chip = container_of(work,
			struct step_chg_info, status_change_work.work);
	int rc = 0;
	u64 elapsed_us;

	elapsed_us = ktime_us_delta(ktime_get(), chip->last_update_time);
	if (elapsed_us < STEP_CHG_HYSTERISIS_DELAY_US)
		goto release_ws;
	int reschedule_us;
	int reschedule_jeita_work_us = 0;
	int reschedule_step_work_us = 0;

	if (!is_batt_available(chip))
		goto release_ws;
		return;

	/* skip elapsed_us debounce for handling battery temperature */
	rc = handle_jeita(chip);
	if (rc > 0)
		reschedule_jeita_work_us = rc;
	else if (rc < 0)
		pr_err("Couldn't handle sw jeita rc = %d\n", rc);

	rc = handle_step_chg_config(chip);
	if (rc > 0)
		reschedule_step_work_us = rc;
	if (rc < 0)
		goto release_ws;
		pr_err("Couldn't handle step rc = %d\n", rc);

	chip->last_update_time = ktime_get();

release_ws:
	reschedule_us = min(reschedule_jeita_work_us, reschedule_step_work_us);
	if (reschedule_us == 0)
		__pm_relax(chip->step_chg_ws);
	else
		schedule_delayed_work(&chip->status_change_work,
				usecs_to_jiffies(reschedule_us));
}

static int step_chg_notifier_call(struct notifier_block *nb,
@@ -205,7 +405,7 @@ static int step_chg_register_notifier(struct step_chg_info *chip)
	return 0;
}

int qcom_step_chg_init(bool step_chg_enable)
int qcom_step_chg_init(bool step_chg_enable, bool sw_jeita_enable)
{
	int rc;
	struct step_chg_info *chip;
@@ -226,6 +426,11 @@ int qcom_step_chg_init(bool step_chg_enable)
	}

	chip->step_chg_enable = step_chg_enable;
	chip->sw_jeita_enable = sw_jeita_enable;

	chip->step_index = -EINVAL;
	chip->jeita_fcc_index = -EINVAL;
	chip->jeita_fv_index = -EINVAL;

	if (step_chg_enable && (!step_chg_config.psy_prop ||
				!step_chg_config.prop_name)) {
@@ -234,6 +439,20 @@ int qcom_step_chg_init(bool step_chg_enable)
		return -ENODATA;
	}

	if (sw_jeita_enable && (!jeita_fcc_config.psy_prop ||
				!jeita_fcc_config.prop_name)) {
		/* fail if step-chg configuration is invalid */
		pr_err("Jeita TEMP configuration not defined - fail\n");
		return -ENODATA;
	}

	if (sw_jeita_enable && (!jeita_fv_config.psy_prop ||
				!jeita_fv_config.prop_name)) {
		/* fail if step-chg configuration is invalid */
		pr_err("Jeita TEMP configuration not defined - fail\n");
		return -ENODATA;
	}

	INIT_DELAYED_WORK(&chip->status_change_work, status_change_work);

	rc = step_chg_register_notifier(chip);
Loading