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