1 /* $NetBSD: virtio_mmio.c,v 1.14 2024/03/09 11:55:59 isaki Exp $ */ 2 /* $OpenBSD: virtio_mmio.c,v 1.2 2017/02/24 17:12:31 patrick Exp $ */ 3 4 /*- 5 * Copyright (c) 2024 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Jason R. Thorpe. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 /* 34 * Copyright (c) 2014 Patrick Wildt <patrick@blueri.se> 35 * Copyright (c) 2012 Stefan Fritsch. 36 * Copyright (c) 2010 Minoura Makoto. 37 * All rights reserved. 38 * 39 * Redistribution and use in source and binary forms, with or without 40 * modification, are permitted provided that the following conditions 41 * are met: 42 * 1. Redistributions of source code must retain the above copyright 43 * notice, this list of conditions and the following disclaimer. 44 * 2. Redistributions in binary form must reproduce the above copyright 45 * notice, this list of conditions and the following disclaimer in the 46 * documentation and/or other materials provided with the distribution. 47 * 48 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 49 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 50 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 51 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 52 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 53 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 54 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 55 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 56 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 57 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 58 */ 59 60 #include <sys/cdefs.h> 61 __KERNEL_RCSID(0, "$NetBSD: virtio_mmio.c,v 1.14 2024/03/09 11:55:59 isaki Exp $"); 62 63 #include <sys/param.h> 64 #include <sys/systm.h> 65 #include <sys/kernel.h> 66 #include <sys/device.h> 67 #include <sys/mutex.h> 68 69 #define VIRTIO_PRIVATE 70 #include <dev/virtio/virtio_mmiovar.h> 71 72 #define VIRTIO_MMIO_MAGIC ('v' | 'i' << 8 | 'r' << 16 | 't' << 24) 73 74 #define VIRTIO_MMIO_MAGIC_VALUE 0x000 75 #define VIRTIO_MMIO_VERSION 0x004 76 #define VIRTIO_MMIO_DEVICE_ID 0x008 77 #define VIRTIO_MMIO_VENDOR_ID 0x00c 78 #define VIRTIO_MMIO_DEVICE_FEATURES 0x010 /* "HostFeatures" in v1 */ 79 #define VIRTIO_MMIO_DEVICE_FEATURES_SEL 0x014 /* "HostFeaturesSel" in v1 */ 80 #define VIRTIO_MMIO_DRIVER_FEATURES 0x020 /* "GuestFeatures" in v1 */ 81 #define VIRTIO_MMIO_DRIVER_FEATURES_SEL 0x024 /* "GuestFeaturesSel" in v1 */ 82 #define VIRTIO_MMIO_V1_GUEST_PAGE_SIZE 0x028 83 #define VIRTIO_MMIO_QUEUE_SEL 0x030 84 #define VIRTIO_MMIO_QUEUE_NUM_MAX 0x034 85 #define VIRTIO_MMIO_QUEUE_NUM 0x038 86 #define VIRTIO_MMIO_V1_QUEUE_ALIGN 0x03c 87 #define VIRTIO_MMIO_V1_QUEUE_PFN 0x040 88 #define VIRTIO_MMIO_QUEUE_READY 0x044 89 #define VIRTIO_MMIO_QUEUE_NOTIFY 0x050 90 #define VIRTIO_MMIO_INTERRUPT_STATUS 0x060 91 #define VIRTIO_MMIO_INTERRUPT_ACK 0x064 92 #define VIRTIO_MMIO_STATUS 0x070 93 #define VIRTIO_MMIO_V2_QUEUE_DESC_LOW 0x080 94 #define VIRTIO_MMIO_V2_QUEUE_DESC_HIGH 0x084 95 #define VIRTIO_MMIO_V2_QUEUE_AVAIL_LOW 0x090 96 #define VIRTIO_MMIO_V2_QUEUE_AVAIL_HIGH 0x094 97 #define VIRTIO_MMIO_V2_QUEUE_USED_LOW 0x0a0 98 #define VIRTIO_MMIO_V2_QUEUE_USED_HIGH 0x0a4 99 #define VIRTIO_MMIO_V2_CONFIG_GEN 0x0fc 100 #define VIRTIO_MMIO_CONFIG 0x100 101 102 /* 103 * MMIO configuration space for virtio-mmio v1 is in guest byte order. 104 * 105 * XXX For big-endian aarch64 and arm, see note in virtio_pci.c. 106 */ 107 108 #if (defined(__aarch64__) || defined(__arm__)) && BYTE_ORDER == BIG_ENDIAN 109 # define READ_ENDIAN LITTLE_ENDIAN 110 # define STRUCT_ENDIAN BIG_ENDIAN 111 #elif BYTE_ORDER == BIG_ENDIAN 112 # define READ_ENDIAN BIG_ENDIAN 113 # define STRUCT_ENDIAN BIG_ENDIAN 114 #else 115 # define READ_ENDIAN LITTLE_ENDIAN 116 # define STRUCT_ENDIAN LITTLE_ENDIAN 117 #endif 118 119 120 static void virtio_mmio_kick(struct virtio_softc *, uint16_t); 121 static uint16_t virtio_mmio_read_queue_size(struct virtio_softc *, uint16_t); 122 static void virtio_mmio_v1_setup_queue(struct virtio_softc *, uint16_t, uint64_t); 123 static void virtio_mmio_v2_setup_queue(struct virtio_softc *, uint16_t, uint64_t); 124 static int virtio_mmio_get_status(struct virtio_softc *); 125 static void virtio_mmio_set_status(struct virtio_softc *, int); 126 static void virtio_mmio_negotiate_features(struct virtio_softc *, uint64_t); 127 static int virtio_mmio_alloc_interrupts(struct virtio_softc *); 128 static void virtio_mmio_free_interrupts(struct virtio_softc *); 129 static int virtio_mmio_setup_interrupts(struct virtio_softc *, int); 130 131 static uint32_t 132 virtio_mmio_reg_read(struct virtio_mmio_softc *sc, bus_addr_t reg) 133 { 134 uint32_t val; 135 136 val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, reg); 137 if (sc->sc_le_regs) { 138 val = le32toh(val); 139 } 140 return val; 141 } 142 143 static void 144 virtio_mmio_reg_write(struct virtio_mmio_softc *sc, bus_addr_t reg, 145 uint32_t val) 146 { 147 if (sc->sc_le_regs) { 148 val = htole32(val); 149 } 150 bus_space_write_4(sc->sc_iot, sc->sc_ioh, reg, val); 151 } 152 153 static void 154 virtio_mmio_v2_set_addr(struct virtio_mmio_softc *sc, bus_addr_t reg, 155 uint64_t addr) 156 { 157 virtio_mmio_reg_write(sc, reg, BUS_ADDR_LO32(addr)); 158 virtio_mmio_reg_write(sc, reg + 4, BUS_ADDR_HI32(addr)); 159 } 160 161 static const struct virtio_ops virtio_mmio_v1_ops = { 162 .kick = virtio_mmio_kick, 163 .read_queue_size = virtio_mmio_read_queue_size, 164 .setup_queue = virtio_mmio_v1_setup_queue, 165 .set_status = virtio_mmio_set_status, 166 .neg_features = virtio_mmio_negotiate_features, 167 .alloc_interrupts = virtio_mmio_alloc_interrupts, 168 .free_interrupts = virtio_mmio_free_interrupts, 169 .setup_interrupts = virtio_mmio_setup_interrupts, 170 }; 171 172 static const struct virtio_ops virtio_mmio_v2_ops = { 173 .kick = virtio_mmio_kick, 174 .read_queue_size = virtio_mmio_read_queue_size, 175 .setup_queue = virtio_mmio_v2_setup_queue, 176 .set_status = virtio_mmio_set_status, 177 .neg_features = virtio_mmio_negotiate_features, 178 .alloc_interrupts = virtio_mmio_alloc_interrupts, 179 .free_interrupts = virtio_mmio_free_interrupts, 180 .setup_interrupts = virtio_mmio_setup_interrupts, 181 }; 182 183 static uint16_t 184 virtio_mmio_read_queue_size(struct virtio_softc *vsc, uint16_t idx) 185 { 186 struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc; 187 virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_SEL, idx); 188 return virtio_mmio_reg_read(sc, VIRTIO_MMIO_QUEUE_NUM_MAX); 189 } 190 191 static void 192 virtio_mmio_v1_setup_queue(struct virtio_softc *vsc, uint16_t idx, 193 uint64_t addr) 194 { 195 struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc; 196 197 virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_SEL, idx); 198 virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_NUM, 199 virtio_mmio_reg_read(sc, VIRTIO_MMIO_QUEUE_NUM_MAX)); 200 virtio_mmio_reg_write(sc, VIRTIO_MMIO_V1_QUEUE_ALIGN, 201 VIRTIO_PAGE_SIZE); 202 virtio_mmio_reg_write(sc, VIRTIO_MMIO_V1_QUEUE_PFN, 203 addr / VIRTIO_PAGE_SIZE); 204 } 205 206 static void 207 virtio_mmio_v2_setup_queue(struct virtio_softc *vsc, uint16_t idx, 208 uint64_t addr) 209 { 210 struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc; 211 struct virtqueue *vq; 212 213 virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_SEL, idx); 214 if (addr == 0) { 215 virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_READY, 0); 216 virtio_mmio_v2_set_addr(sc, VIRTIO_MMIO_V2_QUEUE_DESC_LOW, 0); 217 virtio_mmio_v2_set_addr(sc, VIRTIO_MMIO_V2_QUEUE_AVAIL_LOW, 0); 218 virtio_mmio_v2_set_addr(sc, VIRTIO_MMIO_V2_QUEUE_USED_LOW, 0); 219 } else { 220 vq = &vsc->sc_vqs[idx]; 221 KASSERT(vq->vq_index == idx); 222 223 virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_NUM, 224 virtio_mmio_reg_read(sc, VIRTIO_MMIO_QUEUE_NUM_MAX)); 225 virtio_mmio_v2_set_addr(sc, VIRTIO_MMIO_V2_QUEUE_DESC_LOW, 226 addr); 227 virtio_mmio_v2_set_addr(sc, VIRTIO_MMIO_V2_QUEUE_AVAIL_LOW, 228 addr + vq->vq_availoffset); 229 virtio_mmio_v2_set_addr(sc, VIRTIO_MMIO_V2_QUEUE_USED_LOW, 230 addr + vq->vq_usedoffset); 231 virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_READY, 1); 232 } 233 } 234 235 static int 236 virtio_mmio_get_status(struct virtio_softc *vsc) 237 { 238 struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc; 239 240 return virtio_mmio_reg_read(sc, VIRTIO_MMIO_STATUS); 241 } 242 243 static void 244 virtio_mmio_set_status(struct virtio_softc *vsc, int status) 245 { 246 struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc; 247 int old = 0; 248 249 if (status != 0) 250 old = virtio_mmio_reg_read(sc, VIRTIO_MMIO_STATUS); 251 virtio_mmio_reg_write(sc, VIRTIO_MMIO_STATUS, status|old); 252 } 253 254 bool 255 virtio_mmio_common_probe_present(struct virtio_mmio_softc *sc) 256 { 257 uint32_t magic; 258 259 /* XXX */ 260 magic = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 261 VIRTIO_MMIO_MAGIC_VALUE); 262 return (magic == VIRTIO_MMIO_MAGIC); 263 } 264 265 266 void 267 virtio_mmio_common_attach(struct virtio_mmio_softc *sc) 268 { 269 struct virtio_softc *vsc = &sc->sc_sc; 270 device_t self = vsc->sc_dev; 271 uint32_t id, magic; 272 int virtio_vers; 273 274 magic = bus_space_read_4(sc->sc_iot, sc->sc_ioh, 275 VIRTIO_MMIO_MAGIC_VALUE); 276 if (magic != VIRTIO_MMIO_MAGIC) { 277 if (magic == le32toh(VIRTIO_MMIO_MAGIC)) { 278 sc->sc_le_regs = true; 279 } else { 280 aprint_error_dev(vsc->sc_dev, 281 "wrong magic value 0x%08x; giving up\n", magic); 282 return; 283 } 284 } 285 vsc->sc_bus_endian = READ_ENDIAN; 286 vsc->sc_struct_endian = STRUCT_ENDIAN; 287 288 sc->sc_mmio_vers = virtio_mmio_reg_read(sc, VIRTIO_MMIO_VERSION); 289 switch (sc->sc_mmio_vers) { 290 case 1: 291 /* we could use PAGE_SIZE, but virtio(4) assumes 4KiB for now */ 292 virtio_mmio_reg_write(sc, 293 VIRTIO_MMIO_V1_GUEST_PAGE_SIZE, VIRTIO_PAGE_SIZE); 294 vsc->sc_ops = &virtio_mmio_v1_ops; 295 /* 296 * MMIO v1 ("legacy") is documented in the VirtIO 0.9.x 297 * draft(s) and uses the same page-oriented queue setup, 298 * so that's what we'll report as the VirtIO version. 299 */ 300 virtio_vers = 0; 301 break; 302 303 case 2: 304 vsc->sc_ops = &virtio_mmio_v2_ops; 305 /* 306 * MMIO v2 is documented in the VirtIO 1.0 spec. 307 */ 308 virtio_vers = 1; 309 break; 310 311 default: 312 aprint_error_dev(vsc->sc_dev, 313 "unknown version 0x%08x; giving up\n", sc->sc_mmio_vers); 314 return; 315 } 316 aprint_normal_dev(self, "VirtIO-MMIO-v%u\n", sc->sc_mmio_vers); 317 318 id = virtio_mmio_reg_read(sc, VIRTIO_MMIO_DEVICE_ID); 319 if (id == 0) { 320 /* no device connected. */ 321 return; 322 } 323 324 virtio_print_device_type(self, id, virtio_vers); 325 326 /* set up our device config tag */ 327 vsc->sc_devcfg_iosize = sc->sc_iosize - VIRTIO_MMIO_CONFIG; 328 vsc->sc_devcfg_iot = sc->sc_iot; 329 if (bus_space_subregion(sc->sc_iot, sc->sc_ioh, 330 VIRTIO_MMIO_CONFIG, vsc->sc_devcfg_iosize, 331 &vsc->sc_devcfg_ioh)) { 332 aprint_error_dev(self, "can't map config i/o space\n"); 333 return; 334 } 335 336 virtio_device_reset(vsc); 337 virtio_mmio_set_status(vsc, VIRTIO_CONFIG_DEVICE_STATUS_ACK); 338 virtio_mmio_set_status(vsc, VIRTIO_CONFIG_DEVICE_STATUS_DRIVER); 339 340 /* XXX: use softc as aux... */ 341 vsc->sc_childdevid = id; 342 vsc->sc_child = NULL; 343 } 344 345 int 346 virtio_mmio_common_detach(struct virtio_mmio_softc *sc, int flags) 347 { 348 struct virtio_softc *vsc = &sc->sc_sc; 349 int r; 350 351 r = config_detach_children(vsc->sc_dev, flags); 352 if (r != 0) 353 return r; 354 355 KASSERT(vsc->sc_child == NULL); 356 KASSERT(vsc->sc_vqs == NULL); 357 KASSERT(sc->sc_ih == NULL); 358 359 if (sc->sc_iosize) { 360 bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_iosize); 361 sc->sc_iosize = 0; 362 } 363 364 return 0; 365 } 366 367 /* 368 * Feature negotiation. 369 * 370 * We fold pre-VirtIO-1.0 feature negotiation into this single routine 371 * because the "legacy" (MMIO-v1) also had the feature sel registers. 372 */ 373 static void 374 virtio_mmio_negotiate_features(struct virtio_softc *vsc, uint64_t 375 driver_features) 376 { 377 struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc; 378 device_t self = vsc->sc_dev; 379 uint64_t saved_driver_features = driver_features; 380 uint64_t device_features, negotiated; 381 uint32_t device_status; 382 383 driver_features |= VIRTIO_F_VERSION_1; 384 vsc->sc_active_features = 0; 385 386 virtio_mmio_reg_write(sc, VIRTIO_MMIO_DEVICE_FEATURES_SEL, 0); 387 device_features = virtio_mmio_reg_read(sc, VIRTIO_MMIO_DEVICE_FEATURES); 388 virtio_mmio_reg_write(sc, VIRTIO_MMIO_DEVICE_FEATURES_SEL, 1); 389 device_features |= (uint64_t) 390 virtio_mmio_reg_read(sc, VIRTIO_MMIO_DEVICE_FEATURES) << 32; 391 392 /* notify on empty is 0.9 only */ 393 if (device_features & VIRTIO_F_VERSION_1) { 394 driver_features &= ~VIRTIO_F_NOTIFY_ON_EMPTY; 395 } else { 396 /* 397 * Require version 1 for MMIO-v2 transport. 398 */ 399 if (sc->sc_mmio_vers >= 2) { 400 aprint_error_dev(self, "MMIO-v%u requires version 1\n", 401 sc->sc_mmio_vers); 402 virtio_mmio_set_status(vsc, 403 VIRTIO_CONFIG_DEVICE_STATUS_FAILED); 404 return; 405 } 406 /* 407 * If the driver requires version 1, but the device doesn't 408 * support it, fail now. 409 */ 410 if (saved_driver_features & VIRTIO_F_VERSION_1) { 411 aprint_error_dev(self, "device rejected version 1\n"); 412 virtio_mmio_set_status(vsc, 413 VIRTIO_CONFIG_DEVICE_STATUS_FAILED); 414 return; 415 } 416 } 417 418 negotiated = device_features & driver_features; 419 420 virtio_mmio_reg_write(sc, VIRTIO_MMIO_DRIVER_FEATURES_SEL, 0); 421 virtio_mmio_reg_write(sc, VIRTIO_MMIO_DRIVER_FEATURES, 422 (uint32_t)negotiated); 423 virtio_mmio_reg_write(sc, VIRTIO_MMIO_DRIVER_FEATURES_SEL, 1); 424 virtio_mmio_reg_write(sc, VIRTIO_MMIO_DRIVER_FEATURES, 425 (uint32_t)(negotiated >> 32)); 426 427 /* 428 * FEATURES_OK status is not present pre-1.0. 429 */ 430 if (device_features & VIRTIO_F_VERSION_1) { 431 virtio_mmio_set_status(vsc, 432 VIRTIO_CONFIG_DEVICE_STATUS_FEATURES_OK); 433 device_status = virtio_mmio_get_status(vsc); 434 if ((device_status & 435 VIRTIO_CONFIG_DEVICE_STATUS_FEATURES_OK) == 0) { 436 aprint_error_dev(self, "feature negotiation failed\n"); 437 virtio_mmio_set_status(vsc, 438 VIRTIO_CONFIG_DEVICE_STATUS_FAILED); 439 return; 440 } 441 } 442 443 if (negotiated & VIRTIO_F_VERSION_1) { 444 /* 445 * All VirtIO 1.0 access is little-endian. 446 */ 447 vsc->sc_bus_endian = LITTLE_ENDIAN; 448 vsc->sc_struct_endian = LITTLE_ENDIAN; 449 } 450 451 vsc->sc_active_features = negotiated; 452 } 453 454 /* 455 * Interrupt handler. 456 */ 457 int 458 virtio_mmio_intr(void *arg) 459 { 460 struct virtio_mmio_softc *sc = arg; 461 struct virtio_softc *vsc = &sc->sc_sc; 462 int isr, r = 0; 463 464 /* check and ack the interrupt */ 465 isr = virtio_mmio_reg_read(sc, VIRTIO_MMIO_INTERRUPT_STATUS); 466 virtio_mmio_reg_write(sc, VIRTIO_MMIO_INTERRUPT_ACK, isr); 467 if ((isr & VIRTIO_CONFIG_ISR_CONFIG_CHANGE) && 468 (vsc->sc_config_change != NULL)) 469 r = (vsc->sc_config_change)(vsc); 470 if ((isr & VIRTIO_CONFIG_ISR_QUEUE_INTERRUPT) && 471 (vsc->sc_intrhand != NULL)) { 472 if (vsc->sc_soft_ih != NULL) 473 softint_schedule(vsc->sc_soft_ih); 474 else 475 r |= (vsc->sc_intrhand)(vsc); 476 } 477 478 return r; 479 } 480 481 static void 482 virtio_mmio_kick(struct virtio_softc *vsc, uint16_t idx) 483 { 484 struct virtio_mmio_softc *sc = (struct virtio_mmio_softc *)vsc; 485 virtio_mmio_reg_write(sc, VIRTIO_MMIO_QUEUE_NOTIFY, idx); 486 } 487 488 static int 489 virtio_mmio_alloc_interrupts(struct virtio_softc *vsc) 490 { 491 struct virtio_mmio_softc * const sc = (struct virtio_mmio_softc *)vsc; 492 493 return sc->sc_alloc_interrupts(sc); 494 } 495 496 static void 497 virtio_mmio_free_interrupts(struct virtio_softc *vsc) 498 { 499 struct virtio_mmio_softc * const sc = (struct virtio_mmio_softc *)vsc; 500 501 sc->sc_free_interrupts(sc); 502 } 503 504 static int 505 virtio_mmio_setup_interrupts(struct virtio_softc *vsc __unused, 506 int reinit __unused) 507 { 508 509 return 0; 510 } 511