[PATCH v3 3/5] PCI: endpoint: Add API to delegate EPC DMA channels to the host

From: Koichiro Den

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


Some endpoint functions expose an EPC-integrated DMA controller to the
host. The endpoint function should not need to know the backend-specific
mechanism used to reserve a channel locally and hand its programming
interface to the host.

Add pci_epc_delegate_dma_chan() and pci_epc_reclaim_dma_chan().
Add matching EPC operations. The public API returns an opaque handle, while
the EPC backend keeps any private channel state. This lets generic endpoint
functions delegate channels without depending on a specific DMAengine
provider.

Let reclaim callers tell the backend whether the channel may have been
exposed to host programming and therefore needs to be quiesced before local
ownership is restored. Bind failure paths that only unwind local
reservations can skip that backend quiesce step.

Signed-off-by: Koichiro Den <den@xxxxxxxxxxxxx>
---
Changes in v3:
- New patch. Introduce EPC-level DMA channel delegation to replace
v2's DMAengine filter-callback metadata path for generic endpoint
functions.
- Let reclaim callers skip backend quiesce for delegated channels that
were never exposed to host programming.

drivers/pci/endpoint/pci-epc-core.c | 100 ++++++++++++++++++++++++++++
include/linux/pci-epc.h | 14 ++++
2 files changed, 114 insertions(+)

diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c
index 831b40458dcd..0f71c4633ee5 100644
--- a/drivers/pci/endpoint/pci-epc-core.c
+++ b/drivers/pci/endpoint/pci-epc-core.c
@@ -18,6 +18,13 @@ static const struct class pci_epc_class = {
.name = "pci_epc",
};

+struct pci_epc_dma_chan {
+ struct pci_epc *epc;
+ u8 func_no;
+ u8 vfunc_no;
+ void *data;
+};
+
static void devm_pci_epc_release(struct device *dev, void *res)
{
struct pci_epc *epc = *(struct pci_epc **)res;
@@ -236,6 +243,99 @@ int pci_epc_get_aux_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
}
EXPORT_SYMBOL_GPL(pci_epc_get_aux_resources);

+/**
+ * pci_epc_delegate_dma_chan() - delegate an EPC-owned DMA channel to the host
+ * @epc: EPC device
+ * @func_no: function number
+ * @vfunc_no: virtual function number
+ * @dir: DMA channel direction relative to the endpoint
+ * @hw_ch: hardware channel number
+ * @chan: output delegated-channel handle
+ *
+ * Some EPC backends integrate DMA channels that can be exposed to the host.
+ * This helper asks the backend to reserve the specified channel locally and
+ * place it in a state where the host driver may program it through the exposed
+ * register window.
+ *
+ * Return: 0 on success, -EOPNOTSUPP if the backend does not support DMA channel
+ * delegation, or another -errno on failure.
+ */
+int pci_epc_delegate_dma_chan(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
+ enum pci_epc_aux_dma_dir dir, u16 hw_ch,
+ struct pci_epc_dma_chan **chan)
+{
+ struct pci_epc_dma_chan *epc_chan;
+ void *data = NULL;
+ int ret;
+
+ if (!chan)
+ return -EINVAL;
+ *chan = NULL;
+
+ if (!epc || !epc->ops)
+ return -EINVAL;
+
+ if (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
+ return -EINVAL;
+
+ if (dir != PCI_EPC_AUX_DMA_EP_TO_RC &&
+ dir != PCI_EPC_AUX_DMA_RC_TO_EP)
+ return -EINVAL;
+
+ if (!epc->ops->delegate_dma_chan || !epc->ops->reclaim_dma_chan)
+ return -EOPNOTSUPP;
+
+ epc_chan = kzalloc_obj(*epc_chan, GFP_KERNEL);
+ if (!epc_chan)
+ return -ENOMEM;
+
+ mutex_lock(&epc->lock);
+ ret = epc->ops->delegate_dma_chan(epc, func_no, vfunc_no, dir, hw_ch,
+ &data);
+ mutex_unlock(&epc->lock);
+ if (ret) {
+ kfree(epc_chan);
+ return ret;
+ }
+
+ epc_chan->epc = epc;
+ epc_chan->func_no = func_no;
+ epc_chan->vfunc_no = vfunc_no;
+ epc_chan->data = data;
+ *chan = epc_chan;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pci_epc_delegate_dma_chan);
+
+/**
+ * pci_epc_reclaim_dma_chan() - reclaim a delegated EPC-owned DMA channel
+ * @chan: delegated-channel handle returned by pci_epc_delegate_dma_chan()
+ * @quiesce: quiesce the channel before reclaiming it
+ *
+ * Reclaim a channel previously delegated to the host. Set @quiesce for channels
+ * that may have been exposed to host programming. Bind failure paths that are
+ * unwinding local reservations before exposure may leave it clear.
+ */
+void pci_epc_reclaim_dma_chan(struct pci_epc_dma_chan *chan, bool quiesce)
+{
+ struct pci_epc *epc;
+
+ if (!chan)
+ return;
+
+ epc = chan->epc;
+ if (epc && epc->ops && epc->ops->reclaim_dma_chan) {
+ mutex_lock(&epc->lock);
+ epc->ops->reclaim_dma_chan(epc, chan->func_no, chan->vfunc_no,
+ chan->data, quiesce);
+ mutex_unlock(&epc->lock);
+ }
+
+ kfree(chan);
+}
+EXPORT_SYMBOL_GPL(pci_epc_reclaim_dma_chan);
+
/**
* pci_epc_stop() - stop the PCI link
* @epc: the link of the EPC device that has to be stopped
diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h
index b86d46411065..84b37d7eb124 100644
--- a/include/linux/pci-epc.h
+++ b/include/linux/pci-epc.h
@@ -13,6 +13,7 @@

struct device;
struct pci_epc;
+struct pci_epc_dma_chan;

enum pci_epc_interface_type {
UNKNOWN_INTERFACE = -1,
@@ -175,6 +176,10 @@ struct pci_epc_aux_resource {
* @get_aux_resources_count: ops to get the number of controller-owned
* auxiliary resources
* @get_aux_resources: ops to retrieve controller-owned auxiliary resources
+ * @delegate_dma_chan: ops to delegate a controller-owned DMA channel to the
+ * host
+ * @reclaim_dma_chan: ops to reclaim a previously delegated DMA channel.
+ * The callback quiesces the channel when requested.
* @owner: the module owner containing the ops
*/
struct pci_epc_ops {
@@ -211,6 +216,11 @@ struct pci_epc_ops {
int (*get_aux_resources)(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
struct pci_epc_aux_resource *resources,
int num_resources);
+ int (*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);
+ void (*reclaim_dma_chan)(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
+ void *data, bool quiesce);
struct module *owner;
};

@@ -444,6 +454,10 @@ int pci_epc_get_aux_resources_count(struct pci_epc *epc, u8 func_no,
int pci_epc_get_aux_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
struct pci_epc_aux_resource *resources,
int num_resources);
+int pci_epc_delegate_dma_chan(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
+ enum pci_epc_aux_dma_dir dir, u16 hw_ch,
+ struct pci_epc_dma_chan **chan);
+void pci_epc_reclaim_dma_chan(struct pci_epc_dma_chan *chan, bool quiesce);
enum pci_barno
pci_epc_get_first_free_bar(const struct pci_epc_features *epc_features);
enum pci_barno pci_epc_get_next_free_bar(const struct pci_epc_features
--
2.51.0