11bb92983SJerome Forissier // SPDX-License-Identifier: BSD-2-Clause 2756aea59SJerome Forissier /* 3756aea59SJerome Forissier * Copyright (c) 2017, Linaro Limited 4756aea59SJerome Forissier */ 5756aea59SJerome Forissier 6756aea59SJerome Forissier #include <compiler.h> 7a2fc83d1SJerome Forissier #include <console.h> 8426790bdSJeffrey Kardatzke #include <drivers/cbmem_console.h> 9458ef442SAlvin Chang #include <drivers/semihosting_console.h> 10*d4a87690SSungbae Yoo #include <drivers/ffa_console.h> 11756aea59SJerome Forissier #include <drivers/serial.h> 12a2fc83d1SJerome Forissier #include <kernel/dt.h> 139e3c57c8SEtienne Carriere #include <kernel/dt_driver.h> 144dc31c52SJerome Forissier #include <kernel/panic.h> 15a2fc83d1SJerome Forissier #include <libfdt.h> 16756aea59SJerome Forissier #include <stdlib.h> 174dc31c52SJerome Forissier #include <string.h> 184a810b90SVolodymyr Babchuk #include <string_ext.h> 194dc31c52SJerome Forissier 20c211d0a4SVolodymyr Babchuk static struct serial_chip *serial_console __nex_bss; 21756aea59SJerome Forissier 2255ab8f06SAlvin Chang /* May be overridden by platform */ 2355ab8f06SAlvin Chang __weak void plat_console_init(void) 2455ab8f06SAlvin Chang { 2555ab8f06SAlvin Chang } 2655ab8f06SAlvin Chang 2755ab8f06SAlvin Chang void console_init(void) 2855ab8f06SAlvin Chang { 29458ef442SAlvin Chang if (IS_ENABLED(CFG_SEMIHOSTING_CONSOLE)) 30458ef442SAlvin Chang semihosting_console_init(CFG_SEMIHOSTING_CONSOLE_FILE); 31*d4a87690SSungbae Yoo else if (IS_ENABLED(CFG_FFA_CONSOLE)) 32*d4a87690SSungbae Yoo ffa_console_init(); 33458ef442SAlvin Chang else 3455ab8f06SAlvin Chang plat_console_init(); 3555ab8f06SAlvin Chang } 3655ab8f06SAlvin Chang 37756aea59SJerome Forissier void __weak console_putc(int ch) 38756aea59SJerome Forissier { 39756aea59SJerome Forissier if (!serial_console) 40756aea59SJerome Forissier return; 41756aea59SJerome Forissier 42756aea59SJerome Forissier if (ch == '\n') 43756aea59SJerome Forissier serial_console->ops->putc(serial_console, '\r'); 44756aea59SJerome Forissier serial_console->ops->putc(serial_console, ch); 45756aea59SJerome Forissier } 46756aea59SJerome Forissier 47756aea59SJerome Forissier void __weak console_flush(void) 48756aea59SJerome Forissier { 497b84e23dSJerome Forissier if (!serial_console || !serial_console->ops->flush) 50756aea59SJerome Forissier return; 51756aea59SJerome Forissier 52756aea59SJerome Forissier serial_console->ops->flush(serial_console); 53756aea59SJerome Forissier } 54756aea59SJerome Forissier 55756aea59SJerome Forissier void register_serial_console(struct serial_chip *chip) 56756aea59SJerome Forissier { 57756aea59SJerome Forissier serial_console = chip; 58756aea59SJerome Forissier } 594dc31c52SJerome Forissier 604dc31c52SJerome Forissier #ifdef CFG_DT 6108baa8c9SEtienne Carriere static int find_chosen_node(void *fdt) 6208baa8c9SEtienne Carriere { 633c6cfce4SJens Wiklander int offset = 0; 643c6cfce4SJens Wiklander 6508baa8c9SEtienne Carriere if (!fdt) 6608baa8c9SEtienne Carriere return -1; 6708baa8c9SEtienne Carriere 683c6cfce4SJens Wiklander offset = fdt_path_offset(fdt, "/secure-chosen"); 6908baa8c9SEtienne Carriere 7008baa8c9SEtienne Carriere if (offset < 0) 7108baa8c9SEtienne Carriere offset = fdt_path_offset(fdt, "/chosen"); 7208baa8c9SEtienne Carriere 7308baa8c9SEtienne Carriere return offset; 7408baa8c9SEtienne Carriere } 754dc31c52SJerome Forissier 76286c31d4SEtienne Carriere TEE_Result get_console_node_from_dt(void *fdt, int *offs_out, 77d2c717b2SJerome Forissier char **path_out, char **params_out) 784dc31c52SJerome Forissier { 794dc31c52SJerome Forissier const struct fdt_property *prop; 804dc31c52SJerome Forissier const char *uart; 814dc31c52SJerome Forissier const char *parms = NULL; 824dc31c52SJerome Forissier int offs; 83770b2afaSEtienne Carriere char *stdout_data; 844dc31c52SJerome Forissier char *p; 85770b2afaSEtienne Carriere TEE_Result rc = TEE_ERROR_GENERIC; 864dc31c52SJerome Forissier 8708baa8c9SEtienne Carriere /* Probe console from secure DT and fallback to non-secure DT */ 8808baa8c9SEtienne Carriere offs = find_chosen_node(fdt); 8908baa8c9SEtienne Carriere if (offs < 0) { 9008baa8c9SEtienne Carriere DMSG("No console directive from DTB"); 91770b2afaSEtienne Carriere return TEE_ERROR_ITEM_NOT_FOUND; 9208baa8c9SEtienne Carriere } 934dc31c52SJerome Forissier 944dc31c52SJerome Forissier prop = fdt_get_property(fdt, offs, "stdout-path", NULL); 954dc31c52SJerome Forissier if (!prop) { 964dc31c52SJerome Forissier /* 9708baa8c9SEtienne Carriere * A secure-chosen or chosen node is present but defined 9808baa8c9SEtienne Carriere * no stdout-path property: no console expected 994dc31c52SJerome Forissier */ 1004dc31c52SJerome Forissier IMSG("Switching off console"); 1014dc31c52SJerome Forissier register_serial_console(NULL); 102770b2afaSEtienne Carriere return TEE_ERROR_ITEM_NOT_FOUND; 1034dc31c52SJerome Forissier } 1044dc31c52SJerome Forissier 1054a810b90SVolodymyr Babchuk stdout_data = nex_strdup(prop->data); 1064dc31c52SJerome Forissier if (!stdout_data) 107770b2afaSEtienne Carriere panic(); 1084dc31c52SJerome Forissier p = strchr(stdout_data, ':'); 1094dc31c52SJerome Forissier if (p) { 1104dc31c52SJerome Forissier *p = '\0'; 1114dc31c52SJerome Forissier parms = p + 1; 1124dc31c52SJerome Forissier } 1134dc31c52SJerome Forissier 1144dc31c52SJerome Forissier /* stdout-path may refer to an alias */ 1154dc31c52SJerome Forissier uart = fdt_get_alias(fdt, stdout_data); 1164dc31c52SJerome Forissier if (!uart) { 1174dc31c52SJerome Forissier /* Not an alias, assume we have a node path */ 1184dc31c52SJerome Forissier uart = stdout_data; 1194dc31c52SJerome Forissier } 1204dc31c52SJerome Forissier offs = fdt_path_offset(fdt, uart); 121770b2afaSEtienne Carriere if (offs >= 0) { 122770b2afaSEtienne Carriere if (offs_out) 123770b2afaSEtienne Carriere *offs_out = offs; 124770b2afaSEtienne Carriere if (params_out) 125d2c717b2SJerome Forissier *params_out = parms ? nex_strdup(parms) : NULL; 126770b2afaSEtienne Carriere if (path_out) 127d2c717b2SJerome Forissier *path_out = uart ? nex_strdup(uart) : NULL; 128770b2afaSEtienne Carriere 129770b2afaSEtienne Carriere rc = TEE_SUCCESS; 130770b2afaSEtienne Carriere } 131770b2afaSEtienne Carriere 1324a810b90SVolodymyr Babchuk nex_free(stdout_data); 133770b2afaSEtienne Carriere 134770b2afaSEtienne Carriere return rc; 135770b2afaSEtienne Carriere } 136770b2afaSEtienne Carriere 137770b2afaSEtienne Carriere void configure_console_from_dt(void) 138770b2afaSEtienne Carriere { 139770b2afaSEtienne Carriere const struct dt_driver *dt_drv; 140770b2afaSEtienne Carriere const struct serial_driver *sdrv; 141770b2afaSEtienne Carriere struct serial_chip *dev; 142d2c717b2SJerome Forissier char *uart = NULL; 143d2c717b2SJerome Forissier char *parms = NULL; 144770b2afaSEtienne Carriere void *fdt; 145770b2afaSEtienne Carriere int offs; 146770b2afaSEtienne Carriere 14775dc8729SEtienne Carriere fdt = get_dt(); 148426790bdSJeffrey Kardatzke 149426790bdSJeffrey Kardatzke if (IS_ENABLED(CFG_CBMEM_CONSOLE) && cbmem_console_init_from_dt(fdt)) 150426790bdSJeffrey Kardatzke return; 151426790bdSJeffrey Kardatzke 152286c31d4SEtienne Carriere if (get_console_node_from_dt(fdt, &offs, &uart, &parms)) 153770b2afaSEtienne Carriere return; 1544dc31c52SJerome Forissier 1554dc31c52SJerome Forissier dt_drv = dt_find_compatible_driver(fdt, offs); 1565e588771SClément Léger if (!dt_drv || dt_drv->type != DT_DRIVER_UART) 157d2c717b2SJerome Forissier goto out; 1584dc31c52SJerome Forissier 1594dc31c52SJerome Forissier sdrv = (const struct serial_driver *)dt_drv->driver; 1604dc31c52SJerome Forissier if (!sdrv) 161d2c717b2SJerome Forissier goto out; 162770b2afaSEtienne Carriere 1634dc31c52SJerome Forissier dev = sdrv->dev_alloc(); 1644dc31c52SJerome Forissier if (!dev) 165d2c717b2SJerome Forissier goto out; 166770b2afaSEtienne Carriere 1674dc31c52SJerome Forissier /* 1684dc31c52SJerome Forissier * If the console is the same as the early console, dev_init() might 1694dc31c52SJerome Forissier * clear pending data. Flush to avoid that. 1704dc31c52SJerome Forissier */ 1714dc31c52SJerome Forissier console_flush(); 1724dc31c52SJerome Forissier if (sdrv->dev_init(dev, fdt, offs, parms) < 0) { 1734dc31c52SJerome Forissier sdrv->dev_free(dev); 174d2c717b2SJerome Forissier goto out; 1754dc31c52SJerome Forissier } 1764dc31c52SJerome Forissier 1774dc31c52SJerome Forissier IMSG("Switching console to device: %s", uart); 1784dc31c52SJerome Forissier register_serial_console(dev); 179d2c717b2SJerome Forissier out: 180d2c717b2SJerome Forissier nex_free(uart); 181d2c717b2SJerome Forissier nex_free(parms); 1824dc31c52SJerome Forissier } 1834dc31c52SJerome Forissier 1844dc31c52SJerome Forissier #endif /* CFG_DT */ 185