The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* [PATCH v6 0/6] PCI: endpoint: Expose endpoint DMA resources (part 2/3)
@ 2026-08-04  3:38 Koichiro Den
  2026-08-04  3:38 ` [PATCH v6 1/6] PCI: endpoint: Define endpoint DMA BAR metadata format Koichiro Den
                   ` (5 more replies)
  0 siblings, 6 replies; 17+ messages in thread
From: Koichiro Den @ 2026-08-04  3:38 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Jingoo Han, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
	Kishon Vijay Abraham I, Frank Li
  Cc: Marek Vasut, Yoshihiro Shimoda, linux-pci, linux-kernel

Hi,

This is v6, part 2 of three series for PCI endpoint DMA.

The three series are:

  * part 1: dmaengine: dw-edma: Prepare for PCI EP DMA
  * part 2: PCI: endpoint: Expose endpoint DMA resources
  * part 3: PCI: endpoint: Add PCI DMA endpoint function

This series defines the endpoint DMA BAR metadata and auxiliary
resources, adds EPC operations for DMA channel delegation, and teaches
the DesignWare endpoint controller to publish and delegate its
integrated DMA resources.

The metadata lives in a normal endpoint BAR, not in PCI config space.
This keeps discovery independent from controller-specific writable
extended capability storage.

Part 1 v6 has landed in linux-next via dmaengine/next:
https://lore.kernel.org/all/20260721062815.4117887-1-den@valinux.co.jp/

v6 is rebased on next-20260803.

Best regards,
Koichiro
---
Changes in v6:
  - Rebase onto next-20260803, which includes part 1 v6.
  - Use one DMA resource snapshot for bounds checking and array filling.
    (Sashiko)
  - Move DMA resource exposure after delegation support so every advertised
    channel is immediately usable. (Sashiko)

Changes in v5:
  - Keep reporting a standalone doorbell when linked-list DMA resources
    are unavailable. (Sashiko)
  - Serialize irq_mode restoration with channel state updates. (Sashiko)
  - Clarify the provider-defined sharing-group quiesce contract.
  - Use guard()/scoped_guard() for new code.

Changes in v4:
  - Rebased onto the new part 1 series.
  - "dmaengine: dw-edma: Add delegated channel request helpers" moved
    from part 1 into this series, next to its only users; this also
    resolves the undeclared-functions issue reported against v3 5/5.
    (Sashiko)
  - Lift the v3 PF0-only restriction on DMA resource exposure and
    channel delegation: part 1 now programs the per-channel requester
    function number. Delegation to PF1+ therefore requires part 1
    applied on the host side.
  - Move pci_epc_function_is_valid() up to avoid a potential error
    pointer dereference. (Sashiko)

Changes in v3:
  - Decouple logical DMA channel metadata from descriptor memory resources.
    Logical channels now refer to descriptor memory by resource ID instead
    of embedding descriptor metadata in each channel resource. (Sashiko)
  - Replace the v2 DMAengine filter-callback metadata with EPC-level DMA
    channel delegation/reclaim operations, keeping DMAengine provider
    details out of generic EPC resource metadata.
  - Add the DesignWare EPC backend for DMA channel delegation.
  - Limit DesignWare endpoint DMA resource exposure to linked-list channels
    until non-LL metadata and host-side parsing are added.
  - Suppress DesignWare DMA auxiliary resources when the local DW eDMA
    provider is not available.
  - Reject VF DMA resource and delegation requests because the DWC
    eDMA/HDMA register window exposed to the Root Complex is PF-only.

Changes in v2:
  - Follow the part 1/3 v2 channel-claim model: EPC DMA resources now
    carry DMAengine filter information instead of raw DMA channel
    pointers. (Sashiko)
  - Update the DesignWare endpoint resource provider accordingly. (Sashiko)

v5: https://lore.kernel.org/r/20260717050635.2145014-1-den@valinux.co.jp/
v4: https://lore.kernel.org/r/20260710082156.2395844-1-den@valinux.co.jp/
v3: https://lore.kernel.org/r/20260620170438.3756593-1-den@valinux.co.jp/
v2: https://lore.kernel.org/r/20260525063129.3316894-1-den@valinux.co.jp/
v1: https://lore.kernel.org/r/20260521063405.2842644-1-den@valinux.co.jp/


Koichiro Den (6):
  PCI: endpoint: Define endpoint DMA BAR metadata format
  PCI: endpoint: Add DMA auxiliary resource metadata
  PCI: endpoint: Add API to delegate EPC DMA channels to the host
  dmaengine: dw-edma: Add delegated channel request helpers
  PCI: dwc: Implement endpoint DMA channel delegation
  PCI: dwc: Expose endpoint DMA resources

 MAINTAINERS                                   |   1 +
 drivers/dma/dw-edma/dw-edma-core.c            |  91 ++++++++
 .../pci/controller/dwc/pcie-designware-ep.c   | 205 +++++++++++++++++-
 drivers/pci/endpoint/pci-epc-core.c           | 105 +++++++++
 include/linux/dma/edma.h                      |  14 ++
 include/linux/pci-ep-dma.h                    | 170 +++++++++++++++
 include/linux/pci-epc.h                       |  62 ++++++
 7 files changed, 640 insertions(+), 8 deletions(-)
 create mode 100644 include/linux/pci-ep-dma.h

base-commit: 9a4cdc958dd79fc6c3b20b51a10debec6ca09fec
-- 
2.51.0


^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v6 1/6] PCI: endpoint: Define endpoint DMA BAR metadata format
  2026-08-04  3:38 [PATCH v6 0/6] PCI: endpoint: Expose endpoint DMA resources (part 2/3) Koichiro Den
@ 2026-08-04  3:38 ` Koichiro Den
  2026-08-04 15:23   ` Frank Li
  2026-08-04  3:38 ` [PATCH v6 2/6] PCI: endpoint: Add DMA auxiliary resource metadata Koichiro Den
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 17+ messages in thread
From: Koichiro Den @ 2026-08-04  3:38 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Jingoo Han, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
	Kishon Vijay Abraham I, Frank Li
  Cc: Marek Vasut, Yoshihiro Shimoda, linux-pci, linux-kernel

Define the BAR-resident metadata format used by endpoint functions that
expose an endpoint-integrated DMA controller to the host.

A VSEC-based discovery scheme would be a natural fit, and existing
dw-edma-pcie providers such as Synopsys EDDA and AMD (Xilinx) MDB/CPM6
already use VSECs for DMA discovery. However, some endpoint controllers
cannot provide enough writable configuration-space storage for a
complete, controller-defined payload. Keep the extensible metadata in a
BAR instead, where the endpoint function controls the layout and size.

The format describes the DMA register window, exported channel counts,
descriptor windows, optional auxiliary windows, endpoint-local descriptor
and auxiliary DMA addresses, and a ready bit that tells the host when the
described BAR windows are usable. Channel entries keep the auxiliary
window optional so layouts that need a separate data or auxiliary aperture
can describe it without a format bump.

Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v6:
  - No changes.

 MAINTAINERS                |   1 +
 include/linux/pci-ep-dma.h | 170 +++++++++++++++++++++++++++++++++++++
 2 files changed, 171 insertions(+)
 create mode 100644 include/linux/pci-ep-dma.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 03febbc91a06..c503cf11b5a2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -20933,6 +20933,7 @@ F:	Documentation/PCI/endpoint/*
 F:	Documentation/misc-devices/pci-endpoint-test.rst
 F:	drivers/misc/pci_endpoint_test.c
 F:	drivers/pci/endpoint/
+F:	include/linux/pci-ep-dma.h
 F:	tools/testing/selftests/pci_endpoint/
 
 PCI ENHANCED ERROR HANDLING (EEH) FOR POWERPC
diff --git a/include/linux/pci-ep-dma.h b/include/linux/pci-ep-dma.h
new file mode 100644
index 000000000000..73f72455843c
--- /dev/null
+++ b/include/linux/pci-ep-dma.h
@@ -0,0 +1,170 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LINUX_PCI_EP_DMA_H
+#define __LINUX_PCI_EP_DMA_H
+
+#include <linux/bits.h>
+
+/*
+ * BAR metadata format used by PCI endpoint functions that expose an
+ * endpoint-integrated DMA controller to a PCI host.
+ *
+ * Offsets are relative to the beginning of the metadata blob. Multi-byte
+ * fields are little-endian. The blob is normally placed at offset 0 of a
+ * function BAR selected by the endpoint function and discovered by the host
+ * driver using device-specific policy. Other data in the same BAR, such as a
+ * standard MSI-X table or PBA, is outside this metadata format.
+ *
+ *          31                                                              0
+ *          +---------------------------------------------------------------+
+ * +0x000   | metadata magic                                                |
+ *          +---------------------------------------------------------------+
+ *          31                            16 15            8 7              0
+ *          +-------------------------------+---------------+---------------+
+ * +0x004   | metadata length               | reserved      | revision      |
+ *          +-------------------------------+---------------+---------------+
+ *          31 30 29     27 26          19 18          11 10           3 2  0
+ *          +--+--+--------+--------------+--------------+--------------+---+
+ * +0x008   |R |H |reserved| ch entry size| RD count     | WR count     |BAR|
+ *          +--+--+--------+--------------+--------------+--------------+---+
+ * +0x00c   | register window offset[31:0]                                  |
+ *          +---------------------------------------------------------------+
+ * +0x010   | register window offset[63:32]                                 |
+ *          +---------------------------------------------------------------+
+ *          31                            16 15            8 7              0
+ *          +-------------------------------+---------------+---------------+
+ * +0x014   | reserved                      | layout data   | layout        |
+ *          +-------------------------------+---------------+---------------+
+ * +0x018   | register window size                                          |
+ *          +---------------------------------------------------------------+
+ * +0x01c   | write table                                                   |
+ *          | ( channel table entries #0 ~ #N )                             |
+ *          +---------------------------------------------------------------+
+ *          | read table                                                    |
+ *          | ( channel table entries #0 ~ #N )                             |
+ *          +---------------------------------------------------------------+
+ *
+ *          metadata magic: PCI_EP_DMA_METADATA_MAGIC.
+ *          metadata length: byte size of the whole metadata blob. The value
+ *                           must fit in PCI_EP_DMA_METADATA_HDR_LEN_FIELD and
+ *                           in the BAR allocation that contains the metadata.
+ *          revision: metadata format revision.
+ *          R: ready bit. Set only by the endpoint after all fields and BAR
+ *             windows described by this metadata have been programmed and can
+ *             be used by the host. The host must not consume the windows
+ *             before observing this bit.
+ *          H: host-request bit. Set only by the host driver after it has found
+ *             this metadata. The endpoint may use this as the trigger to
+ *             program DMA window BAR subrange mappings, and may clear it while
+ *             revoking R during teardown or reinitialization.
+ *          ch entry size: byte stride between consecutive channel table
+ *                         entries. Revision 1 requires at least
+ *                         PCI_EP_DMA_METADATA_CH_ENTRY_SIZE bytes.
+ *          RD count: number of exposed RC-to-endpoint DMA read channels and
+ *                    read channel-table entries.
+ *          WR count: number of exposed endpoint-to-RC DMA write channels and
+ *                    write channel-table entries.
+ *          BAR: BAR that contains the DMA controller register window.
+ *          register window offset: BAR-local byte offset of the DMA controller
+ *                                  register window in BAR.
+ *          register window size: DMA controller register window size in bytes.
+ *          layout: DMA controller register layout identifier.
+ *          layout data: layout-specific data. For
+ *                       PCI_EP_DMA_METADATA_REG_LAYOUT_DW_EDMA this is the
+ *                       DesignWare eDMA/HDMA map format.
+ *          write table: starts at PCI_EP_DMA_METADATA_HDR_LEN if write channel
+ *                       count is non-zero.
+ *          read table: starts at PCI_EP_DMA_METADATA_HDR_LEN plus the write
+ *                      table size if read channel count is non-zero.
+ *          reserved fields and bits: write zero and ignore on read.
+ *
+ *
+ * Channel table entry:
+ *
+ *          31                 17 16 15 14   12 11 10     8 7                0
+ *          +--------------------+--+--+-------+--+--------+----------------+
+ * +0x000   | reserved           |A |rs|aux BAR|rs|desc BAR|hardware channel|
+ *          +--------------------+--+--+-------+--+--------+----------------+
+ * +0x004   | descriptor window BAR offset[31:0]                            |
+ *          +---------------------------------------------------------------+
+ * +0x008   | descriptor window BAR offset[63:32]                           |
+ *          +---------------------------------------------------------------+
+ * +0x00c   | descriptor window size                                        |
+ *          +---------------------------------------------------------------+
+ * +0x010   | descriptor DMA address[31:0]                                  |
+ *          +---------------------------------------------------------------+
+ * +0x014   | descriptor DMA address[63:32]                                 |
+ *          +---------------------------------------------------------------+
+ * +0x018   | auxiliary window BAR offset[31:0]                             |
+ *          +---------------------------------------------------------------+
+ * +0x01c   | auxiliary window BAR offset[63:32]                            |
+ *          +---------------------------------------------------------------+
+ * +0x020   | auxiliary window size                                         |
+ *          +---------------------------------------------------------------+
+ * +0x024   | auxiliary DMA address[31:0]                                   |
+ *          +---------------------------------------------------------------+
+ * +0x028   | auxiliary DMA address[63:32]                                  |
+ *          +---------------------------------------------------------------+
+ *
+ *          A: auxiliary-window-valid bit. If clear, aux BAR and auxiliary
+ *             window fields are ignored.
+ *          aux BAR: BAR that contains the optional auxiliary window.
+ *          desc BAR: BAR that contains the descriptor window.
+ *          hardware channel: DMA controller's hardware channel number.
+ *                            Revision 1 entries are currently consumed in dense
+ *                            0-based order.
+ *          descriptor window BAR offset: BAR-local byte offset of the
+ *                                        descriptor window in desc BAR.
+ *          descriptor window size: descriptor window size in bytes.
+ *          descriptor DMA address: endpoint-local address used by the DMA
+ *                                  controller to fetch descriptors.
+ *          auxiliary window BAR offset: BAR-local byte offset of the auxiliary
+ *                                       window in aux BAR.
+ *          auxiliary window size: auxiliary window size in bytes.
+ *          auxiliary DMA address: endpoint-local address corresponding to the
+ *                                 auxiliary window.
+ *          reserved fields and bits: write zero and ignore on read.
+ */
+#define PCI_EP_DMA_METADATA_MAGIC		0x4d444550 /* "PEDM" */
+#define PCI_EP_DMA_METADATA_REV			0x1
+
+#define PCI_EP_DMA_METADATA_HDR_LEN		0x1c
+
+#define PCI_EP_DMA_METADATA_HDR			0x04
+#define  PCI_EP_DMA_METADATA_HDR_REV		GENMASK(7, 0)
+#define  PCI_EP_DMA_METADATA_HDR_LEN_FIELD	GENMASK(31, 16)
+
+#define PCI_EP_DMA_METADATA_CTRL		0x08
+#define  PCI_EP_DMA_METADATA_CTRL_REG_BAR	GENMASK(2, 0)
+#define  PCI_EP_DMA_METADATA_CTRL_WR_CH_COUNT	GENMASK(10, 3)
+#define  PCI_EP_DMA_METADATA_CTRL_RD_CH_COUNT	GENMASK(18, 11)
+#define  PCI_EP_DMA_METADATA_CTRL_CH_ENTRY_SIZE	GENMASK(26, 19)
+#define  PCI_EP_DMA_METADATA_CTRL_HOST_REQ	BIT(30)
+#define  PCI_EP_DMA_METADATA_CTRL_READY		BIT(31)
+
+#define PCI_EP_DMA_METADATA_REG_OFF_LO		0x0c
+#define PCI_EP_DMA_METADATA_REG_OFF_HI		0x10
+#define PCI_EP_DMA_METADATA_REG_LAYOUT		0x14
+#define  PCI_EP_DMA_METADATA_REG_LAYOUT_ID	GENMASK(7, 0)
+#define  PCI_EP_DMA_METADATA_REG_LAYOUT_DATA	GENMASK(15, 8)
+#define PCI_EP_DMA_METADATA_REG_SIZE		0x18
+
+#define PCI_EP_DMA_METADATA_REG_LAYOUT_DW_EDMA	0x1
+
+#define PCI_EP_DMA_METADATA_CH_ENTRY_SIZE	0x2c
+#define PCI_EP_DMA_METADATA_CH_CTRL		0x00
+#define  PCI_EP_DMA_METADATA_CH_CTRL_HW_CH	GENMASK(7, 0)
+#define  PCI_EP_DMA_METADATA_CH_CTRL_DESC_BAR	GENMASK(10, 8)
+#define  PCI_EP_DMA_METADATA_CH_CTRL_AUX_BAR	GENMASK(14, 12)
+#define  PCI_EP_DMA_METADATA_CH_CTRL_AUX_VALID	BIT(16)
+#define PCI_EP_DMA_METADATA_CH_DESC_OFF_LO	0x04
+#define PCI_EP_DMA_METADATA_CH_DESC_OFF_HI	0x08
+#define PCI_EP_DMA_METADATA_CH_DESC_SIZE	0x0c
+#define PCI_EP_DMA_METADATA_CH_DESC_ADDR_LO	0x10
+#define PCI_EP_DMA_METADATA_CH_DESC_ADDR_HI	0x14
+#define PCI_EP_DMA_METADATA_CH_AUX_OFF_LO	0x18
+#define PCI_EP_DMA_METADATA_CH_AUX_OFF_HI	0x1c
+#define PCI_EP_DMA_METADATA_CH_AUX_SIZE		0x20
+#define PCI_EP_DMA_METADATA_CH_AUX_ADDR_LO	0x24
+#define PCI_EP_DMA_METADATA_CH_AUX_ADDR_HI	0x28
+
+#endif /* __LINUX_PCI_EP_DMA_H */
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v6 2/6] PCI: endpoint: Add DMA auxiliary resource metadata
  2026-08-04  3:38 [PATCH v6 0/6] PCI: endpoint: Expose endpoint DMA resources (part 2/3) Koichiro Den
  2026-08-04  3:38 ` [PATCH v6 1/6] PCI: endpoint: Define endpoint DMA BAR metadata format Koichiro Den
@ 2026-08-04  3:38 ` Koichiro Den
  2026-08-04 15:26   ` Frank Li
  2026-08-04  3:38 ` [PATCH v6 3/6] PCI: endpoint: Add API to delegate EPC DMA channels to the host Koichiro Den
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 17+ messages in thread
From: Koichiro Den @ 2026-08-04  3:38 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Jingoo Han, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
	Kishon Vijay Abraham I, Frank Li
  Cc: Marek Vasut, Yoshihiro Shimoda, linux-pci, linux-kernel

Extend EPC auxiliary resource metadata so endpoint functions can
discover controller-owned DMA registers, logical DMA channels, and
descriptor memory.

The DMA metadata is intentionally generic at the EPC layer. A backend
reports the register layout, channel counts, logical channel resources,
and descriptor memory resources. Logical channels carry hardware channel
numbers and refer to descriptor memory by ID; reserving or delegating
those channels is handled by separate EPC operations so resource metadata
stays independent of any backend-specific DMA provider. Descriptor memory
is identified separately so one memory resource can be shared by multiple
channels.

For DesignWare controllers, reg_layout_data carries the eDMA/HDMA map
format so a consumer can distinguish legacy, unroll, HDMA compatible,
and HDMA native register layouts without making the EPC API itself
DesignWare-specific.

Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v6:
  - No changes.

 include/linux/pci-epc.h | 46 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 46 insertions(+)

diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h
index f247cf9bcf1a..8c89cb6d6733 100644
--- a/include/linux/pci-epc.h
+++ b/include/linux/pci-epc.h
@@ -65,6 +65,9 @@ struct pci_epc_map {
  * enum pci_epc_aux_resource_type - auxiliary resource type identifiers
  * @PCI_EPC_AUX_DOORBELL_MMIO: Doorbell MMIO, that might be outside the DMA
  *                             controller register window
+ * @PCI_EPC_AUX_DMA_CTRL_MMIO: DMA controller MMIO register window
+ * @PCI_EPC_AUX_DMA_CHAN: Logical DMA channel
+ * @PCI_EPC_AUX_DMA_DESC_MEM: DMA descriptor memory
  *
  * EPC backends may expose auxiliary blocks (e.g. DMA engines) by mapping their
  * register windows and descriptor memories into BAR space. This enum
@@ -72,6 +75,29 @@ struct pci_epc_map {
  */
 enum pci_epc_aux_resource_type {
 	PCI_EPC_AUX_DOORBELL_MMIO,
+	PCI_EPC_AUX_DMA_CTRL_MMIO,
+	PCI_EPC_AUX_DMA_CHAN,
+	PCI_EPC_AUX_DMA_DESC_MEM,
+};
+
+/**
+ * enum pci_epc_aux_dma_reg_layout - DMA controller register layout
+ * @PCI_EPC_AUX_DMA_REG_LAYOUT_UNKNOWN: unknown or uninitialized layout
+ * @PCI_EPC_AUX_DMA_REG_LAYOUT_DW_EDMA: Synopsys DesignWare eDMA/HDMA layout
+ */
+enum pci_epc_aux_dma_reg_layout {
+	PCI_EPC_AUX_DMA_REG_LAYOUT_UNKNOWN = 0,
+	PCI_EPC_AUX_DMA_REG_LAYOUT_DW_EDMA,
+};
+
+/**
+ * enum pci_epc_aux_dma_dir - DMA channel direction relative to the endpoint
+ * @PCI_EPC_AUX_DMA_EP_TO_RC: channel moves data from endpoint to root complex
+ * @PCI_EPC_AUX_DMA_RC_TO_EP: channel moves data from root complex to endpoint
+ */
+enum pci_epc_aux_dma_dir {
+	PCI_EPC_AUX_DMA_EP_TO_RC,
+	PCI_EPC_AUX_DMA_RC_TO_EP,
 };
 
 /**
@@ -99,6 +125,26 @@ struct pci_epc_aux_resource {
 			int irq; /* IRQ number for the doorbell handler */
 			u32 data; /* write value to ring the doorbell */
 		} db_mmio;
+
+		/* PCI_EPC_AUX_DMA_CTRL_MMIO */
+		struct {
+			enum pci_epc_aux_dma_reg_layout reg_layout;
+			u32 reg_layout_data;
+			u16 ep_to_rc_ch_cnt;
+			u16 rc_to_ep_ch_cnt;
+		} dma_ctrl;
+
+		/* PCI_EPC_AUX_DMA_CHAN */
+		struct {
+			enum pci_epc_aux_dma_dir dir;
+			u16 hw_ch;
+			u16 desc_mem_id;
+		} dma_chan;
+
+		/* PCI_EPC_AUX_DMA_DESC_MEM */
+		struct {
+			u16 id;
+		} dma_desc;
 	} u;
 };
 
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v6 3/6] PCI: endpoint: Add API to delegate EPC DMA channels to the host
  2026-08-04  3:38 [PATCH v6 0/6] PCI: endpoint: Expose endpoint DMA resources (part 2/3) Koichiro Den
  2026-08-04  3:38 ` [PATCH v6 1/6] PCI: endpoint: Define endpoint DMA BAR metadata format Koichiro Den
  2026-08-04  3:38 ` [PATCH v6 2/6] PCI: endpoint: Add DMA auxiliary resource metadata Koichiro Den
@ 2026-08-04  3:38 ` Koichiro Den
  2026-08-04 16:06   ` Frank Li
  2026-08-04  3:38 ` [PATCH v6 4/6] dmaengine: dw-edma: Add delegated channel request helpers Koichiro Den
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 17+ messages in thread
From: Koichiro Den @ 2026-08-04  3:38 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Jingoo Han, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
	Kishon Vijay Abraham I, Frank Li
  Cc: Marek Vasut, Yoshihiro Shimoda, linux-pci, linux-kernel

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 hardware exposed to host
programming needs to be quiesced before local ownership is restored.
The quiesce may cover a provider-defined sharing group, so callers must
hold every delegated member and stop peer programming before reclaim.
Bind failure paths that only unwind local reservations can skip quiesce.

Reclaim is best-effort because it runs from teardown paths that cannot be
aborted. The backend always consumes the delegation and reports any
quiesce failure itself. The opaque handle is always freed.

Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v6:
  - No changes.

 drivers/pci/endpoint/pci-epc-core.c | 105 ++++++++++++++++++++++++++++
 include/linux/pci-epc.h             |  16 +++++
 2 files changed, 121 insertions(+)

diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c
index 831b40458dcd..344e54677bfa 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,104 @@ 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 (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
+		return -EINVAL;
+
+	if (!chan)
+		return -EINVAL;
+	*chan = NULL;
+
+	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 affected hardware before reclaiming the channel
+ *
+ * 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.
+ *
+ * Some providers share enable and interrupt controls among channels. The
+ * caller must retain every delegated member of that sharing group and prevent
+ * further peer programming before requesting reclaim.
+ *
+ * Reclaim is best-effort because it runs from teardown paths that cannot be
+ * aborted. The backend always consumes the delegation and reports any quiesce
+ * failure itself.
+ */
+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 8c89cb6d6733..2d15194a126b 100644
--- a/include/linux/pci-epc.h
+++ b/include/linux/pci-epc.h
@@ -11,7 +11,9 @@
 
 #include <linux/pci-epf.h>
 
+struct device;
 struct pci_epc;
+struct pci_epc_dma_chan;
 
 enum pci_epc_interface_type {
 	UNKNOWN_INTERFACE = -1,
@@ -174,6 +176,11 @@ 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 or its provider-defined
+ *		      sharing group when requested.
  * @owner: the module owner containing the ops
  */
 struct pci_epc_ops {
@@ -210,6 +217,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;
 };
 
@@ -443,6 +455,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


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v6 4/6] dmaengine: dw-edma: Add delegated channel request helpers
  2026-08-04  3:38 [PATCH v6 0/6] PCI: endpoint: Expose endpoint DMA resources (part 2/3) Koichiro Den
                   ` (2 preceding siblings ...)
  2026-08-04  3:38 ` [PATCH v6 3/6] PCI: endpoint: Add API to delegate EPC DMA channels to the host Koichiro Den
@ 2026-08-04  3:38 ` Koichiro Den
  2026-08-04 16:23   ` Frank Li
  2026-08-04  3:38 ` [PATCH v6 5/6] PCI: dwc: Implement endpoint DMA channel delegation Koichiro Den
  2026-08-04  3:38 ` [PATCH v6 6/6] PCI: dwc: Expose endpoint DMA resources Koichiro Den
  5 siblings, 1 reply; 17+ messages in thread
From: Koichiro Den @ 2026-08-04  3:38 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Jingoo Han, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
	Kishon Vijay Abraham I, Frank Li
  Cc: Marek Vasut, Yoshihiro Shimoda, linux-pci, linux-kernel

Endpoint functions that expose endpoint-local DesignWare eDMA channels
to a remote host need to reserve exact hardware channels and hand
interrupt ownership to the remote side before publishing the channels.

Add DW eDMA-specific helpers that request a write/read hardware channel
through DMAengine, keep the hardware-channel filter private to dw-edma,
and switch the selected endpoint-local channel to remote interrupt
routing after the channel has been successfully reserved. The matching
release helper can quiesce the channel while it is still remote-routed,
then restores the channel's default routing before releasing the
DMAengine reservation. This lets callers skip quiesce when unwinding a
reservation that was never exposed to host programming.

Release is best-effort because its callers cannot abort teardown. Report
a quiesce failure locally, but always restore the default routing and
release the DMAengine reservation.

Signed-off-by: Koichiro Den <den@valinux.co.jp>
---
Changes in v6:
  - No changes.

 drivers/dma/dw-edma/dw-edma-core.c | 91 ++++++++++++++++++++++++++++++
 include/linux/dma/edma.h           | 14 +++++
 2 files changed, 105 insertions(+)

diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index 1f893dc54c79..4c0d2bba755b 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -1302,6 +1302,97 @@ int dw_edma_remove(struct dw_edma_chip *chip)
 }
 EXPORT_SYMBOL_GPL(dw_edma_remove);
 
+struct dw_edma_delegated_chan_filter_args {
+	struct device *dma_dev;
+	bool write;
+	u16 id;
+};
+
+static bool dw_edma_delegated_chan_filter(struct dma_chan *dchan, void *param)
+{
+	struct dw_edma_delegated_chan_filter_args *filter = param;
+	struct dw_edma_chan *chan;
+
+	if (!filter || dchan->device->dev != filter->dma_dev)
+		return false;
+
+	chan = dchan2dw_edma_chan(dchan);
+
+	return chan->dir == (filter->write ? EDMA_DIR_WRITE : EDMA_DIR_READ) &&
+	       chan->id == filter->id;
+}
+
+static int dw_edma_delegate_chan(struct dma_chan *dchan)
+{
+	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
+	int ret = 0;
+
+	if (!(chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL))
+		return -EINVAL;
+
+	guard(spinlock_irqsave)(&chan->vc.lock);
+
+	if (chan->configured || chan->status != EDMA_ST_IDLE ||
+	    chan->request != EDMA_REQ_NONE)
+		ret = -EBUSY;
+	else
+		chan->irq_mode = DW_EDMA_CH_IRQ_REMOTE;
+
+	return ret;
+}
+
+struct dma_chan *dw_edma_request_delegated_chan(struct device *dma_dev,
+						bool write, u16 id)
+{
+	struct dw_edma_delegated_chan_filter_args filter = {
+		.dma_dev = dma_dev,
+		.write = write,
+		.id = id,
+	};
+	struct dma_chan *dchan;
+	dma_cap_mask_t mask;
+
+	if (!dma_dev)
+		return NULL;
+
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+
+	dchan = dma_request_channel(mask, dw_edma_delegated_chan_filter,
+				    &filter);
+	if (!dchan)
+		return NULL;
+
+	if (dw_edma_delegate_chan(dchan)) {
+		dma_release_channel(dchan);
+		return NULL;
+	}
+
+	return dchan;
+}
+EXPORT_SYMBOL_GPL(dw_edma_request_delegated_chan);
+
+void dw_edma_release_delegated_chan(struct dma_chan *dchan, bool quiesce)
+{
+	struct dw_edma_chan *chan;
+
+	if (!dchan)
+		return;
+
+	chan = dchan2dw_edma_chan(dchan);
+	if (quiesce && dw_edma_core_ch_quiesce(chan))
+		dev_warn(chan->dw->chip->dev,
+			 "failed to quiesce delegated %s channel %u\n",
+			 chan->dir == EDMA_DIR_WRITE ? "write" : "read",
+			 chan->id);
+
+	scoped_guard(spinlock_irqsave, &chan->vc.lock)
+		chan->irq_mode = dw_edma_get_default_irq_mode(chan);
+
+	dma_release_channel(dchan);
+}
+EXPORT_SYMBOL_GPL(dw_edma_release_delegated_chan);
+
 MODULE_LICENSE("GPL v2");
 MODULE_DESCRIPTION("Synopsys DesignWare eDMA controller core driver");
 MODULE_AUTHOR("Gustavo Pimentel <gustavo.pimentel@synopsys.com>");
diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h
index 3c8e2ef9dee0..944469258b8b 100644
--- a/include/linux/dma/edma.h
+++ b/include/linux/dma/edma.h
@@ -153,6 +153,9 @@ struct dw_edma_chip {
 #if IS_REACHABLE(CONFIG_DW_EDMA)
 int dw_edma_probe(struct dw_edma_chip *chip);
 int dw_edma_remove(struct dw_edma_chip *chip);
+struct dma_chan *dw_edma_request_delegated_chan(struct device *dma_dev,
+						bool write, u16 id);
+void dw_edma_release_delegated_chan(struct dma_chan *chan, bool quiesce);
 #else
 static inline int dw_edma_probe(struct dw_edma_chip *chip)
 {
@@ -163,6 +166,17 @@ static inline int dw_edma_remove(struct dw_edma_chip *chip)
 {
 	return 0;
 }
+
+static inline struct dma_chan *
+dw_edma_request_delegated_chan(struct device *dma_dev, bool write, u16 id)
+{
+	return NULL;
+}
+
+static inline void dw_edma_release_delegated_chan(struct dma_chan *chan,
+						  bool quiesce)
+{
+}
 #endif /* CONFIG_DW_EDMA */
 
 #endif /* _DW_EDMA_H */
-- 
2.51.0


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v6 5/6] PCI: dwc: Implement endpoint DMA channel delegation
  2026-08-04  3:38 [PATCH v6 0/6] PCI: endpoint: Expose endpoint DMA resources (part 2/3) Koichiro Den
                   ` (3 preceding siblings ...)
  2026-08-04  3:38 ` [PATCH v6 4/6] dmaengine: dw-edma: Add delegated channel request helpers Koichiro Den
@ 2026-08-04  3:38 ` Koichiro Den
  2026-08-04  3:38 ` [PATCH v6 6/6] PCI: dwc: Expose endpoint DMA resources Koichiro Den
  5 siblings, 0 replies; 17+ messages in thread
From: Koichiro Den @ 2026-08-04  3:38 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Jingoo Han, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
	Kishon Vijay Abraham I, Frank Li
  Cc: Marek Vasut, Yoshihiro Shimoda, linux-pci, linux-kernel

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@valinux.co.jp>
---
Changes in v6:
  - Move the shared VF check here to keep the reordered series buildable.
    (Sashiko)

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

diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index 7d2794945704..8c1ac9e3c517 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -858,6 +858,18 @@ dw_pcie_ep_find_bar_rsvd_region(struct dw_pcie_ep *ep,
 	return NULL;
 }
 
+static int dw_pcie_ep_check_edma_vfunc(u8 vfunc_no)
+{
+	/*
+	 * The DWC endpoint databook says it is not possible to assign the
+	 * DMA/HDMA registers to any Virtual Function.
+	 */
+	if (vfunc_no)
+		return -EOPNOTSUPP;
+
+	return 0;
+}
+
 static int
 dw_pcie_ep_get_aux_resources_count(struct pci_epc *epc, u8 func_no,
 				   u8 vfunc_no)
@@ -933,6 +945,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,
@@ -950,6 +1015,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


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v6 6/6] PCI: dwc: Expose endpoint DMA resources
  2026-08-04  3:38 [PATCH v6 0/6] PCI: endpoint: Expose endpoint DMA resources (part 2/3) Koichiro Den
                   ` (4 preceding siblings ...)
  2026-08-04  3:38 ` [PATCH v6 5/6] PCI: dwc: Implement endpoint DMA channel delegation Koichiro Den
@ 2026-08-04  3:38 ` Koichiro Den
  2026-08-04 16:29   ` Frank Li
  5 siblings, 1 reply; 17+ messages in thread
From: Koichiro Den @ 2026-08-04  3:38 UTC (permalink / raw)
  To: Manivannan Sadhasivam, Jingoo Han, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
	Kishon Vijay Abraham I, Frank Li
  Cc: Marek Vasut, Yoshihiro Shimoda, linux-pci, linux-kernel

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 controller and channel resources only after the local DW eDMA
provider has been registered, and only expose channels whose linked-list
descriptor memory is available. The interrupt-emulation doorbell remains
reported when the local DW eDMA provider is registered and its offset is
valid, even if linked-list resources are unavailable. 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@valinux.co.jp>
---
Changes in v6:
  - Use the same DMA resource state for bounds checking and array filling.
    (Sashiko)
  - Move this patch after the delegation support so every advertised DMA
    channel is immediately usable. (Sashiko)

 .../pci/controller/dwc/pcie-designware-ep.c   | 138 +++++++++++++++++-
 1 file changed, 130 insertions(+), 8 deletions(-)

diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
index 8c1ac9e3c517..440311e2bed9 100644
--- a/drivers/pci/controller/dwc/pcie-designware-ep.c
+++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
@@ -858,6 +858,22 @@ dw_pcie_ep_find_bar_rsvd_region(struct dw_pcie_ep *ep,
 	return NULL;
 }
 
+static bool dw_pcie_ep_has_edma_ll_resources(struct dw_edma_chip *edma,
+					     u16 ll_wr_cnt, u16 ll_rd_cnt)
+{
+	unsigned int i;
+
+	for (i = 0; i < ll_wr_cnt; i++)
+		if (!edma->ll_region_wr[i].sz)
+			return false;
+
+	for (i = 0; i < ll_rd_cnt; i++)
+		if (!edma->ll_region_rd[i].sz)
+			return false;
+
+	return true;
+}
+
 static int dw_pcie_ep_check_edma_vfunc(u8 vfunc_no)
 {
 	/*
@@ -877,14 +893,30 @@ 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;
+	u16 ll_wr_cnt, ll_rd_cnt;
+	int count = 0;
+	int ret;
 
 	if (!pci->edma_reg_size)
 		return 0;
 
-	if (edma->db_offset == ~0)
+	ret = dw_pcie_ep_check_edma_vfunc(vfunc_no);
+	if (ret)
+		return ret;
+
+	if (!edma->dw)
 		return 0;
 
-	return 1;
+	ll_wr_cnt = edma->ll_wr_cnt;
+	ll_rd_cnt = edma->ll_rd_cnt;
+
+	if (dw_pcie_ep_has_edma_ll_resources(edma, ll_wr_cnt, ll_rd_cnt))
+		count += 1 + 2 * (ll_wr_cnt + ll_rd_cnt);
+
+	if (edma->db_offset != ~0)
+		count++;
+
+	return count;
 }
 
 static int
@@ -897,14 +929,34 @@ dw_pcie_ep_get_aux_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
 	const struct pci_epc_bar_rsvd_region *rsvd;
 	struct dw_edma_chip *edma = &pci->edma;
 	enum pci_barno dma_ctrl_bar = NO_BAR;
-	resource_size_t db_offset = edma->db_offset;
+	resource_size_t db_offset;
 	resource_size_t dma_ctrl_bar_offset = 0;
 	resource_size_t dma_reg_size;
-	int count;
+	u16 ll_wr_cnt, ll_rd_cnt;
+	bool has_ll;
+	unsigned int i;
+	int count, ret;
 
-	count = dw_pcie_ep_get_aux_resources_count(epc, func_no, vfunc_no);
-	if (count < 0)
-		return count;
+	if (!pci->edma_reg_size)
+		return 0;
+
+	ret = dw_pcie_ep_check_edma_vfunc(vfunc_no);
+	if (ret)
+		return ret;
+
+	if (!edma->dw)
+		return 0;
+
+	/* Keep the bounds check and fill on the same provider view. */
+	ll_wr_cnt = edma->ll_wr_cnt;
+	ll_rd_cnt = edma->ll_rd_cnt;
+	db_offset = edma->db_offset;
+	has_ll = dw_pcie_ep_has_edma_ll_resources(edma, ll_wr_cnt,
+						  ll_rd_cnt);
+
+	count = db_offset != ~0;
+	if (has_ll)
+		count += 1 + 2 * (ll_wr_cnt + ll_rd_cnt);
 
 	if (num_resources < count)
 		return -ENOSPC;
@@ -921,6 +973,76 @@ 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;
+	if (has_ll) {
+		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_offset,
+			.u.dma_ctrl = {
+				.reg_layout = PCI_EPC_AUX_DMA_REG_LAYOUT_DW_EDMA,
+				.reg_layout_data = edma->mf,
+				.ep_to_rc_ch_cnt = ll_wr_cnt,
+				.rc_to_ep_ch_cnt = ll_rd_cnt,
+			},
+		};
+
+		for (i = 0; i < 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 < ll_rd_cnt; i++) {
+			struct dw_edma_region *ll = &edma->ll_region_rd[i];
+			u16 desc_mem_id = 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.
@@ -929,7 +1051,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


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH v6 1/6] PCI: endpoint: Define endpoint DMA BAR metadata format
  2026-08-04  3:38 ` [PATCH v6 1/6] PCI: endpoint: Define endpoint DMA BAR metadata format Koichiro Den
@ 2026-08-04 15:23   ` Frank Li
  2026-08-05  2:03     ` Koichiro Den
  0 siblings, 1 reply; 17+ messages in thread
From: Frank Li @ 2026-08-04 15:23 UTC (permalink / raw)
  To: Koichiro Den
  Cc: Manivannan Sadhasivam, Jingoo Han, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
	Kishon Vijay Abraham I, Frank Li, Marek Vasut, Yoshihiro Shimoda,
	linux-pci, linux-kernel

On Tue, Aug 04, 2026 at 12:38:50PM +0900, Koichiro Den wrote:
> Define the BAR-resident metadata format used by endpoint functions that
> expose an endpoint-integrated DMA controller to the host.
>
> A VSEC-based discovery scheme would be a natural fit, and existing
> dw-edma-pcie providers such as Synopsys EDDA and AMD (Xilinx) MDB/CPM6
> already use VSECs for DMA discovery. However, some endpoint controllers
> cannot provide enough writable configuration-space storage for a
> complete, controller-defined payload. Keep the extensible metadata in a
> BAR instead, where the endpoint function controls the layout and size.
>
> The format describes the DMA register window, exported channel counts,
> descriptor windows, optional auxiliary windows, endpoint-local descriptor
> and auxiliary DMA addresses, and a ready bit that tells the host when the
> described BAR windows are usable. Channel entries keep the auxiliary
> window optional so layouts that need a separate data or auxiliary aperture
> can describe it without a format bump.
>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
> Changes in v6:
>   - No changes.
>
>  MAINTAINERS                |   1 +
>  include/linux/pci-ep-dma.h | 170 +++++++++++++++++++++++++++++++++++++
>  2 files changed, 171 insertions(+)
>  create mode 100644 include/linux/pci-ep-dma.h
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 03febbc91a06..c503cf11b5a2 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -20933,6 +20933,7 @@ F:	Documentation/PCI/endpoint/*
>  F:	Documentation/misc-devices/pci-endpoint-test.rst
>  F:	drivers/misc/pci_endpoint_test.c
>  F:	drivers/pci/endpoint/
> +F:	include/linux/pci-ep-dma.h
>  F:	tools/testing/selftests/pci_endpoint/
>
>  PCI ENHANCED ERROR HANDLING (EEH) FOR POWERPC
> diff --git a/include/linux/pci-ep-dma.h b/include/linux/pci-ep-dma.h
> new file mode 100644
> index 000000000000..73f72455843c
> --- /dev/null
> +++ b/include/linux/pci-ep-dma.h
> @@ -0,0 +1,170 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef __LINUX_PCI_EP_DMA_H
> +#define __LINUX_PCI_EP_DMA_H
> +
> +#include <linux/bits.h>
> +
> +/*
> + * BAR metadata format used by PCI endpoint functions that expose an
> + * endpoint-integrated DMA controller to a PCI host.
> + *
> + * Offsets are relative to the beginning of the metadata blob. Multi-byte
> + * fields are little-endian. The blob is normally placed at offset 0 of a
> + * function BAR selected by the endpoint function and discovered by the host
> + * driver using device-specific policy. Other data in the same BAR, such as a
> + * standard MSI-X table or PBA, is outside this metadata format.
> + *
> + *          31                                                              0
> + *          +---------------------------------------------------------------+
> + * +0x000   | metadata magic                                                |
> + *          +---------------------------------------------------------------+
> + *          31                            16 15            8 7              0
> + *          +-------------------------------+---------------+---------------+
> + * +0x004   | metadata length               | reserved      | revision      |
> + *          +-------------------------------+---------------+---------------+
> + *          31 30 29     27 26          19 18          11 10           3 2  0
> + *          +--+--+--------+--------------+--------------+--------------+---+
> + * +0x008   |R |H |reserved| ch entry size| RD count     | WR count     |BAR|
> + *          +--+--+--------+--------------+--------------+--------------+---+
> + * +0x00c   | register window offset[31:0]                                  |
> + *          +---------------------------------------------------------------+
> + * +0x010   | register window offset[63:32]                                 |
> + *          +---------------------------------------------------------------+
> + *          31                            16 15            8 7              0
> + *          +-------------------------------+---------------+---------------+
> + * +0x014   | reserved                      | layout data   | layout        |
> + *          +-------------------------------+---------------+---------------+
> + * +0x018   | register window size                                          |
> + *          +---------------------------------------------------------------+
> + * +0x01c   | write table                                                   |
> + *          | ( channel table entries #0 ~ #N )                             |
> + *          +---------------------------------------------------------------+
> + *          | read table                                                    |
> + *          | ( channel table entries #0 ~ #N )                             |
> + *          +---------------------------------------------------------------+
> + *
> + *          metadata magic: PCI_EP_DMA_METADATA_MAGIC.
> + *          metadata length: byte size of the whole metadata blob. The value
> + *                           must fit in PCI_EP_DMA_METADATA_HDR_LEN_FIELD and
> + *                           in the BAR allocation that contains the metadata.
> + *          revision: metadata format revision.
> + *          R: ready bit. Set only by the endpoint after all fields and BAR
> + *             windows described by this metadata have been programmed and can
> + *             be used by the host. The host must not consume the windows
> + *             before observing this bit.
> + *          H: host-request bit. Set only by the host driver after it has found
> + *             this metadata. The endpoint may use this as the trigger to
> + *             program DMA window BAR subrange mappings, and may clear it while
> + *             revoking R during teardown or reinitialization.

Is possible to put all H bit to sperate 32bit space to avoid
Read-modify-write in case need sync with EP side although other bit is
read only by host now.

Frank

> + *          ch entry size: byte stride between consecutive channel table
> + *                         entries. Revision 1 requires at least
> + *                         PCI_EP_DMA_METADATA_CH_ENTRY_SIZE bytes.
> + *          RD count: number of exposed RC-to-endpoint DMA read channels and
> + *                    read channel-table entries.
> + *          WR count: number of exposed endpoint-to-RC DMA write channels and
> + *                    write channel-table entries.
> + *          BAR: BAR that contains the DMA controller register window.
> + *          register window offset: BAR-local byte offset of the DMA controller
> + *                                  register window in BAR.
> + *          register window size: DMA controller register window size in bytes.
> + *          layout: DMA controller register layout identifier.
> + *          layout data: layout-specific data. For
> + *                       PCI_EP_DMA_METADATA_REG_LAYOUT_DW_EDMA this is the
> + *                       DesignWare eDMA/HDMA map format.
> + *          write table: starts at PCI_EP_DMA_METADATA_HDR_LEN if write channel
> + *                       count is non-zero.
> + *          read table: starts at PCI_EP_DMA_METADATA_HDR_LEN plus the write
> + *                      table size if read channel count is non-zero.
> + *          reserved fields and bits: write zero and ignore on read.
> + *
> + *
> + * Channel table entry:
> + *
> + *          31                 17 16 15 14   12 11 10     8 7                0
> + *          +--------------------+--+--+-------+--+--------+----------------+
> + * +0x000   | reserved           |A |rs|aux BAR|rs|desc BAR|hardware channel|
> + *          +--------------------+--+--+-------+--+--------+----------------+
> + * +0x004   | descriptor window BAR offset[31:0]                            |
> + *          +---------------------------------------------------------------+
> + * +0x008   | descriptor window BAR offset[63:32]                           |
> + *          +---------------------------------------------------------------+
> + * +0x00c   | descriptor window size                                        |
> + *          +---------------------------------------------------------------+
> + * +0x010   | descriptor DMA address[31:0]                                  |
> + *          +---------------------------------------------------------------+
> + * +0x014   | descriptor DMA address[63:32]                                 |
> + *          +---------------------------------------------------------------+
> + * +0x018   | auxiliary window BAR offset[31:0]                             |
> + *          +---------------------------------------------------------------+
> + * +0x01c   | auxiliary window BAR offset[63:32]                            |
> + *          +---------------------------------------------------------------+
> + * +0x020   | auxiliary window size                                         |
> + *          +---------------------------------------------------------------+
> + * +0x024   | auxiliary DMA address[31:0]                                   |
> + *          +---------------------------------------------------------------+
> + * +0x028   | auxiliary DMA address[63:32]                                  |
> + *          +---------------------------------------------------------------+
> + *
> + *          A: auxiliary-window-valid bit. If clear, aux BAR and auxiliary
> + *             window fields are ignored.
> + *          aux BAR: BAR that contains the optional auxiliary window.
> + *          desc BAR: BAR that contains the descriptor window.
> + *          hardware channel: DMA controller's hardware channel number.
> + *                            Revision 1 entries are currently consumed in dense
> + *                            0-based order.
> + *          descriptor window BAR offset: BAR-local byte offset of the
> + *                                        descriptor window in desc BAR.
> + *          descriptor window size: descriptor window size in bytes.
> + *          descriptor DMA address: endpoint-local address used by the DMA
> + *                                  controller to fetch descriptors.
> + *          auxiliary window BAR offset: BAR-local byte offset of the auxiliary
> + *                                       window in aux BAR.
> + *          auxiliary window size: auxiliary window size in bytes.
> + *          auxiliary DMA address: endpoint-local address corresponding to the
> + *                                 auxiliary window.
> + *          reserved fields and bits: write zero and ignore on read.
> + */
> +#define PCI_EP_DMA_METADATA_MAGIC		0x4d444550 /* "PEDM" */
> +#define PCI_EP_DMA_METADATA_REV			0x1
> +
> +#define PCI_EP_DMA_METADATA_HDR_LEN		0x1c
> +
> +#define PCI_EP_DMA_METADATA_HDR			0x04
> +#define  PCI_EP_DMA_METADATA_HDR_REV		GENMASK(7, 0)
> +#define  PCI_EP_DMA_METADATA_HDR_LEN_FIELD	GENMASK(31, 16)
> +
> +#define PCI_EP_DMA_METADATA_CTRL		0x08
> +#define  PCI_EP_DMA_METADATA_CTRL_REG_BAR	GENMASK(2, 0)
> +#define  PCI_EP_DMA_METADATA_CTRL_WR_CH_COUNT	GENMASK(10, 3)
> +#define  PCI_EP_DMA_METADATA_CTRL_RD_CH_COUNT	GENMASK(18, 11)
> +#define  PCI_EP_DMA_METADATA_CTRL_CH_ENTRY_SIZE	GENMASK(26, 19)
> +#define  PCI_EP_DMA_METADATA_CTRL_HOST_REQ	BIT(30)
> +#define  PCI_EP_DMA_METADATA_CTRL_READY		BIT(31)
> +
> +#define PCI_EP_DMA_METADATA_REG_OFF_LO		0x0c
> +#define PCI_EP_DMA_METADATA_REG_OFF_HI		0x10
> +#define PCI_EP_DMA_METADATA_REG_LAYOUT		0x14
> +#define  PCI_EP_DMA_METADATA_REG_LAYOUT_ID	GENMASK(7, 0)
> +#define  PCI_EP_DMA_METADATA_REG_LAYOUT_DATA	GENMASK(15, 8)
> +#define PCI_EP_DMA_METADATA_REG_SIZE		0x18
> +
> +#define PCI_EP_DMA_METADATA_REG_LAYOUT_DW_EDMA	0x1
> +
> +#define PCI_EP_DMA_METADATA_CH_ENTRY_SIZE	0x2c
> +#define PCI_EP_DMA_METADATA_CH_CTRL		0x00
> +#define  PCI_EP_DMA_METADATA_CH_CTRL_HW_CH	GENMASK(7, 0)
> +#define  PCI_EP_DMA_METADATA_CH_CTRL_DESC_BAR	GENMASK(10, 8)
> +#define  PCI_EP_DMA_METADATA_CH_CTRL_AUX_BAR	GENMASK(14, 12)
> +#define  PCI_EP_DMA_METADATA_CH_CTRL_AUX_VALID	BIT(16)
> +#define PCI_EP_DMA_METADATA_CH_DESC_OFF_LO	0x04
> +#define PCI_EP_DMA_METADATA_CH_DESC_OFF_HI	0x08
> +#define PCI_EP_DMA_METADATA_CH_DESC_SIZE	0x0c
> +#define PCI_EP_DMA_METADATA_CH_DESC_ADDR_LO	0x10
> +#define PCI_EP_DMA_METADATA_CH_DESC_ADDR_HI	0x14
> +#define PCI_EP_DMA_METADATA_CH_AUX_OFF_LO	0x18
> +#define PCI_EP_DMA_METADATA_CH_AUX_OFF_HI	0x1c
> +#define PCI_EP_DMA_METADATA_CH_AUX_SIZE		0x20
> +#define PCI_EP_DMA_METADATA_CH_AUX_ADDR_LO	0x24
> +#define PCI_EP_DMA_METADATA_CH_AUX_ADDR_HI	0x28
> +
> +#endif /* __LINUX_PCI_EP_DMA_H */
> --
> 2.51.0
>

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v6 2/6] PCI: endpoint: Add DMA auxiliary resource metadata
  2026-08-04  3:38 ` [PATCH v6 2/6] PCI: endpoint: Add DMA auxiliary resource metadata Koichiro Den
@ 2026-08-04 15:26   ` Frank Li
  0 siblings, 0 replies; 17+ messages in thread
From: Frank Li @ 2026-08-04 15:26 UTC (permalink / raw)
  To: Koichiro Den
  Cc: Manivannan Sadhasivam, Jingoo Han, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
	Kishon Vijay Abraham I, Frank Li, Marek Vasut, Yoshihiro Shimoda,
	linux-pci, linux-kernel

On Tue, Aug 04, 2026 at 12:38:51PM +0900, Koichiro Den wrote:
> Extend EPC auxiliary resource metadata so endpoint functions can
> discover controller-owned DMA registers, logical DMA channels, and
> descriptor memory.
>
> The DMA metadata is intentionally generic at the EPC layer. A backend
> reports the register layout, channel counts, logical channel resources,
> and descriptor memory resources. Logical channels carry hardware channel
> numbers and refer to descriptor memory by ID; reserving or delegating
> those channels is handled by separate EPC operations so resource metadata
> stays independent of any backend-specific DMA provider. Descriptor memory
> is identified separately so one memory resource can be shared by multiple
> channels.
>
> For DesignWare controllers, reg_layout_data carries the eDMA/HDMA map
> format so a consumer can distinguish legacy, unroll, HDMA compatible,
> and HDMA native register layouts without making the EPC API itself
> DesignWare-specific.
>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---

Reviewed-by: Frank Li <Frank.Li@nxp.com>

> Changes in v6:
>   - No changes.
>
>  include/linux/pci-epc.h | 46 +++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 46 insertions(+)
>
> diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h
> index f247cf9bcf1a..8c89cb6d6733 100644
> --- a/include/linux/pci-epc.h
> +++ b/include/linux/pci-epc.h
> @@ -65,6 +65,9 @@ struct pci_epc_map {
>   * enum pci_epc_aux_resource_type - auxiliary resource type identifiers
>   * @PCI_EPC_AUX_DOORBELL_MMIO: Doorbell MMIO, that might be outside the DMA
>   *                             controller register window
> + * @PCI_EPC_AUX_DMA_CTRL_MMIO: DMA controller MMIO register window
> + * @PCI_EPC_AUX_DMA_CHAN: Logical DMA channel
> + * @PCI_EPC_AUX_DMA_DESC_MEM: DMA descriptor memory
>   *
>   * EPC backends may expose auxiliary blocks (e.g. DMA engines) by mapping their
>   * register windows and descriptor memories into BAR space. This enum
> @@ -72,6 +75,29 @@ struct pci_epc_map {
>   */
>  enum pci_epc_aux_resource_type {
>  	PCI_EPC_AUX_DOORBELL_MMIO,
> +	PCI_EPC_AUX_DMA_CTRL_MMIO,
> +	PCI_EPC_AUX_DMA_CHAN,
> +	PCI_EPC_AUX_DMA_DESC_MEM,
> +};
> +
> +/**
> + * enum pci_epc_aux_dma_reg_layout - DMA controller register layout
> + * @PCI_EPC_AUX_DMA_REG_LAYOUT_UNKNOWN: unknown or uninitialized layout
> + * @PCI_EPC_AUX_DMA_REG_LAYOUT_DW_EDMA: Synopsys DesignWare eDMA/HDMA layout
> + */
> +enum pci_epc_aux_dma_reg_layout {
> +	PCI_EPC_AUX_DMA_REG_LAYOUT_UNKNOWN = 0,
> +	PCI_EPC_AUX_DMA_REG_LAYOUT_DW_EDMA,
> +};
> +
> +/**
> + * enum pci_epc_aux_dma_dir - DMA channel direction relative to the endpoint
> + * @PCI_EPC_AUX_DMA_EP_TO_RC: channel moves data from endpoint to root complex
> + * @PCI_EPC_AUX_DMA_RC_TO_EP: channel moves data from root complex to endpoint
> + */
> +enum pci_epc_aux_dma_dir {
> +	PCI_EPC_AUX_DMA_EP_TO_RC,
> +	PCI_EPC_AUX_DMA_RC_TO_EP,
>  };
>
>  /**
> @@ -99,6 +125,26 @@ struct pci_epc_aux_resource {
>  			int irq; /* IRQ number for the doorbell handler */
>  			u32 data; /* write value to ring the doorbell */
>  		} db_mmio;
> +
> +		/* PCI_EPC_AUX_DMA_CTRL_MMIO */
> +		struct {
> +			enum pci_epc_aux_dma_reg_layout reg_layout;
> +			u32 reg_layout_data;
> +			u16 ep_to_rc_ch_cnt;
> +			u16 rc_to_ep_ch_cnt;
> +		} dma_ctrl;
> +
> +		/* PCI_EPC_AUX_DMA_CHAN */
> +		struct {
> +			enum pci_epc_aux_dma_dir dir;
> +			u16 hw_ch;
> +			u16 desc_mem_id;
> +		} dma_chan;
> +
> +		/* PCI_EPC_AUX_DMA_DESC_MEM */
> +		struct {
> +			u16 id;
> +		} dma_desc;
>  	} u;
>  };
>
> --
> 2.51.0
>

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v6 3/6] PCI: endpoint: Add API to delegate EPC DMA channels to the host
  2026-08-04  3:38 ` [PATCH v6 3/6] PCI: endpoint: Add API to delegate EPC DMA channels to the host Koichiro Den
@ 2026-08-04 16:06   ` Frank Li
  0 siblings, 0 replies; 17+ messages in thread
From: Frank Li @ 2026-08-04 16:06 UTC (permalink / raw)
  Cc: Manivannan Sadhasivam, Jingoo Han, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
	Kishon Vijay Abraham I, Frank Li, Marek Vasut, Yoshihiro Shimoda,
	linux-pci, linux-kernel

On Tue, Aug 04, 2026 at 12:38:52PM +0900, Koichiro Den wrote:
> 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 hardware exposed to host
> programming needs to be quiesced before local ownership is restored.
> The quiesce may cover a provider-defined sharing group, so callers must
> hold every delegated member and stop peer programming before reclaim.
> Bind failure paths that only unwind local reservations can skip quiesce.
>
> Reclaim is best-effort because it runs from teardown paths that cannot be
> aborted. The backend always consumes the delegation and reports any
> quiesce failure itself. The opaque handle is always freed.
>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---

Reviewed-by: Frank Li <Frank.Li@nxp.com>

> Changes in v6:
>   - No changes.
>
>  drivers/pci/endpoint/pci-epc-core.c | 105 ++++++++++++++++++++++++++++
>  include/linux/pci-epc.h             |  16 +++++
>  2 files changed, 121 insertions(+)
>
> diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c
> index 831b40458dcd..344e54677bfa 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,104 @@ 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 (!pci_epc_function_is_valid(epc, func_no, vfunc_no))
> +		return -EINVAL;
> +
> +	if (!chan)
> +		return -EINVAL;
> +	*chan = NULL;
> +
> +	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 affected hardware before reclaiming the channel
> + *
> + * 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.
> + *
> + * Some providers share enable and interrupt controls among channels. The
> + * caller must retain every delegated member of that sharing group and prevent
> + * further peer programming before requesting reclaim.
> + *
> + * Reclaim is best-effort because it runs from teardown paths that cannot be
> + * aborted. The backend always consumes the delegation and reports any quiesce
> + * failure itself.
> + */
> +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 8c89cb6d6733..2d15194a126b 100644
> --- a/include/linux/pci-epc.h
> +++ b/include/linux/pci-epc.h
> @@ -11,7 +11,9 @@
>
>  #include <linux/pci-epf.h>
>
> +struct device;
>  struct pci_epc;
> +struct pci_epc_dma_chan;
>
>  enum pci_epc_interface_type {
>  	UNKNOWN_INTERFACE = -1,
> @@ -174,6 +176,11 @@ 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 or its provider-defined
> + *		      sharing group when requested.
>   * @owner: the module owner containing the ops
>   */
>  struct pci_epc_ops {
> @@ -210,6 +217,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;
>  };
>
> @@ -443,6 +455,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
>

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v6 4/6] dmaengine: dw-edma: Add delegated channel request helpers
  2026-08-04  3:38 ` [PATCH v6 4/6] dmaengine: dw-edma: Add delegated channel request helpers Koichiro Den
@ 2026-08-04 16:23   ` Frank Li
  2026-08-05  2:05     ` Koichiro Den
  0 siblings, 1 reply; 17+ messages in thread
From: Frank Li @ 2026-08-04 16:23 UTC (permalink / raw)
  To: Koichiro Den
  Cc: Manivannan Sadhasivam, Jingoo Han, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
	Kishon Vijay Abraham I, Frank Li, Marek Vasut, Yoshihiro Shimoda,
	linux-pci, linux-kernel

On Tue, Aug 04, 2026 at 12:38:53PM +0900, Koichiro Den wrote:
> Endpoint functions that expose endpoint-local DesignWare eDMA channels
> to a remote host need to reserve exact hardware channels and hand
> interrupt ownership to the remote side before publishing the channels.
>
> Add DW eDMA-specific helpers that request a write/read hardware channel
> through DMAengine, keep the hardware-channel filter private to dw-edma,
> and switch the selected endpoint-local channel to remote interrupt
> routing after the channel has been successfully reserved. The matching
> release helper can quiesce the channel while it is still remote-routed,
> then restores the channel's default routing before releasing the
> DMAengine reservation. This lets callers skip quiesce when unwinding a
> reservation that was never exposed to host programming.
>
> Release is best-effort because its callers cannot abort teardown. Report
> a quiesce failure locally, but always restore the default routing and
> release the DMAengine reservation.
>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
> Changes in v6:
>   - No changes.
>
>  drivers/dma/dw-edma/dw-edma-core.c | 91 ++++++++++++++++++++++++++++++
>  include/linux/dma/edma.h           | 14 +++++
>  2 files changed, 105 insertions(+)
>
> diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> index 1f893dc54c79..4c0d2bba755b 100644
> --- a/drivers/dma/dw-edma/dw-edma-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-core.c
> @@ -1302,6 +1302,97 @@ int dw_edma_remove(struct dw_edma_chip *chip)
>  }
>  EXPORT_SYMBOL_GPL(dw_edma_remove);
>
> +struct dw_edma_delegated_chan_filter_args {
> +	struct device *dma_dev;
> +	bool write;
> +	u16 id;
> +};
> +
> +static bool dw_edma_delegated_chan_filter(struct dma_chan *dchan, void *param)
> +{
> +	struct dw_edma_delegated_chan_filter_args *filter = param;
> +	struct dw_edma_chan *chan;
> +
> +	if (!filter || dchan->device->dev != filter->dma_dev)
> +		return false;
> +
> +	chan = dchan2dw_edma_chan(dchan);
> +
> +	return chan->dir == (filter->write ? EDMA_DIR_WRITE : EDMA_DIR_READ) &&
> +	       chan->id == filter->id;
> +}
> +
> +static int dw_edma_delegate_chan(struct dma_chan *dchan)
> +{
> +	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
> +	int ret = 0;
> +
> +	if (!(chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL))
> +		return -EINVAL;
> +
> +	guard(spinlock_irqsave)(&chan->vc.lock);
> +
> +	if (chan->configured || chan->status != EDMA_ST_IDLE ||
> +	    chan->request != EDMA_REQ_NONE)
> +		ret = -EBUSY;
> +	else
> +		chan->irq_mode = DW_EDMA_CH_IRQ_REMOTE;
> +
> +	return ret;
> +}
> +
> +struct dma_chan *dw_edma_request_delegated_chan(struct device *dma_dev,
> +						bool write, u16 id)
> +{
> +	struct dw_edma_delegated_chan_filter_args filter = {
> +		.dma_dev = dma_dev,
> +		.write = write,
> +		.id = id,
> +	};
> +	struct dma_chan *dchan;
> +	dma_cap_mask_t mask;
> +
> +	if (!dma_dev)
> +		return NULL;
> +
> +	dma_cap_zero(mask);
> +	dma_cap_set(DMA_SLAVE, mask);
> +
> +	dchan = dma_request_channel(mask, dw_edma_delegated_chan_filter,
> +				    &filter);
> +	if (!dchan)
> +		return NULL;

I forget, why need export delegrated dma channel to dmanegine? If EP
driver doesn't report delegrated channel to dma engine, what's happen?

Frank

> +
> +	if (dw_edma_delegate_chan(dchan)) {
> +		dma_release_channel(dchan);
> +		return NULL;
> +	}
> +
> +	return dchan;
> +}
> +EXPORT_SYMBOL_GPL(dw_edma_request_delegated_chan);
> +
> +void dw_edma_release_delegated_chan(struct dma_chan *dchan, bool quiesce)
> +{
> +	struct dw_edma_chan *chan;
> +
> +	if (!dchan)
> +		return;
> +
> +	chan = dchan2dw_edma_chan(dchan);
> +	if (quiesce && dw_edma_core_ch_quiesce(chan))
> +		dev_warn(chan->dw->chip->dev,
> +			 "failed to quiesce delegated %s channel %u\n",
> +			 chan->dir == EDMA_DIR_WRITE ? "write" : "read",
> +			 chan->id);
> +
> +	scoped_guard(spinlock_irqsave, &chan->vc.lock)
> +		chan->irq_mode = dw_edma_get_default_irq_mode(chan);
> +
> +	dma_release_channel(dchan);
> +}
> +EXPORT_SYMBOL_GPL(dw_edma_release_delegated_chan);
> +
>  MODULE_LICENSE("GPL v2");
>  MODULE_DESCRIPTION("Synopsys DesignWare eDMA controller core driver");
>  MODULE_AUTHOR("Gustavo Pimentel <gustavo.pimentel@synopsys.com>");
> diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h
> index 3c8e2ef9dee0..944469258b8b 100644
> --- a/include/linux/dma/edma.h
> +++ b/include/linux/dma/edma.h
> @@ -153,6 +153,9 @@ struct dw_edma_chip {
>  #if IS_REACHABLE(CONFIG_DW_EDMA)
>  int dw_edma_probe(struct dw_edma_chip *chip);
>  int dw_edma_remove(struct dw_edma_chip *chip);
> +struct dma_chan *dw_edma_request_delegated_chan(struct device *dma_dev,
> +						bool write, u16 id);
> +void dw_edma_release_delegated_chan(struct dma_chan *chan, bool quiesce);
>  #else
>  static inline int dw_edma_probe(struct dw_edma_chip *chip)
>  {
> @@ -163,6 +166,17 @@ static inline int dw_edma_remove(struct dw_edma_chip *chip)
>  {
>  	return 0;
>  }
> +
> +static inline struct dma_chan *
> +dw_edma_request_delegated_chan(struct device *dma_dev, bool write, u16 id)
> +{
> +	return NULL;
> +}
> +
> +static inline void dw_edma_release_delegated_chan(struct dma_chan *chan,
> +						  bool quiesce)
> +{
> +}
>  #endif /* CONFIG_DW_EDMA */
>
>  #endif /* _DW_EDMA_H */
> --
> 2.51.0
>

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v6 6/6] PCI: dwc: Expose endpoint DMA resources
  2026-08-04  3:38 ` [PATCH v6 6/6] PCI: dwc: Expose endpoint DMA resources Koichiro Den
@ 2026-08-04 16:29   ` Frank Li
  0 siblings, 0 replies; 17+ messages in thread
From: Frank Li @ 2026-08-04 16:29 UTC (permalink / raw)
  To: Koichiro Den
  Cc: Manivannan Sadhasivam, Jingoo Han, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
	Kishon Vijay Abraham I, Frank Li, Marek Vasut, Yoshihiro Shimoda,
	linux-pci, linux-kernel

On Tue, Aug 04, 2026 at 12:38:55PM +0900, Koichiro Den wrote:
> 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 controller and channel resources only after the local DW eDMA
> provider has been registered, and only expose channels whose linked-list
> descriptor memory is available. The interrupt-emulation doorbell remains
> reported when the local DW eDMA provider is registered and its offset is
> valid, even if linked-list resources are unavailable. 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@valinux.co.jp>
> ---

Reviewed-by: Frank Li <Frank.Li@nxp.com>

> Changes in v6:
>   - Use the same DMA resource state for bounds checking and array filling.
>     (Sashiko)
>   - Move this patch after the delegation support so every advertised DMA
>     channel is immediately usable. (Sashiko)
>
>  .../pci/controller/dwc/pcie-designware-ep.c   | 138 +++++++++++++++++-
>  1 file changed, 130 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c
> index 8c1ac9e3c517..440311e2bed9 100644
> --- a/drivers/pci/controller/dwc/pcie-designware-ep.c
> +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c
> @@ -858,6 +858,22 @@ dw_pcie_ep_find_bar_rsvd_region(struct dw_pcie_ep *ep,
>  	return NULL;
>  }
>
> +static bool dw_pcie_ep_has_edma_ll_resources(struct dw_edma_chip *edma,
> +					     u16 ll_wr_cnt, u16 ll_rd_cnt)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ll_wr_cnt; i++)
> +		if (!edma->ll_region_wr[i].sz)
> +			return false;
> +
> +	for (i = 0; i < ll_rd_cnt; i++)
> +		if (!edma->ll_region_rd[i].sz)
> +			return false;
> +
> +	return true;
> +}
> +
>  static int dw_pcie_ep_check_edma_vfunc(u8 vfunc_no)
>  {
>  	/*
> @@ -877,14 +893,30 @@ 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;
> +	u16 ll_wr_cnt, ll_rd_cnt;
> +	int count = 0;
> +	int ret;
>
>  	if (!pci->edma_reg_size)
>  		return 0;
>
> -	if (edma->db_offset == ~0)
> +	ret = dw_pcie_ep_check_edma_vfunc(vfunc_no);
> +	if (ret)
> +		return ret;
> +
> +	if (!edma->dw)
>  		return 0;
>
> -	return 1;
> +	ll_wr_cnt = edma->ll_wr_cnt;
> +	ll_rd_cnt = edma->ll_rd_cnt;
> +
> +	if (dw_pcie_ep_has_edma_ll_resources(edma, ll_wr_cnt, ll_rd_cnt))
> +		count += 1 + 2 * (ll_wr_cnt + ll_rd_cnt);
> +
> +	if (edma->db_offset != ~0)
> +		count++;
> +
> +	return count;
>  }
>
>  static int
> @@ -897,14 +929,34 @@ dw_pcie_ep_get_aux_resources(struct pci_epc *epc, u8 func_no, u8 vfunc_no,
>  	const struct pci_epc_bar_rsvd_region *rsvd;
>  	struct dw_edma_chip *edma = &pci->edma;
>  	enum pci_barno dma_ctrl_bar = NO_BAR;
> -	resource_size_t db_offset = edma->db_offset;
> +	resource_size_t db_offset;
>  	resource_size_t dma_ctrl_bar_offset = 0;
>  	resource_size_t dma_reg_size;
> -	int count;
> +	u16 ll_wr_cnt, ll_rd_cnt;
> +	bool has_ll;
> +	unsigned int i;
> +	int count, ret;
>
> -	count = dw_pcie_ep_get_aux_resources_count(epc, func_no, vfunc_no);
> -	if (count < 0)
> -		return count;
> +	if (!pci->edma_reg_size)
> +		return 0;
> +
> +	ret = dw_pcie_ep_check_edma_vfunc(vfunc_no);
> +	if (ret)
> +		return ret;
> +
> +	if (!edma->dw)
> +		return 0;
> +
> +	/* Keep the bounds check and fill on the same provider view. */
> +	ll_wr_cnt = edma->ll_wr_cnt;
> +	ll_rd_cnt = edma->ll_rd_cnt;
> +	db_offset = edma->db_offset;
> +	has_ll = dw_pcie_ep_has_edma_ll_resources(edma, ll_wr_cnt,
> +						  ll_rd_cnt);
> +
> +	count = db_offset != ~0;
> +	if (has_ll)
> +		count += 1 + 2 * (ll_wr_cnt + ll_rd_cnt);
>
>  	if (num_resources < count)
>  		return -ENOSPC;
> @@ -921,6 +973,76 @@ 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;
> +	if (has_ll) {
> +		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_offset,
> +			.u.dma_ctrl = {
> +				.reg_layout = PCI_EPC_AUX_DMA_REG_LAYOUT_DW_EDMA,
> +				.reg_layout_data = edma->mf,
> +				.ep_to_rc_ch_cnt = ll_wr_cnt,
> +				.rc_to_ep_ch_cnt = ll_rd_cnt,
> +			},
> +		};
> +
> +		for (i = 0; i < 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 < ll_rd_cnt; i++) {
> +			struct dw_edma_region *ll = &edma->ll_region_rd[i];
> +			u16 desc_mem_id = 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.
> @@ -929,7 +1051,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
>

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v6 1/6] PCI: endpoint: Define endpoint DMA BAR metadata format
  2026-08-04 15:23   ` Frank Li
@ 2026-08-05  2:03     ` Koichiro Den
  0 siblings, 0 replies; 17+ messages in thread
From: Koichiro Den @ 2026-08-05  2:03 UTC (permalink / raw)
  To: Frank Li
  Cc: Manivannan Sadhasivam, Jingoo Han, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
	Kishon Vijay Abraham I, Frank Li, Marek Vasut, Yoshihiro Shimoda,
	linux-pci, linux-kernel

On Tue, Aug 04, 2026 at 10:23:56AM -0500, Frank Li wrote:
> On Tue, Aug 04, 2026 at 12:38:50PM +0900, Koichiro Den wrote:
> > Define the BAR-resident metadata format used by endpoint functions that
> > expose an endpoint-integrated DMA controller to the host.
> >
> > A VSEC-based discovery scheme would be a natural fit, and existing
> > dw-edma-pcie providers such as Synopsys EDDA and AMD (Xilinx) MDB/CPM6
> > already use VSECs for DMA discovery. However, some endpoint controllers
> > cannot provide enough writable configuration-space storage for a
> > complete, controller-defined payload. Keep the extensible metadata in a
> > BAR instead, where the endpoint function controls the layout and size.
> >
> > The format describes the DMA register window, exported channel counts,
> > descriptor windows, optional auxiliary windows, endpoint-local descriptor
> > and auxiliary DMA addresses, and a ready bit that tells the host when the
> > described BAR windows are usable. Channel entries keep the auxiliary
> > window optional so layouts that need a separate data or auxiliary aperture
> > can describe it without a format bump.
> >
> > Signed-off-by: Koichiro Den <den@valinux.co.jp>
> > ---
> > Changes in v6:
> >   - No changes.
> >
> >  MAINTAINERS                |   1 +
> >  include/linux/pci-ep-dma.h | 170 +++++++++++++++++++++++++++++++++++++
> >  2 files changed, 171 insertions(+)
> >  create mode 100644 include/linux/pci-ep-dma.h
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 03febbc91a06..c503cf11b5a2 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -20933,6 +20933,7 @@ F:	Documentation/PCI/endpoint/*
> >  F:	Documentation/misc-devices/pci-endpoint-test.rst
> >  F:	drivers/misc/pci_endpoint_test.c
> >  F:	drivers/pci/endpoint/
> > +F:	include/linux/pci-ep-dma.h
> >  F:	tools/testing/selftests/pci_endpoint/
> >
> >  PCI ENHANCED ERROR HANDLING (EEH) FOR POWERPC
> > diff --git a/include/linux/pci-ep-dma.h b/include/linux/pci-ep-dma.h
> > new file mode 100644
> > index 000000000000..73f72455843c
> > --- /dev/null
> > +++ b/include/linux/pci-ep-dma.h
> > @@ -0,0 +1,170 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +#ifndef __LINUX_PCI_EP_DMA_H
> > +#define __LINUX_PCI_EP_DMA_H
> > +
> > +#include <linux/bits.h>
> > +
> > +/*
> > + * BAR metadata format used by PCI endpoint functions that expose an
> > + * endpoint-integrated DMA controller to a PCI host.
> > + *
> > + * Offsets are relative to the beginning of the metadata blob. Multi-byte
> > + * fields are little-endian. The blob is normally placed at offset 0 of a
> > + * function BAR selected by the endpoint function and discovered by the host
> > + * driver using device-specific policy. Other data in the same BAR, such as a
> > + * standard MSI-X table or PBA, is outside this metadata format.
> > + *
> > + *          31                                                              0
> > + *          +---------------------------------------------------------------+
> > + * +0x000   | metadata magic                                                |
> > + *          +---------------------------------------------------------------+
> > + *          31                            16 15            8 7              0
> > + *          +-------------------------------+---------------+---------------+
> > + * +0x004   | metadata length               | reserved      | revision      |
> > + *          +-------------------------------+---------------+---------------+
> > + *          31 30 29     27 26          19 18          11 10           3 2  0
> > + *          +--+--+--------+--------------+--------------+--------------+---+
> > + * +0x008   |R |H |reserved| ch entry size| RD count     | WR count     |BAR|
> > + *          +--+--+--------+--------------+--------------+--------------+---+
> > + * +0x00c   | register window offset[31:0]                                  |
> > + *          +---------------------------------------------------------------+
> > + * +0x010   | register window offset[63:32]                                 |
> > + *          +---------------------------------------------------------------+
> > + *          31                            16 15            8 7              0
> > + *          +-------------------------------+---------------+---------------+
> > + * +0x014   | reserved                      | layout data   | layout        |
> > + *          +-------------------------------+---------------+---------------+
> > + * +0x018   | register window size                                          |
> > + *          +---------------------------------------------------------------+
> > + * +0x01c   | write table                                                   |
> > + *          | ( channel table entries #0 ~ #N )                             |
> > + *          +---------------------------------------------------------------+
> > + *          | read table                                                    |
> > + *          | ( channel table entries #0 ~ #N )                             |
> > + *          +---------------------------------------------------------------+
> > + *
> > + *          metadata magic: PCI_EP_DMA_METADATA_MAGIC.
> > + *          metadata length: byte size of the whole metadata blob. The value
> > + *                           must fit in PCI_EP_DMA_METADATA_HDR_LEN_FIELD and
> > + *                           in the BAR allocation that contains the metadata.
> > + *          revision: metadata format revision.
> > + *          R: ready bit. Set only by the endpoint after all fields and BAR
> > + *             windows described by this metadata have been programmed and can
> > + *             be used by the host. The host must not consume the windows
> > + *             before observing this bit.
> > + *          H: host-request bit. Set only by the host driver after it has found
> > + *             this metadata. The endpoint may use this as the trigger to
> > + *             program DMA window BAR subrange mappings, and may clear it while
> > + *             revoking R during teardown or reinitialization.
> 
> Is possible to put all H bit to sperate 32bit space to avoid
> Read-modify-write in case need sync with EP side although other bit is
> read only by host now.

That sounds reasonable. I'll respin it with the H bit in a spearate 32-bit word
and increase PCI_EP_DMA_METADATA_HDR_LEN to 0x20.

Thanks for the review,
Koichiro

> 
> Frank
> 
> > + *          ch entry size: byte stride between consecutive channel table
> > + *                         entries. Revision 1 requires at least
> > + *                         PCI_EP_DMA_METADATA_CH_ENTRY_SIZE bytes.
> > + *          RD count: number of exposed RC-to-endpoint DMA read channels and
> > + *                    read channel-table entries.
> > + *          WR count: number of exposed endpoint-to-RC DMA write channels and
> > + *                    write channel-table entries.
> > + *          BAR: BAR that contains the DMA controller register window.
> > + *          register window offset: BAR-local byte offset of the DMA controller
> > + *                                  register window in BAR.
> > + *          register window size: DMA controller register window size in bytes.
> > + *          layout: DMA controller register layout identifier.
> > + *          layout data: layout-specific data. For
> > + *                       PCI_EP_DMA_METADATA_REG_LAYOUT_DW_EDMA this is the
> > + *                       DesignWare eDMA/HDMA map format.
> > + *          write table: starts at PCI_EP_DMA_METADATA_HDR_LEN if write channel
> > + *                       count is non-zero.
> > + *          read table: starts at PCI_EP_DMA_METADATA_HDR_LEN plus the write
> > + *                      table size if read channel count is non-zero.
> > + *          reserved fields and bits: write zero and ignore on read.
> > + *
> > + *
> > + * Channel table entry:
> > + *
> > + *          31                 17 16 15 14   12 11 10     8 7                0
> > + *          +--------------------+--+--+-------+--+--------+----------------+
> > + * +0x000   | reserved           |A |rs|aux BAR|rs|desc BAR|hardware channel|
> > + *          +--------------------+--+--+-------+--+--------+----------------+
> > + * +0x004   | descriptor window BAR offset[31:0]                            |
> > + *          +---------------------------------------------------------------+
> > + * +0x008   | descriptor window BAR offset[63:32]                           |
> > + *          +---------------------------------------------------------------+
> > + * +0x00c   | descriptor window size                                        |
> > + *          +---------------------------------------------------------------+
> > + * +0x010   | descriptor DMA address[31:0]                                  |
> > + *          +---------------------------------------------------------------+
> > + * +0x014   | descriptor DMA address[63:32]                                 |
> > + *          +---------------------------------------------------------------+
> > + * +0x018   | auxiliary window BAR offset[31:0]                             |
> > + *          +---------------------------------------------------------------+
> > + * +0x01c   | auxiliary window BAR offset[63:32]                            |
> > + *          +---------------------------------------------------------------+
> > + * +0x020   | auxiliary window size                                         |
> > + *          +---------------------------------------------------------------+
> > + * +0x024   | auxiliary DMA address[31:0]                                   |
> > + *          +---------------------------------------------------------------+
> > + * +0x028   | auxiliary DMA address[63:32]                                  |
> > + *          +---------------------------------------------------------------+
> > + *
> > + *          A: auxiliary-window-valid bit. If clear, aux BAR and auxiliary
> > + *             window fields are ignored.
> > + *          aux BAR: BAR that contains the optional auxiliary window.
> > + *          desc BAR: BAR that contains the descriptor window.
> > + *          hardware channel: DMA controller's hardware channel number.
> > + *                            Revision 1 entries are currently consumed in dense
> > + *                            0-based order.
> > + *          descriptor window BAR offset: BAR-local byte offset of the
> > + *                                        descriptor window in desc BAR.
> > + *          descriptor window size: descriptor window size in bytes.
> > + *          descriptor DMA address: endpoint-local address used by the DMA
> > + *                                  controller to fetch descriptors.
> > + *          auxiliary window BAR offset: BAR-local byte offset of the auxiliary
> > + *                                       window in aux BAR.
> > + *          auxiliary window size: auxiliary window size in bytes.
> > + *          auxiliary DMA address: endpoint-local address corresponding to the
> > + *                                 auxiliary window.
> > + *          reserved fields and bits: write zero and ignore on read.
> > + */
> > +#define PCI_EP_DMA_METADATA_MAGIC		0x4d444550 /* "PEDM" */
> > +#define PCI_EP_DMA_METADATA_REV			0x1
> > +
> > +#define PCI_EP_DMA_METADATA_HDR_LEN		0x1c
> > +
> > +#define PCI_EP_DMA_METADATA_HDR			0x04
> > +#define  PCI_EP_DMA_METADATA_HDR_REV		GENMASK(7, 0)
> > +#define  PCI_EP_DMA_METADATA_HDR_LEN_FIELD	GENMASK(31, 16)
> > +
> > +#define PCI_EP_DMA_METADATA_CTRL		0x08
> > +#define  PCI_EP_DMA_METADATA_CTRL_REG_BAR	GENMASK(2, 0)
> > +#define  PCI_EP_DMA_METADATA_CTRL_WR_CH_COUNT	GENMASK(10, 3)
> > +#define  PCI_EP_DMA_METADATA_CTRL_RD_CH_COUNT	GENMASK(18, 11)
> > +#define  PCI_EP_DMA_METADATA_CTRL_CH_ENTRY_SIZE	GENMASK(26, 19)
> > +#define  PCI_EP_DMA_METADATA_CTRL_HOST_REQ	BIT(30)
> > +#define  PCI_EP_DMA_METADATA_CTRL_READY		BIT(31)
> > +
> > +#define PCI_EP_DMA_METADATA_REG_OFF_LO		0x0c
> > +#define PCI_EP_DMA_METADATA_REG_OFF_HI		0x10
> > +#define PCI_EP_DMA_METADATA_REG_LAYOUT		0x14
> > +#define  PCI_EP_DMA_METADATA_REG_LAYOUT_ID	GENMASK(7, 0)
> > +#define  PCI_EP_DMA_METADATA_REG_LAYOUT_DATA	GENMASK(15, 8)
> > +#define PCI_EP_DMA_METADATA_REG_SIZE		0x18
> > +
> > +#define PCI_EP_DMA_METADATA_REG_LAYOUT_DW_EDMA	0x1
> > +
> > +#define PCI_EP_DMA_METADATA_CH_ENTRY_SIZE	0x2c
> > +#define PCI_EP_DMA_METADATA_CH_CTRL		0x00
> > +#define  PCI_EP_DMA_METADATA_CH_CTRL_HW_CH	GENMASK(7, 0)
> > +#define  PCI_EP_DMA_METADATA_CH_CTRL_DESC_BAR	GENMASK(10, 8)
> > +#define  PCI_EP_DMA_METADATA_CH_CTRL_AUX_BAR	GENMASK(14, 12)
> > +#define  PCI_EP_DMA_METADATA_CH_CTRL_AUX_VALID	BIT(16)
> > +#define PCI_EP_DMA_METADATA_CH_DESC_OFF_LO	0x04
> > +#define PCI_EP_DMA_METADATA_CH_DESC_OFF_HI	0x08
> > +#define PCI_EP_DMA_METADATA_CH_DESC_SIZE	0x0c
> > +#define PCI_EP_DMA_METADATA_CH_DESC_ADDR_LO	0x10
> > +#define PCI_EP_DMA_METADATA_CH_DESC_ADDR_HI	0x14
> > +#define PCI_EP_DMA_METADATA_CH_AUX_OFF_LO	0x18
> > +#define PCI_EP_DMA_METADATA_CH_AUX_OFF_HI	0x1c
> > +#define PCI_EP_DMA_METADATA_CH_AUX_SIZE		0x20
> > +#define PCI_EP_DMA_METADATA_CH_AUX_ADDR_LO	0x24
> > +#define PCI_EP_DMA_METADATA_CH_AUX_ADDR_HI	0x28
> > +
> > +#endif /* __LINUX_PCI_EP_DMA_H */
> > --
> > 2.51.0
> >

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v6 4/6] dmaengine: dw-edma: Add delegated channel request helpers
  2026-08-04 16:23   ` Frank Li
@ 2026-08-05  2:05     ` Koichiro Den
  2026-08-05 18:47       ` Frank Li
  0 siblings, 1 reply; 17+ messages in thread
From: Koichiro Den @ 2026-08-05  2:05 UTC (permalink / raw)
  To: Frank Li
  Cc: Manivannan Sadhasivam, Jingoo Han, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
	Kishon Vijay Abraham I, Frank Li, Marek Vasut, Yoshihiro Shimoda,
	linux-pci, linux-kernel

On Tue, Aug 04, 2026 at 11:23:47AM -0500, Frank Li wrote:
> On Tue, Aug 04, 2026 at 12:38:53PM +0900, Koichiro Den wrote:
> > Endpoint functions that expose endpoint-local DesignWare eDMA channels
> > to a remote host need to reserve exact hardware channels and hand
> > interrupt ownership to the remote side before publishing the channels.
> >
> > Add DW eDMA-specific helpers that request a write/read hardware channel
> > through DMAengine, keep the hardware-channel filter private to dw-edma,
> > and switch the selected endpoint-local channel to remote interrupt
> > routing after the channel has been successfully reserved. The matching
> > release helper can quiesce the channel while it is still remote-routed,
> > then restores the channel's default routing before releasing the
> > DMAengine reservation. This lets callers skip quiesce when unwinding a
> > reservation that was never exposed to host programming.
> >
> > Release is best-effort because its callers cannot abort teardown. Report
> > a quiesce failure locally, but always restore the default routing and
> > release the DMAengine reservation.
> >
> > Signed-off-by: Koichiro Den <den@valinux.co.jp>
> > ---
> > Changes in v6:
> >   - No changes.
> >
> >  drivers/dma/dw-edma/dw-edma-core.c | 91 ++++++++++++++++++++++++++++++
> >  include/linux/dma/edma.h           | 14 +++++
> >  2 files changed, 105 insertions(+)
> >
> > diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> > index 1f893dc54c79..4c0d2bba755b 100644
> > --- a/drivers/dma/dw-edma/dw-edma-core.c
> > +++ b/drivers/dma/dw-edma/dw-edma-core.c
> > @@ -1302,6 +1302,97 @@ int dw_edma_remove(struct dw_edma_chip *chip)
> >  }
> >  EXPORT_SYMBOL_GPL(dw_edma_remove);
> >
> > +struct dw_edma_delegated_chan_filter_args {
> > +	struct device *dma_dev;
> > +	bool write;
> > +	u16 id;
> > +};
> > +
> > +static bool dw_edma_delegated_chan_filter(struct dma_chan *dchan, void *param)
> > +{
> > +	struct dw_edma_delegated_chan_filter_args *filter = param;
> > +	struct dw_edma_chan *chan;
> > +
> > +	if (!filter || dchan->device->dev != filter->dma_dev)
> > +		return false;
> > +
> > +	chan = dchan2dw_edma_chan(dchan);
> > +
> > +	return chan->dir == (filter->write ? EDMA_DIR_WRITE : EDMA_DIR_READ) &&
> > +	       chan->id == filter->id;
> > +}
> > +
> > +static int dw_edma_delegate_chan(struct dma_chan *dchan)
> > +{
> > +	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
> > +	int ret = 0;
> > +
> > +	if (!(chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL))
> > +		return -EINVAL;
> > +
> > +	guard(spinlock_irqsave)(&chan->vc.lock);
> > +
> > +	if (chan->configured || chan->status != EDMA_ST_IDLE ||
> > +	    chan->request != EDMA_REQ_NONE)
> > +		ret = -EBUSY;
> > +	else
> > +		chan->irq_mode = DW_EDMA_CH_IRQ_REMOTE;
> > +
> > +	return ret;
> > +}
> > +
> > +struct dma_chan *dw_edma_request_delegated_chan(struct device *dma_dev,
> > +						bool write, u16 id)
> > +{
> > +	struct dw_edma_delegated_chan_filter_args filter = {
> > +		.dma_dev = dma_dev,
> > +		.write = write,
> > +		.id = id,
> > +	};
> > +	struct dma_chan *dchan;
> > +	dma_cap_mask_t mask;
> > +
> > +	if (!dma_dev)
> > +		return NULL;
> > +
> > +	dma_cap_zero(mask);
> > +	dma_cap_set(DMA_SLAVE, mask);
> > +
> > +	dchan = dma_request_channel(mask, dw_edma_delegated_chan_filter,
> > +				    &filter);
> > +	if (!dchan)
> > +		return NULL;
> 
> I forget, why need export delegrated dma channel to dmanegine? If EP
> driver doesn't report delegrated channel to dma engine, what's happen?

This just reserves the exact channel against any dmaengine clients before
handing its programming ownership to the host. Without it, a local client
could request the same channel too.

Best regards,
Koichiro

> 
> Frank
> 
> > +
> > +	if (dw_edma_delegate_chan(dchan)) {
> > +		dma_release_channel(dchan);
> > +		return NULL;
> > +	}
> > +
> > +	return dchan;
> > +}
> > +EXPORT_SYMBOL_GPL(dw_edma_request_delegated_chan);
> > +
> > +void dw_edma_release_delegated_chan(struct dma_chan *dchan, bool quiesce)
> > +{
> > +	struct dw_edma_chan *chan;
> > +
> > +	if (!dchan)
> > +		return;
> > +
> > +	chan = dchan2dw_edma_chan(dchan);
> > +	if (quiesce && dw_edma_core_ch_quiesce(chan))
> > +		dev_warn(chan->dw->chip->dev,
> > +			 "failed to quiesce delegated %s channel %u\n",
> > +			 chan->dir == EDMA_DIR_WRITE ? "write" : "read",
> > +			 chan->id);
> > +
> > +	scoped_guard(spinlock_irqsave, &chan->vc.lock)
> > +		chan->irq_mode = dw_edma_get_default_irq_mode(chan);
> > +
> > +	dma_release_channel(dchan);
> > +}
> > +EXPORT_SYMBOL_GPL(dw_edma_release_delegated_chan);
> > +
> >  MODULE_LICENSE("GPL v2");
> >  MODULE_DESCRIPTION("Synopsys DesignWare eDMA controller core driver");
> >  MODULE_AUTHOR("Gustavo Pimentel <gustavo.pimentel@synopsys.com>");
> > diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h
> > index 3c8e2ef9dee0..944469258b8b 100644
> > --- a/include/linux/dma/edma.h
> > +++ b/include/linux/dma/edma.h
> > @@ -153,6 +153,9 @@ struct dw_edma_chip {
> >  #if IS_REACHABLE(CONFIG_DW_EDMA)
> >  int dw_edma_probe(struct dw_edma_chip *chip);
> >  int dw_edma_remove(struct dw_edma_chip *chip);
> > +struct dma_chan *dw_edma_request_delegated_chan(struct device *dma_dev,
> > +						bool write, u16 id);
> > +void dw_edma_release_delegated_chan(struct dma_chan *chan, bool quiesce);
> >  #else
> >  static inline int dw_edma_probe(struct dw_edma_chip *chip)
> >  {
> > @@ -163,6 +166,17 @@ static inline int dw_edma_remove(struct dw_edma_chip *chip)
> >  {
> >  	return 0;
> >  }
> > +
> > +static inline struct dma_chan *
> > +dw_edma_request_delegated_chan(struct device *dma_dev, bool write, u16 id)
> > +{
> > +	return NULL;
> > +}
> > +
> > +static inline void dw_edma_release_delegated_chan(struct dma_chan *chan,
> > +						  bool quiesce)
> > +{
> > +}
> >  #endif /* CONFIG_DW_EDMA */
> >
> >  #endif /* _DW_EDMA_H */
> > --
> > 2.51.0
> >

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v6 4/6] dmaengine: dw-edma: Add delegated channel request helpers
  2026-08-05  2:05     ` Koichiro Den
@ 2026-08-05 18:47       ` Frank Li
  2026-08-06  3:45         ` Koichiro Den
  0 siblings, 1 reply; 17+ messages in thread
From: Frank Li @ 2026-08-05 18:47 UTC (permalink / raw)
  To: Koichiro Den
  Cc: Manivannan Sadhasivam, Jingoo Han, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
	Kishon Vijay Abraham I, Frank Li, Marek Vasut, Yoshihiro Shimoda,
	linux-pci, linux-kernel

On Wed, Aug 05, 2026 at 11:05:21AM +0900, Koichiro Den wrote:
> On Tue, Aug 04, 2026 at 11:23:47AM -0500, Frank Li wrote:
> > On Tue, Aug 04, 2026 at 12:38:53PM +0900, Koichiro Den wrote:
> > > Endpoint functions that expose endpoint-local DesignWare eDMA channels
> > > to a remote host need to reserve exact hardware channels and hand
> > > interrupt ownership to the remote side before publishing the channels.
> > >
> > > Add DW eDMA-specific helpers that request a write/read hardware channel
> > > through DMAengine, keep the hardware-channel filter private to dw-edma,
> > > and switch the selected endpoint-local channel to remote interrupt
> > > routing after the channel has been successfully reserved. The matching
> > > release helper can quiesce the channel while it is still remote-routed,
> > > then restores the channel's default routing before releasing the
> > > DMAengine reservation. This lets callers skip quiesce when unwinding a
> > > reservation that was never exposed to host programming.
> > >
> > > Release is best-effort because its callers cannot abort teardown. Report
> > > a quiesce failure locally, but always restore the default routing and
> > > release the DMAengine reservation.
> > >
> > > Signed-off-by: Koichiro Den <den@valinux.co.jp>
> > > ---
> > > Changes in v6:
> > >   - No changes.
> > >
> > >  drivers/dma/dw-edma/dw-edma-core.c | 91 ++++++++++++++++++++++++++++++
> > >  include/linux/dma/edma.h           | 14 +++++
> > >  2 files changed, 105 insertions(+)
> > >
> > > diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> > > index 1f893dc54c79..4c0d2bba755b 100644
> > > --- a/drivers/dma/dw-edma/dw-edma-core.c
> > > +++ b/drivers/dma/dw-edma/dw-edma-core.c
> > > @@ -1302,6 +1302,97 @@ int dw_edma_remove(struct dw_edma_chip *chip)
> > >  }
> > >  EXPORT_SYMBOL_GPL(dw_edma_remove);
> > >
> > > +struct dw_edma_delegated_chan_filter_args {
> > > +	struct device *dma_dev;
> > > +	bool write;
> > > +	u16 id;
> > > +};
> > > +
> > > +static bool dw_edma_delegated_chan_filter(struct dma_chan *dchan, void *param)
> > > +{
> > > +	struct dw_edma_delegated_chan_filter_args *filter = param;
> > > +	struct dw_edma_chan *chan;
> > > +
> > > +	if (!filter || dchan->device->dev != filter->dma_dev)
> > > +		return false;
> > > +
> > > +	chan = dchan2dw_edma_chan(dchan);
> > > +
> > > +	return chan->dir == (filter->write ? EDMA_DIR_WRITE : EDMA_DIR_READ) &&
> > > +	       chan->id == filter->id;
> > > +}
> > > +
> > > +static int dw_edma_delegate_chan(struct dma_chan *dchan)
> > > +{
> > > +	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
> > > +	int ret = 0;
> > > +
> > > +	if (!(chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL))
> > > +		return -EINVAL;
> > > +
> > > +	guard(spinlock_irqsave)(&chan->vc.lock);
> > > +
> > > +	if (chan->configured || chan->status != EDMA_ST_IDLE ||
> > > +	    chan->request != EDMA_REQ_NONE)
> > > +		ret = -EBUSY;
> > > +	else
> > > +		chan->irq_mode = DW_EDMA_CH_IRQ_REMOTE;
> > > +
> > > +	return ret;
> > > +}
> > > +
> > > +struct dma_chan *dw_edma_request_delegated_chan(struct device *dma_dev,
> > > +						bool write, u16 id)
> > > +{
> > > +	struct dw_edma_delegated_chan_filter_args filter = {
> > > +		.dma_dev = dma_dev,
> > > +		.write = write,
> > > +		.id = id,
> > > +	};
> > > +	struct dma_chan *dchan;
> > > +	dma_cap_mask_t mask;
> > > +
> > > +	if (!dma_dev)
> > > +		return NULL;
> > > +
> > > +	dma_cap_zero(mask);
> > > +	dma_cap_set(DMA_SLAVE, mask);
> > > +
> > > +	dchan = dma_request_channel(mask, dw_edma_delegated_chan_filter,
> > > +				    &filter);
> > > +	if (!dchan)
> > > +		return NULL;
> >
> > I forget, why need export delegrated dma channel to dmanegine? If EP
> > driver doesn't report delegrated channel to dma engine, what's happen?
>
> This just reserves the exact channel against any dmaengine clients before
> handing its programming ownership to the host. Without it, a local client
> could request the same channel too.

why not epf function driver call dma_request_channel() to decide which
channel pass to ownership to the host.

Frank

>
> Best regards,
> Koichiro
>
> >
> > Frank
> >
> > > +
> > > +	if (dw_edma_delegate_chan(dchan)) {
> > > +		dma_release_channel(dchan);
> > > +		return NULL;
> > > +	}
> > > +
> > > +	return dchan;
> > > +}
> > > +EXPORT_SYMBOL_GPL(dw_edma_request_delegated_chan);
> > > +
> > > +void dw_edma_release_delegated_chan(struct dma_chan *dchan, bool quiesce)
> > > +{
> > > +	struct dw_edma_chan *chan;
> > > +
> > > +	if (!dchan)
> > > +		return;
> > > +
> > > +	chan = dchan2dw_edma_chan(dchan);
> > > +	if (quiesce && dw_edma_core_ch_quiesce(chan))
> > > +		dev_warn(chan->dw->chip->dev,
> > > +			 "failed to quiesce delegated %s channel %u\n",
> > > +			 chan->dir == EDMA_DIR_WRITE ? "write" : "read",
> > > +			 chan->id);
> > > +
> > > +	scoped_guard(spinlock_irqsave, &chan->vc.lock)
> > > +		chan->irq_mode = dw_edma_get_default_irq_mode(chan);
> > > +
> > > +	dma_release_channel(dchan);
> > > +}
> > > +EXPORT_SYMBOL_GPL(dw_edma_release_delegated_chan);
> > > +
> > >  MODULE_LICENSE("GPL v2");
> > >  MODULE_DESCRIPTION("Synopsys DesignWare eDMA controller core driver");
> > >  MODULE_AUTHOR("Gustavo Pimentel <gustavo.pimentel@synopsys.com>");
> > > diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h
> > > index 3c8e2ef9dee0..944469258b8b 100644
> > > --- a/include/linux/dma/edma.h
> > > +++ b/include/linux/dma/edma.h
> > > @@ -153,6 +153,9 @@ struct dw_edma_chip {
> > >  #if IS_REACHABLE(CONFIG_DW_EDMA)
> > >  int dw_edma_probe(struct dw_edma_chip *chip);
> > >  int dw_edma_remove(struct dw_edma_chip *chip);
> > > +struct dma_chan *dw_edma_request_delegated_chan(struct device *dma_dev,
> > > +						bool write, u16 id);
> > > +void dw_edma_release_delegated_chan(struct dma_chan *chan, bool quiesce);
> > >  #else
> > >  static inline int dw_edma_probe(struct dw_edma_chip *chip)
> > >  {
> > > @@ -163,6 +166,17 @@ static inline int dw_edma_remove(struct dw_edma_chip *chip)
> > >  {
> > >  	return 0;
> > >  }
> > > +
> > > +static inline struct dma_chan *
> > > +dw_edma_request_delegated_chan(struct device *dma_dev, bool write, u16 id)
> > > +{
> > > +	return NULL;
> > > +}
> > > +
> > > +static inline void dw_edma_release_delegated_chan(struct dma_chan *chan,
> > > +						  bool quiesce)
> > > +{
> > > +}
> > >  #endif /* CONFIG_DW_EDMA */
> > >
> > >  #endif /* _DW_EDMA_H */
> > > --
> > > 2.51.0
> > >

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v6 4/6] dmaengine: dw-edma: Add delegated channel request helpers
  2026-08-05 18:47       ` Frank Li
@ 2026-08-06  3:45         ` Koichiro Den
  2026-08-06 16:50           ` Frank Li
  0 siblings, 1 reply; 17+ messages in thread
From: Koichiro Den @ 2026-08-06  3:45 UTC (permalink / raw)
  To: Frank Li
  Cc: Manivannan Sadhasivam, Jingoo Han, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
	Kishon Vijay Abraham I, Frank Li, Marek Vasut, Yoshihiro Shimoda,
	linux-pci, linux-kernel

On Wed, Aug 05, 2026 at 01:47:45PM -0500, Frank Li wrote:
> On Wed, Aug 05, 2026 at 11:05:21AM +0900, Koichiro Den wrote:
> > On Tue, Aug 04, 2026 at 11:23:47AM -0500, Frank Li wrote:
> > > On Tue, Aug 04, 2026 at 12:38:53PM +0900, Koichiro Den wrote:
> > > > Endpoint functions that expose endpoint-local DesignWare eDMA channels
> > > > to a remote host need to reserve exact hardware channels and hand
> > > > interrupt ownership to the remote side before publishing the channels.
> > > >
> > > > Add DW eDMA-specific helpers that request a write/read hardware channel
> > > > through DMAengine, keep the hardware-channel filter private to dw-edma,
> > > > and switch the selected endpoint-local channel to remote interrupt
> > > > routing after the channel has been successfully reserved. The matching
> > > > release helper can quiesce the channel while it is still remote-routed,
> > > > then restores the channel's default routing before releasing the
> > > > DMAengine reservation. This lets callers skip quiesce when unwinding a
> > > > reservation that was never exposed to host programming.
> > > >
> > > > Release is best-effort because its callers cannot abort teardown. Report
> > > > a quiesce failure locally, but always restore the default routing and
> > > > release the DMAengine reservation.
> > > >
> > > > Signed-off-by: Koichiro Den <den@valinux.co.jp>
> > > > ---
> > > > Changes in v6:
> > > >   - No changes.
> > > >
> > > >  drivers/dma/dw-edma/dw-edma-core.c | 91 ++++++++++++++++++++++++++++++
> > > >  include/linux/dma/edma.h           | 14 +++++
> > > >  2 files changed, 105 insertions(+)
> > > >
> > > > diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> > > > index 1f893dc54c79..4c0d2bba755b 100644
> > > > --- a/drivers/dma/dw-edma/dw-edma-core.c
> > > > +++ b/drivers/dma/dw-edma/dw-edma-core.c
> > > > @@ -1302,6 +1302,97 @@ int dw_edma_remove(struct dw_edma_chip *chip)
> > > >  }
> > > >  EXPORT_SYMBOL_GPL(dw_edma_remove);
> > > >
> > > > +struct dw_edma_delegated_chan_filter_args {
> > > > +	struct device *dma_dev;
> > > > +	bool write;
> > > > +	u16 id;
> > > > +};
> > > > +
> > > > +static bool dw_edma_delegated_chan_filter(struct dma_chan *dchan, void *param)
> > > > +{
> > > > +	struct dw_edma_delegated_chan_filter_args *filter = param;
> > > > +	struct dw_edma_chan *chan;
> > > > +
> > > > +	if (!filter || dchan->device->dev != filter->dma_dev)
> > > > +		return false;
> > > > +
> > > > +	chan = dchan2dw_edma_chan(dchan);
> > > > +
> > > > +	return chan->dir == (filter->write ? EDMA_DIR_WRITE : EDMA_DIR_READ) &&
> > > > +	       chan->id == filter->id;
> > > > +}
> > > > +
> > > > +static int dw_edma_delegate_chan(struct dma_chan *dchan)
> > > > +{
> > > > +	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
> > > > +	int ret = 0;
> > > > +
> > > > +	if (!(chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL))
> > > > +		return -EINVAL;
> > > > +
> > > > +	guard(spinlock_irqsave)(&chan->vc.lock);
> > > > +
> > > > +	if (chan->configured || chan->status != EDMA_ST_IDLE ||
> > > > +	    chan->request != EDMA_REQ_NONE)
> > > > +		ret = -EBUSY;
> > > > +	else
> > > > +		chan->irq_mode = DW_EDMA_CH_IRQ_REMOTE;
> > > > +
> > > > +	return ret;
> > > > +}
> > > > +
> > > > +struct dma_chan *dw_edma_request_delegated_chan(struct device *dma_dev,
> > > > +						bool write, u16 id)
> > > > +{
> > > > +	struct dw_edma_delegated_chan_filter_args filter = {
> > > > +		.dma_dev = dma_dev,
> > > > +		.write = write,
> > > > +		.id = id,
> > > > +	};
> > > > +	struct dma_chan *dchan;
> > > > +	dma_cap_mask_t mask;
> > > > +
> > > > +	if (!dma_dev)
> > > > +		return NULL;
> > > > +
> > > > +	dma_cap_zero(mask);
> > > > +	dma_cap_set(DMA_SLAVE, mask);
> > > > +
> > > > +	dchan = dma_request_channel(mask, dw_edma_delegated_chan_filter,
> > > > +				    &filter);
> > > > +	if (!dchan)
> > > > +		return NULL;
> > >
> > > I forget, why need export delegrated dma channel to dmanegine? If EP
> > > driver doesn't report delegrated channel to dma engine, what's happen?
> >
> > This just reserves the exact channel against any dmaengine clients before
> > handing its programming ownership to the host. Without it, a local client
> > could request the same channel too.
> 
> why not epf function driver call dma_request_channel() to decide which
> channel pass to ownership to the host.

I actually tried that model in v2. pci_epf_dma called dma_request_channel() with
a filter supplied through the EPC resource.

  In v2, .filter_fn was part of the pci_epc_aux_resource definition:
  https://lore.kernel.org/r/20260525063129.3316894-3-den@valinux.co.jp/
  ... and pci_epf_dma_claim_channel() used it to claim the channel here:
  https://lore.kernel.org/r/20260525063456.3317509-3-den@valinux.co.jp/

I changed that in v3 after your comment about why the dw-edma filter needed to
be public. The replacement kept the filter inside dw-edma, and moved channel
reservation, IRQ ownership handoff and reclaim there too. The current EPC
delegation API follows that model.

Or is that v2 model what you prefer here?

That said, dma_get_slave_channel() might be sufficient inside dw-edma here,
since it already knows the exact channel from the direction and hw channel id.

P.S. For clarity, the options tried or discussed so far are:

  (a) Expose dma_chan directly through the EPC resource, then let the EPF
      reserve it with dma_get_slave_channel(). This was v1.
      (https://lore.kernel.org/r/20260521063405.2842644-3-den@valinux.co.jp/)

  (b) Expose the hardware ID, direction and a provider filter, then let the
      EPF call dma_request_channel(). This was v2.
      (https://lore.kernel.org/r/20260525062420.3315904-2-den@valinux.co.jp/)

  (c) Pass the hardware ID and direction to pci_epc_delegate_dma_chan(), then
      let the backend reserve and delegate the channel. This is the current
      v6 design.
      (https://lore.kernel.org/r/20260804033855.2115817-5-den@valinux.co.jp/)

  (d) Request channels normally and correlate them with EPC resources through
      a generic dma_slave_caps.hw_id. I proposed this in a much older series.
      (https://lore.kernel.org/r/20260204145440.950609-2-den@valinux.co.jp/)

Best regards,
Koichiro

> 
> Frank
> 
> >
> > Best regards,
> > Koichiro
> >
> > >
> > > Frank
> > >
> > > > +
> > > > +	if (dw_edma_delegate_chan(dchan)) {
> > > > +		dma_release_channel(dchan);
> > > > +		return NULL;
> > > > +	}
> > > > +
> > > > +	return dchan;
> > > > +}
> > > > +EXPORT_SYMBOL_GPL(dw_edma_request_delegated_chan);
> > > > +
> > > > +void dw_edma_release_delegated_chan(struct dma_chan *dchan, bool quiesce)
> > > > +{
> > > > +	struct dw_edma_chan *chan;
> > > > +
> > > > +	if (!dchan)
> > > > +		return;
> > > > +
> > > > +	chan = dchan2dw_edma_chan(dchan);
> > > > +	if (quiesce && dw_edma_core_ch_quiesce(chan))
> > > > +		dev_warn(chan->dw->chip->dev,
> > > > +			 "failed to quiesce delegated %s channel %u\n",
> > > > +			 chan->dir == EDMA_DIR_WRITE ? "write" : "read",
> > > > +			 chan->id);
> > > > +
> > > > +	scoped_guard(spinlock_irqsave, &chan->vc.lock)
> > > > +		chan->irq_mode = dw_edma_get_default_irq_mode(chan);
> > > > +
> > > > +	dma_release_channel(dchan);
> > > > +}
> > > > +EXPORT_SYMBOL_GPL(dw_edma_release_delegated_chan);
> > > > +
> > > >  MODULE_LICENSE("GPL v2");
> > > >  MODULE_DESCRIPTION("Synopsys DesignWare eDMA controller core driver");
> > > >  MODULE_AUTHOR("Gustavo Pimentel <gustavo.pimentel@synopsys.com>");
> > > > diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h
> > > > index 3c8e2ef9dee0..944469258b8b 100644
> > > > --- a/include/linux/dma/edma.h
> > > > +++ b/include/linux/dma/edma.h
> > > > @@ -153,6 +153,9 @@ struct dw_edma_chip {
> > > >  #if IS_REACHABLE(CONFIG_DW_EDMA)
> > > >  int dw_edma_probe(struct dw_edma_chip *chip);
> > > >  int dw_edma_remove(struct dw_edma_chip *chip);
> > > > +struct dma_chan *dw_edma_request_delegated_chan(struct device *dma_dev,
> > > > +						bool write, u16 id);
> > > > +void dw_edma_release_delegated_chan(struct dma_chan *chan, bool quiesce);
> > > >  #else
> > > >  static inline int dw_edma_probe(struct dw_edma_chip *chip)
> > > >  {
> > > > @@ -163,6 +166,17 @@ static inline int dw_edma_remove(struct dw_edma_chip *chip)
> > > >  {
> > > >  	return 0;
> > > >  }
> > > > +
> > > > +static inline struct dma_chan *
> > > > +dw_edma_request_delegated_chan(struct device *dma_dev, bool write, u16 id)
> > > > +{
> > > > +	return NULL;
> > > > +}
> > > > +
> > > > +static inline void dw_edma_release_delegated_chan(struct dma_chan *chan,
> > > > +						  bool quiesce)
> > > > +{
> > > > +}
> > > >  #endif /* CONFIG_DW_EDMA */
> > > >
> > > >  #endif /* _DW_EDMA_H */
> > > > --
> > > > 2.51.0
> > > >

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v6 4/6] dmaengine: dw-edma: Add delegated channel request helpers
  2026-08-06  3:45         ` Koichiro Den
@ 2026-08-06 16:50           ` Frank Li
  0 siblings, 0 replies; 17+ messages in thread
From: Frank Li @ 2026-08-06 16:50 UTC (permalink / raw)
  To: Koichiro Den
  Cc: Manivannan Sadhasivam, Jingoo Han, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas,
	Kishon Vijay Abraham I, Frank Li, Marek Vasut, Yoshihiro Shimoda,
	linux-pci, linux-kernel

On Thu, Aug 06, 2026 at 12:45:13PM +0900, Koichiro Den wrote:
> On Wed, Aug 05, 2026 at 01:47:45PM -0500, Frank Li wrote:
> > On Wed, Aug 05, 2026 at 11:05:21AM +0900, Koichiro Den wrote:
> > > On Tue, Aug 04, 2026 at 11:23:47AM -0500, Frank Li wrote:
> > > > On Tue, Aug 04, 2026 at 12:38:53PM +0900, Koichiro Den wrote:
> > > > > Endpoint functions that expose endpoint-local DesignWare eDMA channels
> > > > > to a remote host need to reserve exact hardware channels and hand
> > > > > interrupt ownership to the remote side before publishing the channels.
> > > > >
> > > > > Add DW eDMA-specific helpers that request a write/read hardware channel
> > > > > through DMAengine, keep the hardware-channel filter private to dw-edma,
> > > > > and switch the selected endpoint-local channel to remote interrupt
> > > > > routing after the channel has been successfully reserved. The matching
> > > > > release helper can quiesce the channel while it is still remote-routed,
> > > > > then restores the channel's default routing before releasing the
> > > > > DMAengine reservation. This lets callers skip quiesce when unwinding a
> > > > > reservation that was never exposed to host programming.
> > > > >
> > > > > Release is best-effort because its callers cannot abort teardown. Report
> > > > > a quiesce failure locally, but always restore the default routing and
> > > > > release the DMAengine reservation.
> > > > >
> > > > > Signed-off-by: Koichiro Den <den@valinux.co.jp>
> > > > > ---
> > > > > Changes in v6:
> > > > >   - No changes.
> > > > >
> > > > >  drivers/dma/dw-edma/dw-edma-core.c | 91 ++++++++++++++++++++++++++++++
> > > > >  include/linux/dma/edma.h           | 14 +++++
> > > > >  2 files changed, 105 insertions(+)
> > > > >
> > > > > diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> > > > > index 1f893dc54c79..4c0d2bba755b 100644
> > > > > --- a/drivers/dma/dw-edma/dw-edma-core.c
> > > > > +++ b/drivers/dma/dw-edma/dw-edma-core.c
> > > > > @@ -1302,6 +1302,97 @@ int dw_edma_remove(struct dw_edma_chip *chip)
> > > > >  }
> > > > >  EXPORT_SYMBOL_GPL(dw_edma_remove);
> > > > >
> > > > > +struct dw_edma_delegated_chan_filter_args {
> > > > > +	struct device *dma_dev;
> > > > > +	bool write;
> > > > > +	u16 id;
> > > > > +};
> > > > > +
> > > > > +static bool dw_edma_delegated_chan_filter(struct dma_chan *dchan, void *param)
> > > > > +{
> > > > > +	struct dw_edma_delegated_chan_filter_args *filter = param;
> > > > > +	struct dw_edma_chan *chan;
> > > > > +
> > > > > +	if (!filter || dchan->device->dev != filter->dma_dev)
> > > > > +		return false;
> > > > > +
> > > > > +	chan = dchan2dw_edma_chan(dchan);
> > > > > +
> > > > > +	return chan->dir == (filter->write ? EDMA_DIR_WRITE : EDMA_DIR_READ) &&
> > > > > +	       chan->id == filter->id;
> > > > > +}
> > > > > +
> > > > > +static int dw_edma_delegate_chan(struct dma_chan *dchan)
> > > > > +{
> > > > > +	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
> > > > > +	int ret = 0;
> > > > > +
> > > > > +	if (!(chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL))
> > > > > +		return -EINVAL;
> > > > > +
> > > > > +	guard(spinlock_irqsave)(&chan->vc.lock);
> > > > > +
> > > > > +	if (chan->configured || chan->status != EDMA_ST_IDLE ||
> > > > > +	    chan->request != EDMA_REQ_NONE)
> > > > > +		ret = -EBUSY;
> > > > > +	else
> > > > > +		chan->irq_mode = DW_EDMA_CH_IRQ_REMOTE;
> > > > > +
> > > > > +	return ret;
> > > > > +}
> > > > > +
> > > > > +struct dma_chan *dw_edma_request_delegated_chan(struct device *dma_dev,
> > > > > +						bool write, u16 id)
> > > > > +{
> > > > > +	struct dw_edma_delegated_chan_filter_args filter = {
> > > > > +		.dma_dev = dma_dev,
> > > > > +		.write = write,
> > > > > +		.id = id,
> > > > > +	};
> > > > > +	struct dma_chan *dchan;
> > > > > +	dma_cap_mask_t mask;
> > > > > +
> > > > > +	if (!dma_dev)
> > > > > +		return NULL;
> > > > > +
> > > > > +	dma_cap_zero(mask);
> > > > > +	dma_cap_set(DMA_SLAVE, mask);
> > > > > +
> > > > > +	dchan = dma_request_channel(mask, dw_edma_delegated_chan_filter,
> > > > > +				    &filter);
> > > > > +	if (!dchan)
> > > > > +		return NULL;
> > > >
> > > > I forget, why need export delegrated dma channel to dmanegine? If EP
> > > > driver doesn't report delegrated channel to dma engine, what's happen?
> > >
> > > This just reserves the exact channel against any dmaengine clients before
> > > handing its programming ownership to the host. Without it, a local client
> > > could request the same channel too.
> >
> > why not epf function driver call dma_request_channel() to decide which
> > channel pass to ownership to the host.
>
> I actually tried that model in v2. pci_epf_dma called dma_request_channel() with
> a filter supplied through the EPC resource.
>
>   In v2, .filter_fn was part of the pci_epc_aux_resource definition:
>   https://lore.kernel.org/r/20260525063129.3316894-3-den@valinux.co.jp/
>   ... and pci_epf_dma_claim_channel() used it to claim the channel here:
>   https://lore.kernel.org/r/20260525063456.3317509-3-den@valinux.co.jp/
>
> I changed that in v3 after your comment about why the dw-edma filter needed to
> be public. The replacement kept the filter inside dw-edma, and moved channel
> reservation, IRQ ownership handoff and reclaim there too. The current EPC
> delegation API follows that model.
>
> Or is that v2 model what you prefer here?
>
> That said, dma_get_slave_channel() might be sufficient inside dw-edma here,
> since it already knows the exact channel from the direction and hw channel id.
>
> P.S. For clarity, the options tried or discussed so far are:
>
>   (a) Expose dma_chan directly through the EPC resource, then let the EPF
>       reserve it with dma_get_slave_channel(). This was v1.
>       (https://lore.kernel.org/r/20260521063405.2842644-3-den@valinux.co.jp/)
>
>   (b) Expose the hardware ID, direction and a provider filter, then let the
>       EPF call dma_request_channel(). This was v2.
>       (https://lore.kernel.org/r/20260525062420.3315904-2-den@valinux.co.jp/)
>
>   (c) Pass the hardware ID and direction to pci_epc_delegate_dma_chan(), then
>       let the backend reserve and delegate the channel. This is the current
>       v6 design.
>       (https://lore.kernel.org/r/20260804033855.2115817-5-den@valinux.co.jp/)
>
>   (d) Request channels normally and correlate them with EPC resources through
>       a generic dma_slave_caps.hw_id. I proposed this in a much older series.
>       (https://lore.kernel.org/r/20260204145440.950609-2-den@valinux.co.jp/)

Thank you for your great work, let me read and feedback to you, do you have
irc, discord or other instance message channel.

Frank

>
> Best regards,
> Koichiro
>
> >
> > Frank
> >
> > >
> > > Best regards,
> > > Koichiro
> > >
> > > >
> > > > Frank
> > > >
> > > > > +
> > > > > +	if (dw_edma_delegate_chan(dchan)) {
> > > > > +		dma_release_channel(dchan);
> > > > > +		return NULL;
> > > > > +	}
> > > > > +
> > > > > +	return dchan;
> > > > > +}
> > > > > +EXPORT_SYMBOL_GPL(dw_edma_request_delegated_chan);
> > > > > +
> > > > > +void dw_edma_release_delegated_chan(struct dma_chan *dchan, bool quiesce)
> > > > > +{
> > > > > +	struct dw_edma_chan *chan;
> > > > > +
> > > > > +	if (!dchan)
> > > > > +		return;
> > > > > +
> > > > > +	chan = dchan2dw_edma_chan(dchan);
> > > > > +	if (quiesce && dw_edma_core_ch_quiesce(chan))
> > > > > +		dev_warn(chan->dw->chip->dev,
> > > > > +			 "failed to quiesce delegated %s channel %u\n",
> > > > > +			 chan->dir == EDMA_DIR_WRITE ? "write" : "read",
> > > > > +			 chan->id);
> > > > > +
> > > > > +	scoped_guard(spinlock_irqsave, &chan->vc.lock)
> > > > > +		chan->irq_mode = dw_edma_get_default_irq_mode(chan);
> > > > > +
> > > > > +	dma_release_channel(dchan);
> > > > > +}
> > > > > +EXPORT_SYMBOL_GPL(dw_edma_release_delegated_chan);
> > > > > +
> > > > >  MODULE_LICENSE("GPL v2");
> > > > >  MODULE_DESCRIPTION("Synopsys DesignWare eDMA controller core driver");
> > > > >  MODULE_AUTHOR("Gustavo Pimentel <gustavo.pimentel@synopsys.com>");
> > > > > diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h
> > > > > index 3c8e2ef9dee0..944469258b8b 100644
> > > > > --- a/include/linux/dma/edma.h
> > > > > +++ b/include/linux/dma/edma.h
> > > > > @@ -153,6 +153,9 @@ struct dw_edma_chip {
> > > > >  #if IS_REACHABLE(CONFIG_DW_EDMA)
> > > > >  int dw_edma_probe(struct dw_edma_chip *chip);
> > > > >  int dw_edma_remove(struct dw_edma_chip *chip);
> > > > > +struct dma_chan *dw_edma_request_delegated_chan(struct device *dma_dev,
> > > > > +						bool write, u16 id);
> > > > > +void dw_edma_release_delegated_chan(struct dma_chan *chan, bool quiesce);
> > > > >  #else
> > > > >  static inline int dw_edma_probe(struct dw_edma_chip *chip)
> > > > >  {
> > > > > @@ -163,6 +166,17 @@ static inline int dw_edma_remove(struct dw_edma_chip *chip)
> > > > >  {
> > > > >  	return 0;
> > > > >  }
> > > > > +
> > > > > +static inline struct dma_chan *
> > > > > +dw_edma_request_delegated_chan(struct device *dma_dev, bool write, u16 id)
> > > > > +{
> > > > > +	return NULL;
> > > > > +}
> > > > > +
> > > > > +static inline void dw_edma_release_delegated_chan(struct dma_chan *chan,
> > > > > +						  bool quiesce)
> > > > > +{
> > > > > +}
> > > > >  #endif /* CONFIG_DW_EDMA */
> > > > >
> > > > >  #endif /* _DW_EDMA_H */
> > > > > --
> > > > > 2.51.0
> > > > >

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2026-08-06 16:50 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-04  3:38 [PATCH v6 0/6] PCI: endpoint: Expose endpoint DMA resources (part 2/3) Koichiro Den
2026-08-04  3:38 ` [PATCH v6 1/6] PCI: endpoint: Define endpoint DMA BAR metadata format Koichiro Den
2026-08-04 15:23   ` Frank Li
2026-08-05  2:03     ` Koichiro Den
2026-08-04  3:38 ` [PATCH v6 2/6] PCI: endpoint: Add DMA auxiliary resource metadata Koichiro Den
2026-08-04 15:26   ` Frank Li
2026-08-04  3:38 ` [PATCH v6 3/6] PCI: endpoint: Add API to delegate EPC DMA channels to the host Koichiro Den
2026-08-04 16:06   ` Frank Li
2026-08-04  3:38 ` [PATCH v6 4/6] dmaengine: dw-edma: Add delegated channel request helpers Koichiro Den
2026-08-04 16:23   ` Frank Li
2026-08-05  2:05     ` Koichiro Den
2026-08-05 18:47       ` Frank Li
2026-08-06  3:45         ` Koichiro Den
2026-08-06 16:50           ` Frank Li
2026-08-04  3:38 ` [PATCH v6 5/6] PCI: dwc: Implement endpoint DMA channel delegation Koichiro Den
2026-08-04  3:38 ` [PATCH v6 6/6] PCI: dwc: Expose endpoint DMA resources Koichiro Den
2026-08-04 16:29   ` Frank Li

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox