]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/vmd/vmd.c
as3722_gpio_pin_setflags: Use computed mode instead of hardcoded PUSHPULL.
[FreeBSD/FreeBSD.git] / sys / dev / vmd / vmd.c
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         { 0, 0, NULL, 0 }
105 };
106
107 static int
108 vmd_probe(device_t dev)
109 {
110         struct vmd_type *t;
111         uint16_t vid, did;
112
113         vid = pci_get_vendor(dev);
114         did = pci_get_device(dev);
115         for (t = vmd_devs; t->vmd_name != NULL; t++) {
116                 if (vid == t->vmd_vid && did == t->vmd_did) {
117                         device_set_desc(dev, t->vmd_name);
118                         return (BUS_PROBE_DEFAULT);
119                 }
120         }
121         return (ENXIO);
122 }
123
124 static void
125 vmd_free(struct vmd_softc *sc)
126 {
127         struct vmd_irq *vi;
128         struct vmd_irq_user *u;
129         int i;
130
131         if (sc->psc.bus.rman.rm_end != 0)
132                 rman_fini(&sc->psc.bus.rman);
133         if (sc->psc.mem.rman.rm_end != 0)
134                 rman_fini(&sc->psc.mem.rman);
135         while ((u = LIST_FIRST(&sc->vmd_users)) != NULL) {
136                 LIST_REMOVE(u, viu_link);
137                 free(u, M_DEVBUF);
138         }
139         if (sc->vmd_irq != NULL) {
140                 for (i = 0; i < sc->vmd_msix_count; i++) {
141                         vi = &sc->vmd_irq[i];
142                         if (vi->vi_res == NULL)
143                                 continue;
144                         bus_teardown_intr(sc->psc.dev, vi->vi_res,
145                             vi->vi_handle);
146                         bus_release_resource(sc->psc.dev, SYS_RES_IRQ,
147                             vi->vi_rid, vi->vi_res);
148                 }
149         }
150         free(sc->vmd_irq, M_DEVBUF);
151         sc->vmd_irq = NULL;
152         pci_release_msi(sc->psc.dev);
153         for (i = 0; i < VMD_MAX_BAR; i++) {
154                 if (sc->vmd_regs_res[i] != NULL)
155                         bus_release_resource(sc->psc.dev, SYS_RES_MEMORY,
156                             sc->vmd_regs_rid[i], sc->vmd_regs_res[i]);
157         }
158 }
159
160 /* Hidden PCI Roots are hidden in BAR(0). */
161
162 static uint32_t
163 vmd_read_config(device_t dev, u_int b, u_int s, u_int f, u_int reg, int width)
164 {
165         struct vmd_softc *sc;
166         bus_addr_t offset;
167
168         sc = device_get_softc(dev);
169         if (b < sc->vmd_bus_start || b > sc->vmd_bus_end)
170                 return (0xffffffff);
171
172         offset = ((b - sc->vmd_bus_start) << 20) + (s << 15) + (f << 12) + reg;
173
174         switch (width) {
175         case 4:
176                 return (bus_space_read_4(sc->vmd_btag, sc->vmd_bhandle,
177                     offset));
178         case 2:
179                 return (bus_space_read_2(sc->vmd_btag, sc->vmd_bhandle,
180                     offset));
181         case 1:
182                 return (bus_space_read_1(sc->vmd_btag, sc->vmd_bhandle,
183                     offset));
184         default:
185                 __assert_unreachable();
186                 return (0xffffffff);
187         }
188 }
189
190 static void
191 vmd_write_config(device_t dev, u_int b, u_int s, u_int f, u_int reg,
192     uint32_t val, int width)
193 {
194         struct vmd_softc *sc;
195         bus_addr_t offset;
196
197         sc = device_get_softc(dev);
198         if (b < sc->vmd_bus_start || b > sc->vmd_bus_end)
199                 return;
200
201         offset = ((b - sc->vmd_bus_start) << 20) + (s << 15) + (f << 12) + reg;
202
203         switch (width) {
204         case 4:
205                 return (bus_space_write_4(sc->vmd_btag, sc->vmd_bhandle,
206                     offset, val));
207         case 2:
208                 return (bus_space_write_2(sc->vmd_btag, sc->vmd_bhandle,
209                     offset, val));
210         case 1:
211                 return (bus_space_write_1(sc->vmd_btag, sc->vmd_bhandle,
212                     offset, val));
213         default:
214                 __assert_unreachable();
215         }
216 }
217
218 static int
219 vmd_intr(void *arg)
220 {
221         /*
222          * We have nothing to do here, but we have to register some interrupt
223          * handler to make PCI code setup and enable the MSI-X vector.
224          */
225         return (FILTER_STRAY);
226 }
227
228 static int
229 vmd_attach(device_t dev)
230 {
231         struct vmd_softc *sc;
232         struct pcib_secbus *bus;
233         struct pcib_window *w;
234         struct vmd_type *t;
235         struct vmd_irq *vi;
236         uint16_t vid, did;
237         uint32_t bar;
238         int i, j, error;
239         char buf[64];
240
241         sc = device_get_softc(dev);
242         bzero(sc, sizeof(*sc));
243         sc->psc.dev = dev;
244         sc->psc.domain = PCI_DOMAINMAX - device_get_unit(dev);
245
246         pci_enable_busmaster(dev);
247
248         for (i = 0, j = 0; i < VMD_MAX_BAR; i++, j++) {
249                 sc->vmd_regs_rid[i] = PCIR_BAR(j);
250                 bar = pci_read_config(dev, PCIR_BAR(0), 4);
251                 if (PCI_BAR_MEM(bar) && (bar & PCIM_BAR_MEM_TYPE) ==
252                     PCIM_BAR_MEM_64)
253                         j++;
254                 if ((sc->vmd_regs_res[i] = bus_alloc_resource_any(dev,
255                     SYS_RES_MEMORY, &sc->vmd_regs_rid[i], RF_ACTIVE)) == NULL) {
256                         device_printf(dev, "Cannot allocate resources\n");
257                         goto fail;
258                 }
259         }
260
261         sc->vmd_btag = rman_get_bustag(sc->vmd_regs_res[0]);
262         sc->vmd_bhandle = rman_get_bushandle(sc->vmd_regs_res[0]);
263
264         vid = pci_get_vendor(dev);
265         did = pci_get_device(dev);
266         for (t = vmd_devs; t->vmd_name != NULL; t++) {
267                 if (vid == t->vmd_vid && did == t->vmd_did)
268                         break;
269         }
270
271         sc->vmd_bus_start = 0;
272         if ((t->flags & BUS_RESTRICT) &&
273             (pci_read_config(dev, VMD_CAP, 2) & VMD_BUS_RESTRICT)) {
274                 switch (VMD_BUS_START(pci_read_config(dev, VMD_CONFIG, 2))) {
275                 case 0:
276                         sc->vmd_bus_start = 0;
277                         break;
278                 case 1:
279                         sc->vmd_bus_start = 128;
280                         break;
281                 case 2:
282                         sc->vmd_bus_start = 224;
283                         break;
284                 default:
285                         device_printf(dev, "Unknown bus offset\n");
286                         goto fail;
287                 }
288         }
289         sc->vmd_bus_end = MIN(PCI_BUSMAX, sc->vmd_bus_start +
290             (rman_get_size(sc->vmd_regs_res[0]) >> 20) - 1);
291
292         bus = &sc->psc.bus;
293         bus->sec = sc->vmd_bus_start;
294         bus->sub = sc->vmd_bus_end;
295         bus->dev = dev;
296         bus->rman.rm_start = 0;
297         bus->rman.rm_end = PCI_BUSMAX;
298         bus->rman.rm_type = RMAN_ARRAY;
299         snprintf(buf, sizeof(buf), "%s bus numbers", device_get_nameunit(dev));
300         bus->rman.rm_descr = strdup(buf, M_DEVBUF);
301         error = rman_init(&bus->rman);
302         if (error) {
303                 device_printf(dev, "Failed to initialize bus rman\n");
304                 bus->rman.rm_end = 0;
305                 goto fail;
306         }
307         error = rman_manage_region(&bus->rman, sc->vmd_bus_start,
308             sc->vmd_bus_end);
309         if (error) {
310                 device_printf(dev, "Failed to add resource to bus rman\n");
311                 goto fail;
312         }
313
314         w = &sc->psc.mem;
315         w->rman.rm_type = RMAN_ARRAY;
316         snprintf(buf, sizeof(buf), "%s memory window", device_get_nameunit(dev));
317         w->rman.rm_descr = strdup(buf, M_DEVBUF);
318         error = rman_init(&w->rman);
319         if (error) {
320                 device_printf(dev, "Failed to initialize memory rman\n");
321                 w->rman.rm_end = 0;
322                 goto fail;
323         }
324         error = rman_manage_region(&w->rman,
325             rman_get_start(sc->vmd_regs_res[1]),
326             rman_get_end(sc->vmd_regs_res[1]));
327         if (error) {
328                 device_printf(dev, "Failed to add resource to memory rman\n");
329                 goto fail;
330         }
331         error = rman_manage_region(&w->rman,
332             rman_get_start(sc->vmd_regs_res[2]) + 0x2000,
333             rman_get_end(sc->vmd_regs_res[2]));
334         if (error) {
335                 device_printf(dev, "Failed to add resource to memory rman\n");
336                 goto fail;
337         }
338
339         LIST_INIT(&sc->vmd_users);
340         sc->vmd_fist_vector = (t->flags & VECTOR_OFFSET) ? 1 : 0;
341         sc->vmd_msix_count = pci_msix_count(dev);
342         if (pci_alloc_msix(dev, &sc->vmd_msix_count) == 0) {
343                 sc->vmd_irq = malloc(sizeof(struct vmd_irq) *
344                     sc->vmd_msix_count, M_DEVBUF, M_WAITOK | M_ZERO);
345                 for (i = 0; i < sc->vmd_msix_count; i++) {
346                         vi = &sc->vmd_irq[i];
347                         vi->vi_rid = i + 1;
348                         vi->vi_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
349                             &vi->vi_rid, RF_ACTIVE | RF_SHAREABLE);
350                         if (vi->vi_res == NULL) {
351                                 device_printf(dev, "Failed to allocate irq\n");
352                                 goto fail;
353                         }
354                         vi->vi_irq = rman_get_start(vi->vi_res);
355                         if (bus_setup_intr(dev, vi->vi_res, INTR_TYPE_MISC |
356                             INTR_MPSAFE, vmd_intr, NULL, vi, &vi->vi_handle)) {
357                                 device_printf(dev, "Can't set up interrupt\n");
358                                 bus_release_resource(dev, SYS_RES_IRQ,
359                                     vi->vi_rid, vi->vi_res);
360                                 vi->vi_res = NULL;
361                                 goto fail;
362                         }
363                 }
364         }
365
366         sc->vmd_dma_tag = bus_get_dma_tag(dev);
367
368         sc->psc.child = device_add_child(dev, "pci", -1);
369         return (bus_generic_attach(dev));
370
371 fail:
372         vmd_free(sc);
373         return (ENXIO);
374 }
375
376 static int
377 vmd_detach(device_t dev)
378 {
379         struct vmd_softc *sc = device_get_softc(dev);
380         int error;
381
382         error = bus_generic_detach(dev);
383         if (error)
384                 return (error);
385         error = device_delete_children(dev);
386         if (error)
387                 return (error);
388         vmd_free(sc);
389         return (0);
390 }
391
392 static bus_dma_tag_t
393 vmd_get_dma_tag(device_t dev, device_t child)
394 {
395         struct vmd_softc *sc = device_get_softc(dev);
396
397         return (sc->vmd_dma_tag);
398 }
399
400 static struct resource *
401 vmd_alloc_resource(device_t dev, device_t child, int type, int *rid,
402     rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
403 {
404         struct vmd_softc *sc = device_get_softc(dev);
405         struct resource *res;
406
407         switch (type) {
408         case SYS_RES_IRQ:
409                 /* VMD harwdare does not support legacy interrupts. */
410                 if (*rid == 0)
411                         return (NULL);
412                 return (bus_generic_alloc_resource(dev, child, type, rid,
413                     start, end, count, flags | RF_SHAREABLE));
414         case SYS_RES_MEMORY:
415                 res = rman_reserve_resource(&sc->psc.mem.rman, start, end,
416                     count, flags, child);
417                 if (res == NULL)
418                         return (NULL);
419                 if (bootverbose)
420                         device_printf(dev,
421                             "allocated memory range (%#jx-%#jx) for rid %d of %s\n",
422                             rman_get_start(res), rman_get_end(res), *rid,
423                             pcib_child_name(child));
424                 break;
425         case PCI_RES_BUS:
426                 res = rman_reserve_resource(&sc->psc.bus.rman, start, end,
427                     count, flags, child);
428                 if (res == NULL)
429                         return (NULL);
430                 if (bootverbose)
431                         device_printf(dev,
432                             "allocated bus range (%ju-%ju) for rid %d of %s\n",
433                             rman_get_start(res), rman_get_end(res), *rid,
434                             pcib_child_name(child));
435                 break;
436         default:
437                 /* VMD harwdare does not support I/O ports. */
438                 return (NULL);
439         }
440         rman_set_rid(res, *rid);
441         return (res);
442 }
443
444 static int
445 vmd_adjust_resource(device_t dev, device_t child, int type,
446     struct resource *r, rman_res_t start, rman_res_t end)
447 {
448
449         if (type == SYS_RES_IRQ) {
450                 return (bus_generic_adjust_resource(dev, child, type, r,
451                     start, end));
452         }
453         return (rman_adjust_resource(r, start, end));
454 }
455
456 static int
457 vmd_release_resource(device_t dev, device_t child, int type, int rid,
458     struct resource *r)
459 {
460
461         if (type == SYS_RES_IRQ) {
462                 return (bus_generic_release_resource(dev, child, type, rid,
463                     r));
464         }
465         return (rman_release_resource(r));
466 }
467
468 static int
469 vmd_route_interrupt(device_t dev, device_t child, int pin)
470 {
471
472         /* VMD harwdare does not support legacy interrupts. */
473         return (PCI_INVALID_IRQ);
474 }
475
476 static int
477 vmd_alloc_msi(device_t dev, device_t child, int count, int maxcount,
478     int *irqs)
479 {
480         struct vmd_softc *sc = device_get_softc(dev);
481         struct vmd_irq_user *u;
482         int i, ibest = 0, best = INT_MAX;
483
484         if (count > vmd_max_msi)
485                 return (ENOSPC);
486         LIST_FOREACH(u, &sc->vmd_users, viu_link) {
487                 if (u->viu_child == child)
488                         return (EBUSY);
489         }
490
491         for (i = sc->vmd_fist_vector; i < sc->vmd_msix_count; i++) {
492                 if (best > sc->vmd_irq[i].vi_nusers) {
493                         best = sc->vmd_irq[i].vi_nusers;
494                         ibest = i;
495                 }
496         }
497
498         u = malloc(sizeof(*u), M_DEVBUF, M_WAITOK | M_ZERO);
499         u->viu_child = child;
500         u->viu_vector = ibest;
501         LIST_INSERT_HEAD(&sc->vmd_users, u, viu_link);
502         sc->vmd_irq[ibest].vi_nusers += count;
503
504         for (i = 0; i < count; i++)
505                 irqs[i] = sc->vmd_irq[ibest].vi_irq;
506         return (0);
507 }
508
509 static int
510 vmd_release_msi(device_t dev, device_t child, int count, int *irqs)
511 {
512         struct vmd_softc *sc = device_get_softc(dev);
513         struct vmd_irq_user *u;
514
515         LIST_FOREACH(u, &sc->vmd_users, viu_link) {
516                 if (u->viu_child == child) {
517                         sc->vmd_irq[u->viu_vector].vi_nusers -= count;
518                         LIST_REMOVE(u, viu_link);
519                         free(u, M_DEVBUF);
520                         return (0);
521                 }
522         }
523         return (EINVAL);
524 }
525
526 static int
527 vmd_alloc_msix(device_t dev, device_t child, int *irq)
528 {
529         struct vmd_softc *sc = device_get_softc(dev);
530         struct vmd_irq_user *u;
531         int i, ibest = 0, best = INT_MAX;
532
533         i = 0;
534         LIST_FOREACH(u, &sc->vmd_users, viu_link) {
535                 if (u->viu_child == child)
536                         i++;
537         }
538         if (i >= vmd_max_msix)
539                 return (ENOSPC);
540
541         for (i = sc->vmd_fist_vector; i < sc->vmd_msix_count; i++) {
542                 if (best > sc->vmd_irq[i].vi_nusers) {
543                         best = sc->vmd_irq[i].vi_nusers;
544                         ibest = i;
545                 }
546         }
547
548         u = malloc(sizeof(*u), M_DEVBUF, M_WAITOK | M_ZERO);
549         u->viu_child = child;
550         u->viu_vector = ibest;
551         LIST_INSERT_HEAD(&sc->vmd_users, u, viu_link);
552         sc->vmd_irq[ibest].vi_nusers++;
553
554         *irq = sc->vmd_irq[ibest].vi_irq;
555         return (0);
556 }
557
558 static int
559 vmd_release_msix(device_t dev, device_t child, int irq)
560 {
561         struct vmd_softc *sc = device_get_softc(dev);
562         struct vmd_irq_user *u;
563
564         LIST_FOREACH(u, &sc->vmd_users, viu_link) {
565                 if (u->viu_child == child &&
566                     sc->vmd_irq[u->viu_vector].vi_irq == irq) {
567                         sc->vmd_irq[u->viu_vector].vi_nusers--;
568                         LIST_REMOVE(u, viu_link);
569                         free(u, M_DEVBUF);
570                         return (0);
571                 }
572         }
573         return (EINVAL);
574 }
575
576 static int
577 vmd_map_msi(device_t dev, device_t child, int irq, uint64_t *addr, uint32_t *data)
578 {
579         struct vmd_softc *sc = device_get_softc(dev);
580         int i;
581
582         for (i = sc->vmd_fist_vector; i < sc->vmd_msix_count; i++) {
583                 if (sc->vmd_irq[i].vi_irq == irq)
584                         break;
585         }
586         if (i >= sc->vmd_msix_count)
587                 return (EINVAL);
588         *addr = MSI_INTEL_ADDR_BASE | (i << 12);
589         *data = 0;
590         return (0);
591 }
592
593 static device_method_t vmd_pci_methods[] = {
594         /* Device interface */
595         DEVMETHOD(device_probe,                 vmd_probe),
596         DEVMETHOD(device_attach,                vmd_attach),
597         DEVMETHOD(device_detach,                vmd_detach),
598         DEVMETHOD(device_suspend,               bus_generic_suspend),
599         DEVMETHOD(device_resume,                bus_generic_resume),
600         DEVMETHOD(device_shutdown,              bus_generic_shutdown),
601
602         /* Bus interface */
603         DEVMETHOD(bus_get_dma_tag,              vmd_get_dma_tag),
604         DEVMETHOD(bus_read_ivar,                pcib_read_ivar),
605         DEVMETHOD(bus_write_ivar,               pcib_write_ivar),
606         DEVMETHOD(bus_alloc_resource,           vmd_alloc_resource),
607         DEVMETHOD(bus_adjust_resource,          vmd_adjust_resource),
608         DEVMETHOD(bus_release_resource,         vmd_release_resource),
609         DEVMETHOD(bus_activate_resource,        bus_generic_activate_resource),
610         DEVMETHOD(bus_deactivate_resource,      bus_generic_deactivate_resource),
611         DEVMETHOD(bus_setup_intr,               bus_generic_setup_intr),
612         DEVMETHOD(bus_teardown_intr,            bus_generic_teardown_intr),
613
614         /* pcib interface */
615         DEVMETHOD(pcib_maxslots,                pcib_maxslots),
616         DEVMETHOD(pcib_read_config,             vmd_read_config),
617         DEVMETHOD(pcib_write_config,            vmd_write_config),
618         DEVMETHOD(pcib_route_interrupt,         vmd_route_interrupt),
619         DEVMETHOD(pcib_alloc_msi,               vmd_alloc_msi),
620         DEVMETHOD(pcib_release_msi,             vmd_release_msi),
621         DEVMETHOD(pcib_alloc_msix,              vmd_alloc_msix),
622         DEVMETHOD(pcib_release_msix,            vmd_release_msix),
623         DEVMETHOD(pcib_map_msi,                 vmd_map_msi),
624         DEVMETHOD(pcib_request_feature,         pcib_request_feature_allow),
625
626         DEVMETHOD_END
627 };
628
629 static devclass_t pcib_devclass;
630
631 DEFINE_CLASS_0(pcib, vmd_pci_driver, vmd_pci_methods, sizeof(struct vmd_softc));
632 DRIVER_MODULE(vmd, pci, vmd_pci_driver, pcib_devclass, NULL, NULL);
633 MODULE_PNP_INFO("U16:vendor;U16:device;D:#", pci, vmd,
634     vmd_devs, nitems(vmd_devs) - 1);