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