PCI: dwc: Set INCREASE_REGION_SIZE flag based on limit address
authorSerge Semin <Sergey.Semin@baikalelectronics.ru>
Fri, 24 Jun 2022 14:34:14 +0000 (17:34 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 17 Aug 2022 12:23:48 +0000 (14:23 +0200)
[ Upstream commit 777e7c3ab73036105e6fc4a67ed950179dbffbab ]

We program the 64-bit ATU limit address (in PCIE_ATU_LIMIT/
PCIE_ATU_UPPER_LIMIT or PCIE_ATU_UNR_LOWER_LIMIT/PCIE_ATU_UNR_UPPER_LIMIT),
but in addition, the PCIE_ATU_INCREASE_REGION_SIZE bit must be set if the
upper 32 bits of the limit address differ from the upper 32 bits of the
base address (see [1,2]).

5b4cf0f65324 ("PCI: dwc: Add upper limit address for outbound iATU") set
PCIE_ATU_INCREASE_REGION_SIZE, but only when the *size* was greater than
4GB.  It did not set it when a smaller region crossed a 4GB boundary, e.g.,
[mem 0x0_f0000000-0x1_0fffffff].

Set PCIE_ATU_INCREASE_REGION_SIZE whenever PCIE_ATU_UPPER_LIMIT is
greater than PCIE_ATU_UPPER_BASE.

[1] DesignWare Cores PCI Express Controller Databook - DWC PCIe Root Port,
    v5.40a, March 2019, fig.3-36, p.175
[2] DesignWare Cores PCI Express Controller Databook - DWC PCIe Root Port,
    v5.40a, March 2019, fig.3-37, p.176

[bhelgaas: commit log]
Fixes: 5b4cf0f65324 ("PCI: dwc: Add upper limit address for outbound iATU")
Link: https://lore.kernel.org/r/20220624143428.8334-5-Sergey.Semin@baikalelectronics.ru
Tested-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/pci/controller/dwc/pcie-designware.c

index ff74ddf..e863cc2 100644 (file)
@@ -287,8 +287,8 @@ static void dw_pcie_prog_outbound_atu_unroll(struct dw_pcie *pci, u8 func_no,
        dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_TARGET,
                                 upper_32_bits(pci_addr));
        val = type | PCIE_ATU_FUNC_NUM(func_no);
-       val = upper_32_bits(size - 1) ?
-               val | PCIE_ATU_INCREASE_REGION_SIZE : val;
+       if (upper_32_bits(limit_addr) > upper_32_bits(cpu_addr))
+               val |= PCIE_ATU_INCREASE_REGION_SIZE;
        if (pci->version == 0x490A)
                val = dw_pcie_enable_ecrc(val);
        dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, val);
@@ -315,6 +315,7 @@ static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
                                        u64 pci_addr, u64 size)
 {
        u32 retries, val;
+       u64 limit_addr;
 
        if (pci->ops && pci->ops->cpu_addr_fixup)
                cpu_addr = pci->ops->cpu_addr_fixup(pci, cpu_addr);
@@ -325,6 +326,8 @@ static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
                return;
        }
 
+       limit_addr = cpu_addr + size - 1;
+
        dw_pcie_writel_dbi(pci, PCIE_ATU_VIEWPORT,
                           PCIE_ATU_REGION_OUTBOUND | index);
        dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_BASE,
@@ -332,17 +335,18 @@ static void __dw_pcie_prog_outbound_atu(struct dw_pcie *pci, u8 func_no,
        dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_BASE,
                           upper_32_bits(cpu_addr));
        dw_pcie_writel_dbi(pci, PCIE_ATU_LIMIT,
-                          lower_32_bits(cpu_addr + size - 1));
+                          lower_32_bits(limit_addr));
        if (pci->version >= 0x460A)
                dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_LIMIT,
-                                  upper_32_bits(cpu_addr + size - 1));
+                                  upper_32_bits(limit_addr));
        dw_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET,
                           lower_32_bits(pci_addr));
        dw_pcie_writel_dbi(pci, PCIE_ATU_UPPER_TARGET,
                           upper_32_bits(pci_addr));
        val = type | PCIE_ATU_FUNC_NUM(func_no);
-       val = ((upper_32_bits(size - 1)) && (pci->version >= 0x460A)) ?
-               val | PCIE_ATU_INCREASE_REGION_SIZE : val;
+       if (upper_32_bits(limit_addr) > upper_32_bits(cpu_addr) &&
+           pci->version >= 0x460A)
+               val |= PCIE_ATU_INCREASE_REGION_SIZE;
        if (pci->version == 0x490A)
                val = dw_pcie_enable_ecrc(val);
        dw_pcie_writel_dbi(pci, PCIE_ATU_CR1, val);