blob: b23181c4b03ea9913ba471aa8ad0b01f95aaa507 [file] [log] [blame]
Ken Tsou8acade12020-07-09 03:17:35 +08001/*
AleX Pelosi78a4bea2020-09-01 19:02:24 -07002 * Copyright 2018 Google, LLC
Ken Tsou8acade12020-07-09 03:17:35 +08003 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15#include <linux/kernel.h>
16#include <linux/printk.h>
Ken Tsou8acade12020-07-09 03:17:35 +080017#include <linux/of.h>
AleX Pelosi4a9035f2020-02-28 19:09:57 -080018#include <linux/slab.h>
Ken Tsou8acade12020-07-09 03:17:35 +080019#include "google_bms.h"
20#include "google_psy.h"
21#include "qmath.h"
Ken Tsou8acade12020-07-09 03:17:35 +080022
23#ifdef CONFIG_DEBUG_FS
24#include <linux/debugfs.h>
25#endif
26
27#define ELAP_LIMIT_S 60
28
AleX Pelosid0319472020-02-29 12:42:59 -080029
30void ttf_log(const struct batt_ttf_stats *stats, const char *fmt, ...)
31{
32 va_list args;
33
34 va_start(args, fmt);
35 logbuffer_vlog(stats->ttf_log, fmt, args);
36 va_end(args);
37}
38
Ken Tsou8acade12020-07-09 03:17:35 +080039/* actual adapter current capability for this charging event
40 * NOTE: peformance for a tier are known only after entering the tier
41 */
Jenny Ho6f7ec522020-05-19 09:04:53 +080042static int ttf_pwr_icl(const struct gbms_ce_tier_stats *ts,
43 const union gbms_ce_adapter_details *ad)
Ken Tsou8acade12020-07-09 03:17:35 +080044{
Ken Tsou8acade12020-07-09 03:17:35 +080045 int elap, amperage;
46
47 elap = ts->time_fast + ts->time_taper;
48 if (elap <= ELAP_LIMIT_S)
Jenny Ho6f7ec522020-05-19 09:04:53 +080049 amperage = ad->ad_amperage * 100;
Ken Tsou8acade12020-07-09 03:17:35 +080050 else
51 amperage = ts->icl_sum / (elap + ts->time_other);
52
53 return amperage;
54}
55
56/* NOTE: the current in taper might need to be accounted in a different way */
Jenny Ho915dc482022-03-21 09:13:57 +080057int ttf_pwr_ibatt(const struct gbms_ce_tier_stats *ts)
Ken Tsou8acade12020-07-09 03:17:35 +080058{
Ken Tsou8acade12020-07-09 03:17:35 +080059 int avg_ibatt, elap, sign = 1;
60
61 elap = ts->time_fast + ts->time_taper;
Jenny Ho6f7ec522020-05-19 09:04:53 +080062 /* averages are not reliable until after some time in tier */
Ken Tsou8acade12020-07-09 03:17:35 +080063 if (elap <= ELAP_LIMIT_S) {
Jenny Ho6f7ec522020-05-19 09:04:53 +080064 pr_debug("%s: limit=%d elap=%d (%d+%d) o=%d\n", __func__,
65 elap, ELAP_LIMIT_S, ts->time_fast, ts->time_taper,
66 ts->time_other);
Ken Tsou8acade12020-07-09 03:17:35 +080067 return 0;
68 }
69
Jenny Ho6f7ec522020-05-19 09:04:53 +080070 /* actual, called only when avg_ibatt in tier indicates charging */
Ken Tsou8acade12020-07-09 03:17:35 +080071 avg_ibatt = ts->ibatt_sum / (elap + ts->time_other);
72 if (avg_ibatt < 0)
73 sign = -1;
74
Jenny Ho6f7ec522020-05-19 09:04:53 +080075 pr_debug("%s: elap=%d (%d+%d+%d) sum=%ld avg_ibatt=%d\n", __func__,
76 elap, ts->time_fast, ts->time_taper, ts->time_other,
77 ts->ibatt_sum, avg_ibatt * sign);
Ken Tsou8acade12020-07-09 03:17:35 +080078
79 return avg_ibatt * sign;
80}
81
82/* nominal voltage tier index for this soc */
Jenny Ho915dc482022-03-21 09:13:57 +080083int ttf_pwr_vtier_idx(const struct batt_ttf_stats *stats, int soc)
Ken Tsou8acade12020-07-09 03:17:35 +080084{
85 int i;
86
87 for (i = 1; i < GBMS_STATS_TIER_COUNT; i++)
88 if (soc < stats->tier_stats[i].soc_in >> 8)
89 break;
90
91 return i - 1;
92}
93
Jenny Ho6f7ec522020-05-19 09:04:53 +080094/*
95 * reference or current average current demand for a soc at max rate.
96 * NOTE: always <= cc_max for reference temperature
Ken Tsou8acade12020-07-09 03:17:35 +080097 */
Jenny Ho915dc482022-03-21 09:13:57 +080098int ttf_ref_cc(const struct batt_ttf_stats *stats, int soc)
Ken Tsou8acade12020-07-09 03:17:35 +080099{
100 const struct ttf_soc_stats *sstat = NULL;
101 int delta_cc;
102
Jenny Ho915dc482022-03-21 09:13:57 +0800103 /* out of range */
104 if (soc + 1 >= GBMS_SOC_STATS_LEN)
105 return 0;
106
Ken Tsou8acade12020-07-09 03:17:35 +0800107 /* soc average current demand */
Jenny Ho93140142020-08-26 12:52:02 +0800108 if (stats->soc_stats.cc[soc + 1] && stats->soc_stats.cc[soc] &&
109 stats->soc_stats.elap[soc])
Ken Tsou8acade12020-07-09 03:17:35 +0800110 sstat = &stats->soc_stats;
Jenny Ho93140142020-08-26 12:52:02 +0800111 else if (stats->soc_ref.cc[soc + 1] && stats->soc_ref.cc[soc] &&
112 stats->soc_ref.elap[soc])
Ken Tsou8acade12020-07-09 03:17:35 +0800113 sstat = &stats->soc_ref;
114 else
115 return 0;
116
117 delta_cc = (sstat->cc[soc + 1] - sstat->cc[soc]);
118
AleX Pelosic687b092020-08-27 18:23:57 -0700119 pr_debug("%s %d: delta_cc=%d elap=%ld\n", __func__, soc,
120 delta_cc, sstat->elap[soc]);
121
Ken Tsou8acade12020-07-09 03:17:35 +0800122 return (delta_cc * 3600) / sstat->elap[soc];
123}
124
Jenny Ho6f7ec522020-05-19 09:04:53 +0800125/* assumes that health is active for any soc greater than CHG_HEALTH_REST_SOC */
126static int ttf_pwr_health(const struct gbms_charging_event *ce_data,
127 int soc)
128{
129 return CHG_HEALTH_REST_IS_ACTIVE(&ce_data->ce_health) &&
130 soc >= CHG_HEALTH_REST_SOC(&ce_data->ce_health);
131}
132
Jenny Hoa4211822021-05-11 11:19:13 +0800133static int ttf_pwr_health_pause(const struct gbms_charging_event *ce_data,
134 int soc)
135{
136 return CHG_HEALTH_REST_IS_PAUSE(&ce_data->ce_health) &&
137 soc >= CHG_HEALTH_REST_SOC(&ce_data->ce_health);
138}
139
Jenny Ho6f7ec522020-05-19 09:04:53 +0800140/*
141 * equivalent icl: minimum between actual input current limit and battery
142 * everage current _while_in_tier. actual_icl will be lower in high current
143 * tiers for bad cables, ibatt is affected by temperature tier and sysload.
144 */
145static int ttf_pwr_equiv_icl(const struct gbms_charging_event *ce_data,
146 int vbatt_idx, int soc)
147{
AleX Pelosic687b092020-08-27 18:23:57 -0700148 const struct gbms_chg_profile *profile = ce_data->chg_profile;
149 const int aratio = (ce_data->adapter_details.ad_voltage * 10000) /
150 (profile->volt_limits[vbatt_idx] / 1000);
Jenny Ho6f7ec522020-05-19 09:04:53 +0800151 const struct gbms_ce_tier_stats *tier_stats;
AleX Pelosic687b092020-08-27 18:23:57 -0700152 const int efficiency = 95; /* TODO: use real efficiency */
Jenny Hoa4211822021-05-11 11:19:13 +0800153 const u32 capacity_ma = profile->capacity_ma;
154 const int rest_rate = ce_data->ce_health.rest_rate;
AleX Pelosic687b092020-08-27 18:23:57 -0700155 int equiv_icl, act_icl, act_ibatt, health_ibatt = -1;
Jenny Ho6f7ec522020-05-19 09:04:53 +0800156
AleX Pelosic687b092020-08-27 18:23:57 -0700157 /* Health collects in ce_data->health_stats vtier */
Jenny Ho6f7ec522020-05-19 09:04:53 +0800158 if (ttf_pwr_health(ce_data, soc)) {
AleX Pelosic687b092020-08-27 18:23:57 -0700159 health_ibatt = ce_data->ce_health.rest_cc_max / 1000;
Jenny Ho6f7ec522020-05-19 09:04:53 +0800160 tier_stats = &ce_data->health_stats;
Jenny Hoa4211822021-05-11 11:19:13 +0800161 } else if (ttf_pwr_health_pause(ce_data, soc)) {
162 /* use ACTIVE current in PAUSE stat for ttf calculation */
163 health_ibatt = (capacity_ma * rest_rate * 10) / 1000;
164 /* use ACTIVE tier in PAUSE stat for ttf calculation */
165 tier_stats = &ce_data->health_stats;
Jenny Ho6f7ec522020-05-19 09:04:53 +0800166 } else {
167 tier_stats = &ce_data->tier_stats[vbatt_idx];
168 }
169
AleX Pelosic687b092020-08-27 18:23:57 -0700170 /*
171 * actual adapter capabilities at adapter voltage for vtier
172 * NOTE: demand and cable might cause voltage and icl do droop
173 */
Jenny Ho6f7ec522020-05-19 09:04:53 +0800174 act_icl = ttf_pwr_icl(tier_stats, &ce_data->adapter_details);
175 if (act_icl <= 0) {
AleX Pelosic687b092020-08-27 18:23:57 -0700176 pr_debug("%s: negative,null act_icl=%d\n", __func__, act_icl);
177 return -EINVAL;
Jenny Ho6f7ec522020-05-19 09:04:53 +0800178 }
179
AleX Pelosic687b092020-08-27 18:23:57 -0700180 /* scale icl (at adapter voltage) to vtier */
Jenny Ho075989f2022-07-01 13:20:48 +0000181 equiv_icl = (act_icl * aratio * efficiency) / 10000;
AleX Pelosic687b092020-08-27 18:23:57 -0700182 pr_debug("%s: act_icl=%d aratio=%d equiv_icl=%d\n",
183 __func__, act_icl, aratio, equiv_icl);
Jenny Ho6f7ec522020-05-19 09:04:53 +0800184
185 /* actual ibatt in this tier: act_ibatt==0 when too early to tell */
186 act_ibatt = ttf_pwr_ibatt(tier_stats);
AleX Pelosic687b092020-08-27 18:23:57 -0700187 if (act_ibatt == 0 && health_ibatt > 0)
188 act_ibatt = health_ibatt;
Jenny Ho6f7ec522020-05-19 09:04:53 +0800189 if (act_ibatt < 0) {
190 pr_debug("%s: discharging ibatt=%d\n", __func__, act_ibatt);
191 return -EINVAL;
192 }
193
AleX Pelosic687b092020-08-27 18:23:57 -0700194 /* assume that can deliver equiv_icl when act_ibatt == 0 */
195 if (act_ibatt > 0 && act_ibatt < equiv_icl) {
Jenny Ho6f7ec522020-05-19 09:04:53 +0800196 pr_debug("%s: sysload ibatt=%d, reduce icl %d->%d\n",
AleX Pelosic687b092020-08-27 18:23:57 -0700197 __func__, act_ibatt, equiv_icl, act_ibatt);
198 equiv_icl = act_ibatt;
Jenny Ho6f7ec522020-05-19 09:04:53 +0800199 }
200
AleX Pelosic687b092020-08-27 18:23:57 -0700201 pr_debug("%s: equiv_icl=%d\n", __func__, equiv_icl);
202 return equiv_icl;
Jenny Ho6f7ec522020-05-19 09:04:53 +0800203}
204
205/*
206 * time scaling factor for available power and SOC demand.
Ken Tsou8acade12020-07-09 03:17:35 +0800207 * NOTE: usually called when soc < ssoc_in && soc > ce_data->last_soc
208 * TODO: this is very inefficient
209 */
210static int ttf_pwr_ratio(const struct batt_ttf_stats *stats,
211 const struct gbms_charging_event *ce_data,
212 int soc)
213{
Jenny Ho6f7ec522020-05-19 09:04:53 +0800214 const struct gbms_chg_profile *profile = ce_data->chg_profile;
215 int cc_max, vbatt_idx, temp_idx;
AleX Pelosic687b092020-08-27 18:23:57 -0700216 int avg_cc, equiv_icl;
Jenny Ho6f7ec522020-05-19 09:04:53 +0800217 int ratio;
Ken Tsou8acade12020-07-09 03:17:35 +0800218
Jenny Ho6f7ec522020-05-19 09:04:53 +0800219 /* regular charging tier */
220 vbatt_idx = ttf_pwr_vtier_idx(stats, soc);
Ken Tsou8acade12020-07-09 03:17:35 +0800221 if (vbatt_idx < 0)
AleX Pelosi0c88ff32020-04-02 01:04:51 -0700222 return -EINVAL;
Ken Tsou8acade12020-07-09 03:17:35 +0800223
224 /* TODO: compensate with average increase/decrease of temperature? */
225 temp_idx = ce_data->tier_stats[vbatt_idx].temp_idx;
226 if (temp_idx == -1) {
AleX Pelosi0c88ff32020-04-02 01:04:51 -0700227 int64_t t_avg = 0;
Ken Tsou8acade12020-07-09 03:17:35 +0800228 const int elap = ce_data->tier_stats[vbatt_idx].time_fast +
229 ce_data->tier_stats[vbatt_idx].time_taper +
230 ce_data->tier_stats[vbatt_idx].time_other;
Ken Tsou8acade12020-07-09 03:17:35 +0800231
AleX Pelosi0c88ff32020-04-02 01:04:51 -0700232 if (ce_data->tier_stats[vbatt_idx].temp_sum != 0 || elap == 0)
Ken Tsou8acade12020-07-09 03:17:35 +0800233 t_avg = ce_data->tier_stats[vbatt_idx].temp_in;
234 if (t_avg == 0)
235 t_avg = 250;
236
Jenny Ho6f7ec522020-05-19 09:04:53 +0800237 /* average temperature in tier for charge tier index */
Ken Tsou8acade12020-07-09 03:17:35 +0800238 temp_idx = gbms_msc_temp_idx(profile, t_avg);
Jenny Ho6f7ec522020-05-19 09:04:53 +0800239 pr_debug("%s %d: temp_idx=%d t_avg=%ld sum=%ld elap=%d\n",
240 __func__, soc, temp_idx, t_avg,
Ken Tsou8acade12020-07-09 03:17:35 +0800241 ce_data->tier_stats[vbatt_idx].temp_sum,
242 elap);
243
244 if (temp_idx < 0)
AleX Pelosi0c88ff32020-04-02 01:04:51 -0700245 return -EINVAL;
Ken Tsou8acade12020-07-09 03:17:35 +0800246 }
247
Jenny Ho6f7ec522020-05-19 09:04:53 +0800248 /* max tier demand for voltage tier at this temperature index */
Ken Tsou8acade12020-07-09 03:17:35 +0800249 cc_max = GBMS_CCCM_LIMITS(profile, temp_idx, vbatt_idx) / 1000;
Jenny Ho6f7ec522020-05-19 09:04:53 +0800250 /* statistical current demand for soc (<= cc_max) */
251 avg_cc = ttf_ref_cc(stats, soc);
AleX Pelosic687b092020-08-27 18:23:57 -0700252 if (avg_cc <= 0) {
253 /* default to cc_max if we have no data */
Jenny Ho6f7ec522020-05-19 09:04:53 +0800254 pr_debug("%s %d: demand use default avg_cc=%d->%d\n",
255 __func__, soc, avg_cc, cc_max);
Ken Tsou8acade12020-07-09 03:17:35 +0800256 avg_cc = cc_max;
257 }
258
Jenny Ho6f7ec522020-05-19 09:04:53 +0800259 /* statistical or reference max power demand for the tier at */
AleX Pelosic687b092020-08-27 18:23:57 -0700260 pr_debug("%s %d:%d,%d: avg_cc=%d cc_max=%d\n",
261 __func__, soc, temp_idx, vbatt_idx,
Jenny Ho6f7ec522020-05-19 09:04:53 +0800262 avg_cc, cc_max);
Ken Tsou8acade12020-07-09 03:17:35 +0800263
AleX Pelosic687b092020-08-27 18:23:57 -0700264 /* equivalent input current for adapter at vtier */
265 equiv_icl = ttf_pwr_equiv_icl(ce_data, vbatt_idx, soc);
266 if (equiv_icl <= 0) {
Jenny Ho6f7ec522020-05-19 09:04:53 +0800267 pr_debug("%s %d: negative, null act_icl=%d\n",
AleX Pelosic687b092020-08-27 18:23:57 -0700268 __func__, soc, equiv_icl);
AleX Pelosi0c88ff32020-04-02 01:04:51 -0700269 return -EINVAL;
Ken Tsou8acade12020-07-09 03:17:35 +0800270 }
271
AleX Pelosic687b092020-08-27 18:23:57 -0700272 /* lower to cc_max if in HOT and COLD */
273 if (cc_max < equiv_icl) {
Jenny Ho6f7ec522020-05-19 09:04:53 +0800274 pr_debug("%s %d: reduce act_icl=%d to cc_max=%d\n",
AleX Pelosic687b092020-08-27 18:23:57 -0700275 __func__, soc, equiv_icl, cc_max);
276 equiv_icl = cc_max;
Ken Tsou8acade12020-07-09 03:17:35 +0800277 }
278
Jenny Ho6f7ec522020-05-19 09:04:53 +0800279 /*
AleX Pelosic687b092020-08-27 18:23:57 -0700280 * This is the trick that makes everything work:
281 * equiv_icl = min(act_icl, act_ibatt, cc_max)
Jenny Ho6f7ec522020-05-19 09:04:53 +0800282 *
AleX Pelosic687b092020-08-27 18:23:57 -0700283 * act_icl = adapter max or adapter actual icl (due to bad cable,
284 * AC enabled or temperature shift) scaled to vtier
285 * act_ibatt = measured for
286 * at reference temperature or actual < cc_max due to sysload
287 * cc_max = cc_max from profile (lower than ref for HOT or COLD)
Jenny Ho6f7ec522020-05-19 09:04:53 +0800288 *
Ken Tsou8acade12020-07-09 03:17:35 +0800289 */
Ken Tsou8acade12020-07-09 03:17:35 +0800290
Jenny Ho6f7ec522020-05-19 09:04:53 +0800291 /* ratio for elap time: it doesn't work if reference is not maximal */
AleX Pelosic687b092020-08-27 18:23:57 -0700292 if (equiv_icl < avg_cc)
293 ratio = (avg_cc * 100) / equiv_icl;
Ken Tsou8acade12020-07-09 03:17:35 +0800294 else
295 ratio = 100;
296
AleX Pelosic687b092020-08-27 18:23:57 -0700297 pr_debug("%s %d: equiv_icl=%d, avg_cc=%d ratio=%d\n",
298 __func__, soc, equiv_icl, avg_cc, ratio);
Ken Tsou8acade12020-07-09 03:17:35 +0800299
300 return ratio;
301}
302
303/* SOC estimates --------------------------------------------------------- */
304
Jenny Ho6f7ec522020-05-19 09:04:53 +0800305/* reference of current elap for a soc at max rate */
306static int ttf_ref_elap(const struct batt_ttf_stats *stats, int soc)
Ken Tsou8acade12020-07-09 03:17:35 +0800307{
AleX Pelosiab0e9d42020-09-29 11:13:19 -0700308 ktime_t elap;
Ken Tsou8acade12020-07-09 03:17:35 +0800309
Jenny Ho6f7ec522020-05-19 09:04:53 +0800310 if (soc < 0 || soc >= 100)
Ken Tsou8acade12020-07-09 03:17:35 +0800311 return 0;
Ken Tsou8acade12020-07-09 03:17:35 +0800312
Jenny Ho6f7ec522020-05-19 09:04:53 +0800313 elap = stats->soc_stats.elap[soc];
Ken Tsou8acade12020-07-09 03:17:35 +0800314 if (elap == 0)
Jenny Ho6f7ec522020-05-19 09:04:53 +0800315 elap = stats->soc_ref.elap[soc];
Ken Tsou8acade12020-07-09 03:17:35 +0800316
Jenny Ho6f7ec522020-05-19 09:04:53 +0800317 return elap;
318}
319
320/* elap time for a single soc% */
321static int ttf_elap(ktime_t *estimate, const struct batt_ttf_stats *stats,
322 const struct gbms_charging_event *ce_data,
323 int soc)
324{
325 ktime_t elap;
326 int ratio;
327
328 /* cannot really return 0 elap unless the data is corrupted */
329 elap = ttf_ref_elap(stats, soc);
330 if (elap == 0) {
331 pr_debug("%s %d: zero elap\n", __func__, soc);
Ken Tsou8acade12020-07-09 03:17:35 +0800332 return -EINVAL;
333 }
334
Jenny Ho6f7ec522020-05-19 09:04:53 +0800335 ratio = ttf_pwr_ratio(stats, ce_data, soc);
336 if (ratio < 0) {
337 pr_debug("%s %d: negative ratio=%d\n", __func__, soc, ratio);
338 return -EINVAL;
339 }
340
Ken Tsou8acade12020-07-09 03:17:35 +0800341 *estimate = elap * ratio;
342
Jenny Ho6f7ec522020-05-19 09:04:53 +0800343 pr_debug("%s: soc=%d estimate=%lld elap=%lld ratio=%d\n",
344 __func__, soc, *estimate, elap, ratio);
345
Jenny Hob7fc3382021-10-23 21:57:49 +0800346 return ratio;
Ken Tsou8acade12020-07-09 03:17:35 +0800347}
348
Jenny Ho6f7ec522020-05-19 09:04:53 +0800349/*
350 * time to full from SOC% using the actual stats
Ken Tsou8acade12020-07-09 03:17:35 +0800351 * NOTE: prediction is based stats and corrected with the ce_data
352 * NOTE: usually called with soc > ce_data->last_soc
353 */
Jenny Ho6f7ec522020-05-19 09:04:53 +0800354int ttf_soc_estimate(ktime_t *res, const struct batt_ttf_stats *stats,
Ken Tsou8acade12020-07-09 03:17:35 +0800355 const struct gbms_charging_event *ce_data,
356 qnum_t soc, qnum_t last)
357{
358 const int ssoc_in = ce_data->charging_stats.ssoc_in;
AleX Pelosicf7008d2020-08-13 13:48:10 -0700359 ktime_t elap, estimate = 0;
Jenny Hob7fc3382021-10-23 21:57:49 +0800360 int i = 0, ratio, frac, max_ratio = 0;
Ken Tsou8acade12020-07-09 03:17:35 +0800361
AleX Pelosicf7008d2020-08-13 13:48:10 -0700362 if (last > qnum_rconst(100) || last < soc)
Ken Tsou8acade12020-07-09 03:17:35 +0800363 return -EINVAL;
364
AleX Pelosicf7008d2020-08-13 13:48:10 -0700365 if (last == soc) {
366 *res = 0;
367 return 0;
368 }
Ken Tsou8acade12020-07-09 03:17:35 +0800369
Jenny Ho6f7ec522020-05-19 09:04:53 +0800370 /* FIRST: 100 - first 2 digits of the fractional part of soc if any */
AleX Pelosicf7008d2020-08-13 13:48:10 -0700371 frac = (int)qnum_nfracdgt(soc, 2);
372 if (frac) {
373
Jenny Hob7fc3382021-10-23 21:57:49 +0800374 ratio = ttf_elap(&elap, stats, ce_data, qnum_toint(soc));
375 if (ratio >= 0)
AleX Pelosicf7008d2020-08-13 13:48:10 -0700376 estimate += (elap * (100 - frac)) / 100;
Jenny Ho48823402022-08-15 06:57:00 +0000377 if (ratio > max_ratio)
378 max_ratio = ratio;
Jenny Ho6f7ec522020-05-19 09:04:53 +0800379
380 i += 1;
AleX Pelosicf7008d2020-08-13 13:48:10 -0700381 }
382
Jenny Ho6f7ec522020-05-19 09:04:53 +0800383 /* accumulate ttf_elap starting from i + 1 until end */
384 for (i += qnum_toint(soc); i < qnum_toint(last); i++) {
Ken Tsou8acade12020-07-09 03:17:35 +0800385
386 if (i >= ssoc_in && i < ce_data->last_soc) {
387 /* use real data if within charging event */
388 elap = ce_data->soc_stats.elap[i] * 100;
389 } else {
390 /* future (and soc before ssoc_in) */
Jenny Hob7fc3382021-10-23 21:57:49 +0800391 ratio = ttf_elap(&elap, stats, ce_data, i);
392 if (ratio < 0)
393 return ratio;
394 if (ratio > max_ratio)
395 max_ratio = ratio;
Ken Tsou8acade12020-07-09 03:17:35 +0800396 }
397
398 estimate += elap;
399 }
400
Jenny Ho6f7ec522020-05-19 09:04:53 +0800401 /* LAST: first 2 digits of the fractional part of soc if any */
AleX Pelosicf7008d2020-08-13 13:48:10 -0700402 frac = (int)qnum_nfracdgt(last, 2);
403 if (frac) {
Jenny Hob7fc3382021-10-23 21:57:49 +0800404 ratio = ttf_elap(&elap, stats, ce_data, qnum_toint(last));
405 if (ratio >= 0)
AleX Pelosicf7008d2020-08-13 13:48:10 -0700406 estimate += (elap * frac) / 100;
Jenny Ho48823402022-08-15 06:57:00 +0000407 if (ratio > max_ratio)
408 max_ratio = ratio;
AleX Pelosicf7008d2020-08-13 13:48:10 -0700409 }
410
Ken Tsou8acade12020-07-09 03:17:35 +0800411 *res = estimate / 100;
Jenny Hob7fc3382021-10-23 21:57:49 +0800412 return max_ratio;
Ken Tsou8acade12020-07-09 03:17:35 +0800413}
414
Jenny Ho6f7ec522020-05-19 09:04:53 +0800415int ttf_soc_cstr(char *buff, int size, const struct ttf_soc_stats *soc_stats,
Ken Tsou8acade12020-07-09 03:17:35 +0800416 int start, int end)
417{
418 int i, len = 0, split = 100;
419
420 if (start < 0 || start >= GBMS_SOC_STATS_LEN ||
421 end < 0 || end >= GBMS_SOC_STATS_LEN ||
422 start > end)
423 return 0;
424
Jenny Ho93e41c22020-04-28 18:23:51 +0800425 len += scnprintf(&buff[len], size - len, "\n");
426
Ken Tsou8acade12020-07-09 03:17:35 +0800427 /* only one way to print data @ 100 */
428 if (end == 100 && start != 100)
429 end = 99;
430 /* std newline every 10 entries */
431 if (start == 0 && end == 99)
432 split = 10;
433
AleX Pelosi4a9035f2020-02-28 19:09:57 -0800434 /* dump elap time as T: */
Ken Tsou8acade12020-07-09 03:17:35 +0800435 for (i = start; i <= end; i++) {
436 if (i % split == 0 || i == start) {
AleX Pelosi4a9035f2020-02-28 19:09:57 -0800437 len += scnprintf(&buff[len], size - len, "T");
Ken Tsou8acade12020-07-09 03:17:35 +0800438 if (split == 10)
439 len += scnprintf(&buff[len], size - len,
440 "%d", i / 10);
AleX Pelosi4a9035f2020-02-28 19:09:57 -0800441 len += scnprintf(&buff[len], size - len, ":");
Ken Tsou8acade12020-07-09 03:17:35 +0800442 }
443
444 len += scnprintf(&buff[len], size - len, " %4ld",
445 soc_stats->elap[i]);
446 if (i != end && (i + 1) % split == 0)
447 len += scnprintf(&buff[len], size - len, "\n");
448 }
449
450 len += scnprintf(&buff[len], size - len, "\n");
451
AleX Pelosi4a9035f2020-02-28 19:09:57 -0800452 /* dump coulumb count as C: */
Ken Tsou8acade12020-07-09 03:17:35 +0800453 for (i = start; i <= end; i++) {
454 if (i % split == 0 || i == start) {
AleX Pelosi4a9035f2020-02-28 19:09:57 -0800455 len += scnprintf(&buff[len], size - len, "C");
Ken Tsou8acade12020-07-09 03:17:35 +0800456 if (split == 10)
457 len += scnprintf(&buff[len], size - len,
458 "%d", i / 10);
AleX Pelosi4a9035f2020-02-28 19:09:57 -0800459 len += scnprintf(&buff[len], size - len, ":");
Ken Tsou8acade12020-07-09 03:17:35 +0800460 }
461
462 len += scnprintf(&buff[len], size - len, " %4d",
463 soc_stats->cc[i]);
464 if (i != end && (i + 1) % split == 0)
465 len += scnprintf(&buff[len], size - len, "\n");
466 }
467
468 len += scnprintf(&buff[len], size - len, "\n");
469
470 return len;
471}
472
AleX Pelosi4a9035f2020-02-28 19:09:57 -0800473/* TODO: tune these values */
474
475/* discard updates for adapters that have less than 80% of nominal */
476#define TTF_SOC_QUAL_ELAP_RATIO_MAX 200
477/* cap updates of cc to no more of +-*_CUR_ABS_MAX from previous */
478#define TTF_SOC_QUAL_ELAP_DELTA_CUR_ABS_MAX 60
479/* cap udpdates to cc max to no more of +-20% of reference */
480#define TTF_SOC_QUAL_ELAP_DELTA_REF_PCT_MAX 20
481
482/* return the weight to apply to this change */
483static ktime_t ttf_soc_qual_elap(const struct batt_ttf_stats *stats,
484 const struct gbms_charging_event *ce_data,
485 int i)
486{
487 const struct ttf_soc_stats *src = &ce_data->soc_stats;
488 const struct ttf_soc_stats *dst = &stats->soc_stats;
489 const int limit = TTF_SOC_QUAL_ELAP_RATIO_MAX;
490 const int max_elap = ((100 + TTF_SOC_QUAL_ELAP_DELTA_REF_PCT_MAX) *
491 stats->soc_ref.elap[i]) / 100;
492 const int min_elap = ((100 - TTF_SOC_QUAL_ELAP_DELTA_REF_PCT_MAX) *
493 stats->soc_ref.elap[i]) / 100;
494 ktime_t elap, elap_new, elap_cur;
495 int ratio;
496
497 if (!src->elap[i])
498 return 0;
499
500 /* weight the adapter, discard if ratio is too high (poor adapter) */
501 ratio = ttf_pwr_ratio(stats, ce_data, i);
502 if (ratio <= 0 || ratio > limit) {
503 pr_debug("%d: ratio=%d limit=%d\n", i, ratio, limit);
504 return 0;
505 }
506
507 elap_new = (src->elap[i] * 100) / ratio;
508 elap_cur = dst->elap[i];
509 if (!elap_cur)
510 elap_cur = stats->soc_ref.elap[i];
511 elap = (elap_cur + elap_new) / 2;
512
513 /* bounds check to previous */
514 if (elap > (elap_cur + TTF_SOC_QUAL_ELAP_DELTA_CUR_ABS_MAX))
515 elap = elap_cur + TTF_SOC_QUAL_ELAP_DELTA_CUR_ABS_MAX;
516 else if (elap < (elap_cur - TTF_SOC_QUAL_ELAP_DELTA_CUR_ABS_MAX))
517 elap = elap_cur - TTF_SOC_QUAL_ELAP_DELTA_CUR_ABS_MAX;
518
519 /* bounds check to reference */
520 if (elap > max_elap)
521 elap = max_elap;
522 else if (elap < min_elap)
523 elap = min_elap;
524
525 pr_debug("%d: dst->elap=%ld, ref_elap=%ld, elap=%ld, src_elap=%ld ratio=%d, min=%d max=%d\n",
526 i, dst->elap[i], stats->soc_ref.elap[i], elap, src->elap[i],
527 ratio, min_elap, max_elap);
528
529 return elap;
530}
531
532/* cap updates of cc to no more of +-*_CUR_ABS_MAX from previous */
533#define TTF_SOC_QUAL_CC_DELTA_CUR_ABS_MAX 40
534/* cap udpdates to cc max to no more of +-20% of reference */
535#define TTF_SOC_QUAL_CC_DELTA_REF_PCT_MAX 20
536
537static int ttf_soc_qual_cc(const struct batt_ttf_stats *stats,
538 const struct gbms_charging_event *ce_data,
539 int i)
540{
541 const struct ttf_soc_stats *src = &ce_data->soc_stats;
542 const struct ttf_soc_stats *dst = &stats->soc_stats;
543 const int max_cc = ((100 + TTF_SOC_QUAL_CC_DELTA_REF_PCT_MAX) *
544 stats->soc_ref.cc[i]) / 100;
545 const int min_cc = ((100 - TTF_SOC_QUAL_CC_DELTA_REF_PCT_MAX) *
546 stats->soc_ref.cc[i]) / 100;
547 int cc, cc_cur;
548
549 if (!src->cc[i])
550 return 0;
551
552 cc_cur = dst->cc[i];
553 if (cc_cur <= 0)
554 cc_cur = stats->soc_ref.cc[i];
555
556 cc = (cc_cur + src->cc[i]) / 2;
557
558 /* bounds check to previous */
559 if (cc > cc_cur + TTF_SOC_QUAL_CC_DELTA_CUR_ABS_MAX)
560 cc = cc_cur + TTF_SOC_QUAL_CC_DELTA_CUR_ABS_MAX;
561 else if (cc < cc_cur -TTF_SOC_QUAL_CC_DELTA_CUR_ABS_MAX)
562 cc = cc_cur - TTF_SOC_QUAL_CC_DELTA_CUR_ABS_MAX;
563
564 /* bounds check to reference */
565 if (cc > max_cc)
566 cc = max_cc;
567 else if (cc < min_cc)
568 cc = min_cc;
569
AleX Pelosi4f13f9d2021-07-15 23:14:08 -0700570 pr_debug("%d: cc_cur=%d, ref_cc=%d src->cc=%d, cc=%d\n",
571 i, cc_cur, stats->soc_ref.cc[i], src->cc[i], cc);
AleX Pelosi4a9035f2020-02-28 19:09:57 -0800572
573 return cc;
574}
575
Ken Tsou8acade12020-07-09 03:17:35 +0800576/* update soc_stats using the charging event
577 * NOTE: first_soc and last_soc are inclusive, will skip socs that have no
578 * elap and no cc.
579 */
580static void ttf_soc_update(struct batt_ttf_stats *stats,
581 const struct gbms_charging_event *ce_data,
582 int first_soc, int last_soc)
583{
584 const struct ttf_soc_stats *src = &ce_data->soc_stats;
Ken Tsou8acade12020-07-09 03:17:35 +0800585 int i;
586
587 for (i = first_soc; i <= last_soc; i++) {
AleX Pelosi4a9035f2020-02-28 19:09:57 -0800588 ktime_t elap;
589 int cc;
Ken Tsou8acade12020-07-09 03:17:35 +0800590
AleX Pelosi4a9035f2020-02-28 19:09:57 -0800591 /* need to have data on both */
592 if (!src->elap[i] || !src->cc[i])
593 continue;
Ken Tsou8acade12020-07-09 03:17:35 +0800594
AleX Pelosi4a9035f2020-02-28 19:09:57 -0800595 /* average the elap time at soc */
596 elap = ttf_soc_qual_elap(stats, ce_data, i);
597 if (elap)
598 stats->soc_stats.elap[i] = elap;
Ken Tsou8acade12020-07-09 03:17:35 +0800599
AleX Pelosi4a9035f2020-02-28 19:09:57 -0800600 /* average the coulumb count at soc */
601 cc = ttf_soc_qual_cc(stats, ce_data, i);
602 if (cc)
603 stats->soc_stats.cc[i] = cc;
Ken Tsou8acade12020-07-09 03:17:35 +0800604 }
605}
606
607void ttf_soc_init(struct ttf_soc_stats *dst)
608{
609 memset(dst, 0, sizeof(*dst));
610}
611
612/* Tier estimates --------------------------------------------------------- */
613
614#define TTF_STATS_FMT "[%d,%d %d %ld]"
615
616#define BATT_TTF_TS_VALID(ts) \
617 (ts->cc_total != 0 && ts->avg_time != 0)
618
619/* TODO: adjust for adapter capability */
AleX Pelosiab0e9d42020-09-29 11:13:19 -0700620static ktime_t ttf_tier_accumulate(const struct ttf_tier_stat *ts,
Ken Tsou8acade12020-07-09 03:17:35 +0800621 int vbatt_idx,
622 const struct batt_ttf_stats *stats)
623{
AleX Pelosiab0e9d42020-09-29 11:13:19 -0700624 ktime_t estimate = 0;
Ken Tsou8acade12020-07-09 03:17:35 +0800625
626 if (vbatt_idx >= GBMS_STATS_TIER_COUNT)
627 return 0;
628
629 for (; vbatt_idx < GBMS_STATS_TIER_COUNT; vbatt_idx++) {
630
631 /* no data in this tier, sorry */
632 if (!BATT_TTF_TS_VALID(ts))
633 return -ENODATA;
634
635 estimate += ts[vbatt_idx].avg_time;
636 }
637
638 return estimate;
639}
640
641/* */
642static int ttf_tier_sscan(struct batt_ttf_stats *stats,
643 const char *buff,
644 size_t size)
645{
AleX Pelosi42421012020-09-01 17:09:00 -0700646 int j, len = 0;
Ken Tsou8acade12020-07-09 03:17:35 +0800647
Will McVicker0cf4b6d2022-05-03 10:59:17 -0700648 memset(&stats->tier_stats, 0, sizeof(stats->tier_stats));
Ken Tsou8acade12020-07-09 03:17:35 +0800649
Ken Tsou8acade12020-07-09 03:17:35 +0800650 while (buff[len] != '[' && len < size)
651 len++;
652
653 for (j = 0; j < GBMS_STATS_TIER_COUNT; j++) {
AleX Pelosi42421012020-09-01 17:09:00 -0700654 sscanf(&buff[len], TTF_STATS_FMT, &stats->tier_stats[j].soc_in,
655 &stats->tier_stats[j].cc_in,
656 &stats->tier_stats[j].cc_total,
657 &stats->tier_stats[j].avg_time);
Ken Tsou8acade12020-07-09 03:17:35 +0800658
659 len += sizeof(TTF_STATS_FMT) - 1;
660 }
661
662 return 0;
663}
664
AleX Pelosi4a9035f2020-02-28 19:09:57 -0800665int ttf_tier_cstr(char *buff, int size, const struct ttf_tier_stat *tier_stats)
Ken Tsou8acade12020-07-09 03:17:35 +0800666{
667 int len = 0;
668
669 len += scnprintf(&buff[len], size - len,
670 TTF_STATS_FMT,
671 tier_stats->soc_in >> 8,
672 tier_stats->cc_in,
673 tier_stats->cc_total,
674 tier_stats->avg_time);
675 return len;
676}
677
AleX Pelosi4a9035f2020-02-28 19:09:57 -0800678/* average soc_in, cc_in, cc_total an and avg time for charge tier */
Ken Tsou8acade12020-07-09 03:17:35 +0800679static void ttf_tier_update_stats(struct ttf_tier_stat *ttf_ts,
680 const struct gbms_ce_tier_stats *chg_ts,
681 bool force)
682{
683 int elap;
684
685 if (!force) {
686 if (chg_ts->cc_total == 0)
687 return;
688
689 /* TODO: check dsg, qualify with adapter? */
690 }
691
692 /* TODO: check with -1 */
693 if (ttf_ts->soc_in == 0)
694 ttf_ts->soc_in = chg_ts->soc_in;
695 ttf_ts->soc_in = (ttf_ts->soc_in + chg_ts->soc_in) / 2;
696
697 /* TODO: check with -1 */
698 if (ttf_ts->cc_in == 0)
699 ttf_ts->cc_in = chg_ts->cc_in;
700 ttf_ts->cc_in = (ttf_ts->cc_in + chg_ts->cc_in) / 2;
701
702 if (ttf_ts->cc_total == 0)
703 ttf_ts->cc_total = chg_ts->cc_total;
704 ttf_ts->cc_total = (ttf_ts->cc_total + chg_ts->cc_total) / 2;
705
706 /* */
707 elap = chg_ts->time_fast + chg_ts->time_taper + chg_ts->time_other;
708 if (ttf_ts->avg_time == 0)
709 ttf_ts->avg_time = elap;
710
711 /* qualify time with ratio */
712 ttf_ts->avg_time =(ttf_ts->avg_time + elap) / 2;
713}
714
715/* updated tier stats using the charging event
716 * NOTE: the ce has data from 1+ charging voltage and temperature tiers */
717static void ttf_tier_update(struct batt_ttf_stats *stats,
718 const struct gbms_charging_event *data,
719 bool force)
720{
721 int i;
722
723 for (i = 0; i < GBMS_STATS_TIER_COUNT; i++) {
724 const bool last_tier = i == (GBMS_STATS_TIER_COUNT - 1);
725 const struct gbms_ce_tier_stats *chg_ts = &data->tier_stats[i];
726 const struct gbms_ce_stats *chg_s = &data->charging_stats;
727 long elap;
728
729 /* skip data that has a temperature switch */
730 if (chg_ts->temp_idx == -1)
731 continue;
732 /* or entries that have no actual charging */
733 elap = chg_ts->time_fast + chg_ts->time_taper;
734 if (!elap)
735 continue;
736 /* update first tier stats only at low soc_in */
737 if (!force && i == 0 && (chg_ts->soc_in >> 8) > 1)
738 continue;
739 /* update last tier stats only at full */
Jenny Ho1ef60002022-07-24 18:26:51 +0000740 if (!force && last_tier && chg_s->ssoc_out != 100)
Ken Tsou8acade12020-07-09 03:17:35 +0800741 continue;
742
743 /* */
744 ttf_tier_update_stats(&stats->tier_stats[i], chg_ts, false);
745 }
746
747}
748
749/* tier estimates only, */
AleX Pelosiab0e9d42020-09-29 11:13:19 -0700750int ttf_tier_estimate(ktime_t *res, const struct batt_ttf_stats *stats,
Ken Tsou8acade12020-07-09 03:17:35 +0800751 int temp_idx, int vbatt_idx,
752 int capacity, int full_capacity)
753{
AleX Pelosiab0e9d42020-09-29 11:13:19 -0700754 ktime_t estimate = 0;
Ken Tsou8acade12020-07-09 03:17:35 +0800755 const struct ttf_tier_stat *ts;
756
757 /* tier estimates, only when in tier */
758 if (vbatt_idx == -1 && temp_idx == -1)
759 return -EINVAL;
760
761 ts = &stats->tier_stats[vbatt_idx];
762 if (!ts || !BATT_TTF_TS_VALID(ts))
763 return -ENODATA;
764
765 /* accumulate next tier */
766 estimate = ttf_tier_accumulate(ts, vbatt_idx + 1, stats);
767 if (estimate < 0)
768 return -ENODATA;
769
770 /* eyeball current tier
771 * estimate =
772 * (ts->cc_in + ts->cc_total - capacity) *
773 * rs->avg_time) / ts->cc_total
774 */
775
776 /* TODO: adjust for crossing thermals? */
777
778 *res = estimate;
779 return 0;
780}
781
782/* ----------------------------------------------------------------------- */
783
AleX Pelosi4a9035f2020-02-28 19:09:57 -0800784/* QUAL DELTA >= 3 */
785#define TTF_STATS_QUAL_DELTA_MIN 3
786#define TTF_STATS_QUAL_DELTA TTF_STATS_QUAL_DELTA_MIN
787
788static int ttf_soc_cstr_elap(char *buff, int size,
789 const struct ttf_soc_stats *soc_stats,
790 int start, int end)
791{
792 int i, len = 0;
793
794 len += scnprintf(&buff[len], size - len, "T%d:", start);
795 for (i = start; i < end; i++)
796 len += scnprintf(&buff[len], size - len, " %4ld",
797 soc_stats->elap[i]);
798
799 return len;
800}
801
802static int ttf_soc_cstr_cc(char *buff, int size,
803 const struct ttf_soc_stats *soc_stats,
804 int start, int end)
805{
806 int i, len = 0;
807
808 len += scnprintf(&buff[len], size - len, "C%d:", start);
809 for (i = start; i < end; i++)
810 len += scnprintf(&buff[len], size - len, " %4d",
811 soc_stats->cc[i]);
812
813 return len;
814}
815
Ken Tsou8acade12020-07-09 03:17:35 +0800816/* update ttf tier and soc stats using the charging event.
817 * call holding stats->lock
818 */
819void ttf_stats_update(struct batt_ttf_stats *stats,
820 struct gbms_charging_event *ce_data,
821 bool force)
822{
823 int first_soc = ce_data->charging_stats.ssoc_in;
AleX Pelosi4a9035f2020-02-28 19:09:57 -0800824 const int last_soc = ce_data->last_soc;
825 const int delta_soc = last_soc - first_soc;
826 const int limit = force ? TTF_STATS_QUAL_DELTA_MIN :
827 TTF_STATS_QUAL_DELTA;
828 const int tmp_size = PAGE_SIZE;
829 char *tmp;
Ken Tsou8acade12020-07-09 03:17:35 +0800830
AleX Pelosi4a9035f2020-02-28 19:09:57 -0800831 /* skip data short periods */
832 if (delta_soc < limit) {
833 ttf_log(stats, "no updates delta_soc=%d, limit=%d, force=%d",
834 delta_soc, limit, force);
835 return;
836 }
837
Jenny Ho6f7ec522020-05-19 09:04:53 +0800838 /* ignore first nozero and last entry because they are partial */
AleX Pelosi4a9035f2020-02-28 19:09:57 -0800839 for ( ; first_soc <= last_soc; first_soc++)
Ken Tsou8acade12020-07-09 03:17:35 +0800840 if (ce_data->soc_stats.elap[first_soc] != 0)
841 break;
842
AleX Pelosi4a9035f2020-02-28 19:09:57 -0800843 ttf_soc_update(stats, ce_data, first_soc + 1, last_soc - 1);
Ken Tsou8acade12020-07-09 03:17:35 +0800844 ttf_tier_update(stats, ce_data, force);
AleX Pelosi4a9035f2020-02-28 19:09:57 -0800845
846 /* dump update stats to logbuffer */
847 tmp = kzalloc(tmp_size, GFP_KERNEL);
848 if (tmp) {
849 const int split = 10;
850 int i;
851
852 for (i = first_soc + 1;i < last_soc - 1; i += split) {
853 int end_soc = i + split;
854
855 if (end_soc > last_soc - 1)
856 end_soc = last_soc - 1;
857
858 ttf_soc_cstr_elap(tmp, tmp_size, &stats->soc_stats,
859 i, end_soc);
860 ttf_log(stats, "%s", tmp);
861 ttf_soc_cstr_cc(tmp, tmp_size, &stats->soc_stats,
862 i, end_soc);
863 ttf_log(stats, "%s", tmp);
864 }
865
866 kfree(tmp);
867 }
Ken Tsou8acade12020-07-09 03:17:35 +0800868}
869
870static int ttf_init_soc_parse_dt(struct ttf_adapter_stats *as,
871 struct device *device)
872{
873 int table_count;
874 int ret;
875
876 table_count = of_property_count_elems_of_size(device->of_node,
877 "google,ttf-soc-table",
878 sizeof(u32));
879 if (table_count <= 0)
880 return -EINVAL;
881 if (table_count % 2)
882 return -EINVAL;
883
884 as->soc_table = devm_kzalloc(device, table_count * 2 * sizeof(u32),
885 GFP_KERNEL);
886 if (!as->soc_table)
887 return -ENOMEM;
888
889 ret = of_property_read_u32_array(device->of_node,
890 "google,ttf-soc-table",
891 as->soc_table, table_count);
892 if (ret < 0) {
893 pr_err("cannot read google,ttf-soc-table %d\n", ret);
894 return ret;
895 }
896
897 as->elap_table = &as->soc_table[table_count];
898 ret = of_property_read_u32_array(device->of_node,
899 "google,ttf-elap-table",
900 as->elap_table, table_count);
901 if (ret < 0) {
902 pr_err("cannot read google,ttf-elap-table %d\n", ret);
903 return ret;
904 }
905
906 as->table_count = table_count;
907 return 0;
908}
909
910int ttf_stats_sscan(struct batt_ttf_stats *stats,
911 const char *buff,
912 size_t size)
913{
914 /* TODO: scan ttf_soc_* data as well */
915
916 return ttf_tier_sscan(stats, buff, size);
917}
918
919static int ttf_as_default(struct ttf_adapter_stats *as, int i, int table_i)
920{
921 while (i > as->soc_table[table_i] && table_i < as->table_count)
922 table_i++;
923
924 return table_i;
925}
926
Jenny Ho1ef60002022-07-24 18:26:51 +0000927void ttf_tier_reset(struct batt_ttf_stats *stats)
928{
929 int i;
930
931 for (i = 0; i < GBMS_STATS_TIER_COUNT; i++) {
932 stats->tier_stats[i].cc_in = 0;
933 stats->tier_stats[i].cc_total = 0;
934 stats->tier_stats[i].avg_time = 0;
935 }
936}
937
Ken Tsou8acade12020-07-09 03:17:35 +0800938static int ttf_init_tier_parse_dt(struct batt_ttf_stats *stats,
939 struct device *device)
940{
941 int i, count, ret;
942 u32 tier_table[GBMS_STATS_TIER_COUNT];
943
944 count = of_property_count_elems_of_size(device->of_node,
945 "google,ttf-tier-table",
946 sizeof(u32));
947 if (count != GBMS_STATS_TIER_COUNT)
948 return -EINVAL;
949
950 ret = of_property_read_u32_array(device->of_node,
951 "google,ttf-tier-table",
952 tier_table, count);
953 if (ret < 0) {
954 pr_err("cannot read google,ttf-tier-table %d\n", ret);
955 return ret;
956 }
957
958 for (i = 0; i < GBMS_STATS_TIER_COUNT; i++)
959 stats->tier_stats[i].soc_in = tier_table[i] << 8;
960
Jenny Ho1ef60002022-07-24 18:26:51 +0000961 ttf_tier_reset(stats);
962
Ken Tsou8acade12020-07-09 03:17:35 +0800963 return 0;
964}
965
966/* clone and clear the stats */
967struct batt_ttf_stats *ttf_stats_dup(struct batt_ttf_stats *dst,
968 const struct batt_ttf_stats *src)
969{
970 memcpy(dst, src, sizeof(*dst));
971 memset(&dst->soc_stats, 0, sizeof(dst->soc_stats));
972 memset(&dst->tier_stats, 0, sizeof(dst->tier_stats));
973 return dst;
974}
975
AleX Pelosi78a4bea2020-09-01 19:02:24 -0700976static void ttf_init_ref_table(struct batt_ttf_stats *stats,
977 struct ttf_adapter_stats *as,
978 int capacity_ma)
Ken Tsou8acade12020-07-09 03:17:35 +0800979{
980 int i, table_i = 0;
981 const int cc = (capacity_ma * 100) / GBMS_SOC_STATS_LEN;
982
983 for (i = 0; i < GBMS_SOC_STATS_LEN; i++) {
984 table_i = ttf_as_default(as, i, table_i);
985
986 stats->soc_ref.elap[i] = as->elap_table[table_i];
987
988 /* assume same cc for each soc */
989 stats->soc_ref.cc[i] = (cc * i) / 100;
990 }
991
992 /* TODO: allocate as->soc_table witk kzalloc, free here */
993}
994
995/* must come after charge profile */
996int ttf_stats_init(struct batt_ttf_stats *stats, struct device *device,
997 int capacity_ma)
998{
999 struct ttf_adapter_stats as;
1000 u32 value;
1001 int ret;
1002
1003 memset(stats, 0, sizeof(*stats));
1004 stats->ttf_fake = -1;
1005
1006 /* reference adapter */
1007 ret = of_property_read_u32(device->of_node, "google,ttf-adapter",
1008 &value);
1009 if (ret < 0)
1010 return ret;
1011
1012 stats->ref_watts = value;
1013
1014 /* reference temperature */
1015 ret = of_property_read_u32(device->of_node, "google,ttf-temp-idx",
1016 &value);
1017 if (ret < 0)
1018 return ret;
1019
1020 stats->ref_temp_idx = value;
1021
1022 /* reference soc estimates */
1023 ret = ttf_init_soc_parse_dt(&as, device);
1024 if (ret < 0)
1025 return ret;
1026
1027 /* reference tier-based statistics */
1028 ret = ttf_init_tier_parse_dt(stats, device);
1029 if (ret < 0)
1030 return ret;
1031
1032 /* initialize the reference stats for the reference soc estimates */
1033 ttf_init_ref_table(stats, &as, capacity_ma);
1034
1035
1036 /* TODO: use the soc stats to calculate cc_in */
1037 stats->tier_stats[0].cc_in = 0;
1038 stats->tier_stats[1].cc_in = (capacity_ma *
1039 stats->tier_stats[1].soc_in) /
1040 100;
1041 stats->tier_stats[2].cc_in = (capacity_ma *
1042 stats->tier_stats[2].soc_in) /
1043 100;
1044
1045 /* TODO: use the soc stats to calculate cc_total */
1046 stats->tier_stats[0].cc_total = 0;
1047 stats->tier_stats[1].cc_total = (capacity_ma *
1048 (stats->tier_stats[2].soc_in -
1049 stats->tier_stats[1].soc_in)) /
1050 100;
1051 stats->tier_stats[2].cc_total = capacity_ma -
1052 stats->tier_stats[2].cc_in;
1053
1054
1055 return 0;
1056}
AleX Pelosi4a9035f2020-02-28 19:09:57 -08001057
1058
1059/* tier and soc details */
1060ssize_t ttf_dump_details(char *buf, int max_size,
1061 const struct batt_ttf_stats *ttf_stats,
1062 int last_soc)
1063{
1064 int i, len = 0;
1065
1066 /* interleave tier with SOC data */
1067 for (i = 0; i < GBMS_STATS_TIER_COUNT; i++) {
1068 int next_soc_in;
1069
1070 len += scnprintf(&buf[len], max_size - len, "%d: ", i);
1071 len += ttf_tier_cstr(&buf[len], max_size - len,
1072 &ttf_stats->tier_stats[i]);
1073 len += scnprintf(&buf[len], max_size - len, "\n");
1074
1075 /* continue only first */
1076 if (ttf_stats->tier_stats[i].avg_time == 0)
1077 continue;
1078
1079 if (i == GBMS_STATS_TIER_COUNT - 1) {
1080 next_soc_in = -1;
1081 } else {
1082 next_soc_in = ttf_stats->tier_stats[i + 1].soc_in >> 8;
1083 if (next_soc_in == 0)
1084 next_soc_in = -1;
1085 }
1086
1087 if (next_soc_in == -1)
1088 next_soc_in = last_soc - 1;
1089
1090 len += ttf_soc_cstr(&buf[len], max_size - len,
1091 &ttf_stats->soc_stats,
1092 ttf_stats->tier_stats[i].soc_in >> 8,
1093 next_soc_in);
1094 }
1095
1096 return len;
1097}