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