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