Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

PCI/ACPI: Extend pci_mcfg_lookup() to return ECAM config accessors

pci_mcfg_lookup() is the external interface to the generic MCFG code.
Previously it merely looked up the ECAM base address for a given domain and
bus range. We want a way to add MCFG quirks, some of which may require
special config accessors and adjustments to the ECAM address range.

Extend pci_mcfg_lookup() so it can return a pointer to a pci_ecam_ops
structure and a struct resource for the ECAM address space. For now, it
always returns &pci_generic_ecam_ops (the standard accessor) and the
resource described by the MCFG.

No functional changes intended.

[bhelgaas: changelog]
Signed-off-by: Tomasz Nowicki <tn@semihalf.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>

authored by

Tomasz Nowicki and committed by
Bjorn Helgaas
13983eb8 8fd4391e

+33 -16
+5 -12
arch/arm64/kernel/pci.c
··· 137 137 struct device *dev = &root->device->dev; 138 138 struct resource *bus_res = &root->secondary; 139 139 u16 seg = root->segment; 140 + struct pci_ecam_ops *ecam_ops; 140 141 struct resource cfgres; 141 142 struct acpi_device *adev; 142 143 struct pci_config_window *cfg; 143 - unsigned int bsz; 144 + int ret; 144 145 145 - /* Use address from _CBA if present, otherwise lookup MCFG */ 146 - if (!root->mcfg_addr) 147 - root->mcfg_addr = pci_mcfg_lookup(seg, bus_res); 148 - 149 - if (!root->mcfg_addr) { 146 + ret = pci_mcfg_lookup(root, &cfgres, &ecam_ops); 147 + if (ret) { 150 148 dev_err(dev, "%04x:%pR ECAM region not found\n", seg, bus_res); 151 149 return NULL; 152 150 } 153 - 154 - bsz = 1 << pci_generic_ecam_ops.bus_shift; 155 - cfgres.start = root->mcfg_addr + bus_res->start * bsz; 156 - cfgres.end = cfgres.start + resource_size(bus_res) * bsz - 1; 157 - cfgres.flags = IORESOURCE_MEM; 158 151 159 152 adev = acpi_resource_consumer(&cfgres); 160 153 if (adev) ··· 157 164 dev_warn(dev, FW_BUG "ECAM area %pR not reserved in ACPI namespace\n", 158 165 &cfgres); 159 166 160 - cfg = pci_ecam_create(dev, &cfgres, bus_res, &pci_generic_ecam_ops); 167 + cfg = pci_ecam_create(dev, &cfgres, bus_res, ecam_ops); 161 168 if (IS_ERR(cfg)) { 162 169 dev_err(dev, "%04x:%pR error %ld mapping ECAM\n", seg, bus_res, 163 170 PTR_ERR(cfg));
+25 -3
drivers/acpi/pci_mcfg.c
··· 22 22 #include <linux/kernel.h> 23 23 #include <linux/pci.h> 24 24 #include <linux/pci-acpi.h> 25 + #include <linux/pci-ecam.h> 25 26 26 27 /* Structure to hold entries from the MCFG table */ 27 28 struct mcfg_entry { ··· 36 35 /* List to save MCFG entries */ 37 36 static LIST_HEAD(pci_mcfg_list); 38 37 39 - phys_addr_t pci_mcfg_lookup(u16 seg, struct resource *bus_res) 38 + int pci_mcfg_lookup(struct acpi_pci_root *root, struct resource *cfgres, 39 + struct pci_ecam_ops **ecam_ops) 40 40 { 41 + struct pci_ecam_ops *ops = &pci_generic_ecam_ops; 42 + struct resource *bus_res = &root->secondary; 43 + u16 seg = root->segment; 41 44 struct mcfg_entry *e; 45 + struct resource res; 46 + 47 + /* Use address from _CBA if present, otherwise lookup MCFG */ 48 + if (root->mcfg_addr) 49 + goto skip_lookup; 42 50 43 51 /* 44 52 * We expect exact match, unless MCFG entry end bus covers more than ··· 55 45 */ 56 46 list_for_each_entry(e, &pci_mcfg_list, list) { 57 47 if (e->segment == seg && e->bus_start == bus_res->start && 58 - e->bus_end >= bus_res->end) 59 - return e->addr; 48 + e->bus_end >= bus_res->end) { 49 + root->mcfg_addr = e->addr; 50 + } 51 + 60 52 } 61 53 54 + if (!root->mcfg_addr) 55 + return -ENXIO; 56 + 57 + skip_lookup: 58 + memset(&res, 0, sizeof(res)); 59 + res.start = root->mcfg_addr + (bus_res->start << 20); 60 + res.end = res.start + (resource_size(bus_res) << 20) - 1; 61 + res.flags = IORESOURCE_MEM; 62 + *cfgres = res; 63 + *ecam_ops = ops; 62 64 return 0; 63 65 } 64 66
+3 -1
include/linux/pci-acpi.h
··· 24 24 } 25 25 extern phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle); 26 26 27 - extern phys_addr_t pci_mcfg_lookup(u16 domain, struct resource *bus_res); 27 + struct pci_ecam_ops; 28 + extern int pci_mcfg_lookup(struct acpi_pci_root *root, struct resource *cfgres, 29 + struct pci_ecam_ops **ecam_ops); 28 30 29 31 static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev) 30 32 {