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

Commit e96212bc authored by Subbaraman Narayanamurthy's avatar Subbaraman Narayanamurthy
Browse files

power: qpnp-fg: modify the cycle counter implementation



Currently, cycle counter gets incremented only when the charging
happens within the specified low and high soc thresholds. Instead
of that, implement the bucket based cycle counter algorithm which
increments the counters based on multiple charging buckets. This
helps to study the charging pattern.

For example, with a bucket count of 8, if the user charges from 5
to 30% and removes the charger, cycle counter for buckets 1 and 2
would get incremented. This bucket based cycle counters will be
available to the userspace via cycle_count property after the
cycle_count_id property is set to specify which bucket needs to
be read.

For example, to read cycle count for bucket 4, following helps.

echo 4 > /sys/class/power_supply/bms/cycle_count_id
cat /sys/class/power_supply/bms/cycle_count

Since this algorithm operates on all the SOC levels (0-100%),
there is no need for low and high SOC thresolds. Remove them.

CRs-Fixed: 847140
Change-Id: I1c0840f01693909586cafb3d51685637f3a6d2ba
Signed-off-by: default avatarSubbaraman Narayanamurthy <subbaram@codeaurora.org>
parent e698274f
Loading
Loading
Loading
Loading
+0 −6
Original line number Diff line number Diff line
@@ -109,12 +109,6 @@ Parent node optional properties:
					present, then the following properties
					to specify low and high soc thresholds
					should be defined.
- qcom,cycle-counter-low-soc:		Low SOC threshold which will be compared
					against the battery SOC before starting
					the cycle counter algorithm.
- qcom,cycle-counter-high-soc:		High SOC threshold which will be compared
					against the battery SOC before incrementing
					the cycle counter.
- qcom,capacity-learning-on:		A boolean property to have the fuel
					gauge driver attempt to learn the
					battery capacity when charging. Takes
+0 −2
Original line number Diff line number Diff line
@@ -314,8 +314,6 @@
			qcom,fg-iterm-ma = <100>;
			qcom,fg-chg-iterm-ma = <100>;
			qcom,cycle-counter-en;
			qcom,cycle-counter-low-soc = <15>;
			qcom,cycle-counter-high-soc = <85>;
			qcom,capacity-learning-on;
			qcom,fg-cc-cv-threshold-mv = <4340>;
			qcom,pmic-revid = <&pmi8994_revid>;
+159 −124
Original line number Diff line number Diff line
@@ -70,6 +70,8 @@

#define QPNP_FG_DEV_NAME "qcom,qpnp-fg"
#define MEM_IF_TIMEOUT_MS	5000
#define BUCKET_COUNT		8
#define BUCKET_SOC_PCT		(256 / BUCKET_COUNT)

#define BCL_MA_TO_ADC(_current, _adc_val) {		\
	_adc_val = (u8)((_current) * 100 / 976);	\
@@ -140,6 +142,15 @@ struct fg_rslow_data {
	struct mutex		lock;
};

struct fg_cyc_ctr_data {
	bool			en;
	bool			started[BUCKET_COUNT];
	u16			count[BUCKET_COUNT];
	u8			last_soc[BUCKET_COUNT];
	int			id;
	struct mutex		lock;
};

/* FG_MEMIF setting index */
enum fg_mem_setting_index {
	FG_MEM_SOFT_COLD = 0,
@@ -371,7 +382,6 @@ struct fg_chip {
	struct completion	batt_id_avail;
	struct power_supply	bms_psy;
	struct mutex		rw_lock;
	struct mutex		cyc_ctr_lock;
	struct mutex		sysfs_restart_lock;
	struct work_struct	batt_profile_init;
	struct work_struct	dump_sram;
@@ -399,8 +409,6 @@ struct fg_chip {
	bool			power_supply_registered;
	bool			sw_rbias_ctrl;
	bool			use_thermal_coefficients;
	bool			cyc_ctr_en;
	bool			cyc_ctr_started;
	bool			esr_strict_filter;
	bool			soc_empty;
	bool			charge_done;
@@ -412,9 +420,6 @@ struct fg_chip {
	struct delayed_work	check_empty_work;
	char			*batt_profile;
	u8			thermal_coefficients[THERMAL_COEFF_N_BYTES];
	u16			cycle_counter;
	u32			cyc_ctr_low_soc;
	u32			cyc_ctr_hi_soc;
	u32			cc_cv_threshold_mv;
	unsigned int		batt_profile_len;
	unsigned int		batt_max_voltage_uv;
@@ -422,7 +427,6 @@ struct fg_chip {
	const char		*batt_psy_name;
	unsigned long		last_sram_update_time;
	unsigned long		last_temp_update_time;
	int			cyc_ctr_freq;
	int64_t			ocv_coeffs[12];
	int64_t			cutoff_voltage;
	int			evaluation_current;
@@ -440,6 +444,8 @@ struct fg_chip {
	struct work_struct	fg_cap_learning_work;
	/* rslow compensation */
	struct fg_rslow_data	rslow_comp;
	/* cycle counter */
	struct fg_cyc_ctr_data	cyc_ctr;
	/* interleaved memory access */
	u16			*offset;
	bool			ima_supported;
@@ -2101,107 +2107,145 @@ static int fg_get_vbatt_status(struct fg_chip *chip, bool *vbatt_low_sts)
	return rc;
}

#define BATT_CYCLE_NUMBER_REG		0x58C
#define BATT_CYCLE_OFFSET		3
static int fg_inc_store_cycle_ctr(struct fg_chip *chip)
#define BATT_CYCLE_NUMBER_REG		0x5E8
#define BATT_CYCLE_OFFSET		0
static void restore_cycle_counter(struct fg_chip *chip)
{
	int rc = 0;
	u16 cyc_count;
	u8 reg[2];
	int rc = 0, i, address;
	u8 data[2];

	cyc_count = chip->cycle_counter;
	cyc_count++;
	reg[0] = cyc_count & 0xFF;
	reg[1] = cyc_count >> 8;
	rc = fg_mem_write(chip, reg, BATT_CYCLE_NUMBER_REG, 2,
	fg_mem_lock(chip);
	for (i = 0; i < BUCKET_COUNT; i++) {
		address = BATT_CYCLE_NUMBER_REG + i * 2;
		rc = fg_mem_read(chip, (u8 *)&data, address, 2,
				BATT_CYCLE_OFFSET, 0);
		if (rc)
			pr_err("Failed to read BATT_CYCLE_NUMBER[%d] rc: %d\n",
				i, rc);
		else
			chip->cyc_ctr.count[i] = data[0] | data[1] << 8;
	}
	fg_mem_release(chip);
}

static void clear_cycle_counter(struct fg_chip *chip)
{
	int rc = 0, len, i;

	if (!chip->cyc_ctr.en)
		return;

	len = sizeof(chip->cyc_ctr.count);
	memset(chip->cyc_ctr.count, 0, len);
	for (i = 0; i < BUCKET_COUNT; i++) {
		chip->cyc_ctr.started[i] = false;
		chip->cyc_ctr.last_soc[i] = 0;
	}
	rc = fg_mem_write(chip, (u8 *)&chip->cyc_ctr.count,
			BATT_CYCLE_NUMBER_REG, len,
			BATT_CYCLE_OFFSET, 0);
	if (rc)
		pr_err("failed to write BATT_CYCLE_NUMBER rc=%d\n", rc);
}

static int fg_inc_store_cycle_ctr(struct fg_chip *chip, int bucket)
{
	int rc = 0, address;
	u16 cyc_count;
	u8 data[2];

	if (bucket < 0 || (bucket > BUCKET_COUNT - 1))
		return 0;

	cyc_count = chip->cyc_ctr.count[bucket];
	cyc_count++;
	data[0] = cyc_count & 0xFF;
	data[1] = cyc_count >> 8;

	address = BATT_CYCLE_NUMBER_REG + bucket * 2;

	rc = fg_mem_write(chip, data, address, 2, BATT_CYCLE_OFFSET, 0);
	if (rc)
		pr_err("failed to write BATT_CYCLE_NUMBER[%d] rc=%d\n",
			bucket, rc);
	else
		chip->cycle_counter = cyc_count;
		chip->cyc_ctr.count[bucket] = cyc_count;
	return rc;
}

#define CYCLE_CTR_UPDATE_FREQUENCY	2
static void update_cycle_count(struct work_struct *work)
{
	int rc = 0;
	u8 reg[3];
	int rc = 0, bucket, i;
	u8 reg[3], batt_soc;
	struct fg_chip *chip = container_of(work,
				struct fg_chip,
				cycle_count_work);

	chip->cyc_ctr_freq++;
	if (chip->cyc_ctr_freq % CYCLE_CTR_UPDATE_FREQUENCY) {
		if (chip->status == POWER_SUPPLY_STATUS_CHARGING) {
	mutex_lock(&chip->cyc_ctr.lock);
	rc = fg_mem_read(chip, reg, BATTERY_SOC_REG, 3,
			BATTERY_SOC_OFFSET, 0);
	if (rc) {
				pr_err("Failed to read battery soc rc: %d\n",
					rc);
				return;
		pr_err("Failed to read battery soc rc: %d\n", rc);
		goto out;
	}
	batt_soc = reg[2];

			/* Check the MSB of battery SOC against the
			 * Low and High SOC thresholds specified for
			 * cycle counter algorithm. Since the low and
			 * high SOC thresholds are already converted
			 * to the scale of 0-255, they can be used
			 * as is against the battery SOC.
	if (chip->status == POWER_SUPPLY_STATUS_CHARGING) {
		/* Find out which bucket the SOC falls in */
		bucket = batt_soc / BUCKET_SOC_PCT;

		if (fg_debug_mask & FG_STATUS)
			pr_info("batt_soc: %x bucket: %d\n", reg[2], bucket);

		/*
		 * If we've started counting for the previous bucket,
		 * then store the counter for that bucket if the
		 * counter for current bucket is getting started.
		 */
			mutex_lock(&chip->cyc_ctr_lock);
			if ((reg[2] < chip->cyc_ctr_low_soc) &&
					!chip->cyc_ctr_started) {
				chip->cyc_ctr_started = true;
			} else if ((reg[2] > chip->cyc_ctr_hi_soc) &&
					chip->cyc_ctr_started) {
				rc = fg_inc_store_cycle_ctr(chip);
				if (rc)
		if (bucket > 0 && chip->cyc_ctr.started[bucket - 1] &&
			!chip->cyc_ctr.started[bucket]) {
			rc = fg_inc_store_cycle_ctr(chip, bucket - 1);
			if (rc) {
				pr_err("Error in storing cycle_ctr rc: %d\n",
					rc);
				else
					chip->cyc_ctr_started = false;
			}
			mutex_unlock(&chip->cyc_ctr_lock);
				goto out;
			} else {
			/* There is a slim chance where the cycle counter
			 * algorithm might have started and the charger
			 * got disconnected before the delta SOC interrupt
			 * had scheduled this work again. Read the battery
			 * SOC again in such cases to determine whether the
			 * cycle counter needs to be stored.
			 */
			if (chip->cyc_ctr_started) {
				rc = fg_mem_read(chip, reg, BATTERY_SOC_REG, 3,
						BATTERY_SOC_OFFSET, 0);
				if (rc) {
					pr_err("Failed to read battery soc rc: %d\n",
						rc);
					return;
				chip->cyc_ctr.started[bucket - 1] = false;
				chip->cyc_ctr.last_soc[bucket - 1] = 0;
			}
				mutex_lock(&chip->cyc_ctr_lock);
				if (reg[2] > chip->cyc_ctr_hi_soc) {
					rc = fg_inc_store_cycle_ctr(chip);
		}
		if (!chip->cyc_ctr.started[bucket]) {
			chip->cyc_ctr.started[bucket] = true;
			chip->cyc_ctr.last_soc[bucket] = batt_soc;
		}
	} else {
		for (i = 0; i < BUCKET_COUNT; i++) {
			if (chip->cyc_ctr.started[i] &&
				batt_soc > chip->cyc_ctr.last_soc[i]) {
				rc = fg_inc_store_cycle_ctr(chip, i);
				if (rc)
					pr_err("Error in storing cycle_ctr rc: %d\n",
						rc);
				chip->cyc_ctr.last_soc[i] = 0;
			}
				chip->cyc_ctr_started = false;
				mutex_unlock(&chip->cyc_ctr_lock);
			}
			chip->cyc_ctr.started[i] = false;
		}
	} else {
		chip->cyc_ctr_freq = 0;
	}
out:
	mutex_unlock(&chip->cyc_ctr.lock);
}

static int fg_get_cycle_count(struct fg_chip *chip)
{
	int cyc_count;
	mutex_lock(&chip->cyc_ctr_lock);
	cyc_count = chip->cycle_counter;
	mutex_unlock(&chip->cyc_ctr_lock);
	return cyc_count;
	int count;

	if (!chip->cyc_ctr.en)
		return 0;

	mutex_lock(&chip->cyc_ctr.lock);
	count = chip->cyc_ctr.count[chip->cyc_ctr.id - 1];
	mutex_unlock(&chip->cyc_ctr.lock);
	return count;
}

static void half_float_to_buffer(int64_t uval, u8 *buffer)
@@ -2440,6 +2484,7 @@ static enum power_supply_property fg_power_props[] = {
	POWER_SUPPLY_PROP_ESR_COUNT,
	POWER_SUPPLY_PROP_VOLTAGE_MIN,
	POWER_SUPPLY_PROP_CYCLE_COUNT,
	POWER_SUPPLY_PROP_CYCLE_COUNT_ID,
};

static int fg_power_get_property(struct power_supply *psy,
@@ -2492,6 +2537,9 @@ static int fg_power_get_property(struct power_supply *psy,
	case POWER_SUPPLY_PROP_CYCLE_COUNT:
		val->intval = fg_get_cycle_count(chip);
		break;
	case POWER_SUPPLY_PROP_CYCLE_COUNT_ID:
		val->intval = chip->cyc_ctr.id;
		break;
	case POWER_SUPPLY_PROP_RESISTANCE_ID:
		val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ID);
		break;
@@ -2990,6 +3038,8 @@ static void status_change_work(struct work_struct *work)
			schedule_delayed_work(&chip->update_sram_data,
				msecs_to_jiffies(0));
		}
		if (chip->cyc_ctr.en)
			schedule_work(&chip->cycle_count_work);
	}
}

@@ -3030,6 +3080,9 @@ static int fg_power_set_property(struct power_supply *psy,
			schedule_work(&chip->set_resume_soc_work);
		}
		break;
	case POWER_SUPPLY_PROP_CYCLE_COUNT_ID:
		chip->cyc_ctr.id = val->intval;
		break;
	default:
		return -EINVAL;
	};
@@ -3043,6 +3096,7 @@ static int fg_property_is_writeable(struct power_supply *psy,
	switch (psp) {
	case POWER_SUPPLY_PROP_COOL_TEMP:
	case POWER_SUPPLY_PROP_WARM_TEMP:
	case POWER_SUPPLY_PROP_CYCLE_COUNT_ID:
		return 1;
	default:
		break;
@@ -3207,10 +3261,11 @@ static irqreturn_t fg_batt_missing_irq_handler(int irq, void *_chip)
		chip->battery_missing = true;
		chip->profile_loaded = false;
		chip->batt_type = missing_batt_type;
		mutex_lock(&chip->cyc_ctr_lock);
		chip->cycle_counter = 0;
		chip->cyc_ctr_started = false;
		mutex_unlock(&chip->cyc_ctr_lock);
		mutex_lock(&chip->cyc_ctr.lock);
		if (fg_debug_mask & FG_IRQS)
			pr_info("battery missing, clearing cycle counters\n");
		clear_cycle_counter(chip);
		mutex_unlock(&chip->cyc_ctr.lock);
	} else {
		if (!chip->use_otp_profile) {
			reinit_completion(&chip->batt_id_avail);
@@ -3289,7 +3344,7 @@ static irqreturn_t fg_soc_irq_handler(int irq, void *_chip)
			chip->rslow_comp.chg_rslow_comp_c1 > 0 &&
			chip->rslow_comp.chg_rslow_comp_c2 > 0)
		schedule_work(&chip->rslow_comp_work);
	if (chip->cyc_ctr_en)
	if (chip->cyc_ctr.en)
		schedule_work(&chip->cycle_count_work);
	schedule_work(&chip->update_esr_work);
	return IRQ_HANDLED;
@@ -3960,17 +4015,19 @@ wait:
			< settings[FG_MEM_VBAT_EST_DIFF].value * 1000;
	profiles_same = memcmp(chip->batt_profile, data,
					PROFILE_COMPARE_LEN) == 0;
	if (reg & PROFILE_INTEGRITY_BIT)
	if (reg & PROFILE_INTEGRITY_BIT) {
		fg_cap_learning_load_data(chip);
	if ((reg & PROFILE_INTEGRITY_BIT) && vbat_in_range
			&& !fg_is_batt_empty(chip)
			&& profiles_same) {
		if (vbat_in_range && !fg_is_batt_empty(chip) && profiles_same) {
			if (fg_debug_mask & FG_STATUS)
				pr_info("Battery profiles same, using default\n");
			if (fg_est_dump)
				schedule_work(&chip->dump_sram);
			goto done;
		}
	} else {
		pr_info("Battery profile not same, clearing cycle counters\n");
		clear_cycle_counter(chip);
	}
	if (fg_est_dump)
		dump_sram(&chip->dump_sram);
	if ((fg_debug_mask & FG_STATUS) && !vbat_in_range)
@@ -4280,26 +4337,10 @@ static int fg_of_init(struct fg_chip *chip)
	chip->sw_rbias_ctrl = of_property_read_bool(node,
				"qcom,sw-rbias-control");

	chip->cyc_ctr_en = of_property_read_bool(node,
	chip->cyc_ctr.en = of_property_read_bool(node,
				"qcom,cycle-counter-en");
	if (chip->cyc_ctr_en) {
		rc = of_property_read_u32(node, "qcom,cycle-counter-low-soc",
						&chip->cyc_ctr_low_soc);
		rc |= of_property_read_u32(node, "qcom,cycle-counter-high-soc",
						&chip->cyc_ctr_hi_soc);
		if (rc) {
			pr_err("Error in reading cycle-counter-low/high soc rc: %d\n",
				rc);
			chip->cyc_ctr_en = false;
		} else if ((chip->cyc_ctr_hi_soc <= 0) ||
				(chip->cyc_ctr_low_soc <= 0)) {
			pr_err("Couldn't find valid low/high soc\n");
			chip->cyc_ctr_en = false;
		}
		chip->cyc_ctr_low_soc = soc_to_setpoint(chip->cyc_ctr_low_soc);
		chip->cyc_ctr_hi_soc = soc_to_setpoint(chip->cyc_ctr_hi_soc);
		chip->cycle_counter = 0;
	}
	if (chip->cyc_ctr.en)
		chip->cyc_ctr.id = 1;

	return rc;
}
@@ -4501,7 +4542,7 @@ static void fg_cleanup(struct fg_chip *chip)
	power_supply_unregister(&chip->bms_psy);
	mutex_destroy(&chip->rslow_comp.lock);
	mutex_destroy(&chip->rw_lock);
	mutex_destroy(&chip->cyc_ctr_lock);
	mutex_destroy(&chip->cyc_ctr.lock);
	mutex_destroy(&chip->learning_data.learning_lock);
	mutex_destroy(&chip->sysfs_restart_lock);
	wakeup_source_trash(&chip->resume_soc_wakeup_source.source);
@@ -5103,14 +5144,8 @@ static int fg_8994_hw_init(struct fg_chip *chip)
	data[1] = KI_COEFF_PRED_FULL_4_0_MSB;
	fg_mem_write(chip, data, KI_COEFF_PRED_FULL_ADDR, 2, 2, 0);
	/* Read the cycle counter back from FG SRAM */
	if (chip->cyc_ctr_en) {
		rc = fg_mem_read(chip, data, BATT_CYCLE_NUMBER_REG, 2,
				BATT_CYCLE_OFFSET, 0);
		if (rc)
			pr_err("Failed to read BATT_CYCLE_NUMBER rc: %d\n", rc);
		else
			chip->cycle_counter = data[0] | data[1] << 8;
	}
	if (chip->cyc_ctr.en)
		restore_cycle_counter(chip);

	esr_value = ESR_DEFAULT_VALUE;
	rc = fg_mem_write(chip, (u8 *)&esr_value, MAXRSCHANGE_REG, 8,
@@ -5327,7 +5362,7 @@ static int fg_probe(struct spmi_device *spmi)
	wakeup_source_init(&chip->resume_soc_wakeup_source.source,
			"qpnp_fg_set_resume_soc");
	mutex_init(&chip->rw_lock);
	mutex_init(&chip->cyc_ctr_lock);
	mutex_init(&chip->cyc_ctr.lock);
	mutex_init(&chip->learning_data.learning_lock);
	mutex_init(&chip->rslow_comp.lock);
	mutex_init(&chip->sysfs_restart_lock);
@@ -5501,7 +5536,7 @@ cancel_work:
of_init_fail:
	mutex_destroy(&chip->rslow_comp.lock);
	mutex_destroy(&chip->rw_lock);
	mutex_destroy(&chip->cyc_ctr_lock);
	mutex_destroy(&chip->cyc_ctr.lock);
	mutex_destroy(&chip->learning_data.learning_lock);
	mutex_destroy(&chip->sysfs_restart_lock);
	wakeup_source_trash(&chip->resume_soc_wakeup_source.source);