]>
Commit | Line | Data |
---|---|---|
a2db09e2 JK |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Board specific initialization for IOT2050 | |
badaa1f6 | 4 | * Copyright (c) Siemens AG, 2018-2023 |
a2db09e2 JK |
5 | * |
6 | * Authors: | |
7 | * Le Jin <le.jin@siemens.com> | |
8 | * Jan Kiszka <jan.kiszka@siemens.com> | |
9 | */ | |
10 | ||
d678a59d | 11 | #include <common.h> |
a2db09e2 JK |
12 | #include <bootstage.h> |
13 | #include <dm.h> | |
352ed65d | 14 | #include <fdt_support.h> |
a2db09e2 JK |
15 | #include <i2c.h> |
16 | #include <led.h> | |
17 | #include <malloc.h> | |
352ed65d | 18 | #include <mapmem.h> |
a2db09e2 JK |
19 | #include <net.h> |
20 | #include <phy.h> | |
21 | #include <spl.h> | |
22 | #include <version.h> | |
23 | #include <linux/delay.h> | |
a2db09e2 JK |
24 | #include <asm/arch/hardware.h> |
25 | #include <asm/gpio.h> | |
26 | #include <asm/io.h> | |
27 | ||
28 | #define IOT2050_INFO_MAGIC 0x20502050 | |
29 | ||
30 | struct iot2050_info { | |
31 | u32 magic; | |
32 | u16 size; | |
33 | char name[20 + 1]; | |
34 | char serial[16 + 1]; | |
35 | char mlfb[18 + 1]; | |
36 | char uuid[32 + 1]; | |
37 | char a5e[18 + 1]; | |
38 | u8 mac_addr_cnt; | |
39 | u8 mac_addr[8][ARP_HLEN]; | |
40 | char seboot_version[40 + 1]; | |
41 | } __packed; | |
42 | ||
43 | /* | |
44 | * Scratch SRAM (available before DDR RAM) contains extracted EEPROM data. | |
45 | */ | |
46 | #define IOT2050_INFO_DATA ((struct iot2050_info *) \ | |
47 | TI_SRAM_SCRATCH_BOARD_EEPROM_START) | |
48 | ||
49 | DECLARE_GLOBAL_DATA_PTR; | |
50 | ||
352ed65d JK |
51 | struct gpio_config { |
52 | const char *gpio_name; | |
53 | const char *label; | |
54 | }; | |
55 | ||
56 | enum m2_connector_mode { | |
57 | BKEY_PCIEX2 = 0, | |
58 | BKEY_PCIE_EKEY_PCIE, | |
59 | BKEY_USB30_EKEY_PCIE, | |
60 | CONNECTOR_MODE_INVALID | |
61 | }; | |
62 | ||
63 | struct m2_config_pins { | |
64 | int config[4]; | |
65 | }; | |
66 | ||
67 | struct serdes_mux_control { | |
68 | int ctrl_usb30_pcie0_lane0; | |
69 | int ctrl_pcie1_pcie0; | |
70 | int ctrl_usb30_pcie0_lane1; | |
71 | }; | |
72 | ||
73 | struct m2_config_table { | |
74 | struct m2_config_pins config_pins; | |
75 | enum m2_connector_mode mode; | |
76 | }; | |
77 | ||
78 | static const struct gpio_config serdes_mux_ctl_pin_info[] = { | |
79 | {"gpio@600000_88", "CTRL_USB30_PCIE0_LANE0"}, | |
80 | {"gpio@600000_82", "CTRL_PCIE1_PCIE0"}, | |
81 | {"gpio@600000_89", "CTRL_USB30_PCIE0_LANE1"}, | |
82 | }; | |
83 | ||
84 | static const struct gpio_config m2_bkey_cfg_pin_info[] = { | |
85 | {"gpio@601000_18", "KEY_CONFIG_0"}, | |
86 | {"gpio@601000_19", "KEY_CONFIG_1"}, | |
87 | {"gpio@601000_88", "KEY_CONFIG_2"}, | |
88 | {"gpio@601000_89", "KEY_CONFIG_3"}, | |
89 | }; | |
90 | ||
91 | static const struct m2_config_table m2_config_table[] = { | |
92 | {{{0, 1, 0, 0}}, BKEY_PCIEX2}, | |
93 | {{{0, 0, 1, 0}}, BKEY_PCIE_EKEY_PCIE}, | |
94 | {{{0, 1, 1, 0}}, BKEY_PCIE_EKEY_PCIE}, | |
95 | {{{1, 0, 0, 1}}, BKEY_PCIE_EKEY_PCIE}, | |
96 | {{{1, 1, 0, 1}}, BKEY_PCIE_EKEY_PCIE}, | |
97 | {{{0, 0, 0, 1}}, BKEY_USB30_EKEY_PCIE}, | |
98 | {{{0, 1, 0, 1}}, BKEY_USB30_EKEY_PCIE}, | |
99 | {{{0, 0, 1, 1}}, BKEY_USB30_EKEY_PCIE}, | |
100 | {{{0, 1, 1, 1}}, BKEY_USB30_EKEY_PCIE}, | |
101 | {{{1, 0, 1, 1}}, BKEY_USB30_EKEY_PCIE}, | |
102 | }; | |
103 | ||
104 | static const struct serdes_mux_control serdes_mux_ctrl[] = { | |
105 | [BKEY_PCIEX2] = {0, 0, 1}, | |
106 | [BKEY_PCIE_EKEY_PCIE] = {0, 1, 0}, | |
107 | [BKEY_USB30_EKEY_PCIE] = {1, 1, 0}, | |
108 | }; | |
109 | ||
110 | static const char *m2_connector_mode_name[] = { | |
111 | [BKEY_PCIEX2] = "PCIe x2 (key B)", | |
112 | [BKEY_PCIE_EKEY_PCIE] = "PCIe (key B) / PCIe (key E)", | |
113 | [BKEY_USB30_EKEY_PCIE] = "USB 3.0 (key B) / PCIe (key E)", | |
114 | }; | |
115 | ||
116 | static enum m2_connector_mode connector_mode; | |
117 | ||
118 | #if defined(CONFIG_OF_LIBFDT) && defined(CONFIG_OF_BOARD_SETUP) | |
119 | static void *connector_overlay; | |
120 | static u32 connector_overlay_size; | |
121 | #endif | |
122 | ||
123 | static int get_pinvalue(const char *gpio_name, const char *label) | |
124 | { | |
125 | struct gpio_desc gpio; | |
126 | ||
127 | if (dm_gpio_lookup_name(gpio_name, &gpio) < 0 || | |
128 | dm_gpio_request(&gpio, label) < 0 || | |
129 | dm_gpio_set_dir_flags(&gpio, GPIOD_IS_IN) < 0) { | |
130 | pr_err("Cannot get pin %s for M.2 configuration\n", gpio_name); | |
131 | return 0; | |
132 | } | |
133 | ||
134 | return dm_gpio_get_value(&gpio); | |
135 | } | |
136 | ||
137 | static void set_pinvalue(const char *gpio_name, const char *label, int value) | |
138 | { | |
139 | struct gpio_desc gpio; | |
140 | ||
141 | if (dm_gpio_lookup_name(gpio_name, &gpio) < 0 || | |
142 | dm_gpio_request(&gpio, label) < 0 || | |
143 | dm_gpio_set_dir_flags(&gpio, GPIOD_IS_OUT) < 0) { | |
144 | pr_err("Cannot set pin %s for M.2 configuration\n", gpio_name); | |
145 | return; | |
146 | } | |
147 | dm_gpio_set_value(&gpio, value); | |
148 | } | |
149 | ||
badaa1f6 | 150 | static bool board_is_advanced(void) |
352ed65d JK |
151 | { |
152 | struct iot2050_info *info = IOT2050_INFO_DATA; | |
153 | ||
badaa1f6 JK |
154 | return info->magic == IOT2050_INFO_MAGIC && |
155 | strstr((char *)info->name, "IOT2050-ADVANCED") != NULL; | |
352ed65d JK |
156 | } |
157 | ||
33e8020f | 158 | static bool board_is_pg1(void) |
a2db09e2 JK |
159 | { |
160 | struct iot2050_info *info = IOT2050_INFO_DATA; | |
161 | ||
162 | return info->magic == IOT2050_INFO_MAGIC && | |
33e8020f JK |
163 | (strcmp((char *)info->name, "IOT2050-BASIC") == 0 || |
164 | strcmp((char *)info->name, "IOT2050-ADVANCED") == 0); | |
badaa1f6 JK |
165 | } |
166 | ||
167 | static bool board_is_m2(void) | |
168 | { | |
169 | struct iot2050_info *info = IOT2050_INFO_DATA; | |
170 | ||
33e8020f | 171 | return info->magic == IOT2050_INFO_MAGIC && |
badaa1f6 | 172 | strcmp((char *)info->name, "IOT2050-ADVANCED-M2") == 0; |
a2db09e2 JK |
173 | } |
174 | ||
a2db09e2 JK |
175 | static void remove_mmc1_target(void) |
176 | { | |
177 | char *boot_targets = strdup(env_get("boot_targets")); | |
178 | char *mmc1 = strstr(boot_targets, "mmc1"); | |
179 | ||
180 | if (mmc1) { | |
181 | memmove(mmc1, mmc1 + 4, strlen(mmc1 + 4) + 1); | |
182 | env_set("boot_targets", boot_targets); | |
183 | } | |
184 | ||
185 | free(boot_targets); | |
186 | } | |
187 | ||
188 | void set_board_info_env(void) | |
189 | { | |
190 | struct iot2050_info *info = IOT2050_INFO_DATA; | |
191 | u8 __maybe_unused mac_cnt; | |
192 | const char *fdtfile; | |
193 | ||
194 | if (info->magic != IOT2050_INFO_MAGIC) { | |
195 | pr_err("IOT2050: Board info parsing error!\n"); | |
196 | return; | |
197 | } | |
198 | ||
199 | if (env_get("board_uuid")) | |
200 | return; | |
201 | ||
202 | env_set("board_name", info->name); | |
203 | env_set("board_serial", info->serial); | |
204 | env_set("mlfb", info->mlfb); | |
205 | env_set("board_uuid", info->uuid); | |
206 | env_set("board_a5e", info->a5e); | |
207 | env_set("fw_version", PLAIN_VERSION); | |
208 | env_set("seboot_version", info->seboot_version); | |
209 | ||
210 | if (IS_ENABLED(CONFIG_NET)) { | |
211 | /* set MAC addresses to ensure forwarding to the OS */ | |
212 | for (mac_cnt = 0; mac_cnt < info->mac_addr_cnt; mac_cnt++) { | |
213 | if (is_valid_ethaddr(info->mac_addr[mac_cnt])) | |
214 | eth_env_set_enetaddr_by_index("eth", | |
215 | mac_cnt + 1, | |
216 | info->mac_addr[mac_cnt]); | |
217 | } | |
218 | } | |
219 | ||
220 | if (board_is_advanced()) { | |
33e8020f | 221 | if (board_is_pg1()) |
a2db09e2 | 222 | fdtfile = "ti/k3-am6548-iot2050-advanced.dtb"; |
352ed65d JK |
223 | else if(board_is_m2()) |
224 | fdtfile = "ti/k3-am6548-iot2050-advanced-m2.dtb"; | |
a2db09e2 JK |
225 | else |
226 | fdtfile = "ti/k3-am6548-iot2050-advanced-pg2.dtb"; | |
227 | } else { | |
33e8020f | 228 | if (board_is_pg1()) |
a2db09e2 JK |
229 | fdtfile = "ti/k3-am6528-iot2050-basic.dtb"; |
230 | else | |
231 | fdtfile = "ti/k3-am6528-iot2050-basic-pg2.dtb"; | |
232 | /* remove the unavailable eMMC (mmc1) from the list */ | |
233 | remove_mmc1_target(); | |
234 | } | |
235 | env_set("fdtfile", fdtfile); | |
236 | ||
237 | env_save(); | |
238 | } | |
239 | ||
352ed65d JK |
240 | static void m2_overlay_prepare(void) |
241 | { | |
242 | #if defined(CONFIG_OF_LIBFDT) && defined(CONFIG_OF_BOARD_SETUP) | |
243 | const char *overlay_path; | |
244 | void *overlay; | |
245 | u64 loadaddr; | |
246 | ofnode node; | |
247 | int ret; | |
248 | ||
249 | if (connector_mode == BKEY_PCIEX2) | |
250 | return; | |
251 | ||
252 | if (connector_mode == BKEY_PCIE_EKEY_PCIE) | |
253 | overlay_path = "/fit-images/bkey-ekey-pcie-overlay"; | |
254 | else | |
255 | overlay_path = "/fit-images/bkey-usb3-overlay"; | |
256 | ||
257 | node = ofnode_path(overlay_path); | |
258 | if (!ofnode_valid(node)) | |
259 | goto fit_error; | |
260 | ||
261 | ret = ofnode_read_u64(node, "load", &loadaddr); | |
262 | if (ret) | |
263 | goto fit_error; | |
264 | ||
265 | ret = ofnode_read_u32(node, "size", &connector_overlay_size); | |
266 | if (ret) | |
267 | goto fit_error; | |
268 | ||
269 | overlay = map_sysmem(loadaddr, connector_overlay_size); | |
270 | ||
271 | connector_overlay = malloc(connector_overlay_size); | |
272 | if (!connector_overlay) | |
273 | goto fit_error; | |
274 | ||
275 | memcpy(connector_overlay, overlay, connector_overlay_size); | |
276 | return; | |
277 | ||
278 | fit_error: | |
279 | pr_err("M.2 device tree overlay %s not available,\n", overlay_path); | |
280 | #endif | |
281 | } | |
282 | ||
283 | static void m2_connector_setup(void) | |
284 | { | |
285 | ulong m2_manual_config = env_get_ulong("m2_manual_config", 10, | |
286 | CONNECTOR_MODE_INVALID); | |
287 | const char *mode_info = ""; | |
288 | struct m2_config_pins config_pins; | |
289 | unsigned int n; | |
290 | ||
291 | /* enable M.2 connector power */ | |
292 | set_pinvalue("gpio@601000_17", "P3V3_M2_EN", 1); | |
293 | udelay(4 * 100); | |
294 | ||
295 | if (m2_manual_config < CONNECTOR_MODE_INVALID) { | |
296 | mode_info = " [manual mode]"; | |
297 | connector_mode = m2_manual_config; | |
298 | } else { /* auto detection */ | |
299 | for (n = 0; n < ARRAY_SIZE(config_pins.config); n++) | |
300 | config_pins.config[n] = | |
301 | get_pinvalue(m2_bkey_cfg_pin_info[n].gpio_name, | |
302 | m2_bkey_cfg_pin_info[n].label); | |
303 | connector_mode = CONNECTOR_MODE_INVALID; | |
304 | for (n = 0; n < ARRAY_SIZE(m2_config_table); n++) { | |
305 | if (!memcmp(config_pins.config, | |
306 | m2_config_table[n].config_pins.config, | |
307 | sizeof(config_pins.config))) { | |
308 | connector_mode = m2_config_table[n].mode; | |
309 | break; | |
310 | } | |
311 | } | |
312 | if (connector_mode == CONNECTOR_MODE_INVALID) { | |
313 | mode_info = " [fallback, card unknown/unsupported]"; | |
314 | connector_mode = BKEY_USB30_EKEY_PCIE; | |
315 | } | |
316 | } | |
317 | ||
318 | printf("M.2: %s%s\n", m2_connector_mode_name[connector_mode], | |
319 | mode_info); | |
320 | ||
321 | /* configure serdes mux */ | |
322 | set_pinvalue(serdes_mux_ctl_pin_info[0].gpio_name, | |
323 | serdes_mux_ctl_pin_info[0].label, | |
324 | serdes_mux_ctrl[connector_mode].ctrl_usb30_pcie0_lane0); | |
325 | set_pinvalue(serdes_mux_ctl_pin_info[1].gpio_name, | |
326 | serdes_mux_ctl_pin_info[1].label, | |
327 | serdes_mux_ctrl[connector_mode].ctrl_pcie1_pcie0); | |
328 | set_pinvalue(serdes_mux_ctl_pin_info[2].gpio_name, | |
329 | serdes_mux_ctl_pin_info[2].label, | |
330 | serdes_mux_ctrl[connector_mode].ctrl_usb30_pcie0_lane1); | |
331 | ||
332 | m2_overlay_prepare(); | |
333 | } | |
334 | ||
a2db09e2 JK |
335 | int board_init(void) |
336 | { | |
337 | return 0; | |
338 | } | |
339 | ||
340 | int dram_init(void) | |
341 | { | |
342 | if (board_is_advanced()) | |
343 | gd->ram_size = SZ_2G; | |
344 | else | |
345 | gd->ram_size = SZ_1G; | |
346 | ||
347 | return 0; | |
348 | } | |
349 | ||
350 | int dram_init_banksize(void) | |
351 | { | |
352 | dram_init(); | |
353 | ||
354 | /* Bank 0 declares the memory available in the DDR low region */ | |
aa6e94de | 355 | gd->bd->bi_dram[0].start = CFG_SYS_SDRAM_BASE; |
a2db09e2 JK |
356 | gd->bd->bi_dram[0].size = gd->ram_size; |
357 | ||
358 | /* Bank 1 declares the memory available in the DDR high region */ | |
359 | gd->bd->bi_dram[1].start = 0; | |
360 | gd->bd->bi_dram[1].size = 0; | |
361 | ||
362 | return 0; | |
363 | } | |
364 | ||
365 | #ifdef CONFIG_SPL_LOAD_FIT | |
366 | int board_fit_config_name_match(const char *name) | |
367 | { | |
368 | struct iot2050_info *info = IOT2050_INFO_DATA; | |
369 | char upper_name[32]; | |
370 | ||
ffbd5b29 SB |
371 | /* skip the prefix "k3-am65x8-" */ |
372 | name += 10; | |
373 | ||
a2db09e2 JK |
374 | if (info->magic != IOT2050_INFO_MAGIC || |
375 | strlen(name) >= sizeof(upper_name)) | |
376 | return -1; | |
377 | ||
378 | str_to_upper(name, upper_name, sizeof(upper_name)); | |
379 | if (!strcmp(upper_name, (char *)info->name)) | |
380 | return 0; | |
381 | ||
382 | return -1; | |
383 | } | |
384 | #endif | |
385 | ||
386 | int do_board_detect(void) | |
387 | { | |
388 | return 0; | |
389 | } | |
390 | ||
391 | #ifdef CONFIG_IOT2050_BOOT_SWITCH | |
392 | static bool user_button_pressed(void) | |
393 | { | |
394 | struct udevice *red_led = NULL; | |
395 | unsigned long count = 0; | |
396 | struct gpio_desc gpio; | |
397 | ||
398 | memset(&gpio, 0, sizeof(gpio)); | |
399 | ||
f750769a | 400 | if (dm_gpio_lookup_name("gpio@42110000_25", &gpio) < 0 || |
a2db09e2 JK |
401 | dm_gpio_request(&gpio, "USER button") < 0 || |
402 | dm_gpio_set_dir_flags(&gpio, GPIOD_IS_IN) < 0) | |
403 | return false; | |
404 | ||
405 | if (dm_gpio_get_value(&gpio) == 1) | |
406 | return false; | |
407 | ||
408 | printf("USER button pressed - booting from external media only\n"); | |
409 | ||
410 | led_get_by_label("status-led-red", &red_led); | |
411 | ||
412 | if (red_led) | |
413 | led_set_state(red_led, LEDST_ON); | |
414 | ||
415 | while (dm_gpio_get_value(&gpio) == 0 && count++ < 10000) | |
416 | mdelay(1); | |
417 | ||
418 | if (red_led) | |
419 | led_set_state(red_led, LEDST_OFF); | |
420 | ||
421 | return true; | |
422 | } | |
423 | #endif | |
424 | ||
425 | #define SERDES0_LANE_SELECT 0x00104080 | |
426 | ||
427 | int board_late_init(void) | |
428 | { | |
429 | /* change CTRL_MMR register to let serdes0 not output USB3.0 signals. */ | |
430 | writel(0x3, SERDES0_LANE_SELECT); | |
431 | ||
352ed65d JK |
432 | if (board_is_m2()) |
433 | m2_connector_setup(); | |
434 | ||
a2db09e2 JK |
435 | set_board_info_env(); |
436 | ||
437 | /* remove the eMMC if requested via button */ | |
438 | if (IS_ENABLED(CONFIG_IOT2050_BOOT_SWITCH) && board_is_advanced() && | |
439 | user_button_pressed()) | |
440 | remove_mmc1_target(); | |
441 | ||
442 | return 0; | |
443 | } | |
444 | ||
445 | #if defined(CONFIG_OF_LIBFDT) && defined(CONFIG_OF_BOARD_SETUP) | |
352ed65d JK |
446 | static void m2_fdt_fixup(void *blob) |
447 | { | |
448 | void *overlay_copy = NULL; | |
449 | void *fdt_copy = NULL; | |
450 | u32 fdt_size; | |
451 | int err; | |
452 | ||
453 | if (!connector_overlay) | |
454 | return; | |
455 | ||
456 | /* | |
457 | * We need to work with temporary copies here because fdt_overlay_apply | |
458 | * is destructive to the overlay and also to the target blob, even if | |
459 | * application fails. | |
460 | */ | |
461 | fdt_size = fdt_totalsize(blob); | |
462 | fdt_copy = malloc(fdt_size); | |
463 | if (!fdt_copy) | |
464 | goto fixup_error; | |
465 | ||
466 | memcpy(fdt_copy, blob, fdt_size); | |
467 | ||
468 | overlay_copy = malloc(connector_overlay_size); | |
469 | if (!overlay_copy) | |
470 | goto fixup_error; | |
471 | ||
472 | memcpy(overlay_copy, connector_overlay, connector_overlay_size); | |
473 | ||
474 | err = fdt_overlay_apply_verbose(fdt_copy, overlay_copy); | |
475 | if (err) | |
476 | goto fixup_error; | |
477 | ||
478 | memcpy(blob, fdt_copy, fdt_size); | |
479 | ||
480 | cleanup: | |
481 | free(fdt_copy); | |
482 | free(overlay_copy); | |
483 | return; | |
484 | ||
485 | fixup_error: | |
486 | pr_err("Could not apply M.2 device tree overlay\n"); | |
487 | goto cleanup; | |
488 | } | |
489 | ||
a2db09e2 JK |
490 | int ft_board_setup(void *blob, struct bd_info *bd) |
491 | { | |
352ed65d JK |
492 | if (board_is_m2()) |
493 | m2_fdt_fixup(blob); | |
494 | ||
30e96a24 | 495 | return 0; |
a2db09e2 JK |
496 | } |
497 | #endif | |
498 | ||
499 | void spl_board_init(void) | |
500 | { | |
501 | } | |
502 | ||
b55881dd | 503 | #if CONFIG_IS_ENABLED(LED) && CONFIG_IS_ENABLED(SHOW_BOOT_PROGRESS) |
a2db09e2 JK |
504 | /* |
505 | * Indicate any error or (accidental?) entering of CLI via the red status LED. | |
506 | */ | |
507 | void show_boot_progress(int progress) | |
508 | { | |
509 | struct udevice *dev; | |
510 | int ret; | |
511 | ||
f01081d8 JK |
512 | if ((progress < 0 && progress != -BOOTSTAGE_ID_NET_ETH_START) || |
513 | progress == BOOTSTAGE_ID_ENTER_CLI_LOOP) { | |
a2db09e2 JK |
514 | ret = led_get_by_label("status-led-green", &dev); |
515 | if (ret == 0) | |
516 | led_set_state(dev, LEDST_OFF); | |
517 | ||
518 | ret = led_get_by_label("status-led-red", &dev); | |
519 | if (ret == 0) | |
520 | led_set_state(dev, LEDST_ON); | |
521 | } | |
522 | } | |
523 | #endif |