1 /* $NetBSD: acpipchb.c,v 1.17 2020/01/21 11:24:47 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2018 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jared McNeill <jmcneill@invisible.ca>. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following 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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: acpipchb.c,v 1.17 2020/01/21 11:24:47 jmcneill Exp $"); 34 35 #include <sys/param.h> 36 #include <sys/bus.h> 37 #include <sys/device.h> 38 #include <sys/intr.h> 39 #include <sys/systm.h> 40 #include <sys/kernel.h> 41 #include <sys/extent.h> 42 #include <sys/queue.h> 43 #include <sys/mutex.h> 44 #include <sys/kmem.h> 45 46 #include <machine/cpu.h> 47 48 #include <arm/cpufunc.h> 49 50 #include <dev/pci/pcireg.h> 51 #include <dev/pci/pcivar.h> 52 #include <dev/pci/pciconf.h> 53 54 #include <dev/acpi/acpivar.h> 55 #include <dev/acpi/acpi_pci.h> 56 #include <dev/acpi/acpi_mcfg.h> 57 58 #include <arm/acpi/acpi_pci_machdep.h> 59 60 #define PCIHOST_CACHELINE_SIZE arm_dcache_align 61 62 #define ACPIPCHB_MAX_RANGES 64 /* XXX arbitrary limit */ 63 64 struct acpipchb_bus_range { 65 bus_addr_t min; 66 bus_addr_t max; 67 bus_addr_t offset; 68 }; 69 70 struct acpipchb_bus_space { 71 struct bus_space bs; 72 73 struct acpipchb_bus_range range[ACPIPCHB_MAX_RANGES]; 74 int nrange; 75 76 int (*map)(void *, bus_addr_t, bus_size_t, 77 int, bus_space_handle_t *); 78 79 int flags; 80 }; 81 82 struct acpipchb_softc { 83 device_t sc_dev; 84 85 bus_space_tag_t sc_memt; 86 87 ACPI_HANDLE sc_handle; 88 ACPI_INTEGER sc_bus; 89 90 struct acpipchb_bus_space sc_pcimem_bst; 91 struct acpipchb_bus_space sc_pciio_bst; 92 }; 93 94 static int acpipchb_match(device_t, cfdata_t, void *); 95 static void acpipchb_attach(device_t, device_t, void *); 96 97 static void acpipchb_setup_ranges(struct acpipchb_softc *, struct pcibus_attach_args *); 98 static void acpipchb_setup_quirks(struct acpipchb_softc *, struct pcibus_attach_args *); 99 100 CFATTACH_DECL_NEW(acpipchb, sizeof(struct acpipchb_softc), 101 acpipchb_match, acpipchb_attach, NULL, NULL); 102 103 static const char * const compatible[] = { 104 "PNP0A08", 105 NULL 106 }; 107 108 static int 109 acpipchb_match(device_t parent, cfdata_t cf, void *aux) 110 { 111 struct acpi_attach_args *aa = aux; 112 113 if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE) 114 return 0; 115 116 return acpi_match_hid(aa->aa_node->ad_devinfo, compatible); 117 } 118 119 static void 120 acpipchb_attach(device_t parent, device_t self, void *aux) 121 { 122 struct acpipchb_softc * const sc = device_private(self); 123 struct acpi_attach_args *aa = aux; 124 struct pcibus_attach_args pba; 125 ACPI_INTEGER seg; 126 127 sc->sc_dev = self; 128 sc->sc_memt = aa->aa_memt; 129 sc->sc_handle = aa->aa_node->ad_handle; 130 131 if (ACPI_FAILURE(acpi_eval_integer(sc->sc_handle, "_BBN", &sc->sc_bus))) 132 sc->sc_bus = 0; 133 134 if (ACPI_FAILURE(acpi_eval_integer(sc->sc_handle, "_SEG", &seg))) 135 seg = 0; 136 137 aprint_naive("\n"); 138 aprint_normal(": PCI Express Host Bridge\n"); 139 140 if (acpi_pci_ignore_boot_config(sc->sc_handle)) { 141 if (acpimcfg_configure_bus(self, aa->aa_pc, sc->sc_handle, sc->sc_bus, PCIHOST_CACHELINE_SIZE) != 0) 142 aprint_error_dev(self, "failed to configure bus\n"); 143 } 144 145 memset(&pba, 0, sizeof(pba)); 146 pba.pba_flags = aa->aa_pciflags & ~(PCI_FLAGS_MEM_OKAY | PCI_FLAGS_IO_OKAY); 147 pba.pba_memt = 0; 148 pba.pba_iot = 0; 149 pba.pba_dmat = aa->aa_dmat; 150 #ifdef _PCI_HAVE_DMA64 151 pba.pba_dmat64 = aa->aa_dmat64; 152 #endif 153 pba.pba_pc = aa->aa_pc; 154 pba.pba_bus = sc->sc_bus; 155 156 acpipchb_setup_ranges(sc, &pba); 157 acpipchb_setup_quirks(sc, &pba); 158 159 config_found_ia(self, "pcibus", &pba, pcibusprint); 160 } 161 162 struct acpipchb_setup_ranges_args { 163 struct acpipchb_softc *sc; 164 struct pcibus_attach_args *pba; 165 }; 166 167 static int 168 acpipchb_bus_space_map(void *t, bus_addr_t bpa, bus_size_t size, int flag, 169 bus_space_handle_t *bshp) 170 { 171 struct acpipchb_bus_space * const abs = t; 172 int i; 173 174 if (size == 0) 175 return ERANGE; 176 177 if ((abs->flags & PCI_FLAGS_IO_OKAY) != 0) { 178 /* Force strongly ordered mapping for all I/O space */ 179 flag = _ARM_BUS_SPACE_MAP_STRONGLY_ORDERED; 180 } 181 182 for (i = 0; i < abs->nrange; i++) { 183 struct acpipchb_bus_range * const range = &abs->range[i]; 184 if (bpa >= range->min && bpa + size - 1 <= range->max) 185 return abs->map(t, bpa + range->offset, size, flag, bshp); 186 } 187 188 return ERANGE; 189 } 190 191 static ACPI_STATUS 192 acpipchb_setup_ranges_cb(ACPI_RESOURCE *res, void *ctx) 193 { 194 struct acpipchb_setup_ranges_args * const args = ctx; 195 struct acpipchb_softc * const sc = args->sc; 196 struct pcibus_attach_args *pba = args->pba; 197 struct acpipchb_bus_space *abs; 198 struct acpipchb_bus_range *range; 199 const char *range_type; 200 u_int pci_flags; 201 202 if (res->Type != ACPI_RESOURCE_TYPE_ADDRESS32 && 203 res->Type != ACPI_RESOURCE_TYPE_ADDRESS64) 204 return AE_OK; 205 206 switch (res->Data.Address.ResourceType) { 207 case ACPI_IO_RANGE: 208 abs = &sc->sc_pciio_bst; 209 range_type = "I/O"; 210 pci_flags = PCI_FLAGS_IO_OKAY; 211 break; 212 case ACPI_MEMORY_RANGE: 213 abs = &sc->sc_pcimem_bst; 214 range_type = "MEM"; 215 pci_flags = PCI_FLAGS_MEM_OKAY; 216 break; 217 default: 218 return AE_OK; 219 } 220 221 if (abs->nrange == ACPIPCHB_MAX_RANGES) { 222 aprint_error_dev(sc->sc_dev, 223 "maximum number of ranges reached, increase ACPIPCHB_MAX_RANGES\n"); 224 return AE_LIMIT; 225 } 226 227 range = &abs->range[abs->nrange]; 228 switch (res->Type) { 229 case ACPI_RESOURCE_TYPE_ADDRESS32: 230 range->min = res->Data.Address32.Address.Minimum; 231 range->max = res->Data.Address32.Address.Maximum; 232 range->offset = res->Data.Address32.Address.TranslationOffset; 233 break; 234 case ACPI_RESOURCE_TYPE_ADDRESS64: 235 range->min = res->Data.Address64.Address.Minimum; 236 range->max = res->Data.Address64.Address.Maximum; 237 range->offset = res->Data.Address64.Address.TranslationOffset; 238 break; 239 default: 240 return AE_OK; 241 } 242 abs->nrange++; 243 244 aprint_debug_dev(sc->sc_dev, "PCI %s [%#lx-%#lx] -> %#lx\n", range_type, range->min, range->max, range->offset); 245 246 if ((pba->pba_flags & pci_flags) == 0) { 247 abs->bs = *sc->sc_memt; 248 abs->bs.bs_cookie = abs; 249 abs->map = abs->bs.bs_map; 250 abs->flags = pci_flags; 251 abs->bs.bs_map = acpipchb_bus_space_map; 252 if ((pci_flags & PCI_FLAGS_IO_OKAY) != 0) 253 pba->pba_iot = &abs->bs; 254 else if ((pci_flags & PCI_FLAGS_MEM_OKAY) != 0) 255 pba->pba_memt = &abs->bs; 256 pba->pba_flags |= pci_flags; 257 } 258 259 return AE_OK; 260 } 261 262 static void 263 acpipchb_setup_ranges(struct acpipchb_softc *sc, struct pcibus_attach_args *pba) 264 { 265 struct acpipchb_setup_ranges_args args; 266 267 args.sc = sc; 268 args.pba = pba; 269 270 AcpiWalkResources(sc->sc_handle, "_CRS", acpipchb_setup_ranges_cb, &args); 271 } 272 273 static void 274 acpipchb_setup_quirks(struct acpipchb_softc *sc, struct pcibus_attach_args *pba) 275 { 276 struct arm32_pci_chipset *md_pc = (struct arm32_pci_chipset *)pba->pba_pc; 277 struct acpi_pci_context *ap = md_pc->pc_conf_v; 278 279 pba->pba_flags &= ~ap->ap_pciflags_clear; 280 } 281