1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2021 The FreeBSD Foundation 5 * 6 * Portions of this software were developed by Ka Ho Ng 7 * under sponsorship from the FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice unmodified, this list of conditions, and the following 14 * disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include <sys/cdefs.h> 32 #include <sys/param.h> 33 #include <sys/bus.h> 34 #include <sys/kernel.h> 35 #include <sys/module.h> 36 #include <sys/rman.h> 37 38 #include <dev/pci/pcireg.h> 39 #include <dev/pci/pcivar.h> 40 41 #include "amdvi_priv.h" 42 #include "ivhd_if.h" 43 44 struct amdiommu_softc { 45 struct resource *event_res; /* Event interrupt resource. */ 46 void *event_tag; /* Event interrupt tag. */ 47 int event_rid; 48 }; 49 50 static int amdiommu_probe(device_t); 51 static int amdiommu_attach(device_t); 52 static int amdiommu_detach(device_t); 53 static int ivhd_setup_intr(device_t, driver_intr_t, void *, 54 const char *); 55 static int ivhd_teardown_intr(device_t); 56 57 static device_method_t amdiommu_methods[] = { 58 /* device interface */ 59 DEVMETHOD(device_probe, amdiommu_probe), 60 DEVMETHOD(device_attach, amdiommu_attach), 61 DEVMETHOD(device_detach, amdiommu_detach), 62 DEVMETHOD(ivhd_setup_intr, ivhd_setup_intr), 63 DEVMETHOD(ivhd_teardown_intr, ivhd_teardown_intr), 64 DEVMETHOD_END 65 }; 66 static driver_t amdiommu_driver = { 67 "amdiommu", 68 amdiommu_methods, 69 sizeof(struct amdiommu_softc), 70 }; 71 72 static int 73 amdiommu_probe(device_t dev) 74 { 75 int error; 76 int capoff; 77 78 /* 79 * Check base class and sub-class 80 */ 81 if (pci_get_class(dev) != PCIC_BASEPERIPH || 82 pci_get_subclass(dev) != PCIS_BASEPERIPH_IOMMU) 83 return (ENXIO); 84 85 /* 86 * A IOMMU capability block carries a 0Fh capid. 87 */ 88 error = pci_find_cap(dev, PCIY_SECDEV, &capoff); 89 if (error) 90 return (ENXIO); 91 92 /* 93 * bit [18:16] == 011b indicates the capability block is IOMMU 94 * capability block. If the field is not set to 011b, bail out. 95 */ 96 if ((pci_read_config(dev, capoff + 2, 2) & 0x7) != 0x3) 97 return (ENXIO); 98 99 return (BUS_PROBE_SPECIFIC); 100 } 101 102 static int 103 amdiommu_attach(device_t dev) 104 { 105 106 device_set_desc(dev, "AMD-Vi/IOMMU PCI function"); 107 return (0); 108 } 109 110 static int 111 amdiommu_detach(device_t dev) 112 { 113 114 return (0); 115 } 116 117 static int 118 ivhd_setup_intr(device_t dev, driver_intr_t handler, void *arg, 119 const char *desc) 120 { 121 struct amdiommu_softc *sc; 122 int error, msicnt; 123 124 sc = device_get_softc(dev); 125 msicnt = 1; 126 if (sc->event_res != NULL) 127 panic("%s is called without intr teardown", __func__); 128 sc->event_rid = 1; 129 130 error = pci_alloc_msi(dev, &msicnt); 131 if (error) { 132 device_printf(dev, "Couldn't find event MSI IRQ resource.\n"); 133 return (ENOENT); 134 } 135 136 sc->event_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, 137 &sc->event_rid, RF_ACTIVE); 138 if (sc->event_res == NULL) { 139 device_printf(dev, "Unable to allocate event INTR resource.\n"); 140 error = ENOMEM; 141 goto fail; 142 } 143 144 error = bus_setup_intr(dev, sc->event_res, INTR_TYPE_MISC | INTR_MPSAFE, 145 NULL, handler, arg, &sc->event_tag); 146 if (error) { 147 device_printf(dev, "Fail to setup event intr\n"); 148 goto fail; 149 } 150 151 bus_describe_intr(dev, sc->event_res, sc->event_tag, "%s", desc); 152 return (0); 153 154 fail: 155 ivhd_teardown_intr(dev); 156 return (error); 157 } 158 159 static int 160 ivhd_teardown_intr(device_t dev) 161 { 162 struct amdiommu_softc *sc; 163 164 sc = device_get_softc(dev); 165 166 if (sc->event_tag != NULL) { 167 bus_teardown_intr(dev, sc->event_res, sc->event_tag); 168 sc->event_tag = NULL; 169 } 170 if (sc->event_res != NULL) { 171 bus_release_resource(dev, SYS_RES_IRQ, sc->event_rid, 172 sc->event_res); 173 sc->event_res = NULL; 174 } 175 pci_release_msi(dev); 176 return (0); 177 } 178 179 /* This driver has to be loaded before ivhd */ 180 DRIVER_MODULE(amdiommu, pci, amdiommu_driver, 0, 0); 181 MODULE_DEPEND(amdiommu, pci, 1, 1, 1); 182