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