xref: /freebsd/sys/dev/vmd/vmd.c (revision 0c852bb9b9282b30fd047ac1de398358f33777f4)
1f2521a76SDoug Ambrisko /*-
2f2521a76SDoug Ambrisko  * SPDX-License-Identifier: BSD-2-Clause
3f2521a76SDoug Ambrisko  *
4f2521a76SDoug Ambrisko  * Copyright 2019 Cisco Systems, Inc.
5f2521a76SDoug Ambrisko  * All rights reserved.
6f2521a76SDoug Ambrisko  *
7f2521a76SDoug Ambrisko  * Redistribution and use in source and binary forms, with or without
8f2521a76SDoug Ambrisko  * modification, are permitted provided that the following conditions
9f2521a76SDoug Ambrisko  * are met:
10f2521a76SDoug Ambrisko  * 1. Redistributions of source code must retain the above copyright
11f2521a76SDoug Ambrisko  *    notice, this list of conditions and the following disclaimer.
12f2521a76SDoug Ambrisko  * 2. Redistributions in binary form must reproduce the above copyright
13f2521a76SDoug Ambrisko  *    notice, this list of conditions and the following disclaimer in the
14f2521a76SDoug Ambrisko  *    documentation and/or other materials provided with the distribution.
15f2521a76SDoug Ambrisko  *
16f2521a76SDoug Ambrisko  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17f2521a76SDoug Ambrisko  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18f2521a76SDoug Ambrisko  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19f2521a76SDoug Ambrisko  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20f2521a76SDoug Ambrisko  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21f2521a76SDoug Ambrisko  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22f2521a76SDoug Ambrisko  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23f2521a76SDoug Ambrisko  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24f2521a76SDoug Ambrisko  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25f2521a76SDoug Ambrisko  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26f2521a76SDoug Ambrisko  * SUCH DAMAGE.
27f2521a76SDoug Ambrisko  */
28f2521a76SDoug Ambrisko 
29f2521a76SDoug Ambrisko #include <sys/cdefs.h>
30f2521a76SDoug Ambrisko __FBSDID("$FreeBSD$");
31f2521a76SDoug Ambrisko 
32f2521a76SDoug Ambrisko #include <sys/types.h>
33f2521a76SDoug Ambrisko #include <sys/param.h>
34f2521a76SDoug Ambrisko #include <sys/bus.h>
35f2521a76SDoug Ambrisko #include <sys/conf.h>
36f2521a76SDoug Ambrisko #include <sys/kernel.h>
37f2521a76SDoug Ambrisko #include <sys/module.h>
38f2521a76SDoug Ambrisko #include <sys/systm.h>
39f2521a76SDoug Ambrisko #include <sys/malloc.h>
40f2521a76SDoug Ambrisko 
41f2521a76SDoug Ambrisko #include <machine/bus.h>
42f2521a76SDoug Ambrisko #include <machine/resource.h>
43f2521a76SDoug Ambrisko #include <sys/rman.h>
44f2521a76SDoug Ambrisko #include <sys/lock.h>
45f2521a76SDoug Ambrisko #include <sys/mutex.h>
46f2521a76SDoug Ambrisko #include <sys/taskqueue.h>
47f2521a76SDoug Ambrisko 
48f2521a76SDoug Ambrisko #include <sys/pciio.h>
49f2521a76SDoug Ambrisko #include <dev/pci/pcivar.h>
50f2521a76SDoug Ambrisko #include <dev/pci/pcireg.h>
51f2521a76SDoug Ambrisko #include <dev/pci/pci_private.h>
52f2521a76SDoug Ambrisko #include <dev/pci/pcib_private.h>
53f2521a76SDoug Ambrisko 
54f2521a76SDoug Ambrisko #define TASK_QUEUE_INTR 1
55f2521a76SDoug Ambrisko #include <dev/vmd/vmd.h>
56f2521a76SDoug Ambrisko 
57f2521a76SDoug Ambrisko #include "pcib_if.h"
58f2521a76SDoug Ambrisko #include "pci_if.h"
59f2521a76SDoug Ambrisko 
60f2521a76SDoug Ambrisko struct vmd_type {
61f2521a76SDoug Ambrisko 	u_int16_t	vmd_vid;
62f2521a76SDoug Ambrisko 	u_int16_t	vmd_did;
63f2521a76SDoug Ambrisko 	char		*vmd_name;
64*0c852bb9SDoug Ambrisko 	int		flags;
65*0c852bb9SDoug Ambrisko #define BUS_RESTRICT 1
66f2521a76SDoug Ambrisko };
67f2521a76SDoug Ambrisko 
68f2521a76SDoug Ambrisko #define INTEL_VENDOR_ID		0x8086
69*0c852bb9SDoug Ambrisko #define INTEL_DEVICE_ID_201d	0x201d
70*0c852bb9SDoug Ambrisko #define INTEL_DEVICE_ID_28c0	0x28c0
71*0c852bb9SDoug Ambrisko #define INTEL_DEVICE_ID_467f	0x467f
72*0c852bb9SDoug Ambrisko #define INTEL_DEVICE_ID_4c3d	0x4c3d
73*0c852bb9SDoug Ambrisko #define INTEL_DEVICE_ID_9a0b	0x9a0b
74*0c852bb9SDoug Ambrisko 
75*0c852bb9SDoug Ambrisko #define VMD_CAP		0x40
76*0c852bb9SDoug Ambrisko #define VMD_BUS_RESTRICT	0x1
77*0c852bb9SDoug Ambrisko 
78*0c852bb9SDoug Ambrisko #define VMD_CONFIG	0x44
79*0c852bb9SDoug Ambrisko #define VMD_BUS_START(x)	((x >> 8) & 0x3)
80*0c852bb9SDoug Ambrisko 
81*0c852bb9SDoug Ambrisko #define VMD_LOCK	0x70
82f2521a76SDoug Ambrisko 
83f2521a76SDoug Ambrisko static struct vmd_type vmd_devs[] = {
84*0c852bb9SDoug Ambrisko         { INTEL_VENDOR_ID, INTEL_DEVICE_ID_201d, "Intel Volume Management Device", 0 },
85*0c852bb9SDoug Ambrisko         { INTEL_VENDOR_ID, INTEL_DEVICE_ID_28c0, "Intel Volume Management Device", BUS_RESTRICT },
86*0c852bb9SDoug Ambrisko         { INTEL_VENDOR_ID, INTEL_DEVICE_ID_467f, "Intel Volume Management Device", BUS_RESTRICT },
87*0c852bb9SDoug Ambrisko         { INTEL_VENDOR_ID, INTEL_DEVICE_ID_4c3d, "Intel Volume Management Device", BUS_RESTRICT },
88*0c852bb9SDoug Ambrisko         { INTEL_VENDOR_ID, INTEL_DEVICE_ID_9a0b, "Intel Volume Management Device", BUS_RESTRICT },
89*0c852bb9SDoug Ambrisko         { 0, 0, NULL, 0 }
90f2521a76SDoug Ambrisko };
91f2521a76SDoug Ambrisko 
92f2521a76SDoug Ambrisko static int
93f2521a76SDoug Ambrisko vmd_probe(device_t dev)
94f2521a76SDoug Ambrisko {
95f2521a76SDoug Ambrisko 	struct vmd_type *t;
96f2521a76SDoug Ambrisko 	uint16_t vid, did;
97f2521a76SDoug Ambrisko 
98f2521a76SDoug Ambrisko 	t = vmd_devs;
99f2521a76SDoug Ambrisko 	vid = pci_get_vendor(dev);
100f2521a76SDoug Ambrisko 	did = pci_get_device(dev);
101f2521a76SDoug Ambrisko 
102f2521a76SDoug Ambrisko 	while (t->vmd_name != NULL) {
103f2521a76SDoug Ambrisko 		if (vid == t->vmd_vid &&
104f2521a76SDoug Ambrisko 			did == t->vmd_did) {
105f2521a76SDoug Ambrisko 			device_set_desc(dev, t->vmd_name);
106f2521a76SDoug Ambrisko 			return (BUS_PROBE_DEFAULT);
107f2521a76SDoug Ambrisko 		}
108f2521a76SDoug Ambrisko 		t++;
109f2521a76SDoug Ambrisko 	}
110f2521a76SDoug Ambrisko 
111f2521a76SDoug Ambrisko 	return (ENXIO);
112f2521a76SDoug Ambrisko }
113f2521a76SDoug Ambrisko 
114f2521a76SDoug Ambrisko static void
115f2521a76SDoug Ambrisko vmd_free(struct vmd_softc *sc)
116f2521a76SDoug Ambrisko {
117f2521a76SDoug Ambrisko 	int i;
118f2521a76SDoug Ambrisko 	struct vmd_irq_handler *elm, *tmp;
119f2521a76SDoug Ambrisko 
120e65e4e61SMark Johnston 	if (sc->vmd_bus.rman.rm_end != 0)
121e65e4e61SMark Johnston 		rman_fini(&sc->vmd_bus.rman);
122e65e4e61SMark Johnston 
123f2521a76SDoug Ambrisko #ifdef TASK_QUEUE_INTR
124f2521a76SDoug Ambrisko 	if (sc->vmd_irq_tq != NULL) {
125f2521a76SDoug Ambrisko 		taskqueue_drain(sc->vmd_irq_tq, &sc->vmd_irq_task);
126f2521a76SDoug Ambrisko 		taskqueue_free(sc->vmd_irq_tq);
127f2521a76SDoug Ambrisko 		sc->vmd_irq_tq = NULL;
128f2521a76SDoug Ambrisko 	}
129f2521a76SDoug Ambrisko #endif
130f2521a76SDoug Ambrisko 	if (sc->vmd_irq != NULL) {
131f2521a76SDoug Ambrisko 		for (i = 0; i < sc->vmd_msix_count; i++) {
132f2521a76SDoug Ambrisko 			if (sc->vmd_irq[i].vmd_res != NULL) {
133f2521a76SDoug Ambrisko 				bus_teardown_intr(sc->vmd_dev,
134f2521a76SDoug Ambrisko 				    sc->vmd_irq[i].vmd_res,
135f2521a76SDoug Ambrisko 				    sc->vmd_irq[i].vmd_handle);
136f2521a76SDoug Ambrisko 				bus_release_resource(sc->vmd_dev, SYS_RES_IRQ,
137f2521a76SDoug Ambrisko 				    sc->vmd_irq[i].vmd_rid,
138f2521a76SDoug Ambrisko 				    sc->vmd_irq[i].vmd_res);
139f2521a76SDoug Ambrisko 			}
140f2521a76SDoug Ambrisko 		}
141f2521a76SDoug Ambrisko 		TAILQ_FOREACH_SAFE(elm, &sc->vmd_irq[0].vmd_list ,vmd_link,
142f2521a76SDoug Ambrisko 		    tmp) {
143f2521a76SDoug Ambrisko 			TAILQ_REMOVE(&sc->vmd_irq[0].vmd_list, elm, vmd_link);
144f2521a76SDoug Ambrisko 			free(elm, M_DEVBUF);
145f2521a76SDoug Ambrisko 		}
146f2521a76SDoug Ambrisko 	}
147f2521a76SDoug Ambrisko 	free(sc->vmd_irq, M_DEVBUF);
148f2521a76SDoug Ambrisko 	sc->vmd_irq = NULL;
149f2521a76SDoug Ambrisko 	pci_release_msi(sc->vmd_dev);
150f2521a76SDoug Ambrisko 	for (i = 0; i < VMD_MAX_BAR; i++) {
151f2521a76SDoug Ambrisko 		if (sc->vmd_regs_resource[i] != NULL)
152f2521a76SDoug Ambrisko 			bus_release_resource(sc->vmd_dev, SYS_RES_MEMORY,
153f2521a76SDoug Ambrisko 			    sc->vmd_regs_rid[i],
154f2521a76SDoug Ambrisko 			    sc->vmd_regs_resource[i]);
155f2521a76SDoug Ambrisko 	}
156f2521a76SDoug Ambrisko 	if (sc->vmd_io_resource)
157f2521a76SDoug Ambrisko 		bus_release_resource(device_get_parent(sc->vmd_dev),
158f2521a76SDoug Ambrisko 		    SYS_RES_IOPORT, sc->vmd_io_rid, sc->vmd_io_resource);
159f2521a76SDoug Ambrisko 
160f2521a76SDoug Ambrisko #ifndef TASK_QUEUE_INTR
161f2521a76SDoug Ambrisko 	if (mtx_initialized(&sc->vmd_irq_lock)) {
162f2521a76SDoug Ambrisko 		mtx_destroy(&sc->vmd_irq_lock);
163f2521a76SDoug Ambrisko 	}
164f2521a76SDoug Ambrisko #endif
165f2521a76SDoug Ambrisko }
166f2521a76SDoug Ambrisko 
167f2521a76SDoug Ambrisko /* Hidden PCI Roots are hidden in BAR(0). */
168f2521a76SDoug Ambrisko 
169f2521a76SDoug Ambrisko static uint32_t
170f2521a76SDoug Ambrisko vmd_read_config(device_t dev, u_int b, u_int s, u_int f, u_int reg, int width)
171f2521a76SDoug Ambrisko {
172f2521a76SDoug Ambrisko 
173f2521a76SDoug Ambrisko 	struct vmd_softc *sc;
174f2521a76SDoug Ambrisko 	bus_addr_t offset;
175f2521a76SDoug Ambrisko 
176f2521a76SDoug Ambrisko 	sc = device_get_softc(dev);
177*0c852bb9SDoug Ambrisko 	if (b < sc->vmd_bus_start)
178*0c852bb9SDoug Ambrisko 		return (0xffffffff);
179*0c852bb9SDoug Ambrisko 
180*0c852bb9SDoug Ambrisko 	offset = ((b - sc->vmd_bus_start) << 20) + (s << 15) + (f << 12) + reg;
181*0c852bb9SDoug Ambrisko 
182f2521a76SDoug Ambrisko 	switch(width) {
183f2521a76SDoug Ambrisko 	case 4:
184f2521a76SDoug Ambrisko 		return (bus_space_read_4(sc->vmd_btag, sc->vmd_bhandle,
185f2521a76SDoug Ambrisko 		    offset));
186f2521a76SDoug Ambrisko 	case 2:
187f2521a76SDoug Ambrisko 		return (bus_space_read_2(sc->vmd_btag, sc->vmd_bhandle,
188f2521a76SDoug Ambrisko 		    offset));
189f2521a76SDoug Ambrisko 	case 1:
190f2521a76SDoug Ambrisko 		return (bus_space_read_1(sc->vmd_btag, sc->vmd_bhandle,
191f2521a76SDoug Ambrisko 		    offset));
192f2521a76SDoug Ambrisko 	default:
193f2521a76SDoug Ambrisko 		KASSERT(1, ("Invalid width requested"));
194f2521a76SDoug Ambrisko 		return (0xffffffff);
195f2521a76SDoug Ambrisko 	}
196f2521a76SDoug Ambrisko }
197f2521a76SDoug Ambrisko 
198f2521a76SDoug Ambrisko static void
199f2521a76SDoug Ambrisko vmd_write_config(device_t dev, u_int b, u_int s, u_int f, u_int reg,
200f2521a76SDoug Ambrisko     uint32_t val, int width)
201f2521a76SDoug Ambrisko {
202f2521a76SDoug Ambrisko 
203f2521a76SDoug Ambrisko 	struct vmd_softc *sc;
204f2521a76SDoug Ambrisko 	bus_addr_t offset;
205f2521a76SDoug Ambrisko 
206f2521a76SDoug Ambrisko 	sc = device_get_softc(dev);
207*0c852bb9SDoug Ambrisko 	if (b < sc->vmd_bus_start)
208*0c852bb9SDoug Ambrisko 		return;
209*0c852bb9SDoug Ambrisko 
210*0c852bb9SDoug Ambrisko 	offset = ((b - sc->vmd_bus_start) << 20) + (s << 15) + (f << 12) + reg;
211f2521a76SDoug Ambrisko 
212f2521a76SDoug Ambrisko 	switch(width) {
213f2521a76SDoug Ambrisko 	case 4:
214f2521a76SDoug Ambrisko 		return (bus_space_write_4(sc->vmd_btag, sc->vmd_bhandle,
215f2521a76SDoug Ambrisko 		    offset, val));
216f2521a76SDoug Ambrisko 	case 2:
217f2521a76SDoug Ambrisko 		return (bus_space_write_2(sc->vmd_btag, sc->vmd_bhandle,
218f2521a76SDoug Ambrisko 		    offset, val));
219f2521a76SDoug Ambrisko 	case 1:
220f2521a76SDoug Ambrisko 		return (bus_space_write_1(sc->vmd_btag, sc->vmd_bhandle,
221f2521a76SDoug Ambrisko 		    offset, val));
222f2521a76SDoug Ambrisko 	default:
223f2521a76SDoug Ambrisko 		panic("Failed to specific width");
224f2521a76SDoug Ambrisko 	}
225f2521a76SDoug Ambrisko }
226f2521a76SDoug Ambrisko 
227f2521a76SDoug Ambrisko static uint32_t
228f2521a76SDoug Ambrisko vmd_pci_read_config(device_t dev, device_t child, int reg, int width)
229f2521a76SDoug Ambrisko {
230f2521a76SDoug Ambrisko 	struct pci_devinfo *dinfo = device_get_ivars(child);
231f2521a76SDoug Ambrisko 	pcicfgregs *cfg = &dinfo->cfg;
232f2521a76SDoug Ambrisko 
233f2521a76SDoug Ambrisko 	return vmd_read_config(dev, cfg->bus, cfg->slot, cfg->func, reg, width);
234f2521a76SDoug Ambrisko }
235f2521a76SDoug Ambrisko 
236f2521a76SDoug Ambrisko static void
237f2521a76SDoug Ambrisko vmd_pci_write_config(device_t dev, device_t child, int reg, uint32_t val,
238f2521a76SDoug Ambrisko     int width)
239f2521a76SDoug Ambrisko {
240f2521a76SDoug Ambrisko 	struct pci_devinfo *dinfo = device_get_ivars(child);
241f2521a76SDoug Ambrisko 	pcicfgregs *cfg = &dinfo->cfg;
242f2521a76SDoug Ambrisko 
243f2521a76SDoug Ambrisko 	vmd_write_config(dev, cfg->bus, cfg->slot, cfg->func, reg, val, width);
244f2521a76SDoug Ambrisko }
245f2521a76SDoug Ambrisko 
246f2521a76SDoug Ambrisko static struct pci_devinfo *
247f2521a76SDoug Ambrisko vmd_alloc_devinfo(device_t dev)
248f2521a76SDoug Ambrisko {
249f2521a76SDoug Ambrisko 	struct pci_devinfo *dinfo;
250f2521a76SDoug Ambrisko 
251f2521a76SDoug Ambrisko 	dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO);
252f2521a76SDoug Ambrisko 	return (dinfo);
253f2521a76SDoug Ambrisko }
254f2521a76SDoug Ambrisko 
255f2521a76SDoug Ambrisko static void
256f2521a76SDoug Ambrisko vmd_intr(void *arg)
257f2521a76SDoug Ambrisko {
258f2521a76SDoug Ambrisko 	struct vmd_irq  *irq;
259f2521a76SDoug Ambrisko 	struct vmd_softc *sc;
260f2521a76SDoug Ambrisko #ifndef TASK_QUEUE_INTR
261f2521a76SDoug Ambrisko 	struct vmd_irq_handler *elm, *tmp_elm;
262f2521a76SDoug Ambrisko #endif
263f2521a76SDoug Ambrisko 
264f2521a76SDoug Ambrisko 	irq = (struct vmd_irq *)arg;
265f2521a76SDoug Ambrisko 	sc = irq->vmd_sc;
266f2521a76SDoug Ambrisko #ifdef TASK_QUEUE_INTR
267f2521a76SDoug Ambrisko 	taskqueue_enqueue(sc->vmd_irq_tq, &sc->vmd_irq_task);
268f2521a76SDoug Ambrisko #else
269f2521a76SDoug Ambrisko 	mtx_lock(&sc->vmd_irq_lock);
270f2521a76SDoug Ambrisko 	TAILQ_FOREACH_SAFE(elm, &sc->vmd_irq[0].vmd_list, vmd_link, tmp_elm) {
271f2521a76SDoug Ambrisko 		(elm->vmd_intr)(elm->vmd_arg);
272f2521a76SDoug Ambrisko 	}
273f2521a76SDoug Ambrisko 	mtx_unlock(&sc->vmd_irq_lock);
274f2521a76SDoug Ambrisko #endif
275f2521a76SDoug Ambrisko }
276f2521a76SDoug Ambrisko 
277f2521a76SDoug Ambrisko #ifdef TASK_QUEUE_INTR
278f2521a76SDoug Ambrisko static void
279f2521a76SDoug Ambrisko vmd_handle_irq(void *context, int pending)
280f2521a76SDoug Ambrisko {
281f2521a76SDoug Ambrisko 	struct vmd_irq_handler *elm, *tmp_elm;
282f2521a76SDoug Ambrisko 	struct vmd_softc *sc;
283f2521a76SDoug Ambrisko 
284f2521a76SDoug Ambrisko 	sc = context;
285f2521a76SDoug Ambrisko 
286f2521a76SDoug Ambrisko 	TAILQ_FOREACH_SAFE(elm, &sc->vmd_irq[0].vmd_list, vmd_link, tmp_elm) {
287f2521a76SDoug Ambrisko 		(elm->vmd_intr)(elm->vmd_arg);
288f2521a76SDoug Ambrisko 	}
289f2521a76SDoug Ambrisko }
290f2521a76SDoug Ambrisko #endif
291f2521a76SDoug Ambrisko 
292f2521a76SDoug Ambrisko static int
293f2521a76SDoug Ambrisko vmd_attach(device_t dev)
294f2521a76SDoug Ambrisko {
295f2521a76SDoug Ambrisko 	struct vmd_softc *sc;
296f2521a76SDoug Ambrisko 	struct pcib_secbus *bus;
297*0c852bb9SDoug Ambrisko 	struct vmd_type *t;
298*0c852bb9SDoug Ambrisko 	uint16_t vid, did;
299f2521a76SDoug Ambrisko 	uint32_t bar;
300f2521a76SDoug Ambrisko 	int i, j, error;
301f2521a76SDoug Ambrisko 	int rid, sec_reg;
302f2521a76SDoug Ambrisko 	static int b;
303f2521a76SDoug Ambrisko 	static int s;
304f2521a76SDoug Ambrisko 	static int f;
305f2521a76SDoug Ambrisko 	int min_count = 1;
306f2521a76SDoug Ambrisko 	char buf[64];
307f2521a76SDoug Ambrisko 
308f2521a76SDoug Ambrisko 	sc = device_get_softc(dev);
309f2521a76SDoug Ambrisko 	bzero(sc, sizeof(*sc));
310f2521a76SDoug Ambrisko 	sc->vmd_dev = dev;
311f2521a76SDoug Ambrisko 	b = s = f = 0;
312f2521a76SDoug Ambrisko 
313f2521a76SDoug Ambrisko 	pci_enable_busmaster(dev);
314f2521a76SDoug Ambrisko 
315f2521a76SDoug Ambrisko #ifdef TASK_QUEUE_INTR
316f2521a76SDoug Ambrisko 	sc->vmd_irq_tq = taskqueue_create_fast("vmd_taskq", M_NOWAIT,
317f2521a76SDoug Ambrisko 	    taskqueue_thread_enqueue, &sc->vmd_irq_tq);
318f2521a76SDoug Ambrisko 	taskqueue_start_threads(&sc->vmd_irq_tq, 1, PI_DISK, "%s taskq",
319f2521a76SDoug Ambrisko             device_get_nameunit(sc->vmd_dev));
320f2521a76SDoug Ambrisko 	TASK_INIT(&sc->vmd_irq_task, 0, vmd_handle_irq, sc);
321f2521a76SDoug Ambrisko #else
322f2521a76SDoug Ambrisko 	mtx_init(&sc->vmd_irq_lock, "VMD IRQ lock", NULL, MTX_DEF);
323f2521a76SDoug Ambrisko #endif
324f2521a76SDoug Ambrisko 	for (i = 0, j = 0; i < VMD_MAX_BAR; i++, j++ ) {
325f2521a76SDoug Ambrisko 		sc->vmd_regs_rid[i] = PCIR_BAR(j);
326f2521a76SDoug Ambrisko 		bar = pci_read_config(dev, PCIR_BAR(0), 4);
327f2521a76SDoug Ambrisko 		if (PCI_BAR_MEM(bar) && (bar & PCIM_BAR_MEM_TYPE) ==
328f2521a76SDoug Ambrisko 		    PCIM_BAR_MEM_64)
329f2521a76SDoug Ambrisko 			j++;
330f2521a76SDoug Ambrisko 		if ((sc->vmd_regs_resource[i] = bus_alloc_resource_any(
331f2521a76SDoug Ambrisko 		    sc->vmd_dev, SYS_RES_MEMORY, &sc->vmd_regs_rid[i],
332f2521a76SDoug Ambrisko 		    RF_ACTIVE)) == NULL) {
333f2521a76SDoug Ambrisko 			device_printf(dev, "Cannot allocate resources\n");
334f2521a76SDoug Ambrisko 			goto fail;
335f2521a76SDoug Ambrisko 		}
336f2521a76SDoug Ambrisko 	}
337f2521a76SDoug Ambrisko 
338f2521a76SDoug Ambrisko 	sc->vmd_io_rid = PCIR_IOBASEL_1;
339f2521a76SDoug Ambrisko 	sc->vmd_io_resource = bus_alloc_resource_any(
340f2521a76SDoug Ambrisko 	    device_get_parent(sc->vmd_dev), SYS_RES_IOPORT, &sc->vmd_io_rid,
341f2521a76SDoug Ambrisko 	    RF_ACTIVE);
342f2521a76SDoug Ambrisko 	if (sc->vmd_io_resource == NULL) {
343f2521a76SDoug Ambrisko 		device_printf(dev, "Cannot allocate IO\n");
344f2521a76SDoug Ambrisko 		goto fail;
345f2521a76SDoug Ambrisko 	}
346f2521a76SDoug Ambrisko 
347f2521a76SDoug Ambrisko 	sc->vmd_btag = rman_get_bustag(sc->vmd_regs_resource[0]);
348f2521a76SDoug Ambrisko 	sc->vmd_bhandle = rman_get_bushandle(sc->vmd_regs_resource[0]);
349f2521a76SDoug Ambrisko 
350f2521a76SDoug Ambrisko 	pci_write_config(dev, PCIR_PRIBUS_2,
351f2521a76SDoug Ambrisko 	    pcib_get_bus(device_get_parent(dev)), 1);
352f2521a76SDoug Ambrisko 
353*0c852bb9SDoug Ambrisko 	t = vmd_devs;
354*0c852bb9SDoug Ambrisko 	vid = pci_get_vendor(dev);
355*0c852bb9SDoug Ambrisko 	did = pci_get_device(dev);
356*0c852bb9SDoug Ambrisko 
357*0c852bb9SDoug Ambrisko 	sc->vmd_bus_start = 0;
358*0c852bb9SDoug Ambrisko 	while (t->vmd_name != NULL) {
359*0c852bb9SDoug Ambrisko 		if (vid == t->vmd_vid &&
360*0c852bb9SDoug Ambrisko 			did == t->vmd_did) {
361*0c852bb9SDoug Ambrisko 			if (t->flags == BUS_RESTRICT) {
362*0c852bb9SDoug Ambrisko 				if (pci_read_config(dev, VMD_CAP, 2) &
363*0c852bb9SDoug Ambrisko 				    VMD_BUS_RESTRICT)
364*0c852bb9SDoug Ambrisko 					switch (VMD_BUS_START(pci_read_config(
365*0c852bb9SDoug Ambrisko 					    dev, VMD_CONFIG, 2))) {
366*0c852bb9SDoug Ambrisko 					case 1:
367*0c852bb9SDoug Ambrisko 						sc->vmd_bus_start = 128;
368*0c852bb9SDoug Ambrisko 						break;
369*0c852bb9SDoug Ambrisko 					case 2:
370*0c852bb9SDoug Ambrisko 						sc->vmd_bus_start = 224;
371*0c852bb9SDoug Ambrisko 						break;
372*0c852bb9SDoug Ambrisko 					case 3:
373*0c852bb9SDoug Ambrisko 						device_printf(dev,
374*0c852bb9SDoug Ambrisko 						    "Unknown bug offset\n");
375*0c852bb9SDoug Ambrisko 						goto fail;
376*0c852bb9SDoug Ambrisko 						break;
377*0c852bb9SDoug Ambrisko 					}
378*0c852bb9SDoug Ambrisko 			}
379*0c852bb9SDoug Ambrisko 		}
380*0c852bb9SDoug Ambrisko 		t++;
381*0c852bb9SDoug Ambrisko 	}
382*0c852bb9SDoug Ambrisko 
383*0c852bb9SDoug Ambrisko 	device_printf(dev, "VMD bus starts at %d\n", sc->vmd_bus_start);
384*0c852bb9SDoug Ambrisko 
385f2521a76SDoug Ambrisko 	sec_reg = PCIR_SECBUS_1;
386f2521a76SDoug Ambrisko 	bus = &sc->vmd_bus;
387f2521a76SDoug Ambrisko 	bus->sub_reg = PCIR_SUBBUS_1;
388f2521a76SDoug Ambrisko 	bus->sec = vmd_read_config(dev, b, s, f, sec_reg, 1);
389f2521a76SDoug Ambrisko 	bus->sub = vmd_read_config(dev, b, s, f, bus->sub_reg, 1);
390f2521a76SDoug Ambrisko 	bus->dev = dev;
391*0c852bb9SDoug Ambrisko 	bus->rman.rm_start = sc->vmd_bus_start;
392f2521a76SDoug Ambrisko 	bus->rman.rm_end = PCI_BUSMAX;
393f2521a76SDoug Ambrisko 	bus->rman.rm_type = RMAN_ARRAY;
394f2521a76SDoug Ambrisko 	snprintf(buf, sizeof(buf), "%s bus numbers", device_get_nameunit(dev));
395f2521a76SDoug Ambrisko 	bus->rman.rm_descr = strdup(buf, M_DEVBUF);
396f2521a76SDoug Ambrisko 	error = rman_init(&bus->rman);
397f2521a76SDoug Ambrisko 	if (error) {
398f2521a76SDoug Ambrisko 		device_printf(dev, "Failed to initialize %s bus number rman\n",
399f2521a76SDoug Ambrisko 		    device_get_nameunit(dev));
400e65e4e61SMark Johnston 		bus->rman.rm_end = 0;
401f2521a76SDoug Ambrisko 		goto fail;
402f2521a76SDoug Ambrisko 	}
403f2521a76SDoug Ambrisko 
404f2521a76SDoug Ambrisko 	/*
405f2521a76SDoug Ambrisko 	 * Allocate a bus range.  This will return an existing bus range
406f2521a76SDoug Ambrisko 	 * if one exists, or a new bus range if one does not.
407f2521a76SDoug Ambrisko 	 */
408f2521a76SDoug Ambrisko 	rid = 0;
409f2521a76SDoug Ambrisko 	bus->res = bus_alloc_resource_anywhere(dev, PCI_RES_BUS, &rid,
410f2521a76SDoug Ambrisko 	    min_count, 0);
411f2521a76SDoug Ambrisko 	if (bus->res == NULL) {
412f2521a76SDoug Ambrisko 		/*
413f2521a76SDoug Ambrisko 		 * Fall back to just allocating a range of a single bus
414f2521a76SDoug Ambrisko 		 * number.
415f2521a76SDoug Ambrisko 		 */
416f2521a76SDoug Ambrisko 		bus->res = bus_alloc_resource_anywhere(dev, PCI_RES_BUS, &rid,
417f2521a76SDoug Ambrisko 		    1, 0);
418f2521a76SDoug Ambrisko 	} else if (rman_get_size(bus->res) < min_count) {
419f2521a76SDoug Ambrisko 		/*
420f2521a76SDoug Ambrisko 		 * Attempt to grow the existing range to satisfy the
421f2521a76SDoug Ambrisko 		 * minimum desired count.
422f2521a76SDoug Ambrisko 		 */
423f2521a76SDoug Ambrisko 		(void)bus_adjust_resource(dev, PCI_RES_BUS, bus->res,
424f2521a76SDoug Ambrisko 		    rman_get_start(bus->res), rman_get_start(bus->res) +
425f2521a76SDoug Ambrisko 		    min_count - 1);
426f2521a76SDoug Ambrisko 	}
427f2521a76SDoug Ambrisko 
428f2521a76SDoug Ambrisko 	/*
429f2521a76SDoug Ambrisko 	 * Add the initial resource to the rman.
430f2521a76SDoug Ambrisko 	 */
431f2521a76SDoug Ambrisko 	if (bus->res != NULL) {
432f2521a76SDoug Ambrisko 		error = rman_manage_region(&bus->rman, rman_get_start(bus->res),
433f2521a76SDoug Ambrisko 		    rman_get_end(bus->res));
434f2521a76SDoug Ambrisko 		if (error) {
435f2521a76SDoug Ambrisko 			device_printf(dev, "Failed to add resource to rman\n");
436f2521a76SDoug Ambrisko 			goto fail;
437f2521a76SDoug Ambrisko 		}
438f2521a76SDoug Ambrisko 		bus->sec = rman_get_start(bus->res);
439f2521a76SDoug Ambrisko 		bus->sub = rman_get_end(bus->res);
440f2521a76SDoug Ambrisko 	}
441f2521a76SDoug Ambrisko 
442f2521a76SDoug Ambrisko 	sc->vmd_msix_count = pci_msix_count(dev);
443f2521a76SDoug Ambrisko 	if (pci_alloc_msix(dev, &sc->vmd_msix_count) == 0) {
444f2521a76SDoug Ambrisko 		sc->vmd_irq = malloc(sizeof(struct vmd_irq) *
445f2521a76SDoug Ambrisko 		    sc->vmd_msix_count,
446f2521a76SDoug Ambrisko 		    M_DEVBUF, M_WAITOK | M_ZERO);
447f2521a76SDoug Ambrisko 
448f2521a76SDoug Ambrisko 		for (i = 0; i < sc->vmd_msix_count; i++) {
449f2521a76SDoug Ambrisko 			sc->vmd_irq[i].vmd_rid = i + 1;
450f2521a76SDoug Ambrisko 			sc->vmd_irq[i].vmd_sc = sc;
451f2521a76SDoug Ambrisko 			sc->vmd_irq[i].vmd_instance = i;
452f2521a76SDoug Ambrisko 			sc->vmd_irq[i].vmd_res = bus_alloc_resource_any(dev,
453f2521a76SDoug Ambrisko 			    SYS_RES_IRQ, &sc->vmd_irq[i].vmd_rid,
454f2521a76SDoug Ambrisko 			    RF_ACTIVE);
455f2521a76SDoug Ambrisko 			if (sc->vmd_irq[i].vmd_res == NULL) {
456f2521a76SDoug Ambrisko 				device_printf(dev,"Failed to alloc irq\n");
457f2521a76SDoug Ambrisko 				goto fail;
458f2521a76SDoug Ambrisko 			}
459f2521a76SDoug Ambrisko 
460f2521a76SDoug Ambrisko 			TAILQ_INIT(&sc->vmd_irq[i].vmd_list);
461f2521a76SDoug Ambrisko 			if (bus_setup_intr(dev, sc->vmd_irq[i].vmd_res,
462f2521a76SDoug Ambrisko 			    INTR_TYPE_MISC | INTR_MPSAFE, NULL, vmd_intr,
463f2521a76SDoug Ambrisko 			    &sc->vmd_irq[i], &sc->vmd_irq[i].vmd_handle)) {
464f2521a76SDoug Ambrisko 				device_printf(sc->vmd_dev,
465f2521a76SDoug Ambrisko 				    "Cannot set up interrupt\n");
466f2521a76SDoug Ambrisko 				sc->vmd_irq[i].vmd_res = NULL;
467f2521a76SDoug Ambrisko 				goto fail;
468f2521a76SDoug Ambrisko 			}
469f2521a76SDoug Ambrisko 		}
470f2521a76SDoug Ambrisko 	}
471f2521a76SDoug Ambrisko 
472f2521a76SDoug Ambrisko 	sc->vmd_child = device_add_child(dev, NULL, -1);
473f2521a76SDoug Ambrisko 	if (sc->vmd_child == NULL) {
474f2521a76SDoug Ambrisko 		device_printf(dev, "Failed to attach child\n");
475f2521a76SDoug Ambrisko 		goto fail;
476f2521a76SDoug Ambrisko 	}
477f2521a76SDoug Ambrisko 
478f2521a76SDoug Ambrisko 	error = device_probe_and_attach(sc->vmd_child);
479f2521a76SDoug Ambrisko 	if (error) {
480e65e4e61SMark Johnston 		device_printf(dev, "Failed to add probe child: %d\n", error);
481e65e4e61SMark Johnston 		(void)device_delete_child(dev, sc->vmd_child);
482f2521a76SDoug Ambrisko 		goto fail;
483f2521a76SDoug Ambrisko 	}
484f2521a76SDoug Ambrisko 
485f2521a76SDoug Ambrisko 	return (0);
486f2521a76SDoug Ambrisko 
487f2521a76SDoug Ambrisko fail:
488f2521a76SDoug Ambrisko 	vmd_free(sc);
489f2521a76SDoug Ambrisko 	return (ENXIO);
490f2521a76SDoug Ambrisko }
491f2521a76SDoug Ambrisko 
492f2521a76SDoug Ambrisko static int
493f2521a76SDoug Ambrisko vmd_detach(device_t dev)
494f2521a76SDoug Ambrisko {
495f2521a76SDoug Ambrisko 	struct vmd_softc *sc;
496f2521a76SDoug Ambrisko 	int err;
497f2521a76SDoug Ambrisko 
498f2521a76SDoug Ambrisko 	sc = device_get_softc(dev);
499f2521a76SDoug Ambrisko 	if (sc->vmd_child != NULL) {
500f2521a76SDoug Ambrisko 		err = bus_generic_detach(sc->vmd_child);
501f2521a76SDoug Ambrisko 		if (err)
502f2521a76SDoug Ambrisko 			return (err);
503f2521a76SDoug Ambrisko 		err = device_delete_child(dev, sc->vmd_child);
504f2521a76SDoug Ambrisko 		if (err)
505f2521a76SDoug Ambrisko 			return (err);
506f2521a76SDoug Ambrisko 	}
507f2521a76SDoug Ambrisko 	vmd_free(sc);
508f2521a76SDoug Ambrisko 	return (0);
509f2521a76SDoug Ambrisko }
510f2521a76SDoug Ambrisko 
511f2521a76SDoug Ambrisko /* Pass request to alloc an MSI-X message up to the parent bridge. */
512f2521a76SDoug Ambrisko static int
513f2521a76SDoug Ambrisko vmd_alloc_msix(device_t pcib, device_t dev, int *irq)
514f2521a76SDoug Ambrisko {
515f2521a76SDoug Ambrisko 	struct vmd_softc *sc = device_get_softc(pcib);
516f2521a76SDoug Ambrisko 	device_t bus;
517f2521a76SDoug Ambrisko 	int ret;
518f2521a76SDoug Ambrisko 
519f2521a76SDoug Ambrisko 	if (sc->vmd_flags & PCIB_DISABLE_MSIX)
520f2521a76SDoug Ambrisko 		return (ENXIO);
521f2521a76SDoug Ambrisko 	bus = device_get_parent(pcib);
522f2521a76SDoug Ambrisko 	ret = PCIB_ALLOC_MSIX(device_get_parent(bus), dev, irq);
523f2521a76SDoug Ambrisko         return (ret);
524f2521a76SDoug Ambrisko }
525f2521a76SDoug Ambrisko 
526f2521a76SDoug Ambrisko static struct resource *
527f2521a76SDoug Ambrisko vmd_alloc_resource(device_t dev, device_t child, int type, int *rid,
528f2521a76SDoug Ambrisko     rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
529f2521a76SDoug Ambrisko {
530f2521a76SDoug Ambrisko 	/* Start at max PCI vmd_domain and work down */
531f2521a76SDoug Ambrisko 	if (type == PCI_RES_BUS) {
532f2521a76SDoug Ambrisko 		return (pci_domain_alloc_bus(PCI_DOMAINMAX -
533f2521a76SDoug Ambrisko 		    device_get_unit(dev), child, rid, start, end,
534f2521a76SDoug Ambrisko 		    count, flags));
535f2521a76SDoug Ambrisko 	}
536f2521a76SDoug Ambrisko 
537f2521a76SDoug Ambrisko 	return (pcib_alloc_resource(dev, child, type, rid, start, end,
538f2521a76SDoug Ambrisko 				    count, flags));
539f2521a76SDoug Ambrisko }
540f2521a76SDoug Ambrisko 
541f2521a76SDoug Ambrisko static int
542f2521a76SDoug Ambrisko vmd_adjust_resource(device_t dev, device_t child, int type,
543f2521a76SDoug Ambrisko     struct resource *r, rman_res_t start, rman_res_t end)
544f2521a76SDoug Ambrisko {
545f2521a76SDoug Ambrisko 	struct resource *res = r;
546f2521a76SDoug Ambrisko 
547f2521a76SDoug Ambrisko 	if (type == PCI_RES_BUS)
548f2521a76SDoug Ambrisko 		return (pci_domain_adjust_bus(PCI_DOMAINMAX -
549f2521a76SDoug Ambrisko 			device_get_unit(dev), child, res, start, end));
550f2521a76SDoug Ambrisko 	return (pcib_adjust_resource(dev, child, type, res, start, end));
551f2521a76SDoug Ambrisko }
552f2521a76SDoug Ambrisko 
553f2521a76SDoug Ambrisko static int
554f2521a76SDoug Ambrisko vmd_release_resource(device_t dev, device_t child, int type, int rid,
555f2521a76SDoug Ambrisko     struct resource *r)
556f2521a76SDoug Ambrisko {
557f2521a76SDoug Ambrisko 	if (type == PCI_RES_BUS)
558f2521a76SDoug Ambrisko 		return (pci_domain_release_bus(PCI_DOMAINMAX -
559f2521a76SDoug Ambrisko 		    device_get_unit(dev), child, rid, r));
560f2521a76SDoug Ambrisko 	return (pcib_release_resource(dev, child, type, rid, r));
561f2521a76SDoug Ambrisko }
562f2521a76SDoug Ambrisko 
563f2521a76SDoug Ambrisko static int
564f2521a76SDoug Ambrisko vmd_shutdown(device_t dev)
565f2521a76SDoug Ambrisko {
566f2521a76SDoug Ambrisko 	return (0);
567f2521a76SDoug Ambrisko }
568f2521a76SDoug Ambrisko 
569f2521a76SDoug Ambrisko static int
570f2521a76SDoug Ambrisko vmd_pcib_route_interrupt(device_t pcib, device_t dev, int pin)
571f2521a76SDoug Ambrisko {
572f2521a76SDoug Ambrisko 	return (pcib_route_interrupt(pcib, dev, pin));
573f2521a76SDoug Ambrisko }
574f2521a76SDoug Ambrisko 
575f2521a76SDoug Ambrisko static int
576f2521a76SDoug Ambrisko vmd_pcib_alloc_msi(device_t pcib, device_t dev, int count, int maxcount,
577f2521a76SDoug Ambrisko     int *irqs)
578f2521a76SDoug Ambrisko {
579f2521a76SDoug Ambrisko 	return (pcib_alloc_msi(pcib, dev, count, maxcount, irqs));
580f2521a76SDoug Ambrisko }
581f2521a76SDoug Ambrisko 
582f2521a76SDoug Ambrisko static int
583f2521a76SDoug Ambrisko vmd_pcib_release_msi(device_t pcib, device_t dev, int count, int *irqs)
584f2521a76SDoug Ambrisko {
585f2521a76SDoug Ambrisko 
586f2521a76SDoug Ambrisko 	return (pcib_release_msi(pcib, dev, count, irqs));
587f2521a76SDoug Ambrisko }
588f2521a76SDoug Ambrisko 
589f2521a76SDoug Ambrisko static int
590f2521a76SDoug Ambrisko vmd_pcib_release_msix(device_t pcib, device_t dev, int irq) {
591f2521a76SDoug Ambrisko 	return	pcib_release_msix(pcib, dev, irq);
592f2521a76SDoug Ambrisko }
593f2521a76SDoug Ambrisko 
594f2521a76SDoug Ambrisko static int
595f2521a76SDoug Ambrisko vmd_setup_intr(device_t dev, device_t child, struct resource *irq,
596f2521a76SDoug Ambrisko     int flags, driver_filter_t *filter, driver_intr_t *intr, void *arg,
597f2521a76SDoug Ambrisko     void **cookiep)
598f2521a76SDoug Ambrisko {
599f2521a76SDoug Ambrisko 	struct vmd_irq_handler *elm;
600f2521a76SDoug Ambrisko 	struct vmd_softc *sc;
601f2521a76SDoug Ambrisko 	int i;
602f2521a76SDoug Ambrisko 
603f2521a76SDoug Ambrisko 	sc = device_get_softc(dev);
604f2521a76SDoug Ambrisko 
605f2521a76SDoug Ambrisko 	/*
606f2521a76SDoug Ambrisko 	 * There appears to be no steering of VMD interrupts from device
607f2521a76SDoug Ambrisko 	 * to VMD interrupt
608f2521a76SDoug Ambrisko 	 */
609f2521a76SDoug Ambrisko 
610f2521a76SDoug Ambrisko 	i = 0;
611f2521a76SDoug Ambrisko 	elm = malloc(sizeof(*elm), M_DEVBUF, M_NOWAIT|M_ZERO);
612f2521a76SDoug Ambrisko 	elm->vmd_child = child;
613f2521a76SDoug Ambrisko 	elm->vmd_intr = intr;
614f2521a76SDoug Ambrisko 	elm->vmd_rid = rman_get_rid(irq);
615f2521a76SDoug Ambrisko 	elm->vmd_arg = arg;
616f2521a76SDoug Ambrisko 	TAILQ_INSERT_TAIL(&sc->vmd_irq[i].vmd_list, elm, vmd_link);
617f2521a76SDoug Ambrisko 
618f2521a76SDoug Ambrisko 	return (bus_generic_setup_intr(dev, child, irq, flags, filter, intr,
619f2521a76SDoug Ambrisko 	    arg, cookiep));
620f2521a76SDoug Ambrisko }
621f2521a76SDoug Ambrisko 
622f2521a76SDoug Ambrisko static int
623f2521a76SDoug Ambrisko vmd_teardown_intr(device_t dev, device_t child, struct resource *irq,
624f2521a76SDoug Ambrisko     void *cookie)
625f2521a76SDoug Ambrisko {
626f2521a76SDoug Ambrisko 	struct vmd_irq_handler *elm, *tmp;;
627f2521a76SDoug Ambrisko 	struct vmd_softc *sc;
628f2521a76SDoug Ambrisko 
629f2521a76SDoug Ambrisko 	sc = device_get_softc(dev);
630f2521a76SDoug Ambrisko 	TAILQ_FOREACH_SAFE(elm, &sc->vmd_irq[0].vmd_list, vmd_link, tmp) {
631f2521a76SDoug Ambrisko 		if (elm->vmd_child == child &&
632f2521a76SDoug Ambrisko 		    elm->vmd_rid == rman_get_rid(irq)) {
633f2521a76SDoug Ambrisko 			TAILQ_REMOVE(&sc->vmd_irq[0].vmd_list, elm, vmd_link);
634f2521a76SDoug Ambrisko 			free(elm, M_DEVBUF);
635f2521a76SDoug Ambrisko 		}
636f2521a76SDoug Ambrisko 	}
637f2521a76SDoug Ambrisko 
638f2521a76SDoug Ambrisko 	return (bus_generic_teardown_intr(dev, child, irq, cookie));
639f2521a76SDoug Ambrisko }
640f2521a76SDoug Ambrisko 
641f2521a76SDoug Ambrisko static device_method_t vmd_pci_methods[] = {
642f2521a76SDoug Ambrisko 	/* Device interface */
643f2521a76SDoug Ambrisko 	DEVMETHOD(device_probe,			vmd_probe),
644f2521a76SDoug Ambrisko 	DEVMETHOD(device_attach,		vmd_attach),
645f2521a76SDoug Ambrisko 	DEVMETHOD(device_detach,		vmd_detach),
646f2521a76SDoug Ambrisko 	DEVMETHOD(device_shutdown,		vmd_shutdown),
647f2521a76SDoug Ambrisko 
648f2521a76SDoug Ambrisko 	/* Bus interface */
649f2521a76SDoug Ambrisko 	DEVMETHOD(bus_read_ivar,		pcib_read_ivar),
650f2521a76SDoug Ambrisko 	DEVMETHOD(bus_write_ivar,		pcib_write_ivar),
651f2521a76SDoug Ambrisko 	DEVMETHOD(bus_alloc_resource,		vmd_alloc_resource),
652f2521a76SDoug Ambrisko 	DEVMETHOD(bus_adjust_resource,		vmd_adjust_resource),
653f2521a76SDoug Ambrisko 	DEVMETHOD(bus_release_resource,		vmd_release_resource),
654f2521a76SDoug Ambrisko 	DEVMETHOD(bus_activate_resource,	bus_generic_activate_resource),
655f2521a76SDoug Ambrisko 	DEVMETHOD(bus_deactivate_resource,	bus_generic_deactivate_resource),
656f2521a76SDoug Ambrisko 	DEVMETHOD(bus_setup_intr,		vmd_setup_intr),
657f2521a76SDoug Ambrisko 	DEVMETHOD(bus_teardown_intr,		vmd_teardown_intr),
658f2521a76SDoug Ambrisko 
659f2521a76SDoug Ambrisko 	/* pci interface */
660f2521a76SDoug Ambrisko 	DEVMETHOD(pci_read_config,		vmd_pci_read_config),
661f2521a76SDoug Ambrisko 	DEVMETHOD(pci_write_config,		vmd_pci_write_config),
662f2521a76SDoug Ambrisko 	DEVMETHOD(pci_alloc_devinfo,		vmd_alloc_devinfo),
663f2521a76SDoug Ambrisko 
664f2521a76SDoug Ambrisko 	/* pcib interface */
665f2521a76SDoug Ambrisko 	DEVMETHOD(pcib_maxslots,		pcib_maxslots),
666f2521a76SDoug Ambrisko 	DEVMETHOD(pcib_read_config,		vmd_read_config),
667f2521a76SDoug Ambrisko 	DEVMETHOD(pcib_write_config,		vmd_write_config),
668f2521a76SDoug Ambrisko 	DEVMETHOD(pcib_route_interrupt,		vmd_pcib_route_interrupt),
669f2521a76SDoug Ambrisko 	DEVMETHOD(pcib_alloc_msi,		vmd_pcib_alloc_msi),
670f2521a76SDoug Ambrisko 	DEVMETHOD(pcib_release_msi,		vmd_pcib_release_msi),
671f2521a76SDoug Ambrisko 	DEVMETHOD(pcib_alloc_msix,		vmd_alloc_msix),
672f2521a76SDoug Ambrisko 	DEVMETHOD(pcib_release_msix,		vmd_pcib_release_msix),
673f2521a76SDoug Ambrisko 	DEVMETHOD(pcib_map_msi,			pcib_map_msi),
674f2521a76SDoug Ambrisko 
675f2521a76SDoug Ambrisko 	DEVMETHOD_END
676f2521a76SDoug Ambrisko };
677f2521a76SDoug Ambrisko 
678f2521a76SDoug Ambrisko static devclass_t vmd_devclass;
679f2521a76SDoug Ambrisko 
680f2521a76SDoug Ambrisko DEFINE_CLASS_0(vmd, vmd_pci_driver, vmd_pci_methods, sizeof(struct vmd_softc));
681f2521a76SDoug Ambrisko DRIVER_MODULE(vmd, pci, vmd_pci_driver, vmd_devclass, NULL, NULL);
682f2521a76SDoug Ambrisko MODULE_PNP_INFO("U16:vendor;U16:device;D:#", pci, vmd,
683f2521a76SDoug Ambrisko     vmd_devs, nitems(vmd_devs) - 1);
684f2521a76SDoug Ambrisko MODULE_DEPEND(vmd, vmd_bus, 1, 1, 1);
685