blob: b6ff343951bde82366e2426d610e6826d71647a8 [file] [log] [blame]
Ken Tsou8acade12020-07-09 03:17:35 +08001/* SPDX-License-Identifier: GPL-2.0 */
2/*
3 * Fuel gauge driver for Maxim Fuel Gauges with M5 Algo
4 *
5 * Copyright (C) 2018 Google Inc.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 */
17
18#define pr_fmt(fmt) KBUILD_MODNAME ": %s " fmt, __func__
19
Jenny Hof77200e2021-05-31 17:32:55 +080020#include <linux/crc8.h>
Ken Tsou8acade12020-07-09 03:17:35 +080021#include <linux/err.h>
22#include <linux/module.h>
23#include <linux/of.h>
24#include <linux/of_gpio.h>
25#include <linux/power_supply.h>
26#include <linux/regmap.h>
27#include "google_bms.h"
28#include "google_psy.h"
Ken Tsou8acade12020-07-09 03:17:35 +080029
Ken Tsou8acade12020-07-09 03:17:35 +080030#include "max_m5.h"
31
32#ifdef CONFIG_DEBUG_FS
33#include <linux/debugfs.h>
34#endif
35
AleX Pelosi962aedf2021-01-07 15:39:43 -080036/* Config2: must not enable TAlert */
37#define MODEL_VERSION_REG MAX_M5_TALRTTH
38#define MODEL_VERSION_SHIFT 8
39#define MODEL_VERSION_MASK 0xff
40
41#define MAX_M5_TASKPERIOD_175MS 0x1680
42#define MAX_M5_TASKPERIOD_351MS 0x2D00
43
Jenny Hof77200e2021-05-31 17:32:55 +080044#define MAX_M5_CRC8_POLYNOMIAL 0x07 /* (x^8) + x^2 + x + 1 */
45DECLARE_CRC8_TABLE(m5_crc8_table);
46
Jenny Ho20010d42023-03-16 05:47:15 +080047int max_m5_recalibration(struct max_m5_data *m5_data, int algo, u16 cap);
48
Ken Tsou8acade12020-07-09 03:17:35 +080049static void dump_model(struct device *dev, u16 *data, int count)
50{
51 int i, j, len;
52 char buff[16 * 5 + 1] = {};
53
54 for (i = 0; i < count; i += 16) {
55
56 for (len = 0, j = 0; j < 16; j++)
57 len += scnprintf(&buff[len], sizeof(buff) - len,
58 "%04x ", data[i + j]);
59
60 dev_info(dev, "%x: %s\n", i + MAX_M5_FG_MODEL_START, buff);
61 }
62}
63
64/* input current is in the fuel gauge */
yihsiangpengcfb3ff82021-02-04 14:48:54 +080065int max_m5_read_actual_input_current_ua(struct i2c_client *client, int *iic_raw)
Ken Tsou8acade12020-07-09 03:17:35 +080066{
67 struct max_m5_data *m5_data = max1720x_get_model_data(client);
AleX Pelosie8b80cd2020-12-17 21:22:11 -080068 unsigned long sum = 0;
69 const int loops = 4;
Ken Tsou8acade12020-07-09 03:17:35 +080070 unsigned int tmp;
AleX Pelosie8b80cd2020-12-17 21:22:11 -080071 int i, rtn;
Ken Tsou8acade12020-07-09 03:17:35 +080072
73 if (!m5_data || !m5_data->regmap)
74 return -ENODEV;
75
AleX Pelosie8b80cd2020-12-17 21:22:11 -080076 for (i = 0; i < loops; i++) {
77 rtn = regmap_read(m5_data->regmap->regmap, MAX_M5_IIN, &tmp);
78 if (rtn) {
79 pr_err("Failed to read %x\n", MAX_M5_IIN);
80 return rtn;
81 }
Ken Tsou8acade12020-07-09 03:17:35 +080082
AleX Pelosie8b80cd2020-12-17 21:22:11 -080083 sum += tmp;
84 }
85
yihsiangpengcfb3ff82021-02-04 14:48:54 +080086 *iic_raw = sum / loops;
AleX Pelosie8b80cd2020-12-17 21:22:11 -080087 return 0;
Ken Tsou8acade12020-07-09 03:17:35 +080088}
89EXPORT_SYMBOL_GPL(max_m5_read_actual_input_current_ua);
90
Jack Wu83f98e32021-08-09 18:15:52 +080091int max_m5_read_vbypass(struct i2c_client *client, int *volt)
92{
93 struct max_m5_data *m5_data = max1720x_get_model_data(client);
94 unsigned int tmp;
95 int ret;
96
97 if (!m5_data || !m5_data->regmap)
98 return -ENODEV;
99
100 ret = regmap_read(m5_data->regmap->regmap, MAX_M5_VBYP, &tmp);
101 if (ret) {
102 pr_err("Failed to read %x\n", MAX_M5_VBYP);
103 return ret;
104 }
105
106 /* LSB: 0.427246mV */
107 *volt = div_u64((u64) tmp * 427246, 1000);
108 return 0;
109}
110EXPORT_SYMBOL_GPL(max_m5_read_vbypass);
111
Ken Tsou8acade12020-07-09 03:17:35 +0800112int max_m5_reg_read(struct i2c_client *client, unsigned int reg,
113 unsigned int *val)
114{
115 struct max_m5_data *m5_data = max1720x_get_model_data(client);
116
117 if (!m5_data || !m5_data->regmap)
118 return -ENODEV;
119
120 return regmap_read(m5_data->regmap->regmap, reg, val);
121}
122EXPORT_SYMBOL_GPL(max_m5_reg_read);
123
124int max_m5_reg_write(struct i2c_client *client, unsigned int reg,
125 unsigned int val)
126{
127 struct max_m5_data *m5_data = max1720x_get_model_data(client);
128
129 if (!m5_data || !m5_data->regmap)
130 return -ENODEV;
131
132 return regmap_write(m5_data->regmap->regmap, reg, val);
133}
134EXPORT_SYMBOL_GPL(max_m5_reg_write);
135
136
137static int max_m5_read_custom_model(struct regmap *regmap, u16 *model_data,
138 int count)
139{
140 return regmap_raw_read(regmap, MAX_M5_FG_MODEL_START, model_data,
141 count * 2);
142}
143
144static int max_m5_write_custom_model(struct regmap *regmap, u16 *model_data,
145 int count)
146{
147 return regmap_raw_write(regmap, MAX_M5_FG_MODEL_START, model_data,
148 count * 2);
149}
150
Jenny Ho1ccf7e92023-12-20 17:05:59 +0800151int max_m5_model_lock(struct regmap *regmap, bool enabled)
Ken Tsou8acade12020-07-09 03:17:35 +0800152{
153 u16 code[2] = {0x59, 0xC4};
154
155 if (enabled) {
156 code[0] = 0;
157 code[1] = 0;
158 }
159
160 return regmap_raw_write(regmap, MAX_M5_UNLOCK_MODEL_ACCESS, code,
161 sizeof(code));
162}
163
164static int mem16test(u16 *data, u16 code, int count)
165{
166 int same, i;
167
168 for (i = 0, same = 1; same && i < count; i++)
169 same = data[i] == code;
170
171 return same;
172}
173
174/* load custom model b/137037210 */
175static int max_m5_update_custom_model(struct max_m5_data *m5_data)
176{
177 int retries, ret;
178 bool success;
179 u16 *data;
180
181 data = kzalloc(m5_data->custom_model_size * 2, GFP_KERNEL);
182 if (!data)
183 return -ENOMEM;
184
185 /* un lock, update the model */
186 for (success = false, retries = 3; !success && retries > 0; retries--) {
187
188 ret = max_m5_model_lock(m5_data->regmap->regmap, false);
189 if (ret < 0) {
190 dev_err(m5_data->dev, "cannot unlock model access (%d)\n",
191 ret);
192 continue;
193 }
194
195 ret = max_m5_write_custom_model(m5_data->regmap->regmap,
196 m5_data->custom_model,
197 m5_data->custom_model_size);
198 if (ret < 0) {
199 dev_err(m5_data->dev, "cannot write custom model (%d)\n",
200 ret);
201 continue;
202 }
203
204 ret = max_m5_read_custom_model(m5_data->regmap->regmap,
205 data,
206 m5_data->custom_model_size);
207 if (ret < 0) {
208 dev_err(m5_data->dev, "cannot write custom model (%d)\n",
209 ret);
210 continue;
211 }
212
213 ret = memcmp(m5_data->custom_model, data,
214 m5_data->custom_model_size * 2);
215 success = ret == 0;
216 if (!success) {
217 dump_model(m5_data->dev, m5_data->custom_model,
218 m5_data->custom_model_size);
219 dump_model(m5_data->dev, data,
220 m5_data->custom_model_size);
221 }
222
223 }
224
225 if (!success) {
226 dev_err(m5_data->dev, "cannot write custom model (%d)\n", ret);
227 kfree(data);
228 return -EIO;
229 }
230
231 /* lock and verify lock */
232 for (retries = 3; retries > 0; retries--) {
233 int same;
234
235 ret = max_m5_model_lock(m5_data->regmap->regmap, true);
236 if (ret < 0) {
237 dev_err(m5_data->dev, "cannot lock model access (%d)\n",
238 ret);
239 continue;
240 }
241
242 ret = max_m5_read_custom_model(m5_data->regmap->regmap, data,
243 m5_data->custom_model_size);
244 if (ret < 0) {
245 dev_err(m5_data->dev, "cannot read custom model (%d)\n",
246 ret);
247 continue;
248 }
249
250 /* model is locked when read retuns all 0xffff */
251 same = mem16test(data, 0xffff, m5_data->custom_model_size);
252 if (same)
253 break;
254 }
255
256 kfree(data);
257 return 0;
258}
259
260/* Step 7: Write custom parameters */
261static int max_m5_update_custom_parameters(struct max_m5_data *m5_data)
262{
263 struct max_m5_custom_parameters *cp = &m5_data->parameters;
264 struct max17x0x_regmap *regmap = m5_data->regmap;
265 int tmp, ret;
266 u16 vfsoc;
267
Jenny Hoa1fbc9b2022-05-27 07:33:54 +0000268 ret = REGMAP_WRITE_VERIFY(regmap, MAX_M5_REPCAP, 0x0);
Ken Tsou8acade12020-07-09 03:17:35 +0800269 if (ret == 0)
270 ret = REGMAP_WRITE_VERIFY(regmap, MAX_M5_RELAXCFG,
271 cp->relaxcfg);
272 if (ret < 0)
273 return -EIO;
274
275 ret = REGMAP_WRITE_VERIFY(regmap, MAX_M5_UNLOCK_EXTRA_CONFIG,
276 MAX_M5_UNLOCK_EXTRA_CONFIG_UNLOCK_CODE);
277 if (ret < 0) {
278 dev_err(m5_data->dev, "cannot unlock extra config (%d)\n", ret);
279 return -EIO;
280 }
281
282 ret = REGMAP_READ(regmap, MAX_M5_VFSOC, &vfsoc);
283 if (ret < 0)
284 return ret;
285
286 if (ret == 0)
287 ret = REGMAP_WRITE_VERIFY(regmap, MAX_M5_VFSOC0, vfsoc);
288
289 if (ret == 0)
Jenny Ho64506a92022-07-08 06:51:00 +0000290 ret = REGMAP_WRITE_VERIFY(regmap, MAX_M5_LEARNCFG, cp->learncfg);
Ken Tsou8acade12020-07-09 03:17:35 +0800291 if (ret == 0)
292 ret = REGMAP_WRITE(regmap, MAX_M5_CONFIG, cp->config);
293 if (ret == 0)
294 ret = REGMAP_WRITE(regmap, MAX_M5_CONFIG2, cp->config2);
295 if (ret == 0)
296 ret = REGMAP_WRITE(regmap, MAX_M5_FULLSOCTHR, cp->fullsocthr);
297 if (ret == 0)
298 ret = REGMAP_WRITE_VERIFY(regmap, MAX_M5_FULLCAPREP,
299 cp->fullcaprep);
300 if (ret == 0)
301 ret = REGMAP_WRITE_VERIFY(regmap, MAX_M5_DESIGNCAP,
302 cp->designcap);
303 if (ret == 0)
304 ret = REGMAP_WRITE_VERIFY(regmap, MAX_M5_DPACC, cp->dpacc);
305 if (ret == 0)
306 ret = REGMAP_WRITE_VERIFY(regmap, MAX_M5_DQACC, cp->dqacc);
307 if (ret == 0)
308 ret = REGMAP_WRITE_VERIFY(regmap, MAX_M5_FULLCAPNOM,
309 cp->fullcapnom);
310 if (ret == 0)
311 ret = REGMAP_WRITE(regmap, MAX_M5_VEMPTY, cp->v_empty);
312 if (ret == 0)
313 ret = REGMAP_WRITE_VERIFY(regmap, MAX_M5_QRTABLE00,
314 cp->qresidual00);
315 if (ret == 0)
316 ret = REGMAP_WRITE_VERIFY(regmap, MAX_M5_QRTABLE10,
317 cp->qresidual10);
318 if (ret == 0)
319 ret = REGMAP_WRITE_VERIFY(regmap, MAX_M5_QRTABLE20,
320 cp->qresidual20);
321 if (ret == 0)
322 ret = REGMAP_WRITE_VERIFY(regmap, MAX_M5_QRTABLE30,
323 cp->qresidual30);
324 if (ret == 0)
AleX Pelosid03a2562020-11-16 15:35:19 -0800325 ret = REGMAP_WRITE_VERIFY(regmap, MAX_M5_RCOMP0, cp->rcomp0);
Ken Tsou8acade12020-07-09 03:17:35 +0800326 if (ret == 0)
AleX Pelosid03a2562020-11-16 15:35:19 -0800327 ret = REGMAP_WRITE_VERIFY(regmap, MAX_M5_TEMPCO, cp->tempco);
Ken Tsou8acade12020-07-09 03:17:35 +0800328 if (ret == 0)
Jenny Ho628931e2021-09-29 12:16:40 +0800329 ret = REGMAP_WRITE(regmap, MAX_M5_TASKPERIOD, cp->taskperiod);
330 if (ret == 0)
Ken Tsou8acade12020-07-09 03:17:35 +0800331 ret = REGMAP_WRITE(regmap, MAX_M5_ICHGTERM, cp->ichgterm);
332 if (ret == 0)
333 ret = REGMAP_WRITE(regmap, MAX_M5_TGAIN, cp->tgain);
334 if (ret == 0)
335 ret = REGMAP_WRITE(regmap, MAX_M5_TOFF, cp->toff);
336 if (ret == 0)
337 ret = REGMAP_WRITE(regmap, MAX_M5_MISCCFG, cp->misccfg);
338 if (ret < 0)
339 goto exit_done;
340
341 ret = REGMAP_WRITE_VERIFY(regmap, MAX_M5_UNLOCK_EXTRA_CONFIG,
342 MAX_M5_UNLOCK_EXTRA_CONFIG_UNLOCK_CODE);
343 if (ret < 0) {
344 dev_err(m5_data->dev, "cannot unlock extra config (%d)\n", ret);
345 goto exit_done;
346 }
347
348 if (ret == 0)
349 ret = REGMAP_WRITE(regmap, MAX_M5_ATRATE, cp->atrate);
350 if (ret == 0)
351 ret = REGMAP_WRITE_VERIFY(regmap, MAX_M5_CV_MIXCAP,
352 (cp->fullcapnom * 75) / 100);
353 if (ret == 0)
354 ret = REGMAP_WRITE_VERIFY(regmap, MAX_M5_CV_HALFTIME, 0x600);
355 if (ret == 0)
356 ret = REGMAP_WRITE(regmap, MAX_M5_CONVGCFG, cp->convgcfg);
357
358exit_done:
359 tmp = REGMAP_WRITE_VERIFY(regmap, MAX_M5_UNLOCK_EXTRA_CONFIG,
360 MAX_M5_UNLOCK_EXTRA_CONFIG_LOCK_CODE);
361 if (tmp < 0) {
362 dev_err(m5_data->dev, "cannot lock extra config (%d)\n", tmp);
363 return tmp;
364 }
365
366 return ret;
367}
368
AleX Pelosi962aedf2021-01-07 15:39:43 -0800369int max_m5_model_read_version(const struct max_m5_data *m5_data)
370{
371 u16 version;
372 int ret;
373
374 ret = REGMAP_READ(m5_data->regmap, MODEL_VERSION_REG, &version);
375 if (ret < 0)
376 return ret;
377
378 return (version >> MODEL_VERSION_SHIFT) & MODEL_VERSION_MASK;
379}
380
Spade Lee7812b9d2024-03-29 08:22:59 +0000381int max_m5_model_write_version(const struct max_m5_data *m5_data, int version)
AleX Pelosi962aedf2021-01-07 15:39:43 -0800382{
383 u16 temp;
384 int ret;
385
386 if (version == MAX_M5_INVALID_VERSION)
387 return 0;
388
389 ret = REGMAP_READ(m5_data->regmap, MODEL_VERSION_REG, &temp);
390 if (ret == 0) {
391 temp &= ~(MODEL_VERSION_MASK << MODEL_VERSION_SHIFT);
392 temp |= (version & MODEL_VERSION_MASK) << MODEL_VERSION_SHIFT;
393
394 ret = REGMAP_WRITE(m5_data->regmap, MODEL_VERSION_REG, temp);
395 }
396
397 return ret;
398}
399
Jenny Hoc6bc5882021-02-18 09:21:21 +0800400static int max_m5_model_read_rc(const struct max_m5_data *m5_data)
401{
402 u16 learncfg;
403 int ret;
404
405 ret = REGMAP_READ(m5_data->regmap, MAX_M5_LEARNCFG, &learncfg);
406 if (ret < 0)
407 return ret;
408
409 return (learncfg & MAX_M5_LEARNCFG_RC_VER);
410}
411
412int max_m5_reset_state_data(struct max_m5_data *m5_data)
413{
414 struct model_state_save data;
415 int ret = 0;
416
417 memset(&data, 0xff, sizeof(data));
418
419 ret = gbms_storage_write(GBMS_TAG_GMSR, &data, sizeof(data));
420 if (ret < 0)
421 dev_warn(m5_data->dev, "Erase GMSR fail (%d)\n", ret);
422
Spade Lee4c0fb512023-11-13 04:04:27 +0000423 return ret == sizeof(data) ? 0 : ret;
Jenny Hoc6bc5882021-02-18 09:21:21 +0800424}
425
426int max_m5_needs_reset_model_data(const struct max_m5_data *m5_data)
427{
428 int read_rc, para_rc;
429
Jenny Ho7c691da2021-05-19 06:15:54 +0800430 if (m5_data->force_reset_model_data)
431 return 1;
432
Jenny Hoc6bc5882021-02-18 09:21:21 +0800433 read_rc = max_m5_model_read_rc(m5_data);
434 if (read_rc < 0)
435 return 0;
436
437 para_rc = m5_data->parameters.learncfg & MAX_M5_LEARNCFG_RC_VER;
438
439 /* RC2 -> RC1 */
440 if (read_rc == MAX_M5_LEARNCFG_RC2 && para_rc == MAX_M5_LEARNCFG_RC1)
441 return 1;
442
443 return 0;
444}
445
AleX Pelosi962aedf2021-01-07 15:39:43 -0800446/* convert taskperiod to the scaling factor for capacity */
447static int max_m5_period2caplsb(u16 taskperiod)
448{
449 int cap_lsb = -EINVAL;
450
451 if (taskperiod == MAX_M5_TASKPERIOD_351MS)
452 cap_lsb = 1;
453 else if (taskperiod == MAX_M5_TASKPERIOD_175MS)
454 cap_lsb = 0;
455
456 return cap_lsb;
457}
458
Jenny Ho31bdf262023-07-27 07:52:04 +0800459static int max_m5_update_gauge_custom_parameters(struct max_m5_data *m5_data)
Ken Tsou8acade12020-07-09 03:17:35 +0800460{
461 struct max17x0x_regmap *regmap = m5_data->regmap;
462 int ret, retries;
463 u16 data;
464
AleX Pelosi00eec962021-01-13 19:32:33 -0800465 /* write parameters (which include state) */
Ken Tsou8acade12020-07-09 03:17:35 +0800466 ret = max_m5_update_custom_parameters(m5_data);
467 if (ret < 0) {
468 dev_err(m5_data->dev, "cannot update custom parameters (%d)\n",
469 ret);
470 return ret;
471 }
472
AleX Pelosi962aedf2021-01-07 15:39:43 -0800473 /* tcurve, filterconfig, taskperiod, version are not part of model */
Ken Tsou8acade12020-07-09 03:17:35 +0800474 ret = REGMAP_WRITE(regmap, MAX_M5_TCURVE, m5_data->parameters.tcurve);
475 if (ret < 0) {
AleX Pelosi962aedf2021-01-07 15:39:43 -0800476 dev_err(m5_data->dev, "cannot update tcurve (%d)\n", ret);
Ken Tsou8acade12020-07-09 03:17:35 +0800477 return ret;
478 }
479
480 ret = REGMAP_WRITE(regmap, MAX_M5_FILTERCFG,
481 m5_data->parameters.filtercfg);
482 if (ret < 0) {
AleX Pelosi962aedf2021-01-07 15:39:43 -0800483 dev_err(m5_data->dev, "cannot update filter config (%d)\n", ret);
Ken Tsou8acade12020-07-09 03:17:35 +0800484 return ret;
485 }
486
Jenny Hoe8b877c2022-01-10 03:27:53 +0800487 ret = REGMAP_WRITE(regmap, MAX_M5_CGAIN,
488 m5_data->parameters.cgain);
489 if (ret < 0)
490 dev_err(m5_data->dev, "cannot update cgain (%d)\n", ret);
AleX Pelosi962aedf2021-01-07 15:39:43 -0800491
492 m5_data->cap_lsb = max_m5_period2caplsb(m5_data->parameters.taskperiod);
493
494 /*
495 * version could be in the DT: this will overwrite it if set.
496 * Invalid version is not written out.
497 */
498 ret = max_m5_model_write_version(m5_data, m5_data->model_version);
499 if (ret < 0) {
500 dev_err(m5_data->dev, "cannot update version (%d)\n", ret);
501 return ret;
502 }
503
504 /* trigger load model */
Ken Tsou8acade12020-07-09 03:17:35 +0800505 ret = REGMAP_READ(regmap, MAX_M5_CONFIG2, &data);
506 if (ret == 0)
AleX Pelosi962aedf2021-01-07 15:39:43 -0800507 ret = REGMAP_WRITE(regmap, MAX_M5_CONFIG2,
508 data | MAX_M5_CONFIG2_LDMDL);
Ken Tsou8acade12020-07-09 03:17:35 +0800509 if (ret < 0) {
510 dev_err(m5_data->dev, "failed start model loading (%d)\n", ret);
511 return ret;
512 }
513
AleX Pelosi962aedf2021-01-07 15:39:43 -0800514 /* around 400ms for this usually */
515 for (retries = 20; retries > 0; retries--) {
Ken Tsou8acade12020-07-09 03:17:35 +0800516
517 mdelay(50);
518
519 ret = REGMAP_READ(regmap, MAX_M5_CONFIG2, &data);
AleX Pelosi962aedf2021-01-07 15:39:43 -0800520 if (ret == 0 && !(data & MAX_M5_CONFIG2_LDMDL)) {
Jenny Ho61d4f782023-09-20 13:09:29 +0800521 ret = REGMAP_READ(regmap, MAX_M5_REPCAP, &data);
522 if (ret == 0 && data != 0) {
523 int temp;
AleX Pelosi962aedf2021-01-07 15:39:43 -0800524
Jenny Ho61d4f782023-09-20 13:09:29 +0800525 temp = max_m5_model_read_version(m5_data);
526 if (m5_data->model_version == MAX_M5_INVALID_VERSION) {
527 dev_info(m5_data->dev, "No Model Version, Current %x\n",
528 temp);
529 } else if (temp != m5_data->model_version) {
530 dev_info(m5_data->dev, "Model Version %x, Mismatch %x\n",
531 m5_data->model_version, temp);
532 return -EINVAL;
533 }
534
535 return 0;
AleX Pelosi962aedf2021-01-07 15:39:43 -0800536 }
AleX Pelosi962aedf2021-01-07 15:39:43 -0800537 }
Ken Tsou8acade12020-07-09 03:17:35 +0800538 }
539
540 return -ETIMEDOUT;
541}
542
Jenny Ho31bdf262023-07-27 07:52:04 +0800543/* protected from mutex_lock(&chip->model_lock) */
544static int max_m5_check_model_parameters(struct max_m5_data *m5_data)
545{
546 struct max_m5_custom_parameters *cp = &m5_data->parameters;
547 struct max17x0x_regmap *regmap = m5_data->regmap;
548 int ret, cap_delta_threshold, cap_delta_real;
549 u16 fullcaprep, fullcapnom;
550
551 /* b/240115405#comment44 */
552 cap_delta_threshold = abs(cp->fullcapnom - cp->fullcaprep) + cp->designcap / 100;
553
554 ret = REGMAP_READ(regmap, MAX_M5_FULLCAPREP, &fullcaprep);
555 if (ret < 0)
556 return ret;
557
558 ret = REGMAP_READ(regmap, MAX_M5_FULLCAPNOM, &fullcapnom);
559 if (ret < 0)
560 return ret;
561
562 cap_delta_real = abs(fullcapnom - fullcaprep);
563
564 dev_info(m5_data->dev, "write: nom:%#x, rep:%#x, design:%#x (threshold=%d),"
565 " read: nom:%#x, rep:%#x (delta=%d), retry:%d\n",
566 cp->fullcapnom, cp->fullcaprep, cp->designcap, cap_delta_threshold,
567 fullcapnom, fullcaprep, cap_delta_real, m5_data->load_retry);
568
569 if (cap_delta_real > cap_delta_threshold && m5_data->load_retry < MAX_M5_RETRY_TIMES)
570 return -ERANGE;
571
572 return 0;
573}
574
575/* 0 is ok , protected from mutex_lock(&chip->model_lock) in max7102x_battery.c */
576int max_m5_load_gauge_model(struct max_m5_data *m5_data)
577{
578 struct max17x0x_regmap *regmap = m5_data->regmap;
579 int ret, retries;
580 u16 data;
581
582 if (!regmap)
583 return -EIO;
584
585 if (!m5_data || !m5_data->custom_model || !m5_data->custom_model_size)
586 return -ENODATA;
587
588 /* check FStat.DNR to wait it clear for data ready */
589 for (retries = 20; retries > 0; retries--) {
590 ret = REGMAP_READ(regmap, MAX_M5_FSTAT, &data);
591 if (ret == 0 && !(data & MAX_M5_FSTAT_DNR))
592 break;
593 msleep(50);
594 }
595 dev_info(m5_data->dev, "retries:%d, FSTAT:%#x\n", retries, data);
596
597 /* loading in progress, this is not good (tm) */
598 ret = REGMAP_READ(regmap, MAX_M5_CONFIG2, &data);
599 if (ret == 0 && (data & MAX_M5_CONFIG2_LDMDL)) {
600 dev_err(m5_data->dev, "load model in progress (%x)\n", data);
601 return -EINVAL;
602 }
603
604 ret = max_m5_update_custom_model(m5_data);
605 if (ret < 0) {
606 dev_err(m5_data->dev, "cannot update custom model (%d)\n", ret);
607 return ret;
608 }
609
610 do {
611 msleep(500);
612
613 ret = max_m5_update_gauge_custom_parameters(m5_data);
614 if (ret < 0)
615 return ret;
616
617 ret = max_m5_check_model_parameters(m5_data);
618 if (ret < 0) {
619 m5_data->load_retry++;
620 } else {
621 m5_data->load_retry = 0;
622 break;
623 }
624 } while (m5_data->load_retry < MAX_M5_RETRY_TIMES);
625
626 return ret;
627}
628
AleX Pelosifed7fb12020-11-10 01:22:22 -0800629/* algo version is ignored here, check code in max1720x_outliers */
630int max_m5_fixup_outliers(struct max1720x_drift_data *ddata,
631 struct max_m5_data *m5_data)
632{
633 if (ddata->design_capacity != m5_data->parameters.designcap)
634 ddata->design_capacity = m5_data->parameters.designcap;
635 if (ddata->ini_rcomp0 != m5_data->parameters.rcomp0)
636 ddata->ini_rcomp0 = m5_data->parameters.rcomp0;
637 if (ddata->ini_tempco != m5_data->parameters.tempco)
638 ddata->ini_tempco = m5_data->parameters.tempco;
639
640 return 0;
641}
642
AleX Pelosifc6f66d2021-01-12 18:14:48 -0800643static bool memtst(void *buf, char c, size_t count)
644{
645 bool same = true;
646 int i;
647
648 for (i = 0; same && i < count; i++)
AleX Pelosi00eec962021-01-13 19:32:33 -0800649 same = ((char *)buf)[i] == c;
AleX Pelosifc6f66d2021-01-12 18:14:48 -0800650
651 return same;
652}
653
Jenny Ho1cf43582023-03-24 12:34:23 +0800654/* TODO: make it adjustable, set 10% tolerance here */
655#define MAX_M5_CAP_MAX_RATIO 110
656static int max_m5_check_state_data(struct model_state_save *state,
657 struct max_m5_custom_parameters *ini)
AleX Pelosifc6f66d2021-01-12 18:14:48 -0800658{
659 bool bad_residual, empty;
Jenny Ho1cf43582023-03-24 12:34:23 +0800660 int max_cap = ini->designcap * MAX_M5_CAP_MAX_RATIO / 100;
AleX Pelosifc6f66d2021-01-12 18:14:48 -0800661
AleX Pelosi00eec962021-01-13 19:32:33 -0800662 empty = memtst(state, 0xff, sizeof(*state));
AleX Pelosifc6f66d2021-01-12 18:14:48 -0800663 if (empty)
AleX Pelosi00eec962021-01-13 19:32:33 -0800664 return -ENODATA;
AleX Pelosifc6f66d2021-01-12 18:14:48 -0800665
666 if (state->rcomp0 == 0xFF)
AleX Pelosi00eec962021-01-13 19:32:33 -0800667 return -ERANGE;
AleX Pelosifc6f66d2021-01-12 18:14:48 -0800668
Jenny Ho7e578102021-05-24 18:22:55 +0800669 if (state->tempco == 0xFFFF)
670 return -ERANGE;
671
AleX Pelosifc6f66d2021-01-12 18:14:48 -0800672 bad_residual = state->qresidual00 == 0xffff &&
673 state->qresidual10 == 0xffff &&
674 state->qresidual20 == 0xffff &&
675 state->qresidual30 == 0xffff;
AleX Pelosi00eec962021-01-13 19:32:33 -0800676
AleX Pelosifc6f66d2021-01-12 18:14:48 -0800677 if (bad_residual)
AleX Pelosi00eec962021-01-13 19:32:33 -0800678 return -EINVAL;
AleX Pelosifc6f66d2021-01-12 18:14:48 -0800679
Jenny Ho1cf43582023-03-24 12:34:23 +0800680 if (state->fullcaprep > max_cap)
681 return -ERANGE;
682
683 if (state->fullcapnom > max_cap)
684 return -ERANGE;
685
AleX Pelosifc6f66d2021-01-12 18:14:48 -0800686 return 0;
687}
688
Jenny Hof77200e2021-05-31 17:32:55 +0800689static u8 max_m5_crc(u8 *pdata, size_t nbytes, u8 crc)
690{
691 return crc8(m5_crc8_table, pdata, nbytes, crc);
692}
693
694static u8 max_m5_data_crc(char *reason, struct model_state_save *state)
695{
696 u8 crc;
697
698 /* Last byte is for saving CRC */
699 crc = max_m5_crc((u8 *)state, sizeof(struct model_state_save) - 1,
700 CRC8_INIT_VALUE);
701
702 pr_info("%s gmsr: %X %X %X %X %X %X %X %X %X %X %X %X (%X)\n",
703 reason, state->rcomp0, state->tempco,
704 state->fullcaprep, state->fullcapnom,
705 state->qresidual00, state->qresidual10,
706 state->qresidual20, state->qresidual30,
Jenny Hodc6c8a02021-10-29 06:14:33 +0800707 state->cycles, state->cv_mixcap,
Jenny Hof77200e2021-05-31 17:32:55 +0800708 state->halftime, state->crc, crc);
709
710 return crc;
711}
712
AleX Pelosifc6f66d2021-01-12 18:14:48 -0800713/*
714 * Load parameters and model state from permanent storage.
715 * Called on boot after POR
716 */
Ken Tsou8acade12020-07-09 03:17:35 +0800717int max_m5_load_state_data(struct max_m5_data *m5_data)
718{
AleX Pelosifc6f66d2021-01-12 18:14:48 -0800719 struct max_m5_custom_parameters *cp = &m5_data->parameters;
Jenny Hof77200e2021-05-31 17:32:55 +0800720 u8 crc;
AleX Pelosifc6f66d2021-01-12 18:14:48 -0800721 int ret;
Ken Tsou8acade12020-07-09 03:17:35 +0800722
723 if (!m5_data)
724 return -EINVAL;
725
726 /* might return -EAGAIN during init */
727 ret = gbms_storage_read(GBMS_TAG_GMSR, &m5_data->model_save,
728 sizeof(m5_data->model_save));
729 if (ret < 0) {
730 dev_info(m5_data->dev, "Load Model Data Failed ret=%d\n", ret);
731 return ret;
732 }
733
Jenny Ho1cf43582023-03-24 12:34:23 +0800734 ret = max_m5_check_state_data(&m5_data->model_save, cp);
AleX Pelosifc6f66d2021-01-12 18:14:48 -0800735 if (ret < 0)
736 return ret;
Ken Tsou8acade12020-07-09 03:17:35 +0800737
Jenny Hof77200e2021-05-31 17:32:55 +0800738 crc = max_m5_data_crc("restore", &m5_data->model_save);
739 if (crc != m5_data->model_save.crc)
740 return -EINVAL;
741
AleX Pelosifc6f66d2021-01-12 18:14:48 -0800742 cp->rcomp0 = m5_data->model_save.rcomp0;
743 cp->tempco = m5_data->model_save.tempco;
744 cp->fullcaprep = m5_data->model_save.fullcaprep;
745 cp->fullcapnom = m5_data->model_save.fullcapnom;
746 cp->qresidual00 = m5_data->model_save.qresidual00;
747 cp->qresidual10 = m5_data->model_save.qresidual10;
748 cp->qresidual20 = m5_data->model_save.qresidual20;
749 cp->qresidual30 = m5_data->model_save.qresidual30;
Jenny Ho5a7e49b2023-04-25 21:05:42 +0800750 /* b/278492168 restore dpacc with fullcapnom for taskperiod=351ms */
751 if (cp->taskperiod == 0x2d00 && cp->dpacc == 0x3200)
752 cp->dqacc = cp->fullcapnom >> 2;
753 else if (cp->taskperiod == 0x2d00 && cp->dpacc == 0x0c80)
754 cp->dqacc = cp->fullcapnom >> 4;
755 else
756 dev_warn(m5_data->dev, "taskperiod:%#x, dpacc:%#x, dqacc:%#x\n",
757 cp->taskperiod, cp->dpacc, cp->dqacc);
AleX Pelosifc6f66d2021-01-12 18:14:48 -0800758
Ken Tsou8acade12020-07-09 03:17:35 +0800759 m5_data->cycles = m5_data->model_save.cycles;
Jenny Hodc6c8a02021-10-29 06:14:33 +0800760 m5_data->cv_mixcap = m5_data->model_save.cv_mixcap;
Ken Tsou8acade12020-07-09 03:17:35 +0800761 m5_data->halftime = m5_data->model_save.halftime;
762
AleX Pelosi00eec962021-01-13 19:32:33 -0800763 return 0;
Ken Tsou8acade12020-07-09 03:17:35 +0800764}
765
766/* save/commit parameters and model state to permanent storage */
767int max_m5_save_state_data(struct max_m5_data *m5_data)
768{
AleX Pelosifc6f66d2021-01-12 18:14:48 -0800769 struct max_m5_custom_parameters *cp = &m5_data->parameters;
Jenny Hof77200e2021-05-31 17:32:55 +0800770 struct model_state_save rb;
Jenny Hoddf88b32022-03-02 18:18:13 +0800771 u16 learncfg;
Ken Tsou8acade12020-07-09 03:17:35 +0800772 int ret = 0;
773
Jenny Hoddf88b32022-03-02 18:18:13 +0800774 /* Do not save when in RC1 stage b/213425610 */
775 ret = REGMAP_READ(m5_data->regmap, MAX_M5_LEARNCFG, &learncfg);
776 if (ret < 0)
777 return ret;
778
779 if ((learncfg & MAX_M5_LEARNCFG_RC_VER) == MAX_M5_LEARNCFG_RC1)
780 return -ENOSYS;
781
AleX Pelosifc6f66d2021-01-12 18:14:48 -0800782 m5_data->model_save.rcomp0 = cp->rcomp0;
783 m5_data->model_save.tempco = cp->tempco;
784 m5_data->model_save.fullcaprep = cp->fullcaprep;
785 m5_data->model_save.fullcapnom = cp->fullcapnom;
786 m5_data->model_save.qresidual00 = cp->qresidual00;
787 m5_data->model_save.qresidual10 = cp->qresidual10;
788 m5_data->model_save.qresidual20 = cp->qresidual20;
789 m5_data->model_save.qresidual30 = cp->qresidual30;
790
Ken Tsou8acade12020-07-09 03:17:35 +0800791 m5_data->model_save.cycles = m5_data->cycles;
Jenny Hodc6c8a02021-10-29 06:14:33 +0800792 m5_data->model_save.cv_mixcap = m5_data->cv_mixcap;
Ken Tsou8acade12020-07-09 03:17:35 +0800793 m5_data->model_save.halftime = m5_data->halftime;
794
Jenny Hof77200e2021-05-31 17:32:55 +0800795 m5_data->model_save.crc = max_m5_data_crc("save",
796 &m5_data->model_save);
797
Ken Tsou8acade12020-07-09 03:17:35 +0800798 ret = gbms_storage_write(GBMS_TAG_GMSR,
799 (const void *)&m5_data->model_save,
800 sizeof(m5_data->model_save));
801 if (ret < 0)
802 return ret;
803
Jenny Hof77200e2021-05-31 17:32:55 +0800804 if (ret != sizeof(m5_data->model_save))
805 return -ERANGE;
806
807 /* Read back to make sure data all good */
808 ret = gbms_storage_read(GBMS_TAG_GMSR, &rb, sizeof(rb));
809 if (ret < 0) {
810 dev_info(m5_data->dev, "Read Back Data Failed ret=%d\n", ret);
811 return ret;
812 }
813
814 if (rb.rcomp0 != m5_data->model_save.rcomp0 ||
815 rb.tempco != m5_data->model_save.tempco ||
816 rb.fullcaprep != m5_data->model_save.fullcaprep ||
817 rb.fullcapnom != m5_data->model_save.fullcapnom ||
818 rb.qresidual00 != m5_data->model_save.qresidual00 ||
819 rb.qresidual10 != m5_data->model_save.qresidual10 ||
820 rb.qresidual20 != m5_data->model_save.qresidual20 ||
821 rb.qresidual30 != m5_data->model_save.qresidual30 ||
822 rb.cycles != m5_data->model_save.cycles ||
Jenny Hodc6c8a02021-10-29 06:14:33 +0800823 rb.cv_mixcap != m5_data->model_save.cv_mixcap ||
Jenny Hof77200e2021-05-31 17:32:55 +0800824 rb.halftime != m5_data->model_save.halftime ||
825 rb.crc != m5_data->model_save.crc)
826 return -EINVAL;
827
828 return 0;
Ken Tsou8acade12020-07-09 03:17:35 +0800829}
830
AleX Pelosifc6f66d2021-01-12 18:14:48 -0800831/* 0 ok, < 0 error. Call after reading from the FG */
832int max_m5_model_check_state(struct max_m5_data *m5_data)
833{
834 struct max_m5_custom_parameters *fg_param = &m5_data->parameters;
835 bool bad_residual;
836
837 if (fg_param->rcomp0 == 0xFF)
AleX Pelosi00eec962021-01-13 19:32:33 -0800838 return -ERANGE;
AleX Pelosifc6f66d2021-01-12 18:14:48 -0800839
Jenny Ho7e578102021-05-24 18:22:55 +0800840 if (fg_param->tempco == 0xFFFF)
841 return -ERANGE;
842
AleX Pelosifc6f66d2021-01-12 18:14:48 -0800843 bad_residual = fg_param->qresidual00 == 0xffff &&
844 fg_param->qresidual10 == 0xffff &&
845 fg_param->qresidual20 == 0xffff &&
846 fg_param->qresidual30 == 0xffff;
847 if (bad_residual)
AleX Pelosi00eec962021-01-13 19:32:33 -0800848 return -EINVAL;
AleX Pelosifc6f66d2021-01-12 18:14:48 -0800849
850 return 0;
851}
852
853/*
854 * read fuel gauge state to parameters/model state.
855 * NOTE: Called on boot if POR is not set or during save state.
856 */
Ken Tsou8acade12020-07-09 03:17:35 +0800857int max_m5_model_read_state(struct max_m5_data *m5_data)
858{
859 int rc;
860 struct max17x0x_regmap *regmap = m5_data->regmap;
861
862 rc= REGMAP_READ(regmap, MAX_M5_RCOMP0, &m5_data->parameters.rcomp0);
863 if (rc == 0)
864 rc = REGMAP_READ(regmap, MAX_M5_TEMPCO,
865 &m5_data->parameters.tempco);
866 if (rc == 0)
867 rc = REGMAP_READ(regmap, MAX_M5_FULLCAPREP,
868 &m5_data->parameters.fullcaprep);
869 if (rc == 0)
870 rc = REGMAP_READ(regmap, MAX_M5_CYCLES, &m5_data->cycles);
871 if (rc == 0)
872 rc = REGMAP_READ(regmap, MAX_M5_FULLCAPNOM,
873 &m5_data->parameters.fullcapnom);
874 if (rc == 0)
875 rc = REGMAP_READ(regmap, MAX_M5_QRTABLE00,
876 &m5_data->parameters.qresidual00);
877 if (rc == 0)
878 rc = REGMAP_READ(regmap, MAX_M5_QRTABLE10,
879 &m5_data->parameters.qresidual10);
880 if (rc == 0)
881 rc = REGMAP_READ(regmap, MAX_M5_QRTABLE20,
882 &m5_data->parameters.qresidual20);
883 if (rc == 0)
884 rc = REGMAP_READ(regmap, MAX_M5_QRTABLE30,
885 &m5_data->parameters.qresidual30);
886 if (rc == 0)
Jenny Hodc6c8a02021-10-29 06:14:33 +0800887 rc = REGMAP_READ(regmap, MAX_M5_CV_MIXCAP,
888 &m5_data->cv_mixcap);
Ken Tsou8acade12020-07-09 03:17:35 +0800889 if (rc == 0)
890 rc = REGMAP_READ(regmap, MAX_M5_CV_HALFTIME,
891 &m5_data->halftime);
Jenny Hoe8b877c2022-01-10 03:27:53 +0800892 if (rc == 0)
893 rc = REGMAP_READ(regmap, MAX_M5_CGAIN,
894 &m5_data->parameters.cgain);
Ken Tsou8acade12020-07-09 03:17:35 +0800895
896 return rc;
897}
898
Jenny Ho3baaaaf2023-09-01 18:16:58 +0800899int max_m5_get_designcap(const struct max_m5_data *m5_data)
900{
901 return m5_data->parameters.designcap;
902}
903
Ken Tsou8acade12020-07-09 03:17:35 +0800904ssize_t max_m5_model_state_cstr(char *buf, int max,
905 struct max_m5_data *m5_data)
906{
907 int len = 0;
908
909 len += scnprintf(&buf[len], max - len,"%02x:%02x\n", MAX_M5_RCOMP0,
910 m5_data->parameters.rcomp0);
911 len += scnprintf(&buf[len], max - len,"%02x:%02x\n", MAX_M5_TEMPCO,
912 m5_data->parameters.tempco);
913 len += scnprintf(&buf[len], max - len,"%02x:%02x\n", MAX_M5_FULLCAPREP,
914 m5_data->parameters.fullcaprep);
915 len += scnprintf(&buf[len], max - len,"%02x:%02x\n", MAX_M5_CYCLES,
916 m5_data->cycles);
917 len += scnprintf(&buf[len], max - len,"%02x:%02x\n", MAX_M5_FULLCAPNOM,
918 m5_data->parameters.fullcapnom);
919 len += scnprintf(&buf[len], max - len,"%02x:%02x\n", MAX_M5_QRTABLE00,
920 m5_data->parameters.qresidual00);
921 len += scnprintf(&buf[len], max - len,"%02x:%02x\n", MAX_M5_QRTABLE10,
922 m5_data->parameters.qresidual10);
923 len += scnprintf(&buf[len], max - len,"%02x:%02x\n", MAX_M5_QRTABLE20,
924 m5_data->parameters.qresidual20);
925 len += scnprintf(&buf[len], max - len,"%02x:%02x\n", MAX_M5_QRTABLE30,
926 m5_data->parameters.qresidual30);
Jenny Hodc6c8a02021-10-29 06:14:33 +0800927 len += scnprintf(&buf[len], max - len,"%02x:%02x\n", MAX_M5_CV_MIXCAP,
928 m5_data->cv_mixcap);
Ken Tsou8acade12020-07-09 03:17:35 +0800929 len += scnprintf(&buf[len], max - len,"%02x:%02x\n", MAX_M5_CV_HALFTIME,
930 m5_data->halftime);
931
932 return len;
933}
934
Jenny Hoc6bc5882021-02-18 09:21:21 +0800935ssize_t max_m5_gmsr_state_cstr(char *buf, int max)
936{
937 struct model_state_save saved_data;
938 int ret = 0, len = 0;
939
940 ret = gbms_storage_read(GBMS_TAG_GMSR, &saved_data, GBMS_GMSR_LEN);
941 if (ret < 0)
942 return ret;
943
944 len = scnprintf(&buf[len], max - len,
945 "rcomp0 :%04X\ntempco :%04X\n"
946 "fullcaprep :%04X\ncycles :%04X\n"
947 "fullcapnom :%04X\nqresidual00:%04X\n"
948 "qresidual10:%04X\nqresidual20:%04X\n"
Jenny Hodc6c8a02021-10-29 06:14:33 +0800949 "qresidual30:%04X\ncv_mixcap :%04X\n"
Jenny Hoc6bc5882021-02-18 09:21:21 +0800950 "halftime :%04X\n",
951 saved_data.rcomp0, saved_data.tempco,
952 saved_data.fullcaprep, saved_data.cycles,
953 saved_data.fullcapnom, saved_data.qresidual00,
954 saved_data.qresidual10, saved_data.qresidual20,
Jenny Hodc6c8a02021-10-29 06:14:33 +0800955 saved_data.qresidual30, saved_data.cv_mixcap,
Jenny Hoc6bc5882021-02-18 09:21:21 +0800956 saved_data.halftime);
957
958 return len;
959}
960
Ken Tsou8acade12020-07-09 03:17:35 +0800961/* can be use to restore parametes and model state after POR */
962int max_m5_model_state_sscan(struct max_m5_data *m5_data, const char *buf,
963 int max)
964{
965 int ret, index, reg, val;
966
967 for (index = 0; index < max ; index += 1) {
968 ret = sscanf(&buf[index], "%x:%x", &reg, &val);
969 if (ret != 2) {
970 dev_err(m5_data->dev, "@%d: sscan error %d\n",
971 index, ret);
972 return -EINVAL;
973 }
974
975 dev_info(m5_data->dev, "@%d: reg=%x val=%x\n", index, reg, val);
976
977 switch (reg) {
AleX Pelosifc6f66d2021-01-12 18:14:48 -0800978 /* model parameters (fg-params) */
Ken Tsou8acade12020-07-09 03:17:35 +0800979 case MAX_M5_IAVGEMPTY:
980 m5_data->parameters.iavg_empty = val;
981 break;
982 case MAX_M5_RELAXCFG:
983 m5_data->parameters.relaxcfg = val;
984 break;
985 case MAX_M5_LEARNCFG:
986 m5_data->parameters.learncfg = val;
987 break;
988 case MAX_M5_CONFIG:
989 m5_data->parameters.config = val;
990 break;
991 case MAX_M5_CONFIG2:
992 m5_data->parameters.config2 = val;
993 break;
994 case MAX_M5_FULLSOCTHR:
995 m5_data->parameters.fullsocthr = val;
996 break;
997 case MAX_M5_DESIGNCAP:
998 m5_data->parameters.designcap = val;
999 break;
1000 case MAX_M5_DPACC:
1001 m5_data->parameters.dpacc = val;
1002 break;
1003 case MAX_M5_DQACC:
1004 m5_data->parameters.dqacc = val;
1005 break;
1006 case MAX_M5_VEMPTY:
1007 m5_data->parameters.v_empty = val;
1008 break;
1009 case MAX_M5_TGAIN:
1010 m5_data->parameters.tgain = val;
1011 break;
1012 case MAX_M5_TOFF:
1013 m5_data->parameters.toff = val;
1014 break;
1015 case MAX_M5_TCURVE:
1016 m5_data->parameters.tcurve = val;
1017 break;
1018 case MAX_M5_MISCCFG:
1019 m5_data->parameters.misccfg = val;
1020 break;
1021 case MAX_M5_ATRATE:
1022 m5_data->parameters.atrate = val;
1023 break;
1024 case MAX_M5_CONVGCFG:
1025 m5_data->parameters.convgcfg = val;
1026 break;
1027 case MAX_M5_FILTERCFG:
1028 m5_data->parameters.filtercfg = val;
1029 break;
AleX Pelosi962aedf2021-01-07 15:39:43 -08001030 case MAX_M5_TASKPERIOD:
1031 if (val != MAX_M5_TASKPERIOD_175MS &&
1032 val != MAX_M5_TASKPERIOD_351MS) {
1033 dev_err(m5_data->dev, "@%d: reg=%x val %x not allowed\n",
1034 index, reg, val);
1035 return -EINVAL;
1036 }
1037
1038 m5_data->parameters.taskperiod = val;
1039 break;
Ken Tsou8acade12020-07-09 03:17:35 +08001040
AleX Pelosifc6f66d2021-01-12 18:14:48 -08001041 /* model state, saved and restored */
Ken Tsou8acade12020-07-09 03:17:35 +08001042 case MAX_M5_RCOMP0:
1043 m5_data->parameters.rcomp0 = val;
1044 break;
1045 case MAX_M5_TEMPCO:
1046 m5_data->parameters.tempco = val;
1047 break;
1048 case MAX_M5_FULLCAPREP:
1049 m5_data->parameters.fullcaprep = val;
1050 break;
1051 case MAX_M5_CYCLES:
1052 m5_data->cycles = val;
1053 break;
1054 case MAX_M5_FULLCAPNOM:
1055 m5_data->parameters.fullcapnom = val;
1056 break;
1057 case MAX_M5_QRTABLE00:
1058 m5_data->parameters.qresidual00 = val;
1059 break;
1060 case MAX_M5_QRTABLE10:
1061 m5_data->parameters.qresidual10 = val;
1062 break;
1063 case MAX_M5_QRTABLE20:
1064 m5_data->parameters.qresidual20 = val;
1065 break;
1066 case MAX_M5_QRTABLE30:
1067 m5_data->parameters.qresidual30 = val;
1068 break;
Jenny Hodc6c8a02021-10-29 06:14:33 +08001069 case MAX_M5_CV_MIXCAP:
1070 m5_data->cv_mixcap = val;
Ken Tsou8acade12020-07-09 03:17:35 +08001071 break;
1072 case MAX_M5_CV_HALFTIME:
1073 m5_data->halftime = val;
1074 break;
Jenny Hoe8b877c2022-01-10 03:27:53 +08001075 case MAX_M5_CGAIN:
1076 m5_data->parameters.cgain = val;
1077 break;
Ken Tsou8acade12020-07-09 03:17:35 +08001078 default:
1079 dev_err(m5_data->dev, "@%d: reg=%x out of range\n",
1080 index, reg);
1081 return -EINVAL;
1082 }
1083
1084 for ( ; index < max && buf[index] != '\n'; index++)
1085 ;
1086 }
1087
1088 return 0;
1089}
1090
AleX Pelosi962aedf2021-01-07 15:39:43 -08001091/* b/177099997 TaskPeriod = 351 ms changes the lsb for capacity conversions */
1092static int max_m5_read_taskperiod(int *cap_lsb, struct max17x0x_regmap *regmap)
1093{
1094 u16 data;
1095 int ret;
1096
1097 /* TaskPeriod = 351 ms changes the lsb for capacity conversions */
1098 ret = REGMAP_READ(regmap, MAX_M5_TASKPERIOD, &data);
1099 if (ret == 0)
1100 ret = max_m5_period2caplsb(data);
1101 if (ret < 0)
1102 return ret;
1103
1104 *cap_lsb = ret;
1105 return 0;
1106}
1107
AleX Pelosifc6f66d2021-01-12 18:14:48 -08001108int max_m5_model_get_cap_lsb(const struct max_m5_data *m5_data)
1109{
1110 struct max17x0x_regmap *regmap = m5_data->regmap;
1111 int cap_lsb;
1112
1113 return max_m5_read_taskperiod(&cap_lsb, regmap) < 0 ? -1 : cap_lsb;
1114}
1115
Ken Tsou8acade12020-07-09 03:17:35 +08001116/* custom model parameters */
1117int max_m5_fg_model_cstr(char *buf, int max, const struct max_m5_data *m5_data)
1118{
1119 int i, len;
1120
1121 if (!m5_data->custom_model || !m5_data->custom_model_size)
1122 return -EINVAL;
1123
1124 for (len = 0, i = 0; i < m5_data->custom_model_size; i += 1)
1125 len += scnprintf(&buf[len], max - len, "%x: %04x\n",
1126 MAX_M5_FG_MODEL_START + i,
1127 m5_data->custom_model[i]);
1128
1129 return len;
1130}
1131
Jenny Ho70a44322023-11-22 00:13:54 +08001132int max_m5_get_rc_switch_param(struct max_m5_data *m5_data, u16 *rc2_tempco, u16 *rc2_learncfg)
1133{
1134 if (m5_data->parameters.tempco <= 0 || m5_data->parameters.learncfg <= 0)
1135 return -EINVAL;
1136
1137 *rc2_tempco = m5_data->parameters.tempco;
1138 *rc2_learncfg = m5_data->parameters.learncfg;
1139
1140 return 0;
1141}
1142
Ken Tsou8acade12020-07-09 03:17:35 +08001143/* custom model parameters */
1144int max_m5_fg_model_sscan(struct max_m5_data *m5_data, const char *buf, int max)
1145{
1146 int ret, index, reg, val, fg_model_end;
1147
1148 if (!m5_data->custom_model)
1149 return -EINVAL;
1150
1151 /* use the default size */
1152 if (!m5_data->custom_model_size)
1153 m5_data->custom_model_size = MAX_M5_FG_MODEL_SIZE;
1154
1155 fg_model_end = MAX_M5_FG_MODEL_START + m5_data->custom_model_size;
1156 for (index = 0; index < max ; index += 1) {
1157 ret = sscanf(&buf[index], "%x:%x", &reg, &val);
1158 if (ret != 2) {
1159 dev_err(m5_data->dev, "@%d: sscan error %d\n",
1160 index, ret);
1161 return -EINVAL;
1162 }
1163
1164 dev_info(m5_data->dev, "@%d: reg=%x val=%x\n", index, reg, val);
1165
1166 if (reg >= MAX_M5_FG_MODEL_START && reg < fg_model_end) {
1167 const int offset = reg - MAX_M5_FG_MODEL_START;
1168
1169 m5_data->custom_model[offset] = val;
1170 }
1171
1172 for ( ; index < max && buf[index] != '\n'; index++)
1173 ;
1174 }
1175
1176 return 0;
1177}
1178
Jenny Ho20010d42023-03-16 05:47:15 +08001179static u16 max_m5_recal_dpacc(struct max_m5_data *m5_data)
1180{
1181 if (m5_data->parameters.taskperiod == MAX_M5_TASKPERIOD_351MS)
1182 return 0x0c80;
1183
1184 return m5_data->parameters.dpacc;
1185}
1186
1187static u16 max_m5_recal_dqacc(struct max_m5_data *m5_data, u16 target_cap)
1188{
1189 if (m5_data->parameters.taskperiod == MAX_M5_TASKPERIOD_351MS)
1190 return target_cap >> 4;
1191
1192 return m5_data->parameters.dqacc;
1193}
1194
1195static u16 max_m5_recal_new_cap(struct max_m5_data *m5_data, u16 dqacc, u16 dpacc)
1196{
1197 /*
1198 * dQacc LSb is 16mAh with 10mohm, *2 by 5mohm sense resistot, *2 by double task period
1199 * dPacc LSb is 0.0625% (1/16)
1200 * new capacity is dQacc/dPacc, took LSb in units into consideration:
1201 * dQacc * 16 * 2 * 2 dQacc
1202 * ------------------------ * 100(%) / 2 (for write to fullcapnom/cap) = ------- x 51200
1203 * dPacc * 0.0625 dPacc
1204 */
1205 if (m5_data->parameters.taskperiod == MAX_M5_TASKPERIOD_351MS)
1206 return dqacc * 0xc800 / dpacc;
1207
1208 return m5_data->parameters.designcap;
1209}
1210
1211static int max_m5_end_recal(struct max_m5_data *m5_data, int algo, u16 new_cap)
1212{
1213 struct max17x0x_regmap *regmap = m5_data->regmap;
1214 int ret = 0;
1215
1216 if (algo == RE_CAL_ALGO_0)
1217 return ret;
1218
1219 ret = max_m5_model_read_state(m5_data);
1220 if (ret != 0)
1221 return ret;
1222
1223 ret = max_m5_model_check_state(m5_data);
1224 if (ret != 0)
1225 return ret;
1226
1227 m5_data->parameters.fullcaprep = new_cap;
1228 m5_data->parameters.fullcapnom = new_cap;
1229 ret = max_m5_save_state_data(m5_data);
1230 if (ret != 0)
1231 return ret;
1232
1233 ret = max_m5_load_state_data(m5_data);
1234 if (ret != 0)
1235 return ret;
1236
1237 ret = REGMAP_WRITE(regmap, MAX_M5_COMMAND, MAX_M5_COMMAND_HARDWARE_RESET);
1238
1239 return ret;
1240}
1241
1242static bool max_m5_needs_recal(struct max_m5_data *m5_data, u16 new_cap)
1243{
1244 u16 design_cap = m5_data->parameters.designcap;
1245
1246 if (new_cap > (design_cap * 110 / 100) &&
1247 m5_data->recal.rounds < MAX_M5_RECAL_MAX_ROUNDS)
1248 return true;
1249 return false;
1250}
1251
1252static int max_m5_recal_release(struct max_m5_data *m5_data)
1253{
1254 struct max17x0x_regmap *regmap = m5_data->regmap;
1255 u16 reg_cycle, data, dpacc, dqacc;
1256 int ret = 0, retries = 0;
1257
1258 mutex_lock(&m5_data->recal.lock);
1259 /* use designcap if not set bhi_target_capacity */
1260 if (m5_data->recal.target_cap == 0)
1261 m5_data->recal.target_cap = m5_data->parameters.designcap;
1262
1263 /* save current cycle before reset to 0 */
1264 ret = REGMAP_READ(regmap, MAX_M5_CYCLES, &reg_cycle);
1265 if (ret < 0)
1266 goto error_done;
1267
1268 m5_data->recal.base_cycle_reg = reg_cycle;
1269
1270 /* set 200% dPacc/dQacc */
1271 dpacc = max_m5_recal_dpacc(m5_data);
1272 dqacc = max_m5_recal_dqacc(m5_data, m5_data->recal.target_cap);
1273 do {
1274 retries++;
1275 ret = REGMAP_WRITE(regmap, MAX_M5_DPACC, dpacc);
1276 if (ret == 0)
1277 ret = REGMAP_WRITE(regmap, MAX_M5_DQACC, dqacc);
1278 if (ret < 0) {
1279 msleep(50);
1280 continue;
1281 }
1282
1283 break;
1284 } while (ret < 0 && retries < 3);
1285
1286 if (ret < 0)
1287 goto error_done;
1288
1289 /* set Cycle to 0 */
1290 ret = REGMAP_WRITE(regmap, MAX_M5_CYCLES, 0x0);
1291 if (ret < 0)
1292 goto error_done;
1293
1294 /* Set LearnCfg: FCLrnStage=0x0, FCLrn=0x2 */
1295 ret = REGMAP_READ(regmap, MAX_M5_LEARNCFG, &data);
1296 if (ret < 0)
1297 goto error_done;
1298
1299 data = MAX_M5_LEARNCFG_FCLRNSTAGE_CLR(data);
1300 data = MAX_M5_LEARNCFG_FCLM_CLR(data) | (0x2 << MAX_M5_LEARNCFG_FCLM_SHIFT);
1301 ret = REGMAP_WRITE_VERIFY(regmap, MAX_M5_LEARNCFG, data);
1302 if (ret < 0)
1303 goto error_done;
1304
1305 m5_data->recal.state = RE_CAL_STATE_LEARNING;
1306
1307error_done:
1308 if (ret < 0)
1309 dev_info(m5_data->dev, "unable to set RECAL data, ret=%d\n", ret);
1310 mutex_unlock(&m5_data->recal.lock);
1311 return ret;
1312}
1313
1314/* b/291077564 */
1315static int max_m5_recal_internal(struct max_m5_data *m5_data)
1316{
1317 struct max_m5_custom_parameters *cp = &m5_data->parameters;
1318 struct max17x0x_regmap *regmap = m5_data->regmap;
1319 u16 reg_cycle, learncfg;
1320 int ret = 0;
1321
1322 mutex_lock(&m5_data->recal.lock);
1323 /* save current cycle before reset to 0 */
1324 ret = REGMAP_READ(regmap, MAX_M5_CYCLES, &reg_cycle);
1325 if (ret < 0)
1326 goto error_done;
1327
1328 m5_data->recal.base_cycle_reg = reg_cycle;
1329
1330 /* Clear GMSR */
1331 ret = max_m5_reset_state_data(m5_data);
1332 if (ret < 0)
1333 goto error_done;
1334
1335 /* Set dPacc/dQacc to ther target capacity from 200% */
1336 cp->dpacc = max_m5_recal_dpacc(m5_data);
1337 cp->dqacc = max_m5_recal_dqacc(m5_data, cp->fullcapnom);
1338
1339 /* Set LearnCfg: FCLrnStage=0x0, FCLrn=0x2 */
1340 learncfg = cp->learncfg;
1341 learncfg = MAX_M5_LEARNCFG_FCLRNSTAGE_CLR(learncfg);
1342 learncfg = MAX_M5_LEARNCFG_FCLM_CLR(learncfg) | (0x2 << MAX_M5_LEARNCFG_FCLM_SHIFT);
1343 cp->learncfg = learncfg;
1344
1345 /* reset FG */
1346 ret = REGMAP_WRITE(regmap, MAX_M5_COMMAND, MAX_M5_COMMAND_HARDWARE_RESET);
1347 if (ret < 0)
1348 goto error_done;
1349
1350 m5_data->recal.state = RE_CAL_STATE_FG_RESET;
1351
1352error_done:
1353 mutex_unlock(&m5_data->recal.lock);
1354 return ret;
1355}
1356
1357int max_m5_check_recal_state(struct max_m5_data *m5_data, int algo, u16 eeprom_cycle)
1358{
1359 struct max17x0x_regmap *regmap = m5_data->regmap;
1360 u16 learncfg, status, reg_cycle, dqacc, dpacc, new_cap;
1361 int ret;
1362
1363 if (m5_data->recal.state == RE_CAL_STATE_IDLE)
1364 return 0;
1365
1366 if (m5_data->recal.state == RE_CAL_STATE_FG_RESET) {
1367 ret = REGMAP_READ(regmap, MAX_M5_STATUS, &status);
1368 if (ret < 0)
1369 return ret;
1370 if ((status & MAX_M5_STATUS_POR) == 0)
1371 m5_data->recal.state = RE_CAL_STATE_LEARNING;
1372 }
1373
1374 /* check learncfg for recalibration status */
1375 ret = REGMAP_READ(regmap, MAX_M5_LEARNCFG, &learncfg);
1376 if (ret < 0)
1377 return ret;
1378
1379 /* under learning progress */
1380 if ((learncfg & MAX_M5_LEARNCFG_FCLRNSTAGE) != MAX_M5_LEARNCFG_FCLRNSTAGE)
1381 return 0;
1382
1383 if ((learncfg & MAX_M5_LEARNCFG_RC_VER) != MAX_M5_LEARNCFG_RC2)
1384 return 0;
1385
1386 /* restore real cycle */
1387 reg_cycle = eeprom_cycle << 1;
1388 ret = REGMAP_WRITE(regmap, MAX_M5_CYCLES, reg_cycle);
1389 if (ret < 0)
1390 return ret;
1391
1392 m5_data->recal.base_cycle_reg = 0;
1393
1394 /* Check learning capacity */
1395 ret = REGMAP_READ(regmap, MAX_M5_DQACC, &dqacc);
1396 if (ret < 0)
1397 return ret;
1398
1399 ret = REGMAP_READ(regmap, MAX_M5_DPACC, &dpacc);
1400 if (ret < 0)
1401 return ret;
1402
1403 new_cap = max_m5_recal_new_cap(m5_data, dqacc, dpacc);
1404
1405 if (max_m5_needs_recal(m5_data, new_cap)) {
1406 ret = max_m5_recalibration(m5_data, algo, m5_data->recal.target_cap);
1407 return ret;
1408 }
1409
1410 ret = max_m5_end_recal(m5_data, algo, new_cap);
1411 if (ret < 0)
1412 dev_warn(m5_data->dev, "fail to restore new capacity, ret=%d\n", ret);
1413
1414 m5_data->recal.state = RE_CAL_STATE_IDLE;
1415
1416 return ret;
1417}
1418
1419int max_m5_recalibration(struct max_m5_data *m5_data, int algo, u16 cap)
1420{
1421 int ret = 0;
1422
1423 if (!m5_data)
1424 return -EINVAL;
1425
1426 if (m5_data->recal.rounds >= MAX_M5_RECAL_MAX_ROUNDS)
1427 return ret;
1428
1429 m5_data->recal.target_cap = cap;
1430
1431 /* TODO: add maximum recalibration times */
1432 if (algo == RE_CAL_ALGO_0)
1433 ret = max_m5_recal_release(m5_data);
1434 else if (algo == RE_CAL_ALGO_1)
1435 ret = max_m5_recal_internal(m5_data);
1436
1437 if (ret == 0)
1438 m5_data->recal.rounds++;
1439
1440 return ret;
1441}
1442
1443int max_m5_recal_state(const struct max_m5_data *m5_data)
1444{
1445 return m5_data->recal.state;
1446}
1447
1448int max_m5_recal_cycle(const struct max_m5_data *m5_data)
1449{
1450 return m5_data->recal.base_cycle_reg;
1451}
1452
Ken Tsou8acade12020-07-09 03:17:35 +08001453/* Initial values??? */
Jenny Hoe8b877c2022-01-10 03:27:53 +08001454#define CGAIN_RESET_VAL 0x0400
Jenny Ho20010d42023-03-16 05:47:15 +08001455int m5_init_custom_parameters(struct device *dev, struct max_m5_data *m5_data,
1456 struct device_node *node)
Ken Tsou8acade12020-07-09 03:17:35 +08001457{
Jenny Ho20010d42023-03-16 05:47:15 +08001458 struct max_m5_custom_parameters *cp = &m5_data->parameters;
Ken Tsou8acade12020-07-09 03:17:35 +08001459 const char *propname = "maxim,fg-params";
Jenny Hoe8b877c2022-01-10 03:27:53 +08001460 const int cnt_default = sizeof(*cp) / 2 - 1;
1461 const int cnt_w_cgain = sizeof(*cp) / 2;
Ken Tsou8acade12020-07-09 03:17:35 +08001462 int ret, cnt;
1463
1464 memset(cp, 0, sizeof(*cp));
1465
1466 cnt = of_property_count_elems_of_size(node, propname, sizeof(u16));
1467 if (cnt < 0)
1468 return -ENODATA;
1469
Jenny Hoe8b877c2022-01-10 03:27:53 +08001470 cp->cgain = CGAIN_RESET_VAL;
1471 if (cnt != cnt_default && cnt != cnt_w_cgain) {
AleX Pelosi5077fc52020-09-10 21:44:48 -07001472 dev_err(dev, "fg-params: %s has %d elements, need %ld\n",
Ken Tsou8acade12020-07-09 03:17:35 +08001473 propname, cnt, sizeof(*cp) / 2);
1474 return -ERANGE;
1475 }
1476
1477 ret = of_property_read_u16_array(node, propname, (u16 *)cp, cnt);
1478 if (ret < 0) {
1479 dev_err(dev, "fg-params: failed to read %s %s: %d\n",
1480 node->name, propname, ret);
1481 return -EINVAL;
1482 }
1483
1484 return 0;
1485}
1486
Jenny Ho20010d42023-03-16 05:47:15 +08001487void max_m5_free_data(struct max_m5_data *m5_data)
Ken Tsou8acade12020-07-09 03:17:35 +08001488{
Jenny Ho20010d42023-03-16 05:47:15 +08001489 devm_kfree(m5_data->dev, m5_data);
Ken Tsou8acade12020-07-09 03:17:35 +08001490}
1491
1492void *max_m5_init_data(struct device *dev, struct device_node *node,
1493 struct max17x0x_regmap *regmap)
1494{
1495 const char *propname = "maxim,fg-model";
1496 struct max_m5_data *m5_data;
1497 int cnt, ret;
1498 u16 *model;
AleX Pelosi962aedf2021-01-07 15:39:43 -08001499 u32 temp;
Ken Tsou8acade12020-07-09 03:17:35 +08001500
1501 m5_data = devm_kzalloc(dev, sizeof(*m5_data), GFP_KERNEL);
1502 if (!m5_data) {
1503 dev_err(dev, "fg-model: %s not found\n", propname);
1504 return ERR_PTR(-ENOMEM);
1505 }
1506
1507 model = devm_kmalloc_array(dev, MAX_M5_FG_MODEL_SIZE, sizeof(u16),
1508 GFP_KERNEL);
1509 if (!model) {
1510 dev_err(dev, "fg-model: out of memory\n");
1511 return ERR_PTR(-ENOMEM);
1512 }
1513
1514 cnt = of_property_count_elems_of_size(node, propname, sizeof(u16));
1515 if (cnt != MAX_M5_FG_MODEL_SIZE) {
1516 dev_err(dev, "fg-model: not found, or invalid %d\n", cnt);
1517 } else {
1518 ret = of_property_read_u16_array(node, propname, model, cnt);
1519 if (ret < 0)
1520 dev_err(dev, "fg-model: no data cnt=%d %s %s: %d\n",
1521 cnt, node->name, propname, ret);
1522 else
1523 m5_data->custom_model_size = cnt;
1524 }
1525
AleX Pelosi962aedf2021-01-07 15:39:43 -08001526 ret = of_property_read_u32(node, "maxim,model-version", &temp);
1527 if (ret < 0 || temp > 255)
1528 temp = MAX_M5_INVALID_VERSION;
1529 m5_data->model_version = temp;
1530
Jenny Ho7c691da2021-05-19 06:15:54 +08001531 m5_data->force_reset_model_data =
1532 of_property_read_bool(node, "maxim,force-reset-model-data");
1533
Ken Tsou8acade12020-07-09 03:17:35 +08001534 /*
1535 * Initial values: check max_m5_model_read_state() for the registers
1536 * updated from max1720x_model_work()
1537 */
Jenny Ho20010d42023-03-16 05:47:15 +08001538 ret = m5_init_custom_parameters(dev, m5_data, node);
Ken Tsou8acade12020-07-09 03:17:35 +08001539 if (ret < 0)
1540 dev_err(dev, "fg-params: %s not found\n", propname);
1541
AleX Pelosi962aedf2021-01-07 15:39:43 -08001542 /* b/177099997 TaskPeriod changes LSB for capacity etc. */
1543 ret = max_m5_read_taskperiod(&m5_data->cap_lsb, regmap);
1544 if (ret < 0)
1545 dev_err(dev, "Cannot set TaskPeriod (%d)\n", ret);
1546
Jenny Hof77200e2021-05-31 17:32:55 +08001547 crc8_populate_msb(m5_crc8_table, MAX_M5_CRC8_POLYNOMIAL);
1548
Ken Tsou8acade12020-07-09 03:17:35 +08001549 m5_data->custom_model = model;
1550 m5_data->regmap = regmap;
1551 m5_data->dev = dev;
1552
1553 return m5_data;
1554}
1555
1556static bool max_m5_is_reg(struct device *dev, unsigned int reg)
1557{
1558 switch (reg) {
1559 case 0x00 ... 0x4F:
1560 case 0xB0 ... 0xBF:
1561 case 0xD0: /* IIC */
1562 case 0xDC ... 0xDF:
1563 case 0xFB:
1564 case 0xFF: /* VFSOC */
1565 return true;
1566 case 0x60: /* Model unlock */
1567 case 0x62: /* Unlock Model Access */
1568 case 0x63: /* Unlock Model Access */
1569 case 0x80 ... 0xAF: /* FG Model */
1570 /* TODO: add a check on unlock */
1571 return true;
YiKai Peng2d18d762023-12-21 07:37:14 +00001572 case 0xEB: /* CoTrim */
1573 return true;
Ken Tsou8acade12020-07-09 03:17:35 +08001574 }
1575
1576 return false;
1577}
1578
1579const struct regmap_config max_m5_regmap_cfg = {
1580 .reg_bits = 8,
1581 .val_bits = 16,
1582 .val_format_endian = REGMAP_ENDIAN_NATIVE,
1583 .max_register = MAX_M5_VFSOC,
1584 .readable_reg = max_m5_is_reg,
1585 .volatile_reg = max_m5_is_reg,
1586};
1587const struct max17x0x_reg max_m5[] = {
1588 [MAX17X0X_TAG_avgc] = { ATOM_INIT_REG16(MAX_M5_AVGCURRENT)},
1589 [MAX17X0X_TAG_cnfg] = { ATOM_INIT_REG16(MAX_M5_CONFIG)},
1590 [MAX17X0X_TAG_mmdv] = { ATOM_INIT_REG16(MAX_M5_MAXMINVOLT)},
1591 [MAX17X0X_TAG_vcel] = { ATOM_INIT_REG16(MAX_M5_VCELL)},
1592 [MAX17X0X_TAG_temp] = { ATOM_INIT_REG16(MAX_M5_TEMP)},
1593 [MAX17X0X_TAG_curr] = { ATOM_INIT_REG16(MAX_M5_CURRENT)},
1594 [MAX17X0X_TAG_mcap] = { ATOM_INIT_REG16(MAX_M5_MIXCAP)},
1595 [MAX17X0X_TAG_vfsoc] = { ATOM_INIT_REG16(MAX_M5_VFSOC)},
AleX Pelosi556defd2024-02-17 01:42:00 +00001596
1597 [MAXFG_TAG_tempco] = { ATOM_INIT_REG16(MAX_M5_TEMPCO)},
1598 [MAXFG_TAG_rcomp0] = { ATOM_INIT_REG16(MAX_M5_RCOMP0)},
1599 [MAXFG_TAG_fcnom] = { ATOM_INIT_REG16(MAX_M5_FULLCAPNOM)},
1600 [MAXFG_TAG_fcrep] = { ATOM_INIT_REG16(MAX_M5_FULLCAPREP)},
1601 [MAXFG_TAG_repsoc] = { ATOM_INIT_REG16(MAX_M5_REPSOC)},
1602 [MAXFG_TAG_msoc] = { ATOM_INIT_REG16(MAX_M5_MIXSOC)},
1603 [MAXFG_TAG_learn] = { ATOM_INIT_REG16(MAX_M5_LEARNCFG)},
1604 [MAXFG_TAG_fstat] = { ATOM_INIT_REG16(MAX_M5_FSTAT)},
1605 [MAXFG_TAG_dqacc] = { ATOM_INIT_REG16(MAX_M5_DQACC)},
1606 [MAXFG_TAG_dpacc] = { ATOM_INIT_REG16(MAX_M5_DPACC)},
Jenny Ho46f24092024-03-27 22:29:27 +08001607 [MAXFG_TAG_avgt] = { ATOM_INIT_REG16(MAX_M5_AVGTA)},
1608 [MAXFG_TAG_avgv] = { ATOM_INIT_REG16(MAX_M5_AVGVCELL)},
1609 [MAXFG_TAG_vfocv] = { ATOM_INIT_REG16(MAX_M5_VFOCV)},
Spade Leee30fa6a2024-04-21 17:35:53 +00001610 [MAXFG_TAG_qh] = { ATOM_INIT_REG16(MAX_M5_QH)},
1611 [MAXFG_TAG_vcel] = { ATOM_INIT_REG16(MAX_M5_VCELL)},
Ken Tsou8acade12020-07-09 03:17:35 +08001612};
1613
1614int max_m5_regmap_init(struct max17x0x_regmap *regmap, struct i2c_client *clnt)
1615{
1616 struct regmap *map;
1617
1618 map = devm_regmap_init_i2c(clnt, &max_m5_regmap_cfg);
1619 if (IS_ERR(map))
1620 return IS_ERR_VALUE(map);
1621
1622 regmap->regtags.max = ARRAY_SIZE(max_m5);
1623 regmap->regtags.map = max_m5;
1624 regmap->regmap = map;
1625 return 0;
1626}