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/cdefs.h> 39 #include <sys/param.h> 40 #include <sys/systm.h> 41 #include <sys/bus.h> 42 #include <sys/kernel.h> 43 #include <sys/module.h> 44 #include <sys/malloc.h> 45 #include <sys/rman.h> 46 #include <sys/timeet.h> 47 #include <sys/timetc.h> 48 #include <sys/watchdog.h> 49 50 #include <machine/bus.h> 51 #include <machine/fdt.h> 52 #include <machine/cpu.h> 53 #include <machine/cache.h> 54 55 #include <dev/fdt/fdt_common.h> 56 #include <dev/ofw/openfirm.h> 57 #include <dev/ofw/ofw_bus.h> 58 #include <dev/ofw/ofw_bus_subr.h> 59 60 #include <dev/beri/virtio/virtio_mmio_platform.h> 61 #include <dev/virtio/mmio/virtio_mmio.h> 62 #include <dev/altera/pio/pio.h> 63 64 #include "virtio_mmio_if.h" 65 #include "pio_if.h" 66 67 static void platform_intr(void *arg); 68 69 struct virtio_mmio_platform_softc { 70 struct resource *res[1]; 71 void *ih; 72 bus_space_tag_t bst; 73 bus_space_handle_t bsh; 74 device_t dev; 75 void (*intr_handler)(void *); 76 void *ih_user; 77 device_t pio_recv; 78 device_t pio_send; 79 int use_pio; 80 }; 81 82 static int 83 setup_pio(struct virtio_mmio_platform_softc *sc, char *name, device_t *dev) 84 { 85 phandle_t pio_node; 86 struct fdt_ic *ic; 87 phandle_t xref; 88 phandle_t node; 89 90 if ((node = ofw_bus_get_node(sc->dev)) == -1) 91 return (ENXIO); 92 93 if (OF_searchencprop(node, name, &xref, 94 sizeof(xref)) == -1) { 95 return (ENXIO); 96 } 97 98 pio_node = OF_node_from_xref(xref); 99 SLIST_FOREACH(ic, &fdt_ic_list_head, fdt_ics) { 100 if (ic->iph == pio_node) { 101 *dev = ic->dev; 102 PIO_CONFIGURE(*dev, PIO_OUT_ALL, 103 PIO_UNMASK_ALL); 104 return (0); 105 } 106 } 107 108 return (ENXIO); 109 } 110 111 static int 112 virtio_mmio_platform_probe(device_t dev) 113 { 114 115 if (!ofw_bus_status_okay(dev)) 116 return (ENXIO); 117 118 if (!ofw_bus_is_compatible(dev, "beri,virtio_mmio_platform")) 119 return (ENXIO); 120 121 device_set_desc(dev, "Virtio MMIO platform"); 122 return (BUS_PROBE_DEFAULT); 123 } 124 125 static int 126 virtio_mmio_platform_attach(device_t dev) 127 { 128 struct virtio_mmio_platform_softc *sc; 129 struct fdt_ic *fic; 130 phandle_t node; 131 132 sc = device_get_softc(dev); 133 sc->dev = dev; 134 sc->use_pio = 1; 135 136 if ((setup_pio(sc, "pio-send", &sc->pio_send) != 0) || 137 (setup_pio(sc, "pio-recv", &sc->pio_recv) != 0)) 138 sc->use_pio = 0; 139 140 if ((node = ofw_bus_get_node(sc->dev)) == -1) 141 return (ENXIO); 142 143 fic = malloc(sizeof(*fic), M_DEVBUF, M_WAITOK|M_ZERO); 144 fic->iph = node; 145 fic->dev = dev; 146 SLIST_INSERT_HEAD(&fdt_ic_list_head, fic, fdt_ics); 147 148 return (0); 149 } 150 151 static int 152 platform_prewrite(device_t dev, size_t offset, int val) 153 { 154 struct virtio_mmio_platform_softc *sc; 155 156 sc = device_get_softc(dev); 157 158 switch (offset) { 159 case (VIRTIO_MMIO_QUEUE_NOTIFY): 160 mips_dcache_wbinv_all(); 161 break; 162 default: 163 break; 164 } 165 166 return (0); 167 } 168 169 static int 170 platform_note(device_t dev, size_t offset, int val) 171 { 172 struct virtio_mmio_platform_softc *sc; 173 int note; 174 int i; 175 176 sc = device_get_softc(dev); 177 178 switch (offset) { 179 case (VIRTIO_MMIO_QUEUE_NOTIFY): 180 if (val == 0) 181 note = Q_NOTIFY; 182 else if (val == 1) 183 note = Q_NOTIFY1; 184 else 185 note = 0; 186 break; 187 case (VIRTIO_MMIO_QUEUE_PFN): 188 note = Q_PFN; 189 break; 190 case (VIRTIO_MMIO_QUEUE_SEL): 191 note = Q_SEL; 192 break; 193 default: 194 note = 0; 195 } 196 197 if (note) { 198 mips_dcache_wbinv_all(); 199 200 if (!sc->use_pio) 201 return (0); 202 203 PIO_SET(sc->pio_send, note, 1); 204 205 /* 206 * Wait until host ack the request. 207 * Usually done within few cycles. 208 * TODO: bad 209 */ 210 211 for (i = 100; i > 0; i--) { 212 if (PIO_READ(sc->pio_send) == 0) 213 break; 214 } 215 216 if (i == 0) 217 device_printf(sc->dev, "Warning: host busy\n"); 218 } 219 220 return (0); 221 } 222 223 static void 224 platform_intr(void *arg) 225 { 226 struct virtio_mmio_platform_softc *sc; 227 int reg; 228 229 sc = arg; 230 231 if (sc->use_pio) { 232 /* Read pending */ 233 reg = PIO_READ(sc->pio_recv); 234 235 /* Ack */ 236 PIO_SET(sc->pio_recv, reg, 0); 237 } 238 239 /* Writeback, invalidate cache */ 240 mips_dcache_wbinv_all(); 241 242 if (sc->intr_handler != NULL) 243 sc->intr_handler(sc->ih_user); 244 } 245 246 static int 247 platform_setup_intr(device_t dev, device_t mmio_dev, 248 void *intr_handler, void *ih_user) 249 { 250 struct virtio_mmio_platform_softc *sc; 251 int rid; 252 253 sc = device_get_softc(dev); 254 255 sc->intr_handler = intr_handler; 256 sc->ih_user = ih_user; 257 258 if (sc->use_pio) { 259 PIO_SETUP_IRQ(sc->pio_recv, platform_intr, sc); 260 return (0); 261 } 262 263 rid = 0; 264 sc->res[0] = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 265 RF_ACTIVE); 266 if (!sc->res[0]) { 267 device_printf(dev, "Can't allocate interrupt\n"); 268 return (ENXIO); 269 } 270 271 if (bus_setup_intr(dev, sc->res[0], INTR_TYPE_MISC | INTR_MPSAFE, 272 NULL, platform_intr, sc, &sc->ih)) { 273 device_printf(dev, "Can't setup the interrupt\n"); 274 return (ENXIO); 275 } 276 277 return (0); 278 } 279 280 static int 281 platform_poll(device_t dev) 282 { 283 284 mips_dcache_wbinv_all(); 285 286 return (0); 287 } 288 289 static device_method_t virtio_mmio_platform_methods[] = { 290 DEVMETHOD(device_probe, virtio_mmio_platform_probe), 291 DEVMETHOD(device_attach, virtio_mmio_platform_attach), 292 293 /* virtio_mmio_if.h */ 294 DEVMETHOD(virtio_mmio_prewrite, platform_prewrite), 295 DEVMETHOD(virtio_mmio_note, platform_note), 296 DEVMETHOD(virtio_mmio_poll, platform_poll), 297 DEVMETHOD(virtio_mmio_setup_intr, platform_setup_intr), 298 DEVMETHOD_END 299 }; 300 301 static driver_t virtio_mmio_platform_driver = { 302 "virtio_mmio_platform", 303 virtio_mmio_platform_methods, 304 sizeof(struct virtio_mmio_platform_softc), 305 }; 306 307 DRIVER_MODULE(virtio_mmio_platform, simplebus, virtio_mmio_platform_driver, 308 0, 0); 309