Prepare v2024.10
[platform/kernel/u-boot.git] / board / gateworks / gw_ventana / eeprom.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2014 Gateworks Corporation
4  * Author: Tim Harvey <tharvey@gateworks.com>
5  */
6
7 #include <command.h>
8 #include <gsc.h>
9 #include <hexdump.h>
10 #include <i2c.h>
11 #include <asm/arch/sys_proto.h>
12 #include <dm/device.h>
13 #include <dm/uclass.h>
14 #include <linux/ctype.h>
15 #include <linux/delay.h>
16
17 #include "eeprom.h"
18
19 /*
20  * EEPROM board info struct populated by read_eeprom so that we only have to
21  * read it once.
22  */
23 struct ventana_board_info ventana_info;
24 int board_type;
25
26 #if CONFIG_IS_ENABLED(DM_I2C)
27 struct udevice *i2c_get_dev(int busno, int slave)
28 {
29         struct udevice *dev, *bus;
30         int ret;
31
32         ret = uclass_get_device_by_seq(UCLASS_I2C, busno, &bus);
33         if (ret)
34                 return NULL;
35         ret = dm_i2c_probe(bus, slave, 0, &dev);
36         if (ret)
37                 return NULL;
38
39         return dev;
40 }
41 #endif
42
43 /*
44  * The Gateworks System Controller will fail to ACK a master transaction if
45  * it is busy, which can occur during its 1HZ timer tick while reading ADC's.
46  * When this does occur, it will never be busy long enough to fail more than
47  * 2 back-to-back transfers.  Thus we wrap i2c_read and i2c_write with
48  * 3 retries.
49  */
50 int gsc_i2c_read(uchar chip, uint addr, int alen, uchar *buf, int len)
51 {
52         int retry = 3;
53         int n = 0;
54         int ret;
55 #if CONFIG_IS_ENABLED(DM_I2C)
56         struct udevice *dev;
57
58         dev = i2c_get_dev(BOARD_EEPROM_BUSNO, chip);
59         if (!dev)
60                 return -ENODEV;
61         ret = i2c_set_chip_offset_len(dev, alen);
62         if (ret) {
63                 puts("EEPROM: Failed to set alen\n");
64                 return ret;
65         }
66 #else
67         i2c_set_bus_num(BOARD_EEPROM_BUSNO);
68 #endif
69
70         while (n++ < retry) {
71 #if CONFIG_IS_ENABLED(DM_I2C)
72                 ret = dm_i2c_read(dev, addr, buf, len);
73 #else
74                 ret = i2c_read(chip, addr, alen, buf, len);
75 #endif
76                 if (!ret)
77                         break;
78                 debug("%s: 0x%02x 0x%02x retry%d: %d\n", __func__, chip, addr,
79                       n, ret);
80                 if (ret != -ENODEV)
81                         break;
82                 mdelay(10);
83         }
84         return ret;
85 }
86
87 int gsc_i2c_write(uchar chip, uint addr, int alen, uchar *buf, int len)
88 {
89         int retry = 3;
90         int n = 0;
91         int ret;
92 #if CONFIG_IS_ENABLED(DM_I2C)
93         struct udevice *dev;
94
95         dev = i2c_get_dev(BOARD_EEPROM_BUSNO, chip);
96         if (!dev)
97                 return -ENODEV;
98         ret = i2c_set_chip_offset_len(dev, alen);
99         if (ret) {
100                 puts("EEPROM: Failed to set alen\n");
101                 return ret;
102         }
103 #endif
104
105         while (n++ < retry) {
106 #if CONFIG_IS_ENABLED(DM_I2C)
107                 ret = dm_i2c_write(dev, addr, buf, len);
108 #else
109                 ret = i2c_write(chip, addr, alen, buf, len);
110 #endif
111                 if (!ret)
112                         break;
113                 debug("%s: 0x%02x 0x%02x retry%d: %d\n", __func__, chip, addr,
114                       n, ret);
115                 if (ret != -ENODEV)
116                         break;
117                 mdelay(10);
118         }
119         mdelay(100);
120         return ret;
121 }
122
123 /* determine BOM revision from model */
124 int get_bom_rev(const char *str)
125 {
126         int  rev_bom = 0;
127         int i;
128
129         for (i = strlen(str) - 1; i > 0; i--) {
130                 if (str[i] == '-')
131                         break;
132                 if (str[i] >= '1' && str[i] <= '9') {
133                         rev_bom = str[i] - '0';
134                         break;
135                 }
136         }
137         return rev_bom;
138 }
139
140 /* determine PCB revision from model */
141 char get_pcb_rev(const char *str)
142 {
143         char rev_pcb = 'A';
144         int i;
145
146         for (i = strlen(str) - 1; i > 0; i--) {
147                 if (str[i] == '-')
148                         break;
149                 if (str[i] >= 'A') {
150                         rev_pcb = str[i];
151                         break;
152                 }
153         }
154         return rev_pcb;
155 }
156
157 /*
158  * get dt name based on model and detail level:
159  */
160 const char *gsc_get_dtb_name(int level, char *buf, int sz)
161 {
162         const char *model = (const char *)ventana_info.model;
163         const char *pre = is_mx6dq() ? "imx6q-" : "imx6dl-";
164         int modelno, rev_pcb, rev_bom;
165
166         /* a few board models are dt equivalents to other models */
167         if (strncasecmp(model, "gw5906", 6) == 0)
168                 model = "gw552x-d";
169         else if (strncasecmp(model, "gw5908", 6) == 0)
170                 model = "gw53xx-f";
171         else if (strncasecmp(model, "gw5905", 6) == 0)
172                 model = "gw5904-a";
173
174         modelno = ((model[2] - '0') * 1000)
175                   + ((model[3] - '0') * 100)
176                   + ((model[4] - '0') * 10)
177                   + (model[5] - '0');
178         rev_pcb = tolower(get_pcb_rev(model));
179         rev_bom = get_bom_rev(model);
180
181         /* compare model/rev/bom in order of most specific to least */
182         snprintf(buf, sz, "%s%04d", pre, modelno);
183         switch (level) {
184         case 0: /* full model first (ie gw5400-a1) */
185                 if (rev_bom) {
186                         snprintf(buf, sz, "%sgw%04d-%c%d", pre, modelno, rev_pcb, rev_bom);
187                         break;
188                 }
189                 fallthrough;
190         case 1: /* don't care about bom rev (ie gw5400-a) */
191                 snprintf(buf, sz, "%sgw%04d-%c", pre, modelno, rev_pcb);
192                 break;
193         case 2: /* don't care about the pcb rev (ie gw5400) */
194                 snprintf(buf, sz, "%sgw%04d", pre, modelno);
195                 break;
196         case 3: /* look for generic model (ie gw540x) */
197                 snprintf(buf, sz, "%sgw%03dx", pre, modelno / 10);
198                 break;
199         case 4: /* look for more generic model (ie gw54xx) */
200                 snprintf(buf, sz, "%sgw%02dxx", pre, modelno / 100);
201                 break;
202         default: /* give up */
203                 return NULL;
204         }
205
206         return buf;
207 }
208 /* read ventana EEPROM, check for validity, and return baseboard type */
209 int
210 read_eeprom(struct ventana_board_info *info)
211 {
212         int i;
213         int chksum;
214         char baseboard;
215         int type;
216         unsigned char *buf = (unsigned char *)info;
217
218         memset(info, 0, sizeof(*info));
219
220         /* read eeprom config section */
221         if (gsc_i2c_read(BOARD_EEPROM_ADDR, 0x00, 1, buf, sizeof(*info))) {
222                 puts("EEPROM: Failed to read EEPROM\n");
223                 return GW_UNKNOWN;
224         }
225
226         /* sanity checks */
227         if (info->model[0] != 'G' || info->model[1] != 'W') {
228                 puts("EEPROM: Invalid Model in EEPROM\n");
229                 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf,
230                                      sizeof(*info));
231                 return GW_UNKNOWN;
232         }
233
234         /* validate checksum */
235         for (chksum = 0, i = 0; i < sizeof(*info)-2; i++)
236                 chksum += buf[i];
237         if ((info->chksum[0] != chksum>>8) ||
238             (info->chksum[1] != (chksum&0xff))) {
239                 puts("EEPROM: Failed EEPROM checksum\n");
240                 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf,
241                                      sizeof(*info));
242                 return GW_UNKNOWN;
243         }
244
245         /* original GW5400-A prototype */
246         baseboard = info->model[3];
247         if (strncasecmp((const char *)info->model, "GW5400-A", 8) == 0)
248                 baseboard = '0';
249
250         type = GW_UNKNOWN;
251         switch (baseboard) {
252         case '0': /* original GW5400-A prototype */
253                 type = GW54proto;
254                 break;
255         case '1':
256                 type = GW51xx;
257                 break;
258         case '2':
259                 type = GW52xx;
260                 break;
261         case '3':
262                 type = GW53xx;
263                 break;
264         case '4':
265                 type = GW54xx;
266                 break;
267         case '5':
268                 if (info->model[4] == '1') {
269                         type = GW551x;
270                         break;
271                 } else if (info->model[4] == '2') {
272                         type = GW552x;
273                         break;
274                 } else if (info->model[4] == '3') {
275                         type = GW553x;
276                         break;
277                 }
278                 break;
279         case '6':
280                 if (info->model[4] == '0')
281                         type = GW560x;
282                 break;
283         case '9':
284                 if (info->model[4] == '0' && info->model[5] == '1')
285                         type = GW5901;
286                 else if (info->model[4] == '0' && info->model[5] == '2')
287                         type = GW5902;
288                 else if (info->model[4] == '0' && info->model[5] == '3')
289                         type = GW5903;
290                 else if (info->model[4] == '0' && info->model[5] == '4')
291                         type = GW5904;
292                 else if (info->model[4] == '0' && info->model[5] == '5')
293                         type = GW5905;
294                 else if (info->model[4] == '0' && info->model[5] == '6')
295                         type = GW5906;
296                 else if (info->model[4] == '0' && info->model[5] == '7')
297                         type = GW5907;
298                 else if (info->model[4] == '0' && info->model[5] == '8')
299                         type = GW5908;
300                 else if (info->model[4] == '0' && info->model[5] == '9')
301                         type = GW5909;
302                 else if (info->model[4] == '1' && info->model[5] == '0')
303                         type = GW5910;
304                 else if (info->model[4] == '1' && info->model[5] == '2')
305                         type = GW5912;
306                 else if (info->model[4] == '1' && info->model[5] == '3')
307                         type = GW5913;
308                 break;
309         default:
310                 printf("EEPROM: Unknown model in EEPROM: %s\n", info->model);
311                 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf,
312                                      sizeof(*info));
313                 break;
314         }
315         return type;
316 }
317
318 /* list of config bits that the bootloader will remove from dtb if not set */
319 struct ventana_eeprom_config econfig[] = {
320         { "eth0", "ethernet0", EECONFIG_ETH0 },
321         { "usb0", NULL, EECONFIG_USB0 },
322         { "usb1", NULL, EECONFIG_USB1 },
323         { "mmc0", NULL, EECONFIG_SD0 },
324         { "mmc1", NULL, EECONFIG_SD1 },
325         { "mmc2", NULL, EECONFIG_SD2 },
326         { "mmc3", NULL, EECONFIG_SD3 },
327         { /* Sentinel */ }
328 };
329
330 #if defined(CONFIG_CMD_EECONFIG) && !defined(CONFIG_SPL_BUILD)
331 static struct ventana_eeprom_config *get_config(const char *name)
332 {
333         struct ventana_eeprom_config *cfg = econfig;
334
335         while (cfg->name) {
336                 if (0 == strcmp(name, cfg->name))
337                         return cfg;
338                 cfg++;
339         }
340         return NULL;
341 }
342
343 static u8 econfig_bytes[sizeof(ventana_info.config)];
344 static int econfig_init = -1;
345
346 static int do_econfig(struct cmd_tbl *cmdtp, int flag, int argc,
347                       char *const argv[])
348 {
349         struct ventana_eeprom_config *cfg;
350         struct ventana_board_info *info = &ventana_info;
351         int i;
352
353         if (argc < 2)
354                 return CMD_RET_USAGE;
355
356         /* initialize */
357         if (econfig_init != 1) {
358                 memcpy(econfig_bytes, info->config, sizeof(econfig_bytes));
359                 econfig_init = 1;
360         }
361
362         /* list configs */
363         if ((strncmp(argv[1], "list", 4) == 0)) {
364                 cfg = econfig;
365                 while (cfg->name) {
366                         printf("%s: %d\n", cfg->name,
367                                test_bit(cfg->bit, econfig_bytes) ?  1 : 0);
368                         cfg++;
369                 }
370         }
371
372         /* save */
373         else if ((strncmp(argv[1], "save", 4) == 0)) {
374                 unsigned char *buf = (unsigned char *)info;
375                 int chksum;
376
377                 /* calculate new checksum */
378                 memcpy(info->config, econfig_bytes, sizeof(econfig_bytes));
379                 for (chksum = 0, i = 0; i < sizeof(*info)-2; i++)
380                         chksum += buf[i];
381                 debug("old chksum:0x%04x\n",
382                       (info->chksum[0] << 8) | info->chksum[1]);
383                 debug("new chksum:0x%04x\n", chksum);
384                 info->chksum[0] = chksum >> 8;
385                 info->chksum[1] = chksum & 0xff;
386
387                 /* write new config data */
388                 if (gsc_i2c_write(BOARD_EEPROM_ADDR, info->config - (u8 *)info,
389                                   1, econfig_bytes, sizeof(econfig_bytes))) {
390                         printf("EEPROM: Failed updating config\n");
391                         return CMD_RET_FAILURE;
392                 }
393
394                 /* write new config data */
395                 if (gsc_i2c_write(BOARD_EEPROM_ADDR, info->chksum - (u8 *)info,
396                                   1, info->chksum, 2)) {
397                         printf("EEPROM: Failed updating checksum\n");
398                         return CMD_RET_FAILURE;
399                 }
400
401                 printf("Config saved to EEPROM\n");
402         }
403
404         /* get config */
405         else if (argc == 2) {
406                 cfg = get_config(argv[1]);
407                 if (cfg) {
408                         printf("%s: %d\n", cfg->name,
409                                test_bit(cfg->bit, econfig_bytes) ? 1 : 0);
410                 } else {
411                         printf("invalid config: %s\n", argv[1]);
412                         return CMD_RET_FAILURE;
413                 }
414         }
415
416         /* set config */
417         else if (argc == 3) {
418                 cfg = get_config(argv[1]);
419                 if (cfg) {
420                         if (simple_strtol(argv[2], NULL, 10)) {
421                                 test_and_set_bit(cfg->bit, econfig_bytes);
422                                 printf("Enabled %s\n", cfg->name);
423                         } else {
424                                 test_and_clear_bit(cfg->bit, econfig_bytes);
425                                 printf("Disabled %s\n", cfg->name);
426                         }
427                 } else {
428                         printf("invalid config: %s\n", argv[1]);
429                         return CMD_RET_FAILURE;
430                 }
431         }
432
433         else
434                 return CMD_RET_USAGE;
435
436         return CMD_RET_SUCCESS;
437 }
438
439 U_BOOT_CMD(
440         econfig, 3, 0, do_econfig,
441         "EEPROM configuration",
442         "list - list config\n"
443         "save - save config to EEPROM\n"
444         "<name> - get config 'name'\n"
445         "<name> [0|1] - set config 'name' to value\n"
446 );
447
448 #endif /* CONFIG_CMD_EECONFIG */