blob: 34c90ece40fc47d6ee1b3af0d5dc59e1e217f2e9 [file] [log] [blame]
Peng Fan8162f8f2020-08-06 12:42:50 +03001// SPDX-License-Identifier: GPL-2.0
2/*
3 * (C) 2018 NXP
4 * (C) 2020 EPAM Systems Inc.
5 */
6#include <common.h>
7#include <cpu_func.h>
AKASHI Takahiro84fd3162020-10-15 13:25:16 +09008#include <debug_uart.h>
Peng Fan8162f8f2020-08-06 12:42:50 +03009#include <dm.h>
10#include <serial.h>
11#include <watchdog.h>
12
13#include <linux/bug.h>
14
15#include <xen/hvm.h>
16#include <xen/events.h>
17
18#include <xen/interface/sched.h>
AKASHI Takahiro84fd3162020-10-15 13:25:16 +090019#include <xen/interface/xen.h>
Peng Fan8162f8f2020-08-06 12:42:50 +030020#include <xen/interface/hvm/hvm_op.h>
21#include <xen/interface/hvm/params.h>
22#include <xen/interface/io/console.h>
23#include <xen/interface/io/ring.h>
24
AKASHI Takahiro84fd3162020-10-15 13:25:16 +090025#include <asm/xen/hypercall.h>
26
Peng Fan8162f8f2020-08-06 12:42:50 +030027DECLARE_GLOBAL_DATA_PTR;
28
29u32 console_evtchn;
30
31/*
32 * struct xen_uart_priv - Structure representing a Xen UART info
33 * @intf: Console I/O interface for Xen guest OSes
34 * @evtchn: Console event channel
35 */
36struct xen_uart_priv {
37 struct xencons_interface *intf;
38 u32 evtchn;
39};
40
41int xen_serial_setbrg(struct udevice *dev, int baudrate)
42{
43 return 0;
44}
45
46static int xen_serial_probe(struct udevice *dev)
47{
48 struct xen_uart_priv *priv = dev_get_priv(dev);
49 u64 val = 0;
50 unsigned long gfn;
51 int ret;
52
53 ret = hvm_get_parameter(HVM_PARAM_CONSOLE_EVTCHN, &val);
54 if (ret < 0 || val == 0)
55 return ret;
56
57 priv->evtchn = val;
58 console_evtchn = val;
59
60 ret = hvm_get_parameter(HVM_PARAM_CONSOLE_PFN, &val);
61 if (ret < 0)
62 return ret;
63
64 if (!val)
65 return -EINVAL;
66
67 gfn = val;
68 priv->intf = (struct xencons_interface *)(gfn << XEN_PAGE_SHIFT);
69
70 return 0;
71}
72
73static int xen_serial_pending(struct udevice *dev, bool input)
74{
75 struct xen_uart_priv *priv = dev_get_priv(dev);
76 struct xencons_interface *intf = priv->intf;
77
78 if (!input || intf->in_cons == intf->in_prod)
79 return 0;
80
81 return 1;
82}
83
84static int xen_serial_getc(struct udevice *dev)
85{
86 struct xen_uart_priv *priv = dev_get_priv(dev);
87 struct xencons_interface *intf = priv->intf;
88 XENCONS_RING_IDX cons;
89 char c;
90
91 while (intf->in_cons == intf->in_prod)
92 mb(); /* wait */
93
94 cons = intf->in_cons;
95 mb(); /* get pointers before reading ring */
96
97 c = intf->in[MASK_XENCONS_IDX(cons++, intf->in)];
98
99 mb(); /* read ring before consuming */
100 intf->in_cons = cons;
101
102 notify_remote_via_evtchn(priv->evtchn);
103
104 return c;
105}
106
107static int __write_console(struct udevice *dev, const char *data, int len)
108{
109 struct xen_uart_priv *priv = dev_get_priv(dev);
110 struct xencons_interface *intf = priv->intf;
111 XENCONS_RING_IDX cons, prod;
112 int sent = 0;
113
114 cons = intf->out_cons;
115 prod = intf->out_prod;
116 mb(); /* Update pointer */
117
118 WARN_ON((prod - cons) > sizeof(intf->out));
119
120 while ((sent < len) && ((prod - cons) < sizeof(intf->out)))
121 intf->out[MASK_XENCONS_IDX(prod++, intf->out)] = data[sent++];
122
123 mb(); /* Update data before pointer */
124 intf->out_prod = prod;
125
126 if (sent)
127 notify_remote_via_evtchn(priv->evtchn);
128
129 return sent;
130}
131
132static int write_console(struct udevice *dev, const char *data, int len)
133{
134 /*
135 * Make sure the whole buffer is emitted, polling if
136 * necessary. We don't ever want to rely on the hvc daemon
137 * because the most interesting console output is when the
138 * kernel is crippled.
139 */
140 while (len) {
141 int sent = __write_console(dev, data, len);
142
143 data += sent;
144 len -= sent;
145
146 if (unlikely(len))
147 HYPERVISOR_sched_op(SCHEDOP_yield, NULL);
148 }
149
150 return 0;
151}
152
153static int xen_serial_putc(struct udevice *dev, const char ch)
154{
155 write_console(dev, &ch, 1);
156
157 return 0;
158}
159
160static const struct dm_serial_ops xen_serial_ops = {
161 .putc = xen_serial_putc,
162 .getc = xen_serial_getc,
163 .pending = xen_serial_pending,
164};
165
166#if CONFIG_IS_ENABLED(OF_CONTROL)
167static const struct udevice_id xen_serial_ids[] = {
168 { .compatible = "xen,xen" },
169 { }
170};
171#endif
172
173U_BOOT_DRIVER(serial_xen) = {
174 .name = "serial_xen",
175 .id = UCLASS_SERIAL,
176#if CONFIG_IS_ENABLED(OF_CONTROL)
177 .of_match = xen_serial_ids,
178#endif
179 .priv_auto_alloc_size = sizeof(struct xen_uart_priv),
180 .probe = xen_serial_probe,
181 .ops = &xen_serial_ops,
Peng Fan8162f8f2020-08-06 12:42:50 +0300182 .flags = DM_FLAG_PRE_RELOC,
Peng Fan8162f8f2020-08-06 12:42:50 +0300183};
184
AKASHI Takahiro84fd3162020-10-15 13:25:16 +0900185#if defined(CONFIG_DEBUG_UART_XEN)
186static inline void _debug_uart_init(void) {}
187
188static inline void _debug_uart_putc(int c)
189{
190#if CONFIG_IS_ENABLED(ARM)
191 xen_debug_putc(c);
192#else
193 /* the type cast should work on LE only */
194 HYPERVISOR_console_io(CONSOLEIO_write, 1, (char *)&ch);
195#endif
196}
197
198DEBUG_UART_FUNCS
199
200#endif