From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from omzsmtpe01.verizonbusiness.com ([199.249.25.210]:39332 "EHLO omzsmtpe01.verizonbusiness.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754690AbdDDTdF (ORCPT ); Tue, 4 Apr 2017 15:33:05 -0400 From: alexander.levin@verizon.com To: "gregkh@linuxfoundation.org" CC: "stable@vger.kernel.org" Subject: [PATCH for 4.9 22/98] PCI/ACPI: Check for platform-specific MCFG quirks Date: Tue, 4 Apr 2017 19:32:10 +0000 Message-ID: <20170404193158.19041-23-alexander.levin@verizon.com> References: <20170404193158.19041-1-alexander.levin@verizon.com> In-Reply-To: <20170404193158.19041-1-alexander.levin@verizon.com> Content-Language: en-US Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Sender: stable-owner@vger.kernel.org List-ID: From: Tomasz Nowicki [ Upstream commit 5b69b85ba1ddd36be01f5c57830b37a3c8256009 ] The PCIe spec (r3.0, sec 7.2.2) specifies an "Enhanced Configuration Access Mechanism" (ECAM) for memory-mapped access to configuration space. ECAM is required for PCIe systems unless there's a standard firmware interface for config access. In the absence of a firmware interface, we use pci_generic_ecam_ops, and on ACPI systems, we discover the ECAM space via the MCFG table and/or the _CBA method. Unfortunately some systems provide MCFG but don't implement ECAM according to spec, so we need a mechanism for quirks to make those systems work. Add an MCFG quirk mechanism to override the config accessor functions and/or the memory-mapped address space. A quirk is selected if it matches all of the following: - OEM ID - OEM Table ID - OEM Revision - PCI segment (from _SEG) - PCI bus number range (from _CRS, wildcard allowed) If the quirk specifies config accessor functions or a memory-mapped address range, these override the defaults. [bhelgaas: changelog, reorder quirk matching, fix oem_revision typo per Duc, add under #ifdef CONFIG_PCI_QUIRKS] Signed-off-by: Tomasz Nowicki Signed-off-by: Dongdong Liu Signed-off-by: Christopher Covington Signed-off-by: Bjorn Helgaas Signed-off-by: Sasha Levin --- drivers/acpi/pci_mcfg.c | 92 +++++++++++++++++++++++++++++++++++++++++++++= ---- 1 file changed, 86 insertions(+), 6 deletions(-) diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c index ffcc651..1ef7285 100644 --- a/drivers/acpi/pci_mcfg.c +++ b/drivers/acpi/pci_mcfg.c @@ -33,6 +33,69 @@ struct mcfg_entry { u8 bus_end; }; =20 +#ifdef CONFIG_PCI_QUIRKS +struct mcfg_fixup { + char oem_id[ACPI_OEM_ID_SIZE + 1]; + char oem_table_id[ACPI_OEM_TABLE_ID_SIZE + 1]; + u32 oem_revision; + u16 segment; + struct resource bus_range; + struct pci_ecam_ops *ops; + struct resource cfgres; +}; + +#define MCFG_BUS_RANGE(start, end) DEFINE_RES_NAMED((start), \ + ((end) - (start) + 1), \ + NULL, IORESOURCE_BUS) +#define MCFG_BUS_ANY MCFG_BUS_RANGE(0x0, 0xff) + +static struct mcfg_fixup mcfg_quirks[] =3D { +/* { OEM_ID, OEM_TABLE_ID, REV, SEGMENT, BUS_RANGE, ops, cfgres }, */ +}; + +static char mcfg_oem_id[ACPI_OEM_ID_SIZE]; +static char mcfg_oem_table_id[ACPI_OEM_TABLE_ID_SIZE]; +static u32 mcfg_oem_revision; + +static int pci_mcfg_quirk_matches(struct mcfg_fixup *f, u16 segment, + struct resource *bus_range) +{ + if (!memcmp(f->oem_id, mcfg_oem_id, ACPI_OEM_ID_SIZE) && + !memcmp(f->oem_table_id, mcfg_oem_table_id, + ACPI_OEM_TABLE_ID_SIZE) && + f->oem_revision =3D=3D mcfg_oem_revision && + f->segment =3D=3D segment && + resource_contains(&f->bus_range, bus_range)) + return 1; + + return 0; +} +#endif + +static void pci_mcfg_apply_quirks(struct acpi_pci_root *root, + struct resource *cfgres, + struct pci_ecam_ops **ecam_ops) +{ +#ifdef CONFIG_PCI_QUIRKS + u16 segment =3D root->segment; + struct resource *bus_range =3D &root->secondary; + struct mcfg_fixup *f; + int i; + + for (i =3D 0, f =3D mcfg_quirks; i < ARRAY_SIZE(mcfg_quirks); i++, f++) { + if (pci_mcfg_quirk_matches(f, segment, bus_range)) { + if (f->cfgres.start) + *cfgres =3D f->cfgres; + if (f->ops) + *ecam_ops =3D f->ops; + dev_info(&root->device->dev, "MCFG quirk: ECAM at %pR for %pR with %ps\= n", + cfgres, bus_range, *ecam_ops); + return; + } + } +#endif +} + /* List to save MCFG entries */ static LIST_HEAD(pci_mcfg_list); =20 @@ -61,14 +124,24 @@ int pci_mcfg_lookup(struct acpi_pci_root *root, struct= resource *cfgres, =20 } =20 - if (!root->mcfg_addr) - return -ENXIO; - skip_lookup: memset(&res, 0, sizeof(res)); - res.start =3D root->mcfg_addr + (bus_res->start << 20); - res.end =3D res.start + (resource_size(bus_res) << 20) - 1; - res.flags =3D IORESOURCE_MEM; + if (root->mcfg_addr) { + res.start =3D root->mcfg_addr + (bus_res->start << 20); + res.end =3D res.start + (resource_size(bus_res) << 20) - 1; + res.flags =3D IORESOURCE_MEM; + } + + /* + * Allow quirks to override default ECAM ops and CFG resource + * range. This may even fabricate a CFG resource range in case + * MCFG does not have it. Invalid CFG start address means MCFG + * firmware bug or we need another quirk in array. + */ + pci_mcfg_apply_quirks(root, &res, &ops); + if (!res.start) + return -ENXIO; + *cfgres =3D res; *ecam_ops =3D ops; return 0; @@ -101,6 +174,13 @@ static __init int pci_mcfg_parse(struct acpi_table_hea= der *header) list_add(&e->list, &pci_mcfg_list); } =20 +#ifdef CONFIG_PCI_QUIRKS + /* Save MCFG IDs and revision for quirks matching */ + memcpy(mcfg_oem_id, header->oem_id, ACPI_OEM_ID_SIZE); + memcpy(mcfg_oem_table_id, header->oem_table_id, ACPI_OEM_TABLE_ID_SIZE); + mcfg_oem_revision =3D header->oem_revision; +#endif + pr_info("MCFG table detected, %d entries\n", n); return 0; } --=20 2.9.3