Commit 00707e9398c8397b6a90acc50634c2b538f6ab67
1 parent
c35bf3b38c
Exists in
smarc_8mq-imx_v2020.04_5.4.24_2.1.0
and in
1 other branch
MLK-20373-5 serial: serial_xen: support normal uboot console
Support output/input using `xl console [domid]`. Signed-off-by: Peng Fan <peng.fan@nxp.com> Reviewed-by: Flynn xu <flynn.xu@nxp.com> (cherry picked from commit 75a9833506aa13c1d5a8641c81ac951c41ced55e) (cherry picked from commit 5a495c39679b6641d369fc0c9cfdadafd8a408f6) (cherry picked from commit 6f3cc8513ac1afe0cf679422ae82c4a5930a8c64)
Showing 2 changed files with 181 additions and 28 deletions Side-by-side Diff
drivers/serial/Kconfig
... | ... | @@ -187,6 +187,9 @@ |
187 | 187 | depends on DEBUG_UART |
188 | 188 | default DEBUG_UART_NS16550 |
189 | 189 | |
190 | +config DEBUG_UART_XEN | |
191 | + bool "debug uart xen" | |
192 | + | |
190 | 193 | config DEBUG_UART_ALTERA_JTAGUART |
191 | 194 | bool "Altera JTAG UART" |
192 | 195 | help |
... | ... | @@ -394,6 +397,7 @@ |
394 | 397 | hex "Base address of UART" |
395 | 398 | depends on DEBUG_UART |
396 | 399 | default 0 if DEBUG_UART_SANDBOX |
400 | + default 0 if DEBUG_UART_XEN | |
397 | 401 | help |
398 | 402 | This is the base address of your UART for memory-mapped UARTs. |
399 | 403 | |
... | ... | @@ -404,6 +408,7 @@ |
404 | 408 | int "UART input clock" |
405 | 409 | depends on DEBUG_UART |
406 | 410 | default 0 if DEBUG_UART_SANDBOX |
411 | + default 0 if DEBUG_UART_XEN | |
407 | 412 | help |
408 | 413 | The UART input clock determines the speed of the internal UART |
409 | 414 | circuitry. The baud rate is derived from this by dividing the input |
drivers/serial/serial_xen.c
... | ... | @@ -5,67 +5,215 @@ |
5 | 5 | */ |
6 | 6 | |
7 | 7 | #include <common.h> |
8 | +#include <debug_uart.h> | |
8 | 9 | #include <dm.h> |
9 | 10 | #include <errno.h> |
11 | +#include <fdtdec.h> | |
12 | +#include <linux/compiler.h> | |
10 | 13 | #include <serial.h> |
11 | 14 | |
12 | -extern void xenprintf(const char *buf); | |
13 | -extern void xenprintc(const char c); | |
14 | -#ifndef CONFIG_DM_SERIAL | |
15 | +#include <xen/events.h> | |
16 | +#include <xen/hvm.h> | |
17 | +#include <xen/interface/sched.h> | |
18 | +#include <xen/interface/hvm/hvm_op.h> | |
19 | +#include <xen/interface/hvm/params.h> | |
20 | +#include <xen/interface/io/console.h> | |
21 | +#include <xen/interface/io/ring.h> | |
15 | 22 | |
16 | -static void xen_debug_serial_putc(const char c) | |
23 | +DECLARE_GLOBAL_DATA_PTR; | |
24 | + | |
25 | +#ifdef CONFIG_DM_SERIAL | |
26 | +struct xen_uart_priv { | |
27 | + struct xencons_interface *intf; | |
28 | + u32 evtchn; | |
29 | + int vtermno; | |
30 | + struct hvc_struct *hvc; | |
31 | + grant_ref_t gntref; | |
32 | +}; | |
33 | + | |
34 | +int xen_serial_setbrg(struct udevice *dev, int baudrate) | |
17 | 35 | { |
18 | - /* If \n, also do \r */ | |
19 | - if (c == '\n') | |
20 | - serial_putc('\r'); | |
36 | + return 0; | |
37 | +} | |
21 | 38 | |
22 | - xenprintc(c); | |
39 | +static int xen_serial_probe(struct udevice *dev) | |
40 | +{ | |
41 | + struct xen_uart_priv *priv = dev_get_priv(dev); | |
42 | + u64 v = 0; | |
43 | + unsigned long gfn; | |
44 | + int r; | |
45 | + | |
46 | + r = hvm_get_parameter(HVM_PARAM_CONSOLE_EVTCHN, &v); | |
47 | + if (r < 0 || v == 0) | |
48 | + return r; | |
49 | + | |
50 | + priv->evtchn = v; | |
51 | + | |
52 | + r = hvm_get_parameter(HVM_PARAM_CONSOLE_PFN, &v); | |
53 | + if (r < 0 || v == 0) | |
54 | + return -ENODEV; | |
55 | + | |
56 | + gfn = v; | |
57 | + | |
58 | + priv->intf = (struct xencons_interface *)(gfn << XEN_PAGE_SHIFT); | |
59 | + if (!priv->intf) | |
60 | + return -EINVAL; | |
61 | + | |
62 | + return 0; | |
23 | 63 | } |
24 | 64 | |
25 | -static void xen_debug_serial_puts(const char *buf) | |
65 | +static int xen_serial_pending(struct udevice *dev, bool input) | |
26 | 66 | { |
27 | - xenprintf(buf); | |
67 | + struct xen_uart_priv *priv = dev_get_priv(dev); | |
68 | + struct xencons_interface *intf = priv->intf; | |
69 | + | |
70 | + if (!input || intf->in_cons == intf->in_prod) | |
71 | + return 0; | |
72 | + | |
73 | + return 1; | |
28 | 74 | } |
29 | 75 | |
30 | -static int xen_debug_serial_start(void) | |
76 | +static int xen_serial_getc(struct udevice *dev) | |
31 | 77 | { |
32 | - return 0; | |
78 | + struct xen_uart_priv *priv = dev_get_priv(dev); | |
79 | + struct xencons_interface *intf = priv->intf; | |
80 | + XENCONS_RING_IDX cons; | |
81 | + char c; | |
82 | + | |
83 | + while (intf->in_cons == intf->in_prod) { | |
84 | + mb(); /* wait */ | |
85 | + } | |
86 | + | |
87 | + cons = intf->in_cons; | |
88 | + mb(); /* get pointers before reading ring */ | |
89 | + | |
90 | + c = intf->in[MASK_XENCONS_IDX(cons++, intf->in)]; | |
91 | + | |
92 | + mb(); /* read ring before consuming */ | |
93 | + intf->in_cons = cons; | |
94 | + | |
95 | + notify_remote_via_evtchn(priv->evtchn); | |
96 | + | |
97 | + return c; | |
33 | 98 | } |
34 | 99 | |
35 | -static void xen_debug_serial_setbrg(void) | |
100 | +static int __write_console(struct udevice *dev, const char *data, int len) | |
36 | 101 | { |
102 | + struct xen_uart_priv *priv = dev_get_priv(dev); | |
103 | + struct xencons_interface *intf = priv->intf; | |
104 | + XENCONS_RING_IDX cons, prod; | |
105 | + int sent = 0; | |
37 | 106 | |
107 | + cons = intf->out_cons; | |
108 | + prod = intf->out_prod; | |
109 | + mb(); /* Update pointer */ | |
110 | + | |
111 | + WARN_ON((prod - cons) > sizeof(intf->out)); | |
112 | + | |
113 | + while ((sent < len) && ((prod - cons) < sizeof(intf->out))) | |
114 | + intf->out[MASK_XENCONS_IDX(prod++, intf->out)] = data[sent++]; | |
115 | + | |
116 | + mb(); /* Update data before pointer */ | |
117 | + intf->out_prod = prod; | |
118 | + | |
119 | + if (sent) | |
120 | + notify_remote_via_evtchn(priv->evtchn); | |
121 | + | |
122 | + if (data[sent - 1] == '\n') | |
123 | + serial_puts("\r"); | |
124 | + | |
125 | + return sent; | |
38 | 126 | } |
39 | 127 | |
40 | -static int xen_debug_serial_getc(void) | |
128 | +static int write_console(struct udevice *dev, const char *data, int len) | |
41 | 129 | { |
130 | + /* | |
131 | + * Make sure the whole buffer is emitted, polling if | |
132 | + * necessary. We don't ever want to rely on the hvc daemon | |
133 | + * because the most interesting console output is when the | |
134 | + * kernel is crippled. | |
135 | + */ | |
136 | + while (len) { | |
137 | + int sent = __write_console(dev, data, len); | |
138 | + | |
139 | + data += sent; | |
140 | + len -= sent; | |
141 | + | |
142 | + if (unlikely(len)) | |
143 | + HYPERVISOR_sched_op(SCHEDOP_yield, NULL); | |
144 | + } | |
145 | + | |
42 | 146 | return 0; |
43 | 147 | } |
44 | 148 | |
45 | -static int xen_debug_serial_tstc(void) | |
149 | +static int xen_serial_puts(struct udevice *dev, const char *str) | |
46 | 150 | { |
151 | +#ifdef CONFIG_SPL_BUILD | |
152 | + (void)HYPERVISOR_console_io(CONSOLEIO_write, strlen(str), str); | |
153 | +#else | |
154 | + write_console(dev, str, strlen(str)); | |
155 | +#endif | |
156 | + | |
47 | 157 | return 0; |
48 | 158 | } |
49 | 159 | |
50 | -static struct serial_device xen_debug_serial_drv = { | |
51 | - .name = "xen_debug_serial", | |
52 | - .start = xen_debug_serial_start, | |
53 | - .stop = NULL, | |
54 | - .setbrg = xen_debug_serial_setbrg, | |
55 | - .putc = xen_debug_serial_putc, | |
56 | - .puts = xen_debug_serial_puts, | |
57 | - .getc = xen_debug_serial_getc, | |
58 | - .tstc = xen_debug_serial_tstc, | |
160 | +static int xen_serial_putc(struct udevice *dev, const char ch) | |
161 | +{ | |
162 | +#ifdef CONFIG_SPL_BUILD | |
163 | + (void)HYPERVISOR_console_io(CONSOLEIO_write, 1, &ch); | |
164 | +#else | |
165 | + write_console(dev, &ch, 1); | |
166 | +#endif | |
167 | + | |
168 | + return 0; | |
169 | +} | |
170 | + | |
171 | +static const struct dm_serial_ops xen_serial_ops = { | |
172 | + .puts = xen_serial_puts, | |
173 | + .putc = xen_serial_putc, | |
174 | + .getc = xen_serial_getc, | |
175 | + .pending = xen_serial_pending, | |
59 | 176 | }; |
60 | 177 | |
61 | -void xen_debug_serial_initialize(void) | |
178 | +static const struct udevice_id xen_serial_ids[] = { | |
179 | + { .compatible = "xen,xen" }, | |
180 | + { } | |
181 | +}; | |
182 | + | |
183 | +U_BOOT_DRIVER(serial_xen) = { | |
184 | + .name = "serial_xen", | |
185 | + .id = UCLASS_SERIAL, | |
186 | + .of_match = xen_serial_ids, | |
187 | + .priv_auto_alloc_size = sizeof(struct xen_uart_priv), | |
188 | + .probe = xen_serial_probe, | |
189 | + .ops = &xen_serial_ops, | |
190 | + .flags = DM_FLAG_PRE_RELOC | DM_FLAG_IGNORE_DEFAULT_CLKS, | |
191 | +}; | |
192 | +#else | |
193 | +__weak struct serial_device *default_serial_console(void) | |
62 | 194 | { |
63 | - serial_register(&xen_debug_serial_drv); | |
195 | + return NULL; | |
64 | 196 | } |
65 | 197 | |
66 | -__weak struct serial_device *default_serial_console(void) | |
198 | +#endif | |
199 | + | |
200 | +#ifdef CONFIG_DEBUG_UART_XEN | |
201 | +void _debug_uart_init(void) | |
67 | 202 | { |
68 | - return &xen_debug_serial_drv; | |
69 | 203 | } |
204 | + | |
205 | +void _debug_uart_putc(int ch) | |
206 | +{ | |
207 | + /* If \n, also do \r */ | |
208 | + if (ch == '\n') | |
209 | + serial_putc('\r'); | |
210 | + | |
211 | + (void)HYPERVISOR_console_io(CONSOLEIO_write, 1, &ch); | |
212 | + | |
213 | + return; | |
214 | +} | |
215 | + | |
216 | +DEBUG_UART_FUNCS | |
217 | + | |
70 | 218 | #endif |