]>
Commit | Line | Data |
---|---|---|
9755e3db MS |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
d61728c8 | 3 | * (C) Copyright 2014 - 2020 Xilinx, Inc. |
9755e3db MS |
4 | * Michal Simek <[email protected]> |
5 | */ | |
6 | ||
7 | #include <common.h> | |
09140113 | 8 | #include <env.h> |
f7ae49fc | 9 | #include <log.h> |
401d1c4f | 10 | #include <asm/global_data.h> |
fc274a59 | 11 | #include <asm/sections.h> |
9755e3db MS |
12 | #include <dm/uclass.h> |
13 | #include <i2c.h> | |
a29511ee | 14 | #include <linux/sizes.h> |
d61728c8 | 15 | #include <malloc.h> |
80fdef12 | 16 | #include "board.h" |
d61728c8 MS |
17 | #include <dm.h> |
18 | #include <i2c_eeprom.h> | |
19 | #include <net.h> | |
05af4834 | 20 | #include <generated/dt.h> |
80c0d38a | 21 | #include <soc.h> |
9755e3db | 22 | |
f149b39c MS |
23 | #include "fru.h" |
24 | ||
9fea3b18 | 25 | #if defined(CONFIG_ZYNQ_GEM_I2C_MAC_OFFSET) |
829e8c73 MS |
26 | int zynq_board_read_rom_ethaddr(unsigned char *ethaddr) |
27 | { | |
28 | int ret = -EINVAL; | |
829e8c73 MS |
29 | struct udevice *dev; |
30 | ofnode eeprom; | |
31 | ||
32 | eeprom = ofnode_get_chosen_node("xlnx,eeprom"); | |
33 | if (!ofnode_valid(eeprom)) | |
34 | return -ENODEV; | |
35 | ||
36 | debug("%s: Path to EEPROM %s\n", __func__, | |
14ca9f7f | 37 | ofnode_read_chosen_string("xlnx,eeprom")); |
829e8c73 MS |
38 | |
39 | ret = uclass_get_device_by_ofnode(UCLASS_I2C_EEPROM, eeprom, &dev); | |
40 | if (ret) | |
41 | return ret; | |
42 | ||
43 | ret = dm_i2c_read(dev, CONFIG_ZYNQ_GEM_I2C_MAC_OFFSET, ethaddr, 6); | |
44 | if (ret) | |
45 | debug("%s: I2C EEPROM MAC address read failed\n", __func__); | |
46 | else | |
47 | debug("%s: I2C EEPROM MAC %pM\n", __func__, ethaddr); | |
829e8c73 MS |
48 | |
49 | return ret; | |
50 | } | |
9fea3b18 | 51 | #endif |
fec657be | 52 | |
d61728c8 MS |
53 | #define EEPROM_HEADER_MAGIC 0xdaaddeed |
54 | #define EEPROM_HDR_MANUFACTURER_LEN 16 | |
55 | #define EEPROM_HDR_NAME_LEN 16 | |
56 | #define EEPROM_HDR_REV_LEN 8 | |
57 | #define EEPROM_HDR_SERIAL_LEN 20 | |
58 | #define EEPROM_HDR_NO_OF_MAC_ADDR 4 | |
59 | #define EEPROM_HDR_ETH_ALEN ETH_ALEN | |
60 | ||
61 | struct xilinx_board_description { | |
62 | u32 header; | |
63 | char manufacturer[EEPROM_HDR_MANUFACTURER_LEN + 1]; | |
64 | char name[EEPROM_HDR_NAME_LEN + 1]; | |
65 | char revision[EEPROM_HDR_REV_LEN + 1]; | |
66 | char serial[EEPROM_HDR_SERIAL_LEN + 1]; | |
67 | u8 mac_addr[EEPROM_HDR_NO_OF_MAC_ADDR][EEPROM_HDR_ETH_ALEN + 1]; | |
68 | }; | |
69 | ||
a03b5947 MS |
70 | static int highest_id = -1; |
71 | static struct xilinx_board_description **board_info; | |
d61728c8 | 72 | |
f149b39c | 73 | #define XILINX_I2C_DETECTION_BITS sizeof(struct fru_common_hdr) |
d61728c8 MS |
74 | |
75 | /* Variable which stores pointer to array which stores eeprom content */ | |
76 | struct xilinx_legacy_format { | |
77 | char board_sn[18]; /* 0x0 */ | |
78 | char unused0[14]; /* 0x12 */ | |
79 | char eth_mac[6]; /* 0x20 */ | |
80 | char unused1[170]; /* 0x26 */ | |
81 | char board_name[11]; /* 0xd0 */ | |
82 | char unused2[5]; /* 0xdc */ | |
83 | char board_revision[3]; /* 0xe0 */ | |
84 | char unused3[29]; /* 0xe3 */ | |
85 | }; | |
86 | ||
87 | static void xilinx_eeprom_legacy_cleanup(char *eeprom, int size) | |
88 | { | |
89 | int i; | |
90 | char byte; | |
91 | ||
92 | for (i = 0; i < size; i++) { | |
93 | byte = eeprom[i]; | |
94 | ||
95 | /* Remove all ffs and spaces */ | |
96 | if (byte == 0xff || byte == ' ') | |
97 | eeprom[i] = 0; | |
98 | ||
99 | /* Convert strings to lower case */ | |
100 | if (byte >= 'A' && byte <= 'Z') | |
101 | eeprom[i] = byte + 'a' - 'A'; | |
102 | } | |
103 | } | |
104 | ||
105 | static int xilinx_read_eeprom_legacy(struct udevice *dev, char *name, | |
106 | struct xilinx_board_description *desc) | |
107 | { | |
108 | int ret, size; | |
109 | struct xilinx_legacy_format *eeprom_content; | |
110 | bool eth_valid = false; | |
111 | ||
112 | size = sizeof(*eeprom_content); | |
113 | ||
114 | eeprom_content = calloc(1, size); | |
115 | if (!eeprom_content) | |
116 | return -ENOMEM; | |
117 | ||
118 | debug("%s: I2C EEPROM read pass data at %p\n", __func__, | |
119 | eeprom_content); | |
120 | ||
121 | ret = dm_i2c_read(dev, 0, (uchar *)eeprom_content, size); | |
122 | if (ret) { | |
123 | debug("%s: I2C EEPROM read failed\n", __func__); | |
124 | free(eeprom_content); | |
125 | return ret; | |
126 | } | |
127 | ||
128 | xilinx_eeprom_legacy_cleanup((char *)eeprom_content, size); | |
129 | ||
130 | printf("Xilinx I2C Legacy format at %s:\n", name); | |
131 | printf(" Board name:\t%s\n", eeprom_content->board_name); | |
132 | printf(" Board rev:\t%s\n", eeprom_content->board_revision); | |
133 | printf(" Board SN:\t%s\n", eeprom_content->board_sn); | |
134 | ||
135 | eth_valid = is_valid_ethaddr((const u8 *)eeprom_content->eth_mac); | |
136 | if (eth_valid) | |
137 | printf(" Ethernet mac:\t%pM\n", eeprom_content->eth_mac); | |
138 | ||
139 | /* Terminating \0 chars ensure end of string */ | |
140 | strcpy(desc->name, eeprom_content->board_name); | |
141 | strcpy(desc->revision, eeprom_content->board_revision); | |
142 | strcpy(desc->serial, eeprom_content->board_sn); | |
143 | if (eth_valid) | |
144 | memcpy(desc->mac_addr[0], eeprom_content->eth_mac, ETH_ALEN); | |
145 | ||
146 | desc->header = EEPROM_HEADER_MAGIC; | |
147 | ||
148 | free(eeprom_content); | |
149 | ||
150 | return ret; | |
151 | } | |
152 | ||
153 | static bool xilinx_detect_legacy(u8 *buffer) | |
154 | { | |
155 | int i; | |
156 | char c; | |
157 | ||
158 | for (i = 0; i < XILINX_I2C_DETECTION_BITS; i++) { | |
159 | c = buffer[i]; | |
160 | ||
161 | if (c < '0' || c > '9') | |
162 | return false; | |
163 | } | |
164 | ||
165 | return true; | |
166 | } | |
167 | ||
f149b39c MS |
168 | static int xilinx_read_eeprom_fru(struct udevice *dev, char *name, |
169 | struct xilinx_board_description *desc) | |
170 | { | |
530560b6 | 171 | int i, ret, eeprom_size; |
f149b39c MS |
172 | u8 *fru_content; |
173 | ||
174 | /* FIXME this is shortcut - if eeprom type is wrong it will fail */ | |
175 | eeprom_size = i2c_eeprom_size(dev); | |
176 | ||
177 | fru_content = calloc(1, eeprom_size); | |
178 | if (!fru_content) | |
179 | return -ENOMEM; | |
180 | ||
181 | debug("%s: I2C EEPROM read pass data at %p\n", __func__, | |
182 | fru_content); | |
183 | ||
184 | ret = dm_i2c_read(dev, 0, (uchar *)fru_content, | |
185 | eeprom_size); | |
186 | if (ret) { | |
187 | debug("%s: I2C EEPROM read failed\n", __func__); | |
188 | free(fru_content); | |
189 | return ret; | |
190 | } | |
191 | ||
192 | printf("Xilinx I2C FRU format at %s:\n", name); | |
193 | fru_capture((unsigned long)fru_content); | |
194 | ret = fru_display(0); | |
195 | if (ret) { | |
196 | printf("FRU format decoding failed.\n"); | |
197 | return ret; | |
198 | } | |
199 | ||
200 | if (desc->header == EEPROM_HEADER_MAGIC) { | |
201 | debug("Information already filled\n"); | |
202 | return -EINVAL; | |
203 | } | |
204 | ||
205 | /* It is clear that FRU was captured and structures were filled */ | |
206 | strncpy(desc->manufacturer, (char *)fru_data.brd.manufacturer_name, | |
207 | sizeof(desc->manufacturer)); | |
208 | strncpy(desc->name, (char *)fru_data.brd.product_name, | |
209 | sizeof(desc->name)); | |
530560b6 MS |
210 | for (i = 0; i < sizeof(desc->name); i++) { |
211 | if (desc->name[i] == ' ') | |
212 | desc->name[i] = '\0'; | |
213 | } | |
f149b39c MS |
214 | strncpy(desc->revision, (char *)fru_data.brd.rev, |
215 | sizeof(desc->revision)); | |
216 | strncpy(desc->serial, (char *)fru_data.brd.serial_number, | |
217 | sizeof(desc->serial)); | |
218 | desc->header = EEPROM_HEADER_MAGIC; | |
219 | ||
220 | return 0; | |
221 | } | |
222 | ||
223 | static bool xilinx_detect_fru(u8 *buffer) | |
224 | { | |
225 | u8 checksum = 0; | |
226 | int i; | |
227 | ||
228 | checksum = fru_checksum((u8 *)buffer, sizeof(struct fru_common_hdr)); | |
229 | if (checksum) { | |
230 | debug("%s Common header CRC FAIL\n", __func__); | |
231 | return false; | |
232 | } | |
233 | ||
234 | bool all_zeros = true; | |
235 | /* Checksum over all zeros is also zero that's why detect this case */ | |
236 | for (i = 0; i < sizeof(struct fru_common_hdr); i++) { | |
237 | if (buffer[i] != 0) | |
238 | all_zeros = false; | |
239 | } | |
240 | ||
241 | if (all_zeros) | |
242 | return false; | |
243 | ||
244 | debug("%s Common header CRC PASS\n", __func__); | |
245 | return true; | |
246 | } | |
247 | ||
d61728c8 MS |
248 | static int xilinx_read_eeprom_single(char *name, |
249 | struct xilinx_board_description *desc) | |
250 | { | |
251 | int ret; | |
252 | struct udevice *dev; | |
253 | ofnode eeprom; | |
254 | u8 buffer[XILINX_I2C_DETECTION_BITS]; | |
255 | ||
256 | eeprom = ofnode_get_aliases_node(name); | |
257 | if (!ofnode_valid(eeprom)) | |
258 | return -ENODEV; | |
259 | ||
260 | ret = uclass_get_device_by_ofnode(UCLASS_I2C_EEPROM, eeprom, &dev); | |
261 | if (ret) | |
262 | return ret; | |
263 | ||
264 | ret = dm_i2c_read(dev, 0, buffer, sizeof(buffer)); | |
265 | if (ret) { | |
266 | debug("%s: I2C EEPROM read failed\n", __func__); | |
267 | return ret; | |
268 | } | |
269 | ||
270 | debug("%s: i2c memory detected: %s\n", __func__, name); | |
271 | ||
f149b39c MS |
272 | if (CONFIG_IS_ENABLED(CMD_FRU) && xilinx_detect_fru(buffer)) |
273 | return xilinx_read_eeprom_fru(dev, name, desc); | |
274 | ||
d61728c8 MS |
275 | if (xilinx_detect_legacy(buffer)) |
276 | return xilinx_read_eeprom_legacy(dev, name, desc); | |
277 | ||
278 | return -ENODEV; | |
279 | } | |
280 | ||
281 | __maybe_unused int xilinx_read_eeprom(void) | |
282 | { | |
a03b5947 | 283 | int id, ret; |
d61728c8 | 284 | char name_buf[8]; /* 8 bytes should be enough for nvmem+number */ |
a03b5947 | 285 | struct xilinx_board_description *desc; |
d61728c8 MS |
286 | |
287 | highest_id = dev_read_alias_highest_id("nvmem"); | |
288 | /* No nvmem aliases present */ | |
289 | if (highest_id < 0) | |
290 | return -EINVAL; | |
291 | ||
a03b5947 | 292 | board_info = calloc(1, sizeof(desc) * highest_id); |
d61728c8 MS |
293 | if (!board_info) |
294 | return -ENOMEM; | |
295 | ||
296 | debug("%s: Highest ID %d, board_info %p\n", __func__, | |
297 | highest_id, board_info); | |
298 | ||
299 | for (id = 0; id <= highest_id; id++) { | |
300 | snprintf(name_buf, sizeof(name_buf), "nvmem%d", id); | |
301 | ||
a03b5947 MS |
302 | /* Alloc structure */ |
303 | desc = board_info[id]; | |
304 | if (!desc) { | |
305 | desc = calloc(1, sizeof(*desc)); | |
306 | if (!desc) | |
307 | return -ENOMEM; | |
308 | ||
309 | board_info[id] = desc; | |
310 | } | |
311 | ||
d61728c8 | 312 | /* Ignoring return value for supporting multiple chips */ |
a03b5947 MS |
313 | ret = xilinx_read_eeprom_single(name_buf, desc); |
314 | if (ret) { | |
315 | free(desc); | |
316 | board_info[id] = NULL; | |
317 | } | |
d61728c8 MS |
318 | } |
319 | ||
a03b5947 MS |
320 | /* |
321 | * Consider to clean board_info structure when board/cards are not | |
322 | * detected. | |
323 | */ | |
d61728c8 MS |
324 | |
325 | return 0; | |
326 | } | |
327 | ||
fc274a59 | 328 | #if defined(CONFIG_OF_BOARD) || defined(CONFIG_OF_SEPARATE) |
fec657be IE |
329 | void *board_fdt_blob_setup(void) |
330 | { | |
e2572b55 | 331 | void *fdt_blob; |
453bb77d | 332 | |
a672b987 MS |
333 | if (!IS_ENABLED(CONFIG_SPL_BUILD) && |
334 | !IS_ENABLED(CONFIG_VERSAL_NO_DDR) && | |
fc3c6fd7 | 335 | !IS_ENABLED(CONFIG_ZYNQMP_NO_DDR)) { |
506009fc | 336 | fdt_blob = (void *)CONFIG_XILINX_OF_BOARD_DTB_ADDR; |
fec657be | 337 | |
506009fc MS |
338 | if (fdt_magic(fdt_blob) == FDT_MAGIC) |
339 | return fdt_blob; | |
fec657be | 340 | |
506009fc MS |
341 | debug("DTB is not passed via %p\n", fdt_blob); |
342 | } | |
fc274a59 | 343 | |
506009fc MS |
344 | if (IS_ENABLED(CONFIG_SPL_BUILD)) { |
345 | /* | |
346 | * FDT is at end of BSS unless it is in a different memory | |
347 | * region | |
348 | */ | |
349 | if (IS_ENABLED(CONFIG_SPL_SEPARATE_BSS)) | |
350 | fdt_blob = (ulong *)&_image_binary_end; | |
351 | else | |
352 | fdt_blob = (ulong *)&__bss_end; | |
353 | } else { | |
354 | /* FDT is at end of image */ | |
355 | fdt_blob = (ulong *)&_end; | |
356 | } | |
fc274a59 MS |
357 | |
358 | if (fdt_magic(fdt_blob) == FDT_MAGIC) | |
359 | return fdt_blob; | |
360 | ||
361 | debug("DTB is also not passed via %p\n", fdt_blob); | |
362 | ||
363 | return NULL; | |
fec657be IE |
364 | } |
365 | #endif | |
80fdef12 | 366 | |
7c553ac0 | 367 | #if defined(CONFIG_BOARD_LATE_INIT) |
a03b5947 MS |
368 | static int env_set_by_index(const char *name, int index, char *data) |
369 | { | |
370 | char var[32]; | |
371 | ||
372 | if (!index) | |
373 | sprintf(var, "board_%s", name); | |
374 | else | |
375 | sprintf(var, "card%d_%s", index, name); | |
376 | ||
377 | return env_set(var, data); | |
378 | } | |
379 | ||
80fdef12 MS |
380 | int board_late_init_xilinx(void) |
381 | { | |
ca0f6165 | 382 | u32 ret = 0; |
a03b5947 MS |
383 | int i, id, macid = 0; |
384 | struct xilinx_board_description *desc; | |
2570cc64 | 385 | phys_size_t bootm_size = gd->ram_size; |
d388cedd | 386 | |
25484d90 | 387 | if (!CONFIG_IS_ENABLED(MICROBLAZE)) { |
d388cedd KR |
388 | ulong scriptaddr; |
389 | ||
390 | scriptaddr = env_get_hex("scriptaddr", 0); | |
25484d90 | 391 | ret |= env_set_hex("scriptaddr", gd->ram_base + scriptaddr); |
d388cedd | 392 | } |
2570cc64 | 393 | |
12305821 | 394 | if (CONFIG_IS_ENABLED(ARCH_ZYNQ) || CONFIG_IS_ENABLED(MICROBLAZE)) |
2570cc64 | 395 | bootm_size = min(bootm_size, (phys_size_t)(SZ_512M + SZ_256M)); |
80fdef12 | 396 | |
ca0f6165 MS |
397 | ret |= env_set_hex("script_offset_f", CONFIG_BOOT_SCRIPT_OFFSET); |
398 | ||
399 | ret |= env_set_addr("bootm_low", (void *)gd->ram_base); | |
2570cc64 | 400 | ret |= env_set_addr("bootm_size", (void *)bootm_size); |
ca0f6165 | 401 | |
a03b5947 MS |
402 | for (id = 0; id <= highest_id; id++) { |
403 | desc = board_info[id]; | |
404 | if (desc && desc->header == EEPROM_HEADER_MAGIC) { | |
405 | if (desc->manufacturer[0]) | |
406 | ret |= env_set_by_index("manufacturer", id, | |
407 | desc->manufacturer); | |
408 | if (desc->name[0]) | |
409 | ret |= env_set_by_index("name", id, | |
410 | desc->name); | |
411 | if (desc->revision[0]) | |
412 | ret |= env_set_by_index("rev", id, | |
413 | desc->revision); | |
414 | if (desc->serial[0]) | |
415 | ret |= env_set_by_index("serial", id, | |
416 | desc->serial); | |
d61728c8 MS |
417 | |
418 | if (!CONFIG_IS_ENABLED(NET)) | |
419 | continue; | |
420 | ||
a03b5947 MS |
421 | for (i = 0; i < EEPROM_HDR_NO_OF_MAC_ADDR; i++) { |
422 | if (!desc->mac_addr[i]) | |
423 | continue; | |
424 | ||
425 | if (is_valid_ethaddr((const u8 *)desc->mac_addr[i])) | |
426 | ret |= eth_env_set_enetaddr_by_index("eth", | |
427 | macid++, desc->mac_addr[i]); | |
428 | } | |
d61728c8 MS |
429 | } |
430 | } | |
431 | ||
ca0f6165 MS |
432 | if (ret) |
433 | printf("%s: Saving run time variables FAILED\n", __func__); | |
c8da6513 | 434 | |
80fdef12 MS |
435 | return 0; |
436 | } | |
7c553ac0 | 437 | #endif |
05af4834 MS |
438 | |
439 | int __maybe_unused board_fit_config_name_match(const char *name) | |
440 | { | |
441 | debug("%s: Check %s, default %s\n", __func__, name, DEVICE_TREE); | |
442 | ||
443 | if (!strcmp(name, DEVICE_TREE)) | |
444 | return 0; | |
445 | ||
446 | return -1; | |
447 | } | |
80c0d38a KR |
448 | |
449 | #if defined(CONFIG_DISPLAY_CPUINFO) && !defined(CONFIG_ARCH_ZYNQ) | |
450 | int print_cpuinfo(void) | |
451 | { | |
452 | struct udevice *soc; | |
453 | char name[SOC_MAX_STR_SIZE]; | |
454 | int ret; | |
455 | ||
456 | ret = soc_get(&soc); | |
457 | if (ret) { | |
458 | printf("CPU: UNKNOWN\n"); | |
459 | return 0; | |
460 | } | |
461 | ||
462 | ret = soc_get_family(soc, name, SOC_MAX_STR_SIZE); | |
463 | if (ret) | |
464 | printf("CPU: %s\n", name); | |
465 | ||
466 | ret = soc_get_revision(soc, name, SOC_MAX_STR_SIZE); | |
467 | if (ret) | |
468 | printf("Silicon: %s\n", name); | |
469 | ||
470 | return 0; | |
471 | } | |
472 | #endif |