Commit 75a9833506aa13c1d5a8641c81ac951c41ced55e
1 parent
c96a9844cd
Exists in
smarc_8mm-imx_v2018.03_4.14.98_2.0.0_ga
and in
5 other branches
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: Peng Fan <peng.fan@nxp.com> Reviewed-by: Flynn xu <flynn.xu@nxp.com>
Showing 1 changed file with 168 additions and 28 deletions Inline Diff
drivers/serial/serial_xen.c
1 | /* | 1 | /* |
2 | * Copyright 2018 NXP | 2 | * Copyright 2018 NXP |
3 | * | 3 | * |
4 | * SPDX-License-Identifier: GPL-2.0+ | 4 | * SPDX-License-Identifier: GPL-2.0+ |
5 | */ | 5 | */ |
6 | 6 | ||
7 | #include <common.h> | 7 | #include <common.h> |
8 | #include <debug_uart.h> | ||
8 | #include <dm.h> | 9 | #include <dm.h> |
9 | #include <errno.h> | 10 | #include <errno.h> |
11 | #include <fdtdec.h> | ||
12 | #include <linux/compiler.h> | ||
10 | #include <serial.h> | 13 | #include <serial.h> |
11 | 14 | ||
12 | extern void xenprintf(const char *buf); | 15 | #include <xen/events.h> |
13 | extern void xenprintc(const char c); | 16 | #include <xen/hvm.h> |
14 | #ifndef CONFIG_DM_SERIAL | 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 */ | 36 | return 0; |
19 | if (c == '\n') | 37 | } |
20 | serial_putc('\r'); | ||
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 | return 0; | 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 | write_console(dev, str, strlen(str)); | ||
152 | |||
47 | return 0; | 153 | return 0; |
48 | } | 154 | } |
49 | 155 | ||
50 | static struct serial_device xen_debug_serial_drv = { | 156 | static int xen_serial_putc(struct udevice *dev, const char ch) |
51 | .name = "xen_debug_serial", | 157 | { |
52 | .start = xen_debug_serial_start, | 158 | write_console(dev, &ch, 1); |
53 | .stop = NULL, | 159 | |
54 | .setbrg = xen_debug_serial_setbrg, | 160 | return 0; |
55 | .putc = xen_debug_serial_putc, | 161 | } |
56 | .puts = xen_debug_serial_puts, | 162 | |
57 | .getc = xen_debug_serial_getc, | 163 | static const struct dm_serial_ops xen_serial_ops = { |
58 | .tstc = xen_debug_serial_tstc, | 164 | .puts = xen_serial_puts, |
165 | .putc = xen_serial_putc, | ||
166 | .getc = xen_serial_getc, | ||
167 | .pending = xen_serial_pending, | ||
59 | }; | 168 | }; |
60 | 169 | ||
61 | void xen_debug_serial_initialize(void) | 170 | static const struct udevice_id xen_serial_ids[] = { |
171 | { .compatible = "xen,xen" }, | ||
172 | { } | ||
173 | }; | ||
174 | |||
175 | U_BOOT_DRIVER(serial_xen) = { | ||
176 | .name = "serial_xen", | ||
177 | .id = UCLASS_SERIAL, | ||
178 | .of_match = xen_serial_ids, | ||
179 | .priv_auto_alloc_size = sizeof(struct xen_uart_priv), | ||
180 | .probe = xen_serial_probe, | ||
181 | .ops = &xen_serial_ops, | ||
182 | .flags = DM_FLAG_PRE_RELOC, | ||
183 | }; | ||
184 | #else | ||
185 | __weak struct serial_device *default_serial_console(void) | ||
62 | { | 186 | { |
63 | serial_register(&xen_debug_serial_drv); | 187 | return NULL; |
64 | } | 188 | } |
65 | 189 | ||
66 | __weak struct serial_device *default_serial_console(void) | 190 | #endif |
191 | |||
192 | #ifdef CONFIG_DEBUG_UART_XEN | ||
193 | void _debug_uart_init(void) | ||
67 | { | 194 | { |
68 | return &xen_debug_serial_drv; | ||
69 | } | 195 | } |
196 | |||
197 | void _debug_uart_putc(int ch) | ||
198 | { | ||
199 | /* If \n, also do \r */ | ||
200 | if (ch == '\n') | ||
201 | serial_putc('\r'); | ||
202 | |||
203 | (void)HYPERVISOR_console_io(CONSOLEIO_write, 1, &ch); | ||
204 | |||
205 | return; | ||
206 | } | ||
207 | |||
208 | DEBUG_UART_FUNCS | ||
209 |