xref: /rk3399_ARM-atf/plat/xilinx/common/plat_console.c (revision 09a02ce0bd37585a85f5b3e7f8dd6d7dc82e5f14)
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 (CONSOLE_IS(dtb) && defined(XILINX_OF_BOARD_DTB_ADDR)) && \
29 	(!defined(PLAT_zynqmp) || (defined(PLAT_zynqmp) && \
30 				   !IS_TFA_IN_OCM(BL31_BASE))) || defined(CONSOLE_RUNTIME)
31 /**
32  * register_console() - Registers the runtime uart with console list.
33  * @uart_base: UART base address
34  * @clock: UART clock.
35  * @baud_rate: UART buad rate
36  * @console: Pointer to the console information structure.
37  * @flags: console flags.
38  */
39 static void register_console(uintptr_t uart_base, uint32_t clock,
40 		uint32_t baud_rate, console_t *console,
41 		uint32_t flags, uint8_t console_type)
42 {
43 	int32_t rc = 0;
44 
45 	if (console_type == PLAT_XLNX_CONSOLE_TYPE_DEFAULT) {
46 #if defined(PLAT_zynqmp)
47 		rc = console_cdns_register(uart_base,
48 				clock,
49 				baud_rate,
50 				console);
51 #elif defined(PLAT_versal) || defined(PLAT_versal_net) || defined(PLAT_versal2)
52 		rc = console_pl011_register(uart_base,
53 				clock,
54 				baud_rate,
55 				console);
56 #endif
57 	} else if (console_type == PLAT_XLNX_CONSOLE_TYPE_DEBUG) {
58 		rc = console_dcc_register(console);
59 	} else {
60 		INFO("Invalid console type\n");
61 	}
62 
63 	if (rc == 0) {
64 		panic();
65 	}
66 
67 	console_set_scope(console, flags);
68 }
69 #endif
70 
71 
72 #if (CONSOLE_IS(dtb) && defined(XILINX_OF_BOARD_DTB_ADDR)) && \
73 	(!defined(PLAT_zynqmp) || (defined(PLAT_zynqmp) && \
74 				   !IS_TFA_IN_OCM(BL31_BASE)))
75 static console_t dt_console;
76 /**
77  * get_baudrate() - Get the baudrate form DTB.
78  * @dtb: Address of the Device Tree Blob (DTB).
79  *
80  * Return: On success returns the baudrate; on failure returns an error.
81  */
82 static int32_t get_baudrate(void *dtb)
83 {
84 	int node;
85 	int32_t ret = 0;
86 	const char *prop, *path;
87 	char *end;
88 	int32_t baud_rate = 0;
89 
90 	node = fdt_path_offset(dtb, "/secure-chosen");
91 	if (node < 0) {
92 		node = fdt_path_offset(dtb, "/chosen");
93 		if (node < 0) {
94 			ret = -FDT_ERR_NOTFOUND;
95 			goto error;
96 		}
97 	}
98 
99 	prop = fdt_getprop(dtb, node, "stdout-path", NULL);
100 	if (prop == NULL) {
101 		ret = -FDT_ERR_NOTFOUND;
102 		goto error;
103 	}
104 
105 	/* Parse string serial0:115200n8 */
106 	path = strchr(prop, ':');
107 	if (!path) {
108 		ret = -FDT_ERR_NOTFOUND;
109 		goto error;
110 	} else {
111 
112 		baud_rate = strtoul(path + 1, &end, 10);
113 		if (baud_rate == 0 && end == path) {
114 			ERROR("Conversion error occurred: %d\n", baud_rate);
115 			ret = -FDT_ERR_NOTFOUND;
116 			goto error;
117 		}
118 		ret = baud_rate;
119 	}
120 
121 error:
122 	return ret;
123 }
124 
125 /**
126  * get_node_status() - Get the DTB node status.
127  * @dtb: Address of the Device Tree Blob (DTB).
128  * @node: Node address in the device tree.
129  *
130  * Return: On success, it returns 1; on failure, it returns an 0.
131  */
132 static uint32_t get_node_status(void *dtb, int node)
133 {
134 	const char *status_cell;
135 	uint32_t status = 0;
136 
137 	status_cell = fdt_getprop(dtb, node, "status", NULL);
138 	if (!status_cell || strcmp(status_cell, "okay") == 0) {
139 		status = 1;
140 	} else {
141 		status = 0;
142 	}
143 
144 	return status;
145 }
146 
147 /**
148  * fdt_add_uart_info() - Add DTB information to a UART structure.
149  * @info: Pointer to the UART information structure.
150  * @node: Node address in the device tree.
151  * @dtb: Address of the Device Tree Blob(DTB).
152  *
153  * Return: On success, it returns 1; on failure, it returns an 0.
154  */
155 static uint32_t fdt_add_uart_info(dt_uart_info_t *info, int node, void *dtb)
156 {
157 	uintptr_t base_addr;
158 	const char *com;
159 	int32_t ret = 0;
160 
161 	com = fdt_getprop(dtb, node, "compatible", NULL);
162 	if (com != NULL) {
163 		strlcpy(info->compatible, com, sizeof(info->compatible));
164 	} else {
165 		ERROR("Compatible property not found in DTB node\n");
166 		ret  = -FDT_ERR_NOTFOUND;
167 		goto error;
168 	}
169 
170 	info->status = get_node_status(dtb, node);
171 	if (info->status == 0) {
172 		ERROR("Uart node is disabled in DTB\n");
173 		ret  = -FDT_ERR_NOTFOUND;
174 		goto error;
175 	}
176 
177 	if (strncmp(info->compatible, DT_UART_DCC_COMPAT, strlen(DT_UART_DCC_COMPAT)) != 0) {
178 		ret = fdt_get_reg_props_by_index(dtb, node, 0, &base_addr, NULL);
179 		if (ret >= 0) {
180 			info->base = base_addr;
181 		} else {
182 			ERROR("Failed to retrieve base address. Error code: %d\n", ret);
183 			ret  = -FDT_ERR_NOTFOUND;
184 			goto error;
185 		}
186 
187 		info->baud_rate = get_baudrate(dtb);
188 	}
189 
190 error:
191 	return ret;
192 }
193 
194 /**
195  * fdt_get_uart_info() - Get the uart information form DTB.
196  * @info: Pointer to the UART information structure.
197  *
198  * Return: On success, it returns 0; on failure, it returns an error+reason.
199  */
200 static int fdt_get_uart_info(dt_uart_info_t *info)
201 {
202 	int node = 0, ret = 0;
203 	void *dtb = (void *)XILINX_OF_BOARD_DTB_ADDR;
204 
205 	ret = is_valid_dtb(dtb);
206 	if (ret < 0) {
207 		ERROR("Invalid Device Tree at %p: error %d\n", dtb, ret);
208 		goto error;
209 	}
210 
211 	node = fdt_get_stdout_node_offset(dtb);
212 	if (node < 0) {
213 		ERROR("DT get stdout node failed : %d\n", node);
214 		goto error;
215 	}
216 
217 	ret = fdt_add_uart_info(info, node, dtb);
218 	if (ret < 0) {
219 		ERROR("Failed to add DT UART info: %d\n", ret);
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 
370 #if (CONSOLE_IS(dtb) && defined(XILINX_OF_BOARD_DTB_ADDR)) && \
371 	(!defined(PLAT_zynqmp) || (defined(PLAT_zynqmp) && \
372 				   !IS_TFA_IN_OCM(BL31_BASE)))
373 	console_t *console = &dt_console;
374 #else
375 	console_t *console = &boot_console;
376 #endif
377 
378 #if (RT_CONSOLE_IS(dtb) && defined(XILINX_OF_BOARD_DTB_ADDR)) && \
379 	(!defined(PLAT_zynqmp) || (defined(PLAT_zynqmp) && \
380 				   !IS_TFA_IN_OCM(BL31_BASE)))
381 	static dt_uart_info_t dt_info = {0};
382 	uint32_t rc = parse_uart_info(&dt_info);
383 
384 	if (rc < 0) {
385 		goto error;
386 	} else {
387 		rt_uart_base = dt_info.base;
388 		buad_rate = dt_info.baud_rate;
389 	}
390 #elif defined(PLAT_zynqmp)
391 	if (RT_CONSOLE_IS(cadence) || (RT_CONSOLE_IS(cadence1))) {
392 		rt_uart_base = (uintptr_t)RT_UART_BASE;
393 		buad_rate = (uint32_t)UART_BAUDRATE;
394 	}
395 #else
396 	if (RT_CONSOLE_IS(pl011) || (RT_CONSOLE_IS(pl011_1))) {
397 		rt_uart_base = (uintptr_t)RT_UART_BASE;
398 		buad_rate = (uint32_t)UART_BAUDRATE;
399 	}
400 #endif
401 	/*skip console registration if runtime and boot console are same */
402 	if (console->base != rt_uart_base) {
403 		/* Remove runtime flag from boot console */
404 		console_set_scope(console, CONSOLE_FLAG_BOOT | CONSOLE_FLAG_CRASH);
405 
406 		register_console(rt_uart_base, uart_clk, buad_rate, &runtime_console,
407 				 CONSOLE_FLAG_RUNTIME, PLAT_XLNX_CONSOLE_TYPE_DEFAULT);
408 		INFO("Successfully initialized runtime console\n");
409 	}
410 
411 #if (RT_CONSOLE_IS(dtb) && defined(XILINX_OF_BOARD_DTB_ADDR)) && \
412 	(!defined(PLAT_zynqmp) || (defined(PLAT_zynqmp) && \
413 				   !IS_TFA_IN_OCM(BL31_BASE)))
414 error:
415 	if (rc < 0) {
416 		ERROR("Failed to parse uart info in runtime console\n");
417 	}
418 #endif
419 }
420 #endif
421 
422 void setup_console(void)
423 {
424 	int32_t rc;
425 	uint32_t uart_clk = get_uart_clk();
426 
427 #if (CONSOLE_IS(dtb) && defined(XILINX_OF_BOARD_DTB_ADDR)) && \
428 	(!defined(PLAT_zynqmp) || (defined(PLAT_zynqmp) && \
429 				   !IS_TFA_IN_OCM(BL31_BASE)))
430 
431 	static dt_uart_info_t uart_info = {0};
432 
433 	/* Initialize the DTB console using UART information from the DTB */
434 	rc = dt_console_init(&uart_info, &boot_console, uart_clk);
435 	if (rc < 0) {
436 		ERROR("Failed to initialize DT console: %d\n", rc);
437 	} else {
438 		goto cnslsetup;
439 	}
440 #endif
441 
442 #if defined(PLAT_zynqmp)
443 	if (CONSOLE_IS(cadence) || (CONSOLE_IS(cadence1))) {
444 		rc = console_cdns_register(UART_BASE,
445 					   uart_clk,
446 					   UART_BAUDRATE,
447 					   &boot_console);
448 		if (rc == 0) {
449 			panic();
450 		}
451 
452 		console_set_scope(&boot_console, CONSOLE_FLAG_BOOT |
453 				  CONSOLE_FLAG_RUNTIME | CONSOLE_FLAG_CRASH);
454 	}
455 #else
456 	if (CONSOLE_IS(pl011) || (CONSOLE_IS(pl011_1))) {
457 		/* Initialize the console to provide early debug support */
458 		rc = console_pl011_register((uint32_t)UART_BASE,
459 					   uart_clk,
460 					   (uint32_t)UART_BAUDRATE,
461 					   &boot_console);
462 		if (rc == 0) {
463 			panic();
464 		}
465 
466 		console_set_scope(&boot_console, CONSOLE_FLAG_BOOT |
467 				  CONSOLE_FLAG_RUNTIME | CONSOLE_FLAG_CRASH);
468 	}
469 #endif
470 	if (CONSOLE_IS(dcc)) {
471 		/* Initialize the dcc console for debug */
472 		rc = console_dcc_register(&boot_console);
473 		if (rc == 0) {
474 			panic();
475 		}
476 	}
477 
478 #if (CONSOLE_IS(dtb) && defined(XILINX_OF_BOARD_DTB_ADDR)) && \
479 	(!defined(PLAT_zynqmp) || (defined(PLAT_zynqmp) && \
480 				   !IS_TFA_IN_OCM(BL31_BASE)))
481 cnslsetup:
482 #endif
483 	INFO("BL31: Early console setup\n");
484 }
485