1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Board specific initialization for IOT2050
4 * Copyright (c) Siemens AG, 2018-2023
5 *
6 * Authors:
7 *   Le Jin <le.jin@siemens.com>
8 *   Jan Kiszka <jan.kiszka@siemens.com>
9 */
10
11#include <common.h>
12#include <bootstage.h>
13#include <dm.h>
14#include <fdt_support.h>
15#include <i2c.h>
16#include <led.h>
17#include <malloc.h>
18#include <mapmem.h>
19#include <net.h>
20#include <phy.h>
21#include <spl.h>
22#include <version.h>
23#include <linux/delay.h>
24#include <asm/arch/hardware.h>
25#include <asm/gpio.h>
26#include <asm/io.h>
27
28#define IOT2050_INFO_MAGIC		0x20502050
29
30struct 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
49DECLARE_GLOBAL_DATA_PTR;
50
51struct gpio_config {
52	const char *gpio_name;
53	const char *label;
54};
55
56enum m2_connector_mode {
57	BKEY_PCIEX2 = 0,
58	BKEY_PCIE_EKEY_PCIE,
59	BKEY_USB30_EKEY_PCIE,
60	CONNECTOR_MODE_INVALID
61};
62
63struct m2_config_pins {
64	int config[4];
65};
66
67struct serdes_mux_control {
68	int ctrl_usb30_pcie0_lane0;
69	int ctrl_pcie1_pcie0;
70	int ctrl_usb30_pcie0_lane1;
71};
72
73struct m2_config_table {
74	struct m2_config_pins config_pins;
75	enum m2_connector_mode mode;
76};
77
78static 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
84static 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
91static 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
104static 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
110static 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
116static enum m2_connector_mode connector_mode;
117
118#if defined(CONFIG_OF_LIBFDT) && defined(CONFIG_OF_BOARD_SETUP)
119static void *connector_overlay;
120static u32 connector_overlay_size;
121#endif
122
123static 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
137static 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
150static bool board_is_advanced(void)
151{
152	struct iot2050_info *info = IOT2050_INFO_DATA;
153
154	return info->magic == IOT2050_INFO_MAGIC &&
155		strstr((char *)info->name, "IOT2050-ADVANCED") != NULL;
156}
157
158static bool board_is_pg1(void)
159{
160	struct iot2050_info *info = IOT2050_INFO_DATA;
161
162	return info->magic == IOT2050_INFO_MAGIC &&
163		(strcmp((char *)info->name, "IOT2050-BASIC") == 0 ||
164		 strcmp((char *)info->name, "IOT2050-ADVANCED") == 0);
165}
166
167static bool board_is_m2(void)
168{
169	struct iot2050_info *info = IOT2050_INFO_DATA;
170
171	return info->magic == IOT2050_INFO_MAGIC &&
172		strcmp((char *)info->name, "IOT2050-ADVANCED-M2") == 0;
173}
174
175static 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
188void 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()) {
221		if (board_is_pg1())
222			fdtfile = "ti/k3-am6548-iot2050-advanced.dtb";
223		else if(board_is_m2())
224			fdtfile = "ti/k3-am6548-iot2050-advanced-m2.dtb";
225		else
226			fdtfile = "ti/k3-am6548-iot2050-advanced-pg2.dtb";
227	} else {
228		if (board_is_pg1())
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
240static 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
278fit_error:
279	pr_err("M.2 device tree overlay %s not available,\n", overlay_path);
280#endif
281}
282
283static 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
335int board_init(void)
336{
337	return 0;
338}
339
340int 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
350int dram_init_banksize(void)
351{
352	dram_init();
353
354	/* Bank 0 declares the memory available in the DDR low region */
355	gd->bd->bi_dram[0].start = CFG_SYS_SDRAM_BASE;
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
366int board_fit_config_name_match(const char *name)
367{
368	struct iot2050_info *info = IOT2050_INFO_DATA;
369	char upper_name[32];
370
371	/* skip the prefix "k3-am65x8-" */
372	name += 10;
373
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
386int do_board_detect(void)
387{
388	return 0;
389}
390
391#ifdef CONFIG_IOT2050_BOOT_SWITCH
392static 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
400	if (dm_gpio_lookup_name("gpio@42110000_25", &gpio) < 0 ||
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
427int 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
432	if (board_is_m2())
433		m2_connector_setup();
434
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)
446static 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
480cleanup:
481	free(fdt_copy);
482	free(overlay_copy);
483	return;
484
485fixup_error:
486	pr_err("Could not apply M.2 device tree overlay\n");
487	goto cleanup;
488}
489
490int ft_board_setup(void *blob, struct bd_info *bd)
491{
492	if (board_is_m2())
493		m2_fdt_fixup(blob);
494
495	return 0;
496}
497#endif
498
499void spl_board_init(void)
500{
501}
502
503#if CONFIG_IS_ENABLED(LED) && CONFIG_IS_ENABLED(SHOW_BOOT_PROGRESS)
504/*
505 * Indicate any error or (accidental?) entering of CLI via the red status LED.
506 */
507void show_boot_progress(int progress)
508{
509	struct udevice *dev;
510	int ret;
511
512	if ((progress < 0 && progress != -BOOTSTAGE_ID_NET_ETH_START) ||
513	    progress == BOOTSTAGE_ID_ENTER_CLI_LOOP) {
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
524