11bb92983SJerome Forissier // SPDX-License-Identifier: BSD-2-Clause 2756aea59SJerome Forissier /* 3756aea59SJerome Forissier * Copyright (c) 2017, Linaro Limited 4756aea59SJerome Forissier */ 5756aea59SJerome Forissier 6756aea59SJerome Forissier #include <console.h> 7756aea59SJerome Forissier #include <compiler.h> 8756aea59SJerome Forissier #include <drivers/serial.h> 92f82082fSEdison Ai #include <kernel/generic_boot.h> 104dc31c52SJerome Forissier #include <kernel/panic.h> 11756aea59SJerome Forissier #include <stdlib.h> 124dc31c52SJerome Forissier #include <string.h> 13*4a810b90SVolodymyr Babchuk #include <string_ext.h> 144dc31c52SJerome Forissier 154dc31c52SJerome Forissier #ifdef CFG_DT 164dc31c52SJerome Forissier #include <kernel/dt.h> 174dc31c52SJerome Forissier #include <libfdt.h> 184dc31c52SJerome Forissier #endif 19756aea59SJerome Forissier 20c211d0a4SVolodymyr Babchuk static struct serial_chip *serial_console __nex_bss; 21756aea59SJerome Forissier 22756aea59SJerome Forissier void __weak console_putc(int ch) 23756aea59SJerome Forissier { 24756aea59SJerome Forissier if (!serial_console) 25756aea59SJerome Forissier return; 26756aea59SJerome Forissier 27756aea59SJerome Forissier if (ch == '\n') 28756aea59SJerome Forissier serial_console->ops->putc(serial_console, '\r'); 29756aea59SJerome Forissier serial_console->ops->putc(serial_console, ch); 30756aea59SJerome Forissier } 31756aea59SJerome Forissier 32756aea59SJerome Forissier void __weak console_flush(void) 33756aea59SJerome Forissier { 34756aea59SJerome Forissier if (!serial_console) 35756aea59SJerome Forissier return; 36756aea59SJerome Forissier 37756aea59SJerome Forissier serial_console->ops->flush(serial_console); 38756aea59SJerome Forissier } 39756aea59SJerome Forissier 40756aea59SJerome Forissier void register_serial_console(struct serial_chip *chip) 41756aea59SJerome Forissier { 42756aea59SJerome Forissier serial_console = chip; 43756aea59SJerome Forissier } 444dc31c52SJerome Forissier 454dc31c52SJerome Forissier #ifdef CFG_DT 4608baa8c9SEtienne Carriere static int find_chosen_node(void *fdt) 4708baa8c9SEtienne Carriere { 4808baa8c9SEtienne Carriere if (!fdt) 4908baa8c9SEtienne Carriere return -1; 5008baa8c9SEtienne Carriere 5108baa8c9SEtienne Carriere int offset = fdt_path_offset(fdt, "/secure-chosen"); 5208baa8c9SEtienne Carriere 5308baa8c9SEtienne Carriere if (offset < 0) 5408baa8c9SEtienne Carriere offset = fdt_path_offset(fdt, "/chosen"); 5508baa8c9SEtienne Carriere 5608baa8c9SEtienne Carriere return offset; 5708baa8c9SEtienne Carriere } 584dc31c52SJerome Forissier 59770b2afaSEtienne Carriere TEE_Result get_console_node_from_dt(void **fdt_out, int *offs_out, 60770b2afaSEtienne Carriere const char **path_out, 61770b2afaSEtienne Carriere const char **params_out) 624dc31c52SJerome Forissier { 634dc31c52SJerome Forissier const struct fdt_property *prop; 644dc31c52SJerome Forissier const char *uart; 654dc31c52SJerome Forissier const char *parms = NULL; 6608baa8c9SEtienne Carriere void *fdt; 674dc31c52SJerome Forissier int offs; 68770b2afaSEtienne Carriere char *stdout_data; 694dc31c52SJerome Forissier char *p; 70770b2afaSEtienne Carriere TEE_Result rc = TEE_ERROR_GENERIC; 714dc31c52SJerome Forissier 7208baa8c9SEtienne Carriere /* Probe console from secure DT and fallback to non-secure DT */ 7308baa8c9SEtienne Carriere fdt = get_embedded_dt(); 7408baa8c9SEtienne Carriere offs = find_chosen_node(fdt); 7508baa8c9SEtienne Carriere if (offs < 0) { 7608baa8c9SEtienne Carriere fdt = get_external_dt(); 7708baa8c9SEtienne Carriere offs = find_chosen_node(fdt); 7808baa8c9SEtienne Carriere } 7908baa8c9SEtienne Carriere if (offs < 0) { 8008baa8c9SEtienne Carriere DMSG("No console directive from DTB"); 81770b2afaSEtienne Carriere return TEE_ERROR_ITEM_NOT_FOUND; 8208baa8c9SEtienne Carriere } 834dc31c52SJerome Forissier 844dc31c52SJerome Forissier prop = fdt_get_property(fdt, offs, "stdout-path", NULL); 854dc31c52SJerome Forissier if (!prop) { 864dc31c52SJerome Forissier /* 8708baa8c9SEtienne Carriere * A secure-chosen or chosen node is present but defined 8808baa8c9SEtienne Carriere * no stdout-path property: no console expected 894dc31c52SJerome Forissier */ 904dc31c52SJerome Forissier IMSG("Switching off console"); 914dc31c52SJerome Forissier register_serial_console(NULL); 92770b2afaSEtienne Carriere return TEE_ERROR_ITEM_NOT_FOUND; 934dc31c52SJerome Forissier } 944dc31c52SJerome Forissier 95*4a810b90SVolodymyr Babchuk stdout_data = nex_strdup(prop->data); 964dc31c52SJerome Forissier if (!stdout_data) 97770b2afaSEtienne Carriere panic(); 984dc31c52SJerome Forissier p = strchr(stdout_data, ':'); 994dc31c52SJerome Forissier if (p) { 1004dc31c52SJerome Forissier *p = '\0'; 1014dc31c52SJerome Forissier parms = p + 1; 1024dc31c52SJerome Forissier } 1034dc31c52SJerome Forissier 1044dc31c52SJerome Forissier /* stdout-path may refer to an alias */ 1054dc31c52SJerome Forissier uart = fdt_get_alias(fdt, stdout_data); 1064dc31c52SJerome Forissier if (!uart) { 1074dc31c52SJerome Forissier /* Not an alias, assume we have a node path */ 1084dc31c52SJerome Forissier uart = stdout_data; 1094dc31c52SJerome Forissier } 1104dc31c52SJerome Forissier offs = fdt_path_offset(fdt, uart); 111770b2afaSEtienne Carriere if (offs >= 0) { 112770b2afaSEtienne Carriere if (fdt_out) 113770b2afaSEtienne Carriere *fdt_out = fdt; 114770b2afaSEtienne Carriere if (offs_out) 115770b2afaSEtienne Carriere *offs_out = offs; 116770b2afaSEtienne Carriere if (params_out) 117770b2afaSEtienne Carriere *params_out = parms; 118770b2afaSEtienne Carriere if (path_out) 119770b2afaSEtienne Carriere *path_out = uart; 120770b2afaSEtienne Carriere 121770b2afaSEtienne Carriere rc = TEE_SUCCESS; 122770b2afaSEtienne Carriere } 123770b2afaSEtienne Carriere 124*4a810b90SVolodymyr Babchuk nex_free(stdout_data); 125770b2afaSEtienne Carriere 126770b2afaSEtienne Carriere return rc; 127770b2afaSEtienne Carriere } 128770b2afaSEtienne Carriere 129770b2afaSEtienne Carriere void configure_console_from_dt(void) 130770b2afaSEtienne Carriere { 131770b2afaSEtienne Carriere const struct dt_driver *dt_drv; 132770b2afaSEtienne Carriere const struct serial_driver *sdrv; 133770b2afaSEtienne Carriere struct serial_chip *dev; 134770b2afaSEtienne Carriere const char *uart; 135770b2afaSEtienne Carriere const char *parms; 136770b2afaSEtienne Carriere void *fdt; 137770b2afaSEtienne Carriere int offs; 138770b2afaSEtienne Carriere 139770b2afaSEtienne Carriere if (get_console_node_from_dt(&fdt, &offs, &uart, &parms)) 140770b2afaSEtienne Carriere return; 1414dc31c52SJerome Forissier 1424dc31c52SJerome Forissier dt_drv = dt_find_compatible_driver(fdt, offs); 1434dc31c52SJerome Forissier if (!dt_drv) 144770b2afaSEtienne Carriere return; 1454dc31c52SJerome Forissier 1464dc31c52SJerome Forissier sdrv = (const struct serial_driver *)dt_drv->driver; 1474dc31c52SJerome Forissier if (!sdrv) 148770b2afaSEtienne Carriere return; 149770b2afaSEtienne Carriere 1504dc31c52SJerome Forissier dev = sdrv->dev_alloc(); 1514dc31c52SJerome Forissier if (!dev) 152770b2afaSEtienne Carriere return; 153770b2afaSEtienne Carriere 1544dc31c52SJerome Forissier /* 1554dc31c52SJerome Forissier * If the console is the same as the early console, dev_init() might 1564dc31c52SJerome Forissier * clear pending data. Flush to avoid that. 1574dc31c52SJerome Forissier */ 1584dc31c52SJerome Forissier console_flush(); 1594dc31c52SJerome Forissier if (sdrv->dev_init(dev, fdt, offs, parms) < 0) { 1604dc31c52SJerome Forissier sdrv->dev_free(dev); 161770b2afaSEtienne Carriere return; 1624dc31c52SJerome Forissier } 1634dc31c52SJerome Forissier 1644dc31c52SJerome Forissier IMSG("Switching console to device: %s", uart); 1654dc31c52SJerome Forissier register_serial_console(dev); 1664dc31c52SJerome Forissier } 1674dc31c52SJerome Forissier 1684dc31c52SJerome Forissier #endif /* CFG_DT */ 169