From: Mark Salter msalter@redhat.com
Some MCFG tables may be broken or the underlying hardware may not be fully compliant with the PCIe ECAM mechanism. This patch provides a mechanism to override the default mmconfig read/write routines and/or do other MCFG related fixups.
Signed-off-by: Mark Salter msalter@redhat.com --- arch/arm64/include/asm/pci.h | 24 ++++++++++++++++++++++++ arch/arm64/pci/mmconfig.c | 30 ++++++++++++++++++++++++++---- include/asm-generic/vmlinux.lds.h | 7 +++++++ 3 files changed, 57 insertions(+), 4 deletions(-)
diff --git a/arch/arm64/include/asm/pci.h b/arch/arm64/include/asm/pci.h index 3b04035..b850d6e 100644 --- a/arch/arm64/include/asm/pci.h +++ b/arch/arm64/include/asm/pci.h @@ -51,9 +51,21 @@ extern const struct pci_raw_ops *raw_pci_ops; /* "PCI MMCONFIG %04x [bus %02x-%02x]" */ #define PCI_MMCFG_RESOURCE_NAME_LEN (22 + 4 + 2 + 2)
+struct acpi_pci_root; +struct pci_mmcfg_region; + +typedef int (*acpi_mcfg_fixup_t)(struct acpi_pci_root *root, + struct pci_mmcfg_region *cfg); + struct pci_mmcfg_region { struct list_head list; struct resource res; + int (*read)(struct pci_mmcfg_region *cfg, unsigned int bus, + unsigned int devfn, int reg, int len, u32 *value); + int (*write)(struct pci_mmcfg_region *cfg, unsigned int bus, + unsigned int devfn, int reg, int len, u32 value); + acpi_mcfg_fixup_t fixup; + void *data; u64 address; char __iomem *virt; u16 segment; @@ -62,6 +74,18 @@ struct pci_mmcfg_region { char name[PCI_MMCFG_RESOURCE_NAME_LEN]; };
+struct acpi_mcfg_fixup { + char oem_id[7]; + char oem_table_id[9]; + acpi_mcfg_fixup_t hook; +}; + +/* Designate a routine to fix up buggy MCFG */ +#define DECLARE_ACPI_MCFG_FIXUP(oem_id, table_id, hook) \ + static const struct acpi_mcfg_fixup __acpi_fixup_##hook __used \ + __attribute__((__section__(".acpi_fixup_mcfg"), aligned((sizeof(void *))))) \ + = { {oem_id}, {table_id}, hook }; + extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus);
#endif /* __KERNEL__ */ diff --git a/arch/arm64/pci/mmconfig.c b/arch/arm64/pci/mmconfig.c index 0bda9b7..110fec7 100644 --- a/arch/arm64/pci/mmconfig.c +++ b/arch/arm64/pci/mmconfig.c @@ -111,7 +111,10 @@ err: *value = -1; goto err; }
- ret = __pci_mmcfg_read(cfg, bus, devfn, reg, len, value); + if (cfg->read) + ret = (*cfg->read)(cfg, bus, devfn, reg, len, value); + else + ret = __pci_mmcfg_read(cfg, bus, devfn, reg, len, value);
rcu_read_unlock();
@@ -154,7 +157,10 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus, return -EINVAL; }
- ret = __pci_mmcfg_write(cfg, bus, devfn, reg, len, value); + if (cfg->write) + ret = (*cfg->write)(cfg, bus, devfn, reg, len, value); + else + ret = __pci_mmcfg_write(cfg, bus, devfn, reg, len, value);
rcu_read_unlock();
@@ -290,10 +296,15 @@ static struct pci_mmcfg_region *__init pci_mmconfig_add(int segment, int start, return new; }
+extern struct acpi_mcfg_fixup __start_acpi_mcfg_fixups[]; +extern struct acpi_mcfg_fixup __end_acpi_mcfg_fixups[]; + static int __init pci_parse_mcfg(struct acpi_table_header *header) { struct acpi_table_mcfg *mcfg; struct acpi_mcfg_allocation *cfg_table, *cfg; + struct acpi_mcfg_fixup *fixup; + struct pci_mmcfg_region *new; unsigned long i; int entries;
@@ -315,16 +326,27 @@ static int __init pci_parse_mcfg(struct acpi_table_header *header) return -ENODEV; }
+ fixup = __start_acpi_mcfg_fixups; + while (fixup < __end_acpi_mcfg_fixups) { + if (!strncmp(fixup->oem_id, header->oem_id, 6) && + !strncmp(fixup->oem_table_id, header->oem_table_id, 8)) + break; + ++fixup; + } + cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1]; for (i = 0; i < entries; i++) { cfg = &cfg_table[i];
- if (pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number, - cfg->end_bus_number, cfg->address) == NULL) { + new = pci_mmconfig_add(cfg->pci_segment, cfg->start_bus_number, + cfg->end_bus_number, cfg->address); + if (!new) { pr_warn(PREFIX "no memory for MCFG entries\n"); free_all_mmcfg(); return -ENOMEM; } + if (fixup < __end_acpi_mcfg_fixups) + new->fixup = fixup->hook; }
return 0; diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index aa70cbd..1261fef 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -275,6 +275,13 @@ VMLINUX_SYMBOL(__end_pci_fixups_suspend_late) = .; \ } \ \ + /* ACPI quirks */ \ + .acpi_fixup : AT(ADDR(.acpi_fixup) - LOAD_OFFSET) { \ + VMLINUX_SYMBOL(__start_acpi_mcfg_fixups) = .; \ + *(.acpi_fixup_mcfg) \ + VMLINUX_SYMBOL(__end_acpi_mcfg_fixups) = .; \ + } \ + \ /* Built-in firmware blobs */ \ .builtin_fw : AT(ADDR(.builtin_fw) - LOAD_OFFSET) { \ VMLINUX_SYMBOL(__start_builtin_fw) = .; \