PCI: mediatek-gen3: Fix translation window size calculation

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: d3bf75b579 ("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>
This commit is contained in:
Jianjun Wang 2023-10-23 16:14:23 +08:00 committed by Bjorn Helgaas
parent 4e11c29873
commit 9ccc1318cf

View 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;
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);
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";
}
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)++;
}
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(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;
else
val = PCIE_ATR_TYPE_MEM | PCIE_ATR_TLP_TYPE_MEM;
writel_relaxed(val, table + PCIE_ATR_TRSL_PARAM_OFFSET);
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;