blob: 6fcc43d01dd741b3b155bbbc942702c13327e1a0 [file] [log] [blame]
AleX Pelosi78a4bea2020-09-01 19:02:24 -07001/* SPDX-License-Identifier: GPL-2.0 */
Ken Tsou8acade12020-07-09 03:17:35 +08002/*
AleX Pelosi78a4bea2020-09-01 19:02:24 -07003 * Support For Battery EEPROM
Ken Tsou8acade12020-07-09 03:17:35 +08004 *
AleX Pelosi78a4bea2020-09-01 19:02:24 -07005 * Copyright 2018 Google, LLC
Ken Tsou8acade12020-07-09 03:17:35 +08006 *
Ken Tsou8acade12020-07-09 03:17:35 +08007 */
8
AleX Pelosi78a4bea2020-09-01 19:02:24 -07009#define pr_fmt(fmt) KBUILD_MODNAME ": %s " fmt, __func__
Ken Tsou8acade12020-07-09 03:17:35 +080010
11#include <linux/kernel.h>
AleX Pelosi2e487e52021-12-08 17:52:06 -080012#include <linux/slab.h>
Ken Tsou8acade12020-07-09 03:17:35 +080013#include <linux/pm_runtime.h>
14#include <linux/nvmem-consumer.h>
AleX Pelosi78a4bea2020-09-01 19:02:24 -070015#include <linux/module.h>
Jenny Ho18ee6322021-02-04 13:32:00 +080016#include <linux/delay.h>
AleX Pelosi664e21e2020-09-01 11:29:51 -070017#include "gbms_storage.h"
Ken Tsou8acade12020-07-09 03:17:35 +080018
Ken Tsou8acade12020-07-09 03:17:35 +080019#define BATT_EEPROM_TAG_MINF_OFFSET 0x00
20#define BATT_EEPROM_TAG_MINF_LEN GBMS_MINF_LEN
AleX Pelosi2e487e52021-12-08 17:52:06 -080021#define BATT_EEPROM_TAG_BGPN_OFFSET 0x03
22#define BATT_EEPROM_TAG_BGPN_LEN GBMS_BGPN_LEN
23#define BATT_EEPROM_TAG_BRID_OFFSET 0x17
24#define BATT_EEPROM_TAG_BRID_LEN 1
Jenny Ho3341bbb2022-01-24 14:49:40 +080025#define BATT_EEPROM_TAG_STRD_OFFSET 0x1E
26#define BATT_EEPROM_TAG_STRD_LEN 12
Jenny Ho1da205c2020-11-18 03:58:10 +080027#define BATT_EEPROM_TAG_BCNT_OFFSET 0x2E
Ken Tsou8acade12020-07-09 03:17:35 +080028#define BATT_EEPROM_TAG_BCNT_LEN (GBMS_CCBIN_BUCKET_COUNT * 2)
Jenny Ho1da205c2020-11-18 03:58:10 +080029#define BATT_EEPROM_TAG_GMSR_OFFSET 0x42
Ken Tsou8acade12020-07-09 03:17:35 +080030#define BATT_EEPROM_TAG_GMSR_LEN GBMS_GMSR_LEN
Jenny Hof740ce22021-06-02 10:15:00 +080031#define BATT_EEPROM_TAG_LOTR_OFFSET 0x59 // Layout Track
Jenny Ho1da205c2020-11-18 03:58:10 +080032#define BATT_EEPROM_TAG_LOTR_LEN 1
Jenny Ho1da205c2020-11-18 03:58:10 +080033#define BATT_EEPROM_TAG_CNHS_OFFSET 0x5A
Jenny Ho9e675102020-10-29 10:38:24 +080034#define BATT_EEPROM_TAG_CNHS_LEN 2
Jenny Ho9a559fe2021-07-06 01:40:31 +080035#define BATT_EEPROM_TAG_SELC_OFFSET 0x5C
36#define BATT_EEPROM_TAG_SELC_LEN 1
37#define BATT_EEPROM_TAG_CELC_OFFSET 0x5D
38#define BATT_EEPROM_TAG_CELC_LEN 1
AleX Pelosi2e487e52021-12-08 17:52:06 -080039
Jenny Ho9a559fe2021-07-06 01:40:31 +080040#define BATT_EEPROM_TAG_HIST_OFFSET 0x5E
Jenny Ho1da205c2020-11-18 03:58:10 +080041#define BATT_EEPROM_TAG_HIST_LEN BATT_ONE_HIST_LEN
Ken Tsou8acade12020-07-09 03:17:35 +080042
Jenny Ho3341bbb2022-01-24 14:49:40 +080043#define BATT_EEPROM_TAG_DINF_OFFSET 0x3FA
44#define BATT_EEPROM_TAG_DINF_LEN 4 // GBMS_DINF_LEN
45
AleX Pelosibdb12882021-12-07 09:09:10 -080046static struct gbms_storage_desc *gbee_desc;
47
48#define GBEE_GET_NVRAM(ptr) ((struct nvmem_device *)(ptr))
49#define GBEE_STORAGE_INFO(tag, addr, count, ptr) \
50 (gbee_desc && gbee_desc->info) ? gbee_desc->info(tag, addr, count, ptr) : \
51 gbee_storage_info(tag, addr, count, ptr)
52
Jenny Ho18ee6322021-02-04 13:32:00 +080053/*
54 * I2C error when try to write continuous data.
55 * Add delay before write to wait previous internal write complete
56 * http://b/179235291#comment8
57 */
58#define BATT_WAIT_INTERNAL_WRITE_MS 1
59
AleX Pelosibdb12882021-12-07 09:09:10 -080060int gbee_storage_info(gbms_tag_t tag, size_t *addr, size_t *count, void *ptr)
Ken Tsou8acade12020-07-09 03:17:35 +080061{
62 int ret = 0;
63
64 switch (tag) {
65 case GBMS_TAG_MINF:
66 *addr = BATT_EEPROM_TAG_MINF_OFFSET;
67 *count = BATT_EEPROM_TAG_MINF_LEN;
68 break;
69 case GBMS_TAG_DINF:
70 *addr = BATT_EEPROM_TAG_DINF_OFFSET;
71 *count = BATT_EEPROM_TAG_DINF_LEN;
72 break;
73 case GBMS_TAG_HIST:
74 *addr = BATT_EEPROM_TAG_HIST_OFFSET;
75 *count = BATT_EEPROM_TAG_HIST_LEN;
76 break;
77 case GBMS_TAG_SNUM:
78 case GBMS_TAG_BGPN:
79 *addr = BATT_EEPROM_TAG_BGPN_OFFSET;
80 *count = BATT_EEPROM_TAG_BGPN_LEN;
81 break;
82 case GBMS_TAG_GMSR:
83 *addr = BATT_EEPROM_TAG_GMSR_OFFSET;
84 *count = BATT_EEPROM_TAG_GMSR_LEN;
85 break;
86 case GBMS_TAG_BCNT:
87 *addr = BATT_EEPROM_TAG_BCNT_OFFSET;
88 *count = BATT_EEPROM_TAG_BCNT_LEN;
89 break;
Jenny Ho9e675102020-10-29 10:38:24 +080090 case GBMS_TAG_CNHS:
91 *addr = BATT_EEPROM_TAG_CNHS_OFFSET;
92 *count = BATT_EEPROM_TAG_CNHS_LEN;
93 break;
Jenny Ho1da205c2020-11-18 03:58:10 +080094 case GBMS_TAG_LOTR:
95 *addr = BATT_EEPROM_TAG_LOTR_OFFSET;
96 *count = BATT_EEPROM_TAG_LOTR_LEN;
97 break;
Jenny Ho9a559fe2021-07-06 01:40:31 +080098 case GBMS_TAG_SELC:
99 *addr = BATT_EEPROM_TAG_SELC_OFFSET;
100 *count = BATT_EEPROM_TAG_SELC_LEN;
101 break;
102 case GBMS_TAG_CELC:
103 *addr = BATT_EEPROM_TAG_CELC_OFFSET;
104 *count = BATT_EEPROM_TAG_CELC_LEN;
105 break;
Jenny Ho3341bbb2022-01-24 14:49:40 +0800106 case GBMS_TAG_STRD:
107 *addr = BATT_EEPROM_TAG_STRD_OFFSET;
108 *count = BATT_EEPROM_TAG_STRD_LEN;
109 break;
Ken Tsou8acade12020-07-09 03:17:35 +0800110 default:
111 ret = -ENOENT;
112 break;
113 }
114
115 return ret;
116}
117
118static int gbee_storage_iter(int index, gbms_tag_t *tag, void *ptr)
119{
AleX Pelosibdb12882021-12-07 09:09:10 -0800120 static const gbms_tag_t keys[] = { GBMS_TAG_BGPN, GBMS_TAG_MINF,
121 GBMS_TAG_DINF, GBMS_TAG_HIST,
122 GBMS_TAG_BRID, GBMS_TAG_SNUM,
123 GBMS_TAG_GMSR, GBMS_TAG_BCNT,
124 GBMS_TAG_CNHS, GBMS_TAG_SELC,
Jenny Ho3341bbb2022-01-24 14:49:40 +0800125 GBMS_TAG_CELC, GBMS_TAG_LOTR,
126 GBMS_TAG_STRD };
Ken Tsou8acade12020-07-09 03:17:35 +0800127 const int count = ARRAY_SIZE(keys);
128
129 if (index < 0 || index >= count)
130 return -ENOENT;
131
132 *tag = keys[index];
133 return 0;
134}
135
AleX Pelosibdb12882021-12-07 09:09:10 -0800136static int gbee_storage_read(gbms_tag_t tag, void *buff, size_t size, void *ptr)
Ken Tsou8acade12020-07-09 03:17:35 +0800137{
AleX Pelosibdb12882021-12-07 09:09:10 -0800138 struct nvmem_device *nvmem = GBEE_GET_NVRAM(ptr);
Ken Tsou8acade12020-07-09 03:17:35 +0800139 size_t offset = 0, len = 0;
140 int ret;
141
142 if (tag == GBMS_TAG_BRID) {
143 u8 temp;
144
145 if (size != sizeof(u32))
146 return -ENOMEM;
147
148 ret = nvmem_device_read(nvmem, BATT_EEPROM_TAG_BRID_OFFSET,
149 1, &temp);
Ken Tsou8acade12020-07-09 03:17:35 +0800150 if (ret < 0)
151 return ret;
152
153 ((u32*)buff)[0] = temp;
154 return len;
155 }
156
AleX Pelosibdb12882021-12-07 09:09:10 -0800157 ret = GBEE_STORAGE_INFO(tag, &offset, &len, ptr);
Ken Tsou8acade12020-07-09 03:17:35 +0800158 if (ret < 0)
159 return ret;
160 if (!len)
161 return -ENOENT;
162 if (len > size)
163 return -ENOMEM;
164
165 ret = nvmem_device_read(nvmem, offset, len, buff);
166 if (ret == 0)
167 ret = len;
168
169 return ret;
170}
171
172static int gbee_storage_write(gbms_tag_t tag, const void *buff, size_t size,
173 void *ptr)
174{
175 struct nvmem_device *nvmem = ptr;
176 size_t offset = 0, len = 0;
Jenny Ho18ee6322021-02-04 13:32:00 +0800177 int ret, write_size = 0;
Ken Tsou8acade12020-07-09 03:17:35 +0800178
179 if ((tag != GBMS_TAG_DINF) && (tag != GBMS_TAG_GMSR) &&
Jenny Ho9a559fe2021-07-06 01:40:31 +0800180 (tag != GBMS_TAG_BCNT) && (tag != GBMS_TAG_CNHS) &&
Jack Wu5ac39922021-10-29 17:53:24 +0800181 (tag != GBMS_TAG_SELC) && (tag != GBMS_TAG_CELC) &&
Jenny Ho3341bbb2022-01-24 14:49:40 +0800182 (tag != GBMS_TAG_BPST) && (tag != GBMS_TAG_STRD))
Ken Tsou8acade12020-07-09 03:17:35 +0800183 return -ENOENT;
184
AleX Pelosibdb12882021-12-07 09:09:10 -0800185 ret = GBEE_STORAGE_INFO(tag, &offset, &len, ptr);
Ken Tsou8acade12020-07-09 03:17:35 +0800186 if (ret < 0)
187 return ret;
188 if (size > len)
189 return -ENOMEM;
190
Jenny Ho18ee6322021-02-04 13:32:00 +0800191 for (write_size = 0; write_size < size; write_size++) {
192 ret = nvmem_device_write(nvmem, write_size + offset, 1,
193 &((char *)buff)[write_size]);
194 if (ret < 0)
195 return ret;
196 msleep(BATT_WAIT_INTERNAL_WRITE_MS);
197 }
198
199 ret = size;
Ken Tsou8acade12020-07-09 03:17:35 +0800200
201 return ret;
202}
203
204static int gbee_storage_read_data(gbms_tag_t tag, void *data, size_t count,
205 int idx, void *ptr)
206{
AleX Pelosibdb12882021-12-07 09:09:10 -0800207 struct nvmem_device *nvmem = GBEE_GET_NVRAM(ptr);
Ken Tsou8acade12020-07-09 03:17:35 +0800208 size_t offset = 0, len = 0;
209 int ret;
210
211 switch (tag) {
212 case GBMS_TAG_HIST:
AleX Pelosibdb12882021-12-07 09:09:10 -0800213 ret = GBEE_STORAGE_INFO(tag, &offset, &len, ptr);
Ken Tsou8acade12020-07-09 03:17:35 +0800214 break;
215 default:
216 ret = -ENOENT;
217 break;
218 }
219
220 if (ret < 0)
221 return ret;
222
223 if (!data || !count) {
224 if (idx == GBMS_STORAGE_INDEX_INVALID)
225 return 0;
226 else
227 return BATT_MAX_HIST_CNT;
228 }
229
230 if (idx < 0)
231 return -EINVAL;
232
233 /* index == 0 is ok here */
234 if (idx >= BATT_MAX_HIST_CNT)
235 return -ENODATA;
236
237 if (len > count)
238 return -EINVAL;
239
240 offset += len * idx;
241
242 ret = nvmem_device_read(nvmem, offset, len, data);
243 if (ret == 0)
244 ret = len;
245
246 return ret;
247}
248
249static int gbee_storage_write_data(gbms_tag_t tag, const void *data,
250 size_t count, int idx, void *ptr)
251{
AleX Pelosibdb12882021-12-07 09:09:10 -0800252 struct nvmem_device *nvmem = GBEE_GET_NVRAM(ptr);
Ken Tsou8acade12020-07-09 03:17:35 +0800253 size_t offset = 0, len = 0;
Jenny Ho18ee6322021-02-04 13:32:00 +0800254 int ret, write_size = 0;
Ken Tsou8acade12020-07-09 03:17:35 +0800255
256 switch (tag) {
257 case GBMS_TAG_HIST:
AleX Pelosibdb12882021-12-07 09:09:10 -0800258 ret = GBEE_STORAGE_INFO(tag, &offset, &len, ptr);
Ken Tsou8acade12020-07-09 03:17:35 +0800259 break;
260 default:
261 ret = -ENOENT;
262 break;
263 }
264
265 if (ret < 0)
266 return ret;
267
268 if (idx < 0 || !data || !count)
269 return -EINVAL;
270
271 /* index == 0 is ok here */
272 if (idx >= BATT_MAX_HIST_CNT)
273 return -ENODATA;
274
275 if (count > len)
276 return -EINVAL;
277
278 offset += len * idx;
279
Jenny Ho18ee6322021-02-04 13:32:00 +0800280 for (write_size = 0; write_size < len; write_size++) {
281 ret = nvmem_device_write(nvmem, write_size + offset, 1,
282 &((char *)data)[write_size]);
283 if (ret < 0)
284 return ret;
285 msleep(BATT_WAIT_INTERNAL_WRITE_MS);
286 }
287
288 ret = len;
Ken Tsou8acade12020-07-09 03:17:35 +0800289
290 return ret;
291}
292
Ken Tsou8acade12020-07-09 03:17:35 +0800293static struct gbms_storage_desc gbee_storage_dsc = {
294 .info = gbee_storage_info,
295 .iter = gbee_storage_iter,
296 .read = gbee_storage_read,
297 .write = gbee_storage_write,
298 .read_data = gbee_storage_read_data,
299 .write_data = gbee_storage_write_data,
300};
301
AleX Pelosibdb12882021-12-07 09:09:10 -0800302struct gbms_storage_desc gbee_storage01_dsc = {
303 .info = gbee_storage01_info,
304 .iter = gbee_storage01_iter,
305 .read = gbee_storage_read,
306 .write = gbee_storage_write,
307 .read_data = gbee_storage_read_data,
308 .write_data = gbee_storage_write_data,
309};
310
AleX Pelosi2e487e52021-12-08 17:52:06 -0800311/* TODO: factor history mechanics out of google battery? */
312static int gbms_hist_move(struct nvmem_device *nvmem, int from, int to, int len)
AleX Pelosibdb12882021-12-07 09:09:10 -0800313{
AleX Pelosi2e487e52021-12-08 17:52:06 -0800314 u8 *buff, *p;
315 int index, ret;
316
317 buff = kzalloc(len, GFP_KERNEL);
318 if (!buff)
319 return -ENOMEM;
320
321 /* move only the entries that are used */
322 p = buff;
323 for (index = 0; index < BATT_MAX_HIST_CNT; index++) {
324 ret = nvmem_device_read(nvmem, from, BATT_ONE_HIST_LEN, p);
325 if (ret < 0) {
326 pr_err("%s: cannot read history data (%d)\n", __func__, ret);
327 goto exit;
328 }
329
330 /* verify 1st byte for tempco */
331 if (*p == 0xff)
332 break;
333 /* move to next history entry */
334 from += BATT_ONE_HIST_LEN;
335 p += BATT_ONE_HIST_LEN;
336 }
337
338 ret = nvmem_device_write(nvmem, to, (BATT_ONE_HIST_LEN * index), buff);
339 if (ret < 0)
340 pr_err("%s: cannot write history data (%d)\n", __func__, ret);
341
342exit:
343 kfree(buff);
344 return ret;
345}
346
347/* LOTR is in a fixed position, move */
348static int gbms_lotr_update(struct nvmem_device *nvmem, int lotr_to)
349{
350 int ret, lotr_from = 0;
351 static u8 init_data[5]= { 0 };
AleX Pelosibdb12882021-12-07 09:09:10 -0800352
353 ret = nvmem_device_read(nvmem, BATT_EEPROM_TAG_LOTR_OFFSET,
AleX Pelosi2e487e52021-12-08 17:52:06 -0800354 BATT_EEPROM_TAG_LOTR_LEN, &lotr_from);
355 if (ret < 0 || lotr_from == lotr_to)
AleX Pelosibdb12882021-12-07 09:09:10 -0800356 return ret;
357
AleX Pelosi2e487e52021-12-08 17:52:06 -0800358 if (lotr_to != GBMS_LOTR_V1 || lotr_from != GBMS_LOTR_DEFAULT)
359 return 0;
360
361 ret = gbms_hist_move(nvmem, 0x5E, 0x64, BATT_TOTAL_HIST_LEN);
362 if (ret < 0) {
363 pr_err("%s: cannot move history\n", __func__);
364 return ret;
365 /* TODO: flag this in BPST? */
366 }
367
368 ret = nvmem_device_write(nvmem, 0x5E, sizeof(init_data), init_data);
369 if (ret != sizeof(init_data)) {
370 pr_err("%s: cannot init new fields\n", __func__);
371 return ret < 0 ? ret : -EINVAL;
372 }
373
374 /* TODO: how do we handle backporting? */
AleX Pelosibdb12882021-12-07 09:09:10 -0800375
376 /* now write lotr to the right place */
AleX Pelosi2e487e52021-12-08 17:52:06 -0800377 ret = nvmem_device_write(nvmem, BATT_EEPROM_TAG_LOTR_OFFSET,
378 BATT_EEPROM_TAG_LOTR_LEN, &lotr_to);
379 if (ret == BATT_EEPROM_TAG_LOTR_LEN)
380 pr_info("%s: lotr migrated %d->%d\n", __func__, lotr_from, lotr_to);
381
382 return ret;
AleX Pelosibdb12882021-12-07 09:09:10 -0800383}
384
385static struct gbms_storage_desc *gbms_lotr_2_dsc(int lotr_ver)
386{
387 switch (lotr_ver) {
388 case GBMS_LOTR_V1:
389 return &gbee_storage01_dsc;
390 default:
391 return &gbee_storage_dsc;
392 }
393}
394
Ken Tsou8acade12020-07-09 03:17:35 +0800395/*
396 * Caller will use something like of_nvmem_device_get() to retrieve the
397 * nvmem_device instance.
AleX Pelosibdb12882021-12-07 09:09:10 -0800398 * TODO: this only supports a singleton but the model can be extended to
399 * multiple eeproms passing a structure to gbms_storage_register() and
400 * modifying the implementation of GBEE_GET_NVRAM and GBEE_STORAGE_INFO
Ken Tsou8acade12020-07-09 03:17:35 +0800401 * TODO: map nvram cells to tags
402 */
AleX Pelosibdb12882021-12-07 09:09:10 -0800403int gbee_register_device(const char *name, int lotr, struct nvmem_device *nvram)
Ken Tsou8acade12020-07-09 03:17:35 +0800404{
AleX Pelosibdb12882021-12-07 09:09:10 -0800405 int ret;
406
407 gbee_desc = gbms_lotr_2_dsc(lotr);
408 if (!gbee_desc)
409 return -EINVAL;
410
411 /* convert the layout (if needed) */
412 ret = gbms_lotr_update(nvram, lotr);
413 if (ret < 0) {
414 pr_err("gbee %s update lotr failed, %d\n", name, ret);
415 goto error_exit;
416 }
417
418 /* watch out for races on gbee_desc */
419 ret = gbms_storage_register(gbee_desc, name, nvram);
420 if (ret == 0)
421 return 0;
422
423error_exit:
424 gbee_desc = NULL;
425 return ret;
Ken Tsou8acade12020-07-09 03:17:35 +0800426}
AleX Pelosi78a4bea2020-09-01 19:02:24 -0700427EXPORT_SYMBOL_GPL(gbee_register_device);
Ken Tsou8acade12020-07-09 03:17:35 +0800428
429void gbee_destroy_device(void)
430{
431
432}
AleX Pelosi78a4bea2020-09-01 19:02:24 -0700433EXPORT_SYMBOL_GPL(gbee_destroy_device);
434
435MODULE_AUTHOR("AleX Pelosi <apelosi@google.com>");
AleX Pelosibdb12882021-12-07 09:09:10 -0800436MODULE_DESCRIPTION("Google EEPROM");
AleX Pelosi78a4bea2020-09-01 19:02:24 -0700437MODULE_LICENSE("GPL");