From cfada24d9c4ccfb58fdb64c0fea045236f07930c Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 26 Oct 2020 13:16:52 -0500 Subject: [PATCH] BACKPORT: PCI: dwc: Support multiple ATU memory regions The current ATU setup only supports a single memory resource which isn't sufficient if there are also prefetchable memory regions. In order to support multiple memory regions, we need to move away from fixed ATU slots and rework the assignment. As there's always an ATU entry for config space, let's assign index 0 to config space. Then we assign memory resources to index 1 and up. Finally, if we have an I/O region and slots remaining, we assign the I/O region last. If there aren't remaining slots, we keep the same config and I/O space sharing. Bug: 268559201 Link: https://lore.kernel.org/r/20201026181652.418729-1-robh@kernel.org Tested-by: Vidya Sagar Signed-off-by: Rob Herring Signed-off-by: Lorenzo Pieralisi Reviewed-by: Vidya Sagar Acked-by: Jingoo Han Cc: Vidya Sagar Cc: Jingoo Han Cc: Gustavo Pimentel Cc: Lorenzo Pieralisi Cc: Bjorn Helgaas Signed-off-by: Shawn Lin Signed-off-by: Jon Lin Change-Id: Ib945de723c29a80f055227474a01806283bd1873 (cherry picked from commit 9f9e59a4809563f24e3d1377aa804a4b7386a418) [Revert the struct change to keep KMI and setting DWC_IATU_IOCFG_SHARED in a vendor hook so that not to affect vendor modules not use it] Signed-off-by: Kever Yang --- .../pci/controller/dwc/pcie-designware-host.c | 52 +++++++++++-------- drivers/pci/controller/dwc/pcie-designware.c | 11 ++-- drivers/pci/controller/dwc/pcie-designware.h | 5 +- 3 files changed, 39 insertions(+), 29 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index a3b9ff4635a9..d23cd42a1e60 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -465,9 +465,7 @@ static void __iomem *dw_pcie_other_conf_map_bus(struct pci_bus *bus, type = PCIE_ATU_TYPE_CFG1; - dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1, - type, pp->cfg0_base, - busdev, pp->cfg0_size); + dw_pcie_prog_outbound_atu(pci, 0, type, pp->cfg0_base, busdev, pp->cfg0_size); return pp->va_cfg0_base + where; } @@ -481,9 +479,9 @@ static int dw_pcie_rd_other_conf(struct pci_bus *bus, unsigned int devfn, ret = pci_generic_config_read(bus, devfn, where, size, val); - if (!ret && pci->num_viewport <= 2) - dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1, - PCIE_ATU_TYPE_IO, pp->io_base, + if (!ret && ((pci->num_viewport <= 2) || + (pci->iatu_unroll_enabled & DWC_IATU_IOCFG_SHARED))) + dw_pcie_prog_outbound_atu(pci, 0, PCIE_ATU_TYPE_IO, pp->io_base, pp->io_bus_addr, pp->io_size); return ret; @@ -498,9 +496,9 @@ static int dw_pcie_wr_other_conf(struct pci_bus *bus, unsigned int devfn, ret = pci_generic_config_write(bus, devfn, where, size, val); - if (!ret && pci->num_viewport <= 2) - dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX1, - PCIE_ATU_TYPE_IO, pp->io_base, + if (!ret && ((pci->num_viewport <= 2) || + (pci->iatu_unroll_enabled & DWC_IATU_IOCFG_SHARED))) + dw_pcie_prog_outbound_atu(pci, 0, PCIE_ATU_TYPE_IO, pp->io_base, pp->io_bus_addr, pp->io_size); return ret; @@ -587,21 +585,33 @@ void dw_pcie_setup_rc(struct pcie_port *pp) * ATU, so we should not program the ATU here. */ if (pp->bridge->child_ops == &dw_child_pcie_ops) { - struct resource_entry *tmp, *entry = NULL; + int atu_idx = 0; + struct resource_entry *entry; /* Get last memory resource entry */ - resource_list_for_each_entry(tmp, &pp->bridge->windows) - if (resource_type(tmp->res) == IORESOURCE_MEM) - entry = tmp; + resource_list_for_each_entry(entry, &pp->bridge->windows) { + if (resource_type(entry->res) != IORESOURCE_MEM) + continue; - dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX0, - PCIE_ATU_TYPE_MEM, entry->res->start, - entry->res->start - entry->offset, - resource_size(entry->res)); - if (pci->num_viewport > 2) - dw_pcie_prog_outbound_atu(pci, PCIE_ATU_REGION_INDEX2, - PCIE_ATU_TYPE_IO, pp->io_base, - pp->io_bus_addr, pp->io_size); + if (pci->num_viewport <= ++atu_idx) + break; + + dw_pcie_prog_outbound_atu(pci, atu_idx, + PCIE_ATU_TYPE_MEM, entry->res->start, + entry->res->start - entry->offset, + resource_size(entry->res)); + } + + if (pp->io_size) { + if (pci->num_viewport > ++atu_idx) + dw_pcie_prog_outbound_atu(pci, atu_idx, + PCIE_ATU_TYPE_IO, pp->io_base, + pp->io_bus_addr, pp->io_size); + } + + if (pci->num_viewport <= atu_idx) + dev_warn(pci->dev, "Resources exceed number of ATU entries (%d)", + pci->num_viewport); } dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0); diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index db8911024073..02b369eb683e 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -274,7 +274,7 @@ static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no, if (pci->ops->cpu_addr_fixup) cpu_addr = pci->ops->cpu_addr_fixup(pci, cpu_addr); - if (pci->iatu_unroll_enabled) { + if (pci->iatu_unroll_enabled & DWC_IATU_UNROLL_EN) { dw_pcie_prog_outbound_atu_unroll(pci, func_no, index, type, cpu_addr, pci_addr, size); return; @@ -394,7 +394,7 @@ int dw_pcie_prog_inbound_atu(struct dw_pcie *pci, u8 func_no, int index, int type; u32 retries, val; - if (pci->iatu_unroll_enabled) + if (pci->iatu_unroll_enabled & DWC_IATU_UNROLL_EN) return dw_pcie_prog_inbound_atu_unroll(pci, func_no, index, bar, cpu_addr, as_type); @@ -452,7 +452,7 @@ void dw_pcie_disable_atu(struct dw_pcie *pci, int index, return; } - if (pci->iatu_unroll_enabled) { + if (pci->iatu_unroll_enabled & DWC_IATU_UNROLL_EN) { if (region == PCIE_ATU_REGION_INBOUND) { dw_pcie_writel_ib_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2, ~(u32)PCIE_ATU_ENABLE); @@ -564,14 +564,15 @@ void dw_pcie_setup(struct dw_pcie *pci) if (pci->version >= 0x480A || (!pci->version && dw_pcie_iatu_unroll_enabled(pci))) { - pci->iatu_unroll_enabled = true; + pci->iatu_unroll_enabled |= DWC_IATU_UNROLL_EN; if (!pci->atu_base) pci->atu_base = devm_platform_ioremap_resource_byname(pdev, "atu"); if (IS_ERR(pci->atu_base)) pci->atu_base = pci->dbi_base + DEFAULT_DBI_ATU_OFFSET; } - dev_dbg(pci->dev, "iATU unroll: %s\n", pci->iatu_unroll_enabled ? + dev_dbg(pci->dev, "iATU unroll: %s\n", + pci->iatu_unroll_enabled & DWC_IATU_UNROLL_EN ? "enabled" : "disabled"); if (pci->link_gen > 0) diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index c098ecc33ae3..8363f77b2f26 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -81,9 +81,6 @@ #define PCIE_ATU_VIEWPORT 0x900 #define PCIE_ATU_REGION_INBOUND BIT(31) #define PCIE_ATU_REGION_OUTBOUND 0 -#define PCIE_ATU_REGION_INDEX2 0x2 -#define PCIE_ATU_REGION_INDEX1 0x1 -#define PCIE_ATU_REGION_INDEX0 0x0 #define PCIE_ATU_CR1 0x904 #define PCIE_ATU_TYPE_MEM 0x0 #define PCIE_ATU_TYPE_IO 0x2 @@ -260,6 +257,8 @@ struct dw_pcie_ops { void (*stop_link)(struct dw_pcie *pcie); }; +#define DWC_IATU_UNROLL_EN BIT(0) +#define DWC_IATU_IOCFG_SHARED BIT(1) struct dw_pcie { struct device *dev; void __iomem *dbi_base;