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

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

Merge "power: qpnp-fg: modify the cycle counter implementation"

parents 12d209ce e96212bc
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>;
+1 −0
Original line number Diff line number Diff line
@@ -238,6 +238,7 @@ static struct device_attribute power_supply_attrs[] = {
	POWER_SUPPLY_ATTR(dp_dm),
	POWER_SUPPLY_ATTR(input_current_limited),
	POWER_SUPPLY_ATTR(rerun_aicl),
	POWER_SUPPLY_ATTR(cycle_count_id),
	/* Local extensions of type int64_t */
	POWER_SUPPLY_ATTR(charge_counter_ext),
	/* Properties of type `const char *' */
+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);
+1 −0
Original line number Diff line number Diff line
@@ -198,6 +198,7 @@ enum power_supply_property {
	POWER_SUPPLY_PROP_DP_DM,
	POWER_SUPPLY_PROP_INPUT_CURRENT_LIMITED,
	POWER_SUPPLY_PROP_RERUN_AICL,
	POWER_SUPPLY_PROP_CYCLE_COUNT_ID,
	/* Local extensions of type int64_t */
	POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT,
	/* Properties of type `const char *' */