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