aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGraeme Gregory <graeme.gregory@linaro.org>2015-07-10 13:38:21 +0100
committerGraeme Gregory <graeme.gregory@linaro.org>2015-07-10 13:38:21 +0100
commit5145d4994555d0bdf7f986891856672c226acd43 (patch)
tree95806408731ca761f6880d2487867547859fede8
parent490eabdfb555a10cd0c05479660355bb7802915b (diff)
parent5ab31d126bca889fa1426e9fe03a826ef53a3e42 (diff)
downloadleg-kernel-5145d4994555d0bdf7f986891856672c226acd43.tar.gz
Merge branch 'acpi-topic-mmconfig' into leg-kernel
-rw-r--r--arch/x86/Kconfig4
-rw-r--r--arch/x86/include/asm/pci_x86.h34
-rw-r--r--arch/x86/pci/Makefile4
-rw-r--r--arch/x86/pci/acpi.c3
-rw-r--r--arch/x86/pci/mmconfig-shared.c328
-rw-r--r--arch/x86/pci/mmconfig_32.c47
-rw-r--r--arch/x86/pci/mmconfig_64.c153
-rw-r--r--arch/x86/pci/numachip.c29
-rw-r--r--drivers/acpi/Makefile1
-rw-r--r--drivers/acpi/mcfg.c103
-rw-r--r--drivers/pci/Kconfig10
-rw-r--r--drivers/pci/Makefile5
-rw-r--r--drivers/pci/ecam.c358
-rw-r--r--drivers/xen/pci.c9
-rw-r--r--include/linux/acpi.h2
-rw-r--r--include/linux/ecam.h56
16 files changed, 671 insertions, 475 deletions
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 55bced17dc95..51552607f8b1 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -149,6 +149,8 @@ config X86
select VIRT_TO_BUS
select X86_DEV_DMA_OPS if X86_64
select X86_FEATURE_NAMES if PROC_FS
+ select HAVE_PCI_ECAM
+ select GENERIC_PCI_ECAM if X86_64
config INSTRUCTION_DECODER
def_bool y
@@ -2277,6 +2279,7 @@ config PCI_DIRECT
config PCI_MMCONFIG
def_bool y
+ select PCI_ECAM
depends on X86_32 && PCI && (ACPI || SFI) && (PCI_GOMMCONFIG || PCI_GOANY)
config PCI_OLPC
@@ -2294,6 +2297,7 @@ config PCI_DOMAINS
config PCI_MMCONFIG
bool "Support mmconfig PCI config space access"
+ select PCI_ECAM
depends on X86_64 && PCI && ACPI
config PCI_CNB20LE_QUIRK
diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h
index 164e3f8d3c3d..2ea44a780200 100644
--- a/arch/x86/include/asm/pci_x86.h
+++ b/arch/x86/include/asm/pci_x86.h
@@ -124,40 +124,18 @@ extern int pci_legacy_init(void);
extern void pcibios_fixup_irqs(void);
/* pci-mmconfig.c */
-
-/* "PCI MMCONFIG %04x [bus %02x-%02x]" */
-#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2)
-
-struct pci_mmcfg_region {
- struct list_head list;
- struct resource res;
- u64 address;
- char __iomem *virt;
- u16 segment;
- u8 start_bus;
- u8 end_bus;
- char name[PCI_MMCFG_RESOURCE_NAME_LEN];
-};
-
-extern int __init pci_mmcfg_arch_init(void);
-extern void __init pci_mmcfg_arch_free(void);
-extern int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg);
-extern void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg);
extern int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
phys_addr_t addr);
-extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end);
-extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus);
-
-extern struct list_head pci_mmcfg_list;
-
-#define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20)
/*
* AMD Fam10h CPUs are buggy, and cannot access MMIO config space
- * on their northbrige except through the * %eax register. As such, you MUST
- * NOT use normal IOMEM accesses, you need to only use the magic mmio-config
+ * on their northbridge except through the * %eax register. As such, you MUST
+ * NOT use normal IOMEM accesses, you need to only use the magic mmio_config_*
* accessor functions.
- * In fact just use pci_config_*, nothing else please.
+ *
+ * Please refer to the following doc:
+ * "BIOS and Kernel Developer's Guide (BKDG) For AMD Family 10h Processors",
+ * rev. 3.48, sec 2.11.1, "MMIO Configuration Coding Requirements".
*/
static inline unsigned char mmio_config_readb(void __iomem *pos)
{
diff --git a/arch/x86/pci/Makefile b/arch/x86/pci/Makefile
index 5c6fc3577a49..efc193d9ac09 100644
--- a/arch/x86/pci/Makefile
+++ b/arch/x86/pci/Makefile
@@ -1,7 +1,9 @@
obj-y := i386.o init.o
obj-$(CONFIG_PCI_BIOS) += pcbios.o
-obj-$(CONFIG_PCI_MMCONFIG) += mmconfig_$(BITS).o direct.o mmconfig-shared.o
+mmconfig-y := direct.o mmconfig-shared.o
+mmconfig-$(CONFIG_X86_32) += mmconfig_32.o
+obj-$(CONFIG_PCI_MMCONFIG) += $(mmconfig-y)
obj-$(CONFIG_PCI_DIRECT) += direct.o
obj-$(CONFIG_PCI_OLPC) += olpc.o
obj-$(CONFIG_PCI_XEN) += xen.o
diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c
index ff9911707160..e766b91fdff3 100644
--- a/arch/x86/pci/acpi.c
+++ b/arch/x86/pci/acpi.c
@@ -4,6 +4,7 @@
#include <linux/irq.h>
#include <linux/dmi.h>
#include <linux/slab.h>
+#include <linux/ecam.h>
#include <asm/numa.h>
#include <asm/pci_x86.h>
@@ -211,7 +212,7 @@ static int setup_mcfg_map(struct pci_root_info *info, u16 seg, u8 start,
static void teardown_mcfg_map(struct pci_root_info *info)
{
if (info->mcfg_added) {
- pci_mmconfig_delete(info->segment, info->start_bus,
+ pci_ecam_delete(info->segment, info->start_bus,
info->end_bus);
info->mcfg_added = false;
}
diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c
index dd30b7e08bc2..a26f1f3131cb 100644
--- a/arch/x86/pci/mmconfig-shared.c
+++ b/arch/x86/pci/mmconfig-shared.c
@@ -18,6 +18,7 @@
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/rculist.h>
+#include <linux/ecam.h>
#include <asm/e820.h>
#include <asm/pci_x86.h>
#include <asm/acpi.h>
@@ -27,103 +28,52 @@
/* Indicate if the mmcfg resources have been placed into the resource table. */
static bool pci_mmcfg_running_state;
static bool pci_mmcfg_arch_init_failed;
-static DEFINE_MUTEX(pci_mmcfg_lock);
-LIST_HEAD(pci_mmcfg_list);
-
-static void __init pci_mmconfig_remove(struct pci_mmcfg_region *cfg)
-{
- if (cfg->res.parent)
- release_resource(&cfg->res);
- list_del(&cfg->list);
- kfree(cfg);
-}
+const struct pci_raw_ops pci_mmcfg = {
+ .read = pci_ecam_read,
+ .write = pci_ecam_write,
+};
-static void __init free_all_mmcfg(void)
+static u32
+pci_mmconfig_amd_read(int len, void __iomem *addr)
{
- struct pci_mmcfg_region *cfg, *tmp;
+ u32 data = 0;
- pci_mmcfg_arch_free();
- list_for_each_entry_safe(cfg, tmp, &pci_mmcfg_list, list)
- pci_mmconfig_remove(cfg);
-}
-
-static void list_add_sorted(struct pci_mmcfg_region *new)
-{
- struct pci_mmcfg_region *cfg;
-
- /* keep list sorted by segment and starting bus number */
- list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) {
- if (cfg->segment > new->segment ||
- (cfg->segment == new->segment &&
- cfg->start_bus >= new->start_bus)) {
- list_add_tail_rcu(&new->list, &cfg->list);
- return;
- }
+ switch (len) {
+ case 1:
+ data = mmio_config_readb(addr);
+ break;
+ case 2:
+ data = mmio_config_readw(addr);
+ break;
+ case 4:
+ data = mmio_config_readl(addr);
+ break;
}
- list_add_tail_rcu(&new->list, &pci_mmcfg_list);
-}
-
-static struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start,
- int end, u64 addr)
-{
- struct pci_mmcfg_region *new;
- struct resource *res;
-
- if (addr == 0)
- return NULL;
-
- new = kzalloc(sizeof(*new), GFP_KERNEL);
- if (!new)
- return NULL;
- new->address = addr;
- new->segment = segment;
- new->start_bus = start;
- new->end_bus = end;
-
- res = &new->res;
- res->start = addr + PCI_MMCFG_BUS_OFFSET(start);
- res->end = addr + PCI_MMCFG_BUS_OFFSET(end + 1) - 1;
- res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
- snprintf(new->name, PCI_MMCFG_RESOURCE_NAME_LEN,
- "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end);
- res->name = new->name;
-
- return new;
+ return data;
}
-static struct pci_mmcfg_region *__init pci_mmconfig_add(int segment, int start,
- int end, u64 addr)
+static void
+pci_mmconfig_amd_write(int len, void __iomem *addr, u32 value)
{
- struct pci_mmcfg_region *new;
-
- new = pci_mmconfig_alloc(segment, start, end, addr);
- if (new) {
- mutex_lock(&pci_mmcfg_lock);
- list_add_sorted(new);
- mutex_unlock(&pci_mmcfg_lock);
-
- pr_info(PREFIX
- "MMCONFIG for domain %04x [bus %02x-%02x] at %pR "
- "(base %#lx)\n",
- segment, start, end, &new->res, (unsigned long)addr);
+ switch (len) {
+ case 1:
+ mmio_config_writeb(addr, value);
+ break;
+ case 2:
+ mmio_config_writew(addr, value);
+ break;
+ case 4:
+ mmio_config_writel(addr, value);
+ break;
}
-
- return new;
}
-struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus)
-{
- struct pci_mmcfg_region *cfg;
-
- list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
- if (cfg->segment == segment &&
- cfg->start_bus <= bus && bus <= cfg->end_bus)
- return cfg;
-
- return NULL;
-}
+static struct pci_ecam_mmio_ops pci_mmcfg_mmio_amd_fam10h = {
+ .read = pci_mmconfig_amd_read,
+ .write = pci_mmconfig_amd_write,
+};
static const char *__init pci_mmcfg_e7520(void)
{
@@ -134,7 +84,7 @@ static const char *__init pci_mmcfg_e7520(void)
if (win == 0x0000 || win == 0xf000)
return NULL;
- if (pci_mmconfig_add(0, 0, 255, win << 16) == NULL)
+ if (pci_ecam_add(0, 0, 255, win << 16) == NULL)
return NULL;
return "Intel Corporation E7520 Memory Controller Hub";
@@ -178,7 +128,7 @@ static const char *__init pci_mmcfg_intel_945(void)
if ((pciexbar & mask) >= 0xf0000000U)
return NULL;
- if (pci_mmconfig_add(0, 0, (len >> 20) - 1, pciexbar & mask) == NULL)
+ if (pci_ecam_add(0, 0, (len >> 20) - 1, pciexbar & mask) == NULL)
return NULL;
return "Intel Corporation 945G/GZ/P/PL Express Memory Controller Hub";
@@ -225,12 +175,14 @@ static const char *__init pci_mmcfg_amd_fam10h(void)
end_bus = (1 << busnbits) - 1;
for (i = 0; i < (1 << segnbits); i++)
- if (pci_mmconfig_add(i, 0, end_bus,
+ if (pci_ecam_add(i, 0, end_bus,
base + (1<<28) * i) == NULL) {
- free_all_mmcfg();
+ pci_ecam_free_all();
return NULL;
}
+ pci_ecam_register_mmio(&pci_mmcfg_mmio_amd_fam10h);
+
return "AMD Family 10h NB";
}
@@ -258,7 +210,7 @@ static const char *__init pci_mmcfg_nvidia_mcp55(void)
/*
* do check if amd fam10h already took over
*/
- if (!acpi_disabled || !list_empty(&pci_mmcfg_list) || mcp55_checked)
+ if (!acpi_disabled || !list_empty(&pci_ecam_list) || mcp55_checked)
return NULL;
mcp55_checked = true;
@@ -287,7 +239,7 @@ static const char *__init pci_mmcfg_nvidia_mcp55(void)
base <<= extcfg_base_lshift;
start = (extcfg & extcfg_start_mask) >> extcfg_start_shift;
end = start + extcfg_sizebus[size_index] - 1;
- if (pci_mmconfig_add(0, start, end, base) == NULL)
+ if (pci_ecam_add(0, start, end, base) == NULL)
continue;
mcp55_mmconf_found++;
}
@@ -321,15 +273,15 @@ static const struct pci_mmcfg_hostbridge_probe pci_mmcfg_probes[] __initconst =
static void __init pci_mmcfg_check_end_bus_number(void)
{
- struct pci_mmcfg_region *cfg, *cfgx;
+ struct pci_ecam_region *cfg, *cfgx;
/* Fixup overlaps */
- list_for_each_entry(cfg, &pci_mmcfg_list, list) {
+ list_for_each_entry(cfg, &pci_ecam_list, list) {
if (cfg->end_bus < cfg->start_bus)
cfg->end_bus = 255;
/* Don't access the list head ! */
- if (cfg->list.next == &pci_mmcfg_list)
+ if (cfg->list.next == &pci_ecam_list)
break;
cfgx = list_entry(cfg->list.next, typeof(*cfg), list);
@@ -349,7 +301,7 @@ static int __init pci_mmcfg_check_hostbridge(void)
if (!raw_pci_ops)
return 0;
- free_all_mmcfg();
+ pci_ecam_free_all();
for (i = 0; i < ARRAY_SIZE(pci_mmcfg_probes); i++) {
bus = pci_mmcfg_probes[i].bus;
@@ -370,7 +322,7 @@ static int __init pci_mmcfg_check_hostbridge(void)
/* some end_bus_number is crazy, fix it */
pci_mmcfg_check_end_bus_number();
- return !list_empty(&pci_mmcfg_list);
+ return !list_empty(&pci_ecam_list);
}
static acpi_status check_mcfg_resource(struct acpi_resource *res, void *data)
@@ -443,7 +395,7 @@ static int is_acpi_reserved(u64 start, u64 end, unsigned not_used)
typedef int (*check_reserved_t)(u64 start, u64 end, unsigned type);
static int __ref is_mmconf_reserved(check_reserved_t is_reserved,
- struct pci_mmcfg_region *cfg,
+ struct pci_ecam_region *cfg,
struct device *dev, int with_e820)
{
u64 addr = cfg->res.start;
@@ -473,8 +425,8 @@ static int __ref is_mmconf_reserved(check_reserved_t is_reserved,
cfg->end_bus = cfg->start_bus + ((size>>20) - 1);
num_buses = cfg->end_bus - cfg->start_bus + 1;
cfg->res.end = cfg->res.start +
- PCI_MMCFG_BUS_OFFSET(num_buses) - 1;
- snprintf(cfg->name, PCI_MMCFG_RESOURCE_NAME_LEN,
+ PCI_ECAM_BUS_OFFSET(num_buses) - 1;
+ snprintf(cfg->name, PCI_ECAM_RESOURCE_NAME_LEN,
"PCI MMCONFIG %04x [bus %02x-%02x]",
cfg->segment, cfg->start_bus, cfg->end_bus);
@@ -495,7 +447,7 @@ static int __ref is_mmconf_reserved(check_reserved_t is_reserved,
}
static int __ref pci_mmcfg_check_reserved(struct device *dev,
- struct pci_mmcfg_region *cfg, int early)
+ struct pci_ecam_region *cfg, int early)
{
if (!early && !acpi_disabled) {
if (is_mmconf_reserved(is_acpi_reserved, cfg, dev, 0))
@@ -532,19 +484,19 @@ static int __ref pci_mmcfg_check_reserved(struct device *dev,
static void __init pci_mmcfg_reject_broken(int early)
{
- struct pci_mmcfg_region *cfg;
+ struct pci_ecam_region *cfg;
- list_for_each_entry(cfg, &pci_mmcfg_list, list) {
+ list_for_each_entry(cfg, &pci_ecam_list, list) {
if (pci_mmcfg_check_reserved(NULL, cfg, early) == 0) {
pr_info(PREFIX "not using MMCONFIG\n");
- free_all_mmcfg();
+ pci_ecam_free_all();
return;
}
}
}
-static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
- struct acpi_mcfg_allocation *cfg)
+int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
+ struct acpi_mcfg_allocation *cfg)
{
int year;
@@ -566,50 +518,6 @@ static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
return -EINVAL;
}
-static int __init pci_parse_mcfg(struct acpi_table_header *header)
-{
- struct acpi_table_mcfg *mcfg;
- struct acpi_mcfg_allocation *cfg_table, *cfg;
- unsigned long i;
- int entries;
-
- if (!header)
- return -EINVAL;
-
- mcfg = (struct acpi_table_mcfg *)header;
-
- /* how many config structures do we have */
- free_all_mmcfg();
- entries = 0;
- i = header->length - sizeof(struct acpi_table_mcfg);
- while (i >= sizeof(struct acpi_mcfg_allocation)) {
- entries++;
- i -= sizeof(struct acpi_mcfg_allocation);
- }
- if (entries == 0) {
- pr_err(PREFIX "MMCONFIG has no entries\n");
- return -ENODEV;
- }
-
- cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1];
- for (i = 0; i < entries; i++) {
- cfg = &cfg_table[i];
- if (acpi_mcfg_check_entry(mcfg, cfg)) {
- free_all_mmcfg();
- return -ENODEV;
- }
-
- if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number,
- cfg->end_bus_number, cfg->address) == NULL) {
- pr_warn(PREFIX "no memory for MCFG entries\n");
- free_all_mmcfg();
- return -ENOMEM;
- }
- }
-
- return 0;
-}
-
#ifdef CONFIG_ACPI_APEI
extern int (*arch_apei_filter_addr)(int (*func)(__u64 start, __u64 size,
void *data), void *data);
@@ -617,13 +525,13 @@ extern int (*arch_apei_filter_addr)(int (*func)(__u64 start, __u64 size,
static int pci_mmcfg_for_each_region(int (*func)(__u64 start, __u64 size,
void *data), void *data)
{
- struct pci_mmcfg_region *cfg;
+ struct pci_ecam_region *cfg;
int rc;
- if (list_empty(&pci_mmcfg_list))
+ if (list_empty(&pci_ecam_list))
return 0;
- list_for_each_entry(cfg, &pci_mmcfg_list, list) {
+ list_for_each_entry(cfg, &pci_ecam_list, list) {
rc = func(cfg->res.start, resource_size(&cfg->res), data);
if (rc)
return rc;
@@ -639,23 +547,24 @@ static int pci_mmcfg_for_each_region(int (*func)(__u64 start, __u64 size,
static void __init __pci_mmcfg_init(int early)
{
pci_mmcfg_reject_broken(early);
- if (list_empty(&pci_mmcfg_list))
+ if (list_empty(&pci_ecam_list))
return;
if (pcibios_last_bus < 0) {
- const struct pci_mmcfg_region *cfg;
+ const struct pci_ecam_region *cfg;
- list_for_each_entry(cfg, &pci_mmcfg_list, list) {
+ list_for_each_entry(cfg, &pci_ecam_list, list) {
if (cfg->segment)
break;
pcibios_last_bus = cfg->end_bus;
}
}
- if (pci_mmcfg_arch_init())
+ if (pci_ecam_arch_init()) {
+ raw_pci_ext_ops = &pci_mmcfg;
pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF;
- else {
- free_all_mmcfg();
+ } else {
+ pci_ecam_free_all();
pci_mmcfg_arch_init_failed = true;
}
}
@@ -668,7 +577,7 @@ void __init pci_mmcfg_early_init(void)
if (pci_mmcfg_check_hostbridge())
known_bridge = 1;
else
- acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
+ acpi_sfi_table_parse(ACPI_SIG_MCFG, acpi_parse_mcfg);
__pci_mmcfg_init(1);
set_apei_filter();
@@ -686,14 +595,14 @@ void __init pci_mmcfg_late_init(void)
/* MMCONFIG hasn't been enabled yet, try again */
if (pci_probe & PCI_PROBE_MASK & ~PCI_PROBE_MMCONF) {
- acpi_sfi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
+ acpi_sfi_table_parse(ACPI_SIG_MCFG, acpi_parse_mcfg);
__pci_mmcfg_init(0);
}
}
static int __init pci_mmcfg_late_insert_resources(void)
{
- struct pci_mmcfg_region *cfg;
+ struct pci_ecam_region *cfg;
pci_mmcfg_running_state = true;
@@ -706,7 +615,7 @@ static int __init pci_mmcfg_late_insert_resources(void)
* marked so it won't cause request errors when __request_region is
* called.
*/
- list_for_each_entry(cfg, &pci_mmcfg_list, list)
+ list_for_each_entry(cfg, &pci_ecam_list, list)
if (!cfg->res.parent)
insert_resource(&iomem_resource, &cfg->res);
@@ -726,7 +635,7 @@ int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
{
int rc;
struct resource *tmp = NULL;
- struct pci_mmcfg_region *cfg;
+ struct pci_ecam_region *cfg;
if (!(pci_probe & PCI_PROBE_MMCONF) || pci_mmcfg_arch_init_failed)
return -ENODEV;
@@ -734,85 +643,46 @@ int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end,
if (start > end)
return -EINVAL;
- mutex_lock(&pci_mmcfg_lock);
- cfg = pci_mmconfig_lookup(seg, start);
- if (cfg) {
- if (cfg->end_bus < end)
- dev_info(dev, FW_INFO
- "MMCONFIG for "
- "domain %04x [bus %02x-%02x] "
- "only partially covers this bridge\n",
- cfg->segment, cfg->start_bus, cfg->end_bus);
- mutex_unlock(&pci_mmcfg_lock);
+ rcu_read_lock();
+ cfg = pci_ecam_lookup(seg, start);
+ rcu_read_unlock();
+ if (cfg)
return -EEXIST;
- }
- if (!addr) {
- mutex_unlock(&pci_mmcfg_lock);
- return -EINVAL;
- }
+ if (!addr)
+ return -EINVAL;
rc = -EBUSY;
- cfg = pci_mmconfig_alloc(seg, start, end, addr);
+ cfg = pci_ecam_alloc(seg, start, end, addr);
if (cfg == NULL) {
dev_warn(dev, "fail to add MMCONFIG (out of memory)\n");
- rc = -ENOMEM;
+ return -ENOMEM;
} else if (!pci_mmcfg_check_reserved(dev, cfg, 0)) {
dev_warn(dev, FW_BUG "MMCONFIG %pR isn't reserved\n",
&cfg->res);
- } else {
- /* Insert resource if it's not in boot stage */
- if (pci_mmcfg_running_state)
- tmp = insert_resource_conflict(&iomem_resource,
- &cfg->res);
-
- if (tmp) {
- dev_warn(dev,
- "MMCONFIG %pR conflicts with "
- "%s %pR\n",
- &cfg->res, tmp->name, tmp);
- } else if (pci_mmcfg_arch_map(cfg)) {
- dev_warn(dev, "fail to map MMCONFIG %pR.\n",
- &cfg->res);
- } else {
- list_add_sorted(cfg);
- dev_info(dev, "MMCONFIG at %pR (base %#lx)\n",
- &cfg->res, (unsigned long)addr);
- cfg = NULL;
- rc = 0;
- }
+ goto error;
}
- if (cfg) {
- if (cfg->res.parent)
- release_resource(&cfg->res);
- kfree(cfg);
+ /* Insert resource if it's not in boot stage */
+ if (pci_mmcfg_running_state)
+ tmp = insert_resource_conflict(&iomem_resource, &cfg->res);
+
+ if (tmp) {
+ dev_warn(dev,
+ "MMCONFIG %pR conflicts with %s %pR\n",
+ &cfg->res, tmp->name, tmp);
+ goto error;
}
- mutex_unlock(&pci_mmcfg_lock);
+ rc = pci_ecam_inject(cfg);
+ if (rc)
+ goto error;
- return rc;
-}
-
-/* Delete MMCFG information for host bridges */
-int pci_mmconfig_delete(u16 seg, u8 start, u8 end)
-{
- struct pci_mmcfg_region *cfg;
-
- mutex_lock(&pci_mmcfg_lock);
- list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list)
- if (cfg->segment == seg && cfg->start_bus == start &&
- cfg->end_bus == end) {
- list_del_rcu(&cfg->list);
- synchronize_rcu();
- pci_mmcfg_arch_unmap(cfg);
- if (cfg->res.parent)
- release_resource(&cfg->res);
- mutex_unlock(&pci_mmcfg_lock);
- kfree(cfg);
- return 0;
- }
- mutex_unlock(&pci_mmcfg_lock);
+ return 0;
- return -ENOENT;
+error:
+ if (cfg->res.parent)
+ release_resource(&cfg->res);
+ kfree(cfg);
+ return rc;
}
diff --git a/arch/x86/pci/mmconfig_32.c b/arch/x86/pci/mmconfig_32.c
index 43984bc1665a..27e707d862cc 100644
--- a/arch/x86/pci/mmconfig_32.c
+++ b/arch/x86/pci/mmconfig_32.c
@@ -12,6 +12,7 @@
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/rcupdate.h>
+#include <linux/ecam.h>
#include <asm/e820.h>
#include <asm/pci_x86.h>
@@ -27,7 +28,7 @@ static int mmcfg_last_accessed_cpu;
*/
static u32 get_base_addr(unsigned int seg, int bus, unsigned devfn)
{
- struct pci_mmcfg_region *cfg = pci_mmconfig_lookup(seg, bus);
+ struct pci_ecam_region *cfg = pci_ecam_lookup(seg, bus);
if (cfg)
return cfg->address;
@@ -39,7 +40,7 @@ static u32 get_base_addr(unsigned int seg, int bus, unsigned devfn)
*/
static void pci_exp_set_dev_base(unsigned int base, int bus, int devfn)
{
- u32 dev_base = base | PCI_MMCFG_BUS_OFFSET(bus) | (devfn << 12);
+ u32 dev_base = base | PCI_ECAM_BUS_OFFSET(bus) | (devfn << 12);
int cpu = smp_processor_id();
if (dev_base != mmcfg_last_accessed_device ||
cpu != mmcfg_last_accessed_cpu) {
@@ -49,7 +50,7 @@ static void pci_exp_set_dev_base(unsigned int base, int bus, int devfn)
}
}
-static int pci_mmcfg_read(unsigned int seg, unsigned int bus,
+int pci_ecam_read(unsigned int seg, unsigned int bus,
unsigned int devfn, int reg, int len, u32 *value)
{
unsigned long flags;
@@ -71,24 +72,14 @@ err: *value = -1;
pci_exp_set_dev_base(base, bus, devfn);
- switch (len) {
- case 1:
- *value = mmio_config_readb(mmcfg_virt_addr + reg);
- break;
- case 2:
- *value = mmio_config_readw(mmcfg_virt_addr + reg);
- break;
- case 4:
- *value = mmio_config_readl(mmcfg_virt_addr + reg);
- break;
- }
+ *value = pci_mmio_read(len, mmcfg_virt_addr + reg);
raw_spin_unlock_irqrestore(&pci_config_lock, flags);
rcu_read_unlock();
return 0;
}
-static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
+int pci_ecam_write(unsigned int seg, unsigned int bus,
unsigned int devfn, int reg, int len, u32 value)
{
unsigned long flags;
@@ -108,45 +99,29 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
pci_exp_set_dev_base(base, bus, devfn);
- switch (len) {
- case 1:
- mmio_config_writeb(mmcfg_virt_addr + reg, value);
- break;
- case 2:
- mmio_config_writew(mmcfg_virt_addr + reg, value);
- break;
- case 4:
- mmio_config_writel(mmcfg_virt_addr + reg, value);
- break;
- }
+ pci_mmio_write(len, mmcfg_virt_addr + reg, value);
raw_spin_unlock_irqrestore(&pci_config_lock, flags);
rcu_read_unlock();
return 0;
}
-const struct pci_raw_ops pci_mmcfg = {
- .read = pci_mmcfg_read,
- .write = pci_mmcfg_write,
-};
-
-int __init pci_mmcfg_arch_init(void)
+int __init pci_ecam_arch_init(void)
{
printk(KERN_INFO "PCI: Using MMCONFIG for extended config space\n");
- raw_pci_ext_ops = &pci_mmcfg;
return 1;
}
-void __init pci_mmcfg_arch_free(void)
+void __init pci_ecam_arch_free(void)
{
}
-int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg)
+int pci_ecam_arch_map(struct pci_ecam_region *cfg)
{
return 0;
}
-void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg)
+void pci_ecam_arch_unmap(struct pci_ecam_region *cfg)
{
unsigned long flags;
diff --git a/arch/x86/pci/mmconfig_64.c b/arch/x86/pci/mmconfig_64.c
deleted file mode 100644
index bea52496aea6..000000000000
--- a/arch/x86/pci/mmconfig_64.c
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * mmconfig.c - Low-level direct PCI config space access via MMCONFIG
- *
- * This is an 64bit optimized version that always keeps the full mmconfig
- * space mapped. This allows lockless config space operation.
- */
-
-#include <linux/pci.h>
-#include <linux/init.h>
-#include <linux/acpi.h>
-#include <linux/bitmap.h>
-#include <linux/rcupdate.h>
-#include <asm/e820.h>
-#include <asm/pci_x86.h>
-
-#define PREFIX "PCI: "
-
-static char __iomem *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn)
-{
- struct pci_mmcfg_region *cfg = pci_mmconfig_lookup(seg, bus);
-
- if (cfg && cfg->virt)
- return cfg->virt + (PCI_MMCFG_BUS_OFFSET(bus) | (devfn << 12));
- return NULL;
-}
-
-static int pci_mmcfg_read(unsigned int seg, unsigned int bus,
- unsigned int devfn, int reg, int len, u32 *value)
-{
- char __iomem *addr;
-
- /* Why do we have this when nobody checks it. How about a BUG()!? -AK */
- if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) {
-err: *value = -1;
- return -EINVAL;
- }
-
- rcu_read_lock();
- addr = pci_dev_base(seg, bus, devfn);
- if (!addr) {
- rcu_read_unlock();
- goto err;
- }
-
- switch (len) {
- case 1:
- *value = mmio_config_readb(addr + reg);
- break;
- case 2:
- *value = mmio_config_readw(addr + reg);
- break;
- case 4:
- *value = mmio_config_readl(addr + reg);
- break;
- }
- rcu_read_unlock();
-
- return 0;
-}
-
-static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
- unsigned int devfn, int reg, int len, u32 value)
-{
- char __iomem *addr;
-
- /* Why do we have this when nobody checks it. How about a BUG()!? -AK */
- if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095)))
- return -EINVAL;
-
- rcu_read_lock();
- addr = pci_dev_base(seg, bus, devfn);
- if (!addr) {
- rcu_read_unlock();
- return -EINVAL;
- }
-
- switch (len) {
- case 1:
- mmio_config_writeb(addr + reg, value);
- break;
- case 2:
- mmio_config_writew(addr + reg, value);
- break;
- case 4:
- mmio_config_writel(addr + reg, value);
- break;
- }
- rcu_read_unlock();
-
- return 0;
-}
-
-const struct pci_raw_ops pci_mmcfg = {
- .read = pci_mmcfg_read,
- .write = pci_mmcfg_write,
-};
-
-static void __iomem *mcfg_ioremap(struct pci_mmcfg_region *cfg)
-{
- void __iomem *addr;
- u64 start, size;
- int num_buses;
-
- start = cfg->address + PCI_MMCFG_BUS_OFFSET(cfg->start_bus);
- num_buses = cfg->end_bus - cfg->start_bus + 1;
- size = PCI_MMCFG_BUS_OFFSET(num_buses);
- addr = ioremap_nocache(start, size);
- if (addr)
- addr -= PCI_MMCFG_BUS_OFFSET(cfg->start_bus);
- return addr;
-}
-
-int __init pci_mmcfg_arch_init(void)
-{
- struct pci_mmcfg_region *cfg;
-
- list_for_each_entry(cfg, &pci_mmcfg_list, list)
- if (pci_mmcfg_arch_map(cfg)) {
- pci_mmcfg_arch_free();
- return 0;
- }
-
- raw_pci_ext_ops = &pci_mmcfg;
-
- return 1;
-}
-
-void __init pci_mmcfg_arch_free(void)
-{
- struct pci_mmcfg_region *cfg;
-
- list_for_each_entry(cfg, &pci_mmcfg_list, list)
- pci_mmcfg_arch_unmap(cfg);
-}
-
-int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg)
-{
- cfg->virt = mcfg_ioremap(cfg);
- if (!cfg->virt) {
- pr_err(PREFIX "can't map MMCONFIG at %pR\n", &cfg->res);
- return -ENOMEM;
- }
-
- return 0;
-}
-
-void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg)
-{
- if (cfg && cfg->virt) {
- iounmap(cfg->virt + PCI_MMCFG_BUS_OFFSET(cfg->start_bus));
- cfg->virt = NULL;
- }
-}
diff --git a/arch/x86/pci/numachip.c b/arch/x86/pci/numachip.c
index 2e565e65c893..af412473a6e6 100644
--- a/arch/x86/pci/numachip.c
+++ b/arch/x86/pci/numachip.c
@@ -13,6 +13,7 @@
*
*/
+#include <linux/ecam.h>
#include <linux/pci.h>
#include <asm/pci_x86.h>
@@ -20,10 +21,10 @@ static u8 limit __read_mostly;
static inline char __iomem *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn)
{
- struct pci_mmcfg_region *cfg = pci_mmconfig_lookup(seg, bus);
+ struct pci_ecam_region *cfg = pci_ecam_lookup(seg, bus);
if (cfg && cfg->virt)
- return cfg->virt + (PCI_MMCFG_BUS_OFFSET(bus) | (devfn << 12));
+ return cfg->virt + (PCI_ECAM_BUS_OFFSET(bus) | (devfn << 12));
return NULL;
}
@@ -51,17 +52,7 @@ err: *value = -1;
goto err;
}
- switch (len) {
- case 1:
- *value = mmio_config_readb(addr + reg);
- break;
- case 2:
- *value = mmio_config_readw(addr + reg);
- break;
- case 4:
- *value = mmio_config_readl(addr + reg);
- break;
- }
+ *value = pci_mmio_read(len, addr + reg);
rcu_read_unlock();
return 0;
@@ -87,17 +78,7 @@ static int pci_mmcfg_write_numachip(unsigned int seg, unsigned int bus,
return -EINVAL;
}
- switch (len) {
- case 1:
- mmio_config_writeb(addr + reg, value);
- break;
- case 2:
- mmio_config_writew(addr + reg, value);
- break;
- case 4:
- mmio_config_writel(addr + reg, value);
- break;
- }
+ pci_mmio_write(len, addr + reg, value);
rcu_read_unlock();
return 0;
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 97ec73b2e9d5..43d02d59a26e 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -65,6 +65,7 @@ obj-$(CONFIG_ACPI_BUTTON) += button.o
obj-$(CONFIG_ACPI_FAN) += fan.o
obj-$(CONFIG_ACPI_VIDEO) += video.o
obj-$(CONFIG_ACPI_PCI_SLOT) += pci_slot.o
+obj-$(CONFIG_PCI_MMCONFIG) += mcfg.o
obj-$(CONFIG_ACPI_PROCESSOR) += processor.o
obj-y += container.o
obj-$(CONFIG_ACPI_THERMAL) += thermal.o
diff --git a/drivers/acpi/mcfg.c b/drivers/acpi/mcfg.c
new file mode 100644
index 000000000000..2a6bbce47af8
--- /dev/null
+++ b/drivers/acpi/mcfg.c
@@ -0,0 +1,103 @@
+/*
+ * MCFG ACPI table parser.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/acpi.h>
+#include <linux/ecam.h>
+
+#define PREFIX "MCFG: "
+
+/*
+ * raw_pci_read/write - ACPI PCI config space accessors.
+ *
+ * ACPI spec defines MCFG table as the way we can describe access to PCI config
+ * space, so let MCFG be default (__weak).
+ *
+ * If platform needs more fancy stuff, should provides its own implementation.
+ */
+int __weak raw_pci_read(unsigned int domain, unsigned int bus,
+ unsigned int devfn, int reg, int len, u32 *val)
+{
+ return pci_ecam_read(domain, bus, devfn, reg, len, val);
+}
+
+int __weak raw_pci_write(unsigned int domain, unsigned int bus,
+ unsigned int devfn, int reg, int len, u32 val)
+{
+ return pci_ecam_write(domain, bus, devfn, reg, len, val);
+}
+
+int __init acpi_parse_mcfg(struct acpi_table_header *header)
+{
+ struct acpi_table_mcfg *mcfg;
+ struct acpi_mcfg_allocation *cfg_table, *cfg;
+ unsigned long i;
+ int entries;
+
+ if (!header)
+ return -EINVAL;
+
+ mcfg = (struct acpi_table_mcfg *)header;
+
+ /* how many config structures do we have */
+ pci_ecam_free_all();
+ entries = 0;
+ i = header->length - sizeof(struct acpi_table_mcfg);
+ while (i >= sizeof(struct acpi_mcfg_allocation)) {
+ entries++;
+ i -= sizeof(struct acpi_mcfg_allocation);
+ }
+ if (entries == 0) {
+ pr_err(PREFIX "MCFG table has no entries\n");
+ return -ENODEV;
+ }
+
+ cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1];
+ for (i = 0; i < entries; i++) {
+ cfg = &cfg_table[i];
+ if (acpi_mcfg_check_entry(mcfg, cfg)) {
+ pci_ecam_free_all();
+ return -ENODEV;
+ }
+
+ if (pci_ecam_add(cfg->pci_segment, cfg->start_bus_number,
+ cfg->end_bus_number, cfg->address) == NULL) {
+ pr_warn(PREFIX "no memory for MCFG entries\n");
+ pci_ecam_free_all();
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+int __init __weak acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
+ struct acpi_mcfg_allocation *cfg)
+{
+ return 0;
+}
+
+void __init __weak pci_mmcfg_early_init(void)
+{
+
+}
+
+void __init __weak pci_mmcfg_late_init(void)
+{
+ struct pci_ecam_region *cfg;
+
+ acpi_table_parse(ACPI_SIG_MCFG, acpi_parse_mcfg);
+
+ if (list_empty(&pci_ecam_list))
+ return;
+ if (!pci_ecam_arch_init())
+ pci_ecam_free_all();
+
+ list_for_each_entry(cfg, &pci_ecam_list, list)
+ insert_resource(&iomem_resource, &cfg->res);
+}
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 73de4efcbe6e..37426f529c7c 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -26,6 +26,16 @@ config PCI_MSI_IRQ_DOMAIN
depends on PCI_MSI
select GENERIC_MSI_IRQ_DOMAIN
+config PCI_ECAM
+ bool "Enhanced Configuration Access Mechanism (ECAM)"
+ depends on PCI && HAVE_PCI_ECAM
+
+config HAVE_PCI_ECAM
+ bool
+
+config GENERIC_PCI_ECAM
+ bool
+
config PCI_DEBUG
bool "PCI Debugging"
depends on PCI && DEBUG_KERNEL
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 73e4af400a5a..ce7b6308dd85 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -41,6 +41,11 @@ obj-$(CONFIG_SPARC_LEON) += setup-irq.o
obj-$(CONFIG_M68K) += setup-irq.o
#
+# Enhanced Configuration Access Mechanism (ECAM)
+#
+obj-$(CONFIG_PCI_ECAM) += ecam.o
+
+#
# ACPI Related PCI FW Functions
# ACPI _DSM provided firmware instance and string name
#
diff --git a/drivers/pci/ecam.c b/drivers/pci/ecam.c
new file mode 100644
index 000000000000..d6281a2b3d83
--- /dev/null
+++ b/drivers/pci/ecam.c
@@ -0,0 +1,358 @@
+/*
+ * Arch agnostic direct PCI config space access via
+ * ECAM (Enhanced Configuration Access Mechanism)
+ *
+ * Per-architecture code takes care of the mappings, region validation and
+ * accesses themselves.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/mutex.h>
+#include <linux/rculist.h>
+#include <linux/ecam.h>
+
+#include <asm/io.h>
+
+#define PREFIX "PCI ECAM: "
+
+static DEFINE_MUTEX(pci_ecam_lock);
+
+LIST_HEAD(pci_ecam_list);
+
+#ifdef CONFIG_GENERIC_PCI_ECAM
+static char __iomem *pci_dev_base(unsigned int seg, unsigned int bus,
+ unsigned int devfn)
+{
+ struct pci_ecam_region *cfg = pci_ecam_lookup(seg, bus);
+
+ if (cfg && cfg->virt)
+ return cfg->virt + (PCI_ECAM_BUS_OFFSET(bus) | (devfn << 12));
+ return NULL;
+}
+
+int pci_ecam_read(unsigned int seg, unsigned int bus,
+ unsigned int devfn, int reg, int len, u32 *value)
+{
+ char __iomem *addr;
+
+ /* Why do we have this when nobody checks it. How about a BUG()!? -AK */
+ if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) {
+err: *value = -1;
+ return -EINVAL;
+ }
+
+ rcu_read_lock();
+ addr = pci_dev_base(seg, bus, devfn);
+ if (!addr) {
+ rcu_read_unlock();
+ goto err;
+ }
+
+ *value = pci_mmio_read(len, addr + reg);
+ rcu_read_unlock();
+
+ return 0;
+}
+
+int pci_ecam_write(unsigned int seg, unsigned int bus,
+ unsigned int devfn, int reg, int len, u32 value)
+{
+ char __iomem *addr;
+
+ /* Why do we have this when nobody checks it. How about a BUG()!? -AK */
+ if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095)))
+ return -EINVAL;
+
+ rcu_read_lock();
+ addr = pci_dev_base(seg, bus, devfn);
+ if (!addr) {
+ rcu_read_unlock();
+ return -EINVAL;
+ }
+
+ pci_mmio_write(len, addr + reg, value);
+ rcu_read_unlock();
+
+ return 0;
+}
+
+static void __iomem *pci_ecam_ioremap(struct pci_ecam_region *cfg)
+{
+ void __iomem *addr;
+ u64 start, size;
+ int num_buses;
+
+ start = cfg->address + PCI_ECAM_BUS_OFFSET(cfg->start_bus);
+ num_buses = cfg->end_bus - cfg->start_bus + 1;
+ size = PCI_ECAM_BUS_OFFSET(num_buses);
+ addr = ioremap_nocache(start, size);
+ if (addr)
+ addr -= PCI_ECAM_BUS_OFFSET(cfg->start_bus);
+ return addr;
+}
+
+int __init pci_ecam_arch_init(void)
+{
+ struct pci_ecam_region *cfg;
+
+ list_for_each_entry(cfg, &pci_ecam_list, list)
+ if (pci_ecam_arch_map(cfg)) {
+ pci_ecam_arch_free();
+ return 0;
+ }
+
+ return 1;
+}
+
+void __init pci_ecam_arch_free(void)
+{
+ struct pci_ecam_region *cfg;
+
+ list_for_each_entry(cfg, &pci_ecam_list, list)
+ pci_ecam_arch_unmap(cfg);
+}
+
+int pci_ecam_arch_map(struct pci_ecam_region *cfg)
+{
+ cfg->virt = pci_ecam_ioremap(cfg);
+ if (!cfg->virt) {
+ pr_err(PREFIX "can't map ECAM at %pR\n", &cfg->res);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+void pci_ecam_arch_unmap(struct pci_ecam_region *cfg)
+{
+ if (cfg && cfg->virt) {
+ iounmap(cfg->virt + PCI_ECAM_BUS_OFFSET(cfg->start_bus));
+ cfg->virt = NULL;
+ }
+}
+#endif
+
+static u32
+pci_ecam_generic_read(int len, void __iomem *addr)
+{
+ u32 data = 0;
+
+ switch (len) {
+ case 1:
+ data = readb(addr);
+ break;
+ case 2:
+ data = readw(addr);
+ break;
+ case 4:
+ data = readl(addr);
+ break;
+ }
+
+ return data;
+}
+
+static void
+pci_ecam_generic_write(int len, void __iomem *addr, u32 value)
+{
+ switch (len) {
+ case 1:
+ writeb(value, addr);
+ break;
+ case 2:
+ writew(value, addr);
+ break;
+ case 4:
+ writel(value, addr);
+ break;
+ }
+}
+
+static struct pci_ecam_mmio_ops pci_ecam_mmio_default = {
+ .read = pci_ecam_generic_read,
+ .write = pci_ecam_generic_write,
+};
+
+static struct pci_ecam_mmio_ops *pci_ecam_mmio = &pci_ecam_mmio_default;
+
+void
+pci_ecam_register_mmio(struct pci_ecam_mmio_ops *ops)
+{
+ pci_ecam_mmio = ops;
+}
+
+u32
+pci_mmio_read(int len, void __iomem *addr)
+{
+ if (!pci_ecam_mmio) {
+ pr_err("PCI config space has no accessors !");
+ return 0;
+ }
+
+ return pci_ecam_mmio->read(len, addr);
+}
+
+void
+pci_mmio_write(int len, void __iomem *addr, u32 value)
+{
+ if (!pci_ecam_mmio) {
+ pr_err("PCI config space has no accessors !");
+ return;
+ }
+
+ pci_ecam_mmio->write(len, addr, value);
+}
+
+static void __init pci_ecam_remove(struct pci_ecam_region *cfg)
+{
+ if (cfg->res.parent)
+ release_resource(&cfg->res);
+ list_del(&cfg->list);
+ kfree(cfg);
+}
+
+void __init pci_ecam_free_all(void)
+{
+ struct pci_ecam_region *cfg, *tmp;
+
+ pci_ecam_arch_free();
+ list_for_each_entry_safe(cfg, tmp, &pci_ecam_list, list)
+ pci_ecam_remove(cfg);
+}
+
+void pci_ecam_list_add_sorted(struct pci_ecam_region *new)
+{
+ struct pci_ecam_region *cfg;
+
+ /* keep list sorted by segment and starting bus number */
+ list_for_each_entry_rcu(cfg, &pci_ecam_list, list) {
+ if (cfg->segment > new->segment ||
+ (cfg->segment == new->segment &&
+ cfg->start_bus >= new->start_bus)) {
+ list_add_tail_rcu(&new->list, &cfg->list);
+ return;
+ }
+ }
+ list_add_tail_rcu(&new->list, &pci_ecam_list);
+}
+
+struct pci_ecam_region *pci_ecam_alloc(int segment, int start,
+ int end, u64 addr)
+{
+ struct pci_ecam_region *new;
+ struct resource *res;
+
+ if (addr == 0)
+ return NULL;
+
+ new = kzalloc(sizeof(*new), GFP_KERNEL);
+ if (!new)
+ return NULL;
+
+ new->address = addr;
+ new->segment = segment;
+ new->start_bus = start;
+ new->end_bus = end;
+
+ res = &new->res;
+ res->start = addr + PCI_ECAM_BUS_OFFSET(start);
+ res->end = addr + PCI_ECAM_BUS_OFFSET(end + 1) - 1;
+ res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+ snprintf(new->name, PCI_ECAM_RESOURCE_NAME_LEN,
+ "PCI ECAM %04x [bus %02x-%02x]", segment, start, end);
+ res->name = new->name;
+
+ return new;
+}
+
+struct pci_ecam_region *pci_ecam_add(int segment, int start,
+ int end, u64 addr)
+{
+ struct pci_ecam_region *new;
+
+ new = pci_ecam_alloc(segment, start, end, addr);
+ if (new) {
+ mutex_lock(&pci_ecam_lock);
+ pci_ecam_list_add_sorted(new);
+ mutex_unlock(&pci_ecam_lock);
+
+ pr_info(PREFIX
+ "ECAM for domain %04x [bus %02x-%02x] at %pR "
+ "(base %#lx)\n",
+ segment, start, end, &new->res, (unsigned long)addr);
+ }
+
+ return new;
+}
+
+struct pci_ecam_region *pci_ecam_lookup(int segment, int bus)
+{
+ struct pci_ecam_region *cfg;
+
+ list_for_each_entry_rcu(cfg, &pci_ecam_list, list)
+ if (cfg->segment == segment &&
+ cfg->start_bus <= bus && bus <= cfg->end_bus)
+ return cfg;
+
+ return NULL;
+}
+
+/* Delete ECAM information for host bridges */
+int pci_ecam_delete(u16 seg, u8 start, u8 end)
+{
+ struct pci_ecam_region *cfg;
+
+ mutex_lock(&pci_ecam_lock);
+ list_for_each_entry_rcu(cfg, &pci_ecam_list, list)
+ if (cfg->segment == seg && cfg->start_bus == start &&
+ cfg->end_bus == end) {
+ list_del_rcu(&cfg->list);
+ synchronize_rcu();
+ pci_ecam_arch_unmap(cfg);
+ if (cfg->res.parent)
+ release_resource(&cfg->res);
+ mutex_unlock(&pci_ecam_lock);
+ kfree(cfg);
+ return 0;
+ }
+ mutex_unlock(&pci_ecam_lock);
+
+ return -ENOENT;
+}
+
+int pci_ecam_inject(struct pci_ecam_region *cfg)
+{
+ struct pci_ecam_region *cfg_conflict;
+ int err = 0;
+
+ mutex_lock(&pci_ecam_lock);
+ cfg_conflict = pci_ecam_lookup(cfg->segment, cfg->start_bus);
+ if (cfg_conflict) {
+ if (cfg_conflict->end_bus < cfg->end_bus)
+ pr_info(FW_INFO "ECAM for "
+ "domain %04x [bus %02x-%02x] "
+ "only partially covers this bridge\n",
+ cfg_conflict->segment, cfg_conflict->start_bus,
+ cfg_conflict->end_bus);
+ err = -EEXIST;
+ goto out;
+ }
+
+ if (pci_ecam_arch_map(cfg)) {
+ pr_warn("fail to map ECAM %pR.\n", &cfg->res);
+ err = -ENOMEM;
+ goto out;
+ } else {
+ pci_ecam_list_add_sorted(cfg);
+ pr_info("ECAM at %pR (base %#lx)\n",
+ &cfg->res, (unsigned long)cfg->address);
+
+ }
+out:
+ mutex_unlock(&pci_ecam_lock);
+ return err;
+}
diff --git a/drivers/xen/pci.c b/drivers/xen/pci.c
index 7494dbeb4409..32886e716d3a 100644
--- a/drivers/xen/pci.c
+++ b/drivers/xen/pci.c
@@ -19,7 +19,10 @@
#include <linux/pci.h>
#include <linux/acpi.h>
+
#include <linux/pci-acpi.h>
+#include <linux/ecam.h>
+
#include <xen/xen.h>
#include <xen/interface/physdev.h>
#include <xen/interface/xen.h>
@@ -215,7 +218,7 @@ arch_initcall(register_xen_pci_notifier);
#ifdef CONFIG_PCI_MMCONFIG
static int __init xen_mcfg_late(void)
{
- struct pci_mmcfg_region *cfg;
+ struct pci_ecam_region *cfg;
int rc;
if (!xen_initial_domain())
@@ -224,11 +227,11 @@ static int __init xen_mcfg_late(void)
if ((pci_probe & PCI_PROBE_MMCONF) == 0)
return 0;
- if (list_empty(&pci_mmcfg_list))
+ if (list_empty(&pci_ecam_list))
return 0;
/* Check whether they are in the right area. */
- list_for_each_entry(cfg, &pci_mmcfg_list, list) {
+ list_for_each_entry(cfg, &pci_ecam_list, list) {
struct physdev_pci_mmcfg_reserved r;
r.address = cfg->address;
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index e0c05fce5d1a..384c2c1f07f4 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -157,6 +157,8 @@ int acpi_table_parse_madt(enum acpi_madt_type id,
acpi_tbl_entry_handler handler,
unsigned int max_entries);
int acpi_parse_mcfg (struct acpi_table_header *header);
+int acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg,
+ struct acpi_mcfg_allocation *cfg);
void acpi_table_print_madt_entry (struct acpi_subtable_header *madt);
/* the following four functions are architecture-dependent */
diff --git a/include/linux/ecam.h b/include/linux/ecam.h
new file mode 100644
index 000000000000..363691b7fbfc
--- /dev/null
+++ b/include/linux/ecam.h
@@ -0,0 +1,56 @@
+#ifndef __ECAM_H
+#define __ECAM_H
+#ifdef __KERNEL__
+
+#include <linux/types.h>
+#include <linux/acpi.h>
+
+/* "PCI ECAM %04x [bus %02x-%02x]" */
+#define PCI_ECAM_RESOURCE_NAME_LEN (22 + 4 + 2 + 2)
+
+struct pci_ecam_region {
+ struct list_head list;
+ struct resource res;
+ u64 address;
+ char __iomem *virt;
+ u16 segment;
+ u8 start_bus;
+ u8 end_bus;
+ char name[PCI_ECAM_RESOURCE_NAME_LEN];
+};
+
+struct pci_ecam_mmio_ops {
+ u32 (*read)(int len, void __iomem *addr);
+ void (*write)(int len, void __iomem *addr, u32 value);
+};
+
+struct pci_ecam_region *pci_ecam_lookup(int segment, int bus);
+struct pci_ecam_region *pci_ecam_alloc(int segment, int start,
+ int end, u64 addr);
+int pci_ecam_inject(struct pci_ecam_region *cfg);
+struct pci_ecam_region *pci_ecam_add(int segment, int start,
+ int end, u64 addr);
+void pci_ecam_list_add_sorted(struct pci_ecam_region *new);
+void pci_ecam_free_all(void);
+int pci_ecam_delete(u16 seg, u8 start, u8 end);
+
+/* Arch specific calls */
+int pci_ecam_arch_init(void);
+void pci_ecam_arch_free(void);
+int pci_ecam_arch_map(struct pci_ecam_region *cfg);
+void pci_ecam_arch_unmap(struct pci_ecam_region *cfg);
+extern u32 pci_mmio_read(int len, void __iomem *addr);
+extern void pci_mmio_write(int len, void __iomem *addr, u32 value);
+extern void pci_ecam_register_mmio(struct pci_ecam_mmio_ops *ops);
+
+extern struct list_head pci_ecam_list;
+
+#define PCI_ECAM_BUS_OFFSET(bus) ((bus) << 20)
+
+int pci_ecam_read(unsigned int seg, unsigned int bus,
+ unsigned int devfn, int reg, int len, u32 *value);
+int pci_ecam_write(unsigned int seg, unsigned int bus,
+ unsigned int devfn, int reg, int len, u32 value);
+
+#endif /* __KERNEL__ */
+#endif /* __ECAM_H */