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, &reg, 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