public inbox for dmaengine@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v8 0/2] Add AMD MDB Endpoint and non-LL mode Support
@ 2026-01-09 12:03 Devendra K Verma
  2026-01-09 12:03 ` [PATCH v8 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint Support Devendra K Verma
  2026-01-09 12:03 ` [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode Devendra K Verma
  0 siblings, 2 replies; 24+ messages in thread
From: Devendra K Verma @ 2026-01-09 12:03 UTC (permalink / raw)
  To: bhelgaas, mani, vkoul
  Cc: dmaengine, linux-pci, linux-kernel, michal.simek, Devendra.Verma

This series of patch support the following:

 - AMD MDB Endpoint Support, as part of this patch following are
   added:
   o AMD supported device ID and vendor ID (Xilinx)
   o AMD MDB specific driver data
   o AMD specific VSEC capabilities to retrieve the base of
     phys address of MDB side DDR
   o Logic to assign the offsets to LL and data blocks if
     more number of channels are enabled than configured
     in the given pci_data struct.

 - Addition of non-LL mode
   o The IP supported non-LL mode functions
   o Flexibility to choose non-LL mode via dma_slave_config
     param peripheral_config, by the client for all the vendors
     using HDMA IP.
   o Allow IP utilization if LL mode is not available

Devendra K Verma (2):
  dmaengine: dw-edma: Add AMD MDB Endpoint Support
  dmaengine: dw-edma: Add non-LL mode

 drivers/dma/dw-edma/dw-edma-core.c    |  42 ++++++-
 drivers/dma/dw-edma/dw-edma-core.h    |   1 +
 drivers/dma/dw-edma/dw-edma-pcie.c    | 222 +++++++++++++++++++++++++++++++---
 drivers/dma/dw-edma/dw-hdma-v0-core.c |  61 +++++++++-
 drivers/dma/dw-edma/dw-hdma-v0-regs.h |   1 +
 include/linux/dma/edma.h              |   1 +
 6 files changed, 302 insertions(+), 26 deletions(-)

-- 
1.8.3.1


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

* [PATCH v8 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint Support
  2026-01-09 12:03 [PATCH v8 0/2] Add AMD MDB Endpoint and non-LL mode Support Devendra K Verma
@ 2026-01-09 12:03 ` Devendra K Verma
  2026-01-15 16:21   ` Frank Li
  2026-01-09 12:03 ` [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode Devendra K Verma
  1 sibling, 1 reply; 24+ messages in thread
From: Devendra K Verma @ 2026-01-09 12:03 UTC (permalink / raw)
  To: bhelgaas, mani, vkoul
  Cc: dmaengine, linux-pci, linux-kernel, michal.simek, Devendra.Verma

AMD MDB PCIe endpoint support. For AMD specific support
added the following
  - AMD supported PCIe Device IDs and Vendor ID (Xilinx).
  - AMD MDB specific driver data
  - AMD MDB specific VSEC capability to retrieve the device DDR
    base address.

Signed-off-by: Devendra K Verma <devendra.verma@amd.com>
---
Changes in v8:
Changed the contant names to includer product vendor.
Moved the vendor specific code to vendor specific functions.

Changes in v7:
Introduced vendor specific functions to retrieve the
vsec data.

Changes in v6:
Included "sizes.h" header and used the appropriate
definitions instead of constants.

Changes in v5:
Added the definitions for Xilinx specific VSEC header id,
revision, and register offsets.
Corrected the error type when no physical offset found for
device side memory.
Corrected the order of variables.

Changes in v4:
Configured 8 read and 8 write channels for Xilinx vendor
Added checks to validate vendor ID for vendor
specific vsec id.
Added Xilinx specific vendor id for vsec specific to Xilinx
Added the LL and data region offsets, size as input params to
function dw_edma_set_chan_region_offset().
Moved the LL and data region offsets assignment to function
for Xilinx specific case.
Corrected comments.

Changes in v3:
Corrected a typo when assigning AMD (Xilinx) vsec id macro
and condition check.

Changes in v2:
Reverted the devmem_phys_off type to u64.
Renamed the function appropriately to suit the
functionality for setting the LL & data region offsets.

Changes in v1:
Removed the pci device id from pci_ids.h file.
Added the vendor id macro as per the suggested method.
Changed the type of the newly added devmem_phys_off variable.
Added to logic to assign offsets for LL and data region blocks
in case more number of channels are enabled than given in
amd_mdb_data struct.
---
 drivers/dma/dw-edma/dw-edma-pcie.c | 192 ++++++++++++++++++++++++++++++++++---
 1 file changed, 178 insertions(+), 14 deletions(-)

diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c b/drivers/dma/dw-edma/dw-edma-pcie.c
index 3371e0a7..2efd149 100644
--- a/drivers/dma/dw-edma/dw-edma-pcie.c
+++ b/drivers/dma/dw-edma/dw-edma-pcie.c
@@ -14,14 +14,35 @@
 #include <linux/pci-epf.h>
 #include <linux/msi.h>
 #include <linux/bitfield.h>
+#include <linux/sizes.h>
 
 #include "dw-edma-core.h"
 
-#define DW_PCIE_VSEC_DMA_ID			0x6
-#define DW_PCIE_VSEC_DMA_BAR			GENMASK(10, 8)
-#define DW_PCIE_VSEC_DMA_MAP			GENMASK(2, 0)
-#define DW_PCIE_VSEC_DMA_WR_CH			GENMASK(9, 0)
-#define DW_PCIE_VSEC_DMA_RD_CH			GENMASK(25, 16)
+/* Synopsys */
+#define DW_PCIE_SYNOPSYS_VSEC_DMA_ID		0x6
+#define DW_PCIE_SYNOPSYS_VSEC_DMA_BAR		GENMASK(10, 8)
+#define DW_PCIE_SYNOPSYS_VSEC_DMA_MAP		GENMASK(2, 0)
+#define DW_PCIE_SYNOPSYS_VSEC_DMA_WR_CH		GENMASK(9, 0)
+#define DW_PCIE_SYNOPSYS_VSEC_DMA_RD_CH		GENMASK(25, 16)
+
+/* AMD MDB (Xilinx) specific defines */
+#define PCI_DEVICE_ID_XILINX_B054		0xb054
+
+#define DW_PCIE_XILINX_MDB_VSEC_DMA_ID		0x6
+#define DW_PCIE_XILINX_MDB_VSEC_ID		0x20
+#define DW_PCIE_XILINX_MDB_VSEC_DMA_BAR		GENMASK(10, 8)
+#define DW_PCIE_XILINX_MDB_VSEC_DMA_MAP		GENMASK(2, 0)
+#define DW_PCIE_XILINX_MDB_VSEC_DMA_WR_CH	GENMASK(9, 0)
+#define DW_PCIE_XILINX_MDB_VSEC_DMA_RD_CH	GENMASK(25, 16)
+
+#define DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_HIGH	0xc
+#define DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_LOW	0x8
+#define DW_PCIE_XILINX_MDB_INVALID_ADDR		(~0ULL)
+
+#define DW_PCIE_XILINX_MDB_LL_OFF_GAP		0x200000
+#define DW_PCIE_XILINX_MDB_LL_SIZE		0x800
+#define DW_PCIE_XILINX_MDB_DT_OFF_GAP		0x100000
+#define DW_PCIE_XILINX_MDB_DT_SIZE		0x800
 
 #define DW_BLOCK(a, b, c) \
 	{ \
@@ -50,6 +71,7 @@ struct dw_edma_pcie_data {
 	u8				irqs;
 	u16				wr_ch_cnt;
 	u16				rd_ch_cnt;
+	u64				devmem_phys_off;
 };
 
 static const struct dw_edma_pcie_data snps_edda_data = {
@@ -90,6 +112,64 @@ struct dw_edma_pcie_data {
 	.rd_ch_cnt			= 2,
 };
 
+static const struct dw_edma_pcie_data xilinx_mdb_data = {
+	/* MDB registers location */
+	.rg.bar				= BAR_0,
+	.rg.off				= SZ_4K,	/*  4 Kbytes */
+	.rg.sz				= SZ_8K,	/*  8 Kbytes */
+
+	/* Other */
+	.mf				= EDMA_MF_HDMA_NATIVE,
+	.irqs				= 1,
+	.wr_ch_cnt			= 8,
+	.rd_ch_cnt			= 8,
+};
+
+static void dw_edma_set_chan_region_offset(struct dw_edma_pcie_data *pdata,
+					   enum pci_barno bar, off_t start_off,
+					   off_t ll_off_gap, size_t ll_size,
+					   off_t dt_off_gap, size_t dt_size)
+{
+	u16 wr_ch = pdata->wr_ch_cnt;
+	u16 rd_ch = pdata->rd_ch_cnt;
+	off_t off;
+	u16 i;
+
+	off = start_off;
+
+	/* Write channel LL region */
+	for (i = 0; i < wr_ch; i++) {
+		pdata->ll_wr[i].bar = bar;
+		pdata->ll_wr[i].off = off;
+		pdata->ll_wr[i].sz = ll_size;
+		off += ll_off_gap;
+	}
+
+	/* Read channel LL region */
+	for (i = 0; i < rd_ch; i++) {
+		pdata->ll_rd[i].bar = bar;
+		pdata->ll_rd[i].off = off;
+		pdata->ll_rd[i].sz = ll_size;
+		off += ll_off_gap;
+	}
+
+	/* Write channel data region */
+	for (i = 0; i < wr_ch; i++) {
+		pdata->dt_wr[i].bar = bar;
+		pdata->dt_wr[i].off = off;
+		pdata->dt_wr[i].sz = dt_size;
+		off += dt_off_gap;
+	}
+
+	/* Read channel data region */
+	for (i = 0; i < rd_ch; i++) {
+		pdata->dt_rd[i].bar = bar;
+		pdata->dt_rd[i].off = off;
+		pdata->dt_rd[i].sz = dt_size;
+		off += dt_off_gap;
+	}
+}
+
 static int dw_edma_pcie_irq_vector(struct device *dev, unsigned int nr)
 {
 	return pci_irq_vector(to_pci_dev(dev), nr);
@@ -114,15 +194,15 @@ static u64 dw_edma_pcie_address(struct device *dev, phys_addr_t cpu_addr)
 	.pci_address = dw_edma_pcie_address,
 };
 
-static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
-					   struct dw_edma_pcie_data *pdata)
+static void dw_edma_pcie_get_synopsys_dma_data(struct pci_dev *pdev,
+					       struct dw_edma_pcie_data *pdata)
 {
 	u32 val, map;
 	u16 vsec;
 	u64 off;
 
 	vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_SYNOPSYS,
-					DW_PCIE_VSEC_DMA_ID);
+					DW_PCIE_SYNOPSYS_VSEC_DMA_ID);
 	if (!vsec)
 		return;
 
@@ -131,9 +211,9 @@ static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
 	    PCI_VNDR_HEADER_LEN(val) != 0x18)
 		return;
 
-	pci_dbg(pdev, "Detected PCIe Vendor-Specific Extended Capability DMA\n");
+	pci_dbg(pdev, "Detected Synopsys PCIe Vendor-Specific Extended Capability DMA\n");
 	pci_read_config_dword(pdev, vsec + 0x8, &val);
-	map = FIELD_GET(DW_PCIE_VSEC_DMA_MAP, val);
+	map = FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_MAP, val);
 	if (map != EDMA_MF_EDMA_LEGACY &&
 	    map != EDMA_MF_EDMA_UNROLL &&
 	    map != EDMA_MF_HDMA_COMPAT &&
@@ -141,13 +221,13 @@ static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
 		return;
 
 	pdata->mf = map;
-	pdata->rg.bar = FIELD_GET(DW_PCIE_VSEC_DMA_BAR, val);
+	pdata->rg.bar = FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_BAR, val);
 
 	pci_read_config_dword(pdev, vsec + 0xc, &val);
 	pdata->wr_ch_cnt = min_t(u16, pdata->wr_ch_cnt,
-				 FIELD_GET(DW_PCIE_VSEC_DMA_WR_CH, val));
+				 FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_WR_CH, val));
 	pdata->rd_ch_cnt = min_t(u16, pdata->rd_ch_cnt,
-				 FIELD_GET(DW_PCIE_VSEC_DMA_RD_CH, val));
+				 FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_RD_CH, val));
 
 	pci_read_config_dword(pdev, vsec + 0x14, &val);
 	off = val;
@@ -157,6 +237,67 @@ static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
 	pdata->rg.off = off;
 }
 
+static void dw_edma_pcie_get_xilinx_dma_data(struct pci_dev *pdev,
+					     struct dw_edma_pcie_data *pdata)
+{
+	u32 val, map;
+	u16 vsec;
+	u64 off;
+
+	pdata->devmem_phys_off = DW_PCIE_XILINX_MDB_INVALID_ADDR;
+
+	vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_XILINX,
+					DW_PCIE_XILINX_MDB_VSEC_DMA_ID);
+	if (!vsec)
+		return;
+
+	pci_read_config_dword(pdev, vsec + PCI_VNDR_HEADER, &val);
+	if (PCI_VNDR_HEADER_REV(val) != 0x00 ||
+	    PCI_VNDR_HEADER_LEN(val) != 0x18)
+		return;
+
+	pci_dbg(pdev, "Detected Xilinx PCIe Vendor-Specific Extended Capability DMA\n");
+	pci_read_config_dword(pdev, vsec + 0x8, &val);
+	map = FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_MAP, val);
+	if (map != EDMA_MF_EDMA_LEGACY &&
+	    map != EDMA_MF_EDMA_UNROLL &&
+	    map != EDMA_MF_HDMA_COMPAT &&
+	    map != EDMA_MF_HDMA_NATIVE)
+		return;
+
+	pdata->mf = map;
+	pdata->rg.bar = FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_BAR, val);
+
+	pci_read_config_dword(pdev, vsec + 0xc, &val);
+	pdata->wr_ch_cnt = min_t(u16, pdata->wr_ch_cnt,
+				 FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_WR_CH, val));
+	pdata->rd_ch_cnt = min_t(u16, pdata->rd_ch_cnt,
+				 FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_RD_CH, val));
+
+	pci_read_config_dword(pdev, vsec + 0x14, &val);
+	off = val;
+	pci_read_config_dword(pdev, vsec + 0x10, &val);
+	off <<= 32;
+	off |= val;
+	pdata->rg.off = off;
+
+	vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_XILINX,
+					DW_PCIE_XILINX_MDB_VSEC_ID);
+	if (!vsec)
+		return;
+
+	pci_read_config_dword(pdev,
+			      vsec + DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_HIGH,
+			      &val);
+	off = val;
+	pci_read_config_dword(pdev,
+			      vsec + DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_LOW,
+			      &val);
+	off <<= 32;
+	off |= val;
+	pdata->devmem_phys_off = off;
+}
+
 static int dw_edma_pcie_probe(struct pci_dev *pdev,
 			      const struct pci_device_id *pid)
 {
@@ -184,7 +325,28 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
 	 * Tries to find if exists a PCIe Vendor-Specific Extended Capability
 	 * for the DMA, if one exists, then reconfigures it.
 	 */
-	dw_edma_pcie_get_vsec_dma_data(pdev, vsec_data);
+	dw_edma_pcie_get_synopsys_dma_data(pdev, vsec_data);
+	dw_edma_pcie_get_xilinx_dma_data(pdev, vsec_data);
+
+	if (pdev->vendor == PCI_VENDOR_ID_XILINX) {
+		/*
+		 * There is no valid address found for the LL memory
+		 * space on the device side.
+		 */
+		if (vsec_data->devmem_phys_off == DW_PCIE_XILINX_MDB_INVALID_ADDR)
+			return -ENOMEM;
+
+		/*
+		 * Configure the channel LL and data blocks if number of
+		 * channels enabled in VSEC capability are more than the
+		 * channels configured in xilinx_mdb_data.
+		 */
+		dw_edma_set_chan_region_offset(vsec_data, BAR_2, 0,
+					       DW_PCIE_XILINX_MDB_LL_OFF_GAP,
+					       DW_PCIE_XILINX_MDB_LL_SIZE,
+					       DW_PCIE_XILINX_MDB_DT_OFF_GAP,
+					       DW_PCIE_XILINX_MDB_DT_SIZE);
+	}
 
 	/* Mapping PCI BAR regions */
 	mask = BIT(vsec_data->rg.bar);
@@ -367,6 +529,8 @@ static void dw_edma_pcie_remove(struct pci_dev *pdev)
 
 static const struct pci_device_id dw_edma_pcie_id_table[] = {
 	{ PCI_DEVICE_DATA(SYNOPSYS, EDDA, &snps_edda_data) },
+	{ PCI_VDEVICE(XILINX, PCI_DEVICE_ID_XILINX_B054),
+	  (kernel_ulong_t)&xilinx_mdb_data },
 	{ }
 };
 MODULE_DEVICE_TABLE(pci, dw_edma_pcie_id_table);
-- 
1.8.3.1


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

* [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
  2026-01-09 12:03 [PATCH v8 0/2] Add AMD MDB Endpoint and non-LL mode Support Devendra K Verma
  2026-01-09 12:03 ` [PATCH v8 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint Support Devendra K Verma
@ 2026-01-09 12:03 ` Devendra K Verma
  2026-01-15 16:37   ` Frank Li
                     ` (2 more replies)
  1 sibling, 3 replies; 24+ messages in thread
From: Devendra K Verma @ 2026-01-09 12:03 UTC (permalink / raw)
  To: bhelgaas, mani, vkoul
  Cc: dmaengine, linux-pci, linux-kernel, michal.simek, Devendra.Verma

AMD MDB IP supports Linked List (LL) mode as well as non-LL mode.
The current code does not have the mechanisms to enable the
DMA transactions using the non-LL mode. The following two cases
are added with this patch:
- For the AMD (Xilinx) only, when a valid physical base address of
  the device side DDR is not configured, then the IP can still be
  used in non-LL mode. For all the channels DMA transactions will
  be using the non-LL mode only. This, the default non-LL mode,
  is not applicable for Synopsys IP with the current code addition.

- If the default mode is LL-mode, for both AMD (Xilinx) and Synosys,
  and if user wants to use non-LL mode then user can do so via
  configuring the peripheral_config param of dma_slave_config.

Signed-off-by: Devendra K Verma <devendra.verma@amd.com>
---
Changes in v8
  Cosmetic change related to comment and code.

Changes in v7
  No change

Changes in v6
  Gave definition to bits used for channel configuration.
  Removed the comment related to doorbell.

Changes in v5
  Variable name 'nollp' changed to 'non_ll'.
  In the dw_edma_device_config() WARN_ON replaced with dev_err().
  Comments follow the 80-column guideline.

Changes in v4
  No change

Changes in v3
  No change

Changes in v2
  Reverted the function return type to u64 for
  dw_edma_get_phys_addr().

Changes in v1
  Changed the function return type for dw_edma_get_phys_addr().
  Corrected the typo raised in review.
---
 drivers/dma/dw-edma/dw-edma-core.c    | 42 +++++++++++++++++++++---
 drivers/dma/dw-edma/dw-edma-core.h    |  1 +
 drivers/dma/dw-edma/dw-edma-pcie.c    | 46 ++++++++++++++++++--------
 drivers/dma/dw-edma/dw-hdma-v0-core.c | 61 ++++++++++++++++++++++++++++++++++-
 drivers/dma/dw-edma/dw-hdma-v0-regs.h |  1 +
 include/linux/dma/edma.h              |  1 +
 6 files changed, 132 insertions(+), 20 deletions(-)

diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index b43255f..d37112b 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -223,8 +223,32 @@ static int dw_edma_device_config(struct dma_chan *dchan,
 				 struct dma_slave_config *config)
 {
 	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
+	int non_ll = 0;
+
+	if (config->peripheral_config &&
+	    config->peripheral_size != sizeof(int)) {
+		dev_err(dchan->device->dev,
+			"config param peripheral size mismatch\n");
+		return -EINVAL;
+	}
 
 	memcpy(&chan->config, config, sizeof(*config));
+
+	/*
+	 * When there is no valid LLP base address available then the default
+	 * DMA ops will use the non-LL mode.
+	 *
+	 * Cases where LL mode is enabled and client wants to use the non-LL
+	 * mode then also client can do so via providing the peripheral_config
+	 * param.
+	 */
+	if (config->peripheral_config)
+		non_ll = *(int *)config->peripheral_config;
+
+	chan->non_ll = false;
+	if (chan->dw->chip->non_ll || (!chan->dw->chip->non_ll && non_ll))
+		chan->non_ll = true;
+
 	chan->configured = true;
 
 	return 0;
@@ -353,7 +377,7 @@ static void dw_edma_device_issue_pending(struct dma_chan *dchan)
 	struct dw_edma_chan *chan = dchan2dw_edma_chan(xfer->dchan);
 	enum dma_transfer_direction dir = xfer->direction;
 	struct scatterlist *sg = NULL;
-	struct dw_edma_chunk *chunk;
+	struct dw_edma_chunk *chunk = NULL;
 	struct dw_edma_burst *burst;
 	struct dw_edma_desc *desc;
 	u64 src_addr, dst_addr;
@@ -419,9 +443,11 @@ static void dw_edma_device_issue_pending(struct dma_chan *dchan)
 	if (unlikely(!desc))
 		goto err_alloc;
 
-	chunk = dw_edma_alloc_chunk(desc);
-	if (unlikely(!chunk))
-		goto err_alloc;
+	if (!chan->non_ll) {
+		chunk = dw_edma_alloc_chunk(desc);
+		if (unlikely(!chunk))
+			goto err_alloc;
+	}
 
 	if (xfer->type == EDMA_XFER_INTERLEAVED) {
 		src_addr = xfer->xfer.il->src_start;
@@ -450,7 +476,13 @@ static void dw_edma_device_issue_pending(struct dma_chan *dchan)
 		if (xfer->type == EDMA_XFER_SCATTER_GATHER && !sg)
 			break;
 
-		if (chunk->bursts_alloc == chan->ll_max) {
+		/*
+		 * For non-LL mode, only a single burst can be handled
+		 * in a single chunk unlike LL mode where multiple bursts
+		 * can be configured in a single chunk.
+		 */
+		if ((chunk && chunk->bursts_alloc == chan->ll_max) ||
+		    chan->non_ll) {
 			chunk = dw_edma_alloc_chunk(desc);
 			if (unlikely(!chunk))
 				goto err_alloc;
diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
index 71894b9..c8e3d19 100644
--- a/drivers/dma/dw-edma/dw-edma-core.h
+++ b/drivers/dma/dw-edma/dw-edma-core.h
@@ -86,6 +86,7 @@ struct dw_edma_chan {
 	u8				configured;
 
 	struct dma_slave_config		config;
+	bool				non_ll;
 };
 
 struct dw_edma_irq {
diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c b/drivers/dma/dw-edma/dw-edma-pcie.c
index 2efd149..277ca50 100644
--- a/drivers/dma/dw-edma/dw-edma-pcie.c
+++ b/drivers/dma/dw-edma/dw-edma-pcie.c
@@ -298,6 +298,15 @@ static void dw_edma_pcie_get_xilinx_dma_data(struct pci_dev *pdev,
 	pdata->devmem_phys_off = off;
 }
 
+static u64 dw_edma_get_phys_addr(struct pci_dev *pdev,
+				 struct dw_edma_pcie_data *pdata,
+				 enum pci_barno bar)
+{
+	if (pdev->vendor == PCI_VENDOR_ID_XILINX)
+		return pdata->devmem_phys_off;
+	return pci_bus_address(pdev, bar);
+}
+
 static int dw_edma_pcie_probe(struct pci_dev *pdev,
 			      const struct pci_device_id *pid)
 {
@@ -307,6 +316,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
 	struct dw_edma_chip *chip;
 	int err, nr_irqs;
 	int i, mask;
+	bool non_ll = false;
 
 	vsec_data = kmalloc(sizeof(*vsec_data), GFP_KERNEL);
 	if (!vsec_data)
@@ -331,21 +341,24 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
 	if (pdev->vendor == PCI_VENDOR_ID_XILINX) {
 		/*
 		 * There is no valid address found for the LL memory
-		 * space on the device side.
+		 * space on the device side. In the absence of LL base
+		 * address use the non-LL mode or simple mode supported by
+		 * the HDMA IP.
 		 */
-		if (vsec_data->devmem_phys_off == DW_PCIE_XILINX_MDB_INVALID_ADDR)
-			return -ENOMEM;
+		if (vsec_data->devmem_phys_off == DW_PCIE_AMD_MDB_INVALID_ADDR)
+			non_ll = true;
 
 		/*
 		 * Configure the channel LL and data blocks if number of
 		 * channels enabled in VSEC capability are more than the
 		 * channels configured in xilinx_mdb_data.
 		 */
-		dw_edma_set_chan_region_offset(vsec_data, BAR_2, 0,
-					       DW_PCIE_XILINX_MDB_LL_OFF_GAP,
-					       DW_PCIE_XILINX_MDB_LL_SIZE,
-					       DW_PCIE_XILINX_MDB_DT_OFF_GAP,
-					       DW_PCIE_XILINX_MDB_DT_SIZE);
+		if (!non_ll)
+			dw_edma_set_chan_region_offset(vsec_data, BAR_2, 0,
+						       DW_PCIE_XILINX_LL_OFF_GAP,
+						       DW_PCIE_XILINX_LL_SIZE,
+						       DW_PCIE_XILINX_DT_OFF_GAP,
+						       DW_PCIE_XILINX_DT_SIZE);
 	}
 
 	/* Mapping PCI BAR regions */
@@ -393,6 +406,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
 	chip->mf = vsec_data->mf;
 	chip->nr_irqs = nr_irqs;
 	chip->ops = &dw_edma_pcie_plat_ops;
+	chip->non_ll = non_ll;
 
 	chip->ll_wr_cnt = vsec_data->wr_ch_cnt;
 	chip->ll_rd_cnt = vsec_data->rd_ch_cnt;
@@ -401,7 +415,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
 	if (!chip->reg_base)
 		return -ENOMEM;
 
-	for (i = 0; i < chip->ll_wr_cnt; i++) {
+	for (i = 0; i < chip->ll_wr_cnt && !non_ll; i++) {
 		struct dw_edma_region *ll_region = &chip->ll_region_wr[i];
 		struct dw_edma_region *dt_region = &chip->dt_region_wr[i];
 		struct dw_edma_block *ll_block = &vsec_data->ll_wr[i];
@@ -412,7 +426,8 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
 			return -ENOMEM;
 
 		ll_region->vaddr.io += ll_block->off;
-		ll_region->paddr = pci_bus_address(pdev, ll_block->bar);
+		ll_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
+							 ll_block->bar);
 		ll_region->paddr += ll_block->off;
 		ll_region->sz = ll_block->sz;
 
@@ -421,12 +436,13 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
 			return -ENOMEM;
 
 		dt_region->vaddr.io += dt_block->off;
-		dt_region->paddr = pci_bus_address(pdev, dt_block->bar);
+		dt_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
+							 dt_block->bar);
 		dt_region->paddr += dt_block->off;
 		dt_region->sz = dt_block->sz;
 	}
 
-	for (i = 0; i < chip->ll_rd_cnt; i++) {
+	for (i = 0; i < chip->ll_rd_cnt && !non_ll; i++) {
 		struct dw_edma_region *ll_region = &chip->ll_region_rd[i];
 		struct dw_edma_region *dt_region = &chip->dt_region_rd[i];
 		struct dw_edma_block *ll_block = &vsec_data->ll_rd[i];
@@ -437,7 +453,8 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
 			return -ENOMEM;
 
 		ll_region->vaddr.io += ll_block->off;
-		ll_region->paddr = pci_bus_address(pdev, ll_block->bar);
+		ll_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
+							 ll_block->bar);
 		ll_region->paddr += ll_block->off;
 		ll_region->sz = ll_block->sz;
 
@@ -446,7 +463,8 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
 			return -ENOMEM;
 
 		dt_region->vaddr.io += dt_block->off;
-		dt_region->paddr = pci_bus_address(pdev, dt_block->bar);
+		dt_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
+							 dt_block->bar);
 		dt_region->paddr += dt_block->off;
 		dt_region->sz = dt_block->sz;
 	}
diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.c b/drivers/dma/dw-edma/dw-hdma-v0-core.c
index e3f8db4..a5d12bc 100644
--- a/drivers/dma/dw-edma/dw-hdma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c
@@ -225,7 +225,7 @@ static void dw_hdma_v0_sync_ll_data(struct dw_edma_chunk *chunk)
 		readl(chunk->ll_region.vaddr.io);
 }
 
-static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
+static void dw_hdma_v0_core_ll_start(struct dw_edma_chunk *chunk, bool first)
 {
 	struct dw_edma_chan *chan = chunk->chan;
 	struct dw_edma *dw = chan->dw;
@@ -263,6 +263,65 @@ static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
 	SET_CH_32(dw, chan->dir, chan->id, doorbell, HDMA_V0_DOORBELL_START);
 }
 
+static void dw_hdma_v0_core_non_ll_start(struct dw_edma_chunk *chunk)
+{
+	struct dw_edma_chan *chan = chunk->chan;
+	struct dw_edma *dw = chan->dw;
+	struct dw_edma_burst *child;
+	u32 val;
+
+	list_for_each_entry(child, &chunk->burst->list, list) {
+		SET_CH_32(dw, chan->dir, chan->id, ch_en, HDMA_V0_CH_EN);
+
+		/* Source address */
+		SET_CH_32(dw, chan->dir, chan->id, sar.lsb,
+			  lower_32_bits(child->sar));
+		SET_CH_32(dw, chan->dir, chan->id, sar.msb,
+			  upper_32_bits(child->sar));
+
+		/* Destination address */
+		SET_CH_32(dw, chan->dir, chan->id, dar.lsb,
+			  lower_32_bits(child->dar));
+		SET_CH_32(dw, chan->dir, chan->id, dar.msb,
+			  upper_32_bits(child->dar));
+
+		/* Transfer size */
+		SET_CH_32(dw, chan->dir, chan->id, transfer_size, child->sz);
+
+		/* Interrupt setup */
+		val = GET_CH_32(dw, chan->dir, chan->id, int_setup) |
+				HDMA_V0_STOP_INT_MASK |
+				HDMA_V0_ABORT_INT_MASK |
+				HDMA_V0_LOCAL_STOP_INT_EN |
+				HDMA_V0_LOCAL_ABORT_INT_EN;
+
+		if (!(dw->chip->flags & DW_EDMA_CHIP_LOCAL)) {
+			val |= HDMA_V0_REMOTE_STOP_INT_EN |
+			       HDMA_V0_REMOTE_ABORT_INT_EN;
+		}
+
+		SET_CH_32(dw, chan->dir, chan->id, int_setup, val);
+
+		/* Channel control setup */
+		val = GET_CH_32(dw, chan->dir, chan->id, control1);
+		val &= ~HDMA_V0_LINKLIST_EN;
+		SET_CH_32(dw, chan->dir, chan->id, control1, val);
+
+		SET_CH_32(dw, chan->dir, chan->id, doorbell,
+			  HDMA_V0_DOORBELL_START);
+	}
+}
+
+static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
+{
+	struct dw_edma_chan *chan = chunk->chan;
+
+	if (chan->non_ll)
+		dw_hdma_v0_core_non_ll_start(chunk);
+	else
+		dw_hdma_v0_core_ll_start(chunk, first);
+}
+
 static void dw_hdma_v0_core_ch_config(struct dw_edma_chan *chan)
 {
 	struct dw_edma *dw = chan->dw;
diff --git a/drivers/dma/dw-edma/dw-hdma-v0-regs.h b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
index eab5fd7..7759ba9 100644
--- a/drivers/dma/dw-edma/dw-hdma-v0-regs.h
+++ b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
@@ -12,6 +12,7 @@
 #include <linux/dmaengine.h>
 
 #define HDMA_V0_MAX_NR_CH			8
+#define HDMA_V0_CH_EN				BIT(0)
 #define HDMA_V0_LOCAL_ABORT_INT_EN		BIT(6)
 #define HDMA_V0_REMOTE_ABORT_INT_EN		BIT(5)
 #define HDMA_V0_LOCAL_STOP_INT_EN		BIT(4)
diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h
index 3080747..78ce31b 100644
--- a/include/linux/dma/edma.h
+++ b/include/linux/dma/edma.h
@@ -99,6 +99,7 @@ struct dw_edma_chip {
 	enum dw_edma_map_format	mf;
 
 	struct dw_edma		*dw;
+	bool			non_ll;
 };
 
 /* Export to the platform drivers */
-- 
1.8.3.1


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

* Re: [PATCH v8 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint Support
  2026-01-09 12:03 ` [PATCH v8 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint Support Devendra K Verma
@ 2026-01-15 16:21   ` Frank Li
  2026-01-19  9:09     ` Verma, Devendra
  0 siblings, 1 reply; 24+ messages in thread
From: Frank Li @ 2026-01-15 16:21 UTC (permalink / raw)
  To: Devendra K Verma
  Cc: bhelgaas, mani, vkoul, dmaengine, linux-pci, linux-kernel,
	michal.simek

On Fri, Jan 09, 2026 at 05:33:53PM +0530, Devendra K Verma wrote:
> AMD MDB PCIe endpoint support. For AMD specific support
> added the following
>   - AMD supported PCIe Device IDs and Vendor ID (Xilinx).
>   - AMD MDB specific driver data
>   - AMD MDB specific VSEC capability to retrieve the device DDR
>     base address.
>
> Signed-off-by: Devendra K Verma <devendra.verma@amd.com>
> ---
> Changes in v8:
> Changed the contant names to includer product vendor.
> Moved the vendor specific code to vendor specific functions.
>
> Changes in v7:
> Introduced vendor specific functions to retrieve the
> vsec data.
>
> Changes in v6:
> Included "sizes.h" header and used the appropriate
> definitions instead of constants.
>
> Changes in v5:
> Added the definitions for Xilinx specific VSEC header id,
> revision, and register offsets.
> Corrected the error type when no physical offset found for
> device side memory.
> Corrected the order of variables.
>
> Changes in v4:
> Configured 8 read and 8 write channels for Xilinx vendor
> Added checks to validate vendor ID for vendor
> specific vsec id.
> Added Xilinx specific vendor id for vsec specific to Xilinx
> Added the LL and data region offsets, size as input params to
> function dw_edma_set_chan_region_offset().
> Moved the LL and data region offsets assignment to function
> for Xilinx specific case.
> Corrected comments.
>
> Changes in v3:
> Corrected a typo when assigning AMD (Xilinx) vsec id macro
> and condition check.
>
> Changes in v2:
> Reverted the devmem_phys_off type to u64.
> Renamed the function appropriately to suit the
> functionality for setting the LL & data region offsets.
>
> Changes in v1:
> Removed the pci device id from pci_ids.h file.
> Added the vendor id macro as per the suggested method.
> Changed the type of the newly added devmem_phys_off variable.
> Added to logic to assign offsets for LL and data region blocks
> in case more number of channels are enabled than given in
> amd_mdb_data struct.
> ---
>  drivers/dma/dw-edma/dw-edma-pcie.c | 192 ++++++++++++++++++++++++++++++++++---
>  1 file changed, 178 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c b/drivers/dma/dw-edma/dw-edma-pcie.c
> index 3371e0a7..2efd149 100644
> --- a/drivers/dma/dw-edma/dw-edma-pcie.c
> +++ b/drivers/dma/dw-edma/dw-edma-pcie.c
> @@ -14,14 +14,35 @@
>  #include <linux/pci-epf.h>
>  #include <linux/msi.h>
>  #include <linux/bitfield.h>
> +#include <linux/sizes.h>
>
>  #include "dw-edma-core.h"
>
> -#define DW_PCIE_VSEC_DMA_ID			0x6
> -#define DW_PCIE_VSEC_DMA_BAR			GENMASK(10, 8)
> -#define DW_PCIE_VSEC_DMA_MAP			GENMASK(2, 0)
> -#define DW_PCIE_VSEC_DMA_WR_CH			GENMASK(9, 0)
> -#define DW_PCIE_VSEC_DMA_RD_CH			GENMASK(25, 16)
> +/* Synopsys */
> +#define DW_PCIE_SYNOPSYS_VSEC_DMA_ID		0x6
> +#define DW_PCIE_SYNOPSYS_VSEC_DMA_BAR		GENMASK(10, 8)
> +#define DW_PCIE_SYNOPSYS_VSEC_DMA_MAP		GENMASK(2, 0)
> +#define DW_PCIE_SYNOPSYS_VSEC_DMA_WR_CH		GENMASK(9, 0)
> +#define DW_PCIE_SYNOPSYS_VSEC_DMA_RD_CH		GENMASK(25, 16)

Sorry, jump into at v8.
According to my understand 'DW' means 'Synopsys'.

> +
> +/* AMD MDB (Xilinx) specific defines */
> +#define PCI_DEVICE_ID_XILINX_B054		0xb054
> +
> +#define DW_PCIE_XILINX_MDB_VSEC_DMA_ID		0x6
> +#define DW_PCIE_XILINX_MDB_VSEC_ID		0x20
> +#define DW_PCIE_XILINX_MDB_VSEC_DMA_BAR		GENMASK(10, 8)
> +#define DW_PCIE_XILINX_MDB_VSEC_DMA_MAP		GENMASK(2, 0)
> +#define DW_PCIE_XILINX_MDB_VSEC_DMA_WR_CH	GENMASK(9, 0)
> +#define DW_PCIE_XILINX_MDB_VSEC_DMA_RD_CH	GENMASK(25, 16)

These defination is the same. Need redefine again

> +
> +#define DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_HIGH	0xc
> +#define DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_LOW	0x8
> +#define DW_PCIE_XILINX_MDB_INVALID_ADDR		(~0ULL)

I think XILINX_PCIE_MDB_DEVMEM_OFF_REG_HIGH

> +
> +#define DW_PCIE_XILINX_MDB_LL_OFF_GAP		0x200000
> +#define DW_PCIE_XILINX_MDB_LL_SIZE		0x800
> +#define DW_PCIE_XILINX_MDB_DT_OFF_GAP		0x100000
> +#define DW_PCIE_XILINX_MDB_DT_SIZE		0x800
>
>  #define DW_BLOCK(a, b, c) \
>  	{ \
> @@ -50,6 +71,7 @@ struct dw_edma_pcie_data {
>  	u8				irqs;
>  	u16				wr_ch_cnt;
>  	u16				rd_ch_cnt;
> +	u64				devmem_phys_off;
>  };
>
>  static const struct dw_edma_pcie_data snps_edda_data = {
> @@ -90,6 +112,64 @@ struct dw_edma_pcie_data {
>  	.rd_ch_cnt			= 2,
>  };
>
> +static const struct dw_edma_pcie_data xilinx_mdb_data = {
> +	/* MDB registers location */
> +	.rg.bar				= BAR_0,
> +	.rg.off				= SZ_4K,	/*  4 Kbytes */
> +	.rg.sz				= SZ_8K,	/*  8 Kbytes */
> +
> +	/* Other */
> +	.mf				= EDMA_MF_HDMA_NATIVE,
> +	.irqs				= 1,
> +	.wr_ch_cnt			= 8,
> +	.rd_ch_cnt			= 8,
> +};
> +
> +static void dw_edma_set_chan_region_offset(struct dw_edma_pcie_data *pdata,
> +					   enum pci_barno bar, off_t start_off,
> +					   off_t ll_off_gap, size_t ll_size,
> +					   off_t dt_off_gap, size_t dt_size)
> +{
> +	u16 wr_ch = pdata->wr_ch_cnt;
> +	u16 rd_ch = pdata->rd_ch_cnt;
> +	off_t off;
> +	u16 i;
> +
> +	off = start_off;
> +
> +	/* Write channel LL region */
> +	for (i = 0; i < wr_ch; i++) {
> +		pdata->ll_wr[i].bar = bar;
> +		pdata->ll_wr[i].off = off;
> +		pdata->ll_wr[i].sz = ll_size;
> +		off += ll_off_gap;
> +	}
> +
> +	/* Read channel LL region */
> +	for (i = 0; i < rd_ch; i++) {
> +		pdata->ll_rd[i].bar = bar;
> +		pdata->ll_rd[i].off = off;
> +		pdata->ll_rd[i].sz = ll_size;
> +		off += ll_off_gap;
> +	}
> +
> +	/* Write channel data region */
> +	for (i = 0; i < wr_ch; i++) {
> +		pdata->dt_wr[i].bar = bar;
> +		pdata->dt_wr[i].off = off;
> +		pdata->dt_wr[i].sz = dt_size;
> +		off += dt_off_gap;
> +	}
> +
> +	/* Read channel data region */
> +	for (i = 0; i < rd_ch; i++) {
> +		pdata->dt_rd[i].bar = bar;
> +		pdata->dt_rd[i].off = off;
> +		pdata->dt_rd[i].sz = dt_size;
> +		off += dt_off_gap;
> +	}
> +}
> +
>  static int dw_edma_pcie_irq_vector(struct device *dev, unsigned int nr)
>  {
>  	return pci_irq_vector(to_pci_dev(dev), nr);
> @@ -114,15 +194,15 @@ static u64 dw_edma_pcie_address(struct device *dev, phys_addr_t cpu_addr)
>  	.pci_address = dw_edma_pcie_address,
>  };
>
> -static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
> -					   struct dw_edma_pcie_data *pdata)
> +static void dw_edma_pcie_get_synopsys_dma_data(struct pci_dev *pdev,
> +					       struct dw_edma_pcie_data *pdata)
>  {
>  	u32 val, map;
>  	u16 vsec;
>  	u64 off;
>
>  	vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_SYNOPSYS,
> -					DW_PCIE_VSEC_DMA_ID);
> +					DW_PCIE_SYNOPSYS_VSEC_DMA_ID);
>  	if (!vsec)
>  		return;
>
> @@ -131,9 +211,9 @@ static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
>  	    PCI_VNDR_HEADER_LEN(val) != 0x18)
>  		return;
>
> -	pci_dbg(pdev, "Detected PCIe Vendor-Specific Extended Capability DMA\n");
> +	pci_dbg(pdev, "Detected Synopsys PCIe Vendor-Specific Extended Capability DMA\n");
>  	pci_read_config_dword(pdev, vsec + 0x8, &val);
> -	map = FIELD_GET(DW_PCIE_VSEC_DMA_MAP, val);
> +	map = FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_MAP, val);
>  	if (map != EDMA_MF_EDMA_LEGACY &&
>  	    map != EDMA_MF_EDMA_UNROLL &&
>  	    map != EDMA_MF_HDMA_COMPAT &&
> @@ -141,13 +221,13 @@ static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
>  		return;
>
>  	pdata->mf = map;
> -	pdata->rg.bar = FIELD_GET(DW_PCIE_VSEC_DMA_BAR, val);
> +	pdata->rg.bar = FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_BAR, val);
>
>  	pci_read_config_dword(pdev, vsec + 0xc, &val);
>  	pdata->wr_ch_cnt = min_t(u16, pdata->wr_ch_cnt,
> -				 FIELD_GET(DW_PCIE_VSEC_DMA_WR_CH, val));
> +				 FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_WR_CH, val));
>  	pdata->rd_ch_cnt = min_t(u16, pdata->rd_ch_cnt,
> -				 FIELD_GET(DW_PCIE_VSEC_DMA_RD_CH, val));
> +				 FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_RD_CH, val));

If you don't change macro name, these change is not necessary. If really
need change macro name, make change macro name as sperated patch.

>
>  	pci_read_config_dword(pdev, vsec + 0x14, &val);
>  	off = val;
> @@ -157,6 +237,67 @@ static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
>  	pdata->rg.off = off;
>  }
>
> +static void dw_edma_pcie_get_xilinx_dma_data(struct pci_dev *pdev,
> +					     struct dw_edma_pcie_data *pdata)
> +{
> +	u32 val, map;
> +	u16 vsec;
> +	u64 off;
> +
> +	pdata->devmem_phys_off = DW_PCIE_XILINX_MDB_INVALID_ADDR;
> +
> +	vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_XILINX,
> +					DW_PCIE_XILINX_MDB_VSEC_DMA_ID);
> +	if (!vsec)
> +		return;
> +
> +	pci_read_config_dword(pdev, vsec + PCI_VNDR_HEADER, &val);
> +	if (PCI_VNDR_HEADER_REV(val) != 0x00 ||
> +	    PCI_VNDR_HEADER_LEN(val) != 0x18)
> +		return;
> +
> +	pci_dbg(pdev, "Detected Xilinx PCIe Vendor-Specific Extended Capability DMA\n");
> +	pci_read_config_dword(pdev, vsec + 0x8, &val);
> +	map = FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_MAP, val);
> +	if (map != EDMA_MF_EDMA_LEGACY &&
> +	    map != EDMA_MF_EDMA_UNROLL &&
> +	    map != EDMA_MF_HDMA_COMPAT &&
> +	    map != EDMA_MF_HDMA_NATIVE)
> +		return;
> +
> +	pdata->mf = map;
> +	pdata->rg.bar = FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_BAR, val);
> +
> +	pci_read_config_dword(pdev, vsec + 0xc, &val);
> +	pdata->wr_ch_cnt = min_t(u16, pdata->wr_ch_cnt,
> +				 FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_WR_CH, val));
> +	pdata->rd_ch_cnt = min_t(u16, pdata->rd_ch_cnt,
> +				 FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_RD_CH, val));
> +
> +	pci_read_config_dword(pdev, vsec + 0x14, &val);
> +	off = val;
> +	pci_read_config_dword(pdev, vsec + 0x10, &val);
> +	off <<= 32;
> +	off |= val;
> +	pdata->rg.off = off;
> +
> +	vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_XILINX,
> +					DW_PCIE_XILINX_MDB_VSEC_ID);
> +	if (!vsec)
> +		return;
> +
> +	pci_read_config_dword(pdev,
> +			      vsec + DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_HIGH,
> +			      &val);
> +	off = val;
> +	pci_read_config_dword(pdev,
> +			      vsec + DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_LOW,
> +			      &val);
> +	off <<= 32;
> +	off |= val;
> +	pdata->devmem_phys_off = off;
> +}
> +
>  static int dw_edma_pcie_probe(struct pci_dev *pdev,
>  			      const struct pci_device_id *pid)
>  {
> @@ -184,7 +325,28 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
>  	 * Tries to find if exists a PCIe Vendor-Specific Extended Capability
>  	 * for the DMA, if one exists, then reconfigures it.
>  	 */
> -	dw_edma_pcie_get_vsec_dma_data(pdev, vsec_data);
> +	dw_edma_pcie_get_synopsys_dma_data(pdev, vsec_data);
> +	dw_edma_pcie_get_xilinx_dma_data(pdev, vsec_data);
> +
> +	if (pdev->vendor == PCI_VENDOR_ID_XILINX) {

dw_edma_pcie_get_xilinx_dma_data() should be here.

Frank
> +		/*
> +		 * There is no valid address found for the LL memory
> +		 * space on the device side.
> +		 */
> +		if (vsec_data->devmem_phys_off == DW_PCIE_XILINX_MDB_INVALID_ADDR)
> +			return -ENOMEM;
> +
> +		/*
> +		 * Configure the channel LL and data blocks if number of
> +		 * channels enabled in VSEC capability are more than the
> +		 * channels configured in xilinx_mdb_data.
> +		 */
> +		dw_edma_set_chan_region_offset(vsec_data, BAR_2, 0,
> +					       DW_PCIE_XILINX_MDB_LL_OFF_GAP,
> +					       DW_PCIE_XILINX_MDB_LL_SIZE,
> +					       DW_PCIE_XILINX_MDB_DT_OFF_GAP,
> +					       DW_PCIE_XILINX_MDB_DT_SIZE);
> +	}
>
>  	/* Mapping PCI BAR regions */
>  	mask = BIT(vsec_data->rg.bar);
> @@ -367,6 +529,8 @@ static void dw_edma_pcie_remove(struct pci_dev *pdev)
>
>  static const struct pci_device_id dw_edma_pcie_id_table[] = {
>  	{ PCI_DEVICE_DATA(SYNOPSYS, EDDA, &snps_edda_data) },
> +	{ PCI_VDEVICE(XILINX, PCI_DEVICE_ID_XILINX_B054),
> +	  (kernel_ulong_t)&xilinx_mdb_data },
>  	{ }
>  };
>  MODULE_DEVICE_TABLE(pci, dw_edma_pcie_id_table);
> --
> 1.8.3.1
>

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

* Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
  2026-01-09 12:03 ` [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode Devendra K Verma
@ 2026-01-15 16:37   ` Frank Li
  2026-01-19  9:09     ` Verma, Devendra
  2026-01-16  2:36   ` kernel test robot
  2026-01-16  3:59   ` kernel test robot
  2 siblings, 1 reply; 24+ messages in thread
From: Frank Li @ 2026-01-15 16:37 UTC (permalink / raw)
  To: Devendra K Verma
  Cc: bhelgaas, mani, vkoul, dmaengine, linux-pci, linux-kernel,
	michal.simek

On Fri, Jan 09, 2026 at 05:33:54PM +0530, Devendra K Verma wrote:
> AMD MDB IP supports Linked List (LL) mode as well as non-LL mode.
> The current code does not have the mechanisms to enable the
> DMA transactions using the non-LL mode. The following two cases
> are added with this patch:
> - For the AMD (Xilinx) only, when a valid physical base address of
>   the device side DDR is not configured, then the IP can still be
>   used in non-LL mode. For all the channels DMA transactions will

If DDR have not configured, where DATA send to in device side by non-LL
mode.

>   be using the non-LL mode only. This, the default non-LL mode,
>   is not applicable for Synopsys IP with the current code addition.
>
> - If the default mode is LL-mode, for both AMD (Xilinx) and Synosys,
>   and if user wants to use non-LL mode then user can do so via
>   configuring the peripheral_config param of dma_slave_config.
>
> Signed-off-by: Devendra K Verma <devendra.verma@amd.com>
> ---
> Changes in v8
>   Cosmetic change related to comment and code.
>
> Changes in v7
>   No change
>
> Changes in v6
>   Gave definition to bits used for channel configuration.
>   Removed the comment related to doorbell.
>
> Changes in v5
>   Variable name 'nollp' changed to 'non_ll'.
>   In the dw_edma_device_config() WARN_ON replaced with dev_err().
>   Comments follow the 80-column guideline.
>
> Changes in v4
>   No change
>
> Changes in v3
>   No change
>
> Changes in v2
>   Reverted the function return type to u64 for
>   dw_edma_get_phys_addr().
>
> Changes in v1
>   Changed the function return type for dw_edma_get_phys_addr().
>   Corrected the typo raised in review.
> ---
>  drivers/dma/dw-edma/dw-edma-core.c    | 42 +++++++++++++++++++++---
>  drivers/dma/dw-edma/dw-edma-core.h    |  1 +
>  drivers/dma/dw-edma/dw-edma-pcie.c    | 46 ++++++++++++++++++--------
>  drivers/dma/dw-edma/dw-hdma-v0-core.c | 61 ++++++++++++++++++++++++++++++++++-
>  drivers/dma/dw-edma/dw-hdma-v0-regs.h |  1 +

edma-v0-core.c have not update, if don't support, at least need return
failure at dw_edma_device_config() when backend is eDMA.

>  include/linux/dma/edma.h              |  1 +
>  6 files changed, 132 insertions(+), 20 deletions(-)
>
> diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> index b43255f..d37112b 100644
> --- a/drivers/dma/dw-edma/dw-edma-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-core.c
> @@ -223,8 +223,32 @@ static int dw_edma_device_config(struct dma_chan *dchan,
>  				 struct dma_slave_config *config)
>  {
>  	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
> +	int non_ll = 0;
> +
> +	if (config->peripheral_config &&
> +	    config->peripheral_size != sizeof(int)) {
> +		dev_err(dchan->device->dev,
> +			"config param peripheral size mismatch\n");
> +		return -EINVAL;
> +	}
>
>  	memcpy(&chan->config, config, sizeof(*config));
> +
> +	/*
> +	 * When there is no valid LLP base address available then the default
> +	 * DMA ops will use the non-LL mode.
> +	 *
> +	 * Cases where LL mode is enabled and client wants to use the non-LL
> +	 * mode then also client can do so via providing the peripheral_config
> +	 * param.
> +	 */
> +	if (config->peripheral_config)
> +		non_ll = *(int *)config->peripheral_config;
> +
> +	chan->non_ll = false;
> +	if (chan->dw->chip->non_ll || (!chan->dw->chip->non_ll && non_ll))
> +		chan->non_ll = true;
> +
>  	chan->configured = true;
>
>  	return 0;
> @@ -353,7 +377,7 @@ static void dw_edma_device_issue_pending(struct dma_chan *dchan)
>  	struct dw_edma_chan *chan = dchan2dw_edma_chan(xfer->dchan);
>  	enum dma_transfer_direction dir = xfer->direction;
>  	struct scatterlist *sg = NULL;
> -	struct dw_edma_chunk *chunk;
> +	struct dw_edma_chunk *chunk = NULL;
>  	struct dw_edma_burst *burst;
>  	struct dw_edma_desc *desc;
>  	u64 src_addr, dst_addr;
> @@ -419,9 +443,11 @@ static void dw_edma_device_issue_pending(struct dma_chan *dchan)
>  	if (unlikely(!desc))
>  		goto err_alloc;
>
> -	chunk = dw_edma_alloc_chunk(desc);
> -	if (unlikely(!chunk))
> -		goto err_alloc;
> +	if (!chan->non_ll) {
> +		chunk = dw_edma_alloc_chunk(desc);
> +		if (unlikely(!chunk))
> +			goto err_alloc;
> +	}

non_ll is the same as ll_max = 1. (or 2, there are link back entry).

If you set ll_max = 1, needn't change this code.

>
>  	if (xfer->type == EDMA_XFER_INTERLEAVED) {
>  		src_addr = xfer->xfer.il->src_start;
> @@ -450,7 +476,13 @@ static void dw_edma_device_issue_pending(struct dma_chan *dchan)
>  		if (xfer->type == EDMA_XFER_SCATTER_GATHER && !sg)
>  			break;
>
> -		if (chunk->bursts_alloc == chan->ll_max) {
> +		/*
> +		 * For non-LL mode, only a single burst can be handled
> +		 * in a single chunk unlike LL mode where multiple bursts
> +		 * can be configured in a single chunk.
> +		 */
> +		if ((chunk && chunk->bursts_alloc == chan->ll_max) ||
> +		    chan->non_ll) {
>  			chunk = dw_edma_alloc_chunk(desc);
>  			if (unlikely(!chunk))
>  				goto err_alloc;
> diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
> index 71894b9..c8e3d19 100644
> --- a/drivers/dma/dw-edma/dw-edma-core.h
> +++ b/drivers/dma/dw-edma/dw-edma-core.h
> @@ -86,6 +86,7 @@ struct dw_edma_chan {
>  	u8				configured;
>
>  	struct dma_slave_config		config;
> +	bool				non_ll;
>  };
>
>  struct dw_edma_irq {
> diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c b/drivers/dma/dw-edma/dw-edma-pcie.c
> index 2efd149..277ca50 100644
> --- a/drivers/dma/dw-edma/dw-edma-pcie.c
> +++ b/drivers/dma/dw-edma/dw-edma-pcie.c
> @@ -298,6 +298,15 @@ static void dw_edma_pcie_get_xilinx_dma_data(struct pci_dev *pdev,
>  	pdata->devmem_phys_off = off;
>  }
>
> +static u64 dw_edma_get_phys_addr(struct pci_dev *pdev,
> +				 struct dw_edma_pcie_data *pdata,
> +				 enum pci_barno bar)
> +{
> +	if (pdev->vendor == PCI_VENDOR_ID_XILINX)
> +		return pdata->devmem_phys_off;
> +	return pci_bus_address(pdev, bar);
> +}
> +
>  static int dw_edma_pcie_probe(struct pci_dev *pdev,
>  			      const struct pci_device_id *pid)
>  {
> @@ -307,6 +316,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
>  	struct dw_edma_chip *chip;
>  	int err, nr_irqs;
>  	int i, mask;
> +	bool non_ll = false;
>
>  	vsec_data = kmalloc(sizeof(*vsec_data), GFP_KERNEL);
>  	if (!vsec_data)
> @@ -331,21 +341,24 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
>  	if (pdev->vendor == PCI_VENDOR_ID_XILINX) {
>  		/*
>  		 * There is no valid address found for the LL memory
> -		 * space on the device side.
> +		 * space on the device side. In the absence of LL base
> +		 * address use the non-LL mode or simple mode supported by
> +		 * the HDMA IP.
>  		 */
> -		if (vsec_data->devmem_phys_off == DW_PCIE_XILINX_MDB_INVALID_ADDR)
> -			return -ENOMEM;
> +		if (vsec_data->devmem_phys_off == DW_PCIE_AMD_MDB_INVALID_ADDR)
> +			non_ll = true;
>
>  		/*
>  		 * Configure the channel LL and data blocks if number of
>  		 * channels enabled in VSEC capability are more than the
>  		 * channels configured in xilinx_mdb_data.
>  		 */
> -		dw_edma_set_chan_region_offset(vsec_data, BAR_2, 0,
> -					       DW_PCIE_XILINX_MDB_LL_OFF_GAP,
> -					       DW_PCIE_XILINX_MDB_LL_SIZE,
> -					       DW_PCIE_XILINX_MDB_DT_OFF_GAP,
> -					       DW_PCIE_XILINX_MDB_DT_SIZE);
> +		if (!non_ll)
> +			dw_edma_set_chan_region_offset(vsec_data, BAR_2, 0,
> +						       DW_PCIE_XILINX_LL_OFF_GAP,
> +						       DW_PCIE_XILINX_LL_SIZE,
> +						       DW_PCIE_XILINX_DT_OFF_GAP,
> +						       DW_PCIE_XILINX_DT_SIZE);
>  	}
>
>  	/* Mapping PCI BAR regions */
> @@ -393,6 +406,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
>  	chip->mf = vsec_data->mf;
>  	chip->nr_irqs = nr_irqs;
>  	chip->ops = &dw_edma_pcie_plat_ops;
> +	chip->non_ll = non_ll;
>
>  	chip->ll_wr_cnt = vsec_data->wr_ch_cnt;
>  	chip->ll_rd_cnt = vsec_data->rd_ch_cnt;
> @@ -401,7 +415,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
>  	if (!chip->reg_base)
>  		return -ENOMEM;
>
> -	for (i = 0; i < chip->ll_wr_cnt; i++) {
> +	for (i = 0; i < chip->ll_wr_cnt && !non_ll; i++) {
>  		struct dw_edma_region *ll_region = &chip->ll_region_wr[i];
>  		struct dw_edma_region *dt_region = &chip->dt_region_wr[i];
>  		struct dw_edma_block *ll_block = &vsec_data->ll_wr[i];
> @@ -412,7 +426,8 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
>  			return -ENOMEM;
>
>  		ll_region->vaddr.io += ll_block->off;
> -		ll_region->paddr = pci_bus_address(pdev, ll_block->bar);
> +		ll_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
> +							 ll_block->bar);

This change need do prepare patch, which only change pci_bus_address() to
dw_edma_get_phys_addr().

>  		ll_region->paddr += ll_block->off;
>  		ll_region->sz = ll_block->sz;
>
> @@ -421,12 +436,13 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
>  			return -ENOMEM;
>
>  		dt_region->vaddr.io += dt_block->off;
> -		dt_region->paddr = pci_bus_address(pdev, dt_block->bar);
> +		dt_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
> +							 dt_block->bar);
>  		dt_region->paddr += dt_block->off;
>  		dt_region->sz = dt_block->sz;
>  	}
>
> -	for (i = 0; i < chip->ll_rd_cnt; i++) {
> +	for (i = 0; i < chip->ll_rd_cnt && !non_ll; i++) {
>  		struct dw_edma_region *ll_region = &chip->ll_region_rd[i];
>  		struct dw_edma_region *dt_region = &chip->dt_region_rd[i];
>  		struct dw_edma_block *ll_block = &vsec_data->ll_rd[i];
> @@ -437,7 +453,8 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
>  			return -ENOMEM;
>
>  		ll_region->vaddr.io += ll_block->off;
> -		ll_region->paddr = pci_bus_address(pdev, ll_block->bar);
> +		ll_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
> +							 ll_block->bar);
>  		ll_region->paddr += ll_block->off;
>  		ll_region->sz = ll_block->sz;
>
> @@ -446,7 +463,8 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
>  			return -ENOMEM;
>
>  		dt_region->vaddr.io += dt_block->off;
> -		dt_region->paddr = pci_bus_address(pdev, dt_block->bar);
> +		dt_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
> +							 dt_block->bar);
>  		dt_region->paddr += dt_block->off;
>  		dt_region->sz = dt_block->sz;
>  	}
> diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.c b/drivers/dma/dw-edma/dw-hdma-v0-core.c
> index e3f8db4..a5d12bc 100644
> --- a/drivers/dma/dw-edma/dw-hdma-v0-core.c
> +++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c
> @@ -225,7 +225,7 @@ static void dw_hdma_v0_sync_ll_data(struct dw_edma_chunk *chunk)
>  		readl(chunk->ll_region.vaddr.io);
>  }
>
> -static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> +static void dw_hdma_v0_core_ll_start(struct dw_edma_chunk *chunk, bool first)
>  {
>  	struct dw_edma_chan *chan = chunk->chan;
>  	struct dw_edma *dw = chan->dw;
> @@ -263,6 +263,65 @@ static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
>  	SET_CH_32(dw, chan->dir, chan->id, doorbell, HDMA_V0_DOORBELL_START);
>  }
>
> +static void dw_hdma_v0_core_non_ll_start(struct dw_edma_chunk *chunk)
> +{
> +	struct dw_edma_chan *chan = chunk->chan;
> +	struct dw_edma *dw = chan->dw;
> +	struct dw_edma_burst *child;
> +	u32 val;
> +
> +	list_for_each_entry(child, &chunk->burst->list, list) {

why need iterated list, it doesn't support ll. Need wait for irq to start
next one.

Frank

> +		SET_CH_32(dw, chan->dir, chan->id, ch_en, HDMA_V0_CH_EN);
> +
> +		/* Source address */
> +		SET_CH_32(dw, chan->dir, chan->id, sar.lsb,
> +			  lower_32_bits(child->sar));
> +		SET_CH_32(dw, chan->dir, chan->id, sar.msb,
> +			  upper_32_bits(child->sar));
> +
> +		/* Destination address */
> +		SET_CH_32(dw, chan->dir, chan->id, dar.lsb,
> +			  lower_32_bits(child->dar));
> +		SET_CH_32(dw, chan->dir, chan->id, dar.msb,
> +			  upper_32_bits(child->dar));
> +
> +		/* Transfer size */
> +		SET_CH_32(dw, chan->dir, chan->id, transfer_size, child->sz);
> +
> +		/* Interrupt setup */
> +		val = GET_CH_32(dw, chan->dir, chan->id, int_setup) |
> +				HDMA_V0_STOP_INT_MASK |
> +				HDMA_V0_ABORT_INT_MASK |
> +				HDMA_V0_LOCAL_STOP_INT_EN |
> +				HDMA_V0_LOCAL_ABORT_INT_EN;
> +
> +		if (!(dw->chip->flags & DW_EDMA_CHIP_LOCAL)) {
> +			val |= HDMA_V0_REMOTE_STOP_INT_EN |
> +			       HDMA_V0_REMOTE_ABORT_INT_EN;
> +		}
> +
> +		SET_CH_32(dw, chan->dir, chan->id, int_setup, val);
> +
> +		/* Channel control setup */
> +		val = GET_CH_32(dw, chan->dir, chan->id, control1);
> +		val &= ~HDMA_V0_LINKLIST_EN;
> +		SET_CH_32(dw, chan->dir, chan->id, control1, val);
> +
> +		SET_CH_32(dw, chan->dir, chan->id, doorbell,
> +			  HDMA_V0_DOORBELL_START);
> +	}
> +}
> +
> +static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> +{
> +	struct dw_edma_chan *chan = chunk->chan;
> +
> +	if (chan->non_ll)
> +		dw_hdma_v0_core_non_ll_start(chunk);
> +	else
> +		dw_hdma_v0_core_ll_start(chunk, first);
> +}
> +
>  static void dw_hdma_v0_core_ch_config(struct dw_edma_chan *chan)
>  {
>  	struct dw_edma *dw = chan->dw;
> diff --git a/drivers/dma/dw-edma/dw-hdma-v0-regs.h b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> index eab5fd7..7759ba9 100644
> --- a/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> +++ b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> @@ -12,6 +12,7 @@
>  #include <linux/dmaengine.h>
>
>  #define HDMA_V0_MAX_NR_CH			8
> +#define HDMA_V0_CH_EN				BIT(0)
>  #define HDMA_V0_LOCAL_ABORT_INT_EN		BIT(6)
>  #define HDMA_V0_REMOTE_ABORT_INT_EN		BIT(5)
>  #define HDMA_V0_LOCAL_STOP_INT_EN		BIT(4)
> diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h
> index 3080747..78ce31b 100644
> --- a/include/linux/dma/edma.h
> +++ b/include/linux/dma/edma.h
> @@ -99,6 +99,7 @@ struct dw_edma_chip {
>  	enum dw_edma_map_format	mf;
>
>  	struct dw_edma		*dw;
> +	bool			non_ll;
>  };
>
>  /* Export to the platform drivers */
> --
> 1.8.3.1
>

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

* Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
  2026-01-09 12:03 ` [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode Devendra K Verma
  2026-01-15 16:37   ` Frank Li
@ 2026-01-16  2:36   ` kernel test robot
  2026-01-16  3:59   ` kernel test robot
  2 siblings, 0 replies; 24+ messages in thread
From: kernel test robot @ 2026-01-16  2:36 UTC (permalink / raw)
  To: Devendra K Verma, bhelgaas, mani, vkoul
  Cc: llvm, oe-kbuild-all, dmaengine, linux-pci, linux-kernel,
	michal.simek, Devendra.Verma

Hi Devendra,

kernel test robot noticed the following build errors:

[auto build test ERROR on v6.19-rc4]
[also build test ERROR on linus/master next-20260115]
[cannot apply to vkoul-dmaengine/next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Devendra-K-Verma/dmaengine-dw-edma-Add-AMD-MDB-Endpoint-Support/20260109-200654
base:   v6.19-rc4
patch link:    https://lore.kernel.org/r/20260109120354.306048-3-devendra.verma%40amd.com
patch subject: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
config: s390-allmodconfig (https://download.01.org/0day-ci/archive/20260116/202601161018.9e9hUXUa-lkp@intel.com/config)
compiler: clang version 18.1.8 (https://github.com/llvm/llvm-project 3b5b5c1ec4a3095ab096dd780e84d7ab81f3d7ff)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260116/202601161018.9e9hUXUa-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202601161018.9e9hUXUa-lkp@intel.com/

All errors (new ones prefixed by >>):

>> drivers/dma/dw-edma/dw-edma-pcie.c:348:37: error: use of undeclared identifier 'DW_PCIE_AMD_MDB_INVALID_ADDR'
     348 |                 if (vsec_data->devmem_phys_off == DW_PCIE_AMD_MDB_INVALID_ADDR)
         |                                                   ^
>> drivers/dma/dw-edma/dw-edma-pcie.c:358:14: error: use of undeclared identifier 'DW_PCIE_XILINX_LL_OFF_GAP'
     358 |                                                        DW_PCIE_XILINX_LL_OFF_GAP,
         |                                                        ^
>> drivers/dma/dw-edma/dw-edma-pcie.c:359:14: error: use of undeclared identifier 'DW_PCIE_XILINX_LL_SIZE'
     359 |                                                        DW_PCIE_XILINX_LL_SIZE,
         |                                                        ^
>> drivers/dma/dw-edma/dw-edma-pcie.c:360:14: error: use of undeclared identifier 'DW_PCIE_XILINX_DT_OFF_GAP'
     360 |                                                        DW_PCIE_XILINX_DT_OFF_GAP,
         |                                                        ^
>> drivers/dma/dw-edma/dw-edma-pcie.c:361:14: error: use of undeclared identifier 'DW_PCIE_XILINX_DT_SIZE'
     361 |                                                        DW_PCIE_XILINX_DT_SIZE);
         |                                                        ^
   5 errors generated.


vim +/DW_PCIE_AMD_MDB_INVALID_ADDR +348 drivers/dma/dw-edma/dw-edma-pcie.c

   309	
   310	static int dw_edma_pcie_probe(struct pci_dev *pdev,
   311				      const struct pci_device_id *pid)
   312	{
   313		struct dw_edma_pcie_data *pdata = (void *)pid->driver_data;
   314		struct dw_edma_pcie_data *vsec_data __free(kfree) = NULL;
   315		struct device *dev = &pdev->dev;
   316		struct dw_edma_chip *chip;
   317		int err, nr_irqs;
   318		int i, mask;
   319		bool non_ll = false;
   320	
   321		vsec_data = kmalloc(sizeof(*vsec_data), GFP_KERNEL);
   322		if (!vsec_data)
   323			return -ENOMEM;
   324	
   325		/* Enable PCI device */
   326		err = pcim_enable_device(pdev);
   327		if (err) {
   328			pci_err(pdev, "enabling device failed\n");
   329			return err;
   330		}
   331	
   332		memcpy(vsec_data, pdata, sizeof(struct dw_edma_pcie_data));
   333	
   334		/*
   335		 * Tries to find if exists a PCIe Vendor-Specific Extended Capability
   336		 * for the DMA, if one exists, then reconfigures it.
   337		 */
   338		dw_edma_pcie_get_synopsys_dma_data(pdev, vsec_data);
   339		dw_edma_pcie_get_xilinx_dma_data(pdev, vsec_data);
   340	
   341		if (pdev->vendor == PCI_VENDOR_ID_XILINX) {
   342			/*
   343			 * There is no valid address found for the LL memory
   344			 * space on the device side. In the absence of LL base
   345			 * address use the non-LL mode or simple mode supported by
   346			 * the HDMA IP.
   347			 */
 > 348			if (vsec_data->devmem_phys_off == DW_PCIE_AMD_MDB_INVALID_ADDR)
   349				non_ll = true;
   350	
   351			/*
   352			 * Configure the channel LL and data blocks if number of
   353			 * channels enabled in VSEC capability are more than the
   354			 * channels configured in xilinx_mdb_data.
   355			 */
   356			if (!non_ll)
   357				dw_edma_set_chan_region_offset(vsec_data, BAR_2, 0,
 > 358							       DW_PCIE_XILINX_LL_OFF_GAP,
 > 359							       DW_PCIE_XILINX_LL_SIZE,
 > 360							       DW_PCIE_XILINX_DT_OFF_GAP,
 > 361							       DW_PCIE_XILINX_DT_SIZE);
   362		}
   363	
   364		/* Mapping PCI BAR regions */
   365		mask = BIT(vsec_data->rg.bar);
   366		for (i = 0; i < vsec_data->wr_ch_cnt; i++) {
   367			mask |= BIT(vsec_data->ll_wr[i].bar);
   368			mask |= BIT(vsec_data->dt_wr[i].bar);
   369		}
   370		for (i = 0; i < vsec_data->rd_ch_cnt; i++) {
   371			mask |= BIT(vsec_data->ll_rd[i].bar);
   372			mask |= BIT(vsec_data->dt_rd[i].bar);
   373		}
   374		err = pcim_iomap_regions(pdev, mask, pci_name(pdev));
   375		if (err) {
   376			pci_err(pdev, "eDMA BAR I/O remapping failed\n");
   377			return err;
   378		}
   379	
   380		pci_set_master(pdev);
   381	
   382		/* DMA configuration */
   383		err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
   384		if (err) {
   385			pci_err(pdev, "DMA mask 64 set failed\n");
   386			return err;
   387		}
   388	
   389		/* Data structure allocation */
   390		chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
   391		if (!chip)
   392			return -ENOMEM;
   393	
   394		/* IRQs allocation */
   395		nr_irqs = pci_alloc_irq_vectors(pdev, 1, vsec_data->irqs,
   396						PCI_IRQ_MSI | PCI_IRQ_MSIX);
   397		if (nr_irqs < 1) {
   398			pci_err(pdev, "fail to alloc IRQ vector (number of IRQs=%u)\n",
   399				nr_irqs);
   400			return -EPERM;
   401		}
   402	
   403		/* Data structure initialization */
   404		chip->dev = dev;
   405	
   406		chip->mf = vsec_data->mf;
   407		chip->nr_irqs = nr_irqs;
   408		chip->ops = &dw_edma_pcie_plat_ops;
   409		chip->non_ll = non_ll;
   410	
   411		chip->ll_wr_cnt = vsec_data->wr_ch_cnt;
   412		chip->ll_rd_cnt = vsec_data->rd_ch_cnt;
   413	
   414		chip->reg_base = pcim_iomap_table(pdev)[vsec_data->rg.bar];
   415		if (!chip->reg_base)
   416			return -ENOMEM;
   417	
   418		for (i = 0; i < chip->ll_wr_cnt && !non_ll; i++) {
   419			struct dw_edma_region *ll_region = &chip->ll_region_wr[i];
   420			struct dw_edma_region *dt_region = &chip->dt_region_wr[i];
   421			struct dw_edma_block *ll_block = &vsec_data->ll_wr[i];
   422			struct dw_edma_block *dt_block = &vsec_data->dt_wr[i];
   423	
   424			ll_region->vaddr.io = pcim_iomap_table(pdev)[ll_block->bar];
   425			if (!ll_region->vaddr.io)
   426				return -ENOMEM;
   427	
   428			ll_region->vaddr.io += ll_block->off;
   429			ll_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
   430								 ll_block->bar);
   431			ll_region->paddr += ll_block->off;
   432			ll_region->sz = ll_block->sz;
   433	
   434			dt_region->vaddr.io = pcim_iomap_table(pdev)[dt_block->bar];
   435			if (!dt_region->vaddr.io)
   436				return -ENOMEM;
   437	
   438			dt_region->vaddr.io += dt_block->off;
   439			dt_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
   440								 dt_block->bar);
   441			dt_region->paddr += dt_block->off;
   442			dt_region->sz = dt_block->sz;
   443		}
   444	
   445		for (i = 0; i < chip->ll_rd_cnt && !non_ll; i++) {
   446			struct dw_edma_region *ll_region = &chip->ll_region_rd[i];
   447			struct dw_edma_region *dt_region = &chip->dt_region_rd[i];
   448			struct dw_edma_block *ll_block = &vsec_data->ll_rd[i];
   449			struct dw_edma_block *dt_block = &vsec_data->dt_rd[i];
   450	
   451			ll_region->vaddr.io = pcim_iomap_table(pdev)[ll_block->bar];
   452			if (!ll_region->vaddr.io)
   453				return -ENOMEM;
   454	
   455			ll_region->vaddr.io += ll_block->off;
   456			ll_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
   457								 ll_block->bar);
   458			ll_region->paddr += ll_block->off;
   459			ll_region->sz = ll_block->sz;
   460	
   461			dt_region->vaddr.io = pcim_iomap_table(pdev)[dt_block->bar];
   462			if (!dt_region->vaddr.io)
   463				return -ENOMEM;
   464	
   465			dt_region->vaddr.io += dt_block->off;
   466			dt_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
   467								 dt_block->bar);
   468			dt_region->paddr += dt_block->off;
   469			dt_region->sz = dt_block->sz;
   470		}
   471	
   472		/* Debug info */
   473		if (chip->mf == EDMA_MF_EDMA_LEGACY)
   474			pci_dbg(pdev, "Version:\teDMA Port Logic (0x%x)\n", chip->mf);
   475		else if (chip->mf == EDMA_MF_EDMA_UNROLL)
   476			pci_dbg(pdev, "Version:\teDMA Unroll (0x%x)\n", chip->mf);
   477		else if (chip->mf == EDMA_MF_HDMA_COMPAT)
   478			pci_dbg(pdev, "Version:\tHDMA Compatible (0x%x)\n", chip->mf);
   479		else if (chip->mf == EDMA_MF_HDMA_NATIVE)
   480			pci_dbg(pdev, "Version:\tHDMA Native (0x%x)\n", chip->mf);
   481		else
   482			pci_dbg(pdev, "Version:\tUnknown (0x%x)\n", chip->mf);
   483	
   484		pci_dbg(pdev, "Registers:\tBAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p)\n",
   485			vsec_data->rg.bar, vsec_data->rg.off, vsec_data->rg.sz,
   486			chip->reg_base);
   487	
   488	
   489		for (i = 0; i < chip->ll_wr_cnt; i++) {
   490			pci_dbg(pdev, "L. List:\tWRITE CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n",
   491				i, vsec_data->ll_wr[i].bar,
   492				vsec_data->ll_wr[i].off, chip->ll_region_wr[i].sz,
   493				chip->ll_region_wr[i].vaddr.io, &chip->ll_region_wr[i].paddr);
   494	
   495			pci_dbg(pdev, "Data:\tWRITE CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n",
   496				i, vsec_data->dt_wr[i].bar,
   497				vsec_data->dt_wr[i].off, chip->dt_region_wr[i].sz,
   498				chip->dt_region_wr[i].vaddr.io, &chip->dt_region_wr[i].paddr);
   499		}
   500	
   501		for (i = 0; i < chip->ll_rd_cnt; i++) {
   502			pci_dbg(pdev, "L. List:\tREAD CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n",
   503				i, vsec_data->ll_rd[i].bar,
   504				vsec_data->ll_rd[i].off, chip->ll_region_rd[i].sz,
   505				chip->ll_region_rd[i].vaddr.io, &chip->ll_region_rd[i].paddr);
   506	
   507			pci_dbg(pdev, "Data:\tREAD CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n",
   508				i, vsec_data->dt_rd[i].bar,
   509				vsec_data->dt_rd[i].off, chip->dt_region_rd[i].sz,
   510				chip->dt_region_rd[i].vaddr.io, &chip->dt_region_rd[i].paddr);
   511		}
   512	
   513		pci_dbg(pdev, "Nr. IRQs:\t%u\n", chip->nr_irqs);
   514	
   515		/* Validating if PCI interrupts were enabled */
   516		if (!pci_dev_msi_enabled(pdev)) {
   517			pci_err(pdev, "enable interrupt failed\n");
   518			return -EPERM;
   519		}
   520	
   521		/* Starting eDMA driver */
   522		err = dw_edma_probe(chip);
   523		if (err) {
   524			pci_err(pdev, "eDMA probe failed\n");
   525			return err;
   526		}
   527	
   528		/* Saving data structure reference */
   529		pci_set_drvdata(pdev, chip);
   530	
   531		return 0;
   532	}
   533	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

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

* Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
  2026-01-09 12:03 ` [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode Devendra K Verma
  2026-01-15 16:37   ` Frank Li
  2026-01-16  2:36   ` kernel test robot
@ 2026-01-16  3:59   ` kernel test robot
  2 siblings, 0 replies; 24+ messages in thread
From: kernel test robot @ 2026-01-16  3:59 UTC (permalink / raw)
  To: Devendra K Verma, bhelgaas, mani, vkoul
  Cc: oe-kbuild-all, dmaengine, linux-pci, linux-kernel, michal.simek,
	Devendra.Verma

Hi Devendra,

kernel test robot noticed the following build errors:

[auto build test ERROR on v6.19-rc4]
[also build test ERROR on linus/master next-20260115]
[cannot apply to vkoul-dmaengine/next]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Devendra-K-Verma/dmaengine-dw-edma-Add-AMD-MDB-Endpoint-Support/20260109-200654
base:   v6.19-rc4
patch link:    https://lore.kernel.org/r/20260109120354.306048-3-devendra.verma%40amd.com
patch subject: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
config: alpha-allyesconfig (https://download.01.org/0day-ci/archive/20260116/202601161151.MTsHwjVO-lkp@intel.com/config)
compiler: alpha-linux-gcc (GCC) 15.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260116/202601161151.MTsHwjVO-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202601161151.MTsHwjVO-lkp@intel.com/

All errors (new ones prefixed by >>):

   drivers/dma/dw-edma/dw-edma-pcie.c: In function 'dw_edma_pcie_probe':
>> drivers/dma/dw-edma/dw-edma-pcie.c:348:51: error: 'DW_PCIE_AMD_MDB_INVALID_ADDR' undeclared (first use in this function); did you mean 'DW_PCIE_XILINX_MDB_INVALID_ADDR'?
     348 |                 if (vsec_data->devmem_phys_off == DW_PCIE_AMD_MDB_INVALID_ADDR)
         |                                                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
         |                                                   DW_PCIE_XILINX_MDB_INVALID_ADDR
   drivers/dma/dw-edma/dw-edma-pcie.c:348:51: note: each undeclared identifier is reported only once for each function it appears in
>> drivers/dma/dw-edma/dw-edma-pcie.c:358:56: error: 'DW_PCIE_XILINX_LL_OFF_GAP' undeclared (first use in this function); did you mean 'DW_PCIE_XILINX_MDB_LL_OFF_GAP'?
     358 |                                                        DW_PCIE_XILINX_LL_OFF_GAP,
         |                                                        ^~~~~~~~~~~~~~~~~~~~~~~~~
         |                                                        DW_PCIE_XILINX_MDB_LL_OFF_GAP
>> drivers/dma/dw-edma/dw-edma-pcie.c:359:56: error: 'DW_PCIE_XILINX_LL_SIZE' undeclared (first use in this function); did you mean 'DW_PCIE_XILINX_MDB_LL_SIZE'?
     359 |                                                        DW_PCIE_XILINX_LL_SIZE,
         |                                                        ^~~~~~~~~~~~~~~~~~~~~~
         |                                                        DW_PCIE_XILINX_MDB_LL_SIZE
>> drivers/dma/dw-edma/dw-edma-pcie.c:360:56: error: 'DW_PCIE_XILINX_DT_OFF_GAP' undeclared (first use in this function); did you mean 'DW_PCIE_XILINX_MDB_DT_OFF_GAP'?
     360 |                                                        DW_PCIE_XILINX_DT_OFF_GAP,
         |                                                        ^~~~~~~~~~~~~~~~~~~~~~~~~
         |                                                        DW_PCIE_XILINX_MDB_DT_OFF_GAP
>> drivers/dma/dw-edma/dw-edma-pcie.c:361:56: error: 'DW_PCIE_XILINX_DT_SIZE' undeclared (first use in this function); did you mean 'DW_PCIE_XILINX_MDB_DT_SIZE'?
     361 |                                                        DW_PCIE_XILINX_DT_SIZE);
         |                                                        ^~~~~~~~~~~~~~~~~~~~~~
         |                                                        DW_PCIE_XILINX_MDB_DT_SIZE


vim +348 drivers/dma/dw-edma/dw-edma-pcie.c

   309	
   310	static int dw_edma_pcie_probe(struct pci_dev *pdev,
   311				      const struct pci_device_id *pid)
   312	{
   313		struct dw_edma_pcie_data *pdata = (void *)pid->driver_data;
   314		struct dw_edma_pcie_data *vsec_data __free(kfree) = NULL;
   315		struct device *dev = &pdev->dev;
   316		struct dw_edma_chip *chip;
   317		int err, nr_irqs;
   318		int i, mask;
   319		bool non_ll = false;
   320	
   321		vsec_data = kmalloc(sizeof(*vsec_data), GFP_KERNEL);
   322		if (!vsec_data)
   323			return -ENOMEM;
   324	
   325		/* Enable PCI device */
   326		err = pcim_enable_device(pdev);
   327		if (err) {
   328			pci_err(pdev, "enabling device failed\n");
   329			return err;
   330		}
   331	
   332		memcpy(vsec_data, pdata, sizeof(struct dw_edma_pcie_data));
   333	
   334		/*
   335		 * Tries to find if exists a PCIe Vendor-Specific Extended Capability
   336		 * for the DMA, if one exists, then reconfigures it.
   337		 */
   338		dw_edma_pcie_get_synopsys_dma_data(pdev, vsec_data);
   339		dw_edma_pcie_get_xilinx_dma_data(pdev, vsec_data);
   340	
   341		if (pdev->vendor == PCI_VENDOR_ID_XILINX) {
   342			/*
   343			 * There is no valid address found for the LL memory
   344			 * space on the device side. In the absence of LL base
   345			 * address use the non-LL mode or simple mode supported by
   346			 * the HDMA IP.
   347			 */
 > 348			if (vsec_data->devmem_phys_off == DW_PCIE_AMD_MDB_INVALID_ADDR)
   349				non_ll = true;
   350	
   351			/*
   352			 * Configure the channel LL and data blocks if number of
   353			 * channels enabled in VSEC capability are more than the
   354			 * channels configured in xilinx_mdb_data.
   355			 */
   356			if (!non_ll)
   357				dw_edma_set_chan_region_offset(vsec_data, BAR_2, 0,
 > 358							       DW_PCIE_XILINX_LL_OFF_GAP,
 > 359							       DW_PCIE_XILINX_LL_SIZE,
 > 360							       DW_PCIE_XILINX_DT_OFF_GAP,
 > 361							       DW_PCIE_XILINX_DT_SIZE);
   362		}
   363	
   364		/* Mapping PCI BAR regions */
   365		mask = BIT(vsec_data->rg.bar);
   366		for (i = 0; i < vsec_data->wr_ch_cnt; i++) {
   367			mask |= BIT(vsec_data->ll_wr[i].bar);
   368			mask |= BIT(vsec_data->dt_wr[i].bar);
   369		}
   370		for (i = 0; i < vsec_data->rd_ch_cnt; i++) {
   371			mask |= BIT(vsec_data->ll_rd[i].bar);
   372			mask |= BIT(vsec_data->dt_rd[i].bar);
   373		}
   374		err = pcim_iomap_regions(pdev, mask, pci_name(pdev));
   375		if (err) {
   376			pci_err(pdev, "eDMA BAR I/O remapping failed\n");
   377			return err;
   378		}
   379	
   380		pci_set_master(pdev);
   381	
   382		/* DMA configuration */
   383		err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
   384		if (err) {
   385			pci_err(pdev, "DMA mask 64 set failed\n");
   386			return err;
   387		}
   388	
   389		/* Data structure allocation */
   390		chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
   391		if (!chip)
   392			return -ENOMEM;
   393	
   394		/* IRQs allocation */
   395		nr_irqs = pci_alloc_irq_vectors(pdev, 1, vsec_data->irqs,
   396						PCI_IRQ_MSI | PCI_IRQ_MSIX);
   397		if (nr_irqs < 1) {
   398			pci_err(pdev, "fail to alloc IRQ vector (number of IRQs=%u)\n",
   399				nr_irqs);
   400			return -EPERM;
   401		}
   402	
   403		/* Data structure initialization */
   404		chip->dev = dev;
   405	
   406		chip->mf = vsec_data->mf;
   407		chip->nr_irqs = nr_irqs;
   408		chip->ops = &dw_edma_pcie_plat_ops;
   409		chip->non_ll = non_ll;
   410	
   411		chip->ll_wr_cnt = vsec_data->wr_ch_cnt;
   412		chip->ll_rd_cnt = vsec_data->rd_ch_cnt;
   413	
   414		chip->reg_base = pcim_iomap_table(pdev)[vsec_data->rg.bar];
   415		if (!chip->reg_base)
   416			return -ENOMEM;
   417	
   418		for (i = 0; i < chip->ll_wr_cnt && !non_ll; i++) {
   419			struct dw_edma_region *ll_region = &chip->ll_region_wr[i];
   420			struct dw_edma_region *dt_region = &chip->dt_region_wr[i];
   421			struct dw_edma_block *ll_block = &vsec_data->ll_wr[i];
   422			struct dw_edma_block *dt_block = &vsec_data->dt_wr[i];
   423	
   424			ll_region->vaddr.io = pcim_iomap_table(pdev)[ll_block->bar];
   425			if (!ll_region->vaddr.io)
   426				return -ENOMEM;
   427	
   428			ll_region->vaddr.io += ll_block->off;
   429			ll_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
   430								 ll_block->bar);
   431			ll_region->paddr += ll_block->off;
   432			ll_region->sz = ll_block->sz;
   433	
   434			dt_region->vaddr.io = pcim_iomap_table(pdev)[dt_block->bar];
   435			if (!dt_region->vaddr.io)
   436				return -ENOMEM;
   437	
   438			dt_region->vaddr.io += dt_block->off;
   439			dt_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
   440								 dt_block->bar);
   441			dt_region->paddr += dt_block->off;
   442			dt_region->sz = dt_block->sz;
   443		}
   444	
   445		for (i = 0; i < chip->ll_rd_cnt && !non_ll; i++) {
   446			struct dw_edma_region *ll_region = &chip->ll_region_rd[i];
   447			struct dw_edma_region *dt_region = &chip->dt_region_rd[i];
   448			struct dw_edma_block *ll_block = &vsec_data->ll_rd[i];
   449			struct dw_edma_block *dt_block = &vsec_data->dt_rd[i];
   450	
   451			ll_region->vaddr.io = pcim_iomap_table(pdev)[ll_block->bar];
   452			if (!ll_region->vaddr.io)
   453				return -ENOMEM;
   454	
   455			ll_region->vaddr.io += ll_block->off;
   456			ll_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
   457								 ll_block->bar);
   458			ll_region->paddr += ll_block->off;
   459			ll_region->sz = ll_block->sz;
   460	
   461			dt_region->vaddr.io = pcim_iomap_table(pdev)[dt_block->bar];
   462			if (!dt_region->vaddr.io)
   463				return -ENOMEM;
   464	
   465			dt_region->vaddr.io += dt_block->off;
   466			dt_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
   467								 dt_block->bar);
   468			dt_region->paddr += dt_block->off;
   469			dt_region->sz = dt_block->sz;
   470		}
   471	
   472		/* Debug info */
   473		if (chip->mf == EDMA_MF_EDMA_LEGACY)
   474			pci_dbg(pdev, "Version:\teDMA Port Logic (0x%x)\n", chip->mf);
   475		else if (chip->mf == EDMA_MF_EDMA_UNROLL)
   476			pci_dbg(pdev, "Version:\teDMA Unroll (0x%x)\n", chip->mf);
   477		else if (chip->mf == EDMA_MF_HDMA_COMPAT)
   478			pci_dbg(pdev, "Version:\tHDMA Compatible (0x%x)\n", chip->mf);
   479		else if (chip->mf == EDMA_MF_HDMA_NATIVE)
   480			pci_dbg(pdev, "Version:\tHDMA Native (0x%x)\n", chip->mf);
   481		else
   482			pci_dbg(pdev, "Version:\tUnknown (0x%x)\n", chip->mf);
   483	
   484		pci_dbg(pdev, "Registers:\tBAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p)\n",
   485			vsec_data->rg.bar, vsec_data->rg.off, vsec_data->rg.sz,
   486			chip->reg_base);
   487	
   488	
   489		for (i = 0; i < chip->ll_wr_cnt; i++) {
   490			pci_dbg(pdev, "L. List:\tWRITE CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n",
   491				i, vsec_data->ll_wr[i].bar,
   492				vsec_data->ll_wr[i].off, chip->ll_region_wr[i].sz,
   493				chip->ll_region_wr[i].vaddr.io, &chip->ll_region_wr[i].paddr);
   494	
   495			pci_dbg(pdev, "Data:\tWRITE CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n",
   496				i, vsec_data->dt_wr[i].bar,
   497				vsec_data->dt_wr[i].off, chip->dt_region_wr[i].sz,
   498				chip->dt_region_wr[i].vaddr.io, &chip->dt_region_wr[i].paddr);
   499		}
   500	
   501		for (i = 0; i < chip->ll_rd_cnt; i++) {
   502			pci_dbg(pdev, "L. List:\tREAD CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n",
   503				i, vsec_data->ll_rd[i].bar,
   504				vsec_data->ll_rd[i].off, chip->ll_region_rd[i].sz,
   505				chip->ll_region_rd[i].vaddr.io, &chip->ll_region_rd[i].paddr);
   506	
   507			pci_dbg(pdev, "Data:\tREAD CH%.2u, BAR=%u, off=0x%.8lx, sz=0x%zx bytes, addr(v=%p, p=%pa)\n",
   508				i, vsec_data->dt_rd[i].bar,
   509				vsec_data->dt_rd[i].off, chip->dt_region_rd[i].sz,
   510				chip->dt_region_rd[i].vaddr.io, &chip->dt_region_rd[i].paddr);
   511		}
   512	
   513		pci_dbg(pdev, "Nr. IRQs:\t%u\n", chip->nr_irqs);
   514	
   515		/* Validating if PCI interrupts were enabled */
   516		if (!pci_dev_msi_enabled(pdev)) {
   517			pci_err(pdev, "enable interrupt failed\n");
   518			return -EPERM;
   519		}
   520	
   521		/* Starting eDMA driver */
   522		err = dw_edma_probe(chip);
   523		if (err) {
   524			pci_err(pdev, "eDMA probe failed\n");
   525			return err;
   526		}
   527	
   528		/* Saving data structure reference */
   529		pci_set_drvdata(pdev, chip);
   530	
   531		return 0;
   532	}
   533	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

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

* RE: [PATCH v8 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint Support
  2026-01-15 16:21   ` Frank Li
@ 2026-01-19  9:09     ` Verma, Devendra
  2026-01-19 15:59       ` Frank Li
  0 siblings, 1 reply; 24+ messages in thread
From: Verma, Devendra @ 2026-01-19  9:09 UTC (permalink / raw)
  To: Frank Li
  Cc: bhelgaas@google.com, mani@kernel.org, vkoul@kernel.org,
	dmaengine@vger.kernel.org, linux-pci@vger.kernel.org,
	linux-kernel@vger.kernel.org, Simek, Michal, Verma, Devendra

[AMD Official Use Only - AMD Internal Distribution Only]

Hi Frank

Please check my comments inline.

Regards,
Devendra

> -----Original Message-----
> From: Frank Li <Frank.li@nxp.com>
> Sent: Thursday, January 15, 2026 9:51 PM
> To: Verma, Devendra <Devendra.Verma@amd.com>
> Cc: bhelgaas@google.com; mani@kernel.org; vkoul@kernel.org;
> dmaengine@vger.kernel.org; linux-pci@vger.kernel.org; linux-
> kernel@vger.kernel.org; Simek, Michal <michal.simek@amd.com>
> Subject: Re: [PATCH v8 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint
> Support
>
> Caution: This message originated from an External Source. Use proper
> caution when opening attachments, clicking links, or responding.
>
>
> On Fri, Jan 09, 2026 at 05:33:53PM +0530, Devendra K Verma wrote:
> > AMD MDB PCIe endpoint support. For AMD specific support added the
> > following
> >   - AMD supported PCIe Device IDs and Vendor ID (Xilinx).
> >   - AMD MDB specific driver data
> >   - AMD MDB specific VSEC capability to retrieve the device DDR
> >     base address.
> >
> > Signed-off-by: Devendra K Verma <devendra.verma@amd.com>
> > ---
> > Changes in v8:
> > Changed the contant names to includer product vendor.
> > Moved the vendor specific code to vendor specific functions.
> >
> > Changes in v7:
> > Introduced vendor specific functions to retrieve the vsec data.
> >
> > Changes in v6:
> > Included "sizes.h" header and used the appropriate definitions instead
> > of constants.
> >
> > Changes in v5:
> > Added the definitions for Xilinx specific VSEC header id, revision,
> > and register offsets.
> > Corrected the error type when no physical offset found for device side
> > memory.
> > Corrected the order of variables.
> >
> > Changes in v4:
> > Configured 8 read and 8 write channels for Xilinx vendor Added checks
> > to validate vendor ID for vendor specific vsec id.
> > Added Xilinx specific vendor id for vsec specific to Xilinx Added the
> > LL and data region offsets, size as input params to function
> > dw_edma_set_chan_region_offset().
> > Moved the LL and data region offsets assignment to function for Xilinx
> > specific case.
> > Corrected comments.
> >
> > Changes in v3:
> > Corrected a typo when assigning AMD (Xilinx) vsec id macro and
> > condition check.
> >
> > Changes in v2:
> > Reverted the devmem_phys_off type to u64.
> > Renamed the function appropriately to suit the functionality for
> > setting the LL & data region offsets.
> >
> > Changes in v1:
> > Removed the pci device id from pci_ids.h file.
> > Added the vendor id macro as per the suggested method.
> > Changed the type of the newly added devmem_phys_off variable.
> > Added to logic to assign offsets for LL and data region blocks in case
> > more number of channels are enabled than given in amd_mdb_data struct.
> > ---
> >  drivers/dma/dw-edma/dw-edma-pcie.c | 192
> > ++++++++++++++++++++++++++++++++++---
> >  1 file changed, 178 insertions(+), 14 deletions(-)
> >
> > diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c
> > b/drivers/dma/dw-edma/dw-edma-pcie.c
> > index 3371e0a7..2efd149 100644
> > --- a/drivers/dma/dw-edma/dw-edma-pcie.c
> > +++ b/drivers/dma/dw-edma/dw-edma-pcie.c
> > @@ -14,14 +14,35 @@
> >  #include <linux/pci-epf.h>
> >  #include <linux/msi.h>
> >  #include <linux/bitfield.h>
> > +#include <linux/sizes.h>
> >
> >  #include "dw-edma-core.h"
> >
> > -#define DW_PCIE_VSEC_DMA_ID                  0x6
> > -#define DW_PCIE_VSEC_DMA_BAR                 GENMASK(10, 8)
> > -#define DW_PCIE_VSEC_DMA_MAP                 GENMASK(2, 0)
> > -#define DW_PCIE_VSEC_DMA_WR_CH                       GENMASK(9, 0)
> > -#define DW_PCIE_VSEC_DMA_RD_CH                       GENMASK(25, 16)
> > +/* Synopsys */
> > +#define DW_PCIE_SYNOPSYS_VSEC_DMA_ID         0x6
> > +#define DW_PCIE_SYNOPSYS_VSEC_DMA_BAR                GENMASK(10, 8)
> > +#define DW_PCIE_SYNOPSYS_VSEC_DMA_MAP                GENMASK(2, 0)
> > +#define DW_PCIE_SYNOPSYS_VSEC_DMA_WR_CH              GENMASK(9, 0)
> > +#define DW_PCIE_SYNOPSYS_VSEC_DMA_RD_CH              GENMASK(25, 16)
>
> Sorry, jump into at v8.
> According to my understand 'DW' means 'Synopsys'.
>

Yes, DW means Designware representing Synopsys here.
For the sake of clarity, a distinction was required to separate the names of macros
having the similar purpose for other IP, Xilinx in this case. Otherwise, it is causing confusion
which macros to use for which vendor. This also helps in future if any of the vendors
try to retrieve a new or different VSEC IDs then all they need is to define macros which
clearly show the association with the vendor, thus eliminating the confusion.

> > +
> > +/* AMD MDB (Xilinx) specific defines */
> > +#define PCI_DEVICE_ID_XILINX_B054            0xb054
> > +
> > +#define DW_PCIE_XILINX_MDB_VSEC_DMA_ID               0x6
> > +#define DW_PCIE_XILINX_MDB_VSEC_ID           0x20
> > +#define DW_PCIE_XILINX_MDB_VSEC_DMA_BAR              GENMASK(10, 8)
> > +#define DW_PCIE_XILINX_MDB_VSEC_DMA_MAP              GENMASK(2, 0)
> > +#define DW_PCIE_XILINX_MDB_VSEC_DMA_WR_CH    GENMASK(9, 0)
> > +#define DW_PCIE_XILINX_MDB_VSEC_DMA_RD_CH    GENMASK(25, 16)
>
> These defination is the same. Need redefine again
>

It is the similar case as explained for the previous comment. Please check.

> > +
> > +#define DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_HIGH       0xc
> > +#define DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_LOW        0x8
> > +#define DW_PCIE_XILINX_MDB_INVALID_ADDR              (~0ULL)
>
> I think XILINX_PCIE_MDB_DEVMEM_OFF_REG_HIGH
>
> > +
> > +#define DW_PCIE_XILINX_MDB_LL_OFF_GAP                0x200000
> > +#define DW_PCIE_XILINX_MDB_LL_SIZE           0x800
> > +#define DW_PCIE_XILINX_MDB_DT_OFF_GAP                0x100000
> > +#define DW_PCIE_XILINX_MDB_DT_SIZE           0x800
> >
> >  #define DW_BLOCK(a, b, c) \
> >       { \
> > @@ -50,6 +71,7 @@ struct dw_edma_pcie_data {
> >       u8                              irqs;
> >       u16                             wr_ch_cnt;
> >       u16                             rd_ch_cnt;
> > +     u64                             devmem_phys_off;
> >  };
> >
> >  static const struct dw_edma_pcie_data snps_edda_data = { @@ -90,6
> > +112,64 @@ struct dw_edma_pcie_data {
> >       .rd_ch_cnt                      = 2,
> >  };
> >
> > +static const struct dw_edma_pcie_data xilinx_mdb_data = {
> > +     /* MDB registers location */
> > +     .rg.bar                         = BAR_0,
> > +     .rg.off                         = SZ_4K,        /*  4 Kbytes */
> > +     .rg.sz                          = SZ_8K,        /*  8 Kbytes */
> > +
> > +     /* Other */
> > +     .mf                             = EDMA_MF_HDMA_NATIVE,
> > +     .irqs                           = 1,
> > +     .wr_ch_cnt                      = 8,
> > +     .rd_ch_cnt                      = 8,
> > +};
> > +
> > +static void dw_edma_set_chan_region_offset(struct dw_edma_pcie_data
> *pdata,
> > +                                        enum pci_barno bar, off_t start_off,
> > +                                        off_t ll_off_gap, size_t ll_size,
> > +                                        off_t dt_off_gap, size_t
> > +dt_size) {
> > +     u16 wr_ch = pdata->wr_ch_cnt;
> > +     u16 rd_ch = pdata->rd_ch_cnt;
> > +     off_t off;
> > +     u16 i;
> > +
> > +     off = start_off;
> > +
> > +     /* Write channel LL region */
> > +     for (i = 0; i < wr_ch; i++) {
> > +             pdata->ll_wr[i].bar = bar;
> > +             pdata->ll_wr[i].off = off;
> > +             pdata->ll_wr[i].sz = ll_size;
> > +             off += ll_off_gap;
> > +     }
> > +
> > +     /* Read channel LL region */
> > +     for (i = 0; i < rd_ch; i++) {
> > +             pdata->ll_rd[i].bar = bar;
> > +             pdata->ll_rd[i].off = off;
> > +             pdata->ll_rd[i].sz = ll_size;
> > +             off += ll_off_gap;
> > +     }
> > +
> > +     /* Write channel data region */
> > +     for (i = 0; i < wr_ch; i++) {
> > +             pdata->dt_wr[i].bar = bar;
> > +             pdata->dt_wr[i].off = off;
> > +             pdata->dt_wr[i].sz = dt_size;
> > +             off += dt_off_gap;
> > +     }
> > +
> > +     /* Read channel data region */
> > +     for (i = 0; i < rd_ch; i++) {
> > +             pdata->dt_rd[i].bar = bar;
> > +             pdata->dt_rd[i].off = off;
> > +             pdata->dt_rd[i].sz = dt_size;
> > +             off += dt_off_gap;
> > +     }
> > +}
> > +
> >  static int dw_edma_pcie_irq_vector(struct device *dev, unsigned int
> > nr)  {
> >       return pci_irq_vector(to_pci_dev(dev), nr); @@ -114,15 +194,15
> > @@ static u64 dw_edma_pcie_address(struct device *dev, phys_addr_t
> cpu_addr)
> >       .pci_address = dw_edma_pcie_address,  };
> >
> > -static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
> > -                                        struct dw_edma_pcie_data *pdata)
> > +static void dw_edma_pcie_get_synopsys_dma_data(struct pci_dev *pdev,
> > +                                            struct dw_edma_pcie_data
> > +*pdata)
> >  {
> >       u32 val, map;
> >       u16 vsec;
> >       u64 off;
> >
> >       vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_SYNOPSYS,
> > -                                     DW_PCIE_VSEC_DMA_ID);
> > +                                     DW_PCIE_SYNOPSYS_VSEC_DMA_ID);
> >       if (!vsec)
> >               return;
> >
> > @@ -131,9 +211,9 @@ static void
> dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
> >           PCI_VNDR_HEADER_LEN(val) != 0x18)
> >               return;
> >
> > -     pci_dbg(pdev, "Detected PCIe Vendor-Specific Extended Capability
> DMA\n");
> > +     pci_dbg(pdev, "Detected Synopsys PCIe Vendor-Specific Extended
> > + Capability DMA\n");
> >       pci_read_config_dword(pdev, vsec + 0x8, &val);
> > -     map = FIELD_GET(DW_PCIE_VSEC_DMA_MAP, val);
> > +     map = FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_MAP, val);
> >       if (map != EDMA_MF_EDMA_LEGACY &&
> >           map != EDMA_MF_EDMA_UNROLL &&
> >           map != EDMA_MF_HDMA_COMPAT && @@ -141,13 +221,13 @@
> static
> > void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
> >               return;
> >
> >       pdata->mf = map;
> > -     pdata->rg.bar = FIELD_GET(DW_PCIE_VSEC_DMA_BAR, val);
> > +     pdata->rg.bar = FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_BAR, val);
> >
> >       pci_read_config_dword(pdev, vsec + 0xc, &val);
> >       pdata->wr_ch_cnt = min_t(u16, pdata->wr_ch_cnt,
> > -                              FIELD_GET(DW_PCIE_VSEC_DMA_WR_CH, val));
> > +
> > + FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_WR_CH, val));
> >       pdata->rd_ch_cnt = min_t(u16, pdata->rd_ch_cnt,
> > -                              FIELD_GET(DW_PCIE_VSEC_DMA_RD_CH, val));
> > +
> > + FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_RD_CH, val));
>
> If you don't change macro name, these change is not necessary. If really need
> change macro name, make change macro name as sperated patch.
>

As explained above, the name change is required to avoid confusion.
The trigger to have the separate names for each IP is the inclusion of Xilinx IP that
is why no separate patch is created.

> >
> >       pci_read_config_dword(pdev, vsec + 0x14, &val);
> >       off = val;
> > @@ -157,6 +237,67 @@ static void
> dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
> >       pdata->rg.off = off;
> >  }
> >
> > +static void dw_edma_pcie_get_xilinx_dma_data(struct pci_dev *pdev,
> > +                                          struct dw_edma_pcie_data
> > +*pdata) {
> > +     u32 val, map;
> > +     u16 vsec;
> > +     u64 off;
> > +
> > +     pdata->devmem_phys_off = DW_PCIE_XILINX_MDB_INVALID_ADDR;
> > +
> > +     vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_XILINX,
> > +                                     DW_PCIE_XILINX_MDB_VSEC_DMA_ID);
> > +     if (!vsec)
> > +             return;
> > +
> > +     pci_read_config_dword(pdev, vsec + PCI_VNDR_HEADER, &val);
> > +     if (PCI_VNDR_HEADER_REV(val) != 0x00 ||
> > +         PCI_VNDR_HEADER_LEN(val) != 0x18)
> > +             return;
> > +
> > +     pci_dbg(pdev, "Detected Xilinx PCIe Vendor-Specific Extended Capability
> DMA\n");
> > +     pci_read_config_dword(pdev, vsec + 0x8, &val);
> > +     map = FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_MAP, val);
> > +     if (map != EDMA_MF_EDMA_LEGACY &&
> > +         map != EDMA_MF_EDMA_UNROLL &&
> > +         map != EDMA_MF_HDMA_COMPAT &&
> > +         map != EDMA_MF_HDMA_NATIVE)
> > +             return;
> > +
> > +     pdata->mf = map;
> > +     pdata->rg.bar = FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_BAR,
> val);
> > +
> > +     pci_read_config_dword(pdev, vsec + 0xc, &val);
> > +     pdata->wr_ch_cnt = min_t(u16, pdata->wr_ch_cnt,
> > +                              FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_WR_CH,
> val));
> > +     pdata->rd_ch_cnt = min_t(u16, pdata->rd_ch_cnt,
> > +
> > + FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_RD_CH, val));
> > +
> > +     pci_read_config_dword(pdev, vsec + 0x14, &val);
> > +     off = val;
> > +     pci_read_config_dword(pdev, vsec + 0x10, &val);
> > +     off <<= 32;
> > +     off |= val;
> > +     pdata->rg.off = off;
> > +
> > +     vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_XILINX,
> > +                                     DW_PCIE_XILINX_MDB_VSEC_ID);
> > +     if (!vsec)
> > +             return;
> > +
> > +     pci_read_config_dword(pdev,
> > +                           vsec + DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_HIGH,
> > +                           &val);
> > +     off = val;
> > +     pci_read_config_dword(pdev,
> > +                           vsec + DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_LOW,
> > +                           &val);
> > +     off <<= 32;
> > +     off |= val;
> > +     pdata->devmem_phys_off = off;
> > +}
> > +
> >  static int dw_edma_pcie_probe(struct pci_dev *pdev,
> >                             const struct pci_device_id *pid)  { @@
> > -184,7 +325,28 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
> >        * Tries to find if exists a PCIe Vendor-Specific Extended Capability
> >        * for the DMA, if one exists, then reconfigures it.
> >        */
> > -     dw_edma_pcie_get_vsec_dma_data(pdev, vsec_data);
> > +     dw_edma_pcie_get_synopsys_dma_data(pdev, vsec_data);
> > +     dw_edma_pcie_get_xilinx_dma_data(pdev, vsec_data);
> > +
> > +     if (pdev->vendor == PCI_VENDOR_ID_XILINX) {
>
> dw_edma_pcie_get_xilinx_dma_data() should be here.
>
> Frank

Yes, this is good suggestion. Thanks!

> > +             /*
> > +              * There is no valid address found for the LL memory
> > +              * space on the device side.
> > +              */
> > +             if (vsec_data->devmem_phys_off ==
> DW_PCIE_XILINX_MDB_INVALID_ADDR)
> > +                     return -ENOMEM;
> > +
> > +             /*
> > +              * Configure the channel LL and data blocks if number of
> > +              * channels enabled in VSEC capability are more than the
> > +              * channels configured in xilinx_mdb_data.
> > +              */
> > +             dw_edma_set_chan_region_offset(vsec_data, BAR_2, 0,
> > +                                            DW_PCIE_XILINX_MDB_LL_OFF_GAP,
> > +                                            DW_PCIE_XILINX_MDB_LL_SIZE,
> > +                                            DW_PCIE_XILINX_MDB_DT_OFF_GAP,
> > +                                            DW_PCIE_XILINX_MDB_DT_SIZE);
> > +     }
> >
> >       /* Mapping PCI BAR regions */
> >       mask = BIT(vsec_data->rg.bar);
> > @@ -367,6 +529,8 @@ static void dw_edma_pcie_remove(struct pci_dev
> > *pdev)
> >
> >  static const struct pci_device_id dw_edma_pcie_id_table[] = {
> >       { PCI_DEVICE_DATA(SYNOPSYS, EDDA, &snps_edda_data) },
> > +     { PCI_VDEVICE(XILINX, PCI_DEVICE_ID_XILINX_B054),
> > +       (kernel_ulong_t)&xilinx_mdb_data },
> >       { }
> >  };
> >  MODULE_DEVICE_TABLE(pci, dw_edma_pcie_id_table);
> > --
> > 1.8.3.1
> >

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

* RE: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
  2026-01-15 16:37   ` Frank Li
@ 2026-01-19  9:09     ` Verma, Devendra
  2026-01-19 15:45       ` Frank Li
  0 siblings, 1 reply; 24+ messages in thread
From: Verma, Devendra @ 2026-01-19  9:09 UTC (permalink / raw)
  To: Frank Li
  Cc: bhelgaas@google.com, mani@kernel.org, vkoul@kernel.org,
	dmaengine@vger.kernel.org, linux-pci@vger.kernel.org,
	linux-kernel@vger.kernel.org, Simek, Michal, Verma, Devendra

[AMD Official Use Only - AMD Internal Distribution Only]

Hi Frank

Please check my response inline.

Regards,
Devendra
> -----Original Message-----
> From: Frank Li <Frank.li@nxp.com>
> Sent: Thursday, January 15, 2026 10:07 PM
> To: Verma, Devendra <Devendra.Verma@amd.com>
> Cc: bhelgaas@google.com; mani@kernel.org; vkoul@kernel.org;
> dmaengine@vger.kernel.org; linux-pci@vger.kernel.org; linux-
> kernel@vger.kernel.org; Simek, Michal <michal.simek@amd.com>
> Subject: Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
>
> Caution: This message originated from an External Source. Use proper
> caution when opening attachments, clicking links, or responding.
>
>
> On Fri, Jan 09, 2026 at 05:33:54PM +0530, Devendra K Verma wrote:
> > AMD MDB IP supports Linked List (LL) mode as well as non-LL mode.
> > The current code does not have the mechanisms to enable the DMA
> > transactions using the non-LL mode. The following two cases are added
> > with this patch:
> > - For the AMD (Xilinx) only, when a valid physical base address of
> >   the device side DDR is not configured, then the IP can still be
> >   used in non-LL mode. For all the channels DMA transactions will
>
> If DDR have not configured, where DATA send to in device side by non-LL
> mode.
>

The DDR base address in the VSEC capability is used for driving the DMA
transfers when used in the LL mode. The DDR is configured and present
all the time but the DMA PCIe driver uses this DDR base address (physical address)
to configure the LLP address.

In the scenario, where this DDR base address in VSEC capability is not
configured then the current controller cannot be used as the default mode
supported is LL mode only. In order to make the controller usable non-LL mode
is being added which just needs SAR, DAR, XFERLEN and control register to initiate the
transfer. So, the DDR is always present, but the DMA PCIe driver need to know
the DDR base physical address to make the transfer. This is useful in scenarios
where the memory allocated for LL can be used for DMA transactions as well.

> >   be using the non-LL mode only. This, the default non-LL mode,
> >   is not applicable for Synopsys IP with the current code addition.
> >
> > - If the default mode is LL-mode, for both AMD (Xilinx) and Synosys,
> >   and if user wants to use non-LL mode then user can do so via
> >   configuring the peripheral_config param of dma_slave_config.
> >
> > Signed-off-by: Devendra K Verma <devendra.verma@amd.com>
> > ---
> > Changes in v8
> >   Cosmetic change related to comment and code.
> >
> > Changes in v7
> >   No change
> >
> > Changes in v6
> >   Gave definition to bits used for channel configuration.
> >   Removed the comment related to doorbell.
> >
> > Changes in v5
> >   Variable name 'nollp' changed to 'non_ll'.
> >   In the dw_edma_device_config() WARN_ON replaced with dev_err().
> >   Comments follow the 80-column guideline.
> >
> > Changes in v4
> >   No change
> >
> > Changes in v3
> >   No change
> >
> > Changes in v2
> >   Reverted the function return type to u64 for
> >   dw_edma_get_phys_addr().
> >
> > Changes in v1
> >   Changed the function return type for dw_edma_get_phys_addr().
> >   Corrected the typo raised in review.
> > ---
> >  drivers/dma/dw-edma/dw-edma-core.c    | 42 +++++++++++++++++++++---
> >  drivers/dma/dw-edma/dw-edma-core.h    |  1 +
> >  drivers/dma/dw-edma/dw-edma-pcie.c    | 46 ++++++++++++++++++--------
> >  drivers/dma/dw-edma/dw-hdma-v0-core.c | 61
> > ++++++++++++++++++++++++++++++++++-
> >  drivers/dma/dw-edma/dw-hdma-v0-regs.h |  1 +
>
> edma-v0-core.c have not update, if don't support, at least need return failure
> at dw_edma_device_config() when backend is eDMA.
>
> >  include/linux/dma/edma.h              |  1 +
> >  6 files changed, 132 insertions(+), 20 deletions(-)
> >
> > diff --git a/drivers/dma/dw-edma/dw-edma-core.c
> > b/drivers/dma/dw-edma/dw-edma-core.c
> > index b43255f..d37112b 100644
> > --- a/drivers/dma/dw-edma/dw-edma-core.c
> > +++ b/drivers/dma/dw-edma/dw-edma-core.c
> > @@ -223,8 +223,32 @@ static int dw_edma_device_config(struct
> dma_chan *dchan,
> >                                struct dma_slave_config *config)  {
> >       struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
> > +     int non_ll = 0;
> > +
> > +     if (config->peripheral_config &&
> > +         config->peripheral_size != sizeof(int)) {
> > +             dev_err(dchan->device->dev,
> > +                     "config param peripheral size mismatch\n");
> > +             return -EINVAL;
> > +     }
> >
> >       memcpy(&chan->config, config, sizeof(*config));
> > +
> > +     /*
> > +      * When there is no valid LLP base address available then the default
> > +      * DMA ops will use the non-LL mode.
> > +      *
> > +      * Cases where LL mode is enabled and client wants to use the non-LL
> > +      * mode then also client can do so via providing the peripheral_config
> > +      * param.
> > +      */
> > +     if (config->peripheral_config)
> > +             non_ll = *(int *)config->peripheral_config;
> > +
> > +     chan->non_ll = false;
> > +     if (chan->dw->chip->non_ll || (!chan->dw->chip->non_ll && non_ll))
> > +             chan->non_ll = true;
> > +
> >       chan->configured = true;
> >
> >       return 0;
> > @@ -353,7 +377,7 @@ static void dw_edma_device_issue_pending(struct
> dma_chan *dchan)
> >       struct dw_edma_chan *chan = dchan2dw_edma_chan(xfer->dchan);
> >       enum dma_transfer_direction dir = xfer->direction;
> >       struct scatterlist *sg = NULL;
> > -     struct dw_edma_chunk *chunk;
> > +     struct dw_edma_chunk *chunk = NULL;
> >       struct dw_edma_burst *burst;
> >       struct dw_edma_desc *desc;
> >       u64 src_addr, dst_addr;
> > @@ -419,9 +443,11 @@ static void dw_edma_device_issue_pending(struct
> dma_chan *dchan)
> >       if (unlikely(!desc))
> >               goto err_alloc;
> >
> > -     chunk = dw_edma_alloc_chunk(desc);
> > -     if (unlikely(!chunk))
> > -             goto err_alloc;
> > +     if (!chan->non_ll) {
> > +             chunk = dw_edma_alloc_chunk(desc);
> > +             if (unlikely(!chunk))
> > +                     goto err_alloc;
> > +     }
>
> non_ll is the same as ll_max = 1. (or 2, there are link back entry).
>
> If you set ll_max = 1, needn't change this code.
>

The ll_max is defined for the session till the driver is loaded in the kernel.
This code also enables the non-LL mode dynamically upon input from the
DMA client. In this scenario, touching ll_max would not be a good idea
as the ll_max controls the LL entries for all the DMA channels not just for
a single DMA transaction.

> >
> >       if (xfer->type == EDMA_XFER_INTERLEAVED) {
> >               src_addr = xfer->xfer.il->src_start; @@ -450,7 +476,13
> > @@ static void dw_edma_device_issue_pending(struct dma_chan *dchan)
> >               if (xfer->type == EDMA_XFER_SCATTER_GATHER && !sg)
> >                       break;
> >
> > -             if (chunk->bursts_alloc == chan->ll_max) {
> > +             /*
> > +              * For non-LL mode, only a single burst can be handled
> > +              * in a single chunk unlike LL mode where multiple bursts
> > +              * can be configured in a single chunk.
> > +              */
> > +             if ((chunk && chunk->bursts_alloc == chan->ll_max) ||
> > +                 chan->non_ll) {
> >                       chunk = dw_edma_alloc_chunk(desc);
> >                       if (unlikely(!chunk))
> >                               goto err_alloc; diff --git
> > a/drivers/dma/dw-edma/dw-edma-core.h
> > b/drivers/dma/dw-edma/dw-edma-core.h
> > index 71894b9..c8e3d19 100644
> > --- a/drivers/dma/dw-edma/dw-edma-core.h
> > +++ b/drivers/dma/dw-edma/dw-edma-core.h
> > @@ -86,6 +86,7 @@ struct dw_edma_chan {
> >       u8                              configured;
> >
> >       struct dma_slave_config         config;
> > +     bool                            non_ll;
> >  };
> >
> >  struct dw_edma_irq {
> > diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c
> > b/drivers/dma/dw-edma/dw-edma-pcie.c
> > index 2efd149..277ca50 100644
> > --- a/drivers/dma/dw-edma/dw-edma-pcie.c
> > +++ b/drivers/dma/dw-edma/dw-edma-pcie.c
> > @@ -298,6 +298,15 @@ static void
> dw_edma_pcie_get_xilinx_dma_data(struct pci_dev *pdev,
> >       pdata->devmem_phys_off = off;
> >  }
> >
> > +static u64 dw_edma_get_phys_addr(struct pci_dev *pdev,
> > +                              struct dw_edma_pcie_data *pdata,
> > +                              enum pci_barno bar) {
> > +     if (pdev->vendor == PCI_VENDOR_ID_XILINX)
> > +             return pdata->devmem_phys_off;
> > +     return pci_bus_address(pdev, bar); }
> > +
> >  static int dw_edma_pcie_probe(struct pci_dev *pdev,
> >                             const struct pci_device_id *pid)  { @@
> > -307,6 +316,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
> >       struct dw_edma_chip *chip;
> >       int err, nr_irqs;
> >       int i, mask;
> > +     bool non_ll = false;
> >
> >       vsec_data = kmalloc(sizeof(*vsec_data), GFP_KERNEL);
> >       if (!vsec_data)
> > @@ -331,21 +341,24 @@ static int dw_edma_pcie_probe(struct pci_dev
> *pdev,
> >       if (pdev->vendor == PCI_VENDOR_ID_XILINX) {
> >               /*
> >                * There is no valid address found for the LL memory
> > -              * space on the device side.
> > +              * space on the device side. In the absence of LL base
> > +              * address use the non-LL mode or simple mode supported by
> > +              * the HDMA IP.
> >                */
> > -             if (vsec_data->devmem_phys_off ==
> DW_PCIE_XILINX_MDB_INVALID_ADDR)
> > -                     return -ENOMEM;
> > +             if (vsec_data->devmem_phys_off ==
> DW_PCIE_AMD_MDB_INVALID_ADDR)
> > +                     non_ll = true;
> >
> >               /*
> >                * Configure the channel LL and data blocks if number of
> >                * channels enabled in VSEC capability are more than the
> >                * channels configured in xilinx_mdb_data.
> >                */
> > -             dw_edma_set_chan_region_offset(vsec_data, BAR_2, 0,
> > -                                            DW_PCIE_XILINX_MDB_LL_OFF_GAP,
> > -                                            DW_PCIE_XILINX_MDB_LL_SIZE,
> > -                                            DW_PCIE_XILINX_MDB_DT_OFF_GAP,
> > -                                            DW_PCIE_XILINX_MDB_DT_SIZE);
> > +             if (!non_ll)
> > +                     dw_edma_set_chan_region_offset(vsec_data, BAR_2, 0,
> > +                                                    DW_PCIE_XILINX_LL_OFF_GAP,
> > +                                                    DW_PCIE_XILINX_LL_SIZE,
> > +                                                    DW_PCIE_XILINX_DT_OFF_GAP,
> > +
> > + DW_PCIE_XILINX_DT_SIZE);
> >       }
> >
> >       /* Mapping PCI BAR regions */
> > @@ -393,6 +406,7 @@ static int dw_edma_pcie_probe(struct pci_dev
> *pdev,
> >       chip->mf = vsec_data->mf;
> >       chip->nr_irqs = nr_irqs;
> >       chip->ops = &dw_edma_pcie_plat_ops;
> > +     chip->non_ll = non_ll;
> >
> >       chip->ll_wr_cnt = vsec_data->wr_ch_cnt;
> >       chip->ll_rd_cnt = vsec_data->rd_ch_cnt; @@ -401,7 +415,7 @@
> > static int dw_edma_pcie_probe(struct pci_dev *pdev,
> >       if (!chip->reg_base)
> >               return -ENOMEM;
> >
> > -     for (i = 0; i < chip->ll_wr_cnt; i++) {
> > +     for (i = 0; i < chip->ll_wr_cnt && !non_ll; i++) {
> >               struct dw_edma_region *ll_region = &chip->ll_region_wr[i];
> >               struct dw_edma_region *dt_region = &chip->dt_region_wr[i];
> >               struct dw_edma_block *ll_block = &vsec_data->ll_wr[i];
> > @@ -412,7 +426,8 @@ static int dw_edma_pcie_probe(struct pci_dev
> *pdev,
> >                       return -ENOMEM;
> >
> >               ll_region->vaddr.io += ll_block->off;
> > -             ll_region->paddr = pci_bus_address(pdev, ll_block->bar);
> > +             ll_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
> > +                                                      ll_block->bar);
>
> This change need do prepare patch, which only change pci_bus_address() to
> dw_edma_get_phys_addr().
>

This is not clear.

> >               ll_region->paddr += ll_block->off;
> >               ll_region->sz = ll_block->sz;
> >
> > @@ -421,12 +436,13 @@ static int dw_edma_pcie_probe(struct pci_dev
> *pdev,
> >                       return -ENOMEM;
> >
> >               dt_region->vaddr.io += dt_block->off;
> > -             dt_region->paddr = pci_bus_address(pdev, dt_block->bar);
> > +             dt_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
> > +                                                      dt_block->bar);
> >               dt_region->paddr += dt_block->off;
> >               dt_region->sz = dt_block->sz;
> >       }
> >
> > -     for (i = 0; i < chip->ll_rd_cnt; i++) {
> > +     for (i = 0; i < chip->ll_rd_cnt && !non_ll; i++) {
> >               struct dw_edma_region *ll_region = &chip->ll_region_rd[i];
> >               struct dw_edma_region *dt_region = &chip->dt_region_rd[i];
> >               struct dw_edma_block *ll_block = &vsec_data->ll_rd[i];
> > @@ -437,7 +453,8 @@ static int dw_edma_pcie_probe(struct pci_dev
> *pdev,
> >                       return -ENOMEM;
> >
> >               ll_region->vaddr.io += ll_block->off;
> > -             ll_region->paddr = pci_bus_address(pdev, ll_block->bar);
> > +             ll_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
> > +                                                      ll_block->bar);
> >               ll_region->paddr += ll_block->off;
> >               ll_region->sz = ll_block->sz;
> >
> > @@ -446,7 +463,8 @@ static int dw_edma_pcie_probe(struct pci_dev
> *pdev,
> >                       return -ENOMEM;
> >
> >               dt_region->vaddr.io += dt_block->off;
> > -             dt_region->paddr = pci_bus_address(pdev, dt_block->bar);
> > +             dt_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
> > +                                                      dt_block->bar);
> >               dt_region->paddr += dt_block->off;
> >               dt_region->sz = dt_block->sz;
> >       }
> > diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.c
> > b/drivers/dma/dw-edma/dw-hdma-v0-core.c
> > index e3f8db4..a5d12bc 100644
> > --- a/drivers/dma/dw-edma/dw-hdma-v0-core.c
> > +++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c
> > @@ -225,7 +225,7 @@ static void dw_hdma_v0_sync_ll_data(struct
> dw_edma_chunk *chunk)
> >               readl(chunk->ll_region.vaddr.io);  }
> >
> > -static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool
> > first)
> > +static void dw_hdma_v0_core_ll_start(struct dw_edma_chunk *chunk,
> > +bool first)
> >  {
> >       struct dw_edma_chan *chan = chunk->chan;
> >       struct dw_edma *dw = chan->dw;
> > @@ -263,6 +263,65 @@ static void dw_hdma_v0_core_start(struct
> dw_edma_chunk *chunk, bool first)
> >       SET_CH_32(dw, chan->dir, chan->id, doorbell,
> > HDMA_V0_DOORBELL_START);  }
> >
> > +static void dw_hdma_v0_core_non_ll_start(struct dw_edma_chunk
> *chunk)
> > +{
> > +     struct dw_edma_chan *chan = chunk->chan;
> > +     struct dw_edma *dw = chan->dw;
> > +     struct dw_edma_burst *child;
> > +     u32 val;
> > +
> > +     list_for_each_entry(child, &chunk->burst->list, list) {
>
> why need iterated list, it doesn't support ll. Need wait for irq to start next one.
>
> Frank

Yes, this is true. The format is kept similar to LL mode.

>
> > +             SET_CH_32(dw, chan->dir, chan->id, ch_en,
> > + HDMA_V0_CH_EN);
> > +
> > +             /* Source address */
> > +             SET_CH_32(dw, chan->dir, chan->id, sar.lsb,
> > +                       lower_32_bits(child->sar));
> > +             SET_CH_32(dw, chan->dir, chan->id, sar.msb,
> > +                       upper_32_bits(child->sar));
> > +
> > +             /* Destination address */
> > +             SET_CH_32(dw, chan->dir, chan->id, dar.lsb,
> > +                       lower_32_bits(child->dar));
> > +             SET_CH_32(dw, chan->dir, chan->id, dar.msb,
> > +                       upper_32_bits(child->dar));
> > +
> > +             /* Transfer size */
> > +             SET_CH_32(dw, chan->dir, chan->id, transfer_size,
> > + child->sz);
> > +
> > +             /* Interrupt setup */
> > +             val = GET_CH_32(dw, chan->dir, chan->id, int_setup) |
> > +                             HDMA_V0_STOP_INT_MASK |
> > +                             HDMA_V0_ABORT_INT_MASK |
> > +                             HDMA_V0_LOCAL_STOP_INT_EN |
> > +                             HDMA_V0_LOCAL_ABORT_INT_EN;
> > +
> > +             if (!(dw->chip->flags & DW_EDMA_CHIP_LOCAL)) {
> > +                     val |= HDMA_V0_REMOTE_STOP_INT_EN |
> > +                            HDMA_V0_REMOTE_ABORT_INT_EN;
> > +             }
> > +
> > +             SET_CH_32(dw, chan->dir, chan->id, int_setup, val);
> > +
> > +             /* Channel control setup */
> > +             val = GET_CH_32(dw, chan->dir, chan->id, control1);
> > +             val &= ~HDMA_V0_LINKLIST_EN;
> > +             SET_CH_32(dw, chan->dir, chan->id, control1, val);
> > +
> > +             SET_CH_32(dw, chan->dir, chan->id, doorbell,
> > +                       HDMA_V0_DOORBELL_START);
> > +     }
> > +}
> > +
> > +static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool
> > +first) {
> > +     struct dw_edma_chan *chan = chunk->chan;
> > +
> > +     if (chan->non_ll)
> > +             dw_hdma_v0_core_non_ll_start(chunk);
> > +     else
> > +             dw_hdma_v0_core_ll_start(chunk, first); }
> > +
> >  static void dw_hdma_v0_core_ch_config(struct dw_edma_chan *chan)  {
> >       struct dw_edma *dw = chan->dw;
> > diff --git a/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > index eab5fd7..7759ba9 100644
> > --- a/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > +++ b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > @@ -12,6 +12,7 @@
> >  #include <linux/dmaengine.h>
> >
> >  #define HDMA_V0_MAX_NR_CH                    8
> > +#define HDMA_V0_CH_EN                                BIT(0)
> >  #define HDMA_V0_LOCAL_ABORT_INT_EN           BIT(6)
> >  #define HDMA_V0_REMOTE_ABORT_INT_EN          BIT(5)
> >  #define HDMA_V0_LOCAL_STOP_INT_EN            BIT(4)
> > diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h index
> > 3080747..78ce31b 100644
> > --- a/include/linux/dma/edma.h
> > +++ b/include/linux/dma/edma.h
> > @@ -99,6 +99,7 @@ struct dw_edma_chip {
> >       enum dw_edma_map_format mf;
> >
> >       struct dw_edma          *dw;
> > +     bool                    non_ll;
> >  };
> >
> >  /* Export to the platform drivers */
> > --
> > 1.8.3.1
> >

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

* Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
  2026-01-19  9:09     ` Verma, Devendra
@ 2026-01-19 15:45       ` Frank Li
  2026-01-20 11:36         ` Verma, Devendra
  0 siblings, 1 reply; 24+ messages in thread
From: Frank Li @ 2026-01-19 15:45 UTC (permalink / raw)
  To: Verma, Devendra
  Cc: bhelgaas@google.com, mani@kernel.org, vkoul@kernel.org,
	dmaengine@vger.kernel.org, linux-pci@vger.kernel.org,
	linux-kernel@vger.kernel.org, Simek, Michal

On Mon, Jan 19, 2026 at 09:09:17AM +0000, Verma, Devendra wrote:
> [AMD Official Use Only - AMD Internal Distribution Only]
>
> Hi Frank
>
> Please check my response inline.
>
> Regards,
> Devendra
> > -----Original Message-----
> > From: Frank Li <Frank.li@nxp.com>
> > Sent: Thursday, January 15, 2026 10:07 PM
> > To: Verma, Devendra <Devendra.Verma@amd.com>
> > Cc: bhelgaas@google.com; mani@kernel.org; vkoul@kernel.org;
> > dmaengine@vger.kernel.org; linux-pci@vger.kernel.org; linux-
> > kernel@vger.kernel.org; Simek, Michal <michal.simek@amd.com>
> > Subject: Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
> >
> > Caution: This message originated from an External Source. Use proper
> > caution when opening attachments, clicking links, or responding.
> >
> >
> > On Fri, Jan 09, 2026 at 05:33:54PM +0530, Devendra K Verma wrote:
> > > AMD MDB IP supports Linked List (LL) mode as well as non-LL mode.
> > > The current code does not have the mechanisms to enable the DMA
> > > transactions using the non-LL mode. The following two cases are added
> > > with this patch:
> > > - For the AMD (Xilinx) only, when a valid physical base address of
> > >   the device side DDR is not configured, then the IP can still be
> > >   used in non-LL mode. For all the channels DMA transactions will
> >
> > If DDR have not configured, where DATA send to in device side by non-LL
> > mode.
> >
>
> The DDR base address in the VSEC capability is used for driving the DMA
> transfers when used in the LL mode. The DDR is configured and present
> all the time but the DMA PCIe driver uses this DDR base address (physical address)
> to configure the LLP address.
>
> In the scenario, where this DDR base address in VSEC capability is not
> configured then the current controller cannot be used as the default mode
> supported is LL mode only. In order to make the controller usable non-LL mode
> is being added which just needs SAR, DAR, XFERLEN and control register to initiate the
> transfer. So, the DDR is always present, but the DMA PCIe driver need to know
> the DDR base physical address to make the transfer. This is useful in scenarios
> where the memory allocated for LL can be used for DMA transactions as well.

Do you means use DMA transfer LL's context?

>
> > >   be using the non-LL mode only. This, the default non-LL mode,
> > >   is not applicable for Synopsys IP with the current code addition.
> > >
> > > - If the default mode is LL-mode, for both AMD (Xilinx) and Synosys,
> > >   and if user wants to use non-LL mode then user can do so via
> > >   configuring the peripheral_config param of dma_slave_config.
> > >
> > > Signed-off-by: Devendra K Verma <devendra.verma@amd.com>
> > > ---
> > > Changes in v8
> > >   Cosmetic change related to comment and code.
> > >
> > > Changes in v7
> > >   No change
> > >
> > > Changes in v6
> > >   Gave definition to bits used for channel configuration.
> > >   Removed the comment related to doorbell.
> > >
> > > Changes in v5
> > >   Variable name 'nollp' changed to 'non_ll'.
> > >   In the dw_edma_device_config() WARN_ON replaced with dev_err().
> > >   Comments follow the 80-column guideline.
> > >
> > > Changes in v4
> > >   No change
> > >
> > > Changes in v3
> > >   No change
> > >
> > > Changes in v2
> > >   Reverted the function return type to u64 for
> > >   dw_edma_get_phys_addr().
> > >
> > > Changes in v1
> > >   Changed the function return type for dw_edma_get_phys_addr().
> > >   Corrected the typo raised in review.
> > > ---
> > >  drivers/dma/dw-edma/dw-edma-core.c    | 42 +++++++++++++++++++++---
> > >  drivers/dma/dw-edma/dw-edma-core.h    |  1 +
> > >  drivers/dma/dw-edma/dw-edma-pcie.c    | 46 ++++++++++++++++++--------
> > >  drivers/dma/dw-edma/dw-hdma-v0-core.c | 61
> > > ++++++++++++++++++++++++++++++++++-
> > >  drivers/dma/dw-edma/dw-hdma-v0-regs.h |  1 +
> >
> > edma-v0-core.c have not update, if don't support, at least need return failure
> > at dw_edma_device_config() when backend is eDMA.
> >
> > >  include/linux/dma/edma.h              |  1 +
> > >  6 files changed, 132 insertions(+), 20 deletions(-)
> > >
> > > diff --git a/drivers/dma/dw-edma/dw-edma-core.c
> > > b/drivers/dma/dw-edma/dw-edma-core.c
> > > index b43255f..d37112b 100644
> > > --- a/drivers/dma/dw-edma/dw-edma-core.c
> > > +++ b/drivers/dma/dw-edma/dw-edma-core.c
> > > @@ -223,8 +223,32 @@ static int dw_edma_device_config(struct
> > dma_chan *dchan,
> > >                                struct dma_slave_config *config)  {
> > >       struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
> > > +     int non_ll = 0;
> > > +
> > > +     if (config->peripheral_config &&
> > > +         config->peripheral_size != sizeof(int)) {
> > > +             dev_err(dchan->device->dev,
> > > +                     "config param peripheral size mismatch\n");
> > > +             return -EINVAL;
> > > +     }
> > >
> > >       memcpy(&chan->config, config, sizeof(*config));
> > > +
> > > +     /*
> > > +      * When there is no valid LLP base address available then the default
> > > +      * DMA ops will use the non-LL mode.
> > > +      *
> > > +      * Cases where LL mode is enabled and client wants to use the non-LL
> > > +      * mode then also client can do so via providing the peripheral_config
> > > +      * param.
> > > +      */
> > > +     if (config->peripheral_config)
> > > +             non_ll = *(int *)config->peripheral_config;
> > > +
> > > +     chan->non_ll = false;
> > > +     if (chan->dw->chip->non_ll || (!chan->dw->chip->non_ll && non_ll))
> > > +             chan->non_ll = true;
> > > +
> > >       chan->configured = true;
> > >
> > >       return 0;
> > > @@ -353,7 +377,7 @@ static void dw_edma_device_issue_pending(struct
> > dma_chan *dchan)
> > >       struct dw_edma_chan *chan = dchan2dw_edma_chan(xfer->dchan);
> > >       enum dma_transfer_direction dir = xfer->direction;
> > >       struct scatterlist *sg = NULL;
> > > -     struct dw_edma_chunk *chunk;
> > > +     struct dw_edma_chunk *chunk = NULL;
> > >       struct dw_edma_burst *burst;
> > >       struct dw_edma_desc *desc;
> > >       u64 src_addr, dst_addr;
> > > @@ -419,9 +443,11 @@ static void dw_edma_device_issue_pending(struct
> > dma_chan *dchan)
> > >       if (unlikely(!desc))
> > >               goto err_alloc;
> > >
> > > -     chunk = dw_edma_alloc_chunk(desc);
> > > -     if (unlikely(!chunk))
> > > -             goto err_alloc;
> > > +     if (!chan->non_ll) {
> > > +             chunk = dw_edma_alloc_chunk(desc);
> > > +             if (unlikely(!chunk))
> > > +                     goto err_alloc;
> > > +     }
> >
> > non_ll is the same as ll_max = 1. (or 2, there are link back entry).
> >
> > If you set ll_max = 1, needn't change this code.
> >
>
> The ll_max is defined for the session till the driver is loaded in the kernel.
> This code also enables the non-LL mode dynamically upon input from the
> DMA client. In this scenario, touching ll_max would not be a good idea
> as the ll_max controls the LL entries for all the DMA channels not just for
> a single DMA transaction.

You can use new variable, such as ll_avail.

>
> > >
> > >       if (xfer->type == EDMA_XFER_INTERLEAVED) {
> > >               src_addr = xfer->xfer.il->src_start; @@ -450,7 +476,13
> > > @@ static void dw_edma_device_issue_pending(struct dma_chan *dchan)
> > >               if (xfer->type == EDMA_XFER_SCATTER_GATHER && !sg)
> > >                       break;
> > >
> > > -             if (chunk->bursts_alloc == chan->ll_max) {
> > > +             /*
> > > +              * For non-LL mode, only a single burst can be handled
> > > +              * in a single chunk unlike LL mode where multiple bursts
> > > +              * can be configured in a single chunk.
> > > +              */
> > > +             if ((chunk && chunk->bursts_alloc == chan->ll_max) ||
> > > +                 chan->non_ll) {
> > >                       chunk = dw_edma_alloc_chunk(desc);
> > >                       if (unlikely(!chunk))
> > >                               goto err_alloc; diff --git
> > > a/drivers/dma/dw-edma/dw-edma-core.h
> > > b/drivers/dma/dw-edma/dw-edma-core.h
> > > index 71894b9..c8e3d19 100644
> > > --- a/drivers/dma/dw-edma/dw-edma-core.h
> > > +++ b/drivers/dma/dw-edma/dw-edma-core.h
> > > @@ -86,6 +86,7 @@ struct dw_edma_chan {
> > >       u8                              configured;
> > >
> > >       struct dma_slave_config         config;
> > > +     bool                            non_ll;
> > >  };
> > >
> > >  struct dw_edma_irq {
> > > diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c
> > > b/drivers/dma/dw-edma/dw-edma-pcie.c
> > > index 2efd149..277ca50 100644
> > > --- a/drivers/dma/dw-edma/dw-edma-pcie.c
> > > +++ b/drivers/dma/dw-edma/dw-edma-pcie.c
> > > @@ -298,6 +298,15 @@ static void
> > dw_edma_pcie_get_xilinx_dma_data(struct pci_dev *pdev,
> > >       pdata->devmem_phys_off = off;
> > >  }
> > >
> > > +static u64 dw_edma_get_phys_addr(struct pci_dev *pdev,
> > > +                              struct dw_edma_pcie_data *pdata,
> > > +                              enum pci_barno bar) {
> > > +     if (pdev->vendor == PCI_VENDOR_ID_XILINX)
> > > +             return pdata->devmem_phys_off;
> > > +     return pci_bus_address(pdev, bar); }
> > > +
> > >  static int dw_edma_pcie_probe(struct pci_dev *pdev,
> > >                             const struct pci_device_id *pid)  { @@
> > > -307,6 +316,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
> > >       struct dw_edma_chip *chip;
> > >       int err, nr_irqs;
> > >       int i, mask;
> > > +     bool non_ll = false;
> > >
> > >       vsec_data = kmalloc(sizeof(*vsec_data), GFP_KERNEL);
> > >       if (!vsec_data)
> > > @@ -331,21 +341,24 @@ static int dw_edma_pcie_probe(struct pci_dev
> > *pdev,
> > >       if (pdev->vendor == PCI_VENDOR_ID_XILINX) {
> > >               /*
> > >                * There is no valid address found for the LL memory
> > > -              * space on the device side.
> > > +              * space on the device side. In the absence of LL base
> > > +              * address use the non-LL mode or simple mode supported by
> > > +              * the HDMA IP.
> > >                */
> > > -             if (vsec_data->devmem_phys_off ==
> > DW_PCIE_XILINX_MDB_INVALID_ADDR)
> > > -                     return -ENOMEM;
> > > +             if (vsec_data->devmem_phys_off ==
> > DW_PCIE_AMD_MDB_INVALID_ADDR)
> > > +                     non_ll = true;
> > >
> > >               /*
> > >                * Configure the channel LL and data blocks if number of
> > >                * channels enabled in VSEC capability are more than the
> > >                * channels configured in xilinx_mdb_data.
> > >                */
> > > -             dw_edma_set_chan_region_offset(vsec_data, BAR_2, 0,
> > > -                                            DW_PCIE_XILINX_MDB_LL_OFF_GAP,
> > > -                                            DW_PCIE_XILINX_MDB_LL_SIZE,
> > > -                                            DW_PCIE_XILINX_MDB_DT_OFF_GAP,
> > > -                                            DW_PCIE_XILINX_MDB_DT_SIZE);
> > > +             if (!non_ll)
> > > +                     dw_edma_set_chan_region_offset(vsec_data, BAR_2, 0,
> > > +                                                    DW_PCIE_XILINX_LL_OFF_GAP,
> > > +                                                    DW_PCIE_XILINX_LL_SIZE,
> > > +                                                    DW_PCIE_XILINX_DT_OFF_GAP,
> > > +
> > > + DW_PCIE_XILINX_DT_SIZE);
> > >       }
> > >
> > >       /* Mapping PCI BAR regions */
> > > @@ -393,6 +406,7 @@ static int dw_edma_pcie_probe(struct pci_dev
> > *pdev,
> > >       chip->mf = vsec_data->mf;
> > >       chip->nr_irqs = nr_irqs;
> > >       chip->ops = &dw_edma_pcie_plat_ops;
> > > +     chip->non_ll = non_ll;
> > >
> > >       chip->ll_wr_cnt = vsec_data->wr_ch_cnt;
> > >       chip->ll_rd_cnt = vsec_data->rd_ch_cnt; @@ -401,7 +415,7 @@
> > > static int dw_edma_pcie_probe(struct pci_dev *pdev,
> > >       if (!chip->reg_base)
> > >               return -ENOMEM;
> > >
> > > -     for (i = 0; i < chip->ll_wr_cnt; i++) {
> > > +     for (i = 0; i < chip->ll_wr_cnt && !non_ll; i++) {
> > >               struct dw_edma_region *ll_region = &chip->ll_region_wr[i];
> > >               struct dw_edma_region *dt_region = &chip->dt_region_wr[i];
> > >               struct dw_edma_block *ll_block = &vsec_data->ll_wr[i];
> > > @@ -412,7 +426,8 @@ static int dw_edma_pcie_probe(struct pci_dev
> > *pdev,
> > >                       return -ENOMEM;
> > >
> > >               ll_region->vaddr.io += ll_block->off;
> > > -             ll_region->paddr = pci_bus_address(pdev, ll_block->bar);
> > > +             ll_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
> > > +                                                      ll_block->bar);
> >
> > This change need do prepare patch, which only change pci_bus_address() to
> > dw_edma_get_phys_addr().
> >
>
> This is not clear.

why not. only trivial add helper patch, which help reviewer

>
> > >               ll_region->paddr += ll_block->off;
> > >               ll_region->sz = ll_block->sz;
> > >
> > > @@ -421,12 +436,13 @@ static int dw_edma_pcie_probe(struct pci_dev
> > *pdev,
> > >                       return -ENOMEM;
> > >
> > >               dt_region->vaddr.io += dt_block->off;
> > > -             dt_region->paddr = pci_bus_address(pdev, dt_block->bar);
> > > +             dt_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
> > > +                                                      dt_block->bar);
> > >               dt_region->paddr += dt_block->off;
> > >               dt_region->sz = dt_block->sz;
> > >       }
> > >
> > > -     for (i = 0; i < chip->ll_rd_cnt; i++) {
> > > +     for (i = 0; i < chip->ll_rd_cnt && !non_ll; i++) {
> > >               struct dw_edma_region *ll_region = &chip->ll_region_rd[i];
> > >               struct dw_edma_region *dt_region = &chip->dt_region_rd[i];
> > >               struct dw_edma_block *ll_block = &vsec_data->ll_rd[i];
> > > @@ -437,7 +453,8 @@ static int dw_edma_pcie_probe(struct pci_dev
> > *pdev,
> > >                       return -ENOMEM;
> > >
> > >               ll_region->vaddr.io += ll_block->off;
> > > -             ll_region->paddr = pci_bus_address(pdev, ll_block->bar);
> > > +             ll_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
> > > +                                                      ll_block->bar);
> > >               ll_region->paddr += ll_block->off;
> > >               ll_region->sz = ll_block->sz;
> > >
> > > @@ -446,7 +463,8 @@ static int dw_edma_pcie_probe(struct pci_dev
> > *pdev,
> > >                       return -ENOMEM;
> > >
> > >               dt_region->vaddr.io += dt_block->off;
> > > -             dt_region->paddr = pci_bus_address(pdev, dt_block->bar);
> > > +             dt_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
> > > +                                                      dt_block->bar);
> > >               dt_region->paddr += dt_block->off;
> > >               dt_region->sz = dt_block->sz;
> > >       }
> > > diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.c
> > > b/drivers/dma/dw-edma/dw-hdma-v0-core.c
> > > index e3f8db4..a5d12bc 100644
> > > --- a/drivers/dma/dw-edma/dw-hdma-v0-core.c
> > > +++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c
> > > @@ -225,7 +225,7 @@ static void dw_hdma_v0_sync_ll_data(struct
> > dw_edma_chunk *chunk)
> > >               readl(chunk->ll_region.vaddr.io);  }
> > >
> > > -static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool
> > > first)
> > > +static void dw_hdma_v0_core_ll_start(struct dw_edma_chunk *chunk,
> > > +bool first)
> > >  {
> > >       struct dw_edma_chan *chan = chunk->chan;
> > >       struct dw_edma *dw = chan->dw;
> > > @@ -263,6 +263,65 @@ static void dw_hdma_v0_core_start(struct
> > dw_edma_chunk *chunk, bool first)
> > >       SET_CH_32(dw, chan->dir, chan->id, doorbell,
> > > HDMA_V0_DOORBELL_START);  }
> > >
> > > +static void dw_hdma_v0_core_non_ll_start(struct dw_edma_chunk
> > *chunk)
> > > +{
> > > +     struct dw_edma_chan *chan = chunk->chan;
> > > +     struct dw_edma *dw = chan->dw;
> > > +     struct dw_edma_burst *child;
> > > +     u32 val;
> > > +
> > > +     list_for_each_entry(child, &chunk->burst->list, list) {
> >
> > why need iterated list, it doesn't support ll. Need wait for irq to start next one.
> >
> > Frank
>
> Yes, this is true. The format is kept similar to LL mode.

Just fill one. list_for_each_entry() cause confuse.

Frank
>
> >
> > > +             SET_CH_32(dw, chan->dir, chan->id, ch_en,
> > > + HDMA_V0_CH_EN);
> > > +
> > > +             /* Source address */
> > > +             SET_CH_32(dw, chan->dir, chan->id, sar.lsb,
> > > +                       lower_32_bits(child->sar));
> > > +             SET_CH_32(dw, chan->dir, chan->id, sar.msb,
> > > +                       upper_32_bits(child->sar));
> > > +
> > > +             /* Destination address */
> > > +             SET_CH_32(dw, chan->dir, chan->id, dar.lsb,
> > > +                       lower_32_bits(child->dar));
> > > +             SET_CH_32(dw, chan->dir, chan->id, dar.msb,
> > > +                       upper_32_bits(child->dar));
> > > +
> > > +             /* Transfer size */
> > > +             SET_CH_32(dw, chan->dir, chan->id, transfer_size,
> > > + child->sz);
> > > +
> > > +             /* Interrupt setup */
> > > +             val = GET_CH_32(dw, chan->dir, chan->id, int_setup) |
> > > +                             HDMA_V0_STOP_INT_MASK |
> > > +                             HDMA_V0_ABORT_INT_MASK |
> > > +                             HDMA_V0_LOCAL_STOP_INT_EN |
> > > +                             HDMA_V0_LOCAL_ABORT_INT_EN;
> > > +
> > > +             if (!(dw->chip->flags & DW_EDMA_CHIP_LOCAL)) {
> > > +                     val |= HDMA_V0_REMOTE_STOP_INT_EN |
> > > +                            HDMA_V0_REMOTE_ABORT_INT_EN;
> > > +             }
> > > +
> > > +             SET_CH_32(dw, chan->dir, chan->id, int_setup, val);
> > > +
> > > +             /* Channel control setup */
> > > +             val = GET_CH_32(dw, chan->dir, chan->id, control1);
> > > +             val &= ~HDMA_V0_LINKLIST_EN;
> > > +             SET_CH_32(dw, chan->dir, chan->id, control1, val);
> > > +
> > > +             SET_CH_32(dw, chan->dir, chan->id, doorbell,
> > > +                       HDMA_V0_DOORBELL_START);
> > > +     }
> > > +}
> > > +
> > > +static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool
> > > +first) {
> > > +     struct dw_edma_chan *chan = chunk->chan;
> > > +
> > > +     if (chan->non_ll)
> > > +             dw_hdma_v0_core_non_ll_start(chunk);
> > > +     else
> > > +             dw_hdma_v0_core_ll_start(chunk, first); }
> > > +
> > >  static void dw_hdma_v0_core_ch_config(struct dw_edma_chan *chan)  {
> > >       struct dw_edma *dw = chan->dw;
> > > diff --git a/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > index eab5fd7..7759ba9 100644
> > > --- a/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > +++ b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > @@ -12,6 +12,7 @@
> > >  #include <linux/dmaengine.h>
> > >
> > >  #define HDMA_V0_MAX_NR_CH                    8
> > > +#define HDMA_V0_CH_EN                                BIT(0)
> > >  #define HDMA_V0_LOCAL_ABORT_INT_EN           BIT(6)
> > >  #define HDMA_V0_REMOTE_ABORT_INT_EN          BIT(5)
> > >  #define HDMA_V0_LOCAL_STOP_INT_EN            BIT(4)
> > > diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h index
> > > 3080747..78ce31b 100644
> > > --- a/include/linux/dma/edma.h
> > > +++ b/include/linux/dma/edma.h
> > > @@ -99,6 +99,7 @@ struct dw_edma_chip {
> > >       enum dw_edma_map_format mf;
> > >
> > >       struct dw_edma          *dw;
> > > +     bool                    non_ll;
> > >  };
> > >
> > >  /* Export to the platform drivers */
> > > --
> > > 1.8.3.1
> > >

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

* Re: [PATCH v8 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint Support
  2026-01-19  9:09     ` Verma, Devendra
@ 2026-01-19 15:59       ` Frank Li
  2026-01-20 11:36         ` Verma, Devendra
  0 siblings, 1 reply; 24+ messages in thread
From: Frank Li @ 2026-01-19 15:59 UTC (permalink / raw)
  To: Verma, Devendra
  Cc: bhelgaas@google.com, mani@kernel.org, vkoul@kernel.org,
	dmaengine@vger.kernel.org, linux-pci@vger.kernel.org,
	linux-kernel@vger.kernel.org, Simek, Michal

On Mon, Jan 19, 2026 at 09:09:11AM +0000, Verma, Devendra wrote:
> [AMD Official Use Only - AMD Internal Distribution Only]
>
> Hi Frank
>
> Please check my comments inline.
>
> Regards,
> Devendra
>
> > -----Original Message-----
> > From: Frank Li <Frank.li@nxp.com>
> > Sent: Thursday, January 15, 2026 9:51 PM
> > To: Verma, Devendra <Devendra.Verma@amd.com>
> > Cc: bhelgaas@google.com; mani@kernel.org; vkoul@kernel.org;
> > dmaengine@vger.kernel.org; linux-pci@vger.kernel.org; linux-
> > kernel@vger.kernel.org; Simek, Michal <michal.simek@amd.com>
> > Subject: Re: [PATCH v8 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint
> > Support
> >
> > Caution: This message originated from an External Source. Use proper
> > caution when opening attachments, clicking links, or responding.
> >
> >
> > On Fri, Jan 09, 2026 at 05:33:53PM +0530, Devendra K Verma wrote:
> > > AMD MDB PCIe endpoint support. For AMD specific support added the
> > > following
> > >   - AMD supported PCIe Device IDs and Vendor ID (Xilinx).
> > >   - AMD MDB specific driver data
> > >   - AMD MDB specific VSEC capability to retrieve the device DDR
> > >     base address.
> > >
> > > Signed-off-by: Devendra K Verma <devendra.verma@amd.com>
> > > ---
> > > Changes in v8:
> > > Changed the contant names to includer product vendor.
> > > Moved the vendor specific code to vendor specific functions.
> > >
> > > Changes in v7:
> > > Introduced vendor specific functions to retrieve the vsec data.
> > >
> > > Changes in v6:
> > > Included "sizes.h" header and used the appropriate definitions instead
> > > of constants.
> > >
> > > Changes in v5:
> > > Added the definitions for Xilinx specific VSEC header id, revision,
> > > and register offsets.
> > > Corrected the error type when no physical offset found for device side
> > > memory.
> > > Corrected the order of variables.
> > >
> > > Changes in v4:
> > > Configured 8 read and 8 write channels for Xilinx vendor Added checks
> > > to validate vendor ID for vendor specific vsec id.
> > > Added Xilinx specific vendor id for vsec specific to Xilinx Added the
> > > LL and data region offsets, size as input params to function
> > > dw_edma_set_chan_region_offset().
> > > Moved the LL and data region offsets assignment to function for Xilinx
> > > specific case.
> > > Corrected comments.
> > >
> > > Changes in v3:
> > > Corrected a typo when assigning AMD (Xilinx) vsec id macro and
> > > condition check.
> > >
> > > Changes in v2:
> > > Reverted the devmem_phys_off type to u64.
> > > Renamed the function appropriately to suit the functionality for
> > > setting the LL & data region offsets.
> > >
> > > Changes in v1:
> > > Removed the pci device id from pci_ids.h file.
> > > Added the vendor id macro as per the suggested method.
> > > Changed the type of the newly added devmem_phys_off variable.
> > > Added to logic to assign offsets for LL and data region blocks in case
> > > more number of channels are enabled than given in amd_mdb_data struct.
> > > ---
> > >  drivers/dma/dw-edma/dw-edma-pcie.c | 192
> > > ++++++++++++++++++++++++++++++++++---
> > >  1 file changed, 178 insertions(+), 14 deletions(-)
> > >
> > > diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c
> > > b/drivers/dma/dw-edma/dw-edma-pcie.c
> > > index 3371e0a7..2efd149 100644
> > > --- a/drivers/dma/dw-edma/dw-edma-pcie.c
> > > +++ b/drivers/dma/dw-edma/dw-edma-pcie.c
> > > @@ -14,14 +14,35 @@
> > >  #include <linux/pci-epf.h>
> > >  #include <linux/msi.h>
> > >  #include <linux/bitfield.h>
> > > +#include <linux/sizes.h>
> > >
> > >  #include "dw-edma-core.h"
> > >
> > > -#define DW_PCIE_VSEC_DMA_ID                  0x6
> > > -#define DW_PCIE_VSEC_DMA_BAR                 GENMASK(10, 8)
> > > -#define DW_PCIE_VSEC_DMA_MAP                 GENMASK(2, 0)
> > > -#define DW_PCIE_VSEC_DMA_WR_CH                       GENMASK(9, 0)
> > > -#define DW_PCIE_VSEC_DMA_RD_CH                       GENMASK(25, 16)
> > > +/* Synopsys */
> > > +#define DW_PCIE_SYNOPSYS_VSEC_DMA_ID         0x6
> > > +#define DW_PCIE_SYNOPSYS_VSEC_DMA_BAR                GENMASK(10, 8)
> > > +#define DW_PCIE_SYNOPSYS_VSEC_DMA_MAP                GENMASK(2, 0)
> > > +#define DW_PCIE_SYNOPSYS_VSEC_DMA_WR_CH              GENMASK(9, 0)
> > > +#define DW_PCIE_SYNOPSYS_VSEC_DMA_RD_CH              GENMASK(25, 16)
> >
> > Sorry, jump into at v8.
> > According to my understand 'DW' means 'Synopsys'.
> >
>
> Yes, DW means Designware representing Synopsys here.
> For the sake of clarity, a distinction was required to separate the names of macros
> having the similar purpose for other IP, Xilinx in this case. Otherwise, it is causing confusion
> which macros to use for which vendor. This also helps in future if any of the vendors
> try to retrieve a new or different VSEC IDs then all they need is to define macros which
> clearly show the association with the vendor, thus eliminating the confusion.

If want to reuse the driver, driver owner take reponsiblity to find the
difference.

If define a whole set of register, the reader is hard to find real
difference.

>
> > > +
> > > +/* AMD MDB (Xilinx) specific defines */
> > > +#define PCI_DEVICE_ID_XILINX_B054            0xb054
> > > +
> > > +#define DW_PCIE_XILINX_MDB_VSEC_DMA_ID               0x6
> > > +#define DW_PCIE_XILINX_MDB_VSEC_ID           0x20
> > > +#define DW_PCIE_XILINX_MDB_VSEC_DMA_BAR              GENMASK(10, 8)
> > > +#define DW_PCIE_XILINX_MDB_VSEC_DMA_MAP              GENMASK(2, 0)
> > > +#define DW_PCIE_XILINX_MDB_VSEC_DMA_WR_CH    GENMASK(9, 0)
> > > +#define DW_PCIE_XILINX_MDB_VSEC_DMA_RD_CH    GENMASK(25, 16)
> >
> > These defination is the same. Need redefine again
> >
>
> It is the similar case as explained for the previous comment. Please check.
>
> > > +
> > > +#define DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_HIGH       0xc
> > > +#define DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_LOW        0x8
> > > +#define DW_PCIE_XILINX_MDB_INVALID_ADDR              (~0ULL)
> >
> > I think XILINX_PCIE_MDB_DEVMEM_OFF_REG_HIGH
> >
> > > +
> > > +#define DW_PCIE_XILINX_MDB_LL_OFF_GAP                0x200000
> > > +#define DW_PCIE_XILINX_MDB_LL_SIZE           0x800
> > > +#define DW_PCIE_XILINX_MDB_DT_OFF_GAP                0x100000
> > > +#define DW_PCIE_XILINX_MDB_DT_SIZE           0x800
> > >
> > >  #define DW_BLOCK(a, b, c) \
> > >       { \
> > > @@ -50,6 +71,7 @@ struct dw_edma_pcie_data {
> > >       u8                              irqs;
> > >       u16                             wr_ch_cnt;
> > >       u16                             rd_ch_cnt;
> > > +     u64                             devmem_phys_off;
> > >  };
> > >
> > >  static const struct dw_edma_pcie_data snps_edda_data = { @@ -90,6
> > > +112,64 @@ struct dw_edma_pcie_data {
> > >       .rd_ch_cnt                      = 2,
> > >  };
> > >
> > > +static const struct dw_edma_pcie_data xilinx_mdb_data = {
> > > +     /* MDB registers location */
> > > +     .rg.bar                         = BAR_0,
> > > +     .rg.off                         = SZ_4K,        /*  4 Kbytes */
> > > +     .rg.sz                          = SZ_8K,        /*  8 Kbytes */
> > > +
> > > +     /* Other */
> > > +     .mf                             = EDMA_MF_HDMA_NATIVE,
> > > +     .irqs                           = 1,
> > > +     .wr_ch_cnt                      = 8,
> > > +     .rd_ch_cnt                      = 8,
> > > +};
> > > +
> > > +static void dw_edma_set_chan_region_offset(struct dw_edma_pcie_data
> > *pdata,
> > > +                                        enum pci_barno bar, off_t start_off,
> > > +                                        off_t ll_off_gap, size_t ll_size,
> > > +                                        off_t dt_off_gap, size_t
> > > +dt_size) {
> > > +     u16 wr_ch = pdata->wr_ch_cnt;
> > > +     u16 rd_ch = pdata->rd_ch_cnt;
> > > +     off_t off;
> > > +     u16 i;
> > > +
> > > +     off = start_off;
> > > +
> > > +     /* Write channel LL region */
> > > +     for (i = 0; i < wr_ch; i++) {
> > > +             pdata->ll_wr[i].bar = bar;
> > > +             pdata->ll_wr[i].off = off;
> > > +             pdata->ll_wr[i].sz = ll_size;
> > > +             off += ll_off_gap;
> > > +     }
> > > +
> > > +     /* Read channel LL region */
> > > +     for (i = 0; i < rd_ch; i++) {
> > > +             pdata->ll_rd[i].bar = bar;
> > > +             pdata->ll_rd[i].off = off;
> > > +             pdata->ll_rd[i].sz = ll_size;
> > > +             off += ll_off_gap;
> > > +     }
> > > +
> > > +     /* Write channel data region */
> > > +     for (i = 0; i < wr_ch; i++) {
> > > +             pdata->dt_wr[i].bar = bar;
> > > +             pdata->dt_wr[i].off = off;
> > > +             pdata->dt_wr[i].sz = dt_size;
> > > +             off += dt_off_gap;
> > > +     }
> > > +
> > > +     /* Read channel data region */
> > > +     for (i = 0; i < rd_ch; i++) {
> > > +             pdata->dt_rd[i].bar = bar;
> > > +             pdata->dt_rd[i].off = off;
> > > +             pdata->dt_rd[i].sz = dt_size;
> > > +             off += dt_off_gap;
> > > +     }
> > > +}
> > > +
> > >  static int dw_edma_pcie_irq_vector(struct device *dev, unsigned int
> > > nr)  {
> > >       return pci_irq_vector(to_pci_dev(dev), nr); @@ -114,15 +194,15
> > > @@ static u64 dw_edma_pcie_address(struct device *dev, phys_addr_t
> > cpu_addr)
> > >       .pci_address = dw_edma_pcie_address,  };
> > >
> > > -static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
> > > -                                        struct dw_edma_pcie_data *pdata)
> > > +static void dw_edma_pcie_get_synopsys_dma_data(struct pci_dev *pdev,
> > > +                                            struct dw_edma_pcie_data
> > > +*pdata)
> > >  {
> > >       u32 val, map;
> > >       u16 vsec;
> > >       u64 off;
> > >
> > >       vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_SYNOPSYS,
> > > -                                     DW_PCIE_VSEC_DMA_ID);
> > > +                                     DW_PCIE_SYNOPSYS_VSEC_DMA_ID);
> > >       if (!vsec)
> > >               return;
> > >
> > > @@ -131,9 +211,9 @@ static void
> > dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
> > >           PCI_VNDR_HEADER_LEN(val) != 0x18)
> > >               return;
> > >
> > > -     pci_dbg(pdev, "Detected PCIe Vendor-Specific Extended Capability
> > DMA\n");
> > > +     pci_dbg(pdev, "Detected Synopsys PCIe Vendor-Specific Extended
> > > + Capability DMA\n");
> > >       pci_read_config_dword(pdev, vsec + 0x8, &val);
> > > -     map = FIELD_GET(DW_PCIE_VSEC_DMA_MAP, val);
> > > +     map = FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_MAP, val);
> > >       if (map != EDMA_MF_EDMA_LEGACY &&
> > >           map != EDMA_MF_EDMA_UNROLL &&
> > >           map != EDMA_MF_HDMA_COMPAT && @@ -141,13 +221,13 @@
> > static
> > > void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
> > >               return;
> > >
> > >       pdata->mf = map;
> > > -     pdata->rg.bar = FIELD_GET(DW_PCIE_VSEC_DMA_BAR, val);
> > > +     pdata->rg.bar = FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_BAR, val);
> > >
> > >       pci_read_config_dword(pdev, vsec + 0xc, &val);
> > >       pdata->wr_ch_cnt = min_t(u16, pdata->wr_ch_cnt,
> > > -                              FIELD_GET(DW_PCIE_VSEC_DMA_WR_CH, val));
> > > +
> > > + FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_WR_CH, val));
> > >       pdata->rd_ch_cnt = min_t(u16, pdata->rd_ch_cnt,
> > > -                              FIELD_GET(DW_PCIE_VSEC_DMA_RD_CH, val));
> > > +
> > > + FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_RD_CH, val));
> >
> > If you don't change macro name, these change is not necessary. If really need
> > change macro name, make change macro name as sperated patch.
> >
>
> As explained above, the name change is required to avoid confusion.
> The trigger to have the separate names for each IP is the inclusion of Xilinx IP that
> is why no separate patch is created.

Separate patch renmae macro only. Reviewer can simple bypass this typo
trivial patch.

Then add new one.

Actually, Needn't rename at all.  You can directly use XLINNK_PCIE_*

Frank
>
> > >
> > >       pci_read_config_dword(pdev, vsec + 0x14, &val);
> > >       off = val;
> > > @@ -157,6 +237,67 @@ static void
> > dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
> > >       pdata->rg.off = off;
> > >  }
> > >
> > > +static void dw_edma_pcie_get_xilinx_dma_data(struct pci_dev *pdev,
> > > +                                          struct dw_edma_pcie_data
> > > +*pdata) {
> > > +     u32 val, map;
> > > +     u16 vsec;
> > > +     u64 off;
> > > +
> > > +     pdata->devmem_phys_off = DW_PCIE_XILINX_MDB_INVALID_ADDR;
> > > +
> > > +     vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_XILINX,
> > > +                                     DW_PCIE_XILINX_MDB_VSEC_DMA_ID);
> > > +     if (!vsec)
> > > +             return;
> > > +
> > > +     pci_read_config_dword(pdev, vsec + PCI_VNDR_HEADER, &val);
> > > +     if (PCI_VNDR_HEADER_REV(val) != 0x00 ||
> > > +         PCI_VNDR_HEADER_LEN(val) != 0x18)
> > > +             return;
> > > +
> > > +     pci_dbg(pdev, "Detected Xilinx PCIe Vendor-Specific Extended Capability
> > DMA\n");
> > > +     pci_read_config_dword(pdev, vsec + 0x8, &val);
> > > +     map = FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_MAP, val);
> > > +     if (map != EDMA_MF_EDMA_LEGACY &&
> > > +         map != EDMA_MF_EDMA_UNROLL &&
> > > +         map != EDMA_MF_HDMA_COMPAT &&
> > > +         map != EDMA_MF_HDMA_NATIVE)
> > > +             return;
> > > +
> > > +     pdata->mf = map;
> > > +     pdata->rg.bar = FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_BAR,
> > val);
> > > +
> > > +     pci_read_config_dword(pdev, vsec + 0xc, &val);
> > > +     pdata->wr_ch_cnt = min_t(u16, pdata->wr_ch_cnt,
> > > +                              FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_WR_CH,
> > val));
> > > +     pdata->rd_ch_cnt = min_t(u16, pdata->rd_ch_cnt,
> > > +
> > > + FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_RD_CH, val));
> > > +
> > > +     pci_read_config_dword(pdev, vsec + 0x14, &val);
> > > +     off = val;
> > > +     pci_read_config_dword(pdev, vsec + 0x10, &val);
> > > +     off <<= 32;
> > > +     off |= val;
> > > +     pdata->rg.off = off;
> > > +
> > > +     vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_XILINX,
> > > +                                     DW_PCIE_XILINX_MDB_VSEC_ID);
> > > +     if (!vsec)
> > > +             return;
> > > +
> > > +     pci_read_config_dword(pdev,
> > > +                           vsec + DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_HIGH,
> > > +                           &val);
> > > +     off = val;
> > > +     pci_read_config_dword(pdev,
> > > +                           vsec + DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_LOW,
> > > +                           &val);
> > > +     off <<= 32;
> > > +     off |= val;
> > > +     pdata->devmem_phys_off = off;
> > > +}
> > > +
> > >  static int dw_edma_pcie_probe(struct pci_dev *pdev,
> > >                             const struct pci_device_id *pid)  { @@
> > > -184,7 +325,28 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
> > >        * Tries to find if exists a PCIe Vendor-Specific Extended Capability
> > >        * for the DMA, if one exists, then reconfigures it.
> > >        */
> > > -     dw_edma_pcie_get_vsec_dma_data(pdev, vsec_data);
> > > +     dw_edma_pcie_get_synopsys_dma_data(pdev, vsec_data);
> > > +     dw_edma_pcie_get_xilinx_dma_data(pdev, vsec_data);
> > > +
> > > +     if (pdev->vendor == PCI_VENDOR_ID_XILINX) {
> >
> > dw_edma_pcie_get_xilinx_dma_data() should be here.
> >
> > Frank
>
> Yes, this is good suggestion. Thanks!
>
> > > +             /*
> > > +              * There is no valid address found for the LL memory
> > > +              * space on the device side.
> > > +              */
> > > +             if (vsec_data->devmem_phys_off ==
> > DW_PCIE_XILINX_MDB_INVALID_ADDR)
> > > +                     return -ENOMEM;
> > > +
> > > +             /*
> > > +              * Configure the channel LL and data blocks if number of
> > > +              * channels enabled in VSEC capability are more than the
> > > +              * channels configured in xilinx_mdb_data.
> > > +              */
> > > +             dw_edma_set_chan_region_offset(vsec_data, BAR_2, 0,
> > > +                                            DW_PCIE_XILINX_MDB_LL_OFF_GAP,
> > > +                                            DW_PCIE_XILINX_MDB_LL_SIZE,
> > > +                                            DW_PCIE_XILINX_MDB_DT_OFF_GAP,
> > > +                                            DW_PCIE_XILINX_MDB_DT_SIZE);
> > > +     }
> > >
> > >       /* Mapping PCI BAR regions */
> > >       mask = BIT(vsec_data->rg.bar);
> > > @@ -367,6 +529,8 @@ static void dw_edma_pcie_remove(struct pci_dev
> > > *pdev)
> > >
> > >  static const struct pci_device_id dw_edma_pcie_id_table[] = {
> > >       { PCI_DEVICE_DATA(SYNOPSYS, EDDA, &snps_edda_data) },
> > > +     { PCI_VDEVICE(XILINX, PCI_DEVICE_ID_XILINX_B054),
> > > +       (kernel_ulong_t)&xilinx_mdb_data },
> > >       { }
> > >  };
> > >  MODULE_DEVICE_TABLE(pci, dw_edma_pcie_id_table);
> > > --
> > > 1.8.3.1
> > >

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

* RE: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
  2026-01-19 15:45       ` Frank Li
@ 2026-01-20 11:36         ` Verma, Devendra
  2026-01-21 17:18           ` Frank Li
  0 siblings, 1 reply; 24+ messages in thread
From: Verma, Devendra @ 2026-01-20 11:36 UTC (permalink / raw)
  To: Frank Li
  Cc: bhelgaas@google.com, mani@kernel.org, vkoul@kernel.org,
	dmaengine@vger.kernel.org, linux-pci@vger.kernel.org,
	linux-kernel@vger.kernel.org, Simek, Michal, Verma, Devendra

[AMD Official Use Only - AMD Internal Distribution Only]

> -----Original Message-----
> From: Frank Li <Frank.li@nxp.com>
> Sent: Monday, January 19, 2026 9:15 PM
> To: Verma, Devendra <Devendra.Verma@amd.com>
> Cc: bhelgaas@google.com; mani@kernel.org; vkoul@kernel.org;
> dmaengine@vger.kernel.org; linux-pci@vger.kernel.org; linux-
> kernel@vger.kernel.org; Simek, Michal <michal.simek@amd.com>
> Subject: Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
>
> Caution: This message originated from an External Source. Use proper caution
> when opening attachments, clicking links, or responding.
>
>
> On Mon, Jan 19, 2026 at 09:09:17AM +0000, Verma, Devendra wrote:
> > [AMD Official Use Only - AMD Internal Distribution Only]
> >
> > Hi Frank
> >
> > Please check my response inline.
> >
> > Regards,
> > Devendra
> > > -----Original Message-----
> > > From: Frank Li <Frank.li@nxp.com>
> > > Sent: Thursday, January 15, 2026 10:07 PM
> > > To: Verma, Devendra <Devendra.Verma@amd.com>
> > > Cc: bhelgaas@google.com; mani@kernel.org; vkoul@kernel.org;
> > > dmaengine@vger.kernel.org; linux-pci@vger.kernel.org; linux-
> > > kernel@vger.kernel.org; Simek, Michal <michal.simek@amd.com>
> > > Subject: Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
> > >
> > > Caution: This message originated from an External Source. Use proper
> > > caution when opening attachments, clicking links, or responding.
> > >
> > >
> > > On Fri, Jan 09, 2026 at 05:33:54PM +0530, Devendra K Verma wrote:
> > > > AMD MDB IP supports Linked List (LL) mode as well as non-LL mode.
> > > > The current code does not have the mechanisms to enable the DMA
> > > > transactions using the non-LL mode. The following two cases are
> > > > added with this patch:
> > > > - For the AMD (Xilinx) only, when a valid physical base address of
> > > >   the device side DDR is not configured, then the IP can still be
> > > >   used in non-LL mode. For all the channels DMA transactions will
> > >
> > > If DDR have not configured, where DATA send to in device side by
> > > non-LL mode.
> > >
> >
> > The DDR base address in the VSEC capability is used for driving the
> > DMA transfers when used in the LL mode. The DDR is configured and
> > present all the time but the DMA PCIe driver uses this DDR base
> > address (physical address) to configure the LLP address.
> >
> > In the scenario, where this DDR base address in VSEC capability is not
> > configured then the current controller cannot be used as the default
> > mode supported is LL mode only. In order to make the controller usable
> > non-LL mode is being added which just needs SAR, DAR, XFERLEN and
> > control register to initiate the transfer. So, the DDR is always
> > present, but the DMA PCIe driver need to know the DDR base physical
> > address to make the transfer. This is useful in scenarios where the memory
> allocated for LL can be used for DMA transactions as well.
>
> Do you means use DMA transfer LL's context?
>

Yes, the device side memory reserved for the link list to store the descriptors,
accessed by the host via BAR_2 in this driver code.

> >
> > > >   be using the non-LL mode only. This, the default non-LL mode,
> > > >   is not applicable for Synopsys IP with the current code addition.
> > > >
> > > > - If the default mode is LL-mode, for both AMD (Xilinx) and Synosys,
> > > >   and if user wants to use non-LL mode then user can do so via
> > > >   configuring the peripheral_config param of dma_slave_config.
> > > >
> > > > Signed-off-by: Devendra K Verma <devendra.verma@amd.com>
> > > > ---
> > > > Changes in v8
> > > >   Cosmetic change related to comment and code.
> > > >
> > > > Changes in v7
> > > >   No change
> > > >
> > > > Changes in v6
> > > >   Gave definition to bits used for channel configuration.
> > > >   Removed the comment related to doorbell.
> > > >
> > > > Changes in v5
> > > >   Variable name 'nollp' changed to 'non_ll'.
> > > >   In the dw_edma_device_config() WARN_ON replaced with dev_err().
> > > >   Comments follow the 80-column guideline.
> > > >
> > > > Changes in v4
> > > >   No change
> > > >
> > > > Changes in v3
> > > >   No change
> > > >
> > > > Changes in v2
> > > >   Reverted the function return type to u64 for
> > > >   dw_edma_get_phys_addr().
> > > >
> > > > Changes in v1
> > > >   Changed the function return type for dw_edma_get_phys_addr().
> > > >   Corrected the typo raised in review.
> > > > ---
> > > >  drivers/dma/dw-edma/dw-edma-core.c    | 42
> +++++++++++++++++++++---
> > > >  drivers/dma/dw-edma/dw-edma-core.h    |  1 +
> > > >  drivers/dma/dw-edma/dw-edma-pcie.c    | 46 ++++++++++++++++++--
> ------
> > > >  drivers/dma/dw-edma/dw-hdma-v0-core.c | 61
> > > > ++++++++++++++++++++++++++++++++++-
> > > >  drivers/dma/dw-edma/dw-hdma-v0-regs.h |  1 +
> > >
> > > edma-v0-core.c have not update, if don't support, at least need
> > > return failure at dw_edma_device_config() when backend is eDMA.
> > >
> > > >  include/linux/dma/edma.h              |  1 +
> > > >  6 files changed, 132 insertions(+), 20 deletions(-)
> > > >
> > > > diff --git a/drivers/dma/dw-edma/dw-edma-core.c
> > > > b/drivers/dma/dw-edma/dw-edma-core.c
> > > > index b43255f..d37112b 100644
> > > > --- a/drivers/dma/dw-edma/dw-edma-core.c
> > > > +++ b/drivers/dma/dw-edma/dw-edma-core.c
> > > > @@ -223,8 +223,32 @@ static int dw_edma_device_config(struct
> > > dma_chan *dchan,
> > > >                                struct dma_slave_config *config)  {
> > > >       struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
> > > > +     int non_ll = 0;
> > > > +
> > > > +     if (config->peripheral_config &&
> > > > +         config->peripheral_size != sizeof(int)) {
> > > > +             dev_err(dchan->device->dev,
> > > > +                     "config param peripheral size mismatch\n");
> > > > +             return -EINVAL;
> > > > +     }
> > > >
> > > >       memcpy(&chan->config, config, sizeof(*config));
> > > > +
> > > > +     /*
> > > > +      * When there is no valid LLP base address available then the default
> > > > +      * DMA ops will use the non-LL mode.
> > > > +      *
> > > > +      * Cases where LL mode is enabled and client wants to use the non-LL
> > > > +      * mode then also client can do so via providing the peripheral_config
> > > > +      * param.
> > > > +      */
> > > > +     if (config->peripheral_config)
> > > > +             non_ll = *(int *)config->peripheral_config;
> > > > +
> > > > +     chan->non_ll = false;
> > > > +     if (chan->dw->chip->non_ll || (!chan->dw->chip->non_ll && non_ll))
> > > > +             chan->non_ll = true;
> > > > +
> > > >       chan->configured = true;
> > > >
> > > >       return 0;
> > > > @@ -353,7 +377,7 @@ static void
> > > > dw_edma_device_issue_pending(struct
> > > dma_chan *dchan)
> > > >       struct dw_edma_chan *chan = dchan2dw_edma_chan(xfer->dchan);
> > > >       enum dma_transfer_direction dir = xfer->direction;
> > > >       struct scatterlist *sg = NULL;
> > > > -     struct dw_edma_chunk *chunk;
> > > > +     struct dw_edma_chunk *chunk = NULL;
> > > >       struct dw_edma_burst *burst;
> > > >       struct dw_edma_desc *desc;
> > > >       u64 src_addr, dst_addr;
> > > > @@ -419,9 +443,11 @@ static void
> > > > dw_edma_device_issue_pending(struct
> > > dma_chan *dchan)
> > > >       if (unlikely(!desc))
> > > >               goto err_alloc;
> > > >
> > > > -     chunk = dw_edma_alloc_chunk(desc);
> > > > -     if (unlikely(!chunk))
> > > > -             goto err_alloc;
> > > > +     if (!chan->non_ll) {
> > > > +             chunk = dw_edma_alloc_chunk(desc);
> > > > +             if (unlikely(!chunk))
> > > > +                     goto err_alloc;
> > > > +     }
> > >
> > > non_ll is the same as ll_max = 1. (or 2, there are link back entry).
> > >
> > > If you set ll_max = 1, needn't change this code.
> > >
> >
> > The ll_max is defined for the session till the driver is loaded in the kernel.
> > This code also enables the non-LL mode dynamically upon input from the
> > DMA client. In this scenario, touching ll_max would not be a good idea
> > as the ll_max controls the LL entries for all the DMA channels not
> > just for a single DMA transaction.
>
> You can use new variable, such as ll_avail.
>

In order to separate out the execution paths a new meaningful variable "non_ll"
is used. The variable "non_ll" alone is sufficient. Using another variable
along side "non_ll" for the similar purpose may not have any added advantage.

> >
> > > >
> > > >       if (xfer->type == EDMA_XFER_INTERLEAVED) {
> > > >               src_addr = xfer->xfer.il->src_start; @@ -450,7
> > > > +476,13 @@ static void dw_edma_device_issue_pending(struct
> dma_chan *dchan)
> > > >               if (xfer->type == EDMA_XFER_SCATTER_GATHER && !sg)
> > > >                       break;
> > > >
> > > > -             if (chunk->bursts_alloc == chan->ll_max) {
> > > > +             /*
> > > > +              * For non-LL mode, only a single burst can be handled
> > > > +              * in a single chunk unlike LL mode where multiple bursts
> > > > +              * can be configured in a single chunk.
> > > > +              */
> > > > +             if ((chunk && chunk->bursts_alloc == chan->ll_max) ||
> > > > +                 chan->non_ll) {
> > > >                       chunk = dw_edma_alloc_chunk(desc);
> > > >                       if (unlikely(!chunk))
> > > >                               goto err_alloc; diff --git
> > > > a/drivers/dma/dw-edma/dw-edma-core.h
> > > > b/drivers/dma/dw-edma/dw-edma-core.h
> > > > index 71894b9..c8e3d19 100644
> > > > --- a/drivers/dma/dw-edma/dw-edma-core.h
> > > > +++ b/drivers/dma/dw-edma/dw-edma-core.h
> > > > @@ -86,6 +86,7 @@ struct dw_edma_chan {
> > > >       u8                              configured;
> > > >
> > > >       struct dma_slave_config         config;
> > > > +     bool                            non_ll;
> > > >  };
> > > >
> > > >  struct dw_edma_irq {
> > > > diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c
> > > > b/drivers/dma/dw-edma/dw-edma-pcie.c
> > > > index 2efd149..277ca50 100644
> > > > --- a/drivers/dma/dw-edma/dw-edma-pcie.c
> > > > +++ b/drivers/dma/dw-edma/dw-edma-pcie.c
> > > > @@ -298,6 +298,15 @@ static void
> > > dw_edma_pcie_get_xilinx_dma_data(struct pci_dev *pdev,
> > > >       pdata->devmem_phys_off = off;  }
> > > >
> > > > +static u64 dw_edma_get_phys_addr(struct pci_dev *pdev,
> > > > +                              struct dw_edma_pcie_data *pdata,
> > > > +                              enum pci_barno bar) {
> > > > +     if (pdev->vendor == PCI_VENDOR_ID_XILINX)
> > > > +             return pdata->devmem_phys_off;
> > > > +     return pci_bus_address(pdev, bar); }
> > > > +
> > > >  static int dw_edma_pcie_probe(struct pci_dev *pdev,
> > > >                             const struct pci_device_id *pid)  { @@
> > > > -307,6 +316,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
> > > >       struct dw_edma_chip *chip;
> > > >       int err, nr_irqs;
> > > >       int i, mask;
> > > > +     bool non_ll = false;
> > > >
> > > >       vsec_data = kmalloc(sizeof(*vsec_data), GFP_KERNEL);
> > > >       if (!vsec_data)
> > > > @@ -331,21 +341,24 @@ static int dw_edma_pcie_probe(struct
> pci_dev
> > > *pdev,
> > > >       if (pdev->vendor == PCI_VENDOR_ID_XILINX) {
> > > >               /*
> > > >                * There is no valid address found for the LL memory
> > > > -              * space on the device side.
> > > > +              * space on the device side. In the absence of LL base
> > > > +              * address use the non-LL mode or simple mode supported by
> > > > +              * the HDMA IP.
> > > >                */
> > > > -             if (vsec_data->devmem_phys_off ==
> > > DW_PCIE_XILINX_MDB_INVALID_ADDR)
> > > > -                     return -ENOMEM;
> > > > +             if (vsec_data->devmem_phys_off ==
> > > DW_PCIE_AMD_MDB_INVALID_ADDR)
> > > > +                     non_ll = true;
> > > >
> > > >               /*
> > > >                * Configure the channel LL and data blocks if number of
> > > >                * channels enabled in VSEC capability are more than the
> > > >                * channels configured in xilinx_mdb_data.
> > > >                */
> > > > -             dw_edma_set_chan_region_offset(vsec_data, BAR_2, 0,
> > > > -                                            DW_PCIE_XILINX_MDB_LL_OFF_GAP,
> > > > -                                            DW_PCIE_XILINX_MDB_LL_SIZE,
> > > > -                                            DW_PCIE_XILINX_MDB_DT_OFF_GAP,
> > > > -                                            DW_PCIE_XILINX_MDB_DT_SIZE);
> > > > +             if (!non_ll)
> > > > +                     dw_edma_set_chan_region_offset(vsec_data, BAR_2, 0,
> > > > +                                                    DW_PCIE_XILINX_LL_OFF_GAP,
> > > > +                                                    DW_PCIE_XILINX_LL_SIZE,
> > > > +
> > > > + DW_PCIE_XILINX_DT_OFF_GAP,
> > > > +
> > > > + DW_PCIE_XILINX_DT_SIZE);
> > > >       }
> > > >
> > > >       /* Mapping PCI BAR regions */ @@ -393,6 +406,7 @@ static int
> > > > dw_edma_pcie_probe(struct pci_dev
> > > *pdev,
> > > >       chip->mf = vsec_data->mf;
> > > >       chip->nr_irqs = nr_irqs;
> > > >       chip->ops = &dw_edma_pcie_plat_ops;
> > > > +     chip->non_ll = non_ll;
> > > >
> > > >       chip->ll_wr_cnt = vsec_data->wr_ch_cnt;
> > > >       chip->ll_rd_cnt = vsec_data->rd_ch_cnt; @@ -401,7 +415,7 @@
> > > > static int dw_edma_pcie_probe(struct pci_dev *pdev,
> > > >       if (!chip->reg_base)
> > > >               return -ENOMEM;
> > > >
> > > > -     for (i = 0; i < chip->ll_wr_cnt; i++) {
> > > > +     for (i = 0; i < chip->ll_wr_cnt && !non_ll; i++) {
> > > >               struct dw_edma_region *ll_region = &chip->ll_region_wr[i];
> > > >               struct dw_edma_region *dt_region = &chip->dt_region_wr[i];
> > > >               struct dw_edma_block *ll_block =
> > > > &vsec_data->ll_wr[i]; @@ -412,7 +426,8 @@ static int
> > > > dw_edma_pcie_probe(struct pci_dev
> > > *pdev,
> > > >                       return -ENOMEM;
> > > >
> > > >               ll_region->vaddr.io += ll_block->off;
> > > > -             ll_region->paddr = pci_bus_address(pdev, ll_block->bar);
> > > > +             ll_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
> > > > +
> > > > + ll_block->bar);
> > >
> > > This change need do prepare patch, which only change
> > > pci_bus_address() to dw_edma_get_phys_addr().
> > >
> >
> > This is not clear.
>
> why not. only trivial add helper patch, which help reviewer
>

I was not clear on the question you asked.
It does not look justified when a patch is raised alone just to replace this function.
The function change is required cause the same code *can* support different IPs from
different vendors. And, with this single change alone in the code the support for another
IP is added. That's why it is easier to get the reason for the change in
the function name and syntax.

> >
> > > >               ll_region->paddr += ll_block->off;
> > > >               ll_region->sz = ll_block->sz;
> > > >
> > > > @@ -421,12 +436,13 @@ static int dw_edma_pcie_probe(struct
> pci_dev
> > > *pdev,
> > > >                       return -ENOMEM;
> > > >
> > > >               dt_region->vaddr.io += dt_block->off;
> > > > -             dt_region->paddr = pci_bus_address(pdev, dt_block->bar);
> > > > +             dt_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
> > > > +
> > > > + dt_block->bar);
> > > >               dt_region->paddr += dt_block->off;
> > > >               dt_region->sz = dt_block->sz;
> > > >       }
> > > >
> > > > -     for (i = 0; i < chip->ll_rd_cnt; i++) {
> > > > +     for (i = 0; i < chip->ll_rd_cnt && !non_ll; i++) {
> > > >               struct dw_edma_region *ll_region = &chip->ll_region_rd[i];
> > > >               struct dw_edma_region *dt_region = &chip->dt_region_rd[i];
> > > >               struct dw_edma_block *ll_block =
> > > > &vsec_data->ll_rd[i]; @@ -437,7 +453,8 @@ static int
> > > > dw_edma_pcie_probe(struct pci_dev
> > > *pdev,
> > > >                       return -ENOMEM;
> > > >
> > > >               ll_region->vaddr.io += ll_block->off;
> > > > -             ll_region->paddr = pci_bus_address(pdev, ll_block->bar);
> > > > +             ll_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
> > > > +
> > > > + ll_block->bar);
> > > >               ll_region->paddr += ll_block->off;
> > > >               ll_region->sz = ll_block->sz;
> > > >
> > > > @@ -446,7 +463,8 @@ static int dw_edma_pcie_probe(struct pci_dev
> > > *pdev,
> > > >                       return -ENOMEM;
> > > >
> > > >               dt_region->vaddr.io += dt_block->off;
> > > > -             dt_region->paddr = pci_bus_address(pdev, dt_block->bar);
> > > > +             dt_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
> > > > +
> > > > + dt_block->bar);
> > > >               dt_region->paddr += dt_block->off;
> > > >               dt_region->sz = dt_block->sz;
> > > >       }
> > > > diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.c
> > > > b/drivers/dma/dw-edma/dw-hdma-v0-core.c
> > > > index e3f8db4..a5d12bc 100644
> > > > --- a/drivers/dma/dw-edma/dw-hdma-v0-core.c
> > > > +++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c
> > > > @@ -225,7 +225,7 @@ static void dw_hdma_v0_sync_ll_data(struct
> > > dw_edma_chunk *chunk)
> > > >               readl(chunk->ll_region.vaddr.io);  }
> > > >
> > > > -static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk,
> > > > bool
> > > > first)
> > > > +static void dw_hdma_v0_core_ll_start(struct dw_edma_chunk *chunk,
> > > > +bool first)
> > > >  {
> > > >       struct dw_edma_chan *chan = chunk->chan;
> > > >       struct dw_edma *dw = chan->dw; @@ -263,6 +263,65 @@ static
> > > > void dw_hdma_v0_core_start(struct
> > > dw_edma_chunk *chunk, bool first)
> > > >       SET_CH_32(dw, chan->dir, chan->id, doorbell,
> > > > HDMA_V0_DOORBELL_START);  }
> > > >
> > > > +static void dw_hdma_v0_core_non_ll_start(struct dw_edma_chunk
> > > *chunk)
> > > > +{
> > > > +     struct dw_edma_chan *chan = chunk->chan;
> > > > +     struct dw_edma *dw = chan->dw;
> > > > +     struct dw_edma_burst *child;
> > > > +     u32 val;
> > > > +
> > > > +     list_for_each_entry(child, &chunk->burst->list, list) {
> > >
> > > why need iterated list, it doesn't support ll. Need wait for irq to start next
> one.
> > >
> > > Frank
> >
> > Yes, this is true. The format is kept similar to LL mode.
>
> Just fill one. list_for_each_entry() cause confuse.
>
> Frank

I see, we can use list_first_entry_or_null() which is dependent on giving
the type of pointer, compared to this list_for_each_entry() looks neat and
agnostic to the pointer type being used. Though, it can be explored further.
Also, when the chunk is allocated, the comment clearly spells out how
the allocation would be for the non LL mode so it is evident that each
chunk would have single entry and with that understanding it is clear that
loop will also be used in a similar manner, to retrieve a single entry. It is a
similar use case as of "do {}while (0)" albeit needs a context to understand it.

> >
> > >
> > > > +             SET_CH_32(dw, chan->dir, chan->id, ch_en,
> > > > + HDMA_V0_CH_EN);
> > > > +
> > > > +             /* Source address */
> > > > +             SET_CH_32(dw, chan->dir, chan->id, sar.lsb,
> > > > +                       lower_32_bits(child->sar));
> > > > +             SET_CH_32(dw, chan->dir, chan->id, sar.msb,
> > > > +                       upper_32_bits(child->sar));
> > > > +
> > > > +             /* Destination address */
> > > > +             SET_CH_32(dw, chan->dir, chan->id, dar.lsb,
> > > > +                       lower_32_bits(child->dar));
> > > > +             SET_CH_32(dw, chan->dir, chan->id, dar.msb,
> > > > +                       upper_32_bits(child->dar));
> > > > +
> > > > +             /* Transfer size */
> > > > +             SET_CH_32(dw, chan->dir, chan->id, transfer_size,
> > > > + child->sz);
> > > > +
> > > > +             /* Interrupt setup */
> > > > +             val = GET_CH_32(dw, chan->dir, chan->id, int_setup) |
> > > > +                             HDMA_V0_STOP_INT_MASK |
> > > > +                             HDMA_V0_ABORT_INT_MASK |
> > > > +                             HDMA_V0_LOCAL_STOP_INT_EN |
> > > > +                             HDMA_V0_LOCAL_ABORT_INT_EN;
> > > > +
> > > > +             if (!(dw->chip->flags & DW_EDMA_CHIP_LOCAL)) {
> > > > +                     val |= HDMA_V0_REMOTE_STOP_INT_EN |
> > > > +                            HDMA_V0_REMOTE_ABORT_INT_EN;
> > > > +             }
> > > > +
> > > > +             SET_CH_32(dw, chan->dir, chan->id, int_setup, val);
> > > > +
> > > > +             /* Channel control setup */
> > > > +             val = GET_CH_32(dw, chan->dir, chan->id, control1);
> > > > +             val &= ~HDMA_V0_LINKLIST_EN;
> > > > +             SET_CH_32(dw, chan->dir, chan->id, control1, val);
> > > > +
> > > > +             SET_CH_32(dw, chan->dir, chan->id, doorbell,
> > > > +                       HDMA_V0_DOORBELL_START);
> > > > +     }
> > > > +}
> > > > +
> > > > +static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk,
> > > > +bool
> > > > +first) {
> > > > +     struct dw_edma_chan *chan = chunk->chan;
> > > > +
> > > > +     if (chan->non_ll)
> > > > +             dw_hdma_v0_core_non_ll_start(chunk);
> > > > +     else
> > > > +             dw_hdma_v0_core_ll_start(chunk, first); }
> > > > +
> > > >  static void dw_hdma_v0_core_ch_config(struct dw_edma_chan *chan)
> {
> > > >       struct dw_edma *dw = chan->dw; diff --git
> > > > a/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > index eab5fd7..7759ba9 100644
> > > > --- a/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > +++ b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > @@ -12,6 +12,7 @@
> > > >  #include <linux/dmaengine.h>
> > > >
> > > >  #define HDMA_V0_MAX_NR_CH                    8
> > > > +#define HDMA_V0_CH_EN                                BIT(0)
> > > >  #define HDMA_V0_LOCAL_ABORT_INT_EN           BIT(6)
> > > >  #define HDMA_V0_REMOTE_ABORT_INT_EN          BIT(5)
> > > >  #define HDMA_V0_LOCAL_STOP_INT_EN            BIT(4)
> > > > diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h
> > > > index 3080747..78ce31b 100644
> > > > --- a/include/linux/dma/edma.h
> > > > +++ b/include/linux/dma/edma.h
> > > > @@ -99,6 +99,7 @@ struct dw_edma_chip {
> > > >       enum dw_edma_map_format mf;
> > > >
> > > >       struct dw_edma          *dw;
> > > > +     bool                    non_ll;
> > > >  };
> > > >
> > > >  /* Export to the platform drivers */
> > > > --
> > > > 1.8.3.1
> > > >

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

* RE: [PATCH v8 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint Support
  2026-01-19 15:59       ` Frank Li
@ 2026-01-20 11:36         ` Verma, Devendra
  2026-01-21 16:54           ` Frank Li
  0 siblings, 1 reply; 24+ messages in thread
From: Verma, Devendra @ 2026-01-20 11:36 UTC (permalink / raw)
  To: Frank Li
  Cc: bhelgaas@google.com, mani@kernel.org, vkoul@kernel.org,
	dmaengine@vger.kernel.org, linux-pci@vger.kernel.org,
	linux-kernel@vger.kernel.org, Simek, Michal, Verma, Devendra

[AMD Official Use Only - AMD Internal Distribution Only]

> -----Original Message-----
> From: Frank Li <Frank.li@nxp.com>
> Sent: Monday, January 19, 2026 9:30 PM
> To: Verma, Devendra <Devendra.Verma@amd.com>
> Cc: bhelgaas@google.com; mani@kernel.org; vkoul@kernel.org;
> dmaengine@vger.kernel.org; linux-pci@vger.kernel.org; linux-
> kernel@vger.kernel.org; Simek, Michal <michal.simek@amd.com>
> Subject: Re: [PATCH v8 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint
> Support
>
> Caution: This message originated from an External Source. Use proper
> caution when opening attachments, clicking links, or responding.
>
>
> On Mon, Jan 19, 2026 at 09:09:11AM +0000, Verma, Devendra wrote:
> > [AMD Official Use Only - AMD Internal Distribution Only]
> >
> > Hi Frank
> >
> > Please check my comments inline.
> >
> > Regards,
> > Devendra
> >
> > > -----Original Message-----
> > > From: Frank Li <Frank.li@nxp.com>
> > > Sent: Thursday, January 15, 2026 9:51 PM
> > > To: Verma, Devendra <Devendra.Verma@amd.com>
> > > Cc: bhelgaas@google.com; mani@kernel.org; vkoul@kernel.org;
> > > dmaengine@vger.kernel.org; linux-pci@vger.kernel.org; linux-
> > > kernel@vger.kernel.org; Simek, Michal <michal.simek@amd.com>
> > > Subject: Re: [PATCH v8 1/2] dmaengine: dw-edma: Add AMD MDB
> Endpoint
> > > Support
> > >
> > > Caution: This message originated from an External Source. Use proper
> > > caution when opening attachments, clicking links, or responding.
> > >
> > >
> > > On Fri, Jan 09, 2026 at 05:33:53PM +0530, Devendra K Verma wrote:
> > > > AMD MDB PCIe endpoint support. For AMD specific support added the
> > > > following
> > > >   - AMD supported PCIe Device IDs and Vendor ID (Xilinx).
> > > >   - AMD MDB specific driver data
> > > >   - AMD MDB specific VSEC capability to retrieve the device DDR
> > > >     base address.
> > > >
> > > > Signed-off-by: Devendra K Verma <devendra.verma@amd.com>
> > > > ---
> > > > Changes in v8:
> > > > Changed the contant names to includer product vendor.
> > > > Moved the vendor specific code to vendor specific functions.
> > > >
> > > > Changes in v7:
> > > > Introduced vendor specific functions to retrieve the vsec data.
> > > >
> > > > Changes in v6:
> > > > Included "sizes.h" header and used the appropriate definitions
> > > > instead of constants.
> > > >
> > > > Changes in v5:
> > > > Added the definitions for Xilinx specific VSEC header id,
> > > > revision, and register offsets.
> > > > Corrected the error type when no physical offset found for device
> > > > side memory.
> > > > Corrected the order of variables.
> > > >
> > > > Changes in v4:
> > > > Configured 8 read and 8 write channels for Xilinx vendor Added
> > > > checks to validate vendor ID for vendor specific vsec id.
> > > > Added Xilinx specific vendor id for vsec specific to Xilinx Added
> > > > the LL and data region offsets, size as input params to function
> > > > dw_edma_set_chan_region_offset().
> > > > Moved the LL and data region offsets assignment to function for
> > > > Xilinx specific case.
> > > > Corrected comments.
> > > >
> > > > Changes in v3:
> > > > Corrected a typo when assigning AMD (Xilinx) vsec id macro and
> > > > condition check.
> > > >
> > > > Changes in v2:
> > > > Reverted the devmem_phys_off type to u64.
> > > > Renamed the function appropriately to suit the functionality for
> > > > setting the LL & data region offsets.
> > > >
> > > > Changes in v1:
> > > > Removed the pci device id from pci_ids.h file.
> > > > Added the vendor id macro as per the suggested method.
> > > > Changed the type of the newly added devmem_phys_off variable.
> > > > Added to logic to assign offsets for LL and data region blocks in
> > > > case more number of channels are enabled than given in
> amd_mdb_data struct.
> > > > ---
> > > >  drivers/dma/dw-edma/dw-edma-pcie.c | 192
> > > > ++++++++++++++++++++++++++++++++++---
> > > >  1 file changed, 178 insertions(+), 14 deletions(-)
> > > >
> > > > diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c
> > > > b/drivers/dma/dw-edma/dw-edma-pcie.c
> > > > index 3371e0a7..2efd149 100644
> > > > --- a/drivers/dma/dw-edma/dw-edma-pcie.c
> > > > +++ b/drivers/dma/dw-edma/dw-edma-pcie.c
> > > > @@ -14,14 +14,35 @@
> > > >  #include <linux/pci-epf.h>
> > > >  #include <linux/msi.h>
> > > >  #include <linux/bitfield.h>
> > > > +#include <linux/sizes.h>
> > > >
> > > >  #include "dw-edma-core.h"
> > > >
> > > > -#define DW_PCIE_VSEC_DMA_ID                  0x6
> > > > -#define DW_PCIE_VSEC_DMA_BAR                 GENMASK(10, 8)
> > > > -#define DW_PCIE_VSEC_DMA_MAP                 GENMASK(2, 0)
> > > > -#define DW_PCIE_VSEC_DMA_WR_CH                       GENMASK(9, 0)
> > > > -#define DW_PCIE_VSEC_DMA_RD_CH                       GENMASK(25, 16)
> > > > +/* Synopsys */
> > > > +#define DW_PCIE_SYNOPSYS_VSEC_DMA_ID         0x6
> > > > +#define DW_PCIE_SYNOPSYS_VSEC_DMA_BAR                GENMASK(10,
> 8)
> > > > +#define DW_PCIE_SYNOPSYS_VSEC_DMA_MAP                GENMASK(2, 0)
> > > > +#define DW_PCIE_SYNOPSYS_VSEC_DMA_WR_CH              GENMASK(9,
> 0)
> > > > +#define DW_PCIE_SYNOPSYS_VSEC_DMA_RD_CH              GENMASK(25,
> 16)
> > >
> > > Sorry, jump into at v8.
> > > According to my understand 'DW' means 'Synopsys'.
> > >
> >
> > Yes, DW means Designware representing Synopsys here.
> > For the sake of clarity, a distinction was required to separate the
> > names of macros having the similar purpose for other IP, Xilinx in
> > this case. Otherwise, it is causing confusion which macros to use for
> > which vendor. This also helps in future if any of the vendors try to
> > retrieve a new or different VSEC IDs then all they need is to define macros
> which clearly show the association with the vendor, thus eliminating the
> confusion.
>
> If want to reuse the driver, driver owner take reponsiblity to find the
> difference.
>
> If define a whole set of register, the reader is hard to find real difference.
>

It is not regarding the register set rather VSEC capability which can differ
in the purpose even for the similar IPs. As is the current case where one
VSEC ID serves the similar purpose for both the IPs while the VSEC ID = 0x20
differs in meaning for Synopsys and Xilinx thus I think it is OK to define new
macros as long as they do not create confusion.

> >
> > > > +
> > > > +/* AMD MDB (Xilinx) specific defines */
> > > > +#define PCI_DEVICE_ID_XILINX_B054            0xb054
> > > > +
> > > > +#define DW_PCIE_XILINX_MDB_VSEC_DMA_ID               0x6
> > > > +#define DW_PCIE_XILINX_MDB_VSEC_ID           0x20
> > > > +#define DW_PCIE_XILINX_MDB_VSEC_DMA_BAR              GENMASK(10,
> 8)
> > > > +#define DW_PCIE_XILINX_MDB_VSEC_DMA_MAP              GENMASK(2,
> 0)
> > > > +#define DW_PCIE_XILINX_MDB_VSEC_DMA_WR_CH    GENMASK(9, 0)
> > > > +#define DW_PCIE_XILINX_MDB_VSEC_DMA_RD_CH    GENMASK(25,
> 16)
> > >
> > > These defination is the same. Need redefine again
> > >
> >
> > It is the similar case as explained for the previous comment. Please check.
> >
> > > > +
> > > > +#define DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_HIGH       0xc
> > > > +#define DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_LOW        0x8
> > > > +#define DW_PCIE_XILINX_MDB_INVALID_ADDR              (~0ULL)
> > >
> > > I think XILINX_PCIE_MDB_DEVMEM_OFF_REG_HIGH
> > >
> > > > +
> > > > +#define DW_PCIE_XILINX_MDB_LL_OFF_GAP                0x200000
> > > > +#define DW_PCIE_XILINX_MDB_LL_SIZE           0x800
> > > > +#define DW_PCIE_XILINX_MDB_DT_OFF_GAP                0x100000
> > > > +#define DW_PCIE_XILINX_MDB_DT_SIZE           0x800
> > > >
> > > >  #define DW_BLOCK(a, b, c) \
> > > >       { \
> > > > @@ -50,6 +71,7 @@ struct dw_edma_pcie_data {
> > > >       u8                              irqs;
> > > >       u16                             wr_ch_cnt;
> > > >       u16                             rd_ch_cnt;
> > > > +     u64                             devmem_phys_off;
> > > >  };
> > > >
> > > >  static const struct dw_edma_pcie_data snps_edda_data = { @@ -90,6
> > > > +112,64 @@ struct dw_edma_pcie_data {
> > > >       .rd_ch_cnt                      = 2,
> > > >  };
> > > >
> > > > +static const struct dw_edma_pcie_data xilinx_mdb_data = {
> > > > +     /* MDB registers location */
> > > > +     .rg.bar                         = BAR_0,
> > > > +     .rg.off                         = SZ_4K,        /*  4 Kbytes */
> > > > +     .rg.sz                          = SZ_8K,        /*  8 Kbytes */
> > > > +
> > > > +     /* Other */
> > > > +     .mf                             = EDMA_MF_HDMA_NATIVE,
> > > > +     .irqs                           = 1,
> > > > +     .wr_ch_cnt                      = 8,
> > > > +     .rd_ch_cnt                      = 8,
> > > > +};
> > > > +
> > > > +static void dw_edma_set_chan_region_offset(struct
> > > > +dw_edma_pcie_data
> > > *pdata,
> > > > +                                        enum pci_barno bar, off_t start_off,
> > > > +                                        off_t ll_off_gap, size_t ll_size,
> > > > +                                        off_t dt_off_gap, size_t
> > > > +dt_size) {
> > > > +     u16 wr_ch = pdata->wr_ch_cnt;
> > > > +     u16 rd_ch = pdata->rd_ch_cnt;
> > > > +     off_t off;
> > > > +     u16 i;
> > > > +
> > > > +     off = start_off;
> > > > +
> > > > +     /* Write channel LL region */
> > > > +     for (i = 0; i < wr_ch; i++) {
> > > > +             pdata->ll_wr[i].bar = bar;
> > > > +             pdata->ll_wr[i].off = off;
> > > > +             pdata->ll_wr[i].sz = ll_size;
> > > > +             off += ll_off_gap;
> > > > +     }
> > > > +
> > > > +     /* Read channel LL region */
> > > > +     for (i = 0; i < rd_ch; i++) {
> > > > +             pdata->ll_rd[i].bar = bar;
> > > > +             pdata->ll_rd[i].off = off;
> > > > +             pdata->ll_rd[i].sz = ll_size;
> > > > +             off += ll_off_gap;
> > > > +     }
> > > > +
> > > > +     /* Write channel data region */
> > > > +     for (i = 0; i < wr_ch; i++) {
> > > > +             pdata->dt_wr[i].bar = bar;
> > > > +             pdata->dt_wr[i].off = off;
> > > > +             pdata->dt_wr[i].sz = dt_size;
> > > > +             off += dt_off_gap;
> > > > +     }
> > > > +
> > > > +     /* Read channel data region */
> > > > +     for (i = 0; i < rd_ch; i++) {
> > > > +             pdata->dt_rd[i].bar = bar;
> > > > +             pdata->dt_rd[i].off = off;
> > > > +             pdata->dt_rd[i].sz = dt_size;
> > > > +             off += dt_off_gap;
> > > > +     }
> > > > +}
> > > > +
> > > >  static int dw_edma_pcie_irq_vector(struct device *dev, unsigned
> > > > int
> > > > nr)  {
> > > >       return pci_irq_vector(to_pci_dev(dev), nr); @@ -114,15
> > > > +194,15 @@ static u64 dw_edma_pcie_address(struct device *dev,
> > > > phys_addr_t
> > > cpu_addr)
> > > >       .pci_address = dw_edma_pcie_address,  };
> > > >
> > > > -static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
> > > > -                                        struct dw_edma_pcie_data *pdata)
> > > > +static void dw_edma_pcie_get_synopsys_dma_data(struct pci_dev
> *pdev,
> > > > +                                            struct
> > > > +dw_edma_pcie_data
> > > > +*pdata)
> > > >  {
> > > >       u32 val, map;
> > > >       u16 vsec;
> > > >       u64 off;
> > > >
> > > >       vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_SYNOPSYS,
> > > > -                                     DW_PCIE_VSEC_DMA_ID);
> > > > +
> > > > + DW_PCIE_SYNOPSYS_VSEC_DMA_ID);
> > > >       if (!vsec)
> > > >               return;
> > > >
> > > > @@ -131,9 +211,9 @@ static void
> > > dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
> > > >           PCI_VNDR_HEADER_LEN(val) != 0x18)
> > > >               return;
> > > >
> > > > -     pci_dbg(pdev, "Detected PCIe Vendor-Specific Extended Capability
> > > DMA\n");
> > > > +     pci_dbg(pdev, "Detected Synopsys PCIe Vendor-Specific
> > > > + Extended Capability DMA\n");
> > > >       pci_read_config_dword(pdev, vsec + 0x8, &val);
> > > > -     map = FIELD_GET(DW_PCIE_VSEC_DMA_MAP, val);
> > > > +     map = FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_MAP, val);
> > > >       if (map != EDMA_MF_EDMA_LEGACY &&
> > > >           map != EDMA_MF_EDMA_UNROLL &&
> > > >           map != EDMA_MF_HDMA_COMPAT && @@ -141,13 +221,13 @@
> > > static
> > > > void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
> > > >               return;
> > > >
> > > >       pdata->mf = map;
> > > > -     pdata->rg.bar = FIELD_GET(DW_PCIE_VSEC_DMA_BAR, val);
> > > > +     pdata->rg.bar = FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_BAR,
> > > > + val);
> > > >
> > > >       pci_read_config_dword(pdev, vsec + 0xc, &val);
> > > >       pdata->wr_ch_cnt = min_t(u16, pdata->wr_ch_cnt,
> > > > -                              FIELD_GET(DW_PCIE_VSEC_DMA_WR_CH, val));
> > > > +
> > > > + FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_WR_CH, val));
> > > >       pdata->rd_ch_cnt = min_t(u16, pdata->rd_ch_cnt,
> > > > -                              FIELD_GET(DW_PCIE_VSEC_DMA_RD_CH, val));
> > > > +
> > > > + FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_RD_CH, val));
> > >
> > > If you don't change macro name, these change is not necessary. If
> > > really need change macro name, make change macro name as sperated
> patch.
> > >
> >
> > As explained above, the name change is required to avoid confusion.
> > The trigger to have the separate names for each IP is the inclusion of
> > Xilinx IP that is why no separate patch is created.
>
> Separate patch renmae macro only. Reviewer can simple bypass this typo
> trivial patch.
>
> Then add new one.
>
> Actually, Needn't rename at all.  You can directly use XLINNK_PCIE_*
>
> Frank

Please check the discussion on previous versions of the same patch series.
We have this patch as the outcome of those discussions.
Other reviewing members felt it that keeping the name similar for the
VSEC ID having similar purpose for two different IPs was causing the confusion
that is why it was agreed upon the separate out the naming as per the
vendor-name of VSEC ID. Regarding the separate patch, the reason is introduction
of the new IP which mostly supports the similar functionality except in case of VSEC IDs
that's why the name separation became part of these patches. It sets the context for
changing the name of the existing macros.

> >
> > > >
> > > >       pci_read_config_dword(pdev, vsec + 0x14, &val);
> > > >       off = val;
> > > > @@ -157,6 +237,67 @@ static void
> > > dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
> > > >       pdata->rg.off = off;
> > > >  }
> > > >
> > > > +static void dw_edma_pcie_get_xilinx_dma_data(struct pci_dev *pdev,
> > > > +                                          struct
> > > > +dw_edma_pcie_data
> > > > +*pdata) {
> > > > +     u32 val, map;
> > > > +     u16 vsec;
> > > > +     u64 off;
> > > > +
> > > > +     pdata->devmem_phys_off =
> DW_PCIE_XILINX_MDB_INVALID_ADDR;
> > > > +
> > > > +     vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_XILINX,
> > > > +                                     DW_PCIE_XILINX_MDB_VSEC_DMA_ID);
> > > > +     if (!vsec)
> > > > +             return;
> > > > +
> > > > +     pci_read_config_dword(pdev, vsec + PCI_VNDR_HEADER, &val);
> > > > +     if (PCI_VNDR_HEADER_REV(val) != 0x00 ||
> > > > +         PCI_VNDR_HEADER_LEN(val) != 0x18)
> > > > +             return;
> > > > +
> > > > +     pci_dbg(pdev, "Detected Xilinx PCIe Vendor-Specific Extended
> > > > + Capability
> > > DMA\n");
> > > > +     pci_read_config_dword(pdev, vsec + 0x8, &val);
> > > > +     map = FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_MAP, val);
> > > > +     if (map != EDMA_MF_EDMA_LEGACY &&
> > > > +         map != EDMA_MF_EDMA_UNROLL &&
> > > > +         map != EDMA_MF_HDMA_COMPAT &&
> > > > +         map != EDMA_MF_HDMA_NATIVE)
> > > > +             return;
> > > > +
> > > > +     pdata->mf = map;
> > > > +     pdata->rg.bar =
> FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_BAR,
> > > val);
> > > > +
> > > > +     pci_read_config_dword(pdev, vsec + 0xc, &val);
> > > > +     pdata->wr_ch_cnt = min_t(u16, pdata->wr_ch_cnt,
> > > > +
> > > > + FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_WR_CH,
> > > val));
> > > > +     pdata->rd_ch_cnt = min_t(u16, pdata->rd_ch_cnt,
> > > > +
> > > > + FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_RD_CH, val));
> > > > +
> > > > +     pci_read_config_dword(pdev, vsec + 0x14, &val);
> > > > +     off = val;
> > > > +     pci_read_config_dword(pdev, vsec + 0x10, &val);
> > > > +     off <<= 32;
> > > > +     off |= val;
> > > > +     pdata->rg.off = off;
> > > > +
> > > > +     vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_XILINX,
> > > > +                                     DW_PCIE_XILINX_MDB_VSEC_ID);
> > > > +     if (!vsec)
> > > > +             return;
> > > > +
> > > > +     pci_read_config_dword(pdev,
> > > > +                           vsec +
> DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_HIGH,
> > > > +                           &val);
> > > > +     off = val;
> > > > +     pci_read_config_dword(pdev,
> > > > +                           vsec +
> DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_LOW,
> > > > +                           &val);
> > > > +     off <<= 32;
> > > > +     off |= val;
> > > > +     pdata->devmem_phys_off = off; }
> > > > +
> > > >  static int dw_edma_pcie_probe(struct pci_dev *pdev,
> > > >                             const struct pci_device_id *pid)  { @@
> > > > -184,7 +325,28 @@ static int dw_edma_pcie_probe(struct pci_dev
> *pdev,
> > > >        * Tries to find if exists a PCIe Vendor-Specific Extended Capability
> > > >        * for the DMA, if one exists, then reconfigures it.
> > > >        */
> > > > -     dw_edma_pcie_get_vsec_dma_data(pdev, vsec_data);
> > > > +     dw_edma_pcie_get_synopsys_dma_data(pdev, vsec_data);
> > > > +     dw_edma_pcie_get_xilinx_dma_data(pdev, vsec_data);
> > > > +
> > > > +     if (pdev->vendor == PCI_VENDOR_ID_XILINX) {
> > >
> > > dw_edma_pcie_get_xilinx_dma_data() should be here.
> > >
> > > Frank
> >
> > Yes, this is good suggestion. Thanks!
> >
> > > > +             /*
> > > > +              * There is no valid address found for the LL memory
> > > > +              * space on the device side.
> > > > +              */
> > > > +             if (vsec_data->devmem_phys_off ==
> > > DW_PCIE_XILINX_MDB_INVALID_ADDR)
> > > > +                     return -ENOMEM;
> > > > +
> > > > +             /*
> > > > +              * Configure the channel LL and data blocks if number of
> > > > +              * channels enabled in VSEC capability are more than the
> > > > +              * channels configured in xilinx_mdb_data.
> > > > +              */
> > > > +             dw_edma_set_chan_region_offset(vsec_data, BAR_2, 0,
> > > > +                                            DW_PCIE_XILINX_MDB_LL_OFF_GAP,
> > > > +                                            DW_PCIE_XILINX_MDB_LL_SIZE,
> > > > +                                            DW_PCIE_XILINX_MDB_DT_OFF_GAP,
> > > > +                                            DW_PCIE_XILINX_MDB_DT_SIZE);
> > > > +     }
> > > >
> > > >       /* Mapping PCI BAR regions */
> > > >       mask = BIT(vsec_data->rg.bar); @@ -367,6 +529,8 @@ static
> > > > void dw_edma_pcie_remove(struct pci_dev
> > > > *pdev)
> > > >
> > > >  static const struct pci_device_id dw_edma_pcie_id_table[] = {
> > > >       { PCI_DEVICE_DATA(SYNOPSYS, EDDA, &snps_edda_data) },
> > > > +     { PCI_VDEVICE(XILINX, PCI_DEVICE_ID_XILINX_B054),
> > > > +       (kernel_ulong_t)&xilinx_mdb_data },
> > > >       { }
> > > >  };
> > > >  MODULE_DEVICE_TABLE(pci, dw_edma_pcie_id_table);
> > > > --
> > > > 1.8.3.1
> > > >

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

* Re: [PATCH v8 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint Support
  2026-01-20 11:36         ` Verma, Devendra
@ 2026-01-21 16:54           ` Frank Li
  2026-01-23 14:26             ` Verma, Devendra
  0 siblings, 1 reply; 24+ messages in thread
From: Frank Li @ 2026-01-21 16:54 UTC (permalink / raw)
  To: Verma, Devendra
  Cc: bhelgaas@google.com, mani@kernel.org, vkoul@kernel.org,
	dmaengine@vger.kernel.org, linux-pci@vger.kernel.org,
	linux-kernel@vger.kernel.org, Simek, Michal

On Tue, Jan 20, 2026 at 11:36:51AM +0000, Verma, Devendra wrote:
> [AMD Official Use Only - AMD Internal Distribution Only]
>
> > -----Original Message-----
> > From: Frank Li <Frank.li@nxp.com>
> > Sent: Monday, January 19, 2026 9:30 PM
> > To: Verma, Devendra <Devendra.Verma@amd.com>
> > Cc: bhelgaas@google.com; mani@kernel.org; vkoul@kernel.org;
> > dmaengine@vger.kernel.org; linux-pci@vger.kernel.org; linux-
> > kernel@vger.kernel.org; Simek, Michal <michal.simek@amd.com>
> > Subject: Re: [PATCH v8 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint
> > Support
> >
> > Caution: This message originated from an External Source. Use proper
> > caution when opening attachments, clicking links, or responding.
> >
> >
> > On Mon, Jan 19, 2026 at 09:09:11AM +0000, Verma, Devendra wrote:
> > > [AMD Official Use Only - AMD Internal Distribution Only]
> > >
> > > Hi Frank
> > >
> > > Please check my comments inline.
> > >
> > > Regards,
> > > Devendra
> > >
> > > > -----Original Message-----
> > > > From: Frank Li <Frank.li@nxp.com>
> > > > Sent: Thursday, January 15, 2026 9:51 PM
> > > > To: Verma, Devendra <Devendra.Verma@amd.com>
> > > > Cc: bhelgaas@google.com; mani@kernel.org; vkoul@kernel.org;
> > > > dmaengine@vger.kernel.org; linux-pci@vger.kernel.org; linux-
> > > > kernel@vger.kernel.org; Simek, Michal <michal.simek@amd.com>
> > > > Subject: Re: [PATCH v8 1/2] dmaengine: dw-edma: Add AMD MDB
> > Endpoint
> > > > Support
> > > >
> > > > Caution: This message originated from an External Source. Use proper
> > > > caution when opening attachments, clicking links, or responding.
> > > >
> > > >
> > > > On Fri, Jan 09, 2026 at 05:33:53PM +0530, Devendra K Verma wrote:
> > > > > AMD MDB PCIe endpoint support. For AMD specific support added the
> > > > > following
> > > > >   - AMD supported PCIe Device IDs and Vendor ID (Xilinx).
> > > > >   - AMD MDB specific driver data
> > > > >   - AMD MDB specific VSEC capability to retrieve the device DDR
> > > > >     base address.
> > > > >
> > > > > Signed-off-by: Devendra K Verma <devendra.verma@amd.com>
> > > > > ---
> > > > > Changes in v8:
> > > > > Changed the contant names to includer product vendor.
> > > > > Moved the vendor specific code to vendor specific functions.
> > > > >
> > > > > Changes in v7:
> > > > > Introduced vendor specific functions to retrieve the vsec data.
> > > > >
> > > > > Changes in v6:
> > > > > Included "sizes.h" header and used the appropriate definitions
> > > > > instead of constants.
> > > > >
> > > > > Changes in v5:
> > > > > Added the definitions for Xilinx specific VSEC header id,
> > > > > revision, and register offsets.
> > > > > Corrected the error type when no physical offset found for device
> > > > > side memory.
> > > > > Corrected the order of variables.
> > > > >
> > > > > Changes in v4:
> > > > > Configured 8 read and 8 write channels for Xilinx vendor Added
> > > > > checks to validate vendor ID for vendor specific vsec id.
> > > > > Added Xilinx specific vendor id for vsec specific to Xilinx Added
> > > > > the LL and data region offsets, size as input params to function
> > > > > dw_edma_set_chan_region_offset().
> > > > > Moved the LL and data region offsets assignment to function for
> > > > > Xilinx specific case.
> > > > > Corrected comments.
> > > > >
> > > > > Changes in v3:
> > > > > Corrected a typo when assigning AMD (Xilinx) vsec id macro and
> > > > > condition check.
> > > > >
> > > > > Changes in v2:
> > > > > Reverted the devmem_phys_off type to u64.
> > > > > Renamed the function appropriately to suit the functionality for
> > > > > setting the LL & data region offsets.
> > > > >
> > > > > Changes in v1:
> > > > > Removed the pci device id from pci_ids.h file.
> > > > > Added the vendor id macro as per the suggested method.
> > > > > Changed the type of the newly added devmem_phys_off variable.
> > > > > Added to logic to assign offsets for LL and data region blocks in
> > > > > case more number of channels are enabled than given in
> > amd_mdb_data struct.
> > > > > ---
> > > > >  drivers/dma/dw-edma/dw-edma-pcie.c | 192
> > > > > ++++++++++++++++++++++++++++++++++---
> > > > >  1 file changed, 178 insertions(+), 14 deletions(-)
> > > > >
> > > > > diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c
> > > > > b/drivers/dma/dw-edma/dw-edma-pcie.c
> > > > > index 3371e0a7..2efd149 100644
> > > > > --- a/drivers/dma/dw-edma/dw-edma-pcie.c
> > > > > +++ b/drivers/dma/dw-edma/dw-edma-pcie.c
> > > > > @@ -14,14 +14,35 @@
> > > > >  #include <linux/pci-epf.h>
> > > > >  #include <linux/msi.h>
> > > > >  #include <linux/bitfield.h>
> > > > > +#include <linux/sizes.h>
> > > > >
> > > > >  #include "dw-edma-core.h"
> > > > >
> > > > > -#define DW_PCIE_VSEC_DMA_ID                  0x6
> > > > > -#define DW_PCIE_VSEC_DMA_BAR                 GENMASK(10, 8)
> > > > > -#define DW_PCIE_VSEC_DMA_MAP                 GENMASK(2, 0)
> > > > > -#define DW_PCIE_VSEC_DMA_WR_CH                       GENMASK(9, 0)
> > > > > -#define DW_PCIE_VSEC_DMA_RD_CH                       GENMASK(25, 16)
> > > > > +/* Synopsys */
> > > > > +#define DW_PCIE_SYNOPSYS_VSEC_DMA_ID         0x6
> > > > > +#define DW_PCIE_SYNOPSYS_VSEC_DMA_BAR                GENMASK(10,
> > 8)
> > > > > +#define DW_PCIE_SYNOPSYS_VSEC_DMA_MAP                GENMASK(2, 0)
> > > > > +#define DW_PCIE_SYNOPSYS_VSEC_DMA_WR_CH              GENMASK(9,
> > 0)
> > > > > +#define DW_PCIE_SYNOPSYS_VSEC_DMA_RD_CH              GENMASK(25,
> > 16)
> > > >
> > > > Sorry, jump into at v8.
> > > > According to my understand 'DW' means 'Synopsys'.
> > > >
> > >
> > > Yes, DW means Designware representing Synopsys here.
> > > For the sake of clarity, a distinction was required to separate the
> > > names of macros having the similar purpose for other IP, Xilinx in
> > > this case. Otherwise, it is causing confusion which macros to use for
> > > which vendor. This also helps in future if any of the vendors try to
> > > retrieve a new or different VSEC IDs then all they need is to define macros
> > which clearly show the association with the vendor, thus eliminating the
> > confusion.
> >
> > If want to reuse the driver, driver owner take reponsiblity to find the
> > difference.
> >
> > If define a whole set of register, the reader is hard to find real difference.
> >
>
> It is not regarding the register set rather VSEC capability which can differ
> in the purpose even for the similar IPs. As is the current case where one
> VSEC ID serves the similar purpose for both the IPs while the VSEC ID = 0x20
> differs in meaning for Synopsys and Xilinx thus I think it is OK to define new
> macros as long as they do not create confusion.

But need put rename existing macro to new patches. And I think
use XILINX_PCIE_MDB_* should be enough without renaming. Everyone know
DW is SYNOPSYS.

>
> > >
> > > > > +
> > > > > +/* AMD MDB (Xilinx) specific defines */
> > > > > +#define PCI_DEVICE_ID_XILINX_B054            0xb054
> > > > > +
> > > > > +#define DW_PCIE_XILINX_MDB_VSEC_DMA_ID               0x6
> > > > > +#define DW_PCIE_XILINX_MDB_VSEC_ID           0x20
> > > > > +#define DW_PCIE_XILINX_MDB_VSEC_DMA_BAR              GENMASK(10,
> > 8)
> > > > > +#define DW_PCIE_XILINX_MDB_VSEC_DMA_MAP              GENMASK(2,
> > 0)
> > > > > +#define DW_PCIE_XILINX_MDB_VSEC_DMA_WR_CH    GENMASK(9, 0)
> > > > > +#define DW_PCIE_XILINX_MDB_VSEC_DMA_RD_CH    GENMASK(25,
> > 16)
> > > >
> > > > These defination is the same. Need redefine again
> > > >
> > >
...
> > >
> > > As explained above, the name change is required to avoid confusion.
> > > The trigger to have the separate names for each IP is the inclusion of
> > > Xilinx IP that is why no separate patch is created.
> >
> > Separate patch renmae macro only. Reviewer can simple bypass this typo
> > trivial patch.
> >
> > Then add new one.
> >
> > Actually, Needn't rename at all.  You can directly use XLINNK_PCIE_*
> >
> > Frank
>
> Please check the discussion on previous versions of the same patch series.

It'd better you put link here to support your purposal.

> We have this patch as the outcome of those discussions.
> Other reviewing members felt it that keeping the name similar for the
> VSEC ID having similar purpose for two different IPs was causing the confusion
> that is why it was agreed upon the separate out the naming as per the
> vendor-name of VSEC ID. Regarding the separate patch, the reason is introduction
> of the new IP which mostly supports the similar functionality except in case of VSEC IDs
> that's why the name separation became part of these patches. It sets the context for
> changing the name of the existing macros.

If put trivial big part to new patch to reduce, it will reduce review
efforts.

Frank

>
> > >
> > > > >
> > > > >       pci_read_config_dword(pdev, vsec + 0x14, &val);
> > > > >       off = val;
> > > > > @@ -157,6 +237,67 @@ static void
> > > > dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
> > > > >       pdata->rg.off = off;
> > > > >  }
> > > > >
> > > > > +static void dw_edma_pcie_get_xilinx_dma_data(struct pci_dev *pdev,
> > > > > +                                          struct
> > > > > +dw_edma_pcie_data
> > > > > +*pdata) {
> > > > > +     u32 val, map;
> > > > > +     u16 vsec;
> > > > > +     u64 off;
> > > > > +
> > > > > +     pdata->devmem_phys_off =
> > DW_PCIE_XILINX_MDB_INVALID_ADDR;
> > > > > +
> > > > > +     vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_XILINX,
> > > > > +                                     DW_PCIE_XILINX_MDB_VSEC_DMA_ID);
> > > > > +     if (!vsec)
> > > > > +             return;
> > > > > +
> > > > > +     pci_read_config_dword(pdev, vsec + PCI_VNDR_HEADER, &val);
> > > > > +     if (PCI_VNDR_HEADER_REV(val) != 0x00 ||
> > > > > +         PCI_VNDR_HEADER_LEN(val) != 0x18)
> > > > > +             return;
> > > > > +
> > > > > +     pci_dbg(pdev, "Detected Xilinx PCIe Vendor-Specific Extended
> > > > > + Capability
> > > > DMA\n");
> > > > > +     pci_read_config_dword(pdev, vsec + 0x8, &val);
> > > > > +     map = FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_MAP, val);
> > > > > +     if (map != EDMA_MF_EDMA_LEGACY &&
> > > > > +         map != EDMA_MF_EDMA_UNROLL &&
> > > > > +         map != EDMA_MF_HDMA_COMPAT &&
> > > > > +         map != EDMA_MF_HDMA_NATIVE)
> > > > > +             return;
> > > > > +
> > > > > +     pdata->mf = map;
> > > > > +     pdata->rg.bar =
> > FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_BAR,
> > > > val);
> > > > > +
> > > > > +     pci_read_config_dword(pdev, vsec + 0xc, &val);
> > > > > +     pdata->wr_ch_cnt = min_t(u16, pdata->wr_ch_cnt,
> > > > > +
> > > > > + FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_WR_CH,
> > > > val));
> > > > > +     pdata->rd_ch_cnt = min_t(u16, pdata->rd_ch_cnt,
> > > > > +
> > > > > + FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_RD_CH, val));
> > > > > +
> > > > > +     pci_read_config_dword(pdev, vsec + 0x14, &val);
> > > > > +     off = val;
> > > > > +     pci_read_config_dword(pdev, vsec + 0x10, &val);
> > > > > +     off <<= 32;
> > > > > +     off |= val;
> > > > > +     pdata->rg.off = off;
> > > > > +
> > > > > +     vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_XILINX,
> > > > > +                                     DW_PCIE_XILINX_MDB_VSEC_ID);
> > > > > +     if (!vsec)
> > > > > +             return;
> > > > > +
> > > > > +     pci_read_config_dword(pdev,
> > > > > +                           vsec +
> > DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_HIGH,
> > > > > +                           &val);
> > > > > +     off = val;
> > > > > +     pci_read_config_dword(pdev,
> > > > > +                           vsec +
> > DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_LOW,
> > > > > +                           &val);
> > > > > +     off <<= 32;
> > > > > +     off |= val;
> > > > > +     pdata->devmem_phys_off = off; }
> > > > > +
> > > > >  static int dw_edma_pcie_probe(struct pci_dev *pdev,
> > > > >                             const struct pci_device_id *pid)  { @@
> > > > > -184,7 +325,28 @@ static int dw_edma_pcie_probe(struct pci_dev
> > *pdev,
> > > > >        * Tries to find if exists a PCIe Vendor-Specific Extended Capability
> > > > >        * for the DMA, if one exists, then reconfigures it.
> > > > >        */
> > > > > -     dw_edma_pcie_get_vsec_dma_data(pdev, vsec_data);
> > > > > +     dw_edma_pcie_get_synopsys_dma_data(pdev, vsec_data);
> > > > > +     dw_edma_pcie_get_xilinx_dma_data(pdev, vsec_data);
> > > > > +
> > > > > +     if (pdev->vendor == PCI_VENDOR_ID_XILINX) {
> > > >
> > > > dw_edma_pcie_get_xilinx_dma_data() should be here.
> > > >
> > > > Frank
> > >
> > > Yes, this is good suggestion. Thanks!
> > >
> > > > > +             /*
> > > > > +              * There is no valid address found for the LL memory
> > > > > +              * space on the device side.
> > > > > +              */
> > > > > +             if (vsec_data->devmem_phys_off ==
> > > > DW_PCIE_XILINX_MDB_INVALID_ADDR)
> > > > > +                     return -ENOMEM;
> > > > > +
> > > > > +             /*
> > > > > +              * Configure the channel LL and data blocks if number of
> > > > > +              * channels enabled in VSEC capability are more than the
> > > > > +              * channels configured in xilinx_mdb_data.
> > > > > +              */
> > > > > +             dw_edma_set_chan_region_offset(vsec_data, BAR_2, 0,
> > > > > +                                            DW_PCIE_XILINX_MDB_LL_OFF_GAP,
> > > > > +                                            DW_PCIE_XILINX_MDB_LL_SIZE,
> > > > > +                                            DW_PCIE_XILINX_MDB_DT_OFF_GAP,
> > > > > +                                            DW_PCIE_XILINX_MDB_DT_SIZE);
> > > > > +     }
> > > > >
> > > > >       /* Mapping PCI BAR regions */
> > > > >       mask = BIT(vsec_data->rg.bar); @@ -367,6 +529,8 @@ static
> > > > > void dw_edma_pcie_remove(struct pci_dev
> > > > > *pdev)
> > > > >
> > > > >  static const struct pci_device_id dw_edma_pcie_id_table[] = {
> > > > >       { PCI_DEVICE_DATA(SYNOPSYS, EDDA, &snps_edda_data) },
> > > > > +     { PCI_VDEVICE(XILINX, PCI_DEVICE_ID_XILINX_B054),
> > > > > +       (kernel_ulong_t)&xilinx_mdb_data },
> > > > >       { }
> > > > >  };
> > > > >  MODULE_DEVICE_TABLE(pci, dw_edma_pcie_id_table);
> > > > > --
> > > > > 1.8.3.1
> > > > >

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

* Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
  2026-01-20 11:36         ` Verma, Devendra
@ 2026-01-21 17:18           ` Frank Li
  2026-01-23 14:26             ` Verma, Devendra
  0 siblings, 1 reply; 24+ messages in thread
From: Frank Li @ 2026-01-21 17:18 UTC (permalink / raw)
  To: Verma, Devendra
  Cc: bhelgaas@google.com, mani@kernel.org, vkoul@kernel.org,
	dmaengine@vger.kernel.org, linux-pci@vger.kernel.org,
	linux-kernel@vger.kernel.org, Simek, Michal

On Tue, Jan 20, 2026 at 11:36:10AM +0000, Verma, Devendra wrote:
> [AMD Official Use Only - AMD Internal Distribution Only]
>
> > -----Original Message-----
> > From: Frank Li <Frank.li@nxp.com>
> > Sent: Monday, January 19, 2026 9:15 PM
> > To: Verma, Devendra <Devendra.Verma@amd.com>
> > Cc: bhelgaas@google.com; mani@kernel.org; vkoul@kernel.org;
> > dmaengine@vger.kernel.org; linux-pci@vger.kernel.org; linux-
> > kernel@vger.kernel.org; Simek, Michal <michal.simek@amd.com>
> > Subject: Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
> >
> > Caution: This message originated from an External Source. Use proper caution
> > when opening attachments, clicking links, or responding.
> >
> >
> > On Mon, Jan 19, 2026 at 09:09:17AM +0000, Verma, Devendra wrote:
> > > [AMD Official Use Only - AMD Internal Distribution Only]
> > >
> > > Hi Frank
> > >
> > > Please check my response inline.
> > >
> > > Regards,
> > > Devendra
> > > > -----Original Message-----
> > > > From: Frank Li <Frank.li@nxp.com>
> > > > Sent: Thursday, January 15, 2026 10:07 PM
> > > > To: Verma, Devendra <Devendra.Verma@amd.com>
> > > > Cc: bhelgaas@google.com; mani@kernel.org; vkoul@kernel.org;
> > > > dmaengine@vger.kernel.org; linux-pci@vger.kernel.org; linux-
> > > > kernel@vger.kernel.org; Simek, Michal <michal.simek@amd.com>
> > > > Subject: Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
> > > >
> > > > Caution: This message originated from an External Source. Use proper
> > > > caution when opening attachments, clicking links, or responding.
> > > >
> > > >
> > > > On Fri, Jan 09, 2026 at 05:33:54PM +0530, Devendra K Verma wrote:
> > > > > AMD MDB IP supports Linked List (LL) mode as well as non-LL mode.
> > > > > The current code does not have the mechanisms to enable the DMA
> > > > > transactions using the non-LL mode. The following two cases are
> > > > > added with this patch:
> > > > > - For the AMD (Xilinx) only, when a valid physical base address of
> > > > >   the device side DDR is not configured, then the IP can still be
> > > > >   used in non-LL mode. For all the channels DMA transactions will
> > > >
> > > > If DDR have not configured, where DATA send to in device side by
> > > > non-LL mode.
> > > >
> > >
> > > The DDR base address in the VSEC capability is used for driving the
> > > DMA transfers when used in the LL mode. The DDR is configured and
> > > present all the time but the DMA PCIe driver uses this DDR base
> > > address (physical address) to configure the LLP address.
> > >
> > > In the scenario, where this DDR base address in VSEC capability is not
> > > configured then the current controller cannot be used as the default
> > > mode supported is LL mode only. In order to make the controller usable
> > > non-LL mode is being added which just needs SAR, DAR, XFERLEN and
> > > control register to initiate the transfer. So, the DDR is always
> > > present, but the DMA PCIe driver need to know the DDR base physical
> > > address to make the transfer. This is useful in scenarios where the memory
> > allocated for LL can be used for DMA transactions as well.
> >
> > Do you means use DMA transfer LL's context?
> >
>
> Yes, the device side memory reserved for the link list to store the descriptors,
> accessed by the host via BAR_2 in this driver code.
>
> > >
> > > > >   be using the non-LL mode only. This, the default non-LL mode,
> > > > >   is not applicable for Synopsys IP with the current code addition.
> > > > >
> > > > > - If the default mode is LL-mode, for both AMD (Xilinx) and Synosys,
> > > > >   and if user wants to use non-LL mode then user can do so via
> > > > >   configuring the peripheral_config param of dma_slave_config.
> > > > >
> > > > > Signed-off-by: Devendra K Verma <devendra.verma@amd.com>
> > > > > ---
> > > > > Changes in v8
> > > > >   Cosmetic change related to comment and code.
> > > > >
> > > > > Changes in v7
> > > > >   No change
> > > > >
> > > > > Changes in v6
> > > > >   Gave definition to bits used for channel configuration.
> > > > >   Removed the comment related to doorbell.
> > > > >
> > > > > Changes in v5
> > > > >   Variable name 'nollp' changed to 'non_ll'.
> > > > >   In the dw_edma_device_config() WARN_ON replaced with dev_err().
> > > > >   Comments follow the 80-column guideline.
> > > > >
> > > > > Changes in v4
> > > > >   No change
> > > > >
> > > > > Changes in v3
> > > > >   No change
> > > > >
> > > > > Changes in v2
> > > > >   Reverted the function return type to u64 for
> > > > >   dw_edma_get_phys_addr().
> > > > >
> > > > > Changes in v1
> > > > >   Changed the function return type for dw_edma_get_phys_addr().
> > > > >   Corrected the typo raised in review.
> > > > > ---
> > > > >  drivers/dma/dw-edma/dw-edma-core.c    | 42
> > +++++++++++++++++++++---
> > > > >  drivers/dma/dw-edma/dw-edma-core.h    |  1 +
> > > > >  drivers/dma/dw-edma/dw-edma-pcie.c    | 46 ++++++++++++++++++--
> > ------
> > > > >  drivers/dma/dw-edma/dw-hdma-v0-core.c | 61
> > > > > ++++++++++++++++++++++++++++++++++-
> > > > >  drivers/dma/dw-edma/dw-hdma-v0-regs.h |  1 +
> > > >
> > > > edma-v0-core.c have not update, if don't support, at least need
> > > > return failure at dw_edma_device_config() when backend is eDMA.
> > > >
> > > > >  include/linux/dma/edma.h              |  1 +
> > > > >  6 files changed, 132 insertions(+), 20 deletions(-)
> > > > >
> > > > > diff --git a/drivers/dma/dw-edma/dw-edma-core.c
> > > > > b/drivers/dma/dw-edma/dw-edma-core.c
> > > > > index b43255f..d37112b 100644
> > > > > --- a/drivers/dma/dw-edma/dw-edma-core.c
> > > > > +++ b/drivers/dma/dw-edma/dw-edma-core.c
> > > > > @@ -223,8 +223,32 @@ static int dw_edma_device_config(struct
> > > > dma_chan *dchan,
> > > > >                                struct dma_slave_config *config)  {
> > > > >       struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
> > > > > +     int non_ll = 0;
> > > > > +
> > > > > +     if (config->peripheral_config &&
> > > > > +         config->peripheral_size != sizeof(int)) {
> > > > > +             dev_err(dchan->device->dev,
> > > > > +                     "config param peripheral size mismatch\n");
> > > > > +             return -EINVAL;
> > > > > +     }
> > > > >
> > > > >       memcpy(&chan->config, config, sizeof(*config));
> > > > > +
> > > > > +     /*
> > > > > +      * When there is no valid LLP base address available then the default
> > > > > +      * DMA ops will use the non-LL mode.
> > > > > +      *
> > > > > +      * Cases where LL mode is enabled and client wants to use the non-LL
> > > > > +      * mode then also client can do so via providing the peripheral_config
> > > > > +      * param.
> > > > > +      */
> > > > > +     if (config->peripheral_config)
> > > > > +             non_ll = *(int *)config->peripheral_config;
> > > > > +
> > > > > +     chan->non_ll = false;
> > > > > +     if (chan->dw->chip->non_ll || (!chan->dw->chip->non_ll && non_ll))
> > > > > +             chan->non_ll = true;
> > > > > +
> > > > >       chan->configured = true;
> > > > >
> > > > >       return 0;
> > > > > @@ -353,7 +377,7 @@ static void
> > > > > dw_edma_device_issue_pending(struct
> > > > dma_chan *dchan)
> > > > >       struct dw_edma_chan *chan = dchan2dw_edma_chan(xfer->dchan);
> > > > >       enum dma_transfer_direction dir = xfer->direction;
> > > > >       struct scatterlist *sg = NULL;
> > > > > -     struct dw_edma_chunk *chunk;
> > > > > +     struct dw_edma_chunk *chunk = NULL;
> > > > >       struct dw_edma_burst *burst;
> > > > >       struct dw_edma_desc *desc;
> > > > >       u64 src_addr, dst_addr;
> > > > > @@ -419,9 +443,11 @@ static void
> > > > > dw_edma_device_issue_pending(struct
> > > > dma_chan *dchan)
> > > > >       if (unlikely(!desc))
> > > > >               goto err_alloc;
> > > > >
> > > > > -     chunk = dw_edma_alloc_chunk(desc);
> > > > > -     if (unlikely(!chunk))
> > > > > -             goto err_alloc;
> > > > > +     if (!chan->non_ll) {
> > > > > +             chunk = dw_edma_alloc_chunk(desc);
> > > > > +             if (unlikely(!chunk))
> > > > > +                     goto err_alloc;
> > > > > +     }
> > > >
> > > > non_ll is the same as ll_max = 1. (or 2, there are link back entry).
> > > >
> > > > If you set ll_max = 1, needn't change this code.
> > > >
> > >
> > > The ll_max is defined for the session till the driver is loaded in the kernel.
> > > This code also enables the non-LL mode dynamically upon input from the
> > > DMA client. In this scenario, touching ll_max would not be a good idea
> > > as the ll_max controls the LL entries for all the DMA channels not
> > > just for a single DMA transaction.
> >
> > You can use new variable, such as ll_avail.
> >
>
> In order to separate out the execution paths a new meaningful variable "non_ll"
> is used. The variable "non_ll" alone is sufficient. Using another variable
> along side "non_ll" for the similar purpose may not have any added advantage.

ll_avail can help debug/fine tune how much impact preformance by adjust
ll length. And it make code logic clean and consistent. also ll_avail can
help test corner case when ll item small. Normall case it is hard to reach
ll_max.

>
> > >
> > > > >
...
> > > > > +
> > > > > + ll_block->bar);
> > > >
> > > > This change need do prepare patch, which only change
> > > > pci_bus_address() to dw_edma_get_phys_addr().
> > > >
> > >
> > > This is not clear.
> >
> > why not. only trivial add helper patch, which help reviewer
> >
>
> I was not clear on the question you asked.
> It does not look justified when a patch is raised alone just to replace this function.
> The function change is required cause the same code *can* support different IPs from
> different vendors. And, with this single change alone in the code the support for another
> IP is added. That's why it is easier to get the reason for the change in
> the function name and syntax.

Add replace pci_bus_address() with dw_edma_get_phys_addr() to make review
easily and get ack for such replacement patches.

two patches
patch1, just replace exist pci_bus_address() with dw_edma_get_phys_addr()
patch2, add new logic in dw_edma_get_phys_addr() to support new vendor.

>
> > >
> > > > >               ll_region->paddr += ll_block->off;
> > > > >               ll_region->sz = ll_block->sz;
> > > > >
...
> > > > >
> > > > > +static void dw_hdma_v0_core_non_ll_start(struct dw_edma_chunk
> > > > *chunk)
> > > > > +{
> > > > > +     struct dw_edma_chan *chan = chunk->chan;
> > > > > +     struct dw_edma *dw = chan->dw;
> > > > > +     struct dw_edma_burst *child;
> > > > > +     u32 val;
> > > > > +
> > > > > +     list_for_each_entry(child, &chunk->burst->list, list) {
> > > >
> > > > why need iterated list, it doesn't support ll. Need wait for irq to start next
> > one.
> > > >
> > > > Frank
> > >
> > > Yes, this is true. The format is kept similar to LL mode.
> >
> > Just fill one. list_for_each_entry() cause confuse.
> >
> > Frank
>
> I see, we can use list_first_entry_or_null() which is dependent on giving
> the type of pointer, compared to this list_for_each_entry() looks neat and
> agnostic to the pointer type being used. Though, it can be explored further.
> Also, when the chunk is allocated, the comment clearly spells out how
> the allocation would be for the non LL mode so it is evident that each
> chunk would have single entry and with that understanding it is clear that
> loop will also be used in a similar manner, to retrieve a single entry. It is a
> similar use case as of "do {}while (0)" albeit needs a context to understand it.

I don't think so. list_for_each_entry() is miss leading to reader think it
is not only to one item in burst list, and use polling method to to finish
many burst transfer.

list_for_each_entry() {
	...
	readl_timeout()
}

Generally, EDMA is very quick, polling is much quicker than irq if data
is small.

Frank

>
> > >
> > > >
> > > > > +             SET_CH_32(dw, chan->dir, chan->id, ch_en,
> > > > > + HDMA_V0_CH_EN);
> > > > > +
> > > > > +             /* Source address */
> > > > > +             SET_CH_32(dw, chan->dir, chan->id, sar.lsb,
> > > > > +                       lower_32_bits(child->sar));
> > > > > +             SET_CH_32(dw, chan->dir, chan->id, sar.msb,
> > > > > +                       upper_32_bits(child->sar));
> > > > > +
> > > > > +             /* Destination address */
> > > > > +             SET_CH_32(dw, chan->dir, chan->id, dar.lsb,
> > > > > +                       lower_32_bits(child->dar));
> > > > > +             SET_CH_32(dw, chan->dir, chan->id, dar.msb,
> > > > > +                       upper_32_bits(child->dar));
> > > > > +
> > > > > +             /* Transfer size */
> > > > > +             SET_CH_32(dw, chan->dir, chan->id, transfer_size,
> > > > > + child->sz);
> > > > > +
> > > > > +             /* Interrupt setup */
> > > > > +             val = GET_CH_32(dw, chan->dir, chan->id, int_setup) |
> > > > > +                             HDMA_V0_STOP_INT_MASK |
> > > > > +                             HDMA_V0_ABORT_INT_MASK |
> > > > > +                             HDMA_V0_LOCAL_STOP_INT_EN |
> > > > > +                             HDMA_V0_LOCAL_ABORT_INT_EN;
> > > > > +
> > > > > +             if (!(dw->chip->flags & DW_EDMA_CHIP_LOCAL)) {
> > > > > +                     val |= HDMA_V0_REMOTE_STOP_INT_EN |
> > > > > +                            HDMA_V0_REMOTE_ABORT_INT_EN;
> > > > > +             }
> > > > > +
> > > > > +             SET_CH_32(dw, chan->dir, chan->id, int_setup, val);
> > > > > +
> > > > > +             /* Channel control setup */
> > > > > +             val = GET_CH_32(dw, chan->dir, chan->id, control1);
> > > > > +             val &= ~HDMA_V0_LINKLIST_EN;
> > > > > +             SET_CH_32(dw, chan->dir, chan->id, control1, val);
> > > > > +
> > > > > +             SET_CH_32(dw, chan->dir, chan->id, doorbell,
> > > > > +                       HDMA_V0_DOORBELL_START);
> > > > > +     }
> > > > > +}
> > > > > +
> > > > > +static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk,
> > > > > +bool
> > > > > +first) {
> > > > > +     struct dw_edma_chan *chan = chunk->chan;
> > > > > +
> > > > > +     if (chan->non_ll)
> > > > > +             dw_hdma_v0_core_non_ll_start(chunk);
> > > > > +     else
> > > > > +             dw_hdma_v0_core_ll_start(chunk, first); }
> > > > > +
> > > > >  static void dw_hdma_v0_core_ch_config(struct dw_edma_chan *chan)
> > {
> > > > >       struct dw_edma *dw = chan->dw; diff --git
> > > > > a/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > index eab5fd7..7759ba9 100644
> > > > > --- a/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > +++ b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > @@ -12,6 +12,7 @@
> > > > >  #include <linux/dmaengine.h>
> > > > >
> > > > >  #define HDMA_V0_MAX_NR_CH                    8
> > > > > +#define HDMA_V0_CH_EN                                BIT(0)
> > > > >  #define HDMA_V0_LOCAL_ABORT_INT_EN           BIT(6)
> > > > >  #define HDMA_V0_REMOTE_ABORT_INT_EN          BIT(5)
> > > > >  #define HDMA_V0_LOCAL_STOP_INT_EN            BIT(4)
> > > > > diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h
> > > > > index 3080747..78ce31b 100644
> > > > > --- a/include/linux/dma/edma.h
> > > > > +++ b/include/linux/dma/edma.h
> > > > > @@ -99,6 +99,7 @@ struct dw_edma_chip {
> > > > >       enum dw_edma_map_format mf;
> > > > >
> > > > >       struct dw_edma          *dw;
> > > > > +     bool                    non_ll;
> > > > >  };
> > > > >
> > > > >  /* Export to the platform drivers */
> > > > > --
> > > > > 1.8.3.1
> > > > >

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

* RE: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
  2026-01-21 17:18           ` Frank Li
@ 2026-01-23 14:26             ` Verma, Devendra
  2026-01-26 19:00               ` Frank Li
  0 siblings, 1 reply; 24+ messages in thread
From: Verma, Devendra @ 2026-01-23 14:26 UTC (permalink / raw)
  To: Frank Li
  Cc: bhelgaas@google.com, mani@kernel.org, vkoul@kernel.org,
	dmaengine@vger.kernel.org, linux-pci@vger.kernel.org,
	linux-kernel@vger.kernel.org, Simek, Michal, Verma, Devendra

[AMD Official Use Only - AMD Internal Distribution Only]

> -----Original Message-----
> From: Frank Li <Frank.li@nxp.com>
> Sent: Wednesday, January 21, 2026 10:49 PM
> To: Verma, Devendra <Devendra.Verma@amd.com>
> Cc: bhelgaas@google.com; mani@kernel.org; vkoul@kernel.org;
> dmaengine@vger.kernel.org; linux-pci@vger.kernel.org; linux-
> kernel@vger.kernel.org; Simek, Michal <michal.simek@amd.com>
> Subject: Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
>
> Caution: This message originated from an External Source. Use proper
> caution when opening attachments, clicking links, or responding.

--[ Snipped some headers to reduce the size of this mail ]--

> > > > >
> > > > > On Fri, Jan 09, 2026 at 05:33:54PM +0530, Devendra K Verma wrote:
> > > > > > AMD MDB IP supports Linked List (LL) mode as well as non-LL mode.
> > > > > > The current code does not have the mechanisms to enable the
> > > > > > DMA transactions using the non-LL mode. The following two
> > > > > > cases are added with this patch:
> > > > > > - For the AMD (Xilinx) only, when a valid physical base address of
> > > > > >   the device side DDR is not configured, then the IP can still be
> > > > > >   used in non-LL mode. For all the channels DMA transactions
> > > > > > will
> > > > >
> > > > > If DDR have not configured, where DATA send to in device side by
> > > > > non-LL mode.
> > > > >
> > > >
> > > > The DDR base address in the VSEC capability is used for driving
> > > > the DMA transfers when used in the LL mode. The DDR is configured
> > > > and present all the time but the DMA PCIe driver uses this DDR
> > > > base address (physical address) to configure the LLP address.
> > > >
> > > > In the scenario, where this DDR base address in VSEC capability is
> > > > not configured then the current controller cannot be used as the
> > > > default mode supported is LL mode only. In order to make the
> > > > controller usable non-LL mode is being added which just needs SAR,
> > > > DAR, XFERLEN and control register to initiate the transfer. So,
> > > > the DDR is always present, but the DMA PCIe driver need to know
> > > > the DDR base physical address to make the transfer. This is useful
> > > > in scenarios where the memory
> > > allocated for LL can be used for DMA transactions as well.
> > >
> > > Do you means use DMA transfer LL's context?
> > >
> >
> > Yes, the device side memory reserved for the link list to store the
> > descriptors, accessed by the host via BAR_2 in this driver code.
> >
> > > >
> > > > > >   be using the non-LL mode only. This, the default non-LL mode,
> > > > > >   is not applicable for Synopsys IP with the current code addition.
> > > > > >
> > > > > > - If the default mode is LL-mode, for both AMD (Xilinx) and Synosys,
> > > > > >   and if user wants to use non-LL mode then user can do so via
> > > > > >   configuring the peripheral_config param of dma_slave_config.
> > > > > >
> > > > > > Signed-off-by: Devendra K Verma <devendra.verma@amd.com>
> > > > > > ---
> > > > > > Changes in v8
> > > > > >   Cosmetic change related to comment and code.
> > > > > >
> > > > > > Changes in v7
> > > > > >   No change
> > > > > >
> > > > > > Changes in v6
> > > > > >   Gave definition to bits used for channel configuration.
> > > > > >   Removed the comment related to doorbell.
> > > > > >
> > > > > > Changes in v5
> > > > > >   Variable name 'nollp' changed to 'non_ll'.
> > > > > >   In the dw_edma_device_config() WARN_ON replaced with
> dev_err().
> > > > > >   Comments follow the 80-column guideline.
> > > > > >
> > > > > > Changes in v4
> > > > > >   No change
> > > > > >
> > > > > > Changes in v3
> > > > > >   No change
> > > > > >
> > > > > > Changes in v2
> > > > > >   Reverted the function return type to u64 for
> > > > > >   dw_edma_get_phys_addr().
> > > > > >
> > > > > > Changes in v1
> > > > > >   Changed the function return type for dw_edma_get_phys_addr().
> > > > > >   Corrected the typo raised in review.
> > > > > > ---
> > > > > >  drivers/dma/dw-edma/dw-edma-core.c    | 42
> > > +++++++++++++++++++++---
> > > > > >  drivers/dma/dw-edma/dw-edma-core.h    |  1 +
> > > > > >  drivers/dma/dw-edma/dw-edma-pcie.c    | 46
> ++++++++++++++++++--
> > > ------
> > > > > >  drivers/dma/dw-edma/dw-hdma-v0-core.c | 61
> > > > > > ++++++++++++++++++++++++++++++++++-
> > > > > >  drivers/dma/dw-edma/dw-hdma-v0-regs.h |  1 +
> > > > >
> > > > > edma-v0-core.c have not update, if don't support, at least need
> > > > > return failure at dw_edma_device_config() when backend is eDMA.
> > > > >
> > > > > >  include/linux/dma/edma.h              |  1 +
> > > > > >  6 files changed, 132 insertions(+), 20 deletions(-)
> > > > > >
> > > > > > diff --git a/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > b/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > index b43255f..d37112b 100644
> > > > > > --- a/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > +++ b/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > @@ -223,8 +223,32 @@ static int dw_edma_device_config(struct
> > > > > dma_chan *dchan,
> > > > > >                                struct dma_slave_config *config)  {
> > > > > >       struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
> > > > > > +     int non_ll = 0;
> > > > > > +
> > > > > > +     if (config->peripheral_config &&
> > > > > > +         config->peripheral_size != sizeof(int)) {
> > > > > > +             dev_err(dchan->device->dev,
> > > > > > +                     "config param peripheral size mismatch\n");
> > > > > > +             return -EINVAL;
> > > > > > +     }
> > > > > >
> > > > > >       memcpy(&chan->config, config, sizeof(*config));
> > > > > > +
> > > > > > +     /*
> > > > > > +      * When there is no valid LLP base address available then the
> default
> > > > > > +      * DMA ops will use the non-LL mode.
> > > > > > +      *
> > > > > > +      * Cases where LL mode is enabled and client wants to use the
> non-LL
> > > > > > +      * mode then also client can do so via providing the
> peripheral_config
> > > > > > +      * param.
> > > > > > +      */
> > > > > > +     if (config->peripheral_config)
> > > > > > +             non_ll = *(int *)config->peripheral_config;
> > > > > > +
> > > > > > +     chan->non_ll = false;
> > > > > > +     if (chan->dw->chip->non_ll || (!chan->dw->chip->non_ll &&
> non_ll))
> > > > > > +             chan->non_ll = true;
> > > > > > +
> > > > > >       chan->configured = true;
> > > > > >
> > > > > >       return 0;
> > > > > > @@ -353,7 +377,7 @@ static void
> > > > > > dw_edma_device_issue_pending(struct
> > > > > dma_chan *dchan)
> > > > > >       struct dw_edma_chan *chan = dchan2dw_edma_chan(xfer-
> >dchan);
> > > > > >       enum dma_transfer_direction dir = xfer->direction;
> > > > > >       struct scatterlist *sg = NULL;
> > > > > > -     struct dw_edma_chunk *chunk;
> > > > > > +     struct dw_edma_chunk *chunk = NULL;
> > > > > >       struct dw_edma_burst *burst;
> > > > > >       struct dw_edma_desc *desc;
> > > > > >       u64 src_addr, dst_addr;
> > > > > > @@ -419,9 +443,11 @@ static void
> > > > > > dw_edma_device_issue_pending(struct
> > > > > dma_chan *dchan)
> > > > > >       if (unlikely(!desc))
> > > > > >               goto err_alloc;
> > > > > >
> > > > > > -     chunk = dw_edma_alloc_chunk(desc);
> > > > > > -     if (unlikely(!chunk))
> > > > > > -             goto err_alloc;
> > > > > > +     if (!chan->non_ll) {
> > > > > > +             chunk = dw_edma_alloc_chunk(desc);
> > > > > > +             if (unlikely(!chunk))
> > > > > > +                     goto err_alloc;
> > > > > > +     }
> > > > >
> > > > > non_ll is the same as ll_max = 1. (or 2, there are link back entry).
> > > > >
> > > > > If you set ll_max = 1, needn't change this code.
> > > > >
> > > >
> > > > The ll_max is defined for the session till the driver is loaded in the
> kernel.
> > > > This code also enables the non-LL mode dynamically upon input from
> > > > the DMA client. In this scenario, touching ll_max would not be a
> > > > good idea as the ll_max controls the LL entries for all the DMA
> > > > channels not just for a single DMA transaction.
> > >
> > > You can use new variable, such as ll_avail.
> > >
> >
> > In order to separate out the execution paths a new meaningful variable
> "non_ll"
> > is used. The variable "non_ll" alone is sufficient. Using another
> > variable along side "non_ll" for the similar purpose may not have any
> added advantage.
>
> ll_avail can help debug/fine tune how much impact preformance by adjust ll
> length. And it make code logic clean and consistent. also ll_avail can help test
> corner case when ll item small. Normall case it is hard to reach ll_max.
>

Thank you for your suggestion. The ll_max is max limit on the descriptors that can
be accommodated on the device side DDR. The ll_avail will always be less than ll_max.
The optimization being referred can be tried without even having to declare the ll_avail
cause the number of descriptors given can be controlled by the DMA client based on the length
argument to the dmaengine_prep_* APIs. So, the use of dynamic ll_avail is not necessarily
required. Without increasing the ll_max, ll_avail cannot be increased. In order to increase
ll_max one may need to alter size and recompile this driver.

However, the requirement of ll_avail does not help for the supporting the non-LL mode.
For non-LL mode to use:
1) Either LL mode shall be not available, as it can happen for the Xilinx IP.
2) User provides the preference for non-LL mode.
For both above, the function calls are different which can be differentiated by using
the "non_ll" flag. So, even if I try to accommodate ll_avail, the call for LL or non-LL would be
ambiguous as in case of LL mode also we can have a single descriptor as similar to non-LL mode.
Please check the function dw_hdma_v0_core_start() in this review where the decision is taken
Based on non_ll flag.

> >
> > > >
> > > > > >
> ...
> > > > > > +
> > > > > > + ll_block->bar);
> > > > >
> > > > > This change need do prepare patch, which only change
> > > > > pci_bus_address() to dw_edma_get_phys_addr().
> > > > >
> > > >
> > > > This is not clear.
> > >
> > > why not. only trivial add helper patch, which help reviewer
> > >
> >
> > I was not clear on the question you asked.
> > It does not look justified when a patch is raised alone just to replace this
> function.
> > The function change is required cause the same code *can* support
> > different IPs from different vendors. And, with this single change
> > alone in the code the support for another IP is added. That's why it
> > is easier to get the reason for the change in the function name and syntax.
>
> Add replace pci_bus_address() with dw_edma_get_phys_addr() to make
> review easily and get ack for such replacement patches.
>
> two patches
> patch1, just replace exist pci_bus_address() with dw_edma_get_phys_addr()
> patch2, add new logic in dw_edma_get_phys_addr() to support new vendor.
>

I understand your concern about making the review easier. However, given that
we've been iterating on this patch series since September and are now at v9,
I believe the current approach is justified. The function renames from
pci_bus_address() to dw_edma_get_phys_addr() is directly tied to the
non-LL mode functionality being added - it's needed because the same code
now supports different IPs from different vendors.

Splitting this into a separate preparatory patch at this stage would further
delay the review process. The change is kind of straightforward and the context is clear
within the current patch. I request you to review this patch to avoid additional review cycles.

This also increases the work related to testing and maintaining multiple patches.
I have commitment for delivery of this, and I can see adding one more series definitely
add 3-4 months of review cycle from here. Please excuse me but this code has already
been reviewed extensively by other reviewers and almost by you as well. You can check
the detailed discussion wrt this function at the following link:
https://lore.kernel.org/all/SA1PR12MB8120341DFFD56D90EAD70EDE9514A@SA1PR12MB8120.namprd12.prod.outlook.com/

> >
> > > >
> > > > > >               ll_region->paddr += ll_block->off;
> > > > > >               ll_region->sz = ll_block->sz;
> > > > > >
> ...
> > > > > >
> > > > > > +static void dw_hdma_v0_core_non_ll_start(struct dw_edma_chunk
> > > > > *chunk)
> > > > > > +{
> > > > > > +     struct dw_edma_chan *chan = chunk->chan;
> > > > > > +     struct dw_edma *dw = chan->dw;
> > > > > > +     struct dw_edma_burst *child;
> > > > > > +     u32 val;
> > > > > > +
> > > > > > +     list_for_each_entry(child, &chunk->burst->list, list) {
> > > > >
> > > > > why need iterated list, it doesn't support ll. Need wait for irq
> > > > > to start next
> > > one.
> > > > >
> > > > > Frank
> > > >
> > > > Yes, this is true. The format is kept similar to LL mode.
> > >
> > > Just fill one. list_for_each_entry() cause confuse.
> > >
> > > Frank
> >
> > I see, we can use list_first_entry_or_null() which is dependent on
> > giving the type of pointer, compared to this list_for_each_entry()
> > looks neat and agnostic to the pointer type being used. Though, it can be
> explored further.
> > Also, when the chunk is allocated, the comment clearly spells out how
> > the allocation would be for the non LL mode so it is evident that each
> > chunk would have single entry and with that understanding it is clear
> > that loop will also be used in a similar manner, to retrieve a single
> > entry. It is a similar use case as of "do {}while (0)" albeit needs a context to
> understand it.
>
> I don't think so. list_for_each_entry() is miss leading to reader think it is not
> only to one item in burst list, and use polling method to to finish many burst
> transfer.
>
> list_for_each_entry() {
>         ...
>         readl_timeout()
> }
>
> Generally, EDMA is very quick, polling is much quicker than irq if data is small.
>
> Frank
>

The polling is not required. The single burst will raise the interrupt and from the
interrupt context another chunk will be scheduled. This cycle repeats till all the chunks
with single burst are exhausted.

The following comment made in function dw_edma_device_transfer() in the same patch
makes it amply clear that only a single burst would be handled for the non-LL mode.
+       /*
+        * For non-LL mode, only a single burst can be handled
+        * in a single chunk unlike LL mode where multiple bursts
+        * can be configured in a single chunk.
+        */

Looking at the way bursts are appended to chunks and chunks in dw_edma_device_transfer()
are scheduled for non-LL mode then it is clear what non-LL mode would handle in terms of bursts.
I just kept the format to match it with the LL mode format otherwise there is no
need of this comment and we can follow the syntax for a single entry alone.
Please share your suggestion if these descriptions fail to provide the clear context and intent.

> >
> > > >
> > > > >
> > > > > > +             SET_CH_32(dw, chan->dir, chan->id, ch_en,
> > > > > > + HDMA_V0_CH_EN);
> > > > > > +
> > > > > > +             /* Source address */
> > > > > > +             SET_CH_32(dw, chan->dir, chan->id, sar.lsb,
> > > > > > +                       lower_32_bits(child->sar));
> > > > > > +             SET_CH_32(dw, chan->dir, chan->id, sar.msb,
> > > > > > +                       upper_32_bits(child->sar));
> > > > > > +
> > > > > > +             /* Destination address */
> > > > > > +             SET_CH_32(dw, chan->dir, chan->id, dar.lsb,
> > > > > > +                       lower_32_bits(child->dar));
> > > > > > +             SET_CH_32(dw, chan->dir, chan->id, dar.msb,
> > > > > > +                       upper_32_bits(child->dar));
> > > > > > +
> > > > > > +             /* Transfer size */
> > > > > > +             SET_CH_32(dw, chan->dir, chan->id,
> > > > > > + transfer_size,
> > > > > > + child->sz);
> > > > > > +
> > > > > > +             /* Interrupt setup */
> > > > > > +             val = GET_CH_32(dw, chan->dir, chan->id, int_setup) |
> > > > > > +                             HDMA_V0_STOP_INT_MASK |
> > > > > > +                             HDMA_V0_ABORT_INT_MASK |
> > > > > > +                             HDMA_V0_LOCAL_STOP_INT_EN |
> > > > > > +                             HDMA_V0_LOCAL_ABORT_INT_EN;
> > > > > > +
> > > > > > +             if (!(dw->chip->flags & DW_EDMA_CHIP_LOCAL)) {
> > > > > > +                     val |= HDMA_V0_REMOTE_STOP_INT_EN |
> > > > > > +                            HDMA_V0_REMOTE_ABORT_INT_EN;
> > > > > > +             }
> > > > > > +
> > > > > > +             SET_CH_32(dw, chan->dir, chan->id, int_setup,
> > > > > > + val);
> > > > > > +
> > > > > > +             /* Channel control setup */
> > > > > > +             val = GET_CH_32(dw, chan->dir, chan->id, control1);
> > > > > > +             val &= ~HDMA_V0_LINKLIST_EN;
> > > > > > +             SET_CH_32(dw, chan->dir, chan->id, control1,
> > > > > > + val);
> > > > > > +
> > > > > > +             SET_CH_32(dw, chan->dir, chan->id, doorbell,
> > > > > > +                       HDMA_V0_DOORBELL_START);
> > > > > > +     }
> > > > > > +}
> > > > > > +
> > > > > > +static void dw_hdma_v0_core_start(struct dw_edma_chunk
> > > > > > +*chunk, bool
> > > > > > +first) {
> > > > > > +     struct dw_edma_chan *chan = chunk->chan;
> > > > > > +
> > > > > > +     if (chan->non_ll)
> > > > > > +             dw_hdma_v0_core_non_ll_start(chunk);
> > > > > > +     else
> > > > > > +             dw_hdma_v0_core_ll_start(chunk, first); }
> > > > > > +
> > > > > >  static void dw_hdma_v0_core_ch_config(struct dw_edma_chan
> > > > > > *chan)
> > > {
> > > > > >       struct dw_edma *dw = chan->dw; diff --git
> > > > > > a/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > index eab5fd7..7759ba9 100644
> > > > > > --- a/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > +++ b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > @@ -12,6 +12,7 @@
> > > > > >  #include <linux/dmaengine.h>
> > > > > >
> > > > > >  #define HDMA_V0_MAX_NR_CH                    8
> > > > > > +#define HDMA_V0_CH_EN                                BIT(0)
> > > > > >  #define HDMA_V0_LOCAL_ABORT_INT_EN           BIT(6)
> > > > > >  #define HDMA_V0_REMOTE_ABORT_INT_EN          BIT(5)
> > > > > >  #define HDMA_V0_LOCAL_STOP_INT_EN            BIT(4)
> > > > > > diff --git a/include/linux/dma/edma.h
> > > > > > b/include/linux/dma/edma.h index 3080747..78ce31b 100644
> > > > > > --- a/include/linux/dma/edma.h
> > > > > > +++ b/include/linux/dma/edma.h
> > > > > > @@ -99,6 +99,7 @@ struct dw_edma_chip {
> > > > > >       enum dw_edma_map_format mf;
> > > > > >
> > > > > >       struct dw_edma          *dw;
> > > > > > +     bool                    non_ll;
> > > > > >  };
> > > > > >
> > > > > >  /* Export to the platform drivers */
> > > > > > --
> > > > > > 1.8.3.1
> > > > > >

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

* RE: [PATCH v8 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint Support
  2026-01-21 16:54           ` Frank Li
@ 2026-01-23 14:26             ` Verma, Devendra
  0 siblings, 0 replies; 24+ messages in thread
From: Verma, Devendra @ 2026-01-23 14:26 UTC (permalink / raw)
  To: Frank Li
  Cc: bhelgaas@google.com, mani@kernel.org, vkoul@kernel.org,
	dmaengine@vger.kernel.org, linux-pci@vger.kernel.org,
	linux-kernel@vger.kernel.org, Simek, Michal, Verma, Devendra

[AMD Official Use Only - AMD Internal Distribution Only]

Hi Frank

Please check my response inline.

Regards,
Devendra
> -----Original Message-----
> From: Frank Li <Frank.li@nxp.com>
> Sent: Wednesday, January 21, 2026 10:24 PM
> To: Verma, Devendra <Devendra.Verma@amd.com>
> Cc: bhelgaas@google.com; mani@kernel.org; vkoul@kernel.org;
> dmaengine@vger.kernel.org; linux-pci@vger.kernel.org; linux-
> kernel@vger.kernel.org; Simek, Michal <michal.simek@amd.com>
> Subject: Re: [PATCH v8 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint
> Support
>
[Removed some extra headers to reduce the size of the mail]
> > > > > On Fri, Jan 09, 2026 at 05:33:53PM +0530, Devendra K Verma wrote:
> > > > > > AMD MDB PCIe endpoint support. For AMD specific support added
> > > > > > the following
> > > > > >   - AMD supported PCIe Device IDs and Vendor ID (Xilinx).
> > > > > >   - AMD MDB specific driver data
> > > > > >   - AMD MDB specific VSEC capability to retrieve the device DDR
> > > > > >     base address.
> > > > > >
> > > > > > Signed-off-by: Devendra K Verma <devendra.verma@amd.com>
> > > > > > ---
> > > > > > Changes in v8:
> > > > > > Changed the contant names to includer product vendor.
> > > > > > Moved the vendor specific code to vendor specific functions.
> > > > > >
> > > > > > Changes in v7:
> > > > > > Introduced vendor specific functions to retrieve the vsec data.
> > > > > >
> > > > > > Changes in v6:
> > > > > > Included "sizes.h" header and used the appropriate definitions
> > > > > > instead of constants.
> > > > > >
> > > > > > Changes in v5:
> > > > > > Added the definitions for Xilinx specific VSEC header id,
> > > > > > revision, and register offsets.
> > > > > > Corrected the error type when no physical offset found for
> > > > > > device side memory.
> > > > > > Corrected the order of variables.
> > > > > >
> > > > > > Changes in v4:
> > > > > > Configured 8 read and 8 write channels for Xilinx vendor Added
> > > > > > checks to validate vendor ID for vendor specific vsec id.
> > > > > > Added Xilinx specific vendor id for vsec specific to Xilinx
> > > > > > Added the LL and data region offsets, size as input params to
> > > > > > function dw_edma_set_chan_region_offset().
> > > > > > Moved the LL and data region offsets assignment to function
> > > > > > for Xilinx specific case.
> > > > > > Corrected comments.
> > > > > >
> > > > > > Changes in v3:
> > > > > > Corrected a typo when assigning AMD (Xilinx) vsec id macro and
> > > > > > condition check.
> > > > > >
> > > > > > Changes in v2:
> > > > > > Reverted the devmem_phys_off type to u64.
> > > > > > Renamed the function appropriately to suit the functionality
> > > > > > for setting the LL & data region offsets.
> > > > > >
> > > > > > Changes in v1:
> > > > > > Removed the pci device id from pci_ids.h file.
> > > > > > Added the vendor id macro as per the suggested method.
> > > > > > Changed the type of the newly added devmem_phys_off variable.
> > > > > > Added to logic to assign offsets for LL and data region blocks
> > > > > > in case more number of channels are enabled than given in
> > > amd_mdb_data struct.
> > > > > > ---
> > > > > >  drivers/dma/dw-edma/dw-edma-pcie.c | 192
> > > > > > ++++++++++++++++++++++++++++++++++---
> > > > > >  1 file changed, 178 insertions(+), 14 deletions(-)
> > > > > >
> > > > > > diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c
> > > > > > b/drivers/dma/dw-edma/dw-edma-pcie.c
> > > > > > index 3371e0a7..2efd149 100644
> > > > > > --- a/drivers/dma/dw-edma/dw-edma-pcie.c
> > > > > > +++ b/drivers/dma/dw-edma/dw-edma-pcie.c
> > > > > > @@ -14,14 +14,35 @@
> > > > > >  #include <linux/pci-epf.h>
> > > > > >  #include <linux/msi.h>
> > > > > >  #include <linux/bitfield.h>
> > > > > > +#include <linux/sizes.h>
> > > > > >
> > > > > >  #include "dw-edma-core.h"
> > > > > >
> > > > > > -#define DW_PCIE_VSEC_DMA_ID                  0x6
> > > > > > -#define DW_PCIE_VSEC_DMA_BAR                 GENMASK(10, 8)
> > > > > > -#define DW_PCIE_VSEC_DMA_MAP                 GENMASK(2, 0)
> > > > > > -#define DW_PCIE_VSEC_DMA_WR_CH                       GENMASK(9, 0)
> > > > > > -#define DW_PCIE_VSEC_DMA_RD_CH                       GENMASK(25,
> 16)
> > > > > > +/* Synopsys */
> > > > > > +#define DW_PCIE_SYNOPSYS_VSEC_DMA_ID         0x6
> > > > > > +#define DW_PCIE_SYNOPSYS_VSEC_DMA_BAR
> GENMASK(10,
> > > 8)
> > > > > > +#define DW_PCIE_SYNOPSYS_VSEC_DMA_MAP
> GENMASK(2, 0)
> > > > > > +#define DW_PCIE_SYNOPSYS_VSEC_DMA_WR_CH
> GENMASK(9,
> > > 0)
> > > > > > +#define DW_PCIE_SYNOPSYS_VSEC_DMA_RD_CH
> GENMASK(25,
> > > 16)
> > > > >
> > > > > Sorry, jump into at v8.
> > > > > According to my understand 'DW' means 'Synopsys'.
> > > > >
> > > >
> > > > Yes, DW means Designware representing Synopsys here.
> > > > For the sake of clarity, a distinction was required to separate
> > > > the names of macros having the similar purpose for other IP,
> > > > Xilinx in this case. Otherwise, it is causing confusion which
> > > > macros to use for which vendor. This also helps in future if any
> > > > of the vendors try to retrieve a new or different VSEC IDs then
> > > > all they need is to define macros
> > > which clearly show the association with the vendor, thus eliminating
> > > the confusion.
> > >
> > > If want to reuse the driver, driver owner take reponsiblity to find
> > > the difference.
> > >
> > > If define a whole set of register, the reader is hard to find real difference.
> > >
> >
> > It is not regarding the register set rather VSEC capability which can
> > differ in the purpose even for the similar IPs. As is the current case
> > where one VSEC ID serves the similar purpose for both the IPs while
> > the VSEC ID = 0x20 differs in meaning for Synopsys and Xilinx thus I
> > think it is OK to define new macros as long as they do not create confusion.
>
> But need put rename existing macro to new patches. And I think use
> XILINX_PCIE_MDB_* should be enough without renaming. Everyone know
> DW is SYNOPSYS.
>

As the current addition of the code related to Xilinx piggybacking on the existing
DW code the prefix "DW" was added to the macros related to Xilinx as well.
The format available in the file has been followed.
When a necessity arises then perhaps the files can be separated and the name
suggestion would clearly indicate the distinction and difference required between
these two drivers.
Thanks to the reviewer(s), I had received multiple reviews regarding the naming and finally
it was agreed upon the convention available in this version.
Please check the discussion in the following thread:
https://lore.kernel.org/all/SA1PR12MB81204C807C2F7C72730EC82295ADA@SA1PR12MB8120.namprd12.prod.outlook.com/

> >
> > > >
> > > > > > +
> > > > > > +/* AMD MDB (Xilinx) specific defines */
> > > > > > +#define PCI_DEVICE_ID_XILINX_B054            0xb054
> > > > > > +
> > > > > > +#define DW_PCIE_XILINX_MDB_VSEC_DMA_ID               0x6
> > > > > > +#define DW_PCIE_XILINX_MDB_VSEC_ID           0x20
> > > > > > +#define DW_PCIE_XILINX_MDB_VSEC_DMA_BAR
> GENMASK(10,
> > > 8)
> > > > > > +#define DW_PCIE_XILINX_MDB_VSEC_DMA_MAP
> GENMASK(2,
> > > 0)
> > > > > > +#define DW_PCIE_XILINX_MDB_VSEC_DMA_WR_CH    GENMASK(9,
> 0)
> > > > > > +#define DW_PCIE_XILINX_MDB_VSEC_DMA_RD_CH    GENMASK(25,
> > > 16)
> > > > >
> > > > > These defination is the same. Need redefine again
> > > > >
> > > >
> ...
> > > >
> > > > As explained above, the name change is required to avoid confusion.
> > > > The trigger to have the separate names for each IP is the
> > > > inclusion of Xilinx IP that is why no separate patch is created.
> > >
> > > Separate patch renmae macro only. Reviewer can simple bypass this
> > > typo trivial patch.
> > >
> > > Then add new one.
> > >
> > > Actually, Needn't rename at all.  You can directly use XLINNK_PCIE_*
> > >
> > > Frank
> >
> > Please check the discussion on previous versions of the same patch series.
>
> It'd better you put link here to support your purposal.
>
> > We have this patch as the outcome of those discussions.
> > Other reviewing members felt it that keeping the name similar for the
> > VSEC ID having similar purpose for two different IPs was causing the
> > confusion that is why it was agreed upon the separate out the naming
> > as per the vendor-name of VSEC ID. Regarding the separate patch, the
> > reason is introduction of the new IP which mostly supports the similar
> > functionality except in case of VSEC IDs that's why the name
> > separation became part of these patches. It sets the context for changing
> the name of the existing macros.
>
> If put trivial big part to new patch to reduce, it will reduce review efforts.
>
> Frank
>

As requested, a link related to discussion in this regard is available on the above
comments, please take a look.

> >
> > > >
> > > > > >
> > > > > >       pci_read_config_dword(pdev, vsec + 0x14, &val);
> > > > > >       off = val;
> > > > > > @@ -157,6 +237,67 @@ static void
> > > > > dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
> > > > > >       pdata->rg.off = off;
> > > > > >  }
> > > > > >
> > > > > > +static void dw_edma_pcie_get_xilinx_dma_data(struct pci_dev
> *pdev,
> > > > > > +                                          struct
> > > > > > +dw_edma_pcie_data
> > > > > > +*pdata) {
> > > > > > +     u32 val, map;
> > > > > > +     u16 vsec;
> > > > > > +     u64 off;
> > > > > > +
> > > > > > +     pdata->devmem_phys_off =
> > > DW_PCIE_XILINX_MDB_INVALID_ADDR;
> > > > > > +
> > > > > > +     vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_XILINX,
> > > > > > +                                     DW_PCIE_XILINX_MDB_VSEC_DMA_ID);
> > > > > > +     if (!vsec)
> > > > > > +             return;
> > > > > > +
> > > > > > +     pci_read_config_dword(pdev, vsec + PCI_VNDR_HEADER, &val);
> > > > > > +     if (PCI_VNDR_HEADER_REV(val) != 0x00 ||
> > > > > > +         PCI_VNDR_HEADER_LEN(val) != 0x18)
> > > > > > +             return;
> > > > > > +
> > > > > > +     pci_dbg(pdev, "Detected Xilinx PCIe Vendor-Specific
> > > > > > + Extended Capability
> > > > > DMA\n");
> > > > > > +     pci_read_config_dword(pdev, vsec + 0x8, &val);
> > > > > > +     map = FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_MAP, val);
> > > > > > +     if (map != EDMA_MF_EDMA_LEGACY &&
> > > > > > +         map != EDMA_MF_EDMA_UNROLL &&
> > > > > > +         map != EDMA_MF_HDMA_COMPAT &&
> > > > > > +         map != EDMA_MF_HDMA_NATIVE)
> > > > > > +             return;
> > > > > > +
> > > > > > +     pdata->mf = map;
> > > > > > +     pdata->rg.bar =
> > > FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_BAR,
> > > > > val);
> > > > > > +
> > > > > > +     pci_read_config_dword(pdev, vsec + 0xc, &val);
> > > > > > +     pdata->wr_ch_cnt = min_t(u16, pdata->wr_ch_cnt,
> > > > > > +
> > > > > > + FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_WR_CH,
> > > > > val));
> > > > > > +     pdata->rd_ch_cnt = min_t(u16, pdata->rd_ch_cnt,
> > > > > > +
> > > > > > + FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_RD_CH, val));
> > > > > > +
> > > > > > +     pci_read_config_dword(pdev, vsec + 0x14, &val);
> > > > > > +     off = val;
> > > > > > +     pci_read_config_dword(pdev, vsec + 0x10, &val);
> > > > > > +     off <<= 32;
> > > > > > +     off |= val;
> > > > > > +     pdata->rg.off = off;
> > > > > > +
> > > > > > +     vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_XILINX,
> > > > > > +                                     DW_PCIE_XILINX_MDB_VSEC_ID);
> > > > > > +     if (!vsec)
> > > > > > +             return;
> > > > > > +
> > > > > > +     pci_read_config_dword(pdev,
> > > > > > +                           vsec +
> > > DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_HIGH,
> > > > > > +                           &val);
> > > > > > +     off = val;
> > > > > > +     pci_read_config_dword(pdev,
> > > > > > +                           vsec +
> > > DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_LOW,
> > > > > > +                           &val);
> > > > > > +     off <<= 32;
> > > > > > +     off |= val;
> > > > > > +     pdata->devmem_phys_off = off; }
> > > > > > +
> > > > > >  static int dw_edma_pcie_probe(struct pci_dev *pdev,
> > > > > >                             const struct pci_device_id *pid)
> > > > > > { @@
> > > > > > -184,7 +325,28 @@ static int dw_edma_pcie_probe(struct pci_dev
> > > *pdev,
> > > > > >        * Tries to find if exists a PCIe Vendor-Specific Extended Capability
> > > > > >        * for the DMA, if one exists, then reconfigures it.
> > > > > >        */
> > > > > > -     dw_edma_pcie_get_vsec_dma_data(pdev, vsec_data);
> > > > > > +     dw_edma_pcie_get_synopsys_dma_data(pdev, vsec_data);
> > > > > > +     dw_edma_pcie_get_xilinx_dma_data(pdev, vsec_data);
> > > > > > +
> > > > > > +     if (pdev->vendor == PCI_VENDOR_ID_XILINX) {
> > > > >
> > > > > dw_edma_pcie_get_xilinx_dma_data() should be here.
> > > > >
> > > > > Frank
> > > >
> > > > Yes, this is good suggestion. Thanks!
> > > >
> > > > > > +             /*
> > > > > > +              * There is no valid address found for the LL memory
> > > > > > +              * space on the device side.
> > > > > > +              */
> > > > > > +             if (vsec_data->devmem_phys_off ==
> > > > > DW_PCIE_XILINX_MDB_INVALID_ADDR)
> > > > > > +                     return -ENOMEM;
> > > > > > +
> > > > > > +             /*
> > > > > > +              * Configure the channel LL and data blocks if number of
> > > > > > +              * channels enabled in VSEC capability are more than the
> > > > > > +              * channels configured in xilinx_mdb_data.
> > > > > > +              */
> > > > > > +             dw_edma_set_chan_region_offset(vsec_data, BAR_2, 0,
> > > > > > +                                            DW_PCIE_XILINX_MDB_LL_OFF_GAP,
> > > > > > +                                            DW_PCIE_XILINX_MDB_LL_SIZE,
> > > > > > +                                            DW_PCIE_XILINX_MDB_DT_OFF_GAP,
> > > > > > +                                            DW_PCIE_XILINX_MDB_DT_SIZE);
> > > > > > +     }
> > > > > >
> > > > > >       /* Mapping PCI BAR regions */
> > > > > >       mask = BIT(vsec_data->rg.bar); @@ -367,6 +529,8 @@
> > > > > > static void dw_edma_pcie_remove(struct pci_dev
> > > > > > *pdev)
> > > > > >
> > > > > >  static const struct pci_device_id dw_edma_pcie_id_table[] = {
> > > > > >       { PCI_DEVICE_DATA(SYNOPSYS, EDDA, &snps_edda_data) },
> > > > > > +     { PCI_VDEVICE(XILINX, PCI_DEVICE_ID_XILINX_B054),
> > > > > > +       (kernel_ulong_t)&xilinx_mdb_data },
> > > > > >       { }
> > > > > >  };
> > > > > >  MODULE_DEVICE_TABLE(pci, dw_edma_pcie_id_table);
> > > > > > --
> > > > > > 1.8.3.1
> > > > > >

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

* Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
  2026-01-23 14:26             ` Verma, Devendra
@ 2026-01-26 19:00               ` Frank Li
  2026-01-28 12:59                 ` Verma, Devendra
  0 siblings, 1 reply; 24+ messages in thread
From: Frank Li @ 2026-01-26 19:00 UTC (permalink / raw)
  To: Verma, Devendra
  Cc: bhelgaas@google.com, mani@kernel.org, vkoul@kernel.org,
	dmaengine@vger.kernel.org, linux-pci@vger.kernel.org,
	linux-kernel@vger.kernel.org, Simek, Michal

On Fri, Jan 23, 2026 at 02:26:15PM +0000, Verma, Devendra wrote:
> [AMD Official Use Only - AMD Internal Distribution Only]
>
> > -----Original Message-----
> > From: Frank Li <Frank.li@nxp.com>
> > Sent: Wednesday, January 21, 2026 10:49 PM
> > To: Verma, Devendra <Devendra.Verma@amd.com>
> > Cc: bhelgaas@google.com; mani@kernel.org; vkoul@kernel.org;
> > dmaengine@vger.kernel.org; linux-pci@vger.kernel.org; linux-
> > kernel@vger.kernel.org; Simek, Michal <michal.simek@amd.com>
> > Subject: Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
> >
> > Caution: This message originated from an External Source. Use proper
> > caution when opening attachments, clicking links, or responding.
>
> --[ Snipped some headers to reduce the size of this mail ]--
>
> > > > > >
> > > > > > On Fri, Jan 09, 2026 at 05:33:54PM +0530, Devendra K Verma wrote:
> > > > > > > AMD MDB IP supports Linked List (LL) mode as well as non-LL mode.
> > > > > > > The current code does not have the mechanisms to enable the
> > > > > > > DMA transactions using the non-LL mode. The following two
> > > > > > > cases are added with this patch:
> > > > > > > - For the AMD (Xilinx) only, when a valid physical base address of
> > > > > > >   the device side DDR is not configured, then the IP can still be
> > > > > > >   used in non-LL mode. For all the channels DMA transactions
> > > > > > > will
> > > > > >
> > > > > > If DDR have not configured, where DATA send to in device side by
> > > > > > non-LL mode.
> > > > > >
> > > > >
> > > > > The DDR base address in the VSEC capability is used for driving
> > > > > the DMA transfers when used in the LL mode. The DDR is configured
> > > > > and present all the time but the DMA PCIe driver uses this DDR
> > > > > base address (physical address) to configure the LLP address.
> > > > >
> > > > > In the scenario, where this DDR base address in VSEC capability is
> > > > > not configured then the current controller cannot be used as the
> > > > > default mode supported is LL mode only. In order to make the
> > > > > controller usable non-LL mode is being added which just needs SAR,
> > > > > DAR, XFERLEN and control register to initiate the transfer. So,
> > > > > the DDR is always present, but the DMA PCIe driver need to know
> > > > > the DDR base physical address to make the transfer. This is useful
> > > > > in scenarios where the memory
> > > > allocated for LL can be used for DMA transactions as well.
> > > >
> > > > Do you means use DMA transfer LL's context?
> > > >
> > >
> > > Yes, the device side memory reserved for the link list to store the
> > > descriptors, accessed by the host via BAR_2 in this driver code.
> > >
> > > > >
> > > > > > >   be using the non-LL mode only. This, the default non-LL mode,
> > > > > > >   is not applicable for Synopsys IP with the current code addition.
> > > > > > >
> > > > > > > - If the default mode is LL-mode, for both AMD (Xilinx) and Synosys,
> > > > > > >   and if user wants to use non-LL mode then user can do so via
> > > > > > >   configuring the peripheral_config param of dma_slave_config.
> > > > > > >
> > > > > > > Signed-off-by: Devendra K Verma <devendra.verma@amd.com>
> > > > > > > ---
> > > > > > > Changes in v8
> > > > > > >   Cosmetic change related to comment and code.
> > > > > > >
> > > > > > > Changes in v7
> > > > > > >   No change
> > > > > > >
> > > > > > > Changes in v6
> > > > > > >   Gave definition to bits used for channel configuration.
> > > > > > >   Removed the comment related to doorbell.
> > > > > > >
> > > > > > > Changes in v5
> > > > > > >   Variable name 'nollp' changed to 'non_ll'.
> > > > > > >   In the dw_edma_device_config() WARN_ON replaced with
> > dev_err().
> > > > > > >   Comments follow the 80-column guideline.
> > > > > > >
> > > > > > > Changes in v4
> > > > > > >   No change
> > > > > > >
> > > > > > > Changes in v3
> > > > > > >   No change
> > > > > > >
> > > > > > > Changes in v2
> > > > > > >   Reverted the function return type to u64 for
> > > > > > >   dw_edma_get_phys_addr().
> > > > > > >
> > > > > > > Changes in v1
> > > > > > >   Changed the function return type for dw_edma_get_phys_addr().
> > > > > > >   Corrected the typo raised in review.
> > > > > > > ---
> > > > > > >  drivers/dma/dw-edma/dw-edma-core.c    | 42
> > > > +++++++++++++++++++++---
> > > > > > >  drivers/dma/dw-edma/dw-edma-core.h    |  1 +
> > > > > > >  drivers/dma/dw-edma/dw-edma-pcie.c    | 46
> > ++++++++++++++++++--
> > > > ------
> > > > > > >  drivers/dma/dw-edma/dw-hdma-v0-core.c | 61
> > > > > > > ++++++++++++++++++++++++++++++++++-
> > > > > > >  drivers/dma/dw-edma/dw-hdma-v0-regs.h |  1 +
> > > > > >
> > > > > > edma-v0-core.c have not update, if don't support, at least need
> > > > > > return failure at dw_edma_device_config() when backend is eDMA.
> > > > > >
> > > > > > >  include/linux/dma/edma.h              |  1 +
> > > > > > >  6 files changed, 132 insertions(+), 20 deletions(-)
> > > > > > >
> > > > > > > diff --git a/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > > b/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > > index b43255f..d37112b 100644
> > > > > > > --- a/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > > +++ b/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > > @@ -223,8 +223,32 @@ static int dw_edma_device_config(struct
> > > > > > dma_chan *dchan,
> > > > > > >                                struct dma_slave_config *config)  {
> > > > > > >       struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
> > > > > > > +     int non_ll = 0;
> > > > > > > +
> > > > > > > +     if (config->peripheral_config &&
> > > > > > > +         config->peripheral_size != sizeof(int)) {
> > > > > > > +             dev_err(dchan->device->dev,
> > > > > > > +                     "config param peripheral size mismatch\n");
> > > > > > > +             return -EINVAL;
> > > > > > > +     }
> > > > > > >
> > > > > > >       memcpy(&chan->config, config, sizeof(*config));
> > > > > > > +
> > > > > > > +     /*
> > > > > > > +      * When there is no valid LLP base address available then the
> > default
> > > > > > > +      * DMA ops will use the non-LL mode.
> > > > > > > +      *
> > > > > > > +      * Cases where LL mode is enabled and client wants to use the
> > non-LL
> > > > > > > +      * mode then also client can do so via providing the
> > peripheral_config
> > > > > > > +      * param.
> > > > > > > +      */
> > > > > > > +     if (config->peripheral_config)
> > > > > > > +             non_ll = *(int *)config->peripheral_config;
> > > > > > > +
> > > > > > > +     chan->non_ll = false;
> > > > > > > +     if (chan->dw->chip->non_ll || (!chan->dw->chip->non_ll &&
> > non_ll))
> > > > > > > +             chan->non_ll = true;
> > > > > > > +
> > > > > > >       chan->configured = true;
> > > > > > >
> > > > > > >       return 0;
> > > > > > > @@ -353,7 +377,7 @@ static void
> > > > > > > dw_edma_device_issue_pending(struct
> > > > > > dma_chan *dchan)
> > > > > > >       struct dw_edma_chan *chan = dchan2dw_edma_chan(xfer-
> > >dchan);
> > > > > > >       enum dma_transfer_direction dir = xfer->direction;
> > > > > > >       struct scatterlist *sg = NULL;
> > > > > > > -     struct dw_edma_chunk *chunk;
> > > > > > > +     struct dw_edma_chunk *chunk = NULL;
> > > > > > >       struct dw_edma_burst *burst;
> > > > > > >       struct dw_edma_desc *desc;
> > > > > > >       u64 src_addr, dst_addr;
> > > > > > > @@ -419,9 +443,11 @@ static void
> > > > > > > dw_edma_device_issue_pending(struct
> > > > > > dma_chan *dchan)
> > > > > > >       if (unlikely(!desc))
> > > > > > >               goto err_alloc;
> > > > > > >
> > > > > > > -     chunk = dw_edma_alloc_chunk(desc);
> > > > > > > -     if (unlikely(!chunk))
> > > > > > > -             goto err_alloc;
> > > > > > > +     if (!chan->non_ll) {
> > > > > > > +             chunk = dw_edma_alloc_chunk(desc);
> > > > > > > +             if (unlikely(!chunk))
> > > > > > > +                     goto err_alloc;
> > > > > > > +     }
> > > > > >
> > > > > > non_ll is the same as ll_max = 1. (or 2, there are link back entry).
> > > > > >
> > > > > > If you set ll_max = 1, needn't change this code.
> > > > > >
> > > > >
> > > > > The ll_max is defined for the session till the driver is loaded in the
> > kernel.
> > > > > This code also enables the non-LL mode dynamically upon input from
> > > > > the DMA client. In this scenario, touching ll_max would not be a
> > > > > good idea as the ll_max controls the LL entries for all the DMA
> > > > > channels not just for a single DMA transaction.
> > > >
> > > > You can use new variable, such as ll_avail.
> > > >
> > >
> > > In order to separate out the execution paths a new meaningful variable
> > "non_ll"
> > > is used. The variable "non_ll" alone is sufficient. Using another
> > > variable along side "non_ll" for the similar purpose may not have any
> > added advantage.
> >
> > ll_avail can help debug/fine tune how much impact preformance by adjust ll
> > length. And it make code logic clean and consistent. also ll_avail can help test
> > corner case when ll item small. Normall case it is hard to reach ll_max.
> >
>
> Thank you for your suggestion. The ll_max is max limit on the descriptors that can
> be accommodated on the device side DDR. The ll_avail will always be less than ll_max.
> The optimization being referred can be tried without even having to declare the ll_avail
> cause the number of descriptors given can be controlled by the DMA client based on the length
> argument to the dmaengine_prep_* APIs.

I optimzied it to allow dynatmic appended dma descriptors.

https://lore.kernel.org/dmaengine/20260109-edma_dymatic-v1-0-9a98c9c98536@nxp.com/T/#t

> So, the use of dynamic ll_avail is not necessarily
> required. Without increasing the ll_max, ll_avail cannot be increased. In order to increase
> ll_max one may need to alter size and recompile this driver.
>
> However, the requirement of ll_avail does not help for the supporting the non-LL mode.
> For non-LL mode to use:
> 1) Either LL mode shall be not available, as it can happen for the Xilinx IP.
> 2) User provides the preference for non-LL mode.
> For both above, the function calls are different which can be differentiated by using
> the "non_ll" flag. So, even if I try to accommodate ll_avail, the call for LL or non-LL would be
> ambiguous as in case of LL mode also we can have a single descriptor as similar to non-LL mode.
> Please check the function dw_hdma_v0_core_start() in this review where the decision is taken
> Based on non_ll flag.

We can treat ll_avail == 1 as no_ll mode because needn't set extra LL in
memory at all.

>
> > >
> > > > >
> > > > > > >
> > ...
> > > > > > > +
> > > > > > > + ll_block->bar);
> > > > > >
> > > > > > This change need do prepare patch, which only change
> > > > > > pci_bus_address() to dw_edma_get_phys_addr().
> > > > > >
> > > > >
> > > > > This is not clear.
> > > >
> > > > why not. only trivial add helper patch, which help reviewer
> > > >
> > >
> > > I was not clear on the question you asked.
> > > It does not look justified when a patch is raised alone just to replace this
> > function.
> > > The function change is required cause the same code *can* support
> > > different IPs from different vendors. And, with this single change
> > > alone in the code the support for another IP is added. That's why it
> > > is easier to get the reason for the change in the function name and syntax.
> >
> > Add replace pci_bus_address() with dw_edma_get_phys_addr() to make
> > review easily and get ack for such replacement patches.
> >
> > two patches
> > patch1, just replace exist pci_bus_address() with dw_edma_get_phys_addr()
> > patch2, add new logic in dw_edma_get_phys_addr() to support new vendor.
> >
>
> I understand your concern about making the review easier. However, given that
> we've been iterating on this patch series since September and are now at v9,
> I believe the current approach is justified. The function renames from
> pci_bus_address() to dw_edma_get_phys_addr() is directly tied to the
> non-LL mode functionality being added - it's needed because the same code
> now supports different IPs from different vendors.
>
> Splitting this into a separate preparatory patch at this stage would further
> delay the review process. The change is kind of straightforward and the context is clear
> within the current patch. I request you to review this patch to avoid additional review cycles.
>
> This also increases the work related to testing and maintaining multiple patches.
> I have commitment for delivery of this, and I can see adding one more series definitely
> add 3-4 months of review cycle from here. Please excuse me but this code has already

Thank you for your persevere.

> been reviewed extensively by other reviewers and almost by you as well. You can check
> the detailed discussion wrt this function at the following link:
> https://lore.kernel.org/all/SA1PR12MB8120341DFFD56D90EAD70EDE9514A@SA1PR12MB8120.namprd12.prod.outlook.com/
>

But still not got reviewed by tags. The recently,  Manivannan Sadhasivam
, Niklas Cassel and me active worked on this driver. You'd better to get
their feedback. Bjorn as pci maintainer to provide generally feedback.


> > >
> > > > >
> > > > > > >               ll_region->paddr += ll_block->off;
> > > > > > >               ll_region->sz = ll_block->sz;
> > > > > > >
> > ...
> > > > > > >
> > > > > > > +static void dw_hdma_v0_core_non_ll_start(struct dw_edma_chunk
> > > > > > *chunk)
> > > > > > > +{
> > > > > > > +     struct dw_edma_chan *chan = chunk->chan;
> > > > > > > +     struct dw_edma *dw = chan->dw;
> > > > > > > +     struct dw_edma_burst *child;
> > > > > > > +     u32 val;
> > > > > > > +
> > > > > > > +     list_for_each_entry(child, &chunk->burst->list, list) {
> > > > > >
> > > > > > why need iterated list, it doesn't support ll. Need wait for irq
> > > > > > to start next
> > > > one.
> > > > > >
> > > > > > Frank
> > > > >
> > > > > Yes, this is true. The format is kept similar to LL mode.
> > > >
> > > > Just fill one. list_for_each_entry() cause confuse.
> > > >
> > > > Frank
> > >
> > > I see, we can use list_first_entry_or_null() which is dependent on
> > > giving the type of pointer, compared to this list_for_each_entry()
> > > looks neat and agnostic to the pointer type being used. Though, it can be
> > explored further.
> > > Also, when the chunk is allocated, the comment clearly spells out how
> > > the allocation would be for the non LL mode so it is evident that each
> > > chunk would have single entry and with that understanding it is clear
> > > that loop will also be used in a similar manner, to retrieve a single
> > > entry. It is a similar use case as of "do {}while (0)" albeit needs a context to
> > understand it.
> >
> > I don't think so. list_for_each_entry() is miss leading to reader think it is not
> > only to one item in burst list, and use polling method to to finish many burst
> > transfer.
> >
> > list_for_each_entry() {
> >         ...
> >         readl_timeout()
> > }
> >
> > Generally, EDMA is very quick, polling is much quicker than irq if data is small.
> >
> > Frank
> >
>
> The polling is not required. The single burst will raise the interrupt and from the
> interrupt context another chunk will be scheduled. This cycle repeats till all the chunks
> with single burst are exhausted.
>
> The following comment made in function dw_edma_device_transfer() in the same patch
> makes it amply clear that only a single burst would be handled for the non-LL mode.
> +       /*
> +        * For non-LL mode, only a single burst can be handled
> +        * in a single chunk unlike LL mode where multiple bursts
> +        * can be configured in a single chunk.
> +        */
>
> Looking at the way bursts are appended to chunks and chunks in dw_edma_device_transfer()
> are scheduled for non-LL mode then it is clear what non-LL mode would handle in terms of bursts.
> I just kept the format to match it with the LL mode format otherwise there is no
> need of this comment and we can follow the syntax for a single entry alone.
> Please share your suggestion if these descriptions fail to provide the clear context and intent.

Avoid use list_for_each_entry() here to prevent miss-leading.

Frank

>
> > >
> > > > >
> > > > > >
> > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, ch_en,
> > > > > > > + HDMA_V0_CH_EN);
> > > > > > > +
> > > > > > > +             /* Source address */
> > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, sar.lsb,
> > > > > > > +                       lower_32_bits(child->sar));
> > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, sar.msb,
> > > > > > > +                       upper_32_bits(child->sar));
> > > > > > > +
> > > > > > > +             /* Destination address */
> > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, dar.lsb,
> > > > > > > +                       lower_32_bits(child->dar));
> > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, dar.msb,
> > > > > > > +                       upper_32_bits(child->dar));
> > > > > > > +
> > > > > > > +             /* Transfer size */
> > > > > > > +             SET_CH_32(dw, chan->dir, chan->id,
> > > > > > > + transfer_size,
> > > > > > > + child->sz);
> > > > > > > +
> > > > > > > +             /* Interrupt setup */
> > > > > > > +             val = GET_CH_32(dw, chan->dir, chan->id, int_setup) |
> > > > > > > +                             HDMA_V0_STOP_INT_MASK |
> > > > > > > +                             HDMA_V0_ABORT_INT_MASK |
> > > > > > > +                             HDMA_V0_LOCAL_STOP_INT_EN |
> > > > > > > +                             HDMA_V0_LOCAL_ABORT_INT_EN;
> > > > > > > +
> > > > > > > +             if (!(dw->chip->flags & DW_EDMA_CHIP_LOCAL)) {
> > > > > > > +                     val |= HDMA_V0_REMOTE_STOP_INT_EN |
> > > > > > > +                            HDMA_V0_REMOTE_ABORT_INT_EN;
> > > > > > > +             }
> > > > > > > +
> > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, int_setup,
> > > > > > > + val);
> > > > > > > +
> > > > > > > +             /* Channel control setup */
> > > > > > > +             val = GET_CH_32(dw, chan->dir, chan->id, control1);
> > > > > > > +             val &= ~HDMA_V0_LINKLIST_EN;
> > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, control1,
> > > > > > > + val);
> > > > > > > +
> > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, doorbell,
> > > > > > > +                       HDMA_V0_DOORBELL_START);
> > > > > > > +     }
> > > > > > > +}
> > > > > > > +
> > > > > > > +static void dw_hdma_v0_core_start(struct dw_edma_chunk
> > > > > > > +*chunk, bool
> > > > > > > +first) {
> > > > > > > +     struct dw_edma_chan *chan = chunk->chan;
> > > > > > > +
> > > > > > > +     if (chan->non_ll)
> > > > > > > +             dw_hdma_v0_core_non_ll_start(chunk);
> > > > > > > +     else
> > > > > > > +             dw_hdma_v0_core_ll_start(chunk, first); }
> > > > > > > +
> > > > > > >  static void dw_hdma_v0_core_ch_config(struct dw_edma_chan
> > > > > > > *chan)
> > > > {
> > > > > > >       struct dw_edma *dw = chan->dw; diff --git
> > > > > > > a/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > > b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > > index eab5fd7..7759ba9 100644
> > > > > > > --- a/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > > +++ b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > > @@ -12,6 +12,7 @@
> > > > > > >  #include <linux/dmaengine.h>
> > > > > > >
> > > > > > >  #define HDMA_V0_MAX_NR_CH                    8
> > > > > > > +#define HDMA_V0_CH_EN                                BIT(0)
> > > > > > >  #define HDMA_V0_LOCAL_ABORT_INT_EN           BIT(6)
> > > > > > >  #define HDMA_V0_REMOTE_ABORT_INT_EN          BIT(5)
> > > > > > >  #define HDMA_V0_LOCAL_STOP_INT_EN            BIT(4)
> > > > > > > diff --git a/include/linux/dma/edma.h
> > > > > > > b/include/linux/dma/edma.h index 3080747..78ce31b 100644
> > > > > > > --- a/include/linux/dma/edma.h
> > > > > > > +++ b/include/linux/dma/edma.h
> > > > > > > @@ -99,6 +99,7 @@ struct dw_edma_chip {
> > > > > > >       enum dw_edma_map_format mf;
> > > > > > >
> > > > > > >       struct dw_edma          *dw;
> > > > > > > +     bool                    non_ll;
> > > > > > >  };
> > > > > > >
> > > > > > >  /* Export to the platform drivers */
> > > > > > > --
> > > > > > > 1.8.3.1
> > > > > > >

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

* RE: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
  2026-01-26 19:00               ` Frank Li
@ 2026-01-28 12:59                 ` Verma, Devendra
  2026-01-28 15:07                   ` Frank Li
  0 siblings, 1 reply; 24+ messages in thread
From: Verma, Devendra @ 2026-01-28 12:59 UTC (permalink / raw)
  To: Frank Li
  Cc: bhelgaas@google.com, mani@kernel.org, vkoul@kernel.org,
	dmaengine@vger.kernel.org, linux-pci@vger.kernel.org,
	linux-kernel@vger.kernel.org, Simek, Michal, Verma, Devendra

[AMD Official Use Only - AMD Internal Distribution Only]

> -----Original Message-----
> From: Frank Li <Frank.li@nxp.com>
> Sent: Tuesday, January 27, 2026 12:30 AM
> To: Verma, Devendra <Devendra.Verma@amd.com>
> Cc: bhelgaas@google.com; mani@kernel.org; vkoul@kernel.org;
> dmaengine@vger.kernel.org; linux-pci@vger.kernel.org; linux-
> kernel@vger.kernel.org; Simek, Michal <michal.simek@amd.com>
> Subject: Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode

--[ Snipped some headers to reduce the size of this mail ]--

> > > > > > > > AMD MDB IP supports Linked List (LL) mode as well as non-LL
> mode.
> > > > > > > > The current code does not have the mechanisms to enable
> > > > > > > > the DMA transactions using the non-LL mode. The following
> > > > > > > > two cases are added with this patch:
> > > > > > > > - For the AMD (Xilinx) only, when a valid physical base address of
> > > > > > > >   the device side DDR is not configured, then the IP can still be
> > > > > > > >   used in non-LL mode. For all the channels DMA
> > > > > > > > transactions will
> > > > > > >
> > > > > > > If DDR have not configured, where DATA send to in device
> > > > > > > side by non-LL mode.
> > > > > > >
> > > > > >
> > > > > > The DDR base address in the VSEC capability is used for
> > > > > > driving the DMA transfers when used in the LL mode. The DDR is
> > > > > > configured and present all the time but the DMA PCIe driver
> > > > > > uses this DDR base address (physical address) to configure the LLP
> address.
> > > > > >
> > > > > > In the scenario, where this DDR base address in VSEC
> > > > > > capability is not configured then the current controller
> > > > > > cannot be used as the default mode supported is LL mode only.
> > > > > > In order to make the controller usable non-LL mode is being
> > > > > > added which just needs SAR, DAR, XFERLEN and control register
> > > > > > to initiate the transfer. So, the DDR is always present, but
> > > > > > the DMA PCIe driver need to know the DDR base physical address
> > > > > > to make the transfer. This is useful in scenarios where the
> > > > > > memory
> > > > > allocated for LL can be used for DMA transactions as well.
> > > > >
> > > > > Do you means use DMA transfer LL's context?
> > > > >
> > > >
> > > > Yes, the device side memory reserved for the link list to store
> > > > the descriptors, accessed by the host via BAR_2 in this driver code.
> > > >
> > > > > >
> > > > > > > >   be using the non-LL mode only. This, the default non-LL mode,
> > > > > > > >   is not applicable for Synopsys IP with the current code addition.
> > > > > > > >
> > > > > > > > - If the default mode is LL-mode, for both AMD (Xilinx) and
> Synosys,
> > > > > > > >   and if user wants to use non-LL mode then user can do so via
> > > > > > > >   configuring the peripheral_config param of dma_slave_config.
> > > > > > > >
> > > > > > > > Signed-off-by: Devendra K Verma <devendra.verma@amd.com>
> > > > > > > > ---
> > > > > > > > Changes in v8
> > > > > > > >   Cosmetic change related to comment and code.
> > > > > > > >
> > > > > > > > Changes in v7
> > > > > > > >   No change
> > > > > > > >
> > > > > > > > Changes in v6
> > > > > > > >   Gave definition to bits used for channel configuration.
> > > > > > > >   Removed the comment related to doorbell.
> > > > > > > >
> > > > > > > > Changes in v5
> > > > > > > >   Variable name 'nollp' changed to 'non_ll'.
> > > > > > > >   In the dw_edma_device_config() WARN_ON replaced with
> > > dev_err().
> > > > > > > >   Comments follow the 80-column guideline.
> > > > > > > >
> > > > > > > > Changes in v4
> > > > > > > >   No change
> > > > > > > >
> > > > > > > > Changes in v3
> > > > > > > >   No change
> > > > > > > >
> > > > > > > > Changes in v2
> > > > > > > >   Reverted the function return type to u64 for
> > > > > > > >   dw_edma_get_phys_addr().
> > > > > > > >
> > > > > > > > Changes in v1
> > > > > > > >   Changed the function return type for
> dw_edma_get_phys_addr().
> > > > > > > >   Corrected the typo raised in review.
> > > > > > > > ---
> > > > > > > >  drivers/dma/dw-edma/dw-edma-core.c    | 42
> > > > > +++++++++++++++++++++---
> > > > > > > >  drivers/dma/dw-edma/dw-edma-core.h    |  1 +
> > > > > > > >  drivers/dma/dw-edma/dw-edma-pcie.c    | 46
> > > ++++++++++++++++++--
> > > > > ------
> > > > > > > >  drivers/dma/dw-edma/dw-hdma-v0-core.c | 61
> > > > > > > > ++++++++++++++++++++++++++++++++++-
> > > > > > > >  drivers/dma/dw-edma/dw-hdma-v0-regs.h |  1 +
> > > > > > >
> > > > > > > edma-v0-core.c have not update, if don't support, at least
> > > > > > > need return failure at dw_edma_device_config() when backend is
> eDMA.
> > > > > > >
> > > > > > > >  include/linux/dma/edma.h              |  1 +
> > > > > > > >  6 files changed, 132 insertions(+), 20 deletions(-)
> > > > > > > >
> > > > > > > > diff --git a/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > > > b/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > > > index b43255f..d37112b 100644
> > > > > > > > --- a/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > > > +++ b/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > > > @@ -223,8 +223,32 @@ static int
> > > > > > > > dw_edma_device_config(struct
> > > > > > > dma_chan *dchan,
> > > > > > > >                                struct dma_slave_config *config)  {
> > > > > > > >       struct dw_edma_chan *chan =
> > > > > > > > dchan2dw_edma_chan(dchan);
> > > > > > > > +     int non_ll = 0;
> > > > > > > > +
> > > > > > > > +     if (config->peripheral_config &&
> > > > > > > > +         config->peripheral_size != sizeof(int)) {
> > > > > > > > +             dev_err(dchan->device->dev,
> > > > > > > > +                     "config param peripheral size mismatch\n");
> > > > > > > > +             return -EINVAL;
> > > > > > > > +     }
> > > > > > > >
> > > > > > > >       memcpy(&chan->config, config, sizeof(*config));
> > > > > > > > +
> > > > > > > > +     /*
> > > > > > > > +      * When there is no valid LLP base address available
> > > > > > > > + then the
> > > default
> > > > > > > > +      * DMA ops will use the non-LL mode.
> > > > > > > > +      *
> > > > > > > > +      * Cases where LL mode is enabled and client wants
> > > > > > > > + to use the
> > > non-LL
> > > > > > > > +      * mode then also client can do so via providing the
> > > peripheral_config
> > > > > > > > +      * param.
> > > > > > > > +      */
> > > > > > > > +     if (config->peripheral_config)
> > > > > > > > +             non_ll = *(int *)config->peripheral_config;
> > > > > > > > +
> > > > > > > > +     chan->non_ll = false;
> > > > > > > > +     if (chan->dw->chip->non_ll ||
> > > > > > > > + (!chan->dw->chip->non_ll &&
> > > non_ll))
> > > > > > > > +             chan->non_ll = true;
> > > > > > > > +
> > > > > > > >       chan->configured = true;
> > > > > > > >
> > > > > > > >       return 0;
> > > > > > > > @@ -353,7 +377,7 @@ static void
> > > > > > > > dw_edma_device_issue_pending(struct
> > > > > > > dma_chan *dchan)
> > > > > > > >       struct dw_edma_chan *chan = dchan2dw_edma_chan(xfer-
> > > >dchan);
> > > > > > > >       enum dma_transfer_direction dir = xfer->direction;
> > > > > > > >       struct scatterlist *sg = NULL;
> > > > > > > > -     struct dw_edma_chunk *chunk;
> > > > > > > > +     struct dw_edma_chunk *chunk = NULL;
> > > > > > > >       struct dw_edma_burst *burst;
> > > > > > > >       struct dw_edma_desc *desc;
> > > > > > > >       u64 src_addr, dst_addr; @@ -419,9 +443,11 @@ static
> > > > > > > > void dw_edma_device_issue_pending(struct
> > > > > > > dma_chan *dchan)
> > > > > > > >       if (unlikely(!desc))
> > > > > > > >               goto err_alloc;
> > > > > > > >
> > > > > > > > -     chunk = dw_edma_alloc_chunk(desc);
> > > > > > > > -     if (unlikely(!chunk))
> > > > > > > > -             goto err_alloc;
> > > > > > > > +     if (!chan->non_ll) {
> > > > > > > > +             chunk = dw_edma_alloc_chunk(desc);
> > > > > > > > +             if (unlikely(!chunk))
> > > > > > > > +                     goto err_alloc;
> > > > > > > > +     }
> > > > > > >
> > > > > > > non_ll is the same as ll_max = 1. (or 2, there are link back entry).
> > > > > > >
> > > > > > > If you set ll_max = 1, needn't change this code.
> > > > > > >
> > > > > >
> > > > > > The ll_max is defined for the session till the driver is
> > > > > > loaded in the
> > > kernel.
> > > > > > This code also enables the non-LL mode dynamically upon input
> > > > > > from the DMA client. In this scenario, touching ll_max would
> > > > > > not be a good idea as the ll_max controls the LL entries for
> > > > > > all the DMA channels not just for a single DMA transaction.
> > > > >
> > > > > You can use new variable, such as ll_avail.
> > > > >
> > > >
> > > > In order to separate out the execution paths a new meaningful
> > > > variable
> > > "non_ll"
> > > > is used. The variable "non_ll" alone is sufficient. Using another
> > > > variable along side "non_ll" for the similar purpose may not have
> > > > any
> > > added advantage.
> > >
> > > ll_avail can help debug/fine tune how much impact preformance by
> > > adjust ll length. And it make code logic clean and consistent. also
> > > ll_avail can help test corner case when ll item small. Normall case it is
> hard to reach ll_max.
> > >
> >
> > Thank you for your suggestion. The ll_max is max limit on the
> > descriptors that can be accommodated on the device side DDR. The ll_avail
> will always be less than ll_max.
> > The optimization being referred can be tried without even having to
> > declare the ll_avail cause the number of descriptors given can be
> > controlled by the DMA client based on the length argument to the
> dmaengine_prep_* APIs.
>
> I optimzied it to allow dynatmic appended dma descriptors.
>
> https://lore.kernel.org/dmaengine/20260109-edma_dymatic-v1-0-
> 9a98c9c98536@nxp.com/T/#t
>
> > So, the use of dynamic ll_avail is not necessarily required. Without
> > increasing the ll_max, ll_avail cannot be increased. In order to
> > increase ll_max one may need to alter size and recompile this driver.
> >
> > However, the requirement of ll_avail does not help for the supporting the
> non-LL mode.
> > For non-LL mode to use:
> > 1) Either LL mode shall be not available, as it can happen for the Xilinx IP.
> > 2) User provides the preference for non-LL mode.
> > For both above, the function calls are different which can be
> > differentiated by using the "non_ll" flag. So, even if I try to
> > accommodate ll_avail, the call for LL or non-LL would be ambiguous as in
> case of LL mode also we can have a single descriptor as similar to non-LL
> mode.
> > Please check the function dw_hdma_v0_core_start() in this review where
> > the decision is taken Based on non_ll flag.
>
> We can treat ll_avail == 1 as no_ll mode because needn't set extra LL in
> memory at all.
>

I analyzed the use of ll_avail but I think the use of this variable does not fit at
this location in the code for the following reasons:
1. The use of a new variable breaks the continuity for non-LL mode. The variable
    name non_ll is being used for driving the non-LL mode not only in this file but also
   in the files relevant to HDMA. This flag gives the clear indication of LL vs non-LL mode.
   In the function dw_hdma_v0_core_start(), non_ll decided the mode to choose.
2. The use of "ll_avail" is ambiguous for both the modes. First, it is associated with LL mode only.
     It will be used for optimizing / testing the Controller performance based on the
    number of descriptors available on the Device DDR side which is for LL mode. So when
    it is being used for LL mode then it is obvious that it excludes any use for non-LL mode.
    In the API dw_edma_device_transfer(), the ll_avail will be used for creating the bursts.
    If ll_avail = 1 in the above API, then it is ambiguous whether it is creating the burst for
     LL or non-LL mode. In the above API, the non_ll is sufficient to have the LL mode
     and non-LL burst allocation related piece of code.

I think ll_avail, if used for trying out to optimize / debug the settings related to number of
descriptors for LL mode then it should be part of the separate discussion / update not related to
non-LL.

> >
> > > >
> > > > > >
> > > > > > > >
> > > ...
> > > > > > > > +
> > > > > > > > + ll_block->bar);
> > > > > > >
> > > > > > > This change need do prepare patch, which only change
> > > > > > > pci_bus_address() to dw_edma_get_phys_addr().
> > > > > > >
> > > > > >
> > > > > > This is not clear.
> > > > >
> > > > > why not. only trivial add helper patch, which help reviewer
> > > > >
> > > >
> > > > I was not clear on the question you asked.
> > > > It does not look justified when a patch is raised alone just to
> > > > replace this
> > > function.
> > > > The function change is required cause the same code *can* support
> > > > different IPs from different vendors. And, with this single change
> > > > alone in the code the support for another IP is added. That's why
> > > > it is easier to get the reason for the change in the function name and
> syntax.
> > >
> > > Add replace pci_bus_address() with dw_edma_get_phys_addr() to make
> > > review easily and get ack for such replacement patches.
> > >
> > > two patches
> > > patch1, just replace exist pci_bus_address() with
> > > dw_edma_get_phys_addr() patch2, add new logic in
> dw_edma_get_phys_addr() to support new vendor.
> > >
> >
> > I understand your concern about making the review easier. However,
> > given that we've been iterating on this patch series since September
> > and are now at v9, I believe the current approach is justified. The
> > function renames from
> > pci_bus_address() to dw_edma_get_phys_addr() is directly tied to the
> > non-LL mode functionality being added - it's needed because the same
> > code now supports different IPs from different vendors.
> >
> > Splitting this into a separate preparatory patch at this stage would
> > further delay the review process. The change is kind of
> > straightforward and the context is clear within the current patch. I request
> you to review this patch to avoid additional review cycles.
> >
> > This also increases the work related to testing and maintaining multiple
> patches.
> > I have commitment for delivery of this, and I can see adding one more
> > series definitely add 3-4 months of review cycle from here. Please
> > excuse me but this code has already
>
> Thank you for your persevere.
>

Thank you for your support.

> > been reviewed extensively by other reviewers and almost by you as
> > well. You can check the detailed discussion wrt this function at the following
> link:
> >
> https://lore.kernel.org/all/SA1PR12MB8120341DFFD56D90EAD70EDE9514A@
> SA1
> > PR12MB8120.namprd12.prod.outlook.com/
> >
>
> But still not got reviewed by tags. The recently,  Manivannan Sadhasivam ,
> Niklas Cassel and me active worked on this driver. You'd better to get their
> feedback. Bjorn as pci maintainer to provide generally feedback.
>

Hi Manivannan Sadhasivam, Vinod Koul and Bjorn Helgaas
Could you please provide your feedback on the patch?
You have reviewed these patches extensively on the previous versions of the same series.

Regards,
Devendra

>
> > > >
> > > > > >
> > > > > > > >               ll_region->paddr += ll_block->off;
> > > > > > > >               ll_region->sz = ll_block->sz;
> > > > > > > >
> > > ...
> > > > > > > >
> > > > > > > > +static void dw_hdma_v0_core_non_ll_start(struct
> > > > > > > > +dw_edma_chunk
> > > > > > > *chunk)
> > > > > > > > +{
> > > > > > > > +     struct dw_edma_chan *chan = chunk->chan;
> > > > > > > > +     struct dw_edma *dw = chan->dw;
> > > > > > > > +     struct dw_edma_burst *child;
> > > > > > > > +     u32 val;
> > > > > > > > +
> > > > > > > > +     list_for_each_entry(child, &chunk->burst->list,
> > > > > > > > + list) {
> > > > > > >
> > > > > > > why need iterated list, it doesn't support ll. Need wait for
> > > > > > > irq to start next
> > > > > one.
> > > > > > >
> > > > > > > Frank
> > > > > >
> > > > > > Yes, this is true. The format is kept similar to LL mode.
> > > > >
> > > > > Just fill one. list_for_each_entry() cause confuse.
> > > > >
> > > > > Frank
> > > >
> > > > I see, we can use list_first_entry_or_null() which is dependent on
> > > > giving the type of pointer, compared to this list_for_each_entry()
> > > > looks neat and agnostic to the pointer type being used. Though, it
> > > > can be
> > > explored further.
> > > > Also, when the chunk is allocated, the comment clearly spells out
> > > > how the allocation would be for the non LL mode so it is evident
> > > > that each chunk would have single entry and with that
> > > > understanding it is clear that loop will also be used in a similar
> > > > manner, to retrieve a single entry. It is a similar use case as of
> > > > "do {}while (0)" albeit needs a context to
> > > understand it.
> > >
> > > I don't think so. list_for_each_entry() is miss leading to reader
> > > think it is not only to one item in burst list, and use polling
> > > method to to finish many burst transfer.
> > >
> > > list_for_each_entry() {
> > >         ...
> > >         readl_timeout()
> > > }
> > >
> > > Generally, EDMA is very quick, polling is much quicker than irq if data is
> small.
> > >
> > > Frank
> > >
> >
> > The polling is not required. The single burst will raise the interrupt
> > and from the interrupt context another chunk will be scheduled. This
> > cycle repeats till all the chunks with single burst are exhausted.
> >
> > The following comment made in function dw_edma_device_transfer() in
> > the same patch makes it amply clear that only a single burst would be
> handled for the non-LL mode.
> > +       /*
> > +        * For non-LL mode, only a single burst can be handled
> > +        * in a single chunk unlike LL mode where multiple bursts
> > +        * can be configured in a single chunk.
> > +        */
> >
> > Looking at the way bursts are appended to chunks and chunks in
> > dw_edma_device_transfer() are scheduled for non-LL mode then it is clear
> what non-LL mode would handle in terms of bursts.
> > I just kept the format to match it with the LL mode format otherwise
> > there is no need of this comment and we can follow the syntax for a single
> entry alone.
> > Please share your suggestion if these descriptions fail to provide the clear
> context and intent.
>
> Avoid use list_for_each_entry() here to prevent miss-leading.
>
> Frank
>

Sure, thanks, I will push this change in next version.

> >
> > > >
> > > > > >
> > > > > > >
> > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, ch_en,
> > > > > > > > + HDMA_V0_CH_EN);
> > > > > > > > +
> > > > > > > > +             /* Source address */
> > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, sar.lsb,
> > > > > > > > +                       lower_32_bits(child->sar));
> > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, sar.msb,
> > > > > > > > +                       upper_32_bits(child->sar));
> > > > > > > > +
> > > > > > > > +             /* Destination address */
> > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, dar.lsb,
> > > > > > > > +                       lower_32_bits(child->dar));
> > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, dar.msb,
> > > > > > > > +                       upper_32_bits(child->dar));
> > > > > > > > +
> > > > > > > > +             /* Transfer size */
> > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id,
> > > > > > > > + transfer_size,
> > > > > > > > + child->sz);
> > > > > > > > +
> > > > > > > > +             /* Interrupt setup */
> > > > > > > > +             val = GET_CH_32(dw, chan->dir, chan->id, int_setup) |
> > > > > > > > +                             HDMA_V0_STOP_INT_MASK |
> > > > > > > > +                             HDMA_V0_ABORT_INT_MASK |
> > > > > > > > +                             HDMA_V0_LOCAL_STOP_INT_EN |
> > > > > > > > +                             HDMA_V0_LOCAL_ABORT_INT_EN;
> > > > > > > > +
> > > > > > > > +             if (!(dw->chip->flags & DW_EDMA_CHIP_LOCAL)) {
> > > > > > > > +                     val |= HDMA_V0_REMOTE_STOP_INT_EN |
> > > > > > > > +                            HDMA_V0_REMOTE_ABORT_INT_EN;
> > > > > > > > +             }
> > > > > > > > +
> > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id,
> > > > > > > > + int_setup, val);
> > > > > > > > +
> > > > > > > > +             /* Channel control setup */
> > > > > > > > +             val = GET_CH_32(dw, chan->dir, chan->id, control1);
> > > > > > > > +             val &= ~HDMA_V0_LINKLIST_EN;
> > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, control1,
> > > > > > > > + val);
> > > > > > > > +
> > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, doorbell,
> > > > > > > > +                       HDMA_V0_DOORBELL_START);
> > > > > > > > +     }
> > > > > > > > +}
> > > > > > > > +
> > > > > > > > +static void dw_hdma_v0_core_start(struct dw_edma_chunk
> > > > > > > > +*chunk, bool
> > > > > > > > +first) {
> > > > > > > > +     struct dw_edma_chan *chan = chunk->chan;
> > > > > > > > +
> > > > > > > > +     if (chan->non_ll)
> > > > > > > > +             dw_hdma_v0_core_non_ll_start(chunk);
> > > > > > > > +     else
> > > > > > > > +             dw_hdma_v0_core_ll_start(chunk, first); }
> > > > > > > > +
> > > > > > > >  static void dw_hdma_v0_core_ch_config(struct dw_edma_chan
> > > > > > > > *chan)
> > > > > {
> > > > > > > >       struct dw_edma *dw = chan->dw; diff --git
> > > > > > > > a/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > > > b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > > > index eab5fd7..7759ba9 100644
> > > > > > > > --- a/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > > > +++ b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > > > @@ -12,6 +12,7 @@
> > > > > > > >  #include <linux/dmaengine.h>
> > > > > > > >
> > > > > > > >  #define HDMA_V0_MAX_NR_CH                    8
> > > > > > > > +#define HDMA_V0_CH_EN                                BIT(0)
> > > > > > > >  #define HDMA_V0_LOCAL_ABORT_INT_EN           BIT(6)
> > > > > > > >  #define HDMA_V0_REMOTE_ABORT_INT_EN          BIT(5)
> > > > > > > >  #define HDMA_V0_LOCAL_STOP_INT_EN            BIT(4)
> > > > > > > > diff --git a/include/linux/dma/edma.h
> > > > > > > > b/include/linux/dma/edma.h index 3080747..78ce31b 100644
> > > > > > > > --- a/include/linux/dma/edma.h
> > > > > > > > +++ b/include/linux/dma/edma.h
> > > > > > > > @@ -99,6 +99,7 @@ struct dw_edma_chip {
> > > > > > > >       enum dw_edma_map_format mf;
> > > > > > > >
> > > > > > > >       struct dw_edma          *dw;
> > > > > > > > +     bool                    non_ll;
> > > > > > > >  };
> > > > > > > >
> > > > > > > >  /* Export to the platform drivers */
> > > > > > > > --
> > > > > > > > 1.8.3.1
> > > > > > > >

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

* Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
  2026-01-28 12:59                 ` Verma, Devendra
@ 2026-01-28 15:07                   ` Frank Li
  2026-01-29 11:55                     ` Verma, Devendra
  0 siblings, 1 reply; 24+ messages in thread
From: Frank Li @ 2026-01-28 15:07 UTC (permalink / raw)
  To: Verma, Devendra
  Cc: bhelgaas@google.com, mani@kernel.org, vkoul@kernel.org,
	dmaengine@vger.kernel.org, linux-pci@vger.kernel.org,
	linux-kernel@vger.kernel.org, Simek, Michal

On Wed, Jan 28, 2026 at 12:59:43PM +0000, Verma, Devendra wrote:
> [AMD Official Use Only - AMD Internal Distribution Only]
>
> > -----Original Message-----
> > From: Frank Li <Frank.li@nxp.com>
> > Sent: Tuesday, January 27, 2026 12:30 AM
> > To: Verma, Devendra <Devendra.Verma@amd.com>
> > Cc: bhelgaas@google.com; mani@kernel.org; vkoul@kernel.org;
> > dmaengine@vger.kernel.org; linux-pci@vger.kernel.org; linux-
> > kernel@vger.kernel.org; Simek, Michal <michal.simek@amd.com>
> > Subject: Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
>
> --[ Snipped some headers to reduce the size of this mail ]--
>
> > > > > > > > > AMD MDB IP supports Linked List (LL) mode as well as non-LL
> > mode.
> > > > > > > > > The current code does not have the mechanisms to enable
> > > > > > > > > the DMA transactions using the non-LL mode. The following
> > > > > > > > > two cases are added with this patch:
> > > > > > > > > - For the AMD (Xilinx) only, when a valid physical base address of
> > > > > > > > >   the device side DDR is not configured, then the IP can still be
> > > > > > > > >   used in non-LL mode. For all the channels DMA
> > > > > > > > > transactions will
> > > > > > > >
> > > > > > > > If DDR have not configured, where DATA send to in device
> > > > > > > > side by non-LL mode.
> > > > > > > >
> > > > > > >
> > > > > > > The DDR base address in the VSEC capability is used for
> > > > > > > driving the DMA transfers when used in the LL mode. The DDR is
> > > > > > > configured and present all the time but the DMA PCIe driver
> > > > > > > uses this DDR base address (physical address) to configure the LLP
> > address.
> > > > > > >
> > > > > > > In the scenario, where this DDR base address in VSEC
> > > > > > > capability is not configured then the current controller
> > > > > > > cannot be used as the default mode supported is LL mode only.
> > > > > > > In order to make the controller usable non-LL mode is being
> > > > > > > added which just needs SAR, DAR, XFERLEN and control register
> > > > > > > to initiate the transfer. So, the DDR is always present, but
> > > > > > > the DMA PCIe driver need to know the DDR base physical address
> > > > > > > to make the transfer. This is useful in scenarios where the
> > > > > > > memory
> > > > > > allocated for LL can be used for DMA transactions as well.
> > > > > >
> > > > > > Do you means use DMA transfer LL's context?
> > > > > >
> > > > >
> > > > > Yes, the device side memory reserved for the link list to store
> > > > > the descriptors, accessed by the host via BAR_2 in this driver code.
> > > > >
> > > > > > >
> > > > > > > > >   be using the non-LL mode only. This, the default non-LL mode,
> > > > > > > > >   is not applicable for Synopsys IP with the current code addition.
> > > > > > > > >
> > > > > > > > > - If the default mode is LL-mode, for both AMD (Xilinx) and
> > Synosys,
> > > > > > > > >   and if user wants to use non-LL mode then user can do so via
> > > > > > > > >   configuring the peripheral_config param of dma_slave_config.
> > > > > > > > >
> > > > > > > > > Signed-off-by: Devendra K Verma <devendra.verma@amd.com>
> > > > > > > > > ---
> > > > > > > > > Changes in v8
> > > > > > > > >   Cosmetic change related to comment and code.
> > > > > > > > >
> > > > > > > > > Changes in v7
> > > > > > > > >   No change
> > > > > > > > >
> > > > > > > > > Changes in v6
> > > > > > > > >   Gave definition to bits used for channel configuration.
> > > > > > > > >   Removed the comment related to doorbell.
> > > > > > > > >
> > > > > > > > > Changes in v5
> > > > > > > > >   Variable name 'nollp' changed to 'non_ll'.
> > > > > > > > >   In the dw_edma_device_config() WARN_ON replaced with
> > > > dev_err().
> > > > > > > > >   Comments follow the 80-column guideline.
> > > > > > > > >
> > > > > > > > > Changes in v4
> > > > > > > > >   No change
> > > > > > > > >
> > > > > > > > > Changes in v3
> > > > > > > > >   No change
> > > > > > > > >
> > > > > > > > > Changes in v2
> > > > > > > > >   Reverted the function return type to u64 for
> > > > > > > > >   dw_edma_get_phys_addr().
> > > > > > > > >
> > > > > > > > > Changes in v1
> > > > > > > > >   Changed the function return type for
> > dw_edma_get_phys_addr().
> > > > > > > > >   Corrected the typo raised in review.
> > > > > > > > > ---
> > > > > > > > >  drivers/dma/dw-edma/dw-edma-core.c    | 42
> > > > > > +++++++++++++++++++++---
> > > > > > > > >  drivers/dma/dw-edma/dw-edma-core.h    |  1 +
> > > > > > > > >  drivers/dma/dw-edma/dw-edma-pcie.c    | 46
> > > > ++++++++++++++++++--
> > > > > > ------
> > > > > > > > >  drivers/dma/dw-edma/dw-hdma-v0-core.c | 61
> > > > > > > > > ++++++++++++++++++++++++++++++++++-
> > > > > > > > >  drivers/dma/dw-edma/dw-hdma-v0-regs.h |  1 +
> > > > > > > >
> > > > > > > > edma-v0-core.c have not update, if don't support, at least
> > > > > > > > need return failure at dw_edma_device_config() when backend is
> > eDMA.
> > > > > > > >
> > > > > > > > >  include/linux/dma/edma.h              |  1 +
> > > > > > > > >  6 files changed, 132 insertions(+), 20 deletions(-)
> > > > > > > > >
> > > > > > > > > diff --git a/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > > > > b/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > > > > index b43255f..d37112b 100644
> > > > > > > > > --- a/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > > > > +++ b/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > > > > @@ -223,8 +223,32 @@ static int
> > > > > > > > > dw_edma_device_config(struct
> > > > > > > > dma_chan *dchan,
> > > > > > > > >                                struct dma_slave_config *config)  {
> > > > > > > > >       struct dw_edma_chan *chan =
> > > > > > > > > dchan2dw_edma_chan(dchan);
> > > > > > > > > +     int non_ll = 0;
> > > > > > > > > +
> > > > > > > > > +     if (config->peripheral_config &&
> > > > > > > > > +         config->peripheral_size != sizeof(int)) {
> > > > > > > > > +             dev_err(dchan->device->dev,
> > > > > > > > > +                     "config param peripheral size mismatch\n");
> > > > > > > > > +             return -EINVAL;
> > > > > > > > > +     }
> > > > > > > > >
> > > > > > > > >       memcpy(&chan->config, config, sizeof(*config));
> > > > > > > > > +
> > > > > > > > > +     /*
> > > > > > > > > +      * When there is no valid LLP base address available
> > > > > > > > > + then the
> > > > default
> > > > > > > > > +      * DMA ops will use the non-LL mode.
> > > > > > > > > +      *
> > > > > > > > > +      * Cases where LL mode is enabled and client wants
> > > > > > > > > + to use the
> > > > non-LL
> > > > > > > > > +      * mode then also client can do so via providing the
> > > > peripheral_config
> > > > > > > > > +      * param.
> > > > > > > > > +      */
> > > > > > > > > +     if (config->peripheral_config)
> > > > > > > > > +             non_ll = *(int *)config->peripheral_config;
> > > > > > > > > +
> > > > > > > > > +     chan->non_ll = false;
> > > > > > > > > +     if (chan->dw->chip->non_ll ||
> > > > > > > > > + (!chan->dw->chip->non_ll &&
> > > > non_ll))
> > > > > > > > > +             chan->non_ll = true;
> > > > > > > > > +
> > > > > > > > >       chan->configured = true;
> > > > > > > > >
> > > > > > > > >       return 0;
> > > > > > > > > @@ -353,7 +377,7 @@ static void
> > > > > > > > > dw_edma_device_issue_pending(struct
> > > > > > > > dma_chan *dchan)
> > > > > > > > >       struct dw_edma_chan *chan = dchan2dw_edma_chan(xfer-
> > > > >dchan);
> > > > > > > > >       enum dma_transfer_direction dir = xfer->direction;
> > > > > > > > >       struct scatterlist *sg = NULL;
> > > > > > > > > -     struct dw_edma_chunk *chunk;
> > > > > > > > > +     struct dw_edma_chunk *chunk = NULL;
> > > > > > > > >       struct dw_edma_burst *burst;
> > > > > > > > >       struct dw_edma_desc *desc;
> > > > > > > > >       u64 src_addr, dst_addr; @@ -419,9 +443,11 @@ static
> > > > > > > > > void dw_edma_device_issue_pending(struct
> > > > > > > > dma_chan *dchan)
> > > > > > > > >       if (unlikely(!desc))
> > > > > > > > >               goto err_alloc;
> > > > > > > > >
> > > > > > > > > -     chunk = dw_edma_alloc_chunk(desc);
> > > > > > > > > -     if (unlikely(!chunk))
> > > > > > > > > -             goto err_alloc;
> > > > > > > > > +     if (!chan->non_ll) {
> > > > > > > > > +             chunk = dw_edma_alloc_chunk(desc);
> > > > > > > > > +             if (unlikely(!chunk))
> > > > > > > > > +                     goto err_alloc;
> > > > > > > > > +     }
> > > > > > > >
> > > > > > > > non_ll is the same as ll_max = 1. (or 2, there are link back entry).
> > > > > > > >
> > > > > > > > If you set ll_max = 1, needn't change this code.
> > > > > > > >
> > > > > > >
> > > > > > > The ll_max is defined for the session till the driver is
> > > > > > > loaded in the
> > > > kernel.
> > > > > > > This code also enables the non-LL mode dynamically upon input
> > > > > > > from the DMA client. In this scenario, touching ll_max would
> > > > > > > not be a good idea as the ll_max controls the LL entries for
> > > > > > > all the DMA channels not just for a single DMA transaction.
> > > > > >
> > > > > > You can use new variable, such as ll_avail.
> > > > > >
> > > > >
> > > > > In order to separate out the execution paths a new meaningful
> > > > > variable
> > > > "non_ll"
> > > > > is used. The variable "non_ll" alone is sufficient. Using another
> > > > > variable along side "non_ll" for the similar purpose may not have
> > > > > any
> > > > added advantage.
> > > >
> > > > ll_avail can help debug/fine tune how much impact preformance by
> > > > adjust ll length. And it make code logic clean and consistent. also
> > > > ll_avail can help test corner case when ll item small. Normall case it is
> > hard to reach ll_max.
> > > >
> > >
> > > Thank you for your suggestion. The ll_max is max limit on the
> > > descriptors that can be accommodated on the device side DDR. The ll_avail
> > will always be less than ll_max.
> > > The optimization being referred can be tried without even having to
> > > declare the ll_avail cause the number of descriptors given can be
> > > controlled by the DMA client based on the length argument to the
> > dmaengine_prep_* APIs.
> >
> > I optimzied it to allow dynatmic appended dma descriptors.
> >
> > https://lore.kernel.org/dmaengine/20260109-edma_dymatic-v1-0-
> > 9a98c9c98536@nxp.com/T/#t
> >
> > > So, the use of dynamic ll_avail is not necessarily required. Without
> > > increasing the ll_max, ll_avail cannot be increased. In order to
> > > increase ll_max one may need to alter size and recompile this driver.
> > >
> > > However, the requirement of ll_avail does not help for the supporting the
> > non-LL mode.
> > > For non-LL mode to use:
> > > 1) Either LL mode shall be not available, as it can happen for the Xilinx IP.
> > > 2) User provides the preference for non-LL mode.
> > > For both above, the function calls are different which can be
> > > differentiated by using the "non_ll" flag. So, even if I try to
> > > accommodate ll_avail, the call for LL or non-LL would be ambiguous as in
> > case of LL mode also we can have a single descriptor as similar to non-LL
> > mode.
> > > Please check the function dw_hdma_v0_core_start() in this review where
> > > the decision is taken Based on non_ll flag.
> >
> > We can treat ll_avail == 1 as no_ll mode because needn't set extra LL in
> > memory at all.
> >
>
> I analyzed the use of ll_avail but I think the use of this variable does not fit at
> this location in the code for the following reasons:
> 1. The use of a new variable breaks the continuity for non-LL mode. The variable
>     name non_ll is being used for driving the non-LL mode not only in this file but also
>    in the files relevant to HDMA. This flag gives the clear indication of LL vs non-LL mode.
>    In the function dw_hdma_v0_core_start(), non_ll decided the mode to choose.
> 2. The use of "ll_avail" is ambiguous for both the modes. First, it is associated with LL mode only.
>      It will be used for optimizing / testing the Controller performance based on the
>     number of descriptors available on the Device DDR side which is for LL mode. So when
>     it is being used for LL mode then it is obvious that it excludes any use for non-LL mode.
>     In the API dw_edma_device_transfer(), the ll_avail will be used for creating the bursts.
>     If ll_avail = 1 in the above API, then it is ambiguous whether it is creating the burst for
>      LL or non-LL mode. In the above API, the non_ll is sufficient to have the LL mode
>      and non-LL burst allocation related piece of code.

If really like non-ll, you'd better set ll_avail = 1 in prepare code.
Normal case ll_avail should be ll_max. It will reduce if-else branch in
prep dma burst code.

+		/*
+		 * For non-LL mode, only a single burst can be handled
+		 * in a single chunk unlike LL mode where multiple bursts
+		 * can be configured in a single chunk.
+		 */

It is actually wrong, current software should handle that. If there are
multiple bursts, only HEAD of bursts trigger DMA, in irq handle, it will
auto move to next burst. (only irq latency is bigger compared to LL,
software's resule is the same).

The code logic is totally equal ll_max = 1, except write differece
registers.

And anthor important is that,

in dw_edma_device_config() should return if backend is not HDMA.

Frank

>
> I think ll_avail, if used for trying out to optimize / debug the settings related to number of
> descriptors for LL mode then it should be part of the separate discussion / update not related to
> non-LL.
>
> > >
> > > > >
> > > > > > >
> > > > > > > > >
> > > > ...
> > > > > > > > > +
> > > > > > > > > + ll_block->bar);
> > > > > > > >
> > > > > > > > This change need do prepare patch, which only change
> > > > > > > > pci_bus_address() to dw_edma_get_phys_addr().
> > > > > > > >
> > > > > > >
> > > > > > > This is not clear.
> > > > > >
> > > > > > why not. only trivial add helper patch, which help reviewer
> > > > > >
> > > > >
> > > > > I was not clear on the question you asked.
> > > > > It does not look justified when a patch is raised alone just to
> > > > > replace this
> > > > function.
> > > > > The function change is required cause the same code *can* support
> > > > > different IPs from different vendors. And, with this single change
> > > > > alone in the code the support for another IP is added. That's why
> > > > > it is easier to get the reason for the change in the function name and
> > syntax.
> > > >
> > > > Add replace pci_bus_address() with dw_edma_get_phys_addr() to make
> > > > review easily and get ack for such replacement patches.
> > > >
> > > > two patches
> > > > patch1, just replace exist pci_bus_address() with
> > > > dw_edma_get_phys_addr() patch2, add new logic in
> > dw_edma_get_phys_addr() to support new vendor.
> > > >
> > >
> > > I understand your concern about making the review easier. However,
> > > given that we've been iterating on this patch series since September
> > > and are now at v9, I believe the current approach is justified. The
> > > function renames from
> > > pci_bus_address() to dw_edma_get_phys_addr() is directly tied to the
> > > non-LL mode functionality being added - it's needed because the same
> > > code now supports different IPs from different vendors.
> > >
> > > Splitting this into a separate preparatory patch at this stage would
> > > further delay the review process. The change is kind of
> > > straightforward and the context is clear within the current patch. I request
> > you to review this patch to avoid additional review cycles.
> > >
> > > This also increases the work related to testing and maintaining multiple
> > patches.
> > > I have commitment for delivery of this, and I can see adding one more
> > > series definitely add 3-4 months of review cycle from here. Please
> > > excuse me but this code has already
> >
> > Thank you for your persevere.
> >
>
> Thank you for your support.
>
> > > been reviewed extensively by other reviewers and almost by you as
> > > well. You can check the detailed discussion wrt this function at the following
> > link:
> > >
> > https://lore.kernel.org/all/SA1PR12MB8120341DFFD56D90EAD70EDE9514A@
> > SA1
> > > PR12MB8120.namprd12.prod.outlook.com/
> > >
> >
> > But still not got reviewed by tags. The recently,  Manivannan Sadhasivam ,
> > Niklas Cassel and me active worked on this driver. You'd better to get their
> > feedback. Bjorn as pci maintainer to provide generally feedback.
> >
>
> Hi Manivannan Sadhasivam, Vinod Koul and Bjorn Helgaas
> Could you please provide your feedback on the patch?
> You have reviewed these patches extensively on the previous versions of the same series.
>
> Regards,
> Devendra
>
> >
> > > > >
> > > > > > >
> > > > > > > > >               ll_region->paddr += ll_block->off;
> > > > > > > > >               ll_region->sz = ll_block->sz;
> > > > > > > > >
> > > > ...
> > > > > > > > >
> > > > > > > > > +static void dw_hdma_v0_core_non_ll_start(struct
> > > > > > > > > +dw_edma_chunk
> > > > > > > > *chunk)
> > > > > > > > > +{
> > > > > > > > > +     struct dw_edma_chan *chan = chunk->chan;
> > > > > > > > > +     struct dw_edma *dw = chan->dw;
> > > > > > > > > +     struct dw_edma_burst *child;
> > > > > > > > > +     u32 val;
> > > > > > > > > +
> > > > > > > > > +     list_for_each_entry(child, &chunk->burst->list,
> > > > > > > > > + list) {
> > > > > > > >
> > > > > > > > why need iterated list, it doesn't support ll. Need wait for
> > > > > > > > irq to start next
> > > > > > one.
> > > > > > > >
> > > > > > > > Frank
> > > > > > >
> > > > > > > Yes, this is true. The format is kept similar to LL mode.
> > > > > >
> > > > > > Just fill one. list_for_each_entry() cause confuse.
> > > > > >
> > > > > > Frank
> > > > >
> > > > > I see, we can use list_first_entry_or_null() which is dependent on
> > > > > giving the type of pointer, compared to this list_for_each_entry()
> > > > > looks neat and agnostic to the pointer type being used. Though, it
> > > > > can be
> > > > explored further.
> > > > > Also, when the chunk is allocated, the comment clearly spells out
> > > > > how the allocation would be for the non LL mode so it is evident
> > > > > that each chunk would have single entry and with that
> > > > > understanding it is clear that loop will also be used in a similar
> > > > > manner, to retrieve a single entry. It is a similar use case as of
> > > > > "do {}while (0)" albeit needs a context to
> > > > understand it.
> > > >
> > > > I don't think so. list_for_each_entry() is miss leading to reader
> > > > think it is not only to one item in burst list, and use polling
> > > > method to to finish many burst transfer.
> > > >
> > > > list_for_each_entry() {
> > > >         ...
> > > >         readl_timeout()
> > > > }
> > > >
> > > > Generally, EDMA is very quick, polling is much quicker than irq if data is
> > small.
> > > >
> > > > Frank
> > > >
> > >
> > > The polling is not required. The single burst will raise the interrupt
> > > and from the interrupt context another chunk will be scheduled. This
> > > cycle repeats till all the chunks with single burst are exhausted.
> > >
> > > The following comment made in function dw_edma_device_transfer() in
> > > the same patch makes it amply clear that only a single burst would be
> > handled for the non-LL mode.
> > > +       /*
> > > +        * For non-LL mode, only a single burst can be handled
> > > +        * in a single chunk unlike LL mode where multiple bursts
> > > +        * can be configured in a single chunk.
> > > +        */
> > >
> > > Looking at the way bursts are appended to chunks and chunks in
> > > dw_edma_device_transfer() are scheduled for non-LL mode then it is clear
> > what non-LL mode would handle in terms of bursts.
> > > I just kept the format to match it with the LL mode format otherwise
> > > there is no need of this comment and we can follow the syntax for a single
> > entry alone.
> > > Please share your suggestion if these descriptions fail to provide the clear
> > context and intent.
> >
> > Avoid use list_for_each_entry() here to prevent miss-leading.
> >
> > Frank
> >
>
> Sure, thanks, I will push this change in next version.
>
> > >
> > > > >
> > > > > > >
> > > > > > > >
> > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, ch_en,
> > > > > > > > > + HDMA_V0_CH_EN);
> > > > > > > > > +
> > > > > > > > > +             /* Source address */
> > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, sar.lsb,
> > > > > > > > > +                       lower_32_bits(child->sar));
> > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, sar.msb,
> > > > > > > > > +                       upper_32_bits(child->sar));
> > > > > > > > > +
> > > > > > > > > +             /* Destination address */
> > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, dar.lsb,
> > > > > > > > > +                       lower_32_bits(child->dar));
> > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, dar.msb,
> > > > > > > > > +                       upper_32_bits(child->dar));
> > > > > > > > > +
> > > > > > > > > +             /* Transfer size */
> > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id,
> > > > > > > > > + transfer_size,
> > > > > > > > > + child->sz);
> > > > > > > > > +
> > > > > > > > > +             /* Interrupt setup */
> > > > > > > > > +             val = GET_CH_32(dw, chan->dir, chan->id, int_setup) |
> > > > > > > > > +                             HDMA_V0_STOP_INT_MASK |
> > > > > > > > > +                             HDMA_V0_ABORT_INT_MASK |
> > > > > > > > > +                             HDMA_V0_LOCAL_STOP_INT_EN |
> > > > > > > > > +                             HDMA_V0_LOCAL_ABORT_INT_EN;
> > > > > > > > > +
> > > > > > > > > +             if (!(dw->chip->flags & DW_EDMA_CHIP_LOCAL)) {
> > > > > > > > > +                     val |= HDMA_V0_REMOTE_STOP_INT_EN |
> > > > > > > > > +                            HDMA_V0_REMOTE_ABORT_INT_EN;
> > > > > > > > > +             }
> > > > > > > > > +
> > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id,
> > > > > > > > > + int_setup, val);
> > > > > > > > > +
> > > > > > > > > +             /* Channel control setup */
> > > > > > > > > +             val = GET_CH_32(dw, chan->dir, chan->id, control1);
> > > > > > > > > +             val &= ~HDMA_V0_LINKLIST_EN;
> > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, control1,
> > > > > > > > > + val);
> > > > > > > > > +
> > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, doorbell,
> > > > > > > > > +                       HDMA_V0_DOORBELL_START);
> > > > > > > > > +     }
> > > > > > > > > +}
> > > > > > > > > +
> > > > > > > > > +static void dw_hdma_v0_core_start(struct dw_edma_chunk
> > > > > > > > > +*chunk, bool
> > > > > > > > > +first) {
> > > > > > > > > +     struct dw_edma_chan *chan = chunk->chan;
> > > > > > > > > +
> > > > > > > > > +     if (chan->non_ll)
> > > > > > > > > +             dw_hdma_v0_core_non_ll_start(chunk);
> > > > > > > > > +     else
> > > > > > > > > +             dw_hdma_v0_core_ll_start(chunk, first); }
> > > > > > > > > +
> > > > > > > > >  static void dw_hdma_v0_core_ch_config(struct dw_edma_chan
> > > > > > > > > *chan)
> > > > > > {
> > > > > > > > >       struct dw_edma *dw = chan->dw; diff --git
> > > > > > > > > a/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > > > > b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > > > > index eab5fd7..7759ba9 100644
> > > > > > > > > --- a/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > > > > +++ b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > > > > @@ -12,6 +12,7 @@
> > > > > > > > >  #include <linux/dmaengine.h>
> > > > > > > > >
> > > > > > > > >  #define HDMA_V0_MAX_NR_CH                    8
> > > > > > > > > +#define HDMA_V0_CH_EN                                BIT(0)
> > > > > > > > >  #define HDMA_V0_LOCAL_ABORT_INT_EN           BIT(6)
> > > > > > > > >  #define HDMA_V0_REMOTE_ABORT_INT_EN          BIT(5)
> > > > > > > > >  #define HDMA_V0_LOCAL_STOP_INT_EN            BIT(4)
> > > > > > > > > diff --git a/include/linux/dma/edma.h
> > > > > > > > > b/include/linux/dma/edma.h index 3080747..78ce31b 100644
> > > > > > > > > --- a/include/linux/dma/edma.h
> > > > > > > > > +++ b/include/linux/dma/edma.h
> > > > > > > > > @@ -99,6 +99,7 @@ struct dw_edma_chip {
> > > > > > > > >       enum dw_edma_map_format mf;
> > > > > > > > >
> > > > > > > > >       struct dw_edma          *dw;
> > > > > > > > > +     bool                    non_ll;
> > > > > > > > >  };
> > > > > > > > >
> > > > > > > > >  /* Export to the platform drivers */
> > > > > > > > > --
> > > > > > > > > 1.8.3.1
> > > > > > > > >

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

* RE: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
  2026-01-28 15:07                   ` Frank Li
@ 2026-01-29 11:55                     ` Verma, Devendra
  2026-02-03 12:03                       ` Verma, Devendra
  0 siblings, 1 reply; 24+ messages in thread
From: Verma, Devendra @ 2026-01-29 11:55 UTC (permalink / raw)
  To: Frank Li
  Cc: bhelgaas@google.com, mani@kernel.org, vkoul@kernel.org,
	dmaengine@vger.kernel.org, linux-pci@vger.kernel.org,
	linux-kernel@vger.kernel.org, Simek, Michal, Verma, Devendra

[AMD Official Use Only - AMD Internal Distribution Only]

> -----Original Message-----
> From: Frank Li <Frank.li@nxp.com>
> Sent: Wednesday, January 28, 2026 8:38 PM
> To: Verma, Devendra <Devendra.Verma@amd.com>
> Cc: bhelgaas@google.com; mani@kernel.org; vkoul@kernel.org;
> dmaengine@vger.kernel.org; linux-pci@vger.kernel.org; linux-
> kernel@vger.kernel.org; Simek, Michal <michal.simek@amd.com>
> Subject: Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode

--[ Snipped some headers to reduce the size of this mail ]--

> > > > > > > > > > AMD MDB IP supports Linked List (LL) mode as well as
> > > > > > > > > > non-LL
> > > mode.
> > > > > > > > > > The current code does not have the mechanisms to
> > > > > > > > > > enable the DMA transactions using the non-LL mode. The
> > > > > > > > > > following two cases are added with this patch:
> > > > > > > > > > - For the AMD (Xilinx) only, when a valid physical base
> address of
> > > > > > > > > >   the device side DDR is not configured, then the IP can still be
> > > > > > > > > >   used in non-LL mode. For all the channels DMA
> > > > > > > > > > transactions will
> > > > > > > > >
> > > > > > > > > If DDR have not configured, where DATA send to in device
> > > > > > > > > side by non-LL mode.
> > > > > > > > >
> > > > > > > >
> > > > > > > > The DDR base address in the VSEC capability is used for
> > > > > > > > driving the DMA transfers when used in the LL mode. The
> > > > > > > > DDR is configured and present all the time but the DMA
> > > > > > > > PCIe driver uses this DDR base address (physical address)
> > > > > > > > to configure the LLP
> > > address.
> > > > > > > >
> > > > > > > > In the scenario, where this DDR base address in VSEC
> > > > > > > > capability is not configured then the current controller
> > > > > > > > cannot be used as the default mode supported is LL mode only.
> > > > > > > > In order to make the controller usable non-LL mode is
> > > > > > > > being added which just needs SAR, DAR, XFERLEN and control
> > > > > > > > register to initiate the transfer. So, the DDR is always
> > > > > > > > present, but the DMA PCIe driver need to know the DDR base
> > > > > > > > physical address to make the transfer. This is useful in
> > > > > > > > scenarios where the memory
> > > > > > > allocated for LL can be used for DMA transactions as well.
> > > > > > >
> > > > > > > Do you means use DMA transfer LL's context?
> > > > > > >
> > > > > >
> > > > > > Yes, the device side memory reserved for the link list to
> > > > > > store the descriptors, accessed by the host via BAR_2 in this driver
> code.
> > > > > >
> > > > > > > >
> > > > > > > > > >   be using the non-LL mode only. This, the default non-LL
> mode,
> > > > > > > > > >   is not applicable for Synopsys IP with the current code
> addition.
> > > > > > > > > >
> > > > > > > > > > - If the default mode is LL-mode, for both AMD
> > > > > > > > > > (Xilinx) and
> > > Synosys,
> > > > > > > > > >   and if user wants to use non-LL mode then user can do so
> via
> > > > > > > > > >   configuring the peripheral_config param of
> dma_slave_config.
> > > > > > > > > >
> > > > > > > > > > Signed-off-by: Devendra K Verma
> > > > > > > > > > <devendra.verma@amd.com>
> > > > > > > > > > ---
> > > > > > > > > > Changes in v8
> > > > > > > > > >   Cosmetic change related to comment and code.
> > > > > > > > > >
> > > > > > > > > > Changes in v7
> > > > > > > > > >   No change
> > > > > > > > > >
> > > > > > > > > > Changes in v6
> > > > > > > > > >   Gave definition to bits used for channel configuration.
> > > > > > > > > >   Removed the comment related to doorbell.
> > > > > > > > > >
> > > > > > > > > > Changes in v5
> > > > > > > > > >   Variable name 'nollp' changed to 'non_ll'.
> > > > > > > > > >   In the dw_edma_device_config() WARN_ON replaced with
> > > > > dev_err().
> > > > > > > > > >   Comments follow the 80-column guideline.
> > > > > > > > > >
> > > > > > > > > > Changes in v4
> > > > > > > > > >   No change
> > > > > > > > > >
> > > > > > > > > > Changes in v3
> > > > > > > > > >   No change
> > > > > > > > > >
> > > > > > > > > > Changes in v2
> > > > > > > > > >   Reverted the function return type to u64 for
> > > > > > > > > >   dw_edma_get_phys_addr().
> > > > > > > > > >
> > > > > > > > > > Changes in v1
> > > > > > > > > >   Changed the function return type for
> > > dw_edma_get_phys_addr().
> > > > > > > > > >   Corrected the typo raised in review.
> > > > > > > > > > ---
> > > > > > > > > >  drivers/dma/dw-edma/dw-edma-core.c    | 42
> > > > > > > +++++++++++++++++++++---
> > > > > > > > > >  drivers/dma/dw-edma/dw-edma-core.h    |  1 +
> > > > > > > > > >  drivers/dma/dw-edma/dw-edma-pcie.c    | 46
> > > > > ++++++++++++++++++--
> > > > > > > ------
> > > > > > > > > >  drivers/dma/dw-edma/dw-hdma-v0-core.c | 61
> > > > > > > > > > ++++++++++++++++++++++++++++++++++-
> > > > > > > > > >  drivers/dma/dw-edma/dw-hdma-v0-regs.h |  1 +
> > > > > > > > >
> > > > > > > > > edma-v0-core.c have not update, if don't support, at
> > > > > > > > > least need return failure at dw_edma_device_config()
> > > > > > > > > when backend is
> > > eDMA.
> > > > > > > > >
> > > > > > > > > >  include/linux/dma/edma.h              |  1 +
> > > > > > > > > >  6 files changed, 132 insertions(+), 20 deletions(-)
> > > > > > > > > >
> > > > > > > > > > diff --git a/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > > > > > b/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > > > > > index b43255f..d37112b 100644
> > > > > > > > > > --- a/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > > > > > +++ b/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > > > > > @@ -223,8 +223,32 @@ static int
> > > > > > > > > > dw_edma_device_config(struct
> > > > > > > > > dma_chan *dchan,
> > > > > > > > > >                                struct dma_slave_config *config)  {
> > > > > > > > > >       struct dw_edma_chan *chan =
> > > > > > > > > > dchan2dw_edma_chan(dchan);
> > > > > > > > > > +     int non_ll = 0;
> > > > > > > > > > +
> > > > > > > > > > +     if (config->peripheral_config &&
> > > > > > > > > > +         config->peripheral_size != sizeof(int)) {
> > > > > > > > > > +             dev_err(dchan->device->dev,
> > > > > > > > > > +                     "config param peripheral size mismatch\n");
> > > > > > > > > > +             return -EINVAL;
> > > > > > > > > > +     }
> > > > > > > > > >
> > > > > > > > > >       memcpy(&chan->config, config, sizeof(*config));
> > > > > > > > > > +
> > > > > > > > > > +     /*
> > > > > > > > > > +      * When there is no valid LLP base address
> > > > > > > > > > + available then the
> > > > > default
> > > > > > > > > > +      * DMA ops will use the non-LL mode.
> > > > > > > > > > +      *
> > > > > > > > > > +      * Cases where LL mode is enabled and client
> > > > > > > > > > + wants to use the
> > > > > non-LL
> > > > > > > > > > +      * mode then also client can do so via providing
> > > > > > > > > > + the
> > > > > peripheral_config
> > > > > > > > > > +      * param.
> > > > > > > > > > +      */
> > > > > > > > > > +     if (config->peripheral_config)
> > > > > > > > > > +             non_ll = *(int
> > > > > > > > > > + *)config->peripheral_config;
> > > > > > > > > > +
> > > > > > > > > > +     chan->non_ll = false;
> > > > > > > > > > +     if (chan->dw->chip->non_ll ||
> > > > > > > > > > + (!chan->dw->chip->non_ll &&
> > > > > non_ll))
> > > > > > > > > > +             chan->non_ll = true;
> > > > > > > > > > +
> > > > > > > > > >       chan->configured = true;
> > > > > > > > > >
> > > > > > > > > >       return 0;
> > > > > > > > > > @@ -353,7 +377,7 @@ static void
> > > > > > > > > > dw_edma_device_issue_pending(struct
> > > > > > > > > dma_chan *dchan)
> > > > > > > > > >       struct dw_edma_chan *chan =
> > > > > > > > > > dchan2dw_edma_chan(xfer-
> > > > > >dchan);
> > > > > > > > > >       enum dma_transfer_direction dir = xfer->direction;
> > > > > > > > > >       struct scatterlist *sg = NULL;
> > > > > > > > > > -     struct dw_edma_chunk *chunk;
> > > > > > > > > > +     struct dw_edma_chunk *chunk = NULL;
> > > > > > > > > >       struct dw_edma_burst *burst;
> > > > > > > > > >       struct dw_edma_desc *desc;
> > > > > > > > > >       u64 src_addr, dst_addr; @@ -419,9 +443,11 @@
> > > > > > > > > > static void dw_edma_device_issue_pending(struct
> > > > > > > > > dma_chan *dchan)
> > > > > > > > > >       if (unlikely(!desc))
> > > > > > > > > >               goto err_alloc;
> > > > > > > > > >
> > > > > > > > > > -     chunk = dw_edma_alloc_chunk(desc);
> > > > > > > > > > -     if (unlikely(!chunk))
> > > > > > > > > > -             goto err_alloc;
> > > > > > > > > > +     if (!chan->non_ll) {
> > > > > > > > > > +             chunk = dw_edma_alloc_chunk(desc);
> > > > > > > > > > +             if (unlikely(!chunk))
> > > > > > > > > > +                     goto err_alloc;
> > > > > > > > > > +     }
> > > > > > > > >
> > > > > > > > > non_ll is the same as ll_max = 1. (or 2, there are link back
> entry).
> > > > > > > > >
> > > > > > > > > If you set ll_max = 1, needn't change this code.
> > > > > > > > >
> > > > > > > >
> > > > > > > > The ll_max is defined for the session till the driver is
> > > > > > > > loaded in the
> > > > > kernel.
> > > > > > > > This code also enables the non-LL mode dynamically upon
> > > > > > > > input from the DMA client. In this scenario, touching
> > > > > > > > ll_max would not be a good idea as the ll_max controls the
> > > > > > > > LL entries for all the DMA channels not just for a single DMA
> transaction.
> > > > > > >
> > > > > > > You can use new variable, such as ll_avail.
> > > > > > >
> > > > > >
> > > > > > In order to separate out the execution paths a new meaningful
> > > > > > variable
> > > > > "non_ll"
> > > > > > is used. The variable "non_ll" alone is sufficient. Using
> > > > > > another variable along side "non_ll" for the similar purpose
> > > > > > may not have any
> > > > > added advantage.
> > > > >
> > > > > ll_avail can help debug/fine tune how much impact preformance by
> > > > > adjust ll length. And it make code logic clean and consistent.
> > > > > also ll_avail can help test corner case when ll item small.
> > > > > Normall case it is
> > > hard to reach ll_max.
> > > > >
> > > >
> > > > Thank you for your suggestion. The ll_max is max limit on the
> > > > descriptors that can be accommodated on the device side DDR. The
> > > > ll_avail
> > > will always be less than ll_max.
> > > > The optimization being referred can be tried without even having
> > > > to declare the ll_avail cause the number of descriptors given can
> > > > be controlled by the DMA client based on the length argument to
> > > > the
> > > dmaengine_prep_* APIs.
> > >
> > > I optimzied it to allow dynatmic appended dma descriptors.
> > >
> > > https://lore.kernel.org/dmaengine/20260109-edma_dymatic-v1-0-
> > > 9a98c9c98536@nxp.com/T/#t
> > >
> > > > So, the use of dynamic ll_avail is not necessarily required.
> > > > Without increasing the ll_max, ll_avail cannot be increased. In
> > > > order to increase ll_max one may need to alter size and recompile this
> driver.
> > > >
> > > > However, the requirement of ll_avail does not help for the
> > > > supporting the
> > > non-LL mode.
> > > > For non-LL mode to use:
> > > > 1) Either LL mode shall be not available, as it can happen for the Xilinx IP.
> > > > 2) User provides the preference for non-LL mode.
> > > > For both above, the function calls are different which can be
> > > > differentiated by using the "non_ll" flag. So, even if I try to
> > > > accommodate ll_avail, the call for LL or non-LL would be ambiguous
> > > > as in
> > > case of LL mode also we can have a single descriptor as similar to
> > > non-LL mode.
> > > > Please check the function dw_hdma_v0_core_start() in this review
> > > > where the decision is taken Based on non_ll flag.
> > >
> > > We can treat ll_avail == 1 as no_ll mode because needn't set extra
> > > LL in memory at all.
> > >
> >
> > I analyzed the use of ll_avail but I think the use of this variable
> > does not fit at this location in the code for the following reasons:
> > 1. The use of a new variable breaks the continuity for non-LL mode. The
> variable
> >     name non_ll is being used for driving the non-LL mode not only in this file
> but also
> >    in the files relevant to HDMA. This flag gives the clear indication of LL vs
> non-LL mode.
> >    In the function dw_hdma_v0_core_start(), non_ll decided the mode to
> choose.
> > 2. The use of "ll_avail" is ambiguous for both the modes. First, it is
> associated with LL mode only.
> >      It will be used for optimizing / testing the Controller performance based
> on the
> >     number of descriptors available on the Device DDR side which is for LL
> mode. So when
> >     it is being used for LL mode then it is obvious that it excludes any use for
> non-LL mode.
> >     In the API dw_edma_device_transfer(), the ll_avail will be used for
> creating the bursts.
> >     If ll_avail = 1 in the above API, then it is ambiguous whether it is creating
> the burst for
> >      LL or non-LL mode. In the above API, the non_ll is sufficient to have the
> LL mode
> >      and non-LL burst allocation related piece of code.
>
> If really like non-ll, you'd better set ll_avail = 1 in prepare code.
> Normal case ll_avail should be ll_max. It will reduce if-else branch in prep
> dma burst code.
>

I think we are not on the same page, and it is creating confusion.
If non_ll flag is being used to differentiate between the modes, then in this scenario
the use of ll_avail does not fit any requirement related to differentiation of different
modes. In the last response, I pointed out that ll_avail, if used, creates ambiguity
rather than bringing clarity for both LL & non-LL mode. If non_ll flag is used and
initialized properly then this is sufficient to drive the execution for non-LL mode.

In the function doing the preparation, there also no if-else clause is introduced rather
the same "if" condition is extended to support the non-LL mode.

Could you elaborate what is the approach using ll_avail if I need to maintain the
continuity of the non-LL context and use non-LL mode without any ambiguity anywhere,
instead of using non_ll flag?
If possible, please give a code snippet. Depending upon the usability and issue it fixes, I
will check its feasibility.

> +               /*
> +                * For non-LL mode, only a single burst can be handled
> +                * in a single chunk unlike LL mode where multiple bursts
> +                * can be configured in a single chunk.
> +                */
>
> It is actually wrong, current software should handle that. If there are multiple
> bursts, only HEAD of bursts trigger DMA, in irq handle, it will auto move to
> next burst. (only irq latency is bigger compared to LL, software's resule is the
> same).
>
> The code logic is totally equal ll_max = 1, except write differece registers.
>

Changing the ll_max dynamically for a single request is not feasible. As pointed out
earlier it may require the logic to retain the initially configured value, during the probe,
and then use the retained value depending on the use-case.
Could you elaborate the statement,
" The code logic is totally equal ll_max = 1, except write differece registers." ?

The irq_handler() for success case calls dw_edma_start_transfer() which schedules the chunks
not bursts. The bursts associated with that chunk will get written to controller DDR area /
scheduled (for non-LL) in the dw_hdma_v0_core_start(), for HDMA.
With this flow, for the non-LL mode each chunk needs to contain a single burst as controller
can handle only one burst at a time in non-LL mode.


> And anthor important is that,
>
> in dw_edma_device_config() should return if backend is not HDMA.
>

Thanks, this is a valid concern, will address in the upcoming version.

> Frank
>
> >
> > I think ll_avail, if used for trying out to optimize / debug the
> > settings related to number of descriptors for LL mode then it should
> > be part of the separate discussion / update not related to non-LL.
> >
> > > >
> > > > > >
> > > > > > > >
> > > > > > > > > >
> > > > > ...
> > > > > > > > > > +
> > > > > > > > > > + ll_block->bar);
> > > > > > > > >
> > > > > > > > > This change need do prepare patch, which only change
> > > > > > > > > pci_bus_address() to dw_edma_get_phys_addr().
> > > > > > > > >
> > > > > > > >
> > > > > > > > This is not clear.
> > > > > > >
> > > > > > > why not. only trivial add helper patch, which help reviewer
> > > > > > >
> > > > > >
> > > > > > I was not clear on the question you asked.
> > > > > > It does not look justified when a patch is raised alone just
> > > > > > to replace this
> > > > > function.
> > > > > > The function change is required cause the same code *can*
> > > > > > support different IPs from different vendors. And, with this
> > > > > > single change alone in the code the support for another IP is
> > > > > > added. That's why it is easier to get the reason for the
> > > > > > change in the function name and
> > > syntax.
> > > > >
> > > > > Add replace pci_bus_address() with dw_edma_get_phys_addr() to
> > > > > make review easily and get ack for such replacement patches.
> > > > >
> > > > > two patches
> > > > > patch1, just replace exist pci_bus_address() with
> > > > > dw_edma_get_phys_addr() patch2, add new logic in
> > > dw_edma_get_phys_addr() to support new vendor.
> > > > >
> > > >
> > > > I understand your concern about making the review easier. However,
> > > > given that we've been iterating on this patch series since
> > > > September and are now at v9, I believe the current approach is
> > > > justified. The function renames from
> > > > pci_bus_address() to dw_edma_get_phys_addr() is directly tied to
> > > > the non-LL mode functionality being added - it's needed because
> > > > the same code now supports different IPs from different vendors.
> > > >
> > > > Splitting this into a separate preparatory patch at this stage
> > > > would further delay the review process. The change is kind of
> > > > straightforward and the context is clear within the current patch.
> > > > I request
> > > you to review this patch to avoid additional review cycles.
> > > >
> > > > This also increases the work related to testing and maintaining
> > > > multiple
> > > patches.
> > > > I have commitment for delivery of this, and I can see adding one
> > > > more series definitely add 3-4 months of review cycle from here.
> > > > Please excuse me but this code has already
> > >
> > > Thank you for your persevere.
> > >
> >
> > Thank you for your support.
> >
> > > > been reviewed extensively by other reviewers and almost by you as
> > > > well. You can check the detailed discussion wrt this function at
> > > > the following
> > > link:
> > > >
> > >
> https://lore.kernel.org/all/SA1PR12MB8120341DFFD56D90EAD70EDE9514A@
> > > SA1
> > > > PR12MB8120.namprd12.prod.outlook.com/
> > > >
> > >
> > > But still not got reviewed by tags. The recently,  Manivannan
> > > Sadhasivam , Niklas Cassel and me active worked on this driver.
> > > You'd better to get their feedback. Bjorn as pci maintainer to provide
> generally feedback.
> > >
> >
> > Hi Manivannan Sadhasivam, Vinod Koul and Bjorn Helgaas Could you
> > please provide your feedback on the patch?
> > You have reviewed these patches extensively on the previous versions of
> the same series.
> >
> > Regards,
> > Devendra
> >
> > >
> > > > > >
> > > > > > > >
> > > > > > > > > >               ll_region->paddr += ll_block->off;
> > > > > > > > > >               ll_region->sz = ll_block->sz;
> > > > > > > > > >
> > > > > ...
> > > > > > > > > >
> > > > > > > > > > +static void dw_hdma_v0_core_non_ll_start(struct
> > > > > > > > > > +dw_edma_chunk
> > > > > > > > > *chunk)
> > > > > > > > > > +{
> > > > > > > > > > +     struct dw_edma_chan *chan = chunk->chan;
> > > > > > > > > > +     struct dw_edma *dw = chan->dw;
> > > > > > > > > > +     struct dw_edma_burst *child;
> > > > > > > > > > +     u32 val;
> > > > > > > > > > +
> > > > > > > > > > +     list_for_each_entry(child, &chunk->burst->list,
> > > > > > > > > > + list) {
> > > > > > > > >
> > > > > > > > > why need iterated list, it doesn't support ll. Need wait
> > > > > > > > > for irq to start next
> > > > > > > one.
> > > > > > > > >
> > > > > > > > > Frank
> > > > > > > >
> > > > > > > > Yes, this is true. The format is kept similar to LL mode.
> > > > > > >
> > > > > > > Just fill one. list_for_each_entry() cause confuse.
> > > > > > >
> > > > > > > Frank
> > > > > >
> > > > > > I see, we can use list_first_entry_or_null() which is
> > > > > > dependent on giving the type of pointer, compared to this
> > > > > > list_for_each_entry() looks neat and agnostic to the pointer
> > > > > > type being used. Though, it can be
> > > > > explored further.
> > > > > > Also, when the chunk is allocated, the comment clearly spells
> > > > > > out how the allocation would be for the non LL mode so it is
> > > > > > evident that each chunk would have single entry and with that
> > > > > > understanding it is clear that loop will also be used in a
> > > > > > similar manner, to retrieve a single entry. It is a similar
> > > > > > use case as of "do {}while (0)" albeit needs a context to
> > > > > understand it.
> > > > >
> > > > > I don't think so. list_for_each_entry() is miss leading to
> > > > > reader think it is not only to one item in burst list, and use
> > > > > polling method to to finish many burst transfer.
> > > > >
> > > > > list_for_each_entry() {
> > > > >         ...
> > > > >         readl_timeout()
> > > > > }
> > > > >
> > > > > Generally, EDMA is very quick, polling is much quicker than irq
> > > > > if data is
> > > small.
> > > > >
> > > > > Frank
> > > > >
> > > >
> > > > The polling is not required. The single burst will raise the
> > > > interrupt and from the interrupt context another chunk will be
> > > > scheduled. This cycle repeats till all the chunks with single burst are
> exhausted.
> > > >
> > > > The following comment made in function dw_edma_device_transfer()
> > > > in the same patch makes it amply clear that only a single burst
> > > > would be
> > > handled for the non-LL mode.
> > > > +       /*
> > > > +        * For non-LL mode, only a single burst can be handled
> > > > +        * in a single chunk unlike LL mode where multiple bursts
> > > > +        * can be configured in a single chunk.
> > > > +        */
> > > >
> > > > Looking at the way bursts are appended to chunks and chunks in
> > > > dw_edma_device_transfer() are scheduled for non-LL mode then it is
> > > > clear
> > > what non-LL mode would handle in terms of bursts.
> > > > I just kept the format to match it with the LL mode format
> > > > otherwise there is no need of this comment and we can follow the
> > > > syntax for a single
> > > entry alone.
> > > > Please share your suggestion if these descriptions fail to provide
> > > > the clear
> > > context and intent.
> > >
> > > Avoid use list_for_each_entry() here to prevent miss-leading.
> > >
> > > Frank
> > >
> >
> > Sure, thanks, I will push this change in next version.
> >
> > > >
> > > > > >
> > > > > > > >
> > > > > > > > >
> > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id,
> > > > > > > > > > + ch_en, HDMA_V0_CH_EN);
> > > > > > > > > > +
> > > > > > > > > > +             /* Source address */
> > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, sar.lsb,
> > > > > > > > > > +                       lower_32_bits(child->sar));
> > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, sar.msb,
> > > > > > > > > > +                       upper_32_bits(child->sar));
> > > > > > > > > > +
> > > > > > > > > > +             /* Destination address */
> > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, dar.lsb,
> > > > > > > > > > +                       lower_32_bits(child->dar));
> > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, dar.msb,
> > > > > > > > > > +                       upper_32_bits(child->dar));
> > > > > > > > > > +
> > > > > > > > > > +             /* Transfer size */
> > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id,
> > > > > > > > > > + transfer_size,
> > > > > > > > > > + child->sz);
> > > > > > > > > > +
> > > > > > > > > > +             /* Interrupt setup */
> > > > > > > > > > +             val = GET_CH_32(dw, chan->dir, chan->id, int_setup)
> |
> > > > > > > > > > +                             HDMA_V0_STOP_INT_MASK |
> > > > > > > > > > +                             HDMA_V0_ABORT_INT_MASK |
> > > > > > > > > > +                             HDMA_V0_LOCAL_STOP_INT_EN |
> > > > > > > > > > +
> > > > > > > > > > + HDMA_V0_LOCAL_ABORT_INT_EN;
> > > > > > > > > > +
> > > > > > > > > > +             if (!(dw->chip->flags & DW_EDMA_CHIP_LOCAL)) {
> > > > > > > > > > +                     val |= HDMA_V0_REMOTE_STOP_INT_EN |
> > > > > > > > > > +                            HDMA_V0_REMOTE_ABORT_INT_EN;
> > > > > > > > > > +             }
> > > > > > > > > > +
> > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id,
> > > > > > > > > > + int_setup, val);
> > > > > > > > > > +
> > > > > > > > > > +             /* Channel control setup */
> > > > > > > > > > +             val = GET_CH_32(dw, chan->dir, chan->id, control1);
> > > > > > > > > > +             val &= ~HDMA_V0_LINKLIST_EN;
> > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id,
> > > > > > > > > > + control1, val);
> > > > > > > > > > +
> > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, doorbell,
> > > > > > > > > > +                       HDMA_V0_DOORBELL_START);
> > > > > > > > > > +     }
> > > > > > > > > > +}
> > > > > > > > > > +
> > > > > > > > > > +static void dw_hdma_v0_core_start(struct
> > > > > > > > > > +dw_edma_chunk *chunk, bool
> > > > > > > > > > +first) {
> > > > > > > > > > +     struct dw_edma_chan *chan = chunk->chan;
> > > > > > > > > > +
> > > > > > > > > > +     if (chan->non_ll)
> > > > > > > > > > +             dw_hdma_v0_core_non_ll_start(chunk);
> > > > > > > > > > +     else
> > > > > > > > > > +             dw_hdma_v0_core_ll_start(chunk, first);
> > > > > > > > > > + }
> > > > > > > > > > +
> > > > > > > > > >  static void dw_hdma_v0_core_ch_config(struct
> > > > > > > > > > dw_edma_chan
> > > > > > > > > > *chan)
> > > > > > > {
> > > > > > > > > >       struct dw_edma *dw = chan->dw; diff --git
> > > > > > > > > > a/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > > > > > b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > > > > > index eab5fd7..7759ba9 100644
> > > > > > > > > > --- a/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > > > > > +++ b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > > > > > @@ -12,6 +12,7 @@
> > > > > > > > > >  #include <linux/dmaengine.h>
> > > > > > > > > >
> > > > > > > > > >  #define HDMA_V0_MAX_NR_CH                    8
> > > > > > > > > > +#define HDMA_V0_CH_EN                                BIT(0)
> > > > > > > > > >  #define HDMA_V0_LOCAL_ABORT_INT_EN           BIT(6)
> > > > > > > > > >  #define HDMA_V0_REMOTE_ABORT_INT_EN          BIT(5)
> > > > > > > > > >  #define HDMA_V0_LOCAL_STOP_INT_EN            BIT(4)
> > > > > > > > > > diff --git a/include/linux/dma/edma.h
> > > > > > > > > > b/include/linux/dma/edma.h index 3080747..78ce31b
> > > > > > > > > > 100644
> > > > > > > > > > --- a/include/linux/dma/edma.h
> > > > > > > > > > +++ b/include/linux/dma/edma.h
> > > > > > > > > > @@ -99,6 +99,7 @@ struct dw_edma_chip {
> > > > > > > > > >       enum dw_edma_map_format mf;
> > > > > > > > > >
> > > > > > > > > >       struct dw_edma          *dw;
> > > > > > > > > > +     bool                    non_ll;
> > > > > > > > > >  };
> > > > > > > > > >
> > > > > > > > > >  /* Export to the platform drivers */
> > > > > > > > > > --
> > > > > > > > > > 1.8.3.1
> > > > > > > > > >

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

* RE: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
  2026-01-29 11:55                     ` Verma, Devendra
@ 2026-02-03 12:03                       ` Verma, Devendra
  2026-02-03 16:52                         ` Frank Li
  0 siblings, 1 reply; 24+ messages in thread
From: Verma, Devendra @ 2026-02-03 12:03 UTC (permalink / raw)
  To: Frank Li
  Cc: bhelgaas@google.com, mani@kernel.org, vkoul@kernel.org,
	dmaengine@vger.kernel.org, linux-pci@vger.kernel.org,
	linux-kernel@vger.kernel.org, Simek, Michal, Verma, Devendra

[AMD Official Use Only - AMD Internal Distribution Only]

Hi Frank

Any update on this review?

Regards,
Devendra

> -----Original Message-----
> From: Verma, Devendra <Devendra.Verma@amd.com>
> Sent: Thursday, January 29, 2026 5:26 PM
> To: Frank Li <Frank.li@nxp.com>
> Cc: bhelgaas@google.com; mani@kernel.org; vkoul@kernel.org;
> dmaengine@vger.kernel.org; linux-pci@vger.kernel.org; linux-
> kernel@vger.kernel.org; Simek, Michal <michal.simek@amd.com>; Verma,
> Devendra <Devendra.Verma@amd.com>
> Subject: RE: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
>
> [AMD Official Use Only - AMD Internal Distribution Only]
>
> > -----Original Message-----
> > From: Frank Li <Frank.li@nxp.com>
> > Sent: Wednesday, January 28, 2026 8:38 PM
> > To: Verma, Devendra <Devendra.Verma@amd.com>
> > Cc: bhelgaas@google.com; mani@kernel.org; vkoul@kernel.org;
> > dmaengine@vger.kernel.org; linux-pci@vger.kernel.org; linux-
> > kernel@vger.kernel.org; Simek, Michal <michal.simek@amd.com>
> > Subject: Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
>
> --[ Snipped some headers to reduce the size of this mail ]--
>
> > > > > > > > > > > AMD MDB IP supports Linked List (LL) mode as well as
> > > > > > > > > > > non-LL
> > > > mode.
> > > > > > > > > > > The current code does not have the mechanisms to
> > > > > > > > > > > enable the DMA transactions using the non-LL mode.
> > > > > > > > > > > The following two cases are added with this patch:
> > > > > > > > > > > - For the AMD (Xilinx) only, when a valid physical
> > > > > > > > > > > base
> > address of
> > > > > > > > > > >   the device side DDR is not configured, then the IP can still
> be
> > > > > > > > > > >   used in non-LL mode. For all the channels DMA
> > > > > > > > > > > transactions will
> > > > > > > > > >
> > > > > > > > > > If DDR have not configured, where DATA send to in
> > > > > > > > > > device side by non-LL mode.
> > > > > > > > > >
> > > > > > > > >
> > > > > > > > > The DDR base address in the VSEC capability is used for
> > > > > > > > > driving the DMA transfers when used in the LL mode. The
> > > > > > > > > DDR is configured and present all the time but the DMA
> > > > > > > > > PCIe driver uses this DDR base address (physical
> > > > > > > > > address) to configure the LLP
> > > > address.
> > > > > > > > >
> > > > > > > > > In the scenario, where this DDR base address in VSEC
> > > > > > > > > capability is not configured then the current controller
> > > > > > > > > cannot be used as the default mode supported is LL mode only.
> > > > > > > > > In order to make the controller usable non-LL mode is
> > > > > > > > > being added which just needs SAR, DAR, XFERLEN and
> > > > > > > > > control register to initiate the transfer. So, the DDR
> > > > > > > > > is always present, but the DMA PCIe driver need to know
> > > > > > > > > the DDR base physical address to make the transfer. This
> > > > > > > > > is useful in scenarios where the memory
> > > > > > > > allocated for LL can be used for DMA transactions as well.
> > > > > > > >
> > > > > > > > Do you means use DMA transfer LL's context?
> > > > > > > >
> > > > > > >
> > > > > > > Yes, the device side memory reserved for the link list to
> > > > > > > store the descriptors, accessed by the host via BAR_2 in
> > > > > > > this driver
> > code.
> > > > > > >
> > > > > > > > >
> > > > > > > > > > >   be using the non-LL mode only. This, the default
> > > > > > > > > > > non-LL
> > mode,
> > > > > > > > > > >   is not applicable for Synopsys IP with the current
> > > > > > > > > > > code
> > addition.
> > > > > > > > > > >
> > > > > > > > > > > - If the default mode is LL-mode, for both AMD
> > > > > > > > > > > (Xilinx) and
> > > > Synosys,
> > > > > > > > > > >   and if user wants to use non-LL mode then user can
> > > > > > > > > > > do so
> > via
> > > > > > > > > > >   configuring the peripheral_config param of
> > dma_slave_config.
> > > > > > > > > > >
> > > > > > > > > > > Signed-off-by: Devendra K Verma
> > > > > > > > > > > <devendra.verma@amd.com>
> > > > > > > > > > > ---
> > > > > > > > > > > Changes in v8
> > > > > > > > > > >   Cosmetic change related to comment and code.
> > > > > > > > > > >
> > > > > > > > > > > Changes in v7
> > > > > > > > > > >   No change
> > > > > > > > > > >
> > > > > > > > > > > Changes in v6
> > > > > > > > > > >   Gave definition to bits used for channel configuration.
> > > > > > > > > > >   Removed the comment related to doorbell.
> > > > > > > > > > >
> > > > > > > > > > > Changes in v5
> > > > > > > > > > >   Variable name 'nollp' changed to 'non_ll'.
> > > > > > > > > > >   In the dw_edma_device_config() WARN_ON replaced
> > > > > > > > > > > with
> > > > > > dev_err().
> > > > > > > > > > >   Comments follow the 80-column guideline.
> > > > > > > > > > >
> > > > > > > > > > > Changes in v4
> > > > > > > > > > >   No change
> > > > > > > > > > >
> > > > > > > > > > > Changes in v3
> > > > > > > > > > >   No change
> > > > > > > > > > >
> > > > > > > > > > > Changes in v2
> > > > > > > > > > >   Reverted the function return type to u64 for
> > > > > > > > > > >   dw_edma_get_phys_addr().
> > > > > > > > > > >
> > > > > > > > > > > Changes in v1
> > > > > > > > > > >   Changed the function return type for
> > > > dw_edma_get_phys_addr().
> > > > > > > > > > >   Corrected the typo raised in review.
> > > > > > > > > > > ---
> > > > > > > > > > >  drivers/dma/dw-edma/dw-edma-core.c    | 42
> > > > > > > > +++++++++++++++++++++---
> > > > > > > > > > >  drivers/dma/dw-edma/dw-edma-core.h    |  1 +
> > > > > > > > > > >  drivers/dma/dw-edma/dw-edma-pcie.c    | 46
> > > > > > ++++++++++++++++++--
> > > > > > > > ------
> > > > > > > > > > >  drivers/dma/dw-edma/dw-hdma-v0-core.c | 61
> > > > > > > > > > > ++++++++++++++++++++++++++++++++++-
> > > > > > > > > > >  drivers/dma/dw-edma/dw-hdma-v0-regs.h |  1 +
> > > > > > > > > >
> > > > > > > > > > edma-v0-core.c have not update, if don't support, at
> > > > > > > > > > least need return failure at dw_edma_device_config()
> > > > > > > > > > when backend is
> > > > eDMA.
> > > > > > > > > >
> > > > > > > > > > >  include/linux/dma/edma.h              |  1 +
> > > > > > > > > > >  6 files changed, 132 insertions(+), 20 deletions(-)
> > > > > > > > > > >
> > > > > > > > > > > diff --git a/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > > > > > > b/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > > > > > > index b43255f..d37112b 100644
> > > > > > > > > > > --- a/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > > > > > > +++ b/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > > > > > > @@ -223,8 +223,32 @@ static int
> > > > > > > > > > > dw_edma_device_config(struct
> > > > > > > > > > dma_chan *dchan,
> > > > > > > > > > >                                struct dma_slave_config *config)  {
> > > > > > > > > > >       struct dw_edma_chan *chan =
> > > > > > > > > > > dchan2dw_edma_chan(dchan);
> > > > > > > > > > > +     int non_ll = 0;
> > > > > > > > > > > +
> > > > > > > > > > > +     if (config->peripheral_config &&
> > > > > > > > > > > +         config->peripheral_size != sizeof(int)) {
> > > > > > > > > > > +             dev_err(dchan->device->dev,
> > > > > > > > > > > +                     "config param peripheral size mismatch\n");
> > > > > > > > > > > +             return -EINVAL;
> > > > > > > > > > > +     }
> > > > > > > > > > >
> > > > > > > > > > >       memcpy(&chan->config, config,
> > > > > > > > > > > sizeof(*config));
> > > > > > > > > > > +
> > > > > > > > > > > +     /*
> > > > > > > > > > > +      * When there is no valid LLP base address
> > > > > > > > > > > + available then the
> > > > > > default
> > > > > > > > > > > +      * DMA ops will use the non-LL mode.
> > > > > > > > > > > +      *
> > > > > > > > > > > +      * Cases where LL mode is enabled and client
> > > > > > > > > > > + wants to use the
> > > > > > non-LL
> > > > > > > > > > > +      * mode then also client can do so via
> > > > > > > > > > > + providing the
> > > > > > peripheral_config
> > > > > > > > > > > +      * param.
> > > > > > > > > > > +      */
> > > > > > > > > > > +     if (config->peripheral_config)
> > > > > > > > > > > +             non_ll = *(int
> > > > > > > > > > > + *)config->peripheral_config;
> > > > > > > > > > > +
> > > > > > > > > > > +     chan->non_ll = false;
> > > > > > > > > > > +     if (chan->dw->chip->non_ll ||
> > > > > > > > > > > + (!chan->dw->chip->non_ll &&
> > > > > > non_ll))
> > > > > > > > > > > +             chan->non_ll = true;
> > > > > > > > > > > +
> > > > > > > > > > >       chan->configured = true;
> > > > > > > > > > >
> > > > > > > > > > >       return 0;
> > > > > > > > > > > @@ -353,7 +377,7 @@ static void
> > > > > > > > > > > dw_edma_device_issue_pending(struct
> > > > > > > > > > dma_chan *dchan)
> > > > > > > > > > >       struct dw_edma_chan *chan =
> > > > > > > > > > > dchan2dw_edma_chan(xfer-
> > > > > > >dchan);
> > > > > > > > > > >       enum dma_transfer_direction dir = xfer->direction;
> > > > > > > > > > >       struct scatterlist *sg = NULL;
> > > > > > > > > > > -     struct dw_edma_chunk *chunk;
> > > > > > > > > > > +     struct dw_edma_chunk *chunk = NULL;
> > > > > > > > > > >       struct dw_edma_burst *burst;
> > > > > > > > > > >       struct dw_edma_desc *desc;
> > > > > > > > > > >       u64 src_addr, dst_addr; @@ -419,9 +443,11 @@
> > > > > > > > > > > static void dw_edma_device_issue_pending(struct
> > > > > > > > > > dma_chan *dchan)
> > > > > > > > > > >       if (unlikely(!desc))
> > > > > > > > > > >               goto err_alloc;
> > > > > > > > > > >
> > > > > > > > > > > -     chunk = dw_edma_alloc_chunk(desc);
> > > > > > > > > > > -     if (unlikely(!chunk))
> > > > > > > > > > > -             goto err_alloc;
> > > > > > > > > > > +     if (!chan->non_ll) {
> > > > > > > > > > > +             chunk = dw_edma_alloc_chunk(desc);
> > > > > > > > > > > +             if (unlikely(!chunk))
> > > > > > > > > > > +                     goto err_alloc;
> > > > > > > > > > > +     }
> > > > > > > > > >
> > > > > > > > > > non_ll is the same as ll_max = 1. (or 2, there are
> > > > > > > > > > link back
> > entry).
> > > > > > > > > >
> > > > > > > > > > If you set ll_max = 1, needn't change this code.
> > > > > > > > > >
> > > > > > > > >
> > > > > > > > > The ll_max is defined for the session till the driver is
> > > > > > > > > loaded in the
> > > > > > kernel.
> > > > > > > > > This code also enables the non-LL mode dynamically upon
> > > > > > > > > input from the DMA client. In this scenario, touching
> > > > > > > > > ll_max would not be a good idea as the ll_max controls
> > > > > > > > > the LL entries for all the DMA channels not just for a
> > > > > > > > > single DMA
> > transaction.
> > > > > > > >
> > > > > > > > You can use new variable, such as ll_avail.
> > > > > > > >
> > > > > > >
> > > > > > > In order to separate out the execution paths a new
> > > > > > > meaningful variable
> > > > > > "non_ll"
> > > > > > > is used. The variable "non_ll" alone is sufficient. Using
> > > > > > > another variable along side "non_ll" for the similar purpose
> > > > > > > may not have any
> > > > > > added advantage.
> > > > > >
> > > > > > ll_avail can help debug/fine tune how much impact preformance
> > > > > > by adjust ll length. And it make code logic clean and consistent.
> > > > > > also ll_avail can help test corner case when ll item small.
> > > > > > Normall case it is
> > > > hard to reach ll_max.
> > > > > >
> > > > >
> > > > > Thank you for your suggestion. The ll_max is max limit on the
> > > > > descriptors that can be accommodated on the device side DDR. The
> > > > > ll_avail
> > > > will always be less than ll_max.
> > > > > The optimization being referred can be tried without even having
> > > > > to declare the ll_avail cause the number of descriptors given
> > > > > can be controlled by the DMA client based on the length argument
> > > > > to the
> > > > dmaengine_prep_* APIs.
> > > >
> > > > I optimzied it to allow dynatmic appended dma descriptors.
> > > >
> > > > https://lore.kernel.org/dmaengine/20260109-edma_dymatic-v1-0-
> > > > 9a98c9c98536@nxp.com/T/#t
> > > >
> > > > > So, the use of dynamic ll_avail is not necessarily required.
> > > > > Without increasing the ll_max, ll_avail cannot be increased. In
> > > > > order to increase ll_max one may need to alter size and
> > > > > recompile this
> > driver.
> > > > >
> > > > > However, the requirement of ll_avail does not help for the
> > > > > supporting the
> > > > non-LL mode.
> > > > > For non-LL mode to use:
> > > > > 1) Either LL mode shall be not available, as it can happen for the Xilinx
> IP.
> > > > > 2) User provides the preference for non-LL mode.
> > > > > For both above, the function calls are different which can be
> > > > > differentiated by using the "non_ll" flag. So, even if I try to
> > > > > accommodate ll_avail, the call for LL or non-LL would be
> > > > > ambiguous as in
> > > > case of LL mode also we can have a single descriptor as similar to
> > > > non-LL mode.
> > > > > Please check the function dw_hdma_v0_core_start() in this review
> > > > > where the decision is taken Based on non_ll flag.
> > > >
> > > > We can treat ll_avail == 1 as no_ll mode because needn't set extra
> > > > LL in memory at all.
> > > >
> > >
> > > I analyzed the use of ll_avail but I think the use of this variable
> > > does not fit at this location in the code for the following reasons:
> > > 1. The use of a new variable breaks the continuity for non-LL mode.
> > > The
> > variable
> > >     name non_ll is being used for driving the non-LL mode not only
> > > in this file
> > but also
> > >    in the files relevant to HDMA. This flag gives the clear
> > > indication of LL vs
> > non-LL mode.
> > >    In the function dw_hdma_v0_core_start(), non_ll decided the mode
> > > to
> > choose.
> > > 2. The use of "ll_avail" is ambiguous for both the modes. First, it
> > > is
> > associated with LL mode only.
> > >      It will be used for optimizing / testing the Controller
> > > performance based
> > on the
> > >     number of descriptors available on the Device DDR side which is
> > > for LL
> > mode. So when
> > >     it is being used for LL mode then it is obvious that it excludes
> > > any use for
> > non-LL mode.
> > >     In the API dw_edma_device_transfer(), the ll_avail will be used
> > > for
> > creating the bursts.
> > >     If ll_avail = 1 in the above API, then it is ambiguous whether
> > > it is creating
> > the burst for
> > >      LL or non-LL mode. In the above API, the non_ll is sufficient
> > > to have the
> > LL mode
> > >      and non-LL burst allocation related piece of code.
> >
> > If really like non-ll, you'd better set ll_avail = 1 in prepare code.
> > Normal case ll_avail should be ll_max. It will reduce if-else branch
> > in prep dma burst code.
> >
>
> I think we are not on the same page, and it is creating confusion.
> If non_ll flag is being used to differentiate between the modes, then in this
> scenario the use of ll_avail does not fit any requirement related to
> differentiation of different modes. In the last response, I pointed out that
> ll_avail, if used, creates ambiguity rather than bringing clarity for both LL &
> non-LL mode. If non_ll flag is used and initialized properly then this is
> sufficient to drive the execution for non-LL mode.
>
> In the function doing the preparation, there also no if-else clause is
> introduced rather the same "if" condition is extended to support the non-LL
> mode.
>
> Could you elaborate what is the approach using ll_avail if I need to maintain
> the continuity of the non-LL context and use non-LL mode without any
> ambiguity anywhere, instead of using non_ll flag?
> If possible, please give a code snippet. Depending upon the usability and
> issue it fixes, I will check its feasibility.
>
> > +               /*
> > +                * For non-LL mode, only a single burst can be handled
> > +                * in a single chunk unlike LL mode where multiple bursts
> > +                * can be configured in a single chunk.
> > +                */
> >
> > It is actually wrong, current software should handle that. If there
> > are multiple bursts, only HEAD of bursts trigger DMA, in irq handle,
> > it will auto move to next burst. (only irq latency is bigger compared
> > to LL, software's resule is the same).
> >
> > The code logic is totally equal ll_max = 1, except write differece registers.
> >
>
> Changing the ll_max dynamically for a single request is not feasible. As
> pointed out earlier it may require the logic to retain the initially configured
> value, during the probe, and then use the retained value depending on the
> use-case.
> Could you elaborate the statement,
> " The code logic is totally equal ll_max = 1, except write differece registers." ?
>
> The irq_handler() for success case calls dw_edma_start_transfer() which
> schedules the chunks not bursts. The bursts associated with that chunk will
> get written to controller DDR area / scheduled (for non-LL) in the
> dw_hdma_v0_core_start(), for HDMA.
> With this flow, for the non-LL mode each chunk needs to contain a single
> burst as controller can handle only one burst at a time in non-LL mode.
>
>
> > And anthor important is that,
> >
> > in dw_edma_device_config() should return if backend is not HDMA.
> >
>
> Thanks, this is a valid concern, will address in the upcoming version.
>
> > Frank
> >
> > >
> > > I think ll_avail, if used for trying out to optimize / debug the
> > > settings related to number of descriptors for LL mode then it should
> > > be part of the separate discussion / update not related to non-LL.
> > >
> > > > >
> > > > > > >
> > > > > > > > >
> > > > > > > > > > >
> > > > > > ...
> > > > > > > > > > > +
> > > > > > > > > > > + ll_block->bar);
> > > > > > > > > >
> > > > > > > > > > This change need do prepare patch, which only change
> > > > > > > > > > pci_bus_address() to dw_edma_get_phys_addr().
> > > > > > > > > >
> > > > > > > > >
> > > > > > > > > This is not clear.
> > > > > > > >
> > > > > > > > why not. only trivial add helper patch, which help
> > > > > > > > reviewer
> > > > > > > >
> > > > > > >
> > > > > > > I was not clear on the question you asked.
> > > > > > > It does not look justified when a patch is raised alone just
> > > > > > > to replace this
> > > > > > function.
> > > > > > > The function change is required cause the same code *can*
> > > > > > > support different IPs from different vendors. And, with this
> > > > > > > single change alone in the code the support for another IP
> > > > > > > is added. That's why it is easier to get the reason for the
> > > > > > > change in the function name and
> > > > syntax.
> > > > > >
> > > > > > Add replace pci_bus_address() with dw_edma_get_phys_addr() to
> > > > > > make review easily and get ack for such replacement patches.
> > > > > >
> > > > > > two patches
> > > > > > patch1, just replace exist pci_bus_address() with
> > > > > > dw_edma_get_phys_addr() patch2, add new logic in
> > > > dw_edma_get_phys_addr() to support new vendor.
> > > > > >
> > > > >
> > > > > I understand your concern about making the review easier.
> > > > > However, given that we've been iterating on this patch series
> > > > > since September and are now at v9, I believe the current
> > > > > approach is justified. The function renames from
> > > > > pci_bus_address() to dw_edma_get_phys_addr() is directly tied to
> > > > > the non-LL mode functionality being added - it's needed because
> > > > > the same code now supports different IPs from different vendors.
> > > > >
> > > > > Splitting this into a separate preparatory patch at this stage
> > > > > would further delay the review process. The change is kind of
> > > > > straightforward and the context is clear within the current patch.
> > > > > I request
> > > > you to review this patch to avoid additional review cycles.
> > > > >
> > > > > This also increases the work related to testing and maintaining
> > > > > multiple
> > > > patches.
> > > > > I have commitment for delivery of this, and I can see adding one
> > > > > more series definitely add 3-4 months of review cycle from here.
> > > > > Please excuse me but this code has already
> > > >
> > > > Thank you for your persevere.
> > > >
> > >
> > > Thank you for your support.
> > >
> > > > > been reviewed extensively by other reviewers and almost by you
> > > > > as well. You can check the detailed discussion wrt this function
> > > > > at the following
> > > > link:
> > > > >
> > > >
> >
> https://lore.kernel.org/all/SA1PR12MB8120341DFFD56D90EAD70EDE9514A@
> > > > SA1
> > > > > PR12MB8120.namprd12.prod.outlook.com/
> > > > >
> > > >
> > > > But still not got reviewed by tags. The recently,  Manivannan
> > > > Sadhasivam , Niklas Cassel and me active worked on this driver.
> > > > You'd better to get their feedback. Bjorn as pci maintainer to
> > > > provide
> > generally feedback.
> > > >
> > >
> > > Hi Manivannan Sadhasivam, Vinod Koul and Bjorn Helgaas Could you
> > > please provide your feedback on the patch?
> > > You have reviewed these patches extensively on the previous versions
> > > of
> > the same series.
> > >
> > > Regards,
> > > Devendra
> > >
> > > >
> > > > > > >
> > > > > > > > >
> > > > > > > > > > >               ll_region->paddr += ll_block->off;
> > > > > > > > > > >               ll_region->sz = ll_block->sz;
> > > > > > > > > > >
> > > > > > ...
> > > > > > > > > > >
> > > > > > > > > > > +static void dw_hdma_v0_core_non_ll_start(struct
> > > > > > > > > > > +dw_edma_chunk
> > > > > > > > > > *chunk)
> > > > > > > > > > > +{
> > > > > > > > > > > +     struct dw_edma_chan *chan = chunk->chan;
> > > > > > > > > > > +     struct dw_edma *dw = chan->dw;
> > > > > > > > > > > +     struct dw_edma_burst *child;
> > > > > > > > > > > +     u32 val;
> > > > > > > > > > > +
> > > > > > > > > > > +     list_for_each_entry(child,
> > > > > > > > > > > + &chunk->burst->list,
> > > > > > > > > > > + list) {
> > > > > > > > > >
> > > > > > > > > > why need iterated list, it doesn't support ll. Need
> > > > > > > > > > wait for irq to start next
> > > > > > > > one.
> > > > > > > > > >
> > > > > > > > > > Frank
> > > > > > > > >
> > > > > > > > > Yes, this is true. The format is kept similar to LL mode.
> > > > > > > >
> > > > > > > > Just fill one. list_for_each_entry() cause confuse.
> > > > > > > >
> > > > > > > > Frank
> > > > > > >
> > > > > > > I see, we can use list_first_entry_or_null() which is
> > > > > > > dependent on giving the type of pointer, compared to this
> > > > > > > list_for_each_entry() looks neat and agnostic to the pointer
> > > > > > > type being used. Though, it can be
> > > > > > explored further.
> > > > > > > Also, when the chunk is allocated, the comment clearly
> > > > > > > spells out how the allocation would be for the non LL mode
> > > > > > > so it is evident that each chunk would have single entry and
> > > > > > > with that understanding it is clear that loop will also be
> > > > > > > used in a similar manner, to retrieve a single entry. It is
> > > > > > > a similar use case as of "do {}while (0)" albeit needs a
> > > > > > > context to
> > > > > > understand it.
> > > > > >
> > > > > > I don't think so. list_for_each_entry() is miss leading to
> > > > > > reader think it is not only to one item in burst list, and use
> > > > > > polling method to to finish many burst transfer.
> > > > > >
> > > > > > list_for_each_entry() {
> > > > > >         ...
> > > > > >         readl_timeout()
> > > > > > }
> > > > > >
> > > > > > Generally, EDMA is very quick, polling is much quicker than
> > > > > > irq if data is
> > > > small.
> > > > > >
> > > > > > Frank
> > > > > >
> > > > >
> > > > > The polling is not required. The single burst will raise the
> > > > > interrupt and from the interrupt context another chunk will be
> > > > > scheduled. This cycle repeats till all the chunks with single
> > > > > burst are
> > exhausted.
> > > > >
> > > > > The following comment made in function dw_edma_device_transfer()
> > > > > in the same patch makes it amply clear that only a single burst
> > > > > would be
> > > > handled for the non-LL mode.
> > > > > +       /*
> > > > > +        * For non-LL mode, only a single burst can be handled
> > > > > +        * in a single chunk unlike LL mode where multiple bursts
> > > > > +        * can be configured in a single chunk.
> > > > > +        */
> > > > >
> > > > > Looking at the way bursts are appended to chunks and chunks in
> > > > > dw_edma_device_transfer() are scheduled for non-LL mode then it
> > > > > is clear
> > > > what non-LL mode would handle in terms of bursts.
> > > > > I just kept the format to match it with the LL mode format
> > > > > otherwise there is no need of this comment and we can follow the
> > > > > syntax for a single
> > > > entry alone.
> > > > > Please share your suggestion if these descriptions fail to
> > > > > provide the clear
> > > > context and intent.
> > > >
> > > > Avoid use list_for_each_entry() here to prevent miss-leading.
> > > >
> > > > Frank
> > > >
> > >
> > > Sure, thanks, I will push this change in next version.
> > >
> > > > >
> > > > > > >
> > > > > > > > >
> > > > > > > > > >
> > > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id,
> > > > > > > > > > > + ch_en, HDMA_V0_CH_EN);
> > > > > > > > > > > +
> > > > > > > > > > > +             /* Source address */
> > > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, sar.lsb,
> > > > > > > > > > > +                       lower_32_bits(child->sar));
> > > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, sar.msb,
> > > > > > > > > > > +                       upper_32_bits(child->sar));
> > > > > > > > > > > +
> > > > > > > > > > > +             /* Destination address */
> > > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, dar.lsb,
> > > > > > > > > > > +                       lower_32_bits(child->dar));
> > > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, dar.msb,
> > > > > > > > > > > +                       upper_32_bits(child->dar));
> > > > > > > > > > > +
> > > > > > > > > > > +             /* Transfer size */
> > > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id,
> > > > > > > > > > > + transfer_size,
> > > > > > > > > > > + child->sz);
> > > > > > > > > > > +
> > > > > > > > > > > +             /* Interrupt setup */
> > > > > > > > > > > +             val = GET_CH_32(dw, chan->dir,
> > > > > > > > > > > + chan->id, int_setup)
> > |
> > > > > > > > > > > +                             HDMA_V0_STOP_INT_MASK |
> > > > > > > > > > > +                             HDMA_V0_ABORT_INT_MASK |
> > > > > > > > > > > +
> > > > > > > > > > > + HDMA_V0_LOCAL_STOP_INT_EN |
> > > > > > > > > > > +
> > > > > > > > > > > + HDMA_V0_LOCAL_ABORT_INT_EN;
> > > > > > > > > > > +
> > > > > > > > > > > +             if (!(dw->chip->flags & DW_EDMA_CHIP_LOCAL)) {
> > > > > > > > > > > +                     val |= HDMA_V0_REMOTE_STOP_INT_EN |
> > > > > > > > > > > +                            HDMA_V0_REMOTE_ABORT_INT_EN;
> > > > > > > > > > > +             }
> > > > > > > > > > > +
> > > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id,
> > > > > > > > > > > + int_setup, val);
> > > > > > > > > > > +
> > > > > > > > > > > +             /* Channel control setup */
> > > > > > > > > > > +             val = GET_CH_32(dw, chan->dir, chan->id, control1);
> > > > > > > > > > > +             val &= ~HDMA_V0_LINKLIST_EN;
> > > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id,
> > > > > > > > > > > + control1, val);
> > > > > > > > > > > +
> > > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, doorbell,
> > > > > > > > > > > +                       HDMA_V0_DOORBELL_START);
> > > > > > > > > > > +     }
> > > > > > > > > > > +}
> > > > > > > > > > > +
> > > > > > > > > > > +static void dw_hdma_v0_core_start(struct
> > > > > > > > > > > +dw_edma_chunk *chunk, bool
> > > > > > > > > > > +first) {
> > > > > > > > > > > +     struct dw_edma_chan *chan = chunk->chan;
> > > > > > > > > > > +
> > > > > > > > > > > +     if (chan->non_ll)
> > > > > > > > > > > +             dw_hdma_v0_core_non_ll_start(chunk);
> > > > > > > > > > > +     else
> > > > > > > > > > > +             dw_hdma_v0_core_ll_start(chunk,
> > > > > > > > > > > + first); }
> > > > > > > > > > > +
> > > > > > > > > > >  static void dw_hdma_v0_core_ch_config(struct
> > > > > > > > > > > dw_edma_chan
> > > > > > > > > > > *chan)
> > > > > > > > {
> > > > > > > > > > >       struct dw_edma *dw = chan->dw; diff --git
> > > > > > > > > > > a/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > > > > > > b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > > > > > > index eab5fd7..7759ba9 100644
> > > > > > > > > > > --- a/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > > > > > > +++ b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > > > > > > @@ -12,6 +12,7 @@
> > > > > > > > > > >  #include <linux/dmaengine.h>
> > > > > > > > > > >
> > > > > > > > > > >  #define HDMA_V0_MAX_NR_CH                    8
> > > > > > > > > > > +#define HDMA_V0_CH_EN                                BIT(0)
> > > > > > > > > > >  #define HDMA_V0_LOCAL_ABORT_INT_EN           BIT(6)
> > > > > > > > > > >  #define HDMA_V0_REMOTE_ABORT_INT_EN          BIT(5)
> > > > > > > > > > >  #define HDMA_V0_LOCAL_STOP_INT_EN            BIT(4)
> > > > > > > > > > > diff --git a/include/linux/dma/edma.h
> > > > > > > > > > > b/include/linux/dma/edma.h index 3080747..78ce31b
> > > > > > > > > > > 100644
> > > > > > > > > > > --- a/include/linux/dma/edma.h
> > > > > > > > > > > +++ b/include/linux/dma/edma.h
> > > > > > > > > > > @@ -99,6 +99,7 @@ struct dw_edma_chip {
> > > > > > > > > > >       enum dw_edma_map_format mf;
> > > > > > > > > > >
> > > > > > > > > > >       struct dw_edma          *dw;
> > > > > > > > > > > +     bool                    non_ll;
> > > > > > > > > > >  };
> > > > > > > > > > >
> > > > > > > > > > >  /* Export to the platform drivers */
> > > > > > > > > > > --
> > > > > > > > > > > 1.8.3.1
> > > > > > > > > > >


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

* Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
  2026-02-03 12:03                       ` Verma, Devendra
@ 2026-02-03 16:52                         ` Frank Li
  2026-02-04 10:50                           ` Verma, Devendra
  0 siblings, 1 reply; 24+ messages in thread
From: Frank Li @ 2026-02-03 16:52 UTC (permalink / raw)
  To: Verma, Devendra
  Cc: bhelgaas@google.com, mani@kernel.org, vkoul@kernel.org,
	dmaengine@vger.kernel.org, linux-pci@vger.kernel.org,
	linux-kernel@vger.kernel.org, Simek, Michal

On Tue, Feb 03, 2026 at 12:03:54PM +0000, Verma, Devendra wrote:
> [AMD Official Use Only - AMD Internal Distribution Only]
>
> Hi Frank
>
> Any update on this review?
>
> Regards,
> Devendra
>
> > -----Original Message-----
> > From: Verma, Devendra <Devendra.Verma@amd.com>
> > Sent: Thursday, January 29, 2026 5:26 PM
> > To: Frank Li <Frank.li@nxp.com>
> > Cc: bhelgaas@google.com; mani@kernel.org; vkoul@kernel.org;
> > dmaengine@vger.kernel.org; linux-pci@vger.kernel.org; linux-
> > kernel@vger.kernel.org; Simek, Michal <michal.simek@amd.com>; Verma,
> > Devendra <Devendra.Verma@amd.com>
> > Subject: RE: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
> >
> > [AMD Official Use Only - AMD Internal Distribution Only]
> >
> > > -----Original Message-----
> > > From: Frank Li <Frank.li@nxp.com>
> > > Sent: Wednesday, January 28, 2026 8:38 PM
> > > To: Verma, Devendra <Devendra.Verma@amd.com>
> > > Cc: bhelgaas@google.com; mani@kernel.org; vkoul@kernel.org;
> > > dmaengine@vger.kernel.org; linux-pci@vger.kernel.org; linux-
> > > kernel@vger.kernel.org; Simek, Michal <michal.simek@amd.com>
> > > Subject: Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
> >
> > --[ Snipped some headers to reduce the size of this mail ]--
> >
> > > > > > > > > > > > AMD MDB IP supports Linked List (LL) mode as well as
> > > > > > > > > > > > non-LL
> > > > > mode.
> > > > > > > > > > > > The current code does not have the mechanisms to
> > > > > > > > > > > > enable the DMA transactions using the non-LL mode.
> > > > > > > > > > > > The following two cases are added with this patch:
> > > > > > > > > > > > - For the AMD (Xilinx) only, when a valid physical
> > > > > > > > > > > > base
> > > address of
> > > > > > > > > > > >   the device side DDR is not configured, then the IP can still
> > be
> > > > > > > > > > > >   used in non-LL mode. For all the channels DMA
> > > > > > > > > > > > transactions will
> > > > > > > > > > >
> > > > > > > > > > > If DDR have not configured, where DATA send to in
> > > > > > > > > > > device side by non-LL mode.
> > > > > > > > > > >
> > > > > > > > > >
> > > > > > > > > > The DDR base address in the VSEC capability is used for
> > > > > > > > > > driving the DMA transfers when used in the LL mode. The
> > > > > > > > > > DDR is configured and present all the time but the DMA
> > > > > > > > > > PCIe driver uses this DDR base address (physical
> > > > > > > > > > address) to configure the LLP
> > > > > address.
> > > > > > > > > >
> > > > > > > > > > In the scenario, where this DDR base address in VSEC
> > > > > > > > > > capability is not configured then the current controller
> > > > > > > > > > cannot be used as the default mode supported is LL mode only.
> > > > > > > > > > In order to make the controller usable non-LL mode is
> > > > > > > > > > being added which just needs SAR, DAR, XFERLEN and
> > > > > > > > > > control register to initiate the transfer. So, the DDR
> > > > > > > > > > is always present, but the DMA PCIe driver need to know
> > > > > > > > > > the DDR base physical address to make the transfer. This
> > > > > > > > > > is useful in scenarios where the memory
> > > > > > > > > allocated for LL can be used for DMA transactions as well.
> > > > > > > > >
> > > > > > > > > Do you means use DMA transfer LL's context?
> > > > > > > > >
> > > > > > > >
> > > > > > > > Yes, the device side memory reserved for the link list to
> > > > > > > > store the descriptors, accessed by the host via BAR_2 in
> > > > > > > > this driver
> > > code.
> > > > > > > >
> > > > > > > > > >
> > > > > > > > > > > >   be using the non-LL mode only. This, the default
> > > > > > > > > > > > non-LL
> > > mode,
> > > > > > > > > > > >   is not applicable for Synopsys IP with the current
> > > > > > > > > > > > code
> > > addition.
> > > > > > > > > > > >
> > > > > > > > > > > > - If the default mode is LL-mode, for both AMD
> > > > > > > > > > > > (Xilinx) and
> > > > > Synosys,
> > > > > > > > > > > >   and if user wants to use non-LL mode then user can
> > > > > > > > > > > > do so
> > > via
> > > > > > > > > > > >   configuring the peripheral_config param of
> > > dma_slave_config.
> > > > > > > > > > > >
> > > > > > > > > > > > Signed-off-by: Devendra K Verma
> > > > > > > > > > > > <devendra.verma@amd.com>
> > > > > > > > > > > > ---
> > > > > > > > > > > > Changes in v8
> > > > > > > > > > > >   Cosmetic change related to comment and code.
> > > > > > > > > > > >
> > > > > > > > > > > > Changes in v7
> > > > > > > > > > > >   No change
> > > > > > > > > > > >
> > > > > > > > > > > > Changes in v6
> > > > > > > > > > > >   Gave definition to bits used for channel configuration.
> > > > > > > > > > > >   Removed the comment related to doorbell.
> > > > > > > > > > > >
> > > > > > > > > > > > Changes in v5
> > > > > > > > > > > >   Variable name 'nollp' changed to 'non_ll'.
> > > > > > > > > > > >   In the dw_edma_device_config() WARN_ON replaced
> > > > > > > > > > > > with
> > > > > > > dev_err().
> > > > > > > > > > > >   Comments follow the 80-column guideline.
> > > > > > > > > > > >
> > > > > > > > > > > > Changes in v4
> > > > > > > > > > > >   No change
> > > > > > > > > > > >
> > > > > > > > > > > > Changes in v3
> > > > > > > > > > > >   No change
> > > > > > > > > > > >
> > > > > > > > > > > > Changes in v2
> > > > > > > > > > > >   Reverted the function return type to u64 for
> > > > > > > > > > > >   dw_edma_get_phys_addr().
> > > > > > > > > > > >
> > > > > > > > > > > > Changes in v1
> > > > > > > > > > > >   Changed the function return type for
> > > > > dw_edma_get_phys_addr().
> > > > > > > > > > > >   Corrected the typo raised in review.
> > > > > > > > > > > > ---
> > > > > > > > > > > >  drivers/dma/dw-edma/dw-edma-core.c    | 42
> > > > > > > > > +++++++++++++++++++++---
> > > > > > > > > > > >  drivers/dma/dw-edma/dw-edma-core.h    |  1 +
> > > > > > > > > > > >  drivers/dma/dw-edma/dw-edma-pcie.c    | 46
> > > > > > > ++++++++++++++++++--
> > > > > > > > > ------
> > > > > > > > > > > >  drivers/dma/dw-edma/dw-hdma-v0-core.c | 61
> > > > > > > > > > > > ++++++++++++++++++++++++++++++++++-
> > > > > > > > > > > >  drivers/dma/dw-edma/dw-hdma-v0-regs.h |  1 +
> > > > > > > > > > >
> > > > > > > > > > > edma-v0-core.c have not update, if don't support, at
> > > > > > > > > > > least need return failure at dw_edma_device_config()
> > > > > > > > > > > when backend is
> > > > > eDMA.
> > > > > > > > > > >
> > > > > > > > > > > >  include/linux/dma/edma.h              |  1 +
> > > > > > > > > > > >  6 files changed, 132 insertions(+), 20 deletions(-)
> > > > > > > > > > > >
> > > > > > > > > > > > diff --git a/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > > > > > > > b/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > > > > > > > index b43255f..d37112b 100644
> > > > > > > > > > > > --- a/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > > > > > > > +++ b/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > > > > > > > @@ -223,8 +223,32 @@ static int
> > > > > > > > > > > > dw_edma_device_config(struct
> > > > > > > > > > > dma_chan *dchan,
> > > > > > > > > > > >                                struct dma_slave_config *config)  {
> > > > > > > > > > > >       struct dw_edma_chan *chan =
> > > > > > > > > > > > dchan2dw_edma_chan(dchan);
> > > > > > > > > > > > +     int non_ll = 0;
> > > > > > > > > > > > +
> > > > > > > > > > > > +     if (config->peripheral_config &&
> > > > > > > > > > > > +         config->peripheral_size != sizeof(int)) {
> > > > > > > > > > > > +             dev_err(dchan->device->dev,
> > > > > > > > > > > > +                     "config param peripheral size mismatch\n");
> > > > > > > > > > > > +             return -EINVAL;
> > > > > > > > > > > > +     }
> > > > > > > > > > > >
> > > > > > > > > > > >       memcpy(&chan->config, config,
> > > > > > > > > > > > sizeof(*config));
> > > > > > > > > > > > +
> > > > > > > > > > > > +     /*
> > > > > > > > > > > > +      * When there is no valid LLP base address
> > > > > > > > > > > > + available then the
> > > > > > > default
> > > > > > > > > > > > +      * DMA ops will use the non-LL mode.
> > > > > > > > > > > > +      *
> > > > > > > > > > > > +      * Cases where LL mode is enabled and client
> > > > > > > > > > > > + wants to use the
> > > > > > > non-LL
> > > > > > > > > > > > +      * mode then also client can do so via
> > > > > > > > > > > > + providing the
> > > > > > > peripheral_config
> > > > > > > > > > > > +      * param.
> > > > > > > > > > > > +      */
> > > > > > > > > > > > +     if (config->peripheral_config)
> > > > > > > > > > > > +             non_ll = *(int
> > > > > > > > > > > > + *)config->peripheral_config;
> > > > > > > > > > > > +
> > > > > > > > > > > > +     chan->non_ll = false;
> > > > > > > > > > > > +     if (chan->dw->chip->non_ll ||
> > > > > > > > > > > > + (!chan->dw->chip->non_ll &&
> > > > > > > non_ll))
> > > > > > > > > > > > +             chan->non_ll = true;
> > > > > > > > > > > > +
> > > > > > > > > > > >       chan->configured = true;
> > > > > > > > > > > >
> > > > > > > > > > > >       return 0;
> > > > > > > > > > > > @@ -353,7 +377,7 @@ static void
> > > > > > > > > > > > dw_edma_device_issue_pending(struct
> > > > > > > > > > > dma_chan *dchan)
> > > > > > > > > > > >       struct dw_edma_chan *chan =
> > > > > > > > > > > > dchan2dw_edma_chan(xfer-
> > > > > > > >dchan);
> > > > > > > > > > > >       enum dma_transfer_direction dir = xfer->direction;
> > > > > > > > > > > >       struct scatterlist *sg = NULL;
> > > > > > > > > > > > -     struct dw_edma_chunk *chunk;
> > > > > > > > > > > > +     struct dw_edma_chunk *chunk = NULL;
> > > > > > > > > > > >       struct dw_edma_burst *burst;
> > > > > > > > > > > >       struct dw_edma_desc *desc;
> > > > > > > > > > > >       u64 src_addr, dst_addr; @@ -419,9 +443,11 @@
> > > > > > > > > > > > static void dw_edma_device_issue_pending(struct
> > > > > > > > > > > dma_chan *dchan)
> > > > > > > > > > > >       if (unlikely(!desc))
> > > > > > > > > > > >               goto err_alloc;
> > > > > > > > > > > >
> > > > > > > > > > > > -     chunk = dw_edma_alloc_chunk(desc);
> > > > > > > > > > > > -     if (unlikely(!chunk))
> > > > > > > > > > > > -             goto err_alloc;
> > > > > > > > > > > > +     if (!chan->non_ll) {
> > > > > > > > > > > > +             chunk = dw_edma_alloc_chunk(desc);
> > > > > > > > > > > > +             if (unlikely(!chunk))
> > > > > > > > > > > > +                     goto err_alloc;
> > > > > > > > > > > > +     }
> > > > > > > > > > >
> > > > > > > > > > > non_ll is the same as ll_max = 1. (or 2, there are
> > > > > > > > > > > link back
> > > entry).
> > > > > > > > > > >
> > > > > > > > > > > If you set ll_max = 1, needn't change this code.
> > > > > > > > > > >
> > > > > > > > > >
> > > > > > > > > > The ll_max is defined for the session till the driver is
> > > > > > > > > > loaded in the
> > > > > > > kernel.
> > > > > > > > > > This code also enables the non-LL mode dynamically upon
> > > > > > > > > > input from the DMA client. In this scenario, touching
> > > > > > > > > > ll_max would not be a good idea as the ll_max controls
> > > > > > > > > > the LL entries for all the DMA channels not just for a
> > > > > > > > > > single DMA
> > > transaction.
> > > > > > > > >
> > > > > > > > > You can use new variable, such as ll_avail.
> > > > > > > > >
> > > > > > > >
> > > > > > > > In order to separate out the execution paths a new
> > > > > > > > meaningful variable
> > > > > > > "non_ll"
> > > > > > > > is used. The variable "non_ll" alone is sufficient. Using
> > > > > > > > another variable along side "non_ll" for the similar purpose
> > > > > > > > may not have any
> > > > > > > added advantage.
> > > > > > >
> > > > > > > ll_avail can help debug/fine tune how much impact preformance
> > > > > > > by adjust ll length. And it make code logic clean and consistent.
> > > > > > > also ll_avail can help test corner case when ll item small.
> > > > > > > Normall case it is
> > > > > hard to reach ll_max.
> > > > > > >
> > > > > >
> > > > > > Thank you for your suggestion. The ll_max is max limit on the
> > > > > > descriptors that can be accommodated on the device side DDR. The
> > > > > > ll_avail
> > > > > will always be less than ll_max.
> > > > > > The optimization being referred can be tried without even having
> > > > > > to declare the ll_avail cause the number of descriptors given
> > > > > > can be controlled by the DMA client based on the length argument
> > > > > > to the
> > > > > dmaengine_prep_* APIs.
> > > > >
> > > > > I optimzied it to allow dynatmic appended dma descriptors.
> > > > >
> > > > > https://lore.kernel.org/dmaengine/20260109-edma_dymatic-v1-0-
> > > > > 9a98c9c98536@nxp.com/T/#t
> > > > >
> > > > > > So, the use of dynamic ll_avail is not necessarily required.
> > > > > > Without increasing the ll_max, ll_avail cannot be increased. In
> > > > > > order to increase ll_max one may need to alter size and
> > > > > > recompile this
> > > driver.
> > > > > >
> > > > > > However, the requirement of ll_avail does not help for the
> > > > > > supporting the
> > > > > non-LL mode.
> > > > > > For non-LL mode to use:
> > > > > > 1) Either LL mode shall be not available, as it can happen for the Xilinx
> > IP.
> > > > > > 2) User provides the preference for non-LL mode.
> > > > > > For both above, the function calls are different which can be
> > > > > > differentiated by using the "non_ll" flag. So, even if I try to
> > > > > > accommodate ll_avail, the call for LL or non-LL would be
> > > > > > ambiguous as in
> > > > > case of LL mode also we can have a single descriptor as similar to
> > > > > non-LL mode.
> > > > > > Please check the function dw_hdma_v0_core_start() in this review
> > > > > > where the decision is taken Based on non_ll flag.
> > > > >
> > > > > We can treat ll_avail == 1 as no_ll mode because needn't set extra
> > > > > LL in memory at all.
> > > > >
> > > >
> > > > I analyzed the use of ll_avail but I think the use of this variable
> > > > does not fit at this location in the code for the following reasons:
> > > > 1. The use of a new variable breaks the continuity for non-LL mode.
> > > > The
> > > variable
> > > >     name non_ll is being used for driving the non-LL mode not only
> > > > in this file
> > > but also
> > > >    in the files relevant to HDMA. This flag gives the clear
> > > > indication of LL vs
> > > non-LL mode.
> > > >    In the function dw_hdma_v0_core_start(), non_ll decided the mode
> > > > to
> > > choose.
> > > > 2. The use of "ll_avail" is ambiguous for both the modes. First, it
> > > > is
> > > associated with LL mode only.
> > > >      It will be used for optimizing / testing the Controller
> > > > performance based
> > > on the
> > > >     number of descriptors available on the Device DDR side which is
> > > > for LL
> > > mode. So when
> > > >     it is being used for LL mode then it is obvious that it excludes
> > > > any use for
> > > non-LL mode.
> > > >     In the API dw_edma_device_transfer(), the ll_avail will be used
> > > > for
> > > creating the bursts.
> > > >     If ll_avail = 1 in the above API, then it is ambiguous whether
> > > > it is creating
> > > the burst for
> > > >      LL or non-LL mode. In the above API, the non_ll is sufficient
> > > > to have the
> > > LL mode
> > > >      and non-LL burst allocation related piece of code.
> > >
> > > If really like non-ll, you'd better set ll_avail = 1 in prepare code.
> > > Normal case ll_avail should be ll_max. It will reduce if-else branch
> > > in prep dma burst code.
> > >
> >
> > I think we are not on the same page, and it is creating confusion.
> > If non_ll flag is being used to differentiate between the modes, then in this
> > scenario the use of ll_avail does not fit any requirement related to
> > differentiation of different modes. In the last response, I pointed out that
> > ll_avail, if used, creates ambiguity rather than bringing clarity for both LL &
> > non-LL mode. If non_ll flag is used and initialized properly then this is
> > sufficient to drive the execution for non-LL mode.
> >
> > In the function doing the preparation, there also no if-else clause is
> > introduced rather the same "if" condition is extended to support the non-LL
> > mode.
> >
> > Could you elaborate what is the approach using ll_avail if I need to maintain
> > the continuity of the non-LL context and use non-LL mode without any
> > ambiguity anywhere, instead of using non_ll flag?
> > If possible, please give a code snippet. Depending upon the usability and
> > issue it fixes, I will check its feasibility.
> >
> > > +               /*
> > > +                * For non-LL mode, only a single burst can be handled
> > > +                * in a single chunk unlike LL mode where multiple bursts
> > > +                * can be configured in a single chunk.
> > > +                */
> > >
> > > It is actually wrong, current software should handle that. If there
> > > are multiple bursts, only HEAD of bursts trigger DMA, in irq handle,
> > > it will auto move to next burst. (only irq latency is bigger compared
> > > to LL, software's resule is the same).
> > >
> > > The code logic is totally equal ll_max = 1, except write differece registers.
> > >
> >
> > Changing the ll_max dynamically for a single request is not feasible. As
> > pointed out earlier it may require the logic to retain the initially configured
> > value, during the probe, and then use the retained value depending on the
> > use-case.

As my previous suggest, add ll_avail, config can set it in [1..ll_max].
then replace ll_max with ll_avail.

> > Could you elaborate the statement,
> > " The code logic is totally equal ll_max = 1, except write differece registers." ?

My means don't touch actual logic in dw_edma_device_transfer() except
change ll_max to ll_avail or other variable, with value 1.

Even though you really want to use non_ll, you can use below code

dw_edma_device_transfer()
{

	size_t avail = no_ll ? 1 : ll_max;
	...

	if (chunk->bursts_alloc == avail)
	...
}

> >
> > The irq_handler() for success case calls dw_edma_start_transfer() which
> > schedules the chunks not bursts.

Current code, it is that. I forget I just change it.

>  The bursts associated with that chunk will
> > get written to controller DDR area / scheduled (for non-LL) in the
> > dw_hdma_v0_core_start(), for HDMA.

Original code have unnecessary complex about chunks and burst, which actually
add overhead.

See my patch to reduce 2 useless malloc()

https://lore.kernel.org/dmaengine/20251212-edma_ll-v1-0-fc863d9f5ca3@nxp.com/

> > With this flow, for the non-LL mode each chunk needs to contain a single
> > burst as controller can handle only one burst at a time in non-LL mode.

Non-LL case, you just fill one. The only difference is fill to DDR or
registers.

Frank

> >
> >
> > > And anthor important is that,
> > >
> > > in dw_edma_device_config() should return if backend is not HDMA.
> > >
> >
> > Thanks, this is a valid concern, will address in the upcoming version.
> >
> > > Frank
> > >
> > > >
> > > > I think ll_avail, if used for trying out to optimize / debug the
> > > > settings related to number of descriptors for LL mode then it should
> > > > be part of the separate discussion / update not related to non-LL.
> > > >
> > > > > >
> > > > > > > >
> > > > > > > > > >
> > > > > > > > > > > >
> > > > > > > ...
> > > > > > > > > > > > +
> > > > > > > > > > > > + ll_block->bar);
> > > > > > > > > > >
> > > > > > > > > > > This change need do prepare patch, which only change
> > > > > > > > > > > pci_bus_address() to dw_edma_get_phys_addr().
> > > > > > > > > > >
> > > > > > > > > >
> > > > > > > > > > This is not clear.
> > > > > > > > >
> > > > > > > > > why not. only trivial add helper patch, which help
> > > > > > > > > reviewer
> > > > > > > > >
> > > > > > > >
> > > > > > > > I was not clear on the question you asked.
> > > > > > > > It does not look justified when a patch is raised alone just
> > > > > > > > to replace this
> > > > > > > function.
> > > > > > > > The function change is required cause the same code *can*
> > > > > > > > support different IPs from different vendors. And, with this
> > > > > > > > single change alone in the code the support for another IP
> > > > > > > > is added. That's why it is easier to get the reason for the
> > > > > > > > change in the function name and
> > > > > syntax.
> > > > > > >
> > > > > > > Add replace pci_bus_address() with dw_edma_get_phys_addr() to
> > > > > > > make review easily and get ack for such replacement patches.
> > > > > > >
> > > > > > > two patches
> > > > > > > patch1, just replace exist pci_bus_address() with
> > > > > > > dw_edma_get_phys_addr() patch2, add new logic in
> > > > > dw_edma_get_phys_addr() to support new vendor.
> > > > > > >
> > > > > >
> > > > > > I understand your concern about making the review easier.
> > > > > > However, given that we've been iterating on this patch series
> > > > > > since September and are now at v9, I believe the current
> > > > > > approach is justified. The function renames from
> > > > > > pci_bus_address() to dw_edma_get_phys_addr() is directly tied to
> > > > > > the non-LL mode functionality being added - it's needed because
> > > > > > the same code now supports different IPs from different vendors.
> > > > > >
> > > > > > Splitting this into a separate preparatory patch at this stage
> > > > > > would further delay the review process. The change is kind of
> > > > > > straightforward and the context is clear within the current patch.
> > > > > > I request
> > > > > you to review this patch to avoid additional review cycles.
> > > > > >
> > > > > > This also increases the work related to testing and maintaining
> > > > > > multiple
> > > > > patches.
> > > > > > I have commitment for delivery of this, and I can see adding one
> > > > > > more series definitely add 3-4 months of review cycle from here.
> > > > > > Please excuse me but this code has already
> > > > >
> > > > > Thank you for your persevere.
> > > > >
> > > >
> > > > Thank you for your support.
> > > >
> > > > > > been reviewed extensively by other reviewers and almost by you
> > > > > > as well. You can check the detailed discussion wrt this function
> > > > > > at the following
> > > > > link:
> > > > > >
> > > > >
> > >
> > https://lore.kernel.org/all/SA1PR12MB8120341DFFD56D90EAD70EDE9514A@
> > > > > SA1
> > > > > > PR12MB8120.namprd12.prod.outlook.com/
> > > > > >
> > > > >
> > > > > But still not got reviewed by tags. The recently,  Manivannan
> > > > > Sadhasivam , Niklas Cassel and me active worked on this driver.
> > > > > You'd better to get their feedback. Bjorn as pci maintainer to
> > > > > provide
> > > generally feedback.
> > > > >
> > > >
> > > > Hi Manivannan Sadhasivam, Vinod Koul and Bjorn Helgaas Could you
> > > > please provide your feedback on the patch?
> > > > You have reviewed these patches extensively on the previous versions
> > > > of
> > > the same series.
> > > >
> > > > Regards,
> > > > Devendra
> > > >
> > > > >
> > > > > > > >
> > > > > > > > > >
> > > > > > > > > > > >               ll_region->paddr += ll_block->off;
> > > > > > > > > > > >               ll_region->sz = ll_block->sz;
> > > > > > > > > > > >
> > > > > > > ...
> > > > > > > > > > > >
> > > > > > > > > > > > +static void dw_hdma_v0_core_non_ll_start(struct
> > > > > > > > > > > > +dw_edma_chunk
> > > > > > > > > > > *chunk)
> > > > > > > > > > > > +{
> > > > > > > > > > > > +     struct dw_edma_chan *chan = chunk->chan;
> > > > > > > > > > > > +     struct dw_edma *dw = chan->dw;
> > > > > > > > > > > > +     struct dw_edma_burst *child;
> > > > > > > > > > > > +     u32 val;
> > > > > > > > > > > > +
> > > > > > > > > > > > +     list_for_each_entry(child,
> > > > > > > > > > > > + &chunk->burst->list,
> > > > > > > > > > > > + list) {
> > > > > > > > > > >
> > > > > > > > > > > why need iterated list, it doesn't support ll. Need
> > > > > > > > > > > wait for irq to start next
> > > > > > > > > one.
> > > > > > > > > > >
> > > > > > > > > > > Frank
> > > > > > > > > >
> > > > > > > > > > Yes, this is true. The format is kept similar to LL mode.
> > > > > > > > >
> > > > > > > > > Just fill one. list_for_each_entry() cause confuse.
> > > > > > > > >
> > > > > > > > > Frank
> > > > > > > >
> > > > > > > > I see, we can use list_first_entry_or_null() which is
> > > > > > > > dependent on giving the type of pointer, compared to this
> > > > > > > > list_for_each_entry() looks neat and agnostic to the pointer
> > > > > > > > type being used. Though, it can be
> > > > > > > explored further.
> > > > > > > > Also, when the chunk is allocated, the comment clearly
> > > > > > > > spells out how the allocation would be for the non LL mode
> > > > > > > > so it is evident that each chunk would have single entry and
> > > > > > > > with that understanding it is clear that loop will also be
> > > > > > > > used in a similar manner, to retrieve a single entry. It is
> > > > > > > > a similar use case as of "do {}while (0)" albeit needs a
> > > > > > > > context to
> > > > > > > understand it.
> > > > > > >
> > > > > > > I don't think so. list_for_each_entry() is miss leading to
> > > > > > > reader think it is not only to one item in burst list, and use
> > > > > > > polling method to to finish many burst transfer.
> > > > > > >
> > > > > > > list_for_each_entry() {
> > > > > > >         ...
> > > > > > >         readl_timeout()
> > > > > > > }
> > > > > > >
> > > > > > > Generally, EDMA is very quick, polling is much quicker than
> > > > > > > irq if data is
> > > > > small.
> > > > > > >
> > > > > > > Frank
> > > > > > >
> > > > > >
> > > > > > The polling is not required. The single burst will raise the
> > > > > > interrupt and from the interrupt context another chunk will be
> > > > > > scheduled. This cycle repeats till all the chunks with single
> > > > > > burst are
> > > exhausted.
> > > > > >
> > > > > > The following comment made in function dw_edma_device_transfer()
> > > > > > in the same patch makes it amply clear that only a single burst
> > > > > > would be
> > > > > handled for the non-LL mode.
> > > > > > +       /*
> > > > > > +        * For non-LL mode, only a single burst can be handled
> > > > > > +        * in a single chunk unlike LL mode where multiple bursts
> > > > > > +        * can be configured in a single chunk.
> > > > > > +        */
> > > > > >
> > > > > > Looking at the way bursts are appended to chunks and chunks in
> > > > > > dw_edma_device_transfer() are scheduled for non-LL mode then it
> > > > > > is clear
> > > > > what non-LL mode would handle in terms of bursts.
> > > > > > I just kept the format to match it with the LL mode format
> > > > > > otherwise there is no need of this comment and we can follow the
> > > > > > syntax for a single
> > > > > entry alone.
> > > > > > Please share your suggestion if these descriptions fail to
> > > > > > provide the clear
> > > > > context and intent.
> > > > >
> > > > > Avoid use list_for_each_entry() here to prevent miss-leading.
> > > > >
> > > > > Frank
> > > > >
> > > >
> > > > Sure, thanks, I will push this change in next version.
> > > >
> > > > > >
> > > > > > > >
> > > > > > > > > >
> > > > > > > > > > >
> > > > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id,
> > > > > > > > > > > > + ch_en, HDMA_V0_CH_EN);
> > > > > > > > > > > > +
> > > > > > > > > > > > +             /* Source address */
> > > > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, sar.lsb,
> > > > > > > > > > > > +                       lower_32_bits(child->sar));
> > > > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, sar.msb,
> > > > > > > > > > > > +                       upper_32_bits(child->sar));
> > > > > > > > > > > > +
> > > > > > > > > > > > +             /* Destination address */
> > > > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, dar.lsb,
> > > > > > > > > > > > +                       lower_32_bits(child->dar));
> > > > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, dar.msb,
> > > > > > > > > > > > +                       upper_32_bits(child->dar));
> > > > > > > > > > > > +
> > > > > > > > > > > > +             /* Transfer size */
> > > > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id,
> > > > > > > > > > > > + transfer_size,
> > > > > > > > > > > > + child->sz);
> > > > > > > > > > > > +
> > > > > > > > > > > > +             /* Interrupt setup */
> > > > > > > > > > > > +             val = GET_CH_32(dw, chan->dir,
> > > > > > > > > > > > + chan->id, int_setup)
> > > |
> > > > > > > > > > > > +                             HDMA_V0_STOP_INT_MASK |
> > > > > > > > > > > > +                             HDMA_V0_ABORT_INT_MASK |
> > > > > > > > > > > > +
> > > > > > > > > > > > + HDMA_V0_LOCAL_STOP_INT_EN |
> > > > > > > > > > > > +
> > > > > > > > > > > > + HDMA_V0_LOCAL_ABORT_INT_EN;
> > > > > > > > > > > > +
> > > > > > > > > > > > +             if (!(dw->chip->flags & DW_EDMA_CHIP_LOCAL)) {
> > > > > > > > > > > > +                     val |= HDMA_V0_REMOTE_STOP_INT_EN |
> > > > > > > > > > > > +                            HDMA_V0_REMOTE_ABORT_INT_EN;
> > > > > > > > > > > > +             }
> > > > > > > > > > > > +
> > > > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id,
> > > > > > > > > > > > + int_setup, val);
> > > > > > > > > > > > +
> > > > > > > > > > > > +             /* Channel control setup */
> > > > > > > > > > > > +             val = GET_CH_32(dw, chan->dir, chan->id, control1);
> > > > > > > > > > > > +             val &= ~HDMA_V0_LINKLIST_EN;
> > > > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id,
> > > > > > > > > > > > + control1, val);
> > > > > > > > > > > > +
> > > > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, doorbell,
> > > > > > > > > > > > +                       HDMA_V0_DOORBELL_START);
> > > > > > > > > > > > +     }
> > > > > > > > > > > > +}
> > > > > > > > > > > > +
> > > > > > > > > > > > +static void dw_hdma_v0_core_start(struct
> > > > > > > > > > > > +dw_edma_chunk *chunk, bool
> > > > > > > > > > > > +first) {
> > > > > > > > > > > > +     struct dw_edma_chan *chan = chunk->chan;
> > > > > > > > > > > > +
> > > > > > > > > > > > +     if (chan->non_ll)
> > > > > > > > > > > > +             dw_hdma_v0_core_non_ll_start(chunk);
> > > > > > > > > > > > +     else
> > > > > > > > > > > > +             dw_hdma_v0_core_ll_start(chunk,
> > > > > > > > > > > > + first); }
> > > > > > > > > > > > +
> > > > > > > > > > > >  static void dw_hdma_v0_core_ch_config(struct
> > > > > > > > > > > > dw_edma_chan
> > > > > > > > > > > > *chan)
> > > > > > > > > {
> > > > > > > > > > > >       struct dw_edma *dw = chan->dw; diff --git
> > > > > > > > > > > > a/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > > > > > > > b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > > > > > > > index eab5fd7..7759ba9 100644
> > > > > > > > > > > > --- a/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > > > > > > > +++ b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > > > > > > > @@ -12,6 +12,7 @@
> > > > > > > > > > > >  #include <linux/dmaengine.h>
> > > > > > > > > > > >
> > > > > > > > > > > >  #define HDMA_V0_MAX_NR_CH                    8
> > > > > > > > > > > > +#define HDMA_V0_CH_EN                                BIT(0)
> > > > > > > > > > > >  #define HDMA_V0_LOCAL_ABORT_INT_EN           BIT(6)
> > > > > > > > > > > >  #define HDMA_V0_REMOTE_ABORT_INT_EN          BIT(5)
> > > > > > > > > > > >  #define HDMA_V0_LOCAL_STOP_INT_EN            BIT(4)
> > > > > > > > > > > > diff --git a/include/linux/dma/edma.h
> > > > > > > > > > > > b/include/linux/dma/edma.h index 3080747..78ce31b
> > > > > > > > > > > > 100644
> > > > > > > > > > > > --- a/include/linux/dma/edma.h
> > > > > > > > > > > > +++ b/include/linux/dma/edma.h
> > > > > > > > > > > > @@ -99,6 +99,7 @@ struct dw_edma_chip {
> > > > > > > > > > > >       enum dw_edma_map_format mf;
> > > > > > > > > > > >
> > > > > > > > > > > >       struct dw_edma          *dw;
> > > > > > > > > > > > +     bool                    non_ll;
> > > > > > > > > > > >  };
> > > > > > > > > > > >
> > > > > > > > > > > >  /* Export to the platform drivers */
> > > > > > > > > > > > --
> > > > > > > > > > > > 1.8.3.1
> > > > > > > > > > > >
>

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

* RE: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
  2026-02-03 16:52                         ` Frank Li
@ 2026-02-04 10:50                           ` Verma, Devendra
  0 siblings, 0 replies; 24+ messages in thread
From: Verma, Devendra @ 2026-02-04 10:50 UTC (permalink / raw)
  To: Frank Li
  Cc: bhelgaas@google.com, mani@kernel.org, vkoul@kernel.org,
	dmaengine@vger.kernel.org, linux-pci@vger.kernel.org,
	linux-kernel@vger.kernel.org, Simek, Michal, Verma, Devendra

[AMD Official Use Only - AMD Internal Distribution Only]

Hi Frank

Thanks for your suggestions, please check my response inline.

Regards,
Devendra

> -----Original Message-----
> From: Frank Li <Frank.li@nxp.com>
> Sent: Tuesday, February 3, 2026 10:23 PM
> To: Verma, Devendra <Devendra.Verma@amd.com>
> Cc: bhelgaas@google.com; mani@kernel.org; vkoul@kernel.org;
> dmaengine@vger.kernel.org; linux-pci@vger.kernel.org; linux-
> kernel@vger.kernel.org; Simek, Michal <michal.simek@amd.com>
> Subject: Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode
>

 --[ Snipped some headers to reduce the size of this mail ]--


> > >
> > > > > > > > > > > > > AMD MDB IP supports Linked List (LL) mode as
> > > > > > > > > > > > > well as non-LL
> > > > > > mode.
> > > > > > > > > > > > > The current code does not have the mechanisms to
> > > > > > > > > > > > > enable the DMA transactions using the non-LL mode.
> > > > > > > > > > > > > The following two cases are added with this patch:
> > > > > > > > > > > > > - For the AMD (Xilinx) only, when a valid
> > > > > > > > > > > > > physical base
> > > > address of
> > > > > > > > > > > > >   the device side DDR is not configured, then
> > > > > > > > > > > > > the IP can still
> > > be
> > > > > > > > > > > > >   used in non-LL mode. For all the channels DMA
> > > > > > > > > > > > > transactions will
> > > > > > > > > > > >
> > > > > > > > > > > > If DDR have not configured, where DATA send to in
> > > > > > > > > > > > device side by non-LL mode.
> > > > > > > > > > > >
> > > > > > > > > > >
> > > > > > > > > > > The DDR base address in the VSEC capability is used
> > > > > > > > > > > for driving the DMA transfers when used in the LL
> > > > > > > > > > > mode. The DDR is configured and present all the time
> > > > > > > > > > > but the DMA PCIe driver uses this DDR base address
> > > > > > > > > > > (physical
> > > > > > > > > > > address) to configure the LLP
> > > > > > address.
> > > > > > > > > > >
> > > > > > > > > > > In the scenario, where this DDR base address in VSEC
> > > > > > > > > > > capability is not configured then the current
> > > > > > > > > > > controller cannot be used as the default mode supported is
> LL mode only.
> > > > > > > > > > > In order to make the controller usable non-LL mode
> > > > > > > > > > > is being added which just needs SAR, DAR, XFERLEN
> > > > > > > > > > > and control register to initiate the transfer. So,
> > > > > > > > > > > the DDR is always present, but the DMA PCIe driver
> > > > > > > > > > > need to know the DDR base physical address to make
> > > > > > > > > > > the transfer. This is useful in scenarios where the
> > > > > > > > > > > memory
> > > > > > > > > > allocated for LL can be used for DMA transactions as well.
> > > > > > > > > >
> > > > > > > > > > Do you means use DMA transfer LL's context?
> > > > > > > > > >
> > > > > > > > >
> > > > > > > > > Yes, the device side memory reserved for the link list
> > > > > > > > > to store the descriptors, accessed by the host via BAR_2
> > > > > > > > > in this driver
> > > > code.
> > > > > > > > >
> > > > > > > > > > >
> > > > > > > > > > > > >   be using the non-LL mode only. This, the
> > > > > > > > > > > > > default non-LL
> > > > mode,
> > > > > > > > > > > > >   is not applicable for Synopsys IP with the
> > > > > > > > > > > > > current code
> > > > addition.
> > > > > > > > > > > > >
> > > > > > > > > > > > > - If the default mode is LL-mode, for both AMD
> > > > > > > > > > > > > (Xilinx) and
> > > > > > Synosys,
> > > > > > > > > > > > >   and if user wants to use non-LL mode then user
> > > > > > > > > > > > > can do so
> > > > via
> > > > > > > > > > > > >   configuring the peripheral_config param of
> > > > dma_slave_config.
> > > > > > > > > > > > >
> > > > > > > > > > > > > Signed-off-by: Devendra K Verma
> > > > > > > > > > > > > <devendra.verma@amd.com>
> > > > > > > > > > > > > ---
> > > > > > > > > > > > > Changes in v8
> > > > > > > > > > > > >   Cosmetic change related to comment and code.
> > > > > > > > > > > > >
> > > > > > > > > > > > > Changes in v7
> > > > > > > > > > > > >   No change
> > > > > > > > > > > > >
> > > > > > > > > > > > > Changes in v6
> > > > > > > > > > > > >   Gave definition to bits used for channel configuration.
> > > > > > > > > > > > >   Removed the comment related to doorbell.
> > > > > > > > > > > > >
> > > > > > > > > > > > > Changes in v5
> > > > > > > > > > > > >   Variable name 'nollp' changed to 'non_ll'.
> > > > > > > > > > > > >   In the dw_edma_device_config() WARN_ON
> > > > > > > > > > > > > replaced with
> > > > > > > > dev_err().
> > > > > > > > > > > > >   Comments follow the 80-column guideline.
> > > > > > > > > > > > >
> > > > > > > > > > > > > Changes in v4
> > > > > > > > > > > > >   No change
> > > > > > > > > > > > >
> > > > > > > > > > > > > Changes in v3
> > > > > > > > > > > > >   No change
> > > > > > > > > > > > >
> > > > > > > > > > > > > Changes in v2
> > > > > > > > > > > > >   Reverted the function return type to u64 for
> > > > > > > > > > > > >   dw_edma_get_phys_addr().
> > > > > > > > > > > > >
> > > > > > > > > > > > > Changes in v1
> > > > > > > > > > > > >   Changed the function return type for
> > > > > > dw_edma_get_phys_addr().
> > > > > > > > > > > > >   Corrected the typo raised in review.
> > > > > > > > > > > > > ---
> > > > > > > > > > > > >  drivers/dma/dw-edma/dw-edma-core.c    | 42
> > > > > > > > > > +++++++++++++++++++++---
> > > > > > > > > > > > >  drivers/dma/dw-edma/dw-edma-core.h    |  1 +
> > > > > > > > > > > > >  drivers/dma/dw-edma/dw-edma-pcie.c    | 46
> > > > > > > > ++++++++++++++++++--
> > > > > > > > > > ------
> > > > > > > > > > > > >  drivers/dma/dw-edma/dw-hdma-v0-core.c | 61
> > > > > > > > > > > > > ++++++++++++++++++++++++++++++++++-
> > > > > > > > > > > > >  drivers/dma/dw-edma/dw-hdma-v0-regs.h |  1 +
> > > > > > > > > > > >
> > > > > > > > > > > > edma-v0-core.c have not update, if don't support,
> > > > > > > > > > > > at least need return failure at
> > > > > > > > > > > > dw_edma_device_config() when backend is
> > > > > > eDMA.
> > > > > > > > > > > >
> > > > > > > > > > > > >  include/linux/dma/edma.h              |  1 +
> > > > > > > > > > > > >  6 files changed, 132 insertions(+), 20
> > > > > > > > > > > > > deletions(-)
> > > > > > > > > > > > >
> > > > > > > > > > > > > diff --git a/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > > > > > > > > b/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > > > > > > > > index b43255f..d37112b 100644
> > > > > > > > > > > > > --- a/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > > > > > > > > +++ b/drivers/dma/dw-edma/dw-edma-core.c
> > > > > > > > > > > > > @@ -223,8 +223,32 @@ static int
> > > > > > > > > > > > > dw_edma_device_config(struct
> > > > > > > > > > > > dma_chan *dchan,
> > > > > > > > > > > > >                                struct dma_slave_config *config)  {
> > > > > > > > > > > > >       struct dw_edma_chan *chan =
> > > > > > > > > > > > > dchan2dw_edma_chan(dchan);
> > > > > > > > > > > > > +     int non_ll = 0;
> > > > > > > > > > > > > +
> > > > > > > > > > > > > +     if (config->peripheral_config &&
> > > > > > > > > > > > > +         config->peripheral_size != sizeof(int)) {
> > > > > > > > > > > > > +             dev_err(dchan->device->dev,
> > > > > > > > > > > > > +                     "config param peripheral size
> mismatch\n");
> > > > > > > > > > > > > +             return -EINVAL;
> > > > > > > > > > > > > +     }
> > > > > > > > > > > > >
> > > > > > > > > > > > >       memcpy(&chan->config, config,
> > > > > > > > > > > > > sizeof(*config));
> > > > > > > > > > > > > +
> > > > > > > > > > > > > +     /*
> > > > > > > > > > > > > +      * When there is no valid LLP base address
> > > > > > > > > > > > > + available then the
> > > > > > > > default
> > > > > > > > > > > > > +      * DMA ops will use the non-LL mode.
> > > > > > > > > > > > > +      *
> > > > > > > > > > > > > +      * Cases where LL mode is enabled and
> > > > > > > > > > > > > + client wants to use the
> > > > > > > > non-LL
> > > > > > > > > > > > > +      * mode then also client can do so via
> > > > > > > > > > > > > + providing the
> > > > > > > > peripheral_config
> > > > > > > > > > > > > +      * param.
> > > > > > > > > > > > > +      */
> > > > > > > > > > > > > +     if (config->peripheral_config)
> > > > > > > > > > > > > +             non_ll = *(int
> > > > > > > > > > > > > + *)config->peripheral_config;
> > > > > > > > > > > > > +
> > > > > > > > > > > > > +     chan->non_ll = false;
> > > > > > > > > > > > > +     if (chan->dw->chip->non_ll ||
> > > > > > > > > > > > > + (!chan->dw->chip->non_ll &&
> > > > > > > > non_ll))
> > > > > > > > > > > > > +             chan->non_ll = true;
> > > > > > > > > > > > > +
> > > > > > > > > > > > >       chan->configured = true;
> > > > > > > > > > > > >
> > > > > > > > > > > > >       return 0;
> > > > > > > > > > > > > @@ -353,7 +377,7 @@ static void
> > > > > > > > > > > > > dw_edma_device_issue_pending(struct
> > > > > > > > > > > > dma_chan *dchan)
> > > > > > > > > > > > >       struct dw_edma_chan *chan =
> > > > > > > > > > > > > dchan2dw_edma_chan(xfer-
> > > > > > > > >dchan);
> > > > > > > > > > > > >       enum dma_transfer_direction dir = xfer->direction;
> > > > > > > > > > > > >       struct scatterlist *sg = NULL;
> > > > > > > > > > > > > -     struct dw_edma_chunk *chunk;
> > > > > > > > > > > > > +     struct dw_edma_chunk *chunk = NULL;
> > > > > > > > > > > > >       struct dw_edma_burst *burst;
> > > > > > > > > > > > >       struct dw_edma_desc *desc;
> > > > > > > > > > > > >       u64 src_addr, dst_addr; @@ -419,9 +443,11
> > > > > > > > > > > > > @@ static void
> > > > > > > > > > > > > dw_edma_device_issue_pending(struct
> > > > > > > > > > > > dma_chan *dchan)
> > > > > > > > > > > > >       if (unlikely(!desc))
> > > > > > > > > > > > >               goto err_alloc;
> > > > > > > > > > > > >
> > > > > > > > > > > > > -     chunk = dw_edma_alloc_chunk(desc);
> > > > > > > > > > > > > -     if (unlikely(!chunk))
> > > > > > > > > > > > > -             goto err_alloc;
> > > > > > > > > > > > > +     if (!chan->non_ll) {
> > > > > > > > > > > > > +             chunk = dw_edma_alloc_chunk(desc);
> > > > > > > > > > > > > +             if (unlikely(!chunk))
> > > > > > > > > > > > > +                     goto err_alloc;
> > > > > > > > > > > > > +     }
> > > > > > > > > > > >
> > > > > > > > > > > > non_ll is the same as ll_max = 1. (or 2, there are
> > > > > > > > > > > > link back
> > > > entry).
> > > > > > > > > > > >
> > > > > > > > > > > > If you set ll_max = 1, needn't change this code.
> > > > > > > > > > > >
> > > > > > > > > > >
> > > > > > > > > > > The ll_max is defined for the session till the
> > > > > > > > > > > driver is loaded in the
> > > > > > > > kernel.
> > > > > > > > > > > This code also enables the non-LL mode dynamically
> > > > > > > > > > > upon input from the DMA client. In this scenario,
> > > > > > > > > > > touching ll_max would not be a good idea as the
> > > > > > > > > > > ll_max controls the LL entries for all the DMA
> > > > > > > > > > > channels not just for a single DMA
> > > > transaction.
> > > > > > > > > >
> > > > > > > > > > You can use new variable, such as ll_avail.
> > > > > > > > > >
> > > > > > > > >
> > > > > > > > > In order to separate out the execution paths a new
> > > > > > > > > meaningful variable
> > > > > > > > "non_ll"
> > > > > > > > > is used. The variable "non_ll" alone is sufficient.
> > > > > > > > > Using another variable along side "non_ll" for the
> > > > > > > > > similar purpose may not have any
> > > > > > > > added advantage.
> > > > > > > >
> > > > > > > > ll_avail can help debug/fine tune how much impact
> > > > > > > > preformance by adjust ll length. And it make code logic clean and
> consistent.
> > > > > > > > also ll_avail can help test corner case when ll item small.
> > > > > > > > Normall case it is
> > > > > > hard to reach ll_max.
> > > > > > > >
> > > > > > >
> > > > > > > Thank you for your suggestion. The ll_max is max limit on
> > > > > > > the descriptors that can be accommodated on the device side
> > > > > > > DDR. The ll_avail
> > > > > > will always be less than ll_max.
> > > > > > > The optimization being referred can be tried without even
> > > > > > > having to declare the ll_avail cause the number of
> > > > > > > descriptors given can be controlled by the DMA client based
> > > > > > > on the length argument to the
> > > > > > dmaengine_prep_* APIs.
> > > > > >
> > > > > > I optimzied it to allow dynatmic appended dma descriptors.
> > > > > >
> > > > > > https://lore.kernel.org/dmaengine/20260109-edma_dymatic-v1-0-
> > > > > > 9a98c9c98536@nxp.com/T/#t
> > > > > >
> > > > > > > So, the use of dynamic ll_avail is not necessarily required.
> > > > > > > Without increasing the ll_max, ll_avail cannot be increased.
> > > > > > > In order to increase ll_max one may need to alter size and
> > > > > > > recompile this
> > > > driver.
> > > > > > >
> > > > > > > However, the requirement of ll_avail does not help for the
> > > > > > > supporting the
> > > > > > non-LL mode.
> > > > > > > For non-LL mode to use:
> > > > > > > 1) Either LL mode shall be not available, as it can happen
> > > > > > > for the Xilinx
> > > IP.
> > > > > > > 2) User provides the preference for non-LL mode.
> > > > > > > For both above, the function calls are different which can
> > > > > > > be differentiated by using the "non_ll" flag. So, even if I
> > > > > > > try to accommodate ll_avail, the call for LL or non-LL would
> > > > > > > be ambiguous as in
> > > > > > case of LL mode also we can have a single descriptor as
> > > > > > similar to non-LL mode.
> > > > > > > Please check the function dw_hdma_v0_core_start() in this
> > > > > > > review where the decision is taken Based on non_ll flag.
> > > > > >
> > > > > > We can treat ll_avail == 1 as no_ll mode because needn't set
> > > > > > extra LL in memory at all.
> > > > > >
> > > > >
> > > > > I analyzed the use of ll_avail but I think the use of this
> > > > > variable does not fit at this location in the code for the following
> reasons:
> > > > > 1. The use of a new variable breaks the continuity for non-LL mode.
> > > > > The
> > > > variable
> > > > >     name non_ll is being used for driving the non-LL mode not
> > > > > only in this file
> > > > but also
> > > > >    in the files relevant to HDMA. This flag gives the clear
> > > > > indication of LL vs
> > > > non-LL mode.
> > > > >    In the function dw_hdma_v0_core_start(), non_ll decided the
> > > > > mode to
> > > > choose.
> > > > > 2. The use of "ll_avail" is ambiguous for both the modes. First,
> > > > > it is
> > > > associated with LL mode only.
> > > > >      It will be used for optimizing / testing the Controller
> > > > > performance based
> > > > on the
> > > > >     number of descriptors available on the Device DDR side which
> > > > > is for LL
> > > > mode. So when
> > > > >     it is being used for LL mode then it is obvious that it
> > > > > excludes any use for
> > > > non-LL mode.
> > > > >     In the API dw_edma_device_transfer(), the ll_avail will be
> > > > > used for
> > > > creating the bursts.
> > > > >     If ll_avail = 1 in the above API, then it is ambiguous
> > > > > whether it is creating
> > > > the burst for
> > > > >      LL or non-LL mode. In the above API, the non_ll is
> > > > > sufficient to have the
> > > > LL mode
> > > > >      and non-LL burst allocation related piece of code.
> > > >
> > > > If really like non-ll, you'd better set ll_avail = 1 in prepare code.
> > > > Normal case ll_avail should be ll_max. It will reduce if-else
> > > > branch in prep dma burst code.
> > > >
> > >
> > > I think we are not on the same page, and it is creating confusion.
> > > If non_ll flag is being used to differentiate between the modes,
> > > then in this scenario the use of ll_avail does not fit any
> > > requirement related to differentiation of different modes. In the
> > > last response, I pointed out that ll_avail, if used, creates
> > > ambiguity rather than bringing clarity for both LL & non-LL mode. If
> > > non_ll flag is used and initialized properly then this is sufficient to drive
> the execution for non-LL mode.
> > >
> > > In the function doing the preparation, there also no if-else clause
> > > is introduced rather the same "if" condition is extended to support
> > > the non-LL mode.
> > >
> > > Could you elaborate what is the approach using ll_avail if I need to
> > > maintain the continuity of the non-LL context and use non-LL mode
> > > without any ambiguity anywhere, instead of using non_ll flag?
> > > If possible, please give a code snippet. Depending upon the
> > > usability and issue it fixes, I will check its feasibility.
> > >
> > > > +               /*
> > > > +                * For non-LL mode, only a single burst can be handled
> > > > +                * in a single chunk unlike LL mode where multiple bursts
> > > > +                * can be configured in a single chunk.
> > > > +                */
> > > >
> > > > It is actually wrong, current software should handle that. If
> > > > there are multiple bursts, only HEAD of bursts trigger DMA, in irq
> > > > handle, it will auto move to next burst. (only irq latency is
> > > > bigger compared to LL, software's resule is the same).
> > > >
> > > > The code logic is totally equal ll_max = 1, except write differece registers.
> > > >
> > >
> > > Changing the ll_max dynamically for a single request is not
> > > feasible. As pointed out earlier it may require the logic to retain
> > > the initially configured value, during the probe, and then use the
> > > retained value depending on the use-case.
>
> As my previous suggest, add ll_avail, config can set it in [1..ll_max].
> then replace ll_max with ll_avail.
>
> > > Could you elaborate the statement,
> > > " The code logic is totally equal ll_max = 1, except write differece
> registers." ?
>
> My means don't touch actual logic in dw_edma_device_transfer() except
> change ll_max to ll_avail or other variable, with value 1.
>
> Even though you really want to use non_ll, you can use below code
>
> dw_edma_device_transfer()
> {
>
>         size_t avail = no_ll ? 1 : ll_max;
>         ...
>
>         if (chunk->bursts_alloc == avail)
>         ...
> }
>

Thank you for the code illustration, this is clear to me.
I suggest we shall use the bursts_max variable to compare with
chunk->bursts_alloc instead of ll_avail, that way it is easy to convey
the meaning. I tried using ll_avail but it did not suite well with the intent.
The use of ll_avail puts the upper limit on the allocation of bursts which
bursts_max shows well in comparison to ll_avail. Please review the next
update with these suggestions included.

> > >
> > > The irq_handler() for success case calls dw_edma_start_transfer()
> > > which schedules the chunks not bursts.
>
> Current code, it is that. I forget I just change it.
>
> >  The bursts associated with that chunk will
> > > get written to controller DDR area / scheduled (for non-LL) in the
> > > dw_hdma_v0_core_start(), for HDMA.
>
> Original code have unnecessary complex about chunks and burst, which
> actually add overhead.
>
> See my patch to reduce 2 useless malloc()
>
> https://lore.kernel.org/dmaengine/20251212-edma_ll-v1-0-
> fc863d9f5ca3@nxp.com/
>
> > > With this flow, for the non-LL mode each chunk needs to contain a
> > > single burst as controller can handle only one burst at a time in non-LL
> mode.
>
> Non-LL case, you just fill one. The only difference is fill to DDR or registers.
>
> Frank
>

Thanks for the link, it is good improvement.

> > >
> > >
> > > > And anthor important is that,
> > > >
> > > > in dw_edma_device_config() should return if backend is not HDMA.
> > > >
> > >
> > > Thanks, this is a valid concern, will address in the upcoming version.
> > >
> > > > Frank
> > > >
> > > > >
> > > > > I think ll_avail, if used for trying out to optimize / debug the
> > > > > settings related to number of descriptors for LL mode then it
> > > > > should be part of the separate discussion / update not related to non-
> LL.
> > > > >
> > > > > > >
> > > > > > > > >
> > > > > > > > > > >
> > > > > > > > > > > > >
> > > > > > > > ...
> > > > > > > > > > > > > +
> > > > > > > > > > > > > + ll_block->bar);
> > > > > > > > > > > >
> > > > > > > > > > > > This change need do prepare patch, which only
> > > > > > > > > > > > change
> > > > > > > > > > > > pci_bus_address() to dw_edma_get_phys_addr().
> > > > > > > > > > > >
> > > > > > > > > > >
> > > > > > > > > > > This is not clear.
> > > > > > > > > >
> > > > > > > > > > why not. only trivial add helper patch, which help
> > > > > > > > > > reviewer
> > > > > > > > > >
> > > > > > > > >
> > > > > > > > > I was not clear on the question you asked.
> > > > > > > > > It does not look justified when a patch is raised alone
> > > > > > > > > just to replace this
> > > > > > > > function.
> > > > > > > > > The function change is required cause the same code
> > > > > > > > > *can* support different IPs from different vendors. And,
> > > > > > > > > with this single change alone in the code the support
> > > > > > > > > for another IP is added. That's why it is easier to get
> > > > > > > > > the reason for the change in the function name and
> > > > > > syntax.
> > > > > > > >
> > > > > > > > Add replace pci_bus_address() with dw_edma_get_phys_addr()
> > > > > > > > to make review easily and get ack for such replacement patches.
> > > > > > > >
> > > > > > > > two patches
> > > > > > > > patch1, just replace exist pci_bus_address() with
> > > > > > > > dw_edma_get_phys_addr() patch2, add new logic in
> > > > > > dw_edma_get_phys_addr() to support new vendor.
> > > > > > > >
> > > > > > >
> > > > > > > I understand your concern about making the review easier.
> > > > > > > However, given that we've been iterating on this patch
> > > > > > > series since September and are now at v9, I believe the
> > > > > > > current approach is justified. The function renames from
> > > > > > > pci_bus_address() to dw_edma_get_phys_addr() is directly
> > > > > > > tied to the non-LL mode functionality being added - it's
> > > > > > > needed because the same code now supports different IPs from
> different vendors.
> > > > > > >
> > > > > > > Splitting this into a separate preparatory patch at this
> > > > > > > stage would further delay the review process. The change is
> > > > > > > kind of straightforward and the context is clear within the current
> patch.
> > > > > > > I request
> > > > > > you to review this patch to avoid additional review cycles.
> > > > > > >
> > > > > > > This also increases the work related to testing and
> > > > > > > maintaining multiple
> > > > > > patches.
> > > > > > > I have commitment for delivery of this, and I can see adding
> > > > > > > one more series definitely add 3-4 months of review cycle from
> here.
> > > > > > > Please excuse me but this code has already
> > > > > >
> > > > > > Thank you for your persevere.
> > > > > >
> > > > >
> > > > > Thank you for your support.
> > > > >
> > > > > > > been reviewed extensively by other reviewers and almost by
> > > > > > > you as well. You can check the detailed discussion wrt this
> > > > > > > function at the following
> > > > > > link:
> > > > > > >
> > > > > >
> > > >
> > >
> https://lore.kernel.org/all/SA1PR12MB8120341DFFD56D90EAD70EDE9514A@
> > > > > > SA1
> > > > > > > PR12MB8120.namprd12.prod.outlook.com/
> > > > > > >
> > > > > >
> > > > > > But still not got reviewed by tags. The recently,  Manivannan
> > > > > > Sadhasivam , Niklas Cassel and me active worked on this driver.
> > > > > > You'd better to get their feedback. Bjorn as pci maintainer to
> > > > > > provide
> > > > generally feedback.
> > > > > >
> > > > >
> > > > > Hi Manivannan Sadhasivam, Vinod Koul and Bjorn Helgaas Could you
> > > > > please provide your feedback on the patch?
> > > > > You have reviewed these patches extensively on the previous
> > > > > versions of
> > > > the same series.
> > > > >
> > > > > Regards,
> > > > > Devendra
> > > > >
> > > > > >
> > > > > > > > >
> > > > > > > > > > >
> > > > > > > > > > > > >               ll_region->paddr += ll_block->off;
> > > > > > > > > > > > >               ll_region->sz = ll_block->sz;
> > > > > > > > > > > > >
> > > > > > > > ...
> > > > > > > > > > > > >
> > > > > > > > > > > > > +static void dw_hdma_v0_core_non_ll_start(struct
> > > > > > > > > > > > > +dw_edma_chunk
> > > > > > > > > > > > *chunk)
> > > > > > > > > > > > > +{
> > > > > > > > > > > > > +     struct dw_edma_chan *chan = chunk->chan;
> > > > > > > > > > > > > +     struct dw_edma *dw = chan->dw;
> > > > > > > > > > > > > +     struct dw_edma_burst *child;
> > > > > > > > > > > > > +     u32 val;
> > > > > > > > > > > > > +
> > > > > > > > > > > > > +     list_for_each_entry(child,
> > > > > > > > > > > > > + &chunk->burst->list,
> > > > > > > > > > > > > + list) {
> > > > > > > > > > > >
> > > > > > > > > > > > why need iterated list, it doesn't support ll.
> > > > > > > > > > > > Need wait for irq to start next
> > > > > > > > > > one.
> > > > > > > > > > > >
> > > > > > > > > > > > Frank
> > > > > > > > > > >
> > > > > > > > > > > Yes, this is true. The format is kept similar to LL mode.
> > > > > > > > > >
> > > > > > > > > > Just fill one. list_for_each_entry() cause confuse.
> > > > > > > > > >
> > > > > > > > > > Frank
> > > > > > > > >
> > > > > > > > > I see, we can use list_first_entry_or_null() which is
> > > > > > > > > dependent on giving the type of pointer, compared to
> > > > > > > > > this
> > > > > > > > > list_for_each_entry() looks neat and agnostic to the
> > > > > > > > > pointer type being used. Though, it can be
> > > > > > > > explored further.
> > > > > > > > > Also, when the chunk is allocated, the comment clearly
> > > > > > > > > spells out how the allocation would be for the non LL
> > > > > > > > > mode so it is evident that each chunk would have single
> > > > > > > > > entry and with that understanding it is clear that loop
> > > > > > > > > will also be used in a similar manner, to retrieve a
> > > > > > > > > single entry. It is a similar use case as of "do {}while
> > > > > > > > > (0)" albeit needs a context to
> > > > > > > > understand it.
> > > > > > > >
> > > > > > > > I don't think so. list_for_each_entry() is miss leading to
> > > > > > > > reader think it is not only to one item in burst list, and
> > > > > > > > use polling method to to finish many burst transfer.
> > > > > > > >
> > > > > > > > list_for_each_entry() {
> > > > > > > >         ...
> > > > > > > >         readl_timeout()
> > > > > > > > }
> > > > > > > >
> > > > > > > > Generally, EDMA is very quick, polling is much quicker
> > > > > > > > than irq if data is
> > > > > > small.
> > > > > > > >
> > > > > > > > Frank
> > > > > > > >
> > > > > > >
> > > > > > > The polling is not required. The single burst will raise the
> > > > > > > interrupt and from the interrupt context another chunk will
> > > > > > > be scheduled. This cycle repeats till all the chunks with
> > > > > > > single burst are
> > > > exhausted.
> > > > > > >
> > > > > > > The following comment made in function
> > > > > > > dw_edma_device_transfer() in the same patch makes it amply
> > > > > > > clear that only a single burst would be
> > > > > > handled for the non-LL mode.
> > > > > > > +       /*
> > > > > > > +        * For non-LL mode, only a single burst can be handled
> > > > > > > +        * in a single chunk unlike LL mode where multiple bursts
> > > > > > > +        * can be configured in a single chunk.
> > > > > > > +        */
> > > > > > >
> > > > > > > Looking at the way bursts are appended to chunks and chunks
> > > > > > > in
> > > > > > > dw_edma_device_transfer() are scheduled for non-LL mode then
> > > > > > > it is clear
> > > > > > what non-LL mode would handle in terms of bursts.
> > > > > > > I just kept the format to match it with the LL mode format
> > > > > > > otherwise there is no need of this comment and we can follow
> > > > > > > the syntax for a single
> > > > > > entry alone.
> > > > > > > Please share your suggestion if these descriptions fail to
> > > > > > > provide the clear
> > > > > > context and intent.
> > > > > >
> > > > > > Avoid use list_for_each_entry() here to prevent miss-leading.
> > > > > >
> > > > > > Frank
> > > > > >
> > > > >
> > > > > Sure, thanks, I will push this change in next version.
> > > > >
> > > > > > >
> > > > > > > > >
> > > > > > > > > > >
> > > > > > > > > > > >
> > > > > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id,
> > > > > > > > > > > > > + ch_en, HDMA_V0_CH_EN);
> > > > > > > > > > > > > +
> > > > > > > > > > > > > +             /* Source address */
> > > > > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, sar.lsb,
> > > > > > > > > > > > > +                       lower_32_bits(child->sar));
> > > > > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, sar.msb,
> > > > > > > > > > > > > +
> > > > > > > > > > > > > + upper_32_bits(child->sar));
> > > > > > > > > > > > > +
> > > > > > > > > > > > > +             /* Destination address */
> > > > > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, dar.lsb,
> > > > > > > > > > > > > +                       lower_32_bits(child->dar));
> > > > > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, dar.msb,
> > > > > > > > > > > > > +
> > > > > > > > > > > > > + upper_32_bits(child->dar));
> > > > > > > > > > > > > +
> > > > > > > > > > > > > +             /* Transfer size */
> > > > > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id,
> > > > > > > > > > > > > + transfer_size,
> > > > > > > > > > > > > + child->sz);
> > > > > > > > > > > > > +
> > > > > > > > > > > > > +             /* Interrupt setup */
> > > > > > > > > > > > > +             val = GET_CH_32(dw, chan->dir,
> > > > > > > > > > > > > + chan->id, int_setup)
> > > > |
> > > > > > > > > > > > > +                             HDMA_V0_STOP_INT_MASK |
> > > > > > > > > > > > > +
> > > > > > > > > > > > > + HDMA_V0_ABORT_INT_MASK |
> > > > > > > > > > > > > +
> > > > > > > > > > > > > + HDMA_V0_LOCAL_STOP_INT_EN |
> > > > > > > > > > > > > +
> > > > > > > > > > > > > + HDMA_V0_LOCAL_ABORT_INT_EN;
> > > > > > > > > > > > > +
> > > > > > > > > > > > > +             if (!(dw->chip->flags &
> DW_EDMA_CHIP_LOCAL)) {
> > > > > > > > > > > > > +                     val |= HDMA_V0_REMOTE_STOP_INT_EN |
> > > > > > > > > > > > > +                            HDMA_V0_REMOTE_ABORT_INT_EN;
> > > > > > > > > > > > > +             }
> > > > > > > > > > > > > +
> > > > > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id,
> > > > > > > > > > > > > + int_setup, val);
> > > > > > > > > > > > > +
> > > > > > > > > > > > > +             /* Channel control setup */
> > > > > > > > > > > > > +             val = GET_CH_32(dw, chan->dir, chan->id,
> control1);
> > > > > > > > > > > > > +             val &= ~HDMA_V0_LINKLIST_EN;
> > > > > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id,
> > > > > > > > > > > > > + control1, val);
> > > > > > > > > > > > > +
> > > > > > > > > > > > > +             SET_CH_32(dw, chan->dir, chan->id, doorbell,
> > > > > > > > > > > > > +                       HDMA_V0_DOORBELL_START);
> > > > > > > > > > > > > +     }
> > > > > > > > > > > > > +}
> > > > > > > > > > > > > +
> > > > > > > > > > > > > +static void dw_hdma_v0_core_start(struct
> > > > > > > > > > > > > +dw_edma_chunk *chunk, bool
> > > > > > > > > > > > > +first) {
> > > > > > > > > > > > > +     struct dw_edma_chan *chan = chunk->chan;
> > > > > > > > > > > > > +
> > > > > > > > > > > > > +     if (chan->non_ll)
> > > > > > > > > > > > > +             dw_hdma_v0_core_non_ll_start(chunk);
> > > > > > > > > > > > > +     else
> > > > > > > > > > > > > +             dw_hdma_v0_core_ll_start(chunk,
> > > > > > > > > > > > > + first); }
> > > > > > > > > > > > > +
> > > > > > > > > > > > >  static void dw_hdma_v0_core_ch_config(struct
> > > > > > > > > > > > > dw_edma_chan
> > > > > > > > > > > > > *chan)
> > > > > > > > > > {
> > > > > > > > > > > > >       struct dw_edma *dw = chan->dw; diff --git
> > > > > > > > > > > > > a/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > > > > > > > > b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > > > > > > > > index eab5fd7..7759ba9 100644
> > > > > > > > > > > > > --- a/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > > > > > > > > +++ b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
> > > > > > > > > > > > > @@ -12,6 +12,7 @@  #include <linux/dmaengine.h>
> > > > > > > > > > > > >
> > > > > > > > > > > > >  #define HDMA_V0_MAX_NR_CH                    8
> > > > > > > > > > > > > +#define HDMA_V0_CH_EN                                BIT(0)
> > > > > > > > > > > > >  #define HDMA_V0_LOCAL_ABORT_INT_EN           BIT(6)
> > > > > > > > > > > > >  #define HDMA_V0_REMOTE_ABORT_INT_EN
> BIT(5)
> > > > > > > > > > > > >  #define HDMA_V0_LOCAL_STOP_INT_EN            BIT(4)
> > > > > > > > > > > > > diff --git a/include/linux/dma/edma.h
> > > > > > > > > > > > > b/include/linux/dma/edma.h index
> > > > > > > > > > > > > 3080747..78ce31b
> > > > > > > > > > > > > 100644
> > > > > > > > > > > > > --- a/include/linux/dma/edma.h
> > > > > > > > > > > > > +++ b/include/linux/dma/edma.h
> > > > > > > > > > > > > @@ -99,6 +99,7 @@ struct dw_edma_chip {
> > > > > > > > > > > > >       enum dw_edma_map_format mf;
> > > > > > > > > > > > >
> > > > > > > > > > > > >       struct dw_edma          *dw;
> > > > > > > > > > > > > +     bool                    non_ll;
> > > > > > > > > > > > >  };
> > > > > > > > > > > > >
> > > > > > > > > > > > >  /* Export to the platform drivers */
> > > > > > > > > > > > > --
> > > > > > > > > > > > > 1.8.3.1
> > > > > > > > > > > > >
> >

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

end of thread, other threads:[~2026-02-04 10:50 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-09 12:03 [PATCH v8 0/2] Add AMD MDB Endpoint and non-LL mode Support Devendra K Verma
2026-01-09 12:03 ` [PATCH v8 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint Support Devendra K Verma
2026-01-15 16:21   ` Frank Li
2026-01-19  9:09     ` Verma, Devendra
2026-01-19 15:59       ` Frank Li
2026-01-20 11:36         ` Verma, Devendra
2026-01-21 16:54           ` Frank Li
2026-01-23 14:26             ` Verma, Devendra
2026-01-09 12:03 ` [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode Devendra K Verma
2026-01-15 16:37   ` Frank Li
2026-01-19  9:09     ` Verma, Devendra
2026-01-19 15:45       ` Frank Li
2026-01-20 11:36         ` Verma, Devendra
2026-01-21 17:18           ` Frank Li
2026-01-23 14:26             ` Verma, Devendra
2026-01-26 19:00               ` Frank Li
2026-01-28 12:59                 ` Verma, Devendra
2026-01-28 15:07                   ` Frank Li
2026-01-29 11:55                     ` Verma, Devendra
2026-02-03 12:03                       ` Verma, Devendra
2026-02-03 16:52                         ` Frank Li
2026-02-04 10:50                           ` Verma, Devendra
2026-01-16  2:36   ` kernel test robot
2026-01-16  3:59   ` kernel test robot

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