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