PCI: mediatek-gen3: Fix translation window size calculation
authorJianjun Wang <jianjun.wang@mediatek.com>
Mon, 23 Oct 2023 08:14:23 +0000 (16:14 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 25 Jan 2024 23:35:55 +0000 (15:35 -0800)
[ Upstream commit 9ccc1318cf4bd90601f221268e42c3374703d681 ]

When using the fls() helper, the translation table should be a power of
two; otherwise, the resulting value will not be correct.

For example, given fls(0x3e00000) - 1 = 25, the PCIe translation window
size will be set to 0x2000000 instead of the expected size 0x3e00000.

Fix the translation window by splitting the MMIO space into multiple tables
if its size is not a power of two.

[kwilczynski: commit log]
Link: https://lore.kernel.org/linux-pci/20231023081423.18559-1-jianjun.wang@mediatek.com
Fixes: d3bf75b579b9 ("PCI: mediatek-gen3: Add MediaTek Gen3 driver for MT8192")
Signed-off-by: Jianjun Wang <jianjun.wang@mediatek.com>
Signed-off-by: Krzysztof WilczyƄski <kwilczynski@kernel.org>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/pci/controller/pcie-mediatek-gen3.c

index e0e2764..975b302 100644 (file)
@@ -245,35 +245,60 @@ static int mtk_pcie_set_trans_table(struct mtk_gen3_pcie *pcie,
                                    resource_size_t cpu_addr,
                                    resource_size_t pci_addr,
                                    resource_size_t size,
-                                   unsigned long type, int num)
+                                   unsigned long type, int *num)
 {
+       resource_size_t remaining = size;
+       resource_size_t table_size;
+       resource_size_t addr_align;
+       const char *range_type;
        void __iomem *table;
        u32 val;
 
-       if (num >= PCIE_MAX_TRANS_TABLES) {
-               dev_err(pcie->dev, "not enough translate table for addr: %#llx, limited to [%d]\n",
-                       (unsigned long long)cpu_addr, PCIE_MAX_TRANS_TABLES);
-               return -ENODEV;
-       }
+       while (remaining && (*num < PCIE_MAX_TRANS_TABLES)) {
+               /* Table size needs to be a power of 2 */
+               table_size = BIT(fls(remaining) - 1);
+
+               if (cpu_addr > 0) {
+                       addr_align = BIT(ffs(cpu_addr) - 1);
+                       table_size = min(table_size, addr_align);
+               }
+
+               /* Minimum size of translate table is 4KiB */
+               if (table_size < 0x1000) {
+                       dev_err(pcie->dev, "illegal table size %#llx\n",
+                               (unsigned long long)table_size);
+                       return -EINVAL;
+               }
 
-       table = pcie->base + PCIE_TRANS_TABLE_BASE_REG +
-               num * PCIE_ATR_TLB_SET_OFFSET;
+               table = pcie->base + PCIE_TRANS_TABLE_BASE_REG + *num * PCIE_ATR_TLB_SET_OFFSET;
+               writel_relaxed(lower_32_bits(cpu_addr) | PCIE_ATR_SIZE(fls(table_size) - 1), table);
+               writel_relaxed(upper_32_bits(cpu_addr), table + PCIE_ATR_SRC_ADDR_MSB_OFFSET);
+               writel_relaxed(lower_32_bits(pci_addr), table + PCIE_ATR_TRSL_ADDR_LSB_OFFSET);
+               writel_relaxed(upper_32_bits(pci_addr), table + PCIE_ATR_TRSL_ADDR_MSB_OFFSET);
 
-       writel_relaxed(lower_32_bits(cpu_addr) | PCIE_ATR_SIZE(fls(size) - 1),
-                      table);
-       writel_relaxed(upper_32_bits(cpu_addr),
-                      table + PCIE_ATR_SRC_ADDR_MSB_OFFSET);
-       writel_relaxed(lower_32_bits(pci_addr),
-                      table + PCIE_ATR_TRSL_ADDR_LSB_OFFSET);
-       writel_relaxed(upper_32_bits(pci_addr),
-                      table + PCIE_ATR_TRSL_ADDR_MSB_OFFSET);
+               if (type == IORESOURCE_IO) {
+                       val = PCIE_ATR_TYPE_IO | PCIE_ATR_TLP_TYPE_IO;
+                       range_type = "IO";
+               } else {
+                       val = PCIE_ATR_TYPE_MEM | PCIE_ATR_TLP_TYPE_MEM;
+                       range_type = "MEM";
+               }
 
-       if (type == IORESOURCE_IO)
-               val = PCIE_ATR_TYPE_IO | PCIE_ATR_TLP_TYPE_IO;
-       else
-               val = PCIE_ATR_TYPE_MEM | PCIE_ATR_TLP_TYPE_MEM;
+               writel_relaxed(val, table + PCIE_ATR_TRSL_PARAM_OFFSET);
 
-       writel_relaxed(val, table + PCIE_ATR_TRSL_PARAM_OFFSET);
+               dev_dbg(pcie->dev, "set %s trans window[%d]: cpu_addr = %#llx, pci_addr = %#llx, size = %#llx\n",
+                       range_type, *num, (unsigned long long)cpu_addr,
+                       (unsigned long long)pci_addr, (unsigned long long)table_size);
+
+               cpu_addr += table_size;
+               pci_addr += table_size;
+               remaining -= table_size;
+               (*num)++;
+       }
+
+       if (remaining)
+               dev_warn(pcie->dev, "not enough translate table for addr: %#llx, limited to [%d]\n",
+                        (unsigned long long)cpu_addr, PCIE_MAX_TRANS_TABLES);
 
        return 0;
 }
@@ -380,30 +405,20 @@ static int mtk_pcie_startup_port(struct mtk_gen3_pcie *pcie)
                resource_size_t cpu_addr;
                resource_size_t pci_addr;
                resource_size_t size;
-               const char *range_type;
 
-               if (type == IORESOURCE_IO) {
+               if (type == IORESOURCE_IO)
                        cpu_addr = pci_pio_to_address(res->start);
-                       range_type = "IO";
-               } else if (type == IORESOURCE_MEM) {
+               else if (type == IORESOURCE_MEM)
                        cpu_addr = res->start;
-                       range_type = "MEM";
-               } else {
+               else
                        continue;
-               }
 
                pci_addr = res->start - entry->offset;
                size = resource_size(res);
                err = mtk_pcie_set_trans_table(pcie, cpu_addr, pci_addr, size,
-                                              type, table_index);
+                                              type, &table_index);
                if (err)
                        return err;
-
-               dev_dbg(pcie->dev, "set %s trans window[%d]: cpu_addr = %#llx, pci_addr = %#llx, size = %#llx\n",
-                       range_type, table_index, (unsigned long long)cpu_addr,
-                       (unsigned long long)pci_addr, (unsigned long long)size);
-
-               table_index++;
        }
 
        return 0;