7e1a5146f190caed710f77222967bf66256d3d40
[platform/kernel/u-boot.git] / cmd / eeprom.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2000, 2001
4  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
5  */
6
7 /*
8  * Support for read and write access to EEPROM like memory devices. This
9  * includes regular EEPROM as well as  FRAM (ferroelectic nonvolaile RAM).
10  * FRAM devices read and write data at bus speed. In particular, there is no
11  * write delay. Also, there is no limit imposed on the number of bytes that can
12  * be transferred with a single read or write.
13  *
14  * Use the following configuration options to ensure no unneeded performance
15  * degradation (typical for EEPROM) is incured for FRAM memory:
16  *
17  * #define CONFIG_SYS_I2C_FRAM
18  * Set CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS to 0
19  *
20  */
21
22 #include <common.h>
23 #include <config.h>
24 #include <command.h>
25 #include <eeprom.h>
26 #include <i2c.h>
27 #include <eeprom_layout.h>
28 #include <linux/delay.h>
29
30 #ifndef CONFIG_SYS_I2C_SPEED
31 #define CONFIG_SYS_I2C_SPEED    50000
32 #endif
33
34 #ifndef I2C_RXTX_LEN
35 #define I2C_RXTX_LEN    128
36 #endif
37
38 #define EEPROM_PAGE_SIZE        (1 << CONFIG_SYS_EEPROM_PAGE_WRITE_BITS)
39 #define EEPROM_PAGE_OFFSET(x)   ((x) & (EEPROM_PAGE_SIZE - 1))
40
41 #if CONFIG_IS_ENABLED(DM_I2C)
42 static int eeprom_i2c_bus;
43 #endif
44
45 __weak int eeprom_write_enable(unsigned dev_addr, int state)
46 {
47         return 0;
48 }
49
50 void eeprom_init(int bus)
51 {
52         /* I2C EEPROM */
53 #if CONFIG_IS_ENABLED(DM_I2C)
54         eeprom_i2c_bus = bus;
55 #elif defined(CONFIG_SYS_I2C_LEGACY)
56         if (bus >= 0)
57                 i2c_set_bus_num(bus);
58         i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
59 #endif
60 }
61
62 /*
63  * for CONFIG_SYS_I2C_EEPROM_ADDR_LEN == 2 (16-bit EEPROM address) offset is
64  *   0x000nxxxx for EEPROM address selectors at n, offset xxxx in EEPROM.
65  *
66  * for CONFIG_SYS_I2C_EEPROM_ADDR_LEN == 1 (8-bit EEPROM page address) offset is
67  *   0x00000nxx for EEPROM address selectors and page number at n.
68  */
69 static int eeprom_addr(unsigned dev_addr, unsigned offset, uchar *addr)
70 {
71         unsigned blk_off;
72         int alen;
73
74         blk_off = offset & 0xff;        /* block offset */
75 #if CONFIG_SYS_I2C_EEPROM_ADDR_LEN == 1
76         addr[0] = offset >> 8;          /* block number */
77         addr[1] = blk_off;              /* block offset */
78         alen = 2;
79 #else
80         addr[0] = offset >> 16;         /* block number */
81         addr[1] = offset >>  8;         /* upper address octet */
82         addr[2] = blk_off;              /* lower address octet */
83         alen = 3;
84 #endif  /* CONFIG_SYS_I2C_EEPROM_ADDR_LEN */
85
86         addr[0] |= dev_addr;            /* insert device address */
87
88         return alen;
89 }
90
91 static int eeprom_len(unsigned offset, unsigned end)
92 {
93         unsigned len = end - offset;
94
95         /*
96          * For a FRAM device there is no limit on the number of the
97          * bytes that can be accessed with the single read or write
98          * operation.
99          */
100 #if !defined(CONFIG_SYS_I2C_FRAM)
101         unsigned blk_off = offset & 0xff;
102         unsigned maxlen = EEPROM_PAGE_SIZE - EEPROM_PAGE_OFFSET(blk_off);
103
104         if (maxlen > I2C_RXTX_LEN)
105                 maxlen = I2C_RXTX_LEN;
106
107         if (len > maxlen)
108                 len = maxlen;
109 #endif
110
111         return len;
112 }
113
114 static int eeprom_rw_block(unsigned offset, uchar *addr, unsigned alen,
115                            uchar *buffer, unsigned len, bool read)
116 {
117         int ret = 0;
118
119 #if CONFIG_IS_ENABLED(DM_I2C)
120         struct udevice *dev;
121
122         ret = i2c_get_chip_for_busnum(eeprom_i2c_bus, addr[0],
123                                       alen - 1, &dev);
124         if (ret) {
125                 printf("%s: Cannot find udev for a bus %d\n", __func__,
126                        eeprom_i2c_bus);
127                 return CMD_RET_FAILURE;
128         }
129
130         if (read)
131                 ret = dm_i2c_read(dev, offset, buffer, len);
132         else
133                 ret = dm_i2c_write(dev, offset, buffer, len);
134
135 #else /* Non DM I2C support - will be removed */
136
137         if (read)
138                 ret = i2c_read(addr[0], offset, alen - 1, buffer, len);
139         else
140                 ret = i2c_write(addr[0], offset, alen - 1, buffer, len);
141 #endif /* CONFIG_DM_I2C */
142         if (ret)
143                 ret = CMD_RET_FAILURE;
144
145         return ret;
146 }
147
148 static int eeprom_rw(unsigned dev_addr, unsigned offset, uchar *buffer,
149                      unsigned cnt, bool read)
150 {
151         unsigned end = offset + cnt;
152         unsigned alen, len;
153         int rcode = 0;
154         uchar addr[3];
155
156 #if defined(CONFIG_SYS_I2C_EEPROM_BUS)
157         eeprom_init(CONFIG_SYS_I2C_EEPROM_BUS);
158 #endif
159
160         while (offset < end) {
161                 alen = eeprom_addr(dev_addr, offset, addr);
162
163                 len = eeprom_len(offset, end);
164
165                 rcode = eeprom_rw_block(offset, addr, alen, buffer, len, read);
166
167                 buffer += len;
168                 offset += len;
169
170 #if CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS > 0
171                 if (!read)
172                         udelay(CONFIG_SYS_EEPROM_PAGE_WRITE_DELAY_MS * 1000);
173 #endif
174         }
175
176         return rcode;
177 }
178
179 int eeprom_read(unsigned dev_addr, unsigned offset, uchar *buffer, unsigned cnt)
180 {
181         /*
182          * Read data until done or would cross a page boundary.
183          * We must write the address again when changing pages
184          * because the next page may be in a different device.
185          */
186         return eeprom_rw(dev_addr, offset, buffer, cnt, 1);
187 }
188
189 int eeprom_write(unsigned dev_addr, unsigned offset,
190                  uchar *buffer, unsigned cnt)
191 {
192         int ret;
193
194         eeprom_write_enable(dev_addr, 1);
195
196         /*
197          * Write data until done or would cross a write page boundary.
198          * We must write the address again when changing pages
199          * because the address counter only increments within a page.
200          */
201         ret = eeprom_rw(dev_addr, offset, buffer, cnt, 0);
202
203         eeprom_write_enable(dev_addr, 0);
204         return ret;
205 }
206
207 static int parse_numeric_param(char *str)
208 {
209         char *endptr;
210         int value = simple_strtol(str, &endptr, 16);
211
212         return (*endptr != '\0') ? -1 : value;
213 }
214
215 /**
216  * parse_i2c_bus_addr - parse the i2c bus and i2c devaddr parameters
217  *
218  * @i2c_bus:    address to store the i2c bus
219  * @i2c_addr:   address to store the device i2c address
220  * @argc:       count of command line arguments left to parse
221  * @argv:       command line arguments left to parse
222  * @argc_no_bus_addr:   argc value we expect to see when bus & addr aren't given
223  *
224  * @returns:    number of arguments parsed or CMD_RET_USAGE if error
225  */
226 static int parse_i2c_bus_addr(int *i2c_bus, ulong *i2c_addr, int argc,
227                               char *const argv[], int argc_no_bus_addr)
228 {
229         int argc_no_bus = argc_no_bus_addr + 1;
230         int argc_bus_addr = argc_no_bus_addr + 2;
231
232 #ifdef CONFIG_SYS_I2C_EEPROM_ADDR
233         if (argc == argc_no_bus_addr) {
234                 *i2c_bus = -1;
235                 *i2c_addr = CONFIG_SYS_I2C_EEPROM_ADDR;
236
237                 return 0;
238         }
239 #endif
240         if (argc == argc_no_bus) {
241                 *i2c_bus = -1;
242                 *i2c_addr = parse_numeric_param(argv[0]);
243
244                 return 1;
245         }
246
247         if (argc == argc_bus_addr) {
248                 *i2c_bus = parse_numeric_param(argv[0]);
249                 *i2c_addr = parse_numeric_param(argv[1]);
250
251                 return 2;
252         }
253
254         return CMD_RET_USAGE;
255 }
256
257 #ifdef CONFIG_CMD_EEPROM_LAYOUT
258
259 __weak int eeprom_parse_layout_version(char *str)
260 {
261         return LAYOUT_VERSION_UNRECOGNIZED;
262 }
263
264 static unsigned char eeprom_buf[CONFIG_SYS_EEPROM_SIZE];
265
266 #endif
267
268 enum eeprom_action {
269         EEPROM_READ,
270         EEPROM_WRITE,
271         EEPROM_PRINT,
272         EEPROM_UPDATE,
273         EEPROM_ACTION_INVALID,
274 };
275
276 static enum eeprom_action parse_action(char *cmd)
277 {
278         if (!strncmp(cmd, "read", 4))
279                 return EEPROM_READ;
280         if (!strncmp(cmd, "write", 5))
281                 return EEPROM_WRITE;
282 #ifdef CONFIG_CMD_EEPROM_LAYOUT
283         if (!strncmp(cmd, "print", 5))
284                 return EEPROM_PRINT;
285         if (!strncmp(cmd, "update", 6))
286                 return EEPROM_UPDATE;
287 #endif
288
289         return EEPROM_ACTION_INVALID;
290 }
291
292 static int eeprom_execute_command(enum eeprom_action action, int i2c_bus,
293                                   ulong i2c_addr, int layout_ver, char *key,
294                                   char *value, ulong addr, ulong off, ulong cnt)
295 {
296         int rcode = 0;
297         const char *const fmt =
298                 "\nEEPROM @0x%lX %s: addr 0x%08lx  off 0x%04lx  count %ld ... ";
299 #ifdef CONFIG_CMD_EEPROM_LAYOUT
300         struct eeprom_layout layout;
301 #endif
302
303         if (action == EEPROM_ACTION_INVALID)
304                 return CMD_RET_USAGE;
305
306         eeprom_init(i2c_bus);
307         if (action == EEPROM_READ) {
308                 printf(fmt, i2c_addr, "read", addr, off, cnt);
309
310                 rcode = eeprom_read(i2c_addr, off, (uchar *)addr, cnt);
311
312                 puts("done\n");
313                 return rcode;
314         } else if (action == EEPROM_WRITE) {
315                 printf(fmt, i2c_addr, "write", addr, off, cnt);
316
317                 rcode = eeprom_write(i2c_addr, off, (uchar *)addr, cnt);
318
319                 puts("done\n");
320                 return rcode;
321         }
322
323 #ifdef CONFIG_CMD_EEPROM_LAYOUT
324         rcode = eeprom_read(i2c_addr, 0, eeprom_buf, CONFIG_SYS_EEPROM_SIZE);
325         if (rcode < 0)
326                 return rcode;
327
328         eeprom_layout_setup(&layout, eeprom_buf, CONFIG_SYS_EEPROM_SIZE,
329                             layout_ver);
330
331         if (action == EEPROM_PRINT) {
332                 layout.print(&layout);
333                 return 0;
334         }
335
336         layout.update(&layout, key, value);
337
338         rcode = eeprom_write(i2c_addr, 0, layout.data, CONFIG_SYS_EEPROM_SIZE);
339 #endif
340
341         return rcode;
342 }
343
344 #define NEXT_PARAM(argc, index) { (argc)--; (index)++; }
345 int do_eeprom(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
346 {
347         int layout_ver = LAYOUT_VERSION_AUTODETECT;
348         enum eeprom_action action = EEPROM_ACTION_INVALID;
349         int i2c_bus = -1, index = 0;
350         ulong i2c_addr = -1, addr = 0, cnt = 0, off = 0;
351         int ret;
352         char *field_name = "";
353         char *field_value = "";
354
355         if (argc <= 1)
356                 return CMD_RET_USAGE;
357
358         NEXT_PARAM(argc, index); /* Skip program name */
359
360         action = parse_action(argv[index]);
361         NEXT_PARAM(argc, index);
362
363         if (action == EEPROM_ACTION_INVALID)
364                 return CMD_RET_USAGE;
365
366 #ifdef CONFIG_CMD_EEPROM_LAYOUT
367         if (action == EEPROM_PRINT || action == EEPROM_UPDATE) {
368                 if (!strcmp(argv[index], "-l")) {
369                         NEXT_PARAM(argc, index);
370                         layout_ver = eeprom_parse_layout_version(argv[index]);
371                         NEXT_PARAM(argc, index);
372                 }
373         }
374 #endif
375
376         switch (action) {
377         case EEPROM_READ:
378         case EEPROM_WRITE:
379                 ret = parse_i2c_bus_addr(&i2c_bus, &i2c_addr, argc,
380                                          argv + index, 3);
381                 break;
382         case EEPROM_PRINT:
383                 ret = parse_i2c_bus_addr(&i2c_bus, &i2c_addr, argc,
384                                          argv + index, 0);
385                 break;
386         case EEPROM_UPDATE:
387                 ret = parse_i2c_bus_addr(&i2c_bus, &i2c_addr, argc,
388                                          argv + index, 2);
389                 break;
390         default:
391                 /* Get compiler to stop whining */
392                 return CMD_RET_USAGE;
393         }
394
395         if (ret == CMD_RET_USAGE)
396                 return ret;
397
398         while (ret--)
399                 NEXT_PARAM(argc, index);
400
401         if (action == EEPROM_READ || action == EEPROM_WRITE) {
402                 addr = parse_numeric_param(argv[index]);
403                 NEXT_PARAM(argc, index);
404                 off = parse_numeric_param(argv[index]);
405                 NEXT_PARAM(argc, index);
406                 cnt = parse_numeric_param(argv[index]);
407         }
408
409 #ifdef CONFIG_CMD_EEPROM_LAYOUT
410         if (action == EEPROM_UPDATE) {
411                 field_name = argv[index];
412                 NEXT_PARAM(argc, index);
413                 field_value = argv[index];
414                 NEXT_PARAM(argc, index);
415         }
416 #endif
417
418         return eeprom_execute_command(action, i2c_bus, i2c_addr, layout_ver,
419                                       field_name, field_value, addr, off, cnt);
420 }
421
422 U_BOOT_CMD(
423         eeprom, 8,      1,      do_eeprom,
424         "EEPROM sub-system",
425         "read  <bus> <devaddr> addr off cnt\n"
426         "eeprom write <bus> <devaddr> addr off cnt\n"
427         "       - read/write `cnt' bytes from `devaddr` EEPROM at offset `off'"
428 #ifdef CONFIG_CMD_EEPROM_LAYOUT
429         "\n"
430         "eeprom print [-l <layout_version>] <bus> <devaddr>\n"
431         "       - Print layout fields and their data in human readable format\n"
432         "eeprom update [-l <layout_version>] <bus> <devaddr> field_name field_value\n"
433         "       - Update a specific eeprom field with new data.\n"
434         "         The new data must be written in the same human readable format as shown by the print command.\n"
435         "\n"
436         "LAYOUT VERSIONS\n"
437         "The -l option can be used to force the command to interpret the EEPROM data using the chosen layout.\n"
438         "If the -l option is omitted, the command will auto detect the layout based on the data in the EEPROM.\n"
439         "The values which can be provided with the -l option are:\n"
440         CONFIG_EEPROM_LAYOUT_HELP_STRING"\n"
441 #endif
442 )