Skip to content

Commit 9ccc131

Browse files
wang-jianjunbjorn-helgaas
authored andcommitted
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: d3bf75b ("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>
1 parent 4e11c29 commit 9ccc131

File tree

1 file changed

+50
-35
lines changed

1 file changed

+50
-35
lines changed

drivers/pci/controller/pcie-mediatek-gen3.c

Lines changed: 50 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -245,35 +245,60 @@ static int mtk_pcie_set_trans_table(struct mtk_gen3_pcie *pcie,
245245
resource_size_t cpu_addr,
246246
resource_size_t pci_addr,
247247
resource_size_t size,
248-
unsigned long type, int num)
248+
unsigned long type, int *num)
249249
{
250+
resource_size_t remaining = size;
251+
resource_size_t table_size;
252+
resource_size_t addr_align;
253+
const char *range_type;
250254
void __iomem *table;
251255
u32 val;
252256

253-
if (num >= PCIE_MAX_TRANS_TABLES) {
254-
dev_err(pcie->dev, "not enough translate table for addr: %#llx, limited to [%d]\n",
255-
(unsigned long long)cpu_addr, PCIE_MAX_TRANS_TABLES);
256-
return -ENODEV;
257-
}
257+
while (remaining && (*num < PCIE_MAX_TRANS_TABLES)) {
258+
/* Table size needs to be a power of 2 */
259+
table_size = BIT(fls(remaining) - 1);
260+
261+
if (cpu_addr > 0) {
262+
addr_align = BIT(ffs(cpu_addr) - 1);
263+
table_size = min(table_size, addr_align);
264+
}
265+
266+
/* Minimum size of translate table is 4KiB */
267+
if (table_size < 0x1000) {
268+
dev_err(pcie->dev, "illegal table size %#llx\n",
269+
(unsigned long long)table_size);
270+
return -EINVAL;
271+
}
258272

259-
table = pcie->base + PCIE_TRANS_TABLE_BASE_REG +
260-
num * PCIE_ATR_TLB_SET_OFFSET;
273+
table = pcie->base + PCIE_TRANS_TABLE_BASE_REG + *num * PCIE_ATR_TLB_SET_OFFSET;
274+
writel_relaxed(lower_32_bits(cpu_addr) | PCIE_ATR_SIZE(fls(table_size) - 1), table);
275+
writel_relaxed(upper_32_bits(cpu_addr), table + PCIE_ATR_SRC_ADDR_MSB_OFFSET);
276+
writel_relaxed(lower_32_bits(pci_addr), table + PCIE_ATR_TRSL_ADDR_LSB_OFFSET);
277+
writel_relaxed(upper_32_bits(pci_addr), table + PCIE_ATR_TRSL_ADDR_MSB_OFFSET);
261278

262-
writel_relaxed(lower_32_bits(cpu_addr) | PCIE_ATR_SIZE(fls(size) - 1),
263-
table);
264-
writel_relaxed(upper_32_bits(cpu_addr),
265-
table + PCIE_ATR_SRC_ADDR_MSB_OFFSET);
266-
writel_relaxed(lower_32_bits(pci_addr),
267-
table + PCIE_ATR_TRSL_ADDR_LSB_OFFSET);
268-
writel_relaxed(upper_32_bits(pci_addr),
269-
table + PCIE_ATR_TRSL_ADDR_MSB_OFFSET);
279+
if (type == IORESOURCE_IO) {
280+
val = PCIE_ATR_TYPE_IO | PCIE_ATR_TLP_TYPE_IO;
281+
range_type = "IO";
282+
} else {
283+
val = PCIE_ATR_TYPE_MEM | PCIE_ATR_TLP_TYPE_MEM;
284+
range_type = "MEM";
285+
}
270286

271-
if (type == IORESOURCE_IO)
272-
val = PCIE_ATR_TYPE_IO | PCIE_ATR_TLP_TYPE_IO;
273-
else
274-
val = PCIE_ATR_TYPE_MEM | PCIE_ATR_TLP_TYPE_MEM;
287+
writel_relaxed(val, table + PCIE_ATR_TRSL_PARAM_OFFSET);
275288

276-
writel_relaxed(val, table + PCIE_ATR_TRSL_PARAM_OFFSET);
289+
dev_dbg(pcie->dev, "set %s trans window[%d]: cpu_addr = %#llx, pci_addr = %#llx, size = %#llx\n",
290+
range_type, *num, (unsigned long long)cpu_addr,
291+
(unsigned long long)pci_addr, (unsigned long long)table_size);
292+
293+
cpu_addr += table_size;
294+
pci_addr += table_size;
295+
remaining -= table_size;
296+
(*num)++;
297+
}
298+
299+
if (remaining)
300+
dev_warn(pcie->dev, "not enough translate table for addr: %#llx, limited to [%d]\n",
301+
(unsigned long long)cpu_addr, PCIE_MAX_TRANS_TABLES);
277302

278303
return 0;
279304
}
@@ -380,30 +405,20 @@ static int mtk_pcie_startup_port(struct mtk_gen3_pcie *pcie)
380405
resource_size_t cpu_addr;
381406
resource_size_t pci_addr;
382407
resource_size_t size;
383-
const char *range_type;
384408

385-
if (type == IORESOURCE_IO) {
409+
if (type == IORESOURCE_IO)
386410
cpu_addr = pci_pio_to_address(res->start);
387-
range_type = "IO";
388-
} else if (type == IORESOURCE_MEM) {
411+
else if (type == IORESOURCE_MEM)
389412
cpu_addr = res->start;
390-
range_type = "MEM";
391-
} else {
413+
else
392414
continue;
393-
}
394415

395416
pci_addr = res->start - entry->offset;
396417
size = resource_size(res);
397418
err = mtk_pcie_set_trans_table(pcie, cpu_addr, pci_addr, size,
398-
type, table_index);
419+
type, &table_index);
399420
if (err)
400421
return err;
401-
402-
dev_dbg(pcie->dev, "set %s trans window[%d]: cpu_addr = %#llx, pci_addr = %#llx, size = %#llx\n",
403-
range_type, table_index, (unsigned long long)cpu_addr,
404-
(unsigned long long)pci_addr, (unsigned long long)size);
405-
406-
table_index++;
407422
}
408423

409424
return 0;

0 commit comments

Comments
 (0)