xref: /rk3399_ARM-atf/plat/xilinx/common/plat_console.c (revision 4557ab69fe371137d44f8a0ee6bb2129886ab6cd)
1 /*
2  * Copyright (c) 2023, Advanced Micro Devices, Inc. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <assert.h>
8 #include <errno.h>
9 #include <stdlib.h>
10 #include <string.h>
11 
12 #include <common/debug.h>
13 #include <common/fdt_fixup.h>
14 #include <common/fdt_wrappers.h>
15 #include <drivers/arm/dcc.h>
16 #include <drivers/arm/pl011.h>
17 #include <drivers/cadence/cdns_uart.h>
18 #include <drivers/console.h>
19 #include <libfdt.h>
20 #include <plat_console.h>
21 #include <plat_fdt.h>
22 
23 #include <platform_def.h>
24 #include <plat_private.h>
25 
26 static console_t boot_console;
27 
28 #if (defined(XILINX_OF_BOARD_DTB_ADDR) && !IS_TFA_IN_OCM(BL31_BASE)) || \
29 	defined(CONSOLE_RUNTIME)
30 /**
31  * register_console() - Registers the runtime uart with console list.
32  * @uart_base: UART base address
33  * @clock: UART clock.
34  * @baud_rate: UART buad rate
35  * @console: Pointer to the console information structure.
36  * @flags: console flags.
37  */
38 static void register_console(uintptr_t uart_base, uint32_t clock,
39 		uint32_t baud_rate, console_t *console,
40 		uint32_t flags, uint8_t console_type)
41 {
42 	int32_t rc = 0;
43 
44 	if (console_type == PLAT_XLNX_CONSOLE_TYPE_DEFAULT) {
45 #if defined(PLAT_zynqmp)
46 		rc = console_cdns_register(uart_base,
47 				clock,
48 				baud_rate,
49 				console);
50 #elif defined(PLAT_versal) || defined(PLAT_versal_net) || defined(PLAT_versal2)
51 		rc = console_pl011_register(uart_base,
52 				clock,
53 				baud_rate,
54 				console);
55 #endif
56 	} else if (console_type == PLAT_XLNX_CONSOLE_TYPE_DEBUG) {
57 		rc = console_dcc_register(console);
58 	} else {
59 		INFO("Invalid console type\n");
60 	}
61 
62 	if (rc == 0) {
63 		panic();
64 	}
65 
66 	console_set_scope(console, flags);
67 }
68 #endif
69 
70 #if (defined(XILINX_OF_BOARD_DTB_ADDR) && !IS_TFA_IN_OCM(BL31_BASE))
71 
72 static console_t dt_console;
73 /**
74  * get_baudrate() - Get the baudrate form DTB.
75  * @dtb: Address of the Device Tree Blob (DTB).
76  *
77  * Return: On success returns the baudrate; on failure returns an error.
78  */
79 static int32_t get_baudrate(void *dtb)
80 {
81 	int node;
82 	int32_t ret = 0;
83 	const char *prop, *path;
84 	char *end;
85 	int32_t baud_rate = 0;
86 
87 	node = fdt_path_offset(dtb, "/secure-chosen");
88 	if (node < 0) {
89 		node = fdt_path_offset(dtb, "/chosen");
90 		if (node < 0) {
91 			ret = -FDT_ERR_NOTFOUND;
92 			goto error;
93 		}
94 	}
95 
96 	prop = fdt_getprop(dtb, node, "stdout-path", NULL);
97 	if (prop == NULL) {
98 		ret = -FDT_ERR_NOTFOUND;
99 		goto error;
100 	}
101 
102 	/* Parse string serial0:115200n8 */
103 	path = strchr(prop, ':');
104 	if (!path) {
105 		ret = -FDT_ERR_NOTFOUND;
106 		goto error;
107 	} else {
108 
109 		baud_rate = strtoul(path + 1, &end, 10);
110 		if (baud_rate == 0 && end == path) {
111 			ERROR("Conversion error occurred: %d\n", baud_rate);
112 			ret = -FDT_ERR_NOTFOUND;
113 			goto error;
114 		}
115 		ret = baud_rate;
116 	}
117 
118 error:
119 	return ret;
120 }
121 
122 /**
123  * get_node_status() - Get the DTB node status.
124  * @dtb: Address of the Device Tree Blob (DTB).
125  * @node: Node address in the device tree.
126  *
127  * Return: On success, it returns 1; on failure, it returns an 0.
128  */
129 static uint32_t get_node_status(void *dtb, int node)
130 {
131 	const char *status_cell;
132 	uint32_t status = 0;
133 
134 	status_cell = fdt_getprop(dtb, node, "status", NULL);
135 	if (!status_cell || strcmp(status_cell, "okay") == 0) {
136 		status = 1;
137 	} else {
138 		status = 0;
139 	}
140 
141 	return status;
142 }
143 
144 /**
145  * fdt_add_uart_info() - Add DTB information to a UART structure.
146  * @info: Pointer to the UART information structure.
147  * @node: Node address in the device tree.
148  * @dtb: Address of the Device Tree Blob(DTB).
149  *
150  * Return: On success, it returns 1; on failure, it returns an 0.
151  */
152 static uint32_t fdt_add_uart_info(dt_uart_info_t *info, int node, void *dtb)
153 {
154 	uintptr_t base_addr;
155 	const char *com;
156 	int32_t ret = 0;
157 
158 	com = fdt_getprop(dtb, node, "compatible", NULL);
159 	if (com != NULL) {
160 		strlcpy(info->compatible, com, sizeof(info->compatible));
161 	} else {
162 		ERROR("Compatible property not found in DTB node\n");
163 		ret  = -FDT_ERR_NOTFOUND;
164 		goto error;
165 	}
166 
167 	info->status = get_node_status(dtb, node);
168 	if (info->status == 0) {
169 		ERROR("Uart node is disabled in DTB\n");
170 		ret  = -FDT_ERR_NOTFOUND;
171 		goto error;
172 	}
173 
174 	if (strncmp(info->compatible, DT_UART_DCC_COMPAT, strlen(DT_UART_DCC_COMPAT)) != 0) {
175 		ret = fdt_get_reg_props_by_index(dtb, node, 0, &base_addr, NULL);
176 		if (ret >= 0) {
177 			info->base = base_addr;
178 		} else {
179 			ERROR("Failed to retrieve base address. Error code: %d\n", ret);
180 			ret  = -FDT_ERR_NOTFOUND;
181 			goto error;
182 		}
183 
184 		info->baud_rate = get_baudrate(dtb);
185 	}
186 
187 error:
188 	return ret;
189 }
190 
191 /**
192  * fdt_get_uart_info() - Get the uart information form DTB.
193  * @info: Pointer to the UART information structure.
194  *
195  * Return: On success, it returns 0; on failure, it returns an error+reason.
196  */
197 static int fdt_get_uart_info(dt_uart_info_t *info)
198 {
199 	int node = 0, ret = 0;
200 	void *dtb = (void *)XILINX_OF_BOARD_DTB_ADDR;
201 
202 	ret = is_valid_dtb(dtb);
203 	if (ret < 0) {
204 		ERROR("Invalid Device Tree at %p: error %d\n", dtb, ret);
205 		ret  = -FDT_ERR_NOTFOUND;
206 		goto error;
207 	}
208 
209 	node = fdt_get_stdout_node_offset(dtb);
210 	if (node < 0) {
211 		ERROR("DT get stdout node failed : %d\n", node);
212 		ret  = -FDT_ERR_NOTFOUND;
213 		goto error;
214 	}
215 
216 	ret = fdt_add_uart_info(info, node, dtb);
217 	if (ret < 0) {
218 		ERROR("Failed to add DT UART info: %d\n", ret);
219 		ret  = -FDT_ERR_NOTFOUND;
220 		goto error;
221 	}
222 
223 error:
224 	return ret;
225 }
226 
227 /**
228  * check_fdt_uart_info() - Check early uart info with DTB uart info.
229  * @info: Pointer to the UART information structure.
230  *
231  * Return: On success, it returns 0; on failure, it returns an error+reason.
232  */
233 static int32_t check_fdt_uart_info(dt_uart_info_t *info)
234 {
235 	int32_t ret = 0;
236 
237 	if (info->status == 0) {
238 		ret = -ENODEV;
239 		goto error;
240 	}
241 
242 	if ((info->base == boot_console.base) &&
243 	   (info->baud_rate == UART_BAUDRATE) && !CONSOLE_IS(dcc)) {
244 		ret = -ENODEV;
245 		goto error;
246 	}
247 
248 error:
249 	return ret;
250 }
251 
252 /**
253  * console_end() - Unregister the console_t instance form the console list.
254  * @console: Pointer to the console information structure.
255  */
256 static void console_end(console_t *console)
257 {
258 	if (CONSOLE_IS(dcc)) {
259 		console_dcc_unregister(console);
260 	} else {
261 		if (console != NULL) {
262 			console_flush();
263 			(void)console_unregister(console);
264 		}
265 	}
266 }
267 
268 /**
269  * parse_uart_info() - Parse UART information from Device Tree Blob.
270  * @uart_info: Pointer to the UART information structure.
271  *
272  * Return: On success, it returns 0; on failure, it returns an error+reason;
273  */
274 static int32_t parse_uart_info(dt_uart_info_t *uart_info)
275 {
276 	int32_t rc = fdt_get_uart_info(uart_info);
277 
278 	if (rc < 0) {
279 		rc = -FDT_ERR_NOTFOUND;
280 	}
281 	return rc;
282 }
283 
284 /**
285  * handle_dt_console() - Registers the DT console
286  * @uart_info: Pointer to the UART information structure.
287  * @console: Pointer to the console information structure.
288  * @clock: UART clock.
289  * @end_console: Pointer to the console information structure.
290  */
291 static void handle_dt_console(dt_uart_info_t *uart_info, console_t *console,
292 		uint32_t clock, console_t *end_console)
293 {
294 	register_console(uart_info->base, clock, uart_info->baud_rate,
295 			console,  CONSOLE_FLAG_BOOT |
296 			CONSOLE_FLAG_RUNTIME | CONSOLE_FLAG_CRASH,
297 			PLAT_XLNX_CONSOLE_TYPE_DEFAULT);
298 	console_end(end_console);
299 	INFO("DTB console setup\n");
300 }
301 
302 
303 /**
304  * handle_dcc_console() - Registers the DCC console
305  * @console: Pointer to the console information structure.
306  */
307 static void handle_dcc_console(console_t *console)
308 {
309 	int32_t rc = console_dcc_register(console);
310 
311 	if (rc == 0) {
312 		panic();
313 	}
314 	console_end(console);
315 }
316 
317 /**
318  * dt_console_init() - Initializes the DT console information.
319  * @uart_info: Pointer to the UART information structure.
320  * @console: Pointer to the console information structure.
321  * @clock: UART clock.
322  *
323  * Return: On success, it returns 0; on failure, it returns an error+reason;
324  */
325 static int32_t dt_console_init(dt_uart_info_t *uart_info,
326 			  console_t *console,
327 			  uint32_t clock)
328 {
329 	int32_t rc = 0;
330 
331 	/* Parse UART information from Device Tree Blob (DTB) */
332 	rc = parse_uart_info(uart_info);
333 	if (rc < 0) {
334 		goto error;
335 	}
336 
337 	rc = check_fdt_uart_info(uart_info);
338 	if (rc < 0) {
339 		goto error;
340 	}
341 
342 	rc = check_fdt_uart_info(uart_info);
343 	if (rc < 0) {
344 		goto error;
345 	}
346 
347 	if (strncmp(uart_info->compatible, DT_UART_COMPAT,
348 		    strlen(DT_UART_COMPAT)) == 0) {
349 		handle_dt_console(uart_info, &dt_console, clock, console);
350 	} else if (strncmp(uart_info->compatible, DT_UART_DCC_COMPAT,
351 			strlen(DT_UART_DCC_COMPAT)) == 0) {
352 		handle_dcc_console(console);
353 	} else {
354 		WARN("BL31: No console device found in DT.\n");
355 	}
356 
357 error:
358 	return rc;
359 }
360 #endif
361 
362 #if defined(CONSOLE_RUNTIME)
363 void console_runtime_init(void)
364 {
365 	uint32_t uart_clk = get_uart_clk();
366 	static console_t runtime_console;
367 	uintptr_t rt_uart_base = 0;
368 	uint32_t buad_rate = 0;
369 	static dt_uart_info_t dt_info = {0};
370 
371 #if (defined(XILINX_OF_BOARD_DTB_ADDR) && !IS_TFA_IN_OCM(BL31_BASE))
372 	console_t *console = &dt_console;
373 #else
374 	console_t *console = &boot_console;
375 #endif
376 
377 #if (RT_CONSOLE_IS(dtb) && defined(XILINX_OF_BOARD_DTB_ADDR)) && \
378 	(!defined(PLAT_zynqmp) || (defined(PLAT_zynqmp) && \
379 				   !IS_TFA_IN_OCM(BL31_BASE)))
380 	uint32_t rc = parse_uart_info(&dt_info);
381 
382 	if (rc < 0) {
383 		goto error;
384 	} else {
385 		rt_uart_base = dt_info.base;
386 		buad_rate = dt_info.baud_rate;
387 	}
388 #elif defined(PLAT_zynqmp)
389 	if (RT_CONSOLE_IS(cadence) || (RT_CONSOLE_IS(cadence1))) {
390 		rt_uart_base = (uintptr_t)RT_UART_BASE;
391 		buad_rate = (uint32_t)UART_BAUDRATE;
392 	}
393 #else
394 	if (RT_CONSOLE_IS(pl011) || (RT_CONSOLE_IS(pl011_1))) {
395 		rt_uart_base = (uintptr_t)RT_UART_BASE;
396 		buad_rate = (uint32_t)UART_BAUDRATE;
397 	}
398 #endif
399 	/*skip console registration if runtime and boot console are same */
400 	if (console->base != rt_uart_base) {
401 		/* Remove runtime flag from boot console */
402 		console_set_scope(console, CONSOLE_FLAG_BOOT | CONSOLE_FLAG_CRASH);
403 
404 		register_console(rt_uart_base, uart_clk, buad_rate, &runtime_console,
405 				 CONSOLE_FLAG_RUNTIME, PLAT_XLNX_CONSOLE_TYPE_DEFAULT);
406 		INFO("Successfully initialized runtime console\n");
407 	}
408 
409 #if (RT_CONSOLE_IS(dtb) && defined(XILINX_OF_BOARD_DTB_ADDR)) && \
410 	(!defined(PLAT_zynqmp) || (defined(PLAT_zynqmp) && \
411 				   !IS_TFA_IN_OCM(BL31_BASE)))
412 error:
413 	if (rc < 0) {
414 		ERROR("Failed to parse uart info in runtime console\n");
415 	}
416 #endif
417 }
418 #endif
419 
420 void setup_console(void)
421 {
422 	int32_t rc;
423 	uint32_t uart_clk = get_uart_clk();
424 
425 #if defined(PLAT_zynqmp)
426 	if (CONSOLE_IS(cadence) || (CONSOLE_IS(cadence1))) {
427 		rc = console_cdns_register(UART_BASE,
428 					   uart_clk,
429 					   UART_BAUDRATE,
430 					   &boot_console);
431 		if (rc == 0) {
432 			panic();
433 		}
434 
435 		console_set_scope(&boot_console, CONSOLE_FLAG_BOOT |
436 				  CONSOLE_FLAG_RUNTIME | CONSOLE_FLAG_CRASH);
437 	}
438 #else
439 	if (CONSOLE_IS(pl011) || (CONSOLE_IS(pl011_1))) {
440 		/* Initialize the console to provide early debug support */
441 		rc = console_pl011_register((uint32_t)UART_BASE,
442 					   uart_clk,
443 					   (uint32_t)UART_BAUDRATE,
444 					   &boot_console);
445 		if (rc == 0) {
446 			panic();
447 		}
448 
449 		console_set_scope(&boot_console, CONSOLE_FLAG_BOOT |
450 				  CONSOLE_FLAG_RUNTIME | CONSOLE_FLAG_CRASH);
451 	}
452 #endif
453 	if (CONSOLE_IS(dcc)) {
454 		/* Initialize the dcc console for debug */
455 		rc = console_dcc_register(&boot_console);
456 		if (rc == 0) {
457 			panic();
458 		}
459 	}
460 	INFO("BL31: Early console setup\n");
461 
462 #if (defined(XILINX_OF_BOARD_DTB_ADDR) && !IS_TFA_IN_OCM(BL31_BASE))
463 	static dt_uart_info_t uart_info = {0};
464 
465 	/* Initialize the DTB console using UART information from the DTB */
466 	rc = dt_console_init(&uart_info, &boot_console, uart_clk);
467 	if (rc < 0) {
468 		ERROR("Failed to initialize DT console: %d\n", rc);
469 	}
470 #endif
471 }
472