Felix Held has submitted this change. ( https://review.coreboot.org/c/coreboot/+/78044?usp=email )
Change subject: soc/mediatek: PCI: Fix translation window ......................................................................
soc/mediatek: PCI: Fix translation window
Dojo fails to boot from NVMe with CONFIG_RESOURCE_ALLOCATION_TOP_DOWN enabled. The root cause is using __fls() will get a smaller value when the size is not a power of 2, for example, __fls(0x3000000) = 25. Hence the PCIe translation window size is set to 0x2000000. Accessing addresses higher than 0x2300000 will fail.
Fix translation window by splitting the MMIO space to multiple tables if its size is not a power of 2.
Resolves: https://ticket.coreboot.org/issues/508.
TEST=Build pass and boot up to kernel successfully via SSD on Dojo board, it can boot with and without the CONFIG_RESOURCE_ALLOCATION_TOP_DOWN option.
BUS=b:298255933 BRANCH=cherry
Change-Id: I42b0f0bf9222d284dee0c29f1a6ed6366d6e6689 Signed-off-by: Jianjun Wang jianjun.wang@mediatek.com Reviewed-on: https://review.coreboot.org/c/coreboot/+/78044 Reviewed-by: Yu-Ping Wu yupingso@google.com Tested-by: build bot (Jenkins) no-reply@coreboot.org --- M src/soc/mediatek/common/pcie.c 1 file changed, 58 insertions(+), 18 deletions(-)
Approvals: Yu-Ping Wu: Looks good to me, approved build bot (Jenkins): Verified
diff --git a/src/soc/mediatek/common/pcie.c b/src/soc/mediatek/common/pcie.c index 8b5b36b..b3903a5 100644 --- a/src/soc/mediatek/common/pcie.c +++ b/src/soc/mediatek/common/pcie.c @@ -127,11 +127,23 @@ return (void *)(base + PCIE_CFG_OFFSET_ADDR); }
-static int mtk_pcie_set_trans_window(struct device *dev, uintptr_t table, +static void mtk_pcie_set_table(uintptr_t table, uint32_t cpu_addr, + uint32_t pci_addr, uint32_t size, uint32_t attr) +{ + write32p(table, cpu_addr | PCIE_ATR_SIZE(__fls(size))); + write32p(table + PCIE_ATR_SRC_ADDR_MSB_OFFSET, 0); + write32p(table + PCIE_ATR_TRSL_ADDR_LSB_OFFSET, pci_addr); + write32p(table + PCIE_ATR_TRSL_ADDR_MSB_OFFSET, 0); + write32p(table + PCIE_ATR_TRSL_PARAM_OFFSET, attr); +} + +static int mtk_pcie_set_trans_window(size_t *count, uintptr_t table_base, const struct mtk_pcie_mmio_res *mmio_res) { const char *range_type; + uintptr_t table; uint32_t table_attr; + uint32_t cpu_addr, pci_addr, remaining, size;
if (!mmio_res) return -1; @@ -148,17 +160,44 @@ return -1; }
- write32p(table, mmio_res->cpu_addr | - PCIE_ATR_SIZE(__fls(mmio_res->size))); - write32p(table + PCIE_ATR_SRC_ADDR_MSB_OFFSET, 0); - write32p(table + PCIE_ATR_TRSL_ADDR_LSB_OFFSET, mmio_res->pci_addr); - write32p(table + PCIE_ATR_TRSL_ADDR_MSB_OFFSET, 0); - write32p(table + PCIE_ATR_TRSL_PARAM_OFFSET, table_attr); + cpu_addr = mmio_res->cpu_addr; + pci_addr = mmio_res->pci_addr; + remaining = mmio_res->size;
- printk(BIOS_INFO, - "%s: set %s trans window: cpu_addr = %#x, pci_addr = %#x, size = %#x\n", - __func__, range_type, mmio_res->cpu_addr, mmio_res->pci_addr, - mmio_res->size); + while (remaining && *count < PCIE_MAX_TRANS_TABLES) { + /* + * The table size needs to be a power of 2. + * In addition, cpu_addr needs to be aligned to the size. + */ + size = BIT(__fls(remaining)); + + if (cpu_addr > 0) + size = MIN(size, BIT(__ffs(cpu_addr))); + + /* Minimum size of translate table is 4KiB */ + if (size < 4 * KiB) { + printk(BIOS_ERR, "%s: table size %#x is less than 4KiB\n", + __func__, size); + return -1; + } + + table = table_base + *count * PCIE_ATR_TLB_SET_OFFSET; + mtk_pcie_set_table(table, cpu_addr, pci_addr, size, table_attr); + + printk(BIOS_INFO, + "%s: set %s trans window: cpu_addr = %#x, pci_addr = %#x, size = %#x\n", + __func__, range_type, cpu_addr, pci_addr, size); + cpu_addr += size; + pci_addr += size; + remaining -= size; + (*count)++; + } + + if (remaining) { + printk(BIOS_ERR, "%s: Not enough translation windows, remaining size: %#x\n", + __func__, remaining); + return -1; + }
return 0; } @@ -194,17 +233,18 @@ { const mtk_soc_config_t *config = config_of(dev); const struct mtk_pcie_config *conf = &config->pcie_config; - uintptr_t table; + uintptr_t table_base = conf->base + PCIE_TRANS_TABLE_BASE_REG; + size_t count = 0;
/* Initialize I/O space constraints. */ - table = conf->base + PCIE_TRANS_TABLE_BASE_REG; - if (mtk_pcie_set_trans_window(dev, table, &conf->mmio_res_io) < 0) - printk(BIOS_ERR, "%s: Failed to set IO window\n", __func__); + if (mtk_pcie_set_trans_window(&count, table_base, &conf->mmio_res_io) < 0) { + printk(BIOS_ERR, "%s: Failed to set IO window, ignore it\n", + __func__); + count = 0; + }
/* Initialize memory resources constraints. */ - table = conf->base + PCIE_TRANS_TABLE_BASE_REG + - PCIE_ATR_TLB_SET_OFFSET; - if (mtk_pcie_set_trans_window(dev, table, &conf->mmio_res_mem) < 0) + if (mtk_pcie_set_trans_window(&count, table_base, &conf->mmio_res_mem) < 0) printk(BIOS_ERR, "%s: Failed to set MEM window\n", __func__);
pci_domain_set_resources(dev);