blob: b0e520be8be9e1fb0940bad265bc509f25429083 [file] [log] [blame]
AleX Pelosi78a4bea2020-09-01 19:02:24 -07001/* SPDX-License-Identifier: GPL-2.0 */
Ken Tsou8acade12020-07-09 03:17:35 +08002/*
Ken Tsouf956a7f2022-04-12 09:50:46 +08003 * Copyright 2018-2022 Google LLC
Ken Tsou8acade12020-07-09 03:17:35 +08004 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 */
15
16#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
17
18#ifdef CONFIG_PM_SLEEP
19#define SUPPORT_PM_SLEEP 1
20#endif
21
22#include <linux/kernel.h>
23#include <linux/printk.h>
24#include <linux/module.h>
25#include <linux/of.h>
26#include <linux/pm_runtime.h>
27#include <linux/platform_device.h>
Ken Tsou8acade12020-07-09 03:17:35 +080028#include <linux/thermal.h>
29#include <linux/slab.h>
AleX Pelosi856679c2020-09-01 13:47:28 -070030#include "gbms_power_supply.h"
Ken Tsou8acade12020-07-09 03:17:35 +080031#include "google_bms.h"
32#include "google_psy.h"
33#include "qmath.h"
Ken Tsoua15d1fa2022-01-24 14:42:27 +080034#include <misc/gvotable.h>
Ken Tsou8acade12020-07-09 03:17:35 +080035#include <crypto/hash.h>
36
37#include <linux/debugfs.h>
38
39#define BATT_DELAY_INIT_MS 250
40#define BATT_WORK_FAST_RETRY_CNT 30
41#define BATT_WORK_FAST_RETRY_MS 1000
AleX Pelosi32458432020-09-05 11:02:39 -070042#define BATT_WORK_DEBOUNCE_RETRY_MS 3000
Ken Tsou8acade12020-07-09 03:17:35 +080043#define BATT_WORK_ERROR_RETRY_MS 1000
44
45#define DEFAULT_BATT_FAKE_CAPACITY 50
46#define DEFAULT_BATT_UPDATE_INTERVAL 30000
47#define DEFAULT_BATT_DRV_RL_SOC_THRESHOLD 97
Jenny Hoa7d48db2020-12-08 15:22:09 +080048#define DEFAULT_BD_TRICKLE_RL_SOC_THRESHOLD 90
Ken Tsou76ee23d2020-12-03 00:49:48 +080049#define DEFAULT_BD_TRICKLE_RESET_SEC (5 * 60)
Ken Tsou8acade12020-07-09 03:17:35 +080050#define DEFAULT_HIGH_TEMP_UPDATE_THRESHOLD 550
51
Jenny Ho8b2bc5f2021-05-11 09:42:26 +080052#define DEFAULT_HEALTH_SAFETY_MARGIN (30 * 60)
53
Ken Tsou8acade12020-07-09 03:17:35 +080054#define MSC_ERROR_UPDATE_INTERVAL 5000
55#define MSC_DEFAULT_UPDATE_INTERVAL 30000
56
AleX Pelosic10eb8b2021-12-14 13:20:21 -080057
58/* AACR default slope is disabled by default */
Jenny Ho984ea292022-10-11 13:04:26 +080059#define AACR_START_CYCLE_DEFAULT 400
AleX Pelosic10eb8b2021-12-14 13:20:21 -080060#define AACR_MAX_CYCLE_DEFAULT 0 /* disabled */
61
Jenny Hoa9ecc2f2021-04-06 11:39:21 +080062/* qual time is 0 minutes of charge or 0% increase in SOC */
63#define DEFAULT_CHG_STATS_MIN_QUAL_TIME 0
64#define DEFAULT_CHG_STATS_MIN_DELTA_SOC 0
Ken Tsou8acade12020-07-09 03:17:35 +080065
66/* Voters */
67#define MSC_LOGIC_VOTER "msc_logic"
68#define SW_JEITA_VOTER "sw_jeita"
69#define RL_STATE_VOTER "rl_state"
AleX Pelosi8e7fd812019-08-16 10:41:46 -070070#define MSC_HEALTH_VOTER "chg_health"
Jack Wub0a68212021-11-22 22:33:00 +080071#define BPST_DETECT_VOTER "bpst_detect"
Ken Tsou8acade12020-07-09 03:17:35 +080072
73#define UICURVE_MAX 3
74
Jenny Hocced17c2020-04-30 18:20:16 +080075/* sync from google/logbuffer.c */
76#define LOG_BUFFER_ENTRY_SIZE 256
77
Ken Tsou8acade12020-07-09 03:17:35 +080078/* Initial data of history cycle count */
Jenny Ho11d5fcf2020-12-01 06:39:41 +080079#define HCC_DEFAULT_DELTA_CYCLE_CNT 10
Ken Tsou8acade12020-07-09 03:17:35 +080080
Jack Wu5b74da42022-03-12 14:09:28 +080081#define BHI_HEALTH_INDEX_DEFAULT 100
82#define BHI_MARGINAL_THRESHOLD_DEFAULT 80
83#define BHI_NEED_REP_THRESHOLD_DEFAULT 70
AleX Pelosic1cd4752022-05-04 02:15:02 -070084#define BHI_CCBIN_INDEX_LIMIT 90
85#define BHI_ALGO_FULL_HEALTH 10000
AleX Pelosi897284b2022-07-09 20:04:58 +000086#define BHI_ALGO_ROUND_INDEX 50
Jenny Hoa94c30b2022-10-13 16:29:47 +080087#define BHI_CC_MARGINAL_THRESHOLD_DEFAULT 800
88#define BHI_CC_NEED_REP_THRESHOLD_DEFAULT 1000
AleX Pelosi897284b2022-07-09 20:04:58 +000089
90#define BHI_ROUND_INDEX(index) \
91 (((index) + BHI_ALGO_ROUND_INDEX) / 100)
Jack Wu5b74da42022-03-12 14:09:28 +080092
AleX Pelosi43bec422022-04-27 21:59:19 -070093
94/* TODO: this is for Adaptive charging, rename */
Stephane Lee4a984a22022-01-14 15:14:07 -080095enum batt_health_ui {
96 /* Internal value used when health is cleared via dialog */
97 CHG_DEADLINE_DIALOG = -3,
98 /* Internal value used when health is settings disabled while running */
99 CHG_DEADLINE_SETTING_STOP = -2,
100 /* Internal value used when health is settings disabled */
101 CHG_DEADLINE_SETTING = -1,
102 /* Internal value used when health is cleared via alarms/re-plug */
103 CHG_DEADLINE_CLEARED = 0,
104};
Stephane Leeaaf28cd2020-08-26 11:26:06 -0700105
Ken Tsou8acade12020-07-09 03:17:35 +0800106#if (GBMS_CCBIN_BUCKET_COUNT < 1) || (GBMS_CCBIN_BUCKET_COUNT > 100)
107#error "GBMS_CCBIN_BUCKET_COUNT needs to be a value from 1-100"
108#endif
109
110#define get_boot_sec() div_u64(ktime_to_ns(ktime_get_boottime()), NSEC_PER_SEC)
111
112struct ssoc_uicurve {
113 qnum_t real;
114 qnum_t ui;
115};
116
117enum batt_rl_status {
118 BATT_RL_STATUS_NONE = 0,
119 BATT_RL_STATUS_DISCHARGE = -1,
120 BATT_RL_STATUS_RECHARGE = 1,
121};
122
123#define RL_DELTA_SOC_MAX 8
124
125struct batt_ssoc_rl_state {
126 /* rate limiter state */
127 qnum_t rl_ssoc_target;
AleX Pelosiab0e9d42020-09-29 11:13:19 -0700128 ktime_t rl_ssoc_last_update;
Ken Tsou8acade12020-07-09 03:17:35 +0800129
130 /* rate limiter flags */
131 bool rl_no_zero;
132 int rl_fast_track;
133 int rl_track_target;
134 /* rate limiter config */
135 int rl_delta_max_time;
136 qnum_t rl_delta_max_soc;
137
138 int rl_delta_soc_ratio[RL_DELTA_SOC_MAX];
139 qnum_t rl_delta_soc_limit[RL_DELTA_SOC_MAX];
140 int rl_delta_soc_cnt;
141
142 qnum_t rl_ft_low_limit;
143 qnum_t rl_ft_delta_limit;
144};
145
146#define SSOC_STATE_BUF_SZ 128
AleX Pelosi4a3a95f2020-10-12 17:35:17 -0700147#define SSOC_DELTA 3
Ken Tsou8acade12020-07-09 03:17:35 +0800148
149struct batt_ssoc_state {
150 /* output of gauge data filter */
151 qnum_t ssoc_gdf;
152 /* UI Curves */
153 int ssoc_curve_type; /*<0 dsg, >0 chg, 0? */
154 struct ssoc_uicurve ssoc_curve[UICURVE_MAX];
155 qnum_t ssoc_uic;
156 /* output of rate limiter */
157 qnum_t ssoc_rl;
158 struct batt_ssoc_rl_state ssoc_rl_state;
Ken Tsou7c766372020-03-05 17:04:40 +0800159 int ssoc_delta;
Ken Tsou8acade12020-07-09 03:17:35 +0800160
161 /* output of rate limiter */
162 int rl_rate;
163 int rl_last_ssoc;
AleX Pelosiab0e9d42020-09-29 11:13:19 -0700164 ktime_t rl_last_update;
Ken Tsou8acade12020-07-09 03:17:35 +0800165
166 /* connected or disconnected */
Ken Tsou76ee23d2020-12-03 00:49:48 +0800167 ktime_t disconnect_time;
Ken Tsou8acade12020-07-09 03:17:35 +0800168 int buck_enabled;
169
170 /* recharge logic */
171 int rl_soc_threshold;
172 enum batt_rl_status rl_status;
173
Ken Tsoudf64e282020-10-28 09:39:33 +0800174 /* trickle defender */
Jenny Hoa7d48db2020-12-08 15:22:09 +0800175 bool bd_trickle_enable;
176 int bd_trickle_recharge_soc;
Ken Tsoudf64e282020-10-28 09:39:33 +0800177 int bd_trickle_cnt;
Ken Tsou793502d2020-11-19 20:34:34 +0800178 bool bd_trickle_dry_run;
Ken Tsou80f97342022-08-18 17:56:14 +0800179 bool bd_trickle_full;
180 bool bd_trickle_eoc;
Ken Tsou76ee23d2020-12-03 00:49:48 +0800181 u32 bd_trickle_reset_sec;
Ken Tsoudf64e282020-10-28 09:39:33 +0800182
Ken Tsou8acade12020-07-09 03:17:35 +0800183 /* buff */
184 char ssoc_state_cstr[SSOC_STATE_BUF_SZ];
Jenny Ho87fef342020-10-27 17:58:08 +0800185
186 /* Save/Restore fake capacity */
187 bool save_soc_available;
188 u16 save_soc;
Ken Tsou8acade12020-07-09 03:17:35 +0800189};
190
191struct gbatt_ccbin_data {
192 u16 count[GBMS_CCBIN_BUCKET_COUNT];
193 char cyc_ctr_cstr[GBMS_CCBIN_CSTR_SIZE];
194 struct mutex lock;
195 int prev_soc;
196};
197
Ken Tsou8acade12020-07-09 03:17:35 +0800198#define DEFAULT_RES_TEMP_LOW 350
AleX Pelosib3a1a552022-05-03 17:00:11 -0700199#define DEFAULT_RES_TEMP_HIGH 390
200#define DEFAULT_RAVG_SOC_LOW 5
201#define DEFAULT_RAVG_SOC_HIGH 75
Ken Tsou8acade12020-07-09 03:17:35 +0800202#define DEFAULT_RES_FILT_LEN 10
203
204struct batt_res {
205 bool estimate_requested;
206
207 /* samples */
208 int sample_accumulator;
209 int sample_count;
210
211 /* registers */
212 int filter_count;
213 int resistance_avg;
214
215 /* configuration */
216 int estimate_filter;
AleX Pelosib3a1a552022-05-03 17:00:11 -0700217 int ravg_soc_low;
218 int ravg_soc_high;
Ken Tsou8acade12020-07-09 03:17:35 +0800219 int res_temp_low;
220 int res_temp_high;
221};
222
AleX Pelosi43bec422022-04-27 21:59:19 -0700223/* TODO: move single cell disconnect to bhi_data */
Jack Wu58ea50a2022-06-04 13:22:22 +0800224enum bpst_batt_status {
225 BPST_BATT_UNKNOWN = 0,
226 BPST_BATT_CONNECT = 1,
227 BPST_BATT_DISCONNECT = 2,
228 BPST_BATT_CELL_FAULT = 3,
229};
230
Jack Wue3ebc172021-11-11 16:20:34 +0800231struct batt_bpst {
232 struct mutex lock;
233 bool bpst_enable;
Jack Wub0a68212021-11-22 22:33:00 +0800234 bool bpst_detect_disable;
Jack Wub19fad12022-04-27 14:47:04 +0800235 bool bpst_cell_fault;
Jack Wue3ebc172021-11-11 16:20:34 +0800236 /* single battery disconnect status */
237 int bpst_sbd_status;
Jack Wu85983332021-12-07 23:55:40 +0800238 int bpst_count_threshold;
Jack Wub0a68212021-11-22 22:33:00 +0800239 int bpst_chg_rate;
Jack Wu85983332021-12-07 23:55:40 +0800240 u8 bpst_count;
Jack Wue3ebc172021-11-11 16:20:34 +0800241};
242
Jenny Hoc5248b32022-02-16 09:48:32 +0800243#define DEV_SN_LENGTH 20
Ken Tsou8acade12020-07-09 03:17:35 +0800244enum batt_paired_state {
245 BATT_PAIRING_WRITE_ERROR = -4,
246 BATT_PAIRING_READ_ERROR = -3,
247 BATT_PAIRING_MISMATCH = -2,
248 BATT_PAIRING_DISABLED = -1,
249 BATT_PAIRING_ENABLED = 0,
250 BATT_PAIRING_PAIRED = 1,
251 BATT_PAIRING_RESET = 2,
252};
253
254enum batt_lfcollect_status {
AleX Pelosic1cd4752022-05-04 02:15:02 -0700255 BATT_LFCOLLECT_NOT_AVAILABLE = 0,
Ken Tsou8acade12020-07-09 03:17:35 +0800256 BATT_LFCOLLECT_ENABLED = 1,
257 BATT_LFCOLLECT_COLLECT = 2,
258};
259
Jenny Ho23314e62021-11-19 08:58:25 +0800260enum batt_aacr_state {
261 BATT_AACR_UNKNOWN = -3,
262 BATT_AACR_INVALID_CAP = -2,
263 BATT_AACR_UNDER_CYCLES = -1,
264 BATT_AACR_DISABLED = 0,
265 BATT_AACR_ENABLED = 1,
Jenny Ho24fcef62022-07-14 10:45:04 +0000266 BATT_AACR_ALGO_DEFAULT = BATT_AACR_ENABLED,
267 BATT_AACR_ALGO_LOW_B, /* lower bound */
Jenny Ho23314e62021-11-19 08:58:25 +0800268 BATT_AACR_MAX,
269};
270
Jenny Hoc6f13052022-01-03 10:52:21 +0800271#define BATT_TEMP_RECORD_THR 3
Jenny Ho7d8c2212022-01-05 18:07:14 +0800272/* discharge saved after charge */
273#define SD_CHG_START 0
274#define SD_DISCHG_START BATT_TEMP_RECORD_THR
275#define BATT_SD_SAVE_SIZE (BATT_TEMP_RECORD_THR * 2)
Ken Tsoua15d1fa2022-01-24 14:42:27 +0800276#define BATT_SD_MAX_HOURS 15120 /* 90 weeks */
AleX Pelosi43bec422022-04-27 21:59:19 -0700277
278/* TODO: move swelling_data to bhi_data */
Jenny Hoc6f13052022-01-03 10:52:21 +0800279struct swelling_data {
280 /* Time in different temperature */
281 bool is_enable;
282 u32 temp_thr[BATT_TEMP_RECORD_THR];
283 u32 soc_thr[BATT_TEMP_RECORD_THR];
Jenny Ho7d8c2212022-01-05 18:07:14 +0800284 /*
285 * cumulative time array format:
286 * | saved | 0 | 1 | 2 |
287 * |--------| Charge | Charge | Charge |
288 * | Content| > 30degC & > 90% | > 35degC & > 90% | > 40degC & > 95% |
289 *
290 * | saved | 3 | 4 | 5 |
291 * |--------| Discharge | Discharge | Discharge |
292 * | Content| > 30degC & > 90% | > 35degC & > 90% | > 40degC & > 95% |
293 */
294 u16 saved[BATT_SD_SAVE_SIZE];
Jenny Hoc6f13052022-01-03 10:52:21 +0800295 ktime_t chg[BATT_TEMP_RECORD_THR];
296 ktime_t dischg[BATT_TEMP_RECORD_THR];
297 ktime_t last_update;
298};
299
Jenny Hod4bd5bc2022-08-26 16:08:38 +0000300
301struct bhi_weight bhi_w[] = {
302 [BHI_ALGO_ACHI] = {100, 0, 0},
303 [BHI_ALGO_ACHI_B] = {100, 0, 0},
304 [BHI_ALGO_ACHI_RAVG] = {95, 5, 0},
305 [BHI_ALGO_ACHI_RAVG_B] = {95, 5, 0},
306 [BHI_ALGO_MIX_N_MATCH] = {90, 10, 5},
307};
308
AleX Pelosi43bec422022-04-27 21:59:19 -0700309struct bhi_data
AleX Pelosi1c2d9de2022-01-20 18:24:19 -0800310{
AleX Pelosic1cd4752022-05-04 02:15:02 -0700311 /* context */
312 int cycle_count; /* from the FG */
313 int battery_age; /* from the FG, time in field */
AleX Pelosi43bec422022-04-27 21:59:19 -0700314
AleX Pelosic1cd4752022-05-04 02:15:02 -0700315 /* capacity metrics */
316 int capacity_design; /* from the FG or from charge table */
317 int capacity_fade; /* from the FG */
AleX Pelosi43bec422022-04-27 21:59:19 -0700318
AleX Pelosic1cd4752022-05-04 02:15:02 -0700319 /* impedance */
320 u32 act_impedance; /* resistance, qualified */
321 u32 cur_impedance; /* resistance, qualified */
322 struct batt_res res_state; /* google_resistance */
AleX Pelosia3f22de2022-05-03 23:42:24 -0700323
AleX Pelosic1cd4752022-05-04 02:15:02 -0700324 /* swell probability */
325 int swell_cumulative; /* from swell data */
326 int ccbin_index; /* from SOC residency */
327
AleX Pelosi43bec422022-04-27 21:59:19 -0700328};
AleX Pelosia3f22de2022-05-03 23:42:24 -0700329
AleX Pelosi43bec422022-04-27 21:59:19 -0700330struct health_data
331{
AleX Pelosic1cd4752022-05-04 02:15:02 -0700332 /* current algorithm */
AleX Pelosi43bec422022-04-27 21:59:19 -0700333 int bhi_algo;
AleX Pelosi43bec422022-04-27 21:59:19 -0700334 int bhi_w_ci;
335 int bhi_w_pi;
336 int bhi_w_sd;
AleX Pelosic1cd4752022-05-04 02:15:02 -0700337 /* current health index and status */
338 int bhi_index;
AleX Pelosi43bec422022-04-27 21:59:19 -0700339 enum bhi_status bhi_status;
Jack Wu5b74da42022-03-12 14:09:28 +0800340 int marginal_threshold;
341 int need_rep_threshold;
Jenny Hoa94c30b2022-10-13 16:29:47 +0800342 /* cycle count threshold */
343 int cycle_count_marginal_threshold;
344 int cycle_count_need_rep_threshold;
AleX Pelosic1cd4752022-05-04 02:15:02 -0700345 /* current health metrics */
346 int bhi_cap_index;
347 int bhi_imp_index;
348 int bhi_sd_index;
Jenny Hod4bd5bc2022-08-26 16:08:38 +0000349 /* debug health metrics */
Jenny Ho0ffa6c32022-10-13 17:03:19 +0800350 int bhi_debug_cycle_count;
Jenny Hod4bd5bc2022-08-26 16:08:38 +0000351 int bhi_debug_cap_index;
352 int bhi_debug_imp_index;
353 int bhi_debug_sd_index;
354 int bhi_debug_health_index;
AleX Pelosic1cd4752022-05-04 02:15:02 -0700355
356 /* current battery state */
357 struct bhi_data bhi_data;
358
AleX Pelosi1c2d9de2022-01-20 18:24:19 -0800359};
360
Wasb Liuf40cbb22022-01-26 18:41:03 +0800361#define POWER_METRICS_MAX_DATA 50
362
363struct power_metrics_data {
364 unsigned long charge_count;
365 unsigned long voltage;
366 ktime_t time;
367};
368
369struct power_metrics {
370 unsigned int polling_rate;
371 unsigned int interval;
372 unsigned int idx;
373 struct power_metrics_data data[POWER_METRICS_MAX_DATA];
374 struct delayed_work work;
375};
376
AleX Pelosi47ae7762022-05-13 22:33:04 +0000377struct csi_stats {
378 int ssoc;
379
380 int csi_speed_min;
381 int csi_speed_max;
382
383 int csi_current_status;
384 int csi_current_type;
385
386 ktime_t csi_time_sum;
387 int speed_sum;
388
389 ktime_t last_update;
390};
391
Jenny Ho74ae1bf2022-12-12 16:53:24 +0800392#define TEMP_SAMPLE_SIZE 5
393struct batt_temp_filter {
394 struct delayed_work work;
395 struct mutex lock;
396 bool enable;
397 bool force_update;
398 bool resume_delay;
399 int sample[TEMP_SAMPLE_SIZE];
400 int default_interval;
401 int fast_interval;
402 int resume_delay_time;
403 int last_idx;
404};
yihsiangpeng63c71462023-03-16 18:25:50 +0800405#define NB_FAN_BT_LIMITS 4
Ken Tsou8acade12020-07-09 03:17:35 +0800406/* battery driver state */
407struct batt_drv {
408 struct device *device;
409 struct power_supply *psy;
410
411 const char *fg_psy_name;
412 struct power_supply *fg_psy;
413 struct notifier_block fg_nb;
414
415 struct delayed_work init_work;
416 struct delayed_work batt_work;
417
Ken Tsou5ecf2f42020-07-16 08:26:05 +0800418 struct wakeup_source *msc_ws;
419 struct wakeup_source *batt_ws;
420 struct wakeup_source *taper_ws;
421 struct wakeup_source *poll_ws;
Ken Tsou8acade12020-07-09 03:17:35 +0800422 bool hold_taper_ws;
423
424 /* TODO: b/111407333, will likely need to adjust SOC% on wakeup */
425 bool init_complete;
426 bool resume_complete;
427 bool batt_present;
AleX Pelosi8c5fa772020-10-03 13:36:56 -0700428 u32 fake_battery_present;
Ken Tsou8acade12020-07-09 03:17:35 +0800429
430 struct mutex batt_lock;
431 struct mutex chg_lock;
432
433 /* battery work */
434 int fg_status;
435 int batt_fast_update_cnt;
436 u32 batt_update_interval;
437 /* update high temperature in time */
438 int batt_temp;
439 u32 batt_update_high_temp_threshold;
440 /* fake battery temp for thermal testing */
441 int fake_temp;
442 /* triger for recharge logic next update from charger */
443 bool batt_full;
444 struct batt_ssoc_state ssoc_state;
445 /* bin count */
446 struct gbatt_ccbin_data cc_data;
447 /* fg cycle count */
448 int cycle_count;
Jenny Hob88e7ba2022-01-26 05:20:41 +0800449 /* for testing */
450 int fake_aacr_cc;
Ken Tsou8acade12020-07-09 03:17:35 +0800451
452 /* props */
453 int soh;
454 int fake_capacity;
AleX Pelosic1cd4752022-05-04 02:15:02 -0700455 int batt_health; /* health of battery, triggers defender UI */
456 int report_health; /* log health changes for debug */
Ken Tsou8acade12020-07-09 03:17:35 +0800457 bool dead_battery;
458 int capacity_level;
459 bool chg_done;
460
461 /* temp outside the charge table */
462 int jeita_stop_charging;
AleX Pelosi8e7fd812019-08-16 10:41:46 -0700463 /* health based charging */
464 struct batt_chg_health chg_health;
Ken Tsou8acade12020-07-09 03:17:35 +0800465
466 /* MSC charging */
AleX Pelosic10eb8b2021-12-14 13:20:21 -0800467 u32 battery_capacity; /* in mAh */
Ken Tsou8acade12020-07-09 03:17:35 +0800468 struct gbms_chg_profile chg_profile;
469 union gbms_charger_state chg_state;
470
471 int temp_idx;
472 int vbatt_idx;
473 int checked_cv_cnt;
474 int checked_ov_cnt;
475 int checked_tier_switch_cnt;
AleX Pelosi1b7d80b2021-07-15 13:24:52 -0700476 int last_log_cnt;
Ken Tsou8acade12020-07-09 03:17:35 +0800477
478 int fv_uv;
479 int cc_max;
Jenny Ho002c6702021-10-19 16:19:16 +0800480 int topoff;
Ken Tsou8acade12020-07-09 03:17:35 +0800481 int msc_update_interval;
482
483 bool disable_votes;
Ken Tsoua15d1fa2022-01-24 14:42:27 +0800484 struct gvotable_election *msc_interval_votable;
485 struct gvotable_election *fcc_votable;
486 struct gvotable_election *fv_votable;
487 struct gvotable_election *temp_dryrun_votable;
AleX Pelosia7e0da82021-07-15 13:09:42 -0700488
489 /* FAN level */
Ken Tsoua15d1fa2022-01-24 14:42:27 +0800490 struct gvotable_election *fan_level_votable;
AleX Pelosia7e0da82021-07-15 13:09:42 -0700491 int fan_last_level;
Ken Tsou8acade12020-07-09 03:17:35 +0800492
493 /* stats */
494 int msc_state;
495 int msc_irdrop_state;
496 struct mutex stats_lock;
497 struct gbms_charging_event ce_data;
498 struct gbms_charging_event ce_qual;
Jenny Hoa9ecc2f2021-04-06 11:39:21 +0800499 uint32_t chg_sts_qual_time;
500 uint32_t chg_sts_delta_soc;
Ken Tsou8acade12020-07-09 03:17:35 +0800501
Jenny Ho47467492021-05-19 17:29:19 +0800502 /* health charge margin time */
503 int health_safety_margin;
504
Ken Tsou8acade12020-07-09 03:17:35 +0800505 /* time to full */
506 struct batt_ttf_stats ttf_stats;
AleX Pelosi32458432020-09-05 11:02:39 -0700507 bool ttf_debounce;
Jenny Hoffc7bb32021-10-21 04:31:36 +0800508 ktime_t ttf_est;
Ken Tsou8acade12020-07-09 03:17:35 +0800509
510 /* logging */
Ken Tsou8acade12020-07-09 03:17:35 +0800511 struct logbuffer *ssoc_log;
512
513 /* thermal */
514 struct thermal_zone_device *tz_dev;
515
Wasb Liu6120f262022-01-28 08:00:06 +0800516 /* battery virtual sensor */
517 struct thermal_zone_device *batt_vs_tz;
518 int batt_vs_w;
519
Ken Tsou8acade12020-07-09 03:17:35 +0800520 /* used to detect battery replacements and reset statistics */
521 enum batt_paired_state pairing_state;
Jenny Hoc5248b32022-02-16 09:48:32 +0800522 char dev_sn[DEV_SN_LENGTH];
Ken Tsou8acade12020-07-09 03:17:35 +0800523
524 /* collect battery history/lifetime data (history) */
525 enum batt_lfcollect_status blf_state;
AleX Pelosic1cd4752022-05-04 02:15:02 -0700526 u32 blf_collect_now;
Ken Tsou8acade12020-07-09 03:17:35 +0800527 int hist_delta_cycle_cnt;
528 int hist_data_max_cnt;
Jenny Ho11d5fcf2020-12-01 06:39:41 +0800529 int hist_data_saved_cnt;
Ken Tsou8acade12020-07-09 03:17:35 +0800530 void *hist_data;
531
532 /* Battery device info */
533 u8 dev_info_check[GBMS_DINF_LEN];
534
535 /* History Device */
536 struct gbms_storage_device *history;
yihsiangpengbb8ae642021-04-27 17:46:34 +0800537
538 /* Fan control */
539 int fan_level;
yihsiangpeng63c71462023-03-16 18:25:50 +0800540 int fan_bt_limits[NB_FAN_BT_LIMITS];
Jenny Ho23314e62021-11-19 08:58:25 +0800541
542 /* AACR: Aged Adjusted Charging Rate */
543 enum batt_aacr_state aacr_state;
AleX Pelosic10eb8b2021-12-14 13:20:21 -0800544 int aacr_cycle_grace;
545 int aacr_cycle_max;
Jenny Ho24fcef62022-07-14 10:45:04 +0000546 int aacr_algo;
Jenny Hoc6f13052022-01-03 10:52:21 +0800547
AleX Pelosic1cd4752022-05-04 02:15:02 -0700548 /* BHI: updated on disconnect, EOC */
AleX Pelosi43bec422022-04-27 21:59:19 -0700549 struct health_data health_data;
Jenny Hoc6f13052022-01-03 10:52:21 +0800550 struct swelling_data sd;
AleX Pelosi1c2d9de2022-01-20 18:24:19 -0800551
Jenny Ho4f2ad702022-03-01 10:56:19 +0800552 /* CSI: charging speed */
AleX Pelosi47ae7762022-05-13 22:33:04 +0000553 struct csi_stats csi_stats;
Ken Tsoua15d1fa2022-01-24 14:42:27 +0800554 struct gvotable_election *csi_status_votable;
AleX Pelosi9bfe2312022-04-26 13:20:43 -0700555 int csi_current_status;
Ken Tsoua15d1fa2022-01-24 14:42:27 +0800556 struct gvotable_election *csi_type_votable;
AleX Pelosi9bfe2312022-04-26 13:20:43 -0700557 int csi_current_type;
558 int csi_current_speed;
Jenny Ho09637822022-04-18 09:02:57 +0800559 int fake_charging_speed;
Wasb Liuf40cbb22022-01-26 18:41:03 +0800560
561 /* battery power metrics */
562 struct power_metrics power_metrics;
Jack Wue3ebc172021-11-11 16:20:34 +0800563
564 /* battery pack status */
565 struct batt_bpst bpst_state;
Jack Wucfb51f72022-10-26 19:55:26 +0800566
567 /* battery critical level */
568 int batt_critical_voltage;
Jenny Ho74ae1bf2022-12-12 16:53:24 +0800569
570 /* battery temperature filter */
571 struct batt_temp_filter temp_filter;
Ken Tsou8acade12020-07-09 03:17:35 +0800572};
573
Jenny Ho74ae1bf2022-12-12 16:53:24 +0800574static int gbatt_get_temp(struct batt_drv *batt_drv, int *temp);
yihsiangpeng305623d2021-05-06 15:07:05 +0800575
Jenny Ho87fef342020-10-27 17:58:08 +0800576static int gbatt_get_capacity(struct batt_drv *batt_drv);
Jenny Hocced17c2020-04-30 18:20:16 +0800577
Jenny Ho74ae1bf2022-12-12 16:53:24 +0800578static int batt_get_filter_temp(struct batt_temp_filter *temp_filter)
Jenny Ho5a2657e2022-12-12 14:36:57 +0800579{
Jenny Ho74ae1bf2022-12-12 16:53:24 +0800580 int sum = 0, max, min, i;
581
582 mutex_lock(&temp_filter->lock);
583 max = min = temp_filter->sample[0];
584 for (i = 0; i < TEMP_SAMPLE_SIZE; i++) {
585 if (temp_filter->sample[i] > max)
586 max = temp_filter->sample[i];
587 if (temp_filter->sample[i] < min)
588 min = temp_filter->sample[i];
589 sum += temp_filter->sample[i];
590 }
591 mutex_unlock(&temp_filter->lock);
592
593 return (sum - max - min) / (TEMP_SAMPLE_SIZE - 2);
594}
595
596static int gbatt_get_raw_temp(struct batt_drv *batt_drv, int *temp)
597{
598 int err = 0;
Jenny Ho5a2657e2022-12-12 14:36:57 +0800599 union power_supply_propval val;
600
Jenny Ho74ae1bf2022-12-12 16:53:24 +0800601 if (batt_drv->temp_filter.enable) {
602 *temp = batt_get_filter_temp(&batt_drv->temp_filter);
603 return err;
604 }
605
Jenny Ho5a2657e2022-12-12 14:36:57 +0800606 if (!batt_drv->fg_psy)
607 return -EINVAL;
608
609 err = power_supply_get_property(batt_drv->fg_psy, POWER_SUPPLY_PROP_TEMP, &val);
610 if (err == 0)
611 *temp = val.intval;
612
613 return err;
614}
615
Ken Tsou8acade12020-07-09 03:17:35 +0800616static inline void batt_update_cycle_count(struct batt_drv *batt_drv)
617{
618 batt_drv->cycle_count = GPSY_GET_PROP(batt_drv->fg_psy,
619 POWER_SUPPLY_PROP_CYCLE_COUNT);
620}
621
622static int google_battery_tz_get_cycle_count(void *data, int *cycle_count)
623{
624 struct batt_drv *batt_drv = (struct batt_drv *)data;
625
626 if (!cycle_count) {
627 pr_err("Cycle Count NULL");
628 return -EINVAL;
629 }
630
631 if (batt_drv->cycle_count < 0)
632 return batt_drv->cycle_count;
633
634 *cycle_count = batt_drv->cycle_count;
635
636 return 0;
637}
638
Wasb Liu6120f262022-01-28 08:00:06 +0800639static int batt_vs_tz_get(struct thermal_zone_device *tzd, int *batt_vs)
640{
641 struct batt_drv *batt_drv = tzd->devdata;
642 int temp, rc;
643 unsigned int ibat;
644 unsigned long vs_tmp;
645
646 if (!batt_vs)
647 return -EINVAL;
648
Jenny Ho5a2657e2022-12-12 14:36:57 +0800649 rc = gbatt_get_raw_temp(batt_drv, &temp);
Wasb Liu6120f262022-01-28 08:00:06 +0800650 if (rc)
651 return -EINVAL;
652
Jenny Ho5a2657e2022-12-12 14:36:57 +0800653 temp = temp * 100;
654
Wasb Liu6120f262022-01-28 08:00:06 +0800655 ibat = abs(GPSY_GET_INT_PROP(batt_drv->fg_psy, POWER_SUPPLY_PROP_CURRENT_AVG, &rc));
656 if (rc)
657 return -EINVAL;
658
659 vs_tmp = mul_u32_u32(ibat, ibat) * batt_drv->batt_vs_w / 1000000000000;
660
661 *batt_vs = temp - vs_tmp;
662
663 return 0;
664}
665
666static struct thermal_zone_device_ops batt_vs_tz_ops = {
667 .get_temp = batt_vs_tz_get,
668};
669
Ken Tsou8acade12020-07-09 03:17:35 +0800670static int psy_changed(struct notifier_block *nb,
671 unsigned long action, void *data)
672{
673 struct power_supply *psy = data;
674 struct batt_drv *batt_drv = container_of(nb, struct batt_drv, fg_nb);
675
676 pr_debug("name=%s evt=%lu\n", psy->desc->name, action);
677
678 if ((action != PSY_EVENT_PROP_CHANGED) ||
679 (psy == NULL) || (psy->desc == NULL) || (psy->desc->name == NULL))
680 return NOTIFY_OK;
681
682 if (action == PSY_EVENT_PROP_CHANGED &&
683 (!strcmp(psy->desc->name, batt_drv->fg_psy_name))) {
684 mod_delayed_work(system_wq, &batt_drv->batt_work, 0);
685 }
686
687 return NOTIFY_OK;
688}
689
690/* ------------------------------------------------------------------------- */
691
AleX Pelosi1b7d80b2021-07-15 13:24:52 -0700692
693#define BATT_PRLOG_DEBUG 0
694#define BATT_PRLOG_ALWAYS 1
695#define BATT_PRLOG_LAST_LOG_COUNT 10
696
697static int debug_printk_prlog = LOGLEVEL_INFO;
698
699static inline int batt_prlog_level(bool level)
700{
701 return level ? BATT_PRLOG_ALWAYS : BATT_PRLOG_DEBUG;
702}
703
704__printf(2,3)
705static void batt_prlog__(int info_level, const char *format, ...)
706{
707 const int level = info_level == BATT_PRLOG_ALWAYS ? LOGLEVEL_INFO : LOGLEVEL_DEBUG;
708
709 if (level <= debug_printk_prlog) {
710 va_list args;
711
712 va_start(args, format);
713 vprintk(format, args);
714 va_end(args);
715 }
716
717}
718
719#define batt_prlog(l, fmt, ...) batt_prlog__(l, pr_fmt(fmt), ##__VA_ARGS__)
720
721/* ------------------------------------------------------------------------- */
722
Ken Tsou8acade12020-07-09 03:17:35 +0800723#define SSOC_TRUE 15
724#define SSOC_SPOOF 95
725#define SSOC_FULL 100
726#define UICURVE_BUF_SZ (UICURVE_MAX * 15 + 1)
Stephane Leecbb07ee2020-09-14 12:24:09 -0700727#define SSOC_HIGH_SOC 90
Ken Tsou8acade12020-07-09 03:17:35 +0800728
729enum ssoc_uic_type {
730 SSOC_UIC_TYPE_DSG = -1,
731 SSOC_UIC_TYPE_NONE = 0,
732 SSOC_UIC_TYPE_CHG = 1,
733};
734
735const qnum_t ssoc_point_true = qnum_rconst(SSOC_TRUE);
736const qnum_t ssoc_point_spoof = qnum_rconst(SSOC_SPOOF);
737const qnum_t ssoc_point_full = qnum_rconst(SSOC_FULL);
738
739static struct ssoc_uicurve chg_curve[UICURVE_MAX] = {
740 { ssoc_point_true, ssoc_point_true },
741 { ssoc_point_spoof, ssoc_point_spoof },
742 { ssoc_point_full, ssoc_point_full },
743};
744
745static struct ssoc_uicurve dsg_curve[UICURVE_MAX] = {
746 { ssoc_point_true, ssoc_point_true },
747 { ssoc_point_spoof, ssoc_point_full },
748 { ssoc_point_full, ssoc_point_full },
749};
750
751static char *ssoc_uicurve_cstr(char *buff, size_t size,
752 struct ssoc_uicurve *curve)
753{
754 int i, len = 0;
755
756 for (i = 0; i < UICURVE_MAX ; i++) {
757 len += scnprintf(&buff[len], size - len,
758 "[" QNUM_CSTR_FMT " " QNUM_CSTR_FMT "]",
759 qnum_toint(curve[i].real),
760 qnum_fracdgt(curve[i].real),
761 qnum_toint(curve[i].ui),
762 qnum_fracdgt(curve[i].ui));
763 if (len >= size)
764 break;
765 }
766
767 buff[len] = 0;
768 return buff;
769}
770
771/* NOTE: no bounds checks on this one */
772static int ssoc_uicurve_find(qnum_t real, struct ssoc_uicurve *curve)
773{
774 int i;
775
776 for (i = 1; i < UICURVE_MAX ; i++) {
777 if (real == curve[i].real)
778 return i;
779 if (real > curve[i].real)
780 continue;
781 break;
782 }
783
784 return i-1;
785}
786
787static qnum_t ssoc_uicurve_map(qnum_t real, struct ssoc_uicurve *curve)
788{
789 qnum_t slope = 0, delta_ui, delta_re;
790 int i;
791
792 if (real < curve[0].real)
793 return real;
794 if (real >= curve[UICURVE_MAX - 1].ui)
795 return curve[UICURVE_MAX - 1].ui;
796
797 i = ssoc_uicurve_find(real, curve);
798 if (curve[i].real == real)
799 return curve[i].ui;
800
801 delta_ui = curve[i + 1].ui - curve[i].ui;
802 delta_re = curve[i + 1].real - curve[i].real;
803 if (delta_re)
804 slope = qnum_div(delta_ui, delta_re);
805
806 return curve[i].ui + qnum_mul(slope, (real - curve[i].real));
807}
808
809/* "optimized" to work on 3 element curves */
810static void ssoc_uicurve_splice(struct ssoc_uicurve *curve, qnum_t real,
811 qnum_t ui)
812{
813 if (real < curve[0].real || real > curve[2].real)
814 return;
815
816#if UICURVE_MAX != 3
817#error ssoc_uicurve_splice() only support UICURVE_MAX == 3
818#endif
819
820 /* splice only when real is within the curve range */
821 curve[1].real = real;
822 curve[1].ui = ui;
823}
824
825static void ssoc_uicurve_dup(struct ssoc_uicurve *dst,
826 struct ssoc_uicurve *curve)
827{
828 if (dst != curve)
829 memcpy(dst, curve, sizeof(*dst)*UICURVE_MAX);
830}
831
832
833/* ------------------------------------------------------------------------- */
834
835/* could also use the rate of change for this */
836static qnum_t ssoc_rl_max_delta(const struct batt_ssoc_rl_state *rls,
AleX Pelosiab0e9d42020-09-29 11:13:19 -0700837 int bucken, ktime_t delta_time)
Ken Tsou8acade12020-07-09 03:17:35 +0800838{
839 int i;
Wasb Liuf0af1cd2020-04-14 17:33:29 +0800840 const qnum_t max_delta = ((qnumd_t)rls->rl_delta_max_soc * delta_time) /
AleX Pelosi76a3e5b2022-01-06 13:57:18 -0800841 (rls->rl_delta_max_time ? rls->rl_delta_max_time : 1);
Ken Tsou8acade12020-07-09 03:17:35 +0800842
843 if (rls->rl_fast_track)
844 return max_delta;
845
846 /* might have one table for charging and one for discharging */
847 for (i = 0; i < rls->rl_delta_soc_cnt; i++) {
848 if (rls->rl_delta_soc_limit[i] == 0)
849 break;
850
851 if (rls->rl_ssoc_target < rls->rl_delta_soc_limit[i])
Wasb Liuf0af1cd2020-04-14 17:33:29 +0800852 return ((qnumd_t)max_delta * 10) /
Ken Tsou8acade12020-07-09 03:17:35 +0800853 rls->rl_delta_soc_ratio[i];
854 }
855
856 return max_delta;
857}
858
859static qnum_t ssoc_apply_rl(struct batt_ssoc_state *ssoc)
860{
AleX Pelosiab0e9d42020-09-29 11:13:19 -0700861 const ktime_t now = get_boot_sec();
Ken Tsou8acade12020-07-09 03:17:35 +0800862 struct batt_ssoc_rl_state *rls = &ssoc->ssoc_rl_state;
863 qnum_t rl_val;
864
865 /* track ssoc_uic when buck is enabled or the minimum value of uic */
866 if (ssoc->buck_enabled ||
867 (!ssoc->buck_enabled && ssoc->ssoc_uic < rls->rl_ssoc_target))
868 rls->rl_ssoc_target = ssoc->ssoc_uic;
869
870 /* sanity on the target */
871 if (rls->rl_ssoc_target > qnum_fromint(100))
872 rls->rl_ssoc_target = qnum_fromint(100);
873 if (rls->rl_ssoc_target < qnum_fromint(0))
874 rls->rl_ssoc_target = qnum_fromint(0);
875
876 /* closely track target */
877 if (rls->rl_track_target) {
878 rl_val = rls->rl_ssoc_target;
879 } else {
880 qnum_t step;
AleX Pelosiab0e9d42020-09-29 11:13:19 -0700881 const ktime_t delta_time = now - rls->rl_ssoc_last_update;
882 const ktime_t max_delta = ssoc_rl_max_delta(rls,
Ken Tsou8acade12020-07-09 03:17:35 +0800883 ssoc->buck_enabled,
884 delta_time);
885
886 /* apply the rate limiter, delta_soc to target */
887 step = rls->rl_ssoc_target - ssoc->ssoc_rl;
888 if (step < -max_delta)
889 step = -max_delta;
890 else if (step > max_delta)
891 step = max_delta;
892
893 rl_val = ssoc->ssoc_rl + step;
894 }
895
896 /* do not increase when not connected */
897 if (!ssoc->buck_enabled && rl_val > ssoc->ssoc_rl)
898 rl_val = ssoc->ssoc_rl;
899
900 /* will report 0% when rl_no_zero clears */
901 if (rls->rl_no_zero && rl_val <= qnum_fromint(1))
902 rl_val = qnum_fromint(1);
903
904 rls->rl_ssoc_last_update = now;
905 return rl_val;
906}
907
908/* ------------------------------------------------------------------------- */
909
AleX Pelosi2d4e0392020-10-19 10:44:32 -0700910static int ssoc_get_real_raw(const struct batt_ssoc_state *ssoc)
Ken Tsou8acade12020-07-09 03:17:35 +0800911{
AleX Pelosi2d4e0392020-10-19 10:44:32 -0700912 return ssoc->ssoc_gdf;
Ken Tsou8acade12020-07-09 03:17:35 +0800913}
914
AleX Pelosi2d4e0392020-10-19 10:44:32 -0700915/* a statement :-) */
Ken Tsou8acade12020-07-09 03:17:35 +0800916static qnum_t ssoc_get_capacity_raw(const struct batt_ssoc_state *ssoc)
917{
918 return ssoc->ssoc_rl;
919}
920
AleX Pelosi2d4e0392020-10-19 10:44:32 -0700921static int ssoc_get_real(const struct batt_ssoc_state *ssoc)
922{
923 const qnum_t real_raw = ssoc_get_real_raw(ssoc);
924
925 return qnum_toint(real_raw);
926}
927
AleX Pelosicf7008d2020-08-13 13:48:10 -0700928#define SOC_ROUND_BASE 0.5
929
Ken Tsou8acade12020-07-09 03:17:35 +0800930/* reported to userspace: call while holding batt_lock */
931static int ssoc_get_capacity(const struct batt_ssoc_state *ssoc)
932{
933 const qnum_t raw = ssoc_get_capacity_raw(ssoc);
AleX Pelosicf7008d2020-08-13 13:48:10 -0700934
935 return qnum_roundint(raw, SOC_ROUND_BASE);
Ken Tsou8acade12020-07-09 03:17:35 +0800936}
937
938/* ------------------------------------------------------------------------- */
939
AleX Pelosi78a4bea2020-09-01 19:02:24 -0700940static void dump_ssoc_state(struct batt_ssoc_state *ssoc_state,
941 struct logbuffer *log)
Ken Tsou8acade12020-07-09 03:17:35 +0800942{
943 char buff[UICURVE_BUF_SZ] = { 0 };
944
945 scnprintf(ssoc_state->ssoc_state_cstr,
946 sizeof(ssoc_state->ssoc_state_cstr),
Ken Tsou94a4e832020-11-19 18:45:22 +0800947 "SSOC: l=%d%% gdf=%d.%02d uic=%d.%02d rl=%d.%02d ct=%d curve:%s rls=%d bd_cnt=%d",
Ken Tsou8acade12020-07-09 03:17:35 +0800948 ssoc_get_capacity(ssoc_state),
949 qnum_toint(ssoc_state->ssoc_gdf),
950 qnum_fracdgt(ssoc_state->ssoc_gdf),
951 qnum_toint(ssoc_state->ssoc_uic),
952 qnum_fracdgt(ssoc_state->ssoc_uic),
953 qnum_toint(ssoc_state->ssoc_rl),
954 qnum_fracdgt(ssoc_state->ssoc_rl),
955 ssoc_state->ssoc_curve_type,
956 ssoc_uicurve_cstr(buff, sizeof(buff), ssoc_state->ssoc_curve),
Ken Tsou94a4e832020-11-19 18:45:22 +0800957 ssoc_state->rl_status,
958 ssoc_state->bd_trickle_cnt);
Ken Tsou8acade12020-07-09 03:17:35 +0800959
AleX Pelosicf7008d2020-08-13 13:48:10 -0700960 logbuffer_log(log, "%s", ssoc_state->ssoc_state_cstr);
961 pr_debug("%s\n", ssoc_state->ssoc_state_cstr);
Ken Tsou8acade12020-07-09 03:17:35 +0800962}
963
964/* ------------------------------------------------------------------------- */
965
966/* call while holding batt_lock */
967static void ssoc_update(struct batt_ssoc_state *ssoc, qnum_t soc)
968{
969 struct batt_ssoc_rl_state *rls = &ssoc->ssoc_rl_state;
970 qnum_t delta;
971
972 /* low pass filter */
973 ssoc->ssoc_gdf = soc;
974 /* spoof UI @ EOC */
975 ssoc->ssoc_uic = ssoc_uicurve_map(ssoc->ssoc_gdf, ssoc->ssoc_curve);
976
977 /* first target is current UIC */
978 if (rls->rl_ssoc_target == -1) {
979 rls->rl_ssoc_target = ssoc->ssoc_uic;
980 ssoc->ssoc_rl = ssoc->ssoc_uic;
981 }
982
983 /* enable fast track when target under configured limit */
984 rls->rl_fast_track |= rls->rl_ssoc_target < rls->rl_ft_low_limit;
985
986 /*
987 * delta fast tracking during charge
988 * NOTE: might use the stats from TTF to determine the maximum rate
989 */
990 delta = rls->rl_ssoc_target - ssoc->ssoc_rl;
991 if (rls->rl_ft_delta_limit && ssoc->buck_enabled && delta > 0) {
992 /* only when SOC increase */
993 rls->rl_fast_track |= delta > rls->rl_ft_delta_limit;
994 } else if (rls->rl_ft_delta_limit && !ssoc->buck_enabled && delta < 0) {
995 /* enable fast track when target under configured limit */
996 rls->rl_fast_track |= -delta > rls->rl_ft_delta_limit;
997 }
998
999 /*
1000 * Right now a simple test on target metric falling under 0.5%
1001 * TODO: add a filter that decrements no_zero when a specific
1002 * condition is met (ex rl_ssoc_target < 1%).
1003 */
1004 if (rls->rl_no_zero)
1005 rls->rl_no_zero = rls->rl_ssoc_target > qnum_from_q8_8(128);
1006
1007 /* monotonicity and rate of change */
1008 ssoc->ssoc_rl = ssoc_apply_rl(ssoc);
1009}
1010
1011/*
1012 * Maxim could need:
1013 * 1fh AvCap, 10h FullCap. 23h FullCapNom
1014 * QC could need:
1015 * QG_CC_SOC, QG_Raw_SOC, QG_Bat_SOC, QG_Sys_SOC, QG_Mon_SOC
1016 */
1017static int ssoc_work(struct batt_ssoc_state *ssoc_state,
1018 struct power_supply *fg_psy)
1019{
1020 int soc_q8_8;
1021 qnum_t soc_raw;
1022
1023 /*
AleX Pelosid2ca4072020-09-03 22:07:27 -07001024 * TODO: GBMS_PROP_CAPACITY_RAW should return a qnum_t
Ken Tsou8acade12020-07-09 03:17:35 +08001025 * TODO: add an array here configured in DT with the properties
1026 * to query and their weights, make soc_raw come from fusion.
1027 */
AleX Pelosid2ca4072020-09-03 22:07:27 -07001028 soc_q8_8 = GPSY_GET_PROP(fg_psy, GBMS_PROP_CAPACITY_RAW);
Ken Tsou8acade12020-07-09 03:17:35 +08001029 if (soc_q8_8 < 0)
1030 return -EINVAL;
1031
1032 /*
1033 * soc_raw can come from fusion:
1034 * soc_raw = m1 * w1 + m2 * w2 + ...
1035 *
1036 * where m1, m2 are gauge metrics, w1,w1 are weights that change
1037 * with temperature, state of charge, battery health etc.
1038 */
1039 soc_raw = qnum_from_q8_8(soc_q8_8);
1040
1041 ssoc_update(ssoc_state, soc_raw);
1042 return 0;
1043}
1044
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07001045static void ssoc_change_curve_at_gdf(struct batt_ssoc_state *ssoc_state,
1046 qnum_t gdf, qnum_t capacity,
1047 enum ssoc_uic_type type)
Ken Tsou8acade12020-07-09 03:17:35 +08001048{
1049 struct ssoc_uicurve *new_curve;
Ken Tsou8acade12020-07-09 03:17:35 +08001050
1051 new_curve = (type == SSOC_UIC_TYPE_DSG) ? dsg_curve : chg_curve;
1052 ssoc_uicurve_dup(ssoc_state->ssoc_curve, new_curve);
1053 ssoc_state->ssoc_curve_type = type;
1054
1055 /* splice at (->ssoc_gdf,->ssoc_rl) because past spoof */
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07001056 ssoc_uicurve_splice(ssoc_state->ssoc_curve, gdf, capacity);
1057}
1058
1059/*
1060 * Called on connect and disconnect to adjust the UI curve. On disconnect
1061 * splice at GDF less a fixed delta while UI is at 100% (i.e. in RL) to
1062 * avoid showing 100% for "too long" after disconnect.
1063 */
1064static void ssoc_change_curve(struct batt_ssoc_state *ssoc_state, qnum_t delta,
1065 enum ssoc_uic_type type)
1066{
1067 qnum_t ssoc_level = ssoc_get_capacity(ssoc_state);
1068 qnum_t gdf = ssoc_state->ssoc_gdf; /* actual battery level */
1069
1070 /* force dsg curve when connect/disconnect with battery at 100% */
1071 if (ssoc_level >= SSOC_FULL) {
Jenny Ho2f90f952021-06-08 22:58:35 +08001072 const qnum_t rlt = qnum_fromint(ssoc_state->rl_soc_threshold);
1073
1074 /* bounds GDF - DELTA to prevent SSOC/GDF from diverging significantly */
1075 gdf = gdf > rlt ? gdf : rlt;
1076
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07001077 type = SSOC_UIC_TYPE_DSG;
1078 gdf -= delta;
1079 }
1080
Ken Tsou3dbbd1a2021-05-25 08:04:23 +08001081 /* adjust gdf to update curve[1].real in ssoc_uicurve_splice() */
1082 if (gdf > ssoc_point_full)
1083 gdf = ssoc_point_full;
1084
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07001085 ssoc_change_curve_at_gdf(ssoc_state, gdf,
1086 ssoc_get_capacity_raw(ssoc_state), type);
Ken Tsou8acade12020-07-09 03:17:35 +08001087}
1088
AleX Pelosi468b7462021-06-23 12:29:24 -07001089/* Fan levels limits from battery temperature */
1090#define FAN_BT_LIMIT_NOT_CARE 320
1091#define FAN_BT_LIMIT_LOW 420
1092#define FAN_BT_LIMIT_MED 460
1093#define FAN_BT_LIMIT_HIGH 480
1094/* Fan levels limits from charge rate */
Jenny Ho17ad0d72021-10-05 18:20:22 +08001095#define FAN_CHG_LIMIT_NOT_CARE 10
AleX Pelosi468b7462021-06-23 12:29:24 -07001096#define FAN_CHG_LIMIT_LOW 50
1097#define FAN_CHG_LIMIT_MED 70
yihsiangpeng305623d2021-05-06 15:07:05 +08001098
Jenny Ho74ae1bf2022-12-12 16:53:24 +08001099static int fan_bt_calculate_level(struct batt_drv *batt_drv)
AleX Pelosi468b7462021-06-23 12:29:24 -07001100{
1101 int level, temp, ret;
yihsiangpeng305623d2021-05-06 15:07:05 +08001102
1103 ret = gbatt_get_temp(batt_drv, &temp);
AleX Pelosi468b7462021-06-23 12:29:24 -07001104 if (ret < 0) {
yihsiangpeng305623d2021-05-06 15:07:05 +08001105
AleX Pelosi468b7462021-06-23 12:29:24 -07001106 if (batt_drv->temp_idx < 2)
1107 level = FAN_LVL_NOT_CARE;
1108 else if (batt_drv->temp_idx == 3)
1109 level = FAN_LVL_MED;
1110 else
1111 level = FAN_LVL_HIGH;
1112
1113 pr_warn("FAN_LEVEL: level=%d from temp_idx=%d (%d)\n",
1114 level, batt_drv->temp_idx, ret);
1115 return level;
1116 }
1117
yihsiangpeng63c71462023-03-16 18:25:50 +08001118 if (temp <= batt_drv->fan_bt_limits[0])
AleX Pelosi468b7462021-06-23 12:29:24 -07001119 level = FAN_LVL_NOT_CARE;
yihsiangpeng63c71462023-03-16 18:25:50 +08001120 else if (temp <= batt_drv->fan_bt_limits[1])
AleX Pelosi468b7462021-06-23 12:29:24 -07001121 level = FAN_LVL_LOW;
yihsiangpeng63c71462023-03-16 18:25:50 +08001122 else if (temp <= batt_drv->fan_bt_limits[2])
1123 level = FAN_LVL_MED;
1124 else if (temp <= batt_drv->fan_bt_limits[3])
AleX Pelosi468b7462021-06-23 12:29:24 -07001125 level = FAN_LVL_HIGH;
1126 else
1127 level = FAN_LVL_ALARM;
1128
1129 return level;
1130}
1131
Jenny Ho74ae1bf2022-12-12 16:53:24 +08001132static int fan_calculate_level(struct batt_drv *batt_drv)
AleX Pelosi468b7462021-06-23 12:29:24 -07001133{
Jenny Ho17ad0d72021-10-05 18:20:22 +08001134 int charging_rate, fan_level, chg_fan_level, cc_max;
AleX Pelosi468b7462021-06-23 12:29:24 -07001135
AleX Pelosia7e0da82021-07-15 13:09:42 -07001136 if (batt_drv->jeita_stop_charging == 1)
1137 return FAN_LVL_ALARM;
1138
1139 /* defender limits from google_charger */
AleX Pelosi468b7462021-06-23 12:29:24 -07001140 fan_level = fan_bt_calculate_level(batt_drv);
Jenny Ho17ad0d72021-10-05 18:20:22 +08001141
Ken Tsoua15d1fa2022-01-24 14:42:27 +08001142 cc_max = gvotable_get_current_int_vote(batt_drv->fcc_votable);
Jenny Ho17ad0d72021-10-05 18:20:22 +08001143 if (cc_max <= 0 || batt_drv->battery_capacity == 0)
AleX Pelosi468b7462021-06-23 12:29:24 -07001144 return fan_level;
yihsiangpeng305623d2021-05-06 15:07:05 +08001145
Jenny Ho17ad0d72021-10-05 18:20:22 +08001146 /* cc_max is -1 when disconnected */
1147 charging_rate = cc_max / batt_drv->battery_capacity / 10;
1148 if (charging_rate < FAN_CHG_LIMIT_NOT_CARE)
1149 chg_fan_level = FAN_LVL_NOT_CARE;
1150 else if (charging_rate <= FAN_CHG_LIMIT_LOW)
AleX Pelosi468b7462021-06-23 12:29:24 -07001151 chg_fan_level = FAN_LVL_LOW;
1152 else if (charging_rate <= FAN_CHG_LIMIT_MED)
1153 chg_fan_level = FAN_LVL_MED;
1154 else
1155 chg_fan_level = FAN_LVL_HIGH;
yihsiangpeng305623d2021-05-06 15:07:05 +08001156
AleX Pelosi468b7462021-06-23 12:29:24 -07001157 /* Charge rate can increase the level */
1158 if (chg_fan_level > fan_level)
1159 fan_level = chg_fan_level;
yihsiangpeng305623d2021-05-06 15:07:05 +08001160
AleX Pelosi468b7462021-06-23 12:29:24 -07001161 return fan_level;
yihsiangpeng305623d2021-05-06 15:07:05 +08001162}
1163
1164static void fan_level_reset(const struct batt_drv *batt_drv)
1165{
1166
1167 if (batt_drv->fan_level_votable)
Ken Tsoua15d1fa2022-01-24 14:42:27 +08001168 gvotable_cast_int_vote(batt_drv->fan_level_votable,
1169 "MSC_BATT", 0, false);
yihsiangpeng305623d2021-05-06 15:07:05 +08001170}
1171
Ken Tsouf956a7f2022-04-12 09:50:46 +08001172static int fan_level_cb(struct gvotable_election *el,
1173 const char *reason, void *vote)
yihsiangpeng305623d2021-05-06 15:07:05 +08001174{
Ken Tsoua15d1fa2022-01-24 14:42:27 +08001175 struct batt_drv *batt_drv = gvotable_get_data(el);
Ken Tsou15629e42022-04-01 02:10:36 +08001176 int lvl = GVOTABLE_PTR_TO_INT(vote);
yihsiangpeng305623d2021-05-06 15:07:05 +08001177
1178 if (!batt_drv)
Ken Tsouf956a7f2022-04-12 09:50:46 +08001179 return 0;
yihsiangpeng305623d2021-05-06 15:07:05 +08001180
AleX Pelosia7e0da82021-07-15 13:09:42 -07001181 if (batt_drv->fan_last_level != lvl) {
Ken Tsou7f1dce92021-09-10 08:35:44 +08001182 pr_debug("FAN_LEVEL %d->%d reason=%s\n",
Ken Tsoua15d1fa2022-01-24 14:42:27 +08001183 batt_drv->fan_last_level, lvl, reason ? reason : "<>");
AleX Pelosib8690ba2022-04-26 12:33:09 -07001184
Jenny Ho33787472022-07-04 05:18:03 +00001185 if (!chg_state_is_disconnected(&batt_drv->chg_state)) {
AleX Pelosib8690ba2022-04-26 12:33:09 -07001186 logbuffer_log(batt_drv->ttf_stats.ttf_log,
1187 "FAN_LEVEL %d->%d reason=%s",
1188 batt_drv->fan_last_level, lvl,
1189 reason ? reason : "<>");
AleX Pelosia7e0da82021-07-15 13:09:42 -07001190
Jenny Ho33787472022-07-04 05:18:03 +00001191 batt_drv->fan_last_level = lvl;
1192 if (batt_drv->psy)
1193 power_supply_changed(batt_drv->psy);
1194 } else {
1195 /* Disconnected */
1196 batt_drv->fan_last_level = lvl;
1197 }
AleX Pelosia7e0da82021-07-15 13:09:42 -07001198 }
Ken Tsouf956a7f2022-04-12 09:50:46 +08001199
1200 return 0;
yihsiangpeng305623d2021-05-06 15:07:05 +08001201}
Ken Tsou8acade12020-07-09 03:17:35 +08001202/* ------------------------------------------------------------------------- */
1203
1204/*
1205 * enter recharge logic in BATT_RL_STATUS_DISCHARGE on charger_DONE,
1206 * enter BATT_RL_STATUS_RECHARGE on Fuel Gauge FULL
1207 * NOTE: batt_rl_update_status() doesn't call this, it flip from DISCHARGE
1208 * to recharge on its own.
1209 * NOTE: call holding chg_lock
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07001210 * FIX: BatteryDefenderUI different rules when battery defender is enabled
Ken Tsou8acade12020-07-09 03:17:35 +08001211 * @pre rl_status != BATT_RL_STATUS_NONE
1212 */
1213static bool batt_rl_enter(struct batt_ssoc_state *ssoc_state,
1214 enum batt_rl_status rl_status)
1215{
1216 const int rl_current = ssoc_state->rl_status;
Ken Tsouea5a5c32020-12-11 08:09:25 +08001217 const bool enable = ssoc_state->bd_trickle_enable;
1218 const bool dry_run = ssoc_state->bd_trickle_dry_run;
Ken Tsou8acade12020-07-09 03:17:35 +08001219
1220 /*
1221 * NOTE: NO_OP when RL=DISCHARGE since batt_rl_update_status() flip
1222 * between BATT_RL_STATUS_DISCHARGE and BATT_RL_STATUS_RECHARGE
1223 * directly.
1224 */
1225 if (rl_current == rl_status || rl_current == BATT_RL_STATUS_DISCHARGE)
1226 return false;
1227
1228 /*
1229 * NOTE: rl_status transition from *->DISCHARGE on charger FULL (during
1230 * charge or at the end of recharge) and transition from
1231 * NONE->RECHARGE when battery is full (SOC==100%) before charger is.
1232 */
1233 if (rl_status == BATT_RL_STATUS_DISCHARGE) {
Ken Tsouea5a5c32020-12-11 08:09:25 +08001234 if (enable && !dry_run && ssoc_state->bd_trickle_cnt > 0) {
1235 ssoc_change_curve(ssoc_state, 0, SSOC_UIC_TYPE_DSG);
1236 } else {
1237 ssoc_uicurve_dup(ssoc_state->ssoc_curve, dsg_curve);
1238 ssoc_state->ssoc_curve_type = SSOC_UIC_TYPE_DSG;
1239 }
Ken Tsou8acade12020-07-09 03:17:35 +08001240 }
1241
1242 ssoc_update(ssoc_state, ssoc_state->ssoc_gdf);
1243 ssoc_state->rl_status = rl_status;
1244
1245 return true;
1246}
1247
1248static int ssoc_rl_read_dt(struct batt_ssoc_rl_state *rls,
1249 struct device_node *node)
1250{
1251 u32 tmp, delta_soc[RL_DELTA_SOC_MAX];
1252 int ret, i;
1253
1254 ret = of_property_read_u32(node, "google,rl_delta-max-soc", &tmp);
1255 if (ret == 0)
1256 rls->rl_delta_max_soc = qnum_fromint(tmp);
1257
1258 ret = of_property_read_u32(node, "google,rl_delta-max-time", &tmp);
1259 if (ret == 0)
1260 rls->rl_delta_max_time = tmp;
1261
1262 if (!rls->rl_delta_max_soc || !rls->rl_delta_max_time)
1263 return -EINVAL;
1264
1265 rls->rl_no_zero = of_property_read_bool(node, "google,rl_no-zero");
1266 rls->rl_track_target = of_property_read_bool(node,
1267 "google,rl_track-target");
1268
1269 ret = of_property_read_u32(node, "google,rl_ft-low-limit", &tmp);
1270 if (ret == 0)
1271 rls->rl_ft_low_limit = qnum_fromint(tmp);
1272
1273 ret = of_property_read_u32(node, "google,rl_ft-delta-limit", &tmp);
1274 if (ret == 0)
1275 rls->rl_ft_delta_limit = qnum_fromint(tmp);
1276
1277 rls->rl_delta_soc_cnt = of_property_count_elems_of_size(node,
1278 "google,rl_soc-limits",
1279 sizeof(u32));
1280 tmp = of_property_count_elems_of_size(node, "google,rl_soc-rates",
1281 sizeof(u32));
1282 if (rls->rl_delta_soc_cnt != tmp || tmp == 0) {
1283 rls->rl_delta_soc_cnt = 0;
1284 goto done;
1285 }
1286
1287 if (rls->rl_delta_soc_cnt > RL_DELTA_SOC_MAX)
1288 return -EINVAL;
1289
1290 ret = of_property_read_u32_array(node, "google,rl_soc-limits",
1291 delta_soc,
1292 rls->rl_delta_soc_cnt);
1293 if (ret < 0)
1294 return ret;
1295
1296 for (i = 0; i < rls->rl_delta_soc_cnt; i++)
1297 rls->rl_delta_soc_limit[i] = qnum_fromint(delta_soc[i]);
1298
1299 ret = of_property_read_u32_array(node, "google,rl_soc-rates",
1300 delta_soc,
1301 rls->rl_delta_soc_cnt);
1302 if (ret < 0)
1303 return ret;
1304
1305 for (i = 0; i < rls->rl_delta_soc_cnt; i++)
1306 rls->rl_delta_soc_ratio[i] = delta_soc[i];
1307
1308done:
1309 return 0;
1310}
1311
1312
1313/*
1314 * NOTE: might need to use SOC from bootloader as starting point to avoid UI
1315 * SSOC jumping around or taking long time to coverge. Could technically read
1316 * charger voltage and estimate SOC% based on empty and full voltage.
1317 */
1318static int ssoc_init(struct batt_ssoc_state *ssoc_state,
1319 struct device_node *node,
1320 struct power_supply *fg_psy)
1321{
1322 int ret, capacity;
1323
1324 ret = ssoc_rl_read_dt(&ssoc_state->ssoc_rl_state, node);
1325 if (ret < 0)
1326 ssoc_state->ssoc_rl_state.rl_track_target = 1;
1327 ssoc_state->ssoc_rl_state.rl_ssoc_target = -1;
1328
1329 /*
1330 * ssoc_work() needs a curve: start with the charge curve to prevent
1331 * SSOC% from increasing after a reboot. Curve type must be NONE until
1332 * battery knows the charger BUCK_EN state.
1333 */
1334 ssoc_uicurve_dup(ssoc_state->ssoc_curve, chg_curve);
1335 ssoc_state->ssoc_curve_type = SSOC_UIC_TYPE_NONE;
1336
1337 ret = ssoc_work(ssoc_state, fg_psy);
1338 if (ret < 0)
1339 return -EIO;
1340
1341 capacity = ssoc_get_capacity(ssoc_state);
1342 if (capacity >= SSOC_FULL) {
1343 /* consistent behavior when booting without adapter */
1344 ssoc_uicurve_dup(ssoc_state->ssoc_curve, dsg_curve);
1345 } else if (capacity < SSOC_TRUE) {
1346 /* no split */
1347 } else if (capacity < SSOC_SPOOF) {
1348 /* mark the initial point if under spoof */
1349 ssoc_uicurve_splice(ssoc_state->ssoc_curve,
1350 ssoc_state->ssoc_gdf,
1351 ssoc_state->ssoc_rl);
1352
1353 }
1354
1355 return 0;
1356}
1357
1358/* ------------------------------------------------------------------------- */
1359
1360/*
1361 * just reset state, no PS notifications no changes in the UI curve. This is
1362 * called on startup and on disconnect when the charge driver state is reset
1363 * NOTE: call holding chg_lock
1364 */
1365static void batt_rl_reset(struct batt_drv *batt_drv)
1366{
Ken Tsou76ee23d2020-12-03 00:49:48 +08001367 batt_drv->ssoc_state.rl_status = BATT_RL_STATUS_NONE;
Ken Tsou8acade12020-07-09 03:17:35 +08001368}
1369
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07001370/*
1371 * RL recharge: call after SSOC work, restart charging when gdf hit the
1372 * recharge threshold.
Ken Tsou8acade12020-07-09 03:17:35 +08001373 * NOTE: call holding chg_lock
1374 */
1375static void batt_rl_update_status(struct batt_drv *batt_drv)
1376{
1377 struct batt_ssoc_state *ssoc_state = &batt_drv->ssoc_state;
Ken Tsou793502d2020-11-19 20:34:34 +08001378 const bool bd_dry_run = ssoc_state->bd_trickle_dry_run;
1379 const int bd_cnt = ssoc_state->bd_trickle_cnt;
Ken Tsoudf64e282020-10-28 09:39:33 +08001380 int soc, rl_soc_threshold;
Ken Tsou8acade12020-07-09 03:17:35 +08001381
1382 /* already in _RECHARGE or _NONE, done */
1383 if (ssoc_state->rl_status != BATT_RL_STATUS_DISCHARGE)
1384 return;
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07001385 /* no threashold (why I am here???) */
1386 if (!ssoc_state->rl_soc_threshold)
1387 return;
Ken Tsou8acade12020-07-09 03:17:35 +08001388 /* recharge logic work on real soc */
1389 soc = ssoc_get_real(ssoc_state);
Jenny Hoa7d48db2020-12-08 15:22:09 +08001390
1391 if (ssoc_state->bd_trickle_enable)
1392 rl_soc_threshold = ((bd_cnt > 0) && !bd_dry_run) ?
1393 ssoc_state->bd_trickle_recharge_soc :
1394 ssoc_state->rl_soc_threshold;
1395 else
1396 rl_soc_threshold = ssoc_state->rl_soc_threshold;
1397
Ken Tsoudf64e282020-10-28 09:39:33 +08001398 if (soc > rl_soc_threshold)
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07001399 return;
Ken Tsou8acade12020-07-09 03:17:35 +08001400
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07001401 /* change state (will restart charge) on trigger */
1402 ssoc_state->rl_status = BATT_RL_STATUS_RECHARGE;
1403 if (batt_drv->psy)
1404 power_supply_changed(batt_drv->psy);
Ken Tsoudf64e282020-10-28 09:39:33 +08001405
Ken Tsou80f97342022-08-18 17:56:14 +08001406 if (ssoc_state->bd_trickle_full && ssoc_state->bd_trickle_eoc) {
1407 ssoc_state->bd_trickle_cnt++;
1408 ssoc_state->bd_trickle_full = false;
1409 ssoc_state->bd_trickle_eoc = false;
1410 }
Jenny Hoed474f72022-09-30 13:47:19 +08001411
1412 dump_ssoc_state(&batt_drv->ssoc_state, batt_drv->ssoc_log);
Ken Tsou8acade12020-07-09 03:17:35 +08001413}
1414
1415/* ------------------------------------------------------------------------- */
1416
Jenny Hoffc7bb32021-10-21 04:31:36 +08001417static void bat_log_ttf_change(ktime_t estimate, int max_ratio, struct batt_drv *batt_drv)
1418{
1419 const struct gbms_charging_event *ce_data = &batt_drv->ce_data;
1420 char buff[LOG_BUFFER_ENTRY_SIZE];
1421 long elap, ibatt_avg, icl_avg;
1422 int i, len = 0;
1423
1424 len += scnprintf(&buff[len], sizeof(buff) - len,
Jenny Ho48823402022-08-15 06:57:00 +00001425 "MSC_TTF: est:%lld(%lldmin), max_ratio:%d ",
1426 estimate, estimate / 60, max_ratio);
Jenny Hoffc7bb32021-10-21 04:31:36 +08001427
1428 for (i = 0; i < GBMS_STATS_TIER_COUNT; i++) {
1429 elap = ce_data->tier_stats[i].time_fast +
1430 ce_data->tier_stats[i].time_taper +
1431 ce_data->tier_stats[i].time_other;
1432 if (elap) {
1433 ibatt_avg = ce_data->tier_stats[i].ibatt_sum / elap;
1434 icl_avg = ce_data->tier_stats[i].icl_sum / elap;
1435 } else {
1436 ibatt_avg = 0;
1437 icl_avg = 0;
1438 }
1439
1440 len += scnprintf(&buff[len], sizeof(buff) - len,
1441 "[%d:%ld,%ld,%ld]", i, elap / 60, ibatt_avg, icl_avg);
1442 }
1443
1444 pr_info("%s", buff);
1445
1446 batt_drv->ttf_est = estimate;
1447}
1448
AleX Pelosia6ad6ad2020-10-29 13:05:27 -07001449/*
1450 * msc_logic_health() sync ce_data->ce_health to batt_drv->chg_health
AleX Pelosi854b8e12020-10-31 11:17:52 -07001451 * . return -EINVAL when the device is not connected to power -ERANGE when
1452 * ttf_soc_estimate() returns a negative value (invalid parameters, or
1453 * corrupted internal data)
1454 * . the estimate is 0 when the device is at 100%.
1455 * . the estimate is negative during debounce, when in overheat, when
1456 * custom charge levels are active.
AleX Pelosia6ad6ad2020-10-29 13:05:27 -07001457 */
Jenny Hoffc7bb32021-10-21 04:31:36 +08001458#define MIN_DELTA_FOR_LOG_S 60
1459static int batt_ttf_estimate(ktime_t *res, struct batt_drv *batt_drv)
Ken Tsou8acade12020-07-09 03:17:35 +08001460{
Jenny Ho6f7ec522020-05-19 09:04:53 +08001461 qnum_t raw_full = ssoc_point_full - qnum_rconst(SOC_ROUND_BASE);
AleX Pelosi854b8e12020-10-31 11:17:52 -07001462 qnum_t soc_raw = ssoc_get_real_raw(&batt_drv->ssoc_state);
Jenny Hob7fc3382021-10-23 21:57:49 +08001463 ktime_t estimate = 0;
1464 int rc = 0, max_ratio = 0;
Ken Tsou8acade12020-07-09 03:17:35 +08001465
1466 if (batt_drv->ssoc_state.buck_enabled != 1)
1467 return -EINVAL;
1468
AleX Pelosi854b8e12020-10-31 11:17:52 -07001469 if (batt_drv->ttf_stats.ttf_fake != -1) {
1470 estimate = batt_drv->ttf_stats.ttf_fake;
Ken Tsou8acade12020-07-09 03:17:35 +08001471 goto done;
AleX Pelosi854b8e12020-10-31 11:17:52 -07001472 }
Ken Tsou8acade12020-07-09 03:17:35 +08001473
AleX Pelosicf7008d2020-08-13 13:48:10 -07001474 /* TTF is 0 when UI shows 100% */
1475 if (ssoc_get_capacity(&batt_drv->ssoc_state) == SSOC_FULL) {
1476 estimate = 0;
1477 goto done;
1478 }
1479
AleX Pelosi854b8e12020-10-31 11:17:52 -07001480 /* no estimates during debounce or with special profiles */
1481 if (batt_drv->ttf_debounce ||
1482 batt_drv->batt_health == POWER_SUPPLY_HEALTH_OVERHEAT ||
1483 batt_drv->chg_state.f.flags & GBMS_CS_FLAG_CCLVL) {
AleX Pelosi32458432020-09-05 11:02:39 -07001484 estimate = -1;
1485 goto done;
1486 }
1487
AleX Pelosicf7008d2020-08-13 13:48:10 -07001488 /*
Jenny Ho6f7ec522020-05-19 09:04:53 +08001489 * Handle rounding (removing it from the end)
AleX Pelosicf7008d2020-08-13 13:48:10 -07001490 * example: 96.64% with SOC_ROUND_BASE = 0.5 -> UI = 97
1491 * ttf = elap[96] * 0.36 + elap[97] + elap[98] +
Jenny Ho6f7ec522020-05-19 09:04:53 +08001492 * elap[99] * (1 - 0.5)
AleX Pelosi854b8e12020-10-31 11:17:52 -07001493 *
1494 * negative return value (usually) means data corruption
AleX Pelosicf7008d2020-08-13 13:48:10 -07001495 */
Ken Tsou8acade12020-07-09 03:17:35 +08001496 rc = ttf_soc_estimate(&estimate, &batt_drv->ttf_stats,
Jenny Ho6f7ec522020-05-19 09:04:53 +08001497 &batt_drv->ce_data, soc_raw, raw_full);
Ken Tsou8acade12020-07-09 03:17:35 +08001498 if (rc < 0)
1499 estimate = -1;
Jenny Hob7fc3382021-10-23 21:57:49 +08001500 else
1501 max_ratio = rc;
Ken Tsou8acade12020-07-09 03:17:35 +08001502
Jenny Hoffc7bb32021-10-21 04:31:36 +08001503 /* Log data when changed over 1 min */
1504 if (abs(batt_drv->ttf_est - estimate) > MIN_DELTA_FOR_LOG_S)
1505 bat_log_ttf_change(estimate, max_ratio, batt_drv);
Ken Tsou8acade12020-07-09 03:17:35 +08001506
1507done:
1508 *res = estimate;
Jenny Hob7fc3382021-10-23 21:57:49 +08001509 return max_ratio;
Ken Tsou8acade12020-07-09 03:17:35 +08001510}
1511
1512/* ------------------------------------------------------------------------- */
AleX Pelosi0d261512020-05-07 12:17:07 -07001513/* CEV = Charging EVent */
AleX Pelosi4a9035f2020-02-28 19:09:57 -08001514static void cev_stats_init(struct gbms_charging_event *ce_data,
1515 const struct gbms_chg_profile *profile)
Ken Tsou8acade12020-07-09 03:17:35 +08001516{
1517 int i;
1518
1519 memset(ce_data, 0, sizeof(*ce_data));
1520
1521 ce_data->chg_profile = profile;
1522 ce_data->charging_stats.voltage_in = -1;
1523 ce_data->charging_stats.ssoc_in = -1;
1524 ce_data->charging_stats.voltage_out = -1;
1525 ce_data->charging_stats.ssoc_out = -1;
1526
1527 ttf_soc_init(&ce_data->soc_stats);
1528 ce_data->last_soc = -1;
1529
AleX Pelosia6ad6ad2020-10-29 13:05:27 -07001530 for (i = 0; i < GBMS_STATS_TIER_COUNT ; i++)
Prasanna Prapancham320dcfe2022-06-27 20:20:46 +00001531 gbms_tier_stats_init(&ce_data->tier_stats[i], i);
AleX Pelosi0c88ff32020-04-02 01:04:51 -07001532
AleX Pelosi0d261512020-05-07 12:17:07 -07001533 /* batt_chg_health_stats_close() will fix this */
Prasanna Prapancham320dcfe2022-06-27 20:20:46 +00001534 gbms_tier_stats_init(&ce_data->health_stats, GBMS_STATS_AC_TI_INVALID);
1535 gbms_tier_stats_init(&ce_data->health_pause_stats, GBMS_STATS_AC_TI_PAUSE);
1536 gbms_tier_stats_init(&ce_data->health_dryrun_stats, GBMS_STATS_AC_TI_V2_PREDICT);
Stephane Leecbb07ee2020-09-14 12:24:09 -07001537
Prasanna Prapancham320dcfe2022-06-27 20:20:46 +00001538 gbms_tier_stats_init(&ce_data->full_charge_stats, GBMS_STATS_AC_TI_FULL_CHARGE);
1539 gbms_tier_stats_init(&ce_data->high_soc_stats, GBMS_STATS_AC_TI_HIGH_SOC);
1540 gbms_tier_stats_init(&ce_data->overheat_stats, GBMS_STATS_BD_TI_OVERHEAT_TEMP);
1541 gbms_tier_stats_init(&ce_data->cc_lvl_stats, GBMS_STATS_BD_TI_CUSTOM_LEVELS);
1542 gbms_tier_stats_init(&ce_data->trickle_stats, GBMS_STATS_BD_TI_TRICKLE_CLEARED);
Jenny Hof299c742023-02-14 17:58:59 +08001543 gbms_tier_stats_init(&ce_data->temp_filter_stats, GBMS_STATS_TEMP_FILTER);
Ken Tsou8acade12020-07-09 03:17:35 +08001544}
1545
1546static void batt_chg_stats_start(struct batt_drv *batt_drv)
1547{
1548 union gbms_ce_adapter_details ad;
1549 struct gbms_charging_event *ce_data = &batt_drv->ce_data;
AleX Pelosiab0e9d42020-09-29 11:13:19 -07001550 const ktime_t now = get_boot_sec();
Ken Tsou8acade12020-07-09 03:17:35 +08001551 int vin, cc_in;
1552
1553 mutex_lock(&batt_drv->stats_lock);
1554 ad.v = batt_drv->ce_data.adapter_details.v;
AleX Pelosi4a9035f2020-02-28 19:09:57 -08001555 cev_stats_init(ce_data, &batt_drv->chg_profile);
Ken Tsou8acade12020-07-09 03:17:35 +08001556 batt_drv->ce_data.adapter_details.v = ad.v;
1557
AleX Pelosic10eb8b2021-12-14 13:20:21 -08001558 vin = GPSY_GET_PROP(batt_drv->fg_psy, POWER_SUPPLY_PROP_VOLTAGE_NOW);
Ken Tsou8acade12020-07-09 03:17:35 +08001559 ce_data->charging_stats.voltage_in = (vin < 0) ? -1 : vin / 1000;
1560 ce_data->charging_stats.ssoc_in =
1561 ssoc_get_capacity(&batt_drv->ssoc_state);
1562 cc_in = GPSY_GET_PROP(batt_drv->fg_psy,
1563 POWER_SUPPLY_PROP_CHARGE_COUNTER);
1564 ce_data->charging_stats.cc_in = (cc_in < 0) ? -1 : cc_in / 1000;
1565
1566 ce_data->charging_stats.ssoc_out = -1;
1567 ce_data->charging_stats.voltage_out = -1;
1568
1569 ce_data->first_update = now;
1570 ce_data->last_update = now;
1571
1572 mutex_unlock(&batt_drv->stats_lock);
1573}
1574
1575/* call holding stats_lock */
Jenny Hoa9ecc2f2021-04-06 11:39:21 +08001576static bool batt_chg_stats_qual(const struct batt_drv *batt_drv)
Ken Tsou8acade12020-07-09 03:17:35 +08001577{
Jenny Hoa9ecc2f2021-04-06 11:39:21 +08001578 const struct gbms_charging_event *ce_data = &batt_drv->ce_data;
Ken Tsou8acade12020-07-09 03:17:35 +08001579 const long elap = ce_data->last_update - ce_data->first_update;
1580 const long ssoc_delta = ce_data->charging_stats.ssoc_out -
1581 ce_data->charging_stats.ssoc_in;
1582
Jenny Hoa9ecc2f2021-04-06 11:39:21 +08001583 return elap >= batt_drv->chg_sts_qual_time ||
1584 ssoc_delta >= batt_drv->chg_sts_delta_soc;
Ken Tsou8acade12020-07-09 03:17:35 +08001585}
1586
1587/* call holding stats_lock */
Ken Tsou8acade12020-07-09 03:17:35 +08001588static void batt_chg_stats_soc_update(struct gbms_charging_event *ce_data,
AleX Pelosiab0e9d42020-09-29 11:13:19 -07001589 qnum_t soc, ktime_t elap, int tier_index,
Ken Tsou8acade12020-07-09 03:17:35 +08001590 int cc)
1591{
1592 int index;
1593 const int last_soc = ce_data->last_soc;
1594
1595 index = qnum_toint(soc);
1596 if (index < 0)
1597 index = 0;
1598 if (index > 100)
1599 index = 100;
1600 if (index < last_soc)
1601 return;
1602
1603 if (ce_data->soc_stats.elap[index] == 0) {
1604 ce_data->soc_stats.ti[index] = tier_index;
1605 ce_data->soc_stats.cc[index] = cc;
1606 }
1607
1608 if (last_soc != -1)
1609 ce_data->soc_stats.elap[last_soc] += elap;
1610
1611 ce_data->last_soc = index;
1612}
1613
Stephane Leecbb07ee2020-09-14 12:24:09 -07001614/* call holding stats_lock */
1615static void batt_chg_stats_update(struct batt_drv *batt_drv, int temp_idx,
1616 int tier_idx, int ibatt_ma, int temp,
1617 ktime_t elap)
1618{
AleX Pelosi2d4e0392020-10-19 10:44:32 -07001619 const int soc_real = ssoc_get_real(&batt_drv->ssoc_state);
AleX Pelosia6ad6ad2020-10-29 13:05:27 -07001620 const int msc_state = batt_drv->msc_state; /* last msc_state */
1621 struct gbms_charging_event *ce_data = &batt_drv->ce_data;
1622 struct gbms_ce_tier_stats *tier = NULL;
Prasanna Prapancham320dcfe2022-06-27 20:20:46 +00001623 int cc, soc_in;
Stephane Leecbb07ee2020-09-14 12:24:09 -07001624
1625 if (elap == 0)
1626 return;
1627
1628 /* TODO: read at start of tier and update cc_total of previous */
1629 cc = GPSY_GET_PROP(batt_drv->fg_psy, POWER_SUPPLY_PROP_CHARGE_COUNTER);
1630 if (cc < 0) {
1631 pr_debug("MSC_STAT cannot read cc=%d\n", cc);
1632 return;
1633 }
1634 cc = cc / 1000;
1635
Prasanna Prapancham320dcfe2022-06-27 20:20:46 +00001636 soc_in = GPSY_GET_PROP(batt_drv->fg_psy, GBMS_PROP_CAPACITY_RAW);
1637 if (soc_in < 0) {
1638 pr_info("MSC_STAT cannot read soc_in=%d\n", soc_in);
Stephane Leefa678232022-10-12 16:55:04 -07001639 /* We still want to update initialized tiers. */
1640 soc_in = -1;
Prasanna Prapancham320dcfe2022-06-27 20:20:46 +00001641 }
1642
Stephane Lee9af33272021-05-20 17:03:06 -07001643 /* Note: To log new voltage tiers, add to list in go/pixel-vtier-defs */
1644 /* --- Log tiers in PARALLEL below --- */
1645
AleX Pelosia6ad6ad2020-10-29 13:05:27 -07001646 if (soc_real >= SSOC_HIGH_SOC)
Prasanna Prapancham320dcfe2022-06-27 20:20:46 +00001647 gbms_stats_update_tier(temp_idx, ibatt_ma, temp, elap, cc,
1648 &batt_drv->chg_state, msc_state, soc_in,
1649 &ce_data->high_soc_stats);
AleX Pelosia6ad6ad2020-10-29 13:05:27 -07001650
Stephane Lee463e85d2021-08-16 14:50:36 -07001651 if (batt_drv->chg_health.dry_run_deadline > 0)
Prasanna Prapancham320dcfe2022-06-27 20:20:46 +00001652 gbms_stats_update_tier(temp_idx, ibatt_ma, temp, elap, cc,
1653 &batt_drv->chg_state, msc_state, soc_in,
1654 &ce_data->health_dryrun_stats);
Stephane Lee463e85d2021-08-16 14:50:36 -07001655
Stephane Lee9af33272021-05-20 17:03:06 -07001656 /* --- Log tiers in SERIES below --- */
1657 if (batt_drv->batt_full) {
AleX Pelosia6ad6ad2020-10-29 13:05:27 -07001658
Stephane Leecbb07ee2020-09-14 12:24:09 -07001659 /* Override regular charge tiers when fully charged */
Prasanna Prapancham320dcfe2022-06-27 20:20:46 +00001660 gbms_stats_update_tier(temp_idx, ibatt_ma, temp, elap, cc,
1661 &batt_drv->chg_state, msc_state, soc_in,
1662 &ce_data->full_charge_stats);
Stephane Leecbb07ee2020-09-14 12:24:09 -07001663
Stephane Lee2b9ea732021-05-23 23:39:54 -07001664 } else if (msc_state == MSC_HEALTH_PAUSE) {
1665
1666 /*
1667 * We log the pause tier in different AC tier groups so that we
1668 * can capture pause time separately.
1669 */
Prasanna Prapancham320dcfe2022-06-27 20:20:46 +00001670 gbms_stats_update_tier(temp_idx, ibatt_ma, temp, elap, cc,
1671 &batt_drv->chg_state, msc_state, soc_in,
1672 &ce_data->health_pause_stats);
Stephane Lee2b9ea732021-05-23 23:39:54 -07001673
Jenny Ho1c2ec2b2021-12-10 17:43:14 +08001674 } else if (msc_state == MSC_HEALTH || msc_state == MSC_HEALTH_ALWAYS_ON) {
Stephane Leecbb07ee2020-09-14 12:24:09 -07001675 /*
AleX Pelosia6ad6ad2020-10-29 13:05:27 -07001676 * It works because msc_logic call BEFORE updating msc_state.
1677 * NOTE: that OVERHEAT and CCLVL disable AC, I should not be
1678 * here if either of them are set.
Stephane Lee2b9ea732021-05-23 23:39:54 -07001679 * NOTE: We currently only log time when AC is ACTIVE.
1680 * Thus, when disconnecting in ENABLED state, we will log a
1681 * GBMS_STATS_AC_TI_ENABLED tier with no time, and the regular
1682 * charge time is accumulated in normal charge tiers.
Stephane Lee9af33272021-05-20 17:03:06 -07001683 * Similarly, once we reach 100%, we stop counting time in the
1684 * health tier and we rely on the full_charge_stats.
Stephane Leecbb07ee2020-09-14 12:24:09 -07001685 */
1686
1687 /* tier used for TTF during HC, check msc_logic_health() */
Prasanna Prapancham320dcfe2022-06-27 20:20:46 +00001688 gbms_stats_update_tier(temp_idx, ibatt_ma, temp, elap, cc,
1689 &batt_drv->chg_state, msc_state, soc_in,
1690 &ce_data->health_stats);
Stephane Leecbb07ee2020-09-14 12:24:09 -07001691 } else {
1692 const qnum_t soc = ssoc_get_capacity_raw(&batt_drv->ssoc_state);
1693
1694 /* book to previous soc unless discharging */
1695 if (msc_state != MSC_DSG) {
1696 /* TODO: should I use ssoc instead? */
AleX Pelosia6ad6ad2020-10-29 13:05:27 -07001697 batt_chg_stats_soc_update(ce_data, soc, elap,
Stephane Leecbb07ee2020-09-14 12:24:09 -07001698 tier_idx, cc);
1699 }
1700
AleX Pelosia6ad6ad2020-10-29 13:05:27 -07001701 /*
1702 * ce_data.tier_stats[tier_idx] are used for time to full.
1703 * Do not book to them if we are in overheat or LVL
1704 */
1705 tier = &ce_data->tier_stats[tier_idx];
Stephane Leecbb07ee2020-09-14 12:24:09 -07001706 }
1707
Stephane Lee9af33272021-05-20 17:03:06 -07001708 /* --- Log tiers in PARALLEL that MUST NULL normal tiers below --- */
1709
AleX Pelosia6ad6ad2020-10-29 13:05:27 -07001710 /* batt_drv->batt_health is protected with chg_lock, */
1711 if (batt_drv->batt_health == POWER_SUPPLY_HEALTH_OVERHEAT) {
Prasanna Prapancham320dcfe2022-06-27 20:20:46 +00001712 gbms_stats_update_tier(temp_idx, ibatt_ma, temp, elap, cc,
1713 &batt_drv->chg_state, msc_state, soc_in,
1714 &ce_data->overheat_stats);
AleX Pelosia6ad6ad2020-10-29 13:05:27 -07001715 tier = NULL;
Stephane Leecbb07ee2020-09-14 12:24:09 -07001716 }
AleX Pelosia6ad6ad2020-10-29 13:05:27 -07001717
1718 /* custom charge levels (DWELL-DEFEND or RETAIL) */
1719 if (batt_drv->chg_state.f.flags & GBMS_CS_FLAG_CCLVL) {
Prasanna Prapancham320dcfe2022-06-27 20:20:46 +00001720 gbms_stats_update_tier(temp_idx, ibatt_ma, temp, elap, cc,
1721 &batt_drv->chg_state, msc_state, soc_in,
1722 &ce_data->cc_lvl_stats);
AleX Pelosia6ad6ad2020-10-29 13:05:27 -07001723 tier = NULL;
1724 }
1725
Jenny Hof299c742023-02-14 17:58:59 +08001726 if (batt_drv->temp_filter.enable) {
1727 struct batt_temp_filter *temp_filter = &batt_drv->temp_filter;
1728 int no_filter_temp = temp_filter->sample[temp_filter->last_idx];
1729 gbms_stats_update_tier(temp_idx, ibatt_ma, no_filter_temp, elap, cc,
1730 &batt_drv->chg_state, msc_state, soc_in,
1731 &ce_data->temp_filter_stats);
1732 tier = NULL;
1733 }
1734
AleX Pelosia6ad6ad2020-10-29 13:05:27 -07001735 /*
1736 * Time/current spent in OVERHEAT or at CustomLevel should not
1737 * be booked to ce_data.tier_stats[tier_idx]
1738 */
AleX Pelosia6ad6ad2020-10-29 13:05:27 -07001739 if (!tier)
1740 return;
1741
Prasanna Prapancham320dcfe2022-06-27 20:20:46 +00001742 gbms_stats_update_tier(temp_idx, ibatt_ma, temp, elap, cc,
1743 &batt_drv->chg_state, msc_state, soc_in, tier);
Stephane Leecbb07ee2020-09-14 12:24:09 -07001744}
AleX Pelosi0d261512020-05-07 12:17:07 -07001745
AleX Pelosi043ffbe2020-06-24 22:48:30 -07001746static int batt_chg_health_vti(const struct batt_chg_health *chg_health)
AleX Pelosi0d261512020-05-07 12:17:07 -07001747{
AleX Pelosi043ffbe2020-06-24 22:48:30 -07001748 enum chg_health_state rest_state = chg_health->rest_state;
1749 ktime_t rest_deadline = chg_health->rest_deadline;
AleX Pelosi0d261512020-05-07 12:17:07 -07001750 int tier_idx = GBMS_STATS_AC_TI_INVALID;
Stephane Lee595f3182020-06-26 18:13:02 -07001751 bool aon_enabled = chg_health->always_on_soc != -1;
AleX Pelosi0d261512020-05-07 12:17:07 -07001752
1753 switch (rest_state) {
AleX Pelosi600cb122020-10-29 10:05:47 -07001754 /* battery defender did it */
1755 case CHG_HEALTH_BD_DISABLED:
AleX Pelosia6ad6ad2020-10-29 13:05:27 -07001756 case CHG_HEALTH_CCLVL_DISABLED:
AleX Pelosi600cb122020-10-29 10:05:47 -07001757 tier_idx = GBMS_STATS_AC_TI_DEFENDER;
1758 break;
AleX Pelosi0d261512020-05-07 12:17:07 -07001759 /* user disabled with deadline */
1760 case CHG_HEALTH_USER_DISABLED:
Stephane Leeaaf28cd2020-08-26 11:26:06 -07001761 if (rest_deadline == CHG_DEADLINE_SETTING)
Stephane Lee41e87c42020-09-02 23:05:34 -07001762 tier_idx = GBMS_STATS_AC_TI_DISABLE_SETTING;
1763 else if (rest_deadline == CHG_DEADLINE_SETTING_STOP)
1764 tier_idx = GBMS_STATS_AC_TI_DISABLE_SETTING_STOP;
Stephane Lee4a984a22022-01-14 15:14:07 -08001765 else if (rest_deadline == CHG_DEADLINE_DIALOG)
1766 tier_idx = GBMS_STATS_AC_TI_DISABLE_DIALOG;
Stephane Lee41e87c42020-09-02 23:05:34 -07001767 else
1768 tier_idx = GBMS_STATS_AC_TI_DISABLE_MISC;
AleX Pelosi0d261512020-05-07 12:17:07 -07001769 break;
1770 /* missed the deadline, TODO: log the deadline */
1771 case CHG_HEALTH_DISABLED:
1772 tier_idx = GBMS_STATS_AC_TI_DISABLED;
1773 break;
1774 /* disconnected in active mode, TODO: log the deadline */
1775 case CHG_HEALTH_ACTIVE:
Jenny Hof189bfb2021-05-10 16:57:55 +08001776 case CHG_HEALTH_PAUSE:
Stephane Lee595f3182020-06-26 18:13:02 -07001777 if (aon_enabled)
1778 tier_idx = GBMS_STATS_AC_TI_ACTIVE_AON;
1779 else
1780 tier_idx = GBMS_STATS_AC_TI_ACTIVE;
AleX Pelosi0d261512020-05-07 12:17:07 -07001781 break;
1782 /* never became active */
1783 case CHG_HEALTH_ENABLED:
Stephane Lee595f3182020-06-26 18:13:02 -07001784 if (aon_enabled)
1785 tier_idx = GBMS_STATS_AC_TI_ENABLED_AON;
1786 else
1787 tier_idx = GBMS_STATS_AC_TI_ENABLED;
AleX Pelosi0d261512020-05-07 12:17:07 -07001788 break;
1789 /* active, worked */
1790 case CHG_HEALTH_DONE:
Jenny Ho1c2ec2b2021-12-10 17:43:14 +08001791 if (aon_enabled)
1792 tier_idx = GBMS_STATS_AC_TI_DONE_AON;
1793 else
1794 tier_idx = GBMS_STATS_AC_TI_VALID;
AleX Pelosi0d261512020-05-07 12:17:07 -07001795 break;
1796 default:
1797 break;
1798 }
1799
1800 return tier_idx;
1801}
1802
Jenny Hoa36dd632020-08-11 17:47:45 +08001803static int batt_chg_vbat2tier(const int vbatt_idx)
1804{
1805 return vbatt_idx < GBMS_STATS_TIER_COUNT ?
1806 vbatt_idx : GBMS_STATS_TIER_COUNT - 1;
1807}
1808
Jack Wu58ea50a2022-06-04 13:22:22 +08001809static int batt_bpst_stats_update(struct batt_drv *batt_drv)
1810{
1811 struct batt_bpst *bpst_state = &batt_drv->bpst_state;
1812
1813 if (!bpst_state->bpst_enable)
1814 return BPST_BATT_UNKNOWN;
1815
1816 if (bpst_state->bpst_cell_fault)
1817 return BPST_BATT_CELL_FAULT;
1818
1819 if (bpst_state->bpst_sbd_status)
1820 return BPST_BATT_DISCONNECT;
1821
1822 return BPST_BATT_CONNECT;
1823}
1824
Ken Tsou8acade12020-07-09 03:17:35 +08001825/* Only the qualified copy gets the timestamp and the exit voltage. */
1826static bool batt_chg_stats_close(struct batt_drv *batt_drv,
1827 char *reason,
1828 bool force)
1829{
1830 bool publish;
1831 const int vout = GPSY_GET_PROP(batt_drv->fg_psy,
1832 POWER_SUPPLY_PROP_VOLTAGE_NOW);
1833 const int cc_out = GPSY_GET_PROP(batt_drv->fg_psy,
1834 POWER_SUPPLY_PROP_CHARGE_COUNTER);
Stephane Lee463e85d2021-08-16 14:50:36 -07001835 const ktime_t now = get_boot_sec();
1836 const ktime_t dry_run_deadline = batt_drv->chg_health.dry_run_deadline;
Ken Tsou8acade12020-07-09 03:17:35 +08001837
1838 /* book last period to the current tier
1839 * NOTE: vbatt_idx != -1 -> temp_idx != -1
1840 */
1841 if (batt_drv->vbatt_idx != -1 && batt_drv->temp_idx != -1) {
AleX Pelosiab0e9d42020-09-29 11:13:19 -07001842 const ktime_t elap = now - batt_drv->ce_data.last_update;
Jenny Hoa36dd632020-08-11 17:47:45 +08001843 const int tier_idx = batt_chg_vbat2tier(batt_drv->vbatt_idx);
Jenny Ho5a2657e2022-12-12 14:36:57 +08001844 int ibatt, temp, rc = 0;
1845
1846 /* use default value to close charging session when read fail */
1847 ibatt = GPSY_GET_INT_PROP(batt_drv->fg_psy, POWER_SUPPLY_PROP_CURRENT_NOW, &rc);
1848 if (rc != 0)
1849 ibatt = 0;
1850
1851 rc = gbatt_get_raw_temp(batt_drv, &temp);
1852 if (rc != 0)
1853 temp = 250;
Ken Tsou8acade12020-07-09 03:17:35 +08001854
1855 batt_chg_stats_update(batt_drv,
Jenny Hoa36dd632020-08-11 17:47:45 +08001856 batt_drv->temp_idx, tier_idx,
Jenny Hoa5f74e12020-07-28 13:22:31 +08001857 ibatt / 1000, temp, elap);
Ken Tsou8acade12020-07-09 03:17:35 +08001858 batt_drv->ce_data.last_update = now;
1859 }
1860
1861 /* record the closing in data (and qual) */
1862 batt_drv->ce_data.charging_stats.voltage_out =
1863 (vout < 0) ? -1 : vout / 1000;
1864 batt_drv->ce_data.charging_stats.ssoc_out =
1865 ssoc_get_capacity(&batt_drv->ssoc_state);
1866 batt_drv->ce_data.charging_stats.cc_out =
1867 (cc_out < 0) ? -1 : cc_out / 1000;
1868
AleX Pelosi0d261512020-05-07 12:17:07 -07001869 /* close/fix heath charge data (if enabled) */
Jenny Ho6f7ec522020-05-19 09:04:53 +08001870 memcpy(&batt_drv->ce_data.ce_health, &batt_drv->chg_health,
1871 sizeof(batt_drv->ce_data.ce_health));
AleX Pelosi0d261512020-05-07 12:17:07 -07001872 batt_drv->ce_data.health_stats.vtier_idx =
AleX Pelosi043ffbe2020-06-24 22:48:30 -07001873 batt_chg_health_vti(&batt_drv->chg_health);
Stephane Lee463e85d2021-08-16 14:50:36 -07001874 batt_drv->ce_data.health_dryrun_stats.vtier_idx =
1875 (now > dry_run_deadline) ? GBMS_STATS_AC_TI_V2_PREDICT_SUCCESS :
1876 GBMS_STATS_AC_TI_V2_PREDICT;
AleX Pelosi0d261512020-05-07 12:17:07 -07001877
Ken Tsou8acade12020-07-09 03:17:35 +08001878 /* TODO: add a field to ce_data to qual weird charge sessions */
Jenny Hoa9ecc2f2021-04-06 11:39:21 +08001879 publish = force || batt_chg_stats_qual(batt_drv);
Ken Tsou8acade12020-07-09 03:17:35 +08001880 if (publish) {
1881 struct gbms_charging_event *ce_qual = &batt_drv->ce_qual;
1882
AleX Pelosi0d261512020-05-07 12:17:07 -07001883 /* all charge tiers including health */
Ken Tsou8acade12020-07-09 03:17:35 +08001884 memcpy(ce_qual, &batt_drv->ce_data, sizeof(*ce_qual));
1885
Jenny Hof189bfb2021-05-10 16:57:55 +08001886 pr_info("MSC_STAT %s: elap=%lld ssoc=%d->%d v=%d->%d c=%d->%d hdl=%lld hrs=%d hti=%d/%d\n",
Ken Tsou8acade12020-07-09 03:17:35 +08001887 reason,
1888 ce_qual->last_update - ce_qual->first_update,
1889 ce_qual->charging_stats.ssoc_in,
1890 ce_qual->charging_stats.ssoc_out,
1891 ce_qual->charging_stats.voltage_in,
1892 ce_qual->charging_stats.voltage_out,
1893 ce_qual->charging_stats.cc_in,
AleX Pelosi0d261512020-05-07 12:17:07 -07001894 ce_qual->charging_stats.cc_out,
Jenny Ho6f7ec522020-05-19 09:04:53 +08001895 ce_qual->ce_health.rest_deadline,
1896 ce_qual->ce_health.rest_state,
Jenny Hof189bfb2021-05-10 16:57:55 +08001897 ce_qual->health_stats.vtier_idx,
1898 ce_qual->health_pause_stats.vtier_idx);
Ken Tsou8acade12020-07-09 03:17:35 +08001899 }
1900
1901 return publish;
1902}
1903
Jenny Hocced17c2020-04-30 18:20:16 +08001904static int batt_chg_stats_soc_next(const struct gbms_charging_event *ce_data,
1905 int i)
1906{
1907 int soc_next;
1908
1909 if (i == GBMS_STATS_TIER_COUNT -1)
1910 return ce_data->last_soc;
1911
1912 soc_next = ce_data->tier_stats[i + 1].soc_in >> 8;
1913 if (soc_next <= 0)
1914 return ce_data->last_soc;
1915
1916 return soc_next;
1917}
1918
Jenny Hocced17c2020-04-30 18:20:16 +08001919static void bat_log_chg_stats(struct logbuffer *log,
1920 const struct gbms_charging_event *ce_data)
1921{
1922 const char *adapter_name =
1923 gbms_chg_ev_adapter_s(ce_data->adapter_details.ad_type);
1924 int i;
1925
1926 logbuffer_log(log, "A: %s,%d,%d,%d",
1927 adapter_name,
1928 ce_data->adapter_details.ad_type,
1929 ce_data->adapter_details.ad_voltage * 100,
1930 ce_data->adapter_details.ad_amperage * 100);
1931
Jack Wu58ea50a2022-06-04 13:22:22 +08001932 logbuffer_log(log, "S: %hu,%hu, %hu,%hu %hu,%hu %ld,%ld, %u",
Jenny Hocced17c2020-04-30 18:20:16 +08001933 ce_data->charging_stats.ssoc_in,
1934 ce_data->charging_stats.voltage_in,
1935 ce_data->charging_stats.ssoc_out,
1936 ce_data->charging_stats.voltage_out,
1937 ce_data->charging_stats.cc_in,
1938 ce_data->charging_stats.cc_out,
1939 ce_data->first_update,
AleX Pelosic10eb8b2021-12-14 13:20:21 -08001940 ce_data->last_update,
Jack Wu58ea50a2022-06-04 13:22:22 +08001941 ce_data->chg_profile->capacity_ma);
Jenny Hocced17c2020-04-30 18:20:16 +08001942
1943 for (i = 0; i < GBMS_STATS_TIER_COUNT; i++) {
1944 const int soc_next = batt_chg_stats_soc_next(ce_data, i);
1945 const int soc_in = ce_data->tier_stats[i].soc_in >> 8;
1946 const long elap = ce_data->tier_stats[i].time_fast +
1947 ce_data->tier_stats[i].time_taper +
1948 ce_data->tier_stats[i].time_other;
1949 /* retrun len in below functions sometimes more than 256 */
1950 char buff[LOG_BUFFER_ENTRY_SIZE * 2] = {0};
1951 int len = 0;
1952
1953 /* Do not output tiers without time */
1954 if (!elap)
1955 continue;
1956
Prasanna Prapancham320dcfe2022-06-27 20:20:46 +00001957 len = gbms_tier_stats_cstr(buff, sizeof(buff),
Jenny Hocced17c2020-04-30 18:20:16 +08001958 &ce_data->tier_stats[i], true);
Prasanna Prapancham320dcfe2022-06-27 20:20:46 +00001959 gbms_log_cstr_handler(log, buff, len);
Jenny Hocced17c2020-04-30 18:20:16 +08001960
1961 if (soc_next) {
1962 len = ttf_soc_cstr(buff, sizeof(buff),
1963 &ce_data->soc_stats,
1964 soc_in, soc_next);
Prasanna Prapancham320dcfe2022-06-27 20:20:46 +00001965 gbms_log_cstr_handler(log, buff, len);
Jenny Hocced17c2020-04-30 18:20:16 +08001966 }
1967 }
1968}
1969
AleX Pelosi0d261512020-05-07 12:17:07 -07001970/* End of charging: close stats, qualify event publish data */
Stephane Leecbb07ee2020-09-14 12:24:09 -07001971static void batt_chg_stats_pub(struct batt_drv *batt_drv, char *reason,
1972 bool force, bool skip_uevent)
Ken Tsou8acade12020-07-09 03:17:35 +08001973{
1974 bool publish;
1975
1976 mutex_lock(&batt_drv->stats_lock);
1977 publish = batt_chg_stats_close(batt_drv, reason, force);
1978 if (publish) {
1979 ttf_stats_update(&batt_drv->ttf_stats,
1980 &batt_drv->ce_qual, false);
1981
Stephane Leecbb07ee2020-09-14 12:24:09 -07001982 if (skip_uevent == false)
1983 kobject_uevent(&batt_drv->device->kobj, KOBJ_CHANGE);
Ken Tsou8acade12020-07-09 03:17:35 +08001984 }
Jenny Hocced17c2020-04-30 18:20:16 +08001985
1986 bat_log_chg_stats(batt_drv->ttf_stats.ttf_log, &batt_drv->ce_data);
Ken Tsou8acade12020-07-09 03:17:35 +08001987 mutex_unlock(&batt_drv->stats_lock);
1988}
1989
AleX Pelosi043ffbe2020-06-24 22:48:30 -07001990/* health_stats->tier_index is set on stats_close() */
AleX Pelosi0d261512020-05-07 12:17:07 -07001991static int batt_health_stats_cstr(char *buff, int size,
1992 const struct gbms_charging_event *ce_data,
1993 bool verbose)
1994{
1995 const struct gbms_ce_tier_stats *health_stats = &ce_data->health_stats;
Jenny Ho6f7ec522020-05-19 09:04:53 +08001996 const int vti = batt_chg_health_vti(&ce_data->ce_health);
AleX Pelosi0d261512020-05-07 12:17:07 -07001997 int len = 0;
1998
Stephane Lee595f3182020-06-26 18:13:02 -07001999 len += scnprintf(&buff[len], size - len, "\nH: %d %d %lld %d\n",
Jenny Ho6f7ec522020-05-19 09:04:53 +08002000 ce_data->ce_health.rest_state, vti,
2001 ce_data->ce_health.rest_deadline,
2002 ce_data->ce_health.always_on_soc);
AleX Pelosi0d261512020-05-07 12:17:07 -07002003
Stephane Leeaaf28cd2020-08-26 11:26:06 -07002004 /* no additional tier stats when vti is invalid */
Jenny Ho6f7ec522020-05-19 09:04:53 +08002005 if (vti == GBMS_STATS_AC_TI_INVALID)
2006 return len;
2007
Prasanna Prapancham320dcfe2022-06-27 20:20:46 +00002008 len += gbms_tier_stats_cstr(&buff[len], size - len,
2009 health_stats, verbose);
Stephane Lee9af33272021-05-20 17:03:06 -07002010
2011 /* Only add pause tier logging if there is pause time */
2012 if (ce_data->health_pause_stats.soc_in != -1)
Prasanna Prapancham320dcfe2022-06-27 20:20:46 +00002013 len += gbms_tier_stats_cstr(&buff[len], size - len,
2014 &ce_data->health_pause_stats,
2015 verbose);
Jenny Ho1c2ec2b2021-12-10 17:43:14 +08002016
AleX Pelosi0d261512020-05-07 12:17:07 -07002017 return len;
2018}
2019
AleX Pelosi043ffbe2020-06-24 22:48:30 -07002020/* doesn't output hc stats */
Ken Tsou8acade12020-07-09 03:17:35 +08002021static int batt_chg_stats_cstr(char *buff, int size,
2022 const struct gbms_charging_event *ce_data,
2023 bool verbose)
2024{
AleX Pelosi0c88ff32020-04-02 01:04:51 -07002025 int i, len = 0;
Ken Tsou8acade12020-07-09 03:17:35 +08002026
2027 if (verbose) {
2028 const char *adapter_name =
2029 gbms_chg_ev_adapter_s(ce_data->adapter_details.ad_type);
2030
2031 len += scnprintf(&buff[len], size - len, "A: %s,",
2032 adapter_name);
2033 }
2034
2035 len += scnprintf(&buff[len], size - len, "%d,%d,%d",
2036 ce_data->adapter_details.ad_type,
2037 ce_data->adapter_details.ad_voltage * 100,
2038 ce_data->adapter_details.ad_amperage * 100);
2039
Jack Wu58ea50a2022-06-04 13:22:22 +08002040 len += scnprintf(&buff[len], size - len, "%s%hu,%hu, %hu,%hu %u",
Ken Tsou8acade12020-07-09 03:17:35 +08002041 (verbose) ? "\nS: " : ", ",
2042 ce_data->charging_stats.ssoc_in,
2043 ce_data->charging_stats.voltage_in,
2044 ce_data->charging_stats.ssoc_out,
Jenny Hofc44c592022-01-21 16:09:14 +08002045 ce_data->charging_stats.voltage_out,
Jack Wu58ea50a2022-06-04 13:22:22 +08002046 ce_data->chg_profile->capacity_ma);
Ken Tsou8acade12020-07-09 03:17:35 +08002047
2048
2049 if (verbose) {
2050 len += scnprintf(&buff[len], size - len, " %hu,%hu",
2051 ce_data->charging_stats.cc_in,
2052 ce_data->charging_stats.cc_out);
2053
Ken Tsou68d44d12020-11-11 00:01:40 +08002054 len += scnprintf(&buff[len], size - len, " %lld,%lld",
Ken Tsou8acade12020-07-09 03:17:35 +08002055 ce_data->first_update,
2056 ce_data->last_update);
2057 }
2058
2059 for (i = 0; i < GBMS_STATS_TIER_COUNT; i++) {
2060 const int soc_next = batt_chg_stats_soc_next(ce_data, i);
2061 const int soc_in = ce_data->tier_stats[i].soc_in >> 8;
AleX Pelosi0d261512020-05-07 12:17:07 -07002062 const long elap = ce_data->tier_stats[i].time_fast +
2063 ce_data->tier_stats[i].time_taper +
2064 ce_data->tier_stats[i].time_other;
2065
2066 /* Do not output tiers without time */
2067 if (!elap)
2068 continue;
Ken Tsou8acade12020-07-09 03:17:35 +08002069
Prasanna Prapancham320dcfe2022-06-27 20:20:46 +00002070 len += gbms_tier_stats_cstr(&buff[len], size - len,
2071 &ce_data->tier_stats[i],
2072 verbose);
Stephane Leecbb07ee2020-09-14 12:24:09 -07002073
Jenny Ho93e41c22020-04-28 18:23:51 +08002074 if (soc_next)
Ken Tsou8acade12020-07-09 03:17:35 +08002075 len += ttf_soc_cstr(&buff[len], size - len,
AleX Pelosi0c88ff32020-04-02 01:04:51 -07002076 &ce_data->soc_stats,
2077 soc_in, soc_next);
Ken Tsou8acade12020-07-09 03:17:35 +08002078 }
2079
Stephane Lee463e85d2021-08-16 14:50:36 -07002080 /* Does not currently check MSC_HEALTH */
2081 if (ce_data->health_dryrun_stats.soc_in != -1)
Prasanna Prapancham320dcfe2022-06-27 20:20:46 +00002082 len += gbms_tier_stats_cstr(&buff[len], size - len,
2083 &ce_data->health_dryrun_stats,
2084 verbose);
Stephane Lee463e85d2021-08-16 14:50:36 -07002085
AleX Pelosia6ad6ad2020-10-29 13:05:27 -07002086 if (ce_data->full_charge_stats.soc_in != -1)
Prasanna Prapancham320dcfe2022-06-27 20:20:46 +00002087 len += gbms_tier_stats_cstr(&buff[len], size - len,
2088 &ce_data->full_charge_stats,
2089 verbose);
Stephane Leecbb07ee2020-09-14 12:24:09 -07002090
AleX Pelosia6ad6ad2020-10-29 13:05:27 -07002091 if (ce_data->high_soc_stats.soc_in != -1)
Prasanna Prapancham320dcfe2022-06-27 20:20:46 +00002092 len += gbms_tier_stats_cstr(&buff[len], size - len,
2093 &ce_data->high_soc_stats,
2094 verbose);
AleX Pelosia6ad6ad2020-10-29 13:05:27 -07002095
2096 if (ce_data->overheat_stats.soc_in != -1)
Prasanna Prapancham320dcfe2022-06-27 20:20:46 +00002097 len += gbms_tier_stats_cstr(&buff[len], size - len,
2098 &ce_data->overheat_stats,
2099 verbose);
AleX Pelosia6ad6ad2020-10-29 13:05:27 -07002100
2101 if (ce_data->cc_lvl_stats.soc_in != -1)
Prasanna Prapancham320dcfe2022-06-27 20:20:46 +00002102 len += gbms_tier_stats_cstr(&buff[len], size - len,
2103 &ce_data->cc_lvl_stats,
2104 verbose);
Stephane Leecbb07ee2020-09-14 12:24:09 -07002105
Jenny Hof299c742023-02-14 17:58:59 +08002106 if (ce_data->temp_filter_stats.soc_in != -1)
2107 len += gbms_tier_stats_cstr(&buff[len], size - len,
2108 &ce_data->temp_filter_stats,
2109 verbose);
2110
Stephane Leecacee1f2021-09-22 11:51:38 -07002111 /* If bd_clear triggers, we need to know about it even if trickle hasn't
2112 * triggered
2113 */
2114 if (ce_data->trickle_stats.soc_in != -1 || ce_data->bd_clear_trickle)
Prasanna Prapancham320dcfe2022-06-27 20:20:46 +00002115 len += gbms_tier_stats_cstr(&buff[len], size - len,
2116 &ce_data->trickle_stats,
2117 verbose);
Stephane Leecacee1f2021-09-22 11:51:38 -07002118
Ken Tsou8acade12020-07-09 03:17:35 +08002119 return len;
2120}
2121
2122/* ------------------------------------------------------------------------- */
2123
AleX Pelosia3f22de2022-05-03 23:42:24 -07002124static int batt_ravg_value(const struct batt_res *rstate)
2125{
2126 return rstate->resistance_avg * 100;
2127}
2128
2129static void batt_res_dump_logs(const struct batt_res *rstate)
Ken Tsou8acade12020-07-09 03:17:35 +08002130{
AleX Pelosib3a1a552022-05-03 17:00:11 -07002131 pr_info("RAVG: req:%d, sample:%d[%d], filt_cnt:%d, res_avg:%d\n",
Ken Tsou8acade12020-07-09 03:17:35 +08002132 rstate->estimate_requested, rstate->sample_accumulator,
2133 rstate->sample_count, rstate->filter_count,
2134 rstate->resistance_avg);
2135}
2136
2137static void batt_res_state_set(struct batt_res *rstate, bool breq)
2138{
2139 rstate->estimate_requested = breq;
2140 rstate->sample_accumulator = 0;
2141 rstate->sample_count = 0;
Ken Tsou8acade12020-07-09 03:17:35 +08002142}
2143
AleX Pelosib3a1a552022-05-03 17:00:11 -07002144static int batt_ravg_write(int resistance_avg, int filter_count)
Ken Tsou8acade12020-07-09 03:17:35 +08002145{
AleX Pelosib3a1a552022-05-03 17:00:11 -07002146 const u16 ravg = (resistance_avg > 0xffff ) ? 0xffff : resistance_avg;
2147 const u16 rfcn = filter_count & 0xffff;
2148 int ret;
2149
2150 ret = gbms_storage_write(GBMS_TAG_RAVG, &ravg, sizeof(ravg));
2151 if (ret < 0) {
2152 pr_debug("RAVG: failed to write RAVG (%d)\n", ret);
2153 return -EIO;
2154 }
2155
2156 /*
2157 * filter_count <= estimate_filter
2158 * TODO: we might not need this...
2159 * TODO: check the error path (ravg saved but filter count not saved)
2160 */
2161 ret = gbms_storage_write(GBMS_TAG_RFCN, &rfcn, sizeof(rfcn));
2162 if (ret < 0) {
2163 pr_debug("RAVG: failed to write RFCN (%d)\n", ret);
2164 return -EIO;
2165 }
2166
2167 return 0;
2168}
2169
2170static void batt_res_update(struct batt_res *rstate)
2171{
Ken Tsou8acade12020-07-09 03:17:35 +08002172 int filter_estimate = 0;
2173 int total_estimate = 0;
2174 long new_estimate = 0;
Ken Tsou8acade12020-07-09 03:17:35 +08002175
AleX Pelosib3a1a552022-05-03 17:00:11 -07002176 /* accumulator is scaled */
Ken Tsou8acade12020-07-09 03:17:35 +08002177 new_estimate = rstate->sample_accumulator / rstate->sample_count;
2178 filter_estimate = rstate->resistance_avg * rstate->filter_count;
2179
2180 rstate->filter_count++;
2181 if (rstate->filter_count > rstate->estimate_filter) {
2182 rstate->filter_count = rstate->estimate_filter;
2183 filter_estimate -= rstate->resistance_avg;
2184 }
AleX Pelosib3a1a552022-05-03 17:00:11 -07002185
Ken Tsou8acade12020-07-09 03:17:35 +08002186 total_estimate = filter_estimate + new_estimate;
2187 rstate->resistance_avg = total_estimate / rstate->filter_count;
Ken Tsou8acade12020-07-09 03:17:35 +08002188}
2189
2190static int batt_res_load_data(struct batt_res *rstate,
2191 struct power_supply *fg_psy)
2192{
AleX Pelosib3a1a552022-05-03 17:00:11 -07002193 u16 resistance_avg = 0, filter_count = 0;
2194 int ret;
Ken Tsou8acade12020-07-09 03:17:35 +08002195
AleX Pelosib3a1a552022-05-03 17:00:11 -07002196 ret = gbms_storage_read(GBMS_TAG_RAVG, &resistance_avg,
2197 sizeof(resistance_avg));
Ken Tsou8acade12020-07-09 03:17:35 +08002198 if (ret < 0) {
2199 pr_err("failed to get resistance_avg(%d)\n", ret);
AleX Pelosib3a1a552022-05-03 17:00:11 -07002200 goto error_done;
Ken Tsou8acade12020-07-09 03:17:35 +08002201 }
Ken Tsou8acade12020-07-09 03:17:35 +08002202
AleX Pelosib3a1a552022-05-03 17:00:11 -07002203 ret = gbms_storage_read(GBMS_TAG_RFCN, &filter_count,
2204 sizeof(filter_count));
Ken Tsou8acade12020-07-09 03:17:35 +08002205 if (ret < 0) {
Ken Tsou8acade12020-07-09 03:17:35 +08002206 pr_err("failed to get resistance filt_count(%d)\n", ret);
AleX Pelosib3a1a552022-05-03 17:00:11 -07002207 goto error_done;
Ken Tsou8acade12020-07-09 03:17:35 +08002208 }
Ken Tsou8acade12020-07-09 03:17:35 +08002209
AleX Pelosib3a1a552022-05-03 17:00:11 -07002210 /* no value in storage: start now (or start over) */
2211 if (resistance_avg == 0xffff || filter_count == 0xffff) {
2212 resistance_avg = 0;
2213 filter_count = 0;
2214 }
2215
2216error_done:
2217 rstate->resistance_avg = resistance_avg;
2218 rstate->filter_count = filter_count;
Ken Tsou8acade12020-07-09 03:17:35 +08002219 return 0;
2220}
2221
AleX Pelosib3a1a552022-05-03 17:00:11 -07002222/*
2223 * accumulate and resistance when SOC is between ravg_soc_low and ravg_soc_high
2224 * and temperature is in the right range. Discard the new sample if the device
2225 * is disconencted.
AleX Pelosic1cd4752022-05-04 02:15:02 -07002226 * hold mutex_unlock(&batt_drv->chg_lock);
AleX Pelosib3a1a552022-05-03 17:00:11 -07002227 */
Ken Tsou8acade12020-07-09 03:17:35 +08002228static void batt_res_work(struct batt_drv *batt_drv)
2229{
AleX Pelosia3f22de2022-05-03 23:42:24 -07002230 struct batt_res *rstate = &batt_drv->health_data.bhi_data.res_state;
AleX Pelosib3a1a552022-05-03 17:00:11 -07002231 const int soc = ssoc_get_real(&batt_drv->ssoc_state);
2232 struct power_supply *fg_psy = batt_drv->fg_psy;
AleX Pelosib3a1a552022-05-03 17:00:11 -07002233 int ret, temp, resistance;
Ken Tsou8acade12020-07-09 03:17:35 +08002234
AleX Pelosib3a1a552022-05-03 17:00:11 -07002235 if (soc >= rstate->ravg_soc_high) {
2236
2237 /* done: recalculate resistance_avg and save it */
2238 if (rstate->sample_count > 0) {
2239 batt_res_update(rstate);
2240
2241 ret = batt_ravg_write(rstate->resistance_avg,
2242 rstate->filter_count);
2243 if (ret == 0)
2244 batt_res_dump_logs(rstate);
Ken Tsou8acade12020-07-09 03:17:35 +08002245 }
AleX Pelosib3a1a552022-05-03 17:00:11 -07002246
2247 /* loose the new data when it cannot save */
2248 batt_res_state_set(rstate, false);
Ken Tsou8acade12020-07-09 03:17:35 +08002249 return;
2250 }
2251
AleX Pelosib3a1a552022-05-03 17:00:11 -07002252 /* wait for it */
2253 if (soc < rstate->ravg_soc_low)
2254 return;
2255
2256 /* do not collect samples when temperature is outside the range */
Jenny Ho5a2657e2022-12-12 14:36:57 +08002257 ret = gbatt_get_raw_temp(batt_drv, &temp);
AleX Pelosib3a1a552022-05-03 17:00:11 -07002258 if (ret < 0 || temp < rstate->res_temp_low || temp > rstate->res_temp_high)
2259 return;
2260
2261 /* resistance in mOhm, skip read errors */
2262 resistance = GPSY_GET_INT_PROP(fg_psy, GBMS_PROP_RESISTANCE, &ret);
Ken Tsou8acade12020-07-09 03:17:35 +08002263 if (ret < 0)
2264 return;
2265
AleX Pelosib3a1a552022-05-03 17:00:11 -07002266 /* accumulate samples if temperature and SOC are within range */
2267 rstate->sample_accumulator += resistance / 100;
2268 rstate->sample_count++;
2269 pr_debug("RAVG: sample:%d[%d], filt_cnt:%d\n",
2270 rstate->sample_accumulator, rstate->sample_count,
2271 rstate->filter_count);
Ken Tsou8acade12020-07-09 03:17:35 +08002272}
2273
2274/* ------------------------------------------------------------------------- */
2275
Jenny Ho9b191512022-08-30 09:26:15 +00002276static void batt_log_csi_ttf_info(struct batt_drv *batt_drv)
Jenny Ho4f2ad702022-03-01 10:56:19 +08002277{
AleX Pelosi47ae7762022-05-13 22:33:04 +00002278 struct csi_stats *csi_stats = &batt_drv->csi_stats;
2279 const bool same_type_and_status =
2280 csi_stats->csi_current_type == batt_drv->csi_current_type &&
2281 csi_stats->csi_current_status == batt_drv->csi_current_status;
Jenny Ho2a9e48a2022-12-31 09:39:30 +08002282 int current_speed = batt_drv->csi_current_speed;
2283 int min_speed = csi_stats->csi_speed_min;
2284 int max_speed = csi_stats->csi_speed_max;
AleX Pelosi47ae7762022-05-13 22:33:04 +00002285 const ktime_t right_now = get_boot_sec();
2286 int ssoc = -1;
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002287
Jenny Ho9b191512022-08-30 09:26:15 +00002288 if (!batt_drv->init_complete)
2289 return;
2290
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002291 if (chg_state_is_disconnected(&batt_drv->chg_state))
AleX Pelosi47ae7762022-05-13 22:33:04 +00002292 goto log_and_done;
Jenny Ho4f2ad702022-03-01 10:56:19 +08002293
AleX Pelosi47ae7762022-05-13 22:33:04 +00002294 ssoc = ssoc_get_capacity(&batt_drv->ssoc_state);
2295 if (ssoc == csi_stats->ssoc && same_type_and_status) {
2296 const ktime_t elap = right_now - csi_stats->last_update;
2297
2298 csi_stats->last_update = right_now;
2299
2300 /* accumulate only positive*/
Jenny Ho2a9e48a2022-12-31 09:39:30 +08002301 if (current_speed < 0)
AleX Pelosi47ae7762022-05-13 22:33:04 +00002302 return;
2303
Jenny Ho2a9e48a2022-12-31 09:39:30 +08002304 if (min_speed == max_speed && max_speed == 0)
2305 min_speed = max_speed = current_speed;
2306 else if (current_speed < min_speed)
2307 min_speed = current_speed;
2308 else if (current_speed > max_speed)
2309 max_speed = current_speed;
AleX Pelosi47ae7762022-05-13 22:33:04 +00002310
Jenny Ho2a9e48a2022-12-31 09:39:30 +08002311 csi_stats->csi_speed_min = min_speed;
2312 csi_stats->csi_speed_max = max_speed;
AleX Pelosi47ae7762022-05-13 22:33:04 +00002313 csi_stats->speed_sum += current_speed * elap;
2314 csi_stats->csi_time_sum += elap;
2315 return;
2316 }
2317
2318log_and_done:
2319 csi_stats->csi_current_status = batt_drv->csi_current_status;
2320 csi_stats->csi_current_type = batt_drv->csi_current_type;
2321
2322 if (csi_stats->ssoc != -1) {
2323 const int csi_speed_avg = csi_stats->csi_time_sum == 0 ?
Jenny Ho2a9e48a2022-12-31 09:39:30 +08002324 ((csi_stats->csi_speed_min + csi_stats->csi_speed_max) / 2) :
AleX Pelosi47ae7762022-05-13 22:33:04 +00002325 (csi_stats->speed_sum / csi_stats->csi_time_sum);
Jenny Ho9b191512022-08-30 09:26:15 +00002326 const int cc = GPSY_GET_PROP(batt_drv->fg_psy, POWER_SUPPLY_PROP_CHARGE_COUNTER);
2327 ktime_t res = 0;
2328 const int max_ratio = batt_ttf_estimate(&res, batt_drv);
2329
2330 if (max_ratio < 0)
2331 res = 0;
AleX Pelosi47ae7762022-05-13 22:33:04 +00002332
2333 gbms_logbuffer_prlog(batt_drv->ttf_stats.ttf_log, LOGLEVEL_INFO, 0, LOGLEVEL_DEBUG,
Jenny Hoa7026682022-09-30 16:38:45 +08002334 "ssoc=%d temp=%d CSI[min=%d max=%d avg=%d type=%d status=%d TTF[cc=%d time=%lld %lld:%lld:%lld (est=%lld max_ratio=%d)]",
2335 csi_stats->ssoc, batt_drv->batt_temp, csi_stats->csi_speed_min,
Jenny Ho9b191512022-08-30 09:26:15 +00002336 csi_stats->csi_speed_max, csi_speed_avg,
2337 csi_stats->csi_current_type, csi_stats->csi_current_status,
Jenny Hoa7026682022-09-30 16:38:45 +08002338 cc / 1000, right_now, res / 3600, (res % 3600) / 60,
2339 (res % 3600) % 60, res, max_ratio);
2340
AleX Pelosi47ae7762022-05-13 22:33:04 +00002341 }
2342
2343 /* ssoc == -1 on disconnect */
Jenny Ho2a9e48a2022-12-31 09:39:30 +08002344 if (ssoc == -1 || current_speed < 0)
AleX Pelosi47ae7762022-05-13 22:33:04 +00002345 current_speed = 0;
2346
2347 csi_stats->ssoc = ssoc;
2348 csi_stats->csi_speed_min = current_speed;
2349 csi_stats->csi_speed_max = current_speed;
2350
2351 csi_stats->csi_time_sum = 0;
Jenny Ho8fb5a9d2022-10-05 15:47:04 +08002352 csi_stats->speed_sum = 0;
AleX Pelosi47ae7762022-05-13 22:33:04 +00002353 csi_stats->last_update = right_now;
Jenny Ho4f2ad702022-03-01 10:56:19 +08002354}
2355
Ken Tsouf956a7f2022-04-12 09:50:46 +08002356static int csi_status_cb(struct gvotable_election *el, const char *reason,
2357 void *value)
Jenny Ho8afbd5b2022-02-10 18:20:56 +08002358{
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002359 struct batt_drv *batt_drv = gvotable_get_data(el);
2360 int status = GVOTABLE_PTR_TO_INT(value);
2361
2362 if (!batt_drv || batt_drv->csi_current_status == status)
Ken Tsouf956a7f2022-04-12 09:50:46 +08002363 return 0;
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002364
2365 batt_drv->csi_current_status = status;
Jenny Ho9b191512022-08-30 09:26:15 +00002366 batt_log_csi_ttf_info(batt_drv);
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002367
2368 if (batt_drv->psy)
2369 power_supply_changed(batt_drv->psy);
Ken Tsouf956a7f2022-04-12 09:50:46 +08002370
2371 return 0;
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002372}
2373
Ken Tsouf956a7f2022-04-12 09:50:46 +08002374static int csi_type_cb(struct gvotable_election *el, const char *reason,
2375 void *value)
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002376{
2377 struct batt_drv *batt_drv = gvotable_get_data(el);
2378 int type = GVOTABLE_PTR_TO_INT(value);
2379
2380 if (!batt_drv || batt_drv->csi_current_type == type)
Ken Tsouf956a7f2022-04-12 09:50:46 +08002381 return 0;
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002382
2383 batt_drv->csi_current_type = type;
Jenny Ho9b191512022-08-30 09:26:15 +00002384 batt_log_csi_ttf_info(batt_drv);
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002385
2386 if (batt_drv->psy)
2387 power_supply_changed(batt_drv->psy);
Ken Tsouf956a7f2022-04-12 09:50:46 +08002388
2389 return 0;
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002390}
2391
2392/* all reset on disconnect */
2393static void batt_update_csi_type(struct batt_drv *batt_drv)
2394{
2395 const bool is_disconnected = chg_state_is_disconnected(&batt_drv->chg_state);
2396 const bool is_trickle = batt_drv->ssoc_state.bd_trickle_cnt > 0;
2397 const bool is_ac = batt_drv->msc_state == MSC_HEALTH ||
2398 batt_drv->msc_state == MSC_HEALTH_PAUSE ||
2399 batt_drv->msc_state == MSC_HEALTH_ALWAYS_ON;
Jenny Ho8afbd5b2022-02-10 18:20:56 +08002400
Ken Tsoua15d1fa2022-01-24 14:42:27 +08002401 if (!batt_drv->csi_type_votable) {
2402 batt_drv->csi_type_votable =
2403 gvotable_election_get_handle(VOTABLE_CSI_TYPE);
Ken Tsoua15d1fa2022-01-24 14:42:27 +08002404 if (!batt_drv->csi_type_votable)
2405 return;
2406 }
Jenny Ho8afbd5b2022-02-10 18:20:56 +08002407
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002408 /* normal or full if connected, nothing otherwise */
2409 gvotable_cast_long_vote(batt_drv->csi_type_votable, "CSI_TYPE_CONNECTED",
AleX Pelosi2dc499a2022-05-06 18:44:33 +00002410 is_disconnected ? CSI_TYPE_None : CSI_TYPE_Normal,
2411 true);
Jenny Ho4f2ad702022-03-01 10:56:19 +08002412
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002413 /* SW JEITA */
2414 gvotable_cast_long_vote(batt_drv->csi_type_votable, "CSI_TYPE_JEITA",
2415 CSI_TYPE_JEITA,
2416 !is_disconnected && batt_drv->jeita_stop_charging == 1);
2417
2418 /* Longlife is set on TEMP, DWELL and TRICKLE */
2419 gvotable_cast_long_vote(batt_drv->csi_type_votable, "CSI_TYPE_TRICKLE",
2420 CSI_TYPE_LongLife,
2421 !is_disconnected && is_trickle);
2422
2423 /* Adaptive charging, individually */
2424 gvotable_cast_long_vote(batt_drv->csi_type_votable, "CSI_TYPE_AC",
2425 CSI_TYPE_Adaptive,
2426 !is_disconnected && is_ac);
2427
2428 /* CSI_TYPE_Fault is permanent TODO: check single cell disconnect */
2429 gvotable_cast_long_vote(batt_drv->csi_type_votable, "CSI_TYPE_SINGLE_CELL",
2430 CSI_TYPE_Fault, false);
2431}
2432
2433static bool batt_csi_check_ad_qual(const struct batt_drv *chg_drv)
2434{
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002435 return false; /* TODO */
2436}
2437
2438/*
2439 * these are absolute values: an underpowered adapter is a problem when
2440 * charging speed falls under 80%.
2441 */
2442static bool batt_csi_check_ad_power(const union gbms_ce_adapter_details *ad)
2443{
Jenny Hof63f7d02022-10-26 15:43:19 +08002444 const unsigned int ad_mw = (ad->ad_voltage * ad->ad_amperage) * 10000;
2445 unsigned int limit_mw = 9000 * 2000; /* 18 Watts: it changes with the device */
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002446
2447 switch (ad->ad_type) {
2448 case CHG_EV_ADAPTER_TYPE_USB:
2449 case CHG_EV_ADAPTER_TYPE_USB_SDP:
2450 case CHG_EV_ADAPTER_TYPE_USB_CDP:
2451 case CHG_EV_ADAPTER_TYPE_USB_ACA:
2452 case CHG_EV_ADAPTER_TYPE_USB_C:
2453 case CHG_EV_ADAPTER_TYPE_USB_PD:
2454 case CHG_EV_ADAPTER_TYPE_USB_PD_DRP:
2455 case CHG_EV_ADAPTER_TYPE_USB_PD_PPS:
2456 case CHG_EV_ADAPTER_TYPE_USB_BRICKID:
2457 case CHG_EV_ADAPTER_TYPE_USB_HVDCP:
2458 case CHG_EV_ADAPTER_TYPE_USB_HVDCP3:
2459 break;
2460 case CHG_EV_ADAPTER_TYPE_WLC:
2461 case CHG_EV_ADAPTER_TYPE_WLC_EPP:
2462 case CHG_EV_ADAPTER_TYPE_WLC_SPP:
Jenny Hod96ee282022-09-08 17:27:18 +08002463 limit_mw = 7500000;
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002464 break;
Jenny Hof63f7d02022-10-26 15:43:19 +08002465 case CHG_EV_ADAPTER_TYPE_EXT:
2466 case CHG_EV_ADAPTER_TYPE_EXT1:
2467 case CHG_EV_ADAPTER_TYPE_EXT2:
2468 case CHG_EV_ADAPTER_TYPE_EXT_UNKNOWN:
2469 limit_mw = 10500 * 1250;
2470 break;
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002471 default:
2472 break;
Jenny Ho4f2ad702022-03-01 10:56:19 +08002473 }
Jenny Ho8afbd5b2022-02-10 18:20:56 +08002474
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002475 return ad_mw < limit_mw;
Jenny Ho8afbd5b2022-02-10 18:20:56 +08002476}
2477
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002478/*
2479 * COLD and HOT are only at the limits, we might want to flag anything that is
2480 * not the reference tier instead.
2481 */
2482static void batt_update_csi_status(struct batt_drv *batt_drv)
Jenny Ho4f2ad702022-03-01 10:56:19 +08002483{
2484 const struct gbms_chg_profile *profile = &batt_drv->chg_profile;
2485 const int temp_hot_idx = profile->temp_nb_limits - 1;
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002486 const bool is_cold = batt_drv->batt_temp < profile->temp_limits[0];
2487 const bool is_hot = batt_drv->batt_temp >= profile->temp_limits[temp_hot_idx];
2488 const bool is_trickle = batt_drv->ssoc_state.bd_trickle_cnt > 0;
2489 const bool is_disconnected = chg_state_is_disconnected(&batt_drv->chg_state);
2490 const union gbms_ce_adapter_details *ad = &batt_drv->ce_data.adapter_details;
Jenny Ho4f2ad702022-03-01 10:56:19 +08002491
2492 if (!batt_drv->csi_status_votable) {
2493 batt_drv->csi_status_votable =
2494 gvotable_election_get_handle(VOTABLE_CSI_STATUS);
2495 if (!batt_drv->csi_status_votable)
2496 return;
2497 }
2498
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002499 /*
2500 * discharging when the battery current is negative. There will likely
2501 * be a more specific reason (e.g System_* or Adapter_* or one of
2502 * Defender_*).
2503 */
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002504
Jenny Ho9b191512022-08-30 09:26:15 +00002505 /* Charging Status Health_Cold */
2506 gvotable_cast_long_vote(batt_drv->csi_status_votable, "CSI_STATUS_COLD",
2507 CSI_STATUS_Health_Cold,
2508 !is_disconnected && is_cold);
2509
2510 /* Charging Status Health_Hot */
2511 gvotable_cast_long_vote(batt_drv->csi_status_votable, "CSI_STATUS_HOT",
2512 CSI_STATUS_Health_Hot,
2513 !is_disconnected && is_hot);
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002514
2515 /* looks at absolute power, it could look also look at golden adapter */
2516 gvotable_cast_long_vote(batt_drv->csi_status_votable, "CSI_STATUS_ADA_POWR",
2517 CSI_STATUS_Adapter_Power,
2518 !is_disconnected && !batt_drv->chg_done &&
2519 batt_csi_check_ad_power(ad));
2520
2521 /* Adapter quality looks at input voltage and current */
2522 gvotable_cast_long_vote(batt_drv->csi_status_votable, "CSI_STATUS_ADA_QUAL",
2523 CSI_STATUS_Adapter_Quality,
2524 !is_disconnected && batt_csi_check_ad_qual(batt_drv));
Jenny Ho4f2ad702022-03-01 10:56:19 +08002525
Jenny Ho4f2ad702022-03-01 10:56:19 +08002526 /* Charging Status Defender_Trickle */
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002527 gvotable_cast_long_vote(batt_drv->csi_status_votable, "CSI_STATUS_DEFEND_TRICKLE",
2528 CSI_STATUS_Defender_Trickle,
2529 !is_disconnected && is_trickle);
Jenny Ho9b191512022-08-30 09:26:15 +00002530
2531 gvotable_cast_long_vote(batt_drv->csi_status_votable, "CSI_STATUS_DSG",
2532 CSI_STATUS_NotCharging,
2533 !is_disconnected && batt_drv->msc_state == MSC_DSG);
2534
2535 gvotable_cast_long_vote(batt_drv->csi_status_votable, "CSI_STATUS_100",
2536 CSI_STATUS_Charging,
2537 !is_disconnected && batt_drv->chg_done);
2538
2539 gvotable_cast_long_vote(batt_drv->csi_status_votable, "CSI_STATUS_CHG",
2540 CSI_STATUS_Charging, !is_disconnected);
Jenny Ho4f2ad702022-03-01 10:56:19 +08002541}
Jenny Ho8afbd5b2022-02-10 18:20:56 +08002542
Jenny Ho915dc482022-03-21 09:13:57 +08002543#define CSI_CHG_SPEED_MAX 100
2544#define CSI_CHG_SPEED_MIN 0
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002545
2546/*
2547 * slowing down due to batt_drv->temp_idx != from reference is reported
2548 * in status as CSI_STATUS_COLD or CSI_STATUS_HOT.
2549 *
2550 * cc_max = GBMS_CCCM_LIMITS(profile, batt_drv->temp_idx, batt_drv->vbatt_idx);
2551 * if (cc_max && cc_max < nominal_demand)
2552 * nominal_demand = cc_max;
2553 */
Jenny Ho915dc482022-03-21 09:13:57 +08002554static int batt_calc_charging_speed(struct batt_drv *batt_drv)
2555{
Jenny Ho915dc482022-03-21 09:13:57 +08002556 const struct gbms_chg_profile *profile = &batt_drv->chg_profile;
Jenny Ho915dc482022-03-21 09:13:57 +08002557 const int soc = ssoc_get_capacity(&batt_drv->ssoc_state);
AleX Pelosi628f21a2022-04-26 14:53:02 -07002558 const int chg_type = batt_drv->chg_state.f.chg_type;
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002559 int cc_max, vbatt_idx, ibatt, nominal_demand;
2560 int chg_speed = -1;
Jenny Ho915dc482022-03-21 09:13:57 +08002561
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002562 if (chg_state_is_disconnected(&batt_drv->chg_state))
Jenny Ho915dc482022-03-21 09:13:57 +08002563 return -1;
2564
Jenny Ho09637822022-04-18 09:02:57 +08002565 if (batt_drv->fake_charging_speed)
2566 return batt_drv->fake_charging_speed;
2567
AleX Pelosi628f21a2022-04-26 14:53:02 -07002568 /* if the battery is the limit, speed is 100% */
2569 if (chg_type == POWER_SUPPLY_CHARGE_TYPE_TAPER)
2570 return 100;
2571
Jenny Ho915dc482022-03-21 09:13:57 +08002572 /* Get average current via tiers. */
2573 vbatt_idx = ttf_pwr_vtier_idx(&batt_drv->ttf_stats, soc);
Jenny Ho915dc482022-03-21 09:13:57 +08002574
2575 /* Wait 1 min to get avg_ibat */
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002576 ibatt = ttf_pwr_ibatt(&batt_drv->ce_data.tier_stats[vbatt_idx]);
Jenny Ho915dc482022-03-21 09:13:57 +08002577 if (ibatt == 0)
2578 return -1;
2579
2580 /* Get nominal demand current via ttf table */
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002581 nominal_demand = ttf_ref_cc(&batt_drv->ttf_stats, soc);
2582 if (nominal_demand < 0)
Jenny Ho915dc482022-03-21 09:13:57 +08002583 return -1;
2584
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002585 /*
2586 * Adjust demand to battery temperature when batt_drv->temp_idx is
2587 * different from the reference. Here status will either be *_COLD
2588 * or *_HOT.
2589 */
Jenny Ho2a9e48a2022-12-31 09:39:30 +08002590 cc_max = GBMS_CCCM_LIMITS(profile, batt_drv->temp_idx, batt_drv->vbatt_idx) / 1000;
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002591 if (cc_max && cc_max < nominal_demand)
2592 nominal_demand = cc_max;
Jenny Ho915dc482022-03-21 09:13:57 +08002593
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002594 chg_speed = ibatt * 100 / nominal_demand;
Jenny Ho915dc482022-03-21 09:13:57 +08002595
Jenny Ho2a9e48a2022-12-31 09:39:30 +08002596 pr_debug("chg_speed=%d ibatt=%d nominal_demand=%d cc_max=%d",
2597 chg_speed, ibatt, nominal_demand, cc_max);
2598
Jenny Ho915dc482022-03-21 09:13:57 +08002599 /* bound in [0,100] */
2600 if (chg_speed > CSI_CHG_SPEED_MAX)
2601 chg_speed = CSI_CHG_SPEED_MAX;
2602 else if (chg_speed < CSI_CHG_SPEED_MIN)
2603 chg_speed = CSI_CHG_SPEED_MIN;
2604
2605 return chg_speed;
2606}
2607
2608static void batt_update_csi_info(struct batt_drv *batt_drv)
2609{
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002610 int charging_speed;
Jenny Ho915dc482022-03-21 09:13:57 +08002611
AleX Pelosi9bfe2312022-04-26 13:20:43 -07002612 batt_update_csi_type(batt_drv);
2613 batt_update_csi_status(batt_drv);
2614
2615 charging_speed = batt_calc_charging_speed(batt_drv);
2616 if (batt_drv->csi_current_speed != charging_speed) {
2617 batt_drv->csi_current_speed = charging_speed;
Jenny Ho9b191512022-08-30 09:26:15 +00002618 batt_log_csi_ttf_info(batt_drv);
Jenny Ho915dc482022-03-21 09:13:57 +08002619 }
Jenny Ho915dc482022-03-21 09:13:57 +08002620}
2621
Jenny Ho8afbd5b2022-02-10 18:20:56 +08002622/* ------------------------------------------------------------------------- */
2623
AleX Pelosi9e1713b2020-09-09 02:31:43 -07002624/* NOTE: should not reset always_on_soc */
Stephane Leeaaf28cd2020-08-26 11:26:06 -07002625static inline void batt_reset_rest_state(struct batt_chg_health *chg_health)
2626{
Stephane Leeaaf28cd2020-08-26 11:26:06 -07002627 chg_health->rest_cc_max = -1;
2628 chg_health->rest_fv_uv = -1;
2629
Stephane Lee4a984a22022-01-14 15:14:07 -08002630 /* Keep negative deadlines (they mean user has disabled via settings)
2631 * NOTE: CHG_DEADLINE_DIALOG needs to be applied only for the current
2632 * session. Therefore, it should be cleared on disconnect.
2633 */
2634 if (chg_health->rest_deadline < 0 &&
2635 chg_health->rest_deadline != CHG_DEADLINE_DIALOG) {
AleX Pelosi9e1713b2020-09-09 02:31:43 -07002636 chg_health->rest_state = CHG_HEALTH_USER_DISABLED;
2637 } else {
2638 chg_health->rest_state = CHG_HEALTH_INACTIVE;
Stephane Leeaaf28cd2020-08-26 11:26:06 -07002639 chg_health->rest_deadline = 0;
AleX Pelosi9e1713b2020-09-09 02:31:43 -07002640 }
Stephane Lee463e85d2021-08-16 14:50:36 -07002641
2642 chg_health->dry_run_deadline = 0;
Jenny Hod74a8a42021-10-28 12:58:15 +08002643 chg_health->active_time = 0;
Stephane Leeaaf28cd2020-08-26 11:26:06 -07002644}
2645
Ken Tsou8acade12020-07-09 03:17:35 +08002646/* should not reset rl state */
2647static inline void batt_reset_chg_drv_state(struct batt_drv *batt_drv)
2648{
2649 /* the wake assertion will be released on disconnect and on SW JEITA */
2650 if (batt_drv->hold_taper_ws) {
2651 batt_drv->hold_taper_ws = false;
Ken Tsou5ecf2f42020-07-16 08:26:05 +08002652 __pm_relax(batt_drv->taper_ws);
Ken Tsou8acade12020-07-09 03:17:35 +08002653 }
2654
2655 /* polling */
2656 batt_drv->batt_fast_update_cnt = 0;
AleX Pelosi32458432020-09-05 11:02:39 -07002657 batt_drv->ttf_debounce = 1;
Ken Tsou8acade12020-07-09 03:17:35 +08002658 batt_drv->fg_status = POWER_SUPPLY_STATUS_UNKNOWN;
2659 batt_drv->chg_done = false;
Ken Tsou80f97342022-08-18 17:56:14 +08002660 batt_drv->ssoc_state.bd_trickle_full = false;
2661 batt_drv->ssoc_state.bd_trickle_eoc = false;
Ken Tsou8acade12020-07-09 03:17:35 +08002662 /* algo */
2663 batt_drv->temp_idx = -1;
2664 batt_drv->vbatt_idx = -1;
2665 batt_drv->fv_uv = -1;
2666 batt_drv->cc_max = -1;
2667 batt_drv->msc_update_interval = -1;
2668 batt_drv->jeita_stop_charging = -1;
2669 /* timers */
2670 batt_drv->checked_cv_cnt = 0;
2671 batt_drv->checked_ov_cnt = 0;
2672 batt_drv->checked_tier_switch_cnt = 0;
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07002673 /* stats and logs */
Ken Tsou8acade12020-07-09 03:17:35 +08002674 batt_drv->msc_state = -1;
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07002675 batt_drv->last_log_cnt = 0;
AleX Pelosi8e7fd812019-08-16 10:41:46 -07002676 /* health */
Stephane Leeaaf28cd2020-08-26 11:26:06 -07002677 batt_reset_rest_state(&batt_drv->chg_health);
yihsiangpeng305623d2021-05-06 15:07:05 +08002678 /* fan level */
2679 fan_level_reset(batt_drv);
Ken Tsou8acade12020-07-09 03:17:35 +08002680}
2681
2682/*
2683 * software JEITA, disable charging when outside the charge table.
Jack Wuf2340cc2021-10-08 23:03:49 +08002684 * NOTE: ->jeita_stop_charging is either -1 (init or reset), 1 (disable) or 0
Ken Tsou8acade12020-07-09 03:17:35 +08002685 * TODO: need to be able to disable (leave to HW)
2686 */
Jack Wuf2340cc2021-10-08 23:03:49 +08002687static bool msc_logic_soft_jeita(struct batt_drv *batt_drv, int temp)
Ken Tsou8acade12020-07-09 03:17:35 +08002688{
2689 const struct gbms_chg_profile *profile = &batt_drv->chg_profile;
2690
2691 if (temp < profile->temp_limits[0] ||
Jenny Ho3ddf4a02021-12-23 22:48:18 +08002692 temp >= profile->temp_limits[profile->temp_nb_limits - 1]) {
Ken Tsou8acade12020-07-09 03:17:35 +08002693 if (batt_drv->jeita_stop_charging < 0) {
Jack Wuf2340cc2021-10-08 23:03:49 +08002694 batt_drv->jeita_stop_charging = 1;
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07002695 batt_prlog(BATT_PRLOG_ALWAYS,
2696 "MSC_JEITA temp=%d off limits, do not enable charging\n",
2697 temp);
Ken Tsou8acade12020-07-09 03:17:35 +08002698 } else if (batt_drv->jeita_stop_charging == 0) {
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07002699 batt_prlog(BATT_PRLOG_ALWAYS,
2700 "MSC_JEITA temp=%d off limits, disabling charging\n",
2701 temp);
Ken Tsou8acade12020-07-09 03:17:35 +08002702 }
2703
2704 return true;
2705 }
2706
2707 return false;
2708}
2709
2710/* TODO: only change batt_drv->checked_ov_cnt, an */
2711static int msc_logic_irdrop(struct batt_drv *batt_drv,
2712 int vbatt, int ibatt, int temp_idx,
2713 int *vbatt_idx, int *fv_uv,
2714 int *update_interval)
2715{
Ken Tsou8acade12020-07-09 03:17:35 +08002716 const struct gbms_chg_profile *profile = &batt_drv->chg_profile;
2717 const int vtier = profile->volt_limits[*vbatt_idx];
2718 const int chg_type = batt_drv->chg_state.f.chg_type;
2719 const int utv_margin = profile->cv_range_accuracy;
2720 const int otv_margin = profile->cv_otv_margin;
2721 const int switch_cnt = profile->cv_tier_switch_cnt;
AleX Pelosi5304fed2021-02-15 23:04:00 -08002722 int vchg = batt_drv->chg_state.f.vchrg;
2723 int msc_state = MSC_NONE;
2724 bool match_enable;
2725
2726 if (batt_drv->chg_state.f.flags & GBMS_CS_FLAG_NOCOMP)
2727 vchg = 0;
2728 match_enable = vchg != 0;
Ken Tsou8acade12020-07-09 03:17:35 +08002729
2730 if ((vbatt - vtier) > otv_margin) {
2731 /* OVER: vbatt over vtier for more than margin */
2732 const int cc_max = GBMS_CCCM_LIMITS(profile, temp_idx,
2733 *vbatt_idx);
2734
2735 /*
2736 * pullback when over tier voltage, fast poll, penalty
2737 * on TAPER_RAISE and no cv debounce (so will consider
2738 * switching voltage tiers if the current is right).
2739 * NOTE: lowering voltage might cause a small drop in
2740 * current (we should remain under next tier)
2741 */
2742 *fv_uv = gbms_msc_round_fv_uv(profile, vtier,
2743 *fv_uv - profile->fv_uv_resolution);
2744 if (*fv_uv < vtier)
2745 *fv_uv = vtier;
2746
2747 *update_interval = profile->cv_update_interval;
2748 batt_drv->checked_ov_cnt = profile->cv_tier_ov_cnt;
2749 batt_drv->checked_cv_cnt = 0;
2750
2751 if (batt_drv->checked_tier_switch_cnt > 0 || !match_enable) {
2752 /* no pullback, next tier if already counting */
2753 msc_state = MSC_VSWITCH;
2754 *vbatt_idx = batt_drv->vbatt_idx + 1;
2755
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07002756 batt_prlog(BATT_PRLOG_ALWAYS,
2757 "MSC_VSWITCH vt=%d vb=%d ibatt=%d me=%d\n",
2758 vtier, vbatt, ibatt, match_enable);
Ken Tsou8acade12020-07-09 03:17:35 +08002759 } else if (-ibatt == cc_max) {
2760 /* pullback, double penalty if at full current */
2761 msc_state = MSC_VOVER;
2762 batt_drv->checked_ov_cnt *= 2;
2763
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07002764 batt_prlog(BATT_PRLOG_ALWAYS,
2765 "MSC_VOVER vt=%d vb=%d ibatt=%d fv_uv=%d->%d\n",
2766 vtier, vbatt, ibatt,
2767 batt_drv->fv_uv, *fv_uv);
Ken Tsou8acade12020-07-09 03:17:35 +08002768 } else {
AleX Pelosi80d78ec2021-05-19 22:55:44 -07002769 /* simple pullback */
Ken Tsou8acade12020-07-09 03:17:35 +08002770 msc_state = MSC_PULLBACK;
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07002771 batt_prlog(BATT_PRLOG_ALWAYS,
2772 "MSC_PULLBACK vt=%d vb=%d ibatt=%d fv_uv=%d->%d\n",
2773 vtier, vbatt, ibatt,
2774 batt_drv->fv_uv, *fv_uv);
Ken Tsou8acade12020-07-09 03:17:35 +08002775 }
2776
2777 /*
2778 * might get here after windup because algo will track the
2779 * voltage drop caused from load as IRDROP.
2780 * TODO: make sure that being current limited clear
2781 * the taper condition.
2782 */
2783
2784 } else if (chg_type == POWER_SUPPLY_CHARGE_TYPE_FAST) {
2785 /*
2786 * FAST: usual compensation (vchrg is vqcom)
2787 * NOTE: there is a race in reading from charger and
2788 * data might not be consistent (b/110318684)
2789 * NOTE: could add PID loop for management of thermals
2790 */
AleX Pelosi5304fed2021-02-15 23:04:00 -08002791 const int vchrg_ua = vchg * 1000;
Ken Tsou8acade12020-07-09 03:17:35 +08002792
2793 msc_state = MSC_FAST;
2794
2795 /* invalid or 0 vchg disable IDROP compensation */
AleX Pelosi5304fed2021-02-15 23:04:00 -08002796 if (vchrg_ua <= 0) {
Ken Tsou8acade12020-07-09 03:17:35 +08002797 /* could keep it steady instead */
2798 *fv_uv = vtier;
AleX Pelosi5304fed2021-02-15 23:04:00 -08002799 } else if (vchrg_ua > vbatt) {
Ken Tsou8acade12020-07-09 03:17:35 +08002800 *fv_uv = gbms_msc_round_fv_uv(profile, vtier,
AleX Pelosi5304fed2021-02-15 23:04:00 -08002801 vtier + (vchrg_ua - vbatt));
Ken Tsou8acade12020-07-09 03:17:35 +08002802 }
2803
AleX Pelosi5304fed2021-02-15 23:04:00 -08002804 /* no tier switch in fast charge (TODO unless close to tier) */
Ken Tsou8acade12020-07-09 03:17:35 +08002805 if (batt_drv->checked_cv_cnt == 0)
2806 batt_drv->checked_cv_cnt = 1;
2807
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07002808 batt_prlog(BATT_PRLOG_ALWAYS,
2809 "MSC_FAST vt=%d vb=%d ib=%d fv_uv=%d->%d vchrg=%d cv_cnt=%d\n",
2810 vtier, vbatt, ibatt, batt_drv->fv_uv, *fv_uv,
2811 batt_drv->chg_state.f.vchrg,
2812 batt_drv->checked_cv_cnt);
Ken Tsou8acade12020-07-09 03:17:35 +08002813
2814 } else if (chg_type == POWER_SUPPLY_CHARGE_TYPE_TRICKLE) {
2815 /*
2816 * Precharge: charging current/voltage are limited in
2817 * hardware, no point in applying irdrop compensation.
2818 * Just wait for battery voltage to raise over the
2819 * precharge to fast charge threshold.
2820 */
2821 msc_state = MSC_TYPE;
2822
2823 /* no tier switching in trickle */
2824 if (batt_drv->checked_cv_cnt == 0)
2825 batt_drv->checked_cv_cnt = 1;
2826
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07002827 batt_prlog(BATT_PRLOG_ALWAYS, "MSC_PRE vt=%d vb=%d fv_uv=%d chg_type=%d\n",
2828 vtier, vbatt, *fv_uv, chg_type);
Jack Wu61391a52021-10-19 16:39:28 +08002829 } else if (chg_type != POWER_SUPPLY_CHARGE_TYPE_TAPER) {
AleX Pelosi69da6e32021-04-12 17:20:14 -07002830 const int type_margin = utv_margin;
2831
Ken Tsou8acade12020-07-09 03:17:35 +08002832 /*
AleX Pelosi69da6e32021-04-12 17:20:14 -07002833 * Not fast, taper or precharge: in *_UNKNOWN and *_NONE.
2834 * Set checked_cv_cnt=0 when voltage is withing utv_margin of
2835 * vtier (tune marging) to force checking current and avoid
2836 * early termination for lack of headroom. Carry on at the
2837 * same update_interval otherwise.
Ken Tsou8acade12020-07-09 03:17:35 +08002838 */
2839 msc_state = MSC_TYPE;
AleX Pelosi69da6e32021-04-12 17:20:14 -07002840 if (vbatt > (vtier - type_margin)) {
2841 *update_interval = profile->cv_update_interval;
2842 batt_drv->checked_cv_cnt = 0;
2843 } else {
2844 batt_drv->checked_cv_cnt = 1;
2845 }
Ken Tsou8acade12020-07-09 03:17:35 +08002846
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07002847 batt_prlog(BATT_PRLOG_ALWAYS,
2848 "MSC_TYPE vt=%d margin=%d cv_cnt=%d vb=%d fv_uv=%d chg_type=%d\n",
2849 vtier, type_margin, batt_drv->checked_cv_cnt, vbatt,
2850 *fv_uv, chg_type);
Ken Tsou8acade12020-07-09 03:17:35 +08002851
2852 } else if (batt_drv->checked_ov_cnt) {
2853 /*
2854 * TAPER_DLY: countdown to raise fv_uv and/or check
2855 * for tier switch, will keep steady...
2856 */
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07002857 batt_prlog(BATT_PRLOG_ALWAYS,
2858 "MSC_DLY vt=%d vb=%d fv_uv=%d margin=%d cv_cnt=%d, ov_cnt=%d\n",
2859 vtier, vbatt, *fv_uv, profile->cv_range_accuracy,
2860 batt_drv->checked_cv_cnt,
2861 batt_drv->checked_ov_cnt);
Ken Tsou8acade12020-07-09 03:17:35 +08002862
2863 msc_state = MSC_DLY;
2864 batt_drv->checked_ov_cnt -= 1;
2865 *update_interval = profile->cv_update_interval;
2866
2867 } else if ((vtier - vbatt) < utv_margin) {
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07002868 const bool log_level = batt_drv->msc_state != MSC_STEADY &&
2869 batt_drv->msc_state != MSC_RSTC;
2870
Ken Tsou8acade12020-07-09 03:17:35 +08002871 /* TAPER_STEADY: close enough to tier */
2872
2873 msc_state = MSC_STEADY;
2874 *update_interval = profile->cv_update_interval;
2875
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07002876 batt_prlog(batt_prlog_level(log_level),
2877 "MSC_STEADY vt=%d vb=%d fv_uv=%d margin=%d\n",
2878 vtier, vbatt, *fv_uv,
2879 profile->cv_range_accuracy);
Ken Tsou8acade12020-07-09 03:17:35 +08002880 } else if (batt_drv->checked_tier_switch_cnt >= (switch_cnt - 1)) {
2881 /*
2882 * TAPER_TIERCNTING: prepare to switch to next tier
2883 * so not allow to raise vfloat to prevent battery
2884 * voltage over than tier
2885 */
2886 msc_state = MSC_TIERCNTING;
2887 *update_interval = profile->cv_update_interval;
2888
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07002889 batt_prlog(BATT_PRLOG_ALWAYS,
2890 "MSC_TIERCNTING vt=%d vb=%d fv_uv=%d margin=%d\n",
2891 vtier, vbatt, *fv_uv,
2892 profile->cv_range_accuracy);
Ken Tsou8acade12020-07-09 03:17:35 +08002893 } else if (match_enable) {
2894 /*
2895 * TAPER_RAISE: under tier vlim, raise one click &
2896 * debounce taper (see above handling of STEADY)
2897 */
2898 msc_state = MSC_RAISE;
2899 *fv_uv = gbms_msc_round_fv_uv(profile, vtier,
2900 *fv_uv + profile->fv_uv_resolution);
2901 *update_interval = profile->cv_update_interval;
2902
2903 /* debounce next taper voltage adjustment */
2904 batt_drv->checked_cv_cnt = profile->cv_debounce_cnt;
2905
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07002906 batt_prlog(BATT_PRLOG_ALWAYS, "MSC_RAISE vt=%d vb=%d fv_uv=%d->%d\n",
2907 vtier, vbatt, batt_drv->fv_uv, *fv_uv);
Ken Tsou8acade12020-07-09 03:17:35 +08002908 } else {
2909 msc_state = MSC_STEADY;
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07002910 batt_prlog(BATT_PRLOG_DEBUG, "MSC_DISB vt=%d vb=%d fv_uv=%d->%d\n",
Ken Tsou8acade12020-07-09 03:17:35 +08002911 vtier, vbatt, batt_drv->fv_uv, *fv_uv);
2912 }
2913
2914 return msc_state;
2915}
2916
Jenny Ho6f7ec522020-05-19 09:04:53 +08002917/* battery health based charging on SOC */
2918static enum chg_health_state msc_health_active(const struct batt_drv *batt_drv)
AleX Pelosi8e7fd812019-08-16 10:41:46 -07002919{
Jenny Ho6f7ec522020-05-19 09:04:53 +08002920 int ssoc, ssoc_threshold = -1;
AleX Pelosi8e7fd812019-08-16 10:41:46 -07002921
Jenny Ho6f7ec522020-05-19 09:04:53 +08002922 ssoc_threshold = CHG_HEALTH_REST_SOC(&batt_drv->chg_health);
2923 if (ssoc_threshold < 0)
2924 return CHG_HEALTH_INACTIVE;
AleX Pelosi8e7fd812019-08-16 10:41:46 -07002925
Jenny Ho6f7ec522020-05-19 09:04:53 +08002926 ssoc = ssoc_get_capacity(&batt_drv->ssoc_state);
2927 if (ssoc >= ssoc_threshold)
2928 return CHG_HEALTH_ACTIVE;
AleX Pelosi8e7fd812019-08-16 10:41:46 -07002929
Jenny Ho6f7ec522020-05-19 09:04:53 +08002930 return CHG_HEALTH_ENABLED;
AleX Pelosi8e7fd812019-08-16 10:41:46 -07002931}
2932
Jenny Ho8b2bc5f2021-05-11 09:42:26 +08002933#define HEALTH_PAUSE_DEBOUNCE 180
2934#define HEALTH_PAUSE_MAX_SSOC 95
Jenny Hod74a8a42021-10-28 12:58:15 +08002935#define HEALTH_PAUSE_TIME 3
Jenny Ho8b2bc5f2021-05-11 09:42:26 +08002936static bool msc_health_pause(struct batt_drv *batt_drv, const ktime_t ttf,
2937 const ktime_t now,
2938 const enum chg_health_state rest_state) {
2939 const struct gbms_charging_event *ce_data = &batt_drv->ce_data;
2940 const struct gbms_ce_tier_stats *h = &ce_data->health_stats;
Jenny Hod74a8a42021-10-28 12:58:15 +08002941 struct batt_chg_health *rest = &batt_drv->chg_health;
Jenny Ho8b2bc5f2021-05-11 09:42:26 +08002942 const ktime_t deadline = rest->rest_deadline;
Jenny Ho47467492021-05-19 17:29:19 +08002943 const ktime_t safety_margin = (ktime_t)batt_drv->health_safety_margin;
Stephane Lee2b9ea732021-05-23 23:39:54 -07002944 /* Note: We only capture ACTIVE time in health stats */
Jenny Ho8b2bc5f2021-05-11 09:42:26 +08002945 const ktime_t elap_h = h->time_fast + h->time_taper + h->time_other;
2946 const int ssoc = ssoc_get_capacity(&batt_drv->ssoc_state);
2947
2948 /*
Jenny Ho47467492021-05-19 17:29:19 +08002949 * the safety marging cannot be less than 0 (it would subtract time from TTF and would
2950 * cause AC to never meet 100% in time). Use 0<= to disable PAUSE.
2951 */
2952 if (safety_margin <= 0)
2953 return false;
2954
2955 /*
Jenny Ho8b2bc5f2021-05-11 09:42:26 +08002956 * Expected behavior:
2957 * 1. ACTIVE: small current run a while for ttf
2958 * 2. PAUSE: when time is enough to pause
2959 * 3. ACTIVE: when time out and back to ACTIVE charge
2960 */
2961 if (rest_state != CHG_HEALTH_ACTIVE && rest_state != CHG_HEALTH_PAUSE)
2962 return false;
2963
2964 /*
Jenny Ho8b2bc5f2021-05-11 09:42:26 +08002965 * ssoc: transfer in high soc impact charge full condition, disable pause
2966 * behavior in high soc
2967 */
Jenny Hod74a8a42021-10-28 12:58:15 +08002968 if (ssoc > HEALTH_PAUSE_MAX_SSOC)
2969 return false;
2970
2971 /*
2972 * elap_h: running active for a while wait status and current stable
2973 * need to re-check before re-enter pause, so we need to minus previous
2974 * health active time (rest->active_time) for next HEALTH_PAUSE_DEBOUNCE
2975 */
2976 if (elap_h - rest->active_time < HEALTH_PAUSE_DEBOUNCE)
2977 return false;
2978
2979 /* prevent enter <---> leave PAUSE too many times */
2980 if (rest->active_time > (HEALTH_PAUSE_TIME * HEALTH_PAUSE_DEBOUNCE))
Jenny Ho8b2bc5f2021-05-11 09:42:26 +08002981 return false;
2982
Stephane Lee2b9ea732021-05-23 23:39:54 -07002983 /* check if time meets the PAUSE condition or not */
Jenny Ho8b2bc5f2021-05-11 09:42:26 +08002984 if (ttf > 0 && deadline > now + ttf + safety_margin)
2985 return true;
2986
Jenny Hod74a8a42021-10-28 12:58:15 +08002987 /* record time for next pause check */
2988 rest->active_time = elap_h;
2989
Jenny Ho8b2bc5f2021-05-11 09:42:26 +08002990 return false;
2991}
2992
2993
AleX Pelosi0d261512020-05-07 12:17:07 -07002994/*
2995 * for logging, userspace should use
AleX Pelosi043ffbe2020-06-24 22:48:30 -07002996 * deadline == 0 on fast replug (leave initial deadline ok)
AleX Pelosi0d261512020-05-07 12:17:07 -07002997 * deadline == -1 when the feature is disabled
Stephane Leeaaf28cd2020-08-26 11:26:06 -07002998 * if charge health was active/enabled, set to -2
AleX Pelosi043ffbe2020-06-24 22:48:30 -07002999 * deadline == absolute requested deadline (if always_on is set)
3000 * return true if there was a change
AleX Pelosi0d261512020-05-07 12:17:07 -07003001 */
AleX Pelosi57c74e22020-02-27 19:29:27 -08003002static bool batt_health_set_chg_deadline(struct batt_chg_health *chg_health,
AleX Pelosi0d261512020-05-07 12:17:07 -07003003 long long deadline_s)
AleX Pelosi8e7fd812019-08-16 10:41:46 -07003004{
AleX Pelosi57c74e22020-02-27 19:29:27 -08003005 enum chg_health_state rest_state = chg_health->rest_state;
AleX Pelosi0d261512020-05-07 12:17:07 -07003006 bool new_deadline;
AleX Pelosi57c74e22020-02-27 19:29:27 -08003007
AleX Pelosi043ffbe2020-06-24 22:48:30 -07003008 /* disabled in settings */
AleX Pelosi0d261512020-05-07 12:17:07 -07003009 if (deadline_s < 0) {
3010 new_deadline = chg_health->rest_deadline != deadline_s;
3011 chg_health->rest_state = CHG_HEALTH_USER_DISABLED;
Stephane Leeaaf28cd2020-08-26 11:26:06 -07003012
Stephane Lee4a984a22022-01-14 15:14:07 -08003013 /* disabled with notification; assumes that the dialog exists
3014 * only if there is a >0 deadline.
3015 */
3016 if (deadline_s == CHG_DEADLINE_DIALOG)
3017 chg_health->rest_deadline = CHG_DEADLINE_DIALOG;
3018 else if (chg_health->rest_deadline > 0) /* was active */
Stephane Leeaaf28cd2020-08-26 11:26:06 -07003019 chg_health->rest_deadline = CHG_DEADLINE_SETTING_STOP;
3020 else
3021 chg_health->rest_deadline = CHG_DEADLINE_SETTING;
3022
AleX Pelosi043ffbe2020-06-24 22:48:30 -07003023 /* disabled with replug */
AleX Pelosi0d261512020-05-07 12:17:07 -07003024 } else if (deadline_s == 0) {
3025 new_deadline = chg_health->rest_deadline != deadline_s;
AleX Pelosi043ffbe2020-06-24 22:48:30 -07003026 /* ->rest_deadline will be reset to 0 on disconnect */
AleX Pelosi9e1713b2020-09-09 02:31:43 -07003027
Stephane Leecbb07ee2020-09-14 12:24:09 -07003028 /* Don't disable A/C if already done */
3029 if (chg_health->rest_state != CHG_HEALTH_DONE)
3030 chg_health->rest_state = CHG_HEALTH_USER_DISABLED;
3031
Stephane Lee4a984a22022-01-14 15:14:07 -08003032 } else { /* enabled from any previous state */
AleX Pelosi0d261512020-05-07 12:17:07 -07003033 const ktime_t rest_deadline = get_boot_sec() + deadline_s;
3034
AleX Pelosi043ffbe2020-06-24 22:48:30 -07003035 /* ->always_on SOC overrides the deadline */
AleX Pelosi0d261512020-05-07 12:17:07 -07003036 new_deadline = chg_health->rest_deadline != rest_deadline;
AleX Pelosi57c74e22020-02-27 19:29:27 -08003037 chg_health->rest_state = CHG_HEALTH_ENABLED;
AleX Pelosi0d261512020-05-07 12:17:07 -07003038 chg_health->rest_deadline = rest_deadline;
AleX Pelosi57c74e22020-02-27 19:29:27 -08003039 }
3040
3041 return new_deadline || rest_state != chg_health->rest_state;
3042}
3043
Jenny Ho6ff91bf2022-09-16 15:11:04 +08003044#define HEALTH_CHG_RATE_BEFORE_TRIGGER 80
AleX Pelosi57c74e22020-02-27 19:29:27 -08003045/* health based charging trade charging speed for battery cycle life. */
Jenny Ho6f7ec522020-05-19 09:04:53 +08003046static bool msc_logic_health(struct batt_drv *batt_drv)
AleX Pelosi57c74e22020-02-27 19:29:27 -08003047{
AleX Pelosi043ffbe2020-06-24 22:48:30 -07003048 const struct gbms_chg_profile *profile = &batt_drv->chg_profile;
Jenny Ho6f7ec522020-05-19 09:04:53 +08003049 struct batt_chg_health *rest = &batt_drv->chg_health;
AleX Pelosi8e7fd812019-08-16 10:41:46 -07003050 const ktime_t deadline = rest->rest_deadline;
AleX Pelosi57c74e22020-02-27 19:29:27 -08003051 enum chg_health_state rest_state = rest->rest_state;
AleX Pelosi9e1713b2020-09-09 02:31:43 -07003052 const bool aon_enabled = rest->always_on_soc != -1;
Jenny Ho6ff91bf2022-09-16 15:11:04 +08003053 const int capacity_ma = batt_drv->battery_capacity;
AleX Pelosi9e1713b2020-09-09 02:31:43 -07003054 const ktime_t now = get_boot_sec();
AleX Pelosi043ffbe2020-06-24 22:48:30 -07003055 int fv_uv = -1, cc_max = -1;
AleX Pelosi57c74e22020-02-27 19:29:27 -08003056 bool changed = false;
AleX Pelosi043ffbe2020-06-24 22:48:30 -07003057 ktime_t ttf = 0;
AleX Pelosi9e1713b2020-09-09 02:31:43 -07003058 int ret;
AleX Pelosi8e7fd812019-08-16 10:41:46 -07003059
AleX Pelosi9e1713b2020-09-09 02:31:43 -07003060 /* move to ENABLED if INACTIVE when aon_enabled is set */
Stephane Lee595f3182020-06-26 18:13:02 -07003061 if (aon_enabled && rest_state == CHG_HEALTH_INACTIVE)
AleX Pelosi043ffbe2020-06-24 22:48:30 -07003062 rest_state = CHG_HEALTH_ENABLED;
AleX Pelosi8e7fd812019-08-16 10:41:46 -07003063
AleX Pelosi9e1713b2020-09-09 02:31:43 -07003064 /*
3065 * on disconnect batt_reset_rest_state() will set rest_state to
3066 * CHG_HEALTH_USER_DISABLED if the deadline is negative.
3067 */
AleX Pelosia6ad6ad2020-10-29 13:05:27 -07003068 if (rest_state == CHG_HEALTH_CCLVL_DISABLED ||
3069 rest_state == CHG_HEALTH_BD_DISABLED ||
AleX Pelosi600cb122020-10-29 10:05:47 -07003070 rest_state == CHG_HEALTH_USER_DISABLED ||
AleX Pelosi9e1713b2020-09-09 02:31:43 -07003071 rest_state == CHG_HEALTH_DISABLED ||
3072 rest_state == CHG_HEALTH_INACTIVE)
3073 goto done_no_op;
AleX Pelosi0d261512020-05-07 12:17:07 -07003074
AleX Pelosi9e1713b2020-09-09 02:31:43 -07003075 /* Keeps AC enabled after DONE */
3076 if (rest_state == CHG_HEALTH_DONE)
3077 goto done_exit;
AleX Pelosi043ffbe2020-06-24 22:48:30 -07003078
AleX Pelosia6ad6ad2020-10-29 13:05:27 -07003079 /* disable AC because we are running custom charging levels */
3080 if (batt_drv->chg_state.f.flags & GBMS_CS_FLAG_CCLVL) {
3081 rest_state = CHG_HEALTH_CCLVL_DISABLED;
3082 goto done_exit;
3083 }
3084
3085 /* disable AC because BD-TEMP triggered */
AleX Pelosi600cb122020-10-29 10:05:47 -07003086 if (batt_drv->batt_health == POWER_SUPPLY_HEALTH_OVERHEAT) {
3087 rest_state = CHG_HEALTH_BD_DISABLED;
3088 goto done_exit;
3089 }
3090
AleX Pelosi9e1713b2020-09-09 02:31:43 -07003091 /*
3092 * ret < 0 right after plug-in or when the device is discharging due
3093 * to a large sysload or an underpowered adapter (or both). Current
AleX Pelosi600cb122020-10-29 10:05:47 -07003094 * strategy leaves everything as is (hoping) that the load is temporary.
3095 * The estimate will be negative when BD is triggered and during the
3096 * debounce period.
AleX Pelosi9e1713b2020-09-09 02:31:43 -07003097 */
3098 ret = batt_ttf_estimate(&ttf, batt_drv);
3099 if (ret < 0)
3100 return false;
AleX Pelosi043ffbe2020-06-24 22:48:30 -07003101
AleX Pelosi9e1713b2020-09-09 02:31:43 -07003102 /* estimate is 0 at 100%: set to done and keep AC enabled in RL */
3103 if (ttf == 0) {
3104 rest_state = CHG_HEALTH_DONE;
3105 goto done_exit;
AleX Pelosi043ffbe2020-06-24 22:48:30 -07003106 }
3107
AleX Pelosi9e1713b2020-09-09 02:31:43 -07003108 /*
3109 * rest_state here is either ENABLED or ACTIVE, transition to DISABLED
3110 * when the deadline cannot be met with the current rate. set a new
3111 * deadline or reset always_on_soc to re-enable AC for this session.
3112 * NOTE: A device with AON enabled might (will) receive a deadline if
3113 * plugged in within the AC window: ignore it.
3114 * NOTE: cannot have a negative deadline with rest_state different
3115 * from CHG_HEALTH_USER_DISABLED.
3116 * TODO: consider adding a margin or debounce it.
3117 */
3118 if (aon_enabled == false && rest_state == CHG_HEALTH_ACTIVE &&
AleX Pelosi600cb122020-10-29 10:05:47 -07003119 deadline > 0 && ttf != -1 && now + ttf > deadline) {
AleX Pelosi8e7fd812019-08-16 10:41:46 -07003120 rest_state = CHG_HEALTH_DISABLED;
AleX Pelosi043ffbe2020-06-24 22:48:30 -07003121 goto done_exit;
3122 }
AleX Pelosi0d261512020-05-07 12:17:07 -07003123
Jenny Hofe2308e2021-11-22 05:40:34 +08003124 /* Decide enter PAUSE state or not by time if not set ACA */
3125 if (aon_enabled == false &&
3126 msc_health_pause(batt_drv, ttf, now, rest_state)) {
Jenny Ho8b2bc5f2021-05-11 09:42:26 +08003127 rest_state = CHG_HEALTH_PAUSE;
3128 goto done_exit;
3129 }
3130
AleX Pelosi043ffbe2020-06-24 22:48:30 -07003131 /*
AleX Pelosi9e1713b2020-09-09 02:31:43 -07003132 * rest_state here is either ENABLED or ACTIVE,
3133 * NOTE: State might transition from _ACTIVE to _ENABLED after a
3134 * discharge cycle that makes the battery fall under the threshold.
AleX Pelosi043ffbe2020-06-24 22:48:30 -07003135 * State will transition back to _ENABLED after some time unless
3136 * the deadline is met.
3137 */
Jenny Ho6f7ec522020-05-19 09:04:53 +08003138 rest_state = msc_health_active(batt_drv);
AleX Pelosiba6ba8b2020-09-09 02:17:08 -07003139
3140done_exit:
3141 if (rest_state == CHG_HEALTH_ACTIVE || rest_state == CHG_HEALTH_DONE) {
Jenny Ho6ff91bf2022-09-16 15:11:04 +08003142 /* cc_max in ua: capacity in mAh, rest_rate in deciPct */
3143 cc_max = capacity_ma * rest->rest_rate * 10;
AleX Pelosi8e7fd812019-08-16 10:41:46 -07003144
AleX Pelosi57c74e22020-02-27 19:29:27 -08003145 /*
AleX Pelosi0d261512020-05-07 12:17:07 -07003146 * default FV_UV to the last charge tier since fv_uv will be
3147 * set to that on _DONE.
AleX Pelosi57c74e22020-02-27 19:29:27 -08003148 * NOTE this might need to be adjusted for the actual charge
3149 * tiers that have nonzero charging current
AleX Pelosi8e7fd812019-08-16 10:41:46 -07003150 */
3151 fv_uv = profile->volt_limits[profile->volt_nb_limits - 1];
AleX Pelosi8e7fd812019-08-16 10:41:46 -07003152
3153 /* TODO: make sure that we wakeup when we are close to ttf */
Jenny Ho6ff91bf2022-09-16 15:11:04 +08003154 } else if (rest_state == CHG_HEALTH_ENABLED) {
3155 cc_max = capacity_ma * rest->rest_rate_before_trigger * 10;
Jenny Ho8b2bc5f2021-05-11 09:42:26 +08003156 } else if (rest_state == CHG_HEALTH_PAUSE) {
3157 /*
3158 * pause charging behavior when the the deadline is longer than
3159 * expected charge time. return back to CHG_HEALTH_ACTIVE and
3160 * start health charge when now + ttf + margine close to deadline
3161 */
3162 cc_max = 0;
AleX Pelosi8e7fd812019-08-16 10:41:46 -07003163 }
3164
AleX Pelosi9e1713b2020-09-09 02:31:43 -07003165done_no_op:
AleX Pelosi57c74e22020-02-27 19:29:27 -08003166 /* send a power supply event when rest_state changes */
3167 changed = rest->rest_state != rest_state;
AleX Pelosi8e7fd812019-08-16 10:41:46 -07003168
AleX Pelosi57c74e22020-02-27 19:29:27 -08003169 /* msc_logic_* will vote on cc_max and fv_uv. */
AleX Pelosi8e7fd812019-08-16 10:41:46 -07003170 rest->rest_cc_max = cc_max;
3171 rest->rest_fv_uv = fv_uv;
AleX Pelosi57c74e22020-02-27 19:29:27 -08003172
Jenny Ho6f7ec522020-05-19 09:04:53 +08003173 if (!changed)
3174 return false;
3175
Jenny Hoa7026682022-09-30 16:38:45 +08003176 gbms_logbuffer_prlog(batt_drv->ttf_stats.ttf_log, LOGLEVEL_INFO, 0, LOGLEVEL_DEBUG,
3177 "MSC_HEALTH: now=%lld deadline=%lld aon_soc=%d ttf=%lld state=%d->%d fv_uv=%d, cc_max=%d safety_margin=%d active_time:%lld",
3178 now, rest->rest_deadline, rest->always_on_soc, ttf, rest->rest_state,
3179 rest_state, fv_uv, cc_max, batt_drv->health_safety_margin,
3180 rest->active_time);
Jenny Ho6f7ec522020-05-19 09:04:53 +08003181
3182 rest->rest_state = rest_state;
3183 memcpy(&batt_drv->ce_data.ce_health, &batt_drv->chg_health,
3184 sizeof(batt_drv->ce_data.ce_health));
3185 return true;
AleX Pelosi8e7fd812019-08-16 10:41:46 -07003186}
3187
Ken Tsou8acade12020-07-09 03:17:35 +08003188static int msc_pm_hold(int msc_state)
3189{
3190 int pm_state = -1;
3191
3192 switch (msc_state) {
3193 case MSC_RAISE:
3194 case MSC_VOVER:
3195 case MSC_PULLBACK:
3196 pm_state = 1; /* __pm_stay_awake */
3197 break;
3198 case MSC_SEED:
3199 case MSC_DSG:
3200 case MSC_VSWITCH:
3201 case MSC_NEXT:
3202 case MSC_LAST:
AleX Pelosi80d78ec2021-05-19 22:55:44 -07003203 case MSC_RSTC:
AleX Pelosi0c88ff32020-04-02 01:04:51 -07003204 case MSC_HEALTH:
Jenny Ho1c2ec2b2021-12-10 17:43:14 +08003205 case MSC_HEALTH_PAUSE:
3206 case MSC_HEALTH_ALWAYS_ON:
AleX Pelosi80d78ec2021-05-19 22:55:44 -07003207 case MSC_WAIT:
3208 case MSC_FAST:
AleX Pelosi66dab612021-07-17 20:57:54 -07003209 case MSC_NYET:
3210 case MSC_STEADY:
AleX Pelosi0c88ff32020-04-02 01:04:51 -07003211 pm_state = 0; /* pm_relax */
3212 break;
3213 default:
AleX Pelosi66dab612021-07-17 20:57:54 -07003214 pr_debug("hold not defined for msc_state=%d\n", msc_state);
Ken Tsou8acade12020-07-09 03:17:35 +08003215 pm_state = 0; /* pm_relax */
3216 break;
3217 }
3218
3219 return pm_state;
3220}
3221
AleX Pelosic10eb8b2021-12-14 13:20:21 -08003222/* same as design when under the grace period */
3223static u32 aacr_get_reference_capacity(const struct batt_drv *batt_drv, int cycle_count)
Jenny Ho23314e62021-11-19 08:58:25 +08003224{
3225 const int design_capacity = batt_drv->battery_capacity;
AleX Pelosic10eb8b2021-12-14 13:20:21 -08003226 const int aacr_cycle_grace = batt_drv->aacr_cycle_grace;
3227 const int aacr_cycle_max = batt_drv->aacr_cycle_max;
3228 int fade10;
Jenny Ho23314e62021-11-19 08:58:25 +08003229
AleX Pelosic10eb8b2021-12-14 13:20:21 -08003230 fade10 = gbms_aacr_fade10(&batt_drv->chg_profile, cycle_count);
3231 if (fade10 >= 0) {
3232 /* use interpolation between known points */
3233 } else if (aacr_cycle_max && (cycle_count > aacr_cycle_grace)) {
3234 /* or use slope from ->aacr_cycle_grace for 20% @ ->aacr_cycle_max */
3235 fade10 = (200 * (cycle_count - aacr_cycle_grace)) /
3236 (aacr_cycle_max - aacr_cycle_grace);
Jenny Ho23314e62021-11-19 08:58:25 +08003237
AleX Pelosic10eb8b2021-12-14 13:20:21 -08003238 pr_debug("%s: aacr_cycle_max=%d, cycle_count=%d fade10=%d\n",
3239 __func__, aacr_cycle_max, cycle_count, fade10);
3240 } else {
3241 fade10 = 0;
Jenny Ho23314e62021-11-19 08:58:25 +08003242 }
3243
AleX Pelosic10eb8b2021-12-14 13:20:21 -08003244 return design_capacity - (design_capacity * fade10 / 1000);
3245}
Jenny Ho23314e62021-11-19 08:58:25 +08003246
AleX Pelosic10eb8b2021-12-14 13:20:21 -08003247/* 80% of design_capacity min, design_capacity in grace, aacr or negative */
3248static int aacr_get_capacity_at_cycle(const struct batt_drv *batt_drv, int cycle_count)
3249{
3250 const int design_capacity = batt_drv->battery_capacity; /* mAh */
3251 const int min_capacity = (batt_drv->battery_capacity * 80) / 100;
3252 int reference_capacity, full_cap_nom, full_capacity;
3253 struct power_supply *fg_psy = batt_drv->fg_psy;
3254 int aacr_capacity;
3255
3256 /* batt_drv->cycle_count might be negative */
3257 if (cycle_count <= batt_drv->aacr_cycle_grace)
3258 return design_capacity;
3259
3260 /* peg at 80% of design when over limit (if set) */
3261 if (batt_drv->aacr_cycle_max && (cycle_count >= batt_drv->aacr_cycle_max))
3262 return min_capacity;
3263
3264 reference_capacity = aacr_get_reference_capacity(batt_drv, cycle_count);
3265 if (reference_capacity <= 0)
3266 return design_capacity;
3267
3268 /* full_cap_nom in uAh, need to scale to mAh */
Jenny Ho23314e62021-11-19 08:58:25 +08003269 full_cap_nom = GPSY_GET_PROP(fg_psy, POWER_SUPPLY_PROP_CHARGE_FULL);
3270 if (full_cap_nom < 0)
AleX Pelosic10eb8b2021-12-14 13:20:21 -08003271 return full_cap_nom;
Jenny Ho23314e62021-11-19 08:58:25 +08003272
Jenny Ho24fcef62022-07-14 10:45:04 +00003273 full_cap_nom /= 1000;
3274
3275 if (batt_drv->aacr_algo == BATT_AACR_ALGO_LOW_B)
3276 full_capacity = min(min(full_cap_nom, design_capacity), reference_capacity);
3277 else
3278 full_capacity = max(min(full_cap_nom, design_capacity), reference_capacity);
3279
AleX Pelosic10eb8b2021-12-14 13:20:21 -08003280 aacr_capacity = max(full_capacity, min_capacity);
3281 aacr_capacity = (aacr_capacity / 50) * 50; /* 50mAh, ~1% capacity */
Jenny Ho23314e62021-11-19 08:58:25 +08003282
Jenny Ho24fcef62022-07-14 10:45:04 +00003283 pr_debug("%s: design=%d reference=%d full_cap_nom=%d full=%d aacr=%d algo=%d\n",
AleX Pelosic10eb8b2021-12-14 13:20:21 -08003284 __func__, design_capacity, reference_capacity, full_cap_nom,
Jenny Ho24fcef62022-07-14 10:45:04 +00003285 full_capacity, aacr_capacity, batt_drv->aacr_algo);
Jenny Ho23314e62021-11-19 08:58:25 +08003286
AleX Pelosic10eb8b2021-12-14 13:20:21 -08003287 return aacr_capacity;
3288}
3289
3290/* design_capacity when not enabled, never a negative value */
3291static u32 aacr_get_capacity(struct batt_drv *batt_drv)
3292{
3293 int capacity = batt_drv->battery_capacity;
Jenny Hob88e7ba2022-01-26 05:20:41 +08003294 int cycle_count;
3295
3296 if (batt_drv->fake_aacr_cc)
3297 cycle_count = batt_drv->fake_aacr_cc;
3298 else
3299 cycle_count = batt_drv->cycle_count;
AleX Pelosic10eb8b2021-12-14 13:20:21 -08003300
3301 if (batt_drv->aacr_state == BATT_AACR_DISABLED)
3302 goto exit_done;
3303
Jenny Hob88e7ba2022-01-26 05:20:41 +08003304 if (cycle_count <= batt_drv->aacr_cycle_grace) {
AleX Pelosic10eb8b2021-12-14 13:20:21 -08003305 batt_drv->aacr_state = BATT_AACR_UNDER_CYCLES;
3306 } else {
3307 int aacr_capacity;
3308
Jenny Hob88e7ba2022-01-26 05:20:41 +08003309 aacr_capacity = aacr_get_capacity_at_cycle(batt_drv, cycle_count);
AleX Pelosic10eb8b2021-12-14 13:20:21 -08003310 if (aacr_capacity < 0) {
3311 batt_drv->aacr_state = BATT_AACR_INVALID_CAP;
3312 } else {
3313 batt_drv->aacr_state = BATT_AACR_ENABLED;
3314 capacity = aacr_capacity;
3315 }
3316 }
3317
3318exit_done:
3319 return (u32)capacity;
Jenny Ho23314e62021-11-19 08:58:25 +08003320}
3321
AleX Pelosi43bec422022-04-27 21:59:19 -07003322/* BHI -------------------------------------------------------------------- */
3323
Jenny Hoef8e5252022-10-20 12:03:05 +08003324#define ONE_YEAR_HRS (24 * 365)
3325#define BHI_INDI_CAP 85
3326static int bhi_individual_conditions_index(const struct health_data *health_data)
3327{
3328 const struct bhi_data *bhi_data = &health_data->bhi_data;
3329 const int cur_impedance = batt_ravg_value(&bhi_data->res_state);
3330 const int age_impedance_max = bhi_data->act_impedance * 2;
3331 const int cur_capacity_pct = 100 - bhi_data->capacity_fade;
3332
3333 if (health_data->bhi_data.battery_age >= ONE_YEAR_HRS ||
3334 cur_impedance >= age_impedance_max || cur_capacity_pct <= BHI_INDI_CAP)
3335 return health_data->need_rep_threshold * 100;
3336
3337 return BHI_ALGO_FULL_HEALTH;
3338}
3339
AleX Pelosic1cd4752022-05-04 02:15:02 -07003340/* GBMS_PROP_CAPACITY_FADE_RATE access this via GBMS_TAG_HCNT */
AleX Pelosi43bec422022-04-27 21:59:19 -07003341static int hist_get_index(int cycle_count, const struct batt_drv *batt_drv)
Jack Wuecfe7562022-03-17 18:17:53 +08003342{
AleX Pelosic1cd4752022-05-04 02:15:02 -07003343 /* wait for history to be initialized */
3344 if (batt_drv->hist_data_max_cnt <= 0 || cycle_count <= 0)
AleX Pelosi43bec422022-04-27 21:59:19 -07003345 return -ENODATA;
Jack Wuecfe7562022-03-17 18:17:53 +08003346
AleX Pelosi43bec422022-04-27 21:59:19 -07003347 return cycle_count / batt_drv->hist_delta_cycle_cnt;
3348}
Jack Wuecfe7562022-03-17 18:17:53 +08003349
AleX Pelosia3f22de2022-05-03 23:42:24 -07003350static int bhi_cap_data_update(struct bhi_data *bhi_data, struct batt_drv *batt_drv)
AleX Pelosi43bec422022-04-27 21:59:19 -07003351{
AleX Pelosia3f22de2022-05-03 23:42:24 -07003352 struct power_supply *fg_psy = batt_drv->fg_psy;
3353 int cap_fade;
Jack Wuecfe7562022-03-17 18:17:53 +08003354
AleX Pelosic1cd4752022-05-04 02:15:02 -07003355 /* GBMS_PROP_CAPACITY_FADE_RATE is in percent */
AleX Pelosia3f22de2022-05-03 23:42:24 -07003356 cap_fade = GPSY_GET_PROP(fg_psy, GBMS_PROP_CAPACITY_FADE_RATE);
3357 if (cap_fade < 0)
3358 return -ENODATA;
3359 if (cap_fade > 100)
3360 cap_fade = 100;
Jack Wuecfe7562022-03-17 18:17:53 +08003361
AleX Pelosia3f22de2022-05-03 23:42:24 -07003362 bhi_data->capacity_fade = cap_fade;
AleX Pelosi43bec422022-04-27 21:59:19 -07003363
AleX Pelosic1cd4752022-05-04 02:15:02 -07003364 pr_debug("%s: cap_fade=%d, cycle_count=%d\n", __func__,
AleX Pelosi43bec422022-04-27 21:59:19 -07003365 bhi_data->capacity_fade, bhi_data->cycle_count);
AleX Pelosia3f22de2022-05-03 23:42:24 -07003366
Jack Wuecfe7562022-03-17 18:17:53 +08003367 return 0;
3368}
3369
AleX Pelosic1cd4752022-05-04 02:15:02 -07003370
3371/*
3372 * NOTE: make sure that the FG and this code use the same reference value for
3373 * capacity. Also GBMS_PROP_CAPACITY_FADE_RATE is in percent.
AleX Pelosic1cd4752022-05-04 02:15:02 -07003374 */
3375static int bhi_health_get_capacity(int algo, const struct bhi_data *bhi_data)
Jack Wu5b74da42022-03-12 14:09:28 +08003376{
AleX Pelosi897284b2022-07-09 20:04:58 +00003377 return bhi_data->capacity_design * (100 - bhi_data->capacity_fade) / 100;
AleX Pelosi43bec422022-04-27 21:59:19 -07003378}
3379
3380/* The limit for capacity is 80% of design */
Jenny Hoa19f0a72022-07-14 03:17:42 +00003381static int bhi_calc_cap_index(int algo, struct batt_drv *batt_drv)
AleX Pelosi43bec422022-04-27 21:59:19 -07003382{
Jenny Hoa19f0a72022-07-14 03:17:42 +00003383 const struct health_data *health_data = &batt_drv->health_data;
3384 const struct bhi_data *bhi_data = &health_data->bhi_data;
3385 int capacity_health, index, capacity_aacr = 0;
AleX Pelosi43bec422022-04-27 21:59:19 -07003386
3387 if (algo == BHI_ALGO_DISABLED)
AleX Pelosic1cd4752022-05-04 02:15:02 -07003388 return BHI_ALGO_FULL_HEALTH;
AleX Pelosi43bec422022-04-27 21:59:19 -07003389
Jenny Hoe6f05b52022-10-18 16:37:21 +08003390 if (health_data->bhi_debug_cap_index)
3391 return health_data->bhi_debug_cap_index;
3392
AleX Pelosi43bec422022-04-27 21:59:19 -07003393 if (!bhi_data->capacity_design)
Jack Wu5b74da42022-03-12 14:09:28 +08003394 return -ENODATA;
3395
AleX Pelosic1cd4752022-05-04 02:15:02 -07003396 capacity_health = bhi_health_get_capacity(algo, bhi_data);
Jack Wu5b74da42022-03-12 14:09:28 +08003397
Jenny Hoa19f0a72022-07-14 03:17:42 +00003398 /* for BHI_ALGO_ACHI_B compare to aacr capacity */
3399 if (algo == BHI_ALGO_ACHI_B || algo == BHI_ALGO_ACHI_RAVG_B) {
3400 capacity_aacr = aacr_get_capacity(batt_drv);
3401
3402 if (capacity_health < capacity_aacr)
3403 capacity_health = capacity_aacr;
3404 }
3405
AleX Pelosi43bec422022-04-27 21:59:19 -07003406 /*
AleX Pelosic1cd4752022-05-04 02:15:02 -07003407 * TODO: compare to google_capacity?
AleX Pelosi43bec422022-04-27 21:59:19 -07003408 * ret = gbms_storage_read(GBMS_TAG_GCFE, &gcap sizeof(gcap));
AleX Pelosi43bec422022-04-27 21:59:19 -07003409 */
Jack Wu5b74da42022-03-12 14:09:28 +08003410
AleX Pelosic1cd4752022-05-04 02:15:02 -07003411 index = (capacity_health * BHI_ALGO_FULL_HEALTH) / bhi_data->capacity_design;
AleX Pelosi897284b2022-07-09 20:04:58 +00003412 if (index > BHI_ALGO_FULL_HEALTH)
3413 index = BHI_ALGO_FULL_HEALTH;
3414
Jenny Hoa19f0a72022-07-14 03:17:42 +00003415 pr_debug("%s: algo=%d index=%d ch=%d, ca=%d, cd=%d, fr=%d\n", __func__,
3416 algo, index, capacity_health, capacity_aacr, bhi_data->capacity_design,
AleX Pelosi43bec422022-04-27 21:59:19 -07003417 bhi_data->capacity_fade);
3418
3419 return index;
Jack Wu5b74da42022-03-12 14:09:28 +08003420}
3421
AleX Pelosic1cd4752022-05-04 02:15:02 -07003422/* read and qualify the battery initial impedance */
3423static int bhi_imp_read_ai(struct bhi_data *bhi_data, struct power_supply *fg_psy)
3424{
3425 int act_impedance;
3426
3427
3428 /* use ravg if the filter is full? */
3429 act_impedance = batt_ravg_value(&bhi_data->res_state);
3430
3431 /* TODO: qualify with filter length */
3432
3433 return act_impedance;
3434}
3435
3436/* hold mutex_unlock(&batt_drv->chg_lock); */
3437static int bhi_imp_data_update(struct bhi_data *bhi_data, struct power_supply *fg_psy)
3438{
3439 const int use_ravg = true;
3440 int act_impedance = bhi_data->act_impedance;
3441 int cur_impedance;
3442
3443 if (!act_impedance) {
3444 act_impedance = GPSY_GET_PROP(fg_psy, GBMS_PROP_HEALTH_ACT_IMPEDANCE);
3445 if (act_impedance == -EINVAL) {
3446 int ret;
3447
3448 act_impedance = use_ravg ? bhi_imp_read_ai(bhi_data, fg_psy) :
3449 GPSY_GET_PROP(fg_psy, GBMS_PROP_HEALTH_IMPEDANCE);
3450 if (act_impedance <= 0)
3451 goto exit_done;
3452
3453 ret = GPSY_SET_PROP(fg_psy, GBMS_PROP_HEALTH_ACT_IMPEDANCE,
3454 act_impedance);
3455 if (ret < 0)
3456 goto exit_done;
3457 }
3458
3459 if (act_impedance < 0)
3460 goto exit_done;
3461
3462 /* primed, saved */
3463 bhi_data->act_impedance = act_impedance;
3464 return 0;
3465 }
3466
3467
3468 cur_impedance = batt_ravg_value(&bhi_data->res_state);
3469
3470 /*
3471 * Can delegate to the FG with:
3472 * cur_impedance = GPSY_GET_PROP(fg_psy, GBMS_PROP_HEALTH_IMPEDANCE);
3473 * if (cur_impedance < 0)
3474 * goto exit_done;
3475 */
3476
3477 /* max in this session. Use average maybe? */
3478 if (cur_impedance > bhi_data->cur_impedance)
3479 bhi_data->cur_impedance = cur_impedance;
3480
3481exit_done:
3482 pr_debug("%s: cur_impedance=%d, act_impedance=%d\n", __func__,
3483 cur_impedance, act_impedance);
3484 return 0;
3485}
3486/* pick the impedance from the algo */
AleX Pelosia3f22de2022-05-03 23:42:24 -07003487static int bhi_health_get_impedance(int algo, const struct bhi_data *bhi_data)
3488{
3489 u32 cur_impedance;
3490
3491 switch (algo) {
AleX Pelosic1cd4752022-05-04 02:15:02 -07003492 case BHI_ALGO_DISABLED:
3493 case BHI_ALGO_CYCLE_COUNT:
AleX Pelosia3f22de2022-05-03 23:42:24 -07003494 case BHI_ALGO_ACHI_RAVG:
AleX Pelosic1cd4752022-05-04 02:15:02 -07003495 case BHI_ALGO_ACHI_RAVG_B:
3496 case BHI_ALGO_MIX_N_MATCH:
AleX Pelosia3f22de2022-05-03 23:42:24 -07003497 cur_impedance = batt_ravg_value(&bhi_data->res_state);
3498 break;
AleX Pelosia3f22de2022-05-03 23:42:24 -07003499 default:
AleX Pelosic1cd4752022-05-04 02:15:02 -07003500 return 0;
AleX Pelosia3f22de2022-05-03 23:42:24 -07003501 }
3502
AleX Pelosic1cd4752022-05-04 02:15:02 -07003503 if (cur_impedance <= 0)
3504 cur_impedance = bhi_data->act_impedance;
3505
AleX Pelosia3f22de2022-05-03 23:42:24 -07003506 return cur_impedance;
3507}
3508
Jenny Hoe6f05b52022-10-18 16:37:21 +08003509static int bhi_calc_imp_index(int algo, const struct health_data *health_data)
Jack Wu5b74da42022-03-12 14:09:28 +08003510{
Jenny Hoe6f05b52022-10-18 16:37:21 +08003511 const struct bhi_data *bhi_data = &health_data->bhi_data;
AleX Pelosi43bec422022-04-27 21:59:19 -07003512 u32 cur_impedance;
AleX Pelosi2ef31792022-05-04 16:15:09 -07003513 int imp_index;
Jack Wu5b74da42022-03-12 14:09:28 +08003514
AleX Pelosia3f22de2022-05-03 23:42:24 -07003515 if (!bhi_data->act_impedance)
AleX Pelosic1cd4752022-05-04 02:15:02 -07003516 return BHI_ALGO_FULL_HEALTH;
AleX Pelosi43bec422022-04-27 21:59:19 -07003517
Jenny Hoe6f05b52022-10-18 16:37:21 +08003518 if (health_data->bhi_debug_imp_index)
3519 return health_data->bhi_debug_imp_index;
3520
AleX Pelosia3f22de2022-05-03 23:42:24 -07003521 cur_impedance = bhi_health_get_impedance(algo, bhi_data);
AleX Pelosic1cd4752022-05-04 02:15:02 -07003522 if (cur_impedance == 0)
3523 return BHI_ALGO_FULL_HEALTH;
AleX Pelosi43bec422022-04-27 21:59:19 -07003524
3525 /*
AleX Pelosic1cd4752022-05-04 02:15:02 -07003526 * TODO: on algo==*_B bounds check cur_impedance against res10
3527 * before calculating the impedance index.
AleX Pelosi43bec422022-04-27 21:59:19 -07003528 */
3529
3530 /* The limit is 2x of activation. */
AleX Pelosic1cd4752022-05-04 02:15:02 -07003531 imp_index = (2 * bhi_data->act_impedance - cur_impedance) * BHI_ALGO_FULL_HEALTH /
3532 bhi_data->act_impedance;
3533 if (imp_index < 0)
3534 imp_index = 0;
AleX Pelosi43bec422022-04-27 21:59:19 -07003535
AleX Pelosic1cd4752022-05-04 02:15:02 -07003536 pr_debug("%s: algo=%d index=%d current=%d, activation=%d\n", __func__,
3537 algo, imp_index, cur_impedance, bhi_data->act_impedance);
AleX Pelosi43bec422022-04-27 21:59:19 -07003538
AleX Pelosi2ef31792022-05-04 16:15:09 -07003539 return imp_index;
Jack Wu5b74da42022-03-12 14:09:28 +08003540}
3541
AleX Pelosic1cd4752022-05-04 02:15:02 -07003542static int bhi_calc_sd_total(const struct swelling_data *sd)
Jack Wu5b74da42022-03-12 14:09:28 +08003543{
AleX Pelosic1cd4752022-05-04 02:15:02 -07003544 int i, swell_total = 0;
3545 ktime_t time_at;
3546
3547 for (i = 0; i < BATT_TEMP_RECORD_THR ; i++) {
3548 time_at = sd->chg[i] / 3600;
3549 time_at += sd->dischg[i] / 3600;
3550 // TODO: use weights for temperature and soc
3551 // eg. sd->temp_thr[i]/10, sd->soc_thr[i],
3552 swell_total += time_at;
3553 }
3554
3555 return swell_total;
AleX Pelosi43bec422022-04-27 21:59:19 -07003556}
Jack Wu5b74da42022-03-12 14:09:28 +08003557
Jenny Hoe6f05b52022-10-18 16:37:21 +08003558static int bhi_calc_sd_index(int algo, const struct health_data *health_data)
AleX Pelosi43bec422022-04-27 21:59:19 -07003559{
Jenny Hoe6f05b52022-10-18 16:37:21 +08003560 const struct bhi_data *bhi_data = &health_data->bhi_data;
3561
3562 if (health_data->bhi_debug_sd_index)
3563 return health_data->bhi_debug_sd_index;
3564
AleX Pelosic1cd4752022-05-04 02:15:02 -07003565 pr_debug("%s: algo=%d index=%d\n", __func__, algo, bhi_data->ccbin_index);
3566 return bhi_data->ccbin_index;
3567}
Jack Wu5b74da42022-03-12 14:09:28 +08003568
Jenny Hoa94c30b2022-10-13 16:29:47 +08003569static int bhi_cycle_count_index(const struct health_data *health_data)
3570{
3571 const int cc = health_data->bhi_data.cycle_count;
3572 const int cc_mt = health_data->cycle_count_marginal_threshold;
3573 const int cc_nrt = health_data->cycle_count_need_rep_threshold;
3574 const int h_mt = health_data->marginal_threshold;
3575 const int h_nrt = health_data->need_rep_threshold;
3576 int cc_index;
3577
3578 /* threshold should be reasonable */
3579 if (h_mt < h_nrt || cc_nrt <= cc_mt)
3580 return BHI_ALGO_FULL_HEALTH;
3581
3582 /* use interpolation to get index via cycle count/health threshold */
3583 cc_index = (h_mt - h_nrt) * (cc - cc_nrt) / (cc_mt - cc_nrt) + h_nrt;
3584 cc_index = cc_index * 100; /* for BHI_ROUND_INDEX*/
3585
3586 if (cc_index > BHI_ALGO_FULL_HEALTH)
3587 cc_index = BHI_ALGO_FULL_HEALTH;
3588
3589 if (cc_index < 0)
3590 cc_index = 0;
3591
3592 return cc_index;
3593}
3594
Jenny Hod4bd5bc2022-08-26 16:08:38 +00003595static int bhi_calc_health_index(int algo, const struct health_data *health_data,
3596 int cap_index, int imp_index, int sd_index)
AleX Pelosic1cd4752022-05-04 02:15:02 -07003597{
3598 int ratio, index;
3599 int w_ci = 0;
3600 int w_ii = 0;
3601 int w_sd = 0;
AleX Pelosi43bec422022-04-27 21:59:19 -07003602
Jenny Hoe6f05b52022-10-18 16:37:21 +08003603 if (health_data->bhi_debug_health_index)
3604 return health_data->bhi_debug_health_index;
3605
AleX Pelosic1cd4752022-05-04 02:15:02 -07003606 switch (algo) {
3607 case BHI_ALGO_DISABLED:
3608 return BHI_ALGO_FULL_HEALTH;
3609 case BHI_ALGO_CYCLE_COUNT:
Jenny Hoa94c30b2022-10-13 16:29:47 +08003610 return bhi_cycle_count_index(health_data);
AleX Pelosic1cd4752022-05-04 02:15:02 -07003611 case BHI_ALGO_ACHI:
3612 case BHI_ALGO_ACHI_B:
AleX Pelosic1cd4752022-05-04 02:15:02 -07003613 case BHI_ALGO_ACHI_RAVG:
3614 case BHI_ALGO_ACHI_RAVG_B:
AleX Pelosic1cd4752022-05-04 02:15:02 -07003615 case BHI_ALGO_MIX_N_MATCH:
Jenny Hod4bd5bc2022-08-26 16:08:38 +00003616 w_ci = bhi_w[algo].w_ci;
3617 w_ii = bhi_w[algo].w_ii;
3618 w_sd = bhi_w[algo].w_sd;
3619 break;
3620 case BHI_ALGO_DEBUG:
3621 w_ci = health_data->bhi_w_ci;
3622 w_ii = health_data->bhi_w_pi;
3623 w_sd = health_data->bhi_w_sd;
AleX Pelosic1cd4752022-05-04 02:15:02 -07003624 break;
Jenny Hoef8e5252022-10-20 12:03:05 +08003625 case BHI_ALGO_INDI:
3626 return bhi_individual_conditions_index(health_data);
AleX Pelosic1cd4752022-05-04 02:15:02 -07003627 default:
3628 return -EINVAL;
3629 }
3630
AleX Pelosi43bec422022-04-27 21:59:19 -07003631 if (cap_index < 0)
3632 w_ci = 0;
AleX Pelosi2ef31792022-05-04 16:15:09 -07003633 if (imp_index < 0)
AleX Pelosic1cd4752022-05-04 02:15:02 -07003634 w_ii = 0;
AleX Pelosi43bec422022-04-27 21:59:19 -07003635 if (sd_index < 0)
3636 w_sd = 0;
3637
AleX Pelosic1cd4752022-05-04 02:15:02 -07003638 /* TODO: check single cell disconnect */
AleX Pelosi43bec422022-04-27 21:59:19 -07003639
AleX Pelosic1cd4752022-05-04 02:15:02 -07003640 ratio = w_ci + w_ii + w_sd;
3641 if (ratio > 100)
3642 return -ERANGE;
AleX Pelosi43bec422022-04-27 21:59:19 -07003643 if (!ratio)
3644 return 100;
3645
AleX Pelosic1cd4752022-05-04 02:15:02 -07003646 index = (cap_index * w_ci + imp_index * w_ii + sd_index * w_sd) / ratio;
3647 pr_debug("%s: algo=%d index=%d cap_index=%d/%d imp_index=%d/%d sd_index=%d/%d\n",
3648 __func__, algo, index, cap_index, w_ci, imp_index, w_ii, sd_index, w_sd);
3649
3650 return index;
AleX Pelosi43bec422022-04-27 21:59:19 -07003651}
3652
3653static enum bhi_status bhi_calc_health_status(int algo, int health_index,
3654 const struct health_data *data)
3655{
3656 enum bhi_status health_status;
3657
3658 if (algo == BHI_ALGO_DISABLED)
AleX Pelosic1cd4752022-05-04 02:15:02 -07003659 return BH_UNKNOWN;
AleX Pelosi43bec422022-04-27 21:59:19 -07003660
3661 if (health_index < 0)
3662 health_status = BH_UNKNOWN;
AleX Pelosic1cd4752022-05-04 02:15:02 -07003663 else if (health_index <= data->need_rep_threshold)
AleX Pelosi43bec422022-04-27 21:59:19 -07003664 health_status = BH_NEEDS_REPLACEMENT;
AleX Pelosic1cd4752022-05-04 02:15:02 -07003665 else if (health_index <= data->marginal_threshold)
3666 health_status = BH_MARGINAL;
Jack Wu5b74da42022-03-12 14:09:28 +08003667 else
AleX Pelosi43bec422022-04-27 21:59:19 -07003668 health_status = BH_NOMINAL;
Jack Wu5b74da42022-03-12 14:09:28 +08003669
AleX Pelosi43bec422022-04-27 21:59:19 -07003670 return health_status;
Jack Wu5b74da42022-03-12 14:09:28 +08003671}
3672
AleX Pelosic1cd4752022-05-04 02:15:02 -07003673static int batt_bhi_data_save(struct batt_drv *batt_drv)
AleX Pelosi43bec422022-04-27 21:59:19 -07003674{
AleX Pelosic1cd4752022-05-04 02:15:02 -07003675 /* TODO: load save health status, index, cap index, imp index */
3676
3677 /* TODO: save current impedance if not using RAVG */
3678
3679 return 0;
3680}
3681
3682static int batt_bhi_data_load(struct batt_drv *batt_drv)
3683{
3684 /* TODO: load last health status, index, cap index, imp index */
3685 /* TODO: prime current impedance if not using RAVG */
3686
3687 return 0;
3688}
3689
3690/* call holding mutex_lock(&batt_drv->chg_lock) */
3691static int batt_bhi_stats_update(struct batt_drv *batt_drv)
3692{
3693 struct health_data *health_data = &batt_drv->health_data;
3694 struct power_supply *fg_psy = batt_drv->fg_psy;
AleX Pelosi43bec422022-04-27 21:59:19 -07003695 const int bhi_algo = health_data->bhi_algo;
AleX Pelosic1cd4752022-05-04 02:15:02 -07003696 enum bhi_status status;
3697 bool changed = false;
AleX Pelosia3f22de2022-05-03 23:42:24 -07003698 int age, index;
3699
AleX Pelosic1cd4752022-05-04 02:15:02 -07003700
3701 /* age (and cycle count* might be used in the calc */
AleX Pelosia3f22de2022-05-03 23:42:24 -07003702 age = GPSY_GET_PROP(fg_psy, GBMS_PROP_BATTERY_AGE);
3703 if (age < 0)
3704 return -EIO;
3705 health_data->bhi_data.battery_age = age;
AleX Pelosi43bec422022-04-27 21:59:19 -07003706
AleX Pelosic1cd4752022-05-04 02:15:02 -07003707 /* cycle count is cached */
Jenny Ho0ffa6c32022-10-13 17:03:19 +08003708 if (health_data->bhi_debug_cycle_count != 0)
3709 health_data->bhi_data.cycle_count = health_data->bhi_debug_cycle_count;
3710 else
3711 health_data->bhi_data.cycle_count = batt_drv->cycle_count;
AleX Pelosic1cd4752022-05-04 02:15:02 -07003712
Jenny Hoa19f0a72022-07-14 03:17:42 +00003713 index = bhi_calc_cap_index(bhi_algo, batt_drv);
AleX Pelosi43bec422022-04-27 21:59:19 -07003714 if (index < 0)
AleX Pelosic1cd4752022-05-04 02:15:02 -07003715 index = BHI_ALGO_FULL_HEALTH;
3716 changed |= health_data->bhi_cap_index != index;
AleX Pelosi43bec422022-04-27 21:59:19 -07003717 health_data->bhi_cap_index = index;
3718
Jenny Hoe6f05b52022-10-18 16:37:21 +08003719 index = bhi_calc_imp_index(bhi_algo, health_data);
AleX Pelosi43bec422022-04-27 21:59:19 -07003720 if (index < 0)
AleX Pelosic1cd4752022-05-04 02:15:02 -07003721 index = BHI_ALGO_FULL_HEALTH;
3722 changed |= health_data->bhi_imp_index != index;
AleX Pelosi2ef31792022-05-04 16:15:09 -07003723 health_data->bhi_imp_index = index;
AleX Pelosi43bec422022-04-27 21:59:19 -07003724
Jenny Hoe6f05b52022-10-18 16:37:21 +08003725 index = bhi_calc_sd_index(bhi_algo, health_data);
AleX Pelosi43bec422022-04-27 21:59:19 -07003726 if (index < 0)
AleX Pelosic1cd4752022-05-04 02:15:02 -07003727 index = BHI_ALGO_FULL_HEALTH;
3728 changed |= health_data->bhi_sd_index != index;
3729 health_data->bhi_sd_index = index;
3730
Jenny Hod4bd5bc2022-08-26 16:08:38 +00003731 index = bhi_calc_health_index(bhi_algo, health_data,
AleX Pelosic1cd4752022-05-04 02:15:02 -07003732 health_data->bhi_cap_index,
3733 health_data->bhi_imp_index,
3734 health_data->bhi_sd_index);
3735 if (index < 0)
3736 index = BHI_ALGO_FULL_HEALTH;
3737
3738 changed |= health_data->bhi_index != index;
AleX Pelosi43bec422022-04-27 21:59:19 -07003739 health_data->bhi_index = index;
3740
Jenny Hod4bd5bc2022-08-26 16:08:38 +00003741 status = bhi_calc_health_status(bhi_algo, BHI_ROUND_INDEX(index), health_data);
AleX Pelosic1cd4752022-05-04 02:15:02 -07003742 changed |= health_data->bhi_status != status;
3743 health_data->bhi_status = status;
AleX Pelosi43bec422022-04-27 21:59:19 -07003744
AleX Pelosic1cd4752022-05-04 02:15:02 -07003745 pr_debug("%s: algo=%d status=%d bhi=%d cap_index=%d, imp_index=%d sd_index=%d (%d)\n", __func__,
3746 bhi_algo, health_data->bhi_status, health_data->bhi_index,
AleX Pelosi2ef31792022-05-04 16:15:09 -07003747 health_data->bhi_cap_index, health_data->bhi_imp_index,
AleX Pelosic1cd4752022-05-04 02:15:02 -07003748 health_data->bhi_sd_index, changed);
3749
3750
3751 if (changed) {
3752 int ret;
3753
3754 /* TODO: send a power supply event? */
3755
3756 ret = batt_bhi_data_save(batt_drv);
3757 if (ret < 0)
3758 pr_err("BHI: cannot save data (%d)\n", ret);
3759 }
3760
3761 return changed;
3762}
3763
3764/*
3765 * calculate the ratio of the time spent at under the soc_limit vs the time
3766 * spent over the soc_limit in percent.
3767 * call holding mutex_lock(&batt_drv->chg_lock);
3768 */
3769static int bhi_cycle_count_residency(struct gbatt_ccbin_data *ccd , int soc_limit)
3770{
3771 int i, under = 0, over = 0;
3772
3773 for (i = 0; i < GBMS_CCBIN_BUCKET_COUNT; i++) {
3774 if (ccd->count[i] == 0xFFFF)
3775 continue;
Jenny Ho7fd17432022-07-29 19:03:06 +00003776 if ((i * 10) < soc_limit)
AleX Pelosic1cd4752022-05-04 02:15:02 -07003777 under += ccd->count[i];
3778 else
3779 over += ccd->count[i];
3780 }
3781
3782 pr_debug("%s: under=%d, over=%d limit=%d\n", __func__, under, over, soc_limit);
3783 return (under * BHI_ALGO_FULL_HEALTH) / (under + over);
3784}
3785
3786/* call holding mutex_lock(&batt_drv->chg_lock) */
3787static int batt_bhi_stats_update_all(struct batt_drv *batt_drv)
3788{
3789 struct health_data *health_data = &batt_drv->health_data;
3790 int ret;
3791
3792 /* swell probability: cc residecy needs ccd->lock */
3793 batt_drv->health_data.bhi_data.ccbin_index =
3794 bhi_cycle_count_residency(&batt_drv->cc_data, BHI_CCBIN_INDEX_LIMIT);
3795 /* swell cumulative needs a new lock */
3796 batt_drv->health_data.bhi_data.swell_cumulative =
3797 bhi_calc_sd_total(&batt_drv->sd);
3798
3799 pr_debug("BHI: limit=%d%% ccbin_index=%d swell_total=%d\n",
3800 BHI_CCBIN_INDEX_LIMIT,
3801 batt_drv->health_data.bhi_data.ccbin_index,
3802 batt_drv->health_data.bhi_data.swell_cumulative);
3803
3804 /* impedance should be pretty recent */
3805 ret = bhi_imp_data_update(&health_data->bhi_data, batt_drv->fg_psy);
3806 if (ret < 0)
3807 pr_err("bhi imp data not available (%d)\n", ret);
3808
3809 /* bhi_capacity_index on disconnect */
3810 ret = bhi_cap_data_update(&batt_drv->health_data.bhi_data, batt_drv);
3811 if (ret < 0)
3812 pr_err("bhi cap data not available (%d)\n", ret);
3813
3814 batt_bhi_stats_update(batt_drv);
AleX Pelosia3f22de2022-05-03 23:42:24 -07003815
3816 return 0;
AleX Pelosi43bec422022-04-27 21:59:19 -07003817}
3818
3819/* ------------------------------------------------------------------------ */
3820
3821
AleX Pelosi8e7fd812019-08-16 10:41:46 -07003822/* TODO: factor msc_logic_irdop from the logic about tier switch */
3823static int msc_logic(struct batt_drv *batt_drv)
Ken Tsou8acade12020-07-09 03:17:35 +08003824{
3825 bool sw_jeita;
3826 int msc_state = MSC_NONE;
3827 struct power_supply *fg_psy = batt_drv->fg_psy;
3828 struct gbms_chg_profile *profile = &batt_drv->chg_profile;
3829 int vbatt_idx = batt_drv->vbatt_idx, fv_uv = batt_drv->fv_uv, temp_idx;
3830 int temp, ibatt, vbatt, ioerr;
3831 int update_interval = MSC_DEFAULT_UPDATE_INTERVAL;
AleX Pelosiab0e9d42020-09-29 11:13:19 -07003832 const ktime_t now = get_boot_sec();
3833 ktime_t elap = now - batt_drv->ce_data.last_update;
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07003834 bool changed;
Ken Tsou8acade12020-07-09 03:17:35 +08003835
Jenny Ho5a2657e2022-12-12 14:36:57 +08003836 ioerr = gbatt_get_raw_temp(batt_drv, &temp);
Ken Tsou8acade12020-07-09 03:17:35 +08003837 if (ioerr < 0)
3838 return -EIO;
3839
3840 /*
3841 * driver state is (was) reset when we hit the SW jeita limit.
3842 * NOTE: resetting driver state will release the wake assertion
3843 */
3844 sw_jeita = msc_logic_soft_jeita(batt_drv, temp);
3845 if (sw_jeita) {
3846 /* reset batt_drv->jeita_stop_charging to -1 */
3847 if (batt_drv->jeita_stop_charging == 0)
3848 batt_reset_chg_drv_state(batt_drv);
3849
3850 return 0;
3851 } else if (batt_drv->jeita_stop_charging) {
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07003852 batt_prlog(BATT_PRLOG_ALWAYS,
3853 "MSC_JEITA temp=%d ok, enabling charging\n",
3854 temp);
Ken Tsou8acade12020-07-09 03:17:35 +08003855 batt_drv->jeita_stop_charging = 0;
3856 }
3857
3858 ibatt = GPSY_GET_INT_PROP(fg_psy, POWER_SUPPLY_PROP_CURRENT_NOW,
3859 &ioerr);
3860 if (ioerr < 0)
3861 return -EIO;
3862
3863 vbatt = GPSY_GET_PROP(fg_psy, POWER_SUPPLY_PROP_VOLTAGE_NOW);
3864 if (vbatt < 0)
3865 return -EIO;
3866
3867 /*
3868 * Multi Step Charging with IRDROP compensation when vchrg is != 0
3869 * vbatt_idx = batt_drv->vbatt_idx, fv_uv = batt_drv->fv_uv
3870 */
3871 temp_idx = gbms_msc_temp_idx(profile, temp);
3872 if (temp_idx != batt_drv->temp_idx || batt_drv->fv_uv == -1 ||
3873 batt_drv->vbatt_idx == -1) {
3874
3875 msc_state = MSC_SEED;
3876
Jenny Ho23314e62021-11-19 08:58:25 +08003877 /* seed voltage and charging table only on connect, book 0 time */
AleX Pelosic10eb8b2021-12-14 13:20:21 -08003878 if (batt_drv->vbatt_idx == -1)
Ken Tsou8acade12020-07-09 03:17:35 +08003879 vbatt_idx = gbms_msc_voltage_idx(profile, vbatt);
3880
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07003881 batt_prlog(BATT_PRLOG_ALWAYS,
3882 "MSC_SEED temp=%d vb=%d temp_idx:%d->%d, vbatt_idx:%d->%d\n",
3883 temp, vbatt, batt_drv->temp_idx, temp_idx,
3884 batt_drv->vbatt_idx, vbatt_idx);
Ken Tsou8acade12020-07-09 03:17:35 +08003885
3886 /* Debounce tier switch only when not already switching */
3887 if (batt_drv->checked_tier_switch_cnt == 0)
3888 batt_drv->checked_cv_cnt = profile->cv_debounce_cnt;
3889 } else if (ibatt > 0) {
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07003890 const int vtier = profile->volt_limits[vbatt_idx];
3891 const bool log_level = batt_drv->msc_state != MSC_DSG ||
3892 batt_drv->cc_max != 0;
3893
Ken Tsou8acade12020-07-09 03:17:35 +08003894 /*
3895 * Track battery voltage if discharging is due to system load,
3896 * low ILIM or lack of headroom; stop charging work and reset
3897 * batt_drv state() when discharging is due to disconnect.
3898 * NOTE: POWER_SUPPLY_PROP_STATUS return *_DISCHARGING only on
3899 * disconnect.
3900 * NOTE: same vbat_idx will not change fv_uv
3901 */
3902 msc_state = MSC_DSG;
3903 vbatt_idx = gbms_msc_voltage_idx(profile, vbatt);
3904
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07003905 batt_prlog(batt_prlog_level(log_level),
3906 "MSC_DSG vbatt_idx:%d->%d vt=%d fv_uv=%d vb=%d ib=%d cv_cnt=%d ov_cnt=%d\n",
3907 batt_drv->vbatt_idx, vbatt_idx, vtier, fv_uv, vbatt, ibatt,
3908 batt_drv->checked_cv_cnt, batt_drv->checked_ov_cnt);
3909
Ken Tsou8acade12020-07-09 03:17:35 +08003910 } else if (batt_drv->vbatt_idx == profile->volt_nb_limits - 1) {
3911 const int chg_type = batt_drv->chg_state.f.chg_type;
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07003912 const int vtier = profile->volt_limits[vbatt_idx];
3913 int log_level;
Ken Tsou8acade12020-07-09 03:17:35 +08003914
3915 /*
3916 * will not adjust charger voltage only in the configured
3917 * last tier.
3918 * NOTE: might not be the "real" last tier since can I have
3919 * tiers with max charge current == 0.
3920 * NOTE: should I use a voltage limit instead?
3921 */
3922
3923 if (chg_type == POWER_SUPPLY_CHARGE_TYPE_FAST) {
3924 msc_state = MSC_FAST;
Jack Wu61391a52021-10-19 16:39:28 +08003925 } else if (chg_type != POWER_SUPPLY_CHARGE_TYPE_TAPER) {
Ken Tsou8acade12020-07-09 03:17:35 +08003926 msc_state = MSC_TYPE;
3927 } else {
3928 msc_state = MSC_LAST;
3929 }
3930
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07003931 log_level = batt_prlog_level(batt_drv->msc_state != msc_state);
3932 if (log_level != BATT_PRLOG_ALWAYS && msc_state == MSC_LAST) {
3933
3934 if (batt_drv->last_log_cnt > 0)
3935 batt_drv->last_log_cnt--;
3936 if (batt_drv->last_log_cnt == 0) {
3937 batt_drv->last_log_cnt = BATT_PRLOG_LAST_LOG_COUNT;
3938 log_level = batt_prlog_level(true);
3939 }
3940 }
3941
3942 batt_prlog(log_level, "MSC_LAST vt=%d fv_uv=%d vb=%d ib=%d\n",
3943 vtier, fv_uv, vbatt, ibatt);
Ken Tsou8acade12020-07-09 03:17:35 +08003944
3945 } else {
3946 const int tier_idx = batt_drv->vbatt_idx;
3947 const int vtier = profile->volt_limits[vbatt_idx];
3948 const int switch_cnt = profile->cv_tier_switch_cnt;
3949 const int cc_next_max = GBMS_CCCM_LIMITS(profile, temp_idx,
3950 vbatt_idx + 1);
3951
3952 /* book elapsed time to previous tier & msc_irdrop_state */
3953 msc_state = msc_logic_irdrop(batt_drv,
3954 vbatt, ibatt, temp_idx,
3955 &vbatt_idx, &fv_uv,
3956 &update_interval);
3957
3958 if (msc_pm_hold(msc_state) == 1 && !batt_drv->hold_taper_ws) {
Ken Tsou5ecf2f42020-07-16 08:26:05 +08003959 __pm_stay_awake(batt_drv->taper_ws);
Ken Tsou8acade12020-07-09 03:17:35 +08003960 batt_drv->hold_taper_ws = true;
3961 }
3962
3963 mutex_lock(&batt_drv->stats_lock);
Prasanna Prapancham320dcfe2022-06-27 20:20:46 +00003964 gbms_chg_stats_tier(&batt_drv->ce_data.tier_stats[tier_idx],
Ken Tsou8acade12020-07-09 03:17:35 +08003965 batt_drv->msc_irdrop_state, elap);
3966 batt_drv->msc_irdrop_state = msc_state;
3967 mutex_unlock(&batt_drv->stats_lock);
3968
3969 /*
3970 * Basic multi step charging: switch to next tier when ibatt
3971 * is under next tier cc_max.
3972 */
3973 if (batt_drv->checked_cv_cnt > 0) {
3974 /* debounce period on tier switch */
Ken Tsou8acade12020-07-09 03:17:35 +08003975 batt_drv->checked_cv_cnt -= 1;
3976
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07003977 batt_prlog(batt_prlog_level(msc_state != MSC_FAST),
3978 "MSC_WAIT s:%d->%d vt=%d fv_uv=%d vb=%d ib=%d cv_cnt=%d ov_cnt=%d t_cnt=%d\n",
3979 msc_state, MSC_WAIT, vtier, fv_uv, vbatt, ibatt,
3980 batt_drv->checked_cv_cnt, batt_drv->checked_ov_cnt,
3981 batt_drv->checked_tier_switch_cnt);
Ken Tsou8acade12020-07-09 03:17:35 +08003982
3983 if (-ibatt > cc_next_max)
3984 batt_drv->checked_tier_switch_cnt = 0;
3985
AleX Pelosi80d78ec2021-05-19 22:55:44 -07003986 msc_state = MSC_WAIT;
Ken Tsou8acade12020-07-09 03:17:35 +08003987 } else if (-ibatt > cc_next_max) {
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07003988
Ken Tsou8acade12020-07-09 03:17:35 +08003989 /* current over next tier, reset tier switch count */
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07003990 batt_prlog(BATT_PRLOG_ALWAYS,
3991 "MSC_RSTC s:%d->%d vt=%d fv_uv=%d vb=%d ib=%d cc_next_max=%d t_cnt=%d->0\n",
3992 msc_state, MSC_RSTC, vtier, fv_uv, vbatt, ibatt, cc_next_max,
3993 batt_drv->checked_tier_switch_cnt);
AleX Pelosi80d78ec2021-05-19 22:55:44 -07003994
3995 batt_drv->checked_tier_switch_cnt = 0;
3996 msc_state = MSC_RSTC;
Ken Tsou8acade12020-07-09 03:17:35 +08003997 } else if (batt_drv->checked_tier_switch_cnt >= switch_cnt) {
3998 /* next tier, fv_uv detemined at MSC_SET */
Ken Tsou8acade12020-07-09 03:17:35 +08003999 vbatt_idx = batt_drv->vbatt_idx + 1;
4000
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07004001 batt_prlog(BATT_PRLOG_ALWAYS,
4002 "MSC_NEXT s:%d->%d tier vb=%d ib=%d vbatt_idx=%d->%d\n",
4003 msc_state, MSC_NEXT, vbatt, ibatt,
4004 batt_drv->vbatt_idx, vbatt_idx);
AleX Pelosi80d78ec2021-05-19 22:55:44 -07004005
4006 msc_state = MSC_NEXT;
Ken Tsou8acade12020-07-09 03:17:35 +08004007 } else {
4008 /* current under next tier, +1 on tier switch count */
Ken Tsou8acade12020-07-09 03:17:35 +08004009 batt_drv->checked_tier_switch_cnt++;
4010
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07004011 batt_prlog(BATT_PRLOG_ALWAYS,
4012 "MSC_NYET s:%d->%d vt=%d vb=%d ib=%d cc_next_max=%d t_cnt=%d\n",
4013 msc_state, MSC_NYET, vtier, vbatt, ibatt, cc_next_max,
4014 batt_drv->checked_tier_switch_cnt);
AleX Pelosi80d78ec2021-05-19 22:55:44 -07004015
4016 msc_state = MSC_NYET;
Ken Tsou8acade12020-07-09 03:17:35 +08004017 }
4018
4019 }
4020
4021 if (msc_pm_hold(msc_state) == 0 && batt_drv->hold_taper_ws) {
4022 batt_drv->hold_taper_ws = false;
Ken Tsou5ecf2f42020-07-16 08:26:05 +08004023 __pm_relax(batt_drv->taper_ws);
Ken Tsou8acade12020-07-09 03:17:35 +08004024 }
4025
4026 /* need a new fv_uv only on a new voltage tier. */
4027 if (vbatt_idx != batt_drv->vbatt_idx) {
4028 fv_uv = profile->volt_limits[vbatt_idx];
4029 batt_drv->checked_tier_switch_cnt = 0;
4030 batt_drv->checked_ov_cnt = 0;
4031 }
4032
Jenny Ho6f7ec522020-05-19 09:04:53 +08004033 /*
4034 * book elapsed time to previous tier & msc_state
Ken Tsou8acade12020-07-09 03:17:35 +08004035 * NOTE: temp_idx != -1 but batt_drv->msc_state could be -1
4036 */
4037 mutex_lock(&batt_drv->stats_lock);
Jenny Hoa36dd632020-08-11 17:47:45 +08004038 if (vbatt_idx != -1 && vbatt_idx < profile->volt_nb_limits) {
4039 int tier_idx = batt_chg_vbat2tier(batt_drv->vbatt_idx);
Ken Tsou8acade12020-07-09 03:17:35 +08004040
4041 /* this is the seed after the connect */
Jenny Hoa36dd632020-08-11 17:47:45 +08004042 if (tier_idx == -1) {
4043 tier_idx = batt_chg_vbat2tier(vbatt_idx);
Ken Tsou8acade12020-07-09 03:17:35 +08004044 elap = 0;
4045 }
4046
4047 batt_chg_stats_update(batt_drv, temp_idx, tier_idx,
4048 ibatt / 1000, temp,
4049 elap);
4050
4051 }
4052
4053 batt_drv->msc_state = msc_state;
4054 batt_drv->ce_data.last_update = now;
4055 mutex_unlock(&batt_drv->stats_lock);
4056
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07004057 changed = batt_drv->temp_idx != temp_idx ||
4058 batt_drv->vbatt_idx != vbatt_idx ||
4059 batt_drv->fv_uv != fv_uv;
4060 batt_prlog(batt_prlog_level(changed),
4061 "MSC_LOGIC temp_idx:%d->%d, vbatt_idx:%d->%d, fv=%d->%d, ui=%d->%d cv_cnt=%d ov_cnt=%d\n",
4062 batt_drv->temp_idx, temp_idx, batt_drv->vbatt_idx, vbatt_idx,
4063 batt_drv->fv_uv, fv_uv, batt_drv->cc_max, update_interval,
4064 batt_drv->checked_cv_cnt, batt_drv->checked_ov_cnt);
Ken Tsou8acade12020-07-09 03:17:35 +08004065
4066 /* next update */
4067 batt_drv->msc_update_interval = update_interval;
4068 batt_drv->vbatt_idx = vbatt_idx;
4069 batt_drv->temp_idx = temp_idx;
4070 batt_drv->cc_max = GBMS_CCCM_LIMITS(profile, temp_idx, vbatt_idx);
Jenny Ho002c6702021-10-19 16:19:16 +08004071 batt_drv->topoff = profile->topoff_limits[temp_idx];
Ken Tsou8acade12020-07-09 03:17:35 +08004072 batt_drv->fv_uv = fv_uv;
4073
4074 return 0;
4075}
4076
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07004077/* no ssoc_delta when in overheat */
4078static int ssoc_get_delta(struct batt_drv *batt_drv)
4079{
4080 const bool overheat = batt_drv->batt_health ==
4081 POWER_SUPPLY_HEALTH_OVERHEAT;
4082
4083 return overheat ? 0 : qnum_fromint(batt_drv->ssoc_state.ssoc_delta);
4084}
4085
Ken Tsou76ee23d2020-12-03 00:49:48 +08004086/* TODO: handle the whole state buck_enable state */
4087static void ssoc_change_state(struct batt_ssoc_state *ssoc_state, bool ben)
4088{
4089 const ktime_t now = get_boot_sec();
4090
4091 if (!ben) {
4092 ssoc_state->disconnect_time = now;
4093 } else if (ssoc_state->disconnect_time) {
4094 const u32 trickle_reset = ssoc_state->bd_trickle_reset_sec;
4095 const long long elap = now - ssoc_state->disconnect_time;
4096
4097 if (trickle_reset && elap > trickle_reset)
4098 ssoc_state->bd_trickle_cnt = 0;
4099
4100 pr_debug("MSC_BD: bd_trickle_cnt=%d dsc_time=%lld elap=%lld\n",
4101 ssoc_state->bd_trickle_cnt,
4102 ssoc_state->disconnect_time,
4103 elap);
4104
4105 ssoc_state->disconnect_time = 0;
4106 }
4107
4108 ssoc_state->buck_enabled = ben;
4109}
4110
Stephane Leecacee1f2021-09-22 11:51:38 -07004111static void bd_trickle_reset(struct batt_ssoc_state *ssoc_state,
4112 struct gbms_charging_event *ce_data)
Ken Tsou37471122021-07-23 22:39:57 +08004113{
4114 ssoc_state->bd_trickle_cnt = 0;
4115 ssoc_state->disconnect_time = 0;
Ken Tsou80f97342022-08-18 17:56:14 +08004116 ssoc_state->bd_trickle_full = false;
4117 ssoc_state->bd_trickle_eoc = false;
Stephane Leecacee1f2021-09-22 11:51:38 -07004118
4119 /* Set to false in cev_stats_init */
4120 ce_data->bd_clear_trickle = true;
Ken Tsou37471122021-07-23 22:39:57 +08004121}
4122
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07004123static void batt_prlog_din(union gbms_charger_state *chg_state, int log_level)
4124{
4125 batt_prlog(log_level,
4126 "MSC_DIN chg_state=%lx f=0x%x chg_s=%s chg_t=%s vchg=%d icl=%d\n",
4127 (unsigned long)chg_state->v,
4128 chg_state->f.flags,
4129 gbms_chg_status_s(chg_state->f.chg_status),
4130 gbms_chg_type_s(chg_state->f.chg_type),
4131 chg_state->f.vchrg,
4132 chg_state->f.icl);
4133}
4134
AleX Pelosic10eb8b2021-12-14 13:20:21 -08004135static void google_battery_dump_profile(const struct gbms_chg_profile *profile)
4136{
4137 char *buff;
4138
Jenny Ho171aea12022-10-05 16:57:29 +08004139 buff = kzalloc(GBMS_CHG_ALG_BUF_SZ, GFP_KERNEL);
AleX Pelosic10eb8b2021-12-14 13:20:21 -08004140 if (buff) {
Jenny Ho171aea12022-10-05 16:57:29 +08004141 gbms_dump_chg_profile(buff, GBMS_CHG_ALG_BUF_SZ, profile);
AleX Pelosic10eb8b2021-12-14 13:20:21 -08004142 pr_info("%s", buff);
4143 kfree(buff);
4144 }
4145}
4146
Jack Wue3ebc172021-11-11 16:20:34 +08004147/* cell fault: disconnect of one of the battery cells */
4148static bool batt_cell_fault_detect(struct batt_bpst *bpst_state)
4149{
4150 int bpst_sbd_status;
4151
4152 /*
4153 * fake bpst_sbd_status by "echo 1 > /d/bpst/bpst_sbd_status"
4154 * TODO: will implement the code from the algo in b/203019566
4155 */
4156 bpst_sbd_status = bpst_state->bpst_sbd_status;
Jack Wue3ebc172021-11-11 16:20:34 +08004157
Jack Wub0a68212021-11-22 22:33:00 +08004158 return !!bpst_sbd_status && !bpst_state->bpst_detect_disable;
Jack Wue3ebc172021-11-11 16:20:34 +08004159}
4160
Jack Wu85983332021-12-07 23:55:40 +08004161static int batt_bpst_detect_begin(struct batt_bpst *bpst_state)
4162{
Jack Wu466ce282022-04-29 22:28:14 +08004163 const int bpst_count_threshold = bpst_state->bpst_count_threshold;
Jack Wu85983332021-12-07 23:55:40 +08004164 u8 data;
4165 int ret;
4166
Jack Wub0a68212021-11-22 22:33:00 +08004167 if (bpst_state->bpst_detect_disable)
4168 return 0;
4169
Jack Wu85983332021-12-07 23:55:40 +08004170 ret = gbms_storage_read(GBMS_TAG_BPST, &data, sizeof(data));
4171 if (ret < 0)
4172 return -EINVAL;
4173
4174 if (data == 0xff) {
4175 data = 0;
4176 ret = gbms_storage_write(GBMS_TAG_BPST, &data, sizeof(data));
4177 if (ret < 0)
4178 return -EINVAL;
4179 }
4180 bpst_state->bpst_count = data;
Jack Wu466ce282022-04-29 22:28:14 +08004181 if (bpst_count_threshold > 0) {
4182 bpst_state->bpst_cell_fault = (data >= (u8)bpst_count_threshold);
4183 pr_debug("%s: MSC_BPST: single battery disconnect %d\n",
4184 __func__, bpst_state->bpst_cell_fault);
4185 }
Jack Wu85983332021-12-07 23:55:40 +08004186
Jack Wu485dbcc2022-05-04 20:16:19 +08004187 /* reset detection status */
4188 bpst_state->bpst_sbd_status = 0;
4189
Jack Wu85983332021-12-07 23:55:40 +08004190 pr_debug("%s: MSC_BPST: %d in connected\n", __func__, data);
4191 return 0;
4192}
4193
4194static int batt_bpst_detect_update(struct batt_drv *batt_drv)
4195{
4196 struct batt_bpst *bpst_state = &batt_drv->bpst_state;
4197 const u8 data = bpst_state->bpst_count + 1;
Jack Wu85983332021-12-07 23:55:40 +08004198 int ret;
4199
4200 if (data < 0xff) {
4201 ret = gbms_storage_write(GBMS_TAG_BPST, &data, sizeof(data));
4202 if (ret < 0)
4203 return -EINVAL;
4204 }
Jack Wu85983332021-12-07 23:55:40 +08004205
4206 pr_debug("%s: MSC_BPST: %d in disconnected\n", __func__, data);
4207 return 0;
4208}
4209
Jack Wu867a8432021-11-23 22:28:28 +08004210static int batt_bpst_reset(struct batt_bpst *bpst_state)
4211{
4212 if (bpst_state->bpst_enable) {
4213 u8 data = 0;
4214
4215 return gbms_storage_write(GBMS_TAG_BPST, &data, sizeof(data));
4216 }
4217
4218 return 0;
4219}
4220
Jack Wub0a68212021-11-22 22:33:00 +08004221#define BATT_BPST_DEFAULT_CHG_RATE 100
Jack Wue3ebc172021-11-11 16:20:34 +08004222static int batt_init_bpst_profile(struct batt_drv *batt_drv)
4223{
4224 struct batt_bpst *bpst_state = &batt_drv->bpst_state;
4225 struct device_node *node = batt_drv->device->of_node;
Jack Wub0a68212021-11-22 22:33:00 +08004226 int ret;
Jack Wue3ebc172021-11-11 16:20:34 +08004227
Jack Wub19fad12022-04-27 14:47:04 +08004228 /* set cell_fault initial status */
4229 bpst_state->bpst_cell_fault = false;
4230
Jack Wue3ebc172021-11-11 16:20:34 +08004231 bpst_state->bpst_enable = of_property_read_bool(node, "google,bpst-enable");
4232 if (!bpst_state->bpst_enable)
4233 return -EINVAL;
4234
Jack Wub0a68212021-11-22 22:33:00 +08004235 ret = of_property_read_u32(node, "google,bpst-chg-rate", &bpst_state->bpst_chg_rate);
4236 if (ret < 0)
4237 bpst_state->bpst_chg_rate = BATT_BPST_DEFAULT_CHG_RATE;
4238
Jack Wue3ebc172021-11-11 16:20:34 +08004239 return 0;
4240}
4241
AleX Pelosic1cd4752022-05-04 02:15:02 -07004242/* call holding mutex_lock(&batt_drv->chg_lock); */
AleX Pelosi8e7fd812019-08-16 10:41:46 -07004243static int batt_chg_logic(struct batt_drv *batt_drv)
Ken Tsou8acade12020-07-09 03:17:35 +08004244{
AleX Pelosi65ca2222022-03-28 14:07:49 -07004245 int rc, err = 0;
AleX Pelosi6267de82021-06-25 00:01:29 -07004246 bool jeita_stop;
Ken Tsou8acade12020-07-09 03:17:35 +08004247 bool changed = false;
AleX Pelosi8e7fd812019-08-16 10:41:46 -07004248 const bool disable_votes = batt_drv->disable_votes;
Jenny Ho1c2ec2b2021-12-10 17:43:14 +08004249 const int ssoc = ssoc_get_capacity(&batt_drv->ssoc_state);
Ken Tsou8acade12020-07-09 03:17:35 +08004250 union gbms_charger_state *chg_state = &batt_drv->chg_state;
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07004251 int log_vote_level = BATT_PRLOG_DEBUG;
Ken Tsou8acade12020-07-09 03:17:35 +08004252
4253 if (!batt_drv->chg_profile.cccm_limits)
4254 return -EINVAL;
4255
Ken Tsou5ecf2f42020-07-16 08:26:05 +08004256 __pm_stay_awake(batt_drv->msc_ws);
Ken Tsou8acade12020-07-09 03:17:35 +08004257
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07004258 batt_prlog_din(chg_state, BATT_PRLOG_ALWAYS);
Ken Tsou8acade12020-07-09 03:17:35 +08004259
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07004260 /* disconnect! */
AleX Pelosi628d2fd2021-10-24 15:21:08 -07004261 if (chg_state_is_disconnected(chg_state)) {
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07004262 const qnum_t ssoc_delta = ssoc_get_delta(batt_drv);
Ken Tsou8acade12020-07-09 03:17:35 +08004263
4264 if (batt_drv->ssoc_state.buck_enabled == 0)
4265 goto msc_logic_exit;
4266
Jack Wu485dbcc2022-05-04 20:16:19 +08004267 /* update bpst */
4268 mutex_lock(&batt_drv->bpst_state.lock);
4269 if (batt_drv->bpst_state.bpst_enable) {
4270 bool cell_fault_detect = batt_cell_fault_detect(&batt_drv->bpst_state);
4271
4272 if (cell_fault_detect) {
4273 rc = batt_bpst_detect_update(batt_drv);
4274 pr_info("MSC_BPST: cell_fault_detect in disconnected(%d)\n", rc);
4275 }
4276 }
4277 mutex_unlock(&batt_drv->bpst_state.lock);
4278
Ken Tsou8acade12020-07-09 03:17:35 +08004279 /* here on: disconnect */
Jenny Ho9b191512022-08-30 09:26:15 +00004280 batt_log_csi_ttf_info(batt_drv);
Stephane Leecbb07ee2020-09-14 12:24:09 -07004281 batt_chg_stats_pub(batt_drv, "disconnect", false, false);
Ken Tsou8acade12020-07-09 03:17:35 +08004282
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07004283 /* change curve before changing the state. */
4284 ssoc_change_curve(&batt_drv->ssoc_state, ssoc_delta,
4285 SSOC_UIC_TYPE_DSG);
4286
Stephane Leeaaf28cd2020-08-26 11:26:06 -07004287 batt_drv->chg_health.rest_deadline = 0;
Ken Tsou8acade12020-07-09 03:17:35 +08004288 batt_reset_chg_drv_state(batt_drv);
4289 batt_update_cycle_count(batt_drv);
4290 batt_rl_reset(batt_drv);
4291
AleX Pelosib3a1a552022-05-03 17:00:11 -07004292 /* trigger google_capacity learning. */
Ken Tsou8acade12020-07-09 03:17:35 +08004293 err = GPSY_SET_PROP(batt_drv->fg_psy,
AleX Pelosid2ca4072020-09-03 22:07:27 -07004294 GBMS_PROP_BATT_CE_CTRL,
Ken Tsou8acade12020-07-09 03:17:35 +08004295 false);
4296 if (err < 0)
4297 pr_err("Cannot set the BATT_CE_CTRL.\n");
4298
Ken Tsou76ee23d2020-12-03 00:49:48 +08004299 /* TODO: move earlier and include the change to the curve */
4300 ssoc_change_state(&batt_drv->ssoc_state, 0);
Ken Tsou8acade12020-07-09 03:17:35 +08004301 changed = true;
4302
AleX Pelosic1cd4752022-05-04 02:15:02 -07004303 /* google_resistance: update and stop accumulation. */
AleX Pelosib3a1a552022-05-03 17:00:11 -07004304 batt_res_work(batt_drv);
AleX Pelosia3f22de2022-05-03 23:42:24 -07004305 batt_res_state_set(&batt_drv->health_data.bhi_data.res_state, false);
AleX Pelosib3a1a552022-05-03 17:00:11 -07004306
AleX Pelosic1cd4752022-05-04 02:15:02 -07004307 batt_bhi_stats_update_all(batt_drv);
Ken Tsou8acade12020-07-09 03:17:35 +08004308 goto msc_logic_done;
4309 }
4310
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07004311 /*
4312 * here when connected to power supply
4313 * The following block one only on start.
4314 */
Ken Tsou8acade12020-07-09 03:17:35 +08004315 if (batt_drv->ssoc_state.buck_enabled <= 0) {
AleX Pelosia3f22de2022-05-03 23:42:24 -07004316 struct bhi_data *bhi_data = &batt_drv->health_data.bhi_data;
AleX Pelosic10eb8b2021-12-14 13:20:21 -08004317 struct device_node *node = batt_drv->device->of_node;
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07004318 const qnum_t ssoc_delta = ssoc_get_delta(batt_drv);
AleX Pelosic10eb8b2021-12-14 13:20:21 -08004319 u32 capacity;
Ken Tsou8acade12020-07-09 03:17:35 +08004320
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07004321 /*
4322 * FIX: BatteryDefenderUI needs use a different curve because
4323 * bd->bd_voltage_trigger needs now to be 100%. In alternative
4324 * we use the regular charge curve and show that charging stop
4325 * BEFORE reaching 100%. This is similar to what we do if BD
4326 * trigger over bd->bd_voltage_trigger BUT under SSOC=100%
4327 */
4328 ssoc_change_curve(&batt_drv->ssoc_state, ssoc_delta,
4329 SSOC_UIC_TYPE_CHG);
Ken Tsou8acade12020-07-09 03:17:35 +08004330
AleX Pelosib3a1a552022-05-03 17:00:11 -07004331 /* google_resistance is calculated while charging */
AleX Pelosia3f22de2022-05-03 23:42:24 -07004332 if (bhi_data->res_state.estimate_filter)
4333 batt_res_state_set(&bhi_data->res_state, true);
Ken Tsou8acade12020-07-09 03:17:35 +08004334
AleX Pelosic10eb8b2021-12-14 13:20:21 -08004335 capacity = aacr_get_capacity(batt_drv);
4336 if (capacity != batt_drv->chg_profile.capacity_ma) {
4337 gbms_init_chg_table(&batt_drv->chg_profile, node, capacity);
4338 google_battery_dump_profile(&batt_drv->chg_profile);
4339 }
4340
Ken Tsou8acade12020-07-09 03:17:35 +08004341 batt_chg_stats_start(batt_drv);
AleX Pelosic10eb8b2021-12-14 13:20:21 -08004342
4343 err = GPSY_SET_PROP(batt_drv->fg_psy, GBMS_PROP_BATT_CE_CTRL, true);
Ken Tsou8acade12020-07-09 03:17:35 +08004344 if (err < 0)
AleX Pelosib3a1a552022-05-03 17:00:11 -07004345 pr_err("Cannot set the BATT_CE_CTRL (%d)\n", err);
Ken Tsou8acade12020-07-09 03:17:35 +08004346
4347 /* released in battery_work() */
Ken Tsou5ecf2f42020-07-16 08:26:05 +08004348 __pm_stay_awake(batt_drv->poll_ws);
Ken Tsou8acade12020-07-09 03:17:35 +08004349 batt_drv->batt_fast_update_cnt = BATT_WORK_FAST_RETRY_CNT;
4350 mod_delayed_work(system_wq, &batt_drv->batt_work,
Jenny Hoace37002021-01-11 16:33:30 +08004351 msecs_to_jiffies(BATT_WORK_FAST_RETRY_MS));
Ken Tsou8acade12020-07-09 03:17:35 +08004352
Ken Tsou76ee23d2020-12-03 00:49:48 +08004353 /* TODO: move earlier and include the change to the curve */
4354 ssoc_change_state(&batt_drv->ssoc_state, 1);
Ken Tsou8acade12020-07-09 03:17:35 +08004355 changed = true;
Jack Wu85983332021-12-07 23:55:40 +08004356
4357 /* start bpst detect */
4358 mutex_lock(&batt_drv->bpst_state.lock);
4359 if (batt_drv->bpst_state.bpst_enable) {
4360 rc = batt_bpst_detect_begin(&batt_drv->bpst_state);
4361 if (rc < 0)
4362 pr_err("MSC_BPST: Cannot start bpst detect\n");
4363 }
4364 mutex_unlock(&batt_drv->bpst_state.lock);
Jenny Ho1ef60002022-07-24 18:26:51 +00004365
4366 /* reset ttf tier */
4367 ttf_tier_reset(&batt_drv->ttf_stats);
Ken Tsou8acade12020-07-09 03:17:35 +08004368 }
4369
4370 /*
4371 * enter RL in DISCHARGE on charger DONE and enter RL in RECHARGE on
4372 * battery FULL (i.e. SSOC==100%). charger DONE forces the discharge
4373 * curve while RECHARGE will not modify the current curve.
4374 */
4375 if ((batt_drv->chg_state.f.flags & GBMS_CS_FLAG_DONE) != 0) {
4376 changed = batt_rl_enter(&batt_drv->ssoc_state,
4377 BATT_RL_STATUS_DISCHARGE);
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07004378
Ken Tsou8acade12020-07-09 03:17:35 +08004379 batt_drv->chg_done = true;
Ken Tsou80f97342022-08-18 17:56:14 +08004380 batt_drv->ssoc_state.bd_trickle_eoc = true;
Ken Tsou8acade12020-07-09 03:17:35 +08004381 } else if (batt_drv->batt_full) {
4382 changed = batt_rl_enter(&batt_drv->ssoc_state,
4383 BATT_RL_STATUS_RECHARGE);
Ken Tsou80f97342022-08-18 17:56:14 +08004384 batt_drv->ssoc_state.bd_trickle_full = true;
Ken Tsou8acade12020-07-09 03:17:35 +08004385 }
4386
AleX Pelosi8e7fd812019-08-16 10:41:46 -07004387 err = msc_logic(batt_drv);
Ken Tsou8acade12020-07-09 03:17:35 +08004388 if (err < 0) {
4389 /* NOTE: google charger will poll again. */
4390 batt_drv->msc_update_interval = -1;
4391
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07004392 batt_prlog(BATT_PRLOG_ALWAYS,
4393 "MSC_DOUT ERROR=%d fv_uv=%d cc_max=%d update_interval=%d\n",
4394 err, batt_drv->fv_uv, batt_drv->cc_max,
4395 batt_drv->msc_update_interval);
Ken Tsou8acade12020-07-09 03:17:35 +08004396
4397 goto msc_logic_exit;
4398 }
4399
Jenny Ho6f7ec522020-05-19 09:04:53 +08004400 /*
4401 * TODO: might need to behave in a different way when health based
4402 * charging is active
AleX Pelosi8e7fd812019-08-16 10:41:46 -07004403 */
Jenny Ho6f7ec522020-05-19 09:04:53 +08004404 changed |= msc_logic_health(batt_drv);
Jenny Ho1c2ec2b2021-12-10 17:43:14 +08004405 if (CHG_HEALTH_REST_IS_AON(&batt_drv->chg_health, ssoc)) {
4406 batt_drv->msc_state = MSC_HEALTH_ALWAYS_ON;
4407 batt_drv->fv_uv = 0;
4408 } else if (CHG_HEALTH_REST_IS_ACTIVE(&batt_drv->chg_health)) {
AleX Pelosi0c88ff32020-04-02 01:04:51 -07004409 batt_drv->msc_state = MSC_HEALTH;
Jenny Hoe31ef812020-05-19 09:04:53 +08004410 /* make sure using rest_fv_uv when HEALTH_ACTIVE */
4411 batt_drv->fv_uv = 0;
Jenny Hof189bfb2021-05-10 16:57:55 +08004412 } else if (CHG_HEALTH_REST_IS_PAUSE(&batt_drv->chg_health)) {
4413 batt_drv->msc_state = MSC_HEALTH_PAUSE;
Jenny Hoe31ef812020-05-19 09:04:53 +08004414 }
AleX Pelosi8e7fd812019-08-16 10:41:46 -07004415
Ken Tsou8acade12020-07-09 03:17:35 +08004416msc_logic_done:
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07004417
Ken Tsou8acade12020-07-09 03:17:35 +08004418 /* set ->cc_max = 0 on RL and SW_JEITA, no vote on interval in RL_DSG */
4419 if (batt_drv->ssoc_state.rl_status == BATT_RL_STATUS_DISCHARGE) {
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07004420 log_vote_level = batt_prlog_level(batt_drv->cc_max != 0);
Ken Tsou8acade12020-07-09 03:17:35 +08004421 batt_drv->msc_update_interval = -1;
4422 batt_drv->cc_max = 0;
4423 }
AleX Pelosi8e7fd812019-08-16 10:41:46 -07004424
AleX Pelosi6267de82021-06-25 00:01:29 -07004425 jeita_stop = batt_drv->jeita_stop_charging == 1;
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07004426 if (jeita_stop) {
4427 log_vote_level = batt_prlog_level(batt_drv->cc_max != 0);
Ken Tsou8acade12020-07-09 03:17:35 +08004428 batt_drv->cc_max = 0;
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07004429 }
Ken Tsou8acade12020-07-09 03:17:35 +08004430
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07004431 if (changed)
4432 log_vote_level = BATT_PRLOG_ALWAYS;
4433 batt_prlog(log_vote_level,
4434 "%s msc_state=%d cv_cnt=%d ov_cnt=%d rl_sts=%d temp_idx:%d, vbatt_idx:%d fv_uv=%d cc_max=%d update_interval=%d\n",
4435 (disable_votes) ? "MSC_DOUT" : "MSC_VOTE",
4436 batt_drv->msc_state,
4437 batt_drv->checked_cv_cnt, batt_drv->checked_ov_cnt,
4438 batt_drv->ssoc_state.rl_status,
4439 batt_drv->temp_idx, batt_drv->vbatt_idx,
4440 batt_drv->fv_uv, batt_drv->cc_max,
4441 batt_drv->msc_update_interval);
Ken Tsou8acade12020-07-09 03:17:35 +08004442
4443 /*
4444 * google_charger has voted(<=0) on msc_interval_votable and the
4445 * votes on fcc and fv_uv will not be applied until google_charger
4446 * votes a non-zero value.
4447 *
4448 * SW_JEITA: ->jeita_stop_charging != 0
4449 * . ->msc_update_interval = -1 , fv_uv = -1 and ->cc_max = 0
4450 * . vote(0) on ->fcc_votable with SW_JEITA_VOTER
4451 * BATT_RL: rl_status == BATT_RL_STATUS_DISCHARGE
4452 * . ->msc_update_interval = -1 , fv_uv = -1 and ->cc_max = 0
4453 * . vote(0) on ->fcc_votable with SW_JEITA_VOTER
4454 *
4455 * Votes for MSC_LOGIC_VOTER will be all disabled.
4456 */
4457 if (!batt_drv->fv_votable)
Ken Tsoua15d1fa2022-01-24 14:42:27 +08004458 batt_drv->fv_votable =
4459 gvotable_election_get_handle(VOTABLE_MSC_FV);
AleX Pelosi8e7fd812019-08-16 10:41:46 -07004460 if (batt_drv->fv_votable) {
4461 const int rest_fv_uv = batt_drv->chg_health.rest_fv_uv;
4462
Ken Tsoua15d1fa2022-01-24 14:42:27 +08004463 gvotable_cast_int_vote(batt_drv->fv_votable,
4464 MSC_LOGIC_VOTER, batt_drv->fv_uv,
4465 !disable_votes && (batt_drv->fv_uv > 0));
Ken Tsou8acade12020-07-09 03:17:35 +08004466
Ken Tsoua15d1fa2022-01-24 14:42:27 +08004467 gvotable_cast_int_vote(batt_drv->fv_votable,
4468 MSC_HEALTH_VOTER, rest_fv_uv,
4469 !disable_votes && (rest_fv_uv > 0));
AleX Pelosi8e7fd812019-08-16 10:41:46 -07004470 }
4471
Ken Tsou8acade12020-07-09 03:17:35 +08004472 if (!batt_drv->fcc_votable)
Ken Tsoua15d1fa2022-01-24 14:42:27 +08004473 batt_drv->fcc_votable =
4474 gvotable_election_get_handle(VOTABLE_MSC_FCC);
Ken Tsou8acade12020-07-09 03:17:35 +08004475 if (batt_drv->fcc_votable) {
4476 enum batt_rl_status rl_status = batt_drv->ssoc_state.rl_status;
AleX Pelosi8e7fd812019-08-16 10:41:46 -07004477 const int rest_cc_max = batt_drv->chg_health.rest_cc_max;
Jack Wu466ce282022-04-29 22:28:14 +08004478 struct batt_bpst *bpst_state = &batt_drv->bpst_state;
Ken Tsou8acade12020-07-09 03:17:35 +08004479
4480 /* while in RL => ->cc_max != -1 && ->fv_uv != -1 */
Ken Tsoua15d1fa2022-01-24 14:42:27 +08004481 gvotable_cast_int_vote(batt_drv->fcc_votable, RL_STATE_VOTER, 0,
4482 !disable_votes &&
4483 (rl_status == BATT_RL_STATUS_DISCHARGE));
Ken Tsou8acade12020-07-09 03:17:35 +08004484
4485 /* jeita_stop_charging != 0 => ->fv_uv = -1 && cc_max == -1 */
Ken Tsoua15d1fa2022-01-24 14:42:27 +08004486 gvotable_cast_int_vote(batt_drv->fcc_votable, SW_JEITA_VOTER, 0,
4487 !disable_votes && jeita_stop);
Ken Tsou8acade12020-07-09 03:17:35 +08004488
AleX Pelosi8e7fd812019-08-16 10:41:46 -07004489 /* health based charging */
Ken Tsoua15d1fa2022-01-24 14:42:27 +08004490 gvotable_cast_int_vote(batt_drv->fcc_votable,
4491 MSC_HEALTH_VOTER, rest_cc_max,
4492 !disable_votes && (rest_cc_max != -1));
AleX Pelosi8e7fd812019-08-16 10:41:46 -07004493
Ken Tsoua15d1fa2022-01-24 14:42:27 +08004494 gvotable_cast_int_vote(batt_drv->fcc_votable,
4495 MSC_LOGIC_VOTER, batt_drv->cc_max,
4496 !disable_votes &&
4497 (batt_drv->cc_max != -1));
Jack Wub0a68212021-11-22 22:33:00 +08004498
4499 /* bpst detection */
Jack Wu466ce282022-04-29 22:28:14 +08004500 if (bpst_state->bpst_detect_disable || bpst_state->bpst_cell_fault) {
Jack Wub0a68212021-11-22 22:33:00 +08004501 const int chg_rate = batt_drv->bpst_state.bpst_chg_rate;
4502 const int bpst_cc_max = (batt_drv->cc_max == -1) ? batt_drv->cc_max
4503 : ((batt_drv->cc_max * chg_rate) / 100);
4504
4505 gvotable_cast_int_vote(batt_drv->fcc_votable,
4506 BPST_DETECT_VOTER, bpst_cc_max,
4507 !disable_votes &&
4508 (bpst_cc_max != -1));
4509 }
Ken Tsou8acade12020-07-09 03:17:35 +08004510 }
4511
Jenny Ho33787472022-07-04 05:18:03 +00004512 /* Fan level can be updated only during power transfer */
4513 if (batt_drv->fan_level_votable) {
4514 int level = fan_calculate_level(batt_drv);
4515
4516 gvotable_cast_int_vote(batt_drv->fan_level_votable,
4517 "MSC_BATT", level, true);
4518 pr_debug("MSC_FAN_LVL: level=%d\n", level);
4519 }
4520
Ken Tsou8acade12020-07-09 03:17:35 +08004521 if (!batt_drv->msc_interval_votable)
4522 batt_drv->msc_interval_votable =
Ken Tsoua15d1fa2022-01-24 14:42:27 +08004523 gvotable_election_get_handle(VOTABLE_MSC_INTERVAL);
Ken Tsou8acade12020-07-09 03:17:35 +08004524 if (batt_drv->msc_interval_votable)
Ken Tsoua15d1fa2022-01-24 14:42:27 +08004525 gvotable_cast_int_vote(batt_drv->msc_interval_votable,
4526 MSC_LOGIC_VOTER,
4527 batt_drv->msc_update_interval,
4528 !disable_votes &&
4529 (batt_drv->msc_update_interval != -1));
Ken Tsou8acade12020-07-09 03:17:35 +08004530
Jenny Ho915dc482022-03-21 09:13:57 +08004531 batt_update_csi_info(batt_drv);
Jenny Ho4f2ad702022-03-01 10:56:19 +08004532
Ken Tsou8acade12020-07-09 03:17:35 +08004533msc_logic_exit:
4534
4535 if (changed) {
4536 dump_ssoc_state(&batt_drv->ssoc_state, batt_drv->ssoc_log);
4537 if (batt_drv->psy)
4538 power_supply_changed(batt_drv->psy);
4539 }
4540
Ken Tsou5ecf2f42020-07-16 08:26:05 +08004541 __pm_relax(batt_drv->msc_ws);
Ken Tsou8acade12020-07-09 03:17:35 +08004542 return err;
4543}
4544
Jenny Hoa1ad4072022-01-26 10:46:22 +08004545static struct device_node *batt_id_node(struct batt_drv *batt_drv)
4546{
4547 struct device_node *config_node = batt_drv->device->of_node;
4548 struct device_node *child_node;
4549 int ret = 0;
4550 u32 batt_id, gbatt_id;
4551
4552 ret = gbms_storage_read(GBMS_TAG_BRID, &batt_id, sizeof(batt_id));
4553 if (ret < 0) {
4554 pr_warn("Failed to get batt_id (%d)\n", ret);
4555 return config_node;
4556 }
4557
4558 for_each_child_of_node(config_node, child_node) {
4559 ret = of_property_read_u32(child_node, "google,batt-id",
4560 &gbatt_id);
4561 if (ret != 0)
4562 continue;
4563
4564 if (batt_id == gbatt_id)
4565 return child_node;
4566 }
4567
4568 return config_node;
4569}
4570
Ken Tsou8acade12020-07-09 03:17:35 +08004571/* charge profile not in battery */
4572static int batt_init_chg_profile(struct batt_drv *batt_drv)
4573{
Ken Tsou8acade12020-07-09 03:17:35 +08004574 struct gbms_chg_profile *profile = &batt_drv->chg_profile;
AleX Pelosic10eb8b2021-12-14 13:20:21 -08004575 struct device_node *node = batt_drv->device->of_node;
Ken Tsou8acade12020-07-09 03:17:35 +08004576 int ret = 0;
4577
4578 /* handle retry */
4579 if (!profile->cccm_limits) {
4580 ret = gbms_init_chg_profile(profile, node);
4581 if (ret < 0)
4582 return -EINVAL;
4583 }
4584
AleX Pelosic10eb8b2021-12-14 13:20:21 -08004585 /* this is in mAh */
Ken Tsou8acade12020-07-09 03:17:35 +08004586 ret = of_property_read_u32(node, "google,chg-battery-capacity",
4587 &batt_drv->battery_capacity);
4588 if (ret < 0)
4589 pr_warn("read chg-battery-capacity from gauge\n");
4590
4591 /*
4592 * use battery FULL design when is not specified in DT. When battery is
4593 * not present use default capacity from DT (if present) or disable
4594 * charging altogether.
4595 */
4596 if (batt_drv->battery_capacity == 0) {
4597 u32 fc = 0;
4598 struct power_supply *fg_psy = batt_drv->fg_psy;
4599
4600 if (batt_drv->batt_present) {
AleX Pelosic10eb8b2021-12-14 13:20:21 -08004601 fc = GPSY_GET_PROP(fg_psy, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN);
Ken Tsou8acade12020-07-09 03:17:35 +08004602 if (fc == -EAGAIN)
4603 return -EPROBE_DEFER;
4604 if (fc > 0) {
4605 pr_info("successfully read charging profile:\n");
4606 /* convert uA to mAh*/
4607 batt_drv->battery_capacity = fc / 1000;
4608 }
4609
4610 }
4611
4612 if (batt_drv->battery_capacity == 0) {
4613 struct device_node *node = batt_drv->device->of_node;
4614
4615 ret = of_property_read_u32(node,
4616 "google,chg-battery-default-capacity",
4617 &batt_drv->battery_capacity);
4618 if (ret < 0)
4619 pr_warn("battery not present, no default capacity, zero charge table\n");
4620 else
AleX Pelosic10eb8b2021-12-14 13:20:21 -08004621 pr_warn("battery not present, using default capacity\n");
Ken Tsou8acade12020-07-09 03:17:35 +08004622 }
4623 }
4624
Jenny Hoa1ad4072022-01-26 10:46:22 +08004625 /* TODO: dump the AACR table if supported */
4626 ret = gbms_read_aacr_limits(profile, batt_id_node(batt_drv));
4627 if (ret == 0)
4628 pr_info("AACR: supported\n");
4629
AleX Pelosic10eb8b2021-12-14 13:20:21 -08004630 /* aacr tables enable AACR by default UNLESS explicitly disabled */
4631 ret = of_property_read_bool(node, "google,aacr-disable");
4632 if (!ret && profile->aacr_nb_limits)
4633 batt_drv->aacr_state = BATT_AACR_ENABLED;
4634
Jenny Ho24fcef62022-07-14 10:45:04 +00004635 ret = of_property_read_u32(node, "google,aacr-algo", &batt_drv->aacr_algo);
4636 if (ret < 0)
4637 batt_drv->aacr_algo = BATT_AACR_ALGO_DEFAULT;
4638
Ken Tsou8acade12020-07-09 03:17:35 +08004639 /* NOTE: with NG charger tolerance is applied from "charger" */
AleX Pelosic10eb8b2021-12-14 13:20:21 -08004640 gbms_init_chg_table(profile, node, aacr_get_capacity(batt_drv));
Ken Tsou8acade12020-07-09 03:17:35 +08004641
4642 return 0;
4643}
4644
4645/* ------------------------------------------------------------------------- */
4646
4647/* call holding mutex_unlock(&ccd->lock); */
4648static int batt_cycle_count_store(struct gbatt_ccbin_data *ccd)
4649{
4650 int ret;
4651
4652 ret = gbms_storage_write(GBMS_TAG_BCNT, ccd->count, sizeof(ccd->count));
4653 if (ret < 0 && ret != -ENOENT) {
4654 pr_err("failed to set bin_counts ret=%d\n", ret);
4655 return ret;
4656 }
4657
4658 return 0;
4659}
4660
4661/* call holding mutex_unlock(&ccd->lock); */
4662static int batt_cycle_count_load(struct gbatt_ccbin_data *ccd)
4663{
Jenny Ho19d3ca42022-04-07 11:14:44 +08004664 int ret, i;
Ken Tsou8acade12020-07-09 03:17:35 +08004665
4666 ret = gbms_storage_read(GBMS_TAG_BCNT, ccd->count, sizeof(ccd->count));
4667 if (ret < 0 && ret != -ENOENT) {
4668 pr_err("failed to get bin_counts ret=%d\n", ret);
4669 return ret;
4670 }
4671
Jenny Ho19d3ca42022-04-07 11:14:44 +08004672 for (i = 0; i < GBMS_CCBIN_BUCKET_COUNT; i++)
4673 if (ccd->count[i] == 0xFFFF)
4674 ccd->count[i] = 0;
4675
Ken Tsou8acade12020-07-09 03:17:35 +08004676 ccd->prev_soc = -1;
4677 return 0;
4678}
4679
4680/* update only when SSOC is increasing, not need to check charging */
4681static void batt_cycle_count_update(struct batt_drv *batt_drv, int soc)
4682{
4683 struct gbatt_ccbin_data *ccd = &batt_drv->cc_data;
4684
4685 if (soc < 0 || soc > 100)
4686 return;
4687
4688 mutex_lock(&ccd->lock);
4689
4690 if (ccd->prev_soc != -1 && soc > ccd->prev_soc) {
4691 int bucket, cnt;
4692
4693 for (cnt = soc ; cnt > ccd->prev_soc ; cnt--) {
4694 /* cnt decremented by 1 for bucket symmetry */
4695 bucket = (cnt - 1) * GBMS_CCBIN_BUCKET_COUNT / 100;
4696 ccd->count[bucket]++;
4697 }
4698
4699 /* NOTE: could store on FULL or disconnect instead */
4700 (void)batt_cycle_count_store(ccd);
4701 }
4702
4703 ccd->prev_soc = soc;
4704
4705 mutex_unlock(&ccd->lock);
4706}
4707
4708/* ------------------------------------------------------------------------- */
4709
AleX Pelosi57c74e22020-02-27 19:29:27 -08004710#ifdef CONFIG_DEBUG_FS
4711
AleX Pelosi664e21e2020-09-01 11:29:51 -07004712static ssize_t cycle_counts_store(struct device *dev,
4713 struct device_attribute *attr,
4714 const char *buf, size_t count)
Ken Tsou8acade12020-07-09 03:17:35 +08004715{
AleX Pelosi3adb3372020-09-03 00:20:07 -07004716 struct power_supply *psy = container_of(dev, struct power_supply, dev);
4717 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
Ken Tsou8acade12020-07-09 03:17:35 +08004718 int ret;
4719
Ken Tsou8acade12020-07-09 03:17:35 +08004720 mutex_lock(&batt_drv->cc_data.lock);
4721
4722 ret = gbms_cycle_count_sscan(batt_drv->cc_data.count, buf);
4723 if (ret == 0) {
4724 ret = batt_cycle_count_store(&batt_drv->cc_data);
4725 if (ret < 0)
4726 pr_err("cannot store bin count ret=%d\n", ret);
4727 }
4728
4729 if (ret == 0)
4730 ret = count;
4731
4732 mutex_unlock(&batt_drv->cc_data.lock);
4733
4734 return ret;
4735}
4736
AleX Pelosi664e21e2020-09-01 11:29:51 -07004737static ssize_t cycle_counts_show(struct device *dev,
4738 struct device_attribute *attr,
4739 char *buff)
4740{
AleX Pelosi3adb3372020-09-03 00:20:07 -07004741 struct power_supply *psy = container_of(dev, struct power_supply, dev);
4742 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
AleX Pelosi664e21e2020-09-01 11:29:51 -07004743 int len;
4744
4745 mutex_lock(&batt_drv->cc_data.lock);
4746 len = gbms_cycle_count_cstr(buff, PAGE_SIZE, batt_drv->cc_data.count);
4747 mutex_unlock(&batt_drv->cc_data.lock);
4748
4749 return len;
4750}
4751
4752static const DEVICE_ATTR_RW(cycle_counts);
Ken Tsou8acade12020-07-09 03:17:35 +08004753
Jenny Hodaaa8032021-02-04 15:05:24 +08004754static ssize_t resistance_show(struct device *dev,
4755 struct device_attribute *attr,
4756 char *buff)
4757{
AleX Pelosib3a1a552022-05-03 17:00:11 -07004758 struct power_supply *psy = container_of(dev, struct power_supply, dev);
4759 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
Jenny Hocc17da12022-07-12 01:41:43 +00004760 int value = -1;
Jenny Hodaaa8032021-02-04 15:05:24 +08004761
Jenny Hocc17da12022-07-12 01:41:43 +00004762 if (batt_drv->fg_psy)
4763 value = GPSY_GET_PROP(batt_drv->fg_psy, GBMS_PROP_RESISTANCE);
4764
AleX Pelosib3a1a552022-05-03 17:00:11 -07004765 return scnprintf(buff, PAGE_SIZE, "%d\n", value);
Jenny Hodaaa8032021-02-04 15:05:24 +08004766}
4767
4768static const DEVICE_ATTR_RO(resistance);
4769
AleX Pelosi3adb3372020-09-03 00:20:07 -07004770static ssize_t resistance_avg_show(struct device *dev,
4771 struct device_attribute *attr,
4772 char *buff)
4773{
4774 struct power_supply *psy = container_of(dev, struct power_supply, dev);
4775 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
AleX Pelosi3adb3372020-09-03 00:20:07 -07004776
AleX Pelosib3a1a552022-05-03 17:00:11 -07004777 /* resistance_avg is scaled */
4778 return scnprintf(buff, PAGE_SIZE, "%d\n",
AleX Pelosia3f22de2022-05-03 23:42:24 -07004779 batt_ravg_value(&batt_drv->health_data.bhi_data.res_state));
AleX Pelosi3adb3372020-09-03 00:20:07 -07004780}
4781
4782static const DEVICE_ATTR_RO(resistance_avg);
4783
AleX Pelosi3adb3372020-09-03 00:20:07 -07004784static ssize_t charge_full_estimate_show(struct device *dev,
4785 struct device_attribute *attr,
4786 char *buff)
4787{
AleX Pelosib3a1a552022-05-03 17:00:11 -07004788 struct power_supply *psy = container_of(dev, struct power_supply, dev);
4789 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
Jenny Hocc17da12022-07-12 01:41:43 +00004790 int value = -1;
AleX Pelosi3adb3372020-09-03 00:20:07 -07004791
Jenny Hocc17da12022-07-12 01:41:43 +00004792 if (batt_drv->fg_psy)
4793 value = GPSY_GET_PROP(batt_drv->fg_psy, GBMS_PROP_CHARGE_FULL_ESTIMATE);
4794
AleX Pelosib3a1a552022-05-03 17:00:11 -07004795 return scnprintf(buff, PAGE_SIZE, "%d\n", value);
AleX Pelosi3adb3372020-09-03 00:20:07 -07004796}
4797
4798static const DEVICE_ATTR_RO(charge_full_estimate);
4799
Ken Tsou8acade12020-07-09 03:17:35 +08004800
4801static int cycle_count_bins_store(void *data, u64 val)
4802{
4803 struct batt_drv *batt_drv = (struct batt_drv *)data;
4804 int ret;
4805
4806 mutex_lock(&batt_drv->cc_data.lock);
4807 ret = batt_cycle_count_store(&batt_drv->cc_data);
4808 if (ret < 0)
4809 pr_err("cannot store bin count ret=%d\n", ret);
4810 mutex_unlock(&batt_drv->cc_data.lock);
4811
4812 return ret;
4813}
4814
4815static int cycle_count_bins_reload(void *data, u64 *val)
4816{
4817 struct batt_drv *batt_drv = (struct batt_drv *)data;
4818 int ret;
4819
4820 mutex_lock(&batt_drv->cc_data.lock);
4821 ret = batt_cycle_count_load(&batt_drv->cc_data);
4822 if (ret < 0)
4823 pr_err("cannot restore bin count ret=%d\n", ret);
4824 mutex_unlock(&batt_drv->cc_data.lock);
4825 *val = ret;
4826
4827 return ret;
4828}
4829
4830DEFINE_SIMPLE_ATTRIBUTE(cycle_count_bins_sync_fops,
4831 cycle_count_bins_reload,
AleX Pelosi5077fc52020-09-10 21:44:48 -07004832 cycle_count_bins_store, "%llu\n");
Ken Tsou8acade12020-07-09 03:17:35 +08004833
4834
4835static int debug_get_ssoc_gdf(void *data, u64 *val)
4836{
4837 struct batt_drv *batt_drv = (struct batt_drv *)data;
4838 *val = batt_drv->ssoc_state.ssoc_gdf;
4839 return 0;
4840}
4841
AleX Pelosi5077fc52020-09-10 21:44:48 -07004842DEFINE_SIMPLE_ATTRIBUTE(debug_ssoc_gdf_fops, debug_get_ssoc_gdf, NULL, "%llu\n");
Ken Tsou8acade12020-07-09 03:17:35 +08004843
4844
4845static int debug_get_ssoc_uic(void *data, u64 *val)
4846{
4847 struct batt_drv *batt_drv = (struct batt_drv *)data;
4848 *val = batt_drv->ssoc_state.ssoc_uic;
4849 return 0;
4850}
4851
AleX Pelosi5077fc52020-09-10 21:44:48 -07004852DEFINE_SIMPLE_ATTRIBUTE(debug_ssoc_uic_fops, debug_get_ssoc_uic, NULL, "%llu\n");
Ken Tsou8acade12020-07-09 03:17:35 +08004853
4854static int debug_get_ssoc_rls(void *data, u64 *val)
4855{
4856 struct batt_drv *batt_drv = (struct batt_drv *)data;
4857
4858 mutex_lock(&batt_drv->chg_lock);
4859 *val = batt_drv->ssoc_state.rl_status;
4860 mutex_unlock(&batt_drv->chg_lock);
4861
4862 return 0;
4863}
4864
4865static int debug_set_ssoc_rls(void *data, u64 val)
4866{
4867 struct batt_drv *batt_drv = (struct batt_drv *)data;
4868
4869 if (val < 0 || val > 2)
4870 return -EINVAL;
4871
4872 mutex_lock(&batt_drv->chg_lock);
4873 batt_drv->ssoc_state.rl_status = val;
4874 if (!batt_drv->fcc_votable)
Ken Tsoua15d1fa2022-01-24 14:42:27 +08004875 batt_drv->fcc_votable =
4876 gvotable_election_get_handle(VOTABLE_MSC_FCC);
Ken Tsou8acade12020-07-09 03:17:35 +08004877 if (batt_drv->fcc_votable)
Ken Tsoua15d1fa2022-01-24 14:42:27 +08004878 gvotable_cast_int_vote(batt_drv->fcc_votable, RL_STATE_VOTER, 0,
4879 batt_drv->ssoc_state.rl_status ==
4880 BATT_RL_STATUS_DISCHARGE);
Ken Tsou8acade12020-07-09 03:17:35 +08004881 mutex_unlock(&batt_drv->chg_lock);
4882
4883 return 0;
4884}
4885
4886DEFINE_SIMPLE_ATTRIBUTE(debug_ssoc_rls_fops,
AleX Pelosi5077fc52020-09-10 21:44:48 -07004887 debug_get_ssoc_rls, debug_set_ssoc_rls, "%llu\n");
Ken Tsou8acade12020-07-09 03:17:35 +08004888
Ken Tsou8acade12020-07-09 03:17:35 +08004889
AleX Pelosi57c74e22020-02-27 19:29:27 -08004890static ssize_t debug_get_ssoc_uicurve(struct file *filp,
4891 char __user *buf,
4892 size_t count, loff_t *ppos)
Ken Tsou8acade12020-07-09 03:17:35 +08004893{
4894 struct batt_drv *batt_drv = (struct batt_drv *)filp->private_data;
4895 char tmp[UICURVE_BUF_SZ] = { 0 };
4896
4897 mutex_lock(&batt_drv->chg_lock);
4898 ssoc_uicurve_cstr(tmp, sizeof(tmp), batt_drv->ssoc_state.ssoc_curve);
4899 mutex_unlock(&batt_drv->chg_lock);
4900
4901 return simple_read_from_buffer(buf, count, ppos, tmp, strlen(tmp));
4902}
4903
4904static ssize_t debug_set_ssoc_uicurve(struct file *filp,
AleX Pelosi57c74e22020-02-27 19:29:27 -08004905 const char __user *user_buf,
4906 size_t count, loff_t *ppos)
Ken Tsou8acade12020-07-09 03:17:35 +08004907{
4908 struct batt_drv *batt_drv = (struct batt_drv *)filp->private_data;
4909 int ret, curve_type;
4910 char buf[8];
4911
4912 ret = simple_write_to_buffer(buf, sizeof(buf), ppos, user_buf, count);
4913 if (!ret)
4914 return -EFAULT;
4915
4916 mutex_lock(&batt_drv->chg_lock);
4917
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07004918 /* FIX: BatteryDefenderUI doesn't really handle this yet */
Ken Tsou8acade12020-07-09 03:17:35 +08004919 curve_type = (int)simple_strtoull(buf, NULL, 10);
4920 if (curve_type >= -1 && curve_type <= 1)
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07004921 ssoc_change_curve(&batt_drv->ssoc_state, 0, curve_type);
Ken Tsou8acade12020-07-09 03:17:35 +08004922 else
4923 ret = -EINVAL;
4924
4925 mutex_unlock(&batt_drv->chg_lock);
4926
4927 if (ret == 0)
4928 ret = count;
4929
4930 return 0;
4931}
4932
4933BATTERY_DEBUG_ATTRIBUTE(debug_ssoc_uicurve_cstr_fops,
4934 debug_get_ssoc_uicurve,
4935 debug_set_ssoc_uicurve);
4936
AleX Pelosi57c74e22020-02-27 19:29:27 -08004937static int debug_force_psy_update(void *data, u64 val)
4938{
4939 struct batt_drv *batt_drv = (struct batt_drv *)data;
4940
4941 if (!batt_drv->psy)
4942 return -EINVAL;
4943
4944 power_supply_changed(batt_drv->psy);
4945 return 0;
4946}
4947
4948DEFINE_SIMPLE_ATTRIBUTE(debug_force_psy_update_fops,
4949 NULL, debug_force_psy_update, "%llu\n");
4950
4951/* Adaptive Charging */
4952static int debug_chg_health_rest_rate_read(void *data, u64 *val)
4953{
Jenny Ho6ff91bf2022-09-16 15:11:04 +08004954 struct batt_drv *batt_drv = data;
AleX Pelosi57c74e22020-02-27 19:29:27 -08004955
4956 if (!batt_drv->psy)
4957 return -EINVAL;
4958
4959 *val = batt_drv->chg_health.rest_rate;
4960 return 0;
4961}
4962
4963/* Adaptive Charging */
4964static int debug_chg_health_rest_rate_write(void *data, u64 val)
4965{
Jenny Ho6ff91bf2022-09-16 15:11:04 +08004966 struct batt_drv *batt_drv = data;
AleX Pelosi57c74e22020-02-27 19:29:27 -08004967
4968 if (!batt_drv->psy)
4969 return -EINVAL;
4970
4971 batt_drv->chg_health.rest_rate = val;
4972 return 0;
4973}
4974
4975/* Adaptive Charging */
4976DEFINE_SIMPLE_ATTRIBUTE(debug_chg_health_rest_rate_fops,
4977 debug_chg_health_rest_rate_read,
4978 debug_chg_health_rest_rate_write, "%llu\n");
4979
Jenny Ho6ff91bf2022-09-16 15:11:04 +08004980
4981/* Adaptive Charging */
4982static int debug_chg_health_rest_rate_before_trigger_read(void *data, u64 *val)
4983{
4984 struct batt_drv *batt_drv = data;
4985
4986 if (!batt_drv->psy)
4987 return -EINVAL;
4988
4989 *val = batt_drv->chg_health.rest_rate_before_trigger;
4990 return 0;
4991}
4992
4993/* Adaptive Charging */
4994static int debug_chg_health_rest_rate_before_trigger_write(void *data, u64 val)
4995{
4996 struct batt_drv *batt_drv = data;
4997
4998 if (!batt_drv->psy)
4999 return -EINVAL;
5000
5001 batt_drv->chg_health.rest_rate_before_trigger = val;
5002 return 0;
5003}
5004
5005/* Adaptive Charging */
5006DEFINE_SIMPLE_ATTRIBUTE(debug_chg_health_rest_rate_before_trigger_fops,
5007 debug_chg_health_rest_rate_before_trigger_read,
5008 debug_chg_health_rest_rate_before_trigger_write, "%llu\n");
5009
AleX Pelosi57c74e22020-02-27 19:29:27 -08005010/* Adaptive Charging */
5011static int debug_chg_health_thr_soc_read(void *data, u64 *val)
5012{
5013 struct batt_drv *batt_drv = (struct batt_drv *)data;
5014
5015 if (!batt_drv->psy)
5016 return -EINVAL;
5017
5018 *val = batt_drv->chg_health.rest_soc;
5019 return 0;
5020}
5021
5022/* Adaptive Charging */
5023static int debug_chg_health_thr_soc_write(void *data, u64 val)
5024{
5025 struct batt_drv *batt_drv = (struct batt_drv *)data;
5026
5027 if (!batt_drv->psy)
5028 return -EINVAL;
5029
5030 batt_drv->chg_health.rest_soc = val;
5031 return 0;
5032}
5033
5034/* Adaptive Charging */
5035DEFINE_SIMPLE_ATTRIBUTE(debug_chg_health_thr_soc_fops,
5036 debug_chg_health_thr_soc_read,
5037 debug_chg_health_thr_soc_write, "%llu\n");
5038
5039/* Adaptive Charging */
AleX Pelosi57c74e22020-02-27 19:29:27 -08005040static int debug_chg_health_set_stage(void *data, u64 val)
5041{
5042 struct batt_drv *batt_drv = (struct batt_drv *)data;
5043
5044 if (!batt_drv->psy)
5045 return -EINVAL;
5046
5047 switch (val) {
5048 case CHG_HEALTH_DISABLED:
5049 case CHG_HEALTH_INACTIVE:
5050 case CHG_HEALTH_ENABLED:
5051 case CHG_HEALTH_ACTIVE:
5052 case CHG_HEALTH_DONE:
5053 break;
5054 default:
5055 return -EINVAL;
5056 }
5057
5058 batt_drv->chg_health.rest_state = val;
5059 return 0;
5060}
5061
5062/* Adaptive Charging */
5063DEFINE_SIMPLE_ATTRIBUTE(debug_chg_health_stage_fops, NULL,
5064 debug_chg_health_set_stage, "%llu\n");
Jenny Ho68183e82021-12-06 17:39:05 +08005065
AleX Pelosic10eb8b2021-12-14 13:20:21 -08005066/* debug variable */
5067static int raw_profile_cycles;
5068
Jenny Ho68183e82021-12-06 17:39:05 +08005069static ssize_t debug_get_chg_raw_profile(struct file *filp,
5070 char __user *buf,
5071 size_t count, loff_t *ppos)
5072{
5073 struct batt_drv *batt_drv = (struct batt_drv *)filp->private_data;
5074 char *tmp;
5075 int len;
5076
AleX Pelosic10eb8b2021-12-14 13:20:21 -08005077 tmp = kzalloc(PAGE_SIZE, GFP_KERNEL);
Jenny Ho68183e82021-12-06 17:39:05 +08005078 if (!tmp)
5079 return -ENOMEM;
5080
AleX Pelosic10eb8b2021-12-14 13:20:21 -08005081 if (raw_profile_cycles) {
5082 struct gbms_chg_profile profile;
5083 int count;
5084
5085 len = gbms_init_chg_profile(&profile, batt_drv->device->of_node);
5086 if (len < 0)
5087 goto exit_done;
5088
5089 /* len is the capacity */
5090 len = aacr_get_capacity_at_cycle(batt_drv, raw_profile_cycles);
5091 if (len <= 0) {
5092 gbms_free_chg_profile(&profile);
5093 goto exit_done;
5094 }
5095
5096 count = scnprintf(tmp, PAGE_SIZE, "AACR Profile at %d cycles\n",
5097 raw_profile_cycles);
5098 gbms_init_chg_table(&profile, batt_drv->device->of_node, len);
5099 gbms_dump_chg_profile(&tmp[count], PAGE_SIZE - count, &profile);
5100 gbms_free_chg_profile(&profile);
5101 } else {
5102 gbms_dump_chg_profile(tmp, PAGE_SIZE, &batt_drv->chg_profile);
5103 }
5104
Jenny Ho68183e82021-12-06 17:39:05 +08005105 len = simple_read_from_buffer(buf, count, ppos, tmp, strlen(tmp));
5106
AleX Pelosic10eb8b2021-12-14 13:20:21 -08005107exit_done:
Jenny Ho68183e82021-12-06 17:39:05 +08005108 kfree(tmp);
Jenny Ho68183e82021-12-06 17:39:05 +08005109 return len;
5110}
AleX Pelosic10eb8b2021-12-14 13:20:21 -08005111
5112static ssize_t debug_set_chg_raw_profile(struct file *filp,
5113 const char __user *user_buf,
5114 size_t count, loff_t *ppos)
5115{
5116 int ret = 0, val;
5117 char buf[8];
5118
5119 ret = simple_write_to_buffer(buf, sizeof(buf), ppos, user_buf, count);
5120 if (!ret)
5121 return -EFAULT;
5122
5123 buf[ret] = '\0';
5124 ret = kstrtoint(buf, 0, &val);
5125 if (ret < 0)
5126 return ret;
5127
5128 raw_profile_cycles = val;
5129 return count;
5130}
5131
Jenny Ho68183e82021-12-06 17:39:05 +08005132BATTERY_DEBUG_ATTRIBUTE(debug_chg_raw_profile_fops,
5133 debug_get_chg_raw_profile,
AleX Pelosic10eb8b2021-12-14 13:20:21 -08005134 debug_set_chg_raw_profile);
Wasb Liuf40cbb22022-01-26 18:41:03 +08005135
5136static ssize_t debug_get_power_metrics(struct file *filp, char __user *buf,
5137 size_t count, loff_t *ppos)
5138{
5139 struct batt_drv *batt_drv = (struct batt_drv *)filp->private_data;
5140 char *tmp;
5141 int idx, len = 0;
5142
5143 tmp = kzalloc(PAGE_SIZE, GFP_KERNEL);
5144 if (!tmp)
5145 return -ENOMEM;
5146
5147 for(idx = 0; idx < POWER_METRICS_MAX_DATA; idx++) {
5148 len += scnprintf(&tmp[len], PAGE_SIZE - len, "%2d: %8ld/%8ld - %5lld\n", idx,
5149 batt_drv->power_metrics.data[idx].voltage,
5150 batt_drv->power_metrics.data[idx].charge_count,
5151 batt_drv->power_metrics.data[idx].time);
5152 }
5153
5154 len = simple_read_from_buffer(buf, count, ppos, tmp, strlen(tmp));
5155 kfree(tmp);
5156
5157 return len;
5158}
5159
5160BATTERY_DEBUG_ATTRIBUTE(debug_power_metrics_fops, debug_get_power_metrics, NULL);
Jack Wue3ebc172021-11-11 16:20:34 +08005161
5162static int debug_bpst_sbd_status_read(void *data, u64 *val)
5163{
5164 struct batt_drv *batt_drv = (struct batt_drv *)data;
5165
5166 *val = batt_drv->bpst_state.bpst_sbd_status;
5167 return 0;
5168}
5169
5170static int debug_bpst_sbd_status_write(void *data, u64 val)
5171{
5172 struct batt_drv *batt_drv = (struct batt_drv *)data;
5173
5174 if (val < 0 || val > 1)
5175 return -EINVAL;
5176
5177 mutex_lock(&batt_drv->bpst_state.lock);
5178 batt_drv->bpst_state.bpst_sbd_status = val;
5179 mutex_unlock(&batt_drv->bpst_state.lock);
5180
5181 return 0;
5182}
5183
5184DEFINE_SIMPLE_ATTRIBUTE(debug_bpst_sbd_status_fops,
5185 debug_bpst_sbd_status_read,
5186 debug_bpst_sbd_status_write, "%llu\n");
AleX Pelosib3a1a552022-05-03 17:00:11 -07005187
5188static int debug_ravg_fops_write(void *data, u64 val)
5189{
5190 struct batt_drv *batt_drv = (struct batt_drv *)data;
AleX Pelosia3f22de2022-05-03 23:42:24 -07005191 struct batt_res *res_state = &batt_drv->health_data.bhi_data.res_state;
AleX Pelosib3a1a552022-05-03 17:00:11 -07005192 int resistance_avg = val / 100, filter_count = 1;
5193 int ret;
5194
5195 mutex_lock(&batt_drv->chg_lock);
5196
AleX Pelosia3f22de2022-05-03 23:42:24 -07005197 batt_res_state_set(res_state, false);
5198 res_state->resistance_avg = resistance_avg;
5199 res_state->filter_count = filter_count;
AleX Pelosib3a1a552022-05-03 17:00:11 -07005200
5201 /* reset storage to defaults */
5202 if (val == 0) {
5203 resistance_avg = 0xffff;
5204 filter_count = 0xffff;
5205 }
5206
5207 ret = batt_ravg_write(resistance_avg, filter_count);
5208 pr_info("RAVG: update val=%d, resistance_avg=%x filter_count=%x (%d)\n",
5209 (int)val, resistance_avg, filter_count, ret);
5210 mutex_unlock(&batt_drv->chg_lock);
5211
5212 return 0;
5213}
5214
5215DEFINE_SIMPLE_ATTRIBUTE(debug_ravg_fops, NULL, debug_ravg_fops_write, "%llu\n");
5216
AleX Pelosi57c74e22020-02-27 19:29:27 -08005217#endif
5218
AleX Pelosi8e7fd812019-08-16 10:41:46 -07005219/* ------------------------------------------------------------------------- */
5220
Ken Tsou8acade12020-07-09 03:17:35 +08005221static ssize_t debug_get_fake_temp(struct file *filp,
5222 char __user *buf,
5223 size_t count, loff_t *ppos)
5224{
5225 struct batt_drv *batt_drv = (struct batt_drv *)filp->private_data;
5226 char tmp[8];
5227
5228 mutex_lock(&batt_drv->chg_lock);
5229 scnprintf(tmp, sizeof(tmp), "%d\n", batt_drv->fake_temp);
5230 mutex_unlock(&batt_drv->chg_lock);
5231
5232 return simple_read_from_buffer(buf, count, ppos, tmp, strlen(tmp));
5233}
5234
5235static ssize_t debug_set_fake_temp(struct file *filp,
5236 const char __user *user_buf,
5237 size_t count, loff_t *ppos)
5238{
5239 struct batt_drv *batt_drv = (struct batt_drv *)filp->private_data;
5240 int ret = 0, val;
5241 char buf[8];
5242
5243 ret = simple_write_to_buffer(buf, sizeof(buf), ppos, user_buf, count);
5244 if (!ret)
5245 return -EFAULT;
5246
5247 buf[ret] = '\0';
5248 ret = kstrtoint(buf, 0, &val);
5249 if (ret < 0)
5250 return ret;
5251
5252 mutex_lock(&batt_drv->chg_lock);
5253 batt_drv->fake_temp = val;
5254 mutex_unlock(&batt_drv->chg_lock);
5255
5256 return count;
5257}
5258
AleX Pelosi78a4bea2020-09-01 19:02:24 -07005259BATTERY_DEBUG_ATTRIBUTE(debug_fake_temp_fops, debug_get_fake_temp,
5260 debug_set_fake_temp);
Ken Tsou8acade12020-07-09 03:17:35 +08005261
5262
5263static enum batt_paired_state
5264batt_reset_pairing_state(const struct batt_drv *batt_drv)
5265{
5266 char dev_info[GBMS_DINF_LEN];
5267 int ret = 0;
5268
5269 memset(dev_info, 0xff, sizeof(dev_info));
5270 ret = gbms_storage_write(GBMS_TAG_DINF, dev_info, sizeof(dev_info));
5271 if (ret < 0)
5272 return -EIO;
5273
5274 return 0;
5275}
5276
5277static ssize_t debug_set_pairing_state(struct file *filp,
5278 const char __user *user_buf,
5279 size_t count, loff_t *ppos)
5280{
5281 struct batt_drv *batt_drv = (struct batt_drv *)filp->private_data;
5282 int ret = 0, val;
5283 char buf[8];
5284
5285 ret = simple_write_to_buffer(buf, sizeof(buf), ppos, user_buf, count);
5286 if (ret <= 0)
5287 return ret;
5288
5289 buf[ret] = '\0';
5290 ret = kstrtoint(buf, 0, &val);
5291 if (ret < 0)
5292 return ret;
5293
5294 mutex_lock(&batt_drv->chg_lock);
5295
5296 if (val == BATT_PAIRING_ENABLED) {
5297 batt_drv->pairing_state = BATT_PAIRING_ENABLED;
5298 mod_delayed_work(system_wq, &batt_drv->batt_work, 0);
5299 } else if (val == BATT_PAIRING_RESET) {
5300
5301 /* send a paring enable to re-pair OR reboot */
5302 ret = batt_reset_pairing_state(batt_drv);
5303 if (ret == 0)
5304 batt_drv->pairing_state = BATT_PAIRING_DISABLED;
5305 else
5306 count = -EIO;
5307 } else {
5308 count = -EINVAL;
5309 }
5310 mutex_unlock(&batt_drv->chg_lock);
5311
5312 return count;
5313}
5314
5315BATTERY_DEBUG_ATTRIBUTE(debug_pairing_fops, 0, debug_set_pairing_state);
5316
5317/* TODO: add write to stop/start collection, erase history etc. */
5318static ssize_t debug_get_blf_state(struct file *filp, char __user *buf,
5319 size_t count, loff_t *ppos)
5320{
5321 struct batt_drv *batt_drv = (struct batt_drv *)filp->private_data;
5322 char tmp[8];
5323
5324 mutex_lock(&batt_drv->chg_lock);
5325 scnprintf(tmp, sizeof(tmp), "%d\n", batt_drv->blf_state);
5326 mutex_unlock(&batt_drv->chg_lock);
5327
5328 return simple_read_from_buffer(buf, count, ppos, tmp, strlen(tmp));
5329}
5330BATTERY_DEBUG_ATTRIBUTE(debug_blf_state_fops, debug_get_blf_state, 0);
5331
Jenny Hod4bd5bc2022-08-26 16:08:38 +00005332static ssize_t debug_get_bhi_status(struct file *filp, char __user *buf,
5333 size_t count, loff_t *ppos)
5334{
5335 struct batt_drv *batt_drv = (struct batt_drv *)filp->private_data;
5336 struct health_data *health_data = &batt_drv->health_data;
5337 const int cap_idx = health_data->bhi_debug_cap_index;
5338 const int imp_idx = health_data->bhi_debug_imp_index;
5339 const int sd_idx = health_data->bhi_debug_sd_index;
5340 const int algo = BHI_ALGO_DEBUG;
5341 int health_idx = health_data->bhi_debug_health_index;
5342 int health_status, len;
5343 char *tmp;
5344
5345 tmp = kzalloc(PAGE_SIZE, GFP_KERNEL);
5346 if (!tmp)
5347 return -ENOMEM;
5348
5349 if (health_idx == 0)
5350 health_idx = bhi_calc_health_index(algo, health_data, cap_idx, imp_idx, sd_idx);
5351
5352 health_status = bhi_calc_health_status(algo, BHI_ROUND_INDEX(health_idx), health_data);
5353
5354 if (health_data->bhi_debug_health_index != 0)
5355 scnprintf(tmp, PAGE_SIZE, "%d, %d\n", health_status, health_idx);
5356 else
5357 scnprintf(tmp, PAGE_SIZE, "%d, %d [%d/%d %d/%d %d/%d]\n", health_status,
5358 health_idx, cap_idx, health_data->bhi_w_ci, imp_idx,
5359 health_data->bhi_w_pi, sd_idx, health_data->bhi_w_sd);
5360
5361 len = simple_read_from_buffer(buf, count, ppos, tmp, strlen(tmp));
5362 kfree(tmp);
5363
5364 return len;
5365}
5366BATTERY_DEBUG_ATTRIBUTE(debug_bhi_status_fops, debug_get_bhi_status, 0);
5367
Ken Tsou8acade12020-07-09 03:17:35 +08005368/* TODO: add writes to restart pairing (i.e. provide key) */
5369static ssize_t batt_pairing_state_show(struct device *dev,
5370 struct device_attribute *attr,
5371 char *buf)
5372{
5373 struct power_supply *psy = container_of(dev, struct power_supply, dev);
5374 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
5375 int len;
5376
5377 mutex_lock(&batt_drv->chg_lock);
5378 len = scnprintf(buf, PAGE_SIZE, "%d\n", batt_drv->pairing_state);
5379 mutex_unlock(&batt_drv->chg_lock);
5380 return len;
5381}
5382
5383static const DEVICE_ATTR(pairing_state, 0444, batt_pairing_state_show, NULL);
5384
5385
5386static ssize_t batt_ctl_chg_stats_actual(struct device *dev,
5387 struct device_attribute *attr,
5388 const char *buf, size_t count)
5389{
5390 struct power_supply *psy = container_of(dev, struct power_supply, dev);
5391 struct batt_drv *batt_drv =(struct batt_drv *)
5392 power_supply_get_drvdata(psy);
5393
5394 if (count < 1)
5395 return -ENODATA;
5396
5397 switch (buf[0]) {
5398 case 'p': /* publish data to qual */
5399 case 'P': /* force publish data to qual */
Stephane Leecbb07ee2020-09-14 12:24:09 -07005400 batt_chg_stats_pub(batt_drv, "debug cmd", buf[0] == 'P', false);
Ken Tsou8acade12020-07-09 03:17:35 +08005401 break;
5402 default:
5403 count = -EINVAL;
5404 break;
5405 }
5406
5407 return count;
5408}
5409
5410static ssize_t batt_show_chg_stats_actual(struct device *dev,
5411 struct device_attribute *attr, char *buf)
5412{
5413 struct power_supply *psy = container_of(dev, struct power_supply, dev);
5414 struct batt_drv *batt_drv =(struct batt_drv *)
5415 power_supply_get_drvdata(psy);
5416 int len;
5417
5418 mutex_lock(&batt_drv->stats_lock);
5419 len = batt_chg_stats_cstr(buf, PAGE_SIZE, &batt_drv->ce_data, false);
5420 mutex_unlock(&batt_drv->stats_lock);
5421
5422 return len;
5423}
5424
5425static const DEVICE_ATTR(charge_stats_actual, 0664,
5426 batt_show_chg_stats_actual,
5427 batt_ctl_chg_stats_actual);
5428
5429static ssize_t batt_ctl_chg_stats(struct device *dev,
5430 struct device_attribute *attr,
5431 const char *buf, size_t count)
5432{
5433 struct power_supply *psy = container_of(dev, struct power_supply, dev);
5434 struct batt_drv *batt_drv =(struct batt_drv *)
5435 power_supply_get_drvdata(psy);
5436
5437 if (count < 1)
5438 return -ENODATA;
5439
5440 mutex_lock(&batt_drv->stats_lock);
5441 switch (buf[0]) {
5442 case 0:
5443 case '0': /* invalidate current qual */
AleX Pelosi4a9035f2020-02-28 19:09:57 -08005444 cev_stats_init(&batt_drv->ce_qual, &batt_drv->chg_profile);
Ken Tsou8acade12020-07-09 03:17:35 +08005445 break;
5446 }
5447 mutex_unlock(&batt_drv->stats_lock);
5448
5449 return count;
5450}
5451
AleX Pelosi043ffbe2020-06-24 22:48:30 -07005452/* regular and health stats */
AleX Pelosi0d261512020-05-07 12:17:07 -07005453static ssize_t batt_chg_qual_stats_cstr(char *buff, int size,
5454 struct gbms_charging_event *ce_qual,
5455 bool verbose)
5456{
5457 ssize_t len = 0;
5458
5459 len += batt_chg_stats_cstr(&buff[len], size - len, ce_qual, verbose);
Jenny Ho6f7ec522020-05-19 09:04:53 +08005460 if (ce_qual->ce_health.rest_state != CHG_HEALTH_INACTIVE)
AleX Pelosi0d261512020-05-07 12:17:07 -07005461 len += batt_health_stats_cstr(&buff[len], size - len,
5462 ce_qual, verbose);
5463 return len;
5464}
5465
Ken Tsou8acade12020-07-09 03:17:35 +08005466static ssize_t batt_show_chg_stats(struct device *dev,
5467 struct device_attribute *attr, char *buf)
5468{
5469 struct power_supply *psy = container_of(dev, struct power_supply, dev);
5470 struct batt_drv *batt_drv =(struct batt_drv *)
5471 power_supply_get_drvdata(psy);
AleX Pelosi0d261512020-05-07 12:17:07 -07005472 struct gbms_charging_event *ce_qual = &batt_drv->ce_qual;
Ken Tsou8acade12020-07-09 03:17:35 +08005473 int len = -ENODATA;
5474
5475 mutex_lock(&batt_drv->stats_lock);
AleX Pelosi0d261512020-05-07 12:17:07 -07005476 if (ce_qual->last_update - ce_qual->first_update)
5477 len = batt_chg_qual_stats_cstr(buf, PAGE_SIZE, ce_qual, false);
Ken Tsou8acade12020-07-09 03:17:35 +08005478 mutex_unlock(&batt_drv->stats_lock);
5479
5480 return len;
5481}
5482
5483static const DEVICE_ATTR(charge_stats, 0664, batt_show_chg_stats,
5484 batt_ctl_chg_stats);
5485
AleX Pelosi043ffbe2020-06-24 22:48:30 -07005486/* show current/active and qual data */
Ken Tsou8acade12020-07-09 03:17:35 +08005487static ssize_t batt_show_chg_details(struct device *dev,
5488 struct device_attribute *attr, char *buf)
5489{
5490 struct power_supply *psy = container_of(dev, struct power_supply, dev);
5491 struct batt_drv *batt_drv =(struct batt_drv *)
5492 power_supply_get_drvdata(psy);
AleX Pelosi0d261512020-05-07 12:17:07 -07005493 struct gbms_charging_event *ce_data = &batt_drv->ce_data;
Ken Tsou8acade12020-07-09 03:17:35 +08005494 const bool qual_valid = (batt_drv->ce_qual.last_update -
5495 batt_drv->ce_qual.first_update) != 0;
5496 int len = 0;
5497
5498 mutex_lock(&batt_drv->stats_lock);
5499
AleX Pelosi043ffbe2020-06-24 22:48:30 -07005500 /* this is the current one */
AleX Pelosi0d261512020-05-07 12:17:07 -07005501 len += batt_chg_stats_cstr(&buf[len], PAGE_SIZE - len, ce_data, true);
AleX Pelosi043ffbe2020-06-24 22:48:30 -07005502
5503 /*
5504 * stats are accumulated in ce_data->health_stats, rest_* fields
5505 * are set on stats_close()
5506 */
AleX Pelosi0d261512020-05-07 12:17:07 -07005507 if (batt_drv->chg_health.rest_state != CHG_HEALTH_INACTIVE) {
Jenny Hof189bfb2021-05-10 16:57:55 +08005508 const struct gbms_ce_tier_stats *h = &batt_drv->ce_data.health_stats;
5509 const struct gbms_ce_tier_stats *p = &batt_drv->ce_data.health_pause_stats;
5510 const long elap_h = h->time_fast + h->time_taper + h->time_other;
5511 const long elap_p = p->time_fast + p->time_taper + p->time_other;
AleX Pelosi043ffbe2020-06-24 22:48:30 -07005512 const ktime_t now = get_boot_sec();
AleX Pelosi0d261512020-05-07 12:17:07 -07005513 int vti;
5514
AleX Pelosi043ffbe2020-06-24 22:48:30 -07005515 vti = batt_chg_health_vti(&batt_drv->chg_health);
AleX Pelosi0d261512020-05-07 12:17:07 -07005516 len += scnprintf(&buf[len], PAGE_SIZE - len,
Jenny Hof189bfb2021-05-10 16:57:55 +08005517 "\nH: %d %d %ld %ld %lld %lld %d",
AleX Pelosi0d261512020-05-07 12:17:07 -07005518 batt_drv->chg_health.rest_state,
Jenny Hof189bfb2021-05-10 16:57:55 +08005519 vti, elap_h, elap_p, now,
AleX Pelosi043ffbe2020-06-24 22:48:30 -07005520 batt_drv->chg_health.rest_deadline,
5521 batt_drv->chg_health.always_on_soc);
AleX Pelosi0d261512020-05-07 12:17:07 -07005522
AleX Pelosi043ffbe2020-06-24 22:48:30 -07005523 /* NOTE: vtier_idx is -1, can also check elap */
Jenny Hof189bfb2021-05-10 16:57:55 +08005524 if (h->soc_in != -1)
Prasanna Prapancham320dcfe2022-06-27 20:20:46 +00005525 len += gbms_tier_stats_cstr(&buf[len],
5526 PAGE_SIZE - len, h, !!elap_h);
Jenny Hof189bfb2021-05-10 16:57:55 +08005527 if (p->soc_in != -1)
Prasanna Prapancham320dcfe2022-06-27 20:20:46 +00005528 len += gbms_tier_stats_cstr(&buf[len],
5529 PAGE_SIZE - len, p, !!elap_p);
AleX Pelosi0d261512020-05-07 12:17:07 -07005530 }
5531
AleX Pelosi043ffbe2020-06-24 22:48:30 -07005532 len += scnprintf(&buf[len], PAGE_SIZE - len, "\n");
5533
5534 /* this was the last one (if present) */
5535 if (qual_valid) {
AleX Pelosi0d261512020-05-07 12:17:07 -07005536 len += batt_chg_qual_stats_cstr(&buf[len], PAGE_SIZE - len,
5537 &batt_drv->ce_qual, true);
AleX Pelosi043ffbe2020-06-24 22:48:30 -07005538 len += scnprintf(&buf[len], PAGE_SIZE - len, "\n");
5539 }
Ken Tsou8acade12020-07-09 03:17:35 +08005540
5541 mutex_unlock(&batt_drv->stats_lock);
5542
5543 return len;
5544}
5545
5546static const DEVICE_ATTR(charge_details, 0444, batt_show_chg_details,
5547 NULL);
5548
5549/* tier and soc details */
5550static ssize_t batt_show_ttf_details(struct device *dev,
5551 struct device_attribute *attr, char *buf)
5552{
5553 struct power_supply *psy = container_of(dev, struct power_supply, dev);
5554 struct batt_drv *batt_drv = (struct batt_drv *)
5555 power_supply_get_drvdata(psy);
5556 struct batt_ttf_stats *ttf_stats;
AleX Pelosi4a9035f2020-02-28 19:09:57 -08005557 int len;
Ken Tsou8acade12020-07-09 03:17:35 +08005558
5559 if (!batt_drv->ssoc_state.buck_enabled)
5560 return -ENODATA;
5561
5562 ttf_stats = kzalloc(sizeof(*ttf_stats), GFP_KERNEL);
5563 if (!ttf_stats)
5564 return -ENOMEM;
5565
5566 mutex_lock(&batt_drv->stats_lock);
5567 /* update a private copy of ttf stats */
5568 ttf_stats_update(ttf_stats_dup(ttf_stats, &batt_drv->ttf_stats),
AleX Pelosi4a9035f2020-02-28 19:09:57 -08005569 &batt_drv->ce_data, false);
Ken Tsou8acade12020-07-09 03:17:35 +08005570 mutex_unlock(&batt_drv->stats_lock);
5571
AleX Pelosi4a9035f2020-02-28 19:09:57 -08005572 len = ttf_dump_details(buf, PAGE_SIZE, ttf_stats,
5573 batt_drv->ce_data.last_soc);
Ken Tsou8acade12020-07-09 03:17:35 +08005574 kfree(ttf_stats);
5575
5576 return len;
5577}
5578
AleX Pelosi57c74e22020-02-27 19:29:27 -08005579static const DEVICE_ATTR(ttf_details, 0444, batt_show_ttf_details, NULL);
Ken Tsou8acade12020-07-09 03:17:35 +08005580
5581/* house stats */
5582static ssize_t batt_show_ttf_stats(struct device *dev,
5583 struct device_attribute *attr, char *buf)
5584{
5585 struct power_supply *psy = container_of(dev, struct power_supply, dev);
5586 struct batt_drv *batt_drv =(struct batt_drv *)
5587 power_supply_get_drvdata(psy);
5588 const int verbose = true;
5589 int i, len = 0;
5590
5591 mutex_lock(&batt_drv->stats_lock);
5592
5593 for (i = 0; i < GBMS_STATS_TIER_COUNT; i++)
5594 len += ttf_tier_cstr(&buf[len], PAGE_SIZE,
5595 &batt_drv->ttf_stats.tier_stats[i]);
5596
5597 len += scnprintf(&buf[len], PAGE_SIZE - len, "\n");
5598
5599 if (verbose)
5600 len += ttf_soc_cstr(&buf[len], PAGE_SIZE - len,
5601 &batt_drv->ttf_stats.soc_stats,
5602 0, 99);
5603
5604 mutex_unlock(&batt_drv->stats_lock);
5605
5606 return len;
5607}
5608
5609/* userspace restore the TTF data with this */
5610static ssize_t batt_ctl_ttf_stats(struct device *dev,
5611 struct device_attribute *attr,
5612 const char *buf, size_t count)
5613{
5614 int res;
5615 struct power_supply *psy = container_of(dev, struct power_supply, dev);
5616 struct batt_drv *batt_drv =(struct batt_drv *)
5617 power_supply_get_drvdata(psy);
5618
5619 if (count < 1)
5620 return -ENODATA;
5621 if (!batt_drv->ssoc_state.buck_enabled)
5622 return -ENODATA;
5623
5624 mutex_lock(&batt_drv->stats_lock);
5625 switch (buf[0]) {
5626 case 'u':
5627 case 'U': /* force update */
5628 ttf_stats_update(&batt_drv->ttf_stats, &batt_drv->ce_data,
5629 (buf[0] == 'U'));
5630 break;
5631 default:
5632 /* TODO: userspace restore of the data */
5633 res = ttf_stats_sscan(&batt_drv->ttf_stats, buf, count);
5634 if (res < 0)
5635 count = res;
5636 break;
5637 }
5638 mutex_unlock(&batt_drv->stats_lock);
5639
5640 return count;
5641}
5642
5643static const DEVICE_ATTR(ttf_stats, 0664, batt_show_ttf_stats,
5644 batt_ctl_ttf_stats);
5645
AleX Pelosi8e7fd812019-08-16 10:41:46 -07005646/* ------------------------------------------------------------------------- */
5647
AleX Pelosi57c74e22020-02-27 19:29:27 -08005648static ssize_t chg_health_show_stage(struct device *dev,
5649 struct device_attribute *attr, char *buf)
5650{
5651 struct power_supply *psy = container_of(dev, struct power_supply, dev);
5652 struct batt_drv *batt_drv = (struct batt_drv *)
5653 power_supply_get_drvdata(psy);
5654 const char *s = "Inactive";
5655
5656 mutex_lock(&batt_drv->chg_lock);
5657 switch (batt_drv->chg_health.rest_state) {
5658 case CHG_HEALTH_DISABLED:
5659 s = "Disabled";
5660 break;
5661 case CHG_HEALTH_ENABLED:
5662 s = "Enabled";
5663 break;
Stephane Lee2b9ea732021-05-23 23:39:54 -07005664 case CHG_HEALTH_PAUSE:
AleX Pelosi57c74e22020-02-27 19:29:27 -08005665 case CHG_HEALTH_ACTIVE:
5666 s = "Active";
5667 break;
5668 case CHG_HEALTH_DONE:
AleX Pelosi0d261512020-05-07 12:17:07 -07005669 s = "Done";
AleX Pelosi57c74e22020-02-27 19:29:27 -08005670 break;
5671 default:
5672 break;
5673 }
5674 mutex_unlock(&batt_drv->chg_lock);
5675
5676 return scnprintf(buf, PAGE_SIZE, "%s\n", s);
5677}
5678
5679static const DEVICE_ATTR(charge_stage, 0444, chg_health_show_stage, NULL);
5680
AleX Pelosi043ffbe2020-06-24 22:48:30 -07005681static ssize_t chg_health_charge_limit_get(struct device *dev,
5682 struct device_attribute *attr,
5683 char *buf)
5684{
5685 struct power_supply *psy = container_of(dev, struct power_supply, dev);
5686 struct batt_drv *batt_drv =(struct batt_drv *)
5687 power_supply_get_drvdata(psy);
5688
5689 return scnprintf(buf, PAGE_SIZE, "%d\n",
5690 batt_drv->chg_health.always_on_soc);
5691}
5692/* setting disable (deadline = -1) or replug (deadline == 0) will disable */
5693static ssize_t chg_health_charge_limit_set(struct device *dev,
5694 struct device_attribute *attr,
5695 const char *buf, size_t count)
5696{
5697 struct power_supply *psy = container_of(dev, struct power_supply, dev);
5698 struct batt_drv *batt_drv =(struct batt_drv *)
5699 power_supply_get_drvdata(psy);
5700 const int always_on_soc = simple_strtol(buf, NULL, 10);
5701 enum chg_health_state rest_state;
5702
Jenny Ho6f7ec522020-05-19 09:04:53 +08005703 /* Always enable AC when SOC is over trigger */
AleX Pelosi043ffbe2020-06-24 22:48:30 -07005704 if (always_on_soc < -1 || always_on_soc > 99)
5705 return -EINVAL;
5706
5707 mutex_lock(&batt_drv->chg_lock);
5708
Jenny Ho6f7ec522020-05-19 09:04:53 +08005709 /*
5710 * There are interesting overlaps with the AC standard behavior since
5711 * the aon limit can be set at any time (and while AC limit is active)
5712 * TODO: fully document the state machine
5713 */
AleX Pelosi043ffbe2020-06-24 22:48:30 -07005714 rest_state = batt_drv->chg_health.rest_state;
5715
5716 if (always_on_soc != -1) {
5717 switch (rest_state) {
5718 case CHG_HEALTH_DISABLED: /* didn't meet deadline */
5719 case CHG_HEALTH_INACTIVE: /* deadline was not provided */
5720 rest_state = CHG_HEALTH_ENABLED;
5721 break;
5722 default:
5723 /* _DONE, _ENABLED, _ACTIVE, _USER_DISABLED */
5724 break;
5725 }
5726 } else if (batt_drv->chg_health.always_on_soc != -1) {
5727
5728 switch (rest_state) {
5729 case CHG_HEALTH_ENABLED: /* waiting for always_on_soc */
5730 case CHG_HEALTH_ACTIVE: /* activated at always_on_soc */
5731 if (batt_drv->chg_health.rest_deadline > 0)
5732 rest_state = CHG_HEALTH_ENABLED;
5733 else
5734 rest_state = CHG_HEALTH_INACTIVE;
5735 break;
5736 default:
5737 /* _DONE, _DISABLED, _USER_DISABLED */
5738 break;
5739 }
5740 }
5741
5742 batt_drv->chg_health.always_on_soc = always_on_soc;
5743 batt_drv->chg_health.rest_state = rest_state;
5744
5745 mutex_unlock(&batt_drv->chg_lock);
5746 power_supply_changed(batt_drv->psy);
5747 return count;
5748}
5749
5750static DEVICE_ATTR(charge_limit, 0660, chg_health_charge_limit_get,
5751 chg_health_charge_limit_set);
AleX Pelosi57c74e22020-02-27 19:29:27 -08005752
AleX Pelosi8e7fd812019-08-16 10:41:46 -07005753static ssize_t batt_show_chg_deadline(struct device *dev,
5754 struct device_attribute *attr, char *buf)
5755{
5756 struct power_supply *psy = container_of(dev, struct power_supply, dev);
5757 struct batt_drv *batt_drv =(struct batt_drv *)
5758 power_supply_get_drvdata(psy);
Jenny Hofe2308e2021-11-22 05:40:34 +08005759 const struct batt_chg_health *rest = &batt_drv->chg_health;
5760 const bool aon_enabled = rest->always_on_soc != -1;
AleX Pelosi8e7fd812019-08-16 10:41:46 -07005761 const ktime_t now = get_boot_sec();
AleX Pelosi57c74e22020-02-27 19:29:27 -08005762 long long deadline = 0;
Jenny Hofe2308e2021-11-22 05:40:34 +08005763 ktime_t ttf = 0;
5764 int ret = 0;
AleX Pelosi8e7fd812019-08-16 10:41:46 -07005765
AleX Pelosi8e7fd812019-08-16 10:41:46 -07005766 mutex_lock(&batt_drv->chg_lock);
AleX Pelosi57c74e22020-02-27 19:29:27 -08005767
Jenny Ho6f7ec522020-05-19 09:04:53 +08005768 /*
5769 * = (rest_deadline <= 0) means state is either Inactive or Disabled
5770 * = (rest_deadline < now) means state is either Done or Disabled
5771 *
5772 * State becomes Disabled from Enabled or Active when/if msc_logic()
5773 * determines that the device cannot reach full before the deadline.
5774 *
5775 * UI checks for:
5776 * (stage == 'Active' || stage == 'Enabled') && deadline > 0
5777 */
AleX Pelosi57c74e22020-02-27 19:29:27 -08005778 deadline = batt_drv->chg_health.rest_deadline;
Jenny Hofe2308e2021-11-22 05:40:34 +08005779
5780 /* ACA: show time to full when ACA triggered */
5781 if (aon_enabled && rest->rest_state == CHG_HEALTH_ACTIVE) {
5782 ret = batt_ttf_estimate(&ttf, batt_drv);
5783 if (ret < 0)
5784 pr_debug("unable to get ttf (%d)\n", ret);
5785 else
5786 deadline = now + ttf;
5787 }
5788
Jenny Ho6f7ec522020-05-19 09:04:53 +08005789 if (deadline > 0 && deadline > now)
AleX Pelosi57c74e22020-02-27 19:29:27 -08005790 deadline -= now;
Jenny Ho6f7ec522020-05-19 09:04:53 +08005791 else if (deadline > 0)
5792 deadline = 0;
AleX Pelosi57c74e22020-02-27 19:29:27 -08005793
AleX Pelosi8e7fd812019-08-16 10:41:46 -07005794 mutex_unlock(&batt_drv->chg_lock);
5795
Jenny Ho6f7ec522020-05-19 09:04:53 +08005796 /*
5797 * deadline < 0 feature disabled. deadline = 0 expired or disabled for
5798 * this session, deadline > 0 time to deadline otherwise.
5799 */
5800 return scnprintf(buf, PAGE_SIZE, "%lld\n", (long long)deadline);
AleX Pelosi8e7fd812019-08-16 10:41:46 -07005801}
5802
AleX Pelosi8e7fd812019-08-16 10:41:46 -07005803/* userspace restore the TTF data with this */
5804static ssize_t batt_set_chg_deadline(struct device *dev,
5805 struct device_attribute *attr,
5806 const char *buf, size_t count)
5807{
5808 struct power_supply *psy = container_of(dev, struct power_supply, dev);
5809 struct batt_drv *batt_drv =(struct batt_drv *)
5810 power_supply_get_drvdata(psy);
AleX Pelosi0d261512020-05-07 12:17:07 -07005811 long long deadline_s;
AleX Pelosi57c74e22020-02-27 19:29:27 -08005812 bool changed;
AleX Pelosi8e7fd812019-08-16 10:41:46 -07005813
AleX Pelosi8e7fd812019-08-16 10:41:46 -07005814 /* API works in seconds */
Jenny Ho6f7ec522020-05-19 09:04:53 +08005815 deadline_s = simple_strtoll(buf, NULL, 10);
AleX Pelosi0d261512020-05-07 12:17:07 -07005816
AleX Pelosi8e7fd812019-08-16 10:41:46 -07005817 mutex_lock(&batt_drv->chg_lock);
Stephane Leeaaf28cd2020-08-26 11:26:06 -07005818 /* Let deadline < 0 pass to set stats */
5819 if (!batt_drv->ssoc_state.buck_enabled && deadline_s >= 0) {
AleX Pelosi57c74e22020-02-27 19:29:27 -08005820 mutex_unlock(&batt_drv->chg_lock);
5821 return -EINVAL;
5822 }
5823
5824 changed = batt_health_set_chg_deadline(&batt_drv->chg_health,
AleX Pelosi0d261512020-05-07 12:17:07 -07005825 deadline_s);
AleX Pelosi8e7fd812019-08-16 10:41:46 -07005826 mutex_unlock(&batt_drv->chg_lock);
5827
AleX Pelosi57c74e22020-02-27 19:29:27 -08005828 if (changed)
5829 power_supply_changed(batt_drv->psy);
5830
Jenny Hoa7026682022-09-30 16:38:45 +08005831 gbms_logbuffer_prlog(batt_drv->ttf_stats.ttf_log, LOGLEVEL_INFO, 0, LOGLEVEL_DEBUG,
5832 "MSC_HEALTH: deadline_s=%lld deadline at %lld",
5833 deadline_s, batt_drv->chg_health.rest_deadline);
AleX Pelosi8e7fd812019-08-16 10:41:46 -07005834
5835 return count;
5836}
5837
5838static const DEVICE_ATTR(charge_deadline, 0664, batt_show_chg_deadline,
5839 batt_set_chg_deadline);
5840
Stephane Lee463e85d2021-08-16 14:50:36 -07005841static ssize_t charge_deadline_dryrun_store(struct device *dev,
5842 struct device_attribute *attr,
5843 const char *buf, size_t count)
5844{
5845 struct power_supply *psy = container_of(dev, struct power_supply, dev);
5846 struct batt_drv *batt_drv =
5847 (struct batt_drv *)power_supply_get_drvdata(psy);
5848 long long deadline_s;
5849
5850 /* API works in seconds */
5851 deadline_s = simple_strtoll(buf, NULL, 10);
5852
5853 mutex_lock(&batt_drv->chg_lock);
5854 if (!batt_drv->ssoc_state.buck_enabled || deadline_s < 0) {
5855 mutex_unlock(&batt_drv->chg_lock);
5856 return -EINVAL;
5857 }
5858 batt_drv->chg_health.dry_run_deadline = get_boot_sec() + deadline_s;
5859 mutex_unlock(&batt_drv->chg_lock);
5860
5861 return count;
5862}
5863
5864static DEVICE_ATTR_WO(charge_deadline_dryrun);
5865
Stephane Lee02c436e2021-03-23 12:40:17 -07005866enum batt_ssoc_status {
5867 BATT_SSOC_STATUS_UNKNOWN = 0,
5868 BATT_SSOC_STATUS_CONNECTED = 1,
5869 BATT_SSOC_STATUS_DISCONNECTED = 2,
5870 BATT_SSOC_STATUS_FULL = 3,
5871};
5872
5873static ssize_t ssoc_details_show(struct device *dev,
5874 struct device_attribute *attr, char *buf)
5875{
5876 struct power_supply *psy = container_of(dev, struct power_supply, dev);
5877 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
5878 struct batt_ssoc_state *ssoc_state = &batt_drv->ssoc_state;
5879 int len = 0;
5880 enum batt_ssoc_status status = BATT_SSOC_STATUS_UNKNOWN;
5881 char buff[UICURVE_BUF_SZ] = { 0 };
5882
5883 mutex_lock(&batt_drv->chg_lock);
5884
5885 if (ssoc_state->buck_enabled == 0) {
5886 status = BATT_SSOC_STATUS_DISCONNECTED;
5887 } else if (ssoc_state->buck_enabled == 1) {
5888 if (batt_drv->batt_full)
5889 status = BATT_SSOC_STATUS_FULL;
5890 else
5891 status = BATT_SSOC_STATUS_CONNECTED;
5892 }
5893
5894 len = scnprintf(
5895 buf, sizeof(ssoc_state->ssoc_state_cstr),
5896 "soc: l=%d%% gdf=%d.%02d uic=%d.%02d rl=%d.%02d\n"
5897 "curve:%s\n"
5898 "status: ct=%d rl=%d s=%d\n",
5899 ssoc_get_capacity(ssoc_state), qnum_toint(ssoc_state->ssoc_gdf),
5900 qnum_fracdgt(ssoc_state->ssoc_gdf),
5901 qnum_toint(ssoc_state->ssoc_uic),
5902 qnum_fracdgt(ssoc_state->ssoc_uic),
5903 qnum_toint(ssoc_state->ssoc_rl),
5904 qnum_fracdgt(ssoc_state->ssoc_rl),
5905 ssoc_uicurve_cstr(buff, sizeof(buff), ssoc_state->ssoc_curve),
5906 ssoc_state->ssoc_curve_type, ssoc_state->rl_status, status);
5907
5908 mutex_unlock(&batt_drv->chg_lock);
5909
5910 return len;
5911}
5912
5913static const DEVICE_ATTR_RO(ssoc_details);
5914
Jenny Hoa7d48db2020-12-08 15:22:09 +08005915static ssize_t show_bd_trickle_enable(struct device *dev,
5916 struct device_attribute *attr,
5917 char *buf)
5918{
5919 struct power_supply *psy = container_of(dev, struct power_supply, dev);
5920 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
5921
5922 return scnprintf(buf, PAGE_SIZE, "%d\n",
5923 batt_drv->ssoc_state.bd_trickle_enable);
5924}
5925
5926static ssize_t set_bd_trickle_enable(struct device *dev,
5927 struct device_attribute *attr,
5928 const char *buf, size_t count)
5929{
5930 struct power_supply *psy = container_of(dev, struct power_supply, dev);
5931 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
5932 int ret = 0, val;
5933
5934 ret = kstrtoint(buf, 0, &val);
5935 if (ret < 0)
5936 return ret;
5937
5938 batt_drv->ssoc_state.bd_trickle_enable = !!val;
5939
5940 return count;
5941}
5942
5943static DEVICE_ATTR(bd_trickle_enable, 0660,
5944 show_bd_trickle_enable, set_bd_trickle_enable);
5945
Ken Tsouab4bba02020-11-18 20:39:15 +08005946static ssize_t show_bd_trickle_cnt(struct device *dev,
5947 struct device_attribute *attr, char *buf)
5948{
5949 struct power_supply *psy = container_of(dev, struct power_supply, dev);
5950 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
5951
5952 return scnprintf(buf, PAGE_SIZE, "%d\n",
5953 batt_drv->ssoc_state.bd_trickle_cnt);
5954}
5955
5956static ssize_t set_bd_trickle_cnt(struct device *dev,
5957 struct device_attribute *attr,
5958 const char *buf, size_t count)
5959{
5960 struct power_supply *psy = container_of(dev, struct power_supply, dev);
5961 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
5962 int ret = 0, val;
5963
5964 ret = kstrtoint(buf, 0, &val);
5965 if (ret < 0)
5966 return ret;
5967
5968 batt_drv->ssoc_state.bd_trickle_cnt = val;
5969
5970 return count;
5971}
5972
5973static DEVICE_ATTR(bd_trickle_cnt, 0660,
5974 show_bd_trickle_cnt, set_bd_trickle_cnt);
5975
Jenny Hoa7d48db2020-12-08 15:22:09 +08005976static ssize_t show_bd_trickle_recharge_soc(struct device *dev,
5977 struct device_attribute *attr,
5978 char *buf)
Ken Tsouab4bba02020-11-18 20:39:15 +08005979{
5980 struct power_supply *psy = container_of(dev, struct power_supply, dev);
5981 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
5982
5983 return scnprintf(buf, PAGE_SIZE, "%d\n",
Jenny Hoa7d48db2020-12-08 15:22:09 +08005984 batt_drv->ssoc_state.bd_trickle_recharge_soc);
Ken Tsouab4bba02020-11-18 20:39:15 +08005985}
5986
5987#define BD_RL_SOC_FULL 100
Jenny Hoa7d48db2020-12-08 15:22:09 +08005988#define BD_RL_SOC_LOW 50
5989static ssize_t set_bd_trickle_recharge_soc(struct device *dev,
5990 struct device_attribute *attr,
5991 const char *buf, size_t count)
Ken Tsouab4bba02020-11-18 20:39:15 +08005992{
5993 struct power_supply *psy = container_of(dev, struct power_supply, dev);
5994 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
5995 int ret = 0, val;
5996
5997 ret = kstrtoint(buf, 0, &val);
5998 if (ret < 0)
5999 return ret;
6000
Jenny Hoa7d48db2020-12-08 15:22:09 +08006001 if ((val >= BD_RL_SOC_FULL) || (val < BD_RL_SOC_LOW))
Ken Tsouab4bba02020-11-18 20:39:15 +08006002 return count;
6003
Jenny Hoa7d48db2020-12-08 15:22:09 +08006004 batt_drv->ssoc_state.bd_trickle_recharge_soc = val;
Ken Tsouab4bba02020-11-18 20:39:15 +08006005
6006 return count;
6007}
6008
Jenny Hoa7d48db2020-12-08 15:22:09 +08006009static DEVICE_ATTR(bd_trickle_recharge_soc, 0660,
6010 show_bd_trickle_recharge_soc, set_bd_trickle_recharge_soc);
Ken Tsouab4bba02020-11-18 20:39:15 +08006011
Ken Tsou793502d2020-11-19 20:34:34 +08006012static ssize_t show_bd_trickle_dry_run(struct device *dev,
6013 struct device_attribute *attr, char *buf)
6014{
6015 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6016 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6017
6018 return scnprintf(buf, PAGE_SIZE, "%d\n",
6019 batt_drv->ssoc_state.bd_trickle_dry_run);
6020}
6021
6022static ssize_t set_bd_trickle_dry_run(struct device *dev,
6023 struct device_attribute *attr,
6024 const char *buf, size_t count)
6025{
6026 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6027 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6028 int ret = 0, val;
6029
6030 ret = kstrtoint(buf, 0, &val);
6031 if (ret < 0)
6032 return ret;
6033
6034 batt_drv->ssoc_state.bd_trickle_dry_run = val ? true : false;
6035
6036 return count;
6037}
6038
6039static DEVICE_ATTR(bd_trickle_dry_run, 0660,
6040 show_bd_trickle_dry_run, set_bd_trickle_dry_run);
6041
Ken Tsou76ee23d2020-12-03 00:49:48 +08006042static ssize_t show_bd_trickle_reset_sec(struct device *dev,
6043 struct device_attribute *attr,
6044 char *buf)
6045{
6046 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6047 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6048
6049 return scnprintf(buf, PAGE_SIZE, "%d\n",
6050 batt_drv->ssoc_state.bd_trickle_reset_sec);
6051}
6052
6053static ssize_t set_bd_trickle_reset_sec(struct device *dev,
6054 struct device_attribute *attr,
6055 const char *buf, size_t count)
6056{
6057 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6058 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6059 unsigned int val;
6060 int ret = 0;
6061
6062 ret = kstrtouint(buf, 0, &val);
6063 if (ret < 0)
6064 return ret;
6065
6066 batt_drv->ssoc_state.bd_trickle_reset_sec = val;
6067
6068 return count;
6069}
6070
6071static DEVICE_ATTR(bd_trickle_reset_sec, 0660,
6072 show_bd_trickle_reset_sec, set_bd_trickle_reset_sec);
6073
Ken Tsou37471122021-07-23 22:39:57 +08006074static ssize_t bd_clear_store(struct device *dev,
6075 struct device_attribute *attr,
6076 const char *buf, size_t count)
6077{
6078 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6079 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6080 int ret = 0, val = 0;
6081
6082 ret = kstrtoint(buf, 0, &val);
6083 if (ret < 0)
6084 return ret;
6085
6086 if (val)
Stephane Leecacee1f2021-09-22 11:51:38 -07006087 bd_trickle_reset(&batt_drv->ssoc_state, &batt_drv->ce_data);
Ken Tsou37471122021-07-23 22:39:57 +08006088
6089 return count;
6090}
6091
6092static DEVICE_ATTR_WO(bd_clear);
6093
Jenny Ho6f7ec522020-05-19 09:04:53 +08006094static ssize_t batt_show_time_to_ac(struct device *dev,
6095 struct device_attribute *attr, char *buf)
6096{
6097 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6098 struct batt_drv *batt_drv =(struct batt_drv *)
6099 power_supply_get_drvdata(psy);
6100 const int soc = CHG_HEALTH_REST_SOC(&batt_drv->chg_health);
6101 qnum_t soc_raw = ssoc_get_capacity_raw(&batt_drv->ssoc_state);
6102 qnum_t soc_health = qnum_fromint(soc);
6103 ktime_t estimate;
6104 int rc;
6105
6106 rc = ttf_soc_estimate(&estimate, &batt_drv->ttf_stats,
6107 &batt_drv->ce_data, soc_raw,
6108 soc_health - qnum_rconst(SOC_ROUND_BASE));
6109 if (rc < 0)
6110 estimate = -1;
6111
6112 if (estimate == -1)
6113 return -ERANGE;
6114
6115 return scnprintf(buf, PAGE_SIZE, "%lld\n", (long long)estimate);
6116}
6117
6118static const DEVICE_ATTR(time_to_ac, 0444, batt_show_time_to_ac, NULL);
6119
6120static ssize_t batt_show_ac_soc(struct device *dev,
6121 struct device_attribute *attr, char *buf)
6122{
6123 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6124 struct batt_drv *batt_drv =(struct batt_drv *)
6125 power_supply_get_drvdata(psy);
6126
6127 return scnprintf(buf, PAGE_SIZE, "%d\n",
6128 CHG_HEALTH_REST_SOC(&batt_drv->chg_health));
6129}
6130
6131static const DEVICE_ATTR(ac_soc, 0444, batt_show_ac_soc, NULL);
6132
6133
AleX Pelosifa623992021-03-29 14:39:39 -07006134static ssize_t batt_show_charger_state(struct device *dev,
6135 struct device_attribute *attr, char *buf)
6136{
6137 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6138 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6139
6140 return scnprintf(buf, PAGE_SIZE, "0x%llx\n", batt_drv->chg_state.v);
6141}
6142
6143static const DEVICE_ATTR(charger_state, 0444, batt_show_charger_state, NULL);
6144
6145
6146static ssize_t batt_show_charge_type(struct device *dev,
6147 struct device_attribute *attr, char *buf)
6148{
6149 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6150 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6151
6152 return scnprintf(buf, PAGE_SIZE, "%d\n", batt_drv->chg_state.f.chg_type);
6153}
6154
6155static const DEVICE_ATTR(charge_type, 0444, batt_show_charge_type, NULL);
6156
AleX Pelosi0a9c7342021-04-05 12:45:39 -07006157
6158static ssize_t batt_show_constant_charge_current(struct device *dev,
6159 struct device_attribute *attr, char *buf)
6160{
6161 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6162 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6163
6164 return scnprintf(buf, PAGE_SIZE, "%d\n", batt_drv->cc_max);
6165}
6166
6167static const DEVICE_ATTR(constant_charge_current, 0444,
6168 batt_show_constant_charge_current, NULL);
6169
6170
6171static ssize_t batt_show_constant_charge_voltage(struct device *dev,
6172 struct device_attribute *attr, char *buf)
6173{
6174 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6175 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6176
6177 return scnprintf(buf, PAGE_SIZE, "%d\n", batt_drv->fv_uv);
6178}
6179
6180static const DEVICE_ATTR(constant_charge_voltage, 0444,
6181 batt_show_constant_charge_voltage, NULL);
6182
yihsiangpengbb8ae642021-04-27 17:46:34 +08006183static ssize_t fan_level_store(struct device *dev,
6184 struct device_attribute *attr,
6185 const char *buf, size_t count) {
6186 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6187 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6188 int ret = 0;
6189 int level;
6190
6191 ret = kstrtoint(buf, 0, &level);
6192 if (ret < 0)
6193 return ret;
6194
yihsiangpeng305623d2021-05-06 15:07:05 +08006195 if ((level < FAN_LVL_UNKNOWN) || (level > FAN_LVL_ALARM))
yihsiangpengbb8ae642021-04-27 17:46:34 +08006196 return -ERANGE;
6197
6198 batt_drv->fan_level = level;
AleX Pelosia7e0da82021-07-15 13:09:42 -07006199
6200 /* always send a power supply event when forcing the value */
6201 if (batt_drv->psy)
6202 power_supply_changed(batt_drv->psy);
yihsiangpengbb8ae642021-04-27 17:46:34 +08006203
6204 return count;
6205}
6206
6207static ssize_t fan_level_show(struct device *dev,
6208 struct device_attribute *attr, char *buf)
6209{
6210 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6211 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
yihsiangpeng305623d2021-05-06 15:07:05 +08006212 int result = 0;
yihsiangpengbb8ae642021-04-27 17:46:34 +08006213
yihsiangpeng305623d2021-05-06 15:07:05 +08006214 if (batt_drv->fan_level == -1 && batt_drv->fan_level_votable)
Ken Tsoua15d1fa2022-01-24 14:42:27 +08006215 result = gvotable_get_current_int_vote(
6216 batt_drv->fan_level_votable);
yihsiangpeng305623d2021-05-06 15:07:05 +08006217 else
6218 result = batt_drv->fan_level;
yihsiangpengbb8ae642021-04-27 17:46:34 +08006219
yihsiangpeng305623d2021-05-06 15:07:05 +08006220 return scnprintf(buf, PAGE_SIZE, "%d\n", result);
yihsiangpengbb8ae642021-04-27 17:46:34 +08006221}
6222
6223static const DEVICE_ATTR_RW(fan_level);
Jenny Ho47467492021-05-19 17:29:19 +08006224
6225static ssize_t show_health_safety_margin(struct device *dev,
6226 struct device_attribute *attr, char *buf)
6227{
6228 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6229 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6230
6231 return scnprintf(buf, PAGE_SIZE, "%d\n",
6232 batt_drv->health_safety_margin);
6233}
6234
6235static ssize_t set_health_safety_margin(struct device *dev,
6236 struct device_attribute *attr,
6237 const char *buf, size_t count)
6238{
6239 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6240 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6241 int ret = 0, val;
6242
6243 ret = kstrtoint(buf, 0, &val);
6244 if (ret < 0)
6245 return ret;
6246
6247 /*
6248 * less than 0 is not accaptable: we will not reach full in time.
6249 * set to 0 to disable PAUSE but keep AC charge
6250 */
6251 if (val < 0)
6252 val = 0;
6253
6254 batt_drv->health_safety_margin = val;
6255
6256 return count;
6257}
6258
6259static DEVICE_ATTR(health_safety_margin, 0660,
6260 show_health_safety_margin, set_health_safety_margin);
6261
AleX Pelosic1cd4752022-05-04 02:15:02 -07006262/* BPST ------------------------------------------------------------------- */
6263
Jack Wu867a8432021-11-23 22:28:28 +08006264static ssize_t bpst_reset_store(struct device *dev,
6265 struct device_attribute *attr,
6266 const char *buf, size_t count)
6267{
6268 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6269 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6270 struct batt_bpst *bpst_state = &batt_drv->bpst_state;
6271 int ret = 0, val = 0;
6272
6273 ret = kstrtoint(buf, 0, &val);
6274 if (ret < 0)
6275 return ret;
6276
6277 if (val) {
6278 ret = batt_bpst_reset(bpst_state);
6279 if (ret < 0)
6280 pr_err("%s: MSC_BPST: Cannot reset GBMS_TAG_BPST (%d)\n", __func__, ret);
6281 }
6282
6283 return count;
6284}
6285
6286static DEVICE_ATTR_WO(bpst_reset);
6287
Jack Wub0a68212021-11-22 22:33:00 +08006288static ssize_t show_bpst_detect_disable(struct device *dev,
6289 struct device_attribute *attr, char *buf)
6290{
6291 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6292 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6293
6294 return scnprintf(buf, PAGE_SIZE, "%d\n",
6295 batt_drv->bpst_state.bpst_detect_disable);
6296}
6297
6298static ssize_t set_bpst_detect_disable(struct device *dev,
6299 struct device_attribute *attr,
6300 const char *buf, size_t count)
6301{
6302 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6303 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6304 int ret = 0, val;
6305
6306 ret = kstrtoint(buf, 0, &val);
6307 if (ret < 0)
6308 return ret;
6309
6310 mutex_lock(&batt_drv->bpst_state.lock);
6311 batt_drv->bpst_state.bpst_detect_disable = !!val;
6312 mutex_unlock(&batt_drv->bpst_state.lock);
6313 if (batt_drv->psy)
6314 power_supply_changed(batt_drv->psy);
6315
6316 return count;
6317}
6318
6319static DEVICE_ATTR(bpst_detect_disable, 0660,
6320 show_bpst_detect_disable, set_bpst_detect_disable);
6321
AleX Pelosic1cd4752022-05-04 02:15:02 -07006322/* AACR ------------------------------------------------------------------- */
AleX Pelosic10eb8b2021-12-14 13:20:21 -08006323
Jenny Ho23314e62021-11-19 08:58:25 +08006324static ssize_t aacr_state_store(struct device *dev,
6325 struct device_attribute *attr,
6326 const char *buf, size_t count) {
6327 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6328 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
Jenny Ho24fcef62022-07-14 10:45:04 +00006329 int val, state, algo, ret = 0;
Jenny Ho23314e62021-11-19 08:58:25 +08006330
Jenny Ho24fcef62022-07-14 10:45:04 +00006331 ret = kstrtoint(buf, 0, &val);
Jenny Ho23314e62021-11-19 08:58:25 +08006332 if (ret < 0)
6333 return ret;
6334
Jenny Ho24fcef62022-07-14 10:45:04 +00006335 if (val < BATT_AACR_DISABLED) /* not allow minus value */
Jenny Ho23314e62021-11-19 08:58:25 +08006336 return -ERANGE;
6337
Jenny Ho24fcef62022-07-14 10:45:04 +00006338 switch (val) {
6339 case BATT_AACR_DISABLED:
6340 state = BATT_AACR_DISABLED;
6341 break;
6342 case BATT_AACR_ENABLED:
6343 state = BATT_AACR_ENABLED;
6344 algo = BATT_AACR_ALGO_DEFAULT;
6345 break;
6346 case BATT_AACR_ALGO_LOW_B:
6347 state = BATT_AACR_ENABLED;
6348 algo = BATT_AACR_ALGO_LOW_B;
6349 break;
6350 default:
6351 return -ERANGE;
6352 }
6353
6354 if (batt_drv->aacr_state == state && batt_drv->aacr_algo == algo)
Jenny Ho23314e62021-11-19 08:58:25 +08006355 return count;
6356
Jenny Ho24fcef62022-07-14 10:45:04 +00006357 pr_info("aacr_state: %d -> %d, aacr_algo: %d -> %d\n",
6358 batt_drv->aacr_state, state, batt_drv->aacr_algo, algo);
Jenny Ho23314e62021-11-19 08:58:25 +08006359 batt_drv->aacr_state = state;
Jenny Ho24fcef62022-07-14 10:45:04 +00006360 batt_drv->aacr_algo = algo;
Jenny Ho23314e62021-11-19 08:58:25 +08006361 return count;
6362}
6363
6364static ssize_t aacr_state_show(struct device *dev,
6365 struct device_attribute *attr, char *buf)
6366{
6367 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6368 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6369
6370 return scnprintf(buf, PAGE_SIZE, "%d\n", batt_drv->aacr_state);
6371}
6372
6373static const DEVICE_ATTR_RW(aacr_state);
6374
AleX Pelosic10eb8b2021-12-14 13:20:21 -08006375
6376static ssize_t aacr_cycle_grace_store(struct device *dev,
6377 struct device_attribute *attr,
6378 const char *buf, size_t count)
6379{
6380 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6381 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6382 int value, ret = 0;
6383
6384 ret = kstrtoint(buf, 0, &value);
6385 if (ret < 0)
6386 return ret;
6387
6388 batt_drv->aacr_cycle_grace = value;
6389 return count;
6390}
6391
6392static ssize_t aacr_cycle_grace_show(struct device *dev,
6393 struct device_attribute *attr, char *buf)
6394{
6395 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6396 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6397
6398 return scnprintf(buf, PAGE_SIZE, "%d\n", batt_drv->aacr_cycle_grace);
6399}
6400
6401static const DEVICE_ATTR_RW(aacr_cycle_grace);
6402
6403
6404static ssize_t aacr_cycle_max_store(struct device *dev,
6405 struct device_attribute *attr,
6406 const char *buf, size_t count)
6407{
6408 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6409 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6410 int value, ret = 0;
6411
6412 ret = kstrtoint(buf, 0, &value);
6413 if (ret < 0)
6414 return ret;
6415
6416 batt_drv->aacr_cycle_max = value;
6417 return count;
6418}
6419
6420static ssize_t aacr_cycle_max_show(struct device *dev,
6421 struct device_attribute *attr, char *buf)
6422{
6423 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6424 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6425
6426 return scnprintf(buf, PAGE_SIZE, "%d\n", batt_drv->aacr_cycle_max);
6427}
6428
6429static const DEVICE_ATTR_RW(aacr_cycle_max);
6430
Jenny Ho24fcef62022-07-14 10:45:04 +00006431static ssize_t aacr_algo_show(struct device *dev,
6432 struct device_attribute *attr, char *buf)
6433{
6434 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6435 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6436
6437 return scnprintf(buf, PAGE_SIZE, "%d\n", batt_drv->aacr_algo);
6438}
6439
6440static const DEVICE_ATTR_RO(aacr_algo);
6441
AleX Pelosic1cd4752022-05-04 02:15:02 -07006442/* Swelling --------------------------------------------------------------- */
6443
Jenny Hoc6f13052022-01-03 10:52:21 +08006444static ssize_t swelling_data_show(struct device *dev,
6445 struct device_attribute *attr, char *buf)
6446{
6447 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6448 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6449 struct swelling_data *sd = &batt_drv->sd;
6450 int len = 0, i;
6451
6452 len += scnprintf(&buf[len], PAGE_SIZE - len,
6453 "temp/soc\tcharge(s)\tdischarge(s)\n");
6454 for (i = 0; i < BATT_TEMP_RECORD_THR ; i++) {
6455 len += scnprintf(&buf[len], PAGE_SIZE - len,
6456 "%d/%d\t%llu\t%llu\n",
6457 sd->temp_thr[i]/10, sd->soc_thr[i],
6458 sd->chg[i], sd->dischg[i]);
6459 }
6460
6461 return len;
6462}
6463
6464static const DEVICE_ATTR_RO(swelling_data);
6465
AleX Pelosic1cd4752022-05-04 02:15:02 -07006466/* BHI --------------------------------------------------------------------- */
6467
AleX Pelosi1c2d9de2022-01-20 18:24:19 -08006468static ssize_t health_index_show(struct device *dev,
6469 struct device_attribute *attr, char *buf)
6470{
6471 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6472 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6473
AleX Pelosic1cd4752022-05-04 02:15:02 -07006474 return scnprintf(buf, PAGE_SIZE, "%d\n",
6475 BHI_ROUND_INDEX(batt_drv->health_data.bhi_index));
AleX Pelosi1c2d9de2022-01-20 18:24:19 -08006476}
6477
6478static const DEVICE_ATTR_RO(health_index);
6479
6480static ssize_t health_status_show(struct device *dev,
6481 struct device_attribute *attr, char *buf)
6482{
6483 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6484 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6485
AleX Pelosi43bec422022-04-27 21:59:19 -07006486 return scnprintf(buf, PAGE_SIZE, "%d\n", batt_drv->health_data.bhi_status);
AleX Pelosi1c2d9de2022-01-20 18:24:19 -08006487}
6488
6489static const DEVICE_ATTR_RO(health_status);
6490
AleX Pelosi2ef31792022-05-04 16:15:09 -07006491static ssize_t health_impedance_index_show(struct device *dev,
AleX Pelosi1c2d9de2022-01-20 18:24:19 -08006492 struct device_attribute *attr, char *buf)
6493{
6494 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6495 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6496
AleX Pelosic1cd4752022-05-04 02:15:02 -07006497 return scnprintf(buf, PAGE_SIZE, "%d\n",
6498 BHI_ROUND_INDEX(batt_drv->health_data.bhi_imp_index));
AleX Pelosi1c2d9de2022-01-20 18:24:19 -08006499}
6500
AleX Pelosi2ef31792022-05-04 16:15:09 -07006501static const DEVICE_ATTR_RO(health_impedance_index);
AleX Pelosi1c2d9de2022-01-20 18:24:19 -08006502
AleX Pelosi43bec422022-04-27 21:59:19 -07006503static ssize_t health_capacity_index_show(struct device *dev,
6504 struct device_attribute *attr, char *buf)
6505{
6506 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6507 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6508
AleX Pelosic1cd4752022-05-04 02:15:02 -07006509 return scnprintf(buf, PAGE_SIZE, "%d\n",
6510 BHI_ROUND_INDEX(batt_drv->health_data.bhi_cap_index));
AleX Pelosi43bec422022-04-27 21:59:19 -07006511}
6512
6513static const DEVICE_ATTR_RO(health_capacity_index);
6514
AleX Pelosi43bec422022-04-27 21:59:19 -07006515static ssize_t health_index_stats_show(struct device *dev,
6516 struct device_attribute *attr, char *buf)
6517{
6518 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6519 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6520 struct bhi_data *bhi_data = &batt_drv->health_data.bhi_data;
Jenny Hod4bd5bc2022-08-26 16:08:38 +00006521 struct health_data *health_data = &batt_drv->health_data;
AleX Pelosi43bec422022-04-27 21:59:19 -07006522 int len = 0, i;
6523
AleX Pelosic1cd4752022-05-04 02:15:02 -07006524 mutex_lock(&batt_drv->chg_lock);
AleX Pelosi43bec422022-04-27 21:59:19 -07006525
6526 for (i = 0; i < BHI_ALGO_MAX; i++) {
AleX Pelosic1cd4752022-05-04 02:15:02 -07006527 int health_index, health_status, cap_index, imp_index, sd_index;
6528
Jenny Hoa19f0a72022-07-14 03:17:42 +00006529 cap_index = bhi_calc_cap_index(i, batt_drv);
Jenny Hoe6f05b52022-10-18 16:37:21 +08006530 imp_index = bhi_calc_imp_index(i, health_data);
6531 sd_index = bhi_calc_sd_index(i, health_data);
Jenny Hod4bd5bc2022-08-26 16:08:38 +00006532 health_index = bhi_calc_health_index(i, health_data, cap_index, imp_index, sd_index);
AleX Pelosi897284b2022-07-09 20:04:58 +00006533 health_status = bhi_calc_health_status(i, BHI_ROUND_INDEX(health_index), health_data);
AleX Pelosic1cd4752022-05-04 02:15:02 -07006534 if (health_index < 0)
6535 continue;
6536
AleX Pelosi897284b2022-07-09 20:04:58 +00006537 pr_debug("bhi: %d: %d, %d,%d,%d %d,%d,%d %d,%d\n", i,
6538 health_status, health_index, cap_index, imp_index,
6539 bhi_data->swell_cumulative,
6540 bhi_health_get_capacity(i, bhi_data),
6541 bhi_health_get_impedance(i, bhi_data),
6542 bhi_data->battery_age,
6543 bhi_data->cycle_count);
6544
AleX Pelosi43bec422022-04-27 21:59:19 -07006545
6546 len += scnprintf(&buf[len], PAGE_SIZE - len,
Jack Wu58ea50a2022-06-04 13:22:22 +08006547 "%d: %d, %d,%d,%d %d,%d,%d %d,%d, %d\n",
AleX Pelosic1cd4752022-05-04 02:15:02 -07006548 i, health_status,
6549 BHI_ROUND_INDEX(health_index),
6550 BHI_ROUND_INDEX(cap_index),
6551 BHI_ROUND_INDEX(imp_index),
6552 bhi_data->swell_cumulative,
6553 bhi_health_get_capacity(i, bhi_data),
AleX Pelosia3f22de2022-05-03 23:42:24 -07006554 bhi_health_get_impedance(i, bhi_data),
AleX Pelosi43bec422022-04-27 21:59:19 -07006555 bhi_data->battery_age,
Jack Wu58ea50a2022-06-04 13:22:22 +08006556 bhi_data->cycle_count,
6557 batt_bpst_stats_update(batt_drv));
AleX Pelosi43bec422022-04-27 21:59:19 -07006558 }
6559
AleX Pelosic1cd4752022-05-04 02:15:02 -07006560 mutex_unlock(&batt_drv->chg_lock);
6561
AleX Pelosi43bec422022-04-27 21:59:19 -07006562 return len;
6563}
6564
6565static const DEVICE_ATTR_RO(health_index_stats);
6566
AleX Pelosi1c2d9de2022-01-20 18:24:19 -08006567static ssize_t health_algo_store(struct device *dev,
6568 struct device_attribute *attr,
6569 const char *buf, size_t count)
6570{
6571 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6572 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6573 int value, ret;
6574
6575 ret = kstrtoint(buf, 0, &value);
6576 if (ret < 0)
6577 return ret;
6578
AleX Pelosic1cd4752022-05-04 02:15:02 -07006579 mutex_lock(&batt_drv->chg_lock);
AleX Pelosi43bec422022-04-27 21:59:19 -07006580 batt_drv->health_data.bhi_algo = value;
AleX Pelosic1cd4752022-05-04 02:15:02 -07006581 ret = batt_bhi_stats_update_all(batt_drv);
6582 mutex_unlock(&batt_drv->chg_lock);
6583
6584 if (ret < 0)
6585 return ret;
6586
AleX Pelosi1c2d9de2022-01-20 18:24:19 -08006587 return count;
6588}
6589
6590static ssize_t health_algo_show(struct device *dev,
6591 struct device_attribute *attr, char *buf)
6592{
6593 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6594 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6595
AleX Pelosi43bec422022-04-27 21:59:19 -07006596 return scnprintf(buf, PAGE_SIZE, "%d\n", batt_drv->health_data.bhi_algo);
AleX Pelosi1c2d9de2022-01-20 18:24:19 -08006597}
6598
6599static const DEVICE_ATTR_RW(health_algo);
6600
AleX Pelosic1cd4752022-05-04 02:15:02 -07006601/* CSI --------------------------------------------------------------------- */
6602
Jenny Ho09637822022-04-18 09:02:57 +08006603static ssize_t charging_speed_store(struct device *dev,
6604 struct device_attribute *attr,
6605 const char *buf, size_t count)
6606{
6607 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6608 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6609 int value, ret;
6610
6611 ret = kstrtoint(buf, 0, &value);
6612 if (ret < 0)
6613 return ret;
6614
6615 pr_info("fake_charging_speed: %d -> %d\n", batt_drv->fake_charging_speed, value);
6616 batt_drv->fake_charging_speed = value;
6617
6618 return count;
6619}
6620
AleX Pelosi1c2d9de2022-01-20 18:24:19 -08006621static ssize_t charging_speed_show(struct device *dev,
6622 struct device_attribute *attr, char *buf)
6623{
6624 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6625 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6626
AleX Pelosi9bfe2312022-04-26 13:20:43 -07006627 return scnprintf(buf, PAGE_SIZE, "%d\n", batt_drv->csi_current_speed);
AleX Pelosi1c2d9de2022-01-20 18:24:19 -08006628}
6629
Jenny Ho09637822022-04-18 09:02:57 +08006630static const DEVICE_ATTR_RW(charging_speed);
AleX Pelosi1c2d9de2022-01-20 18:24:19 -08006631
Wasb Liuf40cbb22022-01-26 18:41:03 +08006632static ssize_t power_metrics_polling_rate_store(struct device *dev,
6633 struct device_attribute *attr,
6634 const char *buf, size_t count)
6635{
6636 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6637 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6638 unsigned int value, ret = 0;
6639
6640 ret = kstrtouint(buf, 0, &value);
6641 if (ret < 0)
6642 return ret;
6643 if (value <= 0)
6644 return -EINVAL;
6645
6646 batt_drv->power_metrics.polling_rate = value;
6647 return count;
6648}
6649
6650static ssize_t power_metrics_polling_rate_show(struct device *dev,
6651 struct device_attribute *attr, char *buf)
6652{
6653 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6654 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6655
6656 return scnprintf(buf, PAGE_SIZE, "%d\n", batt_drv->power_metrics.polling_rate);
6657}
6658
6659static const DEVICE_ATTR_RW(power_metrics_polling_rate);
6660
6661static ssize_t power_metrics_interval_store(struct device *dev,
6662 struct device_attribute *attr,
6663 const char *buf, size_t count)
6664{
6665 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6666 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6667 unsigned int value, ret = 0;
6668 const int polling_rate = batt_drv->power_metrics.polling_rate;
6669
6670 ret = kstrtouint(buf, 0, &value);
6671 if (ret < 0)
6672 return ret;
6673 if ((value >= polling_rate * POWER_METRICS_MAX_DATA) || value < polling_rate)
6674 return -EINVAL;
6675
6676 batt_drv->power_metrics.interval = value;
6677 return count;
6678}
6679
6680static ssize_t power_metrics_interval_show(struct device *dev,
6681 struct device_attribute *attr, char *buf)
6682{
6683 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6684 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6685
6686 return scnprintf(buf, PAGE_SIZE, "%d\n", batt_drv->power_metrics.interval);
6687}
6688
6689static const DEVICE_ATTR_RW(power_metrics_interval);
6690
6691static long power_metrics_delta_cc(struct batt_drv *batt_drv, int idx1, int idx2)
6692{
6693 return batt_drv->power_metrics.data[idx1].charge_count -
6694 batt_drv->power_metrics.data[idx2].charge_count;
6695}
6696
6697static long power_metrics_avg_vbat(struct batt_drv *batt_drv, int idx1, int idx2)
6698{
6699 unsigned long v1 = batt_drv->power_metrics.data[idx1].voltage;
6700 unsigned long v2 = batt_drv->power_metrics.data[idx2].voltage;
6701
6702 return (v1 + v2) / 2;
6703}
6704
6705static long power_metrics_delta_time(struct batt_drv *batt_drv, int idx1, int idx2)
6706{
6707 return batt_drv->power_metrics.data[idx1].time -
6708 batt_drv->power_metrics.data[idx2].time;
6709}
6710
6711static ssize_t power_metrics_power_show(struct device *dev,
6712 struct device_attribute *attr, char *buf)
6713{
6714 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6715 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6716 const unsigned int polling_rate = batt_drv->power_metrics.polling_rate;
6717 const unsigned int interval = batt_drv->power_metrics.interval;
6718 const unsigned int pm_idx = batt_drv->power_metrics.idx;
6719 unsigned int step, idx, idx_prev, i;
6720 long cc, vbat, time, time_prev;
6721 long power_avg = 0;
6722
6723 step = interval / polling_rate;
6724 if (step == 0)
6725 return scnprintf(buf, PAGE_SIZE, "Error interval.\n");
6726
6727 idx_prev = pm_idx;
6728 for (i = 1; i <= step; i++) {
6729 if (i > pm_idx)
6730 idx = pm_idx + POWER_METRICS_MAX_DATA - i;
6731 else
6732 idx = pm_idx - i;
6733
6734 if (batt_drv->power_metrics.data[idx].voltage == 0)
6735 return scnprintf(buf, PAGE_SIZE, "Not enough data.\n");
6736 if (power_metrics_delta_time(batt_drv, idx_prev, idx) <= 0)
6737 return scnprintf(buf, PAGE_SIZE, "Time stamp error.\n");
6738
6739 time = power_metrics_delta_time(batt_drv, pm_idx, idx);
6740 if (time < interval) {
6741 /* P += (dCC * V / dT) * (dT / interval) */
6742 cc = power_metrics_delta_cc(batt_drv, idx_prev, idx);
6743 vbat = power_metrics_avg_vbat(batt_drv, idx_prev, idx);
6744 power_avg += (cc * vbat) / interval;
6745 idx_prev = idx;
6746 continue;
6747 }
6748
6749 if (i == 1) {
6750 /* P = dCC * V / dT */
6751 cc = power_metrics_delta_cc(batt_drv, pm_idx, idx);
6752 vbat = power_metrics_avg_vbat(batt_drv, pm_idx, idx);
6753 power_avg = cc * vbat / time;
6754 } else {
6755 /* P += (dCC * V / dT) * (the left time to interval / interval) */
6756 cc = power_metrics_delta_cc(batt_drv, idx_prev, idx);
6757 vbat = power_metrics_avg_vbat(batt_drv, idx_prev, idx);
6758 time = power_metrics_delta_time(batt_drv, idx_prev, idx);
6759 time_prev = power_metrics_delta_time(batt_drv, pm_idx, idx_prev);
6760 power_avg += (cc * vbat * (interval - time_prev) / time ) / interval;
6761 }
6762
6763 break;
6764 }
6765
6766 return scnprintf(buf, PAGE_SIZE, "%ld\n", power_avg / 1000000);
6767}
6768
6769static const DEVICE_ATTR_RO(power_metrics_power);
6770
6771static ssize_t power_metrics_current_show(struct device *dev,
6772 struct device_attribute *attr, char *buf)
6773{
6774 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6775 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6776 const unsigned int polling_rate = batt_drv->power_metrics.polling_rate;
6777 const unsigned int interval = batt_drv->power_metrics.interval;
6778 const unsigned int pm_idx = batt_drv->power_metrics.idx;
6779 unsigned int step, idx, idx_prev, i;
6780 long cc, time, time_prev;
6781 long current_avg = 0;
6782
6783 step = interval / polling_rate;
6784 if (step == 0)
6785 return scnprintf(buf, PAGE_SIZE, "Error interval.\n");
6786
6787 idx_prev = pm_idx;
6788 for (i = 1; i <= step; i++) {
6789 if (i > pm_idx)
6790 idx = pm_idx + POWER_METRICS_MAX_DATA - i;
6791 else
6792 idx = pm_idx - i;
6793
6794 if (batt_drv->power_metrics.data[idx].voltage == 0)
6795 return scnprintf(buf, PAGE_SIZE, "Not enough data.\n");
6796 if (power_metrics_delta_time(batt_drv, idx_prev, idx) <= 0)
6797 return scnprintf(buf, PAGE_SIZE, "Time stamp error.\n");
6798
6799 time = power_metrics_delta_time(batt_drv, pm_idx, idx);
6800 if (time < interval) {
6801 /* I += (dCC / dT) * (dT / interval) */
6802 cc = power_metrics_delta_cc(batt_drv, idx_prev, idx);
6803 current_avg += cc / interval;
6804 idx_prev = idx;
6805 continue;
6806 }
6807
6808 if (i == 1) {
6809 /* I = dCC / dT */
6810 cc = power_metrics_delta_cc(batt_drv, pm_idx, idx);
6811 current_avg = cc / time;
6812 } else {
6813 /* I += (dCC / dT) * (the left time to interval / interval) */
6814 cc = power_metrics_delta_cc(batt_drv, idx_prev, idx);
6815 time = power_metrics_delta_time(batt_drv, idx_prev, idx);
6816 time_prev = power_metrics_delta_time(batt_drv, pm_idx, idx_prev);
6817 current_avg += (cc * (interval - time_prev) / time) / interval;
6818 }
6819
6820 break;
6821 }
6822
6823 return scnprintf(buf, PAGE_SIZE, "%ld\n", current_avg);
6824}
6825
6826static const DEVICE_ATTR_RO(power_metrics_current);
6827
Jenny Hoc5248b32022-02-16 09:48:32 +08006828static ssize_t dev_sn_store(struct device *dev,
6829 struct device_attribute *attr,
6830 const char *buf, size_t count)
6831{
6832 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6833 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
Jenny Ho3e153d82023-02-06 14:00:10 +08006834 const size_t max_len = sizeof(batt_drv->dev_sn);
Jenny Hoc5248b32022-02-16 09:48:32 +08006835
Jenny Ho3e153d82023-02-06 14:00:10 +08006836 if (strlcpy(batt_drv->dev_sn, buf, max_len) >= max_len)
6837 pr_warn("Paired data out of bounds\n");
Jenny Hoc5248b32022-02-16 09:48:32 +08006838
6839 return count;
6840}
6841
6842static ssize_t dev_sn_show(struct device *dev,
6843 struct device_attribute *attr, char *buf)
6844{
6845 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6846 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6847
6848 return scnprintf(buf, PAGE_SIZE, "%s\n", batt_drv->dev_sn);
6849}
6850
6851static const DEVICE_ATTR_RW(dev_sn);
6852
Jenny Ho6a863d72023-02-13 14:58:06 +08006853static ssize_t temp_filter_enable_store(struct device *dev,
6854 struct device_attribute *attr,
6855 const char *buf, size_t count)
6856{
6857 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6858 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6859 struct batt_temp_filter *temp_filter = &batt_drv->temp_filter;
6860 int val, ret;
6861 bool enable;
6862
6863 ret = kstrtoint(buf, 0, &val);
6864 if (ret < 0)
6865 return ret;
6866
6867 enable = val != 0;
6868
6869 if (temp_filter->enable != enable) {
6870 temp_filter->enable = enable;
6871 temp_filter->force_update = true;
6872 mod_delayed_work(system_wq, &temp_filter->work, 0);
6873 }
6874
6875 return count;
6876}
6877
6878static ssize_t temp_filter_enable_show(struct device *dev,
6879 struct device_attribute *attr, char *buf)
6880{
6881 struct power_supply *psy = container_of(dev, struct power_supply, dev);
6882 struct batt_drv *batt_drv = power_supply_get_drvdata(psy);
6883
6884 return scnprintf(buf, PAGE_SIZE, "%d\n", batt_drv->temp_filter.enable);
6885}
6886
6887static const DEVICE_ATTR_RW(temp_filter_enable);
AleX Pelosi8e7fd812019-08-16 10:41:46 -07006888/* ------------------------------------------------------------------------- */
AleX Pelosi664e21e2020-09-01 11:29:51 -07006889
Ken Tsou8acade12020-07-09 03:17:35 +08006890static int batt_init_fs(struct batt_drv *batt_drv)
6891{
Ken Tsou8acade12020-07-09 03:17:35 +08006892 int ret;
6893
6894 /* stats */
6895 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_charge_stats);
6896 if (ret)
AleX Pelosifa623992021-03-29 14:39:39 -07006897 dev_err(&batt_drv->psy->dev, "Failed to create charge_stats\n");
Ken Tsou8acade12020-07-09 03:17:35 +08006898
AleX Pelosifa623992021-03-29 14:39:39 -07006899 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_charge_stats_actual);
Ken Tsou8acade12020-07-09 03:17:35 +08006900 if (ret)
AleX Pelosifa623992021-03-29 14:39:39 -07006901 dev_err(&batt_drv->psy->dev, "Failed to create charge_stats_actual\n");
Ken Tsou8acade12020-07-09 03:17:35 +08006902
AleX Pelosifa623992021-03-29 14:39:39 -07006903 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_charge_details);
Ken Tsou8acade12020-07-09 03:17:35 +08006904 if (ret)
AleX Pelosifa623992021-03-29 14:39:39 -07006905 dev_err(&batt_drv->psy->dev, "Failed to create charge_details\n");
Ken Tsou8acade12020-07-09 03:17:35 +08006906
Stephane Lee02c436e2021-03-23 12:40:17 -07006907 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_ssoc_details);
6908 if (ret)
6909 dev_err(&batt_drv->psy->dev, "Failed to create ssoc_details\n");
6910
AleX Pelosid1c48dd2022-05-04 21:15:09 -07006911 /* adaptive charging */
AleX Pelosifa623992021-03-29 14:39:39 -07006912 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_charge_deadline);
AleX Pelosi8e7fd812019-08-16 10:41:46 -07006913 if (ret)
AleX Pelosi043ffbe2020-06-24 22:48:30 -07006914 dev_err(&batt_drv->psy->dev, "Failed to create chg_deadline\n");
AleX Pelosi8e7fd812019-08-16 10:41:46 -07006915
AleX Pelosi57c74e22020-02-27 19:29:27 -08006916 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_charge_stage);
6917 if (ret)
AleX Pelosi043ffbe2020-06-24 22:48:30 -07006918 dev_err(&batt_drv->psy->dev, "Failed to create charge_stage\n");
6919
6920 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_charge_limit);
6921 if (ret != 0)
6922 dev_err(&batt_drv->psy->dev, "Failed to create charge_limit\n");
AleX Pelosi57c74e22020-02-27 19:29:27 -08006923
Jenny Ho6f7ec522020-05-19 09:04:53 +08006924 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_time_to_ac);
6925 if (ret != 0)
6926 dev_err(&batt_drv->psy->dev, "Failed to create time_to_ac\n");
6927
6928 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_ac_soc);
6929 if (ret != 0)
6930 dev_err(&batt_drv->psy->dev, "Failed to create ac_soc\n");
6931
Stephane Lee463e85d2021-08-16 14:50:36 -07006932 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_charge_deadline_dryrun);
6933 if (ret != 0)
6934 dev_err(&batt_drv->psy->dev, "Failed to create chg_deadline_dryrun\n");
6935
Ken Tsou8acade12020-07-09 03:17:35 +08006936 /* time to full */
6937 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_ttf_stats);
6938 if (ret)
AleX Pelosifa623992021-03-29 14:39:39 -07006939 dev_err(&batt_drv->psy->dev, "Failed to create ttf_stats\n");
Ken Tsou8acade12020-07-09 03:17:35 +08006940
6941 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_ttf_details);
6942 if (ret)
AleX Pelosifa623992021-03-29 14:39:39 -07006943 dev_err(&batt_drv->psy->dev, "Failed to create ttf_details\n");
Ken Tsou8acade12020-07-09 03:17:35 +08006944
Ken Tsouab4bba02020-11-18 20:39:15 +08006945 /* TRICKLE-DEFEND */
Jenny Hoa7d48db2020-12-08 15:22:09 +08006946 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_bd_trickle_enable);
6947 if (ret)
6948 dev_err(&batt_drv->psy->dev, "Failed to create bd_trickle_enable\n");
6949
Ken Tsouab4bba02020-11-18 20:39:15 +08006950 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_bd_trickle_cnt);
6951 if (ret)
6952 dev_err(&batt_drv->psy->dev, "Failed to create bd_trickle_cnt\n");
6953
Jenny Hoa7d48db2020-12-08 15:22:09 +08006954 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_bd_trickle_recharge_soc);
Ken Tsouab4bba02020-11-18 20:39:15 +08006955 if (ret)
Jenny Hoa7d48db2020-12-08 15:22:09 +08006956 dev_err(&batt_drv->psy->dev, "Failed to create bd_trickle_recharge_soc\n");
Ken Tsouab4bba02020-11-18 20:39:15 +08006957
Ken Tsou793502d2020-11-19 20:34:34 +08006958 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_bd_trickle_dry_run);
6959 if (ret)
6960 dev_err(&batt_drv->psy->dev, "Failed to create bd_trickle_dry_run\n");
6961
Ken Tsou76ee23d2020-12-03 00:49:48 +08006962 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_bd_trickle_reset_sec);
6963 if (ret)
6964 dev_err(&batt_drv->psy->dev, "Failed to create bd_trickle_reset_sec\n");
6965
Ken Tsou37471122021-07-23 22:39:57 +08006966 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_bd_clear);
6967 if (ret)
6968 dev_err(&batt_drv->psy->dev, "Failed to create bd_clear\n");
6969
AleX Pelosi664e21e2020-09-01 11:29:51 -07006970 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_pairing_state);
Ken Tsou8acade12020-07-09 03:17:35 +08006971 if (ret)
AleX Pelosifa623992021-03-29 14:39:39 -07006972 dev_err(&batt_drv->psy->dev, "Failed to create pairing_state\n");
Ken Tsou8acade12020-07-09 03:17:35 +08006973
AleX Pelosi664e21e2020-09-01 11:29:51 -07006974 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_cycle_counts);
6975 if (ret)
AleX Pelosifa623992021-03-29 14:39:39 -07006976 dev_err(&batt_drv->psy->dev, "Failed to create cycle_counts\n");
AleX Pelosi664e21e2020-09-01 11:29:51 -07006977
AleX Pelosifa623992021-03-29 14:39:39 -07006978 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_charge_full_estimate);
6979 if (ret)
6980 dev_err(&batt_drv->psy->dev, "Failed to create chage_full_estimate\n");
6981
6982 /* google_resistance and resistance */
AleX Pelosi3adb3372020-09-03 00:20:07 -07006983 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_resistance_avg);
6984 if (ret)
AleX Pelosifa623992021-03-29 14:39:39 -07006985 dev_err(&batt_drv->psy->dev, "Failed to create resistance_avg\n");
AleX Pelosi3adb3372020-09-03 00:20:07 -07006986
AleX Pelosifa623992021-03-29 14:39:39 -07006987 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_resistance);
AleX Pelosi3adb3372020-09-03 00:20:07 -07006988 if (ret)
AleX Pelosifa623992021-03-29 14:39:39 -07006989 dev_err(&batt_drv->psy->dev, "Failed to create resistance\n");
AleX Pelosi664e21e2020-09-01 11:29:51 -07006990
AleX Pelosifa623992021-03-29 14:39:39 -07006991 /* monitoring */
6992 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_charger_state);
Jenny Hodaaa8032021-02-04 15:05:24 +08006993 if (ret)
AleX Pelosifa623992021-03-29 14:39:39 -07006994 dev_err(&batt_drv->psy->dev, "Failed to create charger state\n");
6995 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_charge_type);
6996 if (ret)
6997 dev_err(&batt_drv->psy->dev, "Failed to create charge_type\n");
AleX Pelosi0a9c7342021-04-05 12:45:39 -07006998 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_constant_charge_current);
6999 if (ret)
7000 dev_err(&batt_drv->psy->dev, "Failed to create constant charge current\n");
7001 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_constant_charge_voltage);
7002 if (ret)
7003 dev_err(&batt_drv->psy->dev, "Failed to create constant charge voltage\n");
yihsiangpengbb8ae642021-04-27 17:46:34 +08007004 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_fan_level);
7005 if (ret)
7006 dev_err(&batt_drv->psy->dev, "Failed to create fan level\n");
Jenny Ho47467492021-05-19 17:29:19 +08007007 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_health_safety_margin);
7008 if (ret)
7009 dev_err(&batt_drv->psy->dev, "Failed to create health safety margin\n");
AleX Pelosic10eb8b2021-12-14 13:20:21 -08007010 /* aacr */
Jenny Ho23314e62021-11-19 08:58:25 +08007011 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_aacr_state);
7012 if (ret)
7013 dev_err(&batt_drv->psy->dev, "Failed to create aacr state\n");
AleX Pelosic10eb8b2021-12-14 13:20:21 -08007014 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_aacr_cycle_grace);
7015 if (ret)
7016 dev_err(&batt_drv->psy->dev, "Failed to create aacr cycle grace\n");
7017 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_aacr_cycle_max);
7018 if (ret)
7019 dev_err(&batt_drv->psy->dev, "Failed to create aacr cycle max\n");
Jenny Ho24fcef62022-07-14 10:45:04 +00007020 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_aacr_algo);
7021 if (ret)
7022 dev_err(&batt_drv->psy->dev, "Failed to create aacr algo\n");
AleX Pelosi43bec422022-04-27 21:59:19 -07007023
AleX Pelosi1c2d9de2022-01-20 18:24:19 -08007024 /* health and health index */
Jenny Hoc6f13052022-01-03 10:52:21 +08007025 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_swelling_data);
7026 if (ret)
7027 dev_err(&batt_drv->psy->dev, "Failed to create swelling_data\n");
AleX Pelosi1c2d9de2022-01-20 18:24:19 -08007028 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_health_index);
7029 if (ret)
7030 dev_err(&batt_drv->psy->dev, "Failed to create health index\n");
7031 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_health_status);
7032 if (ret)
7033 dev_err(&batt_drv->psy->dev, "Failed to create health status\n");
AleX Pelosi43bec422022-04-27 21:59:19 -07007034 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_health_capacity_index);
7035 if (ret)
7036 dev_err(&batt_drv->psy->dev, "Failed to create health capacity index\n");
7037 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_health_index_stats);
7038 if (ret)
7039 dev_err(&batt_drv->psy->dev, "Failed to create health index stats\n");
AleX Pelosi2ef31792022-05-04 16:15:09 -07007040 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_health_impedance_index);
AleX Pelosi1c2d9de2022-01-20 18:24:19 -08007041 if (ret)
7042 dev_err(&batt_drv->psy->dev, "Failed to create health perf index\n");
7043 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_health_algo);
7044 if (ret)
7045 dev_err(&batt_drv->psy->dev, "Failed to create health algo\n");
AleX Pelosi43bec422022-04-27 21:59:19 -07007046
AleX Pelosi1c2d9de2022-01-20 18:24:19 -08007047 /* csi */
7048 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_charging_speed);
7049 if (ret)
7050 dev_err(&batt_drv->psy->dev, "Failed to create charging speed\n");
Wasb Liuf40cbb22022-01-26 18:41:03 +08007051 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_power_metrics_polling_rate);
7052 if (ret)
7053 dev_err(&batt_drv->psy->dev, "Failed to create power_metrics_polling_rate\n");
7054 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_power_metrics_interval);
7055 if (ret)
7056 dev_err(&batt_drv->psy->dev, "Failed to create power_metrics_interval\n");
7057 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_power_metrics_power);
7058 if (ret)
7059 dev_err(&batt_drv->psy->dev, "Failed to create power_metrics_power\n");
7060 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_power_metrics_current);
7061 if (ret)
7062 dev_err(&batt_drv->psy->dev, "Failed to create power_metrics_current\n");
Jenny Hoc5248b32022-02-16 09:48:32 +08007063 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_dev_sn);
7064 if (ret)
7065 dev_err(&batt_drv->psy->dev, "Failed to create dev sn\n");
Jenny Hodaaa8032021-02-04 15:05:24 +08007066
Jenny Ho6a863d72023-02-13 14:58:06 +08007067 /* temperature filter */
7068 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_temp_filter_enable);
7069 if (ret)
7070 dev_err(&batt_drv->psy->dev, "Failed to create temp_filter_enable\n");
7071
Ken Yang8cd93f42022-06-06 10:02:35 +00007072 return 0;
7073
7074}
7075
7076static int batt_init_debugfs(struct batt_drv *batt_drv)
7077{
7078 struct dentry *de = NULL;
7079
Ken Tsou8acade12020-07-09 03:17:35 +08007080 de = debugfs_create_dir("google_battery", 0);
7081 if (IS_ERR_OR_NULL(de))
7082 return 0;
7083
AleX Pelosi1b7d80b2021-07-15 13:24:52 -07007084 debugfs_create_u32("debug_level", 0644, de, &debug_printk_prlog);
AleX Pelosi78a4bea2020-09-01 19:02:24 -07007085 debugfs_create_file("cycle_count_sync", 0600, de, batt_drv,
7086 &cycle_count_bins_sync_fops);
Jenny Hof98812c2021-06-11 16:35:53 +08007087 debugfs_create_file("ssoc_gdf", 0644, de, batt_drv, &debug_ssoc_gdf_fops);
7088 debugfs_create_file("ssoc_uic", 0644, de, batt_drv, &debug_ssoc_uic_fops);
7089 debugfs_create_file("ssoc_rls", 0444, de, batt_drv, &debug_ssoc_rls_fops);
7090 debugfs_create_file("ssoc_uicurve", 0644, de, batt_drv,
AleX Pelosi78a4bea2020-09-01 19:02:24 -07007091 &debug_ssoc_uicurve_cstr_fops);
7092 debugfs_create_file("force_psy_update", 0400, de, batt_drv,
7093 &debug_force_psy_update_fops);
AleX Pelosifa623992021-03-29 14:39:39 -07007094 debugfs_create_file("pairing_state", 0200, de, batt_drv, &debug_pairing_fops);
AleX Pelosifa623992021-03-29 14:39:39 -07007095 debugfs_create_file("temp", 0400, de, batt_drv, &debug_fake_temp_fops);
AleX Pelosi8c5fa772020-10-03 13:36:56 -07007096 debugfs_create_u32("battery_present", 0600, de,
7097 &batt_drv->fake_battery_present);
Ken Tsou8acade12020-07-09 03:17:35 +08007098
AleX Pelosic1cd4752022-05-04 02:15:02 -07007099 /* history */
7100 debugfs_create_file("blf_state", 0400, de, batt_drv, &debug_blf_state_fops);
7101 debugfs_create_u32("blf_collect_now", 0600, de, &batt_drv->blf_collect_now);
7102
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07007103 /* defender */
7104 debugfs_create_u32("fake_capacity", 0600, de,
7105 &batt_drv->fake_capacity);
7106
Jenny Hob88e7ba2022-01-26 05:20:41 +08007107 /* aacr test */
7108 debugfs_create_u32("fake_aacr_cc", 0600, de,
7109 &batt_drv->fake_aacr_cc);
7110
AleX Pelosid1c48dd2022-05-04 21:15:09 -07007111 /* health charging (adaptive charging) */
AleX Pelosi8e7fd812019-08-16 10:41:46 -07007112 debugfs_create_file("chg_health_thr_soc", 0600, de, batt_drv,
7113 &debug_chg_health_thr_soc_fops);
AleX Pelosi8e7fd812019-08-16 10:41:46 -07007114 debugfs_create_file("chg_health_rest_rate", 0600, de, batt_drv,
7115 &debug_chg_health_rest_rate_fops);
Jenny Ho6ff91bf2022-09-16 15:11:04 +08007116 debugfs_create_file("chg_health_rest_rate_before_trigger", 0600, de, batt_drv,
7117 &debug_chg_health_rest_rate_before_trigger_fops);
AleX Pelosi57c74e22020-02-27 19:29:27 -08007118 debugfs_create_file("chg_health_stage", 0600, de, batt_drv,
7119 &debug_chg_health_stage_fops);
AleX Pelosi8e7fd812019-08-16 10:41:46 -07007120
Jenny Ho68183e82021-12-06 17:39:05 +08007121 /* charging table */
7122 debugfs_create_file("chg_raw_profile", 0644, de, batt_drv,
7123 &debug_chg_raw_profile_fops);
7124
Wasb Liu6120f262022-01-28 08:00:06 +08007125 /* battery virtual sensor*/
7126 debugfs_create_u32("batt_vs_w", 0600, de, &batt_drv->batt_vs_w);
7127
AleX Pelosib3a1a552022-05-03 17:00:11 -07007128 /* power metrics */
Wasb Liuf40cbb22022-01-26 18:41:03 +08007129 debugfs_create_file("power_metrics", 0400, de, batt_drv, &debug_power_metrics_fops);
7130
Jack Wu5b74da42022-03-12 14:09:28 +08007131 /* bhi fullcapnom count */
AleX Pelosid1c48dd2022-05-04 21:15:09 -07007132 debugfs_create_u32("bhi_w_ci", 0644, de, &batt_drv->health_data.bhi_w_ci);
Jenny Hod4bd5bc2022-08-26 16:08:38 +00007133 debugfs_create_u32("bhi_w_pi", 0644, de, &batt_drv->health_data.bhi_w_pi);
7134 debugfs_create_u32("bhi_w_sd", 0644, de, &batt_drv->health_data.bhi_w_sd);
AleX Pelosic1cd4752022-05-04 02:15:02 -07007135 debugfs_create_u32("act_impedance", 0644, de,
7136 &batt_drv->health_data.bhi_data.act_impedance);
Jenny Ho0ffa6c32022-10-13 17:03:19 +08007137 debugfs_create_u32("bhi_debug_cycle_count", 0644, de,
7138 &batt_drv->health_data.bhi_debug_cycle_count);
Jenny Hod4bd5bc2022-08-26 16:08:38 +00007139 debugfs_create_u32("bhi_debug_cap_idx", 0644, de,
7140 &batt_drv->health_data.bhi_debug_cap_index);
7141 debugfs_create_u32("bhi_debug_imp_idx", 0644, de,
7142 &batt_drv->health_data.bhi_debug_imp_index);
7143 debugfs_create_u32("bhi_debug_sd_idx", 0644, de,
7144 &batt_drv->health_data.bhi_debug_sd_index);
7145 debugfs_create_u32("bhi_debug_health_idx", 0644, de,
7146 &batt_drv->health_data.bhi_debug_health_index);
7147 debugfs_create_file("bhi_debug_status", 0644, de, batt_drv,
7148 &debug_bhi_status_fops);
Jack Wu5b74da42022-03-12 14:09:28 +08007149
AleX Pelosib3a1a552022-05-03 17:00:11 -07007150 /* google_resistance, tuning */
AleX Pelosia3f22de2022-05-03 23:42:24 -07007151 debugfs_create_u32("ravg_temp_low", 0644, de,
7152 &batt_drv->health_data.bhi_data.res_state.res_temp_low);
7153 debugfs_create_u32("ravg_temp_high", 0644, de,
7154 &batt_drv->health_data.bhi_data.res_state.res_temp_high);
7155 debugfs_create_u32("ravg_soc_low", 0644, de,
7156 &batt_drv->health_data.bhi_data.res_state.ravg_soc_low);
7157 debugfs_create_u32("ravg_soc_high", 0644, de,
7158 &batt_drv->health_data.bhi_data.res_state.ravg_soc_high);
AleX Pelosib3a1a552022-05-03 17:00:11 -07007159 debugfs_create_file("ravg", 0400, de, batt_drv, &debug_ravg_fops);
7160
Jenny Ho74ae1bf2022-12-12 16:53:24 +08007161 /* battery temperature filter */
7162 debugfs_create_u32("temp_filter_default_interval", 0644, de,
7163 &batt_drv->temp_filter.default_interval);
7164 debugfs_create_u32("temp_filter_fast_interval", 0644, de,
7165 &batt_drv->temp_filter.fast_interval);
7166 debugfs_create_u32("temp_filter_resume_delay_interval", 0644, de,
7167 &batt_drv->temp_filter.resume_delay_time);
Jenny Ho74ae1bf2022-12-12 16:53:24 +08007168
Ken Tsou8acade12020-07-09 03:17:35 +08007169 return 0;
7170}
7171
Jack Wue3ebc172021-11-11 16:20:34 +08007172/* bpst detection */
7173static int batt_bpst_init_fs(struct batt_drv *batt_drv)
7174{
Jack Wub0a68212021-11-22 22:33:00 +08007175 int ret;
Jack Wu867a8432021-11-23 22:28:28 +08007176
Jack Wub0a68212021-11-22 22:33:00 +08007177 if (!batt_drv->bpst_state.bpst_enable)
7178 return 0;
Jack Wue3ebc172021-11-11 16:20:34 +08007179
Jack Wub0a68212021-11-22 22:33:00 +08007180 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_bpst_reset);
7181 if (ret)
7182 dev_err(&batt_drv->psy->dev, "Failed to create bpst_reset\n");
7183 ret = device_create_file(&batt_drv->psy->dev, &dev_attr_bpst_detect_disable);
7184 if (ret)
7185 dev_err(&batt_drv->psy->dev, "Failed to create bpst_detect_disable\n");
Jack Wue3ebc172021-11-11 16:20:34 +08007186
Ken Yang8cd93f42022-06-06 10:02:35 +00007187 return 0;
7188
7189}
7190
7191static int batt_bpst_init_debugfs(struct batt_drv *batt_drv)
7192{
7193 struct dentry *de = NULL;
7194
Jack Wub0a68212021-11-22 22:33:00 +08007195 de = debugfs_create_dir("bpst", 0);
7196 if (IS_ERR_OR_NULL(de))
7197 return 0;
7198
7199 debugfs_create_file("bpst_sbd_status", 0600, de, batt_drv,
7200 &debug_bpst_sbd_status_fops);
7201 debugfs_create_u32("bpst_count_threshold", 0600, de,
7202 &batt_drv->bpst_state.bpst_count_threshold);
7203 debugfs_create_u32("bpst_chg_rate", 0600, de,
7204 &batt_drv->bpst_state.bpst_chg_rate);
7205
Jack Wue3ebc172021-11-11 16:20:34 +08007206 return 0;
7207}
7208
Ken Tsou8acade12020-07-09 03:17:35 +08007209/* ------------------------------------------------------------------------- */
7210
7211/* could also use battery temperature, age */
7212static bool gbatt_check_dead_battery(const struct batt_drv *batt_drv)
7213{
7214 return ssoc_get_capacity(&batt_drv->ssoc_state) == 0;
7215}
7216
Jack Wucfb51f72022-10-26 19:55:26 +08007217#define VBATT_CRITICAL_LEVEL 3300000
7218#define VBATT_CRITICAL_DEADLINE_SEC 40
7219
7220static bool gbatt_check_critical_level(const struct batt_drv *batt_drv,
7221 int fg_status)
7222{
7223 const struct batt_ssoc_state *ssoc_state = &batt_drv->ssoc_state;
7224 const int soc = ssoc_get_real(ssoc_state);
7225
7226 if (fg_status == POWER_SUPPLY_STATUS_UNKNOWN)
7227 return true;
7228
7229 if (soc == 0 && ssoc_state->buck_enabled == 1 &&
7230 fg_status == POWER_SUPPLY_STATUS_DISCHARGING) {
7231 const ktime_t now = get_boot_sec();
7232 int vbatt;
7233
7234 /* disable the check */
7235 if (now > VBATT_CRITICAL_DEADLINE_SEC || batt_drv->batt_critical_voltage == 0)
7236 return true;
7237
7238 vbatt = GPSY_GET_PROP(batt_drv->fg_psy, POWER_SUPPLY_PROP_VOLTAGE_NOW);
7239 if (vbatt == -EAGAIN)
7240 return false;
7241
7242 return (vbatt < 0) ? : vbatt < batt_drv->batt_critical_voltage;
7243 }
7244
7245 return false;
7246}
7247
Ken Tsou8acade12020-07-09 03:17:35 +08007248#define SSOC_LEVEL_FULL SSOC_SPOOF
7249#define SSOC_LEVEL_HIGH 80
7250#define SSOC_LEVEL_NORMAL 30
7251#define SSOC_LEVEL_LOW 0
7252
7253/*
7254 * could also use battery temperature, age.
7255 * NOTE: this implementation looks at the SOC% but it might be looking to
7256 * other quantities or flags.
7257 * NOTE: CRITICAL_LEVEL implies BATTERY_DEAD but BATTERY_DEAD doesn't imply
7258 * CRITICAL.
7259 */
Jack Wucfb51f72022-10-26 19:55:26 +08007260static int gbatt_get_capacity_level(const struct batt_drv *batt_drv,
Jack Wu466ce282022-04-29 22:28:14 +08007261 int fg_status)
Ken Tsou8acade12020-07-09 03:17:35 +08007262{
Jack Wucfb51f72022-10-26 19:55:26 +08007263 const struct batt_ssoc_state *ssoc_state = &batt_drv->ssoc_state;
Jenny Hoa1049752022-03-25 17:01:04 +08007264 const int soc = ssoc_get_real(ssoc_state);
Ken Tsou8acade12020-07-09 03:17:35 +08007265 int capacity_level;
7266
Jenny Hoa1049752022-03-25 17:01:04 +08007267 if (soc >= SSOC_LEVEL_FULL) {
Ken Tsou8acade12020-07-09 03:17:35 +08007268 capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
Jenny Hoa1049752022-03-25 17:01:04 +08007269 } else if (soc > SSOC_LEVEL_HIGH) {
Ken Tsou8acade12020-07-09 03:17:35 +08007270 capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_HIGH;
Jenny Hoa1049752022-03-25 17:01:04 +08007271 } else if (soc > SSOC_LEVEL_NORMAL) {
Ken Tsou8acade12020-07-09 03:17:35 +08007272 capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
Jenny Hoa1049752022-03-25 17:01:04 +08007273 } else if (soc > SSOC_LEVEL_LOW) {
Ken Tsou8acade12020-07-09 03:17:35 +08007274 capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
7275 } else if (ssoc_state->buck_enabled == 0) {
7276 capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
7277 } else if (ssoc_state->buck_enabled == -1) {
7278 /* only at startup, this should not happen */
7279 capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
Jack Wucfb51f72022-10-26 19:55:26 +08007280 } else if (gbatt_check_critical_level(batt_drv, fg_status)) {
Ken Tsou8acade12020-07-09 03:17:35 +08007281 capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
7282 } else {
7283 capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
7284 }
7285
7286 return capacity_level;
7287}
7288
Jenny Ho74ae1bf2022-12-12 16:53:24 +08007289static int gbatt_get_temp(struct batt_drv *batt_drv, int *temp)
Ken Tsou8acade12020-07-09 03:17:35 +08007290{
7291 int err = 0;
Ken Tsou8acade12020-07-09 03:17:35 +08007292
Jenny Ho5a2657e2022-12-12 14:36:57 +08007293 if (batt_drv->fake_temp)
Ken Tsou8acade12020-07-09 03:17:35 +08007294 *temp = batt_drv->fake_temp;
Jenny Ho5a2657e2022-12-12 14:36:57 +08007295 else
7296 err = gbatt_get_raw_temp(batt_drv, temp);
Ken Tsou8acade12020-07-09 03:17:35 +08007297
7298 return err;
7299}
7300
Jenny Hoc5248b32022-02-16 09:48:32 +08007301static int batt_do_md5(const u8 *data, unsigned int len, u8 *result)
Ken Tsou8acade12020-07-09 03:17:35 +08007302{
7303 struct crypto_shash *tfm;
7304 struct shash_desc *shash;
7305 int size, ret = 0;
7306
Jenny Hoc5248b32022-02-16 09:48:32 +08007307 tfm = crypto_alloc_shash("md5", 0, 0);
Ken Tsou8acade12020-07-09 03:17:35 +08007308 if (IS_ERR(tfm)) {
Jenny Hoc5248b32022-02-16 09:48:32 +08007309 pr_err("Error MD5 transform: %ld\n", PTR_ERR(tfm));
Ken Tsou8acade12020-07-09 03:17:35 +08007310 return PTR_ERR(tfm);
7311 }
7312
7313 size = sizeof(struct shash_desc) + crypto_shash_descsize(tfm);
7314 shash = kmalloc(size, GFP_KERNEL);
7315 if (!shash)
7316 return -ENOMEM;
7317
7318 shash->tfm = tfm;
7319 ret = crypto_shash_digest(shash, data, len, result);
7320 kfree(shash);
7321 crypto_free_shash(tfm);
7322
7323 return ret;
7324}
7325
Ken Tsou8acade12020-07-09 03:17:35 +08007326/* called with a lock on ->chg_lock */
AleX Pelosi76a3e5b2022-01-06 13:57:18 -08007327static enum batt_paired_state batt_check_pairing_state(struct batt_drv *batt_drv)
Ken Tsou8acade12020-07-09 03:17:35 +08007328{
Ken Tsou8acade12020-07-09 03:17:35 +08007329 char dev_info[GBMS_DINF_LEN];
7330 char mfg_info[GBMS_MINF_LEN];
7331 u8 *dev_info_check = batt_drv->dev_info_check;
Jenny Hoc5248b32022-02-16 09:48:32 +08007332 int ret, len;
7333
7334 len = strlen(batt_drv->dev_sn);
7335
7336 /* No dev_sn, return current state */
7337 if (len == 0)
7338 return batt_drv->pairing_state;
Ken Tsou8acade12020-07-09 03:17:35 +08007339
7340 ret = gbms_storage_read(GBMS_TAG_DINF, dev_info, GBMS_DINF_LEN);
7341 if (ret < 0) {
7342 pr_err("Read device pairing info failed, ret=%d\n", ret);
7343 return BATT_PAIRING_READ_ERROR;
7344 }
7345
7346 if (batt_drv->dev_info_check[0] == 0) {
Ken Tsou68d44d12020-11-11 00:01:40 +08007347 char data[DEV_SN_LENGTH + GBMS_MINF_LEN];
Ken Tsou8acade12020-07-09 03:17:35 +08007348
AleX Pelosi3adb3372020-09-03 00:20:07 -07007349 ret = gbms_storage_read(GBMS_TAG_MINF, mfg_info, GBMS_MINF_LEN);
Ken Tsou8acade12020-07-09 03:17:35 +08007350 if (ret < 0) {
7351 pr_err("read mfg info. fail, ret=%d\n", ret);
7352 return BATT_PAIRING_READ_ERROR;
7353 }
7354
Jenny Hoc5248b32022-02-16 09:48:32 +08007355 memcpy(data, batt_drv->dev_sn, len);
Ken Tsou8acade12020-07-09 03:17:35 +08007356 memcpy(&data[len], mfg_info, GBMS_MINF_LEN);
7357
Jenny Hoc5248b32022-02-16 09:48:32 +08007358 ret = batt_do_md5(data, len + GBMS_MINF_LEN, dev_info_check);
Ken Tsou8acade12020-07-09 03:17:35 +08007359 if (ret < 0) {
Jenny Hoc5248b32022-02-16 09:48:32 +08007360 pr_err("execute batt_do_md5 fail, ret=%d\n", ret);
Ken Tsou8acade12020-07-09 03:17:35 +08007361 return BATT_PAIRING_MISMATCH;
7362 }
Ken Tsou8acade12020-07-09 03:17:35 +08007363 }
7364
7365 /* new battery: pair the battery to this device */
7366 if (dev_info[0] == 0xFF) {
7367
Jenny Hoc5248b32022-02-16 09:48:32 +08007368 ret = gbms_storage_write(GBMS_TAG_DINF, dev_info_check, GBMS_DINF_LEN);
Ken Tsou8acade12020-07-09 03:17:35 +08007369 if (ret < 0) {
7370 pr_err("Pairing to this device failed, ret=%d\n", ret);
7371 return BATT_PAIRING_WRITE_ERROR;
7372 }
7373
7374 /* recycled battery */
7375 } else if (strncmp(dev_info, dev_info_check, strlen(dev_info_check))) {
Ken Tsou8acade12020-07-09 03:17:35 +08007376 pr_warn("Battery paired to a different device\n");
7377
7378 return BATT_PAIRING_MISMATCH;
7379 }
7380
7381 return BATT_PAIRING_PAIRED;
7382}
7383
AleX Pelosi78a4bea2020-09-01 19:02:24 -07007384/* TODO: handle history collection, use storage */
Jenny Ho11d5fcf2020-12-01 06:39:41 +08007385static int batt_hist_data_collect(void *h, int idx)
AleX Pelosi78a4bea2020-09-01 19:02:24 -07007386{
Jenny Ho11d5fcf2020-12-01 06:39:41 +08007387 int cnt;
AleX Pelosi78a4bea2020-09-01 19:02:24 -07007388
Jenny Ho11d5fcf2020-12-01 06:39:41 +08007389 cnt = gbms_storage_read(GBMS_TAG_CLHI, h, 0);
7390 if (cnt > 0)
7391 cnt = gbms_storage_write_data(GBMS_TAG_HIST, h, cnt, idx);
7392
7393 return cnt;
AleX Pelosi78a4bea2020-09-01 19:02:24 -07007394}
7395
7396/* TODO: handle history collection, use storage */
7397static void batt_hist_free_data(void *p)
7398{
Jenny Ho11d5fcf2020-12-01 06:39:41 +08007399 if (p)
7400 kfree(p);
AleX Pelosi78a4bea2020-09-01 19:02:24 -07007401}
7402
Jenny Ho7d8c2212022-01-05 18:07:14 +08007403/* save data in hours */
7404#define SAVE_UNIT 3600
7405static void gbatt_save_sd(struct swelling_data *sd)
7406{
7407 u16 sd_saved[BATT_SD_SAVE_SIZE];
7408 bool update_save_data = false;
7409 int i, j, ret = 0;
7410
7411 /* Change seconds to hours */
7412 for (i = 0; i < BATT_TEMP_RECORD_THR; i++) {
7413 j = i + SD_DISCHG_START;
7414
7415 sd_saved[i] = sd->chg[i] / SAVE_UNIT < BATT_SD_MAX_HOURS ?
7416 sd->chg[i] / SAVE_UNIT : BATT_SD_MAX_HOURS;
7417
7418 sd_saved[j] = sd->dischg[i] / SAVE_UNIT < BATT_SD_MAX_HOURS ?
7419 sd->dischg[i] / SAVE_UNIT : BATT_SD_MAX_HOURS;
7420
7421 if (sd_saved[i] > sd->saved[i] ||
7422 sd_saved[j] > sd->saved[j]) {
7423 sd->saved[i] = sd_saved[i];
7424 sd->saved[j] = sd_saved[j];
7425 update_save_data = true;
7426 }
7427 }
7428
7429 if (!update_save_data)
7430 return;
7431
7432 ret = gbms_storage_write(GBMS_TAG_STRD, &sd->saved, sizeof(sd->saved));
7433 if (ret < 0)
7434 pr_warn("Failed to save swelling data, ret=%d\n", ret);
7435}
7436
Jenny Hoc6f13052022-01-03 10:52:21 +08007437static void gbatt_record_over_temp(struct batt_drv *batt_drv)
7438{
7439 struct swelling_data *sd = &batt_drv->sd;
7440 struct batt_ssoc_state *ssoc_state = &batt_drv->ssoc_state;
7441 const bool charge = batt_drv->fg_status == POWER_SUPPLY_STATUS_CHARGING ||
7442 (batt_drv->fg_status == POWER_SUPPLY_STATUS_FULL &&
7443 !batt_drv->chg_done);
7444 const int temp = batt_drv->batt_temp;
7445 const int soc = ssoc_get_real(ssoc_state);
7446 const ktime_t now = get_boot_sec();
Jenny Ho7d8c2212022-01-05 18:07:14 +08007447 const ktime_t elap = now - sd->last_update;
7448 bool update_data = false;
Jenny Hoc6f13052022-01-03 10:52:21 +08007449 int i;
7450
7451 for (i = 0; i < BATT_TEMP_RECORD_THR; i++) {
Jenny Ho7d8c2212022-01-05 18:07:14 +08007452 /*
7453 * thresholds table:
7454 * | i | 0 | 1 | 2 |
7455 * |----------|--------|--------|--------|
7456 * | temp_thr | 30degC | 35degC | 40degC |
7457 * | soc_thr | 90% | 90% | 95% |
7458 */
Jenny Hoc6f13052022-01-03 10:52:21 +08007459 if (temp < sd->temp_thr[i] || soc < sd->soc_thr[i])
7460 continue;
7461
7462 if (charge)
7463 sd->chg[i] += elap;
7464 else
7465 sd->dischg[i] += elap;
Jenny Ho7d8c2212022-01-05 18:07:14 +08007466
7467 update_data = true;
Jenny Hoc6f13052022-01-03 10:52:21 +08007468 }
7469
Jenny Ho7d8c2212022-01-05 18:07:14 +08007470 if (update_data)
7471 gbatt_save_sd(&batt_drv->sd);
7472
Jenny Hoc6f13052022-01-03 10:52:21 +08007473 sd->last_update = now;
7474}
7475
Jenny Ho87fef342020-10-27 17:58:08 +08007476static int gbatt_save_capacity(struct batt_ssoc_state *ssoc_state)
7477{
7478 const int ui_soc = ssoc_get_capacity(ssoc_state);
7479 const int gdf_soc = ssoc_get_real(ssoc_state);
7480 int ret = 0, save_now;
7481
7482 if (!ssoc_state->save_soc_available)
7483 return ret;
7484
7485 if (ui_soc == gdf_soc)
7486 save_now = 0;
7487 else
7488 save_now = ui_soc;
7489
7490 if (ssoc_state->save_soc != (u16)save_now) {
7491 ssoc_state->save_soc = (u16)save_now;
7492 ret = gbms_storage_write(GBMS_TAG_RSOC, &ssoc_state->save_soc,
7493 sizeof(ssoc_state->save_soc));
7494 }
7495
7496 return ret;
7497}
7498
AleX Pelosic1cd4752022-05-04 02:15:02 -07007499/* battery history data collection */
7500static int batt_history_data_work(struct batt_drv *batt_drv)
7501{
7502 int cycle_cnt, idx, ret;
7503
7504 /* TODO: google_battery caches cycle count, should use that */
7505 cycle_cnt = GPSY_GET_PROP(batt_drv->fg_psy,
7506 POWER_SUPPLY_PROP_CYCLE_COUNT);
7507 if (cycle_cnt < 0)
7508 return -EIO;
7509
7510 if (batt_drv->blf_collect_now) {
7511 pr_info("MSC_HIST cycle_cnt:%d->%d saved_cnt=%d\n",
7512 cycle_cnt, batt_drv->blf_collect_now,
7513 batt_drv->hist_data_saved_cnt);
7514
7515 cycle_cnt = batt_drv->blf_collect_now;
7516 batt_drv->hist_data_saved_cnt = cycle_cnt - 1;
7517 batt_drv->blf_collect_now = 0;
7518 }
7519
7520 if (cycle_cnt <= batt_drv->hist_data_saved_cnt)
7521 return 0;
7522
7523 idx = cycle_cnt / batt_drv->hist_delta_cycle_cnt;
7524
7525 /* check if the cycle_cnt is valid */
7526 if (idx >= batt_drv->hist_data_max_cnt)
7527 return -ENOENT;
7528
7529 ret = batt_hist_data_collect(batt_drv->hist_data, idx);
7530 if (ret < 0)
7531 return ret;
7532
7533 batt_drv->hist_data_saved_cnt = cycle_cnt;
7534
7535 pr_info("MSC_HIST Update data with cnt:%d\n", cycle_cnt);
7536
7537 return 0;
7538}
7539
7540/* TODO: read from the HIST tag */
7541#define BATT_ONE_HIST_LEN 12
7542
7543static int google_battery_init_hist_work(struct batt_drv *batt_drv )
7544{
7545 const int one_hist_len = BATT_ONE_HIST_LEN; /* TODO: read from the tag */
7546 int cnt;
7547
7548 /*
7549 * Determine the max number of history entries
7550 * NOTE: gbms_storage will return -EPROBE_DEFER during init
7551 */
7552 cnt = gbms_storage_read_data(GBMS_TAG_HIST, NULL, 0, 0);
7553 if (cnt == -EPROBE_DEFER)
7554 return -EAGAIN;
7555
7556 if (cnt <= 0) {
7557 pr_err("MSC_HIST collect history data not available (%d)\n", cnt);
7558 batt_drv->blf_state = BATT_LFCOLLECT_NOT_AVAILABLE;
7559 return -ENODATA;
7560 }
7561
7562 batt_drv->hist_data = kzalloc(one_hist_len, GFP_KERNEL);
7563 if (!batt_drv->hist_data) {
7564 pr_err("MSC_HIST cannot allocate buffer of size=%d\n",
7565 one_hist_len);
7566 batt_drv->blf_state = BATT_LFCOLLECT_NOT_AVAILABLE;
7567 } else {
7568 batt_drv->blf_state = BATT_LFCOLLECT_COLLECT;
7569 batt_drv->hist_data_max_cnt = cnt;
7570 batt_drv->hist_data_saved_cnt = -1;
7571 }
7572
7573 pr_info("MSC_HIST init_hist_work done, state:%d, cnt:%d",
7574 batt_drv->blf_state, cnt);
7575
7576 return 0;
7577}
7578
Jenny Ho74ae1bf2022-12-12 16:53:24 +08007579#define TEMP_FILTER_DEFAULT_INTERVAL_MS 30000
7580#define TEMP_FILTER_FAST_INTERVAL_MS 3000
7581#define TEMP_FILTER_RESUME_DELAY_MS 1500
Jenny Hoe372b302023-02-13 18:30:31 +08007582#define TEMP_FILTER_LOG_DIFF 50
Jenny Ho74ae1bf2022-12-12 16:53:24 +08007583static void batt_init_temp_filter(struct batt_drv *batt_drv)
7584{
7585 struct batt_temp_filter *temp_filter = &batt_drv->temp_filter;
7586 const struct device_node *node = batt_drv->device->of_node;
7587 u32 tmp;
7588 int ret;
7589
7590 mutex_init(&batt_drv->temp_filter.lock);
7591
7592 ret = of_property_read_u32(node, "google,temp-filter-default-interval", &tmp);
7593 if (ret == 0)
7594 temp_filter->default_interval = tmp;
7595 else
7596 temp_filter->default_interval = TEMP_FILTER_DEFAULT_INTERVAL_MS;
7597
7598 ret = of_property_read_u32(node, "google,temp-filter-fast-interval", &tmp);
7599 if (ret == 0)
7600 temp_filter->fast_interval = tmp;
7601 else
7602 temp_filter->fast_interval = TEMP_FILTER_FAST_INTERVAL_MS;
7603
7604 ret = of_property_read_u32(node, "google,temp-filter-resume-delay", &tmp);
7605 if (ret == 0)
7606 temp_filter->resume_delay_time = tmp;
7607 else
7608 temp_filter->resume_delay_time = TEMP_FILTER_RESUME_DELAY_MS;
7609
7610 /* initial temperature value in first read data */
7611 temp_filter->force_update = true;
7612 mod_delayed_work(system_wq, &temp_filter->work, 0);
7613
Jenny Hoe36436b2023-03-16 08:45:29 +08007614 pr_info("temperature filter: default:%ds, fast:%ds, resume:%dms\n",
Jenny Ho74ae1bf2022-12-12 16:53:24 +08007615 temp_filter->default_interval / 1000, temp_filter->fast_interval / 1000,
7616 temp_filter->resume_delay_time);
7617}
7618
7619static void google_battery_temp_filter_work(struct work_struct *work)
7620{
7621 struct batt_drv *batt_drv = container_of(work, struct batt_drv, temp_filter.work.work);
7622 const union gbms_ce_adapter_details *ad = &batt_drv->ce_data.adapter_details;
7623 struct batt_temp_filter *temp_filter = &batt_drv->temp_filter;
7624 int interval = temp_filter->default_interval;
7625 union power_supply_propval val;
7626 int err = 0, i;
7627
7628 if (!temp_filter->enable || interval == 0)
7629 return;
7630
7631 if (!batt_drv->fg_psy)
7632 goto done;
7633
7634 if (temp_filter->resume_delay) {
7635 interval = temp_filter->resume_delay_time; /* i2c might busy when resume */
7636 temp_filter->resume_delay = false;
7637 temp_filter->force_update = true;
7638 goto done;
7639 }
7640
7641 if (ad->ad_type == CHG_EV_ADAPTER_TYPE_WLC ||
7642 ad->ad_type == CHG_EV_ADAPTER_TYPE_WLC_EPP ||
7643 ad->ad_type == CHG_EV_ADAPTER_TYPE_WLC_SPP)
7644 interval = temp_filter->fast_interval;
7645
7646 err = power_supply_get_property(batt_drv->fg_psy, POWER_SUPPLY_PROP_TEMP, &val);
7647 if (err != 0)
7648 goto done;
7649
Jenny Hoe372b302023-02-13 18:30:31 +08007650 /* logging if big difference */
7651 if (abs(val.intval - temp_filter->sample[temp_filter->last_idx]) > TEMP_FILTER_LOG_DIFF)
Jenny Hoe36436b2023-03-16 08:45:29 +08007652 pr_info("temperature filter: [%d, %d, %d, %d, %d] val:%d idx:%d interval=%dms\n",
Jenny Hoe372b302023-02-13 18:30:31 +08007653 temp_filter->sample[0], temp_filter->sample[1], temp_filter->sample[2],
7654 temp_filter->sample[3], temp_filter->sample[4], val.intval,
7655 temp_filter->last_idx, interval);
7656
Jenny Ho74ae1bf2022-12-12 16:53:24 +08007657 mutex_lock(&temp_filter->lock);
7658 if (temp_filter->force_update) {
7659 temp_filter->force_update = false;
7660 for (i = 0; i < TEMP_SAMPLE_SIZE; i++)
7661 temp_filter->sample[i] = val.intval;
7662 } else {
7663 temp_filter->last_idx = (temp_filter->last_idx + 1) % TEMP_SAMPLE_SIZE;
7664 temp_filter->sample[temp_filter->last_idx] = val.intval;
7665 }
7666 mutex_unlock(&temp_filter->lock);
7667
7668done:
Jenny Hoe36436b2023-03-16 08:45:29 +08007669 pr_debug("temperature filter: [%d, %d, %d, %d, %d] interval=%dms\n",
Jenny Hoe372b302023-02-13 18:30:31 +08007670 temp_filter->sample[0], temp_filter->sample[1], temp_filter->sample[2],
7671 temp_filter->sample[3], temp_filter->sample[4], interval);
Jenny Ho74ae1bf2022-12-12 16:53:24 +08007672 mod_delayed_work(system_wq, &temp_filter->work, msecs_to_jiffies(interval));
7673}
AleX Pelosic1cd4752022-05-04 02:15:02 -07007674
Ken Tsou8acade12020-07-09 03:17:35 +08007675/*
7676 * poll the battery, run SOC%, dead battery, critical.
7677 * scheduled from psy_changed and from timer
7678 */
AleX Pelosibdf1d772021-07-21 00:12:19 -07007679
7680#define UPDATE_INTERVAL_AT_FULL_FACTOR 4
7681
Ken Tsou8acade12020-07-09 03:17:35 +08007682static void google_battery_work(struct work_struct *work)
7683{
7684 struct batt_drv *batt_drv =
7685 container_of(work, struct batt_drv, batt_work.work);
7686 struct power_supply *fg_psy = batt_drv->fg_psy;
7687 struct batt_ssoc_state *ssoc_state = &batt_drv->ssoc_state;
7688 int update_interval = batt_drv->batt_update_interval;
7689 const int prev_ssoc = ssoc_get_capacity(ssoc_state);
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07007690 int present, fg_status, batt_temp, ret;
Ken Tsou8acade12020-07-09 03:17:35 +08007691 bool notify_psy_changed = false;
Ken Tsou8acade12020-07-09 03:17:35 +08007692
7693 pr_debug("battery work item\n");
7694
Ken Tsou5ecf2f42020-07-16 08:26:05 +08007695 __pm_stay_awake(batt_drv->batt_ws);
Ken Tsou8acade12020-07-09 03:17:35 +08007696
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07007697 /* chg_lock protect msc_logic */
7698 mutex_lock(&batt_drv->chg_lock);
7699
7700 present = GPSY_GET_PROP(fg_psy, POWER_SUPPLY_PROP_PRESENT);
7701 if (present && !batt_drv->batt_present) {
AleX Pelosi88e55942021-06-19 12:01:22 -07007702 pr_debug("%s: change of battery state %d->%d\n",
7703 __func__, batt_drv->batt_present, present);
7704
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07007705 batt_drv->batt_present = true;
7706 notify_psy_changed = true;
7707 } else if (!present && batt_drv->batt_present) {
AleX Pelosi88e55942021-06-19 12:01:22 -07007708 pr_debug("%s: change of battery state %d->%d\n",
7709 __func__, batt_drv->batt_present, present);
7710
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07007711 batt_drv->batt_present = false;
7712
7713 /* add debounce? */
7714 notify_psy_changed = true;
7715 mutex_unlock(&batt_drv->chg_lock);
Ken Tsou8acade12020-07-09 03:17:35 +08007716 goto reschedule;
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07007717 }
7718
7719 fg_status = GPSY_GET_INT_PROP(fg_psy, POWER_SUPPLY_PROP_STATUS, &ret);
7720 if (ret < 0) {
7721 mutex_unlock(&batt_drv->chg_lock);
7722 goto reschedule;
7723 }
Ken Tsou8acade12020-07-09 03:17:35 +08007724
Ken Tsou8acade12020-07-09 03:17:35 +08007725 /* batt_lock protect SSOC code etc. */
7726 mutex_lock(&batt_drv->batt_lock);
7727
7728 /* TODO: poll rate should be min between ->batt_update_interval and
7729 * whatever ssoc_work() decides (typically rls->rl_delta_max_time)
7730 */
7731 ret = ssoc_work(ssoc_state, fg_psy);
7732 if (ret < 0) {
7733 update_interval = BATT_WORK_ERROR_RETRY_MS;
7734 } else {
7735 bool full;
7736 int ssoc, level;
7737
7738 /* handle charge/recharge */
7739 batt_rl_update_status(batt_drv);
7740
7741 ssoc = ssoc_get_capacity(ssoc_state);
7742 if (prev_ssoc != ssoc) {
AleX Pelosi88e55942021-06-19 12:01:22 -07007743 pr_debug("%s: change of ssoc %d->%d\n", __func__,
7744 prev_ssoc, ssoc);
7745
AleX Pelosibdf1d772021-07-21 00:12:19 -07007746 dump_ssoc_state(ssoc_state, batt_drv->ssoc_log);
Jenny Ho9b191512022-08-30 09:26:15 +00007747 batt_log_csi_ttf_info(batt_drv);
Ken Tsou8acade12020-07-09 03:17:35 +08007748 notify_psy_changed = true;
7749 }
7750
7751 /* TODO(b/138860602): clear ->chg_done to enforce the
7752 * same behavior during the transition 99 -> 100 -> Full
7753 */
7754
Jack Wucfb51f72022-10-26 19:55:26 +08007755 level = gbatt_get_capacity_level(batt_drv, fg_status);
Ken Tsou8acade12020-07-09 03:17:35 +08007756 if (level != batt_drv->capacity_level) {
AleX Pelosi88e55942021-06-19 12:01:22 -07007757 pr_debug("%s: change of capacity level %d->%d\n",
7758 __func__, batt_drv->capacity_level,
7759 level);
7760
Ken Tsou8acade12020-07-09 03:17:35 +08007761 batt_drv->capacity_level = level;
7762 notify_psy_changed = true;
7763 }
7764
7765 if (batt_drv->dead_battery) {
AleX Pelosi88e55942021-06-19 12:01:22 -07007766 batt_drv->dead_battery = gbatt_check_dead_battery(batt_drv);
7767 if (!batt_drv->dead_battery) {
7768 pr_debug("%s: dead_battery 1->0\n", __func__);
Ken Tsou8acade12020-07-09 03:17:35 +08007769 notify_psy_changed = true;
AleX Pelosi88e55942021-06-19 12:01:22 -07007770 }
Ken Tsou8acade12020-07-09 03:17:35 +08007771 }
7772
7773 /* fuel gauge triggered recharge logic. */
7774 full = (ssoc == SSOC_FULL);
AleX Pelosiefe52a72021-07-22 00:16:46 -07007775 if (full && !batt_drv->batt_full) {
Jenny Ho9b191512022-08-30 09:26:15 +00007776 batt_log_csi_ttf_info(batt_drv);
AleX Pelosiefe52a72021-07-22 00:16:46 -07007777 batt_chg_stats_pub(batt_drv, "100%", false, true);
7778 }
Ken Tsou8acade12020-07-09 03:17:35 +08007779 batt_drv->batt_full = full;
AleX Pelosibdf1d772021-07-21 00:12:19 -07007780
AleX Pelosic1cd4752022-05-04 02:15:02 -07007781 /* update resistance all the time and capacity on disconnect */
7782 ret = bhi_imp_data_update(&batt_drv->health_data.bhi_data, fg_psy);
7783 if (ret < 0 && ret != -ENODATA)
7784 pr_warn("cannot update perf index ret=%d\n", ret);
7785
AleX Pelosi43bec422022-04-27 21:59:19 -07007786 /* restore SSOC after reboot */
Jenny Ho87fef342020-10-27 17:58:08 +08007787 ret = gbatt_save_capacity(&batt_drv->ssoc_state);
7788 if (ret < 0)
7789 pr_warn("write save_soc fail, ret=%d\n", ret);
7790
AleX Pelosibdf1d772021-07-21 00:12:19 -07007791 /* debounce fg_status changes at 100% */
AleX Pelosiefe52a72021-07-22 00:16:46 -07007792 if (fg_status != batt_drv->fg_status) {
7793
7794 pr_debug("%s: ssoc=%d full=%d change of fg_status %d->%d\n",
7795 __func__, ssoc, full, batt_drv->fg_status, fg_status);
7796 if (!full)
7797 notify_psy_changed = true;
AleX Pelosibdf1d772021-07-21 00:12:19 -07007798 }
7799
AleX Pelosibdf1d772021-07-21 00:12:19 -07007800 /* slow down the updates at full */
AleX Pelosiefe52a72021-07-22 00:16:46 -07007801 if (full && batt_drv->chg_done)
AleX Pelosibdf1d772021-07-21 00:12:19 -07007802 update_interval *= UPDATE_INTERVAL_AT_FULL_FACTOR;
Ken Tsou8acade12020-07-09 03:17:35 +08007803 }
7804
AleX Pelosibdf1d772021-07-21 00:12:19 -07007805 /* notifications for this are debounced */
7806 batt_drv->fg_status = fg_status;
7807
Ken Tsou8acade12020-07-09 03:17:35 +08007808 /* TODO: poll other data here if needed */
7809
7810 ret = gbatt_get_temp(batt_drv, &batt_temp);
7811 if (ret == 0 && batt_temp != batt_drv->batt_temp) {
7812 const int limit = batt_drv->batt_update_high_temp_threshold;
7813
7814 batt_drv->batt_temp = batt_temp;
AleX Pelosi88e55942021-06-19 12:01:22 -07007815 if (batt_drv->batt_temp > limit) {
7816 pr_debug("%s: temperature over limit %d > %d\n",
7817 __func__, batt_temp, limit);
Ken Tsou8acade12020-07-09 03:17:35 +08007818 notify_psy_changed = true;
AleX Pelosi88e55942021-06-19 12:01:22 -07007819 }
Ken Tsou8acade12020-07-09 03:17:35 +08007820 }
7821
Jenny Hoc6f13052022-01-03 10:52:21 +08007822 if (batt_drv->sd.is_enable)
7823 gbatt_record_over_temp(batt_drv);
7824
Ken Tsou8acade12020-07-09 03:17:35 +08007825 mutex_unlock(&batt_drv->batt_lock);
7826
7827 /*
7828 * wait for timeout or state equal to CHARGING, FULL or UNKNOWN
7829 * (which will likely not happen) even on ssoc error. msc_logic
7830 * hold poll_ws wakelock during this time.
AleX Pelosi32458432020-09-05 11:02:39 -07007831 * Delay the estimates for time to full for BATT_WORK_DEBOUNCE_RETRY_MS
7832 * after the device start charging.
Ken Tsou8acade12020-07-09 03:17:35 +08007833 */
7834 if (batt_drv->batt_fast_update_cnt) {
7835
7836 if (fg_status != POWER_SUPPLY_STATUS_DISCHARGING &&
7837 fg_status != POWER_SUPPLY_STATUS_NOT_CHARGING) {
Ken Tsou8acade12020-07-09 03:17:35 +08007838 batt_drv->batt_fast_update_cnt = 0;
AleX Pelosi32458432020-09-05 11:02:39 -07007839 update_interval = BATT_WORK_DEBOUNCE_RETRY_MS;
Ken Tsou8acade12020-07-09 03:17:35 +08007840 } else {
7841 update_interval = BATT_WORK_FAST_RETRY_MS;
7842 batt_drv->batt_fast_update_cnt -= 1;
7843 }
AleX Pelosi32458432020-09-05 11:02:39 -07007844 } else if (batt_drv->ttf_debounce) {
7845 batt_drv->ttf_debounce = 0;
Jenny Ho9b191512022-08-30 09:26:15 +00007846 batt_log_csi_ttf_info(batt_drv);
Ken Tsou8acade12020-07-09 03:17:35 +08007847 }
7848
7849 /* acquired in msc_logic */
7850 if (batt_drv->batt_fast_update_cnt == 0)
Ken Tsou5ecf2f42020-07-16 08:26:05 +08007851 __pm_relax(batt_drv->poll_ws);
Ken Tsou8acade12020-07-09 03:17:35 +08007852
AleX Pelosib3a1a552022-05-03 17:00:11 -07007853 /* set a connect */
AleX Pelosia3f22de2022-05-03 23:42:24 -07007854 if (batt_drv->health_data.bhi_data.res_state.estimate_requested)
Ken Tsou8acade12020-07-09 03:17:35 +08007855 batt_res_work(batt_drv);
7856
7857 /* check only once and when/if the pairing state is reset */
7858 if (batt_drv->pairing_state == BATT_PAIRING_ENABLED) {
7859 enum batt_paired_state state;
7860
7861 state = batt_check_pairing_state(batt_drv);
7862 switch (state) {
7863 /* somethig is wrong with eeprom comms, HW problem? */
7864 case BATT_PAIRING_READ_ERROR:
7865 break;
7866 /* somethig is wrong with eeprom, HW problem? */
7867 case BATT_PAIRING_WRITE_ERROR:
7868 break;
7869 default:
7870 batt_drv->pairing_state = state;
7871 break;
7872 }
7873 }
7874
7875 mutex_unlock(&batt_drv->chg_lock);
7876
AleX Pelosibdf1d772021-07-21 00:12:19 -07007877 /* TODO: we might not need to do this all the time */
Ken Tsou8acade12020-07-09 03:17:35 +08007878 batt_cycle_count_update(batt_drv, ssoc_get_real(ssoc_state));
Ken Tsou8acade12020-07-09 03:17:35 +08007879
7880reschedule:
7881
7882 if (notify_psy_changed)
7883 power_supply_changed(batt_drv->psy);
7884
AleX Pelosic1cd4752022-05-04 02:15:02 -07007885 if (batt_drv->blf_state == BATT_LFCOLLECT_ENABLED) {
7886
7887 ret = google_battery_init_hist_work(batt_drv);
7888 if (ret == -EAGAIN)
7889 update_interval = BATT_WORK_DEBOUNCE_RETRY_MS;
7890
7891 if (batt_drv->blf_state == BATT_LFCOLLECT_COLLECT) {
7892 ret = batt_history_data_work(batt_drv);
7893 if (ret < 0)
7894 pr_err("BHI: cannot prime history (%d)\n", ret);
7895
7896 mutex_lock(&batt_drv->chg_lock);
7897 ret = batt_bhi_stats_update_all(batt_drv);
7898 if (ret < 0)
7899 pr_err("BHI: cannot init stats (%d)\n", ret);
7900 mutex_unlock(&batt_drv->chg_lock);
7901 }
7902 }
7903
Ken Tsou8acade12020-07-09 03:17:35 +08007904 if (batt_drv->blf_state == BATT_LFCOLLECT_COLLECT) {
7905 ret = batt_history_data_work(batt_drv);
7906 if (ret == -ENOENT) {
7907 batt_drv->blf_state = BATT_LFCOLLECT_NOT_AVAILABLE;
Jenny Ho11d5fcf2020-12-01 06:39:41 +08007908 pr_info("MSC_HIST Battery data collection disabled\n");
Ken Tsou8acade12020-07-09 03:17:35 +08007909 } else if (ret < 0) {
Jenny Ho11d5fcf2020-12-01 06:39:41 +08007910 pr_debug("MSC_HIST cannot collect battery data %d\n", ret);
Ken Tsou8acade12020-07-09 03:17:35 +08007911 }
7912 }
7913
7914 if (update_interval) {
7915 pr_debug("rerun battery work in %d ms\n", update_interval);
7916 schedule_delayed_work(&batt_drv->batt_work,
7917 msecs_to_jiffies(update_interval));
7918 }
7919
AleX Pelosic1cd4752022-05-04 02:15:02 -07007920
Ken Tsou5ecf2f42020-07-16 08:26:05 +08007921 __pm_relax(batt_drv->batt_ws);
Ken Tsou8acade12020-07-09 03:17:35 +08007922}
7923
Wasb Liuf40cbb22022-01-26 18:41:03 +08007924static void power_metrics_data_work(struct work_struct *work)
7925{
7926 struct batt_drv *batt_drv = container_of(work, struct batt_drv,
7927 power_metrics.work.work);
7928 const unsigned int idx = batt_drv->power_metrics.idx;
7929 unsigned long cc, vbat;
7930 unsigned int next_work = batt_drv->power_metrics.polling_rate * 1000;
7931 ktime_t now = get_boot_sec();
7932
7933 if (!batt_drv->fg_psy)
7934 goto error;
7935
7936 cc = GPSY_GET_PROP(batt_drv->fg_psy, POWER_SUPPLY_PROP_CHARGE_COUNTER);
7937 vbat = GPSY_GET_PROP(batt_drv->fg_psy, POWER_SUPPLY_PROP_VOLTAGE_NOW);
7938
7939 if ((cc < 0) || (vbat < 0)) {
7940 if ((cc == -EAGAIN) || (vbat == -EAGAIN))
7941 next_work = 100;
7942 goto error;
7943 }
7944
7945 if ((idx == 0) && (batt_drv->power_metrics.data[idx].voltage == 0))
7946 batt_drv->power_metrics.idx = 0;
7947 else
7948 batt_drv->power_metrics.idx++;
7949 if (batt_drv->power_metrics.idx >= POWER_METRICS_MAX_DATA)
7950 batt_drv->power_metrics.idx = 0;
7951
7952 batt_drv->power_metrics.data[batt_drv->power_metrics.idx].charge_count = cc;
7953 batt_drv->power_metrics.data[batt_drv->power_metrics.idx].voltage = vbat;
7954 batt_drv->power_metrics.data[batt_drv->power_metrics.idx].time = now;
7955
7956error:
7957 schedule_delayed_work(&batt_drv->power_metrics.work, msecs_to_jiffies(next_work));
7958}
7959
Ken Tsou8acade12020-07-09 03:17:35 +08007960/* ------------------------------------------------------------------------- */
7961
7962/*
AleX Pelosid2ca4072020-09-03 22:07:27 -07007963 * Keep the number of properties under UEVENT_NUM_ENVP (minus # of
7964 * standard uevent variables) i.e 26.
Ken Tsou8acade12020-07-09 03:17:35 +08007965 *
AleX Pelosid2ca4072020-09-03 22:07:27 -07007966 * Removed the following from sysnodes
7967 * GBMS_PROP_ADAPTER_DETAILS gbms
7968 * POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT gbms
7969 * POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE gbms
Ken Tsou8acade12020-07-09 03:17:35 +08007970 *
7971 * POWER_SUPPLY_PROP_CHARGE_TYPE,
7972 * POWER_SUPPLY_PROP_CURRENT_AVG,
Ken Tsou8acade12020-07-09 03:17:35 +08007973 * POWER_SUPPLY_PROP_VOLTAGE_AVG,
7974 * POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
7975 * POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
7976 * POWER_SUPPLY_PROP_VOLTAGE_OCV,
7977 */
7978
7979static enum power_supply_property gbatt_battery_props[] = {
7980 POWER_SUPPLY_PROP_CAPACITY,
7981 POWER_SUPPLY_PROP_CAPACITY_LEVEL,
7982 POWER_SUPPLY_PROP_CHARGE_COUNTER,
Ken Tsou8acade12020-07-09 03:17:35 +08007983 POWER_SUPPLY_PROP_CHARGE_FULL,
7984 POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
Jack Wu93d9c7e2022-01-18 16:05:58 +08007985 POWER_SUPPLY_PROP_CURRENT_AVG,
Ken Tsou8acade12020-07-09 03:17:35 +08007986 POWER_SUPPLY_PROP_CURRENT_NOW,
7987 POWER_SUPPLY_PROP_CYCLE_COUNT,
Ken Tsou8acade12020-07-09 03:17:35 +08007988 POWER_SUPPLY_PROP_HEALTH,
7989 POWER_SUPPLY_PROP_PRESENT,
Ken Tsou8acade12020-07-09 03:17:35 +08007990 POWER_SUPPLY_PROP_SERIAL_NUMBER,
7991 POWER_SUPPLY_PROP_STATUS,
7992 POWER_SUPPLY_PROP_TEMP,
7993 POWER_SUPPLY_PROP_TECHNOLOGY,
7994 POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, /* No need for this? */
7995 POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
7996 POWER_SUPPLY_PROP_VOLTAGE_NOW, /* 23 */
Jenny Hodaaa8032021-02-04 15:05:24 +08007997 POWER_SUPPLY_PROP_VOLTAGE_OCV,
Ken Tsou8acade12020-07-09 03:17:35 +08007998
7999 /* hard limit to 26 */
8000};
8001
Ken Tsoua15d1fa2022-01-24 14:42:27 +08008002static bool temp_defend_dry_run(struct gvotable_election *temp_dryrun_votable)
Jenny Ho701a0ea2021-12-08 17:22:05 +08008003{
8004 bool dry_run = 1;
8005
8006 if (!temp_dryrun_votable)
Ken Tsoua15d1fa2022-01-24 14:42:27 +08008007 temp_dryrun_votable =
8008 gvotable_election_get_handle(VOTABLE_TEMP_DRYRUN);
Jenny Ho701a0ea2021-12-08 17:22:05 +08008009 if (temp_dryrun_votable)
Ken Tsoua15d1fa2022-01-24 14:42:27 +08008010 dry_run = !!gvotable_get_current_int_vote(temp_dryrun_votable);
Jenny Ho701a0ea2021-12-08 17:22:05 +08008011
8012 return dry_run;
8013}
8014
Ken Tsou8acade12020-07-09 03:17:35 +08008015/*
8016 * status is:
8017 * . _UNKNOWN during init
8018 * . _DISCHARGING when not connected
8019 * when connected to a power supply status is
8020 * . _FULL (until disconnect) after the charger flags DONE if SSOC=100%
8021 * . _CHARGING if FG reports _FULL but SSOC < 100% (should not happen)
8022 * . _CHARGING if FG reports _NOT_CHARGING
8023 * . _NOT_CHARGING if FG report _DISCHARGING
8024 * . same as FG state otherwise
8025 */
8026static int gbatt_get_status(struct batt_drv *batt_drv,
8027 union power_supply_propval *val)
8028{
8029 int err, ssoc;
8030
8031 if (batt_drv->ssoc_state.buck_enabled == 0) {
8032 val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
8033 return 0;
8034 }
8035
8036 if (batt_drv->ssoc_state.buck_enabled == -1) {
8037 val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
8038 return 0;
8039 }
8040
8041 /* ->buck_enabled = 1, from here ownward device is connected */
AleX Pelosi486def22020-10-29 16:41:40 -07008042
Jenny Hobd405d42021-08-30 17:17:00 +08008043 if (batt_drv->batt_health == POWER_SUPPLY_HEALTH_OVERHEAT &&
Jenny Ho701a0ea2021-12-08 17:22:05 +08008044 !temp_defend_dry_run(batt_drv->temp_dryrun_votable)) {
AleX Pelosi486def22020-10-29 16:41:40 -07008045 val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
8046 return 0;
8047 }
8048
Stephane Lee2b9ea732021-05-23 23:39:54 -07008049 if (batt_drv->msc_state == MSC_HEALTH_PAUSE) {
8050 /* Expect AC to discharge in PAUSE. However, UI must persist */
8051 val->intval = POWER_SUPPLY_STATUS_CHARGING;
8052 return 0;
8053 }
8054
Ken Tsou8acade12020-07-09 03:17:35 +08008055 if (!batt_drv->fg_psy)
8056 return -EINVAL;
8057
8058 ssoc = ssoc_get_capacity(&batt_drv->ssoc_state);
8059
8060 /* FULL when the charger said so and SSOC == 100% */
8061 if (batt_drv->chg_done && ssoc == SSOC_FULL) {
8062 val->intval = POWER_SUPPLY_STATUS_FULL;
8063 return 0;
8064 }
8065
8066 err = power_supply_get_property(batt_drv->fg_psy,
8067 POWER_SUPPLY_PROP_STATUS,
8068 val);
8069 if (err != 0)
8070 return err;
8071
8072 if (val->intval == POWER_SUPPLY_STATUS_FULL) {
8073
8074 /* not full unless the charger says so */
8075 if (!batt_drv->chg_done)
8076 val->intval = POWER_SUPPLY_STATUS_CHARGING;
8077
8078 /* NOTE: FG driver could flag FULL before GDF is at 100% when
8079 * gauge is not tuned or when capacity estimates are wrong.
8080 */
8081 if (ssoc != SSOC_FULL)
8082 val->intval = POWER_SUPPLY_STATUS_CHARGING;
8083
8084 } else if (val->intval == POWER_SUPPLY_STATUS_NOT_CHARGING) {
8085 /* smooth transition between charging and full */
8086 val->intval = POWER_SUPPLY_STATUS_CHARGING;
8087 } else if (val->intval == POWER_SUPPLY_STATUS_DISCHARGING) {
8088 /* connected and discharging is NOT charging */
8089 val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
8090 }
8091
8092 return 0;
8093}
8094
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07008095/* lock batt_drv->batt_lock */
8096static int gbatt_get_capacity(struct batt_drv *batt_drv)
8097{
8098 struct batt_ssoc_state *ssoc_state = &batt_drv->ssoc_state;
8099 int capacity;
8100
8101 if (batt_drv->fake_capacity >= 0 && batt_drv->fake_capacity <= 100)
8102 capacity = batt_drv->fake_capacity;
8103 else
8104 capacity = ssoc_get_capacity(ssoc_state);
8105
8106 return capacity;
8107}
8108
Jenny Ho87fef342020-10-27 17:58:08 +08008109
8110static void gbatt_reset_curve(struct batt_drv *batt_drv, int ssoc_cap)
8111{
8112 struct batt_ssoc_state *ssoc_state = &batt_drv->ssoc_state;
8113 const qnum_t cap = qnum_fromint(ssoc_cap);
8114 const qnum_t gdf = ssoc_state->ssoc_gdf;
8115 enum ssoc_uic_type type;
8116
8117 if (gdf < cap) {
8118 type = SSOC_UIC_TYPE_DSG;
8119 } else {
8120 type = SSOC_UIC_TYPE_CHG;
8121 }
8122
8123 pr_info("reset curve at gdf=%d.%d cap=%d.%d type=%d\n",
8124 qnum_toint(gdf), qnum_fracdgt(gdf),
8125 qnum_toint(cap), qnum_fracdgt(cap),
8126 type);
8127
8128 /* current is the drop point on the discharge curve */
8129 ssoc_change_curve_at_gdf(ssoc_state, gdf, cap, type);
8130 ssoc_work(ssoc_state, batt_drv->fg_psy);
8131 dump_ssoc_state(ssoc_state, batt_drv->ssoc_log);
8132}
8133
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07008134/* splice the curve at point when the SSOC is removed */
8135static void gbatt_set_capacity(struct batt_drv *batt_drv, int capacity)
8136{
AleX Pelosi4080ad02020-11-12 16:22:35 -08008137 if (capacity < 0)
8138 capacity = -EINVAL;
8139
8140 if (batt_drv->batt_health != POWER_SUPPLY_HEALTH_OVERHEAT) {
8141 /* just set the value if not in overheat */
8142 } else if (capacity < 0 && batt_drv->fake_capacity >= 0) {
Jenny Ho87fef342020-10-27 17:58:08 +08008143 gbatt_reset_curve(batt_drv, batt_drv->fake_capacity);
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07008144 } else if (capacity > 0) {
AleX Pelosi4080ad02020-11-12 16:22:35 -08008145 /* TODO: convergence to the new capacity? */
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07008146 }
8147
8148 batt_drv->fake_capacity = capacity;
8149}
8150
Jenny Ho347decb2020-12-08 10:52:50 +08008151static int gbatt_set_health(struct batt_drv *batt_drv, int health)
AleX Pelosi600cb122020-10-29 10:05:47 -07008152{
Jenny Ho347decb2020-12-08 10:52:50 +08008153 if (health > POWER_SUPPLY_HEALTH_HOT ||
8154 health < POWER_SUPPLY_HEALTH_UNKNOWN)
8155 return -EINVAL;
8156
AleX Pelosi600cb122020-10-29 10:05:47 -07008157 batt_drv->batt_health = health;
8158
8159 /* disable health charging if in overheat */
8160 if (health == POWER_SUPPLY_HEALTH_OVERHEAT)
8161 msc_logic_health(batt_drv);
Jenny Ho347decb2020-12-08 10:52:50 +08008162
8163 return 0;
AleX Pelosi600cb122020-10-29 10:05:47 -07008164}
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07008165
Jenny Ho87fef342020-10-27 17:58:08 +08008166#define RESTORE_SOC_THRESHOLD 5
8167static int gbatt_restore_capacity(struct batt_drv *batt_drv)
8168{
8169 struct batt_ssoc_state *ssoc_state = &batt_drv->ssoc_state;
8170 int ret = 0, save_soc, gdf_soc;
8171
8172 ret = gbms_storage_read(GBMS_TAG_RSOC, &ssoc_state->save_soc,
8173 sizeof(ssoc_state->save_soc));
8174
8175 if (ret < 0)
8176 return ret;
8177
8178 if (ssoc_state->save_soc) {
8179 save_soc = (int)ssoc_state->save_soc;
8180 gdf_soc = qnum_toint(ssoc_state->ssoc_gdf);
8181 pr_info("save_soc:%d, gdf:%d", save_soc, gdf_soc);
8182
8183 if ((save_soc < gdf_soc) ||
8184 (save_soc - gdf_soc) > RESTORE_SOC_THRESHOLD)
8185 return ret;
8186
8187 gbatt_reset_curve(batt_drv, save_soc);
8188 }
8189
8190 return ret;
8191}
8192
Jenny Hob7fc3382021-10-23 21:57:49 +08008193#define TTF_REPORT_MAX_RATIO 300
Ken Tsou8acade12020-07-09 03:17:35 +08008194static int gbatt_get_property(struct power_supply *psy,
8195 enum power_supply_property psp,
8196 union power_supply_propval *val)
8197{
8198 struct batt_drv *batt_drv = (struct batt_drv *)
8199 power_supply_get_drvdata(psy);
Ken Tsou8acade12020-07-09 03:17:35 +08008200 int rc, err = 0;
8201
8202 pm_runtime_get_sync(batt_drv->device);
8203 if (!batt_drv->init_complete || !batt_drv->resume_complete) {
8204 pm_runtime_put_sync(batt_drv->device);
8205 return -EAGAIN;
8206 }
8207 pm_runtime_put_sync(batt_drv->device);
8208
8209 switch (psp) {
AleX Pelosid2ca4072020-09-03 22:07:27 -07008210 case GBMS_PROP_ADAPTER_DETAILS:
Ken Tsou8acade12020-07-09 03:17:35 +08008211 val->intval = batt_drv->ce_data.adapter_details.v;
8212 break;
8213
AleX Pelosid2ca4072020-09-03 22:07:27 -07008214 case GBMS_PROP_DEAD_BATTERY:
8215 val->intval = batt_drv->dead_battery;
8216 break;
8217 /*
8218 * ng charging:
8219 * 1) write to GBMS_PROP_CHARGE_CHARGER_STATE,
8220 * 2) read POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT and
8221 * POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE
8222 */
8223 case GBMS_PROP_CHARGE_CHARGER_STATE:
8224 val->intval = batt_drv->chg_state.v;
8225 break;
8226
Ken Tsou8acade12020-07-09 03:17:35 +08008227 case POWER_SUPPLY_PROP_CYCLE_COUNT:
8228 if (batt_drv->cycle_count < 0)
8229 err = batt_drv->cycle_count;
8230 else
8231 val->intval = batt_drv->cycle_count;
8232 break;
8233
Ken Tsou8acade12020-07-09 03:17:35 +08008234 case POWER_SUPPLY_PROP_CAPACITY:
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07008235 mutex_lock(&batt_drv->batt_lock);
8236 val->intval = gbatt_get_capacity(batt_drv);
8237 mutex_unlock(&batt_drv->batt_lock);
Ken Tsou8acade12020-07-09 03:17:35 +08008238 break;
8239
Ken Tsou8acade12020-07-09 03:17:35 +08008240 case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
Ken Tsou27893212020-05-15 14:52:39 +08008241 if (batt_drv->fake_capacity >= 0 &&
8242 batt_drv->fake_capacity <= 100)
8243 val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
8244 else
8245 val->intval = batt_drv->capacity_level;
Ken Tsou8acade12020-07-09 03:17:35 +08008246 break;
8247
Ken Tsou8acade12020-07-09 03:17:35 +08008248 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
8249 mutex_lock(&batt_drv->chg_lock);
8250 val->intval = batt_drv->cc_max;
8251 mutex_unlock(&batt_drv->chg_lock);
8252 break;
8253 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
8254 mutex_lock(&batt_drv->chg_lock);
8255 val->intval = batt_drv->fv_uv;
8256 mutex_unlock(&batt_drv->chg_lock);
8257 break;
8258
8259 /*
8260 * POWER_SUPPLY_PROP_CHARGE_DONE comes from the charger BUT battery
AleX Pelosi3512e112020-08-14 00:42:15 -07008261 * has also an idea about it.
8262 * mutex_lock(&batt_drv->chg_lock);
8263 * val->intval = batt_drv->chg_done;
8264 * mutex_unlock(&batt_drv->chg_lock);
Ken Tsou8acade12020-07-09 03:17:35 +08008265 */
AleX Pelosi3512e112020-08-14 00:42:15 -07008266
Ken Tsou8acade12020-07-09 03:17:35 +08008267 /*
8268 * compat: POWER_SUPPLY_PROP_CHARGE_TYPE comes from the charger so
8269 * using the last value reported from the CHARGER. This (of course)
8270 * means that NG charging needs to be enabled.
8271 */
8272 case POWER_SUPPLY_PROP_CHARGE_TYPE:
8273 mutex_lock(&batt_drv->chg_lock);
8274 val->intval = batt_drv->chg_state.f.chg_type;
8275 mutex_unlock(&batt_drv->chg_lock);
8276 break;
8277
Ken Tsou8acade12020-07-09 03:17:35 +08008278 case POWER_SUPPLY_PROP_STATUS:
8279 err = gbatt_get_status(batt_drv, val);
8280 break;
8281
8282 /* health */
8283 case POWER_SUPPLY_PROP_HEALTH:
Jenny Hoc52aa852021-01-14 14:27:43 +08008284 if (batt_drv->batt_health == POWER_SUPPLY_HEALTH_OVERHEAT &&
Jenny Ho701a0ea2021-12-08 17:22:05 +08008285 temp_defend_dry_run(batt_drv->temp_dryrun_votable)) {
Jenny Hoc52aa852021-01-14 14:27:43 +08008286 val->intval = POWER_SUPPLY_HEALTH_GOOD;
8287 } else if (batt_drv->batt_health !=
8288 POWER_SUPPLY_HEALTH_UNKNOWN) {
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07008289 val->intval = batt_drv->batt_health;
8290 } else if (!batt_drv->fg_psy) {
8291 val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
8292 } else {
8293 rc = power_supply_get_property(batt_drv->fg_psy,
8294 psp, val);
8295 if (rc < 0)
8296 val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
Ken Tsou8acade12020-07-09 03:17:35 +08008297 batt_drv->soh = val->intval;
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07008298 }
Jenny Ho74afc982021-11-03 11:54:00 +08008299 if (batt_drv->report_health != val->intval) {
8300 /* Log health change for debug */
8301 logbuffer_log(batt_drv->ttf_stats.ttf_log,
AleX Pelosi9bfe2312022-04-26 13:20:43 -07008302 "h:%d->%d batt_health:%d dry_run:%d soh:%d",
Jenny Ho74afc982021-11-03 11:54:00 +08008303 batt_drv->report_health, val->intval, batt_drv->batt_health,
Jenny Ho701a0ea2021-12-08 17:22:05 +08008304 temp_defend_dry_run(batt_drv->temp_dryrun_votable),
8305 batt_drv->soh);
Jenny Ho74afc982021-11-03 11:54:00 +08008306 batt_drv->report_health = val->intval;
8307 }
Ken Tsou8acade12020-07-09 03:17:35 +08008308 break;
Ken Tsou8acade12020-07-09 03:17:35 +08008309
AleX Pelosi854b8e12020-10-31 11:17:52 -07008310 /* cannot set err, negative estimate will revert to HAL */
Ken Tsou8acade12020-07-09 03:17:35 +08008311 case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: {
AleX Pelosiab0e9d42020-09-29 11:13:19 -07008312 ktime_t res;
Jenny Hob7fc3382021-10-23 21:57:49 +08008313 int max_ratio;
Ken Tsou8acade12020-07-09 03:17:35 +08008314
Jenny Hob7fc3382021-10-23 21:57:49 +08008315 max_ratio = batt_ttf_estimate(&res, batt_drv);
Jenny Ho5c6275c2022-02-14 13:28:25 +08008316 if (max_ratio >= TTF_REPORT_MAX_RATIO) {
8317 val->intval = 0;
8318 } else if (max_ratio >= 0) {
AleX Pelosi854b8e12020-10-31 11:17:52 -07008319 if (res < 0)
8320 res = 0;
Ken Tsou8acade12020-07-09 03:17:35 +08008321 val->intval = res;
8322 } else if (!batt_drv->fg_psy) {
8323 val->intval = -1;
8324 } else {
8325 rc = power_supply_get_property(batt_drv->fg_psy,
8326 psp, val);
8327 if (rc < 0)
8328 val->intval = -1;
8329 }
8330 } break;
8331
8332 case POWER_SUPPLY_PROP_TEMP:
8333 err = gbatt_get_temp(batt_drv, &val->intval);
8334 break;
8335
8336 case POWER_SUPPLY_PROP_CURRENT_AVG:
8337 case POWER_SUPPLY_PROP_CURRENT_NOW:
8338 if (!batt_drv->fg_psy)
8339 return -EINVAL;
8340 err = power_supply_get_property(batt_drv->fg_psy, psp, val);
8341 if (err == 0)
8342 val->intval = -val->intval;
8343 break;
8344
AleX Pelosi8c5fa772020-10-03 13:36:56 -07008345 /* Can force the state here */
8346 case POWER_SUPPLY_PROP_PRESENT:
8347 if (batt_drv->fake_battery_present != -1) {
8348 val->intval = batt_drv->fake_battery_present;
8349 } else if (batt_drv->fg_psy) {
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07008350
8351 /* TODO: use the cached value? */
AleX Pelosi8c5fa772020-10-03 13:36:56 -07008352 rc = power_supply_get_property(batt_drv->fg_psy,
8353 psp, val);
8354 if (rc < 0)
8355 val->intval = 0;
8356 } else {
8357 err = -EINVAL;
8358 }
8359 break;
8360
Jenny Ho002c6702021-10-19 16:19:16 +08008361 case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT:
8362 if (batt_drv->topoff)
8363 val->intval = batt_drv->topoff;
8364 else
8365 val->intval = -1;
8366 break;
8367
Ken Tsou8acade12020-07-09 03:17:35 +08008368 default:
8369 if (!batt_drv->fg_psy)
8370 return -EINVAL;
8371 err = power_supply_get_property(batt_drv->fg_psy, psp, val);
8372 break;
8373 }
8374
8375 if (err < 0) {
8376 pr_debug("gbatt: get_prop cannot read psp=%d\n", psp);
8377 return err;
8378 }
8379
8380 return 0;
8381}
8382
8383static int gbatt_set_property(struct power_supply *psy,
8384 enum power_supply_property psp,
8385 const union power_supply_propval *val)
8386{
8387 struct batt_drv *batt_drv = (struct batt_drv *)
8388 power_supply_get_drvdata(psy);
8389 int ret = 0;
8390
8391 pm_runtime_get_sync(batt_drv->device);
8392 if (!batt_drv->init_complete || !batt_drv->resume_complete) {
8393 pm_runtime_put_sync(batt_drv->device);
8394 return -EAGAIN;
8395 }
8396 pm_runtime_put_sync(batt_drv->device);
8397
8398 switch (psp) {
AleX Pelosid2ca4072020-09-03 22:07:27 -07008399 case GBMS_PROP_ADAPTER_DETAILS:
Ken Tsou8acade12020-07-09 03:17:35 +08008400 mutex_lock(&batt_drv->stats_lock);
8401 batt_drv->ce_data.adapter_details.v = val->intval;
8402 mutex_unlock(&batt_drv->stats_lock);
8403 break;
8404
8405 /* NG Charging, where it all begins */
AleX Pelosid2ca4072020-09-03 22:07:27 -07008406 case GBMS_PROP_CHARGE_CHARGER_STATE:
Ken Tsou8acade12020-07-09 03:17:35 +08008407 mutex_lock(&batt_drv->chg_lock);
AleX Pelosid2ca4072020-09-03 22:07:27 -07008408 batt_drv->chg_state.v = gbms_propval_int64val(val);
AleX Pelosi8e7fd812019-08-16 10:41:46 -07008409 ret = batt_chg_logic(batt_drv);
Ken Tsou8acade12020-07-09 03:17:35 +08008410 mutex_unlock(&batt_drv->chg_lock);
8411 break;
8412
8413 case POWER_SUPPLY_PROP_CAPACITY:
AleX Pelosi600cb122020-10-29 10:05:47 -07008414 mutex_lock(&batt_drv->chg_lock);
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07008415 if (val->intval != batt_drv->fake_capacity) {
8416 gbatt_set_capacity(batt_drv, val->intval);
8417 if (batt_drv->psy)
8418 power_supply_changed(batt_drv->psy);
8419 }
AleX Pelosi600cb122020-10-29 10:05:47 -07008420 mutex_unlock(&batt_drv->chg_lock);
Ken Tsou8acade12020-07-09 03:17:35 +08008421 break;
Ken Tsou8acade12020-07-09 03:17:35 +08008422
8423 case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
8424 if (val->intval <= 0)
8425 batt_drv->ttf_stats.ttf_fake = -1;
8426 else
8427 batt_drv->ttf_stats.ttf_fake = val->intval;
Ken Tsou68d44d12020-11-11 00:01:40 +08008428 pr_info("time_to_full = %lld\n", batt_drv->ttf_stats.ttf_fake);
Ken Tsou8acade12020-07-09 03:17:35 +08008429 if (batt_drv->psy)
8430 power_supply_changed(batt_drv->psy);
8431 break;
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07008432 case POWER_SUPPLY_PROP_HEALTH:
AleX Pelosi600cb122020-10-29 10:05:47 -07008433 mutex_lock(&batt_drv->chg_lock);
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07008434 if (batt_drv->batt_health != val->intval) {
Jenny Ho347decb2020-12-08 10:52:50 +08008435 ret = gbatt_set_health(batt_drv, val->intval);
8436 if (ret == 0 && batt_drv->psy)
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07008437 power_supply_changed(batt_drv->psy);
8438 }
AleX Pelosi600cb122020-10-29 10:05:47 -07008439 mutex_unlock(&batt_drv->chg_lock);
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07008440 break;
Ken Tsou8acade12020-07-09 03:17:35 +08008441 default:
8442 ret = -EINVAL;
8443 break;
8444 }
8445
8446 if (ret < 0) {
8447 pr_debug("gbatt: get_prop cannot write psp=%d\n", psp);
8448 return ret;
8449 }
8450
8451
8452 return 0;
8453}
8454
8455static int gbatt_property_is_writeable(struct power_supply *psy,
8456 enum power_supply_property psp)
8457{
8458 switch (psp) {
AleX Pelosid2ca4072020-09-03 22:07:27 -07008459 case GBMS_PROP_CHARGE_CHARGER_STATE:
Ken Tsou8acade12020-07-09 03:17:35 +08008460 case POWER_SUPPLY_PROP_CAPACITY:
AleX Pelosid2ca4072020-09-03 22:07:27 -07008461 case GBMS_PROP_ADAPTER_DETAILS:
Ken Tsou8acade12020-07-09 03:17:35 +08008462 case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
AleX Pelosi4a3a95f2020-10-12 17:35:17 -07008463 case POWER_SUPPLY_PROP_HEALTH:
Ken Tsou8acade12020-07-09 03:17:35 +08008464 return 1;
8465 default:
8466 break;
8467 }
8468
8469 return 0;
8470}
8471
8472static struct power_supply_desc gbatt_psy_desc = {
8473 .name = "battery",
8474 .type = POWER_SUPPLY_TYPE_BATTERY,
8475 .get_property = gbatt_get_property,
8476 .set_property = gbatt_set_property,
8477 .property_is_writeable = gbatt_property_is_writeable,
8478 .properties = gbatt_battery_props,
8479 .num_properties = ARRAY_SIZE(gbatt_battery_props),
8480};
8481
8482/* ------------------------------------------------------------------------ */
8483
Jenny Ho7d8c2212022-01-05 18:07:14 +08008484static int batt_init_sd(struct swelling_data *sd)
8485{
Ken Tsoua15d1fa2022-01-24 14:42:27 +08008486 int ret, i, j;
Jenny Ho7d8c2212022-01-05 18:07:14 +08008487
8488 if (!sd->is_enable)
8489 return 0;
8490
8491 ret = gbms_storage_read(GBMS_TAG_STRD, &sd->saved,
8492 sizeof(sd->saved));
8493 if (ret < 0)
8494 return ret;
8495
8496 if (sd->saved[SD_CHG_START] == 0xFFFF) {
8497 /* Empty EEPROM, initial sd_saved */
8498 for (i = 0; i < BATT_SD_SAVE_SIZE; i++)
8499 sd->saved[i] = 0;
8500 } else {
8501 /* Available data, restore */
8502 for (i = 0; i < BATT_TEMP_RECORD_THR; i++) {
8503 j = i + SD_DISCHG_START;
8504 sd->chg[i] = sd->saved[i] * SAVE_UNIT;
8505 sd->dischg[i] = sd->saved[j] * SAVE_UNIT;
8506 }
8507 }
8508
8509 return ret;
8510}
8511
Jack Wu5b74da42022-03-12 14:09:28 +08008512/* bhi_init */
AleX Pelosi43bec422022-04-27 21:59:19 -07008513static int batt_bhi_init(struct batt_drv *batt_drv)
Jack Wu5b74da42022-03-12 14:09:28 +08008514{
AleX Pelosi43bec422022-04-27 21:59:19 -07008515 struct health_data *health_data = &batt_drv->health_data;
Jack Wu5b74da42022-03-12 14:09:28 +08008516 int ret;
8517
AleX Pelosi43bec422022-04-27 21:59:19 -07008518 /* see enum bhi_algo */
Jack Wu5b74da42022-03-12 14:09:28 +08008519 ret = of_property_read_u32(batt_drv->device->of_node, "google,bhi-algo-ver",
AleX Pelosi43bec422022-04-27 21:59:19 -07008520 &health_data->bhi_algo);
Jack Wu5b74da42022-03-12 14:09:28 +08008521 if (ret < 0)
AleX Pelosic1cd4752022-05-04 02:15:02 -07008522 health_data->bhi_algo = BHI_ALGO_DISABLED;
8523 /* default weights */
AleX Pelosi43bec422022-04-27 21:59:19 -07008524 ret = of_property_read_u32(batt_drv->device->of_node, "google,bhi-w_ci",
8525 &health_data->bhi_w_ci);
Jack Wu5b74da42022-03-12 14:09:28 +08008526 if (ret < 0)
AleX Pelosic1cd4752022-05-04 02:15:02 -07008527 health_data->bhi_w_ci = 100;
AleX Pelosi43bec422022-04-27 21:59:19 -07008528 ret = of_property_read_u32(batt_drv->device->of_node, "google,bhi-w_pi",
8529 &health_data->bhi_w_pi);
8530 if (ret < 0)
AleX Pelosic1cd4752022-05-04 02:15:02 -07008531 health_data->bhi_w_pi = 0;
AleX Pelosi43bec422022-04-27 21:59:19 -07008532 ret = of_property_read_u32(batt_drv->device->of_node, "google,bhi-w_sd",
8533 &health_data->bhi_w_sd);
8534 if (ret < 0)
8535 health_data->bhi_w_sd = 0;
AleX Pelosic1cd4752022-05-04 02:15:02 -07008536 /* default thresholds */
Jack Wu5b74da42022-03-12 14:09:28 +08008537 ret = of_property_read_u32(batt_drv->device->of_node, "google,bhi-status-marginal",
AleX Pelosi43bec422022-04-27 21:59:19 -07008538 &health_data->marginal_threshold);
Jack Wu5b74da42022-03-12 14:09:28 +08008539 if (ret < 0)
AleX Pelosi43bec422022-04-27 21:59:19 -07008540 health_data->marginal_threshold = BHI_MARGINAL_THRESHOLD_DEFAULT;
Jack Wu5b74da42022-03-12 14:09:28 +08008541
8542 ret = of_property_read_u32(batt_drv->device->of_node, "google,bhi-status-need-rep",
AleX Pelosi43bec422022-04-27 21:59:19 -07008543 &health_data->need_rep_threshold);
Jack Wu5b74da42022-03-12 14:09:28 +08008544 if (ret < 0)
AleX Pelosi43bec422022-04-27 21:59:19 -07008545 health_data->need_rep_threshold = BHI_NEED_REP_THRESHOLD_DEFAULT;
Jenny Hoa94c30b2022-10-13 16:29:47 +08008546 /* cycle count thresholds */
8547 ret = of_property_read_u32(batt_drv->device->of_node, "google,bhi-cycle-count-marginal",
8548 &health_data->cycle_count_marginal_threshold);
8549 if (ret < 0)
8550 health_data->cycle_count_marginal_threshold = BHI_CC_MARGINAL_THRESHOLD_DEFAULT;
8551
8552 ret = of_property_read_u32(batt_drv->device->of_node, "google,bhi-cycle-count-need-rep",
8553 &health_data->cycle_count_need_rep_threshold);
8554 if (ret < 0)
8555 health_data->cycle_count_need_rep_threshold = BHI_CC_NEED_REP_THRESHOLD_DEFAULT;
AleX Pelosi43bec422022-04-27 21:59:19 -07008556
AleX Pelosia3f22de2022-05-03 23:42:24 -07008557 /* design is the value used to build the charge table */
AleX Pelosi43bec422022-04-27 21:59:19 -07008558 health_data->bhi_data.capacity_design = batt_drv->battery_capacity;
8559
Jenny Hod4bd5bc2022-08-26 16:08:38 +00008560 /* debug data initialization */
Jenny Ho0ffa6c32022-10-13 17:03:19 +08008561 health_data->bhi_debug_cycle_count = 0;
Jenny Hod4bd5bc2022-08-26 16:08:38 +00008562 health_data->bhi_debug_cap_index = 0;
8563 health_data->bhi_debug_imp_index = 0;
8564 health_data->bhi_debug_sd_index = 0;
8565 health_data->bhi_debug_health_index = 0;
8566
AleX Pelosi43bec422022-04-27 21:59:19 -07008567 return 0;
Jack Wu5b74da42022-03-12 14:09:28 +08008568}
8569
yihsiangpeng63c71462023-03-16 18:25:50 +08008570static void batt_fan_bt_init(struct batt_drv *batt_drv) {
8571 int nb_fan_bt, ret;
8572
8573 nb_fan_bt = of_property_count_elems_of_size(batt_drv->device->of_node,
8574 "google,fan-bt-limits", sizeof(u32));
8575 if (nb_fan_bt == NB_FAN_BT_LIMITS) {
8576 ret = of_property_read_u32_array(batt_drv->device->of_node,
8577 "google,fan-bt-limits",
8578 batt_drv->fan_bt_limits,
8579 nb_fan_bt);
8580 if (ret == 0) {
8581 int i;
8582
8583 pr_info("FAN_BT_LIMITS: ");
8584 for (i = 0; i < nb_fan_bt; i++)
8585 pr_info("%d ", batt_drv->fan_bt_limits[i]);
8586
8587 return;
8588 } else {
8589 pr_err("Fail to read google,fan-bt-limits from dtsi, ret=%d\n", ret);
8590 }
8591 }
8592 batt_drv->fan_bt_limits[0] = FAN_BT_LIMIT_NOT_CARE;
8593 batt_drv->fan_bt_limits[1] = FAN_BT_LIMIT_LOW;
8594 batt_drv->fan_bt_limits[2] = FAN_BT_LIMIT_MED;
8595 batt_drv->fan_bt_limits[3] = FAN_BT_LIMIT_HIGH;
8596
8597 pr_info("Use default FAN_BT_LIMITS: %d %d %d %d\n", batt_drv->fan_bt_limits[0],
8598 batt_drv->fan_bt_limits[1],
8599 batt_drv->fan_bt_limits[2],
8600 batt_drv->fan_bt_limits[3]);
8601}
AleX Pelosi43bec422022-04-27 21:59:19 -07008602
8603static int batt_prop_iter(int index, gbms_tag_t *tag, void *ptr)
8604{
8605 static gbms_tag_t keys[] = {GBMS_TAG_HCNT};
8606 const int count = ARRAY_SIZE(keys);
8607
8608 if (index >= 0 && index < count) {
8609 *tag = keys[index];
8610 return 0;
8611 }
8612
8613 return -ENOENT;
8614}
8615
8616static int batt_prop_read(gbms_tag_t tag, void *buff, size_t size, void *ptr)
8617{
8618 struct batt_drv *batt_drv = ptr;
AleX Pelosic1cd4752022-05-04 02:15:02 -07008619 int index, ret = 0;
AleX Pelosi43bec422022-04-27 21:59:19 -07008620
8621 switch (tag) {
8622 case GBMS_TAG_HCNT:
8623 if (size != sizeof(u16))
8624 return -ERANGE;
AleX Pelosic1cd4752022-05-04 02:15:02 -07008625 /* history needs to be enabled for this */
8626 index = hist_get_index(batt_drv->hist_data_saved_cnt, batt_drv);
8627 if (index < 0)
8628 return index;
8629 *(u16 *)buff = index;
AleX Pelosi43bec422022-04-27 21:59:19 -07008630 break;
8631 default:
8632 ret = -ENOENT;
8633 break;
8634 }
8635
8636 return ret;
8637}
8638
8639static struct gbms_storage_desc batt_prop_dsc = {
8640 .iter = batt_prop_iter,
8641 .read = batt_prop_read,
8642};
8643
8644
Ken Tsou8acade12020-07-09 03:17:35 +08008645static void google_battery_init_work(struct work_struct *work)
8646{
8647 struct batt_drv *batt_drv = container_of(work, struct batt_drv,
8648 init_work.work);
8649 struct device_node *node = batt_drv->device->of_node;
8650 struct power_supply *fg_psy = batt_drv->fg_psy;
Wasb Liu6120f262022-01-28 08:00:06 +08008651 const char *batt_vs_tz_name = NULL;
Ken Tsou8acade12020-07-09 03:17:35 +08008652 int ret = 0;
8653
8654 batt_rl_reset(batt_drv);
8655 batt_drv->dead_battery = true; /* clear in batt_work() */
8656 batt_drv->capacity_level = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN;
8657 batt_drv->ssoc_state.buck_enabled = -1;
8658 batt_drv->hold_taper_ws = false;
8659 batt_drv->fake_temp = 0;
AleX Pelosi8c5fa772020-10-03 13:36:56 -07008660 batt_drv->fake_battery_present = -1;
Ken Tsou8acade12020-07-09 03:17:35 +08008661 batt_reset_chg_drv_state(batt_drv);
8662
8663 mutex_init(&batt_drv->chg_lock);
8664 mutex_init(&batt_drv->batt_lock);
8665 mutex_init(&batt_drv->stats_lock);
8666 mutex_init(&batt_drv->cc_data.lock);
Jack Wue3ebc172021-11-11 16:20:34 +08008667 mutex_init(&batt_drv->bpst_state.lock);
Ken Tsou8acade12020-07-09 03:17:35 +08008668
8669 if (!batt_drv->fg_psy) {
8670
8671 fg_psy = power_supply_get_by_name(batt_drv->fg_psy_name);
8672 if (!fg_psy) {
8673 pr_info("failed to get \"%s\" power supply, retrying...\n",
8674 batt_drv->fg_psy_name);
8675 goto retry_init_work;
8676 }
8677
8678 batt_drv->fg_psy = fg_psy;
8679 }
8680
8681 if (!batt_drv->batt_present) {
8682 ret = GPSY_GET_PROP(fg_psy, POWER_SUPPLY_PROP_PRESENT);
8683 if (ret == -EAGAIN)
8684 goto retry_init_work;
8685
8686 batt_drv->batt_present = (ret > 0);
8687 if (!batt_drv->batt_present)
8688 pr_warn("battery not present (ret=%d)\n", ret);
8689 }
8690
8691 ret = of_property_read_u32(node, "google,recharge-soc-threshold",
8692 &batt_drv->ssoc_state.rl_soc_threshold);
8693 if (ret < 0)
8694 batt_drv->ssoc_state.rl_soc_threshold =
8695 DEFAULT_BATT_DRV_RL_SOC_THRESHOLD;
8696
Jenny Hoa7d48db2020-12-08 15:22:09 +08008697 ret = of_property_read_u32(node, "google,bd-trickle-recharge-soc",
8698 &batt_drv->ssoc_state.bd_trickle_recharge_soc);
Ken Tsoudf64e282020-10-28 09:39:33 +08008699 if (ret < 0)
Jenny Hoa7d48db2020-12-08 15:22:09 +08008700 batt_drv->ssoc_state.bd_trickle_recharge_soc =
8701 DEFAULT_BD_TRICKLE_RL_SOC_THRESHOLD;
Ken Tsoudf64e282020-10-28 09:39:33 +08008702
Ken Tsou793502d2020-11-19 20:34:34 +08008703 batt_drv->ssoc_state.bd_trickle_dry_run = false;
8704
Ken Tsou76ee23d2020-12-03 00:49:48 +08008705 ret = of_property_read_u32(node, "google,bd-trickle-reset-sec",
8706 &batt_drv->ssoc_state.bd_trickle_reset_sec);
8707 if (ret < 0)
8708 batt_drv->ssoc_state.bd_trickle_reset_sec =
8709 DEFAULT_BD_TRICKLE_RESET_SEC;
8710
Jenny Ho40dd9752020-12-15 14:39:43 +08008711 batt_drv->ssoc_state.bd_trickle_enable =
8712 of_property_read_bool(node, "google,bd-trickle-enable");
8713
Ken Tsou7c766372020-03-05 17:04:40 +08008714 ret = of_property_read_u32(node, "google,ssoc-delta",
8715 &batt_drv->ssoc_state.ssoc_delta);
8716 if (ret < 0)
8717 batt_drv->ssoc_state.ssoc_delta = SSOC_DELTA;
8718
Jenny Ho47467492021-05-19 17:29:19 +08008719 ret = of_property_read_u32(node, "google,health-safety-margin",
8720 &batt_drv->health_safety_margin);
8721 if (ret < 0)
8722 batt_drv->health_safety_margin = DEFAULT_HEALTH_SAFETY_MARGIN;
8723
Jenny Hoc6f13052022-01-03 10:52:21 +08008724 ret = of_property_read_u32_array(node, "google,temp-record-thr",
8725 batt_drv->sd.temp_thr,
8726 BATT_TEMP_RECORD_THR);
8727 if (ret == 0) {
8728 ret = of_property_read_u32_array(node, "google,soc-record-thr",
8729 batt_drv->sd.soc_thr,
8730 BATT_TEMP_RECORD_THR);
8731 if (ret == 0)
8732 batt_drv->sd.is_enable = true;
8733 }
8734
Jenny Ho7d8c2212022-01-05 18:07:14 +08008735 ret = batt_init_sd(&batt_drv->sd);
8736 if (ret < 0) {
8737 pr_err("Unable to read swelling data, ret=%d\n", ret);
8738 batt_drv->sd.is_enable = false;
8739 }
8740
Jack Wue3ebc172021-11-11 16:20:34 +08008741 /* init bpst setting */
8742 ret = batt_init_bpst_profile(batt_drv);
8743 if (ret < 0)
8744 pr_err("bpst profile disabled, ret=%d\n", ret);
8745
Ken Tsou8acade12020-07-09 03:17:35 +08008746 /* cycle count is cached: read here bc SSOC, chg_profile might use it */
8747 batt_update_cycle_count(batt_drv);
8748
8749 ret = ssoc_init(&batt_drv->ssoc_state, node, fg_psy);
8750 if (ret < 0 && batt_drv->batt_present)
8751 goto retry_init_work;
8752
8753 dump_ssoc_state(&batt_drv->ssoc_state, batt_drv->ssoc_log);
8754
Jenny Ho87fef342020-10-27 17:58:08 +08008755 ret = gbatt_restore_capacity(batt_drv);
Jenny Hodcbef972022-03-28 17:54:21 +08008756 if (ret < 0)
Jenny Ho87fef342020-10-27 17:58:08 +08008757 pr_warn("unable to restore capacity, ret=%d\n", ret);
Jenny Hodcbef972022-03-28 17:54:21 +08008758 else
Jenny Ho87fef342020-10-27 17:58:08 +08008759 batt_drv->ssoc_state.save_soc_available = true;
Jenny Ho87fef342020-10-27 17:58:08 +08008760
Ken Tsou8acade12020-07-09 03:17:35 +08008761 /* could read EEPROM and history here */
8762
AleX Pelosic10eb8b2021-12-14 13:20:21 -08008763 /* chg_profile will use cycle_count when aacr is enabled */
Ken Tsou8acade12020-07-09 03:17:35 +08008764 ret = batt_init_chg_profile(batt_drv);
8765 if (ret == -EPROBE_DEFER)
8766 goto retry_init_work;
8767
8768 if (ret < 0) {
8769 pr_err("charging profile disabled, ret=%d\n", ret);
8770 } else if (batt_drv->battery_capacity) {
AleX Pelosic10eb8b2021-12-14 13:20:21 -08008771 google_battery_dump_profile(&batt_drv->chg_profile);
Ken Tsou8acade12020-07-09 03:17:35 +08008772 }
8773
Jenny Ho74ae1bf2022-12-12 16:53:24 +08008774 batt_drv->temp_filter.enable = of_property_read_bool(node, "google,temp-filter-enable");
8775 if (batt_drv->temp_filter.enable)
8776 batt_init_temp_filter(batt_drv);
8777
AleX Pelosi4a9035f2020-02-28 19:09:57 -08008778 cev_stats_init(&batt_drv->ce_data, &batt_drv->chg_profile);
8779 cev_stats_init(&batt_drv->ce_qual, &batt_drv->chg_profile);
Ken Tsou8acade12020-07-09 03:17:35 +08008780
8781 batt_drv->fg_nb.notifier_call = psy_changed;
8782 ret = power_supply_reg_notifier(&batt_drv->fg_nb);
8783 if (ret < 0)
8784 pr_err("cannot register power supply notifer, ret=%d\n",
8785 ret);
8786
Wasb Liu61720712022-11-16 19:19:53 +08008787 batt_drv->batt_ws = wakeup_source_register(NULL, "google-battery");
Ken Tsou5ecf2f42020-07-16 08:26:05 +08008788 batt_drv->taper_ws = wakeup_source_register(NULL, "Taper");
8789 batt_drv->poll_ws = wakeup_source_register(NULL, "Poll");
8790 batt_drv->msc_ws = wakeup_source_register(NULL, "MSC");
8791 if (!batt_drv->batt_ws || !batt_drv->taper_ws ||
8792 !batt_drv->poll_ws || !batt_drv->msc_ws)
8793 pr_err("failed to register wakeup sources\n");
Ken Tsou8acade12020-07-09 03:17:35 +08008794
8795 mutex_lock(&batt_drv->cc_data.lock);
8796 ret = batt_cycle_count_load(&batt_drv->cc_data);
8797 if (ret < 0)
8798 pr_err("cannot restore bin count ret=%d\n", ret);
8799 mutex_unlock(&batt_drv->cc_data.lock);
8800
8801 batt_drv->fake_capacity = (batt_drv->batt_present) ? -EINVAL
8802 : DEFAULT_BATT_FAKE_CAPACITY;
8803
8804 /* charging configuration */
8805 ret = of_property_read_u32(node, "google,update-interval",
8806 &batt_drv->batt_update_interval);
8807 if (ret < 0)
8808 batt_drv->batt_update_interval = DEFAULT_BATT_UPDATE_INTERVAL;
8809
8810 /* high temperature notify configuration */
8811 ret = of_property_read_u32(batt_drv->device->of_node,
8812 "google,update-high-temp-threshold",
8813 &batt_drv->batt_update_high_temp_threshold);
8814 if (ret < 0)
8815 batt_drv->batt_update_high_temp_threshold =
8816 DEFAULT_HIGH_TEMP_UPDATE_THRESHOLD;
8817 /* charge statistics */
8818 ret = of_property_read_u32(node, "google,chg-stats-qual-time",
Jenny Hoa9ecc2f2021-04-06 11:39:21 +08008819 &batt_drv->chg_sts_qual_time);
Ken Tsou8acade12020-07-09 03:17:35 +08008820 if (ret < 0)
Jenny Hoa9ecc2f2021-04-06 11:39:21 +08008821 batt_drv->chg_sts_qual_time =
Ken Tsou8acade12020-07-09 03:17:35 +08008822 DEFAULT_CHG_STATS_MIN_QUAL_TIME;
8823
8824 ret = of_property_read_u32(node, "google,chg-stats-delta-soc",
Jenny Hoa9ecc2f2021-04-06 11:39:21 +08008825 &batt_drv->chg_sts_delta_soc);
Ken Tsou8acade12020-07-09 03:17:35 +08008826 if (ret < 0)
Jenny Hoa9ecc2f2021-04-06 11:39:21 +08008827 batt_drv->chg_sts_delta_soc =
Ken Tsou8acade12020-07-09 03:17:35 +08008828 DEFAULT_CHG_STATS_MIN_DELTA_SOC;
8829
8830 /* time to full */
8831 ret = ttf_stats_init(&batt_drv->ttf_stats, batt_drv->device,
8832 batt_drv->battery_capacity);
AleX Pelosib3a1a552022-05-03 17:00:11 -07008833 if (ret < 0)
Ken Tsou8acade12020-07-09 03:17:35 +08008834 pr_info("time to full not available\n");
AleX Pelosid0319472020-02-29 12:42:59 -08008835
AleX Pelosib3a1a552022-05-03 17:00:11 -07008836 /* TTF log is used report more things nowadays */
8837 batt_drv->ttf_stats.ttf_log = logbuffer_register("ttf");
8838 if (IS_ERR(batt_drv->ttf_stats.ttf_log)) {
8839 ret = PTR_ERR(batt_drv->ttf_stats.ttf_log);
8840 dev_err(batt_drv->device, "failed to create ttf_log, ret=%d\n", ret);
8841
8842 batt_drv->ttf_stats.ttf_log = NULL;
AleX Pelosid0319472020-02-29 12:42:59 -08008843 }
Ken Tsou8acade12020-07-09 03:17:35 +08008844
AleX Pelosic1cd4752022-05-04 02:15:02 -07008845 /* RAVG: google_resistance */
AleX Pelosia3f22de2022-05-03 23:42:24 -07008846 ret = batt_res_load_data(&batt_drv->health_data.bhi_data.res_state,
8847 batt_drv->fg_psy);
AleX Pelosib3a1a552022-05-03 17:00:11 -07008848 if (ret < 0)
8849 dev_warn(batt_drv->device, "RAVG not available (%d)\n", ret);
AleX Pelosia3f22de2022-05-03 23:42:24 -07008850 batt_res_dump_logs(&batt_drv->health_data.bhi_data.res_state);
Ken Tsou8acade12020-07-09 03:17:35 +08008851
AleX Pelosi043ffbe2020-06-24 22:48:30 -07008852 /* health based charging, triggers */
8853 batt_drv->chg_health.always_on_soc = -1;
8854
AleX Pelosi8e7fd812019-08-16 10:41:46 -07008855 ret = of_property_read_u32(batt_drv->device->of_node,
8856 "google,chg-rest-soc",
8857 &batt_drv->chg_health.rest_soc);
8858 if (ret < 0)
8859 batt_drv->chg_health.rest_soc = -1;
8860
8861 ret = of_property_read_u32(batt_drv->device->of_node,
AleX Pelosi8e7fd812019-08-16 10:41:46 -07008862 "google,chg-rest-rate",
8863 &batt_drv->chg_health.rest_rate);
8864 if (ret < 0)
8865 batt_drv->chg_health.rest_rate = 0;
8866
Jenny Ho6ff91bf2022-09-16 15:11:04 +08008867 ret = of_property_read_u32(batt_drv->device->of_node,
8868 "google,chg-rest-rate-before-trigger",
8869 &batt_drv->chg_health.rest_rate_before_trigger);
8870 if (ret < 0)
8871 batt_drv->chg_health.rest_rate_before_trigger = HEALTH_CHG_RATE_BEFORE_TRIGGER;
8872
Ken Tsou8acade12020-07-09 03:17:35 +08008873 /* override setting google,battery-roundtrip = 0 in device tree */
8874 batt_drv->disable_votes =
8875 of_property_read_bool(node, "google,disable-votes");
8876 if (batt_drv->disable_votes)
8877 pr_info("battery votes disabled\n");
8878
Jenny Ho11d5fcf2020-12-01 06:39:41 +08008879 /* pairing battery vs. device */
8880 if (of_property_read_bool(node, "google,eeprom-pairing"))
Ken Tsou8acade12020-07-09 03:17:35 +08008881 batt_drv->pairing_state = BATT_PAIRING_ENABLED;
Jenny Ho11d5fcf2020-12-01 06:39:41 +08008882 else
Ken Tsou8acade12020-07-09 03:17:35 +08008883 batt_drv->pairing_state = BATT_PAIRING_DISABLED;
Ken Tsou8acade12020-07-09 03:17:35 +08008884
Jenny Ho11d5fcf2020-12-01 06:39:41 +08008885 /* use delta cycle count to adjust collecting period */
Ken Tsou8acade12020-07-09 03:17:35 +08008886 ret = of_property_read_u32(batt_drv->device->of_node,
8887 "google,history-delta-cycle-count",
8888 &batt_drv->hist_delta_cycle_cnt);
8889 if (ret < 0)
8890 batt_drv->hist_delta_cycle_cnt = HCC_DEFAULT_DELTA_CYCLE_CNT;
Jenny Ho11d5fcf2020-12-01 06:39:41 +08008891
Jack Wucfb51f72022-10-26 19:55:26 +08008892 ret = of_property_read_u32(batt_drv->device->of_node, "google,batt-voltage-critical",
8893 &batt_drv->batt_critical_voltage);
8894 if (ret < 0)
8895 batt_drv->batt_critical_voltage = VBATT_CRITICAL_LEVEL;
8896
Wasb Liu6120f262022-01-28 08:00:06 +08008897 /* battery virtual sensor */
8898 ret = of_property_read_string(batt_drv->device->of_node,
8899 "google,batt-vs-tz-name",
8900 &batt_vs_tz_name);
8901 if (ret == 0) {
8902 batt_drv->batt_vs_tz =
8903 thermal_zone_device_register(batt_vs_tz_name, 0, 0,
8904 batt_drv, &batt_vs_tz_ops, NULL, 0, 0);
8905 if (IS_ERR(batt_drv->batt_vs_tz)) {
8906 pr_err("batt_vs tz register failed. err:%ld\n",
8907 PTR_ERR(batt_drv->batt_vs_tz));
8908 batt_drv->batt_vs_tz = NULL;
8909 } else {
8910 thermal_zone_device_update(batt_drv->batt_vs_tz, THERMAL_DEVICE_UP);
8911 }
8912 batt_drv->batt_vs_w = 88;
8913
8914 pr_info("google,batt-vs-tz-name is %s\n", batt_vs_tz_name);
8915 }
8916
AleX Pelosic1cd4752022-05-04 02:15:02 -07008917 /* single battery disconnect */
Ken Yang8cd93f42022-06-06 10:02:35 +00008918 (void)batt_bpst_init_debugfs(batt_drv);
Jack Wue3ebc172021-11-11 16:20:34 +08008919
AleX Pelosi43bec422022-04-27 21:59:19 -07008920 /* these don't require nvm storage */
8921 ret = gbms_storage_register(&batt_prop_dsc, "battery", batt_drv);
8922 if (ret == -EBUSY)
8923 ret = 0;
Jack Wu5b74da42022-03-12 14:09:28 +08008924
AleX Pelosic1cd4752022-05-04 02:15:02 -07008925 /* use delta cycle count != 0 to enable collecting history */
8926 if (batt_drv->hist_delta_cycle_cnt)
8927 batt_drv->blf_state = BATT_LFCOLLECT_ENABLED;
8928
8929 /* google_battery expose history via a standard device */
8930 batt_drv->history = gbms_storage_create_device("battery_history",
8931 GBMS_TAG_HIST);
8932 if (!batt_drv->history)
8933 pr_err("history not available\n");
8934
8935 /* BHI: might need RAVG and battery history */
8936 ret = batt_bhi_init(batt_drv);
8937 if (ret < 0) {
8938 dev_warn(batt_drv->device, "BHI: not supported (%d)\n", ret);
8939 } else {
8940 /* reload the last estimates, */
8941 ret = batt_bhi_data_load(batt_drv);
8942 if (ret < 0)
8943 dev_err(batt_drv->device, "BHI: invalid data, starting fresh (%d)\n", ret);
8944 }
8945
Wasb Liuf40cbb22022-01-26 18:41:03 +08008946 /* power metrics */
8947 schedule_delayed_work(&batt_drv->power_metrics.work,
8948 msecs_to_jiffies(batt_drv->power_metrics.polling_rate * 1000));
8949
AleX Pelosi70444902020-09-08 15:46:12 -07008950 pr_info("google_battery init_work done\n");
Ken Tsou8acade12020-07-09 03:17:35 +08008951
8952 batt_drv->init_complete = true;
8953 batt_drv->resume_complete = true;
8954
8955 schedule_delayed_work(&batt_drv->batt_work, 0);
8956
8957 return;
8958
8959retry_init_work:
8960 schedule_delayed_work(&batt_drv->init_work,
8961 msecs_to_jiffies(BATT_DELAY_INIT_MS));
8962}
8963
8964static struct thermal_zone_of_device_ops google_battery_tz_ops = {
8965 .get_temp = google_battery_tz_get_cycle_count,
8966};
8967
AleX Pelosia3f22de2022-05-03 23:42:24 -07008968static int batt_ravg_init(struct batt_res *res_state, struct device_node *node)
8969{
8970 int ret;
8971
8972 if (of_property_read_bool(node, "google,no-ravg"))
8973 return -ENOENT;
8974
8975 /* Resistance Estimation configuration */
8976 ret = of_property_read_u32(node, "google,res-temp-hi",
8977 &res_state->res_temp_high);
8978 if (ret < 0)
8979 res_state->res_temp_high = DEFAULT_RES_TEMP_HIGH;
8980
8981 ret = of_property_read_u32(node, "google,res-temp-lo",
8982 &res_state->res_temp_low);
8983 if (ret < 0)
8984 res_state->res_temp_low = DEFAULT_RES_TEMP_LOW;
8985
8986 ret = of_property_read_u32(node, "google,res-soc-thresh",
8987 &res_state->ravg_soc_high);
8988 if (ret < 0)
8989 res_state->ravg_soc_high = DEFAULT_RAVG_SOC_HIGH;
8990 ret = of_property_read_u32(node, "google,ravg-soc-low",
8991 &res_state->ravg_soc_low);
8992 if (ret < 0)
8993 res_state->ravg_soc_low = DEFAULT_RAVG_SOC_LOW;
8994
8995 ret = of_property_read_u32(node, "google,res-filt-length",
8996 &res_state->estimate_filter);
8997 if (ret < 0)
8998 res_state->estimate_filter = DEFAULT_RES_FILT_LEN;
8999
9000 return 0;
9001}
9002
Ken Tsou8acade12020-07-09 03:17:35 +08009003static int google_battery_probe(struct platform_device *pdev)
9004{
9005 const char *fg_psy_name, *psy_name = NULL;
9006 struct batt_drv *batt_drv;
9007 int ret;
9008 struct power_supply_config psy_cfg = {};
9009
9010 batt_drv = devm_kzalloc(&pdev->dev, sizeof(*batt_drv), GFP_KERNEL);
9011 if (!batt_drv)
9012 return -ENOMEM;
9013
9014 batt_drv->device = &pdev->dev;
9015
AleX Pelosi70444902020-09-08 15:46:12 -07009016 ret = of_property_read_string(pdev->dev.of_node, "google,fg-psy-name",
9017 &fg_psy_name);
Ken Tsou8acade12020-07-09 03:17:35 +08009018 if (ret != 0) {
9019 pr_err("cannot read google,fg-psy-name, ret=%d\n", ret);
9020 return -EINVAL;
9021 }
9022
AleX Pelosi70444902020-09-08 15:46:12 -07009023 batt_drv->fg_psy_name = devm_kstrdup(&pdev->dev, fg_psy_name,
9024 GFP_KERNEL);
Ken Tsou8acade12020-07-09 03:17:35 +08009025 if (!batt_drv->fg_psy_name)
9026 return -ENOMEM;
9027
9028 /* change name and type for debug/test */
9029 if (of_property_read_bool(pdev->dev.of_node, "google,psy-type-unknown"))
9030 gbatt_psy_desc.type = POWER_SUPPLY_TYPE_UNKNOWN;
9031
9032 ret = of_property_read_string(pdev->dev.of_node,
9033 "google,psy-name", &psy_name);
9034 if (ret == 0) {
9035 gbatt_psy_desc.name =
9036 devm_kstrdup(&pdev->dev, psy_name, GFP_KERNEL);
9037 }
9038
9039 INIT_DELAYED_WORK(&batt_drv->init_work, google_battery_init_work);
9040 INIT_DELAYED_WORK(&batt_drv->batt_work, google_battery_work);
Wasb Liuf40cbb22022-01-26 18:41:03 +08009041 INIT_DELAYED_WORK(&batt_drv->power_metrics.work, power_metrics_data_work);
Jenny Ho74ae1bf2022-12-12 16:53:24 +08009042 INIT_DELAYED_WORK(&batt_drv->temp_filter.work, google_battery_temp_filter_work);
Ken Tsou8acade12020-07-09 03:17:35 +08009043 platform_set_drvdata(pdev, batt_drv);
9044
9045 psy_cfg.drv_data = batt_drv;
9046 psy_cfg.of_node = pdev->dev.of_node;
9047
9048 batt_drv->psy = devm_power_supply_register(batt_drv->device,
9049 &gbatt_psy_desc, &psy_cfg);
9050 if (IS_ERR(batt_drv->psy)) {
9051 ret = PTR_ERR(batt_drv->psy);
9052 if (ret == -EPROBE_DEFER)
9053 return -EPROBE_DEFER;
9054
9055 /* TODO: fail with -ENODEV */
9056 dev_err(batt_drv->device,
9057 "Couldn't register as power supply, ret=%d\n", ret);
9058 }
9059
AleX Pelosid71f5852020-08-26 18:40:28 -07009060 batt_drv->ssoc_log = logbuffer_register("ssoc");
Ken Tsou8acade12020-07-09 03:17:35 +08009061 if (IS_ERR(batt_drv->ssoc_log)) {
9062 ret = PTR_ERR(batt_drv->ssoc_log);
9063 dev_err(batt_drv->device,
9064 "failed to create ssoc_log, ret=%d\n", ret);
9065 batt_drv->ssoc_log = NULL;
9066 }
9067
AleX Pelosia3f22de2022-05-03 23:42:24 -07009068 /* RAVG: google_resistance */
9069 ret = batt_ravg_init(&batt_drv->health_data.bhi_data.res_state,
9070 pdev->dev.of_node);
Ken Tsou8acade12020-07-09 03:17:35 +08009071 if (ret < 0)
AleX Pelosia3f22de2022-05-03 23:42:24 -07009072 dev_info(batt_drv->device, "RAVG: not available\n");
Ken Tsou8acade12020-07-09 03:17:35 +08009073
9074 batt_drv->tz_dev = thermal_zone_of_sensor_register(batt_drv->device,
9075 0, batt_drv, &google_battery_tz_ops);
9076 if (IS_ERR(batt_drv->tz_dev)) {
9077 pr_err("battery tz register failed. err:%ld\n",
9078 PTR_ERR(batt_drv->tz_dev));
9079 ret = PTR_ERR(batt_drv->tz_dev);
9080 batt_drv->tz_dev = NULL;
9081 } else {
9082 thermal_zone_device_update(batt_drv->tz_dev, THERMAL_DEVICE_UP);
9083 }
yihsiangpeng305623d2021-05-06 15:07:05 +08009084
yihsiangpeng63c71462023-03-16 18:25:50 +08009085 /* Fan levels limits from battery temperature */
9086 batt_fan_bt_init(batt_drv);
yihsiangpeng305623d2021-05-06 15:07:05 +08009087 batt_drv->fan_level = -1;
AleX Pelosia7e0da82021-07-15 13:09:42 -07009088 batt_drv->fan_last_level = -1;
yihsiangpeng305623d2021-05-06 15:07:05 +08009089 batt_drv->fan_level_votable =
Ken Tsoua15d1fa2022-01-24 14:42:27 +08009090 gvotable_create_int_election(NULL, gvotable_comparator_int_max,
9091 fan_level_cb, batt_drv);
9092 if (IS_ERR_OR_NULL(batt_drv->fan_level_votable)) {
yihsiangpeng305623d2021-05-06 15:07:05 +08009093 ret = PTR_ERR(batt_drv->fan_level_votable);
9094 dev_err(batt_drv->device, "Fail to create fan_level_votable\n");
9095 batt_drv->fan_level_votable = NULL;
9096 } else {
Ken Tsoua15d1fa2022-01-24 14:42:27 +08009097 gvotable_set_vote2str(batt_drv->fan_level_votable,
9098 gvotable_v2s_int);
9099 gvotable_election_set_name(batt_drv->fan_level_votable,
9100 VOTABLE_FAN_LEVEL);
9101 gvotable_cast_long_vote(batt_drv->fan_level_votable,
9102 "DEFAULT", FAN_LVL_UNKNOWN, true);
yihsiangpeng305623d2021-05-06 15:07:05 +08009103 }
9104
AleX Pelosi9bfe2312022-04-26 13:20:43 -07009105 /* charge speed interface: status and type */
9106 batt_drv->csi_status_votable =
9107 gvotable_create_int_election(NULL, gvotable_comparator_int_min,
9108 csi_status_cb, batt_drv);
9109 if (IS_ERR_OR_NULL(batt_drv->csi_status_votable)) {
9110 ret = PTR_ERR(batt_drv->csi_status_votable);
9111 batt_drv->csi_status_votable = NULL;
9112 }
9113
9114 gvotable_set_default(batt_drv->csi_status_votable, (void *)CSI_STATUS_UNKNOWN);
9115 gvotable_set_vote2str(batt_drv->csi_status_votable, gvotable_v2s_int);
9116 gvotable_election_set_name(batt_drv->csi_status_votable, VOTABLE_CSI_STATUS);
9117
9118 batt_drv->csi_type_votable =
9119 gvotable_create_int_election(NULL, gvotable_comparator_int_min,
9120 csi_type_cb, batt_drv);
9121 if (IS_ERR_OR_NULL(batt_drv->csi_type_votable)) {
9122 ret = PTR_ERR(batt_drv->csi_type_votable);
9123 batt_drv->csi_type_votable = NULL;
9124 }
9125
9126 gvotable_set_default(batt_drv->csi_type_votable, (void *)CSI_TYPE_UNKNOWN);
9127 gvotable_set_vote2str(batt_drv->csi_type_votable, gvotable_v2s_int);
9128 gvotable_election_set_name(batt_drv->csi_type_votable, VOTABLE_CSI_TYPE);
9129
AleX Pelosic10eb8b2021-12-14 13:20:21 -08009130 /* AACR server side */
9131 batt_drv->aacr_cycle_grace = AACR_START_CYCLE_DEFAULT;
9132 batt_drv->aacr_cycle_max = AACR_MAX_CYCLE_DEFAULT;
9133 batt_drv->aacr_state = BATT_AACR_DISABLED;
9134
Ken Yang8cd93f42022-06-06 10:02:35 +00009135 /* create the sysfs node */
9136 batt_init_fs(batt_drv);
9137 batt_bpst_init_fs(batt_drv);
9138
Wasb Liu91a9d712022-11-25 11:35:26 +08009139 /* debugfs */
9140 (void)batt_init_debugfs(batt_drv);
9141
Ken Tsou8acade12020-07-09 03:17:35 +08009142 /* give time to fg driver to start */
9143 schedule_delayed_work(&batt_drv->init_work,
9144 msecs_to_jiffies(BATT_DELAY_INIT_MS));
9145
Wasb Liuf40cbb22022-01-26 18:41:03 +08009146 /* power metrics */
9147 batt_drv->power_metrics.polling_rate = 30;
9148 batt_drv->power_metrics.interval = 120;
9149
Ken Tsou8acade12020-07-09 03:17:35 +08009150 return 0;
9151}
9152
9153static int google_battery_remove(struct platform_device *pdev)
9154{
9155 struct batt_drv *batt_drv = platform_get_drvdata(pdev);
9156
9157 if (!batt_drv)
9158 return 0;
9159
9160 if (batt_drv->ssoc_log)
AleX Pelosid71f5852020-08-26 18:40:28 -07009161 logbuffer_unregister(batt_drv->ssoc_log);
AleX Pelosid0319472020-02-29 12:42:59 -08009162 if (batt_drv->ttf_stats.ttf_log)
9163 logbuffer_unregister(batt_drv->ttf_stats.ttf_log);
Ken Tsou8acade12020-07-09 03:17:35 +08009164 if (batt_drv->tz_dev)
9165 thermal_zone_of_sensor_unregister(batt_drv->device,
9166 batt_drv->tz_dev);
9167 if (batt_drv->history)
9168 gbms_storage_cleanup_device(batt_drv->history);
9169
9170 if (batt_drv->fg_psy)
9171 power_supply_put(batt_drv->fg_psy);
9172
9173 batt_hist_free_data(batt_drv->hist_data);
9174
9175 gbms_free_chg_profile(&batt_drv->chg_profile);
9176
Ken Tsou5ecf2f42020-07-16 08:26:05 +08009177 wakeup_source_unregister(batt_drv->msc_ws);
9178 wakeup_source_unregister(batt_drv->batt_ws);
9179 wakeup_source_unregister(batt_drv->taper_ws);
9180 wakeup_source_unregister(batt_drv->poll_ws);
Ken Tsou8acade12020-07-09 03:17:35 +08009181
Ken Tsoua15d1fa2022-01-24 14:42:27 +08009182 gvotable_destroy_election(batt_drv->fan_level_votable);
AleX Pelosi9bfe2312022-04-26 13:20:43 -07009183 gvotable_destroy_election(batt_drv->csi_status_votable);
9184 gvotable_destroy_election(batt_drv->csi_type_votable);
9185
yihsiangpeng305623d2021-05-06 15:07:05 +08009186 batt_drv->fan_level_votable = NULL;
AleX Pelosi9bfe2312022-04-26 13:20:43 -07009187 batt_drv->csi_status_votable = NULL;
9188 batt_drv->csi_type_votable = NULL;
yihsiangpeng305623d2021-05-06 15:07:05 +08009189
Ken Tsou8acade12020-07-09 03:17:35 +08009190 return 0;
9191}
9192
9193#ifdef SUPPORT_PM_SLEEP
9194static int gbatt_pm_suspend(struct device *dev)
9195{
9196 struct platform_device *pdev = to_platform_device(dev);
9197 struct batt_drv *batt_drv = platform_get_drvdata(pdev);
9198
9199 pm_runtime_get_sync(batt_drv->device);
9200 batt_drv->resume_complete = false;
9201 pm_runtime_put_sync(batt_drv->device);
9202
9203 return 0;
9204}
9205
9206static int gbatt_pm_resume(struct device *dev)
9207{
9208 struct platform_device *pdev = to_platform_device(dev);
9209 struct batt_drv *batt_drv = platform_get_drvdata(pdev);
9210
9211 pm_runtime_get_sync(batt_drv->device);
9212 batt_drv->resume_complete = true;
Jenny Ho74ae1bf2022-12-12 16:53:24 +08009213 batt_drv->temp_filter.resume_delay = true;
Ken Tsou8acade12020-07-09 03:17:35 +08009214 pm_runtime_put_sync(batt_drv->device);
9215
9216 mod_delayed_work(system_wq, &batt_drv->batt_work, 0);
9217
9218 return 0;
9219}
9220
9221static const struct dev_pm_ops gbatt_pm_ops = {
Jenny Ho6594dc92021-03-31 14:44:36 +08009222 SET_SYSTEM_SLEEP_PM_OPS(gbatt_pm_suspend, gbatt_pm_resume)
Ken Tsou8acade12020-07-09 03:17:35 +08009223};
9224#endif
9225
9226
9227static const struct of_device_id google_charger_of_match[] = {
9228 {.compatible = "google,battery"},
9229 {},
9230};
9231MODULE_DEVICE_TABLE(of, google_charger_of_match);
9232
9233
9234static struct platform_driver google_battery_driver = {
9235 .driver = {
9236 .name = "google,battery",
9237 .owner = THIS_MODULE,
9238 .of_match_table = google_charger_of_match,
AleX Pelosi32892282020-09-11 02:29:20 -07009239 .probe_type = PROBE_PREFER_ASYNCHRONOUS,
Ken Tsou8acade12020-07-09 03:17:35 +08009240#ifdef SUPPORT_PM_SLEEP
9241 .pm = &gbatt_pm_ops,
9242#endif
Ken Tsou8acade12020-07-09 03:17:35 +08009243 },
9244 .probe = google_battery_probe,
9245 .remove = google_battery_remove,
9246};
9247
AleX Pelosi70444902020-09-08 15:46:12 -07009248module_platform_driver(google_battery_driver);
9249
Ken Tsou8acade12020-07-09 03:17:35 +08009250MODULE_DESCRIPTION("Google Battery Driver");
9251MODULE_AUTHOR("AleX Pelosi <apelosi@google.com>");
9252MODULE_LICENSE("GPL");