[PATCH v5 6/6] PCI: dwc: Implement endpoint DMA channel delegation

From: Koichiro Den

Date: Fri Jul 17 2026 - 01:07:49 EST


Implement the EPC DMA channel delegation operations for DesignWare
endpoint controllers. The DWC backend uses the local DesignWare eDMA
provider to reserve the requested hardware channel and returns it as an
opaque EPC DMA channel handle to generic endpoint functions.

Validate the requested direction and hardware channel against the
linked-list channel counts before delegation.

DWC eDMA/HDMA generates DMA requests with a programmable requester
function number. For delegated channels, the host-side dw-edma-pcie
instance bound to the exposed DMA function reserves the channel and
programs its own PCI_FUNC() into the per-channel requester field; the
endpoint-side chip func_no does not participate in that handoff.

Reject VF requests because the RC-programmable DWC eDMA/HDMA register
window is assigned to a PF BAR only.

Reclaim releases the delegated local channel through the DesignWare eDMA
provider, which returns it to endpoint ownership. Propagate the EPC
quiesce request so bind failure paths can release unexposed reservations
without touching DMA engine state.

Signed-off-by: Koichiro Den <den@xxxxxxxxxxxxx>
---
Changes in v5:
- No changes.

.../pci/controller/dwc/pcie-designware-ep.c | 55 +++++++++++++++++++
1 file changed, 55 insertions(+)

diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index ef1e2b6d85cf..13ba3122c9cc 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -1045,6 +1045,59 @@ dw_pcie_ep_get_aux_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
return 0;
}

+static int dw_pcie_ep_delegate_dma_chan(struct pci_epc *epc, u8 func_no,
+ u8 vfunc_no,
+ enum pci_epc_aux_dma_dir dir, u16 hw_ch,
+ void **data)
+{
+ 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;
+ struct dma_chan *chan;
+ int ret;
+ bool write;
+
+ if (!data)
+ return -EINVAL;
+ *data = NULL;
+
+ ret = dw_pcie_ep_check_edma_vfunc(vfunc_no);
+ if (ret)
+ return ret;
+
+ if (!edma->dw)
+ return -ENODEV;
+
+ switch (dir) {
+ case PCI_EPC_AUX_DMA_EP_TO_RC:
+ if (hw_ch >= edma->ll_wr_cnt)
+ return -EINVAL;
+ write = true;
+ break;
+ case PCI_EPC_AUX_DMA_RC_TO_EP:
+ if (hw_ch >= edma->ll_rd_cnt)
+ return -EINVAL;
+ write = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ chan = dw_edma_request_delegated_chan(edma->dev, write, hw_ch);
+ if (!chan)
+ return -EBUSY;
+
+ *data = chan;
+
+ return 0;
+}
+
+static void dw_pcie_ep_reclaim_dma_chan(struct pci_epc *epc, u8 func_no,
+ u8 vfunc_no, void *data, bool quiesce)
+{
+ dw_edma_release_delegated_chan(data, quiesce);
+}
+
static const struct pci_epc_ops epc_ops = {
.write_header = dw_pcie_ep_write_header,
.set_bar = dw_pcie_ep_set_bar,
@@ -1062,6 +1115,8 @@ static const struct pci_epc_ops epc_ops = {
.get_features = dw_pcie_ep_get_features,
.get_aux_resources_count = dw_pcie_ep_get_aux_resources_count,
.get_aux_resources = dw_pcie_ep_get_aux_resources,
+ .delegate_dma_chan = dw_pcie_ep_delegate_dma_chan,
+ .reclaim_dma_chan = dw_pcie_ep_reclaim_dma_chan,
};

/**
--
2.51.0