]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
PCI: qcom: Disable mirroring of DBI and iATU register space in BAR region
authorPrudhvi Yarlagadda <quic_pyarlaga@quicinc.com>
Wed, 14 Aug 2024 22:03:38 +0000 (15:03 -0700)
committerKrzysztof Wilczyński <kwilczynski@kernel.org>
Sun, 1 Sep 2024 08:12:27 +0000 (08:12 +0000)
PARF hardware block which is a wrapper on top of DWC PCIe controller
mirrors the DBI and ATU register space. It uses PARF_SLV_ADDR_SPACE_SIZE
register to get the size of the memory block to be mirrored and uses
PARF_DBI_BASE_ADDR, PARF_ATU_BASE_ADDR registers to determine the base
address of DBI and ATU space inside the memory block that is being
mirrored.

When a memory region which is located above the SLV_ADDR_SPACE_SIZE
boundary is used for BAR region then there could be an overlap of DBI and
ATU address space that is getting mirrored and the BAR region. This
results in DBI and ATU address space contents getting updated when a PCIe
function driver tries updating the BAR/MMIO memory region. Reference
memory map of the PCIe memory region with DBI and ATU address space
overlapping BAR region is as below.

                        |---------------|
                        |               |
                        |               |
        ------- --------|---------------|
           |       |    |---------------|
           |       |    |       DBI     |
           |       |    |---------------|---->DBI_BASE_ADDR
           |       |    |               |
           |       |    |               |
           |    PCIe    |               |---->2*SLV_ADDR_SPACE_SIZE
           |    BAR/MMIO|---------------|
           |    Region  |       ATU     |
           |       |    |---------------|---->ATU_BASE_ADDR
           |       |    |               |
        PCIe       |    |---------------|
        Memory     |    |       DBI     |
        Region     |    |---------------|---->DBI_BASE_ADDR
           |       |    |               |
           |    --------|               |
           |            |               |---->SLV_ADDR_SPACE_SIZE
           |            |---------------|
           |            |       ATU     |
           |            |---------------|---->ATU_BASE_ADDR
           |            |               |
           |            |---------------|
           |            |       DBI     |
           |            |---------------|---->DBI_BASE_ADDR
           |            |               |
           |            |               |
        ----------------|---------------|
                        |               |
                        |               |
                        |               |
                        |---------------|

Currently memory region beyond the SLV_ADDR_SPACE_SIZE boundary is not
used for BAR region which is why the above mentioned issue is not
encountered. This issue is discovered as part of internal testing when we
tried moving the BAR region beyond the SLV_ADDR_SPACE_SIZE boundary. Hence
we are trying to fix this.

As PARF hardware block mirrors DBI and ATU register space after every
PARF_SLV_ADDR_SPACE_SIZE (default 0x1000000) boundary multiple, program
maximum possible size to this register by writing 0x80000000 to it(it
considers only powers of 2 as values) to avoid mirroring DBI and ATU to
BAR/MMIO region. Write the physical base address of DBI and ATU register
blocks to PARF_DBI_BASE_ADDR (default 0x0) and PARF_ATU_BASE_ADDR (default
0x1000) respectively to make sure DBI and ATU blocks are at expected
memory locations.

The register offsets PARF_DBI_BASE_ADDR_V2, PARF_SLV_ADDR_SPACE_SIZE_V2
and PARF_ATU_BASE_ADDR are applicable for platforms that use Qcom IP
rev 1.9.0, 2.7.0 and 2.9.0. PARF_DBI_BASE_ADDR_V2 and
PARF_SLV_ADDR_SPACE_SIZE_V2 are applicable for Qcom IP rev 2.3.3.
PARF_DBI_BASE_ADDR and PARF_SLV_ADDR_SPACE_SIZE are applicable for Qcom
IP rev 1.0.0, 2.3.2 and 2.4.0. Update init()/post_init() functions of the
respective Qcom IP versions to program applicable PARF_DBI_BASE_ADDR,
PARF_SLV_ADDR_SPACE_SIZE and PARF_ATU_BASE_ADDR register offsets. Update
the SLV_ADDR_SPACE_SZ macro to 0x80000000 to set highest bit in
PARF_SLV_ADDR_SPACE_SIZE register.

Cache DBI and iATU physical addresses in 'struct dw_pcie' so that
pcie_qcom.c driver can program these addresses in the PARF_DBI_BASE_ADDR
and PARF_ATU_BASE_ADDR registers.

Suggested-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Link: https://lore.kernel.org/linux-pci/20240814220338.1969668-1-quic_pyarlaga@quicinc.com
Signed-off-by: Prudhvi Yarlagadda <quic_pyarlaga@quicinc.com>
Signed-off-by: Krzysztof Wilczyński <kwilczynski@kernel.org>
Reviewed-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Reviewed-by: Mayank Rana <quic_mrana@quicinc.com>
drivers/pci/controller/dwc/pcie-designware.c
drivers/pci/controller/dwc/pcie-designware.h
drivers/pci/controller/dwc/pcie-qcom.c

index 1b5aba1f0c92f93fd7e04ae866cd94895e239de6..bc3a5d6b017797f1d37a77f8b182020d2335d2b5 100644 (file)
@@ -112,6 +112,7 @@ int dw_pcie_get_resources(struct dw_pcie *pci)
                pci->dbi_base = devm_pci_remap_cfg_resource(pci->dev, res);
                if (IS_ERR(pci->dbi_base))
                        return PTR_ERR(pci->dbi_base);
+               pci->dbi_phys_addr = res->start;
        }
 
        /* DBI2 is mainly useful for the endpoint controller */
@@ -134,6 +135,7 @@ int dw_pcie_get_resources(struct dw_pcie *pci)
                        pci->atu_base = devm_ioremap_resource(pci->dev, res);
                        if (IS_ERR(pci->atu_base))
                                return PTR_ERR(pci->atu_base);
+                       pci->atu_phys_addr = res->start;
                } else {
                        pci->atu_base = pci->dbi_base + DEFAULT_DBI_ATU_OFFSET;
                }
index 53c4c8f399c8837f5682aee7c62c23f659556f72..e518f81ea80cdaae1e54953742f6c725cdc6dc57 100644 (file)
@@ -407,8 +407,10 @@ struct dw_pcie_ops {
 struct dw_pcie {
        struct device           *dev;
        void __iomem            *dbi_base;
+       resource_size_t         dbi_phys_addr;
        void __iomem            *dbi_base2;
        void __iomem            *atu_base;
+       resource_size_t         atu_phys_addr;
        size_t                  atu_size;
        u32                     num_ib_windows;
        u32                     num_ob_windows;
index a1d678fe7fa510872dbad63e446d3ab9b68bceb9..1923266acea89f7cc1d8273ffcf2a459573fc30e 100644 (file)
@@ -45,6 +45,7 @@
 #define PARF_PHY_REFCLK                                0x4c
 #define PARF_CONFIG_BITS                       0x50
 #define PARF_DBI_BASE_ADDR                     0x168
+#define PARF_SLV_ADDR_SPACE_SIZE               0x16c
 #define PARF_MHI_CLOCK_RESET_CTRL              0x174
 #define PARF_AXI_MSTR_WR_ADDR_HALT             0x178
 #define PARF_AXI_MSTR_WR_ADDR_HALT_V2          0x1a8
 #define PARF_INT_ALL_MASK                      0x22c
 #define PARF_SID_OFFSET                                0x234
 #define PARF_BDF_TRANSLATE_CFG                 0x24c
-#define PARF_SLV_ADDR_SPACE_SIZE               0x358
+#define PARF_DBI_BASE_ADDR_V2                  0x350
+#define PARF_DBI_BASE_ADDR_V2_HI               0x354
+#define PARF_SLV_ADDR_SPACE_SIZE_V2            0x358
+#define PARF_SLV_ADDR_SPACE_SIZE_V2_HI         0x35c
 #define PARF_NO_SNOOP_OVERIDE                  0x3d4
+#define PARF_ATU_BASE_ADDR                     0x634
+#define PARF_ATU_BASE_ADDR_HI                  0x638
 #define PARF_DEVICE_TYPE                       0x1000
 #define PARF_BDF_TO_SID_TABLE_N                        0x2000
 #define PARF_BDF_TO_SID_CFG                    0x2c00
 #define PHY_RX0_EQ(x)                          FIELD_PREP(GENMASK(26, 24), x)
 
 /* PARF_SLV_ADDR_SPACE_SIZE register value */
-#define SLV_ADDR_SPACE_SZ                      0x10000000
+#define SLV_ADDR_SPACE_SZ                      0x80000000
 
 /* PARF_MHI_CLOCK_RESET_CTRL register fields */
 #define AHB_CLK_EN                             BIT(0)
@@ -330,6 +336,50 @@ static void qcom_pcie_clear_hpc(struct dw_pcie *pci)
        dw_pcie_dbi_ro_wr_dis(pci);
 }
 
+static void qcom_pcie_configure_dbi_base(struct qcom_pcie *pcie)
+{
+       struct dw_pcie *pci = pcie->pci;
+
+       if (pci->dbi_phys_addr) {
+               /*
+                * PARF_DBI_BASE_ADDR register is in CPU domain and require to
+                * be programmed with CPU physical address.
+                */
+               writel(lower_32_bits(pci->dbi_phys_addr), pcie->parf +
+                                                       PARF_DBI_BASE_ADDR);
+               writel(SLV_ADDR_SPACE_SZ, pcie->parf +
+                                               PARF_SLV_ADDR_SPACE_SIZE);
+       }
+}
+
+static void qcom_pcie_configure_dbi_atu_base(struct qcom_pcie *pcie)
+{
+       struct dw_pcie *pci = pcie->pci;
+
+       if (pci->dbi_phys_addr) {
+               /*
+                * PARF_DBI_BASE_ADDR_V2 and PARF_ATU_BASE_ADDR registers are
+                * in CPU domain and require to be programmed with CPU
+                * physical addresses.
+                */
+               writel(lower_32_bits(pci->dbi_phys_addr), pcie->parf +
+                                                       PARF_DBI_BASE_ADDR_V2);
+               writel(upper_32_bits(pci->dbi_phys_addr), pcie->parf +
+                                               PARF_DBI_BASE_ADDR_V2_HI);
+
+               if (pci->atu_phys_addr) {
+                       writel(lower_32_bits(pci->atu_phys_addr), pcie->parf +
+                                                       PARF_ATU_BASE_ADDR);
+                       writel(upper_32_bits(pci->atu_phys_addr), pcie->parf +
+                                                       PARF_ATU_BASE_ADDR_HI);
+               }
+
+               writel(0x0, pcie->parf + PARF_SLV_ADDR_SPACE_SIZE_V2);
+               writel(SLV_ADDR_SPACE_SZ, pcie->parf +
+                                       PARF_SLV_ADDR_SPACE_SIZE_V2_HI);
+       }
+}
+
 static void qcom_pcie_2_1_0_ltssm_enable(struct qcom_pcie *pcie)
 {
        u32 val;
@@ -546,8 +596,7 @@ err_assert_reset:
 
 static int qcom_pcie_post_init_1_0_0(struct qcom_pcie *pcie)
 {
-       /* change DBI base address */
-       writel(0, pcie->parf + PARF_DBI_BASE_ADDR);
+       qcom_pcie_configure_dbi_base(pcie);
 
        if (IS_ENABLED(CONFIG_PCI_MSI)) {
                u32 val = readl(pcie->parf + PARF_AXI_MSTR_WR_ADDR_HALT);
@@ -634,8 +683,7 @@ static int qcom_pcie_post_init_2_3_2(struct qcom_pcie *pcie)
        val &= ~PHY_TEST_PWR_DOWN;
        writel(val, pcie->parf + PARF_PHY_CTRL);
 
-       /* change DBI base address */
-       writel(0, pcie->parf + PARF_DBI_BASE_ADDR);
+       qcom_pcie_configure_dbi_base(pcie);
 
        /* MAC PHY_POWERDOWN MUX DISABLE  */
        val = readl(pcie->parf + PARF_SYS_CTRL);
@@ -817,13 +865,11 @@ static int qcom_pcie_post_init_2_3_3(struct qcom_pcie *pcie)
        u16 offset = dw_pcie_find_capability(pci, PCI_CAP_ID_EXP);
        u32 val;
 
-       writel(SLV_ADDR_SPACE_SZ, pcie->parf + PARF_SLV_ADDR_SPACE_SIZE);
-
        val = readl(pcie->parf + PARF_PHY_CTRL);
        val &= ~PHY_TEST_PWR_DOWN;
        writel(val, pcie->parf + PARF_PHY_CTRL);
 
-       writel(0, pcie->parf + PARF_DBI_BASE_ADDR);
+       qcom_pcie_configure_dbi_atu_base(pcie);
 
        writel(MST_WAKEUP_EN | SLV_WAKEUP_EN | MSTR_ACLK_CGC_DIS
                | SLV_ACLK_CGC_DIS | CORE_CLK_CGC_DIS |
@@ -919,8 +965,7 @@ static int qcom_pcie_init_2_7_0(struct qcom_pcie *pcie)
        val &= ~PHY_TEST_PWR_DOWN;
        writel(val, pcie->parf + PARF_PHY_CTRL);
 
-       /* change DBI base address */
-       writel(0, pcie->parf + PARF_DBI_BASE_ADDR);
+       qcom_pcie_configure_dbi_atu_base(pcie);
 
        /* MAC PHY_POWERDOWN MUX DISABLE  */
        val = readl(pcie->parf + PARF_SYS_CTRL);
@@ -1129,14 +1174,11 @@ static int qcom_pcie_post_init_2_9_0(struct qcom_pcie *pcie)
        u32 val;
        int i;
 
-       writel(SLV_ADDR_SPACE_SZ,
-               pcie->parf + PARF_SLV_ADDR_SPACE_SIZE);
-
        val = readl(pcie->parf + PARF_PHY_CTRL);
        val &= ~PHY_TEST_PWR_DOWN;
        writel(val, pcie->parf + PARF_PHY_CTRL);
 
-       writel(0, pcie->parf + PARF_DBI_BASE_ADDR);
+       qcom_pcie_configure_dbi_atu_base(pcie);
 
        writel(DEVICE_TYPE_RC, pcie->parf + PARF_DEVICE_TYPE);
        writel(BYPASS | MSTR_AXI_CLK_EN | AHB_CLK_EN,