blob: e05805f63726a399cfd030546939bc039627dd8c [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 */
Peng Fan8162f8f2020-08-06 12:42:50 +03006#include <cpu_func.h>
7#include <dm.h>
8#include <serial.h>
9#include <watchdog.h>
Simon Glass3ba929a2020-10-30 21:38:53 -060010#include <asm/global_data.h>
Peng Fan8162f8f2020-08-06 12:42:50 +030011
12#include <linux/bug.h>
13
14#include <xen/hvm.h>
15#include <xen/events.h>
16
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>
22
23DECLARE_GLOBAL_DATA_PTR;
24
25u32 console_evtchn;
26
27/*
28 * struct xen_uart_priv - Structure representing a Xen UART info
29 * @intf: Console I/O interface for Xen guest OSes
30 * @evtchn: Console event channel
31 */
32struct xen_uart_priv {
33 struct xencons_interface *intf;
34 u32 evtchn;
35};
36
37int xen_serial_setbrg(struct udevice *dev, int baudrate)
38{
39 return 0;
40}
41
42static int xen_serial_probe(struct udevice *dev)
43{
44 struct xen_uart_priv *priv = dev_get_priv(dev);
45 u64 val = 0;
46 unsigned long gfn;
47 int ret;
48
49 ret = hvm_get_parameter(HVM_PARAM_CONSOLE_EVTCHN, &val);
50 if (ret < 0 || val == 0)
51 return ret;
52
53 priv->evtchn = val;
54 console_evtchn = val;
55
56 ret = hvm_get_parameter(HVM_PARAM_CONSOLE_PFN, &val);
57 if (ret < 0)
58 return ret;
59
60 if (!val)
61 return -EINVAL;
62
63 gfn = val;
64 priv->intf = (struct xencons_interface *)(gfn << XEN_PAGE_SHIFT);
65
66 return 0;
67}
68
69static int xen_serial_pending(struct udevice *dev, bool input)
70{
71 struct xen_uart_priv *priv = dev_get_priv(dev);
72 struct xencons_interface *intf = priv->intf;
73
74 if (!input || intf->in_cons == intf->in_prod)
75 return 0;
76
77 return 1;
78}
79
80static int xen_serial_getc(struct udevice *dev)
81{
82 struct xen_uart_priv *priv = dev_get_priv(dev);
83 struct xencons_interface *intf = priv->intf;
84 XENCONS_RING_IDX cons;
85 char c;
86
87 while (intf->in_cons == intf->in_prod)
88 mb(); /* wait */
89
90 cons = intf->in_cons;
91 mb(); /* get pointers before reading ring */
92
93 c = intf->in[MASK_XENCONS_IDX(cons++, intf->in)];
94
95 mb(); /* read ring before consuming */
96 intf->in_cons = cons;
97
98 notify_remote_via_evtchn(priv->evtchn);
99
100 return c;
101}
102
103static int __write_console(struct udevice *dev, const char *data, int len)
104{
105 struct xen_uart_priv *priv = dev_get_priv(dev);
106 struct xencons_interface *intf = priv->intf;
107 XENCONS_RING_IDX cons, prod;
108 int sent = 0;
109
110 cons = intf->out_cons;
111 prod = intf->out_prod;
112 mb(); /* Update pointer */
113
114 WARN_ON((prod - cons) > sizeof(intf->out));
115
116 while ((sent < len) && ((prod - cons) < sizeof(intf->out)))
117 intf->out[MASK_XENCONS_IDX(prod++, intf->out)] = data[sent++];
118
119 mb(); /* Update data before pointer */
120 intf->out_prod = prod;
121
122 if (sent)
123 notify_remote_via_evtchn(priv->evtchn);
124
125 return sent;
126}
127
128static int write_console(struct udevice *dev, const char *data, int len)
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
146 return 0;
147}
148
149static int xen_serial_putc(struct udevice *dev, const char ch)
150{
151 write_console(dev, &ch, 1);
152
153 return 0;
154}
155
156static const struct dm_serial_ops xen_serial_ops = {
157 .putc = xen_serial_putc,
158 .getc = xen_serial_getc,
159 .pending = xen_serial_pending,
160};
161
162#if CONFIG_IS_ENABLED(OF_CONTROL)
163static const struct udevice_id xen_serial_ids[] = {
164 { .compatible = "xen,xen" },
165 { }
166};
167#endif
168
169U_BOOT_DRIVER(serial_xen) = {
170 .name = "serial_xen",
171 .id = UCLASS_SERIAL,
172#if CONFIG_IS_ENABLED(OF_CONTROL)
173 .of_match = xen_serial_ids,
174#endif
Simon Glass8a2b47f2020-12-03 16:55:17 -0700175 .priv_auto = sizeof(struct xen_uart_priv),
Peng Fan8162f8f2020-08-06 12:42:50 +0300176 .probe = xen_serial_probe,
177 .ops = &xen_serial_ops,
Tom Rini556f5ab2020-10-23 09:16:13 -0400178#if !CONFIG_IS_ENABLED(OF_CONTROL)
Peng Fan8162f8f2020-08-06 12:42:50 +0300179 .flags = DM_FLAG_PRE_RELOC,
Tom Rini556f5ab2020-10-23 09:16:13 -0400180#endif
Peng Fan8162f8f2020-08-06 12:42:50 +0300181};