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 66static void platform_intr(void *arg); 67 68struct 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 81static int 82setup_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 110static int 111virtio_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 124static int 125virtio_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 150static int 151platform_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 168static int 169platform_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 222static void 223platform_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 245static int 246platform_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 279static int 280platform_poll(device_t dev) 281{ 282 283 mips_dcache_wbinv_all(); 284 285 return (0); 286} 287 288static 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 300static driver_t virtio_mmio_platform_driver = { 301 "virtio_mmio_platform", 302 virtio_mmio_platform_methods, 303 sizeof(struct virtio_mmio_platform_softc), 304}; 305 306DRIVER_MODULE(virtio_mmio_platform, simplebus, virtio_mmio_platform_driver, 307 0, 0); 308