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

Commit dd73629c authored by Xiaozhe Shi's avatar Xiaozhe Shi
Browse files

power: qpnp-bms: increase resolution of temperature lookups



Currently, the BMS driver rounds to the nearest degree when doing
table lookups on FCC, rbatt, and OCV. This is done with no
interpolation.

This can be harmful because in low temperatures, a change in 1/10th of a
degree C can cause very big swings in UUC due to the rbatt lookup
crossing a threshold. There has been cases seen in -20 degC testing
where when SoC goes from -3.9C to -4C, a shift of 80 mOhms of rbatt was
observed. This increased the UUC by 8% and caused large SoC swings.

Fix this by interpolating between the temperature ranges more smoothly.

Change-Id: I21ba32fd56b802c5a68f5c193f0904e445b6a798
Signed-off-by: default avatarXiaozhe Shi <xiaozhes@codeaurora.org>
parent b2e97d15
Loading
Loading
Loading
Loading
+49 −48
Original line number Diff line number Diff line
@@ -34,32 +34,33 @@ int is_between(int left, int right, int value)
	return 0;
}

static int interpolate_single_lut(struct single_row_lut *lut, int x)
static int interpolate_single_lut_scaled(struct single_row_lut *lut,
						int x, int scale)
{
	int i, result;

	if (x < lut->x[0]) {
	if (x < lut->x[0] * scale) {
		pr_debug("x %d less than known range return y = %d lut = %pS\n",
							x, lut->y[0], lut);
		return lut->y[0];
	}
	if (x > lut->x[lut->cols - 1]) {
	if (x > lut->x[lut->cols - 1] * scale) {
		pr_debug("x %d more than known range return y = %d lut = %pS\n",
						x, lut->y[lut->cols - 1], lut);
		return lut->y[lut->cols - 1];
	}

	for (i = 0; i < lut->cols; i++)
		if (x <= lut->x[i])
		if (x <= lut->x[i] * scale)
			break;
	if (x == lut->x[i]) {
	if (x == lut->x[i] * scale) {
		result = lut->y[i];
	} else {
		result = linear_interpolate(
			lut->y[i - 1],
			lut->x[i - 1],
			lut->x[i - 1] * scale,
			lut->y[i],
			lut->x[i],
			lut->x[i] * scale,
			x);
	}
	return result;
@@ -67,9 +68,9 @@ static int interpolate_single_lut(struct single_row_lut *lut, int x)

int interpolate_fcc(struct single_row_lut *fcc_temp_lut, int batt_temp)
{
	/* batt_temp is in tenths of degC - convert it to degC for lookups */
	batt_temp = batt_temp/10;
	return interpolate_single_lut(fcc_temp_lut, batt_temp);
	return interpolate_single_lut_scaled(fcc_temp_lut,
						batt_temp,
						DEGC_SCALE);
}

int interpolate_scalingfactor_fcc(struct single_row_lut *fcc_sf_lut,
@@ -80,7 +81,7 @@ int interpolate_scalingfactor_fcc(struct single_row_lut *fcc_sf_lut,
	 * that case return 100%
	 */
	if (fcc_sf_lut)
		return interpolate_single_lut(fcc_sf_lut, cycles);
		return interpolate_single_lut_scaled(fcc_sf_lut, cycles, 1);
	else
		return 100;
}
@@ -123,15 +124,15 @@ int interpolate_scalingfactor(struct sf_lut *sf_lut, int row_entry, int pc)
		}
	}

	if (row_entry < sf_lut->row_entries[0])
		row_entry = sf_lut->row_entries[0];
	if (row_entry > sf_lut->row_entries[cols - 1])
		row_entry = sf_lut->row_entries[cols - 1];
	if (row_entry < sf_lut->row_entries[0] * DEGC_SCALE)
		row_entry = sf_lut->row_entries[0] * DEGC_SCALE;
	if (row_entry > sf_lut->row_entries[cols - 1] * DEGC_SCALE)
		row_entry = sf_lut->row_entries[cols - 1] * DEGC_SCALE;

	for (i = 0; i < cols; i++)
		if (row_entry <= sf_lut->row_entries[i])
		if (row_entry <= sf_lut->row_entries[i] * DEGC_SCALE)
			break;
	if (row_entry == sf_lut->row_entries[i]) {
	if (row_entry == sf_lut->row_entries[i] * DEGC_SCALE) {
		scalefactor = linear_interpolate(
				sf_lut->sf[row1][i],
				sf_lut->percent[row1],
@@ -143,16 +144,16 @@ int interpolate_scalingfactor(struct sf_lut *sf_lut, int row_entry, int pc)

	scalefactorrow1 = linear_interpolate(
				sf_lut->sf[row1][i - 1],
				sf_lut->row_entries[i - 1],
				sf_lut->row_entries[i - 1] * DEGC_SCALE,
				sf_lut->sf[row1][i],
				sf_lut->row_entries[i],
				sf_lut->row_entries[i] * DEGC_SCALE,
				row_entry);

	scalefactorrow2 = linear_interpolate(
				sf_lut->sf[row2][i - 1],
				sf_lut->row_entries[i - 1],
				sf_lut->row_entries[i - 1] * DEGC_SCALE,
				sf_lut->sf[row2][i],
				sf_lut->row_entries[i],
				sf_lut->row_entries[i] * DEGC_SCALE,
				row_entry);

	scalefactor = linear_interpolate(
@@ -167,7 +168,7 @@ int interpolate_scalingfactor(struct sf_lut *sf_lut, int row_entry, int pc)

/* get ocv given a soc  -- reverse lookup */
int interpolate_ocv(struct pc_temp_ocv_lut *pc_temp_ocv,
				int batt_temp_degc, int pc)
				int batt_temp, int pc)
{
	int i, ocvrow1, ocvrow2, ocv, rows, cols;
	int row1 = 0;
@@ -198,15 +199,15 @@ int interpolate_ocv(struct pc_temp_ocv_lut *pc_temp_ocv,
		}
	}

	if (batt_temp_degc < pc_temp_ocv->temp[0])
		batt_temp_degc = pc_temp_ocv->temp[0];
	if (batt_temp_degc > pc_temp_ocv->temp[cols - 1])
		batt_temp_degc = pc_temp_ocv->temp[cols - 1];
	if (batt_temp < pc_temp_ocv->temp[0] * DEGC_SCALE)
		batt_temp = pc_temp_ocv->temp[0] * DEGC_SCALE;
	if (batt_temp > pc_temp_ocv->temp[cols - 1] * DEGC_SCALE)
		batt_temp = pc_temp_ocv->temp[cols - 1] * DEGC_SCALE;

	for (i = 0; i < cols; i++)
		if (batt_temp_degc <= pc_temp_ocv->temp[i])
		if (batt_temp <= pc_temp_ocv->temp[i] * DEGC_SCALE)
			break;
	if (batt_temp_degc == pc_temp_ocv->temp[i]) {
	if (batt_temp == pc_temp_ocv->temp[i] * DEGC_SCALE) {
		ocv = linear_interpolate(
				pc_temp_ocv->ocv[row1][i],
				pc_temp_ocv->percent[row1],
@@ -218,17 +219,17 @@ int interpolate_ocv(struct pc_temp_ocv_lut *pc_temp_ocv,

	ocvrow1 = linear_interpolate(
				pc_temp_ocv->ocv[row1][i - 1],
				pc_temp_ocv->temp[i - 1],
				pc_temp_ocv->temp[i - 1] * DEGC_SCALE,
				pc_temp_ocv->ocv[row1][i],
				pc_temp_ocv->temp[i],
				batt_temp_degc);
				pc_temp_ocv->temp[i] * DEGC_SCALE,
				batt_temp);

	ocvrow2 = linear_interpolate(
				pc_temp_ocv->ocv[row2][i - 1],
				pc_temp_ocv->temp[i - 1],
				pc_temp_ocv->temp[i - 1] * DEGC_SCALE,
				pc_temp_ocv->ocv[row2][i],
				pc_temp_ocv->temp[i],
				batt_temp_degc);
				pc_temp_ocv->temp[i] * DEGC_SCALE,
				batt_temp);

	ocv = linear_interpolate(
				ocvrow1,
@@ -241,26 +242,26 @@ int interpolate_ocv(struct pc_temp_ocv_lut *pc_temp_ocv,
}

int interpolate_pc(struct pc_temp_ocv_lut *pc_temp_ocv,
				int batt_temp_degc, int ocv)
				int batt_temp, int ocv)
{
	int i, j, pcj, pcj_minus_one, pc;
	int rows = pc_temp_ocv->rows;
	int cols = pc_temp_ocv->cols;

	if (batt_temp_degc < pc_temp_ocv->temp[0]) {
		pr_debug("batt_temp %d < known temp range\n", batt_temp_degc);
		batt_temp_degc = pc_temp_ocv->temp[0];
	if (batt_temp < pc_temp_ocv->temp[0] * DEGC_SCALE) {
		pr_debug("batt_temp %d < known temp range\n", batt_temp);
		batt_temp = pc_temp_ocv->temp[0] * DEGC_SCALE;
	}

	if (batt_temp_degc > pc_temp_ocv->temp[cols - 1]) {
		pr_debug("batt_temp %d > known temp range\n", batt_temp_degc);
		batt_temp_degc = pc_temp_ocv->temp[cols - 1];
	if (batt_temp > pc_temp_ocv->temp[cols - 1] * DEGC_SCALE) {
		pr_debug("batt_temp %d > known temp range\n", batt_temp);
		batt_temp = pc_temp_ocv->temp[cols - 1] * DEGC_SCALE;
	}

	for (j = 0; j < cols; j++)
		if (batt_temp_degc <= pc_temp_ocv->temp[j])
		if (batt_temp <= pc_temp_ocv->temp[j] * DEGC_SCALE)
			break;
	if (batt_temp_degc == pc_temp_ocv->temp[j]) {
	if (batt_temp == pc_temp_ocv->temp[j] * DEGC_SCALE) {
		/* found an exact match for temp in the table */
		if (ocv >= pc_temp_ocv->ocv[0][j])
			return pc_temp_ocv->percent[0];
@@ -282,7 +283,7 @@ int interpolate_pc(struct pc_temp_ocv_lut *pc_temp_ocv,
	}

	/*
	 * batt_temp_degc is within temperature for
	 * batt_temp is within temperature for
	 * column j-1 and j
	 */
	if (ocv >= pc_temp_ocv->ocv[0][j])
@@ -318,10 +319,10 @@ int interpolate_pc(struct pc_temp_ocv_lut *pc_temp_ocv,
		if (pcj && pcj_minus_one) {
			pc = linear_interpolate(
				pcj_minus_one,
				pc_temp_ocv->temp[j-1],
				pc_temp_ocv->temp[j-1] * DEGC_SCALE,
				pcj,
				pc_temp_ocv->temp[j],
				batt_temp_degc);
				pc_temp_ocv->temp[j] * DEGC_SCALE,
				batt_temp);
			return pc;
		}
	}
@@ -333,6 +334,6 @@ int interpolate_pc(struct pc_temp_ocv_lut *pc_temp_ocv,
		return pcj_minus_one;

	pr_debug("%d ocv wasn't found for temp %d in the LUT returning 100%%\n",
							ocv, batt_temp_degc);
							ocv, batt_temp);
	return 100;
}
+7 −11
Original line number Diff line number Diff line
@@ -928,16 +928,15 @@ static void reset_for_new_battery(struct qpnp_bms_chip *chip, int batt_temp)
static int find_ocv_for_pc(struct qpnp_bms_chip *chip, int batt_temp, int pc)
{
	int new_pc;
	int batt_temp_degc = batt_temp / 10;
	int ocv_mv;
	int delta_mv = 5;
	int max_spin_count;
	int count = 0;
	int sign, new_sign;

	ocv_mv = interpolate_ocv(chip->pc_temp_ocv_lut, batt_temp_degc, pc);
	ocv_mv = interpolate_ocv(chip->pc_temp_ocv_lut, batt_temp, pc);

	new_pc = interpolate_pc(chip->pc_temp_ocv_lut, batt_temp_degc, ocv_mv);
	new_pc = interpolate_pc(chip->pc_temp_ocv_lut, batt_temp, ocv_mv);
	pr_debug("test revlookup pc = %d for ocv = %d\n", new_pc, ocv_mv);
	max_spin_count = 1 + (chip->max_voltage_uv - chip->v_cutoff_uv)
						/ UV_PER_SPIN;
@@ -968,7 +967,7 @@ static int find_ocv_for_pc(struct qpnp_bms_chip *chip, int batt_temp, int pc)

		ocv_mv = ocv_mv + delta_mv * sign;
		new_pc = interpolate_pc(chip->pc_temp_ocv_lut,
				batt_temp_degc, ocv_mv);
				batt_temp, ocv_mv);
		pr_debug("test revlookup pc = %d for ocv = %d\n",
			new_pc, ocv_mv);
		count++;
@@ -1066,7 +1065,7 @@ static int calculate_pc(struct qpnp_bms_chip *chip, int ocv_uv,
	int pc;

	pc = interpolate_pc(chip->pc_temp_ocv_lut,
			batt_temp / 10, ocv_uv / 1000);
			batt_temp, ocv_uv / 1000);
	pr_debug("pc = %u %% for ocv = %d uv batt_temp = %d\n",
					pc, ocv_uv, batt_temp);
	/* Multiply the initial FCC value by the scale factor. */
@@ -1189,7 +1188,6 @@ static int get_rbatt(struct qpnp_bms_chip *chip,
		return rbatt_mohm;
	}
	/* Convert the batt_temp to DegC from deciDegC */
	batt_temp = batt_temp / 10;
	scalefactor = interpolate_scalingfactor(chip->rbatt_sf_lut,
						batt_temp, soc_rbatt_mohm);
	rbatt_mohm = (rbatt_mohm * scalefactor) / 100;
@@ -1233,7 +1231,6 @@ static int calculate_termination_uuc(struct qpnp_bms_chip *chip,
	int unusable_uv, pc_unusable, uuc_uah;
	int i = 0;
	int ocv_mv;
	int batt_temp_degc = batt_temp / 10;
	int rbatt_mohm;
	int delta_uv;
	int prev_delta_uv = 0;
@@ -1242,7 +1239,7 @@ static int calculate_termination_uuc(struct qpnp_bms_chip *chip,

	for (i = 0; i <= 100; i++) {
		ocv_mv = interpolate_ocv(chip->pc_temp_ocv_lut,
				batt_temp_degc, i);
				batt_temp, i);
		rbatt_mohm = get_rbatt(chip, i, batt_temp);
		unusable_uv = (rbatt_mohm * uuc_iavg_ma)
							+ (chip->v_cutoff_uv);
@@ -1279,7 +1276,6 @@ static int adjust_uuc(struct qpnp_bms_chip *chip,
			int batt_temp)
{
	int new_unusable_mv, new_iavg_ma;
	int batt_temp_degc = batt_temp / 10;
	int max_percent_change;

	max_percent_change = max(params->delta_time_s
@@ -1302,7 +1298,7 @@ static int adjust_uuc(struct qpnp_bms_chip *chip,

	/* also find update the iavg_ma accordingly */
	new_unusable_mv = interpolate_ocv(chip->pc_temp_ocv_lut,
			batt_temp_degc, chip->prev_pc_unusable);
			batt_temp, chip->prev_pc_unusable);
	if (new_unusable_mv < chip->v_cutoff_uv/1000)
		new_unusable_mv = chip->v_cutoff_uv/1000;

@@ -3085,7 +3081,7 @@ static void fcc_learning_config(struct qpnp_bms_chip *chip, bool start)

	if (start) {
		chip->start_pc = interpolate_pc(chip->pc_temp_ocv_lut,
			batt_temp / 10, raw.last_good_ocv_uv / 1000);
			batt_temp, raw.last_good_ocv_uv / 1000);
		chip->start_cc_uah = calculate_cc(chip, raw.cc, CC, NORESET);
		chip->start_real_soc = calculate_real_soc(chip,
				batt_temp, &raw, chip->start_cc_uah);
+1 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@
#define MAX_SINGLE_LUT_COLS	20

#define MAX_BATT_ID_NUM		4
#define DEGC_SCALE		10

struct single_row_lut {
	int x[MAX_SINGLE_LUT_COLS];