aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Salter <msalter@redhat.com>2014-10-30 20:56:21 -0400
committerGraeme Gregory <graeme.gregory@linaro.org>2014-11-06 12:00:49 +0000
commit35060fa42366447d8f0e173f2386a44dd6569b4c (patch)
treea099f62466bbdc2137edd74177235b278c0ae1e7
parent42e2fbfcbb76e5132aec5d8faf81024286f6e8e6 (diff)
downloadleg-kernel-35060fa42366447d8f0e173f2386a44dd6569b4c.tar.gz
arm64/acpi/pci: provide hook for MCFG fixups
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>
-rw-r--r--arch/arm64/include/asm/pci.h24
-rw-r--r--arch/arm64/pci/mmconfig.c30
-rw-r--r--include/asm-generic/vmlinux.lds.h7
3 files changed, 57 insertions, 4 deletions
diff --git a/arch/arm64/include/asm/pci.h b/arch/arm64/include/asm/pci.h
index 3b040350ddb0..b850d6ebe215 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 0bda9b74d297..110fec7ba7cc 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 aa70cbda327c..1261fef45b36 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) = .; \