1b78673944b22b662b270c8bba5c198f19e4ee4e1Olivier Galibert/*
2b78673944b22b662b270c8bba5c198f19e4ee4e1Olivier Galibert * mmconfig-shared.c - Low-level direct PCI config space access via
3b78673944b22b662b270c8bba5c198f19e4ee4e1Olivier Galibert *                     MMCONFIG - common code between i386 and x86-64.
4b78673944b22b662b270c8bba5c198f19e4ee4e1Olivier Galibert *
5b78673944b22b662b270c8bba5c198f19e4ee4e1Olivier Galibert * This code does:
69358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert * - known chipset handling
7b78673944b22b662b270c8bba5c198f19e4ee4e1Olivier Galibert * - ACPI decoding and validation
8b78673944b22b662b270c8bba5c198f19e4ee4e1Olivier Galibert *
9b78673944b22b662b270c8bba5c198f19e4ee4e1Olivier Galibert * Per-architecture code takes care of the mappings and accesses
10b78673944b22b662b270c8bba5c198f19e4ee4e1Olivier Galibert * themselves.
11b78673944b22b662b270c8bba5c198f19e4ee4e1Olivier Galibert */
12b78673944b22b662b270c8bba5c198f19e4ee4e1Olivier Galibert
13b78673944b22b662b270c8bba5c198f19e4ee4e1Olivier Galibert#include <linux/pci.h>
14b78673944b22b662b270c8bba5c198f19e4ee4e1Olivier Galibert#include <linux/init.h>
155f0db7a2fb78895a197f64e548333b3bbd433996Feng Tang#include <linux/sfi_acpi.h>
16b78673944b22b662b270c8bba5c198f19e4ee4e1Olivier Galibert#include <linux/bitmap.h>
179a08f7d3506019e3833cd4394ca0d7da0ae3689fBjorn Helgaas#include <linux/dmi.h>
185a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
19376f70acfe4bd97493299cdfc00a8d235279d267Jiang Liu#include <linux/mutex.h>
20376f70acfe4bd97493299cdfc00a8d235279d267Jiang Liu#include <linux/rculist.h>
21b78673944b22b662b270c8bba5c198f19e4ee4e1Olivier Galibert#include <asm/e820.h>
22824877111cd7f2b4fd2fe6947c5c5cbbb3ac5bd8Jaswinder Singh Rajput#include <asm/pci_x86.h>
235f0db7a2fb78895a197f64e548333b3bbd433996Feng Tang#include <asm/acpi.h>
24b78673944b22b662b270c8bba5c198f19e4ee4e1Olivier Galibert
25f4a2d5840e9f0e48d1a787b66e7346087a756029Len Brown#define PREFIX "PCI: "
26a192a9580bcc41692be1f36b77c3b681827f566aLen Brown
27a5ba7971045a90a36cef8f7d5a3075600b475b74Aaron Durbin/* Indicate if the mmcfg resources have been placed into the resource table. */
2895c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liustatic bool pci_mmcfg_running_state;
299c95111b330d2ddf851444528a7608f267cbb50cJiang Liustatic bool pci_mmcfg_arch_init_failed;
30376f70acfe4bd97493299cdfc00a8d235279d267Jiang Liustatic DEFINE_MUTEX(pci_mmcfg_lock);
31a5ba7971045a90a36cef8f7d5a3075600b475b74Aaron Durbin
32ff097ddd4aeac790fd51d013c79c2f18ec9a7117Bjorn HelgaasLIST_HEAD(pci_mmcfg_list);
33ff097ddd4aeac790fd51d013c79c2f18ec9a7117Bjorn Helgaas
3464474b5235e83cc5e6002dcdb37145850ad86194Mathias Krausestatic void __init pci_mmconfig_remove(struct pci_mmcfg_region *cfg)
35ba2afbabfc44d6322e8607c004f37868ff786cf8Bjorn Helgaas{
36ba2afbabfc44d6322e8607c004f37868ff786cf8Bjorn Helgaas	if (cfg->res.parent)
37ba2afbabfc44d6322e8607c004f37868ff786cf8Bjorn Helgaas		release_resource(&cfg->res);
38ba2afbabfc44d6322e8607c004f37868ff786cf8Bjorn Helgaas	list_del(&cfg->list);
39ba2afbabfc44d6322e8607c004f37868ff786cf8Bjorn Helgaas	kfree(cfg);
40ba2afbabfc44d6322e8607c004f37868ff786cf8Bjorn Helgaas}
41ba2afbabfc44d6322e8607c004f37868ff786cf8Bjorn Helgaas
4264474b5235e83cc5e6002dcdb37145850ad86194Mathias Krausestatic void __init free_all_mmcfg(void)
437da7d360ae025158d09aab18d66f5d2fe3c02252Bjorn Helgaas{
44ff097ddd4aeac790fd51d013c79c2f18ec9a7117Bjorn Helgaas	struct pci_mmcfg_region *cfg, *tmp;
4556ddf4d3cf04e80254d3d721c6bea2f8ec44c41aBjorn Helgaas
467da7d360ae025158d09aab18d66f5d2fe3c02252Bjorn Helgaas	pci_mmcfg_arch_free();
47ba2afbabfc44d6322e8607c004f37868ff786cf8Bjorn Helgaas	list_for_each_entry_safe(cfg, tmp, &pci_mmcfg_list, list)
48ba2afbabfc44d6322e8607c004f37868ff786cf8Bjorn Helgaas		pci_mmconfig_remove(cfg);
49ff097ddd4aeac790fd51d013c79c2f18ec9a7117Bjorn Helgaas}
50ff097ddd4aeac790fd51d013c79c2f18ec9a7117Bjorn Helgaas
51a18e3690a52790a034d6540d54e8e1f1cd125da2Greg Kroah-Hartmanstatic void list_add_sorted(struct pci_mmcfg_region *new)
52ff097ddd4aeac790fd51d013c79c2f18ec9a7117Bjorn Helgaas{
53ff097ddd4aeac790fd51d013c79c2f18ec9a7117Bjorn Helgaas	struct pci_mmcfg_region *cfg;
54ff097ddd4aeac790fd51d013c79c2f18ec9a7117Bjorn Helgaas
55ff097ddd4aeac790fd51d013c79c2f18ec9a7117Bjorn Helgaas	/* keep list sorted by segment and starting bus number */
56376f70acfe4bd97493299cdfc00a8d235279d267Jiang Liu	list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) {
57ff097ddd4aeac790fd51d013c79c2f18ec9a7117Bjorn Helgaas		if (cfg->segment > new->segment ||
58ff097ddd4aeac790fd51d013c79c2f18ec9a7117Bjorn Helgaas		    (cfg->segment == new->segment &&
59ff097ddd4aeac790fd51d013c79c2f18ec9a7117Bjorn Helgaas		     cfg->start_bus >= new->start_bus)) {
60376f70acfe4bd97493299cdfc00a8d235279d267Jiang Liu			list_add_tail_rcu(&new->list, &cfg->list);
61ff097ddd4aeac790fd51d013c79c2f18ec9a7117Bjorn Helgaas			return;
62ff097ddd4aeac790fd51d013c79c2f18ec9a7117Bjorn Helgaas		}
63ff097ddd4aeac790fd51d013c79c2f18ec9a7117Bjorn Helgaas	}
64376f70acfe4bd97493299cdfc00a8d235279d267Jiang Liu	list_add_tail_rcu(&new->list, &pci_mmcfg_list);
657da7d360ae025158d09aab18d66f5d2fe3c02252Bjorn Helgaas}
667da7d360ae025158d09aab18d66f5d2fe3c02252Bjorn Helgaas
67a18e3690a52790a034d6540d54e8e1f1cd125da2Greg Kroah-Hartmanstatic struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start,
68a18e3690a52790a034d6540d54e8e1f1cd125da2Greg Kroah-Hartman						   int end, u64 addr)
69068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu{
70d215a9c8b46e55a1d3bc1cd907c943ef95938a0eBjorn Helgaas	struct pci_mmcfg_region *new;
7156ddf4d3cf04e80254d3d721c6bea2f8ec44c41aBjorn Helgaas	struct resource *res;
72068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu
73f7ca69848786bb99fdfafb511791b078c298438eBjorn Helgaas	if (addr == 0)
74f7ca69848786bb99fdfafb511791b078c298438eBjorn Helgaas		return NULL;
75f7ca69848786bb99fdfafb511791b078c298438eBjorn Helgaas
76ff097ddd4aeac790fd51d013c79c2f18ec9a7117Bjorn Helgaas	new = kzalloc(sizeof(*new), GFP_KERNEL);
77068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu	if (!new)
787da7d360ae025158d09aab18d66f5d2fe3c02252Bjorn Helgaas		return NULL;
79068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu
8095cf1cf0c5a767feb811dfed298b95b1df8824c7Bjorn Helgaas	new->address = addr;
8195cf1cf0c5a767feb811dfed298b95b1df8824c7Bjorn Helgaas	new->segment = segment;
8295cf1cf0c5a767feb811dfed298b95b1df8824c7Bjorn Helgaas	new->start_bus = start;
8395cf1cf0c5a767feb811dfed298b95b1df8824c7Bjorn Helgaas	new->end_bus = end;
847da7d360ae025158d09aab18d66f5d2fe3c02252Bjorn Helgaas
8556ddf4d3cf04e80254d3d721c6bea2f8ec44c41aBjorn Helgaas	res = &new->res;
8656ddf4d3cf04e80254d3d721c6bea2f8ec44c41aBjorn Helgaas	res->start = addr + PCI_MMCFG_BUS_OFFSET(start);
871ca98fa652bb5dc3c8793335db9ccc5d0f2e1f65Bjorn Helgaas	res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1;
8856ddf4d3cf04e80254d3d721c6bea2f8ec44c41aBjorn Helgaas	res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
8956ddf4d3cf04e80254d3d721c6bea2f8ec44c41aBjorn Helgaas	snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN,
9056ddf4d3cf04e80254d3d721c6bea2f8ec44c41aBjorn Helgaas		 "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end);
9156ddf4d3cf04e80254d3d721c6bea2f8ec44c41aBjorn Helgaas	res->name = new->name;
9256ddf4d3cf04e80254d3d721c6bea2f8ec44c41aBjorn Helgaas
93ff097ddd4aeac790fd51d013c79c2f18ec9a7117Bjorn Helgaas	return new;
94068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu}
95068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu
9664474b5235e83cc5e6002dcdb37145850ad86194Mathias Krausestatic struct pci_mmcfg_region *__init pci_mmconfig_add(int segment, int start,
97846e402300ffa2131239dcf82265b5366cd755f4Jiang Liu							int end, u64 addr)
98846e402300ffa2131239dcf82265b5366cd755f4Jiang Liu{
99846e402300ffa2131239dcf82265b5366cd755f4Jiang Liu	struct pci_mmcfg_region *new;
100846e402300ffa2131239dcf82265b5366cd755f4Jiang Liu
101846e402300ffa2131239dcf82265b5366cd755f4Jiang Liu	new = pci_mmconfig_alloc(segment, start, end, addr);
102376f70acfe4bd97493299cdfc00a8d235279d267Jiang Liu	if (new) {
103376f70acfe4bd97493299cdfc00a8d235279d267Jiang Liu		mutex_lock(&pci_mmcfg_lock);
104846e402300ffa2131239dcf82265b5366cd755f4Jiang Liu		list_add_sorted(new);
105376f70acfe4bd97493299cdfc00a8d235279d267Jiang Liu		mutex_unlock(&pci_mmcfg_lock);
1069c95111b330d2ddf851444528a7608f267cbb50cJiang Liu
10724c97f04c4570e02c5cf4b97c73ab9dc27bacdbeJiang Liu		pr_info(PREFIX
1089c95111b330d2ddf851444528a7608f267cbb50cJiang Liu		       "MMCONFIG for domain %04x [bus %02x-%02x] at %pR "
1099c95111b330d2ddf851444528a7608f267cbb50cJiang Liu		       "(base %#lx)\n",
1109c95111b330d2ddf851444528a7608f267cbb50cJiang Liu		       segment, start, end, &new->res, (unsigned long)addr);
111376f70acfe4bd97493299cdfc00a8d235279d267Jiang Liu	}
112846e402300ffa2131239dcf82265b5366cd755f4Jiang Liu
113846e402300ffa2131239dcf82265b5366cd755f4Jiang Liu	return new;
114846e402300ffa2131239dcf82265b5366cd755f4Jiang Liu}
115846e402300ffa2131239dcf82265b5366cd755f4Jiang Liu
116f6e1d8cc38b3776038fb15d3acc82ed8bb552f82Bjorn Helgaasstruct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus)
117f6e1d8cc38b3776038fb15d3acc82ed8bb552f82Bjorn Helgaas{
118f6e1d8cc38b3776038fb15d3acc82ed8bb552f82Bjorn Helgaas	struct pci_mmcfg_region *cfg;
119f6e1d8cc38b3776038fb15d3acc82ed8bb552f82Bjorn Helgaas
120376f70acfe4bd97493299cdfc00a8d235279d267Jiang Liu	list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
121f6e1d8cc38b3776038fb15d3acc82ed8bb552f82Bjorn Helgaas		if (cfg->segment == segment &&
122f6e1d8cc38b3776038fb15d3acc82ed8bb552f82Bjorn Helgaas		    cfg->start_bus <= bus && bus <= cfg->end_bus)
123f6e1d8cc38b3776038fb15d3acc82ed8bb552f82Bjorn Helgaas			return cfg;
124f6e1d8cc38b3776038fb15d3acc82ed8bb552f82Bjorn Helgaas
125f6e1d8cc38b3776038fb15d3acc82ed8bb552f82Bjorn Helgaas	return NULL;
126f6e1d8cc38b3776038fb15d3acc82ed8bb552f82Bjorn Helgaas}
127f6e1d8cc38b3776038fb15d3acc82ed8bb552f82Bjorn Helgaas
12864474b5235e83cc5e6002dcdb37145850ad86194Mathias Krausestatic const char *__init pci_mmcfg_e7520(void)
1299358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert{
1309358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert	u32 win;
131bb63b4219976d48ed6d22ac33c18be334fb5a78cYinghai Lu	raw_pci_ops->read(0, 0, PCI_DEVFN(0, 0), 0xce, 2, &win);
1329358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert
133b5229dbb857f61d77d8d4048d9033387a5411b8eOlivier Galibert	win = win & 0xf000;
134068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu	if (win == 0x0000 || win == 0xf000)
135068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu		return NULL;
136068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu
1377da7d360ae025158d09aab18d66f5d2fe3c02252Bjorn Helgaas	if (pci_mmconfig_add(0, 0, 255, win << 16) == NULL)
138068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu		return NULL;
139068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu
1409358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert	return "Intel Corporation E7520 Memory Controller Hub";
1419358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert}
1429358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert
14364474b5235e83cc5e6002dcdb37145850ad86194Mathias Krausestatic const char *__init pci_mmcfg_intel_945(void)
1449358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert{
1459358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert	u32 pciexbar, mask = 0, len = 0;
1469358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert
147bb63b4219976d48ed6d22ac33c18be334fb5a78cYinghai Lu	raw_pci_ops->read(0, 0, PCI_DEVFN(0, 0), 0x48, 4, &pciexbar);
1489358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert
1499358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert	/* Enable bit */
1509358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert	if (!(pciexbar & 1))
151068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu		return NULL;
1529358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert
1539358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert	/* Size bits */
1549358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert	switch ((pciexbar >> 1) & 3) {
1559358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert	case 0:
1569358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert		mask = 0xf0000000U;
1579358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert		len  = 0x10000000U;
1589358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert		break;
1599358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert	case 1:
1609358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert		mask = 0xf8000000U;
1619358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert		len  = 0x08000000U;
1629358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert		break;
1639358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert	case 2:
1649358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert		mask = 0xfc000000U;
1659358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert		len  = 0x04000000U;
1669358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert		break;
1679358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert	default:
168068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu		return NULL;
1699358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert	}
1709358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert
1719358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert	/* Errata #2, things break when not aligned on a 256Mb boundary */
1729358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert	/* Can only happen in 64M/128M mode */
1739358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert
1749358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert	if ((pciexbar & mask) & 0x0fffffffU)
175068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu		return NULL;
1769358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert
177b5229dbb857f61d77d8d4048d9033387a5411b8eOlivier Galibert	/* Don't hit the APIC registers and their friends */
178b5229dbb857f61d77d8d4048d9033387a5411b8eOlivier Galibert	if ((pciexbar & mask) >= 0xf0000000U)
179068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu		return NULL;
180068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu
1817da7d360ae025158d09aab18d66f5d2fe3c02252Bjorn Helgaas	if (pci_mmconfig_add(0, 0, (len >> 20) - 1, pciexbar & mask) == NULL)
182068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu		return NULL;
183068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu
1849358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert	return "Intel Corporation 945G/GZ/P/PL Express Memory Controller Hub";
1859358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert}
1869358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert
18764474b5235e83cc5e6002dcdb37145850ad86194Mathias Krausestatic const char *__init pci_mmcfg_amd_fam10h(void)
1887fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu{
1897fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu	u32 low, high, address;
1907fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu	u64 base, msr;
1917fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu	int i;
1927da7d360ae025158d09aab18d66f5d2fe3c02252Bjorn Helgaas	unsigned segnbits = 0, busnbits, end_bus;
1937fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu
1945f0b2976cb2b62668a076f54419c24b8ab677167Yinghai Lu	if (!(pci_probe & PCI_CHECK_ENABLE_AMD_MMCONF))
1955f0b2976cb2b62668a076f54419c24b8ab677167Yinghai Lu		return NULL;
1965f0b2976cb2b62668a076f54419c24b8ab677167Yinghai Lu
1977fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu	address = MSR_FAM10H_MMIO_CONF_BASE;
1987fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu	if (rdmsr_safe(address, &low, &high))
1997fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu		return NULL;
2007fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu
2017fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu	msr = high;
2027fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu	msr <<= 32;
2037fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu	msr |= low;
2047fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu
2057fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu	/* mmconfig is not enable */
2067fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu	if (!(msr & FAM10H_MMIO_CONF_ENABLE))
2077fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu		return NULL;
2087fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu
2097fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu	base = msr & (FAM10H_MMIO_CONF_BASE_MASK<<FAM10H_MMIO_CONF_BASE_SHIFT);
2107fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu
2117fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu	busnbits = (msr >> FAM10H_MMIO_CONF_BUSRANGE_SHIFT) &
2127fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu			 FAM10H_MMIO_CONF_BUSRANGE_MASK;
2137fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu
2147fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu	/*
2157fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu	 * only handle bus 0 ?
2167fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu	 * need to skip it
2177fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu	 */
2187fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu	if (!busnbits)
2197fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu		return NULL;
2207fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu
2217fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu	if (busnbits > 8) {
2227fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu		segnbits = busnbits - 8;
2237fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu		busnbits = 8;
2247fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu	}
2257fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu
2267da7d360ae025158d09aab18d66f5d2fe3c02252Bjorn Helgaas	end_bus = (1 << busnbits) - 1;
227068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu	for (i = 0; i < (1 << segnbits); i++)
2287da7d360ae025158d09aab18d66f5d2fe3c02252Bjorn Helgaas		if (pci_mmconfig_add(i, 0, end_bus,
2297da7d360ae025158d09aab18d66f5d2fe3c02252Bjorn Helgaas				     base + (1<<28) * i) == NULL) {
2307da7d360ae025158d09aab18d66f5d2fe3c02252Bjorn Helgaas			free_all_mmcfg();
2317da7d360ae025158d09aab18d66f5d2fe3c02252Bjorn Helgaas			return NULL;
2327da7d360ae025158d09aab18d66f5d2fe3c02252Bjorn Helgaas		}
2337fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu
2347fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu	return "AMD Family 10h NB";
2357fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu}
2367fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu
2375546d6f56807115a035d140f7364ce5807dbcc87Ed Swierkstatic bool __initdata mcp55_checked;
23864474b5235e83cc5e6002dcdb37145850ad86194Mathias Krausestatic const char *__init pci_mmcfg_nvidia_mcp55(void)
2395546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk{
2405546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk	int bus;
2415546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk	int mcp55_mmconf_found = 0;
2425546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk
243776f7ad6322817a5a021c5c479d688d322fb4b27Mathias Krause	static const u32 extcfg_regnum __initconst	= 0x90;
244776f7ad6322817a5a021c5c479d688d322fb4b27Mathias Krause	static const u32 extcfg_regsize __initconst	= 4;
245776f7ad6322817a5a021c5c479d688d322fb4b27Mathias Krause	static const u32 extcfg_enable_mask __initconst	= 1 << 31;
246776f7ad6322817a5a021c5c479d688d322fb4b27Mathias Krause	static const u32 extcfg_start_mask __initconst	= 0xff << 16;
247776f7ad6322817a5a021c5c479d688d322fb4b27Mathias Krause	static const int extcfg_start_shift __initconst	= 16;
248776f7ad6322817a5a021c5c479d688d322fb4b27Mathias Krause	static const u32 extcfg_size_mask __initconst	= 0x3 << 28;
249776f7ad6322817a5a021c5c479d688d322fb4b27Mathias Krause	static const int extcfg_size_shift __initconst	= 28;
250776f7ad6322817a5a021c5c479d688d322fb4b27Mathias Krause	static const int extcfg_sizebus[] __initconst	= {
251776f7ad6322817a5a021c5c479d688d322fb4b27Mathias Krause		0x100, 0x80, 0x40, 0x20
252776f7ad6322817a5a021c5c479d688d322fb4b27Mathias Krause	};
253776f7ad6322817a5a021c5c479d688d322fb4b27Mathias Krause	static const u32 extcfg_base_mask[] __initconst	= {
254776f7ad6322817a5a021c5c479d688d322fb4b27Mathias Krause		0x7ff8, 0x7ffc, 0x7ffe, 0x7fff
255776f7ad6322817a5a021c5c479d688d322fb4b27Mathias Krause	};
256776f7ad6322817a5a021c5c479d688d322fb4b27Mathias Krause	static const int extcfg_base_lshift __initconst	= 25;
2575546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk
2585546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk	/*
2595546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk	 * do check if amd fam10h already took over
2605546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk	 */
261ff097ddd4aeac790fd51d013c79c2f18ec9a7117Bjorn Helgaas	if (!acpi_disabled || !list_empty(&pci_mmcfg_list) || mcp55_checked)
2625546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk		return NULL;
2635546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk
2645546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk	mcp55_checked = true;
2655546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk	for (bus = 0; bus < 256; bus++) {
2665546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk		u64 base;
2675546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk		u32 l, extcfg;
2685546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk		u16 vendor, device;
2695546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk		int start, size_index, end;
2705546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk
2715546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk		raw_pci_ops->read(0, bus, PCI_DEVFN(0, 0), 0, 4, &l);
2725546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk		vendor = l & 0xffff;
2735546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk		device = (l >> 16) & 0xffff;
2745546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk
2755546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk		if (PCI_VENDOR_ID_NVIDIA != vendor || 0x0369 != device)
2765546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk			continue;
2775546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk
2785546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk		raw_pci_ops->read(0, bus, PCI_DEVFN(0, 0), extcfg_regnum,
2795546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk				  extcfg_regsize, &extcfg);
2805546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk
2815546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk		if (!(extcfg & extcfg_enable_mask))
2825546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk			continue;
2835546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk
2845546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk		size_index = (extcfg & extcfg_size_mask) >> extcfg_size_shift;
2855546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk		base = extcfg & extcfg_base_mask[size_index];
2865546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk		/* base could > 4G */
2875546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk		base <<= extcfg_base_lshift;
2885546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk		start = (extcfg & extcfg_start_mask) >> extcfg_start_shift;
2895546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk		end = start + extcfg_sizebus[size_index] - 1;
2907da7d360ae025158d09aab18d66f5d2fe3c02252Bjorn Helgaas		if (pci_mmconfig_add(0, start, end, base) == NULL)
2917da7d360ae025158d09aab18d66f5d2fe3c02252Bjorn Helgaas			continue;
2925546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk		mcp55_mmconf_found++;
2935546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk	}
2945546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk
2955546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk	if (!mcp55_mmconf_found)
2965546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk		return NULL;
2975546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk
2985546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk	return "nVidia MCP55";
2995546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk}
3005546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk
3019358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibertstruct pci_mmcfg_hostbridge_probe {
3027fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu	u32 bus;
3037fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu	u32 devfn;
3049358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert	u32 vendor;
3059358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert	u32 device;
3069358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert	const char *(*probe)(void);
3079358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert};
3089358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert
3096af13bac777aaec7460475a9fea8e9640c4b606eMathias Krausestatic const struct pci_mmcfg_hostbridge_probe pci_mmcfg_probes[] __initconst = {
3107fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu	{ 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID_INTEL,
3117fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu	  PCI_DEVICE_ID_INTEL_E7520_MCH, pci_mmcfg_e7520 },
3127fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu	{ 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID_INTEL,
3137fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu	  PCI_DEVICE_ID_INTEL_82945G_HB, pci_mmcfg_intel_945 },
3147fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu	{ 0, PCI_DEVFN(0x18, 0), PCI_VENDOR_ID_AMD,
3157fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu	  0x1200, pci_mmcfg_amd_fam10h },
3167fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu	{ 0xff, PCI_DEVFN(0, 0), PCI_VENDOR_ID_AMD,
3177fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu	  0x1200, pci_mmcfg_amd_fam10h },
3185546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk	{ 0, PCI_DEVFN(0, 0), PCI_VENDOR_ID_NVIDIA,
3195546d6f56807115a035d140f7364ce5807dbcc87Ed Swierk	  0x0369, pci_mmcfg_nvidia_mcp55 },
3209358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert};
3219358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert
322068258bc15439c11a966e873f931cc8e513dca61Yinghai Lustatic void __init pci_mmcfg_check_end_bus_number(void)
323068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu{
324987c367b4e93be6826394e7c9cc14d28bb5c8810Bjorn Helgaas	struct pci_mmcfg_region *cfg, *cfgx;
325068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu
326bb8d41330ce27edb91adb6922d3f8e1a8923f727Thomas Gleixner	/* Fixup overlaps */
327ff097ddd4aeac790fd51d013c79c2f18ec9a7117Bjorn Helgaas	list_for_each_entry(cfg, &pci_mmcfg_list, list) {
328d7e6b66fe87c9f42480d73fc314aecaeae84ca6bBjorn Helgaas		if (cfg->end_bus < cfg->start_bus)
329d7e6b66fe87c9f42480d73fc314aecaeae84ca6bBjorn Helgaas			cfg->end_bus = 255;
330068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu
331bb8d41330ce27edb91adb6922d3f8e1a8923f727Thomas Gleixner		/* Don't access the list head ! */
332bb8d41330ce27edb91adb6922d3f8e1a8923f727Thomas Gleixner		if (cfg->list.next == &pci_mmcfg_list)
333bb8d41330ce27edb91adb6922d3f8e1a8923f727Thomas Gleixner			break;
334bb8d41330ce27edb91adb6922d3f8e1a8923f727Thomas Gleixner
335ff097ddd4aeac790fd51d013c79c2f18ec9a7117Bjorn Helgaas		cfgx = list_entry(cfg->list.next, typeof(*cfg), list);
336bb8d41330ce27edb91adb6922d3f8e1a8923f727Thomas Gleixner		if (cfg->end_bus >= cfgx->start_bus)
337d7e6b66fe87c9f42480d73fc314aecaeae84ca6bBjorn Helgaas			cfg->end_bus = cfgx->start_bus - 1;
338068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu	}
339068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu}
340068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu
3419358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibertstatic int __init pci_mmcfg_check_hostbridge(void)
3429358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert{
3439358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert	u32 l;
3447fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu	u32 bus, devfn;
3459358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert	u16 vendor, device;
3469358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert	int i;
3479358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert	const char *name;
3489358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert
349bb63b4219976d48ed6d22ac33c18be334fb5a78cYinghai Lu	if (!raw_pci_ops)
350bb63b4219976d48ed6d22ac33c18be334fb5a78cYinghai Lu		return 0;
351bb63b4219976d48ed6d22ac33c18be334fb5a78cYinghai Lu
3527da7d360ae025158d09aab18d66f5d2fe3c02252Bjorn Helgaas	free_all_mmcfg();
3539358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert
354068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu	for (i = 0; i < ARRAY_SIZE(pci_mmcfg_probes); i++) {
3557fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu		bus =  pci_mmcfg_probes[i].bus;
3567fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu		devfn = pci_mmcfg_probes[i].devfn;
357bb63b4219976d48ed6d22ac33c18be334fb5a78cYinghai Lu		raw_pci_ops->read(0, bus, devfn, 0, 4, &l);
3587fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu		vendor = l & 0xffff;
3597fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu		device = (l >> 16) & 0xffff;
3607fd0da4085d5b012a6bdcbbd63da7ead9fc69ad4Yinghai Lu
361068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu		name = NULL;
362429d512e532ec9c969aa6f66ddbc542f3a5fe4daOGAWA Hirofumi		if (pci_mmcfg_probes[i].vendor == vendor &&
363429d512e532ec9c969aa6f66ddbc542f3a5fe4daOGAWA Hirofumi		    pci_mmcfg_probes[i].device == device)
3649358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert			name = pci_mmcfg_probes[i].probe();
3659358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert
366068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu		if (name)
36724c97f04c4570e02c5cf4b97c73ab9dc27bacdbeJiang Liu			pr_info(PREFIX "%s with MMCONFIG support\n", name);
3689358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert	}
3699358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert
370068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu	/* some end_bus_number is crazy, fix it */
371068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu	pci_mmcfg_check_end_bus_number();
372068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu
373ff097ddd4aeac790fd51d013c79c2f18ec9a7117Bjorn Helgaas	return !list_empty(&pci_mmcfg_list);
3749358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert}
3759358c693c5ac1afde28f24ac651f7903d32a850cOlivier Galibert
376a18e3690a52790a034d6540d54e8e1f1cd125da2Greg Kroah-Hartmanstatic acpi_status check_mcfg_resource(struct acpi_resource *res, void *data)
3777752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock{
3787752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock	struct resource *mcfg_res = data;
3797752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock	struct acpi_resource_address64 address;
3807752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock	acpi_status status;
3817752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock
3827752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock	if (res->type == ACPI_RESOURCE_TYPE_FIXED_MEMORY32) {
3837752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock		struct acpi_resource_fixed_memory32 *fixmem32 =
3847752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock			&res->data.fixed_memory32;
3857752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock		if (!fixmem32)
3867752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock			return AE_OK;
3877752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock		if ((mcfg_res->start >= fixmem32->address) &&
38875e613cdc7bb2ba3795b1bc3ddf19476c767ba68Yinghai Lu		    (mcfg_res->end < (fixmem32->address +
3897752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock				      fixmem32->address_length))) {
3907752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock			mcfg_res->flags = 1;
3917752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock			return AE_CTRL_TERMINATE;
3927752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock		}
3937752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock	}
3947752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock	if ((res->type != ACPI_RESOURCE_TYPE_ADDRESS32) &&
3957752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock	    (res->type != ACPI_RESOURCE_TYPE_ADDRESS64))
3967752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock		return AE_OK;
3977752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock
3987752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock	status = acpi_resource_to_address64(res, &address);
3997752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock	if (ACPI_FAILURE(status) ||
4007752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock	   (address.address_length <= 0) ||
4017752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock	   (address.resource_type != ACPI_MEMORY_RANGE))
4027752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock		return AE_OK;
4037752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock
4047752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock	if ((mcfg_res->start >= address.minimum) &&
40575e613cdc7bb2ba3795b1bc3ddf19476c767ba68Yinghai Lu	    (mcfg_res->end < (address.minimum + address.address_length))) {
4067752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock		mcfg_res->flags = 1;
4077752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock		return AE_CTRL_TERMINATE;
4087752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock	}
4097752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock	return AE_OK;
4107752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock}
4117752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock
412a18e3690a52790a034d6540d54e8e1f1cd125da2Greg Kroah-Hartmanstatic acpi_status find_mboard_resource(acpi_handle handle, u32 lvl,
413a18e3690a52790a034d6540d54e8e1f1cd125da2Greg Kroah-Hartman					void *context, void **rv)
4147752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock{
4157752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock	struct resource *mcfg_res = context;
4167752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock
4177752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock	acpi_walk_resources(handle, METHOD_NAME__CRS,
4187752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock			    check_mcfg_resource, context);
4197752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock
4207752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock	if (mcfg_res->flags)
4217752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock		return AE_CTRL_TERMINATE;
4227752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock
4237752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock	return AE_OK;
4247752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock}
4257752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock
426a18e3690a52790a034d6540d54e8e1f1cd125da2Greg Kroah-Hartmanstatic int is_acpi_reserved(u64 start, u64 end, unsigned not_used)
4277752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock{
4287752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock	struct resource mcfg_res;
4297752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock
4307752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock	mcfg_res.start = start;
43175e613cdc7bb2ba3795b1bc3ddf19476c767ba68Yinghai Lu	mcfg_res.end = end - 1;
4327752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock	mcfg_res.flags = 0;
4337752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock
4347752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock	acpi_get_devices("PNP0C01", find_mboard_resource, &mcfg_res, NULL);
4357752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock
4367752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock	if (!mcfg_res.flags)
4377752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock		acpi_get_devices("PNP0C02", find_mboard_resource, &mcfg_res,
4387752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock				 NULL);
4397752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock
4407752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock	return mcfg_res.flags;
4417752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock}
4427752d5cfe3d11ca0bb9c673ec38bd78ba6578f8eRobert Hancock
443a83fe32fa668c0a17b3f99a1480b006f7d649924Yinghai Lutypedef int (*check_reserved_t)(u64 start, u64 end, unsigned type);
444a83fe32fa668c0a17b3f99a1480b006f7d649924Yinghai Lu
44595c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liustatic int __ref is_mmconf_reserved(check_reserved_t is_reserved,
44695c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu				    struct pci_mmcfg_region *cfg,
44795c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu				    struct device *dev, int with_e820)
448a83fe32fa668c0a17b3f99a1480b006f7d649924Yinghai Lu{
4492f2a8b9c90279e75f87aaf322a948bdced27e89fBjorn Helgaas	u64 addr = cfg->res.start;
4502f2a8b9c90279e75f87aaf322a948bdced27e89fBjorn Helgaas	u64 size = resource_size(&cfg->res);
451a83fe32fa668c0a17b3f99a1480b006f7d649924Yinghai Lu	u64 old_size = size;
45295c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu	int num_buses;
45395c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu	char *method = with_e820 ? "E820" : "ACPI motherboard resources";
454a83fe32fa668c0a17b3f99a1480b006f7d649924Yinghai Lu
455044cd80942e47b9de0915b627902adf05c52377fYinghai Lu	while (!is_reserved(addr, addr + size, E820_RESERVED)) {
456a83fe32fa668c0a17b3f99a1480b006f7d649924Yinghai Lu		size >>= 1;
457a83fe32fa668c0a17b3f99a1480b006f7d649924Yinghai Lu		if (size < (16UL<<20))
458a83fe32fa668c0a17b3f99a1480b006f7d649924Yinghai Lu			break;
459a83fe32fa668c0a17b3f99a1480b006f7d649924Yinghai Lu	}
460a83fe32fa668c0a17b3f99a1480b006f7d649924Yinghai Lu
46195c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu	if (size < (16UL<<20) && size != old_size)
46295c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu		return 0;
46395c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu
46495c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu	if (dev)
46595c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu		dev_info(dev, "MMCONFIG at %pR reserved in %s\n",
46695c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu			 &cfg->res, method);
46795c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu	else
46824c97f04c4570e02c5cf4b97c73ab9dc27bacdbeJiang Liu		pr_info(PREFIX "MMCONFIG at %pR reserved in %s\n",
46995c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu		       &cfg->res, method);
47095c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu
47195c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu	if (old_size != size) {
47295c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu		/* update end_bus */
47395c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu		cfg->end_bus = cfg->start_bus + ((size>>20) - 1);
47495c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu		num_buses = cfg->end_bus - cfg->start_bus + 1;
47595c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu		cfg->res.end = cfg->res.start +
47695c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu		    PCI_MMCFG_BUS_OFFSET(num_buses) - 1;
47795c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu		snprintf(cfg->name, PCI_MMCFG_RESOURCE_NAME_LEN,
47895c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu			 "PCI MMCONFIG %04x [bus %02x-%02x]",
47995c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu			 cfg->segment, cfg->start_bus, cfg->end_bus);
48095c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu
48195c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu		if (dev)
48295c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu			dev_info(dev,
48395c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu				"MMCONFIG "
48495c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu				"at %pR (base %#lx) (size reduced!)\n",
48595c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu				&cfg->res, (unsigned long) cfg->address);
48695c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu		else
48724c97f04c4570e02c5cf4b97c73ab9dc27bacdbeJiang Liu			pr_info(PREFIX
48895c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu				"MMCONFIG for %04x [bus%02x-%02x] "
48995c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu				"at %pR (base %#lx) (size reduced!)\n",
49095c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu				cfg->segment, cfg->start_bus, cfg->end_bus,
49195c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu				&cfg->res, (unsigned long) cfg->address);
492a83fe32fa668c0a17b3f99a1480b006f7d649924Yinghai Lu	}
493a83fe32fa668c0a17b3f99a1480b006f7d649924Yinghai Lu
49495c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu	return 1;
495a83fe32fa668c0a17b3f99a1480b006f7d649924Yinghai Lu}
496a83fe32fa668c0a17b3f99a1480b006f7d649924Yinghai Lu
49795c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liustatic int __ref pci_mmcfg_check_reserved(struct device *dev,
49895c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu		  struct pci_mmcfg_region *cfg, int early)
4992a76c450bd0377f715caf313ded530290d7dc7d7Jiang Liu{
5002a76c450bd0377f715caf313ded530290d7dc7d7Jiang Liu	if (!early && !acpi_disabled) {
50195c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu		if (is_mmconf_reserved(is_acpi_reserved, cfg, dev, 0))
5022a76c450bd0377f715caf313ded530290d7dc7d7Jiang Liu			return 1;
50395c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu
50495c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu		if (dev)
50595c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu			dev_info(dev, FW_INFO
50695c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu				 "MMCONFIG at %pR not reserved in "
50795c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu				 "ACPI motherboard resources\n",
50895c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu				 &cfg->res);
5092a76c450bd0377f715caf313ded530290d7dc7d7Jiang Liu		else
51024c97f04c4570e02c5cf4b97c73ab9dc27bacdbeJiang Liu			pr_info(FW_INFO PREFIX
5112a76c450bd0377f715caf313ded530290d7dc7d7Jiang Liu			       "MMCONFIG at %pR not reserved in "
5122a76c450bd0377f715caf313ded530290d7dc7d7Jiang Liu			       "ACPI motherboard resources\n",
5132a76c450bd0377f715caf313ded530290d7dc7d7Jiang Liu			       &cfg->res);
5142a76c450bd0377f715caf313ded530290d7dc7d7Jiang Liu	}
5152a76c450bd0377f715caf313ded530290d7dc7d7Jiang Liu
51695c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu	/*
51795c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu	 * e820_all_mapped() is marked as __init.
51895c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu	 * All entries from ACPI MCFG table have been checked at boot time.
51995c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu	 * For MCFG information constructed from hotpluggable host bridge's
52095c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu	 * _CBA method, just assume it's reserved.
52195c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu	 */
52295c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu	if (pci_mmcfg_running_state)
52395c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu		return 1;
52495c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu
5252a76c450bd0377f715caf313ded530290d7dc7d7Jiang Liu	/* Don't try to do this check unless configuration
5262a76c450bd0377f715caf313ded530290d7dc7d7Jiang Liu	   type 1 is available. how about type 2 ?*/
5272a76c450bd0377f715caf313ded530290d7dc7d7Jiang Liu	if (raw_pci_ops)
52895c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu		return is_mmconf_reserved(e820_all_mapped, cfg, dev, 1);
5292a76c450bd0377f715caf313ded530290d7dc7d7Jiang Liu
5302a76c450bd0377f715caf313ded530290d7dc7d7Jiang Liu	return 0;
5312a76c450bd0377f715caf313ded530290d7dc7d7Jiang Liu}
5322a76c450bd0377f715caf313ded530290d7dc7d7Jiang Liu
533bb63b4219976d48ed6d22ac33c18be334fb5a78cYinghai Lustatic void __init pci_mmcfg_reject_broken(int early)
53444de0203fab205417b24322272c53ee0883c36e7OGAWA Hirofumi{
535987c367b4e93be6826394e7c9cc14d28bb5c8810Bjorn Helgaas	struct pci_mmcfg_region *cfg;
53626054ed02bb20f5b2e02d92cb6f0be0e2b0196d5OGAWA Hirofumi
537ff097ddd4aeac790fd51d013c79c2f18ec9a7117Bjorn Helgaas	list_for_each_entry(cfg, &pci_mmcfg_list, list) {
53895c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu		if (pci_mmcfg_check_reserved(NULL, cfg, early) == 0) {
53924c97f04c4570e02c5cf4b97c73ab9dc27bacdbeJiang Liu			pr_info(PREFIX "not using MMCONFIG\n");
5402a76c450bd0377f715caf313ded530290d7dc7d7Jiang Liu			free_all_mmcfg();
5412a76c450bd0377f715caf313ded530290d7dc7d7Jiang Liu			return;
542a02ce953a14d6a8aab721b129b3c8ff4981a76e6Feng Tang		}
54344de0203fab205417b24322272c53ee0883c36e7OGAWA Hirofumi	}
54444de0203fab205417b24322272c53ee0883c36e7OGAWA Hirofumi}
54544de0203fab205417b24322272c53ee0883c36e7OGAWA Hirofumi
5469a08f7d3506019e3833cd4394ca0d7da0ae3689fBjorn Helgaasstatic int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
5479a08f7d3506019e3833cd4394ca0d7da0ae3689fBjorn Helgaas					struct acpi_mcfg_allocation *cfg)
548c4bf2f372db09ef8d16a25a60d523bfa1c50f7b5Len Brown{
5499a08f7d3506019e3833cd4394ca0d7da0ae3689fBjorn Helgaas	int year;
5509a08f7d3506019e3833cd4394ca0d7da0ae3689fBjorn Helgaas
5519a08f7d3506019e3833cd4394ca0d7da0ae3689fBjorn Helgaas	if (cfg->address < 0xFFFFFFFF)
5529a08f7d3506019e3833cd4394ca0d7da0ae3689fBjorn Helgaas		return 0;
5539a08f7d3506019e3833cd4394ca0d7da0ae3689fBjorn Helgaas
554526018bc5eccfe3177780f03d2aaba0efee40720Mike Travis	if (!strncmp(mcfg->header.oem_id, "SGI", 3))
5559a08f7d3506019e3833cd4394ca0d7da0ae3689fBjorn Helgaas		return 0;
556c4bf2f372db09ef8d16a25a60d523bfa1c50f7b5Len Brown
5579a08f7d3506019e3833cd4394ca0d7da0ae3689fBjorn Helgaas	if (mcfg->header.revision >= 1) {
5589a08f7d3506019e3833cd4394ca0d7da0ae3689fBjorn Helgaas		if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) &&
5599a08f7d3506019e3833cd4394ca0d7da0ae3689fBjorn Helgaas		    year >= 2010)
5609a08f7d3506019e3833cd4394ca0d7da0ae3689fBjorn Helgaas			return 0;
5619a08f7d3506019e3833cd4394ca0d7da0ae3689fBjorn Helgaas	}
5629a08f7d3506019e3833cd4394ca0d7da0ae3689fBjorn Helgaas
56324c97f04c4570e02c5cf4b97c73ab9dc27bacdbeJiang Liu	pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx "
5649a08f7d3506019e3833cd4394ca0d7da0ae3689fBjorn Helgaas	       "is above 4GB, ignored\n", cfg->pci_segment,
5659a08f7d3506019e3833cd4394ca0d7da0ae3689fBjorn Helgaas	       cfg->start_bus_number, cfg->end_bus_number, cfg->address);
5669a08f7d3506019e3833cd4394ca0d7da0ae3689fBjorn Helgaas	return -EINVAL;
567c4bf2f372db09ef8d16a25a60d523bfa1c50f7b5Len Brown}
568c4bf2f372db09ef8d16a25a60d523bfa1c50f7b5Len Brown
569c4bf2f372db09ef8d16a25a60d523bfa1c50f7b5Len Brownstatic int __init pci_parse_mcfg(struct acpi_table_header *header)
570c4bf2f372db09ef8d16a25a60d523bfa1c50f7b5Len Brown{
571c4bf2f372db09ef8d16a25a60d523bfa1c50f7b5Len Brown	struct acpi_table_mcfg *mcfg;
572d3578ef7aab5b9bb874d085609b3ed5d9abffc48Bjorn Helgaas	struct acpi_mcfg_allocation *cfg_table, *cfg;
573c4bf2f372db09ef8d16a25a60d523bfa1c50f7b5Len Brown	unsigned long i;
5747da7d360ae025158d09aab18d66f5d2fe3c02252Bjorn Helgaas	int entries;
575c4bf2f372db09ef8d16a25a60d523bfa1c50f7b5Len Brown
576c4bf2f372db09ef8d16a25a60d523bfa1c50f7b5Len Brown	if (!header)
577c4bf2f372db09ef8d16a25a60d523bfa1c50f7b5Len Brown		return -EINVAL;
578c4bf2f372db09ef8d16a25a60d523bfa1c50f7b5Len Brown
579c4bf2f372db09ef8d16a25a60d523bfa1c50f7b5Len Brown	mcfg = (struct acpi_table_mcfg *)header;
580c4bf2f372db09ef8d16a25a60d523bfa1c50f7b5Len Brown
581c4bf2f372db09ef8d16a25a60d523bfa1c50f7b5Len Brown	/* how many config structures do we have */
5827da7d360ae025158d09aab18d66f5d2fe3c02252Bjorn Helgaas	free_all_mmcfg();
583e823d6ff581c5d1d76aa8c73a202d7d1419d34b8Bjorn Helgaas	entries = 0;
584c4bf2f372db09ef8d16a25a60d523bfa1c50f7b5Len Brown	i = header->length - sizeof(struct acpi_table_mcfg);
585c4bf2f372db09ef8d16a25a60d523bfa1c50f7b5Len Brown	while (i >= sizeof(struct acpi_mcfg_allocation)) {
586e823d6ff581c5d1d76aa8c73a202d7d1419d34b8Bjorn Helgaas		entries++;
587c4bf2f372db09ef8d16a25a60d523bfa1c50f7b5Len Brown		i -= sizeof(struct acpi_mcfg_allocation);
5884b8073e467e6a66b6a5a8e799d28bc3b243c0d78Peter Senna Tschudin	}
589e823d6ff581c5d1d76aa8c73a202d7d1419d34b8Bjorn Helgaas	if (entries == 0) {
59024c97f04c4570e02c5cf4b97c73ab9dc27bacdbeJiang Liu		pr_err(PREFIX "MMCONFIG has no entries\n");
591c4bf2f372db09ef8d16a25a60d523bfa1c50f7b5Len Brown		return -ENODEV;
592c4bf2f372db09ef8d16a25a60d523bfa1c50f7b5Len Brown	}
593c4bf2f372db09ef8d16a25a60d523bfa1c50f7b5Len Brown
594d3578ef7aab5b9bb874d085609b3ed5d9abffc48Bjorn Helgaas	cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1];
595e823d6ff581c5d1d76aa8c73a202d7d1419d34b8Bjorn Helgaas	for (i = 0; i < entries; i++) {
596d3578ef7aab5b9bb874d085609b3ed5d9abffc48Bjorn Helgaas		cfg = &cfg_table[i];
597d3578ef7aab5b9bb874d085609b3ed5d9abffc48Bjorn Helgaas		if (acpi_mcfg_check_entry(mcfg, cfg)) {
5987da7d360ae025158d09aab18d66f5d2fe3c02252Bjorn Helgaas			free_all_mmcfg();
599c4bf2f372db09ef8d16a25a60d523bfa1c50f7b5Len Brown			return -ENODEV;
600c4bf2f372db09ef8d16a25a60d523bfa1c50f7b5Len Brown		}
6017da7d360ae025158d09aab18d66f5d2fe3c02252Bjorn Helgaas
6027da7d360ae025158d09aab18d66f5d2fe3c02252Bjorn Helgaas		if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number,
6037da7d360ae025158d09aab18d66f5d2fe3c02252Bjorn Helgaas				   cfg->end_bus_number, cfg->address) == NULL) {
60424c97f04c4570e02c5cf4b97c73ab9dc27bacdbeJiang Liu			pr_warn(PREFIX "no memory for MCFG entries\n");
6057da7d360ae025158d09aab18d66f5d2fe3c02252Bjorn Helgaas			free_all_mmcfg();
6067da7d360ae025158d09aab18d66f5d2fe3c02252Bjorn Helgaas			return -ENOMEM;
6077da7d360ae025158d09aab18d66f5d2fe3c02252Bjorn Helgaas		}
608c4bf2f372db09ef8d16a25a60d523bfa1c50f7b5Len Brown	}
609c4bf2f372db09ef8d16a25a60d523bfa1c50f7b5Len Brown
610c4bf2f372db09ef8d16a25a60d523bfa1c50f7b5Len Brown	return 0;
611c4bf2f372db09ef8d16a25a60d523bfa1c50f7b5Len Brown}
612c4bf2f372db09ef8d16a25a60d523bfa1c50f7b5Len Brown
613968cbfad1a727b5689ae620f4d970abf6ce73340Thomas Gleixnerstatic void __init __pci_mmcfg_init(int early)
614b78673944b22b662b270c8bba5c198f19e4ee4e1Olivier Galibert{
615068258bc15439c11a966e873f931cc8e513dca61Yinghai Lu	pci_mmcfg_reject_broken(early);
616ff097ddd4aeac790fd51d013c79c2f18ec9a7117Bjorn Helgaas	if (list_empty(&pci_mmcfg_list))
617b78673944b22b662b270c8bba5c198f19e4ee4e1Olivier Galibert		return;
618b78673944b22b662b270c8bba5c198f19e4ee4e1Olivier Galibert
619a3170c1f924ce2565c4e160b9b095e65c03b2dc6Jan Beulich	if (pcibios_last_bus < 0) {
620a3170c1f924ce2565c4e160b9b095e65c03b2dc6Jan Beulich		const struct pci_mmcfg_region *cfg;
621a3170c1f924ce2565c4e160b9b095e65c03b2dc6Jan Beulich
622a3170c1f924ce2565c4e160b9b095e65c03b2dc6Jan Beulich		list_for_each_entry(cfg, &pci_mmcfg_list, list) {
623a3170c1f924ce2565c4e160b9b095e65c03b2dc6Jan Beulich			if (cfg->segment)
624a3170c1f924ce2565c4e160b9b095e65c03b2dc6Jan Beulich				break;
625a3170c1f924ce2565c4e160b9b095e65c03b2dc6Jan Beulich			pcibios_last_bus = cfg->end_bus;
626a3170c1f924ce2565c4e160b9b095e65c03b2dc6Jan Beulich		}
627a3170c1f924ce2565c4e160b9b095e65c03b2dc6Jan Beulich	}
628a3170c1f924ce2565c4e160b9b095e65c03b2dc6Jan Beulich
629ebd60cd64f8ab1170102c3ab072eb73042b7a33dYinghai Lu	if (pci_mmcfg_arch_init())
630b78673944b22b662b270c8bba5c198f19e4ee4e1Olivier Galibert		pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF;
631ebd60cd64f8ab1170102c3ab072eb73042b7a33dYinghai Lu	else {
63266e8850a2a34e6c52105d92a0f0054b304cb7140Jiang Liu		free_all_mmcfg();
6339c95111b330d2ddf851444528a7608f267cbb50cJiang Liu		pci_mmcfg_arch_init_failed = true;
634b78673944b22b662b270c8bba5c198f19e4ee4e1Olivier Galibert	}
635b78673944b22b662b270c8bba5c198f19e4ee4e1Olivier Galibert}
636a5ba7971045a90a36cef8f7d5a3075600b475b74Aaron Durbin
637574a59414083df3911e5a1514742959b412b6947Jiang Liustatic int __initdata known_bridge;
638574a59414083df3911e5a1514742959b412b6947Jiang Liu
639bb63b4219976d48ed6d22ac33c18be334fb5a78cYinghai Luvoid __init pci_mmcfg_early_init(void)
64005c58b8ac77639c17205f0b2a2d9eb1971dc47adYinghai Lu{
641574a59414083df3911e5a1514742959b412b6947Jiang Liu	if (pci_probe & PCI_PROBE_MMCONF) {
642574a59414083df3911e5a1514742959b412b6947Jiang Liu		if (pci_mmcfg_check_hostbridge())
643574a59414083df3911e5a1514742959b412b6947Jiang Liu			known_bridge = 1;
644574a59414083df3911e5a1514742959b412b6947Jiang Liu		else
645574a59414083df3911e5a1514742959b412b6947Jiang Liu			acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
646574a59414083df3911e5a1514742959b412b6947Jiang Liu		__pci_mmcfg_init(1);
647574a59414083df3911e5a1514742959b412b6947Jiang Liu	}
64805c58b8ac77639c17205f0b2a2d9eb1971dc47adYinghai Lu}
64905c58b8ac77639c17205f0b2a2d9eb1971dc47adYinghai Lu
65005c58b8ac77639c17205f0b2a2d9eb1971dc47adYinghai Luvoid __init pci_mmcfg_late_init(void)
65105c58b8ac77639c17205f0b2a2d9eb1971dc47adYinghai Lu{
652574a59414083df3911e5a1514742959b412b6947Jiang Liu	/* MMCONFIG disabled */
653574a59414083df3911e5a1514742959b412b6947Jiang Liu	if ((pci_probe & PCI_PROBE_MMCONF) == 0)
654574a59414083df3911e5a1514742959b412b6947Jiang Liu		return;
655574a59414083df3911e5a1514742959b412b6947Jiang Liu
656574a59414083df3911e5a1514742959b412b6947Jiang Liu	if (known_bridge)
657574a59414083df3911e5a1514742959b412b6947Jiang Liu		return;
658574a59414083df3911e5a1514742959b412b6947Jiang Liu
659574a59414083df3911e5a1514742959b412b6947Jiang Liu	/* MMCONFIG hasn't been enabled yet, try again */
660574a59414083df3911e5a1514742959b412b6947Jiang Liu	if (pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF) {
661574a59414083df3911e5a1514742959b412b6947Jiang Liu		acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
662574a59414083df3911e5a1514742959b412b6947Jiang Liu		__pci_mmcfg_init(0);
663574a59414083df3911e5a1514742959b412b6947Jiang Liu	}
66405c58b8ac77639c17205f0b2a2d9eb1971dc47adYinghai Lu}
66505c58b8ac77639c17205f0b2a2d9eb1971dc47adYinghai Lu
666a5ba7971045a90a36cef8f7d5a3075600b475b74Aaron Durbinstatic int __init pci_mmcfg_late_insert_resources(void)
667a5ba7971045a90a36cef8f7d5a3075600b475b74Aaron Durbin{
66866e8850a2a34e6c52105d92a0f0054b304cb7140Jiang Liu	struct pci_mmcfg_region *cfg;
66966e8850a2a34e6c52105d92a0f0054b304cb7140Jiang Liu
67095c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu	pci_mmcfg_running_state = true;
67195c5e92f4f691bbaba40bbf3decfc8e13b6ea897Jiang Liu
67266e8850a2a34e6c52105d92a0f0054b304cb7140Jiang Liu	/* If we are not using MMCONFIG, don't insert the resources. */
67366e8850a2a34e6c52105d92a0f0054b304cb7140Jiang Liu	if ((pci_probe & PCI_PROBE_MMCONF) == 0)
674a5ba7971045a90a36cef8f7d5a3075600b475b74Aaron Durbin		return 1;
675a5ba7971045a90a36cef8f7d5a3075600b475b74Aaron Durbin
676a5ba7971045a90a36cef8f7d5a3075600b475b74Aaron Durbin	/*
677a5ba7971045a90a36cef8f7d5a3075600b475b74Aaron Durbin	 * Attempt to insert the mmcfg resources but not with the busy flag
678a5ba7971045a90a36cef8f7d5a3075600b475b74Aaron Durbin	 * marked so it won't cause request errors when __request_region is
679a5ba7971045a90a36cef8f7d5a3075600b475b74Aaron Durbin	 * called.
680a5ba7971045a90a36cef8f7d5a3075600b475b74Aaron Durbin	 */
68166e8850a2a34e6c52105d92a0f0054b304cb7140Jiang Liu	list_for_each_entry(cfg, &pci_mmcfg_list, list)
68266e8850a2a34e6c52105d92a0f0054b304cb7140Jiang Liu		if (!cfg->res.parent)
68366e8850a2a34e6c52105d92a0f0054b304cb7140Jiang Liu			insert_resource(&iomem_resource, &cfg->res);
684a5ba7971045a90a36cef8f7d5a3075600b475b74Aaron Durbin
685a5ba7971045a90a36cef8f7d5a3075600b475b74Aaron Durbin	return 0;
686a5ba7971045a90a36cef8f7d5a3075600b475b74Aaron Durbin}
687a5ba7971045a90a36cef8f7d5a3075600b475b74Aaron Durbin
688a5ba7971045a90a36cef8f7d5a3075600b475b74Aaron Durbin/*
689a5ba7971045a90a36cef8f7d5a3075600b475b74Aaron Durbin * Perform MMCONFIG resource insertion after PCI initialization to allow for
690a5ba7971045a90a36cef8f7d5a3075600b475b74Aaron Durbin * misprogrammed MCFG tables that state larger sizes but actually conflict
691a5ba7971045a90a36cef8f7d5a3075600b475b74Aaron Durbin * with other system resources.
692a5ba7971045a90a36cef8f7d5a3075600b475b74Aaron Durbin */
693a5ba7971045a90a36cef8f7d5a3075600b475b74Aaron Durbinlate_initcall(pci_mmcfg_late_insert_resources);
6949c95111b330d2ddf851444528a7608f267cbb50cJiang Liu
6959c95111b330d2ddf851444528a7608f267cbb50cJiang Liu/* Add MMCFG information for host bridges */
696a18e3690a52790a034d6540d54e8e1f1cd125da2Greg Kroah-Hartmanint pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
697a18e3690a52790a034d6540d54e8e1f1cd125da2Greg Kroah-Hartman			phys_addr_t addr)
6989c95111b330d2ddf851444528a7608f267cbb50cJiang Liu{
6999c95111b330d2ddf851444528a7608f267cbb50cJiang Liu	int rc;
7009c95111b330d2ddf851444528a7608f267cbb50cJiang Liu	struct resource *tmp = NULL;
7019c95111b330d2ddf851444528a7608f267cbb50cJiang Liu	struct pci_mmcfg_region *cfg;
7029c95111b330d2ddf851444528a7608f267cbb50cJiang Liu
7039c95111b330d2ddf851444528a7608f267cbb50cJiang Liu	if (!(pci_probe & PCI_PROBE_MMCONF) || pci_mmcfg_arch_init_failed)
7049c95111b330d2ddf851444528a7608f267cbb50cJiang Liu		return -ENODEV;
7059c95111b330d2ddf851444528a7608f267cbb50cJiang Liu
70667d470e0e1711ca4a4c3a0e5524e0d580654053eBjorn Helgaas	if (start > end)
7079c95111b330d2ddf851444528a7608f267cbb50cJiang Liu		return -EINVAL;
7089c95111b330d2ddf851444528a7608f267cbb50cJiang Liu
7099c95111b330d2ddf851444528a7608f267cbb50cJiang Liu	mutex_lock(&pci_mmcfg_lock);
7109c95111b330d2ddf851444528a7608f267cbb50cJiang Liu	cfg = pci_mmconfig_lookup(seg, start);
7119c95111b330d2ddf851444528a7608f267cbb50cJiang Liu	if (cfg) {
7129c95111b330d2ddf851444528a7608f267cbb50cJiang Liu		if (cfg->end_bus < end)
7139c95111b330d2ddf851444528a7608f267cbb50cJiang Liu			dev_info(dev, FW_INFO
7149c95111b330d2ddf851444528a7608f267cbb50cJiang Liu				 "MMCONFIG for "
7159c95111b330d2ddf851444528a7608f267cbb50cJiang Liu				 "domain %04x [bus %02x-%02x] "
7169c95111b330d2ddf851444528a7608f267cbb50cJiang Liu				 "only partially covers this bridge\n",
7179c95111b330d2ddf851444528a7608f267cbb50cJiang Liu				  cfg->segment, cfg->start_bus, cfg->end_bus);
7189c95111b330d2ddf851444528a7608f267cbb50cJiang Liu		mutex_unlock(&pci_mmcfg_lock);
7199c95111b330d2ddf851444528a7608f267cbb50cJiang Liu		return -EEXIST;
7209c95111b330d2ddf851444528a7608f267cbb50cJiang Liu	}
7219c95111b330d2ddf851444528a7608f267cbb50cJiang Liu
72267d470e0e1711ca4a4c3a0e5524e0d580654053eBjorn Helgaas	if (!addr) {
72367d470e0e1711ca4a4c3a0e5524e0d580654053eBjorn Helgaas		mutex_unlock(&pci_mmcfg_lock);
72467d470e0e1711ca4a4c3a0e5524e0d580654053eBjorn Helgaas		return -EINVAL;
72567d470e0e1711ca4a4c3a0e5524e0d580654053eBjorn Helgaas	}
72667d470e0e1711ca4a4c3a0e5524e0d580654053eBjorn Helgaas
7279c95111b330d2ddf851444528a7608f267cbb50cJiang Liu	rc = -EBUSY;
7289c95111b330d2ddf851444528a7608f267cbb50cJiang Liu	cfg = pci_mmconfig_alloc(seg, start, end, addr);
7299c95111b330d2ddf851444528a7608f267cbb50cJiang Liu	if (cfg == NULL) {
7309c95111b330d2ddf851444528a7608f267cbb50cJiang Liu		dev_warn(dev, "fail to add MMCONFIG (out of memory)\n");
7319c95111b330d2ddf851444528a7608f267cbb50cJiang Liu		rc = -ENOMEM;
7329c95111b330d2ddf851444528a7608f267cbb50cJiang Liu	} else if (!pci_mmcfg_check_reserved(dev, cfg, 0)) {
7339c95111b330d2ddf851444528a7608f267cbb50cJiang Liu		dev_warn(dev, FW_BUG "MMCONFIG %pR isn't reserved\n",
7349c95111b330d2ddf851444528a7608f267cbb50cJiang Liu			 &cfg->res);
7359c95111b330d2ddf851444528a7608f267cbb50cJiang Liu	} else {
7369c95111b330d2ddf851444528a7608f267cbb50cJiang Liu		/* Insert resource if it's not in boot stage */
7379c95111b330d2ddf851444528a7608f267cbb50cJiang Liu		if (pci_mmcfg_running_state)
7389c95111b330d2ddf851444528a7608f267cbb50cJiang Liu			tmp = insert_resource_conflict(&iomem_resource,
7399c95111b330d2ddf851444528a7608f267cbb50cJiang Liu						       &cfg->res);
7409c95111b330d2ddf851444528a7608f267cbb50cJiang Liu
7419c95111b330d2ddf851444528a7608f267cbb50cJiang Liu		if (tmp) {
7429c95111b330d2ddf851444528a7608f267cbb50cJiang Liu			dev_warn(dev,
7439c95111b330d2ddf851444528a7608f267cbb50cJiang Liu				 "MMCONFIG %pR conflicts with "
7449c95111b330d2ddf851444528a7608f267cbb50cJiang Liu				 "%s %pR\n",
7459c95111b330d2ddf851444528a7608f267cbb50cJiang Liu				 &cfg->res, tmp->name, tmp);
7469c95111b330d2ddf851444528a7608f267cbb50cJiang Liu		} else if (pci_mmcfg_arch_map(cfg)) {
7479c95111b330d2ddf851444528a7608f267cbb50cJiang Liu			dev_warn(dev, "fail to map MMCONFIG %pR.\n",
7489c95111b330d2ddf851444528a7608f267cbb50cJiang Liu				 &cfg->res);
7499c95111b330d2ddf851444528a7608f267cbb50cJiang Liu		} else {
7509c95111b330d2ddf851444528a7608f267cbb50cJiang Liu			list_add_sorted(cfg);
7519c95111b330d2ddf851444528a7608f267cbb50cJiang Liu			dev_info(dev, "MMCONFIG at %pR (base %#lx)\n",
7529c95111b330d2ddf851444528a7608f267cbb50cJiang Liu				 &cfg->res, (unsigned long)addr);
7539c95111b330d2ddf851444528a7608f267cbb50cJiang Liu			cfg = NULL;
7549c95111b330d2ddf851444528a7608f267cbb50cJiang Liu			rc = 0;
7559c95111b330d2ddf851444528a7608f267cbb50cJiang Liu		}
7569c95111b330d2ddf851444528a7608f267cbb50cJiang Liu	}
7579c95111b330d2ddf851444528a7608f267cbb50cJiang Liu
7589c95111b330d2ddf851444528a7608f267cbb50cJiang Liu	if (cfg) {
7599c95111b330d2ddf851444528a7608f267cbb50cJiang Liu		if (cfg->res.parent)
7609c95111b330d2ddf851444528a7608f267cbb50cJiang Liu			release_resource(&cfg->res);
7619c95111b330d2ddf851444528a7608f267cbb50cJiang Liu		kfree(cfg);
7629c95111b330d2ddf851444528a7608f267cbb50cJiang Liu	}
7639c95111b330d2ddf851444528a7608f267cbb50cJiang Liu
7649c95111b330d2ddf851444528a7608f267cbb50cJiang Liu	mutex_unlock(&pci_mmcfg_lock);
7659c95111b330d2ddf851444528a7608f267cbb50cJiang Liu
7669c95111b330d2ddf851444528a7608f267cbb50cJiang Liu	return rc;
7679c95111b330d2ddf851444528a7608f267cbb50cJiang Liu}
7689c95111b330d2ddf851444528a7608f267cbb50cJiang Liu
7699c95111b330d2ddf851444528a7608f267cbb50cJiang Liu/* Delete MMCFG information for host bridges */
7709c95111b330d2ddf851444528a7608f267cbb50cJiang Liuint pci_mmconfig_delete(u16 seg, u8 start, u8 end)
7719c95111b330d2ddf851444528a7608f267cbb50cJiang Liu{
7729c95111b330d2ddf851444528a7608f267cbb50cJiang Liu	struct pci_mmcfg_region *cfg;
7739c95111b330d2ddf851444528a7608f267cbb50cJiang Liu
7749c95111b330d2ddf851444528a7608f267cbb50cJiang Liu	mutex_lock(&pci_mmcfg_lock);
7759c95111b330d2ddf851444528a7608f267cbb50cJiang Liu	list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
7769c95111b330d2ddf851444528a7608f267cbb50cJiang Liu		if (cfg->segment == seg && cfg->start_bus == start &&
7779c95111b330d2ddf851444528a7608f267cbb50cJiang Liu		    cfg->end_bus == end) {
7789c95111b330d2ddf851444528a7608f267cbb50cJiang Liu			list_del_rcu(&cfg->list);
7799c95111b330d2ddf851444528a7608f267cbb50cJiang Liu			synchronize_rcu();
7809c95111b330d2ddf851444528a7608f267cbb50cJiang Liu			pci_mmcfg_arch_unmap(cfg);
7819c95111b330d2ddf851444528a7608f267cbb50cJiang Liu			if (cfg->res.parent)
7829c95111b330d2ddf851444528a7608f267cbb50cJiang Liu				release_resource(&cfg->res);
7839c95111b330d2ddf851444528a7608f267cbb50cJiang Liu			mutex_unlock(&pci_mmcfg_lock);
7849c95111b330d2ddf851444528a7608f267cbb50cJiang Liu			kfree(cfg);
7859c95111b330d2ddf851444528a7608f267cbb50cJiang Liu			return 0;
7869c95111b330d2ddf851444528a7608f267cbb50cJiang Liu		}
7879c95111b330d2ddf851444528a7608f267cbb50cJiang Liu	mutex_unlock(&pci_mmcfg_lock);
7889c95111b330d2ddf851444528a7608f267cbb50cJiang Liu
7899c95111b330d2ddf851444528a7608f267cbb50cJiang Liu	return -ENOENT;
7909c95111b330d2ddf851444528a7608f267cbb50cJiang Liu}
791