1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright 2021 Gateworks Corporation
4 */
5
6 #include <gsc.h>
7 #include <hexdump.h>
8 #include <i2c.h>
9 #include <dm/device.h>
10 #include <dm/uclass.h>
11
12 #include "eeprom.h"
13 #include "../fsa.h"
14
15 /* I2C */
16 #define SOM_EEPROM_BUSNO 0
17 #define SOM_EEPROM_ADDR 0x51
18 #define BASEBOARD_EEPROM_BUSNO 1
19 #define BASEBOARD_EEPROM_ADDR 0x52
20
21 struct venice_board_info som_info;
22 struct venice_board_info base_info;
23 char venice_model[64];
24 char venice_som_model[32];
25 char venice_baseboard_model[32];
26 u32 venice_serial;
27
28 /* return a mac address from EEPROM info */
eeprom_getmac(int index,uint8_t * address)29 int eeprom_getmac(int index, uint8_t *address)
30 {
31 int i, j;
32 u32 maclow, machigh;
33 u64 mac;
34
35 j = 0;
36 if (som_info.macno) {
37 maclow = som_info.mac[5];
38 maclow |= som_info.mac[4] << 8;
39 maclow |= som_info.mac[3] << 16;
40 maclow |= som_info.mac[2] << 24;
41 machigh = som_info.mac[1];
42 machigh |= som_info.mac[0] << 8;
43 mac = machigh;
44 mac <<= 32;
45 mac |= maclow;
46 for (i = 0; i < som_info.macno; i++, j++) {
47 if (index == j)
48 goto out;
49 }
50 }
51
52 maclow = base_info.mac[5];
53 maclow |= base_info.mac[4] << 8;
54 maclow |= base_info.mac[3] << 16;
55 maclow |= base_info.mac[2] << 24;
56 machigh = base_info.mac[1];
57 machigh |= base_info.mac[0] << 8;
58 mac = machigh;
59 mac <<= 32;
60 mac |= maclow;
61 for (i = 0; i < base_info.macno; i++, j++) {
62 if (index == j)
63 goto out;
64 }
65
66 return -EINVAL;
67
68 out:
69 mac += i;
70 address[0] = (mac >> 40) & 0xff;
71 address[1] = (mac >> 32) & 0xff;
72 address[2] = (mac >> 24) & 0xff;
73 address[3] = (mac >> 16) & 0xff;
74 address[4] = (mac >> 8) & 0xff;
75 address[5] = (mac >> 0) & 0xff;
76
77 return 0;
78 }
79
eeprom_read(int busno,int slave,int alen,struct venice_board_info * info)80 static int eeprom_read(int busno, int slave, int alen, struct venice_board_info *info)
81 {
82 int i;
83 int chksum;
84 unsigned char *buf = (unsigned char *)info;
85 struct udevice *dev, *bus;
86 int ret;
87
88 /* probe device */
89 ret = uclass_get_device_by_seq(UCLASS_I2C, busno, &bus);
90 if (ret)
91 return ret;
92 ret = dm_i2c_probe(bus, slave, 0, &dev);
93 if (ret)
94 return ret;
95
96 /* read eeprom config section */
97 memset(info, 0, sizeof(*info));
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 ret = dm_i2c_read(dev, 0x00, buf, sizeof(*info));
104 if (ret) {
105 if (slave == SOM_EEPROM_ADDR)
106 printf("EEPROM: Failed to read EEPROM\n");
107 return ret;
108 }
109
110 /* validate checksum */
111 for (chksum = 0, i = 0; i < (int)sizeof(*info) - 2; i++)
112 chksum += buf[i];
113 if ((info->chksum[0] != ((chksum >> 8) & 0xff)) ||
114 (info->chksum[1] != (chksum & 0xff))) {
115 printf("EEPROM: I2C%d@0x%02x: Invalid Checksum\n", busno, slave);
116 print_hex_dump_bytes("", DUMP_PREFIX_NONE, buf, sizeof(*info));
117 memset(info, 0, sizeof(*info));
118 return -EINVAL;
119 }
120
121 /* sanity check valid model */
122 if (info->model[0] != 'G' || info->model[1] != 'W') {
123 printf("EEPROM: I2C%d@0x%02x: Invalid Model in EEPROM\n", busno, slave);
124 print_hex_dump_bytes("", DUMP_PREFIX_NONE, buf, sizeof(*info));
125 memset(info, 0, sizeof(*info));
126 return -EINVAL;
127 }
128
129 return 0;
130 }
131
fsa_eeprom_read(const char * base,int fsa,struct fsa_board_info * info)132 static int fsa_eeprom_read(const char *base, int fsa, struct fsa_board_info *info)
133 {
134 int i;
135 int chksum;
136 unsigned char *buf = (unsigned char *)info;
137 struct udevice *dev, *bus;
138 int ret;
139 u8 reg;
140
141 /* probe mux */
142 ret = uclass_get_device_by_seq(UCLASS_I2C, 2, &bus);
143 if (!ret)
144 ret = dm_i2c_probe(bus, 0x70, 0, &dev);
145 if (ret)
146 return ret;
147 /* steer mux */
148 if (!strncmp(base, "GW82", 4)) {
149 if (fsa < 3)
150 reg = (fsa == 1) ? BIT(1) : BIT(0);
151 else
152 return -EINVAL;
153 }
154 dm_i2c_write(dev, 0x00, ®, 1);
155
156 /* get eeprom */
157 ret = dm_i2c_probe(bus, 0x54, 0, &dev);
158 if (ret)
159 return ret;
160
161 /* read eeprom config section */
162 ret = dm_i2c_read(dev, 0x00, buf, sizeof(*info));
163 if (ret)
164 return ret;
165
166 /* validate checksum */
167 for (chksum = 0, i = 0; i < (int)sizeof(*info) - 2; i++)
168 chksum += buf[i];
169 if ((info->chksum[0] != ((chksum >> 8) & 0xff)) ||
170 (info->chksum[1] != (chksum & 0xff))) {
171 printf("FSA%d EEPROM (board): %s: Invalid Checksum\n", fsa, dev->name);
172 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, buf, sizeof(*info));
173 memset(info, 0, sizeof(*info));
174 return -EINVAL;
175 }
176
177 return 0;
178 }
179
180 /* determine BOM revision from model */
get_bom_rev(const char * str)181 int get_bom_rev(const char *str)
182 {
183 int rev_bom = 0;
184 int i;
185
186 for (i = strlen(str) - 1; i > 0; i--) {
187 if (str[i] == '-')
188 break;
189 if (str[i] >= '1' && str[i] <= '9') {
190 rev_bom = str[i] - '0';
191 break;
192 }
193 }
194 return rev_bom;
195 }
196
197 /* determine PCB revision from model */
get_pcb_rev(const char * str)198 char get_pcb_rev(const char *str)
199 {
200 char rev_pcb = 'A';
201 int i;
202
203 for (i = strlen(str) - 1; i > 0; i--) {
204 if (str[i] == '-')
205 break;
206 if (str[i] >= 'A') {
207 rev_pcb = str[i];
208 break;
209 }
210 }
211 return rev_pcb;
212 }
213
214 /*
215 * get dt name based on model and detail level:
216 *
217 * For boards that are a combination of a SoM plus a Baseboard:
218 * Venice SoM part numbers are GW70xx where xx is:
219 * 7000-7019: same PCB with som dt of '0x'
220 * 7020-7039: same PCB with som dt of '2x'
221 * 7040-7059: same PCB with som dt of '4x'
222 * 7060-7079: same PCB with som dt of '6x'
223 * 7080-7099: same PCB with som dt of '8x'
224 * Venice Baseboard part numbers are GW7xxx where xxx is:
225 * 7100-7199: same PCB with base dt of '71xx'
226 * 7200-7299: same PCB with base dt of '72xx'
227 * 7300-7399: same PCB with base dt of '73xx'
228 * 7400-7499: same PCB with base dt of '74xx'
229 * 7500-7599: same PCB with base dt of '75xx'
230 * 7600-7699: same PCB with base dt of '76xx'
231 * 7700-7799: same PCB with base dt of '77xx'
232 * 7800-7899: same PCB with base dt of '78xx'
233 * DT name is comprised of:
234 * gw<base dt>-<som dt>-[base-pcb-rev][base-bom-rev][som-pcb-rev][som-bom-rev]
235 *
236 * For board models from 7900-7999 each PCB is unique with its own dt:
237 * DT name is comprised:
238 * gw<model>-[pcb-rev][bom-rev]
239 *
240 */
241 #define snprintfcat(dest, sz, fmt, ...) \
242 snprintf((dest) + strlen(dest), (sz) - strlen(dest), fmt, ##__VA_ARGS__)
eeprom_get_dtb_name(int level,char * buf,int sz)243 const char *eeprom_get_dtb_name(int level, char *buf, int sz)
244 {
245 #ifdef CONFIG_IMX8MM
246 const char *pre = "imx8mm-venice-gw";
247 #elif CONFIG_IMX8MN
248 const char *pre = "imx8mn-venice-gw";
249 #elif CONFIG_IMX8MP
250 const char *pre = "imx8mp-venice-gw";
251 #endif
252 int model, rev_pcb, rev_bom;
253
254 model = ((som_info.model[2] - '0') * 1000)
255 + ((som_info.model[3] - '0') * 100)
256 + ((som_info.model[4] - '0') * 10)
257 + (som_info.model[5] - '0');
258 rev_pcb = tolower(get_pcb_rev(som_info.model));
259 rev_bom = get_bom_rev(som_info.model);
260
261 /* som + baseboard*/
262 if (base_info.model[0]) {
263 /* baseboard id: 7100-7199->71; 7200-7299->72; etc */
264 int base = ((base_info.model[2] - '0') * 10) + (base_info.model[3] - '0');
265 /* som id: 7000-7019->1; 7020-7039->2; etc */
266 int som = ((model % 100) / 20) * 2;
267 int rev_base_pcb = tolower(get_pcb_rev(base_info.model));
268 int rev_base_bom = get_bom_rev(base_info.model);
269
270 snprintf(buf, sz, "%s%2dxx-%dx", pre, base, som);
271 /* GW79xx baseboards have no build options */
272 if (base == 79) {
273 base = (int)strtoul(base_info.model + 2, NULL, 10);
274 snprintf(buf, sz, "%s%4d-%dx", pre, base, som);
275 }
276 switch (level) {
277 case 0: /* full model (ie gw73xx-0x-a1a1) */
278 if (rev_base_bom)
279 snprintfcat(buf, sz, "-%c%d", rev_base_pcb, rev_base_bom);
280 else
281 snprintfcat(buf, sz, "-%c", rev_base_pcb);
282 if (rev_bom)
283 snprintfcat(buf, sz, "%c%d", rev_pcb, rev_bom);
284 else
285 snprintfcat(buf, sz, "%c", rev_pcb);
286 break;
287 case 1: /* don't care about SoM revision */
288 if (rev_base_bom)
289 snprintfcat(buf, sz, "-%c%d", rev_base_pcb, rev_base_bom);
290 else
291 snprintfcat(buf, sz, "-%c", rev_base_pcb);
292 snprintfcat(buf, sz, "xx");
293 break;
294 case 2: /* don't care about baseboard revision */
295 snprintfcat(buf, sz, "-xx");
296 if (rev_bom)
297 snprintfcat(buf, sz, "%c%d", rev_pcb, rev_bom);
298 else
299 snprintfcat(buf, sz, "%c", rev_pcb);
300 break;
301 case 3: /* don't care about SoM/baseboard revision */
302 break;
303 default:
304 return NULL;
305 }
306 } else {
307 snprintf(buf, sz, "%s%04d", pre, model);
308 switch (level) {
309 case 0: /* full model wth PCB and BOM revision first (ie gw7901-a1) */
310 if (rev_bom)
311 snprintfcat(buf, sz, "-%c%d", rev_pcb, rev_bom);
312 else
313 snprintfcat(buf, sz, "-%c", rev_pcb);
314 break;
315 case 1: /* don't care about BOM revision */
316 snprintfcat(buf, sz, "-%c", rev_pcb);
317 break;
318 case 2: /* don't care about PCB or BOM revision */
319 break;
320 case 3: /* don't care about last digit of model */
321 buf[strlen(buf) - 1] = 'x';
322 break;
323 case 4: /* don't care about last two digits of model */
324 buf[strlen(buf) - 1] = 'x';
325 buf[strlen(buf) - 2] = 'x';
326 break;
327 default:
328 return NULL;
329 break;
330 }
331 }
332
333 return buf;
334 }
335
eeprom_info(bool verbose)336 static int eeprom_info(bool verbose)
337 {
338 printf("Model : %s\n", venice_model);
339 printf("Serial : %d\n", som_info.serial);
340 printf("MFGDate : %02x-%02x-%02x%02x\n",
341 som_info.mfgdate[0], som_info.mfgdate[1],
342 som_info.mfgdate[2], som_info.mfgdate[3]);
343 if (base_info.model[0] && verbose) {
344 printf("SOM : %s %d %02x-%02x-%02x%02x\n",
345 som_info.model, som_info.serial,
346 som_info.mfgdate[0], som_info.mfgdate[1],
347 som_info.mfgdate[2], som_info.mfgdate[3]);
348 printf("BASE : %s %d %02x-%02x-%02x%02x\n",
349 base_info.model, base_info.serial,
350 base_info.mfgdate[0], base_info.mfgdate[1],
351 base_info.mfgdate[2], base_info.mfgdate[3]);
352 }
353 if (verbose)
354 fsa_show();
355
356 return 0;
357 }
358
venice_eeprom_init(int quiet)359 struct venice_board_info *venice_eeprom_init(int quiet)
360 {
361 char rev_pcb;
362 int rev_bom;
363 int ret;
364
365 ret = eeprom_read(SOM_EEPROM_BUSNO, SOM_EEPROM_ADDR, 1, &som_info);
366 if (ret) {
367 puts("ERROR: Failed to probe EEPROM\n");
368 memset(&som_info, 0, sizeof(som_info));
369 return 0;
370 }
371 strlcpy(venice_som_model, som_info.model, sizeof(venice_som_model));
372
373 /* read optional baseboard EEPROM */
374 eeprom_read(BASEBOARD_EEPROM_BUSNO, BASEBOARD_EEPROM_ADDR, 2, &base_info);
375
376 /* create model strings */
377 if (base_info.model[0]) {
378 sprintf(venice_model, "GW%c%c%c%c-%c%c-",
379 base_info.model[2], /* family */
380 base_info.model[3], /* baseboard */
381 base_info.model[4], base_info.model[5], /* subload of baseboard */
382 som_info.model[4], som_info.model[5]); /* last 2digits of SOM */
383 strlcpy(venice_baseboard_model, base_info.model, sizeof(venice_baseboard_model));
384
385 /* baseboard revision */
386 rev_pcb = get_pcb_rev(base_info.model);
387 rev_bom = get_bom_rev(base_info.model);
388 if (rev_bom)
389 sprintf(venice_model + strlen(venice_model), "%c%d", rev_pcb, rev_bom);
390 else
391 sprintf(venice_model + strlen(venice_model), "%c", rev_pcb);
392 /* som revision */
393 rev_pcb = get_pcb_rev(som_info.model);
394 rev_bom = get_bom_rev(som_info.model);
395 if (rev_bom)
396 sprintf(venice_model + strlen(venice_model), "%c%d", rev_pcb, rev_bom);
397 else
398 sprintf(venice_model + strlen(venice_model), "%c", rev_pcb);
399 } else {
400 strcpy(venice_model, som_info.model);
401 }
402 venice_serial = som_info.serial;
403
404 /* GW8xxx product family naming scheme */
405 if (venice_model[2] == '8') {
406 struct fsa_board_info fsa_info;
407 int i = 0;
408 int fsa;
409
410 /* baseboard */
411 if (base_info.model[0]) {
412 rev_pcb = get_pcb_rev(base_info.model);
413 rev_bom = get_bom_rev(base_info.model);
414 venice_model[i++] = 'G';
415 venice_model[i++] = 'W';
416 venice_model[i++] = base_info.model[2]; /* baseboard */
417 venice_model[i++] = base_info.model[3];
418 venice_model[i++] = base_info.model[4]; /* subload */
419 venice_model[i++] = base_info.model[5];
420 venice_model[i++] = rev_pcb;
421 if (rev_bom)
422 venice_model[i++] = rev_bom;
423 venice_model[i++] = '-';
424 venice_model[i++] = 'S';
425 } else {
426 venice_model[i++] = 'G';
427 venice_model[i++] = 'W';
428 }
429
430 /* som */
431 rev_pcb = get_pcb_rev(som_info.model);
432 rev_bom = get_bom_rev(som_info.model);
433 venice_model[i++] = som_info.model[4];
434 venice_model[i++] = som_info.model[5];
435 venice_model[i++] = rev_pcb;
436 if (rev_bom)
437 venice_model[i++] = rev_bom;
438
439 /* fsa */
440 for (fsa = 1; fsa < FSA_MAX; fsa++) {
441 if (!fsa_eeprom_read(venice_model, fsa, &fsa_info)) {
442 venice_model[i++] = '-';
443 venice_model[i++] = 'F';
444 venice_model[i++] = '0' + fsa;
445 venice_model[i++] = fsa_info.model[5];
446 venice_model[i++] = fsa_info.model[6];
447 venice_model[i++] = fsa_info.model[8];
448 if (fsa_info.model[9])
449 venice_model[i++] = fsa_info.model[9];
450 }
451 }
452
453 /* append extra model info */
454 if (som_info.config[0] >= 32 && som_info.config[0] < 0x7f) {
455 venice_model[i++] = '-';
456 strlcpy(venice_model + i, som_info.config, (sizeof(venice_model) - i) - 1);
457 i += strlen(som_info.config);
458 if (i >= sizeof(venice_model))
459 i = sizeof(venice_model) - 1;
460 }
461 venice_model[i++] = 0;
462 }
463
464 if (!quiet)
465 eeprom_info(false);
466
467 if (!strncmp(venice_model, "GW7901-SP486", 12) &&
468 strcmp(venice_model, "GW7901-SP486-C")) {
469 som_info.sdram_size++;
470 }
471
472 return &som_info;
473 }
474
board_gsc_info(void)475 void board_gsc_info(void)
476 {
477 eeprom_info(true);
478 }
479
eeprom_get_model(void)480 const char *eeprom_get_model(void)
481 {
482 return venice_model;
483 }
484
eeprom_get_som_model(void)485 const char *eeprom_get_som_model(void)
486 {
487 return venice_som_model;
488 }
489
eeprom_get_baseboard_model(void)490 const char *eeprom_get_baseboard_model(void)
491 {
492 return venice_baseboard_model;
493 }
494
eeprom_get_serial(void)495 u32 eeprom_get_serial(void)
496 {
497 return venice_serial;
498 }
499