Commit 9ccc1318 authored by Jianjun Wang's avatar Jianjun Wang Committed by Bjorn Helgaas

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: d3bf75b5 ("PCI: mediatek-gen3: Add MediaTek Gen3 driver for MT8192")
Signed-off-by: default avatarJianjun Wang <jianjun.wang@mediatek.com>
Signed-off-by: default avatarKrzysztof Wilczyński <kwilczynski@kernel.org>
Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
Reviewed-by: default avatarAngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
parent 4e11c298
......@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment