xref: /freebsd/sys/dev/vmd/vmd.c (revision 160179ea3e24651cd8b15a4fafce519546eac505)
1f2521a76SDoug Ambrisko /*-
2f2521a76SDoug Ambrisko  * SPDX-License-Identifier: BSD-2-Clause
3f2521a76SDoug Ambrisko  *
47af4475aSAlexander Motin  * Copyright (c) 2021 Alexander Motin <mav@FreeBSD.org>
5f2521a76SDoug Ambrisko  * Copyright 2019 Cisco Systems, Inc.
6f2521a76SDoug Ambrisko  * All rights reserved.
7f2521a76SDoug Ambrisko  *
8f2521a76SDoug Ambrisko  * Redistribution and use in source and binary forms, with or without
9f2521a76SDoug Ambrisko  * modification, are permitted provided that the following conditions
10f2521a76SDoug Ambrisko  * are met:
11f2521a76SDoug Ambrisko  * 1. Redistributions of source code must retain the above copyright
12f2521a76SDoug Ambrisko  *    notice, this list of conditions and the following disclaimer.
13f2521a76SDoug Ambrisko  * 2. Redistributions in binary form must reproduce the above copyright
14f2521a76SDoug Ambrisko  *    notice, this list of conditions and the following disclaimer in the
15f2521a76SDoug Ambrisko  *    documentation and/or other materials provided with the distribution.
16f2521a76SDoug Ambrisko  *
17f2521a76SDoug Ambrisko  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18f2521a76SDoug Ambrisko  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19f2521a76SDoug Ambrisko  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20f2521a76SDoug Ambrisko  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21f2521a76SDoug Ambrisko  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22f2521a76SDoug Ambrisko  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23f2521a76SDoug Ambrisko  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24f2521a76SDoug Ambrisko  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25f2521a76SDoug Ambrisko  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26f2521a76SDoug Ambrisko  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27f2521a76SDoug Ambrisko  * SUCH DAMAGE.
28f2521a76SDoug Ambrisko  */
29f2521a76SDoug Ambrisko 
30f2521a76SDoug Ambrisko #include <sys/types.h>
31f2521a76SDoug Ambrisko #include <sys/param.h>
32f2521a76SDoug Ambrisko #include <sys/bus.h>
33f2521a76SDoug Ambrisko #include <sys/conf.h>
34f2521a76SDoug Ambrisko #include <sys/kernel.h>
357af4475aSAlexander Motin #include <sys/limits.h>
36f2521a76SDoug Ambrisko #include <sys/module.h>
377af4475aSAlexander Motin #include <sys/sysctl.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>
437af4475aSAlexander Motin #include <machine/intr_machdep.h>
44f2521a76SDoug Ambrisko #include <sys/rman.h>
45f2521a76SDoug Ambrisko #include <sys/lock.h>
46f2521a76SDoug Ambrisko #include <sys/mutex.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 #include <dev/vmd/vmd.h>
55f2521a76SDoug Ambrisko 
56f2521a76SDoug Ambrisko #include "pcib_if.h"
57f2521a76SDoug Ambrisko 
58f2521a76SDoug Ambrisko struct vmd_type {
59f2521a76SDoug Ambrisko 	u_int16_t	vmd_vid;
60f2521a76SDoug Ambrisko 	u_int16_t	vmd_did;
61f2521a76SDoug Ambrisko 	char		*vmd_name;
620c852bb9SDoug Ambrisko 	int		flags;
630c852bb9SDoug Ambrisko #define BUS_RESTRICT	1
647af4475aSAlexander Motin #define VECTOR_OFFSET	2
65c28220d8SAlexander Motin #define CAN_BYPASS_MSI	4
66f2521a76SDoug Ambrisko };
67f2521a76SDoug Ambrisko 
680c852bb9SDoug Ambrisko #define VMD_CAP		0x40
690c852bb9SDoug Ambrisko #define VMD_BUS_RESTRICT	0x1
700c852bb9SDoug Ambrisko 
710c852bb9SDoug Ambrisko #define VMD_CONFIG	0x44
72c28220d8SAlexander Motin #define VMD_BYPASS_MSI		0x2
730c852bb9SDoug Ambrisko #define VMD_BUS_START(x)	((x >> 8) & 0x3)
740c852bb9SDoug Ambrisko 
750c852bb9SDoug Ambrisko #define VMD_LOCK	0x70
76f2521a76SDoug Ambrisko 
777af4475aSAlexander Motin SYSCTL_NODE(_hw, OID_AUTO, vmd, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
787af4475aSAlexander Motin     "Intel Volume Management Device tuning parameters");
797af4475aSAlexander Motin 
807af4475aSAlexander Motin /*
81c28220d8SAlexander Motin  * By default all VMD devices remap children MSI/MSI-X interrupts into their
82c28220d8SAlexander Motin  * own.  It creates additional isolation, but also complicates things due to
83c28220d8SAlexander Motin  * sharing, etc.  Fortunately some VMD devices can bypass the remapping.
84c28220d8SAlexander Motin  */
85c28220d8SAlexander Motin static int vmd_bypass_msi = 1;
86c28220d8SAlexander Motin SYSCTL_INT(_hw_vmd, OID_AUTO, bypass_msi, CTLFLAG_RWTUN, &vmd_bypass_msi, 0,
87c28220d8SAlexander Motin     "Bypass MSI remapping on capable hardware");
88c28220d8SAlexander Motin 
89c28220d8SAlexander Motin /*
907af4475aSAlexander Motin  * All MSIs within a group share address, so VMD can't distinguish them.
917af4475aSAlexander Motin  * It makes no sense to use more than one per device, only if required by
927af4475aSAlexander Motin  * some specific device drivers.
937af4475aSAlexander Motin  */
947af4475aSAlexander Motin static int vmd_max_msi = 1;
957af4475aSAlexander Motin SYSCTL_INT(_hw_vmd, OID_AUTO, max_msi, CTLFLAG_RWTUN, &vmd_max_msi, 0,
967af4475aSAlexander Motin     "Maximum number of MSI vectors per device");
977af4475aSAlexander Motin 
987af4475aSAlexander Motin /*
997af4475aSAlexander Motin  * MSI-X can use different addresses, but we have limited number of MSI-X
1007af4475aSAlexander Motin  * we can route to, so use conservative default to try to avoid sharing.
1017af4475aSAlexander Motin  */
1027af4475aSAlexander Motin static int vmd_max_msix = 3;
1037af4475aSAlexander Motin SYSCTL_INT(_hw_vmd, OID_AUTO, max_msix, CTLFLAG_RWTUN, &vmd_max_msix, 0,
1047af4475aSAlexander Motin     "Maximum number of MSI-X vectors per device");
1057af4475aSAlexander Motin 
106f2521a76SDoug Ambrisko static struct vmd_type vmd_devs[] = {
1077af4475aSAlexander Motin         { 0x8086, 0x201d, "Intel Volume Management Device", 0 },
108c28220d8SAlexander Motin         { 0x8086, 0x28c0, "Intel Volume Management Device", BUS_RESTRICT | CAN_BYPASS_MSI },
1097af4475aSAlexander Motin         { 0x8086, 0x467f, "Intel Volume Management Device", BUS_RESTRICT | VECTOR_OFFSET },
1107af4475aSAlexander Motin         { 0x8086, 0x4c3d, "Intel Volume Management Device", BUS_RESTRICT | VECTOR_OFFSET },
1119bce1141SAlexander Motin         { 0x8086, 0x7d0b, "Intel Volume Management Device", BUS_RESTRICT | VECTOR_OFFSET },
1127af4475aSAlexander Motin         { 0x8086, 0x9a0b, "Intel Volume Management Device", BUS_RESTRICT | VECTOR_OFFSET },
113b07a48d4SNeel Chauhan         { 0x8086, 0xa77f, "Intel Volume Management Device", BUS_RESTRICT | VECTOR_OFFSET },
1149bce1141SAlexander Motin         { 0x8086, 0xad0b, "Intel Volume Management Device", BUS_RESTRICT | VECTOR_OFFSET },
1150c852bb9SDoug Ambrisko         { 0, 0, NULL, 0 }
116f2521a76SDoug Ambrisko };
117f2521a76SDoug Ambrisko 
118f2521a76SDoug Ambrisko static int
vmd_probe(device_t dev)119f2521a76SDoug Ambrisko vmd_probe(device_t dev)
120f2521a76SDoug Ambrisko {
121f2521a76SDoug Ambrisko 	struct vmd_type *t;
122f2521a76SDoug Ambrisko 	uint16_t vid, did;
123f2521a76SDoug Ambrisko 
124f2521a76SDoug Ambrisko 	vid = pci_get_vendor(dev);
125f2521a76SDoug Ambrisko 	did = pci_get_device(dev);
1267af4475aSAlexander Motin 	for (t = vmd_devs; t->vmd_name != NULL; t++) {
1277af4475aSAlexander Motin 		if (vid == t->vmd_vid && did == t->vmd_did) {
128f2521a76SDoug Ambrisko 			device_set_desc(dev, t->vmd_name);
129f2521a76SDoug Ambrisko 			return (BUS_PROBE_DEFAULT);
130f2521a76SDoug Ambrisko 		}
131f2521a76SDoug Ambrisko 	}
132f2521a76SDoug Ambrisko 	return (ENXIO);
133f2521a76SDoug Ambrisko }
134f2521a76SDoug Ambrisko 
135f2521a76SDoug Ambrisko static void
vmd_free(struct vmd_softc * sc)136f2521a76SDoug Ambrisko vmd_free(struct vmd_softc *sc)
137f2521a76SDoug Ambrisko {
1387af4475aSAlexander Motin 	struct vmd_irq *vi;
1397af4475aSAlexander Motin 	struct vmd_irq_user *u;
140f2521a76SDoug Ambrisko 	int i;
141f2521a76SDoug Ambrisko 
1427af4475aSAlexander Motin 	if (sc->psc.bus.rman.rm_end != 0)
1437af4475aSAlexander Motin 		rman_fini(&sc->psc.bus.rman);
1447af4475aSAlexander Motin 	if (sc->psc.mem.rman.rm_end != 0)
1457af4475aSAlexander Motin 		rman_fini(&sc->psc.mem.rman);
1467af4475aSAlexander Motin 	while ((u = LIST_FIRST(&sc->vmd_users)) != NULL) {
1477af4475aSAlexander Motin 		LIST_REMOVE(u, viu_link);
1487af4475aSAlexander Motin 		free(u, M_DEVBUF);
149f2521a76SDoug Ambrisko 	}
150f2521a76SDoug Ambrisko 	if (sc->vmd_irq != NULL) {
151f2521a76SDoug Ambrisko 		for (i = 0; i < sc->vmd_msix_count; i++) {
1527af4475aSAlexander Motin 			vi = &sc->vmd_irq[i];
1537af4475aSAlexander Motin 			if (vi->vi_res == NULL)
1547af4475aSAlexander Motin 				continue;
1557af4475aSAlexander Motin 			bus_teardown_intr(sc->psc.dev, vi->vi_res,
1567af4475aSAlexander Motin 			    vi->vi_handle);
1577af4475aSAlexander Motin 			bus_release_resource(sc->psc.dev, SYS_RES_IRQ,
1587af4475aSAlexander Motin 			    vi->vi_rid, vi->vi_res);
159f2521a76SDoug Ambrisko 		}
160f2521a76SDoug Ambrisko 	}
161f2521a76SDoug Ambrisko 	free(sc->vmd_irq, M_DEVBUF);
162f2521a76SDoug Ambrisko 	sc->vmd_irq = NULL;
1637af4475aSAlexander Motin 	pci_release_msi(sc->psc.dev);
164f2521a76SDoug Ambrisko 	for (i = 0; i < VMD_MAX_BAR; i++) {
1657af4475aSAlexander Motin 		if (sc->vmd_regs_res[i] != NULL)
1667af4475aSAlexander Motin 			bus_release_resource(sc->psc.dev, SYS_RES_MEMORY,
1677af4475aSAlexander Motin 			    sc->vmd_regs_rid[i], sc->vmd_regs_res[i]);
168f2521a76SDoug Ambrisko 	}
169f2521a76SDoug Ambrisko }
170f2521a76SDoug Ambrisko 
171f2521a76SDoug Ambrisko /* Hidden PCI Roots are hidden in BAR(0). */
172f2521a76SDoug Ambrisko 
173f2521a76SDoug Ambrisko static uint32_t
vmd_read_config(device_t dev,u_int b,u_int s,u_int f,u_int reg,int width)174f2521a76SDoug Ambrisko vmd_read_config(device_t dev, u_int b, u_int s, u_int f, u_int reg, int width)
175f2521a76SDoug Ambrisko {
176f2521a76SDoug Ambrisko 	struct vmd_softc *sc;
177f2521a76SDoug Ambrisko 	bus_addr_t offset;
178f2521a76SDoug Ambrisko 
179f2521a76SDoug Ambrisko 	sc = device_get_softc(dev);
1807af4475aSAlexander Motin 	if (b < sc->vmd_bus_start || b > sc->vmd_bus_end)
1810c852bb9SDoug Ambrisko 		return (0xffffffff);
1820c852bb9SDoug Ambrisko 
1830c852bb9SDoug Ambrisko 	offset = ((b - sc->vmd_bus_start) << 20) + (s << 15) + (f << 12) + reg;
1840c852bb9SDoug Ambrisko 
185f2521a76SDoug Ambrisko 	switch (width) {
186f2521a76SDoug Ambrisko 	case 4:
18776c67868SJohn Baldwin 		return (bus_read_4(sc->vmd_regs_res[0], offset));
188f2521a76SDoug Ambrisko 	case 2:
18976c67868SJohn Baldwin 		return (bus_read_2(sc->vmd_regs_res[0], offset));
190f2521a76SDoug Ambrisko 	case 1:
19176c67868SJohn Baldwin 		return (bus_read_1(sc->vmd_regs_res[0], offset));
192f2521a76SDoug Ambrisko 	default:
1937af4475aSAlexander Motin 		__assert_unreachable();
194f2521a76SDoug Ambrisko 		return (0xffffffff);
195f2521a76SDoug Ambrisko 	}
196f2521a76SDoug Ambrisko }
197f2521a76SDoug Ambrisko 
198f2521a76SDoug Ambrisko static void
vmd_write_config(device_t dev,u_int b,u_int s,u_int f,u_int reg,uint32_t val,int width)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 	struct vmd_softc *sc;
203f2521a76SDoug Ambrisko 	bus_addr_t offset;
204f2521a76SDoug Ambrisko 
205f2521a76SDoug Ambrisko 	sc = device_get_softc(dev);
2067af4475aSAlexander Motin 	if (b < sc->vmd_bus_start || b > sc->vmd_bus_end)
2070c852bb9SDoug Ambrisko 		return;
2080c852bb9SDoug Ambrisko 
2090c852bb9SDoug Ambrisko 	offset = ((b - sc->vmd_bus_start) << 20) + (s << 15) + (f << 12) + reg;
210f2521a76SDoug Ambrisko 
211f2521a76SDoug Ambrisko 	switch (width) {
212f2521a76SDoug Ambrisko 	case 4:
21376c67868SJohn Baldwin 		return (bus_write_4(sc->vmd_regs_res[0], offset, val));
214f2521a76SDoug Ambrisko 	case 2:
21576c67868SJohn Baldwin 		return (bus_write_2(sc->vmd_regs_res[0], offset, val));
216f2521a76SDoug Ambrisko 	case 1:
21776c67868SJohn Baldwin 		return (bus_write_1(sc->vmd_regs_res[0], offset, val));
218f2521a76SDoug Ambrisko 	default:
2197af4475aSAlexander Motin 		__assert_unreachable();
220f2521a76SDoug Ambrisko 	}
221f2521a76SDoug Ambrisko }
222f2521a76SDoug Ambrisko 
223c28220d8SAlexander Motin static void
vmd_set_msi_bypass(device_t dev,bool enable)224c28220d8SAlexander Motin vmd_set_msi_bypass(device_t dev, bool enable)
225c28220d8SAlexander Motin {
226c28220d8SAlexander Motin 	uint16_t val;
227c28220d8SAlexander Motin 
228c28220d8SAlexander Motin 	val = pci_read_config(dev, VMD_CONFIG, 2);
229c28220d8SAlexander Motin 	if (enable)
230c28220d8SAlexander Motin 		val |= VMD_BYPASS_MSI;
231c28220d8SAlexander Motin 	else
232c28220d8SAlexander Motin 		val &= ~VMD_BYPASS_MSI;
233c28220d8SAlexander Motin 	pci_write_config(dev, VMD_CONFIG, val, 2);
234c28220d8SAlexander Motin }
235c28220d8SAlexander Motin 
2367af4475aSAlexander Motin static int
vmd_intr(void * arg)237f2521a76SDoug Ambrisko vmd_intr(void *arg)
238f2521a76SDoug Ambrisko {
2397af4475aSAlexander Motin 	/*
2407af4475aSAlexander Motin 	 * We have nothing to do here, but we have to register some interrupt
2417af4475aSAlexander Motin 	 * handler to make PCI code setup and enable the MSI-X vector.
2427af4475aSAlexander Motin 	 */
2437af4475aSAlexander Motin 	return (FILTER_STRAY);
244f2521a76SDoug Ambrisko }
245f2521a76SDoug Ambrisko 
246f2521a76SDoug Ambrisko static int
vmd_attach(device_t dev)247f2521a76SDoug Ambrisko vmd_attach(device_t dev)
248f2521a76SDoug Ambrisko {
249f2521a76SDoug Ambrisko 	struct vmd_softc *sc;
250f2521a76SDoug Ambrisko 	struct pcib_secbus *bus;
2517af4475aSAlexander Motin 	struct pcib_window *w;
2520c852bb9SDoug Ambrisko 	struct vmd_type *t;
2537af4475aSAlexander Motin 	struct vmd_irq *vi;
2540c852bb9SDoug Ambrisko 	uint16_t vid, did;
255f2521a76SDoug Ambrisko 	uint32_t bar;
256f2521a76SDoug Ambrisko 	int i, j, error;
257f2521a76SDoug Ambrisko 	char buf[64];
258f2521a76SDoug Ambrisko 
259f2521a76SDoug Ambrisko 	sc = device_get_softc(dev);
260f2521a76SDoug Ambrisko 	bzero(sc, sizeof(*sc));
2617af4475aSAlexander Motin 	sc->psc.dev = dev;
2627af4475aSAlexander Motin 	sc->psc.domain = PCI_DOMAINMAX - device_get_unit(dev);
263f2521a76SDoug Ambrisko 
264f2521a76SDoug Ambrisko 	pci_enable_busmaster(dev);
265f2521a76SDoug Ambrisko 
266f2521a76SDoug Ambrisko 	for (i = 0, j = 0; i < VMD_MAX_BAR; i++, j++) {
267f2521a76SDoug Ambrisko 		sc->vmd_regs_rid[i] = PCIR_BAR(j);
268f2521a76SDoug Ambrisko 		bar = pci_read_config(dev, PCIR_BAR(0), 4);
269f2521a76SDoug Ambrisko 		if (PCI_BAR_MEM(bar) && (bar & PCIM_BAR_MEM_TYPE) ==
270f2521a76SDoug Ambrisko 		    PCIM_BAR_MEM_64)
271f2521a76SDoug Ambrisko 			j++;
2727af4475aSAlexander Motin 		if ((sc->vmd_regs_res[i] = bus_alloc_resource_any(dev,
2737af4475aSAlexander Motin 		    SYS_RES_MEMORY, &sc->vmd_regs_rid[i], RF_ACTIVE)) == NULL) {
274f2521a76SDoug Ambrisko 			device_printf(dev, "Cannot allocate resources\n");
275f2521a76SDoug Ambrisko 			goto fail;
276f2521a76SDoug Ambrisko 		}
277f2521a76SDoug Ambrisko 	}
278f2521a76SDoug Ambrisko 
2790c852bb9SDoug Ambrisko 	vid = pci_get_vendor(dev);
2800c852bb9SDoug Ambrisko 	did = pci_get_device(dev);
2817af4475aSAlexander Motin 	for (t = vmd_devs; t->vmd_name != NULL; t++) {
2827af4475aSAlexander Motin 		if (vid == t->vmd_vid && did == t->vmd_did)
2837af4475aSAlexander Motin 			break;
2847af4475aSAlexander Motin 	}
2850c852bb9SDoug Ambrisko 
2860c852bb9SDoug Ambrisko 	sc->vmd_bus_start = 0;
2877af4475aSAlexander Motin 	if ((t->flags & BUS_RESTRICT) &&
2887af4475aSAlexander Motin 	    (pci_read_config(dev, VMD_CAP, 2) & VMD_BUS_RESTRICT)) {
2897af4475aSAlexander Motin 		switch (VMD_BUS_START(pci_read_config(dev, VMD_CONFIG, 2))) {
2907af4475aSAlexander Motin 		case 0:
2917af4475aSAlexander Motin 			sc->vmd_bus_start = 0;
2927af4475aSAlexander Motin 			break;
2930c852bb9SDoug Ambrisko 		case 1:
2940c852bb9SDoug Ambrisko 			sc->vmd_bus_start = 128;
2950c852bb9SDoug Ambrisko 			break;
2960c852bb9SDoug Ambrisko 		case 2:
2970c852bb9SDoug Ambrisko 			sc->vmd_bus_start = 224;
2980c852bb9SDoug Ambrisko 			break;
2997af4475aSAlexander Motin 		default:
3007af4475aSAlexander Motin 			device_printf(dev, "Unknown bus offset\n");
3010c852bb9SDoug Ambrisko 			goto fail;
3020c852bb9SDoug Ambrisko 		}
3030c852bb9SDoug Ambrisko 	}
3047af4475aSAlexander Motin 	sc->vmd_bus_end = MIN(PCI_BUSMAX, sc->vmd_bus_start +
3057af4475aSAlexander Motin 	    (rman_get_size(sc->vmd_regs_res[0]) >> 20) - 1);
3060c852bb9SDoug Ambrisko 
3077af4475aSAlexander Motin 	bus = &sc->psc.bus;
3087af4475aSAlexander Motin 	bus->sec = sc->vmd_bus_start;
3097af4475aSAlexander Motin 	bus->sub = sc->vmd_bus_end;
310f2521a76SDoug Ambrisko 	bus->dev = dev;
3117af4475aSAlexander Motin 	bus->rman.rm_start = 0;
312f2521a76SDoug Ambrisko 	bus->rman.rm_end = PCI_BUSMAX;
313f2521a76SDoug Ambrisko 	bus->rman.rm_type = RMAN_ARRAY;
314f2521a76SDoug Ambrisko 	snprintf(buf, sizeof(buf), "%s bus numbers", device_get_nameunit(dev));
315f2521a76SDoug Ambrisko 	bus->rman.rm_descr = strdup(buf, M_DEVBUF);
316f2521a76SDoug Ambrisko 	error = rman_init(&bus->rman);
317f2521a76SDoug Ambrisko 	if (error) {
3187af4475aSAlexander Motin 		device_printf(dev, "Failed to initialize bus rman\n");
319e65e4e61SMark Johnston 		bus->rman.rm_end = 0;
320f2521a76SDoug Ambrisko 		goto fail;
321f2521a76SDoug Ambrisko 	}
3227af4475aSAlexander Motin 	error = rman_manage_region(&bus->rman, sc->vmd_bus_start,
3237af4475aSAlexander Motin 	    sc->vmd_bus_end);
324f2521a76SDoug Ambrisko 	if (error) {
3257af4475aSAlexander Motin 		device_printf(dev, "Failed to add resource to bus rman\n");
326f2521a76SDoug Ambrisko 		goto fail;
327f2521a76SDoug Ambrisko 	}
3287af4475aSAlexander Motin 
3297af4475aSAlexander Motin 	w = &sc->psc.mem;
3307af4475aSAlexander Motin 	w->rman.rm_type = RMAN_ARRAY;
3317af4475aSAlexander Motin 	snprintf(buf, sizeof(buf), "%s memory window", device_get_nameunit(dev));
3327af4475aSAlexander Motin 	w->rman.rm_descr = strdup(buf, M_DEVBUF);
3337af4475aSAlexander Motin 	error = rman_init(&w->rman);
3347af4475aSAlexander Motin 	if (error) {
3357af4475aSAlexander Motin 		device_printf(dev, "Failed to initialize memory rman\n");
3367af4475aSAlexander Motin 		w->rman.rm_end = 0;
3377af4475aSAlexander Motin 		goto fail;
3387af4475aSAlexander Motin 	}
3397af4475aSAlexander Motin 	error = rman_manage_region(&w->rman,
3407af4475aSAlexander Motin 	    rman_get_start(sc->vmd_regs_res[1]),
3417af4475aSAlexander Motin 	    rman_get_end(sc->vmd_regs_res[1]));
3427af4475aSAlexander Motin 	if (error) {
3437af4475aSAlexander Motin 		device_printf(dev, "Failed to add resource to memory rman\n");
3447af4475aSAlexander Motin 		goto fail;
3457af4475aSAlexander Motin 	}
3467af4475aSAlexander Motin 	error = rman_manage_region(&w->rman,
3477af4475aSAlexander Motin 	    rman_get_start(sc->vmd_regs_res[2]) + 0x2000,
3487af4475aSAlexander Motin 	    rman_get_end(sc->vmd_regs_res[2]));
3497af4475aSAlexander Motin 	if (error) {
3507af4475aSAlexander Motin 		device_printf(dev, "Failed to add resource to memory rman\n");
3517af4475aSAlexander Motin 		goto fail;
352f2521a76SDoug Ambrisko 	}
353f2521a76SDoug Ambrisko 
3547af4475aSAlexander Motin 	LIST_INIT(&sc->vmd_users);
3557af4475aSAlexander Motin 	sc->vmd_fist_vector = (t->flags & VECTOR_OFFSET) ? 1 : 0;
356f2521a76SDoug Ambrisko 	sc->vmd_msix_count = pci_msix_count(dev);
357c28220d8SAlexander Motin 	if (vmd_bypass_msi && (t->flags & CAN_BYPASS_MSI)) {
358c28220d8SAlexander Motin 		sc->vmd_msix_count = 0;
359c28220d8SAlexander Motin 		vmd_set_msi_bypass(dev, true);
360c28220d8SAlexander Motin 	} else if (pci_alloc_msix(dev, &sc->vmd_msix_count) == 0) {
361f2521a76SDoug Ambrisko 		sc->vmd_irq = malloc(sizeof(struct vmd_irq) *
3627af4475aSAlexander Motin 		    sc->vmd_msix_count, M_DEVBUF, M_WAITOK | M_ZERO);
363f2521a76SDoug Ambrisko 		for (i = 0; i < sc->vmd_msix_count; i++) {
3647af4475aSAlexander Motin 			vi = &sc->vmd_irq[i];
3657af4475aSAlexander Motin 			vi->vi_rid = i + 1;
3667af4475aSAlexander Motin 			vi->vi_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
3677af4475aSAlexander Motin 			    &vi->vi_rid, RF_ACTIVE | RF_SHAREABLE);
3687af4475aSAlexander Motin 			if (vi->vi_res == NULL) {
3697af4475aSAlexander Motin 				device_printf(dev, "Failed to allocate irq\n");
370f2521a76SDoug Ambrisko 				goto fail;
371f2521a76SDoug Ambrisko 			}
3727af4475aSAlexander Motin 			vi->vi_irq = rman_get_start(vi->vi_res);
3737af4475aSAlexander Motin 			if (bus_setup_intr(dev, vi->vi_res, INTR_TYPE_MISC |
3747af4475aSAlexander Motin 			    INTR_MPSAFE, vmd_intr, NULL, vi, &vi->vi_handle)) {
3757af4475aSAlexander Motin 				device_printf(dev, "Can't set up interrupt\n");
3767af4475aSAlexander Motin 				bus_release_resource(dev, SYS_RES_IRQ,
3777af4475aSAlexander Motin 				    vi->vi_rid, vi->vi_res);
3787af4475aSAlexander Motin 				vi->vi_res = NULL;
379f2521a76SDoug Ambrisko 				goto fail;
380f2521a76SDoug Ambrisko 			}
381f2521a76SDoug Ambrisko 		}
382c28220d8SAlexander Motin 		vmd_set_msi_bypass(dev, false);
383f2521a76SDoug Ambrisko 	}
384f2521a76SDoug Ambrisko 
3857af4475aSAlexander Motin 	sc->vmd_dma_tag = bus_get_dma_tag(dev);
3865b56413dSWarner Losh 	sc->psc.child = device_add_child(dev, "pci", DEVICE_UNIT_ANY);
387*18250ec6SJohn Baldwin 	bus_attach_children(dev);
388*18250ec6SJohn Baldwin 	return (0);
389f2521a76SDoug Ambrisko 
390f2521a76SDoug Ambrisko fail:
391f2521a76SDoug Ambrisko 	vmd_free(sc);
392f2521a76SDoug Ambrisko 	return (ENXIO);
393f2521a76SDoug Ambrisko }
394f2521a76SDoug Ambrisko 
395f2521a76SDoug Ambrisko static int
vmd_detach(device_t dev)396f2521a76SDoug Ambrisko vmd_detach(device_t dev)
397f2521a76SDoug Ambrisko {
3987af4475aSAlexander Motin 	struct vmd_softc *sc = device_get_softc(dev);
3997af4475aSAlexander Motin 	int error;
400f2521a76SDoug Ambrisko 
4017af4475aSAlexander Motin 	error = bus_generic_detach(dev);
4027af4475aSAlexander Motin 	if (error)
4037af4475aSAlexander Motin 		return (error);
404c28220d8SAlexander Motin 	if (sc->vmd_msix_count == 0)
405c28220d8SAlexander Motin 		vmd_set_msi_bypass(dev, false);
406f2521a76SDoug Ambrisko 	vmd_free(sc);
407f2521a76SDoug Ambrisko 	return (0);
408f2521a76SDoug Ambrisko }
409f2521a76SDoug Ambrisko 
4107af4475aSAlexander Motin static bus_dma_tag_t
vmd_get_dma_tag(device_t dev,device_t child)4117af4475aSAlexander Motin vmd_get_dma_tag(device_t dev, device_t child)
412f2521a76SDoug Ambrisko {
4137af4475aSAlexander Motin 	struct vmd_softc *sc = device_get_softc(dev);
414f2521a76SDoug Ambrisko 
4157af4475aSAlexander Motin 	return (sc->vmd_dma_tag);
416f2521a76SDoug Ambrisko }
417f2521a76SDoug Ambrisko 
418d714e73fSJohn Baldwin static struct rman *
vmd_get_rman(device_t dev,int type,u_int flags)419d714e73fSJohn Baldwin vmd_get_rman(device_t dev, int type, u_int flags)
420d714e73fSJohn Baldwin {
421d714e73fSJohn Baldwin 	struct vmd_softc *sc = device_get_softc(dev);
422d714e73fSJohn Baldwin 
423d714e73fSJohn Baldwin 	switch (type) {
424d714e73fSJohn Baldwin 	case SYS_RES_MEMORY:
425d714e73fSJohn Baldwin 		return (&sc->psc.mem.rman);
426d714e73fSJohn Baldwin 	case PCI_RES_BUS:
427d714e73fSJohn Baldwin 		return (&sc->psc.bus.rman);
428d714e73fSJohn Baldwin 	default:
429d714e73fSJohn Baldwin 		/* VMD hardware does not support I/O ports. */
430d714e73fSJohn Baldwin 		return (NULL);
431d714e73fSJohn Baldwin 	}
432d714e73fSJohn Baldwin }
433d714e73fSJohn Baldwin 
434f2521a76SDoug Ambrisko static struct resource *
vmd_alloc_resource(device_t dev,device_t child,int type,int * rid,rman_res_t start,rman_res_t end,rman_res_t count,u_int flags)435f2521a76SDoug Ambrisko vmd_alloc_resource(device_t dev, device_t child, int type, int *rid,
436f2521a76SDoug Ambrisko     rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
437f2521a76SDoug Ambrisko {
4387af4475aSAlexander Motin 	struct resource *res;
439f2521a76SDoug Ambrisko 
440d714e73fSJohn Baldwin 	if (type == SYS_RES_IRQ) {
441b2c48aa4SGordon Bergling 		/* VMD hardware does not support legacy interrupts. */
4427af4475aSAlexander Motin 		if (*rid == 0)
4437af4475aSAlexander Motin 			return (NULL);
4447af4475aSAlexander Motin 		return (bus_generic_alloc_resource(dev, child, type, rid,
4457af4475aSAlexander Motin 		    start, end, count, flags | RF_SHAREABLE));
446d714e73fSJohn Baldwin 	}
447d714e73fSJohn Baldwin 	res = bus_generic_rman_alloc_resource(dev, child, type, rid, start,
448d714e73fSJohn Baldwin 	    end, count, flags);
449d714e73fSJohn Baldwin 	if (bootverbose && res != NULL) {
450d714e73fSJohn Baldwin 		switch (type) {
4517af4475aSAlexander Motin 		case SYS_RES_MEMORY:
4527af4475aSAlexander Motin 			device_printf(dev,
4537af4475aSAlexander Motin 			    "allocated memory range (%#jx-%#jx) for rid %d of %s\n",
4547af4475aSAlexander Motin 			    rman_get_start(res), rman_get_end(res), *rid,
4557af4475aSAlexander Motin 			    pcib_child_name(child));
4567af4475aSAlexander Motin 			break;
4577af4475aSAlexander Motin 		case PCI_RES_BUS:
4587af4475aSAlexander Motin 			device_printf(dev,
4597af4475aSAlexander Motin 			    "allocated bus range (%ju-%ju) for rid %d of %s\n",
4607af4475aSAlexander Motin 			    rman_get_start(res), rman_get_end(res), *rid,
4617af4475aSAlexander Motin 			    pcib_child_name(child));
4627af4475aSAlexander Motin 			break;
4637af4475aSAlexander Motin 		}
464d714e73fSJohn Baldwin 	}
4657af4475aSAlexander Motin 	return (res);
466f2521a76SDoug Ambrisko }
467f2521a76SDoug Ambrisko 
468f2521a76SDoug Ambrisko static int
vmd_adjust_resource(device_t dev,device_t child,struct resource * r,rman_res_t start,rman_res_t end)469fef01f04SJohn Baldwin vmd_adjust_resource(device_t dev, device_t child,
470f2521a76SDoug Ambrisko     struct resource *r, rman_res_t start, rman_res_t end)
471f2521a76SDoug Ambrisko {
472f2521a76SDoug Ambrisko 
473fef01f04SJohn Baldwin 	if (rman_get_type(r) == SYS_RES_IRQ) {
474fef01f04SJohn Baldwin 		return (bus_generic_adjust_resource(dev, child, r, start, end));
4757af4475aSAlexander Motin 	}
476fef01f04SJohn Baldwin 	return (bus_generic_rman_adjust_resource(dev, child, r, start, end));
477f2521a76SDoug Ambrisko }
478f2521a76SDoug Ambrisko 
479f2521a76SDoug Ambrisko static int
vmd_release_resource(device_t dev,device_t child,struct resource * r)4809dbf5b0eSJohn Baldwin vmd_release_resource(device_t dev, device_t child, struct resource *r)
481f2521a76SDoug Ambrisko {
4827af4475aSAlexander Motin 
4839dbf5b0eSJohn Baldwin 	if (rman_get_type(r) == SYS_RES_IRQ) {
4849dbf5b0eSJohn Baldwin 		return (bus_generic_release_resource(dev, child, r));
4857af4475aSAlexander Motin 	}
4869dbf5b0eSJohn Baldwin 	return (bus_generic_rman_release_resource(dev, child, r));
487d714e73fSJohn Baldwin }
488d714e73fSJohn Baldwin 
489d714e73fSJohn Baldwin static int
vmd_activate_resource(device_t dev,device_t child,struct resource * r)4902baed46eSJohn Baldwin vmd_activate_resource(device_t dev, device_t child, struct resource *r)
491d714e73fSJohn Baldwin {
4922baed46eSJohn Baldwin 	if (rman_get_type(r) == SYS_RES_IRQ) {
4932baed46eSJohn Baldwin 		return (bus_generic_activate_resource(dev, child, r));
494d714e73fSJohn Baldwin 	}
4952baed46eSJohn Baldwin 	return (bus_generic_rman_activate_resource(dev, child, r));
496d714e73fSJohn Baldwin }
497d714e73fSJohn Baldwin 
498d714e73fSJohn Baldwin static int
vmd_deactivate_resource(device_t dev,device_t child,struct resource * r)4992baed46eSJohn Baldwin vmd_deactivate_resource(device_t dev, device_t child, struct resource *r)
500d714e73fSJohn Baldwin {
5012baed46eSJohn Baldwin 	if (rman_get_type(r) == SYS_RES_IRQ) {
5022baed46eSJohn Baldwin 		return (bus_generic_deactivate_resource(dev, child, r));
503d714e73fSJohn Baldwin 	}
5042baed46eSJohn Baldwin 	return (bus_generic_rman_deactivate_resource(dev, child, r));
505d714e73fSJohn Baldwin }
506d714e73fSJohn Baldwin 
507d714e73fSJohn Baldwin static struct resource *
vmd_find_parent_resource(struct vmd_softc * sc,struct resource * r)508d714e73fSJohn Baldwin vmd_find_parent_resource(struct vmd_softc *sc, struct resource *r)
509d714e73fSJohn Baldwin {
510d714e73fSJohn Baldwin 	for (int i = 1; i < 3; i++) {
511d714e73fSJohn Baldwin 		if (rman_get_start(sc->vmd_regs_res[i]) <= rman_get_start(r) &&
512d714e73fSJohn Baldwin 		    rman_get_end(sc->vmd_regs_res[i]) >= rman_get_end(r))
513d714e73fSJohn Baldwin 			return (sc->vmd_regs_res[i]);
514d714e73fSJohn Baldwin 	}
515d714e73fSJohn Baldwin 	return (NULL);
516d714e73fSJohn Baldwin }
517d714e73fSJohn Baldwin 
518d714e73fSJohn Baldwin static int
vmd_map_resource(device_t dev,device_t child,struct resource * r,struct resource_map_request * argsp,struct resource_map * map)519d77f2092SJohn Baldwin vmd_map_resource(device_t dev, device_t child, struct resource *r,
520d714e73fSJohn Baldwin     struct resource_map_request *argsp, struct resource_map *map)
521d714e73fSJohn Baldwin {
522d714e73fSJohn Baldwin 	struct vmd_softc *sc = device_get_softc(dev);
523d714e73fSJohn Baldwin 	struct resource_map_request args;
524d714e73fSJohn Baldwin 	struct resource *pres;
525d714e73fSJohn Baldwin 	rman_res_t length, start;
526d714e73fSJohn Baldwin 	int error;
527d714e73fSJohn Baldwin 
528d714e73fSJohn Baldwin 	/* Resources must be active to be mapped. */
529d714e73fSJohn Baldwin 	if (!(rman_get_flags(r) & RF_ACTIVE))
530d714e73fSJohn Baldwin 		return (ENXIO);
531d714e73fSJohn Baldwin 
532d714e73fSJohn Baldwin 	resource_init_map_request(&args);
533d714e73fSJohn Baldwin 	error = resource_validate_map_request(r, argsp, &args, &start, &length);
534d714e73fSJohn Baldwin 	if (error)
535d714e73fSJohn Baldwin 		return (error);
536d714e73fSJohn Baldwin 
537d714e73fSJohn Baldwin 	pres = vmd_find_parent_resource(sc, r);
538d714e73fSJohn Baldwin 	if (pres == NULL)
539d714e73fSJohn Baldwin 		return (ENOENT);
540d714e73fSJohn Baldwin 
541d714e73fSJohn Baldwin 	args.offset = start - rman_get_start(pres);
542d714e73fSJohn Baldwin 	args.length = length;
54398056127SJohn Baldwin 	return (bus_map_resource(dev, pres, &args, map));
544d714e73fSJohn Baldwin }
545d714e73fSJohn Baldwin 
546d714e73fSJohn Baldwin static int
vmd_unmap_resource(device_t dev,device_t child,struct resource * r,struct resource_map * map)547d77f2092SJohn Baldwin vmd_unmap_resource(device_t dev, device_t child, struct resource *r,
548d714e73fSJohn Baldwin     struct resource_map *map)
549d714e73fSJohn Baldwin {
550d714e73fSJohn Baldwin 	struct vmd_softc *sc = device_get_softc(dev);
55198056127SJohn Baldwin 	struct resource *pres;
552d714e73fSJohn Baldwin 
55398056127SJohn Baldwin 	pres = vmd_find_parent_resource(sc, r);
55498056127SJohn Baldwin 	if (pres == NULL)
555d714e73fSJohn Baldwin 		return (ENOENT);
55698056127SJohn Baldwin 	return (bus_unmap_resource(dev, pres, map));
557f2521a76SDoug Ambrisko }
558f2521a76SDoug Ambrisko 
559f2521a76SDoug Ambrisko static int
vmd_route_interrupt(device_t dev,device_t child,int pin)5607af4475aSAlexander Motin vmd_route_interrupt(device_t dev, device_t child, int pin)
561f2521a76SDoug Ambrisko {
5627af4475aSAlexander Motin 
563b2c48aa4SGordon Bergling 	/* VMD hardware does not support legacy interrupts. */
5647af4475aSAlexander Motin 	return (PCI_INVALID_IRQ);
5657af4475aSAlexander Motin }
5667af4475aSAlexander Motin 
5677af4475aSAlexander Motin static int
vmd_alloc_msi(device_t dev,device_t child,int count,int maxcount,int * irqs)5687af4475aSAlexander Motin vmd_alloc_msi(device_t dev, device_t child, int count, int maxcount,
5697af4475aSAlexander Motin     int *irqs)
5707af4475aSAlexander Motin {
5717af4475aSAlexander Motin 	struct vmd_softc *sc = device_get_softc(dev);
5727af4475aSAlexander Motin 	struct vmd_irq_user *u;
5737af4475aSAlexander Motin 	int i, ibest = 0, best = INT_MAX;
5747af4475aSAlexander Motin 
575c28220d8SAlexander Motin 	if (sc->vmd_msix_count == 0) {
576c28220d8SAlexander Motin 		return (PCIB_ALLOC_MSI(device_get_parent(device_get_parent(dev)),
577c28220d8SAlexander Motin 		    child, count, maxcount, irqs));
578c28220d8SAlexander Motin 	}
579c28220d8SAlexander Motin 
5807af4475aSAlexander Motin 	if (count > vmd_max_msi)
5817af4475aSAlexander Motin 		return (ENOSPC);
5827af4475aSAlexander Motin 	LIST_FOREACH(u, &sc->vmd_users, viu_link) {
5837af4475aSAlexander Motin 		if (u->viu_child == child)
5847af4475aSAlexander Motin 			return (EBUSY);
5857af4475aSAlexander Motin 	}
5867af4475aSAlexander Motin 
5877af4475aSAlexander Motin 	for (i = sc->vmd_fist_vector; i < sc->vmd_msix_count; i++) {
5887af4475aSAlexander Motin 		if (best > sc->vmd_irq[i].vi_nusers) {
5897af4475aSAlexander Motin 			best = sc->vmd_irq[i].vi_nusers;
5907af4475aSAlexander Motin 			ibest = i;
5917af4475aSAlexander Motin 		}
5927af4475aSAlexander Motin 	}
5937af4475aSAlexander Motin 
5947af4475aSAlexander Motin 	u = malloc(sizeof(*u), M_DEVBUF, M_WAITOK | M_ZERO);
5957af4475aSAlexander Motin 	u->viu_child = child;
5967af4475aSAlexander Motin 	u->viu_vector = ibest;
5977af4475aSAlexander Motin 	LIST_INSERT_HEAD(&sc->vmd_users, u, viu_link);
5987af4475aSAlexander Motin 	sc->vmd_irq[ibest].vi_nusers += count;
5997af4475aSAlexander Motin 
6007af4475aSAlexander Motin 	for (i = 0; i < count; i++)
6017af4475aSAlexander Motin 		irqs[i] = sc->vmd_irq[ibest].vi_irq;
602f2521a76SDoug Ambrisko 	return (0);
603f2521a76SDoug Ambrisko }
604f2521a76SDoug Ambrisko 
605f2521a76SDoug Ambrisko static int
vmd_release_msi(device_t dev,device_t child,int count,int * irqs)6067af4475aSAlexander Motin vmd_release_msi(device_t dev, device_t child, int count, int *irqs)
607f2521a76SDoug Ambrisko {
6087af4475aSAlexander Motin 	struct vmd_softc *sc = device_get_softc(dev);
6097af4475aSAlexander Motin 	struct vmd_irq_user *u;
6107af4475aSAlexander Motin 
611c28220d8SAlexander Motin 	if (sc->vmd_msix_count == 0) {
612c28220d8SAlexander Motin 		return (PCIB_RELEASE_MSI(device_get_parent(device_get_parent(dev)),
613c28220d8SAlexander Motin 		    child, count, irqs));
614c28220d8SAlexander Motin 	}
615c28220d8SAlexander Motin 
6167af4475aSAlexander Motin 	LIST_FOREACH(u, &sc->vmd_users, viu_link) {
6177af4475aSAlexander Motin 		if (u->viu_child == child) {
6187af4475aSAlexander Motin 			sc->vmd_irq[u->viu_vector].vi_nusers -= count;
6197af4475aSAlexander Motin 			LIST_REMOVE(u, viu_link);
6207af4475aSAlexander Motin 			free(u, M_DEVBUF);
6217af4475aSAlexander Motin 			return (0);
6227af4475aSAlexander Motin 		}
6237af4475aSAlexander Motin 	}
6247af4475aSAlexander Motin 	return (EINVAL);
625f2521a76SDoug Ambrisko }
626f2521a76SDoug Ambrisko 
627f2521a76SDoug Ambrisko static int
vmd_alloc_msix(device_t dev,device_t child,int * irq)6287af4475aSAlexander Motin vmd_alloc_msix(device_t dev, device_t child, int *irq)
629f2521a76SDoug Ambrisko {
6307af4475aSAlexander Motin 	struct vmd_softc *sc = device_get_softc(dev);
6317af4475aSAlexander Motin 	struct vmd_irq_user *u;
6327af4475aSAlexander Motin 	int i, ibest = 0, best = INT_MAX;
633f2521a76SDoug Ambrisko 
634c28220d8SAlexander Motin 	if (sc->vmd_msix_count == 0) {
635c28220d8SAlexander Motin 		return (PCIB_ALLOC_MSIX(device_get_parent(device_get_parent(dev)),
636c28220d8SAlexander Motin 		    child, irq));
637c28220d8SAlexander Motin 	}
638c28220d8SAlexander Motin 
639f2521a76SDoug Ambrisko 	i = 0;
6407af4475aSAlexander Motin 	LIST_FOREACH(u, &sc->vmd_users, viu_link) {
6417af4475aSAlexander Motin 		if (u->viu_child == child)
6427af4475aSAlexander Motin 			i++;
6437af4475aSAlexander Motin 	}
6447af4475aSAlexander Motin 	if (i >= vmd_max_msix)
6457af4475aSAlexander Motin 		return (ENOSPC);
646f2521a76SDoug Ambrisko 
6477af4475aSAlexander Motin 	for (i = sc->vmd_fist_vector; i < sc->vmd_msix_count; i++) {
6487af4475aSAlexander Motin 		if (best > sc->vmd_irq[i].vi_nusers) {
6497af4475aSAlexander Motin 			best = sc->vmd_irq[i].vi_nusers;
6507af4475aSAlexander Motin 			ibest = i;
6517af4475aSAlexander Motin 		}
6527af4475aSAlexander Motin 	}
6537af4475aSAlexander Motin 
6547af4475aSAlexander Motin 	u = malloc(sizeof(*u), M_DEVBUF, M_WAITOK | M_ZERO);
6557af4475aSAlexander Motin 	u->viu_child = child;
6567af4475aSAlexander Motin 	u->viu_vector = ibest;
6577af4475aSAlexander Motin 	LIST_INSERT_HEAD(&sc->vmd_users, u, viu_link);
6587af4475aSAlexander Motin 	sc->vmd_irq[ibest].vi_nusers++;
6597af4475aSAlexander Motin 
6607af4475aSAlexander Motin 	*irq = sc->vmd_irq[ibest].vi_irq;
6617af4475aSAlexander Motin 	return (0);
662f2521a76SDoug Ambrisko }
663f2521a76SDoug Ambrisko 
664f2521a76SDoug Ambrisko static int
vmd_release_msix(device_t dev,device_t child,int irq)6657af4475aSAlexander Motin vmd_release_msix(device_t dev, device_t child, int irq)
666f2521a76SDoug Ambrisko {
6677af4475aSAlexander Motin 	struct vmd_softc *sc = device_get_softc(dev);
6687af4475aSAlexander Motin 	struct vmd_irq_user *u;
669f2521a76SDoug Ambrisko 
670c28220d8SAlexander Motin 	if (sc->vmd_msix_count == 0) {
671c28220d8SAlexander Motin 		return (PCIB_RELEASE_MSIX(device_get_parent(device_get_parent(dev)),
672c28220d8SAlexander Motin 		    child, irq));
673c28220d8SAlexander Motin 	}
674c28220d8SAlexander Motin 
6757af4475aSAlexander Motin 	LIST_FOREACH(u, &sc->vmd_users, viu_link) {
6767af4475aSAlexander Motin 		if (u->viu_child == child &&
6777af4475aSAlexander Motin 		    sc->vmd_irq[u->viu_vector].vi_irq == irq) {
6787af4475aSAlexander Motin 			sc->vmd_irq[u->viu_vector].vi_nusers--;
6797af4475aSAlexander Motin 			LIST_REMOVE(u, viu_link);
6807af4475aSAlexander Motin 			free(u, M_DEVBUF);
6817af4475aSAlexander Motin 			return (0);
682f2521a76SDoug Ambrisko 		}
683f2521a76SDoug Ambrisko 	}
6847af4475aSAlexander Motin 	return (EINVAL);
6857af4475aSAlexander Motin }
686f2521a76SDoug Ambrisko 
6877af4475aSAlexander Motin static int
vmd_map_msi(device_t dev,device_t child,int irq,uint64_t * addr,uint32_t * data)6887af4475aSAlexander Motin vmd_map_msi(device_t dev, device_t child, int irq, uint64_t *addr, uint32_t *data)
6897af4475aSAlexander Motin {
6907af4475aSAlexander Motin 	struct vmd_softc *sc = device_get_softc(dev);
6917af4475aSAlexander Motin 	int i;
6927af4475aSAlexander Motin 
693c28220d8SAlexander Motin 	if (sc->vmd_msix_count == 0) {
694c28220d8SAlexander Motin 		return (PCIB_MAP_MSI(device_get_parent(device_get_parent(dev)),
695c28220d8SAlexander Motin 		    child, irq, addr, data));
696c28220d8SAlexander Motin 	}
697c28220d8SAlexander Motin 
6987af4475aSAlexander Motin 	for (i = sc->vmd_fist_vector; i < sc->vmd_msix_count; i++) {
6997af4475aSAlexander Motin 		if (sc->vmd_irq[i].vi_irq == irq)
7007af4475aSAlexander Motin 			break;
7017af4475aSAlexander Motin 	}
7027af4475aSAlexander Motin 	if (i >= sc->vmd_msix_count)
7037af4475aSAlexander Motin 		return (EINVAL);
7047af4475aSAlexander Motin 	*addr = MSI_INTEL_ADDR_BASE | (i << 12);
7057af4475aSAlexander Motin 	*data = 0;
7067af4475aSAlexander Motin 	return (0);
707f2521a76SDoug Ambrisko }
708f2521a76SDoug Ambrisko 
709f2521a76SDoug Ambrisko static device_method_t vmd_pci_methods[] = {
710f2521a76SDoug Ambrisko 	/* Device interface */
711f2521a76SDoug Ambrisko 	DEVMETHOD(device_probe,			vmd_probe),
712f2521a76SDoug Ambrisko 	DEVMETHOD(device_attach,		vmd_attach),
713f2521a76SDoug Ambrisko 	DEVMETHOD(device_detach,		vmd_detach),
7147af4475aSAlexander Motin 	DEVMETHOD(device_suspend,		bus_generic_suspend),
7157af4475aSAlexander Motin 	DEVMETHOD(device_resume,		bus_generic_resume),
7167af4475aSAlexander Motin 	DEVMETHOD(device_shutdown,		bus_generic_shutdown),
717f2521a76SDoug Ambrisko 
718f2521a76SDoug Ambrisko 	/* Bus interface */
7197af4475aSAlexander Motin 	DEVMETHOD(bus_get_dma_tag,		vmd_get_dma_tag),
720d714e73fSJohn Baldwin 	DEVMETHOD(bus_get_rman,			vmd_get_rman),
721f2521a76SDoug Ambrisko 	DEVMETHOD(bus_read_ivar,		pcib_read_ivar),
722f2521a76SDoug Ambrisko 	DEVMETHOD(bus_write_ivar,		pcib_write_ivar),
723f2521a76SDoug Ambrisko 	DEVMETHOD(bus_alloc_resource,		vmd_alloc_resource),
724f2521a76SDoug Ambrisko 	DEVMETHOD(bus_adjust_resource,		vmd_adjust_resource),
725f2521a76SDoug Ambrisko 	DEVMETHOD(bus_release_resource,		vmd_release_resource),
726d714e73fSJohn Baldwin 	DEVMETHOD(bus_activate_resource,	vmd_activate_resource),
727d714e73fSJohn Baldwin 	DEVMETHOD(bus_deactivate_resource,	vmd_deactivate_resource),
728d714e73fSJohn Baldwin 	DEVMETHOD(bus_map_resource,		vmd_map_resource),
729d714e73fSJohn Baldwin 	DEVMETHOD(bus_unmap_resource,		vmd_unmap_resource),
7307af4475aSAlexander Motin 	DEVMETHOD(bus_setup_intr,		bus_generic_setup_intr),
7317af4475aSAlexander Motin 	DEVMETHOD(bus_teardown_intr,		bus_generic_teardown_intr),
732f2521a76SDoug Ambrisko 
733f2521a76SDoug Ambrisko 	/* pcib interface */
734f2521a76SDoug Ambrisko 	DEVMETHOD(pcib_maxslots,		pcib_maxslots),
735f2521a76SDoug Ambrisko 	DEVMETHOD(pcib_read_config,		vmd_read_config),
736f2521a76SDoug Ambrisko 	DEVMETHOD(pcib_write_config,		vmd_write_config),
7377af4475aSAlexander Motin 	DEVMETHOD(pcib_route_interrupt,		vmd_route_interrupt),
7387af4475aSAlexander Motin 	DEVMETHOD(pcib_alloc_msi,		vmd_alloc_msi),
7397af4475aSAlexander Motin 	DEVMETHOD(pcib_release_msi,		vmd_release_msi),
740f2521a76SDoug Ambrisko 	DEVMETHOD(pcib_alloc_msix,		vmd_alloc_msix),
7417af4475aSAlexander Motin 	DEVMETHOD(pcib_release_msix,		vmd_release_msix),
7427af4475aSAlexander Motin 	DEVMETHOD(pcib_map_msi,			vmd_map_msi),
7437af4475aSAlexander Motin 	DEVMETHOD(pcib_request_feature,		pcib_request_feature_allow),
744f2521a76SDoug Ambrisko 
745f2521a76SDoug Ambrisko 	DEVMETHOD_END
746f2521a76SDoug Ambrisko };
747f2521a76SDoug Ambrisko 
7487af4475aSAlexander Motin DEFINE_CLASS_0(pcib, vmd_pci_driver, vmd_pci_methods, sizeof(struct vmd_softc));
74944031900SJohn Baldwin DRIVER_MODULE(vmd, pci, vmd_pci_driver, NULL, NULL);
750f2521a76SDoug Ambrisko MODULE_PNP_INFO("U16:vendor;U16:device;D:#", pci, vmd,
751f2521a76SDoug Ambrisko     vmd_devs, nitems(vmd_devs) - 1);
752