1 /* 2 * Copyright (c) 2023-2025, 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 #if !(CONSOLE_IS(none)) 27 static console_t boot_console; 28 static console_holder boot_hd_console; 29 #if defined(CONSOLE_RUNTIME) 30 static console_t runtime_console; 31 static console_holder rt_hd_console; 32 #endif 33 34 #if ((CONSOLE_IS(dtb) || RT_CONSOLE_IS(dtb)) && (XLNX_DT_CFG == 1)) 35 static dt_uart_info_t dt_uart_info; 36 #endif 37 38 /** 39 * register_console() - Registers the uart with console list. 40 * @consoleh: Console holder structure with UART base address, 41 * UART clock, UART buad rate, flags & console type 42 * @console: Pointer to the console information structure. 43 */ 44 static void register_console(const console_holder *consoleh, console_t *console) 45 { 46 int32_t rc = 0; 47 48 switch (consoleh->console_type) { 49 #if defined(PLAT_zynqmp) 50 case CONSOLE_CDNS: 51 rc = console_cdns_register(consoleh->base, 52 consoleh->clk, 53 consoleh->baud_rate, 54 console); 55 break; 56 #else 57 case CONSOLE_PL011: 58 rc = console_pl011_register(consoleh->base, 59 consoleh->clk, 60 consoleh->baud_rate, 61 console); 62 break; 63 #endif 64 case CONSOLE_DCC: 65 rc = console_dcc_register(console); 66 break; 67 default: 68 INFO("Invalid console type\n"); 69 break; 70 } 71 72 if (rc == 0) { 73 panic(); 74 } 75 76 console_set_scope(console, consoleh->console_scope); 77 } 78 79 #if ((CONSOLE_IS(dtb) || RT_CONSOLE_IS(dtb)) && (XLNX_DT_CFG == 1)) 80 /** 81 * get_baudrate() - Get the baudrate form DTB. 82 * @dtb: Address of the Device Tree Blob (DTB). 83 * 84 * Return: On success returns the baudrate; on failure returns an error. 85 */ 86 static int32_t get_baudrate(void *dtb) 87 { 88 int node; 89 int32_t ret = 0; 90 const char *prop, *path; 91 char *end; 92 int32_t baud_rate = 0; 93 94 node = fdt_path_offset(dtb, "/secure-chosen"); 95 if (node < 0) { 96 node = fdt_path_offset(dtb, "/chosen"); 97 if (node < 0) { 98 ret = -FDT_ERR_NOTFOUND; 99 goto error; 100 } 101 } 102 103 prop = fdt_getprop(dtb, node, "stdout-path", NULL); 104 if (prop == NULL) { 105 ret = -FDT_ERR_NOTFOUND; 106 goto error; 107 } 108 109 /* Parse string serial0:115200n8 */ 110 path = strchr(prop, ':'); 111 if (!path) { 112 ret = -FDT_ERR_NOTFOUND; 113 goto error; 114 } else { 115 116 baud_rate = strtoul(path + 1, &end, 10); 117 if (baud_rate == 0 && end == path) { 118 ERROR("Conversion error occurred: %d\n", baud_rate); 119 ret = -FDT_ERR_NOTFOUND; 120 goto error; 121 } 122 ret = baud_rate; 123 } 124 125 error: 126 return ret; 127 } 128 129 /** 130 * get_node_status() - Get the DTB node status. 131 * @dtb: Address of the Device Tree Blob (DTB). 132 * @node: Node address in the device tree. 133 * 134 * Return: On success, it returns 1; on failure, it returns an 0. 135 */ 136 static uint32_t get_node_status(void *dtb, int node) 137 { 138 const char *status_cell; 139 uint32_t status = 0; 140 141 status_cell = fdt_getprop(dtb, node, "status", NULL); 142 if (!status_cell || strcmp(status_cell, "okay") == 0) { 143 status = 1; 144 } else { 145 status = 0; 146 } 147 148 return status; 149 } 150 151 /** 152 * fdt_add_uart_info() - Add DTB information to a UART structure. 153 * @info: Pointer to the UART information structure. 154 * @node: Node address in the device tree. 155 * @dtb: Address of the Device Tree Blob(DTB). 156 * 157 * Return: On success, it returns 0; on failure, it returns -1 or -FDT_ERR_NOTFOUND. 158 */ 159 static int32_t fdt_add_uart_info(dt_uart_info_t *info, int node, void *dtb) 160 { 161 uintptr_t base_addr; 162 const char *com; 163 int32_t ret = 0; 164 uint32_t status; 165 166 com = fdt_getprop(dtb, node, "compatible", NULL); 167 if (com != NULL) { 168 strlcpy(info->compatible, com, sizeof(info->compatible)); 169 } else { 170 ERROR("Compatible property not found in DTB node\n"); 171 ret = -FDT_ERR_NOTFOUND; 172 goto error; 173 } 174 175 status = get_node_status(dtb, node); 176 if (status == 0) { 177 ERROR("Uart node is disabled in DTB\n"); 178 ret = -FDT_ERR_NOTFOUND; 179 goto error; 180 } 181 182 if (strncmp(info->compatible, DT_UART_DCC_COMPAT, strlen(DT_UART_DCC_COMPAT)) != 0) { 183 ret = fdt_get_reg_props_by_index(dtb, node, 0, &base_addr, NULL); 184 if (ret >= 0) { 185 info->base = base_addr; 186 } else { 187 ERROR("Failed to retrieve base address. Error code: %d\n", ret); 188 ret = -FDT_ERR_NOTFOUND; 189 goto error; 190 } 191 192 info->baud_rate = get_baudrate(dtb); 193 194 if (strncmp(info->compatible, DT_UART_CAD_COMPAT, 195 strlen(DT_UART_CAD_COMPAT)) == 0) { 196 info->console_type = CONSOLE_CDNS; 197 } else if (strncmp(info->compatible, DT_UART_PL011_COMPAT, 198 strlen(DT_UART_PL011_COMPAT)) == 0) { 199 info->console_type = CONSOLE_PL011; 200 } else { 201 ERROR("Incompatible uart node in DTB\n"); 202 ret = -FDT_ERR_NOTFOUND; 203 } 204 } else { 205 info->console_type = CONSOLE_DCC; 206 } 207 208 error: 209 return ret; 210 } 211 212 /** 213 * fdt_get_uart_info() - Get the uart information form DTB. 214 * @info: Pointer to the UART information structure. 215 * 216 * Return: On success, it returns 0; on failure, it returns an error+reason. 217 */ 218 static int fdt_get_uart_info(dt_uart_info_t *info) 219 { 220 int node = 0, ret = 0; 221 void *dtb = (void *)plat_retrieve_dt_addr(); 222 223 ret = is_valid_dtb(dtb); 224 if (ret < 0) { 225 ERROR("Invalid Device Tree at %p: error %d\n", dtb, ret); 226 goto error; 227 } 228 229 node = fdt_get_stdout_node_offset(dtb); 230 if (node < 0) { 231 ERROR("DT get stdout node failed : %d\n", node); 232 goto error; 233 } 234 235 ret = fdt_add_uart_info(info, node, dtb); 236 if (ret < 0) { 237 ERROR("Failed to add DT UART info: %d\n", ret); 238 goto error; 239 } 240 241 error: 242 return ret; 243 } 244 #endif 245 246 void setup_console(void) 247 { 248 /* This is hardcoded console setup just in case that DTB console fails */ 249 boot_hd_console.base = (uintptr_t)UART_BASE; 250 boot_hd_console.baud_rate = (uint32_t)UART_BAUDRATE; 251 boot_hd_console.clk = get_uart_clk(); 252 boot_hd_console.console_scope = CONSOLE_FLAG_BOOT | CONSOLE_FLAG_CRASH; 253 boot_hd_console.console_type = UART_TYPE; 254 255 /* For DT code decoding uncomment console registration below */ 256 /* register_console(&boot_hd_console, &boot_console); */ 257 258 #if ((CONSOLE_IS(dtb) || RT_CONSOLE_IS(dtb)) && (XLNX_DT_CFG == 1)) 259 /* Parse DTB console for UART information */ 260 if (fdt_get_uart_info(&dt_uart_info) == 0) { 261 if (CONSOLE_IS(dtb)) { 262 boot_hd_console.base = dt_uart_info.base; 263 boot_hd_console.baud_rate = dt_uart_info.baud_rate; 264 boot_hd_console.console_type = dt_uart_info.console_type; 265 } 266 } else { 267 ERROR("Failed to initialize DT console or console node is disabled\n"); 268 } 269 #endif 270 271 /* Initialize the boot console */ 272 register_console(&boot_hd_console, &boot_console); 273 274 INFO("BL31: Early console setup\n"); 275 276 #ifdef CONSOLE_RUNTIME 277 rt_hd_console.base = (uintptr_t)RT_UART_BASE; 278 rt_hd_console.baud_rate = (uint32_t)UART_BAUDRATE; 279 rt_hd_console.console_type = RT_UART_TYPE; 280 281 #if (RT_CONSOLE_IS(dtb) && (XLNX_DT_CFG == 1)) 282 if (dt_uart_info.base != 0U) { 283 rt_hd_console.base = dt_uart_info.base; 284 rt_hd_console.baud_rate = dt_uart_info.baud_rate; 285 rt_hd_console.console_type = dt_uart_info.console_type; 286 } 287 #endif 288 289 if ((rt_hd_console.console_type == boot_hd_console.console_type) && 290 (rt_hd_console.base == boot_hd_console.base)) { 291 console_set_scope(&boot_console, 292 CONSOLE_FLAG_BOOT | CONSOLE_FLAG_CRASH | CONSOLE_FLAG_RUNTIME); 293 INFO("Successfully initialized runtime console\n"); 294 } else { 295 rt_hd_console.clk = get_uart_clk(); 296 rt_hd_console.console_scope = CONSOLE_FLAG_RUNTIME; 297 298 register_console(&rt_hd_console, &runtime_console); 299 INFO("Successfully initialized new runtime console\n"); 300 } 301 #endif 302 } 303 #else 304 void setup_console(void) 305 { 306 } 307 #endif 308