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> 9*2f82082fSEdison Ai #include <kernel/generic_boot.h> 104dc31c52SJerome Forissier #include <kernel/panic.h> 11756aea59SJerome Forissier #include <stdlib.h> 124dc31c52SJerome Forissier #include <string.h> 134dc31c52SJerome Forissier 144dc31c52SJerome Forissier #ifdef CFG_DT 154dc31c52SJerome Forissier #include <kernel/dt.h> 164dc31c52SJerome Forissier #include <libfdt.h> 174dc31c52SJerome Forissier #endif 18756aea59SJerome Forissier 1923660121SJerome Forissier static struct serial_chip *serial_console; 20756aea59SJerome Forissier 21756aea59SJerome Forissier void __weak console_putc(int ch) 22756aea59SJerome Forissier { 23756aea59SJerome Forissier if (!serial_console) 24756aea59SJerome Forissier return; 25756aea59SJerome Forissier 26756aea59SJerome Forissier if (ch == '\n') 27756aea59SJerome Forissier serial_console->ops->putc(serial_console, '\r'); 28756aea59SJerome Forissier serial_console->ops->putc(serial_console, ch); 29756aea59SJerome Forissier } 30756aea59SJerome Forissier 31756aea59SJerome Forissier void __weak console_flush(void) 32756aea59SJerome Forissier { 33756aea59SJerome Forissier if (!serial_console) 34756aea59SJerome Forissier return; 35756aea59SJerome Forissier 36756aea59SJerome Forissier serial_console->ops->flush(serial_console); 37756aea59SJerome Forissier } 38756aea59SJerome Forissier 39756aea59SJerome Forissier void register_serial_console(struct serial_chip *chip) 40756aea59SJerome Forissier { 41756aea59SJerome Forissier serial_console = chip; 42756aea59SJerome Forissier } 434dc31c52SJerome Forissier 444dc31c52SJerome Forissier #ifdef CFG_DT 454dc31c52SJerome Forissier 464dc31c52SJerome Forissier /* 474dc31c52SJerome Forissier * Check if the /secure-chosen node in the DT contains an stdout-path value 484dc31c52SJerome Forissier * for which we have a compatible driver. If so, switch the console to 494dc31c52SJerome Forissier * this device. 504dc31c52SJerome Forissier */ 51*2f82082fSEdison Ai void configure_console_from_dt(void) 524dc31c52SJerome Forissier { 534dc31c52SJerome Forissier const struct dt_driver *dt_drv; 544dc31c52SJerome Forissier const struct serial_driver *sdrv; 554dc31c52SJerome Forissier const struct fdt_property *prop; 564dc31c52SJerome Forissier struct serial_chip *dev; 574dc31c52SJerome Forissier char *stdout_data; 584dc31c52SJerome Forissier const char *uart; 594dc31c52SJerome Forissier const char *parms = NULL; 60*2f82082fSEdison Ai void *fdt = get_dt_blob(); 614dc31c52SJerome Forissier int offs; 624dc31c52SJerome Forissier char *p; 634dc31c52SJerome Forissier 644dc31c52SJerome Forissier if (!fdt) 65*2f82082fSEdison Ai return; 664dc31c52SJerome Forissier 674dc31c52SJerome Forissier offs = fdt_path_offset(fdt, "/secure-chosen"); 684dc31c52SJerome Forissier if (offs < 0) 694dc31c52SJerome Forissier return; 704dc31c52SJerome Forissier prop = fdt_get_property(fdt, offs, "stdout-path", NULL); 714dc31c52SJerome Forissier if (!prop) { 724dc31c52SJerome Forissier /* 734dc31c52SJerome Forissier * /secure-chosen node present but no stdout-path property 744dc31c52SJerome Forissier * means we don't want any console output 754dc31c52SJerome Forissier */ 764dc31c52SJerome Forissier IMSG("Switching off console"); 774dc31c52SJerome Forissier register_serial_console(NULL); 784dc31c52SJerome Forissier return; 794dc31c52SJerome Forissier } 804dc31c52SJerome Forissier 814dc31c52SJerome Forissier stdout_data = strdup(prop->data); 824dc31c52SJerome Forissier if (!stdout_data) 834dc31c52SJerome Forissier return; 844dc31c52SJerome Forissier p = strchr(stdout_data, ':'); 854dc31c52SJerome Forissier if (p) { 864dc31c52SJerome Forissier *p = '\0'; 874dc31c52SJerome Forissier parms = p + 1; 884dc31c52SJerome Forissier } 894dc31c52SJerome Forissier 904dc31c52SJerome Forissier /* stdout-path may refer to an alias */ 914dc31c52SJerome Forissier uart = fdt_get_alias(fdt, stdout_data); 924dc31c52SJerome Forissier if (!uart) { 934dc31c52SJerome Forissier /* Not an alias, assume we have a node path */ 944dc31c52SJerome Forissier uart = stdout_data; 954dc31c52SJerome Forissier } 964dc31c52SJerome Forissier offs = fdt_path_offset(fdt, uart); 974dc31c52SJerome Forissier if (offs < 0) 984dc31c52SJerome Forissier goto out; 994dc31c52SJerome Forissier 1004dc31c52SJerome Forissier dt_drv = dt_find_compatible_driver(fdt, offs); 1014dc31c52SJerome Forissier if (!dt_drv) 1024dc31c52SJerome Forissier goto out; 1034dc31c52SJerome Forissier 1044dc31c52SJerome Forissier sdrv = (const struct serial_driver *)dt_drv->driver; 1054dc31c52SJerome Forissier if (!sdrv) 1064dc31c52SJerome Forissier goto out; 1074dc31c52SJerome Forissier dev = sdrv->dev_alloc(); 1084dc31c52SJerome Forissier if (!dev) 1094dc31c52SJerome Forissier goto out; 1104dc31c52SJerome Forissier /* 1114dc31c52SJerome Forissier * If the console is the same as the early console, dev_init() might 1124dc31c52SJerome Forissier * clear pending data. Flush to avoid that. 1134dc31c52SJerome Forissier */ 1144dc31c52SJerome Forissier console_flush(); 1154dc31c52SJerome Forissier if (sdrv->dev_init(dev, fdt, offs, parms) < 0) { 1164dc31c52SJerome Forissier sdrv->dev_free(dev); 1174dc31c52SJerome Forissier goto out; 1184dc31c52SJerome Forissier } 1194dc31c52SJerome Forissier 1204dc31c52SJerome Forissier IMSG("Switching console to device: %s", uart); 1214dc31c52SJerome Forissier register_serial_console(dev); 1224dc31c52SJerome Forissier out: 1234dc31c52SJerome Forissier free(stdout_data); 1244dc31c52SJerome Forissier } 1254dc31c52SJerome Forissier 1264dc31c52SJerome Forissier #endif /* CFG_DT */ 127