1 // SPDX-License-Identifier: BSD-2-Clause 2 /* 3 * Copyright (c) 2017, Linaro Limited 4 */ 5 6 #include <compiler.h> 7 #include <console.h> 8 #include <drivers/cbmem_console.h> 9 #include <drivers/serial.h> 10 #include <kernel/dt.h> 11 #include <kernel/dt_driver.h> 12 #include <kernel/panic.h> 13 #include <libfdt.h> 14 #include <stdlib.h> 15 #include <string.h> 16 #include <string_ext.h> 17 18 static struct serial_chip *serial_console __nex_bss; 19 20 /* May be overridden by platform */ 21 __weak void plat_console_init(void) 22 { 23 } 24 25 void console_init(void) 26 { 27 plat_console_init(); 28 } 29 30 void __weak console_putc(int ch) 31 { 32 if (!serial_console) 33 return; 34 35 if (ch == '\n') 36 serial_console->ops->putc(serial_console, '\r'); 37 serial_console->ops->putc(serial_console, ch); 38 } 39 40 void __weak console_flush(void) 41 { 42 if (!serial_console || !serial_console->ops->flush) 43 return; 44 45 serial_console->ops->flush(serial_console); 46 } 47 48 void register_serial_console(struct serial_chip *chip) 49 { 50 serial_console = chip; 51 } 52 53 #ifdef CFG_DT 54 static int find_chosen_node(void *fdt) 55 { 56 int offset = 0; 57 58 if (!fdt) 59 return -1; 60 61 offset = fdt_path_offset(fdt, "/secure-chosen"); 62 63 if (offset < 0) 64 offset = fdt_path_offset(fdt, "/chosen"); 65 66 return offset; 67 } 68 69 TEE_Result get_console_node_from_dt(void *fdt, int *offs_out, 70 char **path_out, char **params_out) 71 { 72 const struct fdt_property *prop; 73 const char *uart; 74 const char *parms = NULL; 75 int offs; 76 char *stdout_data; 77 char *p; 78 TEE_Result rc = TEE_ERROR_GENERIC; 79 80 /* Probe console from secure DT and fallback to non-secure DT */ 81 offs = find_chosen_node(fdt); 82 if (offs < 0) { 83 DMSG("No console directive from DTB"); 84 return TEE_ERROR_ITEM_NOT_FOUND; 85 } 86 87 prop = fdt_get_property(fdt, offs, "stdout-path", NULL); 88 if (!prop) { 89 /* 90 * A secure-chosen or chosen node is present but defined 91 * no stdout-path property: no console expected 92 */ 93 IMSG("Switching off console"); 94 register_serial_console(NULL); 95 return TEE_ERROR_ITEM_NOT_FOUND; 96 } 97 98 stdout_data = nex_strdup(prop->data); 99 if (!stdout_data) 100 panic(); 101 p = strchr(stdout_data, ':'); 102 if (p) { 103 *p = '\0'; 104 parms = p + 1; 105 } 106 107 /* stdout-path may refer to an alias */ 108 uart = fdt_get_alias(fdt, stdout_data); 109 if (!uart) { 110 /* Not an alias, assume we have a node path */ 111 uart = stdout_data; 112 } 113 offs = fdt_path_offset(fdt, uart); 114 if (offs >= 0) { 115 if (offs_out) 116 *offs_out = offs; 117 if (params_out) 118 *params_out = parms ? nex_strdup(parms) : NULL; 119 if (path_out) 120 *path_out = uart ? nex_strdup(uart) : NULL; 121 122 rc = TEE_SUCCESS; 123 } 124 125 nex_free(stdout_data); 126 127 return rc; 128 } 129 130 void configure_console_from_dt(void) 131 { 132 const struct dt_driver *dt_drv; 133 const struct serial_driver *sdrv; 134 struct serial_chip *dev; 135 char *uart = NULL; 136 char *parms = NULL; 137 void *fdt; 138 int offs; 139 140 fdt = get_dt(); 141 142 if (IS_ENABLED(CFG_CBMEM_CONSOLE) && cbmem_console_init_from_dt(fdt)) 143 return; 144 145 if (get_console_node_from_dt(fdt, &offs, &uart, &parms)) 146 return; 147 148 dt_drv = dt_find_compatible_driver(fdt, offs); 149 if (!dt_drv || dt_drv->type != DT_DRIVER_UART) 150 goto out; 151 152 sdrv = (const struct serial_driver *)dt_drv->driver; 153 if (!sdrv) 154 goto out; 155 156 dev = sdrv->dev_alloc(); 157 if (!dev) 158 goto out; 159 160 /* 161 * If the console is the same as the early console, dev_init() might 162 * clear pending data. Flush to avoid that. 163 */ 164 console_flush(); 165 if (sdrv->dev_init(dev, fdt, offs, parms) < 0) { 166 sdrv->dev_free(dev); 167 goto out; 168 } 169 170 IMSG("Switching console to device: %s", uart); 171 register_serial_console(dev); 172 out: 173 nex_free(uart); 174 nex_free(parms); 175 } 176 177 #endif /* CFG_DT */ 178