1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2021 Alexander Motin <mav@FreeBSD.org> 5 * Copyright 2019 Cisco Systems, Inc. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #include <sys/types.h> 34 #include <sys/param.h> 35 #include <sys/bus.h> 36 #include <sys/conf.h> 37 #include <sys/kernel.h> 38 #include <sys/limits.h> 39 #include <sys/module.h> 40 #include <sys/sysctl.h> 41 #include <sys/systm.h> 42 #include <sys/malloc.h> 43 44 #include <machine/bus.h> 45 #include <machine/resource.h> 46 #include <machine/intr_machdep.h> 47 #include <sys/rman.h> 48 #include <sys/lock.h> 49 #include <sys/mutex.h> 50 51 #include <sys/pciio.h> 52 #include <dev/pci/pcivar.h> 53 #include <dev/pci/pcireg.h> 54 #include <dev/pci/pci_private.h> 55 #include <dev/pci/pcib_private.h> 56 57 #include <dev/vmd/vmd.h> 58 59 #include "pcib_if.h" 60 61 struct vmd_type { 62 u_int16_t vmd_vid; 63 u_int16_t vmd_did; 64 char *vmd_name; 65 int flags; 66 #define BUS_RESTRICT 1 67 #define VECTOR_OFFSET 2 68 }; 69 70 #define VMD_CAP 0x40 71 #define VMD_BUS_RESTRICT 0x1 72 73 #define VMD_CONFIG 0x44 74 #define VMD_BUS_START(x) ((x >> 8) & 0x3) 75 76 #define VMD_LOCK 0x70 77 78 SYSCTL_NODE(_hw, OID_AUTO, vmd, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, 79 "Intel Volume Management Device tuning parameters"); 80 81 /* 82 * All MSIs within a group share address, so VMD can't distinguish them. 83 * It makes no sense to use more than one per device, only if required by 84 * some specific device drivers. 85 */ 86 static int vmd_max_msi = 1; 87 SYSCTL_INT(_hw_vmd, OID_AUTO, max_msi, CTLFLAG_RWTUN, &vmd_max_msi, 0, 88 "Maximum number of MSI vectors per device"); 89 90 /* 91 * MSI-X can use different addresses, but we have limited number of MSI-X 92 * we can route to, so use conservative default to try to avoid sharing. 93 */ 94 static int vmd_max_msix = 3; 95 SYSCTL_INT(_hw_vmd, OID_AUTO, max_msix, CTLFLAG_RWTUN, &vmd_max_msix, 0, 96 "Maximum number of MSI-X vectors per device"); 97 98 static struct vmd_type vmd_devs[] = { 99 { 0x8086, 0x201d, "Intel Volume Management Device", 0 }, 100 { 0x8086, 0x28c0, "Intel Volume Management Device", BUS_RESTRICT }, 101 { 0x8086, 0x467f, "Intel Volume Management Device", BUS_RESTRICT | VECTOR_OFFSET }, 102 { 0x8086, 0x4c3d, "Intel Volume Management Device", BUS_RESTRICT | VECTOR_OFFSET }, 103 { 0x8086, 0x9a0b, "Intel Volume Management Device", BUS_RESTRICT | VECTOR_OFFSET }, 104 { 0x8086, 0xa77f, "Intel Volume Management Device", BUS_RESTRICT | VECTOR_OFFSET }, 105 { 0, 0, NULL, 0 } 106 }; 107 108 static int 109 vmd_probe(device_t dev) 110 { 111 struct vmd_type *t; 112 uint16_t vid, did; 113 114 vid = pci_get_vendor(dev); 115 did = pci_get_device(dev); 116 for (t = vmd_devs; t->vmd_name != NULL; t++) { 117 if (vid == t->vmd_vid && did == t->vmd_did) { 118 device_set_desc(dev, t->vmd_name); 119 return (BUS_PROBE_DEFAULT); 120 } 121 } 122 return (ENXIO); 123 } 124 125 static void 126 vmd_free(struct vmd_softc *sc) 127 { 128 struct vmd_irq *vi; 129 struct vmd_irq_user *u; 130 int i; 131 132 if (sc->psc.bus.rman.rm_end != 0) 133 rman_fini(&sc->psc.bus.rman); 134 if (sc->psc.mem.rman.rm_end != 0) 135 rman_fini(&sc->psc.mem.rman); 136 while ((u = LIST_FIRST(&sc->vmd_users)) != NULL) { 137 LIST_REMOVE(u, viu_link); 138 free(u, M_DEVBUF); 139 } 140 if (sc->vmd_irq != NULL) { 141 for (i = 0; i < sc->vmd_msix_count; i++) { 142 vi = &sc->vmd_irq[i]; 143 if (vi->vi_res == NULL) 144 continue; 145 bus_teardown_intr(sc->psc.dev, vi->vi_res, 146 vi->vi_handle); 147 bus_release_resource(sc->psc.dev, SYS_RES_IRQ, 148 vi->vi_rid, vi->vi_res); 149 } 150 } 151 free(sc->vmd_irq, M_DEVBUF); 152 sc->vmd_irq = NULL; 153 pci_release_msi(sc->psc.dev); 154 for (i = 0; i < VMD_MAX_BAR; i++) { 155 if (sc->vmd_regs_res[i] != NULL) 156 bus_release_resource(sc->psc.dev, SYS_RES_MEMORY, 157 sc->vmd_regs_rid[i], sc->vmd_regs_res[i]); 158 } 159 } 160 161 /* Hidden PCI Roots are hidden in BAR(0). */ 162 163 static uint32_t 164 vmd_read_config(device_t dev, u_int b, u_int s, u_int f, u_int reg, int width) 165 { 166 struct vmd_softc *sc; 167 bus_addr_t offset; 168 169 sc = device_get_softc(dev); 170 if (b < sc->vmd_bus_start || b > sc->vmd_bus_end) 171 return (0xffffffff); 172 173 offset = ((b - sc->vmd_bus_start) << 20) + (s << 15) + (f << 12) + reg; 174 175 switch (width) { 176 case 4: 177 return (bus_space_read_4(sc->vmd_btag, sc->vmd_bhandle, 178 offset)); 179 case 2: 180 return (bus_space_read_2(sc->vmd_btag, sc->vmd_bhandle, 181 offset)); 182 case 1: 183 return (bus_space_read_1(sc->vmd_btag, sc->vmd_bhandle, 184 offset)); 185 default: 186 __assert_unreachable(); 187 return (0xffffffff); 188 } 189 } 190 191 static void 192 vmd_write_config(device_t dev, u_int b, u_int s, u_int f, u_int reg, 193 uint32_t val, int width) 194 { 195 struct vmd_softc *sc; 196 bus_addr_t offset; 197 198 sc = device_get_softc(dev); 199 if (b < sc->vmd_bus_start || b > sc->vmd_bus_end) 200 return; 201 202 offset = ((b - sc->vmd_bus_start) << 20) + (s << 15) + (f << 12) + reg; 203 204 switch (width) { 205 case 4: 206 return (bus_space_write_4(sc->vmd_btag, sc->vmd_bhandle, 207 offset, val)); 208 case 2: 209 return (bus_space_write_2(sc->vmd_btag, sc->vmd_bhandle, 210 offset, val)); 211 case 1: 212 return (bus_space_write_1(sc->vmd_btag, sc->vmd_bhandle, 213 offset, val)); 214 default: 215 __assert_unreachable(); 216 } 217 } 218 219 static int 220 vmd_intr(void *arg) 221 { 222 /* 223 * We have nothing to do here, but we have to register some interrupt 224 * handler to make PCI code setup and enable the MSI-X vector. 225 */ 226 return (FILTER_STRAY); 227 } 228 229 static int 230 vmd_attach(device_t dev) 231 { 232 struct vmd_softc *sc; 233 struct pcib_secbus *bus; 234 struct pcib_window *w; 235 struct vmd_type *t; 236 struct vmd_irq *vi; 237 uint16_t vid, did; 238 uint32_t bar; 239 int i, j, error; 240 char buf[64]; 241 242 sc = device_get_softc(dev); 243 bzero(sc, sizeof(*sc)); 244 sc->psc.dev = dev; 245 sc->psc.domain = PCI_DOMAINMAX - device_get_unit(dev); 246 247 pci_enable_busmaster(dev); 248 249 for (i = 0, j = 0; i < VMD_MAX_BAR; i++, j++) { 250 sc->vmd_regs_rid[i] = PCIR_BAR(j); 251 bar = pci_read_config(dev, PCIR_BAR(0), 4); 252 if (PCI_BAR_MEM(bar) && (bar & PCIM_BAR_MEM_TYPE) == 253 PCIM_BAR_MEM_64) 254 j++; 255 if ((sc->vmd_regs_res[i] = bus_alloc_resource_any(dev, 256 SYS_RES_MEMORY, &sc->vmd_regs_rid[i], RF_ACTIVE)) == NULL) { 257 device_printf(dev, "Cannot allocate resources\n"); 258 goto fail; 259 } 260 } 261 262 sc->vmd_btag = rman_get_bustag(sc->vmd_regs_res[0]); 263 sc->vmd_bhandle = rman_get_bushandle(sc->vmd_regs_res[0]); 264 265 vid = pci_get_vendor(dev); 266 did = pci_get_device(dev); 267 for (t = vmd_devs; t->vmd_name != NULL; t++) { 268 if (vid == t->vmd_vid && did == t->vmd_did) 269 break; 270 } 271 272 sc->vmd_bus_start = 0; 273 if ((t->flags & BUS_RESTRICT) && 274 (pci_read_config(dev, VMD_CAP, 2) & VMD_BUS_RESTRICT)) { 275 switch (VMD_BUS_START(pci_read_config(dev, VMD_CONFIG, 2))) { 276 case 0: 277 sc->vmd_bus_start = 0; 278 break; 279 case 1: 280 sc->vmd_bus_start = 128; 281 break; 282 case 2: 283 sc->vmd_bus_start = 224; 284 break; 285 default: 286 device_printf(dev, "Unknown bus offset\n"); 287 goto fail; 288 } 289 } 290 sc->vmd_bus_end = MIN(PCI_BUSMAX, sc->vmd_bus_start + 291 (rman_get_size(sc->vmd_regs_res[0]) >> 20) - 1); 292 293 bus = &sc->psc.bus; 294 bus->sec = sc->vmd_bus_start; 295 bus->sub = sc->vmd_bus_end; 296 bus->dev = dev; 297 bus->rman.rm_start = 0; 298 bus->rman.rm_end = PCI_BUSMAX; 299 bus->rman.rm_type = RMAN_ARRAY; 300 snprintf(buf, sizeof(buf), "%s bus numbers", device_get_nameunit(dev)); 301 bus->rman.rm_descr = strdup(buf, M_DEVBUF); 302 error = rman_init(&bus->rman); 303 if (error) { 304 device_printf(dev, "Failed to initialize bus rman\n"); 305 bus->rman.rm_end = 0; 306 goto fail; 307 } 308 error = rman_manage_region(&bus->rman, sc->vmd_bus_start, 309 sc->vmd_bus_end); 310 if (error) { 311 device_printf(dev, "Failed to add resource to bus rman\n"); 312 goto fail; 313 } 314 315 w = &sc->psc.mem; 316 w->rman.rm_type = RMAN_ARRAY; 317 snprintf(buf, sizeof(buf), "%s memory window", device_get_nameunit(dev)); 318 w->rman.rm_descr = strdup(buf, M_DEVBUF); 319 error = rman_init(&w->rman); 320 if (error) { 321 device_printf(dev, "Failed to initialize memory rman\n"); 322 w->rman.rm_end = 0; 323 goto fail; 324 } 325 error = rman_manage_region(&w->rman, 326 rman_get_start(sc->vmd_regs_res[1]), 327 rman_get_end(sc->vmd_regs_res[1])); 328 if (error) { 329 device_printf(dev, "Failed to add resource to memory rman\n"); 330 goto fail; 331 } 332 error = rman_manage_region(&w->rman, 333 rman_get_start(sc->vmd_regs_res[2]) + 0x2000, 334 rman_get_end(sc->vmd_regs_res[2])); 335 if (error) { 336 device_printf(dev, "Failed to add resource to memory rman\n"); 337 goto fail; 338 } 339 340 LIST_INIT(&sc->vmd_users); 341 sc->vmd_fist_vector = (t->flags & VECTOR_OFFSET) ? 1 : 0; 342 sc->vmd_msix_count = pci_msix_count(dev); 343 if (pci_alloc_msix(dev, &sc->vmd_msix_count) == 0) { 344 sc->vmd_irq = malloc(sizeof(struct vmd_irq) * 345 sc->vmd_msix_count, M_DEVBUF, M_WAITOK | M_ZERO); 346 for (i = 0; i < sc->vmd_msix_count; i++) { 347 vi = &sc->vmd_irq[i]; 348 vi->vi_rid = i + 1; 349 vi->vi_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, 350 &vi->vi_rid, RF_ACTIVE | RF_SHAREABLE); 351 if (vi->vi_res == NULL) { 352 device_printf(dev, "Failed to allocate irq\n"); 353 goto fail; 354 } 355 vi->vi_irq = rman_get_start(vi->vi_res); 356 if (bus_setup_intr(dev, vi->vi_res, INTR_TYPE_MISC | 357 INTR_MPSAFE, vmd_intr, NULL, vi, &vi->vi_handle)) { 358 device_printf(dev, "Can't set up interrupt\n"); 359 bus_release_resource(dev, SYS_RES_IRQ, 360 vi->vi_rid, vi->vi_res); 361 vi->vi_res = NULL; 362 goto fail; 363 } 364 } 365 } 366 367 sc->vmd_dma_tag = bus_get_dma_tag(dev); 368 369 sc->psc.child = device_add_child(dev, "pci", -1); 370 return (bus_generic_attach(dev)); 371 372 fail: 373 vmd_free(sc); 374 return (ENXIO); 375 } 376 377 static int 378 vmd_detach(device_t dev) 379 { 380 struct vmd_softc *sc = device_get_softc(dev); 381 int error; 382 383 error = bus_generic_detach(dev); 384 if (error) 385 return (error); 386 error = device_delete_children(dev); 387 if (error) 388 return (error); 389 vmd_free(sc); 390 return (0); 391 } 392 393 static bus_dma_tag_t 394 vmd_get_dma_tag(device_t dev, device_t child) 395 { 396 struct vmd_softc *sc = device_get_softc(dev); 397 398 return (sc->vmd_dma_tag); 399 } 400 401 static struct resource * 402 vmd_alloc_resource(device_t dev, device_t child, int type, int *rid, 403 rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) 404 { 405 struct vmd_softc *sc = device_get_softc(dev); 406 struct resource *res; 407 408 switch (type) { 409 case SYS_RES_IRQ: 410 /* VMD harwdare does not support legacy interrupts. */ 411 if (*rid == 0) 412 return (NULL); 413 return (bus_generic_alloc_resource(dev, child, type, rid, 414 start, end, count, flags | RF_SHAREABLE)); 415 case SYS_RES_MEMORY: 416 res = rman_reserve_resource(&sc->psc.mem.rman, start, end, 417 count, flags, child); 418 if (res == NULL) 419 return (NULL); 420 if (bootverbose) 421 device_printf(dev, 422 "allocated memory range (%#jx-%#jx) for rid %d of %s\n", 423 rman_get_start(res), rman_get_end(res), *rid, 424 pcib_child_name(child)); 425 break; 426 case PCI_RES_BUS: 427 res = rman_reserve_resource(&sc->psc.bus.rman, start, end, 428 count, flags, child); 429 if (res == NULL) 430 return (NULL); 431 if (bootverbose) 432 device_printf(dev, 433 "allocated bus range (%ju-%ju) for rid %d of %s\n", 434 rman_get_start(res), rman_get_end(res), *rid, 435 pcib_child_name(child)); 436 break; 437 default: 438 /* VMD harwdare does not support I/O ports. */ 439 return (NULL); 440 } 441 rman_set_rid(res, *rid); 442 return (res); 443 } 444 445 static int 446 vmd_adjust_resource(device_t dev, device_t child, int type, 447 struct resource *r, rman_res_t start, rman_res_t end) 448 { 449 450 if (type == SYS_RES_IRQ) { 451 return (bus_generic_adjust_resource(dev, child, type, r, 452 start, end)); 453 } 454 return (rman_adjust_resource(r, start, end)); 455 } 456 457 static int 458 vmd_release_resource(device_t dev, device_t child, int type, int rid, 459 struct resource *r) 460 { 461 462 if (type == SYS_RES_IRQ) { 463 return (bus_generic_release_resource(dev, child, type, rid, 464 r)); 465 } 466 return (rman_release_resource(r)); 467 } 468 469 static int 470 vmd_route_interrupt(device_t dev, device_t child, int pin) 471 { 472 473 /* VMD harwdare does not support legacy interrupts. */ 474 return (PCI_INVALID_IRQ); 475 } 476 477 static int 478 vmd_alloc_msi(device_t dev, device_t child, int count, int maxcount, 479 int *irqs) 480 { 481 struct vmd_softc *sc = device_get_softc(dev); 482 struct vmd_irq_user *u; 483 int i, ibest = 0, best = INT_MAX; 484 485 if (count > vmd_max_msi) 486 return (ENOSPC); 487 LIST_FOREACH(u, &sc->vmd_users, viu_link) { 488 if (u->viu_child == child) 489 return (EBUSY); 490 } 491 492 for (i = sc->vmd_fist_vector; i < sc->vmd_msix_count; i++) { 493 if (best > sc->vmd_irq[i].vi_nusers) { 494 best = sc->vmd_irq[i].vi_nusers; 495 ibest = i; 496 } 497 } 498 499 u = malloc(sizeof(*u), M_DEVBUF, M_WAITOK | M_ZERO); 500 u->viu_child = child; 501 u->viu_vector = ibest; 502 LIST_INSERT_HEAD(&sc->vmd_users, u, viu_link); 503 sc->vmd_irq[ibest].vi_nusers += count; 504 505 for (i = 0; i < count; i++) 506 irqs[i] = sc->vmd_irq[ibest].vi_irq; 507 return (0); 508 } 509 510 static int 511 vmd_release_msi(device_t dev, device_t child, int count, int *irqs) 512 { 513 struct vmd_softc *sc = device_get_softc(dev); 514 struct vmd_irq_user *u; 515 516 LIST_FOREACH(u, &sc->vmd_users, viu_link) { 517 if (u->viu_child == child) { 518 sc->vmd_irq[u->viu_vector].vi_nusers -= count; 519 LIST_REMOVE(u, viu_link); 520 free(u, M_DEVBUF); 521 return (0); 522 } 523 } 524 return (EINVAL); 525 } 526 527 static int 528 vmd_alloc_msix(device_t dev, device_t child, int *irq) 529 { 530 struct vmd_softc *sc = device_get_softc(dev); 531 struct vmd_irq_user *u; 532 int i, ibest = 0, best = INT_MAX; 533 534 i = 0; 535 LIST_FOREACH(u, &sc->vmd_users, viu_link) { 536 if (u->viu_child == child) 537 i++; 538 } 539 if (i >= vmd_max_msix) 540 return (ENOSPC); 541 542 for (i = sc->vmd_fist_vector; i < sc->vmd_msix_count; i++) { 543 if (best > sc->vmd_irq[i].vi_nusers) { 544 best = sc->vmd_irq[i].vi_nusers; 545 ibest = i; 546 } 547 } 548 549 u = malloc(sizeof(*u), M_DEVBUF, M_WAITOK | M_ZERO); 550 u->viu_child = child; 551 u->viu_vector = ibest; 552 LIST_INSERT_HEAD(&sc->vmd_users, u, viu_link); 553 sc->vmd_irq[ibest].vi_nusers++; 554 555 *irq = sc->vmd_irq[ibest].vi_irq; 556 return (0); 557 } 558 559 static int 560 vmd_release_msix(device_t dev, device_t child, int irq) 561 { 562 struct vmd_softc *sc = device_get_softc(dev); 563 struct vmd_irq_user *u; 564 565 LIST_FOREACH(u, &sc->vmd_users, viu_link) { 566 if (u->viu_child == child && 567 sc->vmd_irq[u->viu_vector].vi_irq == irq) { 568 sc->vmd_irq[u->viu_vector].vi_nusers--; 569 LIST_REMOVE(u, viu_link); 570 free(u, M_DEVBUF); 571 return (0); 572 } 573 } 574 return (EINVAL); 575 } 576 577 static int 578 vmd_map_msi(device_t dev, device_t child, int irq, uint64_t *addr, uint32_t *data) 579 { 580 struct vmd_softc *sc = device_get_softc(dev); 581 int i; 582 583 for (i = sc->vmd_fist_vector; i < sc->vmd_msix_count; i++) { 584 if (sc->vmd_irq[i].vi_irq == irq) 585 break; 586 } 587 if (i >= sc->vmd_msix_count) 588 return (EINVAL); 589 *addr = MSI_INTEL_ADDR_BASE | (i << 12); 590 *data = 0; 591 return (0); 592 } 593 594 static device_method_t vmd_pci_methods[] = { 595 /* Device interface */ 596 DEVMETHOD(device_probe, vmd_probe), 597 DEVMETHOD(device_attach, vmd_attach), 598 DEVMETHOD(device_detach, vmd_detach), 599 DEVMETHOD(device_suspend, bus_generic_suspend), 600 DEVMETHOD(device_resume, bus_generic_resume), 601 DEVMETHOD(device_shutdown, bus_generic_shutdown), 602 603 /* Bus interface */ 604 DEVMETHOD(bus_get_dma_tag, vmd_get_dma_tag), 605 DEVMETHOD(bus_read_ivar, pcib_read_ivar), 606 DEVMETHOD(bus_write_ivar, pcib_write_ivar), 607 DEVMETHOD(bus_alloc_resource, vmd_alloc_resource), 608 DEVMETHOD(bus_adjust_resource, vmd_adjust_resource), 609 DEVMETHOD(bus_release_resource, vmd_release_resource), 610 DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 611 DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 612 DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 613 DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 614 615 /* pcib interface */ 616 DEVMETHOD(pcib_maxslots, pcib_maxslots), 617 DEVMETHOD(pcib_read_config, vmd_read_config), 618 DEVMETHOD(pcib_write_config, vmd_write_config), 619 DEVMETHOD(pcib_route_interrupt, vmd_route_interrupt), 620 DEVMETHOD(pcib_alloc_msi, vmd_alloc_msi), 621 DEVMETHOD(pcib_release_msi, vmd_release_msi), 622 DEVMETHOD(pcib_alloc_msix, vmd_alloc_msix), 623 DEVMETHOD(pcib_release_msix, vmd_release_msix), 624 DEVMETHOD(pcib_map_msi, vmd_map_msi), 625 DEVMETHOD(pcib_request_feature, pcib_request_feature_allow), 626 627 DEVMETHOD_END 628 }; 629 630 DEFINE_CLASS_0(pcib, vmd_pci_driver, vmd_pci_methods, sizeof(struct vmd_softc)); 631 DRIVER_MODULE(vmd, pci, vmd_pci_driver, NULL, NULL); 632 MODULE_PNP_INFO("U16:vendor;U16:device;D:#", pci, vmd, 633 vmd_devs, nitems(vmd_devs) - 1); 634