This patch will fall back to ACPI MCFG table if _CBA method fails to get the configuration base address of ECAM. Firmware on ARM platform uses MCFG table instead of _CBA method. This is needed to scan the PCIe root complex for ARM SoC. Signed-off-by: Dennis Chen <dennis.chen@xxxxxxx> --- drivers/pci/pci-acpi.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 97 insertions(+), 5 deletions(-) diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 314a625..211b9d9 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -27,18 +27,110 @@ const u8 pci_acpi_dsm_uuid[] = { 0x91, 0x17, 0xea, 0x4d, 0x19, 0xc3, 0x43, 0x4d }; +static int __init acpi_mcfg_check_entry(struct acpi_table_mcfg *mcfg) +{ + if (mcfg->header.revision != 1) { + pr_err("invalid header revision:%d in ACPI MCFG table\n", + mcfg->header.revision); + return -EINVAL; + } + + return 0; +} + +static phys_addr_t __init acpi_get_mcfg_cba(u16 segment, u8 bus) +{ + struct acpi_table_header *table = NULL; + acpi_size tbl_size; + struct acpi_table_mcfg *mcfg = NULL; + struct acpi_mcfg_allocation *cfg_table, *cfg; + acpi_status status; + phys_addr_t cba = 0; + unsigned long i; + int entries; + + if (acpi_disabled) + return 0; + + status = acpi_get_table_with_size(ACPI_SIG_MCFG, 0, &table, &tbl_size); + if (ACPI_FAILURE(status)) { + const char *msg = acpi_format_exception(status); + + pr_err("Failed to get MCFG table, %s\n", msg); + return 0; + } + + if (table) + mcfg = (struct acpi_table_mcfg *)table; + + entries = 0; + i = table->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("ACPI MCFG table has no entries\n"); + goto out; + } + + cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1]; + for (i = 0; i < entries; i++) { + cfg = &cfg_table[i]; + if (acpi_mcfg_check_entry(mcfg)) + goto out; + + if (cfg->pci_segment == segment && + cfg->start_bus_number <= bus && + bus <= cfg->end_bus_number) { + cba = (phys_addr_t)cfg->address; + goto out; + } + } +out: + /* + * acpi_get_table_with_size() creates MCFG table mapping that + * should be released after parsing and before resuming boot + */ + early_acpi_os_unmap_memory(table, tbl_size); + return cba; +} + phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle) { acpi_status status = AE_NOT_EXIST; - unsigned long long mcfg_addr; + unsigned long long mcfg_addr, mcfg_seg, mcfg_bus; + u16 seg; + u8 bus; + + if (!handle) + return 0; - if (handle) - status = acpi_evaluate_integer(handle, METHOD_NAME__CBA, + status = acpi_evaluate_integer(handle, METHOD_NAME__CBA, NULL, &mcfg_addr); + if (ACPI_SUCCESS(status)) + return (phys_addr_t)mcfg_addr; + + pr_info("ACPI: Falling back to acpi mcfg table\n"); + + /* + * Fall back to MCFG table if _CBA failed: + * get the mcfg_addr via ACPI MCFG table. PCI Firmware v3.2 spec: 4.1.2 + */ + status = acpi_evaluate_integer(handle, METHOD_NAME__SEG, + NULL, &mcfg_seg); if (ACPI_FAILURE(status)) - return 0; + mcfg_seg = 0; + + status = acpi_evaluate_integer(handle, METHOD_NAME__BBN, + NULL, &mcfg_bus); + if (ACPI_FAILURE(status)) + mcfg_bus = 0; + + seg = mcfg_seg & 0xFFFF; + bus = mcfg_bus & 0xFF; - return (phys_addr_t)mcfg_addr; + return acpi_get_mcfg_cba(seg, bus); } static acpi_status decode_type0_hpx_record(union acpi_object *record, -- 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html