1*5c830e3fSmlelstv /* $NetBSD: acpi_mcfg.c,v 1.30 2024/11/10 10:45:37 mlelstv Exp $ */ 2605f564fSmsaitoh 3605f564fSmsaitoh /*- 4605f564fSmsaitoh * Copyright (C) 2015 NONAKA Kimihiro <nonaka@NetBSD.org> 5605f564fSmsaitoh * All rights reserved. 6605f564fSmsaitoh * 7605f564fSmsaitoh * Redistribution and use in source and binary forms, with or without 8605f564fSmsaitoh * modification, are permitted provided that the following conditions 9605f564fSmsaitoh * are met: 10605f564fSmsaitoh * 1. Redistributions of source code must retain the above copyright 11605f564fSmsaitoh * notice, this list of conditions and the following disclaimer. 12605f564fSmsaitoh * 2. Redistributions in binary form must reproduce the above copyright 13605f564fSmsaitoh * notice, this list of conditions and the following disclaimer in the 14605f564fSmsaitoh * documentation and/or other materials provided with the distribution. 15605f564fSmsaitoh * 16605f564fSmsaitoh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17605f564fSmsaitoh * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18605f564fSmsaitoh * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19605f564fSmsaitoh * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20605f564fSmsaitoh * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21605f564fSmsaitoh * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22605f564fSmsaitoh * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23605f564fSmsaitoh * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24605f564fSmsaitoh * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25605f564fSmsaitoh * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26605f564fSmsaitoh */ 27605f564fSmsaitoh 28aaf945cfSjmcneill #include "opt_pci.h" 29aaf945cfSjmcneill 30605f564fSmsaitoh #include <sys/cdefs.h> 31*5c830e3fSmlelstv __KERNEL_RCSID(0, "$NetBSD: acpi_mcfg.c,v 1.30 2024/11/10 10:45:37 mlelstv Exp $"); 32605f564fSmsaitoh 33605f564fSmsaitoh #include <sys/param.h> 34605f564fSmsaitoh #include <sys/device.h> 35605f564fSmsaitoh #include <sys/kmem.h> 36605f564fSmsaitoh #include <sys/systm.h> 37605f564fSmsaitoh 38605f564fSmsaitoh #include <dev/pci/pcireg.h> 39605f564fSmsaitoh #include <dev/pci/pcivar.h> 401099f047Sjmcneill #include <dev/pci/pci_resource.h> 41605f564fSmsaitoh #include <dev/pci/pcidevs.h> 42605f564fSmsaitoh 43605f564fSmsaitoh #include <dev/acpi/acpireg.h> 44605f564fSmsaitoh #include <dev/acpi/acpivar.h> 45605f564fSmsaitoh #include <dev/acpi/acpi_mcfg.h> 46605f564fSmsaitoh 47605f564fSmsaitoh #include "locators.h" 48605f564fSmsaitoh 4925babac1Schristos #define _COMPONENT ACPI_RESOURCE_COMPONENT 5025babac1Schristos ACPI_MODULE_NAME ("acpi_mcfg") 5125babac1Schristos 52605f564fSmsaitoh #define EXTCONF_OFFSET(d, f, r) ((((d) * 8 + (f)) * PCI_EXTCONF_SIZE) + (r)) 53605f564fSmsaitoh 54da1fbcb7Sjmcneill #define PCIDEV_SET_VALID(mb, d, f) ((mb)->valid_devs[(d)] |= __BIT((f))) 55da1fbcb7Sjmcneill #define PCIDEV_SET_INVALID(mb, d, f) ((mb)->valid_devs[(d)] &= ~__BIT((f))) 56da1fbcb7Sjmcneill #define PCIDEV_IS_VALID(mb, d, f) ((mb)->valid_devs[(d)] & __BIT((f))) 57da1fbcb7Sjmcneill 58da1fbcb7Sjmcneill #define EXTCONF_SET_VALID(mb, d, f) ((mb)->valid_extconf[(d)] |= __BIT((f))) 59da1fbcb7Sjmcneill #define EXTCONF_SET_INVALID(mb, d, f) ((mb)->valid_extconf[(d)] &= ~__BIT((f))) 60da1fbcb7Sjmcneill #define EXTCONF_IS_VALID(mb, d, f) ((mb)->valid_extconf[(d)] & __BIT((f))) 61605f564fSmsaitoh 62605f564fSmsaitoh struct mcfg_segment { 63605f564fSmsaitoh uint64_t ms_address; /* Base address */ 64605f564fSmsaitoh int ms_segment; /* Segment # */ 65605f564fSmsaitoh int ms_bus_start; /* Start bus # */ 66605f564fSmsaitoh int ms_bus_end; /* End bus # */ 67605f564fSmsaitoh bus_space_tag_t ms_bst; 68605f564fSmsaitoh struct mcfg_bus { 69605f564fSmsaitoh bus_space_handle_t bsh[32][8]; 70605f564fSmsaitoh uint8_t valid_devs[32]; 71da1fbcb7Sjmcneill uint8_t valid_extconf[32]; 72605f564fSmsaitoh int valid_ndevs; 73605f564fSmsaitoh pcitag_t last_probed; 74605f564fSmsaitoh } *ms_bus; 75605f564fSmsaitoh }; 76605f564fSmsaitoh 77605f564fSmsaitoh static struct mcfg_segment *mcfg_segs; 78605f564fSmsaitoh static int mcfg_nsegs; 79605f564fSmsaitoh static ACPI_TABLE_MCFG *mcfg; 80605f564fSmsaitoh static int mcfg_inited; 81605f564fSmsaitoh static struct acpi_softc *acpi_sc; 82605f564fSmsaitoh 83605f564fSmsaitoh static const struct acpimcfg_ops mcfg_default_ops = { 84605f564fSmsaitoh .ao_validate = acpimcfg_default_validate, 85605f564fSmsaitoh 86605f564fSmsaitoh .ao_read = acpimcfg_default_read, 87605f564fSmsaitoh .ao_write = acpimcfg_default_write, 88605f564fSmsaitoh }; 89605f564fSmsaitoh static const struct acpimcfg_ops *mcfg_ops = &mcfg_default_ops; 90605f564fSmsaitoh 91605f564fSmsaitoh /* 92605f564fSmsaitoh * default operations. 93605f564fSmsaitoh */ 94605f564fSmsaitoh bool 95605f564fSmsaitoh acpimcfg_default_validate(uint64_t address, int bus_start, int *bus_end) 96605f564fSmsaitoh { 97605f564fSmsaitoh 98605f564fSmsaitoh /* Always Ok */ 99605f564fSmsaitoh return true; 100605f564fSmsaitoh } 101605f564fSmsaitoh 102605f564fSmsaitoh uint32_t 103605f564fSmsaitoh acpimcfg_default_read(bus_space_tag_t bst, bus_space_handle_t bsh, 104605f564fSmsaitoh bus_addr_t addr) 105605f564fSmsaitoh { 106605f564fSmsaitoh 107605f564fSmsaitoh return bus_space_read_4(bst, bsh, addr); 108605f564fSmsaitoh } 109605f564fSmsaitoh 110605f564fSmsaitoh void 111605f564fSmsaitoh acpimcfg_default_write(bus_space_tag_t bst, bus_space_handle_t bsh, 112605f564fSmsaitoh bus_addr_t addr, uint32_t data) 113605f564fSmsaitoh { 114605f564fSmsaitoh 115605f564fSmsaitoh bus_space_write_4(bst, bsh, addr, data); 116605f564fSmsaitoh } 117605f564fSmsaitoh 118605f564fSmsaitoh 119605f564fSmsaitoh /* 120605f564fSmsaitoh * Check MCFG memory region at system resource 121605f564fSmsaitoh */ 122605f564fSmsaitoh struct acpimcfg_memrange { 123605f564fSmsaitoh const char *hid; 124605f564fSmsaitoh uint64_t address; 125605f564fSmsaitoh int bus_start; 126605f564fSmsaitoh int bus_end; 127605f564fSmsaitoh bool found; 128605f564fSmsaitoh }; 129605f564fSmsaitoh 130605f564fSmsaitoh static ACPI_STATUS 131605f564fSmsaitoh acpimcfg_parse_callback(ACPI_RESOURCE *res, void *ctx) 132605f564fSmsaitoh { 133605f564fSmsaitoh struct acpimcfg_memrange *mr = ctx; 134605f564fSmsaitoh const char *type; 135605f564fSmsaitoh uint64_t size, mapaddr, mapsize; 136605f564fSmsaitoh int n; 137605f564fSmsaitoh 138605f564fSmsaitoh switch (res->Type) { 139605f564fSmsaitoh case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: 140605f564fSmsaitoh type = "FIXED_MEMORY32"; 141605f564fSmsaitoh mapaddr = res->Data.FixedMemory32.Address; 142605f564fSmsaitoh mapsize = res->Data.FixedMemory32.AddressLength; 143605f564fSmsaitoh break; 144605f564fSmsaitoh 145605f564fSmsaitoh case ACPI_RESOURCE_TYPE_ADDRESS32: 146605f564fSmsaitoh /* XXX Only fixed size supported for now */ 147605f564fSmsaitoh if (res->Data.Address32.Address.AddressLength == 0 || 148605f564fSmsaitoh res->Data.Address32.ProducerConsumer != ACPI_CONSUMER) 149605f564fSmsaitoh goto out; 150605f564fSmsaitoh 151605f564fSmsaitoh if (res->Data.Address32.ResourceType != ACPI_MEMORY_RANGE) 152605f564fSmsaitoh goto out; 153605f564fSmsaitoh 154605f564fSmsaitoh if (res->Data.Address32.MinAddressFixed != ACPI_ADDRESS_FIXED || 155605f564fSmsaitoh res->Data.Address32.MaxAddressFixed != ACPI_ADDRESS_FIXED) 156605f564fSmsaitoh goto out; 157605f564fSmsaitoh 158605f564fSmsaitoh type = "ADDRESS32"; 159605f564fSmsaitoh mapaddr = res->Data.Address32.Address.Minimum; 160605f564fSmsaitoh mapsize = res->Data.Address32.Address.AddressLength; 161605f564fSmsaitoh break; 162605f564fSmsaitoh 163605f564fSmsaitoh #ifdef _LP64 164605f564fSmsaitoh case ACPI_RESOURCE_TYPE_ADDRESS64: 165605f564fSmsaitoh /* XXX Only fixed size supported for now */ 166605f564fSmsaitoh if (res->Data.Address64.Address.AddressLength == 0 || 167605f564fSmsaitoh res->Data.Address64.ProducerConsumer != ACPI_CONSUMER) 168605f564fSmsaitoh goto out; 169605f564fSmsaitoh 170605f564fSmsaitoh if (res->Data.Address64.ResourceType != ACPI_MEMORY_RANGE) 171605f564fSmsaitoh goto out; 172605f564fSmsaitoh 173605f564fSmsaitoh if (res->Data.Address64.MinAddressFixed != ACPI_ADDRESS_FIXED || 174605f564fSmsaitoh res->Data.Address64.MaxAddressFixed != ACPI_ADDRESS_FIXED) 175605f564fSmsaitoh goto out; 176605f564fSmsaitoh 177605f564fSmsaitoh type = "ADDRESS64"; 178605f564fSmsaitoh mapaddr = res->Data.Address64.Address.Minimum; 179605f564fSmsaitoh mapsize = res->Data.Address64.Address.AddressLength; 180605f564fSmsaitoh break; 181605f564fSmsaitoh #endif 182605f564fSmsaitoh 183605f564fSmsaitoh default: 184605f564fSmsaitoh out: 185605f564fSmsaitoh aprint_debug_dev(acpi_sc->sc_dev, "MCFG: %s: Type=%d\n", 186605f564fSmsaitoh mr->hid, res->Type); 187605f564fSmsaitoh return_ACPI_STATUS(AE_OK); 188605f564fSmsaitoh } 189605f564fSmsaitoh 190605f564fSmsaitoh aprint_debug_dev(acpi_sc->sc_dev, "MCFG: %s: Type=%d(%s), " 191605f564fSmsaitoh "Address=0x%016" PRIx64 ", Length=0x%016" PRIx64 "\n", 192605f564fSmsaitoh mr->hid, res->Type, type, mapaddr, mapsize); 193605f564fSmsaitoh 194605f564fSmsaitoh if (mr->address < mapaddr || mr->address >= mapaddr + mapsize) 195605f564fSmsaitoh return_ACPI_STATUS(AE_OK); 196605f564fSmsaitoh 197605f564fSmsaitoh size = (mr->bus_end - mr->bus_start + 1) * ACPIMCFG_SIZE_PER_BUS; 198605f564fSmsaitoh 199605f564fSmsaitoh /* full map */ 200605f564fSmsaitoh if (mr->address + size <= mapaddr + mapsize) { 201605f564fSmsaitoh mr->found = true; 202605f564fSmsaitoh return_ACPI_STATUS(AE_CTRL_TERMINATE); 203605f564fSmsaitoh } 204605f564fSmsaitoh 205605f564fSmsaitoh /* partial map */ 206605f564fSmsaitoh n = (mapsize - (mr->address - mapaddr)) / ACPIMCFG_SIZE_PER_BUS; 207605f564fSmsaitoh /* bus_start == bus_end is not allowed. */ 208605f564fSmsaitoh if (n > 1) { 209605f564fSmsaitoh mr->bus_end = mr->bus_start + n - 1; 210605f564fSmsaitoh mr->found = true; 211605f564fSmsaitoh return_ACPI_STATUS(AE_CTRL_TERMINATE); 212605f564fSmsaitoh } 213605f564fSmsaitoh 214605f564fSmsaitoh aprint_debug_dev(acpi_sc->sc_dev, "MCFG: bus %d-%d, " 215605f564fSmsaitoh "address 0x%016" PRIx64 ": invalid size: request 0x%016" PRIx64 216605f564fSmsaitoh ", actual 0x%016" PRIx64 "\n", 217605f564fSmsaitoh mr->bus_start, mr->bus_end, mr->address, size, mapsize); 218605f564fSmsaitoh 219605f564fSmsaitoh return_ACPI_STATUS(AE_OK); 220605f564fSmsaitoh } 221605f564fSmsaitoh 222605f564fSmsaitoh static ACPI_STATUS 223605f564fSmsaitoh acpimcfg_check_system_resource(ACPI_HANDLE handle, UINT32 level, void *ctx, 224605f564fSmsaitoh void **retval) 225605f564fSmsaitoh { 226605f564fSmsaitoh struct acpimcfg_memrange *mr = ctx; 227605f564fSmsaitoh ACPI_STATUS status; 228605f564fSmsaitoh 229605f564fSmsaitoh status = AcpiWalkResources(handle, "_CRS", acpimcfg_parse_callback, mr); 230605f564fSmsaitoh if (ACPI_FAILURE(status)) 231605f564fSmsaitoh return_ACPI_STATUS(status); 232605f564fSmsaitoh 233605f564fSmsaitoh if (mr->found) 234605f564fSmsaitoh return_ACPI_STATUS(AE_CTRL_TERMINATE); 235605f564fSmsaitoh 236605f564fSmsaitoh aprint_debug_dev(acpi_sc->sc_dev, "MCFG: %s: bus %d-%d, " 237605f564fSmsaitoh "address 0x%016" PRIx64 ": no valid region\n", mr->hid, 238605f564fSmsaitoh mr->bus_start, mr->bus_end, mr->address); 239605f564fSmsaitoh 240605f564fSmsaitoh return_ACPI_STATUS(AE_OK); 241605f564fSmsaitoh } 242605f564fSmsaitoh 243605f564fSmsaitoh static bool 244605f564fSmsaitoh acpimcfg_find_system_resource(uint64_t address, int bus_start, int *bus_end) 245605f564fSmsaitoh { 246605f564fSmsaitoh static const char *system_resource_hid[] = { 247605f564fSmsaitoh "PNP0C01", /* System Board */ 248605f564fSmsaitoh "PNP0C02" /* General ID for reserving resources */ 249605f564fSmsaitoh }; 250605f564fSmsaitoh struct acpimcfg_memrange mr; 251605f564fSmsaitoh ACPI_STATUS status; 252605f564fSmsaitoh int i; 253605f564fSmsaitoh 254605f564fSmsaitoh mr.address = address; 255605f564fSmsaitoh mr.bus_start = bus_start; 256605f564fSmsaitoh mr.bus_end = *bus_end; 257605f564fSmsaitoh mr.found = false; 258605f564fSmsaitoh 259605f564fSmsaitoh for (i = 0; i < __arraycount(system_resource_hid); i++) { 260605f564fSmsaitoh mr.hid = system_resource_hid[i]; 261605f564fSmsaitoh status = AcpiGetDevices(__UNCONST(system_resource_hid[i]), 262605f564fSmsaitoh acpimcfg_check_system_resource, &mr, NULL); 263605f564fSmsaitoh if (ACPI_FAILURE(status)) 264605f564fSmsaitoh continue; 265605f564fSmsaitoh if (mr.found) { 266605f564fSmsaitoh *bus_end = mr.bus_end; 267605f564fSmsaitoh return true; 268605f564fSmsaitoh } 269605f564fSmsaitoh } 270605f564fSmsaitoh return false; 271605f564fSmsaitoh } 272605f564fSmsaitoh 273605f564fSmsaitoh 274605f564fSmsaitoh /* 275605f564fSmsaitoh * ACPI MCFG 276605f564fSmsaitoh */ 277605f564fSmsaitoh void 278605f564fSmsaitoh acpimcfg_probe(struct acpi_softc *sc) 279605f564fSmsaitoh { 280605f564fSmsaitoh ACPI_MCFG_ALLOCATION *ama; 281605f564fSmsaitoh ACPI_STATUS status; 282605f564fSmsaitoh uint32_t offset; 283605f564fSmsaitoh int i, nsegs; 284605f564fSmsaitoh 285605f564fSmsaitoh if (acpi_sc != NULL) 286605f564fSmsaitoh panic("acpi_sc != NULL"); 287605f564fSmsaitoh acpi_sc = sc; 288605f564fSmsaitoh 289605f564fSmsaitoh status = AcpiGetTable(ACPI_SIG_MCFG, 0, (ACPI_TABLE_HEADER **)&mcfg); 290605f564fSmsaitoh if (ACPI_FAILURE(status)) { 291605f564fSmsaitoh mcfg = NULL; 292605f564fSmsaitoh return; 293605f564fSmsaitoh } 294605f564fSmsaitoh 295605f564fSmsaitoh nsegs = 0; 296605f564fSmsaitoh offset = sizeof(ACPI_TABLE_MCFG); 297605f564fSmsaitoh ama = ACPI_ADD_PTR(ACPI_MCFG_ALLOCATION, mcfg, offset); 298144c24beSmaxv for (i = 0; offset + sizeof(ACPI_MCFG_ALLOCATION) <= 299144c24beSmaxv mcfg->Header.Length; i++) { 300605f564fSmsaitoh aprint_debug_dev(sc->sc_dev, 301605f564fSmsaitoh "MCFG: segment %d, bus %d-%d, address 0x%016" PRIx64 "\n", 302605f564fSmsaitoh ama->PciSegment, ama->StartBusNumber, ama->EndBusNumber, 303605f564fSmsaitoh ama->Address); 304605f564fSmsaitoh nsegs++; 305605f564fSmsaitoh offset += sizeof(ACPI_MCFG_ALLOCATION); 306aaeacaa7Sjmcneill ama = ACPI_ADD_PTR(ACPI_MCFG_ALLOCATION, mcfg, offset); 307605f564fSmsaitoh } 308605f564fSmsaitoh if (nsegs == 0) { 309605f564fSmsaitoh mcfg = NULL; 310605f564fSmsaitoh return; 311605f564fSmsaitoh } 312605f564fSmsaitoh 313605f564fSmsaitoh mcfg_segs = kmem_zalloc(sizeof(*mcfg_segs) * nsegs, KM_SLEEP); 314605f564fSmsaitoh mcfg_nsegs = nsegs; 315605f564fSmsaitoh } 316605f564fSmsaitoh 317605f564fSmsaitoh int 318605f564fSmsaitoh acpimcfg_init(bus_space_tag_t memt, const struct acpimcfg_ops *ops) 319605f564fSmsaitoh { 320605f564fSmsaitoh ACPI_MCFG_ALLOCATION *ama; 321605f564fSmsaitoh struct mcfg_segment *seg; 322605f564fSmsaitoh uint32_t offset; 323605f564fSmsaitoh int i, n, nsegs, bus_end; 324605f564fSmsaitoh 325605f564fSmsaitoh if (mcfg == NULL) 326605f564fSmsaitoh return ENXIO; 327605f564fSmsaitoh 328605f564fSmsaitoh if (mcfg_inited) 329605f564fSmsaitoh return 0; 330605f564fSmsaitoh 331605f564fSmsaitoh if (ops != NULL) 332605f564fSmsaitoh mcfg_ops = ops; 333605f564fSmsaitoh 334605f564fSmsaitoh nsegs = 0; 335605f564fSmsaitoh offset = sizeof(ACPI_TABLE_MCFG); 336605f564fSmsaitoh ama = ACPI_ADD_PTR(ACPI_MCFG_ALLOCATION, mcfg, offset); 337605f564fSmsaitoh for (i = 0; offset < mcfg->Header.Length; i++) { 338605f564fSmsaitoh #ifndef _LP64 339605f564fSmsaitoh if (ama->Address >= 0x100000000ULL) { 340605f564fSmsaitoh aprint_debug_dev(acpi_sc->sc_dev, 341605f564fSmsaitoh "MCFG: segment %d, bus %d-%d, address 0x%016" PRIx64 342605f564fSmsaitoh ": ignore (64bit address)\n", ama->PciSegment, 343605f564fSmsaitoh ama->StartBusNumber, ama->EndBusNumber, 344605f564fSmsaitoh ama->Address); 345605f564fSmsaitoh goto next; 346605f564fSmsaitoh } 347605f564fSmsaitoh #endif 348605f564fSmsaitoh /* 349605f564fSmsaitoh * Some (broken?) BIOSen have an MCFG table for an empty 350605f564fSmsaitoh * bus range. Ignore those tables. 351605f564fSmsaitoh */ 352605f564fSmsaitoh if (ama->StartBusNumber > ama->EndBusNumber) { 353605f564fSmsaitoh aprint_debug_dev(acpi_sc->sc_dev, 354605f564fSmsaitoh "MCFG: segment %d, bus %d-%d, address 0x%016" PRIx64 355605f564fSmsaitoh ": ignore (bus %d > %d)\n", ama->PciSegment, 356605f564fSmsaitoh ama->StartBusNumber, ama->EndBusNumber, 357605f564fSmsaitoh ama->Address, ama->StartBusNumber, 358605f564fSmsaitoh ama->EndBusNumber); 359605f564fSmsaitoh goto next; 360605f564fSmsaitoh } 361605f564fSmsaitoh 362605f564fSmsaitoh /* Validate MCFG memory range */ 363605f564fSmsaitoh bus_end = ama->EndBusNumber; 364605f564fSmsaitoh if (mcfg_ops->ao_validate != NULL && 365605f564fSmsaitoh !mcfg_ops->ao_validate(ama->Address, ama->StartBusNumber, 366605f564fSmsaitoh &bus_end)) { 367605f564fSmsaitoh if (!acpimcfg_find_system_resource(ama->Address, 368605f564fSmsaitoh ama->StartBusNumber, &bus_end)) { 369605f564fSmsaitoh aprint_debug_dev(acpi_sc->sc_dev, 370605f564fSmsaitoh "MCFG: segment %d, bus %d-%d, " 371605f564fSmsaitoh "address 0x%016" PRIx64 372605f564fSmsaitoh ": ignore (invalid address)\n", 373605f564fSmsaitoh ama->PciSegment, 374605f564fSmsaitoh ama->StartBusNumber, ama->EndBusNumber, 375605f564fSmsaitoh ama->Address); 376605f564fSmsaitoh goto next; 377605f564fSmsaitoh } 378605f564fSmsaitoh } 379605f564fSmsaitoh if (ama->EndBusNumber != bus_end) { 380605f564fSmsaitoh aprint_debug_dev(acpi_sc->sc_dev, 381605f564fSmsaitoh "MCFG: segment %d, bus %d-%d, address 0x%016" PRIx64 382605f564fSmsaitoh " -> bus %d-%d\n", ama->PciSegment, 383605f564fSmsaitoh ama->StartBusNumber, ama->EndBusNumber, 384605f564fSmsaitoh ama->Address, ama->StartBusNumber, bus_end); 385605f564fSmsaitoh } 386605f564fSmsaitoh 38703660f55Sjmcneill #ifndef __HAVE_PCI_GET_SEGMENT 388605f564fSmsaitoh if (ama->PciSegment != 0) { 389605f564fSmsaitoh aprint_debug_dev(acpi_sc->sc_dev, 390605f564fSmsaitoh "MCFG: segment %d, bus %d-%d, address 0x%016" PRIx64 391605f564fSmsaitoh ": ignore (non PCI segment 0)\n", ama->PciSegment, 392605f564fSmsaitoh ama->StartBusNumber, bus_end, ama->Address); 393605f564fSmsaitoh goto next; 394605f564fSmsaitoh } 39503660f55Sjmcneill #endif 396605f564fSmsaitoh 397605f564fSmsaitoh seg = &mcfg_segs[nsegs++]; 398605f564fSmsaitoh seg->ms_address = ama->Address; 399605f564fSmsaitoh seg->ms_segment = ama->PciSegment; 400605f564fSmsaitoh seg->ms_bus_start = ama->StartBusNumber; 401605f564fSmsaitoh seg->ms_bus_end = bus_end; 402605f564fSmsaitoh seg->ms_bst = memt; 403605f564fSmsaitoh n = seg->ms_bus_end - seg->ms_bus_start + 1; 404605f564fSmsaitoh seg->ms_bus = kmem_zalloc(sizeof(*seg->ms_bus) * n, KM_SLEEP); 405605f564fSmsaitoh 406605f564fSmsaitoh next: 407605f564fSmsaitoh offset += sizeof(ACPI_MCFG_ALLOCATION); 408aaeacaa7Sjmcneill ama = ACPI_ADD_PTR(ACPI_MCFG_ALLOCATION, mcfg, offset); 409605f564fSmsaitoh } 410605f564fSmsaitoh if (nsegs == 0) 411605f564fSmsaitoh return ENOENT; 412605f564fSmsaitoh 413605f564fSmsaitoh for (i = 0; i < nsegs; i++) { 414605f564fSmsaitoh seg = &mcfg_segs[i]; 415605f564fSmsaitoh aprint_verbose_dev(acpi_sc->sc_dev, 416605f564fSmsaitoh "MCFG: segment %d, bus %d-%d, address 0x%016" PRIx64 "\n", 417605f564fSmsaitoh seg->ms_segment, seg->ms_bus_start, seg->ms_bus_end, 418605f564fSmsaitoh seg->ms_address); 419605f564fSmsaitoh } 420605f564fSmsaitoh 421605f564fSmsaitoh /* Update # of segment */ 422605f564fSmsaitoh mcfg_nsegs = nsegs; 423605f564fSmsaitoh mcfg_inited = true; 424605f564fSmsaitoh 425605f564fSmsaitoh return 0; 426605f564fSmsaitoh } 427605f564fSmsaitoh 428605f564fSmsaitoh static int 429605f564fSmsaitoh acpimcfg_ext_conf_is_aliased(pci_chipset_tag_t pc, pcitag_t tag) 430605f564fSmsaitoh { 431605f564fSmsaitoh pcireg_t id; 432605f564fSmsaitoh int i; 433605f564fSmsaitoh 434605f564fSmsaitoh id = pci_conf_read(pc, tag, PCI_ID_REG); 435605f564fSmsaitoh for (i = PCI_CONF_SIZE; i < PCI_EXTCONF_SIZE; i += PCI_CONF_SIZE) { 436605f564fSmsaitoh if (pci_conf_read(pc, tag, i) != id) 437605f564fSmsaitoh return false; 438605f564fSmsaitoh } 439605f564fSmsaitoh return true; 440605f564fSmsaitoh } 441605f564fSmsaitoh 442605f564fSmsaitoh static struct mcfg_segment * 44399199cfcSjmcneill acpimcfg_get_segment(pci_chipset_tag_t pc, int bus) 444605f564fSmsaitoh { 445605f564fSmsaitoh struct mcfg_segment *seg; 44699199cfcSjmcneill u_int segment; 447605f564fSmsaitoh int i; 448605f564fSmsaitoh 44999199cfcSjmcneill segment = pci_get_segment(pc); 450605f564fSmsaitoh for (i = 0; i < mcfg_nsegs; i++) { 451605f564fSmsaitoh seg = &mcfg_segs[i]; 45299199cfcSjmcneill if (segment == seg->ms_segment && 45399199cfcSjmcneill bus >= seg->ms_bus_start && bus <= seg->ms_bus_end) 454605f564fSmsaitoh return seg; 455605f564fSmsaitoh } 456605f564fSmsaitoh return NULL; 457605f564fSmsaitoh } 458605f564fSmsaitoh 459605f564fSmsaitoh static int 460605f564fSmsaitoh acpimcfg_device_probe(const struct pci_attach_args *pa) 461605f564fSmsaitoh { 462605f564fSmsaitoh pci_chipset_tag_t pc = pa->pa_pc; 463605f564fSmsaitoh struct mcfg_segment *seg; 464605f564fSmsaitoh struct mcfg_bus *mb; 465605f564fSmsaitoh pcitag_t tag; 466605f564fSmsaitoh pcireg_t reg; 467605f564fSmsaitoh int bus = pa->pa_bus; 468605f564fSmsaitoh int dev = pa->pa_device; 469605f564fSmsaitoh int func = pa->pa_function; 470605f564fSmsaitoh int last_dev, last_func, end_func; 471605f564fSmsaitoh int alias = 0; 472e54d5373Smsaitoh const struct pci_quirkdata *qd; 473e54d5373Smsaitoh bool force_hasextcnf = false; 474e54d5373Smsaitoh bool force_noextcnf = false; 475605f564fSmsaitoh int i, j; 476605f564fSmsaitoh 47799199cfcSjmcneill seg = acpimcfg_get_segment(pc, bus); 478605f564fSmsaitoh if (seg == NULL) 479605f564fSmsaitoh return 0; 480605f564fSmsaitoh 481605f564fSmsaitoh mb = &seg->ms_bus[bus - seg->ms_bus_start]; 482605f564fSmsaitoh tag = pci_make_tag(pc, bus, dev, func); 483605f564fSmsaitoh 484605f564fSmsaitoh /* Mark invalid between last probed device to probed device. */ 485605f564fSmsaitoh pci_decompose_tag(pc, mb->last_probed, NULL, &last_dev, &last_func); 486605f564fSmsaitoh if (dev != 0 || func != 0) { 487605f564fSmsaitoh for (i = last_dev; i <= dev; i++) { 488605f564fSmsaitoh end_func = (i == dev) ? func : 8; 489605f564fSmsaitoh for (j = last_func; j < end_func; j++) { 490605f564fSmsaitoh if (i == last_dev && j == last_func) 491605f564fSmsaitoh continue; 492da1fbcb7Sjmcneill PCIDEV_SET_INVALID(mb, i, j); 493605f564fSmsaitoh } 494605f564fSmsaitoh last_func = 0; 495605f564fSmsaitoh } 496605f564fSmsaitoh } 497605f564fSmsaitoh mb->last_probed = tag; 498605f564fSmsaitoh 499e54d5373Smsaitoh reg = pci_conf_read(pc, tag, PCI_ID_REG); 500e54d5373Smsaitoh qd = pci_lookup_quirkdata(PCI_VENDOR(reg), PCI_PRODUCT(reg)); 501e54d5373Smsaitoh if (qd != NULL && (qd->quirks & PCI_QUIRK_HASEXTCNF) != 0) 502e54d5373Smsaitoh force_hasextcnf = true; 503e54d5373Smsaitoh if (qd != NULL && (qd->quirks & PCI_QUIRK_NOEXTCNF) != 0) 504e54d5373Smsaitoh force_noextcnf = true; 505e54d5373Smsaitoh 506605f564fSmsaitoh /* Probe extended configuration space. */ 507e54d5373Smsaitoh if ((!force_hasextcnf) && ((force_noextcnf) || 508e54d5373Smsaitoh ((reg = pci_conf_read(pc, tag, PCI_CONF_SIZE)) == (pcireg_t)-1) 509e54d5373Smsaitoh || (reg == 0) 510e54d5373Smsaitoh || (alias = acpimcfg_ext_conf_is_aliased(pc, tag)))) { 511605f564fSmsaitoh aprint_debug_dev(acpi_sc->sc_dev, 512605f564fSmsaitoh "MCFG: %03d:%02d:%d: invalid config space " 513605f564fSmsaitoh "(cfg[0x%03x]=0x%08x, alias=%s)\n", bus, dev, func, 514605f564fSmsaitoh PCI_CONF_SIZE, reg, alias ? "true" : "false"); 515605f564fSmsaitoh EXTCONF_SET_INVALID(mb, dev, func); 516605f564fSmsaitoh } 517605f564fSmsaitoh 518da1fbcb7Sjmcneill aprint_debug_dev(acpi_sc->sc_dev, 519da1fbcb7Sjmcneill "MCFG: %03d:%02d:%d: Ok (cfg[0x%03x]=0x%08x extconf=%c)\n", 520da1fbcb7Sjmcneill bus, dev, func, PCI_CONF_SIZE, reg, 521da1fbcb7Sjmcneill EXTCONF_IS_VALID(mb, dev, func) ? 'Y' : 'N'); 522da1fbcb7Sjmcneill mb->valid_ndevs++; 523da1fbcb7Sjmcneill 524605f564fSmsaitoh return 0; 525605f564fSmsaitoh } 526605f564fSmsaitoh 527605f564fSmsaitoh static void 528605f564fSmsaitoh acpimcfg_scan_bus(struct pci_softc *sc, pci_chipset_tag_t pc, int bus) 529605f564fSmsaitoh { 530605f564fSmsaitoh static const int wildcard[PCICF_NLOCS] = { 531605f564fSmsaitoh PCICF_DEV_DEFAULT, PCICF_FUNCTION_DEFAULT 532605f564fSmsaitoh }; 533605f564fSmsaitoh 534605f564fSmsaitoh sc->sc_bus = bus; /* XXX */ 53552b17e17Sjmcneill sc->sc_pc = pc; 536605f564fSmsaitoh 537605f564fSmsaitoh pci_enumerate_bus(sc, wildcard, acpimcfg_device_probe, NULL); 538605f564fSmsaitoh } 539605f564fSmsaitoh 540605f564fSmsaitoh int 541605f564fSmsaitoh acpimcfg_map_bus(device_t self, pci_chipset_tag_t pc, int bus) 542605f564fSmsaitoh { 543605f564fSmsaitoh struct pci_softc *sc = device_private(self); 544605f564fSmsaitoh struct mcfg_segment *seg = NULL; 545605f564fSmsaitoh struct mcfg_bus *mb; 546605f564fSmsaitoh bus_space_handle_t bsh; 547605f564fSmsaitoh bus_addr_t baddr; 548f0929e69Shannken pcitag_t tag; 549f0929e69Shannken pcireg_t reg; 550f0929e69Shannken bool is_e7520_mch; 551605f564fSmsaitoh int boff; 552605f564fSmsaitoh int last_dev, last_func; 553605f564fSmsaitoh int i, j; 554605f564fSmsaitoh int error; 555605f564fSmsaitoh 556605f564fSmsaitoh if (!mcfg_inited) 557605f564fSmsaitoh return ENXIO; 558605f564fSmsaitoh 55999199cfcSjmcneill seg = acpimcfg_get_segment(pc, bus); 560605f564fSmsaitoh if (seg == NULL) 561605f564fSmsaitoh return ENOENT; 562605f564fSmsaitoh 563605f564fSmsaitoh boff = bus - seg->ms_bus_start; 564605f564fSmsaitoh if (seg->ms_bus[boff].valid_ndevs > 0) 565605f564fSmsaitoh return 0; 566605f564fSmsaitoh 567605f564fSmsaitoh mb = &seg->ms_bus[boff]; 568a266bf0eSjmcneill baddr = seg->ms_address + (bus * ACPIMCFG_SIZE_PER_BUS); 569605f564fSmsaitoh 570773e7c3eSjmcneill /* Map extended configuration space of all dev/func. */ 571605f564fSmsaitoh error = bus_space_map(seg->ms_bst, baddr, ACPIMCFG_SIZE_PER_BUS, 0, 572605f564fSmsaitoh &bsh); 573605f564fSmsaitoh if (error != 0) 574605f564fSmsaitoh return error; 575605f564fSmsaitoh for (i = 0; i < 32; i++) { 576605f564fSmsaitoh for (j = 0; j < 8; j++) { 577605f564fSmsaitoh error = bus_space_subregion(seg->ms_bst, bsh, 578605f564fSmsaitoh EXTCONF_OFFSET(i, j, 0), PCI_EXTCONF_SIZE, 579605f564fSmsaitoh &mb->bsh[i][j]); 580605f564fSmsaitoh if (error != 0) 581605f564fSmsaitoh break; 582605f564fSmsaitoh } 583605f564fSmsaitoh } 584605f564fSmsaitoh if (error != 0) 585605f564fSmsaitoh return error; 586605f564fSmsaitoh 587605f564fSmsaitoh aprint_debug("\n"); 588605f564fSmsaitoh 589605f564fSmsaitoh /* Probe extended configuration space of all devices. */ 590605f564fSmsaitoh memset(mb->valid_devs, 0xff, sizeof(mb->valid_devs)); 591da1fbcb7Sjmcneill memset(mb->valid_extconf, 0xff, sizeof(mb->valid_extconf)); 592605f564fSmsaitoh mb->valid_ndevs = 0; 593605f564fSmsaitoh mb->last_probed = pci_make_tag(pc, bus, 0, 0); 594605f564fSmsaitoh 595f0929e69Shannken /* 596f0929e69Shannken * On an Intel E7520 we have to temporarily disable 597f0929e69Shannken * Enhanced Config Access error detection and reporting 598f0929e69Shannken * by setting the appropriate error mask in HI_ERRMASK register. 599f0929e69Shannken * 600f0929e69Shannken * See "Intel E7520 Memory Controller Hub (MCH) Datasheet", 601f0929e69Shannken * Document 303006-002, pg. 82 602f0929e69Shannken */ 603f0929e69Shannken tag = pci_make_tag(pc, 0, 0, 1); 604f0929e69Shannken reg = pci_conf_read(pc, tag, PCI_ID_REG); 605f0929e69Shannken is_e7520_mch = (reg == 606f0929e69Shannken PCI_ID_CODE(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_E7525_MCHER)); 607f0929e69Shannken if (is_e7520_mch) { 608f0929e69Shannken reg = pci_conf_read(pc, tag, 0x54); 609f0929e69Shannken pci_conf_write(pc, tag, 0x54, reg | 0x20); 610f0929e69Shannken } 611f0929e69Shannken 612605f564fSmsaitoh acpimcfg_scan_bus(sc, pc, bus); 613605f564fSmsaitoh 614f0929e69Shannken if (is_e7520_mch) { 615f0929e69Shannken pci_conf_write(pc, tag, 0x54, reg); 616f0929e69Shannken } 617f0929e69Shannken 618da1fbcb7Sjmcneill /* Unmap configuration space of all dev/func. */ 619605f564fSmsaitoh bus_space_unmap(seg->ms_bst, bsh, ACPIMCFG_SIZE_PER_BUS); 620605f564fSmsaitoh memset(mb->bsh, 0, sizeof(mb->bsh)); 621605f564fSmsaitoh 622605f564fSmsaitoh if (mb->valid_ndevs == 0) { 623605f564fSmsaitoh aprint_debug_dev(acpi_sc->sc_dev, 624605f564fSmsaitoh "MCFG: bus %d: no valid devices.\n", bus); 625605f564fSmsaitoh memset(mb->valid_devs, 0, sizeof(mb->valid_devs)); 626605f564fSmsaitoh goto out; 627605f564fSmsaitoh } 628605f564fSmsaitoh 629605f564fSmsaitoh /* Mark invalid on remaining all devices. */ 630605f564fSmsaitoh pci_decompose_tag(pc, mb->last_probed, NULL, &last_dev, &last_func); 631605f564fSmsaitoh for (i = last_dev; i < 32; i++) { 632605f564fSmsaitoh for (j = last_func; j < 8; j++) { 633605f564fSmsaitoh if (i == last_dev && j == last_func) { 634605f564fSmsaitoh /* Don't mark invalid to last probed device. */ 635605f564fSmsaitoh continue; 636605f564fSmsaitoh } 637da1fbcb7Sjmcneill PCIDEV_SET_INVALID(mb, i, j); 638605f564fSmsaitoh } 639605f564fSmsaitoh last_func = 0; 640605f564fSmsaitoh } 641605f564fSmsaitoh 642da1fbcb7Sjmcneill /* Map configuration space per dev/func. */ 643605f564fSmsaitoh for (i = 0; i < 32; i++) { 644605f564fSmsaitoh for (j = 0; j < 8; j++) { 645da1fbcb7Sjmcneill if (!PCIDEV_IS_VALID(mb, i, j)) 646605f564fSmsaitoh continue; 647605f564fSmsaitoh error = bus_space_map(seg->ms_bst, 648605f564fSmsaitoh baddr + EXTCONF_OFFSET(i, j, 0), PCI_EXTCONF_SIZE, 649605f564fSmsaitoh 0, &mb->bsh[i][j]); 650605f564fSmsaitoh if (error != 0) { 651605f564fSmsaitoh /* Unmap all handles when map failed. */ 652605f564fSmsaitoh do { 653605f564fSmsaitoh while (--j >= 0) { 654da1fbcb7Sjmcneill if (!PCIDEV_IS_VALID(mb, i, j)) 655605f564fSmsaitoh continue; 656605f564fSmsaitoh bus_space_unmap(seg->ms_bst, 657605f564fSmsaitoh mb->bsh[i][j], 658605f564fSmsaitoh PCI_EXTCONF_SIZE); 659605f564fSmsaitoh } 660605f564fSmsaitoh j = 8; 661605f564fSmsaitoh } while (--i >= 0); 662605f564fSmsaitoh memset(mb->valid_devs, 0, 663605f564fSmsaitoh sizeof(mb->valid_devs)); 664605f564fSmsaitoh goto out; 665605f564fSmsaitoh } 666605f564fSmsaitoh } 667605f564fSmsaitoh } 668605f564fSmsaitoh 669605f564fSmsaitoh aprint_debug_dev(acpi_sc->sc_dev, "MCFG: bus %d: valid devices\n", bus); 670605f564fSmsaitoh for (i = 0; i < 32; i++) { 671605f564fSmsaitoh for (j = 0; j < 8; j++) { 672da1fbcb7Sjmcneill if (PCIDEV_IS_VALID(mb, i, j)) { 673605f564fSmsaitoh aprint_debug_dev(acpi_sc->sc_dev, 674605f564fSmsaitoh "MCFG: %03d:%02d:%d\n", bus, i, j); 675605f564fSmsaitoh } 676605f564fSmsaitoh } 677605f564fSmsaitoh } 678605f564fSmsaitoh 679605f564fSmsaitoh error = 0; 680605f564fSmsaitoh out: 681605f564fSmsaitoh 682605f564fSmsaitoh return error; 683605f564fSmsaitoh } 684605f564fSmsaitoh 6851099f047Sjmcneill #ifdef PCI_RESOURCE 686bd4d5bc0Sjmcneill ACPI_STATUS 687aaf945cfSjmcneill acpimcfg_configure_bus_cb(ACPI_RESOURCE *res, void *ctx) 688aaf945cfSjmcneill { 6891099f047Sjmcneill struct pci_resource_info *pciinfo = ctx; 690ca8ce3aeSthorpej bus_addr_t addr; 691ca8ce3aeSthorpej bus_size_t size; 6921099f047Sjmcneill int type; 693aaf945cfSjmcneill 6944f305a76Sjmcneill if (res->Type != ACPI_RESOURCE_TYPE_ADDRESS16 && 6954f305a76Sjmcneill res->Type != ACPI_RESOURCE_TYPE_ADDRESS32 && 696aaf945cfSjmcneill res->Type != ACPI_RESOURCE_TYPE_ADDRESS64) 697aaf945cfSjmcneill return AE_OK; 698aaf945cfSjmcneill 699aaf945cfSjmcneill if (res->Data.Address.ProducerConsumer != ACPI_PRODUCER) 700aaf945cfSjmcneill return AE_OK; 701aaf945cfSjmcneill 702aaf945cfSjmcneill if (res->Data.Address.ResourceType != ACPI_MEMORY_RANGE && 7031099f047Sjmcneill res->Data.Address.ResourceType != ACPI_IO_RANGE && 7041099f047Sjmcneill res->Data.Address.ResourceType != ACPI_BUS_NUMBER_RANGE) 705aaf945cfSjmcneill return AE_OK; 706aaf945cfSjmcneill 707aaf945cfSjmcneill if (res->Data.Address.ResourceType == ACPI_MEMORY_RANGE && 708aaf945cfSjmcneill res->Data.Address.Info.Mem.Caching == ACPI_PREFETCHABLE_MEMORY) { 7091099f047Sjmcneill type = PCI_RANGE_PMEM; 710aaf945cfSjmcneill } else if (res->Data.Address.ResourceType == ACPI_MEMORY_RANGE && 711aaf945cfSjmcneill res->Data.Address.Info.Mem.Caching != ACPI_PREFETCHABLE_MEMORY) { 7121099f047Sjmcneill if (res->Type == ACPI_RESOURCE_TYPE_ADDRESS64) { 7131099f047Sjmcneill type = PCI_RANGE_PMEM; 7141099f047Sjmcneill } else { 7151099f047Sjmcneill type = PCI_RANGE_MEM; 7161099f047Sjmcneill } 7171099f047Sjmcneill } else if (res->Data.Address.ResourceType == ACPI_BUS_NUMBER_RANGE) { 7181099f047Sjmcneill type = PCI_RANGE_BUS; 7193d5831c7Sjmcneill } else { 7203d5831c7Sjmcneill KASSERT(res->Data.Address.ResourceType == ACPI_IO_RANGE); 7211099f047Sjmcneill type = PCI_RANGE_IO; 722aaf945cfSjmcneill } 723aaf945cfSjmcneill 724aaf945cfSjmcneill switch (res->Type) { 725aaf945cfSjmcneill case ACPI_RESOURCE_TYPE_ADDRESS16: 726aaf945cfSjmcneill aprint_debug( 727aaf945cfSjmcneill "MCFG: range 0x%04" PRIx16 " size %#" PRIx16 " (16-bit %s)\n", 728aaf945cfSjmcneill res->Data.Address16.Address.Minimum, 729aaf945cfSjmcneill res->Data.Address16.Address.AddressLength, 7301099f047Sjmcneill pci_resource_typename(type)); 731ca8ce3aeSthorpej addr = res->Data.Address16.Address.Minimum; 732ca8ce3aeSthorpej size = res->Data.Address16.Address.AddressLength; 733aaf945cfSjmcneill break; 734aaf945cfSjmcneill case ACPI_RESOURCE_TYPE_ADDRESS32: 735aaf945cfSjmcneill aprint_debug( 736aaf945cfSjmcneill "MCFG: range 0x%08" PRIx32 " size %#" PRIx32 " (32-bit %s)\n", 737aaf945cfSjmcneill res->Data.Address32.Address.Minimum, 738aaf945cfSjmcneill res->Data.Address32.Address.AddressLength, 7391099f047Sjmcneill pci_resource_typename(type)); 740ca8ce3aeSthorpej addr = res->Data.Address32.Address.Minimum; 741ca8ce3aeSthorpej size = res->Data.Address32.Address.AddressLength; 742aaf945cfSjmcneill break; 743aaf945cfSjmcneill case ACPI_RESOURCE_TYPE_ADDRESS64: 744aaf945cfSjmcneill aprint_debug( 745aaf945cfSjmcneill "MCFG: range 0x%016" PRIx64 " size %#" PRIx64 " (64-bit %s)\n", 746aaf945cfSjmcneill res->Data.Address64.Address.Minimum, 747aaf945cfSjmcneill res->Data.Address64.Address.AddressLength, 7481099f047Sjmcneill pci_resource_typename(type)); 749ca8ce3aeSthorpej addr = res->Data.Address64.Address.Minimum; 750ca8ce3aeSthorpej size = res->Data.Address64.Address.AddressLength; 751aaf945cfSjmcneill break; 752ca8ce3aeSthorpej 753ca8ce3aeSthorpej default: 754ca8ce3aeSthorpej return AE_OK; 755aaf945cfSjmcneill } 756aaf945cfSjmcneill 757*5c830e3fSmlelstv if (size > 0) { 7581099f047Sjmcneill pciinfo->ranges[type].start = addr; 7591099f047Sjmcneill pciinfo->ranges[type].end = addr + size - 1; 760*5c830e3fSmlelstv } 761ca8ce3aeSthorpej 7621099f047Sjmcneill return AE_OK; 763aaf945cfSjmcneill } 764aaf945cfSjmcneill 765aaf945cfSjmcneill int 766aaf945cfSjmcneill acpimcfg_configure_bus(device_t self, pci_chipset_tag_t pc, ACPI_HANDLE handle, 7671099f047Sjmcneill int bus, bool mapcfgspace) 768aaf945cfSjmcneill { 7691099f047Sjmcneill struct pci_resource_info pciinfo; 770aaf945cfSjmcneill struct mcfg_segment *seg; 771aaf945cfSjmcneill struct mcfg_bus *mb; 772aaf945cfSjmcneill bus_space_handle_t bsh[256]; 773aaf945cfSjmcneill bool bsh_mapped[256]; 7741099f047Sjmcneill int error, boff, b, d, f, endbus; 775aaf945cfSjmcneill bus_addr_t baddr; 776aaf945cfSjmcneill ACPI_STATUS rv; 777aaf945cfSjmcneill 7781099f047Sjmcneill if (mapcfgspace) { 77999199cfcSjmcneill seg = acpimcfg_get_segment(pc, bus); 780*5c830e3fSmlelstv aprint_debug_dev(acpi_sc->sc_dev, "MCFG: Bus=%d, Seg=%p\n", 781*5c830e3fSmlelstv bus, seg); 7821099f047Sjmcneill if (seg == NULL) { 783aaf945cfSjmcneill return ENOENT; 7841099f047Sjmcneill } 7851099f047Sjmcneill endbus = seg->ms_bus_end; 786ca8ce3aeSthorpej 787aaf945cfSjmcneill /* 7881099f047Sjmcneill * Map config space for all possible busses and mark them valid 7891099f047Sjmcneill * during configuration so pci_configure_bus can access them 7901099f047Sjmcneill * through our chipset tag with acpimcfg_conf_read/write below. 791aaf945cfSjmcneill */ 792aaf945cfSjmcneill memset(bsh_mapped, 0, sizeof(bsh_mapped)); 793aaf945cfSjmcneill for (b = seg->ms_bus_start; b <= seg->ms_bus_end; b++) { 794aaf945cfSjmcneill boff = b - seg->ms_bus_start; 795aaf945cfSjmcneill mb = &seg->ms_bus[boff]; 796a266bf0eSjmcneill baddr = seg->ms_address + (b * ACPIMCFG_SIZE_PER_BUS); 797aaf945cfSjmcneill 798773e7c3eSjmcneill /* Map extended configuration space of all dev/func. */ 7991099f047Sjmcneill error = bus_space_map(seg->ms_bst, baddr, 8001099f047Sjmcneill ACPIMCFG_SIZE_PER_BUS, 0, &bsh[b]); 8011099f047Sjmcneill if (error != 0) { 802aaf945cfSjmcneill goto cleanup; 8031099f047Sjmcneill } 804aaf945cfSjmcneill bsh_mapped[b] = true; 805aaf945cfSjmcneill for (d = 0; d < 32; d++) { 806aaf945cfSjmcneill for (f = 0; f < 8; f++) { 8071099f047Sjmcneill error = bus_space_subregion(seg->ms_bst, 8081099f047Sjmcneill bsh[b], EXTCONF_OFFSET(d, f, 0), 8091099f047Sjmcneill PCI_EXTCONF_SIZE, &mb->bsh[d][f]); 8101099f047Sjmcneill if (error != 0) { 811aaf945cfSjmcneill break; 812aaf945cfSjmcneill } 813aaf945cfSjmcneill } 8141099f047Sjmcneill } 8151099f047Sjmcneill if (error != 0) { 816aaf945cfSjmcneill goto cleanup; 8171099f047Sjmcneill } 818aaf945cfSjmcneill 819aaf945cfSjmcneill memset(mb->valid_devs, 0xff, sizeof(mb->valid_devs)); 820aaf945cfSjmcneill } 8211099f047Sjmcneill } else { 8221099f047Sjmcneill endbus = 255; 8231099f047Sjmcneill } 824aaf945cfSjmcneill 8251099f047Sjmcneill memset(&pciinfo, 0, sizeof(pciinfo)); 8261099f047Sjmcneill pciinfo.pc = pc; 8271099f047Sjmcneill pciinfo.ranges[PCI_RANGE_BUS].start = bus; 8281099f047Sjmcneill pciinfo.ranges[PCI_RANGE_BUS].end = endbus; 829ca8ce3aeSthorpej rv = AcpiWalkResources(handle, "_CRS", acpimcfg_configure_bus_cb, 8301099f047Sjmcneill &pciinfo); 831aaf945cfSjmcneill if (ACPI_FAILURE(rv)) { 832*5c830e3fSmlelstv aprint_debug_dev(acpi_sc->sc_dev, "MCFG: Walk _CRS: %ld\n", 833*5c830e3fSmlelstv (long)rv); 834aaf945cfSjmcneill error = ENXIO; 835aaf945cfSjmcneill goto cleanup; 836aaf945cfSjmcneill } 8371099f047Sjmcneill error = 0; 838aaf945cfSjmcneill 8391099f047Sjmcneill pci_resource_init(&pciinfo); 840aaf945cfSjmcneill 841aaf945cfSjmcneill cleanup: 8421099f047Sjmcneill if (mapcfgspace) { 843aaf945cfSjmcneill /* 8441099f047Sjmcneill * Unmap config space for the segment's busses. Valid devices 8451099f047Sjmcneill * will be re-mapped later on by acpimcfg_map_bus. 846aaf945cfSjmcneill */ 847aaf945cfSjmcneill for (b = seg->ms_bus_start; b <= seg->ms_bus_end; b++) { 848aaf945cfSjmcneill boff = b - seg->ms_bus_start; 849aaf945cfSjmcneill mb = &seg->ms_bus[boff]; 850aaf945cfSjmcneill memset(mb->valid_devs, 0, sizeof(mb->valid_devs)); 851aaf945cfSjmcneill 8521099f047Sjmcneill if (bsh_mapped[b]) { 8531099f047Sjmcneill bus_space_unmap(seg->ms_bst, bsh[b], 8541099f047Sjmcneill ACPIMCFG_SIZE_PER_BUS); 855aaf945cfSjmcneill } 8561099f047Sjmcneill } 8571099f047Sjmcneill } 858aaf945cfSjmcneill 859aaf945cfSjmcneill return error; 860aaf945cfSjmcneill } 861aaf945cfSjmcneill #else 862aaf945cfSjmcneill int 863aaf945cfSjmcneill acpimcfg_configure_bus(device_t self, pci_chipset_tag_t pc, ACPI_HANDLE handle, 8641099f047Sjmcneill int bus, bool mapcfgspace) 865aaf945cfSjmcneill { 866aaf945cfSjmcneill return ENXIO; 867aaf945cfSjmcneill } 868aaf945cfSjmcneill #endif 869aaf945cfSjmcneill 870605f564fSmsaitoh int 871605f564fSmsaitoh acpimcfg_conf_read(pci_chipset_tag_t pc, pcitag_t tag, int reg, pcireg_t *data) 872605f564fSmsaitoh { 873605f564fSmsaitoh struct mcfg_segment *seg = NULL; 874605f564fSmsaitoh struct mcfg_bus *mb; 875605f564fSmsaitoh int bus, dev, func; 876605f564fSmsaitoh 877605f564fSmsaitoh KASSERT(reg < PCI_EXTCONF_SIZE); 878605f564fSmsaitoh KASSERT((reg & 3) == 0); 879605f564fSmsaitoh 880605f564fSmsaitoh if (!mcfg_inited) { 881605f564fSmsaitoh *data = -1; 882605f564fSmsaitoh return ENXIO; 883605f564fSmsaitoh } 884605f564fSmsaitoh 885605f564fSmsaitoh pci_decompose_tag(pc, tag, &bus, &dev, &func); 886605f564fSmsaitoh 88799199cfcSjmcneill seg = acpimcfg_get_segment(pc, bus); 888605f564fSmsaitoh if (seg == NULL) { 889605f564fSmsaitoh *data = -1; 890605f564fSmsaitoh return ERANGE; 891605f564fSmsaitoh } 892605f564fSmsaitoh 893605f564fSmsaitoh mb = &seg->ms_bus[bus - seg->ms_bus_start]; 894da1fbcb7Sjmcneill if (!PCIDEV_IS_VALID(mb, dev, func)) { 895da1fbcb7Sjmcneill *data = -1; 896da1fbcb7Sjmcneill return EINVAL; 897da1fbcb7Sjmcneill } 898da1fbcb7Sjmcneill if (!EXTCONF_IS_VALID(mb, dev, func) && reg >= PCI_CONF_SIZE) { 899605f564fSmsaitoh *data = -1; 900605f564fSmsaitoh return EINVAL; 901605f564fSmsaitoh } 902605f564fSmsaitoh 903605f564fSmsaitoh *data = mcfg_ops->ao_read(seg->ms_bst, mb->bsh[dev][func], reg); 904605f564fSmsaitoh return 0; 905605f564fSmsaitoh } 906605f564fSmsaitoh 907605f564fSmsaitoh int 908605f564fSmsaitoh acpimcfg_conf_write(pci_chipset_tag_t pc, pcitag_t tag, int reg, pcireg_t data) 909605f564fSmsaitoh { 910605f564fSmsaitoh struct mcfg_segment *seg = NULL; 911605f564fSmsaitoh struct mcfg_bus *mb; 912605f564fSmsaitoh int bus, dev, func; 913605f564fSmsaitoh 914605f564fSmsaitoh KASSERT(reg < PCI_EXTCONF_SIZE); 915605f564fSmsaitoh KASSERT((reg & 3) == 0); 916605f564fSmsaitoh 917605f564fSmsaitoh if (!mcfg_inited) 918605f564fSmsaitoh return ENXIO; 919605f564fSmsaitoh 920605f564fSmsaitoh pci_decompose_tag(pc, tag, &bus, &dev, &func); 921605f564fSmsaitoh 92299199cfcSjmcneill seg = acpimcfg_get_segment(pc, bus); 923605f564fSmsaitoh if (seg == NULL) 924605f564fSmsaitoh return ERANGE; 925605f564fSmsaitoh 926605f564fSmsaitoh mb = &seg->ms_bus[bus - seg->ms_bus_start]; 927da1fbcb7Sjmcneill if (!PCIDEV_IS_VALID(mb, dev, func)) 928da1fbcb7Sjmcneill return EINVAL; 929da1fbcb7Sjmcneill if (!EXTCONF_IS_VALID(mb, dev, func) && reg >= PCI_CONF_SIZE) 930605f564fSmsaitoh return EINVAL; 931605f564fSmsaitoh 932605f564fSmsaitoh mcfg_ops->ao_write(seg->ms_bst, mb->bsh[dev][func], reg, data); 933605f564fSmsaitoh return 0; 934605f564fSmsaitoh } 935c57f707cSjmcneill 936c57f707cSjmcneill bool 937c57f707cSjmcneill acpimcfg_conf_valid(pci_chipset_tag_t pc, pcitag_t tag, int reg) 938c57f707cSjmcneill { 939c57f707cSjmcneill struct mcfg_segment *seg = NULL; 940c57f707cSjmcneill struct mcfg_bus *mb; 941c57f707cSjmcneill int bus, dev, func; 942c57f707cSjmcneill 943c57f707cSjmcneill if (!mcfg_inited) 944c57f707cSjmcneill return false; 945c57f707cSjmcneill 946c57f707cSjmcneill pci_decompose_tag(pc, tag, &bus, &dev, &func); 947c57f707cSjmcneill 948c57f707cSjmcneill seg = acpimcfg_get_segment(pc, bus); 949c57f707cSjmcneill if (seg == NULL) 950c57f707cSjmcneill return false; 951c57f707cSjmcneill 952c57f707cSjmcneill mb = &seg->ms_bus[bus - seg->ms_bus_start]; 953c57f707cSjmcneill if (!PCIDEV_IS_VALID(mb, dev, func)) 954c57f707cSjmcneill return false; 955c57f707cSjmcneill if (!EXTCONF_IS_VALID(mb, dev, func) && reg >= PCI_CONF_SIZE) 956c57f707cSjmcneill return false; 957c57f707cSjmcneill 958c57f707cSjmcneill return true; 959c57f707cSjmcneill } 960