Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 1 | /* 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 Ho | f77200e | 2021-05-31 17:32:55 +0800 | [diff] [blame] | 20 | #include <linux/crc8.h> |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 21 | #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 Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 29 | |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 30 | #include "max_m5.h" |
| 31 | |
| 32 | #ifdef CONFIG_DEBUG_FS |
| 33 | #include <linux/debugfs.h> |
| 34 | #endif |
| 35 | |
AleX Pelosi | 962aedf | 2021-01-07 15:39:43 -0800 | [diff] [blame] | 36 | /* 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 Ho | f77200e | 2021-05-31 17:32:55 +0800 | [diff] [blame] | 44 | #define MAX_M5_CRC8_POLYNOMIAL 0x07 /* (x^8) + x^2 + x + 1 */ |
| 45 | DECLARE_CRC8_TABLE(m5_crc8_table); |
| 46 | |
Jenny Ho | 20010d4 | 2023-03-16 05:47:15 +0800 | [diff] [blame] | 47 | int max_m5_recalibration(struct max_m5_data *m5_data, int algo, u16 cap); |
| 48 | |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 49 | static 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 */ |
yihsiangpeng | cfb3ff8 | 2021-02-04 14:48:54 +0800 | [diff] [blame] | 65 | int max_m5_read_actual_input_current_ua(struct i2c_client *client, int *iic_raw) |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 66 | { |
| 67 | struct max_m5_data *m5_data = max1720x_get_model_data(client); |
AleX Pelosi | e8b80cd | 2020-12-17 21:22:11 -0800 | [diff] [blame] | 68 | unsigned long sum = 0; |
| 69 | const int loops = 4; |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 70 | unsigned int tmp; |
AleX Pelosi | e8b80cd | 2020-12-17 21:22:11 -0800 | [diff] [blame] | 71 | int i, rtn; |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 72 | |
| 73 | if (!m5_data || !m5_data->regmap) |
| 74 | return -ENODEV; |
| 75 | |
AleX Pelosi | e8b80cd | 2020-12-17 21:22:11 -0800 | [diff] [blame] | 76 | 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 Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 82 | |
AleX Pelosi | e8b80cd | 2020-12-17 21:22:11 -0800 | [diff] [blame] | 83 | sum += tmp; |
| 84 | } |
| 85 | |
yihsiangpeng | cfb3ff8 | 2021-02-04 14:48:54 +0800 | [diff] [blame] | 86 | *iic_raw = sum / loops; |
AleX Pelosi | e8b80cd | 2020-12-17 21:22:11 -0800 | [diff] [blame] | 87 | return 0; |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 88 | } |
| 89 | EXPORT_SYMBOL_GPL(max_m5_read_actual_input_current_ua); |
| 90 | |
Jack Wu | 83f98e3 | 2021-08-09 18:15:52 +0800 | [diff] [blame] | 91 | int 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 | } |
| 110 | EXPORT_SYMBOL_GPL(max_m5_read_vbypass); |
| 111 | |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 112 | int 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 | } |
| 122 | EXPORT_SYMBOL_GPL(max_m5_reg_read); |
| 123 | |
| 124 | int 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 | } |
| 134 | EXPORT_SYMBOL_GPL(max_m5_reg_write); |
| 135 | |
| 136 | |
| 137 | static 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 | |
| 144 | static 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 Ho | 1ccf7e9 | 2023-12-20 17:05:59 +0800 | [diff] [blame] | 151 | int max_m5_model_lock(struct regmap *regmap, bool enabled) |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 152 | { |
| 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 | |
| 164 | static 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 */ |
| 175 | static 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 */ |
| 261 | static 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 Ho | a1fbc9b | 2022-05-27 07:33:54 +0000 | [diff] [blame] | 268 | ret = REGMAP_WRITE_VERIFY(regmap, MAX_M5_REPCAP, 0x0); |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 269 | 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 Ho | 64506a9 | 2022-07-08 06:51:00 +0000 | [diff] [blame] | 290 | ret = REGMAP_WRITE_VERIFY(regmap, MAX_M5_LEARNCFG, cp->learncfg); |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 291 | 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 Pelosi | d03a256 | 2020-11-16 15:35:19 -0800 | [diff] [blame] | 325 | ret = REGMAP_WRITE_VERIFY(regmap, MAX_M5_RCOMP0, cp->rcomp0); |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 326 | if (ret == 0) |
AleX Pelosi | d03a256 | 2020-11-16 15:35:19 -0800 | [diff] [blame] | 327 | ret = REGMAP_WRITE_VERIFY(regmap, MAX_M5_TEMPCO, cp->tempco); |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 328 | if (ret == 0) |
Jenny Ho | 628931e | 2021-09-29 12:16:40 +0800 | [diff] [blame] | 329 | ret = REGMAP_WRITE(regmap, MAX_M5_TASKPERIOD, cp->taskperiod); |
| 330 | if (ret == 0) |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 331 | 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 | |
| 358 | exit_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 Pelosi | 962aedf | 2021-01-07 15:39:43 -0800 | [diff] [blame] | 369 | int 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 Lee | 7812b9d | 2024-03-29 08:22:59 +0000 | [diff] [blame] | 381 | int max_m5_model_write_version(const struct max_m5_data *m5_data, int version) |
AleX Pelosi | 962aedf | 2021-01-07 15:39:43 -0800 | [diff] [blame] | 382 | { |
| 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 Ho | c6bc588 | 2021-02-18 09:21:21 +0800 | [diff] [blame] | 400 | static 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 | |
| 412 | int 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 Lee | 4c0fb51 | 2023-11-13 04:04:27 +0000 | [diff] [blame] | 423 | return ret == sizeof(data) ? 0 : ret; |
Jenny Ho | c6bc588 | 2021-02-18 09:21:21 +0800 | [diff] [blame] | 424 | } |
| 425 | |
| 426 | int max_m5_needs_reset_model_data(const struct max_m5_data *m5_data) |
| 427 | { |
| 428 | int read_rc, para_rc; |
| 429 | |
Jenny Ho | 7c691da | 2021-05-19 06:15:54 +0800 | [diff] [blame] | 430 | if (m5_data->force_reset_model_data) |
| 431 | return 1; |
| 432 | |
Jenny Ho | c6bc588 | 2021-02-18 09:21:21 +0800 | [diff] [blame] | 433 | 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 Pelosi | 962aedf | 2021-01-07 15:39:43 -0800 | [diff] [blame] | 446 | /* convert taskperiod to the scaling factor for capacity */ |
| 447 | static 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 Ho | 31bdf26 | 2023-07-27 07:52:04 +0800 | [diff] [blame] | 459 | static int max_m5_update_gauge_custom_parameters(struct max_m5_data *m5_data) |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 460 | { |
| 461 | struct max17x0x_regmap *regmap = m5_data->regmap; |
| 462 | int ret, retries; |
| 463 | u16 data; |
| 464 | |
AleX Pelosi | 00eec96 | 2021-01-13 19:32:33 -0800 | [diff] [blame] | 465 | /* write parameters (which include state) */ |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 466 | 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 Pelosi | 962aedf | 2021-01-07 15:39:43 -0800 | [diff] [blame] | 473 | /* tcurve, filterconfig, taskperiod, version are not part of model */ |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 474 | ret = REGMAP_WRITE(regmap, MAX_M5_TCURVE, m5_data->parameters.tcurve); |
| 475 | if (ret < 0) { |
AleX Pelosi | 962aedf | 2021-01-07 15:39:43 -0800 | [diff] [blame] | 476 | dev_err(m5_data->dev, "cannot update tcurve (%d)\n", ret); |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 477 | return ret; |
| 478 | } |
| 479 | |
| 480 | ret = REGMAP_WRITE(regmap, MAX_M5_FILTERCFG, |
| 481 | m5_data->parameters.filtercfg); |
| 482 | if (ret < 0) { |
AleX Pelosi | 962aedf | 2021-01-07 15:39:43 -0800 | [diff] [blame] | 483 | dev_err(m5_data->dev, "cannot update filter config (%d)\n", ret); |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 484 | return ret; |
| 485 | } |
| 486 | |
Jenny Ho | e8b877c | 2022-01-10 03:27:53 +0800 | [diff] [blame] | 487 | 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 Pelosi | 962aedf | 2021-01-07 15:39:43 -0800 | [diff] [blame] | 491 | |
| 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 Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 505 | ret = REGMAP_READ(regmap, MAX_M5_CONFIG2, &data); |
| 506 | if (ret == 0) |
AleX Pelosi | 962aedf | 2021-01-07 15:39:43 -0800 | [diff] [blame] | 507 | ret = REGMAP_WRITE(regmap, MAX_M5_CONFIG2, |
| 508 | data | MAX_M5_CONFIG2_LDMDL); |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 509 | if (ret < 0) { |
| 510 | dev_err(m5_data->dev, "failed start model loading (%d)\n", ret); |
| 511 | return ret; |
| 512 | } |
| 513 | |
AleX Pelosi | 962aedf | 2021-01-07 15:39:43 -0800 | [diff] [blame] | 514 | /* around 400ms for this usually */ |
| 515 | for (retries = 20; retries > 0; retries--) { |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 516 | |
| 517 | mdelay(50); |
| 518 | |
| 519 | ret = REGMAP_READ(regmap, MAX_M5_CONFIG2, &data); |
AleX Pelosi | 962aedf | 2021-01-07 15:39:43 -0800 | [diff] [blame] | 520 | if (ret == 0 && !(data & MAX_M5_CONFIG2_LDMDL)) { |
Jenny Ho | 61d4f78 | 2023-09-20 13:09:29 +0800 | [diff] [blame] | 521 | ret = REGMAP_READ(regmap, MAX_M5_REPCAP, &data); |
| 522 | if (ret == 0 && data != 0) { |
| 523 | int temp; |
AleX Pelosi | 962aedf | 2021-01-07 15:39:43 -0800 | [diff] [blame] | 524 | |
Jenny Ho | 61d4f78 | 2023-09-20 13:09:29 +0800 | [diff] [blame] | 525 | 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 Pelosi | 962aedf | 2021-01-07 15:39:43 -0800 | [diff] [blame] | 536 | } |
AleX Pelosi | 962aedf | 2021-01-07 15:39:43 -0800 | [diff] [blame] | 537 | } |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 538 | } |
| 539 | |
| 540 | return -ETIMEDOUT; |
| 541 | } |
| 542 | |
Jenny Ho | 31bdf26 | 2023-07-27 07:52:04 +0800 | [diff] [blame] | 543 | /* protected from mutex_lock(&chip->model_lock) */ |
| 544 | static 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 */ |
| 576 | int 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 Pelosi | fed7fb1 | 2020-11-10 01:22:22 -0800 | [diff] [blame] | 629 | /* algo version is ignored here, check code in max1720x_outliers */ |
| 630 | int 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 Pelosi | fc6f66d | 2021-01-12 18:14:48 -0800 | [diff] [blame] | 643 | static 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 Pelosi | 00eec96 | 2021-01-13 19:32:33 -0800 | [diff] [blame] | 649 | same = ((char *)buf)[i] == c; |
AleX Pelosi | fc6f66d | 2021-01-12 18:14:48 -0800 | [diff] [blame] | 650 | |
| 651 | return same; |
| 652 | } |
| 653 | |
Jenny Ho | 1cf4358 | 2023-03-24 12:34:23 +0800 | [diff] [blame] | 654 | /* TODO: make it adjustable, set 10% tolerance here */ |
| 655 | #define MAX_M5_CAP_MAX_RATIO 110 |
| 656 | static int max_m5_check_state_data(struct model_state_save *state, |
| 657 | struct max_m5_custom_parameters *ini) |
AleX Pelosi | fc6f66d | 2021-01-12 18:14:48 -0800 | [diff] [blame] | 658 | { |
| 659 | bool bad_residual, empty; |
Jenny Ho | 1cf4358 | 2023-03-24 12:34:23 +0800 | [diff] [blame] | 660 | int max_cap = ini->designcap * MAX_M5_CAP_MAX_RATIO / 100; |
AleX Pelosi | fc6f66d | 2021-01-12 18:14:48 -0800 | [diff] [blame] | 661 | |
AleX Pelosi | 00eec96 | 2021-01-13 19:32:33 -0800 | [diff] [blame] | 662 | empty = memtst(state, 0xff, sizeof(*state)); |
AleX Pelosi | fc6f66d | 2021-01-12 18:14:48 -0800 | [diff] [blame] | 663 | if (empty) |
AleX Pelosi | 00eec96 | 2021-01-13 19:32:33 -0800 | [diff] [blame] | 664 | return -ENODATA; |
AleX Pelosi | fc6f66d | 2021-01-12 18:14:48 -0800 | [diff] [blame] | 665 | |
| 666 | if (state->rcomp0 == 0xFF) |
AleX Pelosi | 00eec96 | 2021-01-13 19:32:33 -0800 | [diff] [blame] | 667 | return -ERANGE; |
AleX Pelosi | fc6f66d | 2021-01-12 18:14:48 -0800 | [diff] [blame] | 668 | |
Jenny Ho | 7e57810 | 2021-05-24 18:22:55 +0800 | [diff] [blame] | 669 | if (state->tempco == 0xFFFF) |
| 670 | return -ERANGE; |
| 671 | |
AleX Pelosi | fc6f66d | 2021-01-12 18:14:48 -0800 | [diff] [blame] | 672 | bad_residual = state->qresidual00 == 0xffff && |
| 673 | state->qresidual10 == 0xffff && |
| 674 | state->qresidual20 == 0xffff && |
| 675 | state->qresidual30 == 0xffff; |
AleX Pelosi | 00eec96 | 2021-01-13 19:32:33 -0800 | [diff] [blame] | 676 | |
AleX Pelosi | fc6f66d | 2021-01-12 18:14:48 -0800 | [diff] [blame] | 677 | if (bad_residual) |
AleX Pelosi | 00eec96 | 2021-01-13 19:32:33 -0800 | [diff] [blame] | 678 | return -EINVAL; |
AleX Pelosi | fc6f66d | 2021-01-12 18:14:48 -0800 | [diff] [blame] | 679 | |
Jenny Ho | 1cf4358 | 2023-03-24 12:34:23 +0800 | [diff] [blame] | 680 | if (state->fullcaprep > max_cap) |
| 681 | return -ERANGE; |
| 682 | |
| 683 | if (state->fullcapnom > max_cap) |
| 684 | return -ERANGE; |
| 685 | |
AleX Pelosi | fc6f66d | 2021-01-12 18:14:48 -0800 | [diff] [blame] | 686 | return 0; |
| 687 | } |
| 688 | |
Jenny Ho | f77200e | 2021-05-31 17:32:55 +0800 | [diff] [blame] | 689 | static u8 max_m5_crc(u8 *pdata, size_t nbytes, u8 crc) |
| 690 | { |
| 691 | return crc8(m5_crc8_table, pdata, nbytes, crc); |
| 692 | } |
| 693 | |
| 694 | static 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 Ho | dc6c8a0 | 2021-10-29 06:14:33 +0800 | [diff] [blame] | 707 | state->cycles, state->cv_mixcap, |
Jenny Ho | f77200e | 2021-05-31 17:32:55 +0800 | [diff] [blame] | 708 | state->halftime, state->crc, crc); |
| 709 | |
| 710 | return crc; |
| 711 | } |
| 712 | |
AleX Pelosi | fc6f66d | 2021-01-12 18:14:48 -0800 | [diff] [blame] | 713 | /* |
| 714 | * Load parameters and model state from permanent storage. |
| 715 | * Called on boot after POR |
| 716 | */ |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 717 | int max_m5_load_state_data(struct max_m5_data *m5_data) |
| 718 | { |
AleX Pelosi | fc6f66d | 2021-01-12 18:14:48 -0800 | [diff] [blame] | 719 | struct max_m5_custom_parameters *cp = &m5_data->parameters; |
Jenny Ho | f77200e | 2021-05-31 17:32:55 +0800 | [diff] [blame] | 720 | u8 crc; |
AleX Pelosi | fc6f66d | 2021-01-12 18:14:48 -0800 | [diff] [blame] | 721 | int ret; |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 722 | |
| 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 Ho | 1cf4358 | 2023-03-24 12:34:23 +0800 | [diff] [blame] | 734 | ret = max_m5_check_state_data(&m5_data->model_save, cp); |
AleX Pelosi | fc6f66d | 2021-01-12 18:14:48 -0800 | [diff] [blame] | 735 | if (ret < 0) |
| 736 | return ret; |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 737 | |
Jenny Ho | f77200e | 2021-05-31 17:32:55 +0800 | [diff] [blame] | 738 | crc = max_m5_data_crc("restore", &m5_data->model_save); |
| 739 | if (crc != m5_data->model_save.crc) |
| 740 | return -EINVAL; |
| 741 | |
AleX Pelosi | fc6f66d | 2021-01-12 18:14:48 -0800 | [diff] [blame] | 742 | 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 Ho | 5a7e49b | 2023-04-25 21:05:42 +0800 | [diff] [blame] | 750 | /* 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 Pelosi | fc6f66d | 2021-01-12 18:14:48 -0800 | [diff] [blame] | 758 | |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 759 | m5_data->cycles = m5_data->model_save.cycles; |
Jenny Ho | dc6c8a0 | 2021-10-29 06:14:33 +0800 | [diff] [blame] | 760 | m5_data->cv_mixcap = m5_data->model_save.cv_mixcap; |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 761 | m5_data->halftime = m5_data->model_save.halftime; |
| 762 | |
AleX Pelosi | 00eec96 | 2021-01-13 19:32:33 -0800 | [diff] [blame] | 763 | return 0; |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 764 | } |
| 765 | |
| 766 | /* save/commit parameters and model state to permanent storage */ |
| 767 | int max_m5_save_state_data(struct max_m5_data *m5_data) |
| 768 | { |
AleX Pelosi | fc6f66d | 2021-01-12 18:14:48 -0800 | [diff] [blame] | 769 | struct max_m5_custom_parameters *cp = &m5_data->parameters; |
Jenny Ho | f77200e | 2021-05-31 17:32:55 +0800 | [diff] [blame] | 770 | struct model_state_save rb; |
Jenny Ho | ddf88b3 | 2022-03-02 18:18:13 +0800 | [diff] [blame] | 771 | u16 learncfg; |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 772 | int ret = 0; |
| 773 | |
Jenny Ho | ddf88b3 | 2022-03-02 18:18:13 +0800 | [diff] [blame] | 774 | /* 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 Pelosi | fc6f66d | 2021-01-12 18:14:48 -0800 | [diff] [blame] | 782 | 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 Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 791 | m5_data->model_save.cycles = m5_data->cycles; |
Jenny Ho | dc6c8a0 | 2021-10-29 06:14:33 +0800 | [diff] [blame] | 792 | m5_data->model_save.cv_mixcap = m5_data->cv_mixcap; |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 793 | m5_data->model_save.halftime = m5_data->halftime; |
| 794 | |
Jenny Ho | f77200e | 2021-05-31 17:32:55 +0800 | [diff] [blame] | 795 | m5_data->model_save.crc = max_m5_data_crc("save", |
| 796 | &m5_data->model_save); |
| 797 | |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 798 | 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 Ho | f77200e | 2021-05-31 17:32:55 +0800 | [diff] [blame] | 804 | 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 Ho | dc6c8a0 | 2021-10-29 06:14:33 +0800 | [diff] [blame] | 823 | rb.cv_mixcap != m5_data->model_save.cv_mixcap || |
Jenny Ho | f77200e | 2021-05-31 17:32:55 +0800 | [diff] [blame] | 824 | rb.halftime != m5_data->model_save.halftime || |
| 825 | rb.crc != m5_data->model_save.crc) |
| 826 | return -EINVAL; |
| 827 | |
| 828 | return 0; |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 829 | } |
| 830 | |
AleX Pelosi | fc6f66d | 2021-01-12 18:14:48 -0800 | [diff] [blame] | 831 | /* 0 ok, < 0 error. Call after reading from the FG */ |
| 832 | int 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 Pelosi | 00eec96 | 2021-01-13 19:32:33 -0800 | [diff] [blame] | 838 | return -ERANGE; |
AleX Pelosi | fc6f66d | 2021-01-12 18:14:48 -0800 | [diff] [blame] | 839 | |
Jenny Ho | 7e57810 | 2021-05-24 18:22:55 +0800 | [diff] [blame] | 840 | if (fg_param->tempco == 0xFFFF) |
| 841 | return -ERANGE; |
| 842 | |
AleX Pelosi | fc6f66d | 2021-01-12 18:14:48 -0800 | [diff] [blame] | 843 | 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 Pelosi | 00eec96 | 2021-01-13 19:32:33 -0800 | [diff] [blame] | 848 | return -EINVAL; |
AleX Pelosi | fc6f66d | 2021-01-12 18:14:48 -0800 | [diff] [blame] | 849 | |
| 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 Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 857 | int 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 Ho | dc6c8a0 | 2021-10-29 06:14:33 +0800 | [diff] [blame] | 887 | rc = REGMAP_READ(regmap, MAX_M5_CV_MIXCAP, |
| 888 | &m5_data->cv_mixcap); |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 889 | if (rc == 0) |
| 890 | rc = REGMAP_READ(regmap, MAX_M5_CV_HALFTIME, |
| 891 | &m5_data->halftime); |
Jenny Ho | e8b877c | 2022-01-10 03:27:53 +0800 | [diff] [blame] | 892 | if (rc == 0) |
| 893 | rc = REGMAP_READ(regmap, MAX_M5_CGAIN, |
| 894 | &m5_data->parameters.cgain); |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 895 | |
| 896 | return rc; |
| 897 | } |
| 898 | |
Jenny Ho | 3baaaaf | 2023-09-01 18:16:58 +0800 | [diff] [blame] | 899 | int max_m5_get_designcap(const struct max_m5_data *m5_data) |
| 900 | { |
| 901 | return m5_data->parameters.designcap; |
| 902 | } |
| 903 | |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 904 | ssize_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 Ho | dc6c8a0 | 2021-10-29 06:14:33 +0800 | [diff] [blame] | 927 | len += scnprintf(&buf[len], max - len,"%02x:%02x\n", MAX_M5_CV_MIXCAP, |
| 928 | m5_data->cv_mixcap); |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 929 | len += scnprintf(&buf[len], max - len,"%02x:%02x\n", MAX_M5_CV_HALFTIME, |
| 930 | m5_data->halftime); |
| 931 | |
| 932 | return len; |
| 933 | } |
| 934 | |
Jenny Ho | c6bc588 | 2021-02-18 09:21:21 +0800 | [diff] [blame] | 935 | ssize_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 Ho | dc6c8a0 | 2021-10-29 06:14:33 +0800 | [diff] [blame] | 949 | "qresidual30:%04X\ncv_mixcap :%04X\n" |
Jenny Ho | c6bc588 | 2021-02-18 09:21:21 +0800 | [diff] [blame] | 950 | "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 Ho | dc6c8a0 | 2021-10-29 06:14:33 +0800 | [diff] [blame] | 955 | saved_data.qresidual30, saved_data.cv_mixcap, |
Jenny Ho | c6bc588 | 2021-02-18 09:21:21 +0800 | [diff] [blame] | 956 | saved_data.halftime); |
| 957 | |
| 958 | return len; |
| 959 | } |
| 960 | |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 961 | /* can be use to restore parametes and model state after POR */ |
| 962 | int 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", ®, &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 Pelosi | fc6f66d | 2021-01-12 18:14:48 -0800 | [diff] [blame] | 978 | /* model parameters (fg-params) */ |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 979 | 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 Pelosi | 962aedf | 2021-01-07 15:39:43 -0800 | [diff] [blame] | 1030 | 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 Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 1040 | |
AleX Pelosi | fc6f66d | 2021-01-12 18:14:48 -0800 | [diff] [blame] | 1041 | /* model state, saved and restored */ |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 1042 | 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 Ho | dc6c8a0 | 2021-10-29 06:14:33 +0800 | [diff] [blame] | 1069 | case MAX_M5_CV_MIXCAP: |
| 1070 | m5_data->cv_mixcap = val; |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 1071 | break; |
| 1072 | case MAX_M5_CV_HALFTIME: |
| 1073 | m5_data->halftime = val; |
| 1074 | break; |
Jenny Ho | e8b877c | 2022-01-10 03:27:53 +0800 | [diff] [blame] | 1075 | case MAX_M5_CGAIN: |
| 1076 | m5_data->parameters.cgain = val; |
| 1077 | break; |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 1078 | 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 Pelosi | 962aedf | 2021-01-07 15:39:43 -0800 | [diff] [blame] | 1091 | /* b/177099997 TaskPeriod = 351 ms changes the lsb for capacity conversions */ |
| 1092 | static 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 Pelosi | fc6f66d | 2021-01-12 18:14:48 -0800 | [diff] [blame] | 1108 | int 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 Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 1116 | /* custom model parameters */ |
| 1117 | int 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 Ho | 70a4432 | 2023-11-22 00:13:54 +0800 | [diff] [blame] | 1132 | int 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 Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 1143 | /* custom model parameters */ |
| 1144 | int 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", ®, &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 Ho | 20010d4 | 2023-03-16 05:47:15 +0800 | [diff] [blame] | 1179 | static 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 | |
| 1187 | static 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 | |
| 1195 | static 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 | |
| 1211 | static 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 | |
| 1242 | static 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 | |
| 1252 | static 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, ®_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 | |
| 1307 | error_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 */ |
| 1315 | static 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, ®_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 | |
| 1352 | error_done: |
| 1353 | mutex_unlock(&m5_data->recal.lock); |
| 1354 | return ret; |
| 1355 | } |
| 1356 | |
| 1357 | int 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 | |
| 1419 | int 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 | |
| 1443 | int max_m5_recal_state(const struct max_m5_data *m5_data) |
| 1444 | { |
| 1445 | return m5_data->recal.state; |
| 1446 | } |
| 1447 | |
| 1448 | int max_m5_recal_cycle(const struct max_m5_data *m5_data) |
| 1449 | { |
| 1450 | return m5_data->recal.base_cycle_reg; |
| 1451 | } |
| 1452 | |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 1453 | /* Initial values??? */ |
Jenny Ho | e8b877c | 2022-01-10 03:27:53 +0800 | [diff] [blame] | 1454 | #define CGAIN_RESET_VAL 0x0400 |
Jenny Ho | 20010d4 | 2023-03-16 05:47:15 +0800 | [diff] [blame] | 1455 | int m5_init_custom_parameters(struct device *dev, struct max_m5_data *m5_data, |
| 1456 | struct device_node *node) |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 1457 | { |
Jenny Ho | 20010d4 | 2023-03-16 05:47:15 +0800 | [diff] [blame] | 1458 | struct max_m5_custom_parameters *cp = &m5_data->parameters; |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 1459 | const char *propname = "maxim,fg-params"; |
Jenny Ho | e8b877c | 2022-01-10 03:27:53 +0800 | [diff] [blame] | 1460 | const int cnt_default = sizeof(*cp) / 2 - 1; |
| 1461 | const int cnt_w_cgain = sizeof(*cp) / 2; |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 1462 | 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 Ho | e8b877c | 2022-01-10 03:27:53 +0800 | [diff] [blame] | 1470 | cp->cgain = CGAIN_RESET_VAL; |
| 1471 | if (cnt != cnt_default && cnt != cnt_w_cgain) { |
AleX Pelosi | 5077fc5 | 2020-09-10 21:44:48 -0700 | [diff] [blame] | 1472 | dev_err(dev, "fg-params: %s has %d elements, need %ld\n", |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 1473 | 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 Ho | 20010d4 | 2023-03-16 05:47:15 +0800 | [diff] [blame] | 1487 | void max_m5_free_data(struct max_m5_data *m5_data) |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 1488 | { |
Jenny Ho | 20010d4 | 2023-03-16 05:47:15 +0800 | [diff] [blame] | 1489 | devm_kfree(m5_data->dev, m5_data); |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 1490 | } |
| 1491 | |
| 1492 | void *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 Pelosi | 962aedf | 2021-01-07 15:39:43 -0800 | [diff] [blame] | 1499 | u32 temp; |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 1500 | |
| 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 Pelosi | 962aedf | 2021-01-07 15:39:43 -0800 | [diff] [blame] | 1526 | 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 Ho | 7c691da | 2021-05-19 06:15:54 +0800 | [diff] [blame] | 1531 | m5_data->force_reset_model_data = |
| 1532 | of_property_read_bool(node, "maxim,force-reset-model-data"); |
| 1533 | |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 1534 | /* |
| 1535 | * Initial values: check max_m5_model_read_state() for the registers |
| 1536 | * updated from max1720x_model_work() |
| 1537 | */ |
Jenny Ho | 20010d4 | 2023-03-16 05:47:15 +0800 | [diff] [blame] | 1538 | ret = m5_init_custom_parameters(dev, m5_data, node); |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 1539 | if (ret < 0) |
| 1540 | dev_err(dev, "fg-params: %s not found\n", propname); |
| 1541 | |
AleX Pelosi | 962aedf | 2021-01-07 15:39:43 -0800 | [diff] [blame] | 1542 | /* 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 Ho | f77200e | 2021-05-31 17:32:55 +0800 | [diff] [blame] | 1547 | crc8_populate_msb(m5_crc8_table, MAX_M5_CRC8_POLYNOMIAL); |
| 1548 | |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 1549 | m5_data->custom_model = model; |
| 1550 | m5_data->regmap = regmap; |
| 1551 | m5_data->dev = dev; |
| 1552 | |
| 1553 | return m5_data; |
| 1554 | } |
| 1555 | |
| 1556 | static 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 Peng | 2d18d76 | 2023-12-21 07:37:14 +0000 | [diff] [blame] | 1572 | case 0xEB: /* CoTrim */ |
| 1573 | return true; |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 1574 | } |
| 1575 | |
| 1576 | return false; |
| 1577 | } |
| 1578 | |
| 1579 | const 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 | }; |
| 1587 | const 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 Pelosi | 556defd | 2024-02-17 01:42:00 +0000 | [diff] [blame] | 1596 | |
| 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 Ho | 46f2409 | 2024-03-27 22:29:27 +0800 | [diff] [blame] | 1607 | [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 Lee | e30fa6a | 2024-04-21 17:35:53 +0000 | [diff] [blame] | 1610 | [MAXFG_TAG_qh] = { ATOM_INIT_REG16(MAX_M5_QH)}, |
| 1611 | [MAXFG_TAG_vcel] = { ATOM_INIT_REG16(MAX_M5_VCELL)}, |
Ken Tsou | 8acade1 | 2020-07-09 03:17:35 +0800 | [diff] [blame] | 1612 | }; |
| 1613 | |
| 1614 | int 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 | } |