1 /* 2 * Copyright (c) 2017, Linaro Limited 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include <console.h> 29 #include <compiler.h> 30 #include <drivers/serial.h> 31 #include <kernel/panic.h> 32 #include <stdlib.h> 33 #include <string.h> 34 35 #ifdef CFG_DT 36 #include <kernel/dt.h> 37 #include <libfdt.h> 38 #endif 39 40 static struct serial_chip *serial_console; 41 42 void __weak console_putc(int ch) 43 { 44 if (!serial_console) 45 return; 46 47 if (ch == '\n') 48 serial_console->ops->putc(serial_console, '\r'); 49 serial_console->ops->putc(serial_console, ch); 50 } 51 52 void __weak console_flush(void) 53 { 54 if (!serial_console) 55 return; 56 57 serial_console->ops->flush(serial_console); 58 } 59 60 void register_serial_console(struct serial_chip *chip) 61 { 62 serial_console = chip; 63 } 64 65 #ifdef CFG_DT 66 67 /* 68 * Check if the /secure-chosen node in the DT contains an stdout-path value 69 * for which we have a compatible driver. If so, switch the console to 70 * this device. 71 */ 72 void configure_console_from_dt(unsigned long phys_fdt) 73 { 74 const struct dt_driver *dt_drv; 75 const struct serial_driver *sdrv; 76 const struct fdt_property *prop; 77 struct serial_chip *dev; 78 char *stdout_data; 79 const char *uart; 80 const char *parms = NULL; 81 void *fdt; 82 int offs; 83 char *p; 84 85 if (!phys_fdt) 86 return; 87 fdt = phys_to_virt(phys_fdt, MEM_AREA_IO_NSEC); 88 if (!fdt) 89 panic(); 90 91 offs = fdt_path_offset(fdt, "/secure-chosen"); 92 if (offs < 0) 93 return; 94 prop = fdt_get_property(fdt, offs, "stdout-path", NULL); 95 if (!prop) { 96 /* 97 * /secure-chosen node present but no stdout-path property 98 * means we don't want any console output 99 */ 100 IMSG("Switching off console"); 101 register_serial_console(NULL); 102 return; 103 } 104 105 stdout_data = strdup(prop->data); 106 if (!stdout_data) 107 return; 108 p = strchr(stdout_data, ':'); 109 if (p) { 110 *p = '\0'; 111 parms = p + 1; 112 } 113 114 /* stdout-path may refer to an alias */ 115 uart = fdt_get_alias(fdt, stdout_data); 116 if (!uart) { 117 /* Not an alias, assume we have a node path */ 118 uart = stdout_data; 119 } 120 offs = fdt_path_offset(fdt, uart); 121 if (offs < 0) 122 goto out; 123 124 dt_drv = dt_find_compatible_driver(fdt, offs); 125 if (!dt_drv) 126 goto out; 127 128 sdrv = (const struct serial_driver *)dt_drv->driver; 129 if (!sdrv) 130 goto out; 131 dev = sdrv->dev_alloc(); 132 if (!dev) 133 goto out; 134 /* 135 * If the console is the same as the early console, dev_init() might 136 * clear pending data. Flush to avoid that. 137 */ 138 console_flush(); 139 if (sdrv->dev_init(dev, fdt, offs, parms) < 0) { 140 sdrv->dev_free(dev); 141 goto out; 142 } 143 144 IMSG("Switching console to device: %s", uart); 145 register_serial_console(dev); 146 out: 147 free(stdout_data); 148 } 149 150 #endif /* CFG_DT */ 151