18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2016 Broadcom 48c2ecf20Sopenharmony_ci * Author: Jayachandran C <jchandra@broadcom.com> 58c2ecf20Sopenharmony_ci * Copyright (C) 2016 Semihalf 68c2ecf20Sopenharmony_ci * Author: Tomasz Nowicki <tn@semihalf.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "ACPI: " fmt 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/pci.h> 138c2ecf20Sopenharmony_ci#include <linux/pci-acpi.h> 148c2ecf20Sopenharmony_ci#include <linux/pci-ecam.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci/* Structure to hold entries from the MCFG table */ 178c2ecf20Sopenharmony_cistruct mcfg_entry { 188c2ecf20Sopenharmony_ci struct list_head list; 198c2ecf20Sopenharmony_ci phys_addr_t addr; 208c2ecf20Sopenharmony_ci u16 segment; 218c2ecf20Sopenharmony_ci u8 bus_start; 228c2ecf20Sopenharmony_ci u8 bus_end; 238c2ecf20Sopenharmony_ci}; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_QUIRKS 268c2ecf20Sopenharmony_cistruct mcfg_fixup { 278c2ecf20Sopenharmony_ci char oem_id[ACPI_OEM_ID_SIZE + 1]; 288c2ecf20Sopenharmony_ci char oem_table_id[ACPI_OEM_TABLE_ID_SIZE + 1]; 298c2ecf20Sopenharmony_ci u32 oem_revision; 308c2ecf20Sopenharmony_ci u16 segment; 318c2ecf20Sopenharmony_ci struct resource bus_range; 328c2ecf20Sopenharmony_ci const struct pci_ecam_ops *ops; 338c2ecf20Sopenharmony_ci struct resource cfgres; 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define MCFG_BUS_RANGE(start, end) DEFINE_RES_NAMED((start), \ 378c2ecf20Sopenharmony_ci ((end) - (start) + 1), \ 388c2ecf20Sopenharmony_ci NULL, IORESOURCE_BUS) 398c2ecf20Sopenharmony_ci#define MCFG_BUS_ANY MCFG_BUS_RANGE(0x0, 0xff) 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic struct mcfg_fixup mcfg_quirks[] = { 428c2ecf20Sopenharmony_ci/* { OEM_ID, OEM_TABLE_ID, REV, SEGMENT, BUS_RANGE, ops, cfgres }, */ 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#ifdef CONFIG_ARM64 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define AL_ECAM(table_id, rev, seg, ops) \ 478c2ecf20Sopenharmony_ci { "AMAZON", table_id, rev, seg, MCFG_BUS_ANY, ops } 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci AL_ECAM("GRAVITON", 0, 0, &al_pcie_ops), 508c2ecf20Sopenharmony_ci AL_ECAM("GRAVITON", 0, 1, &al_pcie_ops), 518c2ecf20Sopenharmony_ci AL_ECAM("GRAVITON", 0, 2, &al_pcie_ops), 528c2ecf20Sopenharmony_ci AL_ECAM("GRAVITON", 0, 3, &al_pcie_ops), 538c2ecf20Sopenharmony_ci AL_ECAM("GRAVITON", 0, 4, &al_pcie_ops), 548c2ecf20Sopenharmony_ci AL_ECAM("GRAVITON", 0, 5, &al_pcie_ops), 558c2ecf20Sopenharmony_ci AL_ECAM("GRAVITON", 0, 6, &al_pcie_ops), 568c2ecf20Sopenharmony_ci AL_ECAM("GRAVITON", 0, 7, &al_pcie_ops), 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#define QCOM_ECAM32(seg) \ 598c2ecf20Sopenharmony_ci { "QCOM ", "QDF2432 ", 1, seg, MCFG_BUS_ANY, &pci_32b_ops } 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci QCOM_ECAM32(0), 628c2ecf20Sopenharmony_ci QCOM_ECAM32(1), 638c2ecf20Sopenharmony_ci QCOM_ECAM32(2), 648c2ecf20Sopenharmony_ci QCOM_ECAM32(3), 658c2ecf20Sopenharmony_ci QCOM_ECAM32(4), 668c2ecf20Sopenharmony_ci QCOM_ECAM32(5), 678c2ecf20Sopenharmony_ci QCOM_ECAM32(6), 688c2ecf20Sopenharmony_ci QCOM_ECAM32(7), 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci#define HISI_QUAD_DOM(table_id, seg, ops) \ 718c2ecf20Sopenharmony_ci { "HISI ", table_id, 0, (seg) + 0, MCFG_BUS_ANY, ops }, \ 728c2ecf20Sopenharmony_ci { "HISI ", table_id, 0, (seg) + 1, MCFG_BUS_ANY, ops }, \ 738c2ecf20Sopenharmony_ci { "HISI ", table_id, 0, (seg) + 2, MCFG_BUS_ANY, ops }, \ 748c2ecf20Sopenharmony_ci { "HISI ", table_id, 0, (seg) + 3, MCFG_BUS_ANY, ops } 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci HISI_QUAD_DOM("HIP05 ", 0, &hisi_pcie_ops), 778c2ecf20Sopenharmony_ci HISI_QUAD_DOM("HIP06 ", 0, &hisi_pcie_ops), 788c2ecf20Sopenharmony_ci HISI_QUAD_DOM("HIP07 ", 0, &hisi_pcie_ops), 798c2ecf20Sopenharmony_ci HISI_QUAD_DOM("HIP07 ", 4, &hisi_pcie_ops), 808c2ecf20Sopenharmony_ci HISI_QUAD_DOM("HIP07 ", 8, &hisi_pcie_ops), 818c2ecf20Sopenharmony_ci HISI_QUAD_DOM("HIP07 ", 12, &hisi_pcie_ops), 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci#define THUNDER_PEM_RES(addr, node) \ 848c2ecf20Sopenharmony_ci DEFINE_RES_MEM((addr) + ((u64) (node) << 44), 0x39 * SZ_16M) 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci#define THUNDER_PEM_QUIRK(rev, node) \ 878c2ecf20Sopenharmony_ci { "CAVIUM", "THUNDERX", rev, 4 + (10 * (node)), MCFG_BUS_ANY, \ 888c2ecf20Sopenharmony_ci &thunder_pem_ecam_ops, THUNDER_PEM_RES(0x88001f000000UL, node) }, \ 898c2ecf20Sopenharmony_ci { "CAVIUM", "THUNDERX", rev, 5 + (10 * (node)), MCFG_BUS_ANY, \ 908c2ecf20Sopenharmony_ci &thunder_pem_ecam_ops, THUNDER_PEM_RES(0x884057000000UL, node) }, \ 918c2ecf20Sopenharmony_ci { "CAVIUM", "THUNDERX", rev, 6 + (10 * (node)), MCFG_BUS_ANY, \ 928c2ecf20Sopenharmony_ci &thunder_pem_ecam_ops, THUNDER_PEM_RES(0x88808f000000UL, node) }, \ 938c2ecf20Sopenharmony_ci { "CAVIUM", "THUNDERX", rev, 7 + (10 * (node)), MCFG_BUS_ANY, \ 948c2ecf20Sopenharmony_ci &thunder_pem_ecam_ops, THUNDER_PEM_RES(0x89001f000000UL, node) }, \ 958c2ecf20Sopenharmony_ci { "CAVIUM", "THUNDERX", rev, 8 + (10 * (node)), MCFG_BUS_ANY, \ 968c2ecf20Sopenharmony_ci &thunder_pem_ecam_ops, THUNDER_PEM_RES(0x894057000000UL, node) }, \ 978c2ecf20Sopenharmony_ci { "CAVIUM", "THUNDERX", rev, 9 + (10 * (node)), MCFG_BUS_ANY, \ 988c2ecf20Sopenharmony_ci &thunder_pem_ecam_ops, THUNDER_PEM_RES(0x89808f000000UL, node) } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci#define THUNDER_ECAM_QUIRK(rev, seg) \ 1018c2ecf20Sopenharmony_ci { "CAVIUM", "THUNDERX", rev, seg, MCFG_BUS_ANY, \ 1028c2ecf20Sopenharmony_ci &pci_thunder_ecam_ops } 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci /* SoC pass2.x */ 1058c2ecf20Sopenharmony_ci THUNDER_PEM_QUIRK(1, 0), 1068c2ecf20Sopenharmony_ci THUNDER_PEM_QUIRK(1, 1), 1078c2ecf20Sopenharmony_ci THUNDER_ECAM_QUIRK(1, 10), 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* SoC pass1.x */ 1108c2ecf20Sopenharmony_ci THUNDER_PEM_QUIRK(2, 0), /* off-chip devices */ 1118c2ecf20Sopenharmony_ci THUNDER_PEM_QUIRK(2, 1), /* off-chip devices */ 1128c2ecf20Sopenharmony_ci THUNDER_ECAM_QUIRK(2, 0), 1138c2ecf20Sopenharmony_ci THUNDER_ECAM_QUIRK(2, 1), 1148c2ecf20Sopenharmony_ci THUNDER_ECAM_QUIRK(2, 2), 1158c2ecf20Sopenharmony_ci THUNDER_ECAM_QUIRK(2, 3), 1168c2ecf20Sopenharmony_ci THUNDER_ECAM_QUIRK(2, 10), 1178c2ecf20Sopenharmony_ci THUNDER_ECAM_QUIRK(2, 11), 1188c2ecf20Sopenharmony_ci THUNDER_ECAM_QUIRK(2, 12), 1198c2ecf20Sopenharmony_ci THUNDER_ECAM_QUIRK(2, 13), 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci#define XGENE_V1_ECAM_MCFG(rev, seg) \ 1228c2ecf20Sopenharmony_ci {"APM ", "XGENE ", rev, seg, MCFG_BUS_ANY, \ 1238c2ecf20Sopenharmony_ci &xgene_v1_pcie_ecam_ops } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci#define XGENE_V2_ECAM_MCFG(rev, seg) \ 1268c2ecf20Sopenharmony_ci {"APM ", "XGENE ", rev, seg, MCFG_BUS_ANY, \ 1278c2ecf20Sopenharmony_ci &xgene_v2_pcie_ecam_ops } 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* X-Gene SoC with v1 PCIe controller */ 1308c2ecf20Sopenharmony_ci XGENE_V1_ECAM_MCFG(1, 0), 1318c2ecf20Sopenharmony_ci XGENE_V1_ECAM_MCFG(1, 1), 1328c2ecf20Sopenharmony_ci XGENE_V1_ECAM_MCFG(1, 2), 1338c2ecf20Sopenharmony_ci XGENE_V1_ECAM_MCFG(1, 3), 1348c2ecf20Sopenharmony_ci XGENE_V1_ECAM_MCFG(1, 4), 1358c2ecf20Sopenharmony_ci XGENE_V1_ECAM_MCFG(2, 0), 1368c2ecf20Sopenharmony_ci XGENE_V1_ECAM_MCFG(2, 1), 1378c2ecf20Sopenharmony_ci XGENE_V1_ECAM_MCFG(2, 2), 1388c2ecf20Sopenharmony_ci XGENE_V1_ECAM_MCFG(2, 3), 1398c2ecf20Sopenharmony_ci XGENE_V1_ECAM_MCFG(2, 4), 1408c2ecf20Sopenharmony_ci /* X-Gene SoC with v2.1 PCIe controller */ 1418c2ecf20Sopenharmony_ci XGENE_V2_ECAM_MCFG(3, 0), 1428c2ecf20Sopenharmony_ci XGENE_V2_ECAM_MCFG(3, 1), 1438c2ecf20Sopenharmony_ci /* X-Gene SoC with v2.2 PCIe controller */ 1448c2ecf20Sopenharmony_ci XGENE_V2_ECAM_MCFG(4, 0), 1458c2ecf20Sopenharmony_ci XGENE_V2_ECAM_MCFG(4, 1), 1468c2ecf20Sopenharmony_ci XGENE_V2_ECAM_MCFG(4, 2), 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci#define ALTRA_ECAM_QUIRK(rev, seg) \ 1498c2ecf20Sopenharmony_ci { "Ampere", "Altra ", rev, seg, MCFG_BUS_ANY, &pci_32b_read_ops } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci ALTRA_ECAM_QUIRK(1, 0), 1528c2ecf20Sopenharmony_ci ALTRA_ECAM_QUIRK(1, 1), 1538c2ecf20Sopenharmony_ci ALTRA_ECAM_QUIRK(1, 2), 1548c2ecf20Sopenharmony_ci ALTRA_ECAM_QUIRK(1, 3), 1558c2ecf20Sopenharmony_ci ALTRA_ECAM_QUIRK(1, 4), 1568c2ecf20Sopenharmony_ci ALTRA_ECAM_QUIRK(1, 5), 1578c2ecf20Sopenharmony_ci ALTRA_ECAM_QUIRK(1, 6), 1588c2ecf20Sopenharmony_ci ALTRA_ECAM_QUIRK(1, 7), 1598c2ecf20Sopenharmony_ci ALTRA_ECAM_QUIRK(1, 8), 1608c2ecf20Sopenharmony_ci ALTRA_ECAM_QUIRK(1, 9), 1618c2ecf20Sopenharmony_ci ALTRA_ECAM_QUIRK(1, 10), 1628c2ecf20Sopenharmony_ci ALTRA_ECAM_QUIRK(1, 11), 1638c2ecf20Sopenharmony_ci ALTRA_ECAM_QUIRK(1, 12), 1648c2ecf20Sopenharmony_ci ALTRA_ECAM_QUIRK(1, 13), 1658c2ecf20Sopenharmony_ci ALTRA_ECAM_QUIRK(1, 14), 1668c2ecf20Sopenharmony_ci ALTRA_ECAM_QUIRK(1, 15), 1678c2ecf20Sopenharmony_ci#endif /* ARM64 */ 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci#ifdef CONFIG_LOONGARCH 1708c2ecf20Sopenharmony_ci#define LOONGSON_ECAM_MCFG(table_id, seg) \ 1718c2ecf20Sopenharmony_ci { "LOONGS", table_id, 1, seg, MCFG_BUS_ANY, &loongson_pci_ecam_ops } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci LOONGSON_ECAM_MCFG("\0", 0), 1748c2ecf20Sopenharmony_ci LOONGSON_ECAM_MCFG("LOONGSON", 0), 1758c2ecf20Sopenharmony_ci LOONGSON_ECAM_MCFG("\0", 1), 1768c2ecf20Sopenharmony_ci LOONGSON_ECAM_MCFG("LOONGSON", 1), 1778c2ecf20Sopenharmony_ci#endif /* LOONGARCH */ 1788c2ecf20Sopenharmony_ci}; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic char mcfg_oem_id[ACPI_OEM_ID_SIZE]; 1818c2ecf20Sopenharmony_cistatic char mcfg_oem_table_id[ACPI_OEM_TABLE_ID_SIZE]; 1828c2ecf20Sopenharmony_cistatic u32 mcfg_oem_revision; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_cistatic int pci_mcfg_quirk_matches(struct mcfg_fixup *f, u16 segment, 1858c2ecf20Sopenharmony_ci struct resource *bus_range) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci if (!memcmp(f->oem_id, mcfg_oem_id, ACPI_OEM_ID_SIZE) && 1888c2ecf20Sopenharmony_ci !memcmp(f->oem_table_id, mcfg_oem_table_id, 1898c2ecf20Sopenharmony_ci ACPI_OEM_TABLE_ID_SIZE) && 1908c2ecf20Sopenharmony_ci f->oem_revision == mcfg_oem_revision && 1918c2ecf20Sopenharmony_ci f->segment == segment && 1928c2ecf20Sopenharmony_ci resource_contains(&f->bus_range, bus_range)) 1938c2ecf20Sopenharmony_ci return 1; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return 0; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci#endif 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_cistatic void pci_mcfg_apply_quirks(struct acpi_pci_root *root, 2008c2ecf20Sopenharmony_ci struct resource *cfgres, 2018c2ecf20Sopenharmony_ci const struct pci_ecam_ops **ecam_ops) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_QUIRKS 2048c2ecf20Sopenharmony_ci u16 segment = root->segment; 2058c2ecf20Sopenharmony_ci struct resource *bus_range = &root->secondary; 2068c2ecf20Sopenharmony_ci struct mcfg_fixup *f; 2078c2ecf20Sopenharmony_ci int i; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci for (i = 0, f = mcfg_quirks; i < ARRAY_SIZE(mcfg_quirks); i++, f++) { 2108c2ecf20Sopenharmony_ci if (pci_mcfg_quirk_matches(f, segment, bus_range)) { 2118c2ecf20Sopenharmony_ci if (f->cfgres.start) 2128c2ecf20Sopenharmony_ci *cfgres = f->cfgres; 2138c2ecf20Sopenharmony_ci if (f->ops) 2148c2ecf20Sopenharmony_ci *ecam_ops = f->ops; 2158c2ecf20Sopenharmony_ci dev_info(&root->device->dev, "MCFG quirk: ECAM at %pR for %pR with %ps\n", 2168c2ecf20Sopenharmony_ci cfgres, bus_range, *ecam_ops); 2178c2ecf20Sopenharmony_ci return; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci#endif 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci/* List to save MCFG entries */ 2248c2ecf20Sopenharmony_cistatic LIST_HEAD(pci_mcfg_list); 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ciint pci_mcfg_lookup(struct acpi_pci_root *root, struct resource *cfgres, 2278c2ecf20Sopenharmony_ci const struct pci_ecam_ops **ecam_ops) 2288c2ecf20Sopenharmony_ci{ 2298c2ecf20Sopenharmony_ci const struct pci_ecam_ops *ops = &pci_generic_ecam_ops; 2308c2ecf20Sopenharmony_ci struct resource *bus_res = &root->secondary; 2318c2ecf20Sopenharmony_ci u16 seg = root->segment; 2328c2ecf20Sopenharmony_ci struct mcfg_entry *e; 2338c2ecf20Sopenharmony_ci struct resource res; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* Use address from _CBA if present, otherwise lookup MCFG */ 2368c2ecf20Sopenharmony_ci if (root->mcfg_addr) 2378c2ecf20Sopenharmony_ci goto skip_lookup; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* 2408c2ecf20Sopenharmony_ci * We expect the range in bus_res in the coverage of MCFG bus range. 2418c2ecf20Sopenharmony_ci */ 2428c2ecf20Sopenharmony_ci list_for_each_entry(e, &pci_mcfg_list, list) { 2438c2ecf20Sopenharmony_ci if (e->segment == seg && e->bus_start <= bus_res->start && 2448c2ecf20Sopenharmony_ci e->bus_end >= bus_res->end) { 2458c2ecf20Sopenharmony_ci root->mcfg_addr = e->addr; 2468c2ecf20Sopenharmony_ci } 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ciskip_lookup: 2518c2ecf20Sopenharmony_ci memset(&res, 0, sizeof(res)); 2528c2ecf20Sopenharmony_ci if (root->mcfg_addr) { 2538c2ecf20Sopenharmony_ci res.start = root->mcfg_addr + (bus_res->start << 20); 2548c2ecf20Sopenharmony_ci res.end = res.start + (resource_size(bus_res) << 20) - 1; 2558c2ecf20Sopenharmony_ci res.flags = IORESOURCE_MEM; 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci /* 2598c2ecf20Sopenharmony_ci * Allow quirks to override default ECAM ops and CFG resource 2608c2ecf20Sopenharmony_ci * range. This may even fabricate a CFG resource range in case 2618c2ecf20Sopenharmony_ci * MCFG does not have it. Invalid CFG start address means MCFG 2628c2ecf20Sopenharmony_ci * firmware bug or we need another quirk in array. 2638c2ecf20Sopenharmony_ci */ 2648c2ecf20Sopenharmony_ci pci_mcfg_apply_quirks(root, &res, &ops); 2658c2ecf20Sopenharmony_ci if (!res.start) 2668c2ecf20Sopenharmony_ci return -ENXIO; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci *cfgres = res; 2698c2ecf20Sopenharmony_ci *ecam_ops = ops; 2708c2ecf20Sopenharmony_ci return 0; 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic __init int pci_mcfg_parse(struct acpi_table_header *header) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci struct acpi_table_mcfg *mcfg; 2768c2ecf20Sopenharmony_ci struct acpi_mcfg_allocation *mptr; 2778c2ecf20Sopenharmony_ci struct mcfg_entry *e, *arr; 2788c2ecf20Sopenharmony_ci int i, n; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (header->length < sizeof(struct acpi_table_mcfg)) 2818c2ecf20Sopenharmony_ci return -EINVAL; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci n = (header->length - sizeof(struct acpi_table_mcfg)) / 2848c2ecf20Sopenharmony_ci sizeof(struct acpi_mcfg_allocation); 2858c2ecf20Sopenharmony_ci mcfg = (struct acpi_table_mcfg *)header; 2868c2ecf20Sopenharmony_ci mptr = (struct acpi_mcfg_allocation *) &mcfg[1]; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci arr = kcalloc(n, sizeof(*arr), GFP_KERNEL); 2898c2ecf20Sopenharmony_ci if (!arr) 2908c2ecf20Sopenharmony_ci return -ENOMEM; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci for (i = 0, e = arr; i < n; i++, mptr++, e++) { 2938c2ecf20Sopenharmony_ci e->segment = mptr->pci_segment; 2948c2ecf20Sopenharmony_ci e->addr = mptr->address; 2958c2ecf20Sopenharmony_ci e->bus_start = mptr->start_bus_number; 2968c2ecf20Sopenharmony_ci e->bus_end = mptr->end_bus_number; 2978c2ecf20Sopenharmony_ci list_add(&e->list, &pci_mcfg_list); 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci#ifdef CONFIG_PCI_QUIRKS 3018c2ecf20Sopenharmony_ci /* Save MCFG IDs and revision for quirks matching */ 3028c2ecf20Sopenharmony_ci memcpy(mcfg_oem_id, header->oem_id, ACPI_OEM_ID_SIZE); 3038c2ecf20Sopenharmony_ci memcpy(mcfg_oem_table_id, header->oem_table_id, ACPI_OEM_TABLE_ID_SIZE); 3048c2ecf20Sopenharmony_ci mcfg_oem_revision = header->oem_revision; 3058c2ecf20Sopenharmony_ci#endif 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci pr_info("MCFG table detected, %d entries\n", n); 3088c2ecf20Sopenharmony_ci return 0; 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci/* Interface called by ACPI - parse and save MCFG table */ 3128c2ecf20Sopenharmony_civoid __init pci_mmcfg_late_init(void) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci int err = acpi_table_parse(ACPI_SIG_MCFG, pci_mcfg_parse); 3158c2ecf20Sopenharmony_ci if (err) 3168c2ecf20Sopenharmony_ci pr_debug("Failed to parse MCFG (%d)\n", err); 3178c2ecf20Sopenharmony_ci} 318