xref: /optee_os/core/kernel/console.c (revision 78b7c7c7653f8bff42fe44d31a79d7f6bbfd4d47)
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