1/* $NetBSD: acpipchb.c,v 1.32 2022/10/15 11:07:38 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.32 2022/10/15 11:07:38 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/queue.h> 42#include <sys/mutex.h> 43#include <sys/kmem.h> 44#include <sys/cpu.h> 45 46#include <arm/cpufunc.h> 47#include <arm/bootconfig.h> 48 49#include <dev/pci/pcireg.h> 50#include <dev/pci/pcivar.h> 51#include <dev/pci/pci_resource.h> 52 53#include <dev/acpi/acpivar.h> 54#include <dev/acpi/acpi_pci.h> 55#include <dev/acpi/acpi_mcfg.h> 56 57#include <arm/acpi/acpi_pci_machdep.h> 58 59#define ACPIPCHB_MAX_RANGES 64 /* XXX arbitrary limit */ 60 61struct acpipchb_bus_range { 62 bus_addr_t min; 63 bus_addr_t max; 64 bus_addr_t offset; 65}; 66 67struct acpipchb_bus_space { 68 struct bus_space bs; 69 70 struct acpipchb_bus_range range[ACPIPCHB_MAX_RANGES]; 71 int nrange; 72 73 int (*map)(void *, bus_addr_t, bus_size_t, 74 int, bus_space_handle_t *); 75 76 int flags; 77}; 78 79struct acpipchb_softc { 80 device_t sc_dev; 81 82 bus_space_tag_t sc_memt; 83 84 ACPI_HANDLE sc_handle; 85 ACPI_INTEGER sc_bus; 86 87 struct acpipchb_bus_space sc_pcimem_bst; 88 struct acpipchb_bus_space sc_pciio_bst; 89}; 90 91static int acpipchb_match(device_t, cfdata_t, void *); 92static void acpipchb_attach(device_t, device_t, void *); 93 94static void acpipchb_configure_bus(struct acpipchb_softc *, struct pcibus_attach_args *); 95static void acpipchb_setup_ranges(struct acpipchb_softc *, 96 struct pcibus_attach_args *); 97static void acpipchb_setup_quirks(struct acpipchb_softc *, 98 struct pcibus_attach_args *); 99 100CFATTACH_DECL_NEW(acpipchb, sizeof(struct acpipchb_softc), 101 acpipchb_match, acpipchb_attach, NULL, NULL); 102 103static const char * const compatible[] = { 104 "PNP0A08", 105 NULL 106}; 107 108static int 109acpipchb_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 119static void 120acpipchb_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, nomsi; 126 ACPI_STATUS rv; 127 uint16_t bus_start; 128 int val; 129 130 sc->sc_dev = self; 131 sc->sc_memt = aa->aa_memt; 132 sc->sc_handle = aa->aa_node->ad_handle; 133 134 /* 135 * First try to derive the base bus number from _CRS. If that fails, 136 * try _BBN. If that fails too, assume bus 0. 137 */ 138 if (ACPI_SUCCESS(acpi_pcidev_pciroot_bus(sc->sc_handle, &bus_start))) { 139 sc->sc_bus = bus_start; 140 } else { 141 rv = acpi_eval_integer(sc->sc_handle, "_BBN", &sc->sc_bus); 142 if (ACPI_FAILURE(rv)) { 143 sc->sc_bus = 0; 144 } 145 } 146 147 if (ACPI_FAILURE(acpi_eval_integer(sc->sc_handle, "_SEG", &seg))) { 148 seg = 0; 149 } 150 151 if (ACPI_FAILURE(acpi_dsd_integer(sc->sc_handle, "linux,pcie-nomsi", 152 &nomsi))) { 153 nomsi = 0; 154 } 155 if (get_bootconf_option(boot_args, "nopcimsi", 156 BOOTOPT_TYPE_BOOLEAN, &val) && val) { 157 nomsi = 1; 158 } 159 160 aprint_naive("\n"); 161 aprint_normal(": PCI Express Host Bridge\n"); 162 163 acpi_claim_childdevs(self, aa->aa_node); 164 165 memset(&pba, 0, sizeof(pba)); 166 pba.pba_flags = aa->aa_pciflags & 167 ~(PCI_FLAGS_MEM_OKAY | PCI_FLAGS_IO_OKAY); 168 if (nomsi) { 169 pba.pba_flags &= ~(PCI_FLAGS_MSI_OKAY | PCI_FLAGS_MSIX_OKAY); 170 } 171 pba.pba_memt = 0; 172 pba.pba_iot = 0; 173 pba.pba_dmat = aa->aa_dmat; 174#ifdef _PCI_HAVE_DMA64 175 pba.pba_dmat64 = aa->aa_dmat64; 176#endif 177 pba.pba_pc = aa->aa_pc; 178 pba.pba_bus = sc->sc_bus; 179 180 acpipchb_setup_ranges(sc, &pba); 181 acpipchb_setup_quirks(sc, &pba); 182 183 acpipchb_configure_bus(sc, &pba); 184 185 config_found(self, &pba, pcibusprint, 186 CFARGS(.devhandle = device_handle(self))); 187} 188 189static void 190acpipchb_configure_bus(struct acpipchb_softc *sc, struct pcibus_attach_args *pba) 191{ 192 struct arm32_pci_chipset *md_pc = 193 (struct arm32_pci_chipset *)pba->pba_pc; 194 struct acpi_pci_context *ap = md_pc->pc_conf_v; 195 const bool mapcfgspace = (ap->ap_flags & ACPI_PCI_FLAG_NO_MCFG) == 0; 196 int error, val; 197 198 if (get_bootconf_option(boot_args, "nopciconf", 199 BOOTOPT_TYPE_BOOLEAN, &val) && val) { 200 return; 201 } 202 203 error = acpimcfg_configure_bus(sc->sc_dev, pba->pba_pc, sc->sc_handle, 204 sc->sc_bus, mapcfgspace); 205 if (error != 0) { 206 aprint_error_dev(sc->sc_dev, "failed to configure bus, error %d\n", 207 error); 208 } 209} 210 211struct acpipchb_setup_ranges_args { 212 struct acpipchb_softc *sc; 213 struct pcibus_attach_args *pba; 214}; 215 216static int 217acpipchb_bus_space_map(void *t, bus_addr_t bpa, bus_size_t size, int flag, 218 bus_space_handle_t *bshp) 219{ 220 struct acpipchb_bus_space * const abs = t; 221 int i; 222 223 if (size == 0) 224 return ERANGE; 225 226 if ((abs->flags & PCI_FLAGS_IO_OKAY) != 0) { 227 /* Force strongly ordered mapping for all I/O space */ 228 flag = BUS_SPACE_MAP_NONPOSTED; 229 } 230 231 for (i = 0; i < abs->nrange; i++) { 232 struct acpipchb_bus_range * const range = &abs->range[i]; 233 if (bpa >= range->min && bpa + size - 1 <= range->max) { 234 return abs->map(t, bpa + range->offset, size, 235 flag, bshp); 236 } 237 } 238 239 return ERANGE; 240} 241 242static ACPI_STATUS 243acpipchb_setup_ranges_cb(ACPI_RESOURCE *res, void *ctx) 244{ 245 struct acpipchb_setup_ranges_args * const args = ctx; 246 struct acpipchb_softc * const sc = args->sc; 247 struct pcibus_attach_args *pba = args->pba; 248 struct acpipchb_bus_space *abs; 249 struct acpipchb_bus_range *range; 250 const char *range_type; 251 u_int pci_flags; 252 253 if (res->Type != ACPI_RESOURCE_TYPE_ADDRESS32 && 254 res->Type != ACPI_RESOURCE_TYPE_ADDRESS64) { 255 return AE_OK; 256 } 257 258 switch (res->Data.Address.ResourceType) { 259 case ACPI_IO_RANGE: 260 abs = &sc->sc_pciio_bst; 261 range_type = "I/O"; 262 pci_flags = PCI_FLAGS_IO_OKAY; 263 break; 264 case ACPI_MEMORY_RANGE: 265 abs = &sc->sc_pcimem_bst; 266 range_type = "MEM"; 267 pci_flags = PCI_FLAGS_MEM_OKAY; 268 break; 269 default: 270 return AE_OK; 271 } 272 273 if (abs->nrange == ACPIPCHB_MAX_RANGES) { 274 aprint_error_dev(sc->sc_dev, 275 "maximum number of ranges reached (ACPIPCHB_MAX_RANGES)\n"); 276 return AE_LIMIT; 277 } 278 279 range = &abs->range[abs->nrange]; 280 switch (res->Type) { 281 case ACPI_RESOURCE_TYPE_ADDRESS32: 282 range->min = res->Data.Address32.Address.Minimum; 283 range->max = res->Data.Address32.Address.Maximum; 284 range->offset = res->Data.Address32.Address.TranslationOffset; 285 break; 286 case ACPI_RESOURCE_TYPE_ADDRESS64: 287 range->min = res->Data.Address64.Address.Minimum; 288 range->max = res->Data.Address64.Address.Maximum; 289 range->offset = res->Data.Address64.Address.TranslationOffset; 290 break; 291 default: 292 return AE_OK; 293 } 294 abs->nrange++; 295 296 aprint_debug_dev(sc->sc_dev, "PCI %s [%#lx-%#lx] -> %#lx\n", 297 range_type, range->min, range->max, range->offset); 298 299 if ((pba->pba_flags & pci_flags) == 0) { 300 abs->bs = *sc->sc_memt; 301 abs->bs.bs_cookie = abs; 302 abs->map = abs->bs.bs_map; 303 abs->flags = pci_flags; 304 abs->bs.bs_map = acpipchb_bus_space_map; 305 if ((pci_flags & PCI_FLAGS_IO_OKAY) != 0) { 306 pba->pba_iot = &abs->bs; 307 } else if ((pci_flags & PCI_FLAGS_MEM_OKAY) != 0) { 308 pba->pba_memt = &abs->bs; 309 } 310 pba->pba_flags |= pci_flags; 311 } 312 313 return AE_OK; 314} 315 316static void 317acpipchb_setup_ranges(struct acpipchb_softc *sc, struct pcibus_attach_args *pba) 318{ 319 struct acpipchb_setup_ranges_args args; 320 321 args.sc = sc; 322 args.pba = pba; 323 324 AcpiWalkResources(sc->sc_handle, "_CRS", acpipchb_setup_ranges_cb, 325 &args); 326} 327 328static void 329acpipchb_setup_quirks(struct acpipchb_softc *sc, struct pcibus_attach_args *pba) 330{ 331 struct arm32_pci_chipset *md_pc = 332 (struct arm32_pci_chipset *)pba->pba_pc; 333 struct acpi_pci_context *ap = md_pc->pc_conf_v; 334 335 pba->pba_flags &= ~ap->ap_pciflags_clear; 336} 337