PCI: dwc: Add iATU regions size detection procedure
authorSerge Semin <Sergey.Semin@baikalelectronics.ru>
Fri, 24 Jun 2022 14:39:44 +0000 (17:39 +0300)
committerBjorn Helgaas <bhelgaas@google.com>
Mon, 1 Aug 2022 20:15:32 +0000 (15:15 -0500)
The DWC PCIe RC/EP/DM IP core configuration parameters determine the number
of inbound and outbound iATU windows, alignment requirements (which is also
the minimum window size), minimum and maximum sizes.  If internal ATU is
enabled, the former settings are determined by CX_ATU_MIN_REGION_SIZE; the
latter are determined by CX_ATU_MAX_REGION_SIZE.

Determine the required alignment and maximum size supported by the
controller and log it to help verify whether the requested inbound or
outbound memory mappings can be fully created.

Note 1. The extended iATU regions have been supported since DWC PCIe
v4.60a. There is no need in testing the upper limit register availability
for the older cores.

Note 2. The regions alignment is determined with using the fls() method
since the lower four bits of the ATU Limit register can be occupied with
the Circular Buffer Increment setting, which can be initialized with zeros.

Link: https://lore.kernel.org/r/20220624143947.8991-13-Sergey.Semin@baikalelectronics.ru
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Rob Herring <robh@kernel.org>
Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
drivers/pci/controller/dwc/pcie-designware.c
drivers/pci/controller/dwc/pcie-designware.h

index 6ddda710d6de08095499c89acbbd60301a7ff8b1..cb6244db92695381dcdaaba67bef0a23b390b235 100644 (file)
@@ -8,9 +8,11 @@
  * Author: Jingoo Han <jg1.han@samsung.com>
  */
 
+#include <linux/bitops.h>
 #include <linux/delay.h>
 #include <linux/of.h>
 #include <linux/of_platform.h>
+#include <linux/sizes.h>
 #include <linux/types.h>
 
 #include "../../pci.h"
@@ -522,7 +524,8 @@ static bool dw_pcie_iatu_unroll_enabled(struct dw_pcie *pci)
 static void dw_pcie_iatu_detect_regions(struct dw_pcie *pci)
 {
        int max_region, ob, ib;
-       u32 val;
+       u32 val, min, dir;
+       u64 max;
 
        if (pci->iatu_unroll_enabled) {
                max_region = min((int)pci->atu_size / 512, 256);
@@ -545,8 +548,29 @@ static void dw_pcie_iatu_detect_regions(struct dw_pcie *pci)
                        break;
        }
 
-       pci->num_ib_windows = ib;
+       if (ob) {
+               dir = PCIE_ATU_REGION_DIR_OB;
+       } else if (ib) {
+               dir = PCIE_ATU_REGION_DIR_IB;
+       } else {
+               dev_err(pci->dev, "No iATU regions found\n");
+               return;
+       }
+
+       dw_pcie_writel_atu(pci, dir, 0, PCIE_ATU_LIMIT, 0x0);
+       min = dw_pcie_readl_atu(pci, dir, 0, PCIE_ATU_LIMIT);
+
+       if (dw_pcie_ver_is_ge(pci, 460A)) {
+               dw_pcie_writel_atu(pci, dir, 0, PCIE_ATU_UPPER_LIMIT, 0xFFFFFFFF);
+               max = dw_pcie_readl_atu(pci, dir, 0, PCIE_ATU_UPPER_LIMIT);
+       } else {
+               max = 0;
+       }
+
        pci->num_ob_windows = ob;
+       pci->num_ib_windows = ib;
+       pci->region_align = 1 << fls(min);
+       pci->region_limit = (max << 32) | (SZ_4G - 1);
 }
 
 void dw_pcie_iatu_detect(struct dw_pcie *pci)
@@ -579,8 +603,9 @@ void dw_pcie_iatu_detect(struct dw_pcie *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\n",
-                pci->num_ob_windows, pci->num_ib_windows);
+       dev_info(pci->dev, "iATU regions: %u ob, %u ib, align %uK, limit %lluG\n",
+                pci->num_ob_windows, pci->num_ib_windows,
+                pci->region_align / SZ_1K, (pci->region_limit + 1) / SZ_1G);
 }
 
 void dw_pcie_setup(struct dw_pcie *pci)
index c18f0db09b31b2c665ad2c1ee3be28371e427261..25c86771c810fd3f4fb41e0f7a91d9998efb3c52 100644 (file)
@@ -272,6 +272,8 @@ struct dw_pcie {
        size_t                  atu_size;
        u32                     num_ib_windows;
        u32                     num_ob_windows;
+       u32                     region_align;
+       u64                     region_limit;
        struct dw_pcie_rp       pp;
        struct dw_pcie_ep       ep;
        const struct dw_pcie_ops *ops;