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

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

Merge "power: qpnp-fg-gen4: add support for capacity learning and cycle counter"

parents c28d2cc0 3e306598
Loading
Loading
Loading
Loading
+49 −0
Original line number Diff line number Diff line
@@ -158,6 +158,55 @@ First Level Node - FG Gen4 device
		    loaded earlier by bootloader doesn't match with the profile
		    available in the device tree.

- qcom,cl-start-capacity
	Usage:      optional
	Value type: <u32>
	Definition: Battery SOC threshold to start the capacity learning.
		    If this is not specified, then the default value used
		    will be 15. Unit is in percentage.

- qcom,cl-min-temp
	Usage:      optional
	Value type: <u32>
	Definition: Lower limit of battery temperature to start the capacity
		    learning. If this is not specified, then the default value
		    used will be 150 (15 C). Unit is in decidegC.

- qcom,cl-max-temp
	Usage:      optional
	Value type: <u32>
	Definition: Upper limit of battery temperature to start the capacity
		    learning. If this is not specified, then the default value
		    used will be 500 (50 C). Unit is in decidegC.

- qcom,cl-max-increment
	Usage:      optional
	Value type: <u32>
	Definition: Maximum capacity increment allowed per capacity learning
		    cycle. If this is not specified, then the default value
		    used will be 5 (0.5%). Unit is in decipercentage.

- qcom,cl-max-decrement
	Usage:      optional
	Value type: <u32>
	Definition: Maximum capacity decrement allowed per capacity learning
		    cycle. If this is not specified, then the default value
		    used will be 100 (10%). Unit is in decipercentage.

- qcom,cl-min-limit
	Usage:      optional
	Value type: <u32>
	Definition: Minimum limit that the capacity cannot go below in a
		    capacity learning cycle. If this is not specified, then
		    the default value is 0. Unit is in decipercentage.

- qcom,cl-max-limit
	Usage:      optional
	Value type: <u32>
	Definition: Maximum limit that the capacity cannot go above in a
		    capacity learning cycle. If this is not specified, then
		    the default value is 0. Unit is in decipercentage.

- qcom,hold-soc-while-full
	Usage:      optional
	Value type: <empty>
+1 −1
Original line number Diff line number Diff line
obj-$(CONFIG_QPNP_FG_GEN3)     += qpnp-fg-gen3.o fg-memif.o fg-util.o
obj-$(CONFIG_QPNP_FG_GEN4)     += qpnp-fg-gen4.o fg-memif.o fg-util.o pmic-voter.o
obj-$(CONFIG_QPNP_FG_GEN4)     += qpnp-fg-gen4.o fg-memif.o fg-util.o fg-alg.o  pmic-voter.o
obj-$(CONFIG_SMB1355_SLAVE_CHARGER)   += smb1355-charger.o pmic-voter.o
obj-$(CONFIG_QPNP_SMB2)		+= step-chg-jeita.o battery.o qpnp-smb2.o smb-lib.o pmic-voter.o storm-watch.o
obj-$(CONFIG_SMB138X_CHARGER)	+= step-chg-jeita.o smb138x-charger.o smb-lib.o pmic-voter.o storm-watch.o battery.o
+328 −7
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#include <linux/qpnp/qpnp-revid.h>
#include "fg-core.h"
#include "fg-reg.h"
#include "fg-alg.h"

#define FG_GEN4_DEV_NAME	"qcom,fg-gen4"

@@ -142,9 +143,12 @@ struct fg_dt_props {
struct fg_gen4_chip {
	struct fg_dev		fg;
	struct fg_dt_props	dt;
	struct cycle_counter	*counter;
	struct cap_learning	*cl;
	struct ttf		ttf;
	struct delayed_work	ttf_work;
	char			batt_profile[PROFILE_LEN];
	char			counter_buf[BUCKET_COUNT * 8];
	bool			ki_coeff_dischg_en;
	bool			slope_limit_en;
};
@@ -321,7 +325,7 @@ static int fg_gen4_get_learned_capacity(void *data, int64_t *learned_cap_uah)
	int rc, act_cap_mah;

	if (!chip)
		return -ENODATA;
		return -ENODEV;

	fg = &chip->fg;
	rc = fg_get_sram_prop(fg, FG_SRAM_ACT_BATT_CAP, &act_cap_mah);
@@ -542,6 +546,128 @@ static inline void get_esr_meas_current(int curr_ma, u8 *val)
	*val <<= ESR_PULL_DOWN_IVAL_SHIFT;
}

/* ALG callback functions below */

static int fg_gen4_store_learned_capacity(void *data, int64_t learned_cap_uah)
{
	struct fg_gen4_chip *chip = data;
	struct fg_dev *fg;
	int16_t cc_mah;
	int rc;

	if (!chip)
		return -ENODEV;

	fg = &chip->fg;
	if (fg->battery_missing || !learned_cap_uah)
		return -EPERM;

	cc_mah = div64_s64(learned_cap_uah, 1000);
	rc = fg_sram_write(fg, fg->sp[FG_SRAM_ACT_BATT_CAP].addr_word,
			fg->sp[FG_SRAM_ACT_BATT_CAP].addr_byte, (u8 *)&cc_mah,
			fg->sp[FG_SRAM_ACT_BATT_CAP].len, FG_IMA_DEFAULT);
	if (rc < 0) {
		pr_err("Error in writing act_batt_cap_bkup, rc=%d\n", rc);
		return rc;
	}

	fg_dbg(fg, FG_CAP_LEARN, "learned capacity %llduah/%dmah stored\n",
		chip->cl->learned_cap_uah, cc_mah);
	return 0;
}

static int fg_gen4_prime_cc_soc_sw(void *data, u32 batt_soc)
{
	struct fg_gen4_chip *chip = data;
	struct fg_dev *fg;
	int rc, cc_soc_sw;

	if (!chip)
		return -ENODEV;

	fg = &chip->fg;
	if (batt_soc == CC_SOC_30BIT)
		cc_soc_sw = batt_soc;
	else
		cc_soc_sw = div64_s64((int64_t)batt_soc * CC_SOC_30BIT,
				BATT_SOC_32BIT);

	rc = fg_sram_write(fg, fg->sp[FG_SRAM_CC_SOC_SW].addr_word,
		fg->sp[FG_SRAM_CC_SOC_SW].addr_byte, (u8 *)&cc_soc_sw,
		fg->sp[FG_SRAM_CC_SOC_SW].len, FG_IMA_ATOMIC);
	if (rc < 0)
		pr_err("Error in writing cc_soc_sw, rc=%d\n", rc);
	else
		fg_dbg(fg, FG_STATUS, "cc_soc_sw: %x\n", cc_soc_sw);

	return rc;
}

static int fg_gen4_get_cc_soc_sw(void *data, int *cc_soc_sw)
{
	struct fg_gen4_chip *chip = data;
	struct fg_dev *fg;
	int rc, temp;

	if (!chip)
		return -ENODEV;

	fg = &chip->fg;
	rc = fg_get_sram_prop(fg, FG_SRAM_CC_SOC_SW, &temp);
	if (rc < 0) {
		pr_err("Error in getting CC_SOC_SW, rc=%d\n", rc);
		return rc;
	}

	*cc_soc_sw = temp;
	return rc;
}

static int fg_gen4_restore_count(void *data, u16 *buf, int length)
{
	struct fg_gen4_chip *chip = data;
	int id, rc = 0;
	u8 tmp[2];

	if (!chip)
		return -ENODEV;

	if (!buf || length > BUCKET_COUNT)
		return -EINVAL;

	for (id = 0; id < length; id++) {
		rc = fg_sram_read(&chip->fg, CYCLE_COUNT_WORD + id,
				CYCLE_COUNT_OFFSET, (u8 *)tmp, 2,
				FG_IMA_DEFAULT);
		if (rc < 0)
			pr_err("failed to read bucket %d rc=%d\n", id, rc);
		else
			*buf++ = tmp[0] | tmp[1] << 8;
	}

	return rc;
}

static int fg_gen4_store_count(void *data, u16 *buf, int id, int length)
{
	struct fg_gen4_chip *chip = data;
	int rc;

	if (!chip)
		return -ENODEV;

	if (!buf || length > BUCKET_COUNT * 2 || id < 0 ||
		id > BUCKET_COUNT - 1 || ((id * 2) + length) > BUCKET_COUNT * 2)
		return -EINVAL;

	rc = fg_sram_write(&chip->fg, CYCLE_COUNT_WORD + id, CYCLE_COUNT_OFFSET,
			(u8 *)buf, length, FG_IMA_DEFAULT);
	if (rc < 0)
		pr_err("failed to write bucket %d rc=%d\n", rc);

	return rc;
}

/* All worker and helper functions below */

#define KI_COEFF_MED_DISCHG_DEFAULT	245
@@ -828,6 +954,7 @@ static void profile_load_work(struct work_struct *work)
				profile_load_work.work);
	struct fg_gen4_chip *chip = container_of(fg,
				struct fg_gen4_chip, fg);
	int64_t nom_cap_uah;
	u8 val;
	int rc;

@@ -853,6 +980,8 @@ static void profile_load_work(struct work_struct *work)
	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) {
@@ -892,6 +1021,14 @@ static void profile_load_work(struct work_struct *work)
		pr_err("Error in configuring battery profile params, rc:%d\n",
			rc);

	rc = fg_gen4_get_nominal_capacity(chip, &nom_cap_uah);
	if (!rc) {
		rc = cap_learning_post_profile_init(chip->cl, nom_cap_uah);
		if (rc < 0)
			pr_err("Error in cap_learning_post_profile_init rc=%d\n",
				rc);
	}

	batt_psy_initialized(fg);
	fg_notify_charger(fg);

@@ -1233,7 +1370,9 @@ static irqreturn_t fg_delta_bsoc_irq_handler(int irq, void *data)
static irqreturn_t fg_delta_msoc_irq_handler(int irq, void *data)
{
	struct fg_dev *fg = data;
	struct fg_gen4_chip *chip = container_of(fg, struct fg_gen4_chip, fg);
	int rc, batt_soc, batt_temp;
	bool input_present = is_input_present(fg);

	fg_dbg(fg, FG_IRQ, "irq %d triggered\n", irq);

@@ -1242,10 +1381,17 @@ static irqreturn_t fg_delta_msoc_irq_handler(int irq, void *data)
	rc = fg_get_sram_prop(fg, FG_SRAM_BATT_SOC, &batt_soc);
	if (rc < 0)
		pr_err("Failed to read battery soc rc: %d\n", rc);
	else
		cycle_count_update(chip->counter, (u32)batt_soc >> 24,
			fg->charge_status, fg->charge_done, input_present);

	rc = fg_gen4_get_battery_temp(fg, &batt_temp);
	if (rc < 0)
		pr_err("Failed to read battery temp rc: %d\n", rc);
	else if (chip->cl->active)
		cap_learning_update(chip->cl, batt_temp, batt_soc,
			fg->charge_status, fg->charge_done, input_present,
			is_qnovo_en(fg));

	rc = fg_gen4_charge_full_update(fg);
	if (rc < 0)
@@ -1485,7 +1631,9 @@ static void status_change_work(struct work_struct *work)
{
	struct fg_dev *fg = container_of(work,
			struct fg_dev, status_change_work);
	struct fg_gen4_chip *chip = container_of(fg, struct fg_gen4_chip, fg);
	int rc, batt_soc, batt_temp;
	bool input_present, qnovo_en;

	if (!batt_psy_initialized(fg)) {
		fg_dbg(fg, FG_STATUS, "Charger not available?!\n");
@@ -1511,6 +1659,16 @@ static void status_change_work(struct work_struct *work)
		goto out;
	}

	input_present = is_input_present(fg);
	qnovo_en = is_qnovo_en(fg);
	cycle_count_update(chip->counter, (u32)batt_soc >> 24,
		fg->charge_status, fg->charge_done, input_present);

	if (fg->charge_status != fg->prev_charge_status)
		cap_learning_update(chip->cl, batt_temp, batt_soc,
			fg->charge_status, fg->charge_done, input_present,
			qnovo_en);

	rc = fg_gen4_charge_full_update(fg);
	if (rc < 0)
		pr_err("Error in charge_full_update, rc=%d\n", rc);
@@ -1879,6 +2037,32 @@ static int fg_get_time_to_empty(struct fg_dev *fg, int *val)
	return 0;
}

static const char *fg_gen4_get_cycle_counts(struct fg_gen4_chip *chip)
{
	int i, rc, len = 0;
	char *buf;

	buf = chip->counter_buf;
	for (i = 1; i <= BUCKET_COUNT; i++) {
		chip->counter->id = i;
		rc = get_cycle_count(chip->counter);
		if (rc < 0) {
			pr_err("Couldn't get cycle count rc=%d\n", rc);
			return NULL;
		}

		if (sizeof(chip->counter_buf) - len < 8) {
			pr_err("Invalid length %d\n", len);
			return NULL;
		}

		len += snprintf(buf+len, 8, "%d ", rc);
	}

	buf[len] = '\0';
	return buf;
}

static void sram_dump_work(struct work_struct *work)
{
	struct fg_dev *fg = container_of(work, struct fg_dev,
@@ -2056,6 +2240,9 @@ static int fg_psy_get_property(struct power_supply *psy,
	case POWER_SUPPLY_PROP_CHARGE_COUNTER_SHADOW:
		rc = fg_gen4_get_charge_counter_shadow(chip, &pval->intval);
		break;
	case POWER_SUPPLY_PROP_CYCLE_COUNTS:
		pval->strval = fg_gen4_get_cycle_counts(chip);
		break;
	case POWER_SUPPLY_PROP_SOC_REPORTING_READY:
		pval->intval = fg->soc_reporting_ready;
		break;
@@ -2087,9 +2274,25 @@ static int fg_psy_set_property(struct power_supply *psy,
				  enum power_supply_property psp,
				  const union power_supply_propval *pval)
{
	struct fg_gen4_chip *chip = power_supply_get_drvdata(psy);
	int rc = 0;

	switch (psp) {
	case POWER_SUPPLY_PROP_CHARGE_FULL:
		if (chip->cl->active) {
			pr_warn("Capacity learning active!\n");
			return 0;
		}
		if (pval->intval <= 0 || pval->intval > chip->cl->nom_cap_uah) {
			pr_err("charge_full is out of bounds\n");
			return -EINVAL;
		}
		mutex_lock(&chip->cl->lock);
		rc = fg_gen4_store_learned_capacity(chip, pval->intval);
		if (!rc)
			chip->cl->learned_cap_uah = pval->intval;
		mutex_unlock(&chip->cl->lock);
		break;
	default:
		break;
	}
@@ -2101,6 +2304,8 @@ static int fg_property_is_writeable(struct power_supply *psy,
						enum power_supply_property psp)
{
	switch (psp) {
	case POWER_SUPPLY_PROP_CHARGE_FULL:
		return 1;
	default:
		break;
	}
@@ -2124,6 +2329,7 @@ static enum power_supply_property fg_psy_props[] = {
	POWER_SUPPLY_PROP_CHARGE_FULL,
	POWER_SUPPLY_PROP_CHARGE_COUNTER,
	POWER_SUPPLY_PROP_CHARGE_COUNTER_SHADOW,
	POWER_SUPPLY_PROP_CYCLE_COUNTS,
	POWER_SUPPLY_PROP_SOC_REPORTING_READY,
	POWER_SUPPLY_PROP_DEBUG_BATTERY,
	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
@@ -2204,6 +2410,59 @@ static int fg_delta_bsoc_irq_en_cb(struct votable *votable, void *data,

/* All init functions below this */

static int fg_alg_init(struct fg_gen4_chip *chip)
{
	struct fg_dev *fg = &chip->fg;
	struct cycle_counter *counter;
	struct cap_learning *cl;
	int rc;

	counter = devm_kzalloc(fg->dev, sizeof(*counter), GFP_KERNEL);
	if (!counter)
		return -ENOMEM;

	counter->restore_count = fg_gen4_restore_count;
	counter->store_count = fg_gen4_store_count;
	counter->data = chip;

	rc = cycle_count_init(counter);
	if (rc < 0) {
		dev_err(fg->dev, "Error in initializing cycle counter, rc:%d\n",
			rc);
		counter->data = NULL;
		devm_kfree(fg->dev, counter);
		return rc;
	}

	chip->counter = counter;

	cl = devm_kzalloc(fg->dev, sizeof(*cl), GFP_KERNEL);
	if (!cl)
		return -ENOMEM;

	cl->cc_soc_max = CC_SOC_30BIT;
	cl->get_cc_soc = fg_gen4_get_cc_soc_sw;
	cl->prime_cc_soc = fg_gen4_prime_cc_soc_sw;
	cl->get_learned_capacity = fg_gen4_get_learned_capacity;
	cl->store_learned_capacity = fg_gen4_store_learned_capacity;
	cl->data = chip;

	rc = cap_learning_init(cl);
	if (rc < 0) {
		dev_err(fg->dev, "Error in initializing capacity learning, rc:%d\n",
			rc);
		counter->data = NULL;
		cl->data = NULL;
		devm_kfree(fg->dev, counter);
		devm_kfree(fg->dev, cl);
		return rc;
	}

	chip->cl = cl;

	return 0;
}

#define BATT_TEMP_HYST_MASK	GENMASK(3, 0)
#define BATT_TEMP_DELTA_MASK	GENMASK(7, 4)
#define BATT_TEMP_DELTA_SHIFT	4
@@ -2419,6 +2678,12 @@ static int fg_gen4_hw_init(struct fg_gen4_chip *chip)
		}
	}

	rc = restore_cycle_count(chip->counter);
	if (rc < 0) {
		pr_err("Error in restoring cycle_count, rc=%d\n", rc);
		return rc;
	}

	return 0;
}

@@ -2695,6 +2960,48 @@ static int fg_gen4_parse_dt(struct fg_gen4_chip *chip)
	chip->dt.force_load_profile = of_property_read_bool(node,
					"qcom,fg-force-load-profile");

	rc = of_property_read_u32(node, "qcom,cl-start-capacity", &temp);
	if (rc < 0)
		chip->cl->dt.start_soc = DEFAULT_CL_START_SOC;
	else
		chip->cl->dt.start_soc = temp;

	rc = of_property_read_u32(node, "qcom,cl-min-temp", &temp);
	if (rc < 0)
		chip->cl->dt.min_temp = DEFAULT_CL_MIN_TEMP_DECIDEGC;
	else
		chip->cl->dt.min_temp = temp;

	rc = of_property_read_u32(node, "qcom,cl-max-temp", &temp);
	if (rc < 0)
		chip->cl->dt.max_temp = DEFAULT_CL_MAX_TEMP_DECIDEGC;
	else
		chip->cl->dt.max_temp = temp;

	rc = of_property_read_u32(node, "qcom,cl-max-increment", &temp);
	if (rc < 0)
		chip->cl->dt.max_cap_inc = DEFAULT_CL_MAX_INC_DECIPERC;
	else
		chip->cl->dt.max_cap_inc = temp;

	rc = of_property_read_u32(node, "qcom,cl-max-decrement", &temp);
	if (rc < 0)
		chip->cl->dt.max_cap_dec = DEFAULT_CL_MAX_DEC_DECIPERC;
	else
		chip->cl->dt.max_cap_dec = temp;

	rc = of_property_read_u32(node, "qcom,cl-min-limit", &temp);
	if (rc < 0)
		chip->cl->dt.min_cap_limit = DEFAULT_CL_MIN_LIM_DECIPERC;
	else
		chip->cl->dt.min_cap_limit = temp;

	rc = of_property_read_u32(node, "qcom,cl-max-limit", &temp);
	if (rc < 0)
		chip->cl->dt.max_cap_limit = DEFAULT_CL_MAX_LIM_DECIPERC;
	else
		chip->cl->dt.max_cap_limit = temp;

	rc = of_property_read_u32(node, "qcom,fg-batt-temp-hot", &temp);
	if (rc < 0)
		chip->dt.batt_temp_hot_thresh = -EINVAL;
@@ -2819,6 +3126,13 @@ static int fg_gen4_probe(struct platform_device *pdev)
		goto exit;
	}

	rc = fg_alg_init(chip);
	if (rc < 0) {
		dev_err(fg->dev, "Error in alg_init, rc:%d\n",
			rc);
		goto exit;
	}

	rc = fg_gen4_parse_dt(chip);
	if (rc < 0) {
		dev_err(fg->dev, "Error in reading DT parameters, rc:%d\n",
@@ -2930,13 +3244,13 @@ static void fg_gen4_shutdown(struct platform_device *pdev)
	struct fg_dev *fg = &chip->fg;
	int rc, bsoc;

	if (fg->charge_full) {
	rc = fg_get_sram_prop(fg, FG_SRAM_BATT_SOC, &bsoc);
	if (rc < 0) {
		pr_err("Error in getting BATT_SOC, rc=%d\n", rc);
		return;
	}

	if (fg->charge_full) {
		/* We need 2 most significant bytes here */
		bsoc = (u32)bsoc >> 16;

@@ -2946,6 +3260,13 @@ static void fg_gen4_shutdown(struct platform_device *pdev)
			return;
		}
	}

	/*
	 * Charging status doesn't matter when the device shuts down and we
	 * have to treat this as charge done. Hence pass charge_done as true.
	 */
	cycle_count_update(chip->counter, (u32)bsoc >> 24,
		POWER_SUPPLY_STATUS_NOT_CHARGING, true, is_input_present(fg));
}

static int fg_gen4_suspend(struct device *dev)