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