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