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

Commit 70b63079 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-qg: add support for TTF and TTE features"

parents 2c7447f5 7f175ff3
Loading
Loading
Loading
Loading
+531 −0
Original line number Diff line number Diff line
@@ -16,12 +16,38 @@
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/power_supply.h>
#include <linux/slab.h>
#include <linux/sort.h>
#include "fg-alg.h"

#define FULL_SOC_RAW		255
#define FULL_BATT_SOC		GENMASK(31, 0)
#define CAPACITY_DELTA_DECIPCT	500

#define CENTI_ICORRECT_C0	105
#define CENTI_ICORRECT_C1	20

#define HOURS_TO_SECONDS	3600
#define OCV_SLOPE_UV		10869
#define MILLI_UNIT		1000
#define MICRO_UNIT		1000000
#define NANO_UNIT		1000000000

#define DEFAULT_TTF_RUN_PERIOD_MS	10000
#define DEFAULT_TTF_ITERM_DELTA_MA	200

static const struct ttf_pt ttf_ln_table[] = {
	{ 1000,		0 },
	{ 2000,		693 },
	{ 4000,		1386 },
	{ 6000,		1792 },
	{ 8000,		2079 },
	{ 16000,	2773 },
	{ 32000,	3466 },
	{ 64000,	4159 },
	{ 128000,	4852 },
};

/* Cycle counter APIs */

/**
@@ -670,3 +696,508 @@ int cap_learning_init(struct cap_learning *cl)
	mutex_init(&cl->lock);
	return 0;
}

/* Time to full/empty algorithm  helper functions */

static void ttf_circ_buf_add(struct ttf_circ_buf *buf, int val)
{
	buf->arr[buf->head] = val;
	buf->head = (buf->head + 1) % ARRAY_SIZE(buf->arr);
	buf->size = min(++buf->size, (int)ARRAY_SIZE(buf->arr));
}

static void ttf_circ_buf_clr(struct ttf_circ_buf *buf)
{
	buf->size = 0;
	buf->head = 0;
	memset(buf->arr, 0, sizeof(buf->arr));
}

static int cmp_int(const void *a, const void *b)
{
	return *(int *)a - *(int *)b;
}

static int ttf_circ_buf_median(struct ttf_circ_buf *buf, int *median)
{
	int *temp;

	if (buf->size == 0)
		return -ENODATA;

	if (buf->size == 1) {
		*median = buf->arr[0];
		return 0;
	}

	temp = kmalloc_array(buf->size, sizeof(*temp), GFP_KERNEL);
	if (!temp)
		return -ENOMEM;

	memcpy(temp, buf->arr, buf->size * sizeof(*temp));
	sort(temp, buf->size, sizeof(*temp), cmp_int, NULL);

	if (buf->size % 2)
		*median = temp[buf->size / 2];
	else
		*median = (temp[buf->size / 2 - 1] + temp[buf->size / 2]) / 2;

	kfree(temp);
	return 0;
}

static int ttf_lerp(const struct ttf_pt *pts, size_t tablesize,
						s32 input, s32 *output)
{
	int i;
	s64 temp;

	if (pts == NULL) {
		pr_err("Table is NULL\n");
		return -EINVAL;
	}

	if (tablesize < 1) {
		pr_err("Table has no entries\n");
		return -ENOENT;
	}

	if (tablesize == 1) {
		*output = pts[0].y;
		return 0;
	}

	if (pts[0].x > pts[1].x) {
		pr_err("Table is not in acending order\n");
		return -EINVAL;
	}

	if (input <= pts[0].x) {
		*output = pts[0].y;
		return 0;
	}

	if (input >= pts[tablesize - 1].x) {
		*output = pts[tablesize - 1].y;
		return 0;
	}

	for (i = 1; i < tablesize; i++) {
		if (input >= pts[i].x)
			continue;

		temp = ((s64)pts[i].y - pts[i - 1].y) *
						((s64)input - pts[i - 1].x);
		temp = div_s64(temp, pts[i].x - pts[i - 1].x);
		*output = temp + pts[i - 1].y;
		return 0;
	}

	return -EINVAL;
}

static int get_time_to_full_locked(struct ttf *ttf, int *val)
{
	int rc, ibatt_avg, vbatt_avg, rbatt = 0, msoc = 0, act_cap_mah = 0,
		i_cc2cv = 0, soc_cc2cv, tau, divisor, iterm = 0, ttf_mode = 0,
		i, soc_per_step, msoc_this_step, msoc_next_step,
		ibatt_this_step, t_predicted_this_step, ttf_slope,
		t_predicted_cv, t_predicted = 0, charge_type = 0,
		float_volt_uv = 0;
	s64 delta_ms;

	rc = ttf->get_ttf_param(ttf->data, TTF_MSOC, &msoc);
	if (rc < 0) {
		pr_err("failed to get msoc rc=%d\n", rc);
		return rc;
	}
	pr_debug("TTF: msoc=%d\n", msoc);

	/* the battery is considered full if the SOC is 100% */
	if (msoc >= 100) {
		*val = 0;
		return 0;
	}

	rc = ttf->get_ttf_param(ttf->data, TTF_MODE, &ttf_mode);

	/* when switching TTF algorithms the TTF needs to be reset */
	if (ttf->mode != ttf_mode) {
		ttf_circ_buf_clr(&ttf->ibatt);
		ttf_circ_buf_clr(&ttf->vbatt);
		ttf->last_ttf = 0;
		ttf->last_ms = 0;
		ttf->mode = ttf_mode;
	}

	/* at least 10 samples are required to produce a stable IBATT */
	if (ttf->ibatt.size < MAX_TTF_SAMPLES) {
		*val = -1;
		return 0;
	}

	rc = ttf_circ_buf_median(&ttf->ibatt, &ibatt_avg);
	if (rc < 0) {
		pr_err("failed to get IBATT AVG rc=%d\n", rc);
		return rc;
	}

	rc = ttf_circ_buf_median(&ttf->vbatt, &vbatt_avg);
	if (rc < 0) {
		pr_err("failed to get VBATT AVG rc=%d\n", rc);
		return rc;
	}

	ibatt_avg = -ibatt_avg / MILLI_UNIT;
	vbatt_avg /= MILLI_UNIT;

	rc = ttf->get_ttf_param(ttf->data, TTF_ITERM, &iterm);
	if (rc < 0) {
		pr_err("failed to get iterm rc=%d\n", rc);
		return rc;
	}
	/* clamp ibatt_avg to iterm */
	if (ibatt_avg < abs(iterm))
		ibatt_avg = abs(iterm);

	rc =  ttf->get_ttf_param(ttf->data, TTF_RBATT, &rbatt);
	if (rc < 0) {
		pr_err("failed to get battery resistance rc=%d\n", rc);
		return rc;
	}
	rbatt /= MILLI_UNIT;

	rc =  ttf->get_ttf_param(ttf->data, TTF_FCC, &act_cap_mah);
	if (rc < 0) {
		pr_err("failed to get ACT_BATT_CAP rc=%d\n", rc);
		return rc;
	}

	pr_debug(" TTF: ibatt_avg=%d vbatt_avg=%d rbatt=%d act_cap_mah=%d\n",
				ibatt_avg, vbatt_avg, rbatt, act_cap_mah);

	rc =  ttf->get_ttf_param(ttf->data, TTF_VFLOAT, &float_volt_uv);
	if (rc < 0) {
		pr_err("failed to get float_volt_uv rc=%d\n", rc);
		return rc;
	}

	rc =  ttf->get_ttf_param(ttf->data, TTF_CHG_TYPE, &charge_type);
	if (rc < 0) {
		pr_err("failed to get charge_type rc=%d\n", rc);
		return rc;
	}
	/* estimated battery current at the CC to CV transition */
	switch (ttf->mode) {
	case TTF_MODE_NORMAL:
		i_cc2cv = ibatt_avg * vbatt_avg /
			max(MILLI_UNIT, float_volt_uv / MILLI_UNIT);
		break;
	case TTF_MODE_QNOVO:
		i_cc2cv = min(
			ttf->cc_step.arr[MAX_CC_STEPS - 1] / MILLI_UNIT,
			ibatt_avg * vbatt_avg /
			max(MILLI_UNIT, float_volt_uv / MILLI_UNIT));
		break;
	default:
		pr_err("TTF mode %d is not supported\n", ttf->mode);
		break;
	}
	pr_debug("TTF: i_cc2cv=%d\n", i_cc2cv);

	/* if we are already in CV state then we can skip estimating CC */
	if (charge_type == POWER_SUPPLY_CHARGE_TYPE_TAPER)
		goto cv_estimate;

	/* estimated SOC at the CC to CV transition */
	soc_cc2cv = DIV_ROUND_CLOSEST(rbatt * i_cc2cv, OCV_SLOPE_UV);
	soc_cc2cv = 100 - soc_cc2cv;
	pr_debug("TTF: soc_cc2cv=%d\n", soc_cc2cv);

	switch (ttf->mode) {
	case TTF_MODE_NORMAL:
		if (soc_cc2cv - msoc <= 0)
			goto cv_estimate;

		divisor = max(100, (ibatt_avg + i_cc2cv) / 2 * 100);
		t_predicted = div_s64((s64)act_cap_mah * (soc_cc2cv - msoc) *
						HOURS_TO_SECONDS, divisor);
		break;
	case TTF_MODE_QNOVO:
		soc_per_step = 100 / MAX_CC_STEPS;
		for (i = msoc / soc_per_step; i < MAX_CC_STEPS - 1; ++i) {
			msoc_next_step = (i + 1) * soc_per_step;
			if (i == msoc / soc_per_step)
				msoc_this_step = msoc;
			else
				msoc_this_step = i * soc_per_step;

			/* scale ibatt by 85% to account for discharge pulses */
			ibatt_this_step = min(
					ttf->cc_step.arr[i] / MILLI_UNIT,
					ibatt_avg) * 85 / 100;
			divisor = max(100, ibatt_this_step * 100);
			t_predicted_this_step = div_s64((s64)act_cap_mah *
					(msoc_next_step - msoc_this_step) *
					HOURS_TO_SECONDS, divisor);
			t_predicted += t_predicted_this_step;
			pr_debug("TTF: [%d, %d] ma=%d t=%d\n",
				msoc_this_step, msoc_next_step,
				ibatt_this_step, t_predicted_this_step);
		}
		break;
	default:
		pr_err("TTF mode %d is not supported\n", ttf->mode);
		break;
	}

cv_estimate:
	pr_debug("TTF: t_predicted_cc=%d\n", t_predicted);

	iterm = max(100, abs(iterm) + ttf->iterm_delta);
	pr_debug("TTF: iterm=%d\n", iterm);

	if (charge_type == POWER_SUPPLY_CHARGE_TYPE_TAPER)
		tau = max(MILLI_UNIT, ibatt_avg * MILLI_UNIT / iterm);
	else
		tau = max(MILLI_UNIT, i_cc2cv * MILLI_UNIT / iterm);

	rc = ttf_lerp(ttf_ln_table, ARRAY_SIZE(ttf_ln_table), tau, &tau);
	if (rc < 0) {
		pr_err("failed to interpolate tau rc=%d\n", rc);
		return rc;
	}

	/* tau is scaled linearly from 95% to 100% SOC */
	if (msoc >= 95)
		tau = tau * 2 * (100 - msoc) / 10;

	pr_debug("TTF: tau=%d\n", tau);
	t_predicted_cv = div_s64((s64)act_cap_mah * rbatt * tau *
						HOURS_TO_SECONDS, NANO_UNIT);
	pr_debug("TTF: t_predicted_cv=%d\n", t_predicted_cv);
	t_predicted += t_predicted_cv;

	pr_debug("TTF: t_predicted_prefilter=%d\n", t_predicted);
	if (ttf->last_ms != 0) {
		delta_ms = ktime_ms_delta(ktime_get_boottime(),
					  ms_to_ktime(ttf->last_ms));
		if (delta_ms > 10000) {
			ttf_slope = div64_s64(
				((s64)t_predicted - ttf->last_ttf) *
				MICRO_UNIT, delta_ms);
			if (ttf_slope > -100)
				ttf_slope = -100;
			else if (ttf_slope < -2000)
				ttf_slope = -2000;

			t_predicted = div_s64(
				(s64)ttf_slope * delta_ms, MICRO_UNIT) +
				ttf->last_ttf;
			pr_debug("TTF: ttf_slope=%d\n", ttf_slope);
		} else {
			t_predicted = ttf->last_ttf;
		}
	}

	/* clamp the ttf to 0 */
	if (t_predicted < 0)
		t_predicted = 0;

	pr_debug("TTF: t_predicted_postfilter=%d\n", t_predicted);
	*val = t_predicted;
	return 0;
}

/**
 * ttf_get_time_to_full -
 * @ttf: ttf object
 * @val: Average time to full returned to the caller
 *
 * Get Average time to full the battery based on current soc, rbatt
 * battery voltage and charge current etc.
 */
int ttf_get_time_to_full(struct ttf *ttf, int *val)
{
	int rc;

	mutex_lock(&ttf->lock);
	rc = get_time_to_full_locked(ttf, val);
	mutex_unlock(&ttf->lock);

	return rc;
}

static void ttf_work(struct work_struct *work)
{
	struct ttf *ttf = container_of(work,
				struct ttf, ttf_work.work);
	int rc, ibatt_now, vbatt_now, ttf_now, charge_status;
	ktime_t ktime_now;

	mutex_lock(&ttf->lock);
	rc =  ttf->get_ttf_param(ttf->data, TTF_CHG_STATUS, &charge_status);
	if (rc < 0) {
		pr_err("failed to get charge_status rc=%d\n", rc);
		goto end_work;
	}
	if (charge_status != POWER_SUPPLY_STATUS_CHARGING &&
			charge_status != POWER_SUPPLY_STATUS_DISCHARGING)
		goto end_work;

	rc =  ttf->get_ttf_param(ttf->data, TTF_IBAT, &ibatt_now);
	if (rc < 0) {
		pr_err("failed to get battery current, rc=%d\n", rc);
		goto end_work;
	}

	rc =  ttf->get_ttf_param(ttf->data, TTF_VBAT, &vbatt_now);
	if (rc < 0) {
		pr_err("failed to get battery voltage, rc=%d\n", rc);
		goto end_work;
	}

	ttf_circ_buf_add(&ttf->ibatt, ibatt_now);
	ttf_circ_buf_add(&ttf->vbatt, vbatt_now);

	if (charge_status == POWER_SUPPLY_STATUS_CHARGING) {
		rc = get_time_to_full_locked(ttf, &ttf_now);
		if (rc < 0) {
			pr_err("failed to get ttf, rc=%d\n", rc);
			goto end_work;
		}

		/* keep the wake lock and prime the IBATT and VBATT buffers */
		if (ttf_now < 0) {
			/* delay for one FG cycle */
			schedule_delayed_work(&ttf->ttf_work,
					msecs_to_jiffies(1000));
			mutex_unlock(&ttf->lock);
			return;
		}

		/* update the TTF reference point every minute */
		ktime_now = ktime_get_boottime();
		if (ktime_ms_delta(ktime_now,
				   ms_to_ktime(ttf->last_ms)) > 60000 ||
				   ttf->last_ms == 0) {
			ttf->last_ttf = ttf_now;
			ttf->last_ms = ktime_to_ms(ktime_now);
		}
	}

	/* recurse every 10 seconds */
	schedule_delayed_work(&ttf->ttf_work, msecs_to_jiffies(ttf->period_ms));
end_work:
	ttf->awake_voter(ttf->data, false);
	mutex_unlock(&ttf->lock);
}

/**
 * ttf_get_time_to_empty -
 * @ttf: ttf object
 * @val: Average time to empty returned to the caller
 *
 * Get Average time to empty the battery based on current soc
 * and average battery current.
 */
int ttf_get_time_to_empty(struct ttf *ttf, int *val)
{
	int rc, ibatt_avg, msoc, act_cap_mah, divisor;

	rc = ttf_circ_buf_median(&ttf->ibatt, &ibatt_avg);
	if (rc < 0) {
		/* try to get instantaneous current */
		rc = ttf->get_ttf_param(ttf->data, TTF_IBAT, &ibatt_avg);
		if (rc < 0) {
			pr_err("failed to get battery current, rc=%d\n", rc);
			return rc;
		}
	}

	ibatt_avg /= MILLI_UNIT;
	/* clamp ibatt_avg to 100mA */
	if (ibatt_avg < 100)
		ibatt_avg = 100;

	rc = ttf->get_ttf_param(ttf->data, TTF_MSOC, &msoc);
	if (rc < 0) {
		pr_err("Error in getting capacity, rc=%d\n", rc);
		return rc;
	}

	rc = ttf->get_ttf_param(ttf->data, TTF_FCC, &act_cap_mah);
	if (rc < 0) {
		pr_err("Error in getting ACT_BATT_CAP, rc=%d\n", rc);
		return rc;
	}

	divisor = CENTI_ICORRECT_C0 * 100 + CENTI_ICORRECT_C1 * msoc;
	divisor = ibatt_avg * divisor / 100;
	divisor = max(100, divisor);
	*val = act_cap_mah * msoc * HOURS_TO_SECONDS / divisor;
	return 0;
}

/**
 * ttf_update -
 * @ttf: ttf object
 * @input_present: Indicator for input presence
 *
 * Called by FG/QG driver when there is a state change (Charging status, SOC)
 *
 */
void ttf_update(struct ttf *ttf, bool input_present)
{
	int delay_ms;

	if (ttf->input_present == input_present)
		return;

	ttf->input_present = input_present;
	if (input_present)
		/* wait 35 seconds for the input to settle */
		delay_ms = 35000;
	else
		/* wait 5 seconds for current to settle during discharge */
		delay_ms = 5000;

	ttf->awake_voter(ttf->data, true);
	cancel_delayed_work_sync(&ttf->ttf_work);
	mutex_lock(&ttf->lock);
	ttf_circ_buf_clr(&ttf->ibatt);
	ttf_circ_buf_clr(&ttf->vbatt);
	ttf->last_ttf = 0;
	ttf->last_ms = 0;
	mutex_unlock(&ttf->lock);
	schedule_delayed_work(&ttf->ttf_work, msecs_to_jiffies(delay_ms));
}

/**
 * ttf_tte_init -
 * @ttf: Time to full object
 *
 * FG/QG have to call this during driver probe to validate the required
 * parameters after allocating ttf object.
 *
 */
int ttf_tte_init(struct ttf *ttf)
{
	if (!ttf)
		return -ENODEV;

	if (!ttf->awake_voter || !ttf->get_ttf_param) {
		pr_err("Insufficient functions for supporting ttf\n");
		return -EINVAL;
	}

	if (!ttf->iterm_delta)
		ttf->iterm_delta = DEFAULT_TTF_ITERM_DELTA_MA;
	if (!ttf->period_ms)
		ttf->period_ms = DEFAULT_TTF_RUN_PERIOD_MS;

	mutex_init(&ttf->lock);
	INIT_DELAYED_WORK(&ttf->ttf_work, ttf_work);

	return 0;
}
+57 −0
Original line number Diff line number Diff line
@@ -15,6 +15,8 @@

#define BUCKET_COUNT		8
#define BUCKET_SOC_PCT		(256 / BUCKET_COUNT)
#define MAX_CC_STEPS		20
#define MAX_TTF_SAMPLES		10

struct cycle_counter {
	void		*data;
@@ -58,6 +60,57 @@ struct cap_learning {
	int (*prime_cc_soc)(void *data, u32 cc_soc_sw);
};

enum ttf_mode {
	TTF_MODE_NORMAL = 0,
	TTF_MODE_QNOVO,
};

enum ttf_param {
	TTF_MSOC = 0,
	TTF_VBAT,
	TTF_IBAT,
	TTF_FCC,
	TTF_MODE,
	TTF_ITERM,
	TTF_RBATT,
	TTF_VFLOAT,
	TTF_CHG_TYPE,
	TTF_CHG_STATUS,
};

struct ttf_circ_buf {
	int	arr[MAX_TTF_SAMPLES];
	int	size;
	int	head;
};

struct ttf_cc_step_data {
	int arr[MAX_CC_STEPS];
	int sel;
};

struct ttf_pt {
	s32 x;
	s32 y;
};

struct ttf {
	void			*data;
	struct ttf_circ_buf	ibatt;
	struct ttf_circ_buf	vbatt;
	struct ttf_cc_step_data	cc_step;
	struct mutex		lock;
	int			mode;
	int			last_ttf;
	int			input_present;
	int			iterm_delta;
	int			period_ms;
	s64			last_ms;
	struct delayed_work	ttf_work;
	int (*get_ttf_param)(void *data, enum ttf_param, int *val);
	int (*awake_voter)(void *data, bool vote);
};

int restore_cycle_count(struct cycle_counter *counter);
void clear_cycle_count(struct cycle_counter *counter);
void cycle_count_update(struct cycle_counter *counter, int batt_soc,
@@ -72,5 +125,9 @@ void cap_learning_update(struct cap_learning *cl, int batt_temp,
int cap_learning_init(struct cap_learning *cl);
int cap_learning_post_profile_init(struct cap_learning *cl,
		int64_t nom_cap_uah);
void ttf_update(struct ttf *ttf, bool input_present);
int ttf_get_time_to_empty(struct ttf *ttf, int *val);
int ttf_get_time_to_full(struct ttf *ttf, int *val);
int ttf_tte_init(struct ttf *ttf);

#endif
+4 −0
Original line number Diff line number Diff line
@@ -118,6 +118,7 @@ struct qpnp_qg {
	bool			charge_full;
	int			charge_status;
	int			charge_type;
	int			chg_iterm_ma;
	int			next_wakeup_ms;
	u32			fifo_done_count;
	u32			wa_flags;
@@ -135,6 +136,7 @@ struct qpnp_qg {
	int			pon_soc;
	int			batt_soc;
	int			cc_soc;
	int			full_soc;
	struct alarm		alarm_timer;
	u32			sdam_data[SDAM_MAX];

@@ -145,6 +147,8 @@ struct qpnp_qg {
	struct cap_learning	*cl;
	/* charge counter */
	struct cycle_counter	*counter;
	/* ttf */
	struct ttf		*ttf;
};

enum ocv_type {
+1 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@
#define GOOD_OCV_VOTER			"GOOD_OCV_VOTER"
#define PROFILE_IRQ_DISABLE		"NO_PROFILE_IRQ_DISABLE"
#define QG_INIT_STATE_IRQ_DISABLE	"QG_INIT_STATE_IRQ_DISABLE"
#define TTF_AWAKE_VOTER			"TTF_AWAKE_VOTER"

#define V_RAW_TO_UV(V_RAW)		div_u64(194637ULL * (u64)V_RAW, 1000)
#define I_RAW_TO_UA(I_RAW)		div_s64(152588LL * (s64)I_RAW, 1000)
+135 −0
Original line number Diff line number Diff line
@@ -221,6 +221,14 @@ static void qg_notify_charger(struct qpnp_qg *chip)
	}

	pr_debug("Notified charger on float voltage and FCC\n");

	rc = power_supply_get_property(chip->batt_psy,
			POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, &prop);
	if (rc < 0) {
		pr_err("Failed to get charge term current, rc=%d\n", rc);
		return;
	}
	chip->chg_iterm_ma = prop.intval;
}

static bool is_batt_available(struct qpnp_qg *chip)
@@ -1014,6 +1022,9 @@ static void process_udata_work(struct work_struct *work)
	if (chip->udata.param[QG_BATT_SOC].valid)
		chip->batt_soc = chip->udata.param[QG_BATT_SOC].data;

	if (chip->udata.param[QG_FULL_SOC].valid)
		chip->full_soc = chip->udata.param[QG_FULL_SOC].data;

	if (chip->udata.param[QG_SOC].valid) {
		qg_dbg(chip, QG_DEBUG_SOC, "udata SOC=%d last SOC=%d\n",
			chip->udata.param[QG_SOC].data, chip->catch_up_soc);
@@ -1044,6 +1055,8 @@ static void process_udata_work(struct work_struct *work)
	if (!chip->dt.esr_disable)
		qg_store_esr_params(chip);

	qg_dbg(chip, QG_DEBUG_STATUS, "udata update: batt_soc=%d cc_soc=%d full_soc=%d qg_esr=%d\n",
		chip->batt_soc, chip->cc_soc, chip->full_soc, chip->esr_last);
	vote(chip->awake_votable, UDATA_READY_VOTER, false, 0);
}

@@ -1570,6 +1583,87 @@ static int qg_get_battery_capacity(struct qpnp_qg *chip, int *soc)
	return 0;
}

static int qg_get_ttf_param(void *data, enum ttf_param param, int *val)
{
	union power_supply_propval prop = {0, };
	struct qpnp_qg *chip = data;
	int rc = 0;
	int64_t temp = 0;

	if (!chip)
		return -ENODEV;

	if (chip->battery_missing || !chip->profile_loaded)
		return -EPERM;

	switch (param) {
	case TTF_MSOC:
		rc = qg_get_battery_capacity(chip, val);
		break;
	case TTF_VBAT:
		rc = qg_get_battery_voltage(chip, val);
		break;
	case TTF_IBAT:
		rc = qg_get_battery_current(chip, val);
		break;
	case TTF_FCC:
		if (chip->qg_psy) {
			rc = power_supply_get_property(chip->qg_psy,
				POWER_SUPPLY_PROP_CHARGE_FULL, &prop);
			if (rc >= 0) {
				temp = div64_u64(prop.intval, 1000);
				*val  = div64_u64(chip->full_soc * temp,
						QG_SOC_FULL);
			}
		}
		break;
	case TTF_MODE:
		*val = TTF_MODE_NORMAL;
		break;
	case TTF_ITERM:
		if (chip->chg_iterm_ma == INT_MIN)
			*val = 0;
		else
			*val = chip->chg_iterm_ma;
		break;
	case TTF_RBATT:
		rc = qg_sdam_read(SDAM_RBAT_MOHM, val);
		if (!rc)
			*val *= 1000;
		break;
	case TTF_VFLOAT:
		*val = chip->bp.float_volt_uv;
		break;
	case TTF_CHG_TYPE:
		*val = chip->charge_type;
		break;
	case TTF_CHG_STATUS:
		*val = chip->charge_status;
		break;
	default:
		pr_err("Unsupported property %d\n", param);
		rc = -EINVAL;
		break;
	}

	return rc;
}

static int qg_ttf_awake_voter(void *data, bool val)
{
	struct qpnp_qg *chip = data;

	if (!chip)
		return -ENODEV;

	if (chip->battery_missing || !chip->profile_loaded)
		return -EPERM;

	vote(chip->awake_votable, TTF_AWAKE_VOTER, val, 0);

	return 0;
}

static int qg_psy_set_property(struct power_supply *psy,
			       enum power_supply_property psp,
			       const union power_supply_propval *pval)
@@ -1685,6 +1779,12 @@ static int qg_psy_get_property(struct power_supply *psy,
	case POWER_SUPPLY_PROP_CYCLE_COUNT:
		rc = get_cycle_count(chip->counter, &pval->intval);
		break;
	case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
		rc = ttf_get_time_to_full(chip->ttf, &pval->intval);
		break;
	case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
		rc = ttf_get_time_to_empty(chip->ttf, &pval->intval);
		break;
	default:
		pr_debug("Unsupported property %d\n", psp);
		break;
@@ -1726,6 +1826,8 @@ static enum power_supply_property qg_psy_props[] = {
	POWER_SUPPLY_PROP_CYCLE_COUNTS,
	POWER_SUPPLY_PROP_CHARGE_FULL,
	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
	POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
	POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
};

static const struct power_supply_desc qg_psy_desc = {
@@ -1912,6 +2014,8 @@ static void qg_status_change_work(struct work_struct *work)
	rc = qg_charge_full_update(chip);
	if (rc < 0)
		pr_err("Failed in charge_full_update, rc=%d\n", rc);

	ttf_update(chip->ttf, chip->usb_present);
out:
	pm_relax(chip->dev);
}
@@ -2688,10 +2792,12 @@ static int qg_request_irqs(struct qpnp_qg *chip)
	return 0;
}

#define QG_TTF_ITERM_DELTA_MA		1
static int qg_alg_init(struct qpnp_qg *chip)
{
	struct cycle_counter *counter;
	struct cap_learning *cl;
	struct ttf *ttf;
	struct device_node *node = chip->dev->of_node;
	int rc;

@@ -2714,6 +2820,28 @@ static int qg_alg_init(struct qpnp_qg *chip)

	chip->counter = counter;

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

	ttf->get_ttf_param = qg_get_ttf_param;
	ttf->awake_voter = qg_ttf_awake_voter;
	ttf->iterm_delta = QG_TTF_ITERM_DELTA_MA;
	ttf->data = chip;

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

	chip->ttf = ttf;

	chip->dt.cl_disable = of_property_read_bool(node,
					"qcom,cl-disable");

@@ -2738,6 +2866,7 @@ static int qg_alg_init(struct qpnp_qg *chip)
		counter->data = NULL;
		cl->data = NULL;
		devm_kfree(chip->dev, counter);
		devm_kfree(chip->dev, ttf);
		devm_kfree(chip->dev, cl);
		return rc;
	}
@@ -3064,6 +3193,7 @@ static int process_suspend(struct qpnp_qg *chip)
	if (!chip->profile_loaded)
		return 0;

	cancel_delayed_work_sync(&chip->ttf->ttf_work);
	/* disable GOOD_OCV IRQ in sleep */
	vote(chip->good_ocv_irq_disable_votable,
			QG_INIT_STATE_IRQ_DISABLE, true, 0);
@@ -3196,6 +3326,8 @@ static int process_resume(struct qpnp_qg *chip)
		chip->suspend_data = false;
	}

	schedule_delayed_work(&chip->ttf->ttf_work, 0);

	return rc;
}

@@ -3273,6 +3405,8 @@ static int qpnp_qg_probe(struct platform_device *pdev)
	chip->maint_soc = -EINVAL;
	chip->batt_soc = INT_MIN;
	chip->cc_soc = INT_MIN;
	chip->full_soc = QG_SOC_FULL;
	chip->chg_iterm_ma = INT_MIN;

	rc = qg_alg_init(chip);
	if (rc < 0) {
@@ -3338,6 +3472,7 @@ static int qpnp_qg_probe(struct platform_device *pdev)
			pr_err("Error in restoring cycle_count, rc=%d\n", rc);
			return rc;
		}
		schedule_delayed_work(&chip->ttf->ttf_work, 10000);
	}

	rc = qg_determine_pon_soc(chip);
Loading