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