MCFG ACPI table and PCI ECAM standard have no arch dependencies so it can be used across all architectures. Currently MMCONFIG related code resides in arch/x86 directories. This patch set is going to isolate non-architecture specific code and make it accessible for all architectures.
v3 -> v4 changes: - bug fixes after kbuild test robot report - add asm/io.h header for ecam.c - rename mmcfg -> ecam for drivers/xen/pci.c and arch/x86/pci/numachip.c - rework Kconfig logic
v2 -> v3 changes: - rebase patch set v2 to v4.0-rc1
v1 -> v2 changes: - use generic readb/readw etc. accessors instead of AMD Fam10h workaround - separate ACPI and ECAM code - improve ECAM code naming
Tomasz Nowicki (9): x86, pci: Clean up comment about buggy MMIO config space access for AMD Fam10h CPUs. x86, pci: Abstract PCI config accessors and use AMD Fam10h workaround exclusively. x86, pci: Reorder logic of pci_mmconfig_insert() function x86, pci, acpi: Move arch-agnostic MMCONFIG (aka ECAM) and ACPI code out of arch/x86/ directory pci, acpi, mcfg: Provide generic implementation of MCFG code initialization. x86, pci: mmconfig_{32,64}.c code refactoring - remove code duplication. x86, pci, ecam: mmconfig_64.c becomes default implementation for ECAM driver. pci, acpi, mcfg: Share ACPI PCI config space accessors. pci, ecam: Improve naming for ecam.c content and areas where it is used.
arch/x86/Kconfig | 4 + arch/x86/include/asm/pci_x86.h | 34 +--- arch/x86/pci/Makefile | 5 +- arch/x86/pci/acpi.c | 3 +- arch/x86/pci/mmconfig-shared.c | 346 +++++++++++---------------------------- arch/x86/pci/mmconfig_32.c | 47 ++---- arch/x86/pci/mmconfig_64.c | 153 ------------------ arch/x86/pci/numachip.c | 29 +--- drivers/acpi/Makefile | 1 + drivers/acpi/mcfg.c | 121 ++++++++++++++ drivers/pci/Kconfig | 10 ++ drivers/pci/Makefile | 5 + drivers/pci/ecam.c | 358 +++++++++++++++++++++++++++++++++++++++++ drivers/xen/pci.c | 7 +- include/linux/ecam.h | 56 +++++++ 15 files changed, 679 insertions(+), 500 deletions(-) delete mode 100644 arch/x86/pci/mmconfig_64.c create mode 100644 drivers/acpi/mcfg.c create mode 100644 drivers/pci/ecam.c create mode 100644 include/linux/ecam.h
- fix typo - improve explanation - add reference to the related document
Signed-off-by: Tomasz Nowicki tomasz.nowicki@linaro.org --- arch/x86/include/asm/pci_x86.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h index fa1195d..d024f4d 100644 --- a/arch/x86/include/asm/pci_x86.h +++ b/arch/x86/include/asm/pci_x86.h @@ -152,10 +152,13 @@ extern struct list_head pci_mmcfg_list;
/* * 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) {
From now on, readb()/writeb()/etc. generic calls are used as default
approach. Special MMIO accessors are registered for AMD Fam10h CPUs only.
Signed-off-by: Tomasz Nowicki tomasz.nowicki@linaro.org --- arch/x86/include/asm/pci_x86.h | 8 +++ arch/x86/pci/mmconfig-shared.c | 114 +++++++++++++++++++++++++++++++++++++++++ arch/x86/pci/mmconfig_32.c | 24 +-------- arch/x86/pci/mmconfig_64.c | 24 +-------- arch/x86/pci/numachip.c | 24 +-------- 5 files changed, 128 insertions(+), 66 deletions(-)
diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h index d024f4d..c57c225 100644 --- a/arch/x86/include/asm/pci_x86.h +++ b/arch/x86/include/asm/pci_x86.h @@ -137,6 +137,11 @@ struct pci_mmcfg_region { char name[PCI_MMCFG_RESOURCE_NAME_LEN]; };
+struct pci_mmcfg_mmio_ops { + u32 (*read)(int len, void __iomem *addr); + void (*write)(int len, void __iomem *addr, u32 value); +}; + 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); @@ -145,6 +150,9 @@ 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 u32 pci_mmio_read(int len, void __iomem *addr); +extern void pci_mmio_write(int len, void __iomem *addr, u32 value); +extern void pci_mmconfig_register_mmio(struct pci_mmcfg_mmio_ops *ops);
extern struct list_head pci_mmcfg_list;
diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c index dd30b7e..8b3bc4f 100644 --- a/arch/x86/pci/mmconfig-shared.c +++ b/arch/x86/pci/mmconfig-shared.c @@ -31,6 +31,118 @@ static DEFINE_MUTEX(pci_mmcfg_lock);
LIST_HEAD(pci_mmcfg_list);
+static u32 +pci_mmconfig_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_mmconfig_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_mmcfg_mmio_ops pci_mmcfg_mmio_default = { + .read = pci_mmconfig_generic_read, + .write = pci_mmconfig_generic_write, +}; + +static struct pci_mmcfg_mmio_ops *pci_mmcfg_mmio = &pci_mmcfg_mmio_default; + +static u32 +pci_mmconfig_amd_read(int len, void __iomem *addr) +{ + u32 data = 0; + + 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; + } + + return data; +} + +static void +pci_mmconfig_amd_write(int len, void __iomem *addr, u32 value) +{ + 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; + } +} + +static struct pci_mmcfg_mmio_ops pci_mmcfg_mmio_amd_fam10h = { + .read = pci_mmconfig_amd_read, + .write = pci_mmconfig_amd_write, +}; + +void +pci_mmconfig_register_mmio(struct pci_mmcfg_mmio_ops *ops) +{ + pci_mmcfg_mmio = ops; +} + +u32 +pci_mmio_read(int len, void __iomem *addr) +{ + if (!pci_mmcfg_mmio) { + pr_err("PCI config space has no accessors !"); + return 0; + } + + return pci_mmcfg_mmio->read(len, addr); +} + +void +pci_mmio_write(int len, void __iomem *addr, u32 value) +{ + if (!pci_mmcfg_mmio) { + pr_err("PCI config space has no accessors !"); + return; + } + + pci_mmcfg_mmio->write(len, addr, value); +} + static void __init pci_mmconfig_remove(struct pci_mmcfg_region *cfg) { if (cfg->res.parent) @@ -231,6 +343,8 @@ static const char *__init pci_mmcfg_amd_fam10h(void) return NULL; }
+ pci_mmconfig_register_mmio(&pci_mmcfg_mmio_amd_fam10h); + return "AMD Family 10h NB"; }
diff --git a/arch/x86/pci/mmconfig_32.c b/arch/x86/pci/mmconfig_32.c index 43984bc..4b3d025 100644 --- a/arch/x86/pci/mmconfig_32.c +++ b/arch/x86/pci/mmconfig_32.c @@ -71,17 +71,7 @@ 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();
@@ -108,17 +98,7 @@ 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();
diff --git a/arch/x86/pci/mmconfig_64.c b/arch/x86/pci/mmconfig_64.c index bea5249..032593d 100644 --- a/arch/x86/pci/mmconfig_64.c +++ b/arch/x86/pci/mmconfig_64.c @@ -42,17 +42,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; @@ -74,17 +64,7 @@ static int pci_mmcfg_write(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/arch/x86/pci/numachip.c b/arch/x86/pci/numachip.c index 2e565e6..5047e9b 100644 --- a/arch/x86/pci/numachip.c +++ b/arch/x86/pci/numachip.c @@ -51,17 +51,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 +77,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;
On Wed, Mar 11, 2015 at 9:12 AM, Tomasz Nowicki tomasz.nowicki@linaro.org wrote:
From now on, readb()/writeb()/etc. generic calls are used as default approach. Special MMIO accessors are registered for AMD Fam10h CPUs only.
Signed-off-by: Tomasz Nowicki tomasz.nowicki@linaro.org
arch/x86/include/asm/pci_x86.h | 8 +++ arch/x86/pci/mmconfig-shared.c | 114 +++++++++++++++++++++++++++++++++++++++++ arch/x86/pci/mmconfig_32.c | 24 +-------- arch/x86/pci/mmconfig_64.c | 24 +-------- arch/x86/pci/numachip.c | 24 +-------- 5 files changed, 128 insertions(+), 66 deletions(-)
diff --git a/arch/x86/include/asm/pci_x86.h b/arch/x86/include/asm/pci_x86.h index d024f4d..c57c225 100644 --- a/arch/x86/include/asm/pci_x86.h +++ b/arch/x86/include/asm/pci_x86.h @@ -137,6 +137,11 @@ struct pci_mmcfg_region { char name[PCI_MMCFG_RESOURCE_NAME_LEN]; };
+struct pci_mmcfg_mmio_ops {
u32 (*read)(int len, void __iomem *addr);
void (*write)(int len, void __iomem *addr, u32 value);
We already have nearly the same struct with pci_ops...
+};
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); @@ -145,6 +150,9 @@ 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 u32 pci_mmio_read(int len, void __iomem *addr); +extern void pci_mmio_write(int len, void __iomem *addr, u32 value); +extern void pci_mmconfig_register_mmio(struct pci_mmcfg_mmio_ops *ops);
extern struct list_head pci_mmcfg_list;
diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c index dd30b7e..8b3bc4f 100644 --- a/arch/x86/pci/mmconfig-shared.c +++ b/arch/x86/pci/mmconfig-shared.c @@ -31,6 +31,118 @@ static DEFINE_MUTEX(pci_mmcfg_lock);
LIST_HEAD(pci_mmcfg_list);
+static u32 +pci_mmconfig_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;
}
This same function logic already exists with pci_generic_config_read. I think you need to move the differentiation between AMD and generic ECAM to pci_ops.
Rob
This patch is the first step for MMCONFIG refactoring process.
Code that uses pci_mmcfg_lock will be moved to common file and become accessible for all architectures. pci_mmconfig_insert() cannot be moved so easily since it is mixing generic mmconfig code with x86 specific logic inside of mutual exclusive block guarded by pci_mmcfg_lock.
To get rid of that constraint, we reorder actions as follow: 1. mmconfig entry allocation can be done first, no need to lock 2. insertion to iomem_resource has its own lock, no need to wrap it into mutex 3. insertion to mmconfig list can be done as the final step in separate function (candidate for further refactoring)
Signed-off-by: Tomasz Nowicki tomasz.nowicki@linaro.org --- arch/x86/pci/mmconfig-shared.c | 100 ++++++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 46 deletions(-)
diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c index 8b3bc4f..685cd4c 100644 --- a/arch/x86/pci/mmconfig-shared.c +++ b/arch/x86/pci/mmconfig-shared.c @@ -834,6 +834,39 @@ static int __init pci_mmcfg_late_insert_resources(void) */ late_initcall(pci_mmcfg_late_insert_resources);
+static int pci_mmconfig_inject(struct pci_mmcfg_region *cfg) +{ + struct pci_mmcfg_region *cfg_conflict; + int err = 0; + + mutex_lock(&pci_mmcfg_lock); + cfg_conflict = pci_mmconfig_lookup(cfg->segment, cfg->start_bus); + if (cfg_conflict) { + if (cfg_conflict->end_bus < cfg->end_bus) + pr_info(FW_INFO "MMCONFIG 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_mmcfg_arch_map(cfg)) { + pr_warn("fail to map MMCONFIG %pR.\n", &cfg->res); + err = -ENOMEM; + goto out; + } else { + list_add_sorted(cfg); + pr_info("MMCONFIG at %pR (base %#lx)\n", + &cfg->res, (unsigned long)cfg->address); + + } +out: + mutex_unlock(&pci_mmcfg_lock); + return err; +} + /* Add MMCFG information for host bridges */ int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, phys_addr_t addr) @@ -845,66 +878,41 @@ int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, if (!(pci_probe & PCI_PROBE_MMCONF) || pci_mmcfg_arch_init_failed) return -ENODEV;
- if (start > end) + if (start > end || !addr) 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); - return -EEXIST; - } - - if (!addr) { - mutex_unlock(&pci_mmcfg_lock); - return -EINVAL; - } - rc = -EBUSY; cfg = pci_mmconfig_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_mmconfig_inject(cfg); + if (rc) + goto error; + + return 0;
+error: + if (cfg->res.parent) + release_resource(&cfg->res); + kfree(cfg); return rc; }
ECAM standard and MCFG table are architecture independent and it makes sense to share common code across all architectures. Both are going to corresponding files - ecam.c and mcfg.c
While we are here, rename pci_parse_mcfg to acpi_parse_mcfg. We already have acpi_parse_mcfg prototype which is used nowhere. At the same time, we need pci_parse_mcfg been global so acpi_parse_mcfg can be used perfectly here.
Signed-off-by: Tomasz Nowicki tomasz.nowicki@linaro.org --- arch/x86/Kconfig | 3 + arch/x86/include/asm/pci_x86.h | 33 ----- arch/x86/pci/acpi.c | 1 + arch/x86/pci/mmconfig-shared.c | 296 +---------------------------------------- arch/x86/pci/mmconfig_32.c | 1 + arch/x86/pci/mmconfig_64.c | 1 + arch/x86/pci/numachip.c | 1 + drivers/acpi/Makefile | 1 + drivers/acpi/mcfg.c | 81 +++++++++++ drivers/pci/Kconfig | 7 + drivers/pci/Makefile | 5 + drivers/pci/ecam.c | 245 ++++++++++++++++++++++++++++++++++ drivers/xen/pci.c | 1 + include/linux/ecam.h | 51 +++++++ 14 files changed, 401 insertions(+), 326 deletions(-) create mode 100644 drivers/acpi/mcfg.c create mode 100644 drivers/pci/ecam.c create mode 100644 include/linux/ecam.h
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index c2fb8a8..aa451bf 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -141,6 +141,7 @@ config X86 select ACPI_LEGACY_TABLES_LOOKUP if ACPI select X86_FEATURE_NAMES if PROC_FS select SRCU + select HAVE_PCI_ECAM
config INSTRUCTION_DECODER def_bool y @@ -2276,6 +2277,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 @@ -2293,6 +2295,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 c57c225..e8a237f 100644 --- a/arch/x86/include/asm/pci_x86.h +++ b/arch/x86/include/asm/pci_x86.h @@ -122,41 +122,8 @@ 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]; -}; - -struct pci_mmcfg_mmio_ops { - u32 (*read)(int len, void __iomem *addr); - void (*write)(int len, void __iomem *addr, u32 value); -}; - -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 u32 pci_mmio_read(int len, void __iomem *addr); -extern void pci_mmio_write(int len, void __iomem *addr, u32 value); -extern void pci_mmconfig_register_mmio(struct pci_mmcfg_mmio_ops *ops); - -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 diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index 6ac2738..5dfccef 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>
diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c index 685cd4c..5064302 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,52 +28,6 @@ /* 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 u32 -pci_mmconfig_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_mmconfig_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_mmcfg_mmio_ops pci_mmcfg_mmio_default = { - .read = pci_mmconfig_generic_read, - .write = pci_mmconfig_generic_write, -}; - -static struct pci_mmcfg_mmio_ops *pci_mmcfg_mmio = &pci_mmcfg_mmio_default;
static u32 pci_mmconfig_amd_read(int len, void __iomem *addr) @@ -115,128 +70,6 @@ static struct pci_mmcfg_mmio_ops pci_mmcfg_mmio_amd_fam10h = { .write = pci_mmconfig_amd_write, };
-void -pci_mmconfig_register_mmio(struct pci_mmcfg_mmio_ops *ops) -{ - pci_mmcfg_mmio = ops; -} - -u32 -pci_mmio_read(int len, void __iomem *addr) -{ - if (!pci_mmcfg_mmio) { - pr_err("PCI config space has no accessors !"); - return 0; - } - - return pci_mmcfg_mmio->read(len, addr); -} - -void -pci_mmio_write(int len, void __iomem *addr, u32 value) -{ - if (!pci_mmcfg_mmio) { - pr_err("PCI config space has no accessors !"); - return; - } - - pci_mmcfg_mmio->write(len, addr, value); -} - -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); -} - -static void __init free_all_mmcfg(void) -{ - struct pci_mmcfg_region *cfg, *tmp; - - 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; - } - } - 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; -} - -static struct pci_mmcfg_region *__init pci_mmconfig_add(int segment, int start, - int end, u64 addr) -{ - 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); - } - - 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 const char *__init pci_mmcfg_e7520(void) { u32 win; @@ -657,73 +490,6 @@ static void __init pci_mmcfg_reject_broken(int early) } }
-static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, - struct acpi_mcfg_allocation *cfg) -{ - int year; - - if (cfg->address < 0xFFFFFFFF) - return 0; - - if (!strncmp(mcfg->header.oem_id, "SGI", 3)) - return 0; - - if (mcfg->header.revision >= 1) { - if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && - year >= 2010) - return 0; - } - - pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx " - "is above 4GB, ignored\n", cfg->pci_segment, - cfg->start_bus_number, cfg->end_bus_number, cfg->address); - 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); @@ -782,7 +548,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(); @@ -800,7 +566,7 @@ 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); } } @@ -834,39 +600,6 @@ static int __init pci_mmcfg_late_insert_resources(void) */ late_initcall(pci_mmcfg_late_insert_resources);
-static int pci_mmconfig_inject(struct pci_mmcfg_region *cfg) -{ - struct pci_mmcfg_region *cfg_conflict; - int err = 0; - - mutex_lock(&pci_mmcfg_lock); - cfg_conflict = pci_mmconfig_lookup(cfg->segment, cfg->start_bus); - if (cfg_conflict) { - if (cfg_conflict->end_bus < cfg->end_bus) - pr_info(FW_INFO "MMCONFIG 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_mmcfg_arch_map(cfg)) { - pr_warn("fail to map MMCONFIG %pR.\n", &cfg->res); - err = -ENOMEM; - goto out; - } else { - list_add_sorted(cfg); - pr_info("MMCONFIG at %pR (base %#lx)\n", - &cfg->res, (unsigned long)cfg->address); - - } -out: - mutex_unlock(&pci_mmcfg_lock); - return err; -} - /* Add MMCFG information for host bridges */ int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, phys_addr_t addr) @@ -915,26 +648,3 @@ error: kfree(cfg); 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 -ENOENT; -} diff --git a/arch/x86/pci/mmconfig_32.c b/arch/x86/pci/mmconfig_32.c index 4b3d025..5cf6291 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>
diff --git a/arch/x86/pci/mmconfig_64.c b/arch/x86/pci/mmconfig_64.c index 032593d..b62ff18 100644 --- a/arch/x86/pci/mmconfig_64.c +++ b/arch/x86/pci/mmconfig_64.c @@ -10,6 +10,7 @@ #include <linux/acpi.h> #include <linux/bitmap.h> #include <linux/rcupdate.h> +#include <linux/ecam.h> #include <asm/e820.h> #include <asm/pci_x86.h>
diff --git a/arch/x86/pci/numachip.c b/arch/x86/pci/numachip.c index 5047e9b..01868b6 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>
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 623b117..39498a1 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -67,6 +67,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 0000000..a2e2326 --- /dev/null +++ b/drivers/acpi/mcfg.c @@ -0,0 +1,81 @@ +/* + * 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/dmi.h> +#include <linux/ecam.h> + +#define PREFIX "MCFG: " + +static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, + struct acpi_mcfg_allocation *cfg) +{ + int year; + + if (cfg->address < 0xFFFFFFFF) + return 0; + + if (!strncmp(mcfg->header.oem_id, "SGI", 3)) + return 0; + + if (mcfg->header.revision >= 1) { + if (dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL) && + year >= 2010) + return 0; + } + + pr_err(PREFIX "MCFG region for %04x [bus %02x-%02x] at %#llx " + "is above 4GB, ignored\n", cfg->pci_segment, + cfg->start_bus_number, cfg->end_bus_number, cfg->address); + return -EINVAL; +} + +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 */ + 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 "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)) { + 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; +} diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 7a8f1c5..90a5fb9 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -22,6 +22,13 @@ 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 PCI_DEBUG bool "PCI Debugging" depends on PCI && DEBUG_KERNEL diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 73e4af4..ce7b630 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 0000000..c588234 --- /dev/null +++ b/drivers/pci/ecam.c @@ -0,0 +1,245 @@ +/* + * 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: " + +static DEFINE_MUTEX(pci_mmcfg_lock); + +LIST_HEAD(pci_mmcfg_list); + +static u32 +pci_mmconfig_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_mmconfig_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_mmcfg_mmio_ops pci_mmcfg_mmio_default = { + .read = pci_mmconfig_generic_read, + .write = pci_mmconfig_generic_write, +}; + +static struct pci_mmcfg_mmio_ops *pci_mmcfg_mmio = &pci_mmcfg_mmio_default; + +void +pci_mmconfig_register_mmio(struct pci_mmcfg_mmio_ops *ops) +{ + pci_mmcfg_mmio = ops; +} + +u32 +pci_mmio_read(int len, void __iomem *addr) +{ + if (!pci_mmcfg_mmio) { + pr_err("PCI config space has no accessors !"); + return 0; + } + + return pci_mmcfg_mmio->read(len, addr); +} + +void +pci_mmio_write(int len, void __iomem *addr, u32 value) +{ + if (!pci_mmcfg_mmio) { + pr_err("PCI config space has no accessors !"); + return; + } + + pci_mmcfg_mmio->write(len, addr, value); +} + +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); +} + +void __init free_all_mmcfg(void) +{ + struct pci_mmcfg_region *cfg, *tmp; + + pci_mmcfg_arch_free(); + list_for_each_entry_safe(cfg, tmp, &pci_mmcfg_list, list) + pci_mmconfig_remove(cfg); +} + +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; + } + } + list_add_tail_rcu(&new->list, &pci_mmcfg_list); +} + +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; +} + +struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, + int end, u64 addr) +{ + 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); + } + + 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; +} + +/* 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 -ENOENT; +} + +int pci_mmconfig_inject(struct pci_mmcfg_region *cfg) +{ + struct pci_mmcfg_region *cfg_conflict; + int err = 0; + + mutex_lock(&pci_mmcfg_lock); + cfg_conflict = pci_mmconfig_lookup(cfg->segment, cfg->start_bus); + if (cfg_conflict) { + if (cfg_conflict->end_bus < cfg->end_bus) + pr_info(FW_INFO "MMCONFIG 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_mmcfg_arch_map(cfg)) { + pr_warn("fail to map MMCONFIG %pR.\n", &cfg->res); + err = -ENOMEM; + goto out; + } else { + list_add_sorted(cfg); + pr_info("MMCONFIG at %pR (base %#lx)\n", + &cfg->res, (unsigned long)cfg->address); + + } +out: + mutex_unlock(&pci_mmcfg_lock); + return err; +} diff --git a/drivers/xen/pci.c b/drivers/xen/pci.c index 95ee430..7a34a72 100644 --- a/drivers/xen/pci.c +++ b/drivers/xen/pci.c @@ -19,6 +19,7 @@
#include <linux/pci.h> #include <linux/acpi.h> +#include <linux/ecam.h> #include <xen/xen.h> #include <xen/interface/physdev.h> #include <xen/interface/xen.h> diff --git a/include/linux/ecam.h b/include/linux/ecam.h new file mode 100644 index 0000000..2387df5 --- /dev/null +++ b/include/linux/ecam.h @@ -0,0 +1,51 @@ +#ifndef __ECAM_H +#define __ECAM_H +#ifdef __KERNEL__ + +#include <linux/types.h> +#include <linux/acpi.h> + +/* "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]; +}; + +struct pci_mmcfg_mmio_ops { + u32 (*read)(int len, void __iomem *addr); + void (*write)(int len, void __iomem *addr, u32 value); +}; + +struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus); +struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start, + int end, u64 addr); +int pci_mmconfig_inject(struct pci_mmcfg_region *cfg); +struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, + int end, u64 addr); +void list_add_sorted(struct pci_mmcfg_region *new); +void free_all_mmcfg(void); +int pci_mmconfig_delete(u16 seg, u8 start, u8 end); + +/* Arch specific calls */ +int pci_mmcfg_arch_init(void); +void pci_mmcfg_arch_free(void); +int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg); +void pci_mmcfg_arch_unmap(struct pci_mmcfg_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_mmconfig_register_mmio(struct pci_mmcfg_mmio_ops *ops); + +extern struct list_head pci_mmcfg_list; + +#define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20) + +#endif /* __KERNEL__ */ +#endif /* __ECAM_H */
Those two functions are required by ACPI subsystem to make PCI config space accessible. Generic code assume to do nothing for early init call but late init call does as follow: - parse MCFG table and add regions to ECAM resource list - map regions - add regions to iomem_resource
Signed-off-by: Tomasz Nowicki tomasz.nowicki@linaro.org --- drivers/acpi/mcfg.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)
diff --git a/drivers/acpi/mcfg.c b/drivers/acpi/mcfg.c index a2e2326..1e646fc 100644 --- a/drivers/acpi/mcfg.c +++ b/drivers/acpi/mcfg.c @@ -79,3 +79,23 @@ int __init acpi_parse_mcfg(struct acpi_table_header *header)
return 0; } + +void __init __weak pci_mmcfg_early_init(void) +{ + +} + +void __init __weak pci_mmcfg_late_init(void) +{ + struct pci_mmcfg_region *cfg; + + acpi_table_parse(ACPI_SIG_MCFG, acpi_parse_mcfg); + + if (list_empty(&pci_mmcfg_list)) + return; + if (!pci_mmcfg_arch_init()) + free_all_mmcfg(); + + list_for_each_entry(cfg, &pci_mmcfg_list, list) + insert_resource(&iomem_resource, &cfg->res); +}
mmconfig_64.c version is going to be default implementation for arch agnostic low-level direct PCI config space accessors for ECAM dirver. However, now it initialize raw_pci_ext_ops pointer which is x86 specific code only. Moreover, mmconfig_32.c is doing the same thing at the same time.
Move it to mmconfig_shared.c so it becomes common for both and mmconfig_64.c turns out to be purely arch agnostic.
Signed-off-by: Tomasz Nowicki tomasz.nowicki@linaro.org --- arch/x86/pci/mmconfig-shared.c | 10 ++++++++-- arch/x86/pci/mmconfig_32.c | 10 ++-------- arch/x86/pci/mmconfig_64.c | 11 ++--------- include/linux/ecam.h | 5 +++++ 4 files changed, 17 insertions(+), 19 deletions(-)
diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c index 5064302..6dfe614 100644 --- a/arch/x86/pci/mmconfig-shared.c +++ b/arch/x86/pci/mmconfig-shared.c @@ -29,6 +29,11 @@ static bool pci_mmcfg_running_state; static bool pci_mmcfg_arch_init_failed;
+const struct pci_raw_ops pci_mmcfg = { + .read = pci_mmcfg_read, + .write = pci_mmcfg_write, +}; + static u32 pci_mmconfig_amd_read(int len, void __iomem *addr) { @@ -532,9 +537,10 @@ static void __init __pci_mmcfg_init(int early) } }
- if (pci_mmcfg_arch_init()) + if (pci_mmcfg_arch_init()) { + raw_pci_ext_ops = &pci_mmcfg; pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF; - else { + } else { free_all_mmcfg(); pci_mmcfg_arch_init_failed = true; } diff --git a/arch/x86/pci/mmconfig_32.c b/arch/x86/pci/mmconfig_32.c index 5cf6291..7a050cb 100644 --- a/arch/x86/pci/mmconfig_32.c +++ b/arch/x86/pci/mmconfig_32.c @@ -50,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_mmcfg_read(unsigned int seg, unsigned int bus, unsigned int devfn, int reg, int len, u32 *value) { unsigned long flags; @@ -79,7 +79,7 @@ err: *value = -1; return 0; }
-static int pci_mmcfg_write(unsigned int seg, unsigned int bus, +int pci_mmcfg_write(unsigned int seg, unsigned int bus, unsigned int devfn, int reg, int len, u32 value) { unsigned long flags; @@ -106,15 +106,9 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus, return 0; }
-const struct pci_raw_ops pci_mmcfg = { - .read = pci_mmcfg_read, - .write = pci_mmcfg_write, -}; - int __init pci_mmcfg_arch_init(void) { printk(KERN_INFO "PCI: Using MMCONFIG for extended config space\n"); - raw_pci_ext_ops = &pci_mmcfg; return 1; }
diff --git a/arch/x86/pci/mmconfig_64.c b/arch/x86/pci/mmconfig_64.c index b62ff18..fd857ea 100644 --- a/arch/x86/pci/mmconfig_64.c +++ b/arch/x86/pci/mmconfig_64.c @@ -25,7 +25,7 @@ static char __iomem *pci_dev_base(unsigned int seg, unsigned int bus, unsigned i return NULL; }
-static int pci_mmcfg_read(unsigned int seg, unsigned int bus, +int pci_mmcfg_read(unsigned int seg, unsigned int bus, unsigned int devfn, int reg, int len, u32 *value) { char __iomem *addr; @@ -49,7 +49,7 @@ err: *value = -1; return 0; }
-static int pci_mmcfg_write(unsigned int seg, unsigned int bus, +int pci_mmcfg_write(unsigned int seg, unsigned int bus, unsigned int devfn, int reg, int len, u32 value) { char __iomem *addr; @@ -71,11 +71,6 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus, 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; @@ -101,8 +96,6 @@ int __init pci_mmcfg_arch_init(void) return 0; }
- raw_pci_ext_ops = &pci_mmcfg; - return 1; }
diff --git a/include/linux/ecam.h b/include/linux/ecam.h index 2387df5..fba5d6b 100644 --- a/include/linux/ecam.h +++ b/include/linux/ecam.h @@ -47,5 +47,10 @@ extern struct list_head pci_mmcfg_list;
#define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20)
+int pci_mmcfg_read(unsigned int seg, unsigned int bus, + unsigned int devfn, int reg, int len, u32 *value); +int pci_mmcfg_write(unsigned int seg, unsigned int bus, + unsigned int devfn, int reg, int len, u32 value); + #endif /* __KERNEL__ */ #endif /* __ECAM_H */
Architectures which want to take advantage of ECAM generic goodness should select CONFIG_PCI_ECAM_GENERIC. Otherwise, like x86 32bits machines, are obligated to provide own low-level ECAM calls.
Signed-off-by: Tomasz Nowicki tomasz.nowicki@linaro.org --- arch/x86/Kconfig | 1 + arch/x86/pci/Makefile | 5 +- arch/x86/pci/mmconfig_64.c | 127 --------------------------------------------- drivers/pci/Kconfig | 3 ++ drivers/pci/ecam.c | 113 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 121 insertions(+), 128 deletions(-) delete mode 100644 arch/x86/pci/mmconfig_64.c
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index aa451bf..3bf1aa5 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -142,6 +142,7 @@ config X86 select X86_FEATURE_NAMES if PROC_FS select SRCU select HAVE_PCI_ECAM + select GENERIC_PCI_ECAM if X86_64
config INSTRUCTION_DECODER def_bool y diff --git a/arch/x86/pci/Makefile b/arch/x86/pci/Makefile index 5c6fc35..35c765b 100644 --- a/arch/x86/pci/Makefile +++ b/arch/x86/pci/Makefile @@ -1,7 +1,10 @@ obj-y := i386.o init.o
obj-$(CONFIG_PCI_BIOS) += pcbios.o -obj-$(CONFIG_PCI_MMCONFIG) += mmconfig_$(BITS).o direct.o mmconfig-shared.o +obj-$(CONFIG_PCI_MMCONFIG) += direct.o mmconfig-shared.o +ifeq ($(BITS),32) +obj-$(CONFIG_PCI_MMCONFIG) += mmconfig_32.o +endif obj-$(CONFIG_PCI_DIRECT) += direct.o obj-$(CONFIG_PCI_OLPC) += olpc.o obj-$(CONFIG_PCI_XEN) += xen.o diff --git a/arch/x86/pci/mmconfig_64.c b/arch/x86/pci/mmconfig_64.c deleted file mode 100644 index fd857ea..0000000 --- a/arch/x86/pci/mmconfig_64.c +++ /dev/null @@ -1,127 +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 <linux/ecam.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; -} - -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; - } - - *value = pci_mmio_read(len, addr + reg); - rcu_read_unlock(); - - return 0; -} - -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; - } - - pci_mmio_write(len, addr + reg, value); - rcu_read_unlock(); - - return 0; -} - -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; - } - - 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/drivers/pci/Kconfig b/drivers/pci/Kconfig index 90a5fb9..fae4aa7 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -29,6 +29,9 @@ config 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/ecam.c b/drivers/pci/ecam.c index c588234..796b6e7 100644 --- a/drivers/pci/ecam.c +++ b/drivers/pci/ecam.c @@ -23,6 +23,119 @@ static DEFINE_MUTEX(pci_mmcfg_lock);
LIST_HEAD(pci_mmcfg_list);
+#ifdef CONFIG_GENERIC_PCI_ECAM +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; +} + +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; + } + + *value = pci_mmio_read(len, addr + reg); + rcu_read_unlock(); + + return 0; +} + +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; + } + + pci_mmio_write(len, addr + reg, value); + rcu_read_unlock(); + + return 0; +} + +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; + } + + 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; + } +} +#endif + static u32 pci_mmconfig_generic_read(int len, void __iomem *addr) {
On Wed, Mar 11, 2015 at 9:12 AM, Tomasz Nowicki tomasz.nowicki@linaro.org wrote:
Architectures which want to take advantage of ECAM generic goodness
This is not necessarily an architecture decision. It is likely per host.
should select CONFIG_PCI_ECAM_GENERIC. Otherwise, like x86 32bits machines, are obligated to provide own low-level ECAM calls.
Signed-off-by: Tomasz Nowicki tomasz.nowicki@linaro.org
[...]
diff --git a/drivers/pci/ecam.c b/drivers/pci/ecam.c index c588234..796b6e7 100644 --- a/drivers/pci/ecam.c +++ b/drivers/pci/ecam.c @@ -23,6 +23,119 @@ static DEFINE_MUTEX(pci_mmcfg_lock);
LIST_HEAD(pci_mmcfg_list);
+#ifdef CONFIG_GENERIC_PCI_ECAM +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;
+}
+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();
What is the purpose of the rcu lock other than the old implementation had it?
addr = pci_dev_base(seg, bus, devfn);
The .map_bus op provides the same function if you restructure to use the generic accessors.
if (!addr) {
rcu_read_unlock();
goto err;
}
*value = pci_mmio_read(len, addr + reg);
rcu_read_unlock();
return 0;
+}
+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;
}
pci_mmio_write(len, addr + reg, value);
rcu_read_unlock();
return 0;
+}
+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)
Where would this be called for the case of the generic host and using DT?
Rob
On 11.03.2015 16:37, Rob Herring wrote:
On Wed, Mar 11, 2015 at 9:12 AM, Tomasz Nowicki tomasz.nowicki@linaro.org wrote:
Architectures which want to take advantage of ECAM generic goodness
This is not necessarily an architecture decision. It is likely per host.
Right, good point.
should select CONFIG_PCI_ECAM_GENERIC. Otherwise, like x86 32bits machines, are obligated to provide own low-level ECAM calls.
Signed-off-by: Tomasz Nowicki tomasz.nowicki@linaro.org
[...]
diff --git a/drivers/pci/ecam.c b/drivers/pci/ecam.c index c588234..796b6e7 100644 --- a/drivers/pci/ecam.c +++ b/drivers/pci/ecam.c @@ -23,6 +23,119 @@ static DEFINE_MUTEX(pci_mmcfg_lock);
LIST_HEAD(pci_mmcfg_list);
+#ifdef CONFIG_GENERIC_PCI_ECAM +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;
+}
+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();
What is the purpose of the rcu lock other than the old implementation had it?
Read/write calls consist on lookup RCU list (with MMCONFIG regions) and then corresponding operation. It is possible to hotplug another pci root bridge which leads to RCU list modification.
addr = pci_dev_base(seg, bus, devfn);
The .map_bus op provides the same function if you restructure to use the generic accessors.
As you noticed, pci_mmcfg_{read,write} and pci_generic_config_{read,write} prototypes are different.
int pci_mmcfg_read(unsigned int seg, unsigned int bus, unsigned int devfn, int reg, int len, u32 *value); vs int pci_generic_config_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val);
This is because pci_mmcfg_{read,write} can be used before pci root bridge initialization (while we have no struct pci_bus *bus) inside of ACPICA code (osl.c --> acpi_os_read_pci_configuration())
For that reason, I decide to create ECAM related new accessors which do not depend on host bridge presence. In other words, pci_generic_config_{read,write} can be built on pci_mmcfg_{read,write} but not the other way around.
In the light of above, I could not used .map_bus. I might not see a nicer way to solve that so any opinion/suggestion very appreciated :)
if (!addr) {
rcu_read_unlock();
goto err;
}
*value = pci_mmio_read(len, addr + reg);
rcu_read_unlock();
return 0;
+}
+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;
}
pci_mmio_write(len, addr + reg, value);
rcu_read_unlock();
return 0;
+}
+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)
Where would this be called for the case of the generic host and using DT?
I focused on sharing the code in ACPI context and did not consider DT. I think we can improve that code as next steps.
Tomasz
On Wed, Mar 11, 2015 at 10:12 AM, Tomasz Nowicki tomasz.nowicki@linaro.org wrote:
diff --git a/arch/x86/pci/Makefile b/arch/x86/pci/Makefile index 5c6fc35..35c765b 100644 --- a/arch/x86/pci/Makefile +++ b/arch/x86/pci/Makefile @@ -1,7 +1,10 @@ obj-y := i386.o init.o
obj-$(CONFIG_PCI_BIOS) += pcbios.o -obj-$(CONFIG_PCI_MMCONFIG) += mmconfig_$(BITS).o direct.o mmconfig-shared.o +obj-$(CONFIG_PCI_MMCONFIG) += direct.o mmconfig-shared.o +ifeq ($(BITS),32) +obj-$(CONFIG_PCI_MMCONFIG) += mmconfig_32.o +endif obj-$(CONFIG_PCI_DIRECT) += direct.o obj-$(CONFIG_PCI_OLPC) += olpc.o obj-$(CONFIG_PCI_XEN) += xen.o
This would be better written as:
mmconfig-y := direct.o mmconfig-shared.o mmconfig-$(CONFIG_X86_32) += mmconfig_32.o obj-$(CONFIG_PCI_MMCONFIG) += $(mmconfig-y)
-- Brian Gerst
On 11.03.2015 17:30, Brian Gerst wrote:
On Wed, Mar 11, 2015 at 10:12 AM, Tomasz Nowicki tomasz.nowicki@linaro.org wrote:
diff --git a/arch/x86/pci/Makefile b/arch/x86/pci/Makefile index 5c6fc35..35c765b 100644 --- a/arch/x86/pci/Makefile +++ b/arch/x86/pci/Makefile @@ -1,7 +1,10 @@ obj-y := i386.o init.o
obj-$(CONFIG_PCI_BIOS) += pcbios.o -obj-$(CONFIG_PCI_MMCONFIG) += mmconfig_$(BITS).o direct.o mmconfig-shared.o +obj-$(CONFIG_PCI_MMCONFIG) += direct.o mmconfig-shared.o +ifeq ($(BITS),32) +obj-$(CONFIG_PCI_MMCONFIG) += mmconfig_32.o +endif obj-$(CONFIG_PCI_DIRECT) += direct.o obj-$(CONFIG_PCI_OLPC) += olpc.o obj-$(CONFIG_PCI_XEN) += xen.o
This would be better written as:
mmconfig-y := direct.o mmconfig-shared.o mmconfig-$(CONFIG_X86_32) += mmconfig_32.o obj-$(CONFIG_PCI_MMCONFIG) += $(mmconfig-y)
Nice! Will update my patch.
Tomasz
MCFG can be used perfectly for all architectures which support ACPI. ACPI mandates MCFG to describe PCI config space ranges which means we should use MMCONFIG accessors by default.
Signed-off-by: Tomasz Nowicki tomasz.nowicki@linaro.org --- drivers/acpi/mcfg.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+)
diff --git a/drivers/acpi/mcfg.c b/drivers/acpi/mcfg.c index 1e646fc..e285f62 100644 --- a/drivers/acpi/mcfg.c +++ b/drivers/acpi/mcfg.c @@ -13,6 +13,26 @@
#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_mmcfg_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_mmcfg_write(domain, bus, devfn, reg, len, val); +} + static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, struct acpi_mcfg_allocation *cfg) {
This commit is consequence of making x86 MMCONFIG code available for all architectures. Current code has old name fashion so for the sake of making it more understandable and express where it comes from, we are changing functions/variables/macros etc. name *mmconfig* -> *ecam*
Signed-off-by: Tomasz Nowicki tomasz.nowicki@linaro.org --- arch/x86/pci/acpi.c | 2 +- arch/x86/pci/mmconfig-shared.c | 70 +++++++++--------- arch/x86/pci/mmconfig_32.c | 16 ++-- arch/x86/pci/numachip.c | 4 +- drivers/acpi/mcfg.c | 22 +++--- drivers/pci/ecam.c | 162 ++++++++++++++++++++--------------------- drivers/xen/pci.c | 6 +- include/linux/ecam.h | 42 +++++------ 8 files changed, 162 insertions(+), 162 deletions(-)
diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index 5dfccef..fa3ef35 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -199,7 +199,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 6dfe614..8f78671 100644 --- a/arch/x86/pci/mmconfig-shared.c +++ b/arch/x86/pci/mmconfig-shared.c @@ -30,8 +30,8 @@ static bool pci_mmcfg_running_state; static bool pci_mmcfg_arch_init_failed;
const struct pci_raw_ops pci_mmcfg = { - .read = pci_mmcfg_read, - .write = pci_mmcfg_write, + .read = pci_ecam_read, + .write = pci_ecam_write, };
static u32 @@ -70,7 +70,7 @@ pci_mmconfig_amd_write(int len, void __iomem *addr, u32 value) } }
-static struct pci_mmcfg_mmio_ops pci_mmcfg_mmio_amd_fam10h = { +static struct pci_ecam_mmio_ops pci_mmcfg_mmio_amd_fam10h = { .read = pci_mmconfig_amd_read, .write = pci_mmconfig_amd_write, }; @@ -84,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"; @@ -128,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"; @@ -175,13 +175,13 @@ 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_mmconfig_register_mmio(&pci_mmcfg_mmio_amd_fam10h); + pci_ecam_register_mmio(&pci_mmcfg_mmio_amd_fam10h);
return "AMD Family 10h NB"; } @@ -210,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; @@ -239,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++; } @@ -273,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); @@ -301,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; @@ -322,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) @@ -395,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; @@ -425,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);
@@ -447,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)) @@ -484,12 +484,12 @@ 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; } } @@ -502,13 +502,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; @@ -524,24 +524,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(); + pci_ecam_free_all(); pci_mmcfg_arch_init_failed = true; } } @@ -579,7 +579,7 @@ void __init pci_mmcfg_late_init(void)
static int __init pci_mmcfg_late_insert_resources(void) { - struct pci_mmcfg_region *cfg; + struct pci_ecam_region *cfg;
pci_mmcfg_running_state = true;
@@ -592,7 +592,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);
@@ -612,7 +612,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; @@ -621,7 +621,7 @@ int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, 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"); return -ENOMEM; @@ -642,7 +642,7 @@ int pci_mmconfig_insert(struct device *dev, u16 seg, u8 start, u8 end, goto error; }
- rc = pci_mmconfig_inject(cfg); + rc = pci_ecam_inject(cfg); if (rc) goto error;
diff --git a/arch/x86/pci/mmconfig_32.c b/arch/x86/pci/mmconfig_32.c index 7a050cb..27e707d 100644 --- a/arch/x86/pci/mmconfig_32.c +++ b/arch/x86/pci/mmconfig_32.c @@ -28,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; @@ -40,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) { @@ -50,7 +50,7 @@ static void pci_exp_set_dev_base(unsigned int base, int bus, int devfn) } }
-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; @@ -79,7 +79,7 @@ err: *value = -1; return 0; }
-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; @@ -106,22 +106,22 @@ int pci_mmcfg_write(unsigned int seg, unsigned int bus, return 0; }
-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"); 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/numachip.c b/arch/x86/pci/numachip.c index 01868b6..af41247 100644 --- a/arch/x86/pci/numachip.c +++ b/arch/x86/pci/numachip.c @@ -21,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; }
diff --git a/drivers/acpi/mcfg.c b/drivers/acpi/mcfg.c index e285f62..40926a3 100644 --- a/drivers/acpi/mcfg.c +++ b/drivers/acpi/mcfg.c @@ -24,13 +24,13 @@ int __weak raw_pci_read(unsigned int domain, unsigned int bus, unsigned int devfn, int reg, int len, u32 *val) { - return pci_mmcfg_read(domain, bus, devfn, reg, len, 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_mmcfg_write(domain, bus, devfn, reg, len, val); + return pci_ecam_write(domain, bus, devfn, reg, len, val); }
static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg, @@ -69,7 +69,7 @@ int __init acpi_parse_mcfg(struct acpi_table_header *header) mcfg = (struct acpi_table_mcfg *)header;
/* how many config structures do we have */ - free_all_mmcfg(); + pci_ecam_free_all(); entries = 0; i = header->length - sizeof(struct acpi_table_mcfg); while (i >= sizeof(struct acpi_mcfg_allocation)) { @@ -85,14 +85,14 @@ int __init acpi_parse_mcfg(struct acpi_table_header *header) for (i = 0; i < entries; i++) { cfg = &cfg_table[i]; if (acpi_mcfg_check_entry(mcfg, cfg)) { - free_all_mmcfg(); + pci_ecam_free_all(); return -ENODEV; }
- if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number, + 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"); - free_all_mmcfg(); + pci_ecam_free_all(); return -ENOMEM; } } @@ -107,15 +107,15 @@ void __init __weak pci_mmcfg_early_init(void)
void __init __weak pci_mmcfg_late_init(void) { - struct pci_mmcfg_region *cfg; + struct pci_ecam_region *cfg;
acpi_table_parse(ACPI_SIG_MCFG, acpi_parse_mcfg);
- if (list_empty(&pci_mmcfg_list)) + if (list_empty(&pci_ecam_list)) return; - if (!pci_mmcfg_arch_init()) - free_all_mmcfg(); + if (!pci_ecam_arch_init()) + pci_ecam_free_all();
- list_for_each_entry(cfg, &pci_mmcfg_list, list) + list_for_each_entry(cfg, &pci_ecam_list, list) insert_resource(&iomem_resource, &cfg->res); } diff --git a/drivers/pci/ecam.c b/drivers/pci/ecam.c index 796b6e7..d6281a2 100644 --- a/drivers/pci/ecam.c +++ b/drivers/pci/ecam.c @@ -17,24 +17,24 @@
#include <asm/io.h>
-#define PREFIX "PCI: " +#define PREFIX "PCI ECAM: "
-static DEFINE_MUTEX(pci_mmcfg_lock); +static DEFINE_MUTEX(pci_ecam_lock);
-LIST_HEAD(pci_mmcfg_list); +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_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; }
-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) { char __iomem *addr; @@ -58,7 +58,7 @@ err: *value = -1; return 0; }
-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) { char __iomem *addr; @@ -80,64 +80,64 @@ int pci_mmcfg_write(unsigned int seg, unsigned int bus, return 0; }
-static void __iomem *mcfg_ioremap(struct pci_mmcfg_region *cfg) +static void __iomem *pci_ecam_ioremap(struct pci_ecam_region *cfg) { void __iomem *addr; u64 start, size; int num_buses;
- start = cfg->address + PCI_MMCFG_BUS_OFFSET(cfg->start_bus); + start = cfg->address + PCI_ECAM_BUS_OFFSET(cfg->start_bus); num_buses = cfg->end_bus - cfg->start_bus + 1; - size = PCI_MMCFG_BUS_OFFSET(num_buses); + size = PCI_ECAM_BUS_OFFSET(num_buses); addr = ioremap_nocache(start, size); if (addr) - addr -= PCI_MMCFG_BUS_OFFSET(cfg->start_bus); + addr -= PCI_ECAM_BUS_OFFSET(cfg->start_bus); return addr; }
-int __init pci_mmcfg_arch_init(void) +int __init pci_ecam_arch_init(void) { - struct pci_mmcfg_region *cfg; + struct pci_ecam_region *cfg;
- list_for_each_entry(cfg, &pci_mmcfg_list, list) - if (pci_mmcfg_arch_map(cfg)) { - pci_mmcfg_arch_free(); + 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_mmcfg_arch_free(void) +void __init pci_ecam_arch_free(void) { - struct pci_mmcfg_region *cfg; + struct pci_ecam_region *cfg;
- list_for_each_entry(cfg, &pci_mmcfg_list, list) - pci_mmcfg_arch_unmap(cfg); + list_for_each_entry(cfg, &pci_ecam_list, list) + pci_ecam_arch_unmap(cfg); }
-int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg) +int pci_ecam_arch_map(struct pci_ecam_region *cfg) { - cfg->virt = mcfg_ioremap(cfg); + cfg->virt = pci_ecam_ioremap(cfg); if (!cfg->virt) { - pr_err(PREFIX "can't map MMCONFIG at %pR\n", &cfg->res); + pr_err(PREFIX "can't map ECAM at %pR\n", &cfg->res); return -ENOMEM; }
return 0; }
-void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg) +void pci_ecam_arch_unmap(struct pci_ecam_region *cfg) { if (cfg && cfg->virt) { - iounmap(cfg->virt + PCI_MMCFG_BUS_OFFSET(cfg->start_bus)); + iounmap(cfg->virt + PCI_ECAM_BUS_OFFSET(cfg->start_bus)); cfg->virt = NULL; } } #endif
static u32 -pci_mmconfig_generic_read(int len, void __iomem *addr) +pci_ecam_generic_read(int len, void __iomem *addr) { u32 data = 0;
@@ -157,7 +157,7 @@ pci_mmconfig_generic_read(int len, void __iomem *addr) }
static void -pci_mmconfig_generic_write(int len, void __iomem *addr, u32 value) +pci_ecam_generic_write(int len, void __iomem *addr, u32 value) { switch (len) { case 1: @@ -172,42 +172,42 @@ pci_mmconfig_generic_write(int len, void __iomem *addr, u32 value) } }
-static struct pci_mmcfg_mmio_ops pci_mmcfg_mmio_default = { - .read = pci_mmconfig_generic_read, - .write = pci_mmconfig_generic_write, +static struct pci_ecam_mmio_ops pci_ecam_mmio_default = { + .read = pci_ecam_generic_read, + .write = pci_ecam_generic_write, };
-static struct pci_mmcfg_mmio_ops *pci_mmcfg_mmio = &pci_mmcfg_mmio_default; +static struct pci_ecam_mmio_ops *pci_ecam_mmio = &pci_ecam_mmio_default;
void -pci_mmconfig_register_mmio(struct pci_mmcfg_mmio_ops *ops) +pci_ecam_register_mmio(struct pci_ecam_mmio_ops *ops) { - pci_mmcfg_mmio = ops; + pci_ecam_mmio = ops; }
u32 pci_mmio_read(int len, void __iomem *addr) { - if (!pci_mmcfg_mmio) { + if (!pci_ecam_mmio) { pr_err("PCI config space has no accessors !"); return 0; }
- return pci_mmcfg_mmio->read(len, addr); + return pci_ecam_mmio->read(len, addr); }
void pci_mmio_write(int len, void __iomem *addr, u32 value) { - if (!pci_mmcfg_mmio) { + if (!pci_ecam_mmio) { pr_err("PCI config space has no accessors !"); return; }
- pci_mmcfg_mmio->write(len, addr, value); + pci_ecam_mmio->write(len, addr, value); }
-static void __init pci_mmconfig_remove(struct pci_mmcfg_region *cfg) +static void __init pci_ecam_remove(struct pci_ecam_region *cfg) { if (cfg->res.parent) release_resource(&cfg->res); @@ -215,21 +215,21 @@ static void __init pci_mmconfig_remove(struct pci_mmcfg_region *cfg) kfree(cfg); }
-void __init free_all_mmcfg(void) +void __init pci_ecam_free_all(void) { - struct pci_mmcfg_region *cfg, *tmp; + struct pci_ecam_region *cfg, *tmp;
- pci_mmcfg_arch_free(); - list_for_each_entry_safe(cfg, tmp, &pci_mmcfg_list, list) - pci_mmconfig_remove(cfg); + pci_ecam_arch_free(); + list_for_each_entry_safe(cfg, tmp, &pci_ecam_list, list) + pci_ecam_remove(cfg); }
-void list_add_sorted(struct pci_mmcfg_region *new) +void pci_ecam_list_add_sorted(struct pci_ecam_region *new) { - struct pci_mmcfg_region *cfg; + struct pci_ecam_region *cfg;
/* keep list sorted by segment and starting bus number */ - list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) { + 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)) { @@ -237,13 +237,13 @@ void list_add_sorted(struct pci_mmcfg_region *new) return; } } - list_add_tail_rcu(&new->list, &pci_mmcfg_list); + list_add_tail_rcu(&new->list, &pci_ecam_list); }
-struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start, +struct pci_ecam_region *pci_ecam_alloc(int segment, int start, int end, u64 addr) { - struct pci_mmcfg_region *new; + struct pci_ecam_region *new; struct resource *res;
if (addr == 0) @@ -259,29 +259,29 @@ struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int 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->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_MMCFG_RESOURCE_NAME_LEN, - "PCI MMCONFIG %04x [bus %02x-%02x]", segment, start, end); + 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_mmcfg_region *pci_mmconfig_add(int segment, int start, +struct pci_ecam_region *pci_ecam_add(int segment, int start, int end, u64 addr) { - struct pci_mmcfg_region *new; + struct pci_ecam_region *new;
- new = pci_mmconfig_alloc(segment, start, end, addr); + new = pci_ecam_alloc(segment, start, end, addr); if (new) { - mutex_lock(&pci_mmcfg_lock); - list_add_sorted(new); - mutex_unlock(&pci_mmcfg_lock); + mutex_lock(&pci_ecam_lock); + pci_ecam_list_add_sorted(new); + mutex_unlock(&pci_ecam_lock);
pr_info(PREFIX - "MMCONFIG for domain %04x [bus %02x-%02x] at %pR " + "ECAM for domain %04x [bus %02x-%02x] at %pR " "(base %#lx)\n", segment, start, end, &new->res, (unsigned long)addr); } @@ -289,11 +289,11 @@ struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, return new; }
-struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus) +struct pci_ecam_region *pci_ecam_lookup(int segment, int bus) { - struct pci_mmcfg_region *cfg; + struct pci_ecam_region *cfg;
- list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) + list_for_each_entry_rcu(cfg, &pci_ecam_list, list) if (cfg->segment == segment && cfg->start_bus <= bus && bus <= cfg->end_bus) return cfg; @@ -301,39 +301,39 @@ struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus) return NULL; }
-/* Delete MMCFG information for host bridges */ -int pci_mmconfig_delete(u16 seg, u8 start, u8 end) +/* Delete ECAM information for host bridges */ +int pci_ecam_delete(u16 seg, u8 start, u8 end) { - struct pci_mmcfg_region *cfg; + struct pci_ecam_region *cfg;
- mutex_lock(&pci_mmcfg_lock); - list_for_each_entry_rcu(cfg, &pci_mmcfg_list, list) + 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_mmcfg_arch_unmap(cfg); + pci_ecam_arch_unmap(cfg); if (cfg->res.parent) release_resource(&cfg->res); - mutex_unlock(&pci_mmcfg_lock); + mutex_unlock(&pci_ecam_lock); kfree(cfg); return 0; } - mutex_unlock(&pci_mmcfg_lock); + mutex_unlock(&pci_ecam_lock);
return -ENOENT; }
-int pci_mmconfig_inject(struct pci_mmcfg_region *cfg) +int pci_ecam_inject(struct pci_ecam_region *cfg) { - struct pci_mmcfg_region *cfg_conflict; + struct pci_ecam_region *cfg_conflict; int err = 0;
- mutex_lock(&pci_mmcfg_lock); - cfg_conflict = pci_mmconfig_lookup(cfg->segment, cfg->start_bus); + 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 "MMCONFIG for " + pr_info(FW_INFO "ECAM for " "domain %04x [bus %02x-%02x] " "only partially covers this bridge\n", cfg_conflict->segment, cfg_conflict->start_bus, @@ -342,17 +342,17 @@ int pci_mmconfig_inject(struct pci_mmcfg_region *cfg) goto out; }
- if (pci_mmcfg_arch_map(cfg)) { - pr_warn("fail to map MMCONFIG %pR.\n", &cfg->res); + if (pci_ecam_arch_map(cfg)) { + pr_warn("fail to map ECAM %pR.\n", &cfg->res); err = -ENOMEM; goto out; } else { - list_add_sorted(cfg); - pr_info("MMCONFIG at %pR (base %#lx)\n", + pci_ecam_list_add_sorted(cfg); + pr_info("ECAM at %pR (base %#lx)\n", &cfg->res, (unsigned long)cfg->address);
} out: - mutex_unlock(&pci_mmcfg_lock); + mutex_unlock(&pci_ecam_lock); return err; } diff --git a/drivers/xen/pci.c b/drivers/xen/pci.c index 7a34a72..76b3ca2 100644 --- a/drivers/xen/pci.c +++ b/drivers/xen/pci.c @@ -205,7 +205,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()) @@ -214,11 +214,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/ecam.h b/include/linux/ecam.h index fba5d6b..363691b 100644 --- a/include/linux/ecam.h +++ b/include/linux/ecam.h @@ -5,10 +5,10 @@ #include <linux/types.h> #include <linux/acpi.h>
-/* "PCI MMCONFIG %04x [bus %02x-%02x]" */ -#define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2) +/* "PCI ECAM %04x [bus %02x-%02x]" */ +#define PCI_ECAM_RESOURCE_NAME_LEN (22 + 4 + 2 + 2)
-struct pci_mmcfg_region { +struct pci_ecam_region { struct list_head list; struct resource res; u64 address; @@ -16,40 +16,40 @@ struct pci_mmcfg_region { u16 segment; u8 start_bus; u8 end_bus; - char name[PCI_MMCFG_RESOURCE_NAME_LEN]; + char name[PCI_ECAM_RESOURCE_NAME_LEN]; };
-struct pci_mmcfg_mmio_ops { +struct pci_ecam_mmio_ops { u32 (*read)(int len, void __iomem *addr); void (*write)(int len, void __iomem *addr, u32 value); };
-struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus); -struct pci_mmcfg_region *pci_mmconfig_alloc(int segment, int start, +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_mmconfig_inject(struct pci_mmcfg_region *cfg); -struct pci_mmcfg_region *pci_mmconfig_add(int segment, int start, +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 list_add_sorted(struct pci_mmcfg_region *new); -void free_all_mmcfg(void); -int pci_mmconfig_delete(u16 seg, u8 start, u8 end); +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_mmcfg_arch_init(void); -void pci_mmcfg_arch_free(void); -int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg); -void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg); +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_mmconfig_register_mmio(struct pci_mmcfg_mmio_ops *ops); +extern void pci_ecam_register_mmio(struct pci_ecam_mmio_ops *ops);
-extern struct list_head pci_mmcfg_list; +extern struct list_head pci_ecam_list;
-#define PCI_MMCFG_BUS_OFFSET(bus) ((bus) << 20) +#define PCI_ECAM_BUS_OFFSET(bus) ((bus) << 20)
-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); -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);
#endif /* __KERNEL__ */