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 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 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 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 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 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 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 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 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