Merge branch 'master' of https://gitlab.denx.de/u-boot/custodians/u-boot-sunxi
[platform/kernel/u-boot.git] / drivers / serial / serial_xen.c
1 // 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>
8 #include <debug_uart.h>
9 #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>
19 #include <xen/interface/xen.h>
20 #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
25 #include <asm/xen/hypercall.h>
26
27 DECLARE_GLOBAL_DATA_PTR;
28
29 u32 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  */
36 struct xen_uart_priv {
37         struct xencons_interface *intf;
38         u32 evtchn;
39 };
40
41 int xen_serial_setbrg(struct udevice *dev, int baudrate)
42 {
43         return 0;
44 }
45
46 static 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
73 static 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
84 static 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
107 static 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
132 static 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
153 static int xen_serial_putc(struct udevice *dev, const char ch)
154 {
155         write_console(dev, &ch, 1);
156
157         return 0;
158 }
159
160 static 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)
167 static const struct udevice_id xen_serial_ids[] = {
168         { .compatible = "xen,xen" },
169         { }
170 };
171 #endif
172
173 U_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,
182         .flags                  = DM_FLAG_PRE_RELOC,
183 };
184
185 #if defined(CONFIG_DEBUG_UART_XEN)
186 static inline void _debug_uart_init(void) {}
187
188 static 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
198 DEBUG_UART_FUNCS
199
200 #endif