Linux PCI subsystem development
 help / color / mirror / Atom feed
* [PATCH v15 0/2] Add AMD MDB Endpoint and non-LL mode Support
@ 2026-03-18  7:04 Devendra K Verma
  2026-03-18  7:04 ` [PATCH v15 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint Support Devendra K Verma
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: Devendra K Verma @ 2026-03-18  7:04 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    |  47 +++++-
 drivers/dma/dw-edma/dw-edma-core.h    |   1 +
 drivers/dma/dw-edma/dw-edma-pcie.c    | 220 +++++++++++++++++++++++---
 drivers/dma/dw-edma/dw-hdma-v0-core.c |  64 +++++++-
 drivers/dma/dw-edma/dw-hdma-v0-regs.h |   1 +
 include/linux/dma/edma.h              |   1 +
 6 files changed, 312 insertions(+), 22 deletions(-)


base-commit: a989fde763f4f24209e4702f50a45be572340e68
-- 
2.43.0


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

* [PATCH v15 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint Support
  2026-03-18  7:04 [PATCH v15 0/2] Add AMD MDB Endpoint and non-LL mode Support Devendra K Verma
@ 2026-03-18  7:04 ` Devendra K Verma
  2026-03-18  7:04 ` [PATCH v15 2/2] dmaengine: dw-edma: Add non-LL mode Devendra K Verma
  2026-03-18 11:27 ` [PATCH v15 0/2] Add AMD MDB Endpoint and non-LL mode Support Vinod Koul
  2 siblings, 0 replies; 8+ messages in thread
From: Devendra K Verma @ 2026-03-18  7:04 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>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
Changes in v15:
Rebased the branch

Changes in v14:
No Changes

Changes in v13:
No Changes

Changes in v12:
No Changes

Changes in v11:
Replaced min_t() function with min().

Changes in v10:
For Xilinx VSEC function kept only HDMA map format as
Xilinx only supports HDMA.

Changes in v9:
Moved Xilinx specific VSEC capability functions under
the vendor ID condition.

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 | 190 ++++++++++++++++++++++++++---
 1 file changed, 176 insertions(+), 14 deletions(-)

diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c b/drivers/dma/dw-edma/dw-edma-pcie.c
index 83230acaa597..0cb5850ca411 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 @@ static const struct dw_edma_pcie_data snps_edda_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 const struct dw_edma_plat_ops dw_edma_pcie_plat_ops = {
 	.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,64 @@ 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_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(pdata->wr_ch_cnt,
+			       FIELD_GET(DW_PCIE_XILINX_MDB_VSEC_DMA_WR_CH, val));
+	pdata->rd_ch_cnt = min(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 +322,29 @@ 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);
+
+	if (pdev->vendor == PCI_VENDOR_ID_XILINX) {
+		dw_edma_pcie_get_xilinx_dma_data(pdev, vsec_data);
+
+		/*
+		 * 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 +527,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);
-- 
2.43.0


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

* [PATCH v15 2/2] dmaengine: dw-edma: Add non-LL mode
  2026-03-18  7:04 [PATCH v15 0/2] Add AMD MDB Endpoint and non-LL mode Support Devendra K Verma
  2026-03-18  7:04 ` [PATCH v15 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint Support Devendra K Verma
@ 2026-03-18  7:04 ` Devendra K Verma
  2026-06-17  3:17   ` Koichiro Den
  2026-03-18 11:27 ` [PATCH v15 0/2] Add AMD MDB Endpoint and non-LL mode Support Vinod Koul
  2 siblings, 1 reply; 8+ messages in thread
From: Devendra K Verma @ 2026-03-18  7:04 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>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
Changes in v15
   Rebased the branch

Changes in v14
   Corrected trailing space error during merge.

Changes in v13
   No Changes

Changes in v12
  Simplified the logic to set the channel non_ll flag

Changes in v11
  Added error check for the following
    If user enables LL mode when non-LL is default mode.
    If config_param provided by user for eDMA
  Renamed the 'non_ll' flag in dw_edma_chip to 'cfg_non_ll'

Changes in v10
  Added the peripheral_config check only for HDMA IP in
  dw_edma_device_config().
  Replaced the loop with single entry retrieval for non-LL
  mode.
  Addressed review comments and handled the burst allocation
  by defining 'bursts_max' as per suggestions.

Changes in v9
  Fixed compilation errors related to macro name mismatch.

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    | 47 +++++++++++++++++++-
 drivers/dma/dw-edma/dw-edma-core.h    |  1 +
 drivers/dma/dw-edma/dw-edma-pcie.c    | 44 ++++++++++++------
 drivers/dma/dw-edma/dw-hdma-v0-core.c | 64 ++++++++++++++++++++++++++-
 drivers/dma/dw-edma/dw-hdma-v0-regs.h |  1 +
 include/linux/dma/edma.h              |  1 +
 6 files changed, 143 insertions(+), 15 deletions(-)

diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index e7d698b352d3..bcfee4e0718a 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -223,6 +223,43 @@ static int dw_edma_device_config(struct dma_chan *dchan,
 				 struct dma_slave_config *config)
 {
 	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
+	bool cfg_non_ll;
+	int non_ll = 0;
+
+	chan->non_ll = false;
+	if (chan->dw->chip->mf == EDMA_MF_HDMA_NATIVE) {
+		if (config->peripheral_config &&
+		    config->peripheral_size != sizeof(int)) {
+			dev_err(dchan->device->dev,
+				"config param peripheral size mismatch\n");
+			return -EINVAL;
+		}
+
+		/*
+		 * 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.
+		 */
+		cfg_non_ll = chan->dw->chip->cfg_non_ll;
+		if (config->peripheral_config) {
+			non_ll = *(int *)config->peripheral_config;
+
+			if (cfg_non_ll && !non_ll) {
+				dev_err(dchan->device->dev, "invalid configuration\n");
+				return -EINVAL;
+			}
+		}
+
+		if (cfg_non_ll || non_ll)
+			chan->non_ll = true;
+	} else if (config->peripheral_config) {
+		dev_err(dchan->device->dev,
+			"peripheral config param applicable only for HDMA\n");
+		return -EINVAL;
+	}
 
 	memcpy(&chan->config, config, sizeof(*config));
 	chan->configured = true;
@@ -358,6 +395,7 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer)
 	struct dw_edma_desc *desc;
 	u64 src_addr, dst_addr;
 	size_t fsz = 0;
+	u32 bursts_max;
 	u32 cnt = 0;
 	int i;
 
@@ -415,6 +453,13 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer)
 		return NULL;
 	}
 
+	/*
+	 * 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.
+	 */
+	bursts_max = chan->non_ll ? 1 : chan->ll_max;
+
 	desc = dw_edma_alloc_desc(chan);
 	if (unlikely(!desc))
 		goto err_alloc;
@@ -450,7 +495,7 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer)
 		if (xfer->type == EDMA_XFER_SCATTER_GATHER && !sg)
 			break;
 
-		if (chunk->bursts_alloc == chan->ll_max) {
+		if (chunk->bursts_alloc == bursts_max) {
 			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 71894b9e0b15..c8e3d196a549 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 0cb5850ca411..0b30ce138503 100644
--- a/drivers/dma/dw-edma/dw-edma-pcie.c
+++ b/drivers/dma/dw-edma/dw-edma-pcie.c
@@ -295,6 +295,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)
 {
@@ -303,6 +312,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;
 
 	struct dw_edma_pcie_data *vsec_data __free(kfree) =
 		kmalloc_obj(*vsec_data);
@@ -329,21 +339,24 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
 
 		/*
 		 * 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;
+			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_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 */
@@ -391,6 +404,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->cfg_non_ll = non_ll;
 
 	chip->ll_wr_cnt = vsec_data->wr_ch_cnt;
 	chip->ll_rd_cnt = vsec_data->rd_ch_cnt;
@@ -399,7 +413,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];
@@ -410,7 +424,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;
 
@@ -419,12 +434,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];
@@ -435,7 +451,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;
 
@@ -444,7 +461,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 e3f8db4fe909..d2b622250430 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,68 @@ 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;
+
+	child = list_first_entry_or_null(&chunk->burst->list,
+					 struct dw_edma_burst, list);
+	if (!child)
+		return;
+
+	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 eab5fd7177e5..7759ba9b4850 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 270b5458aecf..61d6064fcfed 100644
--- a/include/linux/dma/edma.h
+++ b/include/linux/dma/edma.h
@@ -97,6 +97,7 @@ struct dw_edma_chip {
 	enum dw_edma_map_format	mf;
 
 	struct dw_edma		*dw;
+	bool			cfg_non_ll;
 };
 
 /* Export to the platform drivers */
-- 
2.43.0


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

* Re: [PATCH v15 0/2] Add AMD MDB Endpoint and non-LL mode Support
  2026-03-18  7:04 [PATCH v15 0/2] Add AMD MDB Endpoint and non-LL mode Support Devendra K Verma
  2026-03-18  7:04 ` [PATCH v15 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint Support Devendra K Verma
  2026-03-18  7:04 ` [PATCH v15 2/2] dmaengine: dw-edma: Add non-LL mode Devendra K Verma
@ 2026-03-18 11:27 ` Vinod Koul
  2 siblings, 0 replies; 8+ messages in thread
From: Vinod Koul @ 2026-03-18 11:27 UTC (permalink / raw)
  To: bhelgaas, mani, Devendra K Verma
  Cc: dmaengine, linux-pci, linux-kernel, michal.simek, Devendra.Verma


On Wed, 18 Mar 2026 12:34:01 +0530, Devendra K Verma wrote:
> 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.
> 
> [...]

Applied, thanks!

[1/2] dmaengine: dw-edma: Add AMD MDB Endpoint Support
      commit: 14eb9a1d338fdc301a2297af86818ecf716b1539
[2/2] dmaengine: dw-edma: Add non-LL mode
      commit: b7560798466a07d9c3fb011698e92c335ab28baf

Best regards,
-- 
~Vinod



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

* Re: [PATCH v15 2/2] dmaengine: dw-edma: Add non-LL mode
  2026-03-18  7:04 ` [PATCH v15 2/2] dmaengine: dw-edma: Add non-LL mode Devendra K Verma
@ 2026-06-17  3:17   ` Koichiro Den
  2026-06-17 11:32     ` Verma, Devendra
  2026-06-17 11:43     ` Verma, Devendra
  0 siblings, 2 replies; 8+ messages in thread
From: Koichiro Den @ 2026-06-17  3:17 UTC (permalink / raw)
  To: Devendra K Verma
  Cc: Frank.Li, bhelgaas, mani, vkoul, dmaengine, linux-pci,
	linux-kernel, michal.simek

On Wed, Mar 18, 2026 at 12:34:03PM +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
>   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>
> Reviewed-by: Frank Li <Frank.Li@nxp.com>
> ---
> Changes in v15
>    Rebased the branch
> 
[snip]
> +
> +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)

Hi Devendra (cc: Frank),

Sorry for dropping a comment now that this has already landed.

I'm wondering about the lifetime of chan->non_ll. This patch lets a client
select non-LL mode through dma_slave_config.peripheral_config for a transfer,
but the state is stored on the channel.

We use chan->non_ll in prep to choose bursts_max, then read it again later in
dw_hdma_v0_core_start() to choose the LL vs non-LL start path. If the channel is
reconfigured between prep and start, or before a later chunk is started from the
interrupt path, couldn't we start a descriptor in a different mode from the one
it was prepared for?

(Note: Frank's not-yet-merged dma_prep_config v7 series [1] also looks at
potential races around config+prep on the same channel from multiple process
contexts, as I understand it. But this seems like a separate issue, since the
state is read again at transfer start time.)

Should non_ll be snapshotted into the descriptor/chunk, maybe as
dw_edma_desc.non_ll, or is the rule that clients must not reconfigure the
channel while anything is pending/running?

Or was this already discussed, and there is some implicit restriction that
clients must not mix LL and temporary non-LL requests from multiple contexts on
the same channel?

[1] https://lore.kernel.org/dmaengine/20260521-dma_prep_config-v7-0-1f73f4899883@nxp.com/

Thanks,
Koichiro

> +		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 eab5fd7177e5..7759ba9b4850 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 270b5458aecf..61d6064fcfed 100644
> --- a/include/linux/dma/edma.h
> +++ b/include/linux/dma/edma.h
> @@ -97,6 +97,7 @@ struct dw_edma_chip {
>  	enum dw_edma_map_format	mf;
>  
>  	struct dw_edma		*dw;
> +	bool			cfg_non_ll;
>  };
>  
>  /* Export to the platform drivers */
> -- 
> 2.43.0
> 

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

* Re: [PATCH v15 2/2] dmaengine: dw-edma: Add non-LL mode
  2026-06-17  3:17   ` Koichiro Den
@ 2026-06-17 11:32     ` Verma, Devendra
  2026-06-17 11:43     ` Verma, Devendra
  1 sibling, 0 replies; 8+ messages in thread
From: Verma, Devendra @ 2026-06-17 11:32 UTC (permalink / raw)
  To: Koichiro Den, Devendra K Verma
  Cc: Frank.Li, bhelgaas, mani, vkoul, dmaengine, linux-pci,
	linux-kernel, michal.simek, Verma, Devendra

Hi Koichiro

Please check my comments inline.

Regards,
Devendra

On 17-Jun-26 08:47, Koichiro Den wrote:
> On Wed, Mar 18, 2026 at 12:34:03PM +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
>>    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>
>> Reviewed-by: Frank Li <Frank.Li@nxp.com>
>> ---
>> Changes in v15
>>     Rebased the branch
>>
> [snip]
>> +
>> +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)
> 
> Hi Devendra (cc: Frank),
> 
> Sorry for dropping a comment now that this has already landed.
> 
> I'm wondering about the lifetime of chan->non_ll. This patch lets a client
> select non-LL mode through dma_slave_config.peripheral_config for a transfer,
> but the state is stored on the channel.
> 
> We use chan->non_ll in prep to choose bursts_max, then read it again later in
> dw_hdma_v0_core_start() to choose the LL vs non-LL start path. If the channel is
> reconfigured between prep and start, or before a later chunk is started from the
> interrupt path, couldn't we start a descriptor in a different mode from the one
> it was prepared for?
> 

The mode is implemented with the intention that after prep, the 
submitted descriptor shall completed with the chosen mode. So, yes the 
mode is decided in the prep call and all the subsequent descriptors are 
completed with the chosen mode unless it is overridden by another prep call.

> (Note: Frank's not-yet-merged dma_prep_config v7 series [1] also looks at
> potential races around config+prep on the same channel from multiple process
> contexts, as I understand it. But this seems like a separate issue, since the
> state is read again at transfer start time.)
> 
> Should non_ll be snapshotted into the descriptor/chunk, maybe as
> dw_edma_desc.non_ll, or is the rule that clients must not reconfigure the
> channel while anything is pending/running?
> 
> Or was this already discussed, and there is some implicit restriction that
> clients must not mix LL and temporary non-LL requests from multiple contexts on
> the same channel?

I am not aware of any such rule which specifies that modes can not be 
mixed but it would not be a good idea to mix both. Let me give an 
example, in the non-LL mode the channels *can* utilize the LL-regions 
for data transfers. If for such a non-LL data transfer where LL-region 
is used and intended by the user then changing the mode after setting up 
the mode to another one can cause data corruption.
Eg:
Channel LL-region = ADDR
Mode set to non-LL -> DDR destination to ADDR to (ADDR + size)
First non-LL burst -> writes data to ADDR till size bytes.
Second burst configured for LL -> overwrites the data at ADDR with 
descriptor information.

This one causes the data corruption for the first burst.

> 
> [1] https://lore.kernel.org/dmaengine/20260521-dma_prep_config-v7-0-1f73f4899883@nxp.com/
> 
> Thanks,
> Koichiro
> 
>> +		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 eab5fd7177e5..7759ba9b4850 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 270b5458aecf..61d6064fcfed 100644
>> --- a/include/linux/dma/edma.h
>> +++ b/include/linux/dma/edma.h
>> @@ -97,6 +97,7 @@ struct dw_edma_chip {
>>   	enum dw_edma_map_format	mf;
>>   
>>   	struct dw_edma		*dw;
>> +	bool			cfg_non_ll;
>>   };
>>   
>>   /* Export to the platform drivers */
>> -- 
>> 2.43.0
>>


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

* Re: [PATCH v15 2/2] dmaengine: dw-edma: Add non-LL mode
  2026-06-17  3:17   ` Koichiro Den
  2026-06-17 11:32     ` Verma, Devendra
@ 2026-06-17 11:43     ` Verma, Devendra
  2026-06-17 13:51       ` Frank Li
  1 sibling, 1 reply; 8+ messages in thread
From: Verma, Devendra @ 2026-06-17 11:43 UTC (permalink / raw)
  To: Koichiro Den, Devendra K Verma
  Cc: Frank.Li, bhelgaas, mani, vkoul, dmaengine, linux-pci,
	linux-kernel, michal.simek, Verma, Devendra

Hi Koichiro

My first reply was auto-formatted as per the column limit but got
expanded after I sent it.
Re-sending the reply with correct formatting.

Please excuse for the spamming!
regards,
Devendra

On 17-Jun-26 08:47, Koichiro Den wrote:
> On Wed, Mar 18, 2026 at 12:34:03PM +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
>>    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>
>> Reviewed-by: Frank Li <Frank.Li@nxp.com>
>> ---
>> Changes in v15
>>     Rebased the branch
>>
> [snip]
>> +
>> +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)
> 
> Hi Devendra (cc: Frank),
> 
> Sorry for dropping a comment now that this has already landed.
> 
> I'm wondering about the lifetime of chan->non_ll. This patch lets a client
> select non-LL mode through dma_slave_config.peripheral_config for a transfer,
> but the state is stored on the channel.
> 
> We use chan->non_ll in prep to choose bursts_max, then read it again later in
> dw_hdma_v0_core_start() to choose the LL vs non-LL start path. If the channel is
> reconfigured between prep and start, or before a later chunk is started from the
> interrupt path, couldn't we start a descriptor in a different mode from the one
> it was prepared for?
> 

The mode is implemented with the intention that after prep, the
submitted descriptor shall completed with the chosen mode. So, yes the
mode is decided in the prep call and all the subsequent descriptors are
completed with the chosen mode unless it is overridden by another prep
call.

> (Note: Frank's not-yet-merged dma_prep_config v7 series [1] also looks at
> potential races around config+prep on the same channel from multiple process
> contexts, as I understand it. But this seems like a separate issue, since the
> state is read again at transfer start time.)
> 
> Should non_ll be snapshotted into the descriptor/chunk, maybe as
> dw_edma_desc.non_ll, or is the rule that clients must not reconfigure the
> channel while anything is pending/running?
> 

I am not aware of any such rule which specifies that modes can not be
mixed but it would not be a good idea to mix both. Let me give an
example, in the non-LL mode the channels *can* utilize the LL-regions
for data transfers. If for such a non-LL data transfer where LL-region
is used and intended by the user then changing the mode after setting up
the mode to another one can cause data corruption.

Eg:
Channel LL-region = ADDR
Mode set to non-LL -> DDR destination to ADDR to (ADDR + size)
First non-LL burst -> writes data to ADDR till size bytes.
Second burst configured for LL -> overwrites the data at ADDR with
descriptor information.

This one causes the data corruption for the first burst


> Or was this already discussed, and there is some implicit restriction that
> clients must not mix LL and temporary non-LL requests from multiple contexts on
> the same channel?
> 
> [1] https://lore.kernel.org/dmaengine/20260521-dma_prep_config-v7-0-1f73f4899883@nxp.com/
> 
> Thanks,
> Koichiro
> 
>> +		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 eab5fd7177e5..7759ba9b4850 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 270b5458aecf..61d6064fcfed 100644
>> --- a/include/linux/dma/edma.h
>> +++ b/include/linux/dma/edma.h
>> @@ -97,6 +97,7 @@ struct dw_edma_chip {
>>   	enum dw_edma_map_format	mf;
>>   
>>   	struct dw_edma		*dw;
>> +	bool			cfg_non_ll;
>>   };
>>   
>>   /* Export to the platform drivers */
>> -- 
>> 2.43.0
>>


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

* Re: [PATCH v15 2/2] dmaengine: dw-edma: Add non-LL mode
  2026-06-17 11:43     ` Verma, Devendra
@ 2026-06-17 13:51       ` Frank Li
  0 siblings, 0 replies; 8+ messages in thread
From: Frank Li @ 2026-06-17 13:51 UTC (permalink / raw)
  To: Verma, Devendra
  Cc: Koichiro Den, Devendra K Verma, Frank.Li, bhelgaas, mani, vkoul,
	dmaengine, linux-pci, linux-kernel, michal.simek

On Wed, Jun 17, 2026 at 05:13:32PM +0530, Verma, Devendra wrote:
> [You don't often get email from devverma@amd.com. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
>
> Hi Koichiro
>
> My first reply was auto-formatted as per the column limit but got
> expanded after I sent it.
> Re-sending the reply with correct formatting.
>
> Please excuse for the spamming!
> regards,
> Devendra
>
> On 17-Jun-26 08:47, Koichiro Den wrote:
> > On Wed, Mar 18, 2026 at 12:34:03PM +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
> > >    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>
> > > Reviewed-by: Frank Li <Frank.Li@nxp.com>
> > > ---
> > > Changes in v15
> > >     Rebased the branch
> > >
> > [snip]
> > > +
> > > +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)
> >
> > Hi Devendra (cc: Frank),
> >
> > Sorry for dropping a comment now that this has already landed.
> >
> > I'm wondering about the lifetime of chan->non_ll. This patch lets a client
> > select non-LL mode through dma_slave_config.peripheral_config for a transfer,
> > but the state is stored on the channel.
> >
> > We use chan->non_ll in prep to choose bursts_max, then read it again later in
> > dw_hdma_v0_core_start() to choose the LL vs non-LL start path. If the channel is
> > reconfigured between prep and start, or before a later chunk is started from the
> > interrupt path, couldn't we start a descriptor in a different mode from the one
> > it was prepared for?
> >
>
> The mode is implemented with the intention that after prep, the
> submitted descriptor shall completed with the chosen mode. So, yes the
> mode is decided in the prep call and all the subsequent descriptors are
> completed with the chosen mode unless it is overridden by another prep
> call.
>
> > (Note: Frank's not-yet-merged dma_prep_config v7 series [1] also looks at
> > potential races around config+prep on the same channel from multiple process
> > contexts, as I understand it. But this seems like a separate issue, since the
> > state is read again at transfer start time.)
> >
> > Should non_ll be snapshotted into the descriptor/chunk, maybe as
> > dw_edma_desc.non_ll, or is the rule that clients must not reconfigure the
> > channel while anything is pending/running?
> >
>
> I am not aware of any such rule which specifies that modes can not be
> mixed but it would not be a good idea to mix both. Let me give an
> example, in the non-LL mode the channels *can* utilize the LL-regions
> for data transfers. If for such a non-LL data transfer where LL-region
> is used and intended by the user then changing the mode after setting up
> the mode to another one can cause data corruption.
>
> Eg:
> Channel LL-region = ADDR
> Mode set to non-LL -> DDR destination to ADDR to (ADDR + size)
> First non-LL burst -> writes data to ADDR till size bytes.
> Second burst configured for LL -> overwrites the data at ADDR with
> descriptor information.

I don't think we need mix LL and no-LL, previously config suppose only do
once before prepare and never change again.

But recently there are more user case to config for each prep. After [1]
merge, we can consider more. There should be no risk now if DMA consumer
don't change it during prepare.

Frank

>
> This one causes the data corruption for the first burst
>
>
> > Or was this already discussed, and there is some implicit restriction that
> > clients must not mix LL and temporary non-LL requests from multiple contexts on
> > the same channel?
> >
> > [1] https://lore.kernel.org/dmaengine/20260521-dma_prep_config-v7-0-1f73f4899883@nxp.com/
> >
> > Thanks,
> > Koichiro
> >
> > > +            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 eab5fd7177e5..7759ba9b4850 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 270b5458aecf..61d6064fcfed 100644
> > > --- a/include/linux/dma/edma.h
> > > +++ b/include/linux/dma/edma.h
> > > @@ -97,6 +97,7 @@ struct dw_edma_chip {
> > >      enum dw_edma_map_format mf;
> > >
> > >      struct dw_edma          *dw;
> > > +    bool                    cfg_non_ll;
> > >   };
> > >
> > >   /* Export to the platform drivers */
> > > --
> > > 2.43.0
> > >
>

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

end of thread, other threads:[~2026-06-17 13:51 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-18  7:04 [PATCH v15 0/2] Add AMD MDB Endpoint and non-LL mode Support Devendra K Verma
2026-03-18  7:04 ` [PATCH v15 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint Support Devendra K Verma
2026-03-18  7:04 ` [PATCH v15 2/2] dmaengine: dw-edma: Add non-LL mode Devendra K Verma
2026-06-17  3:17   ` Koichiro Den
2026-06-17 11:32     ` Verma, Devendra
2026-06-17 11:43     ` Verma, Devendra
2026-06-17 13:51       ` Frank Li
2026-03-18 11:27 ` [PATCH v15 0/2] Add AMD MDB Endpoint and non-LL mode Support Vinod Koul

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