1 /*-
2 * Copyright (c) 2014-2015 Ruslan Bukin <br@bsdpad.com>
3 * All rights reserved.
4 *
5 * This software was developed by SRI International and the University of
6 * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
7 * ("CTSRD"), as part of the DARPA CRASH research programme.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31 /*
32 * BERI interface for Virtio MMIO bus.
33 *
34 * This driver provides interrupt-engine for software-implemented
35 * Virtio MMIO backend.
36 */
37
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/bus.h>
41 #include <sys/kernel.h>
42 #include <sys/module.h>
43 #include <sys/malloc.h>
44 #include <sys/rman.h>
45 #include <sys/timeet.h>
46 #include <sys/timetc.h>
47 #include <sys/watchdog.h>
48
49 #include <machine/bus.h>
50 #include <machine/fdt.h>
51 #include <machine/cpu.h>
52 #include <machine/cache.h>
53
54 #include <dev/fdt/fdt_common.h>
55 #include <dev/ofw/openfirm.h>
56 #include <dev/ofw/ofw_bus.h>
57 #include <dev/ofw/ofw_bus_subr.h>
58
59 #include <dev/beri/virtio/virtio_mmio_platform.h>
60 #include <dev/virtio/mmio/virtio_mmio.h>
61 #include <dev/altera/pio/pio.h>
62
63 #include "virtio_mmio_if.h"
64 #include "pio_if.h"
65
66 static void platform_intr(void *arg);
67
68 struct virtio_mmio_platform_softc {
69 struct resource *res[1];
70 void *ih;
71 bus_space_tag_t bst;
72 bus_space_handle_t bsh;
73 device_t dev;
74 void (*intr_handler)(void *);
75 void *ih_user;
76 device_t pio_recv;
77 device_t pio_send;
78 int use_pio;
79 };
80
81 static int
setup_pio(struct virtio_mmio_platform_softc * sc,char * name,device_t * dev)82 setup_pio(struct virtio_mmio_platform_softc *sc, char *name, device_t *dev)
83 {
84 phandle_t pio_node;
85 struct fdt_ic *ic;
86 phandle_t xref;
87 phandle_t node;
88
89 if ((node = ofw_bus_get_node(sc->dev)) == -1)
90 return (ENXIO);
91
92 if (OF_searchencprop(node, name, &xref,
93 sizeof(xref)) == -1) {
94 return (ENXIO);
95 }
96
97 pio_node = OF_node_from_xref(xref);
98 SLIST_FOREACH(ic, &fdt_ic_list_head, fdt_ics) {
99 if (ic->iph == pio_node) {
100 *dev = ic->dev;
101 PIO_CONFIGURE(*dev, PIO_OUT_ALL,
102 PIO_UNMASK_ALL);
103 return (0);
104 }
105 }
106
107 return (ENXIO);
108 }
109
110 static int
virtio_mmio_platform_probe(device_t dev)111 virtio_mmio_platform_probe(device_t dev)
112 {
113
114 if (!ofw_bus_status_okay(dev))
115 return (ENXIO);
116
117 if (!ofw_bus_is_compatible(dev, "beri,virtio_mmio_platform"))
118 return (ENXIO);
119
120 device_set_desc(dev, "Virtio MMIO platform");
121 return (BUS_PROBE_DEFAULT);
122 }
123
124 static int
virtio_mmio_platform_attach(device_t dev)125 virtio_mmio_platform_attach(device_t dev)
126 {
127 struct virtio_mmio_platform_softc *sc;
128 struct fdt_ic *fic;
129 phandle_t node;
130
131 sc = device_get_softc(dev);
132 sc->dev = dev;
133 sc->use_pio = 1;
134
135 if ((setup_pio(sc, "pio-send", &sc->pio_send) != 0) ||
136 (setup_pio(sc, "pio-recv", &sc->pio_recv) != 0))
137 sc->use_pio = 0;
138
139 if ((node = ofw_bus_get_node(sc->dev)) == -1)
140 return (ENXIO);
141
142 fic = malloc(sizeof(*fic), M_DEVBUF, M_WAITOK|M_ZERO);
143 fic->iph = node;
144 fic->dev = dev;
145 SLIST_INSERT_HEAD(&fdt_ic_list_head, fic, fdt_ics);
146
147 return (0);
148 }
149
150 static int
platform_prewrite(device_t dev,size_t offset,int val)151 platform_prewrite(device_t dev, size_t offset, int val)
152 {
153 struct virtio_mmio_platform_softc *sc;
154
155 sc = device_get_softc(dev);
156
157 switch (offset) {
158 case (VIRTIO_MMIO_QUEUE_NOTIFY):
159 mips_dcache_wbinv_all();
160 break;
161 default:
162 break;
163 }
164
165 return (0);
166 }
167
168 static int
platform_note(device_t dev,size_t offset,int val)169 platform_note(device_t dev, size_t offset, int val)
170 {
171 struct virtio_mmio_platform_softc *sc;
172 int note;
173 int i;
174
175 sc = device_get_softc(dev);
176
177 switch (offset) {
178 case (VIRTIO_MMIO_QUEUE_NOTIFY):
179 if (val == 0)
180 note = Q_NOTIFY;
181 else if (val == 1)
182 note = Q_NOTIFY1;
183 else
184 note = 0;
185 break;
186 case (VIRTIO_MMIO_QUEUE_PFN):
187 note = Q_PFN;
188 break;
189 case (VIRTIO_MMIO_QUEUE_SEL):
190 note = Q_SEL;
191 break;
192 default:
193 note = 0;
194 }
195
196 if (note) {
197 mips_dcache_wbinv_all();
198
199 if (!sc->use_pio)
200 return (0);
201
202 PIO_SET(sc->pio_send, note, 1);
203
204 /*
205 * Wait until host ack the request.
206 * Usually done within few cycles.
207 * TODO: bad
208 */
209
210 for (i = 100; i > 0; i--) {
211 if (PIO_READ(sc->pio_send) == 0)
212 break;
213 }
214
215 if (i == 0)
216 device_printf(sc->dev, "Warning: host busy\n");
217 }
218
219 return (0);
220 }
221
222 static void
platform_intr(void * arg)223 platform_intr(void *arg)
224 {
225 struct virtio_mmio_platform_softc *sc;
226 int reg;
227
228 sc = arg;
229
230 if (sc->use_pio) {
231 /* Read pending */
232 reg = PIO_READ(sc->pio_recv);
233
234 /* Ack */
235 PIO_SET(sc->pio_recv, reg, 0);
236 }
237
238 /* Writeback, invalidate cache */
239 mips_dcache_wbinv_all();
240
241 if (sc->intr_handler != NULL)
242 sc->intr_handler(sc->ih_user);
243 }
244
245 static int
platform_setup_intr(device_t dev,device_t mmio_dev,void * intr_handler,void * ih_user)246 platform_setup_intr(device_t dev, device_t mmio_dev,
247 void *intr_handler, void *ih_user)
248 {
249 struct virtio_mmio_platform_softc *sc;
250 int rid;
251
252 sc = device_get_softc(dev);
253
254 sc->intr_handler = intr_handler;
255 sc->ih_user = ih_user;
256
257 if (sc->use_pio) {
258 PIO_SETUP_IRQ(sc->pio_recv, platform_intr, sc);
259 return (0);
260 }
261
262 rid = 0;
263 sc->res[0] = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
264 RF_ACTIVE);
265 if (!sc->res[0]) {
266 device_printf(dev, "Can't allocate interrupt\n");
267 return (ENXIO);
268 }
269
270 if (bus_setup_intr(dev, sc->res[0], INTR_TYPE_MISC | INTR_MPSAFE,
271 NULL, platform_intr, sc, &sc->ih)) {
272 device_printf(dev, "Can't setup the interrupt\n");
273 return (ENXIO);
274 }
275
276 return (0);
277 }
278
279 static int
platform_poll(device_t dev)280 platform_poll(device_t dev)
281 {
282
283 mips_dcache_wbinv_all();
284
285 return (0);
286 }
287
288 static device_method_t virtio_mmio_platform_methods[] = {
289 DEVMETHOD(device_probe, virtio_mmio_platform_probe),
290 DEVMETHOD(device_attach, virtio_mmio_platform_attach),
291
292 /* virtio_mmio_if.h */
293 DEVMETHOD(virtio_mmio_prewrite, platform_prewrite),
294 DEVMETHOD(virtio_mmio_note, platform_note),
295 DEVMETHOD(virtio_mmio_poll, platform_poll),
296 DEVMETHOD(virtio_mmio_setup_intr, platform_setup_intr),
297 DEVMETHOD_END
298 };
299
300 static driver_t virtio_mmio_platform_driver = {
301 "virtio_mmio_platform",
302 virtio_mmio_platform_methods,
303 sizeof(struct virtio_mmio_platform_softc),
304 };
305
306 DRIVER_MODULE(virtio_mmio_platform, simplebus, virtio_mmio_platform_driver,
307 0, 0);
308