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

Commit 7ddf1d61 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "power: qpnp-qg: Add the filtered voltage based SOC scaling(FVSS)"

parents 261b7b92 312db073
Loading
Loading
Loading
Loading
+10 −8
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2018 The Linux Foundation. All rights reserved.
 * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
 */

#define pr_fmt(fmt)	"QG-K: %s: " fmt, __func__
@@ -100,7 +100,8 @@ static long qg_battery_data_ioctl(struct file *file, unsigned int cmd,
			rc = -EINVAL;
		} else {
			/* OCV is passed as deci-uV  - 10^-4 V */
			soc = interpolate_soc(&battery->profile[bp.table_index],
			soc = qg_interpolate_soc(
					&battery->profile[bp.table_index],
					bp.batt_temp, UV_TO_DECIUV(bp.ocv_uv));
			soc = CAP(QG_MIN_SOC, QG_MAX_SOC, soc);
			rc = put_user(soc, &bp_user->soc);
@@ -120,7 +121,7 @@ static long qg_battery_data_ioctl(struct file *file, unsigned int cmd,
					bp.table_index);
			rc = -EINVAL;
		} else {
			ocv_uv = interpolate_var(
			ocv_uv = qg_interpolate_var(
					&battery->profile[bp.table_index],
					bp.batt_temp, bp.soc);
			ocv_uv = DECIUV_TO_UV(ocv_uv);
@@ -142,7 +143,7 @@ static long qg_battery_data_ioctl(struct file *file, unsigned int cmd,
					bp.table_index);
			rc = -EINVAL;
		} else {
			fcc_mah = interpolate_single_row_lut(
			fcc_mah = qg_interpolate_single_row_lut(
					&battery->profile[bp.table_index],
					bp.batt_temp, DEGC_SCALE);
			fcc_mah = CAP(QG_MIN_FCC_MAH, QG_MAX_FCC_MAH, fcc_mah);
@@ -162,7 +163,8 @@ static long qg_battery_data_ioctl(struct file *file, unsigned int cmd,
					bp.table_index);
			rc = -EINVAL;
		} else {
			var = interpolate_var(&battery->profile[bp.table_index],
			var = qg_interpolate_var(
					&battery->profile[bp.table_index],
					bp.batt_temp, bp.soc);
			var = CAP(QG_MIN_VAR, QG_MAX_VAR, var);
			rc = put_user(var, &bp_user->var);
@@ -182,7 +184,7 @@ static long qg_battery_data_ioctl(struct file *file, unsigned int cmd,
					bp.table_index);
			rc = -EINVAL;
		} else {
			slope = interpolate_slope(
			slope = qg_interpolate_slope(
					&battery->profile[bp.table_index],
					bp.batt_temp, bp.soc);
			slope = CAP(QG_MIN_SLOPE, QG_MAX_SLOPE, slope);
@@ -394,7 +396,7 @@ int lookup_soc_ocv(u32 *soc, u32 ocv_uv, int batt_temp, bool charging)
	if (!the_battery || !the_battery->profile_node)
		return -ENODEV;

	*soc = interpolate_soc(&the_battery->profile[table_index],
	*soc = qg_interpolate_soc(&the_battery->profile[table_index],
				batt_temp, UV_TO_DECIUV(ocv_uv));

	*soc = CAP(0, 100, DIV_ROUND_CLOSEST(*soc, 100));
@@ -410,7 +412,7 @@ int qg_get_nominal_capacity(u32 *nom_cap_uah, int batt_temp, bool charging)
	if (!the_battery || !the_battery->profile_node)
		return -ENODEV;

	fcc_mah = interpolate_single_row_lut(
	fcc_mah = qg_interpolate_single_row_lut(
				&the_battery->profile[table_index],
					batt_temp, DEGC_SCALE);
	fcc_mah = CAP(QG_MIN_FCC_MAH, QG_MAX_FCC_MAH, fcc_mah);
+7 −0
Original line number Diff line number Diff line
@@ -57,6 +57,7 @@ struct qg_dt {
	int			shutdown_soc_threshold;
	int			min_sleep_time_secs;
	int			sys_min_volt_mv;
	int			fvss_vbat_mv;
	bool			hold_soc_while_full;
	bool			linearize_soc;
	bool			cl_disable;
@@ -67,6 +68,7 @@ struct qg_dt {
	bool			use_s7_ocv;
	bool			qg_sleep_config;
	bool			qg_fast_chg_cfg;
	bool			fvss_enable;
};

struct qg_esr_data {
@@ -129,6 +131,7 @@ struct qpnp_qg {
	bool			dc_present;
	bool			charge_full;
	bool			force_soc;
	bool			fvss_active;
	int			charge_status;
	int			charge_type;
	int			chg_iterm_ma;
@@ -137,6 +140,8 @@ struct qpnp_qg {
	int			esr_nominal;
	int			soh;
	int			soc_reporting_ready;
	int			last_fifo_v_uv;
	int			last_fifo_i_ua;
	u32			fifo_done_count;
	u32			wa_flags;
	u32			seq_no;
@@ -145,6 +150,8 @@ struct qpnp_qg {
	u32			esr_last;
	u32			s2_state;
	u32			s2_state_mask;
	u32			soc_fvss_entry;
	u32			vbat_fvss_entry;
	ktime_t			last_user_update_time;
	ktime_t			last_fifo_update_time;
	unsigned long		last_maint_soc_update_time;
+17 −17
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (c) 2018 The Linux Foundation. All rights reserved.
 * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
 */

#include <linux/module.h>
@@ -9,7 +9,7 @@
#include "qg-profile-lib.h"
#include "qg-defs.h"

static int linear_interpolate(int y0, int x0, int y1, int x1, int x)
int qg_linear_interpolate(int y0, int x0, int y1, int x1, int x)
{
	if (y0 == y1 || x == x0)
		return y0;
@@ -19,7 +19,7 @@ static int linear_interpolate(int y0, int x0, int y1, int x1, int x)
	return y0 + ((y1 - y0) * (x - x0) / (x1 - x0));
}

int interpolate_single_row_lut(struct profile_table_data *lut,
int qg_interpolate_single_row_lut(struct profile_table_data *lut,
						int x, int scale)
{
	int i, result;
@@ -45,7 +45,7 @@ int interpolate_single_row_lut(struct profile_table_data *lut,
	if (x == lut->col_entries[i] * scale) {
		result = lut->data[0][i];
	} else {
		result = linear_interpolate(
		result = qg_linear_interpolate(
			lut->data[0][i-1],
			lut->col_entries[i-1] * scale,
			lut->data[0][i],
@@ -56,7 +56,7 @@ int interpolate_single_row_lut(struct profile_table_data *lut,
	return result;
}

int interpolate_soc(struct profile_table_data *lut,
int qg_interpolate_soc(struct profile_table_data *lut,
				int batt_temp, int ocv)
{
	int i, j, soc_high, soc_low, soc;
@@ -87,7 +87,7 @@ int interpolate_soc(struct profile_table_data *lut,
			if (ocv >= lut->data[i][j]) {
				if (ocv == lut->data[i][j])
					return lut->row_entries[i];
				soc = linear_interpolate(
				soc = qg_linear_interpolate(
					lut->row_entries[i],
					lut->data[i][j],
					lut->row_entries[i - 1],
@@ -108,7 +108,7 @@ int interpolate_soc(struct profile_table_data *lut,
	for (i = 0; i < rows-1; i++) {
		if (soc_high == 0 && is_between(lut->data[i][j],
				lut->data[i+1][j], ocv)) {
			soc_high = linear_interpolate(
			soc_high = qg_linear_interpolate(
				lut->row_entries[i],
				lut->data[i][j],
				lut->row_entries[i + 1],
@@ -118,7 +118,7 @@ int interpolate_soc(struct profile_table_data *lut,

		if (soc_low == 0 && is_between(lut->data[i][j-1],
				lut->data[i+1][j-1], ocv)) {
			soc_low = linear_interpolate(
			soc_low = qg_linear_interpolate(
				lut->row_entries[i],
				lut->data[i][j-1],
				lut->row_entries[i + 1],
@@ -127,7 +127,7 @@ int interpolate_soc(struct profile_table_data *lut,
		}

		if (soc_high && soc_low) {
			soc = linear_interpolate(
			soc = qg_linear_interpolate(
				soc_low,
				lut->col_entries[j-1] * DEGC_SCALE,
				soc_high,
@@ -148,7 +148,7 @@ int interpolate_soc(struct profile_table_data *lut,
	return 10000;
}

int interpolate_var(struct profile_table_data *lut,
int qg_interpolate_var(struct profile_table_data *lut,
				int batt_temp, int soc)
{
	int i, var1, var2, var, rows, cols;
@@ -192,7 +192,7 @@ int interpolate_var(struct profile_table_data *lut,
			break;

	if (batt_temp == lut->col_entries[i] * DEGC_SCALE) {
		var = linear_interpolate(
		var = qg_linear_interpolate(
				lut->data[row1][i],
				lut->row_entries[row1],
				lut->data[row2][i],
@@ -201,21 +201,21 @@ int interpolate_var(struct profile_table_data *lut,
		return var;
	}

	var1 = linear_interpolate(
	var1 = qg_linear_interpolate(
				lut->data[row1][i - 1],
				lut->col_entries[i - 1] * DEGC_SCALE,
				lut->data[row1][i],
				lut->col_entries[i] * DEGC_SCALE,
				batt_temp);

	var2 = linear_interpolate(
	var2 = qg_linear_interpolate(
				lut->data[row2][i - 1],
				lut->col_entries[i - 1] * DEGC_SCALE,
				lut->data[row2][i],
				lut->col_entries[i] * DEGC_SCALE,
				batt_temp);

	var = linear_interpolate(
	var = qg_linear_interpolate(
				var1,
				lut->row_entries[row1],
				var2,
@@ -225,7 +225,7 @@ int interpolate_var(struct profile_table_data *lut,
	return var;
}

int interpolate_slope(struct profile_table_data *lut,
int qg_interpolate_slope(struct profile_table_data *lut,
					int batt_temp, int soc)
{
	int i, ocvrow1, ocvrow2, rows, cols;
@@ -277,14 +277,14 @@ int interpolate_slope(struct profile_table_data *lut,
			lut->row_entries[row2]);
		return slope;
	}
	ocvrow1 = linear_interpolate(
	ocvrow1 = qg_linear_interpolate(
			lut->data[row1][i - 1],
			lut->col_entries[i - 1] * DEGC_SCALE,
			lut->data[row1][i],
			lut->col_entries[i] * DEGC_SCALE,
			batt_temp);

	ocvrow2 = linear_interpolate(
	ocvrow2 = qg_linear_interpolate(
			lut->data[row2][i - 1],
			lut->col_entries[i - 1] * DEGC_SCALE,
			lut->data[row2][i],
+6 −5
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright (c) 2018 The Linux Foundation. All rights reserved.
 * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
 */

#ifndef __QG_PROFILE_LIB_H__
@@ -15,13 +15,14 @@ struct profile_table_data {
	int		**data;
};

int interpolate_single_row_lut(struct profile_table_data *lut,
int qg_linear_interpolate(int y0, int x0, int y1, int x1, int x);
int qg_interpolate_single_row_lut(struct profile_table_data *lut,
						int x, int scale);
int interpolate_soc(struct profile_table_data *lut,
int qg_interpolate_soc(struct profile_table_data *lut,
				int batt_temp, int ocv);
int interpolate_var(struct profile_table_data *lut,
int qg_interpolate_var(struct profile_table_data *lut,
				int batt_temp, int soc);
int interpolate_slope(struct profile_table_data *lut,
int qg_interpolate_slope(struct profile_table_data *lut,
				int batt_temp, int soc);

#endif /*__QG_PROFILE_LIB_H__ */
+91 −2
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#include "qg-reg.h"
#include "qg-util.h"
#include "qg-defs.h"
#include "qg-profile-lib.h"
#include "qg-soc.h"

#define DEFAULT_UPDATE_TIME_MS			64000
@@ -45,6 +46,11 @@ static ssize_t soc_interval_ms_store(struct device *dev,
}
DEVICE_ATTR_RW(soc_interval_ms);

static int qg_fvss_delta_soc_interval_ms = 10000;
module_param_named(
	fvss_soc_interval_ms, qg_fvss_delta_soc_interval_ms, int, 0600
);

static int qg_delta_soc_cold_interval_ms = 4000;
static ssize_t soc_cold_interval_ms_show(struct device *dev,
		struct device_attribute *attr, char *buf)
@@ -87,6 +93,84 @@ static ssize_t maint_soc_update_ms_store(struct device *dev,
}
DEVICE_ATTR_RW(maint_soc_update_ms);

/* FVSS scaling only based on VBAT */
static int qg_fvss_vbat_scaling = 1;
module_param_named(
	fvss_vbat_scaling, qg_fvss_vbat_scaling, int, 0600
);

static int qg_process_fvss_soc(struct qpnp_qg *chip, int sys_soc)
{
	int rc, vbat_uv = 0, vbat_cutoff_uv = chip->dt.vbatt_cutoff_mv * 1000;
	int soc_vbat = 0, wt_vbat = 0, wt_sys = 0, soc_fvss = 0;

	if (!chip->dt.fvss_enable)
		return 0;

	if (chip->charge_status == POWER_SUPPLY_STATUS_CHARGING)
		goto exit_soc_scale;

	rc = qg_get_battery_voltage(chip, &vbat_uv);
	if (rc < 0)
		goto exit_soc_scale;

	if (!chip->last_fifo_v_uv)
		chip->last_fifo_v_uv = vbat_uv;

	if (chip->last_fifo_v_uv > (chip->dt.fvss_vbat_mv * 1000)) {
		qg_dbg(chip, QG_DEBUG_SOC, "FVSS: last_fifo_v=%d fvss_entry_uv=%d - exit\n",
			chip->last_fifo_v_uv, chip->dt.fvss_vbat_mv * 1000);
		goto exit_soc_scale;
	}

	/* Enter FVSS */
	if (!chip->fvss_active) {
		chip->vbat_fvss_entry = CAP(vbat_cutoff_uv,
					chip->dt.fvss_vbat_mv * 1000,
					chip->last_fifo_v_uv);
		chip->soc_fvss_entry = sys_soc;
		chip->fvss_active = true;
	} else if (chip->last_fifo_v_uv > chip->vbat_fvss_entry) {
		/* VBAT has gone beyond the entry voltage */
		chip->vbat_fvss_entry = chip->last_fifo_v_uv;
		chip->soc_fvss_entry = sys_soc;
	}

	soc_vbat = qg_linear_interpolate(chip->soc_fvss_entry,
					chip->vbat_fvss_entry,
					0,
					vbat_cutoff_uv,
					chip->last_fifo_v_uv);
	soc_vbat = CAP(0, 100, soc_vbat);

	if (qg_fvss_vbat_scaling) {
		wt_vbat = 100;
		wt_sys = 0;
	} else {
		wt_sys = qg_linear_interpolate(100,
					chip->soc_fvss_entry,
					0,
					0,
					sys_soc);
		wt_sys = CAP(0, 100, wt_sys);
		wt_vbat = 100 - wt_sys;
	}

	soc_fvss = ((soc_vbat * wt_vbat) + (sys_soc * wt_sys)) / 100;
	soc_fvss = CAP(0, 100, soc_fvss);

	qg_dbg(chip, QG_DEBUG_SOC, "FVSS: vbat_fvss_entry=%d soc_fvss_entry=%d cutoff_uv=%d vbat_uv=%d fifo_avg_v=%d soc_vbat=%d sys_soc=%d wt_vbat=%d wt_sys=%d soc_fvss=%d\n",
			chip->vbat_fvss_entry, chip->soc_fvss_entry,
			vbat_cutoff_uv, vbat_uv, chip->last_fifo_v_uv,
			soc_vbat, sys_soc, wt_vbat, wt_sys, soc_fvss);

	return soc_fvss;

exit_soc_scale:
	chip->fvss_active = false;
	return sys_soc;
}

int qg_adjust_sys_soc(struct qpnp_qg *chip)
{
	int soc, vbat_uv, rc;
@@ -113,8 +197,11 @@ int qg_adjust_sys_soc(struct qpnp_qg *chip)
		soc = DIV_ROUND_CLOSEST(chip->sys_soc, 100);
	}

	qg_dbg(chip, QG_DEBUG_SOC, "last_adj_sys_soc=%d  adj_sys_soc=%d\n",
					chip->last_adj_ssoc, soc);
	qg_dbg(chip, QG_DEBUG_SOC, "sys_soc=%d adjusted sys_soc=%d\n",
					chip->sys_soc, soc);

	soc = qg_process_fvss_soc(chip, soc);

	chip->last_adj_ssoc = soc;

	return soc;
@@ -144,6 +231,8 @@ static void get_next_update_time(struct qpnp_qg *chip)
	else if (chip->maint_soc > 0 && chip->maint_soc >= chip->recharge_soc)
		/* if in maintenance mode scale slower */
		min_delta_soc_interval_ms = qg_maint_soc_update_ms;
	else if (chip->fvss_active)
		min_delta_soc_interval_ms = qg_fvss_delta_soc_interval_ms;

	if (!min_delta_soc_interval_ms)
		min_delta_soc_interval_ms = 1000;	/* 1 second */
Loading