[PATCH v3 4/5] PCI: dwc: Expose endpoint DMA resources

From: Koichiro Den

Date: Sat Jun 20 2026 - 13:08:55 EST


Expose the DesignWare endpoint-integrated eDMA register window, logical
DMA channels, and linked-list descriptor memories through the EPC
auxiliary resource API. This lets endpoint functions decide which
channels to publish to the host.

When the DMA register window is already visible through a reserved BAR
region, report its BAR and offset. Otherwise report it as a normal
physical resource so an endpoint function can map it. DMA channel
resources carry hardware channel selectors and refer to linked-list
descriptor memory by ID.

Expose DMA resources only after the local DW eDMA provider has been
registered, and only expose channels whose linked-list descriptor memory
is available. DWC non-LL exposure needs a metadata ABI and host parser
extension, so leave it unsupported for now. Reject VF auxiliary resource
queries because the RC-programmable DWC eDMA/HDMA register window is
assigned to a PF BAR only.

Signed-off-by: Koichiro Den <den@xxxxxxxxxxxxx>
---
Changes in v3:
- Report logical DMA channels separately from descriptor memory
resources (Sashiko).
- Use channel-referenced descriptor memory resource IDs instead of
embedding descriptor metadata in channel resources (Sashiko).
- Stop exporting DMAengine filter callbacks through EPC auxiliary
resource metadata.
- Suppress DMA aux resources when the local DW eDMA provider is
unavailable.
- Keep DWC exposure limited to linked-list channels until non-LL
metadata and host parsing are added.
- Reject VF resource queries because the DWC eDMA/HDMA register window
is PF-only.

.../pci/controller/dwc/pcie-designware-ep.c | 120 +++++++++++++++++-
1 file changed, 115 insertions(+), 5 deletions(-)

diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index 7d2794945704..770cd7efe699 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -858,6 +858,26 @@ dw_pcie_ep_find_bar_rsvd_region(struct dw_pcie_ep *ep,
return NULL;
}

+static int dw_pcie_ep_check_edma_ll_regions(struct dw_edma_region *region,
+ u16 count)
+{
+ unsigned int i;
+
+ for (i = 0; i < count; i++) {
+ if (region[i].sz)
+ continue;
+
+ /*
+ * To-Do: Add non-LL support to the endpoint DMA metadata ABI
+ * and host parser before exposing channels without descriptor
+ * memory.
+ */
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
static int
dw_pcie_ep_get_aux_resources_count(struct pci_epc *epc, u8 func_no,
u8 vfunc_no)
@@ -865,14 +885,35 @@ dw_pcie_ep_get_aux_resources_count(struct pci_epc *epc, u8 func_no,
struct dw_pcie_ep *ep = epc_get_drvdata(epc);
struct dw_pcie *pci = to_dw_pcie_from_ep(ep);
struct dw_edma_chip *edma = &pci->edma;
+ int ret;
+ int count = 1;

- if (!pci->edma_reg_size)
+ if (!pci->edma_reg_size || !edma->dw)
return 0;

- if (edma->db_offset == ~0)
- return 0;
+ /*
+ * DWC eDMA/HDMA register windows exposed to the Root Complex are
+ * assigned to a PF BAR and cannot be assigned to a VF BAR.
+ */
+ if (vfunc_no)
+ return -EOPNOTSUPP;

- return 1;
+ ret = dw_pcie_ep_check_edma_ll_regions(edma->ll_region_wr,
+ edma->ll_wr_cnt);
+ if (ret)
+ return ret;
+
+ ret = dw_pcie_ep_check_edma_ll_regions(edma->ll_region_rd,
+ edma->ll_rd_cnt);
+ if (ret)
+ return ret;
+
+ count += 2 * (edma->ll_wr_cnt + edma->ll_rd_cnt);
+
+ if (edma->db_offset != ~0)
+ count++;
+
+ return count;
}

static int
@@ -888,6 +929,7 @@ dw_pcie_ep_get_aux_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
resource_size_t db_offset = edma->db_offset;
resource_size_t dma_ctrl_bar_offset = 0;
resource_size_t dma_reg_size;
+ unsigned int i;
int count;

count = dw_pcie_ep_get_aux_resources_count(epc, func_no, vfunc_no);
@@ -909,6 +951,74 @@ dw_pcie_ep_get_aux_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
if (rsvd && rsvd->size < dma_reg_size)
dma_reg_size = rsvd->size;

+ count = 0;
+ resources[count++] = (struct pci_epc_aux_resource) {
+ .type = PCI_EPC_AUX_DMA_CTRL_MMIO,
+ .phys_addr = pci->edma_reg_phys,
+ .size = dma_reg_size,
+ .bar = dma_ctrl_bar,
+ .bar_offset = dma_ctrl_bar != NO_BAR ? dma_ctrl_bar_offset : 0,
+ .u.dma_ctrl = {
+ .reg_layout = PCI_EPC_AUX_DMA_REG_LAYOUT_DW_EDMA,
+ .reg_layout_data = edma->mf,
+ .ep_to_rc_ch_cnt = edma->ll_wr_cnt,
+ .rc_to_ep_ch_cnt = edma->ll_rd_cnt,
+ },
+ };
+
+ for (i = 0; i < edma->ll_wr_cnt; i++) {
+ struct dw_edma_region *ll = &edma->ll_region_wr[i];
+ u16 desc_mem_id = i;
+
+ resources[count++] = (struct pci_epc_aux_resource) {
+ .type = PCI_EPC_AUX_DMA_CHAN,
+ .bar = NO_BAR,
+ .u.dma_chan = {
+ .dir = PCI_EPC_AUX_DMA_EP_TO_RC,
+ .hw_ch = i,
+ .desc_mem_id = desc_mem_id,
+ },
+ };
+
+ resources[count++] = (struct pci_epc_aux_resource) {
+ .type = PCI_EPC_AUX_DMA_DESC_MEM,
+ .phys_addr = ll->paddr,
+ .size = ll->sz,
+ .bar = NO_BAR,
+ .u.dma_desc = {
+ .id = desc_mem_id,
+ },
+ };
+ }
+
+ for (i = 0; i < edma->ll_rd_cnt; i++) {
+ struct dw_edma_region *ll = &edma->ll_region_rd[i];
+ u16 desc_mem_id = edma->ll_wr_cnt + i;
+
+ resources[count++] = (struct pci_epc_aux_resource) {
+ .type = PCI_EPC_AUX_DMA_CHAN,
+ .bar = NO_BAR,
+ .u.dma_chan = {
+ .dir = PCI_EPC_AUX_DMA_RC_TO_EP,
+ .hw_ch = i,
+ .desc_mem_id = desc_mem_id,
+ },
+ };
+
+ resources[count++] = (struct pci_epc_aux_resource) {
+ .type = PCI_EPC_AUX_DMA_DESC_MEM,
+ .phys_addr = ll->paddr,
+ .size = ll->sz,
+ .bar = NO_BAR,
+ .u.dma_desc = {
+ .id = desc_mem_id,
+ },
+ };
+ }
+
+ if (db_offset == ~0)
+ return 0;
+
/*
* For interrupt-emulation doorbells, report a standalone resource
* instead of bundling it into the DMA controller MMIO resource.
@@ -917,7 +1027,7 @@ dw_pcie_ep_get_aux_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
sizeof(u32), dma_reg_size))
return -EINVAL;

- resources[0] = (struct pci_epc_aux_resource) {
+ resources[count] = (struct pci_epc_aux_resource) {
.type = PCI_EPC_AUX_DOORBELL_MMIO,
.phys_addr = pci->edma_reg_phys + db_offset,
.size = sizeof(u32),
--
2.51.0