1 // SPDX-License-Identifier: BSD-2-Clause 2 /* 3 * Copyright (c) 2017, Linaro Limited 4 */ 5 6 #include <console.h> 7 #include <compiler.h> 8 #include <drivers/serial.h> 9 #include <kernel/panic.h> 10 #include <stdlib.h> 11 #include <string.h> 12 13 #ifdef CFG_DT 14 #include <kernel/dt.h> 15 #include <libfdt.h> 16 #endif 17 18 static struct serial_chip *serial_console; 19 20 void __weak console_putc(int ch) 21 { 22 if (!serial_console) 23 return; 24 25 if (ch == '\n') 26 serial_console->ops->putc(serial_console, '\r'); 27 serial_console->ops->putc(serial_console, ch); 28 } 29 30 void __weak console_flush(void) 31 { 32 if (!serial_console) 33 return; 34 35 serial_console->ops->flush(serial_console); 36 } 37 38 void register_serial_console(struct serial_chip *chip) 39 { 40 serial_console = chip; 41 } 42 43 #ifdef CFG_DT 44 45 /* 46 * Check if the /secure-chosen node in the DT contains an stdout-path value 47 * for which we have a compatible driver. If so, switch the console to 48 * this device. 49 */ 50 void configure_console_from_dt(unsigned long phys_fdt) 51 { 52 const struct dt_driver *dt_drv; 53 const struct serial_driver *sdrv; 54 const struct fdt_property *prop; 55 struct serial_chip *dev; 56 char *stdout_data; 57 const char *uart; 58 const char *parms = NULL; 59 void *fdt; 60 int offs; 61 char *p; 62 63 if (!phys_fdt) 64 return; 65 fdt = phys_to_virt(phys_fdt, MEM_AREA_IO_NSEC); 66 if (!fdt) 67 panic(); 68 69 offs = fdt_path_offset(fdt, "/secure-chosen"); 70 if (offs < 0) 71 return; 72 prop = fdt_get_property(fdt, offs, "stdout-path", NULL); 73 if (!prop) { 74 /* 75 * /secure-chosen node present but no stdout-path property 76 * means we don't want any console output 77 */ 78 IMSG("Switching off console"); 79 register_serial_console(NULL); 80 return; 81 } 82 83 stdout_data = strdup(prop->data); 84 if (!stdout_data) 85 return; 86 p = strchr(stdout_data, ':'); 87 if (p) { 88 *p = '\0'; 89 parms = p + 1; 90 } 91 92 /* stdout-path may refer to an alias */ 93 uart = fdt_get_alias(fdt, stdout_data); 94 if (!uart) { 95 /* Not an alias, assume we have a node path */ 96 uart = stdout_data; 97 } 98 offs = fdt_path_offset(fdt, uart); 99 if (offs < 0) 100 goto out; 101 102 dt_drv = dt_find_compatible_driver(fdt, offs); 103 if (!dt_drv) 104 goto out; 105 106 sdrv = (const struct serial_driver *)dt_drv->driver; 107 if (!sdrv) 108 goto out; 109 dev = sdrv->dev_alloc(); 110 if (!dev) 111 goto out; 112 /* 113 * If the console is the same as the early console, dev_init() might 114 * clear pending data. Flush to avoid that. 115 */ 116 console_flush(); 117 if (sdrv->dev_init(dev, fdt, offs, parms) < 0) { 118 sdrv->dev_free(dev); 119 goto out; 120 } 121 122 IMSG("Switching console to device: %s", uart); 123 register_serial_console(dev); 124 out: 125 free(stdout_data); 126 } 127 128 #endif /* CFG_DT */ 129