From e9a5415adb209f86a05e55b850127ada82e070f1 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Fri, 9 Jan 2026 19:34:30 +0800 Subject: [PATCH] PCI: dwc: Use multiple iATU windows for mapping large bridge windows and DMA ranges The DWC driver tries to use a single iATU region for mapping the individual entries of the bridge window and DMA range. If a bridge window/DMA range is larger than the iATU inbound/outbound window size, then the mapping will fail. Hence, avoid this failure by using multiple iATU windows to map the whole region. If the region runs out of iATU windows, then return failure. Signed-off-by: Charles Mirabile Signed-off-by: Samuel Holland Co-developed-by: Randolph Lin Signed-off-by: Randolph Lin [mani: reworded description, minor code cleanup] Signed-off-by: Manivannan Sadhasivam Reviewed-by: Niklas Cassel Reviewed-by: Frank Li Acked-by: Charles Mirabile Link: https://patch.msgid.link/20260109113430.2767264-1-randolph@andestech.com --- .../pci/controller/dwc/pcie-designware-host.c | 74 ++++++++++++++----- 1 file changed, 57 insertions(+), 17 deletions(-) diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index a17833dd6f9d..ab17549af518 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -912,29 +912,50 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp) i = 0; resource_list_for_each_entry(entry, &pp->bridge->windows) { + resource_size_t res_size; + if (resource_type(entry->res) != IORESOURCE_MEM) continue; - if (pci->num_ob_windows <= ++i) + if (pci->num_ob_windows <= i + 1) break; - atu.index = i; atu.type = PCIE_ATU_TYPE_MEM; atu.parent_bus_addr = entry->res->start - pci->parent_bus_offset; atu.pci_addr = entry->res->start - entry->offset; /* Adjust iATU size if MSG TLP region was allocated before */ if (pp->msg_res && pp->msg_res->parent == entry->res) - atu.size = resource_size(entry->res) - + res_size = resource_size(entry->res) - resource_size(pp->msg_res); else - atu.size = resource_size(entry->res); + res_size = resource_size(entry->res); + + while (res_size > 0) { + /* + * Return failure if we run out of windows in the + * middle. Otherwise, we would end up only partially + * mapping a single resource. + */ + if (pci->num_ob_windows <= ++i) { + dev_err(pci->dev, "Exhausted outbound windows for region: %pr\n", + entry->res); + return -ENOMEM; + } - ret = dw_pcie_prog_outbound_atu(pci, &atu); - if (ret) { - dev_err(pci->dev, "Failed to set MEM range %pr\n", - entry->res); - return ret; + atu.index = i; + atu.size = MIN(pci->region_limit + 1, res_size); + + ret = dw_pcie_prog_outbound_atu(pci, &atu); + if (ret) { + dev_err(pci->dev, "Failed to set MEM range %pr\n", + entry->res); + return ret; + } + + atu.parent_bus_addr += atu.size; + atu.pci_addr += atu.size; + res_size -= atu.size; } } @@ -965,20 +986,39 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp) i = 0; resource_list_for_each_entry(entry, &pp->bridge->dma_ranges) { + resource_size_t res_start, res_size, window_size; + if (resource_type(entry->res) != IORESOURCE_MEM) continue; if (pci->num_ib_windows <= i) break; - ret = dw_pcie_prog_inbound_atu(pci, i++, PCIE_ATU_TYPE_MEM, - entry->res->start, - entry->res->start - entry->offset, - resource_size(entry->res)); - if (ret) { - dev_err(pci->dev, "Failed to set DMA range %pr\n", - entry->res); - return ret; + res_size = resource_size(entry->res); + res_start = entry->res->start; + while (res_size > 0) { + /* + * Return failure if we run out of windows in the + * middle. Otherwise, we would end up only partially + * mapping a single resource. + */ + if (pci->num_ib_windows <= i) { + dev_err(pci->dev, "Exhausted inbound windows for region: %pr\n", + entry->res); + return -ENOMEM; + } + + window_size = MIN(pci->region_limit + 1, res_size); + ret = dw_pcie_prog_inbound_atu(pci, i++, PCIE_ATU_TYPE_MEM, res_start, + res_start - entry->offset, window_size); + if (ret) { + dev_err(pci->dev, "Failed to set DMA range %pr\n", + entry->res); + return ret; + } + + res_start += window_size; + res_size -= window_size; } } -- 2.47.3