PCI: dwc: Detect number of iATU windows
authorRob Herring <robh@kernel.org>
Thu, 5 Nov 2020 21:11:59 +0000 (15:11 -0600)
committerLorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Thu, 19 Nov 2020 10:51:41 +0000 (10:51 +0000)
Currently the number of inbound and outbound iATU windows are determined
from DT properties. Unfortunately, there's 'num-viewport' for RC mode
and 'num-ib-windows' and 'num-ob-windows' for EP mode, yet the number of
windows is not mode dependent. Also, 'num-viewport' is not clear whether
that's inbound, outbound or both. We can probably assume it's outbound
windows as that's all RC mode uses.

However, using DT properties isn't really needed as the number of
regions can be detected at runtime by poking the iATU registers. The
basic algorithm is just writing a target address and reading back what
we wrote. In the unrolled ATU case, we have to take care not to go
past the mapped region.

With this, we can drop num_viewport in favor of num_ob_windows instead.

Link: https://lore.kernel.org/r/20201105211159.1814485-17-robh@kernel.org
Tested-by: Marek Szyprowski <m.szyprowski@samsung.com>
Signed-off-by: Rob Herring <robh@kernel.org>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Jingoo Han <jingoohan1@gmail.com>
Cc: Gustavo Pimentel <gustavo.pimentel@synopsys.com>
Cc: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Thierry Reding <thierry.reding@gmail.com>
Cc: Jonathan Hunter <jonathanh@nvidia.com>
Cc: linux-tegra@vger.kernel.org
drivers/pci/controller/dwc/pcie-designware-ep.c
drivers/pci/controller/dwc/pcie-designware-host.c
drivers/pci/controller/dwc/pcie-designware.c
drivers/pci/controller/dwc/pcie-designware.h
drivers/pci/controller/dwc/pcie-intel-gw.c
drivers/pci/controller/dwc/pcie-tegra194.c

index 79b998982e417656f50d05b353f8f8568f9131fe..bcd1cd9ba8c80b20073ec23883939240f42bcc7d 100644 (file)
@@ -714,26 +714,6 @@ int dw_pcie_ep_init(struct dw_pcie_ep *ep)
        ep->phys_base = res->start;
        ep->addr_size = resource_size(res);
 
-       ret = of_property_read_u32(np, "num-ib-windows", &pci->num_ib_windows);
-       if (ret < 0) {
-               dev_err(dev, "Unable to read *num-ib-windows* property\n");
-               return ret;
-       }
-       if (pci->num_ib_windows > MAX_IATU_IN) {
-               dev_err(dev, "Invalid *num-ib-windows*\n");
-               return -EINVAL;
-       }
-
-       ret = of_property_read_u32(np, "num-ob-windows", &pci->num_ob_windows);
-       if (ret < 0) {
-               dev_err(dev, "Unable to read *num-ob-windows* property\n");
-               return ret;
-       }
-       if (pci->num_ob_windows > MAX_IATU_OUT) {
-               dev_err(dev, "Invalid *num-ob-windows*\n");
-               return -EINVAL;
-       }
-
        ep->ib_window_map = devm_kcalloc(dev,
                                         BITS_TO_LONGS(pci->num_ib_windows),
                                         sizeof(long),
index f2b0a15ad72baf672c27cb034b0f7fd1ccb2e134..99ef808a40a95d3503038340c7c62c1c45f2464f 100644 (file)
@@ -356,10 +356,6 @@ int dw_pcie_host_init(struct pcie_port *pp)
                }
        }
 
-       ret = of_property_read_u32(np, "num-viewport", &pci->num_viewport);
-       if (ret)
-               pci->num_viewport = 2;
-
        if (pci->link_gen < 1)
                pci->link_gen = of_pci_get_max_link_speed(np);
 
@@ -598,7 +594,7 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
        dw_pcie_writel_dbi(pci, PCI_COMMAND, val);
 
        /* Ensure all outbound windows are disabled so there are multiple matches */
-       for (i = 0; i < pci->num_viewport; i++)
+       for (i = 0; i < pci->num_ob_windows; i++)
                dw_pcie_disable_atu(pci, i, DW_PCIE_REGION_OUTBOUND);
 
        /*
@@ -615,7 +611,7 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
                        if (resource_type(entry->res) != IORESOURCE_MEM)
                                continue;
 
-                       if (pci->num_viewport <= ++atu_idx)
+                       if (pci->num_ob_windows <= ++atu_idx)
                                break;
 
                        dw_pcie_prog_outbound_atu(pci, atu_idx,
@@ -625,7 +621,7 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
                }
 
                if (pp->io_size) {
-                       if (pci->num_viewport > ++atu_idx)
+                       if (pci->num_ob_windows > ++atu_idx)
                                dw_pcie_prog_outbound_atu(pci, atu_idx,
                                                          PCIE_ATU_TYPE_IO, pp->io_base,
                                                          pp->io_bus_addr, pp->io_size);
@@ -633,9 +629,9 @@ void dw_pcie_setup_rc(struct pcie_port *pp)
                                pci->io_cfg_atu_shared = true;
                }
 
-               if (pci->num_viewport <= atu_idx)
+               if (pci->num_ob_windows <= atu_idx)
                        dev_warn(pci->dev, "Resources exceed number of ATU entries (%d)",
-                                pci->num_viewport);
+                                pci->num_ob_windows);
        }
 
        dw_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0);
index b5e438b70cd585ae47f0390794fe54f1533bc1d8..645fa18923751db2477956522d49d9120edeeb16 100644 (file)
@@ -546,6 +546,70 @@ static u8 dw_pcie_iatu_unroll_enabled(struct dw_pcie *pci)
        return 0;
 }
 
+static void dw_pcie_iatu_detect_regions_unroll(struct dw_pcie *pci)
+{
+       int max_region, i, ob = 0, ib = 0;
+       u32 val;
+
+       max_region = min((int)pci->atu_size / 512, 256);
+
+       for (i = 0; i < max_region; i++) {
+               dw_pcie_writel_ob_unroll(pci, i, PCIE_ATU_UNR_LOWER_TARGET,
+                                       0x11110000);
+
+               val = dw_pcie_readl_ob_unroll(pci, i, PCIE_ATU_UNR_LOWER_TARGET);
+               if (val == 0x11110000)
+                       ob++;
+               else
+                       break;
+       }
+
+       for (i = 0; i < max_region; i++) {
+               dw_pcie_writel_ib_unroll(pci, i, PCIE_ATU_UNR_LOWER_TARGET,
+                                       0x11110000);
+
+               val = dw_pcie_readl_ib_unroll(pci, i, PCIE_ATU_UNR_LOWER_TARGET);
+               if (val == 0x11110000)
+                       ib++;
+               else
+                       break;
+       }
+       pci->num_ib_windows = ib;
+       pci->num_ob_windows = ob;
+}
+
+static void dw_pcie_iatu_detect_regions(struct dw_pcie *pci)
+{
+       int max_region, i, ob = 0, ib = 0;
+       u32 val;
+
+       dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, 0xFF);
+       max_region = dw_pcie_readl_dbi(pci, PCIE_ATU_VIEWPORT) + 1;
+
+       for (i = 0; i < max_region; i++) {
+               dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, PCIE_ATU_REGION_OUTBOUND | i);
+               dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET, 0x11110000);
+               val = dw_pcie_readl_dbi(pci, PCIE_ATU_LOWER_TARGET);
+               if (val == 0x11110000)
+                       ob++;
+               else
+                       break;
+       }
+
+       for (i = 0; i < max_region; i++) {
+               dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT, PCIE_ATU_REGION_INBOUND | i);
+               dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET, 0x11110000);
+               val = dw_pcie_readl_dbi(pci, PCIE_ATU_LOWER_TARGET);
+               if (val == 0x11110000)
+                       ib++;
+               else
+                       break;
+       }
+
+       pci->num_ib_windows = ib;
+       pci->num_ob_windows = ob;
+}
+
 void dw_pcie_setup(struct dw_pcie *pci)
 {
        u32 val;
@@ -556,15 +620,30 @@ 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;
-               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 ?
+               if (!pci->atu_base) {
+                       struct resource *res =
+                               platform_get_resource_byname(pdev, IORESOURCE_MEM, "atu");
+                       if (res)
+                               pci->atu_size = resource_size(res);
+                       pci->atu_base = devm_ioremap_resource(dev, res);
+                       if (IS_ERR(pci->atu_base))
+                               pci->atu_base = pci->dbi_base + DEFAULT_DBI_ATU_OFFSET;
+               }
+
+               if (!pci->atu_size)
+                       /* Pick a minimal default, enough for 8 in and 8 out windows */
+                       pci->atu_size = SZ_4K;
+
+               dw_pcie_iatu_detect_regions_unroll(pci);
+       } else
+               dw_pcie_iatu_detect_regions(pci);
+
+       dev_info(pci->dev, "iATU unroll: %s\n", pci->iatu_unroll_enabled ?
                "enabled" : "disabled");
 
+       dev_info(pci->dev, "Detected iATU regions: %u outbound, %u inbound",
+                pci->num_ob_windows, pci->num_ib_windows);
+
        if (pci->link_gen > 0)
                dw_pcie_link_set_max_speed(pci, pci->link_gen);
 
index ed90eaf36f0fbd58c72eafb57cbd4beb6266ff9e..0207840756c47fc0fae42175b3729df0f2afde38 100644 (file)
@@ -261,7 +261,7 @@ struct dw_pcie {
        void __iomem            *dbi_base2;
        /* Used when iatu_unroll_enabled is true */
        void __iomem            *atu_base;
-       u32                     num_viewport;
+       size_t                  atu_size;
        u32                     num_ib_windows;
        u32                     num_ob_windows;
        struct pcie_port        pp;
index 292b9de865322ab08724c33aa75b76e083f88411..0cedd1f95f372c9e70f875db2921e19f3c0241c9 100644 (file)
@@ -58,7 +58,6 @@
 
 struct intel_pcie_soc {
        unsigned int    pcie_ver;
-       u32             num_viewport;
 };
 
 struct intel_pcie_port {
@@ -400,7 +399,6 @@ static const struct dw_pcie_host_ops intel_pcie_dw_ops = {
 
 static const struct intel_pcie_soc pcie_data = {
        .pcie_ver =             0x520A,
-       .num_viewport =         3,
 };
 
 static int intel_pcie_probe(struct platform_device *pdev)
@@ -443,12 +441,6 @@ static int intel_pcie_probe(struct platform_device *pdev)
                return ret;
        }
 
-       /*
-        * Intel PCIe doesn't configure IO region, so set viewport
-        * to not perform IO region access.
-        */
-       pci->num_viewport = data->num_viewport;
-
        return 0;
 }
 
index 84f3e0bb4c7a3810c8bd6b2c6a0b59ab68d0aa13..fa54d9aaa4308f473d2a91449d56d0b02040e9db 100644 (file)
@@ -2075,6 +2075,7 @@ static int tegra_pcie_dw_probe(struct platform_device *pdev)
        }
        pcie->atu_dma_res = atu_dma_res;
 
+       pci->atu_size = resource_size(atu_dma_res);
        pci->atu_base = devm_ioremap_resource(dev, atu_dma_res);
        if (IS_ERR(pci->atu_base))
                return PTR_ERR(pci->atu_base);