Commit e9a5415a authored by Samuel Holland's avatar Samuel Holland Committed by Manivannan Sadhasivam
Browse files

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: default avatarCharles Mirabile <cmirabil@redhat.com>
Signed-off-by: default avatarSamuel Holland <samuel.holland@sifive.com>
Co-developed-by: default avatarRandolph Lin <randolph@andestech.com>
Signed-off-by: default avatarRandolph Lin <randolph@andestech.com>
[mani: reworded description, minor code cleanup]
Signed-off-by: default avatarManivannan Sadhasivam <mani@kernel.org>
Reviewed-by: default avatarNiklas Cassel <cassel@kernel.org>
Reviewed-by: default avatarFrank Li <Frank.Li@nxp.com>
Acked-by: default avatarCharles Mirabile <cmirabil@redhat.com>
Link: https://patch.msgid.link/20260109113430.2767264-1-randolph@andestech.com
parent 86291f77
Loading
Loading
Loading
Loading
+57 −17
Original line number Diff line number Diff line
@@ -912,23 +912,39 @@ 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;
			}

			atu.index = i;
			atu.size = MIN(pci->region_limit + 1, res_size);

			ret = dw_pcie_prog_outbound_atu(pci, &atu);
			if (ret) {
@@ -936,6 +952,11 @@ static int dw_pcie_iatu_setup(struct dw_pcie_rp *pp)
					entry->res);
				return ret;
			}

			atu.parent_bus_addr += atu.size;
			atu.pci_addr += atu.size;
			res_size -= atu.size;
		}
	}

	if (pp->io_size) {
@@ -965,21 +986,40 @@ 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));
		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;
		}
	}

	if (pci->num_ib_windows <= i)