162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * mmconfig-shared.c - Low-level direct PCI config space access via 462306a36Sopenharmony_ci * MMCONFIG - common code between i386 and x86-64. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This code does: 762306a36Sopenharmony_ci * - known chipset handling 862306a36Sopenharmony_ci * - ACPI decoding and validation 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Per-architecture code takes care of the mappings and accesses 1162306a36Sopenharmony_ci * themselves. 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/acpi.h> 1562306a36Sopenharmony_ci#include <linux/efi.h> 1662306a36Sopenharmony_ci#include <linux/pci.h> 1762306a36Sopenharmony_ci#include <linux/init.h> 1862306a36Sopenharmony_ci#include <linux/bitmap.h> 1962306a36Sopenharmony_ci#include <linux/dmi.h> 2062306a36Sopenharmony_ci#include <linux/slab.h> 2162306a36Sopenharmony_ci#include <linux/mutex.h> 2262306a36Sopenharmony_ci#include <linux/rculist.h> 2362306a36Sopenharmony_ci#include <asm/e820/api.h> 2462306a36Sopenharmony_ci#include <asm/pci_x86.h> 2562306a36Sopenharmony_ci#include <asm/acpi.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define PREFIX "PCI: " 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* Indicate if the mmcfg resources have been placed into the resource table. */ 3062306a36Sopenharmony_cistatic bool pci_mmcfg_running_state; 3162306a36Sopenharmony_cistatic bool pci_mmcfg_arch_init_failed; 3262306a36Sopenharmony_cistatic DEFINE_MUTEX(pci_mmcfg_lock); 3362306a36Sopenharmony_ci#define pci_mmcfg_lock_held() lock_is_held(&(pci_mmcfg_lock).dep_map) 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ciLIST_HEAD(pci_mmcfg_list); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistatic void __init pci_mmconfig_remove(struct pci_mmcfg_region *cfg) 3862306a36Sopenharmony_ci{ 3962306a36Sopenharmony_ci if (cfg->res.parent) 4062306a36Sopenharmony_ci release_resource(&cfg->res); 4162306a36Sopenharmony_ci list_del(&cfg->list); 4262306a36Sopenharmony_ci kfree(cfg); 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic void __init free_all_mmcfg(void) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci struct pci_mmcfg_region *cfg, *tmp; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci pci_mmcfg_arch_free(); 5062306a36Sopenharmony_ci list_for_each_entry_safe(cfg, tmp, &pci_mmcfg_list, list) 5162306a36Sopenharmony_ci pci_mmconfig_remove(cfg); 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic void list_add_sorted(struct pci_mmcfg_region *new) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci struct pci_mmcfg_region *cfg; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci /* keep list sorted by segment and starting bus number */ 5962306a36Sopenharmony_ci list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list, pci_mmcfg_lock_held()) { 6062306a36Sopenharmony_ci if (cfg->segment > new->segment || 6162306a36Sopenharmony_ci (cfg->segment == new->segment && 6262306a36Sopenharmony_ci cfg->start_bus >= new->start_bus)) { 6362306a36Sopenharmony_ci list_add_tail_rcu(&new->list, &cfg->list); 6462306a36Sopenharmony_ci return; 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci } 6762306a36Sopenharmony_ci list_add_tail_rcu(&new->list, &pci_mmcfg_list); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start, 7162306a36Sopenharmony_ci int end, u64 addr) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct pci_mmcfg_region *new; 7462306a36Sopenharmony_ci struct resource *res; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (addr == 0) 7762306a36Sopenharmony_ci return NULL; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci new = kzalloc(sizeof(*new), GFP_KERNEL); 8062306a36Sopenharmony_ci if (!new) 8162306a36Sopenharmony_ci return NULL; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci new->address = addr; 8462306a36Sopenharmony_ci new->segment = segment; 8562306a36Sopenharmony_ci new->start_bus = start; 8662306a36Sopenharmony_ci new->end_bus = end; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci res = &new->res; 8962306a36Sopenharmony_ci res->start = addr + PCI_MMCFG_BUS_OFFSET(start); 9062306a36Sopenharmony_ci res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1; 9162306a36Sopenharmony_ci res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; 9262306a36Sopenharmony_ci snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN, 9362306a36Sopenharmony_ci "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end); 9462306a36Sopenharmony_ci res->name = new->name; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci return new; 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistruct pci_mmcfg_region *__init pci_mmconfig_add(int segment, int start, 10062306a36Sopenharmony_ci int end, u64 addr) 10162306a36Sopenharmony_ci{ 10262306a36Sopenharmony_ci struct pci_mmcfg_region *new; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci new = pci_mmconfig_alloc(segment, start, end, addr); 10562306a36Sopenharmony_ci if (new) { 10662306a36Sopenharmony_ci mutex_lock(&pci_mmcfg_lock); 10762306a36Sopenharmony_ci list_add_sorted(new); 10862306a36Sopenharmony_ci mutex_unlock(&pci_mmcfg_lock); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci pr_info(PREFIX 11162306a36Sopenharmony_ci "MMCONFIG for domain %04x [bus %02x-%02x] at %pR " 11262306a36Sopenharmony_ci "(base %#lx)\n", 11362306a36Sopenharmony_ci segment, start, end, &new->res, (unsigned long)addr); 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return new; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistruct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci struct pci_mmcfg_region *cfg; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list, pci_mmcfg_lock_held()) 12462306a36Sopenharmony_ci if (cfg->segment == segment && 12562306a36Sopenharmony_ci cfg->start_bus <= bus && bus <= cfg->end_bus) 12662306a36Sopenharmony_ci return cfg; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return NULL; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic const char *__init pci_mmcfg_e7520(void) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci u32 win; 13462306a36Sopenharmony_ci raw_pci_ops->read(0, 0, PCI_DEVFN(0, 0), 0xce, 2, &win); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci win = win & 0xf000; 13762306a36Sopenharmony_ci if (win == 0x0000 || win == 0xf000) 13862306a36Sopenharmony_ci return NULL; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (pci_mmconfig_add(0, 0, 255, win << 16) == NULL) 14162306a36Sopenharmony_ci return NULL; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci return "Intel Corporation E7520 Memory Controller Hub"; 14462306a36Sopenharmony_ci} 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic const char *__init pci_mmcfg_intel_945(void) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci u32 pciexbar, mask = 0, len = 0; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci raw_pci_ops->read(0, 0, PCI_DEVFN(0, 0), 0x48, 4, &pciexbar); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* Enable bit */ 15362306a36Sopenharmony_ci if (!(pciexbar & 1)) 15462306a36Sopenharmony_ci return NULL; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* Size bits */ 15762306a36Sopenharmony_ci switch ((pciexbar >> 1) & 3) { 15862306a36Sopenharmony_ci case 0: 15962306a36Sopenharmony_ci mask = 0xf0000000U; 16062306a36Sopenharmony_ci len = 0x10000000U; 16162306a36Sopenharmony_ci break; 16262306a36Sopenharmony_ci case 1: 16362306a36Sopenharmony_ci mask = 0xf8000000U; 16462306a36Sopenharmony_ci len = 0x08000000U; 16562306a36Sopenharmony_ci break; 16662306a36Sopenharmony_ci case 2: 16762306a36Sopenharmony_ci mask = 0xfc000000U; 16862306a36Sopenharmony_ci len = 0x04000000U; 16962306a36Sopenharmony_ci break; 17062306a36Sopenharmony_ci default: 17162306a36Sopenharmony_ci return NULL; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* Errata #2, things break when not aligned on a 256Mb boundary */ 17562306a36Sopenharmony_ci /* Can only happen in 64M/128M mode */ 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci if ((pciexbar & mask) & 0x0fffffffU) 17862306a36Sopenharmony_ci return NULL; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* Don't hit the APIC registers and their friends */ 18162306a36Sopenharmony_ci if ((pciexbar & mask) >= 0xf0000000U) 18262306a36Sopenharmony_ci return NULL; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if (pci_mmconfig_add(0, 0, (len >> 20) - 1, pciexbar & mask) == NULL) 18562306a36Sopenharmony_ci return NULL; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return "Intel Corporation 945G/GZ/P/PL Express Memory Controller Hub"; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic const char *__init pci_mmcfg_amd_fam10h(void) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci u32 low, high, address; 19362306a36Sopenharmony_ci u64 base, msr; 19462306a36Sopenharmony_ci int i; 19562306a36Sopenharmony_ci unsigned segnbits = 0, busnbits, end_bus; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (!(pci_probe & PCI_CHECK_ENABLE_AMD_MMCONF)) 19862306a36Sopenharmony_ci return NULL; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci address = MSR_FAM10H_MMIO_CONF_BASE; 20162306a36Sopenharmony_ci if (rdmsr_safe(address, &low, &high)) 20262306a36Sopenharmony_ci return NULL; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci msr = high; 20562306a36Sopenharmony_ci msr <<= 32; 20662306a36Sopenharmony_ci msr |= low; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci /* mmconfig is not enable */ 20962306a36Sopenharmony_ci if (!(msr & FAM10H_MMIO_CONF_ENABLE)) 21062306a36Sopenharmony_ci return NULL; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci base = msr & (FAM10H_MMIO_CONF_BASE_MASK<<FAM10H_MMIO_CONF_BASE_SHIFT); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci busnbits = (msr >> FAM10H_MMIO_CONF_BUSRANGE_SHIFT) & 21562306a36Sopenharmony_ci FAM10H_MMIO_CONF_BUSRANGE_MASK; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* 21862306a36Sopenharmony_ci * only handle bus 0 ? 21962306a36Sopenharmony_ci * need to skip it 22062306a36Sopenharmony_ci */ 22162306a36Sopenharmony_ci if (!busnbits) 22262306a36Sopenharmony_ci return NULL; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci if (busnbits > 8) { 22562306a36Sopenharmony_ci segnbits = busnbits - 8; 22662306a36Sopenharmony_ci busnbits = 8; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci end_bus = (1 << busnbits) - 1; 23062306a36Sopenharmony_ci for (i = 0; i < (1 << segnbits); i++) 23162306a36Sopenharmony_ci if (pci_mmconfig_add(i, 0, end_bus, 23262306a36Sopenharmony_ci base + (1<<28) * i) == NULL) { 23362306a36Sopenharmony_ci free_all_mmcfg(); 23462306a36Sopenharmony_ci return NULL; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci return "AMD Family 10h NB"; 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic bool __initdata mcp55_checked; 24162306a36Sopenharmony_cistatic const char *__init pci_mmcfg_nvidia_mcp55(void) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci int bus; 24462306a36Sopenharmony_ci int mcp55_mmconf_found = 0; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci static const u32 extcfg_regnum __initconst = 0x90; 24762306a36Sopenharmony_ci static const u32 extcfg_regsize __initconst = 4; 24862306a36Sopenharmony_ci static const u32 extcfg_enable_mask __initconst = 1 << 31; 24962306a36Sopenharmony_ci static const u32 extcfg_start_mask __initconst = 0xff << 16; 25062306a36Sopenharmony_ci static const int extcfg_start_shift __initconst = 16; 25162306a36Sopenharmony_ci static const u32 extcfg_size_mask __initconst = 0x3 << 28; 25262306a36Sopenharmony_ci static const int extcfg_size_shift __initconst = 28; 25362306a36Sopenharmony_ci static const int extcfg_sizebus[] __initconst = { 25462306a36Sopenharmony_ci 0x100, 0x80, 0x40, 0x20 25562306a36Sopenharmony_ci }; 25662306a36Sopenharmony_ci static const u32 extcfg_base_mask[] __initconst = { 25762306a36Sopenharmony_ci 0x7ff8, 0x7ffc, 0x7ffe, 0x7fff 25862306a36Sopenharmony_ci }; 25962306a36Sopenharmony_ci static const int extcfg_base_lshift __initconst = 25; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci /* 26262306a36Sopenharmony_ci * do check if amd fam10h already took over 26362306a36Sopenharmony_ci */ 26462306a36Sopenharmony_ci if (!acpi_disabled || !list_empty(&pci_mmcfg_list) || mcp55_checked) 26562306a36Sopenharmony_ci return NULL; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci mcp55_checked = true; 26862306a36Sopenharmony_ci for (bus = 0; bus < 256; bus++) { 26962306a36Sopenharmony_ci u64 base; 27062306a36Sopenharmony_ci u32 l, extcfg; 27162306a36Sopenharmony_ci u16 vendor, device; 27262306a36Sopenharmony_ci int start, size_index, end; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci raw_pci_ops->read(0, bus, PCI_DEVFN(0, 0), 0, 4, &l); 27562306a36Sopenharmony_ci vendor = l & 0xffff; 27662306a36Sopenharmony_ci device = (l >> 16) & 0xffff; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (PCI_VENDOR_ID_NVIDIA != vendor || 0x0369 != device) 27962306a36Sopenharmony_ci continue; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci raw_pci_ops->read(0, bus, PCI_DEVFN(0, 0), extcfg_regnum, 28262306a36Sopenharmony_ci extcfg_regsize, &extcfg); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci if (!(extcfg & extcfg_enable_mask)) 28562306a36Sopenharmony_ci continue; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci size_index = (extcfg & extcfg_size_mask) >> extcfg_size_shift; 28862306a36Sopenharmony_ci base = extcfg & extcfg_base_mask[size_index]; 28962306a36Sopenharmony_ci /* base could > 4G */ 29062306a36Sopenharmony_ci base <<= extcfg_base_lshift; 29162306a36Sopenharmony_ci start = (extcfg & extcfg_start_mask) >> extcfg_start_shift; 29262306a36Sopenharmony_ci end = start + extcfg_sizebus[size_index] - 1; 29362306a36Sopenharmony_ci if (pci_mmconfig_add(0, start, end, base) == NULL) 29462306a36Sopenharmony_ci continue; 29562306a36Sopenharmony_ci mcp55_mmconf_found++; 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci if (!mcp55_mmconf_found) 29962306a36Sopenharmony_ci return NULL; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci return "nVidia MCP55"; 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistruct pci_mmcfg_hostbridge_probe { 30562306a36Sopenharmony_ci u32 bus; 30662306a36Sopenharmony_ci u32 devfn; 30762306a36Sopenharmony_ci u32 vendor; 30862306a36Sopenharmony_ci u32 device; 30962306a36Sopenharmony_ci const char *(*probe)(void); 31062306a36Sopenharmony_ci}; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic const struct pci_mmcfg_hostbridge_probe pci_mmcfg_probes[] __initconst = { 31362306a36Sopenharmony_ci { 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID_INTEL, 31462306a36Sopenharmony_ci PCI_DEVICE_ID_INTEL_E7520_MCH, pci_mmcfg_e7520 }, 31562306a36Sopenharmony_ci { 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID_INTEL, 31662306a36Sopenharmony_ci PCI_DEVICE_ID_INTEL_82945G_HB, pci_mmcfg_intel_945 }, 31762306a36Sopenharmony_ci { 0, PCI_DEVFN(0x18, 0), PCI_VENDOR_ID_AMD, 31862306a36Sopenharmony_ci 0x1200, pci_mmcfg_amd_fam10h }, 31962306a36Sopenharmony_ci { 0xff, PCI_DEVFN(0, 0), PCI_VENDOR_ID_AMD, 32062306a36Sopenharmony_ci 0x1200, pci_mmcfg_amd_fam10h }, 32162306a36Sopenharmony_ci { 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID_NVIDIA, 32262306a36Sopenharmony_ci 0x0369, pci_mmcfg_nvidia_mcp55 }, 32362306a36Sopenharmony_ci}; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic void __init pci_mmcfg_check_end_bus_number(void) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci struct pci_mmcfg_region *cfg, *cfgx; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci /* Fixup overlaps */ 33062306a36Sopenharmony_ci list_for_each_entry(cfg, &pci_mmcfg_list, list) { 33162306a36Sopenharmony_ci if (cfg->end_bus < cfg->start_bus) 33262306a36Sopenharmony_ci cfg->end_bus = 255; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci /* Don't access the list head ! */ 33562306a36Sopenharmony_ci if (cfg->list.next == &pci_mmcfg_list) 33662306a36Sopenharmony_ci break; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci cfgx = list_entry(cfg->list.next, typeof(*cfg), list); 33962306a36Sopenharmony_ci if (cfg->end_bus >= cfgx->start_bus) 34062306a36Sopenharmony_ci cfg->end_bus = cfgx->start_bus - 1; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic int __init pci_mmcfg_check_hostbridge(void) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci u32 l; 34762306a36Sopenharmony_ci u32 bus, devfn; 34862306a36Sopenharmony_ci u16 vendor, device; 34962306a36Sopenharmony_ci int i; 35062306a36Sopenharmony_ci const char *name; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if (!raw_pci_ops) 35362306a36Sopenharmony_ci return 0; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci free_all_mmcfg(); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(pci_mmcfg_probes); i++) { 35862306a36Sopenharmony_ci bus = pci_mmcfg_probes[i].bus; 35962306a36Sopenharmony_ci devfn = pci_mmcfg_probes[i].devfn; 36062306a36Sopenharmony_ci raw_pci_ops->read(0, bus, devfn, 0, 4, &l); 36162306a36Sopenharmony_ci vendor = l & 0xffff; 36262306a36Sopenharmony_ci device = (l >> 16) & 0xffff; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci name = NULL; 36562306a36Sopenharmony_ci if (pci_mmcfg_probes[i].vendor == vendor && 36662306a36Sopenharmony_ci pci_mmcfg_probes[i].device == device) 36762306a36Sopenharmony_ci name = pci_mmcfg_probes[i].probe(); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (name) 37062306a36Sopenharmony_ci pr_info(PREFIX "%s with MMCONFIG support\n", name); 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* some end_bus_number is crazy, fix it */ 37462306a36Sopenharmony_ci pci_mmcfg_check_end_bus_number(); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci return !list_empty(&pci_mmcfg_list); 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic acpi_status check_mcfg_resource(struct acpi_resource *res, void *data) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci struct resource *mcfg_res = data; 38262306a36Sopenharmony_ci struct acpi_resource_address64 address; 38362306a36Sopenharmony_ci acpi_status status; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (res->type == ACPI_RESOURCE_TYPE_FIXED_MEMORY32) { 38662306a36Sopenharmony_ci struct acpi_resource_fixed_memory32 *fixmem32 = 38762306a36Sopenharmony_ci &res->data.fixed_memory32; 38862306a36Sopenharmony_ci if (!fixmem32) 38962306a36Sopenharmony_ci return AE_OK; 39062306a36Sopenharmony_ci if ((mcfg_res->start >= fixmem32->address) && 39162306a36Sopenharmony_ci (mcfg_res->end < (fixmem32->address + 39262306a36Sopenharmony_ci fixmem32->address_length))) { 39362306a36Sopenharmony_ci mcfg_res->flags = 1; 39462306a36Sopenharmony_ci return AE_CTRL_TERMINATE; 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci if ((res->type != ACPI_RESOURCE_TYPE_ADDRESS32) && 39862306a36Sopenharmony_ci (res->type != ACPI_RESOURCE_TYPE_ADDRESS64)) 39962306a36Sopenharmony_ci return AE_OK; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci status = acpi_resource_to_address64(res, &address); 40262306a36Sopenharmony_ci if (ACPI_FAILURE(status) || 40362306a36Sopenharmony_ci (address.address.address_length <= 0) || 40462306a36Sopenharmony_ci (address.resource_type != ACPI_MEMORY_RANGE)) 40562306a36Sopenharmony_ci return AE_OK; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if ((mcfg_res->start >= address.address.minimum) && 40862306a36Sopenharmony_ci (mcfg_res->end < (address.address.minimum + address.address.address_length))) { 40962306a36Sopenharmony_ci mcfg_res->flags = 1; 41062306a36Sopenharmony_ci return AE_CTRL_TERMINATE; 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci return AE_OK; 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic acpi_status find_mboard_resource(acpi_handle handle, u32 lvl, 41662306a36Sopenharmony_ci void *context, void **rv) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci struct resource *mcfg_res = context; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci acpi_walk_resources(handle, METHOD_NAME__CRS, 42162306a36Sopenharmony_ci check_mcfg_resource, context); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci if (mcfg_res->flags) 42462306a36Sopenharmony_ci return AE_CTRL_TERMINATE; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci return AE_OK; 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic bool is_acpi_reserved(u64 start, u64 end, enum e820_type not_used) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci struct resource mcfg_res; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci mcfg_res.start = start; 43462306a36Sopenharmony_ci mcfg_res.end = end - 1; 43562306a36Sopenharmony_ci mcfg_res.flags = 0; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci acpi_get_devices("PNP0C01", find_mboard_resource, &mcfg_res, NULL); 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (!mcfg_res.flags) 44062306a36Sopenharmony_ci acpi_get_devices("PNP0C02", find_mboard_resource, &mcfg_res, 44162306a36Sopenharmony_ci NULL); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci return mcfg_res.flags; 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cistatic bool is_efi_mmio(u64 start, u64 end, enum e820_type not_used) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci#ifdef CONFIG_EFI 44962306a36Sopenharmony_ci efi_memory_desc_t *md; 45062306a36Sopenharmony_ci u64 size, mmio_start, mmio_end; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci for_each_efi_memory_desc(md) { 45362306a36Sopenharmony_ci if (md->type == EFI_MEMORY_MAPPED_IO) { 45462306a36Sopenharmony_ci size = md->num_pages << EFI_PAGE_SHIFT; 45562306a36Sopenharmony_ci mmio_start = md->phys_addr; 45662306a36Sopenharmony_ci mmio_end = mmio_start + size; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* 45962306a36Sopenharmony_ci * N.B. Caller supplies (start, start + size), 46062306a36Sopenharmony_ci * so to match, mmio_end is the first address 46162306a36Sopenharmony_ci * *past* the EFI_MEMORY_MAPPED_IO area. 46262306a36Sopenharmony_ci */ 46362306a36Sopenharmony_ci if (mmio_start <= start && end <= mmio_end) 46462306a36Sopenharmony_ci return true; 46562306a36Sopenharmony_ci } 46662306a36Sopenharmony_ci } 46762306a36Sopenharmony_ci#endif 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci return false; 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_citypedef bool (*check_reserved_t)(u64 start, u64 end, enum e820_type type); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic bool __ref is_mmconf_reserved(check_reserved_t is_reserved, 47562306a36Sopenharmony_ci struct pci_mmcfg_region *cfg, 47662306a36Sopenharmony_ci struct device *dev, const char *method) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci u64 addr = cfg->res.start; 47962306a36Sopenharmony_ci u64 size = resource_size(&cfg->res); 48062306a36Sopenharmony_ci u64 old_size = size; 48162306a36Sopenharmony_ci int num_buses; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci while (!is_reserved(addr, addr + size, E820_TYPE_RESERVED)) { 48462306a36Sopenharmony_ci size >>= 1; 48562306a36Sopenharmony_ci if (size < (16UL<<20)) 48662306a36Sopenharmony_ci break; 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if (size < (16UL<<20) && size != old_size) 49062306a36Sopenharmony_ci return false; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci if (dev) 49362306a36Sopenharmony_ci dev_info(dev, "MMCONFIG at %pR reserved as %s\n", 49462306a36Sopenharmony_ci &cfg->res, method); 49562306a36Sopenharmony_ci else 49662306a36Sopenharmony_ci pr_info(PREFIX "MMCONFIG at %pR reserved as %s\n", 49762306a36Sopenharmony_ci &cfg->res, method); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (old_size != size) { 50062306a36Sopenharmony_ci /* update end_bus */ 50162306a36Sopenharmony_ci cfg->end_bus = cfg->start_bus + ((size>>20) - 1); 50262306a36Sopenharmony_ci num_buses = cfg->end_bus - cfg->start_bus + 1; 50362306a36Sopenharmony_ci cfg->res.end = cfg->res.start + 50462306a36Sopenharmony_ci PCI_MMCFG_BUS_OFFSET(num_buses) - 1; 50562306a36Sopenharmony_ci snprintf(cfg->name, PCI_MMCFG_RESOURCE_NAME_LEN, 50662306a36Sopenharmony_ci "PCI MMCONFIG %04x [bus %02x-%02x]", 50762306a36Sopenharmony_ci cfg->segment, cfg->start_bus, cfg->end_bus); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci if (dev) 51062306a36Sopenharmony_ci dev_info(dev, 51162306a36Sopenharmony_ci "MMCONFIG " 51262306a36Sopenharmony_ci "at %pR (base %#lx) (size reduced!)\n", 51362306a36Sopenharmony_ci &cfg->res, (unsigned long) cfg->address); 51462306a36Sopenharmony_ci else 51562306a36Sopenharmony_ci pr_info(PREFIX 51662306a36Sopenharmony_ci "MMCONFIG for %04x [bus%02x-%02x] " 51762306a36Sopenharmony_ci "at %pR (base %#lx) (size reduced!)\n", 51862306a36Sopenharmony_ci cfg->segment, cfg->start_bus, cfg->end_bus, 51962306a36Sopenharmony_ci &cfg->res, (unsigned long) cfg->address); 52062306a36Sopenharmony_ci } 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci return true; 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cistatic bool __ref 52662306a36Sopenharmony_cipci_mmcfg_check_reserved(struct device *dev, struct pci_mmcfg_region *cfg, int early) 52762306a36Sopenharmony_ci{ 52862306a36Sopenharmony_ci struct resource *conflict; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (!early && !acpi_disabled) { 53162306a36Sopenharmony_ci if (is_mmconf_reserved(is_acpi_reserved, cfg, dev, 53262306a36Sopenharmony_ci "ACPI motherboard resource")) 53362306a36Sopenharmony_ci return true; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci if (dev) 53662306a36Sopenharmony_ci dev_info(dev, FW_INFO 53762306a36Sopenharmony_ci "MMCONFIG at %pR not reserved in " 53862306a36Sopenharmony_ci "ACPI motherboard resources\n", 53962306a36Sopenharmony_ci &cfg->res); 54062306a36Sopenharmony_ci else 54162306a36Sopenharmony_ci pr_info(FW_INFO PREFIX 54262306a36Sopenharmony_ci "MMCONFIG at %pR not reserved in " 54362306a36Sopenharmony_ci "ACPI motherboard resources\n", 54462306a36Sopenharmony_ci &cfg->res); 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (is_mmconf_reserved(is_efi_mmio, cfg, dev, 54762306a36Sopenharmony_ci "EfiMemoryMappedIO")) { 54862306a36Sopenharmony_ci conflict = insert_resource_conflict(&iomem_resource, 54962306a36Sopenharmony_ci &cfg->res); 55062306a36Sopenharmony_ci if (conflict) 55162306a36Sopenharmony_ci pr_warn("MMCONFIG %pR conflicts with %s %pR\n", 55262306a36Sopenharmony_ci &cfg->res, conflict->name, conflict); 55362306a36Sopenharmony_ci else 55462306a36Sopenharmony_ci pr_info("MMCONFIG %pR reserved to work around lack of ACPI motherboard _CRS\n", 55562306a36Sopenharmony_ci &cfg->res); 55662306a36Sopenharmony_ci return true; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* 56162306a36Sopenharmony_ci * e820__mapped_all() is marked as __init. 56262306a36Sopenharmony_ci * All entries from ACPI MCFG table have been checked at boot time. 56362306a36Sopenharmony_ci * For MCFG information constructed from hotpluggable host bridge's 56462306a36Sopenharmony_ci * _CBA method, just assume it's reserved. 56562306a36Sopenharmony_ci */ 56662306a36Sopenharmony_ci if (pci_mmcfg_running_state) 56762306a36Sopenharmony_ci return true; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci /* Don't try to do this check unless configuration 57062306a36Sopenharmony_ci type 1 is available. how about type 2 ?*/ 57162306a36Sopenharmony_ci if (raw_pci_ops) 57262306a36Sopenharmony_ci return is_mmconf_reserved(e820__mapped_all, cfg, dev, 57362306a36Sopenharmony_ci "E820 entry"); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci return false; 57662306a36Sopenharmony_ci} 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_cistatic void __init pci_mmcfg_reject_broken(int early) 57962306a36Sopenharmony_ci{ 58062306a36Sopenharmony_ci struct pci_mmcfg_region *cfg; 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_ci list_for_each_entry(cfg, &pci_mmcfg_list, list) { 58362306a36Sopenharmony_ci if (pci_mmcfg_check_reserved(NULL, cfg, early) == 0) { 58462306a36Sopenharmony_ci pr_info(PREFIX "not using MMCONFIG\n"); 58562306a36Sopenharmony_ci free_all_mmcfg(); 58662306a36Sopenharmony_ci return; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, 59262306a36Sopenharmony_ci struct acpi_mcfg_allocation *cfg) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci if (cfg->address < 0xFFFFFFFF) 59562306a36Sopenharmony_ci return 0; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (!strncmp(mcfg->header.oem_id, "SGI", 3)) 59862306a36Sopenharmony_ci return 0; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci if ((mcfg->header.revision >= 1) && (dmi_get_bios_year() >= 2010)) 60162306a36Sopenharmony_ci return 0; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx " 60462306a36Sopenharmony_ci "is above 4GB, ignored\n", cfg->pci_segment, 60562306a36Sopenharmony_ci cfg->start_bus_number, cfg->end_bus_number, cfg->address); 60662306a36Sopenharmony_ci return -EINVAL; 60762306a36Sopenharmony_ci} 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_cistatic int __init pci_parse_mcfg(struct acpi_table_header *header) 61062306a36Sopenharmony_ci{ 61162306a36Sopenharmony_ci struct acpi_table_mcfg *mcfg; 61262306a36Sopenharmony_ci struct acpi_mcfg_allocation *cfg_table, *cfg; 61362306a36Sopenharmony_ci unsigned long i; 61462306a36Sopenharmony_ci int entries; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci if (!header) 61762306a36Sopenharmony_ci return -EINVAL; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci mcfg = (struct acpi_table_mcfg *)header; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci /* how many config structures do we have */ 62262306a36Sopenharmony_ci free_all_mmcfg(); 62362306a36Sopenharmony_ci entries = 0; 62462306a36Sopenharmony_ci i = header->length - sizeof(struct acpi_table_mcfg); 62562306a36Sopenharmony_ci while (i >= sizeof(struct acpi_mcfg_allocation)) { 62662306a36Sopenharmony_ci entries++; 62762306a36Sopenharmony_ci i -= sizeof(struct acpi_mcfg_allocation); 62862306a36Sopenharmony_ci } 62962306a36Sopenharmony_ci if (entries == 0) { 63062306a36Sopenharmony_ci pr_err(PREFIX "MMCONFIG has no entries\n"); 63162306a36Sopenharmony_ci return -ENODEV; 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1]; 63562306a36Sopenharmony_ci for (i = 0; i < entries; i++) { 63662306a36Sopenharmony_ci cfg = &cfg_table[i]; 63762306a36Sopenharmony_ci if (acpi_mcfg_check_entry(mcfg, cfg)) { 63862306a36Sopenharmony_ci free_all_mmcfg(); 63962306a36Sopenharmony_ci return -ENODEV; 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number, 64362306a36Sopenharmony_ci cfg->end_bus_number, cfg->address) == NULL) { 64462306a36Sopenharmony_ci pr_warn(PREFIX "no memory for MCFG entries\n"); 64562306a36Sopenharmony_ci free_all_mmcfg(); 64662306a36Sopenharmony_ci return -ENOMEM; 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci return 0; 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci#ifdef CONFIG_ACPI_APEI 65462306a36Sopenharmony_ciextern int (*arch_apei_filter_addr)(int (*func)(__u64 start, __u64 size, 65562306a36Sopenharmony_ci void *data), void *data); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_cistatic int pci_mmcfg_for_each_region(int (*func)(__u64 start, __u64 size, 65862306a36Sopenharmony_ci void *data), void *data) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci struct pci_mmcfg_region *cfg; 66162306a36Sopenharmony_ci int rc; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (list_empty(&pci_mmcfg_list)) 66462306a36Sopenharmony_ci return 0; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci list_for_each_entry(cfg, &pci_mmcfg_list, list) { 66762306a36Sopenharmony_ci rc = func(cfg->res.start, resource_size(&cfg->res), data); 66862306a36Sopenharmony_ci if (rc) 66962306a36Sopenharmony_ci return rc; 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci return 0; 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci#define set_apei_filter() (arch_apei_filter_addr = pci_mmcfg_for_each_region) 67562306a36Sopenharmony_ci#else 67662306a36Sopenharmony_ci#define set_apei_filter() 67762306a36Sopenharmony_ci#endif 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_cistatic void __init __pci_mmcfg_init(int early) 68062306a36Sopenharmony_ci{ 68162306a36Sopenharmony_ci pci_mmcfg_reject_broken(early); 68262306a36Sopenharmony_ci if (list_empty(&pci_mmcfg_list)) 68362306a36Sopenharmony_ci return; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci if (pcibios_last_bus < 0) { 68662306a36Sopenharmony_ci const struct pci_mmcfg_region *cfg; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci list_for_each_entry(cfg, &pci_mmcfg_list, list) { 68962306a36Sopenharmony_ci if (cfg->segment) 69062306a36Sopenharmony_ci break; 69162306a36Sopenharmony_ci pcibios_last_bus = cfg->end_bus; 69262306a36Sopenharmony_ci } 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci if (pci_mmcfg_arch_init()) 69662306a36Sopenharmony_ci pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF; 69762306a36Sopenharmony_ci else { 69862306a36Sopenharmony_ci free_all_mmcfg(); 69962306a36Sopenharmony_ci pci_mmcfg_arch_init_failed = true; 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci} 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_cistatic int __initdata known_bridge; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_civoid __init pci_mmcfg_early_init(void) 70662306a36Sopenharmony_ci{ 70762306a36Sopenharmony_ci if (pci_probe & PCI_PROBE_MMCONF) { 70862306a36Sopenharmony_ci if (pci_mmcfg_check_hostbridge()) 70962306a36Sopenharmony_ci known_bridge = 1; 71062306a36Sopenharmony_ci else 71162306a36Sopenharmony_ci acpi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); 71262306a36Sopenharmony_ci __pci_mmcfg_init(1); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci set_apei_filter(); 71562306a36Sopenharmony_ci } 71662306a36Sopenharmony_ci} 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_civoid __init pci_mmcfg_late_init(void) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci /* MMCONFIG disabled */ 72162306a36Sopenharmony_ci if ((pci_probe & PCI_PROBE_MMCONF) == 0) 72262306a36Sopenharmony_ci return; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci if (known_bridge) 72562306a36Sopenharmony_ci return; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci /* MMCONFIG hasn't been enabled yet, try again */ 72862306a36Sopenharmony_ci if (pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF) { 72962306a36Sopenharmony_ci acpi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg); 73062306a36Sopenharmony_ci __pci_mmcfg_init(0); 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci} 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_cistatic int __init pci_mmcfg_late_insert_resources(void) 73562306a36Sopenharmony_ci{ 73662306a36Sopenharmony_ci struct pci_mmcfg_region *cfg; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci pci_mmcfg_running_state = true; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci /* If we are not using MMCONFIG, don't insert the resources. */ 74162306a36Sopenharmony_ci if ((pci_probe & PCI_PROBE_MMCONF) == 0) 74262306a36Sopenharmony_ci return 1; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci /* 74562306a36Sopenharmony_ci * Attempt to insert the mmcfg resources but not with the busy flag 74662306a36Sopenharmony_ci * marked so it won't cause request errors when __request_region is 74762306a36Sopenharmony_ci * called. 74862306a36Sopenharmony_ci */ 74962306a36Sopenharmony_ci list_for_each_entry(cfg, &pci_mmcfg_list, list) 75062306a36Sopenharmony_ci if (!cfg->res.parent) 75162306a36Sopenharmony_ci insert_resource(&iomem_resource, &cfg->res); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci return 0; 75462306a36Sopenharmony_ci} 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci/* 75762306a36Sopenharmony_ci * Perform MMCONFIG resource insertion after PCI initialization to allow for 75862306a36Sopenharmony_ci * misprogrammed MCFG tables that state larger sizes but actually conflict 75962306a36Sopenharmony_ci * with other system resources. 76062306a36Sopenharmony_ci */ 76162306a36Sopenharmony_cilate_initcall(pci_mmcfg_late_insert_resources); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci/* Add MMCFG information for host bridges */ 76462306a36Sopenharmony_ciint pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, 76562306a36Sopenharmony_ci phys_addr_t addr) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci int rc; 76862306a36Sopenharmony_ci struct resource *tmp = NULL; 76962306a36Sopenharmony_ci struct pci_mmcfg_region *cfg; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci if (!(pci_probe & PCI_PROBE_MMCONF) || pci_mmcfg_arch_init_failed) 77262306a36Sopenharmony_ci return -ENODEV; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci if (start > end) 77562306a36Sopenharmony_ci return -EINVAL; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci mutex_lock(&pci_mmcfg_lock); 77862306a36Sopenharmony_ci cfg = pci_mmconfig_lookup(seg, start); 77962306a36Sopenharmony_ci if (cfg) { 78062306a36Sopenharmony_ci if (cfg->end_bus < end) 78162306a36Sopenharmony_ci dev_info(dev, FW_INFO 78262306a36Sopenharmony_ci "MMCONFIG for " 78362306a36Sopenharmony_ci "domain %04x [bus %02x-%02x] " 78462306a36Sopenharmony_ci "only partially covers this bridge\n", 78562306a36Sopenharmony_ci cfg->segment, cfg->start_bus, cfg->end_bus); 78662306a36Sopenharmony_ci mutex_unlock(&pci_mmcfg_lock); 78762306a36Sopenharmony_ci return -EEXIST; 78862306a36Sopenharmony_ci } 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci if (!addr) { 79162306a36Sopenharmony_ci mutex_unlock(&pci_mmcfg_lock); 79262306a36Sopenharmony_ci return -EINVAL; 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci rc = -EBUSY; 79662306a36Sopenharmony_ci cfg = pci_mmconfig_alloc(seg, start, end, addr); 79762306a36Sopenharmony_ci if (cfg == NULL) { 79862306a36Sopenharmony_ci dev_warn(dev, "fail to add MMCONFIG (out of memory)\n"); 79962306a36Sopenharmony_ci rc = -ENOMEM; 80062306a36Sopenharmony_ci } else if (!pci_mmcfg_check_reserved(dev, cfg, 0)) { 80162306a36Sopenharmony_ci dev_warn(dev, FW_BUG "MMCONFIG %pR isn't reserved\n", 80262306a36Sopenharmony_ci &cfg->res); 80362306a36Sopenharmony_ci } else { 80462306a36Sopenharmony_ci /* Insert resource if it's not in boot stage */ 80562306a36Sopenharmony_ci if (pci_mmcfg_running_state) 80662306a36Sopenharmony_ci tmp = insert_resource_conflict(&iomem_resource, 80762306a36Sopenharmony_ci &cfg->res); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci if (tmp) { 81062306a36Sopenharmony_ci dev_warn(dev, 81162306a36Sopenharmony_ci "MMCONFIG %pR conflicts with " 81262306a36Sopenharmony_ci "%s %pR\n", 81362306a36Sopenharmony_ci &cfg->res, tmp->name, tmp); 81462306a36Sopenharmony_ci } else if (pci_mmcfg_arch_map(cfg)) { 81562306a36Sopenharmony_ci dev_warn(dev, "fail to map MMCONFIG %pR.\n", 81662306a36Sopenharmony_ci &cfg->res); 81762306a36Sopenharmony_ci } else { 81862306a36Sopenharmony_ci list_add_sorted(cfg); 81962306a36Sopenharmony_ci dev_info(dev, "MMCONFIG at %pR (base %#lx)\n", 82062306a36Sopenharmony_ci &cfg->res, (unsigned long)addr); 82162306a36Sopenharmony_ci cfg = NULL; 82262306a36Sopenharmony_ci rc = 0; 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci if (cfg) { 82762306a36Sopenharmony_ci if (cfg->res.parent) 82862306a36Sopenharmony_ci release_resource(&cfg->res); 82962306a36Sopenharmony_ci kfree(cfg); 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci mutex_unlock(&pci_mmcfg_lock); 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci return rc; 83562306a36Sopenharmony_ci} 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci/* Delete MMCFG information for host bridges */ 83862306a36Sopenharmony_ciint pci_mmconfig_delete(u16 seg, u8 start, u8 end) 83962306a36Sopenharmony_ci{ 84062306a36Sopenharmony_ci struct pci_mmcfg_region *cfg; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci mutex_lock(&pci_mmcfg_lock); 84362306a36Sopenharmony_ci list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) 84462306a36Sopenharmony_ci if (cfg->segment == seg && cfg->start_bus == start && 84562306a36Sopenharmony_ci cfg->end_bus == end) { 84662306a36Sopenharmony_ci list_del_rcu(&cfg->list); 84762306a36Sopenharmony_ci synchronize_rcu(); 84862306a36Sopenharmony_ci pci_mmcfg_arch_unmap(cfg); 84962306a36Sopenharmony_ci if (cfg->res.parent) 85062306a36Sopenharmony_ci release_resource(&cfg->res); 85162306a36Sopenharmony_ci mutex_unlock(&pci_mmcfg_lock); 85262306a36Sopenharmony_ci kfree(cfg); 85362306a36Sopenharmony_ci return 0; 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci mutex_unlock(&pci_mmcfg_lock); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci return -ENOENT; 85862306a36Sopenharmony_ci} 859