From: Mark Salter msalter@redhat.com
Xgene doesn't decode bus bits of mmconfig region and only supports devfn 0 of bus 0. For other buses/devices, some internal registers need to be poked. This patch provides a fixup to support ACPI MCFG tables.
Signed-off-by: Mark Salter msalter@redhat.com --- drivers/pci/host/pci-xgene.c | 144 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+)
diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c index 9ecabfa..526812f 100644 --- a/drivers/pci/host/pci-xgene.c +++ b/drivers/pci/host/pci-xgene.c @@ -29,6 +29,7 @@ #include <linux/pci.h> #include <linux/platform_device.h> #include <linux/slab.h> +#include <linux/acpi.h>
#define PCIECORE_CTLANDSTATUS 0x50 #define PIM1_1L 0x80 @@ -600,6 +601,149 @@ static int xgene_pcie_setup(struct xgene_pcie_port *port, return 0; }
+#ifdef CONFIG_ACPI +struct xgene_mcfg_info { + void __iomem *csr_base; +}; + +/* + * When the address bit [17:16] is 2'b01, the Configuration access will be + * treated as Type 1 and it will be forwarded to external PCIe device. + */ +static void __iomem *__get_cfg_base(struct pci_mmcfg_region *cfg, + unsigned int bus) +{ + if (bus > cfg->start_bus) + return cfg->virt + AXI_EP_CFG_ACCESS; + + return cfg->virt; +} + +/* + * For Configuration request, RTDID register is used as Bus Number, + * Device Number and Function number of the header fields. + */ +static void __set_rtdid_reg(struct pci_mmcfg_region *cfg, + unsigned int bus, unsigned int devfn) +{ + struct xgene_mcfg_info *info = cfg->data; + unsigned int b, d, f; + u32 rtdid_val = 0; + + b = bus; + d = PCI_SLOT(devfn); + f = PCI_FUNC(devfn); + + if (bus != cfg->start_bus) + rtdid_val = (b << 8) | (d << 3) | f; + + writel(rtdid_val, info->csr_base + RTDID); + /* read the register back to ensure flush */ + readl(info->csr_base + RTDID); +} + +static int xgene_raw_pci_read(struct pci_mmcfg_region *cfg, unsigned int bus, + unsigned int devfn, int offset, int len, u32 *val) +{ + void __iomem *addr; + + if (bus == cfg->start_bus && devfn != 0) { + *val = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + __set_rtdid_reg(cfg, bus, devfn); + addr = __get_cfg_base(cfg, bus); + switch (len) { + case 1: + xgene_pcie_cfg_in8(addr, offset, val); + break; + case 2: + xgene_pcie_cfg_in16(addr, offset, val); + break; + default: + xgene_pcie_cfg_in32(addr, offset, val); + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static int xgene_raw_pci_write(struct pci_mmcfg_region *cfg, unsigned int bus, + unsigned int devfn, int offset, int len, u32 val) +{ + void __iomem *addr; + + if (bus == cfg->start_bus && devfn != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + + __set_rtdid_reg(cfg, bus, devfn); + addr = __get_cfg_base(cfg, bus); + switch (len) { + case 1: + xgene_pcie_cfg_out8(addr, offset, (u8)val); + break; + case 2: + xgene_pcie_cfg_out16(addr, offset, (u16)val); + break; + default: + xgene_pcie_cfg_out32(addr, offset, val); + break; + } + return PCIBIOS_SUCCESSFUL; +} + +static acpi_status find_csr_base(struct acpi_resource *acpi_res, void *data) +{ + struct pci_mmcfg_region *cfg = data; + struct xgene_mcfg_info *info = cfg->data; + struct acpi_resource_fixed_memory32 *fixed32; + + if (acpi_res->type == ACPI_RESOURCE_TYPE_FIXED_MEMORY32) { + fixed32 = &acpi_res->data.fixed_memory32; + info->csr_base = ioremap(fixed32->address, + fixed32->address_length); + return AE_CTRL_TERMINATE; + } + return AE_OK; +} + +static int xgene_mcfg_fixup(struct acpi_pci_root *root, + struct pci_mmcfg_region *cfg) +{ + struct acpi_device *device = root->device; + struct xgene_mcfg_info *info; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (info == NULL) + return -ENOMEM; + + cfg->data = info; + + acpi_walk_resources(device->handle, METHOD_NAME__CRS, + find_csr_base, cfg); + + if (!info->csr_base) { + kfree(info); + cfg->data = NULL; + return -ENODEV; + } + + cfg->read = xgene_raw_pci_read; + cfg->write = xgene_raw_pci_write; + + /* actual last bus reachable through this mmconfig */ + cfg->end_bus = root->secondary.end; + + /* firmware should have done this */ + xgene_raw_pci_write(cfg, cfg->start_bus, 0, PCI_PRIMARY_BUS, 4, + cfg->start_bus | ((cfg->start_bus + 1) << 8) | + (cfg->end_bus << 16)); + + return 0; +} +DECLARE_ACPI_MCFG_FIXUP("APM ", "XGENE ", xgene_mcfg_fixup); +#endif /* CONFIG_ACPI */ + static int xgene_pcie_probe_bridge(struct platform_device *pdev) { struct device_node *dn = pdev->dev.of_node;