Skip to contentSkip to conversation

Add AMD MDB Endpoint and non-LL mode Support

PATCH Superseded Devendra K Verma 3 participants 24 messages Jan 9 – Feb 4

Subsystems (3)

v8 of 15 Superseded Latest is v15 →

lore
Share & tools
lore
#0 lore raw

v8 cover Add AMD MDB Endpoint and non-LL mode Support

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(-)
Signature · 1 line
1.8.3.1
#1 lore raw

PATCH v8 1/2 dmaengine: dw-edma: Add AMD MDB Endpoint Support

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.

---
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(-)
drivers/dma/dw-edma/dw-edma-pcie.c +178 -14
index 3371e0a7..2efd149 100644
@@ -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);
Signature · 1 line
1.8.3.1
#2 lore raw

Re: [PATCH v8 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint Support

Quoted 72 lines · Devendra K 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.

---
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(-)
drivers/dma/dw-edma/dw-edma-pcie.c +1 -1
index 3371e0a7..2efd149 100644
@@ -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

drivers/dma/dw-edma/dw-edma-pcie.c line 26

Quoted 10 lines
drivers/dma/dw-edma/dw-edma-pcie.c +6 -4
-#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'.

drivers/dma/dw-edma/dw-edma-pcie.c line 36

Quoted 10 lines
drivers/dma/dw-edma/dw-edma-pcie.c +10 -0
+
+/* 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

drivers/dma/dw-edma/dw-edma-pcie.c line 40

Quoted 4 lines
drivers/dma/dw-edma/dw-edma-pcie.c +4 -0
+
+#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

Quoted 116 lines
drivers/dma/dw-edma/dw-edma-pcie.c +69 -5
+
+#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;

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev, · line 230

Quoted 10 lines
drivers/dma/dw-edma/dw-edma-pcie.c +3 -3
-	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.

Quoted 72 lines
drivers/dma/dw-edma/dw-edma-pcie.c +61 -0
 	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)
 {

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct pci_dev *pdev, · line 331

Quoted 8 lines
drivers/dma/dw-edma/dw-edma-pcie.c +4 -1
@@ -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

Quoted 33 lines
drivers/dma/dw-edma/dw-edma-pcie.c +20 -1
+		/*
+		 * 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

#3 lore raw

RE: [PATCH v8 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint Support

[AMD Official Use Only - AMD Internal Distribution Only]

Hi Frank

Please check my comments inline.

Regards,
Devendra

Quoted 85 lines

-----Original Message-----
From: Frank Li <[email protected]>
Sent: Thursday, January 15, 2026 9:51 PM
To: Verma, Devendra <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; linux- [email protected]; Simek, Michal <[email protected]>
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.

Quoted 70 lines · Devendra K 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.

---
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(-)
drivers/dma/dw-edma/dw-edma-pcie.c +1 -0
index 3371e0a7..2efd149 100644
@@ -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"
 
Quoted 15 lines

drivers/dma/dw-edma/dw-edma-pcie.c line 26

Quoted 11 lines
drivers/dma/dw-edma/dw-edma-pcie.c +6 -5
-#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.

Quoted 4 lines
Quoted 4 lines
drivers/dma/dw-edma/dw-edma-pcie.c +4 -0
+
+/* AMD MDB (Xilinx) specific defines */
+#define PCI_DEVICE_ID_XILINX_B054            0xb054
+
Quoted 9 lines

drivers/dma/dw-edma/dw-edma-pcie.c line 36

Quoted 6 lines
drivers/dma/dw-edma/dw-edma-pcie.c +6 -0
+#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.

Quoted 132 lines

drivers/dma/dw-edma/dw-edma-pcie.c line 40

Quoted 4 lines
drivers/dma/dw-edma/dw-edma-pcie.c +4 -0
+
+#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

Quoted 24 lines
drivers/dma/dw-edma/dw-edma-pcie.c +11 -0
+
+#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 */

drivers/dma/dw-edma/dw-edma-pcie.c struct dw_edma_pcie_data {

Quoted 10 lines
drivers/dma/dw-edma/dw-edma-pcie.c +10 -0
+     .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,

Quoted 47 lines
drivers/dma/dw-edma/dw-edma-pcie.c +44 -0
+                                        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
Quoted 1 line

@@ static u64 dw_edma_pcie_address(struct device *dev, phys_addr_t

cpu_addr)

drivers/dma/dw-edma/dw-edma-pcie.c static void

Quoted 19 lines
      .pci_address = dw_edma_pcie_address,  };
drivers/dma/dw-edma/dw-edma-pcie.c +4 -3
-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,

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 4 lines
drivers/dma/dw-edma/dw-edma-pcie.c +0 -1
          PCI_VNDR_HEADER_LEN(val) != 0x18)
              return;
 
-     pci_dbg(pdev, "Detected PCIe Vendor-Specific Extended Capability

DMA\n");

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 8 lines
drivers/dma/dw-edma/dw-edma-pcie.c +3 -1
+     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

Quoted 7 lines

void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

              return;

      pdata->mf = map;
drivers/dma/dw-edma/dw-edma-pcie.c +1 -1
-     pdata->rg.bar = FIELD_GET(DW_PCIE_VSEC_DMA_BAR, val);
+     pdata->rg.bar = FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_BAR, val);
Quoted 13 lines

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev, · line 230

Quoted 9 lines
      pci_read_config_dword(pdev, vsec + 0xc, &val);
      pdata->wr_ch_cnt = min_t(u16, pdata->wr_ch_cnt,
drivers/dma/dw-edma/dw-edma-pcie.c +4 -2
-                              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.

Quoted 87 lines

drivers/dma/dw-edma/dw-edma-pcie.c static void

Quoted 4 lines
      pci_read_config_dword(pdev, vsec + 0x14, &val);
      off = val;
drivers/dma/dw-edma/dw-edma-pcie.c +0 -0
@@ -157,6 +237,67 @@ static void

dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 13 lines
drivers/dma/dw-edma/dw-edma-pcie.c +10 -0
      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,

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 10 lines
drivers/dma/dw-edma/dw-edma-pcie.c +10 -0
+                                     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");

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 10 lines
drivers/dma/dw-edma/dw-edma-pcie.c +10 -0
+     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);

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 4 lines
drivers/dma/dw-edma/dw-edma-pcie.c +4 -0
+
+     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));

Quoted 34 lines
drivers/dma/dw-edma/dw-edma-pcie.c +28 -1
+     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.
*/

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct pci_dev *pdev, · line 331

Quoted 5 lines
drivers/dma/dw-edma/dw-edma-pcie.c +4 -1
-     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.

Quoted 1 line

Frank

Yes, this is good suggestion. Thanks!

Quoted 35 lines

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct pci_dev *pdev,

Quoted 5 lines
drivers/dma/dw-edma/dw-edma-pcie.c +5 -0
+             /*
+              * 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)

Quoted 29 lines
drivers/dma/dw-edma/dw-edma-pcie.c +15 -0
+                     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

#4 lore raw

Re: [PATCH v8 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint Support

Quoted 110 lines · Devendra

[AMD Official Use Only - AMD Internal Distribution Only]

Hi Frank

Please check my comments inline.

Regards,
Devendra

Quoted 85 lines

-----Original Message-----
From: Frank Li <[email protected]>
Sent: Thursday, January 15, 2026 9:51 PM
To: Verma, Devendra <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; linux- [email protected]; Simek, Michal <[email protected]>
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.

Quoted 70 lines · Devendra K 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.

---
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(-)
drivers/dma/dw-edma/dw-edma-pcie.c +1 -0
index 3371e0a7..2efd149 100644
@@ -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"
 
Quoted 15 lines

drivers/dma/dw-edma/dw-edma-pcie.c line 26

Quoted 11 lines
drivers/dma/dw-edma/dw-edma-pcie.c +6 -5
-#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'.

Quoted 6 lines

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.

Quoted 163 lines
Quoted 14 lines

drivers/dma/dw-edma/dw-edma-pcie.c line 36

Quoted 11 lines
drivers/dma/dw-edma/dw-edma-pcie.c +10 -0
+
+/* 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.

Quoted 132 lines

drivers/dma/dw-edma/dw-edma-pcie.c line 40

Quoted 4 lines
drivers/dma/dw-edma/dw-edma-pcie.c +4 -0
+
+#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

Quoted 24 lines
drivers/dma/dw-edma/dw-edma-pcie.c +11 -0
+
+#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 */

drivers/dma/dw-edma/dw-edma-pcie.c struct dw_edma_pcie_data {

Quoted 10 lines
drivers/dma/dw-edma/dw-edma-pcie.c +10 -0
+     .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,

Quoted 47 lines
drivers/dma/dw-edma/dw-edma-pcie.c +44 -0
+                                        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
Quoted 1 line

@@ static u64 dw_edma_pcie_address(struct device *dev, phys_addr_t

cpu_addr)

drivers/dma/dw-edma/dw-edma-pcie.c static void

Quoted 19 lines
      .pci_address = dw_edma_pcie_address,  };
drivers/dma/dw-edma/dw-edma-pcie.c +4 -3
-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,

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 4 lines
drivers/dma/dw-edma/dw-edma-pcie.c +0 -1
          PCI_VNDR_HEADER_LEN(val) != 0x18)
              return;
 
-     pci_dbg(pdev, "Detected PCIe Vendor-Specific Extended Capability

DMA\n");

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 8 lines
drivers/dma/dw-edma/dw-edma-pcie.c +3 -1
+     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

Quoted 7 lines

void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

              return;

      pdata->mf = map;
drivers/dma/dw-edma/dw-edma-pcie.c +1 -1
-     pdata->rg.bar = FIELD_GET(DW_PCIE_VSEC_DMA_BAR, val);
+     pdata->rg.bar = FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_BAR, val);
Quoted 13 lines

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev, · line 230

Quoted 9 lines
      pci_read_config_dword(pdev, vsec + 0xc, &val);
      pdata->wr_ch_cnt = min_t(u16, pdata->wr_ch_cnt,
drivers/dma/dw-edma/dw-edma-pcie.c +4 -2
-                              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.

Quoted 3 lines

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

Quoted 127 lines
Quoted 88 lines

drivers/dma/dw-edma/dw-edma-pcie.c static void

Quoted 5 lines

      pci_read_config_dword(pdev, vsec + 0x14, &val);
      off = val;
drivers/dma/dw-edma/dw-edma-pcie.c +0 -0
@@ -157,6 +237,67 @@ static void

dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 13 lines
drivers/dma/dw-edma/dw-edma-pcie.c +10 -0
      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,

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 10 lines
drivers/dma/dw-edma/dw-edma-pcie.c +10 -0
+                                     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");

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 10 lines
drivers/dma/dw-edma/dw-edma-pcie.c +10 -0
+     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);

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 4 lines
drivers/dma/dw-edma/dw-edma-pcie.c +4 -0
+
+     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));

Quoted 34 lines
drivers/dma/dw-edma/dw-edma-pcie.c +28 -1
+     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.
*/

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct pci_dev *pdev, · line 331

Quoted 5 lines
drivers/dma/dw-edma/dw-edma-pcie.c +4 -1
-     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.

Quoted 1 line

Frank

Yes, this is good suggestion. Thanks!

Quoted 35 lines

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct pci_dev *pdev,

Quoted 5 lines
drivers/dma/dw-edma/dw-edma-pcie.c +5 -0
+             /*
+              * 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)

Quoted 29 lines
drivers/dma/dw-edma/dw-edma-pcie.c +15 -0
+                     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

#5 lore raw

RE: [PATCH v8 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint Support

[AMD Official Use Only - AMD Internal Distribution Only]

Quoted 136 lines

-----Original Message-----
From: Frank Li <[email protected]>
Sent: Monday, January 19, 2026 9:30 PM
To: Verma, Devendra <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; linux- [email protected]; Simek, Michal <[email protected]>
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.

Quoted 17 lines · Devendra

[AMD Official Use Only - AMD Internal Distribution Only]

Hi Frank

Please check my comments inline.

Regards,
Devendra

Quoted 8 lines

-----Original Message-----
From: Frank Li <[email protected]>
Sent: Thursday, January 15, 2026 9:51 PM
To: Verma, Devendra <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; linux- [email protected]; Simek, Michal <[email protected]>
Subject: Re: [PATCH v8 1/2] dmaengine: dw-edma: Add AMD MDB

Endpoint

Quoted 53 lines
Quoted 53 lines

Support

Caution: This message originated from an External Source. Use proper caution when opening attachments, clicking links, or responding.

Quoted 46 lines · Devendra K 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.

---
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.

Quoted 6 lines
Quoted 6 lines
Quoted 6 lines

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.

Quoted 18 lines
Quoted 18 lines
Quoted 18 lines
---
 drivers/dma/dw-edma/dw-edma-pcie.c | 192
++++++++++++++++++++++++++++++++++---
 1 file changed, 178 insertions(+), 14 deletions(-)
drivers/dma/dw-edma/dw-edma-pcie.c +1 -0
index 3371e0a7..2efd149 100644
@@ -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"
 
Quoted 8 lines
Quoted 8 lines

drivers/dma/dw-edma/dw-edma-pcie.c

Quoted 8 lines
drivers/dma/dw-edma/dw-edma-pcie.c +3 -5
-#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)

Quoted 2 lines
Quoted 2 lines

drivers/dma/dw-edma/dw-edma-pcie.c line 25

Quoted 2 lines
drivers/dma/dw-edma/dw-edma-pcie.c +2 -0
+#define DW_PCIE_SYNOPSYS_VSEC_DMA_MAP                GENMASK(2, 0)
+#define DW_PCIE_SYNOPSYS_VSEC_DMA_WR_CH              GENMASK(9,

0)

Quoted 1 line
Quoted 1 line

drivers/dma/dw-edma/dw-edma-pcie.c line 26

Quoted 1 line
drivers/dma/dw-edma/dw-edma-pcie.c +1 -0
+#define DW_PCIE_SYNOPSYS_VSEC_DMA_RD_CH              GENMASK(25,

16)

Quoted 11 lines
Quoted 4 lines

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

Quoted 8 lines

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.

Quoted 178 lines
Quoted 8 lines
Quoted 8 lines

drivers/dma/dw-edma/dw-edma-pcie.c

Quoted 8 lines
drivers/dma/dw-edma/dw-edma-pcie.c +7 -0
+
+/* 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)

Quoted 1 line
Quoted 1 line

drivers/dma/dw-edma/dw-edma-pcie.c line 34

Quoted 1 line
drivers/dma/dw-edma/dw-edma-pcie.c +1 -0
+#define DW_PCIE_XILINX_MDB_VSEC_DMA_MAP              GENMASK(2,

0)

Quoted 2 lines
Quoted 2 lines

drivers/dma/dw-edma/dw-edma-pcie.c line 36

Quoted 2 lines
drivers/dma/dw-edma/dw-edma-pcie.c +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)

Quoted 100 lines
Quoted 3 lines

These defination is the same. Need redefine again

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

Quoted 94 lines

drivers/dma/dw-edma/dw-edma-pcie.c line 40

Quoted 4 lines
drivers/dma/dw-edma/dw-edma-pcie.c +4 -0
+
+#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

Quoted 25 lines
drivers/dma/dw-edma/dw-edma-pcie.c +12 -0
+
+#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 */

drivers/dma/dw-edma/dw-edma-pcie.c struct dw_edma_pcie_data {

Quoted 10 lines
drivers/dma/dw-edma/dw-edma-pcie.c +10 -0
+
+     /* 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,

Quoted 46 lines
drivers/dma/dw-edma/dw-edma-pcie.c +44 -0
+                                        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

Quoted 4 lines
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)

Quoted 5 lines
Quoted 5 lines

drivers/dma/dw-edma/dw-edma-pcie.c static u64 dw_edma_pcie_address(struct device *dev, phys_addr_t cpu_addr)

Quoted 5 lines
      .pci_address = dw_edma_pcie_address,  };
drivers/dma/dw-edma/dw-edma-pcie.c +1 -2
-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,

Quoted 39 lines
Quoted 39 lines

drivers/dma/dw-edma/dw-edma-pcie.c static void

Quoted 16 lines
drivers/dma/dw-edma/dw-edma-pcie.c +5 -1
+                                            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,

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 4 lines
drivers/dma/dw-edma/dw-edma-pcie.c +0 -1
          PCI_VNDR_HEADER_LEN(val) != 0x18)
              return;
 
-     pci_dbg(pdev, "Detected PCIe Vendor-Specific Extended Capability

DMA\n");

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 8 lines
drivers/dma/dw-edma/dw-edma-pcie.c +3 -1
+     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

Quoted 8 lines

void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

              return;

      pdata->mf = map;
drivers/dma/dw-edma/dw-edma-pcie.c +2 -1
-     pdata->rg.bar = FIELD_GET(DW_PCIE_VSEC_DMA_BAR, val);
+     pdata->rg.bar = FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_BAR,
+ val);
Quoted 12 lines
Quoted 4 lines
Quoted 4 lines
      pci_read_config_dword(pdev, vsec + 0xc, &val);
      pdata->wr_ch_cnt = min_t(u16, pdata->wr_ch_cnt,
drivers/dma/dw-edma/dw-edma-pcie.c +1 -1
-                              FIELD_GET(DW_PCIE_VSEC_DMA_WR_CH, val));
+
Quoted 8 lines

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev, · line 230

Quoted 5 lines
+ FIELD_GET(DW_PCIE_SYNOPSYS_VSEC_DMA_WR_CH, val));
      pdata->rd_ch_cnt = min_t(u16, pdata->rd_ch_cnt,
drivers/dma/dw-edma/dw-edma-pcie.c +2 -1
-                              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.

Quoted 5 lines
Quoted 1 line

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.

Quoted 8 lines

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.

Quoted 134 lines
Quoted 9 lines
Quoted 9 lines

drivers/dma/dw-edma/dw-edma-pcie.c static void

Quoted 5 lines

      pci_read_config_dword(pdev, vsec + 0x14, &val);
      off = val;
drivers/dma/dw-edma/dw-edma-pcie.c +0 -0
@@ -157,6 +237,67 @@ static void

dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 3 lines
drivers/dma/dw-edma/dw-edma-pcie.c +0 -0
      pdata->rg.off = off;
 }
 
Quoted 9 lines
Quoted 9 lines

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 9 lines
drivers/dma/dw-edma/dw-edma-pcie.c +9 -0
+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;

Quoted 14 lines
Quoted 14 lines

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 13 lines
drivers/dma/dw-edma/dw-edma-pcie.c +13 -0
+
+     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");

Quoted 10 lines
Quoted 10 lines

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 10 lines
drivers/dma/dw-edma/dw-edma-pcie.c +10 -0
+     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,

Quoted 17 lines
Quoted 17 lines

val);

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 5 lines
drivers/dma/dw-edma/dw-edma-pcie.c +5 -0
+
+     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));

Quoted 10 lines
drivers/dma/dw-edma/dw-edma-pcie.c +10 -0
+     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;
Quoted 8 lines
Quoted 8 lines

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 8 lines
drivers/dma/dw-edma/dw-edma-pcie.c +8 -0
+
+     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,

Quoted 4 lines
Quoted 4 lines

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 4 lines
drivers/dma/dw-edma/dw-edma-pcie.c +4 -0
+                           &val);
+     off = val;
+     pci_read_config_dword(pdev,
+                           vsec +

DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_LOW,

Quoted 8 lines
Quoted 8 lines

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 8 lines
drivers/dma/dw-edma/dw-edma-pcie.c +5 -1
+                           &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,

Quoted 50 lines
Quoted 9 lines

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct pci_dev *pdev, · line 331

Quoted 8 lines

* Tries to find if exists a PCIe Vendor-Specific Extended Capability
* for the DMA, if one exists, then reconfigures it.
*/

drivers/dma/dw-edma/dw-edma-pcie.c +4 -1
-     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) {
Quoted 3 lines

dw_edma_pcie_get_xilinx_dma_data() should be here.

Frank

Yes, this is good suggestion. Thanks!

Quoted 35 lines

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct pci_dev *pdev,

Quoted 5 lines
drivers/dma/dw-edma/dw-edma-pcie.c +5 -0
+             /*
+              * 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)

Quoted 29 lines
drivers/dma/dw-edma/dw-edma-pcie.c +13 -0
+                     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) },

drivers/dma/dw-edma/dw-edma-pcie.c +2 -1
+     { 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

#6 lore raw

Re: [PATCH v8 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint Support

Quoted 144 lines · Devendra

[AMD Official Use Only - AMD Internal Distribution Only]

Quoted 142 lines

-----Original Message-----
From: Frank Li <[email protected]>
Sent: Monday, January 19, 2026 9:30 PM
To: Verma, Devendra <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; linux- [email protected]; Simek, Michal <[email protected]>
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.

Quoted 17 lines · Devendra

[AMD Official Use Only - AMD Internal Distribution Only]

Hi Frank

Please check my comments inline.

Regards,
Devendra

Quoted 8 lines

-----Original Message-----
From: Frank Li <[email protected]>
Sent: Thursday, January 15, 2026 9:51 PM
To: Verma, Devendra <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; linux- [email protected]; Simek, Michal <[email protected]>
Subject: Re: [PATCH v8 1/2] dmaengine: dw-edma: Add AMD MDB

Endpoint

Quoted 53 lines
Quoted 53 lines

Support

Caution: This message originated from an External Source. Use proper caution when opening attachments, clicking links, or responding.

Quoted 46 lines · Devendra K 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.

---
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.

Quoted 6 lines
Quoted 6 lines
Quoted 6 lines

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.

Quoted 18 lines
Quoted 18 lines
Quoted 18 lines
---
 drivers/dma/dw-edma/dw-edma-pcie.c | 192
++++++++++++++++++++++++++++++++++---
 1 file changed, 178 insertions(+), 14 deletions(-)
drivers/dma/dw-edma/dw-edma-pcie.c +1 -0
index 3371e0a7..2efd149 100644
@@ -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"
 
Quoted 8 lines
Quoted 8 lines

drivers/dma/dw-edma/dw-edma-pcie.c

Quoted 8 lines
drivers/dma/dw-edma/dw-edma-pcie.c +3 -5
-#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)

Quoted 2 lines
Quoted 2 lines

drivers/dma/dw-edma/dw-edma-pcie.c line 25

Quoted 2 lines
drivers/dma/dw-edma/dw-edma-pcie.c +2 -0
+#define DW_PCIE_SYNOPSYS_VSEC_DMA_MAP                GENMASK(2, 0)
+#define DW_PCIE_SYNOPSYS_VSEC_DMA_WR_CH              GENMASK(9,

0)

Quoted 1 line
Quoted 1 line

drivers/dma/dw-edma/dw-edma-pcie.c line 26

Quoted 1 line
drivers/dma/dw-edma/dw-edma-pcie.c +1 -0
+#define DW_PCIE_SYNOPSYS_VSEC_DMA_RD_CH              GENMASK(25,

16)

Quoted 11 lines
Quoted 4 lines

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.

Quoted 8 lines
Quoted 2 lines

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.

Quoted 19 lines
Quoted 19 lines
Quoted 9 lines
Quoted 9 lines

drivers/dma/dw-edma/dw-edma-pcie.c

Quoted 9 lines
drivers/dma/dw-edma/dw-edma-pcie.c +7 -0
+
+/* 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)

Quoted 1 line
Quoted 1 line

drivers/dma/dw-edma/dw-edma-pcie.c line 34

Quoted 1 line
drivers/dma/dw-edma/dw-edma-pcie.c +1 -0
+#define DW_PCIE_XILINX_MDB_VSEC_DMA_MAP              GENMASK(2,

0)

Quoted 2 lines
Quoted 2 lines

drivers/dma/dw-edma/dw-edma-pcie.c line 36

Quoted 2 lines
drivers/dma/dw-edma/dw-edma-pcie.c +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)

Quoted 4 lines
Quoted 3 lines

These defination is the same. Need redefine again

...

Quoted 8 lines
Quoted 8 lines
Quoted 4 lines

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.

Quoted 7 lines
Quoted 5 lines

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.

Quoted 8 lines

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

Quoted 135 lines
Quoted 135 lines
Quoted 10 lines
Quoted 10 lines

drivers/dma/dw-edma/dw-edma-pcie.c static void

Quoted 6 lines


      pci_read_config_dword(pdev, vsec + 0x14, &val);
      off = val;
drivers/dma/dw-edma/dw-edma-pcie.c +0 -0
@@ -157,6 +237,67 @@ static void

dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 3 lines
drivers/dma/dw-edma/dw-edma-pcie.c +0 -0
      pdata->rg.off = off;
 }
 
Quoted 9 lines
Quoted 9 lines

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 9 lines
drivers/dma/dw-edma/dw-edma-pcie.c +9 -0
+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;

Quoted 14 lines
Quoted 14 lines

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 13 lines
drivers/dma/dw-edma/dw-edma-pcie.c +13 -0
+
+     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");

Quoted 10 lines
Quoted 10 lines

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 10 lines
drivers/dma/dw-edma/dw-edma-pcie.c +10 -0
+     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,

Quoted 17 lines
Quoted 17 lines

val);

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 5 lines
drivers/dma/dw-edma/dw-edma-pcie.c +5 -0
+
+     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));

Quoted 10 lines
drivers/dma/dw-edma/dw-edma-pcie.c +10 -0
+     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;
Quoted 8 lines
Quoted 8 lines

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 8 lines
drivers/dma/dw-edma/dw-edma-pcie.c +8 -0
+
+     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,

Quoted 4 lines
Quoted 4 lines

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 4 lines
drivers/dma/dw-edma/dw-edma-pcie.c +4 -0
+                           &val);
+     off = val;
+     pci_read_config_dword(pdev,
+                           vsec +

DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_LOW,

Quoted 8 lines
Quoted 8 lines

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 8 lines
drivers/dma/dw-edma/dw-edma-pcie.c +5 -1
+                           &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,

Quoted 50 lines
Quoted 9 lines

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct pci_dev *pdev, · line 331

Quoted 8 lines

* Tries to find if exists a PCIe Vendor-Specific Extended Capability
* for the DMA, if one exists, then reconfigures it.
*/

drivers/dma/dw-edma/dw-edma-pcie.c +4 -1
-     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) {
Quoted 3 lines

dw_edma_pcie_get_xilinx_dma_data() should be here.

Frank

Yes, this is good suggestion. Thanks!

Quoted 35 lines

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct pci_dev *pdev,

Quoted 5 lines
drivers/dma/dw-edma/dw-edma-pcie.c +5 -0
+             /*
+              * 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)

Quoted 29 lines
drivers/dma/dw-edma/dw-edma-pcie.c +13 -0
+                     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) },

drivers/dma/dw-edma/dw-edma-pcie.c +2 -1
+     { 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

#7 lore raw

RE: [PATCH v8 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint Support

[AMD Official Use Only - AMD Internal Distribution Only]

Hi Frank

Please check my response inline.

Regards,
Devendra

Quoted 10 lines

-----Original Message-----
From: Frank Li <[email protected]>
Sent: Wednesday, January 21, 2026 10:24 PM
To: Verma, Devendra <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; linux- [email protected]; Simek, Michal <[email protected]>
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]

Quoted 118 lines
Quoted 70 lines
Quoted 70 lines
Quoted 47 lines
Quoted 47 lines · Devendra K Verma
Quoted 46 lines

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.

---
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.

Quoted 6 lines
Quoted 6 lines
Quoted 6 lines

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.

Quoted 16 lines
Quoted 16 lines
Quoted 16 lines
---
 drivers/dma/dw-edma/dw-edma-pcie.c | 192
++++++++++++++++++++++++++++++++++---
 1 file changed, 178 insertions(+), 14 deletions(-)
drivers/dma/dw-edma/dw-edma-pcie.c +1 -0
index 3371e0a7..2efd149 100644
@@ -14,14 +14,35 @@
 #include <linux/pci-epf.h>
 #include <linux/msi.h>
 #include <linux/bitfield.h>
+#include <linux/sizes.h>
 
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines

drivers/dma/dw-edma/dw-edma-pcie.c

Quoted 7 lines
drivers/dma/dw-edma/dw-edma-pcie.c +0 -5
 #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)

Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

drivers/dma/dw-edma/dw-edma-pcie.c

Quoted 3 lines
drivers/dma/dw-edma/dw-edma-pcie.c +3 -0
+/* Synopsys */
+#define DW_PCIE_SYNOPSYS_VSEC_DMA_ID         0x6
+#define DW_PCIE_SYNOPSYS_VSEC_DMA_BAR

GENMASK(10,

Quoted 2 lines
Quoted 2 lines

8)

Quoted 1 line
Quoted 1 line
Quoted 1 line
drivers/dma/dw-edma/dw-edma-pcie.c +1 -0
+#define DW_PCIE_SYNOPSYS_VSEC_DMA_MAP

GENMASK(2, 0)

Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

drivers/dma/dw-edma/dw-edma-pcie.c line 25

Quoted 1 line
drivers/dma/dw-edma/dw-edma-pcie.c +1 -0
+#define DW_PCIE_SYNOPSYS_VSEC_DMA_WR_CH

GENMASK(9,

Quoted 2 lines
Quoted 2 lines

0)

Quoted 1 line
Quoted 1 line
Quoted 1 line
drivers/dma/dw-edma/dw-edma-pcie.c +1 -0
+#define DW_PCIE_SYNOPSYS_VSEC_DMA_RD_CH

GENMASK(25,

Quoted 19 lines
Quoted 19 lines

16)

Quoted 5 lines
Quoted 4 lines

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

Quoted 7 lines

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.

Quoted 8 lines
Quoted 2 lines

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.

Quoted 4 lines

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/

Quoted 49 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines

drivers/dma/dw-edma/dw-edma-pcie.c

Quoted 9 lines
drivers/dma/dw-edma/dw-edma-pcie.c +7 -0
+
+/* 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,

Quoted 2 lines
Quoted 2 lines

8)

Quoted 1 line
Quoted 1 line
Quoted 1 line
drivers/dma/dw-edma/dw-edma-pcie.c +1 -0
+#define DW_PCIE_XILINX_MDB_VSEC_DMA_MAP

GENMASK(2,

Quoted 2 lines
Quoted 2 lines

0)

Quoted 1 line
Quoted 1 line
Quoted 1 line
drivers/dma/dw-edma/dw-edma-pcie.c +1 -0
+#define DW_PCIE_XILINX_MDB_VSEC_DMA_WR_CH    GENMASK(9,

0)

Quoted 6 lines
Quoted 6 lines
Quoted 1 line
Quoted 1 line

drivers/dma/dw-edma/dw-edma-pcie.c line 36

Quoted 1 line
drivers/dma/dw-edma/dw-edma-pcie.c +1 -0
+#define DW_PCIE_XILINX_MDB_VSEC_DMA_RD_CH    GENMASK(25,

16)

Quoted 4 lines
Quoted 3 lines

These defination is the same. Need redefine again

...

Quoted 8 lines
Quoted 8 lines
Quoted 4 lines

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.

Quoted 7 lines
Quoted 5 lines

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.

Quoted 8 lines

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

Quoted 6 lines

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.

Quoted 137 lines
Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
Quoted 11 lines

drivers/dma/dw-edma/dw-edma-pcie.c static void

Quoted 6 lines


      pci_read_config_dword(pdev, vsec + 0x14, &val);
      off = val;
drivers/dma/dw-edma/dw-edma-pcie.c +0 -0
@@ -157,6 +237,67 @@ static void

dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 4 lines
drivers/dma/dw-edma/dw-edma-pcie.c +1 -0
      pdata->rg.off = off;
 }
 
+static void dw_edma_pcie_get_xilinx_dma_data(struct pci_dev

*pdev,

Quoted 125 lines
Quoted 125 lines
Quoted 8 lines
Quoted 8 lines

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 8 lines
drivers/dma/dw-edma/dw-edma-pcie.c +8 -0
+                                          struct
+dw_edma_pcie_data
+*pdata) {
+     u32 val, map;
+     u16 vsec;
+     u64 off;
+
+     pdata->devmem_phys_off =

DW_PCIE_XILINX_MDB_INVALID_ADDR;

Quoted 14 lines
Quoted 14 lines

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 13 lines
drivers/dma/dw-edma/dw-edma-pcie.c +13 -0
+
+     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");

Quoted 10 lines
Quoted 10 lines

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 10 lines
drivers/dma/dw-edma/dw-edma-pcie.c +10 -0
+     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,

Quoted 17 lines
Quoted 17 lines

val);

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 5 lines
drivers/dma/dw-edma/dw-edma-pcie.c +5 -0
+
+     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));

Quoted 10 lines
drivers/dma/dw-edma/dw-edma-pcie.c +10 -0
+     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;
Quoted 8 lines
Quoted 8 lines

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 8 lines
drivers/dma/dw-edma/dw-edma-pcie.c +8 -0
+
+     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,

Quoted 4 lines
Quoted 4 lines

drivers/dma/dw-edma/dw-edma-pcie.c static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 4 lines
drivers/dma/dw-edma/dw-edma-pcie.c +4 -0
+                           &val);
+     off = val;
+     pci_read_config_dword(pdev,
+                           vsec +

DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_LOW,

Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
drivers/dma/dw-edma/dw-edma-pcie.c +5 -0
+                           &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,

Quoted 50 lines
Quoted 9 lines

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct pci_dev *pdev, · line 331

Quoted 8 lines

* Tries to find if exists a PCIe Vendor-Specific Extended Capability
* for the DMA, if one exists, then reconfigures it.
*/

drivers/dma/dw-edma/dw-edma-pcie.c +4 -1
-     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) {
Quoted 3 lines

dw_edma_pcie_get_xilinx_dma_data() should be here.

Frank

Yes, this is good suggestion. Thanks!

Quoted 35 lines

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct pci_dev *pdev,

Quoted 5 lines
drivers/dma/dw-edma/dw-edma-pcie.c +5 -0
+             /*
+              * 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)

Quoted 29 lines
drivers/dma/dw-edma/dw-edma-pcie.c +13 -0
+                     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) },

drivers/dma/dw-edma/dw-edma-pcie.c +2 -1
+     { 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

#8 lore raw

PATCH v8 2/2 dmaengine: dw-edma: Add non-LL mode

In reply to Devendra K 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.

---
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(-)
drivers/dma/dw-edma/dw-edma-core.c +37 -5
index b43255f..d37112b 100644
@@ -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;
drivers/dma/dw-edma/dw-edma-core.h +1 -0
index 71894b9..c8e3d19 100644
@@ -86,6 +86,7 @@ struct dw_edma_chan {
 	u8				configured;
 
 	struct dma_slave_config		config;
+	bool				non_ll;
 };
 
 struct dw_edma_irq {
drivers/dma/dw-edma/dw-edma-pcie.c +32 -14

111 lines not on this page · raw message

drivers/dma/dw-edma/dw-hdma-v0-core.c +60 -1

73 lines not on this page · raw message

drivers/dma/dw-edma/dw-hdma-v0-regs.h +1 -0
index eab5fd7..7759ba9 100644
@@ -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)
include/linux/dma/edma.h +1 -0
index 3080747..78ce31b 100644
@@ -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 */
Signature · 1 line
1.8.3.1
#9 lore raw

Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode

Quoted 7 lines · Devendra K 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

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

Quoted 37 lines

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.

---
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.

Quoted 6 lines
---
 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.

Quoted 51 lines
 include/linux/dma/edma.h              |  1 +
 6 files changed, 132 insertions(+), 20 deletions(-)
drivers/dma/dw-edma/dw-edma-core.c +25 -1
index b43255f..d37112b 100644
@@ -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))

drivers/dma/dw-edma/dw-edma-core.c static void dw_edma_device_issue_pending(struct dma_chan *dchan) · line 450

Quoted 10 lines
drivers/dma/dw-edma/dw-edma-core.c +5 -3
 		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.

Quoted 109 lines
drivers/dma/dw-edma/dw-edma-core.c +7 -1
 	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;
drivers/dma/dw-edma/dw-edma-core.h +1 -0
index 71894b9..c8e3d19 100644
@@ -86,6 +86,7 @@ struct dw_edma_chan {
 	u8				configured;
 
 	struct dma_slave_config		config;
+	bool				non_ll;
 };
 
 struct dw_edma_irq {
drivers/dma/dw-edma/dw-edma-pcie.c +23 -9
index 2efd149..277ca50 100644
@@ -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];

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct pci_dev *pdev, · line 430

Quoted 6 lines
drivers/dma/dw-edma/dw-edma-pcie.c +2 -1
@@ -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().

Quoted 54 lines
drivers/dma/dw-edma/dw-edma-pcie.c +7 -4
 		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;
 	}
drivers/dma/dw-edma/dw-hdma-v0-core.c +1 -1
index e3f8db4..a5d12bc 100644
@@ -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);

drivers/dma/dw-edma/dw-hdma-v0-core.c static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first) · line 273

Quoted 10 lines
drivers/dma/dw-edma/dw-hdma-v0-core.c +8 -0
 }
 
+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

Quoted 81 lines
drivers/dma/dw-edma/dw-hdma-v0-core.c +51 -0
+		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;
drivers/dma/dw-edma/dw-hdma-v0-regs.h +1 -0
index eab5fd7..7759ba9 100644
@@ -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)
include/linux/dma/edma.h +1 -1
index 3080747..78ce31b 100644
@@ -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

#10 lore raw

RE: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode

[AMD Official Use Only - AMD Internal Distribution Only]

Hi Frank

Please check my response inline.

Regards,
Devendra

Quoted 22 lines

-----Original Message-----
From: Frank Li <[email protected]>
Sent: Thursday, January 15, 2026 10:07 PM
To: Verma, Devendra <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; linux- [email protected]; Simek, Michal <[email protected]>
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.

Quoted 7 lines · Devendra K 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

Quoted 3 lines

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.

Quoted 113 lines
Quoted 37 lines

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.

---
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.

Quoted 7 lines
---
 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.

drivers/dma/dw-edma/dw-edma-core.c static int dw_edma_device_config(struct

Quoted 9 lines
 include/linux/dma/edma.h              |  1 +
 6 files changed, 132 insertions(+), 20 deletions(-)
drivers/dma/dw-edma/dw-edma-core.c +0 -0
index b43255f..d37112b 100644
@@ -223,8 +223,32 @@ static int dw_edma_device_config(struct

dma_chan *dchan,

drivers/dma/dw-edma/dw-edma-core.c static void dw_edma_device_issue_pending(struct

Quoted 32 lines
drivers/dma/dw-edma/dw-edma-core.c +24 -0
                               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)

drivers/dma/dw-edma/dw-edma-core.c static void dw_edma_device_issue_pending(struct

Quoted 9 lines
drivers/dma/dw-edma/dw-edma-core.c +1 -1
      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)

drivers/dma/dw-edma/dw-edma-core.c static void dw_edma_device_issue_pending(struct dma_chan *dchan) · line 450

Quoted 11 lines
drivers/dma/dw-edma/dw-edma-core.c +5 -3
      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;
+     }
Quoted 4 lines

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.

Quoted 117 lines

drivers/dma/dw-edma/dw-edma-pcie.c static void

Quoted 37 lines
drivers/dma/dw-edma/dw-edma-core.c +0 -0
      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;
drivers/dma/dw-edma/dw-edma-core.c +7 -1
-             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

drivers/dma/dw-edma/dw-edma-core.h +1 -0
@@ -86,6 +86,7 @@ struct dw_edma_chan {
      u8                              configured;
 
      struct dma_slave_config         config;
+     bool                            non_ll;
 };
 
 struct dw_edma_irq {
drivers/dma/dw-edma/dw-edma-pcie.c +0 -0
index 2efd149..277ca50 100644
@@ -298,6 +298,15 @@ static void

dw_edma_pcie_get_xilinx_dma_data(struct pci_dev *pdev,

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct pci_dev

Quoted 21 lines
drivers/dma/dw-edma/dw-edma-pcie.c +8 -1
      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,

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct pci_dev *pdev,

Quoted 9 lines
drivers/dma/dw-edma/dw-edma-pcie.c +3 -2
      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)

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct pci_dev · line 348

Quoted 2 lines
drivers/dma/dw-edma/dw-edma-pcie.c +1 -1
-                     return -ENOMEM;
+             if (vsec_data->devmem_phys_off ==

DW_PCIE_AMD_MDB_INVALID_ADDR)

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct pci_dev

Quoted 23 lines
drivers/dma/dw-edma/dw-edma-pcie.c +8 -5
+                     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,

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct pci_dev

Quoted 17 lines
drivers/dma/dw-edma/dw-edma-pcie.c +1 -0
      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;
drivers/dma/dw-edma/dw-edma-pcie.c +1 -1
-     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,

Quoted 2 lines
drivers/dma/dw-edma/dw-edma-pcie.c +0 -0
                      return -ENOMEM;
 
Quoted 8 lines

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct pci_dev *pdev, · line 430

Quoted 4 lines
drivers/dma/dw-edma/dw-edma-pcie.c +2 -1
              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.

Quoted 75 lines

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct pci_dev

Quoted 4 lines
drivers/dma/dw-edma/dw-edma-pcie.c +0 -0
              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,

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct pci_dev

Quoted 16 lines
drivers/dma/dw-edma/dw-edma-pcie.c +3 -2
                      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,

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct pci_dev

Quoted 10 lines
drivers/dma/dw-edma/dw-edma-pcie.c +2 -1
                      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,

drivers/dma/dw-edma/dw-hdma-v0-core.c static void dw_hdma_v0_sync_ll_data(struct

Quoted 15 lines
drivers/dma/dw-edma/dw-edma-pcie.c +2 -1
                      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;
      }
drivers/dma/dw-edma/dw-hdma-v0-core.c +0 -0
index e3f8db4..a5d12bc 100644
@@ -225,7 +225,7 @@ static void dw_hdma_v0_sync_ll_data(struct

dw_edma_chunk *chunk)

drivers/dma/dw-edma/dw-hdma-v0-core.c static void dw_hdma_v0_core_start(struct

Quoted 10 lines
drivers/dma/dw-edma/dw-hdma-v0-core.c +2 -1
              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)

drivers/dma/dw-edma/dw-hdma-v0-core.c static void dw_hdma_v0_core_start(struct · line 266

Quoted 4 lines
drivers/dma/dw-edma/dw-hdma-v0-core.c +1 -0
      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)

drivers/dma/dw-edma/dw-hdma-v0-core.c static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first) · line 273

Quoted 7 lines
drivers/dma/dw-edma/dw-hdma-v0-core.c +7 -0
+{
+     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.

Quoted 1 line

Frank

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

Quoted 83 lines
Quoted 83 lines
drivers/dma/dw-edma/dw-hdma-v0-core.c +52 -0
+             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;
drivers/dma/dw-edma/dw-hdma-v0-regs.h +1 -0
index eab5fd7..7759ba9 100644
@@ -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)
include/linux/dma/edma.h index renamed from include/linux/dma/edma.h +0 -0

3080747..78ce31b 100644

include/linux/dma/edma.h +1 -1
@@ -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

#11 lore raw

Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode

Quoted 39 lines · Devendra

[AMD Official Use Only - AMD Internal Distribution Only]

Hi Frank

Please check my response inline.

Regards,
Devendra

Quoted 22 lines

-----Original Message-----
From: Frank Li <[email protected]>
Sent: Thursday, January 15, 2026 10:07 PM
To: Verma, Devendra <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; linux- [email protected]; Simek, Michal <[email protected]>
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.

Quoted 7 lines · Devendra K 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

Quoted 3 lines

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.

Quoted 7 lines

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?

Quoted 116 lines
Quoted 116 lines
Quoted 38 lines

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.

---
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.

Quoted 7 lines
---
 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.

drivers/dma/dw-edma/dw-edma-core.c static int dw_edma_device_config(struct

Quoted 9 lines
 include/linux/dma/edma.h              |  1 +
 6 files changed, 132 insertions(+), 20 deletions(-)
drivers/dma/dw-edma/dw-edma-core.c +0 -0
index b43255f..d37112b 100644
@@ -223,8 +223,32 @@ static int dw_edma_device_config(struct

dma_chan *dchan,

drivers/dma/dw-edma/dw-edma-core.c static void dw_edma_device_issue_pending(struct

Quoted 32 lines
drivers/dma/dw-edma/dw-edma-core.c +24 -0
                               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)

drivers/dma/dw-edma/dw-edma-core.c static void dw_edma_device_issue_pending(struct

Quoted 9 lines
drivers/dma/dw-edma/dw-edma-core.c +1 -1
      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)

drivers/dma/dw-edma/dw-edma-core.c static void dw_edma_device_issue_pending(struct dma_chan *dchan) · line 450

Quoted 11 lines
drivers/dma/dw-edma/dw-edma-core.c +5 -3
      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).

Quoted 8 lines
Quoted 2 lines

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.

Quoted 123 lines
Quoted 123 lines

drivers/dma/dw-edma/dw-edma-pcie.c static void

Quoted 38 lines
drivers/dma/dw-edma/dw-edma-core.c +0 -0
      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;
drivers/dma/dw-edma/dw-edma-core.c +7 -1
-             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

drivers/dma/dw-edma/dw-edma-core.h +1 -0
@@ -86,6 +86,7 @@ struct dw_edma_chan {
      u8                              configured;
 
      struct dma_slave_config         config;
+     bool                            non_ll;
 };
 
 struct dw_edma_irq {
drivers/dma/dw-edma/dw-edma-pcie.c +0 -0
index 2efd149..277ca50 100644
@@ -298,6 +298,15 @@ static void

dw_edma_pcie_get_xilinx_dma_data(struct pci_dev *pdev,

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct pci_dev

Quoted 21 lines
drivers/dma/dw-edma/dw-edma-pcie.c +8 -1
      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,

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct pci_dev *pdev,

Quoted 9 lines
drivers/dma/dw-edma/dw-edma-pcie.c +3 -2
      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)

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct pci_dev · line 348

Quoted 2 lines
drivers/dma/dw-edma/dw-edma-pcie.c +1 -1
-                     return -ENOMEM;
+             if (vsec_data->devmem_phys_off ==

DW_PCIE_AMD_MDB_INVALID_ADDR)

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct pci_dev

Quoted 23 lines
drivers/dma/dw-edma/dw-edma-pcie.c +8 -5
+                     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,

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct pci_dev

Quoted 17 lines
drivers/dma/dw-edma/dw-edma-pcie.c +1 -0
      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;
drivers/dma/dw-edma/dw-edma-pcie.c +1 -1
-     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,

Quoted 6 lines
drivers/dma/dw-edma/dw-edma-pcie.c +2 -1
                      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);
Quoted 5 lines
Quoted 3 lines

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

Quoted 76 lines
Quoted 76 lines

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct pci_dev

Quoted 5 lines
drivers/dma/dw-edma/dw-edma-pcie.c +0 -0
              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,

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct pci_dev

Quoted 16 lines
drivers/dma/dw-edma/dw-edma-pcie.c +3 -2
                      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,

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct pci_dev

Quoted 10 lines
drivers/dma/dw-edma/dw-edma-pcie.c +2 -1
                      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,

drivers/dma/dw-edma/dw-hdma-v0-core.c static void dw_hdma_v0_sync_ll_data(struct

Quoted 15 lines
drivers/dma/dw-edma/dw-edma-pcie.c +2 -1
                      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;
      }
drivers/dma/dw-edma/dw-hdma-v0-core.c +0 -0
index e3f8db4..a5d12bc 100644
@@ -225,7 +225,7 @@ static void dw_hdma_v0_sync_ll_data(struct

dw_edma_chunk *chunk)

drivers/dma/dw-edma/dw-hdma-v0-core.c static void dw_hdma_v0_core_start(struct

Quoted 10 lines
drivers/dma/dw-edma/dw-hdma-v0-core.c +2 -1
              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)

drivers/dma/dw-edma/dw-hdma-v0-core.c static void dw_hdma_v0_core_start(struct · line 266

Quoted 4 lines
drivers/dma/dw-edma/dw-hdma-v0-core.c +1 -0
      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)

drivers/dma/dw-edma/dw-hdma-v0-core.c static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first) · line 273

Quoted 7 lines
drivers/dma/dw-edma/dw-hdma-v0-core.c +7 -0
+{
+     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.

Quoted 3 lines
Quoted 1 line

Frank

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

Just fill one. list_for_each_entry() cause confuse.

Frank

Quoted 84 lines
Quoted 84 lines
Quoted 84 lines
drivers/dma/dw-edma/dw-hdma-v0-core.c +52 -0
+             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;
drivers/dma/dw-edma/dw-hdma-v0-regs.h +1 -0
index eab5fd7..7759ba9 100644
@@ -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)
include/linux/dma/edma.h index renamed from include/linux/dma/edma.h +0 -0

3080747..78ce31b 100644

include/linux/dma/edma.h +1 -1
@@ -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

#12 lore raw

RE: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode

[AMD Official Use Only - AMD Internal Distribution Only]

Quoted 60 lines

-----Original Message-----
From: Frank Li <[email protected]>
Sent: Monday, January 19, 2026 9:15 PM
To: Verma, Devendra <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; linux- [email protected]; Simek, Michal <[email protected]>
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.

Quoted 39 lines · Devendra

[AMD Official Use Only - AMD Internal Distribution Only]

Hi Frank

Please check my response inline.

Regards,
Devendra

Quoted 22 lines

-----Original Message-----
From: Frank Li <[email protected]>
Sent: Thursday, January 15, 2026 10:07 PM
To: Verma, Devendra <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; linux- [email protected]; Simek, Michal <[email protected]>
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.

Quoted 7 lines · Devendra K 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

Quoted 3 lines

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.

Quoted 7 lines

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

Quoted 4 lines

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.

Quoted 123 lines
Quoted 35 lines
Quoted 35 lines
Quoted 35 lines

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.

---
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().

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

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

+++++++++++++++++++++---

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
 drivers/dma/dw-edma/dw-edma-core.h    |  1 +
 drivers/dma/dw-edma/dw-edma-pcie.c    | 46 ++++++++++++++++++--

------

Quoted 79 lines
Quoted 78 lines
Quoted 3 lines
 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.

drivers/dma/dw-edma/dw-edma-core.c static int dw_edma_device_config(struct

Quoted 9 lines
 include/linux/dma/edma.h              |  1 +
 6 files changed, 132 insertions(+), 20 deletions(-)
drivers/dma/dw-edma/dw-edma-core.c +0 -0
index b43255f..d37112b 100644
@@ -223,8 +223,32 @@ static int dw_edma_device_config(struct

dma_chan *dchan,

Quoted 32 lines
drivers/dma/dw-edma/dw-edma-core.c +24 -0
                               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
Quoted 1 line

dw_edma_device_issue_pending(struct

dma_chan *dchan)

Quoted 10 lines
      struct dw_edma_chan *chan = dchan2dw_edma_chan(xfer->dchan);
      enum dma_transfer_direction dir = xfer->direction;
      struct scatterlist *sg = NULL;
drivers/dma/dw-edma/dw-edma-core.c +1 -1
-     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)

drivers/dma/dw-edma/dw-edma-core.c static void dw_edma_device_issue_pending(struct dma_chan *dchan) · line 450

Quoted 11 lines
      if (unlikely(!desc))
              goto err_alloc;
drivers/dma/dw-edma/dw-edma-core.c +5 -3
-     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.

Quoted 8 lines
Quoted 5 lines

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.

Quoted 127 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

      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)

Quoted 51 lines
Quoted 51 lines

drivers/dma/dw-edma/dw-edma-pcie.c static void

Quoted 33 lines
              if (xfer->type == EDMA_XFER_SCATTER_GATHER && !sg)
                      break;
drivers/dma/dw-edma/dw-edma-core.c +7 -1
-             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

drivers/dma/dw-edma/dw-edma-core.h +1 -0
@@ -86,6 +86,7 @@ struct dw_edma_chan {
      u8                              configured;
 
      struct dma_slave_config         config;
+     bool                            non_ll;
 };
 
 struct dw_edma_irq {
drivers/dma/dw-edma/dw-edma-pcie.c +0 -0
index 2efd149..277ca50 100644
@@ -298,6 +298,15 @@ static void

dw_edma_pcie_get_xilinx_dma_data(struct pci_dev *pdev,

Quoted 17 lines
drivers/dma/dw-edma/dw-edma-pcie.c +8 -1
      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;
Quoted 3 lines
Quoted 3 lines

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct

Quoted 3 lines
      vsec_data = kmalloc(sizeof(*vsec_data), GFP_KERNEL);
      if (!vsec_data)
drivers/dma/dw-edma/dw-edma-pcie.c +0 -0
@@ -331,21 +341,24 @@ static int dw_edma_pcie_probe(struct

pci_dev

Quoted 66 lines
Quoted 66 lines

*pdev,

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct pci_dev *pdev,

Quoted 9 lines
drivers/dma/dw-edma/dw-edma-pcie.c +3 -2
      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)

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct · line 348

Quoted 2 lines
drivers/dma/dw-edma/dw-edma-pcie.c +1 -1
-                     return -ENOMEM;
+             if (vsec_data->devmem_phys_off ==

DW_PCIE_AMD_MDB_INVALID_ADDR)

Quoted 23 lines
drivers/dma/dw-edma/dw-edma-pcie.c +9 -5
+                     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
Quoted 1 line

dw_edma_pcie_probe(struct pci_dev

*pdev,

Quoted 16 lines
      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;
drivers/dma/dw-edma/dw-edma-pcie.c +1 -1
-     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 =
Quoted 2 lines

&vsec_data->ll_wr[i]; @@ -412,7 +426,8 @@ static int dw_edma_pcie_probe(struct pci_dev

*pdev,

Quoted 7 lines
                      return -ENOMEM;

              ll_region->vaddr.io += ll_block->off;
drivers/dma/dw-edma/dw-edma-pcie.c +3 -1
-             ll_region->paddr = pci_bus_address(pdev, ll_block->bar);
+             ll_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
+
+ ll_block->bar);
Quoted 8 lines
Quoted 5 lines
Quoted 3 lines

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.

Quoted 83 lines
Quoted 5 lines
Quoted 5 lines

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct

Quoted 5 lines
              ll_region->paddr += ll_block->off;
              ll_region->sz = ll_block->sz;
drivers/dma/dw-edma/dw-edma-pcie.c +0 -0
@@ -421,12 +436,13 @@ static int dw_edma_pcie_probe(struct

pci_dev

Quoted 66 lines
Quoted 66 lines

*pdev,

Quoted 16 lines
drivers/dma/dw-edma/dw-edma-pcie.c +4 -2
                      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 =
Quoted 2 lines

&vsec_data->ll_rd[i]; @@ -437,7 +453,8 @@ static int dw_edma_pcie_probe(struct pci_dev

*pdev,

drivers/dma/dw-edma/dw-edma-pcie.c static int dw_edma_pcie_probe(struct pci_dev

Quoted 11 lines
                      return -ENOMEM;

              ll_region->vaddr.io += ll_block->off;
drivers/dma/dw-edma/dw-edma-pcie.c +3 -1
-             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,

drivers/dma/dw-edma/dw-hdma-v0-core.c static void dw_hdma_v0_sync_ll_data(struct

Quoted 16 lines
drivers/dma/dw-edma/dw-edma-pcie.c +3 -1
                      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;
      }
drivers/dma/dw-edma/dw-hdma-v0-core.c +0 -0
index e3f8db4..a5d12bc 100644
@@ -225,7 +225,7 @@ static void dw_hdma_v0_sync_ll_data(struct

dw_edma_chunk *chunk)

Quoted 11 lines
drivers/dma/dw-edma/dw-hdma-v0-core.c +0 -1
              readl(chunk->ll_region.vaddr.io);  }
 
-static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk,

bool
first)

drivers/dma/dw-edma/dw-hdma-v0-core.c +2 -0
+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)

Quoted 4 lines
      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)

Quoted 9 lines
Quoted 9 lines

drivers/dma/dw-edma/dw-hdma-v0-core.c static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first) · line 273

Quoted 7 lines
drivers/dma/dw-edma/dw-hdma-v0-core.c +7 -0
+{
+     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.

Quoted 1 line
Quoted 1 line
Quoted 7 lines
Quoted 3 lines
Quoted 1 line

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.

Quoted 86 lines
Quoted 48 lines
Quoted 48 lines
Quoted 48 lines
drivers/dma/dw-edma/dw-hdma-v0-core.c +46 -0
+             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) {
Quoted 8 lines
Quoted 8 lines

drivers/dma/dw-edma/dw-hdma-v0-core.c static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first) · line 325

Quoted 8 lines
drivers/dma/dw-edma/dw-hdma-v0-core.c +7 -0
+     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)

{

Quoted 29 lines
Quoted 29 lines
Quoted 29 lines
      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

drivers/dma/dw-edma/dw-hdma-v0-regs.h +1 -0
@@ -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)
include/linux/dma/edma.h +1 -1
index 3080747..78ce31b 100644
@@ -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

#13 lore raw

Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode

Quoted 199 lines · Devendra

[AMD Official Use Only - AMD Internal Distribution Only]

Quoted 60 lines

-----Original Message-----
From: Frank Li <[email protected]>
Sent: Monday, January 19, 2026 9:15 PM
To: Verma, Devendra <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; linux- [email protected]; Simek, Michal <[email protected]>
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.

Quoted 39 lines · Devendra

[AMD Official Use Only - AMD Internal Distribution Only]

Hi Frank

Please check my response inline.

Regards,
Devendra

Quoted 22 lines

-----Original Message-----
From: Frank Li <[email protected]>
Sent: Thursday, January 15, 2026 10:07 PM
To: Verma, Devendra <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; linux- [email protected]; Simek, Michal <[email protected]>
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.

Quoted 7 lines · Devendra K 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

Quoted 3 lines

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.

Quoted 7 lines

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

Quoted 4 lines

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.

Quoted 129 lines
Quoted 35 lines
Quoted 35 lines
Quoted 35 lines

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.

---
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().

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

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

+++++++++++++++++++++---

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
 drivers/dma/dw-edma/dw-edma-core.h    |  1 +
 drivers/dma/dw-edma/dw-edma-pcie.c    | 46 ++++++++++++++++++--

------

Quoted 84 lines
Quoted 74 lines
Quoted 3 lines
 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.

drivers/dma/dw-edma/dw-edma-core.c static int dw_edma_device_config(struct

Quoted 9 lines
 include/linux/dma/edma.h              |  1 +
 6 files changed, 132 insertions(+), 20 deletions(-)
drivers/dma/dw-edma/dw-edma-core.c +0 -0
index b43255f..d37112b 100644
@@ -223,8 +223,32 @@ static int dw_edma_device_config(struct

dma_chan *dchan,

Quoted 32 lines
drivers/dma/dw-edma/dw-edma-core.c +24 -0
                               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
Quoted 1 line

dw_edma_device_issue_pending(struct

dma_chan *dchan)

Quoted 10 lines
      struct dw_edma_chan *chan = dchan2dw_edma_chan(xfer->dchan);
      enum dma_transfer_direction dir = xfer->direction;
      struct scatterlist *sg = NULL;
drivers/dma/dw-edma/dw-edma-core.c +1 -1
-     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)

drivers/dma/dw-edma/dw-edma-core.c static void dw_edma_device_issue_pending(struct dma_chan *dchan) · line 450

Quoted 11 lines
      if (unlikely(!desc))
              goto err_alloc;
drivers/dma/dw-edma/dw-edma-core.c +5 -3
-     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;
+     }
Quoted 4 lines

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.

Quoted 6 lines
Quoted 2 lines

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.

Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

...

Quoted 12 lines
Quoted 11 lines
Quoted 8 lines
Quoted 6 lines
Quoted 2 lines

+
+ 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

Quoted 6 lines

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.

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

              ll_region->paddr += ll_block->off;
              ll_region->sz = ll_block->sz;

...

Quoted 22 lines
Quoted 14 lines
Quoted 12 lines
Quoted 12 lines
Quoted 2 lines

+static void dw_hdma_v0_core_non_ll_start(struct dw_edma_chunk

*chunk)

drivers/dma/dw-edma/dw-edma-core.c

Quoted 7 lines
drivers/dma/dw-edma/dw-edma-core.c +7 -0
+{
+     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.

Quoted 1 line
Quoted 1 line
Quoted 7 lines
Quoted 3 lines
Quoted 1 line

Frank

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

Just fill one. list_for_each_entry() cause confuse.

Frank

Quoted 8 lines

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

Quoted 87 lines
Quoted 87 lines
Quoted 49 lines
Quoted 49 lines
Quoted 49 lines
drivers/dma/dw-edma/dw-edma-core.c +46 -0
+             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) {
Quoted 8 lines
Quoted 8 lines

drivers/dma/dw-edma/dw-edma-core.c

Quoted 8 lines
drivers/dma/dw-edma/dw-edma-core.c +7 -0
+     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)

{

Quoted 29 lines
Quoted 29 lines
Quoted 29 lines
      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

drivers/dma/dw-edma/dw-hdma-v0-regs.h +1 -0
@@ -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)
include/linux/dma/edma.h +1 -1
index 3080747..78ce31b 100644
@@ -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

#14 lore raw

RE: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode

[AMD Official Use Only - AMD Internal Distribution Only]

Quoted 11 lines

-----Original Message-----
From: Frank Li <[email protected]>
Sent: Wednesday, January 21, 2026 10:49 PM
To: Verma, Devendra <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; linux- [email protected]; Simek, Michal <[email protected]>
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 ]--

Quoted 177 lines
Quoted 28 lines
Quoted 28 lines
Quoted 28 lines
Quoted 11 lines · Devendra K Verma
Quoted 8 lines

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

Quoted 3 lines

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

Quoted 31 lines
Quoted 4 lines

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.

Quoted 23 lines
Quoted 23 lines
Quoted 23 lines
Quoted 23 lines

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.

---
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().

Quoted 12 lines
Quoted 12 lines
Quoted 12 lines
Quoted 12 lines
Quoted 12 lines

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().

Quoted 8 lines
Quoted 8 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

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

+++++++++++++++++++++---

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
 drivers/dma/dw-edma/dw-edma-core.h    |  1 +
 drivers/dma/dw-edma/dw-edma-pcie.c    | 46

++++++++++++++++++--

Quoted 29 lines
Quoted 29 lines

------

Quoted 28 lines
Quoted 28 lines
Quoted 3 lines
 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.

drivers/dma/dw-edma/dw-edma-core.c static int dw_edma_device_config(struct

Quoted 9 lines
 include/linux/dma/edma.h              |  1 +
 6 files changed, 132 insertions(+), 20 deletions(-)
drivers/dma/dw-edma/dw-edma-core.c +0 -0
index b43255f..d37112b 100644
@@ -223,8 +223,32 @@ static int dw_edma_device_config(struct

dma_chan *dchan,

Quoted 11 lines
drivers/dma/dw-edma/dw-edma-core.c +8 -0
                               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;
+     }
 
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines

drivers/dma/dw-edma/dw-edma-core.c static int dw_edma_device_config(struct dma_chan *dchan,

Quoted 4 lines
drivers/dma/dw-edma/dw-edma-core.c +3 -0
      memcpy(&chan->config, config, sizeof(*config));
+
+     /*
+      * When there is no valid LLP base address available then the

default

Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

drivers/dma/dw-edma/dw-edma-core.c static int dw_edma_device_config(struct dma_chan *dchan,

Quoted 3 lines
drivers/dma/dw-edma/dw-edma-core.c +3 -0
+      * DMA ops will use the non-LL mode.
+      *
+      * Cases where LL mode is enabled and client wants to use the

non-LL

Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

drivers/dma/dw-edma/dw-edma-core.c static int dw_edma_device_config(struct · line 241

Quoted 1 line
drivers/dma/dw-edma/dw-edma-core.c +1 -0
+      * mode then also client can do so via providing the

peripheral_config

Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines

drivers/dma/dw-edma/dw-edma-core.c static int dw_edma_device_config(struct dma_chan *dchan,

Quoted 7 lines
drivers/dma/dw-edma/dw-edma-core.c +7 -0
+      * 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))

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
drivers/dma/dw-edma/dw-edma-core.c +2 -0
+             chan->non_ll = true;
+
      chan->configured = true;
 
Quoted 34 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 3 lines
drivers/dma/dw-edma/dw-edma-core.c +0 -0
      return 0;
@@ -353,7 +377,7 @@ static void

dw_edma_device_issue_pending(struct

dma_chan *dchan)

Quoted 1 line

struct dw_edma_chan *chan = dchan2dw_edma_chan(xfer-

dchan);

Quoted 28 lines
Quoted 28 lines
Quoted 22 lines
Quoted 9 lines
      enum dma_transfer_direction dir = xfer->direction;
      struct scatterlist *sg = NULL;
drivers/dma/dw-edma/dw-edma-core.c +1 -1
-     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)

drivers/dma/dw-edma/dw-edma-core.c static void dw_edma_device_issue_pending(struct dma_chan *dchan) · line 450

Quoted 11 lines
      if (unlikely(!desc))
              goto err_alloc;
drivers/dma/dw-edma/dw-edma-core.c +5 -3
-     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;
+     }
Quoted 4 lines

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.

Quoted 9 lines
Quoted 7 lines
Quoted 4 lines

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

Quoted 9 lines

"non_ll"

Quoted 2 lines

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.

Quoted 24 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

...

Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 6 lines
Quoted 2 lines

+
+ ll_block->bar);

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

Quoted 7 lines
Quoted 4 lines
Quoted 1 line

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.

Quoted 4 lines

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.

Quoted 7 lines

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/

Quoted 44 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

              ll_region->paddr += ll_block->off;
              ll_region->sz = ll_block->sz;

...

Quoted 19 lines
Quoted 19 lines
Quoted 13 lines
Quoted 13 lines
Quoted 2 lines

+static void dw_hdma_v0_core_non_ll_start(struct dw_edma_chunk

*chunk)

drivers/dma/dw-edma/dw-edma-core.c

Quoted 7 lines
drivers/dma/dw-edma/dw-edma-core.c +7 -0
+{
+     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.

Quoted 4 lines
Quoted 2 lines

Frank

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

Quoted 7 lines
Quoted 3 lines

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.

Quoted 5 lines

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.

Quoted 9 lines
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.

Quoted 91 lines
Quoted 91 lines
Quoted 91 lines
Quoted 53 lines
Quoted 53 lines
Quoted 53 lines
drivers/dma/dw-edma/dw-edma-core.c +50 -0
+             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;
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
drivers/dma/dw-edma/dw-edma-core.c +6 -0
+
+     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)

{

Quoted 29 lines
Quoted 29 lines
Quoted 29 lines
      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

drivers/dma/dw-edma/dw-hdma-v0-regs.h +1 -0
@@ -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)
(quoted diff) +0 -0

b/include/linux/dma/edma.h index 3080747..78ce31b 100644

include/linux/dma/edma.h +1 -1
@@ -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

#15 lore raw

Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode

Quoted 203 lines · Devendra

[AMD Official Use Only - AMD Internal Distribution Only]

Quoted 11 lines

-----Original Message-----
From: Frank Li <[email protected]>
Sent: Wednesday, January 21, 2026 10:49 PM
To: Verma, Devendra <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; linux- [email protected]; Simek, Michal <[email protected]>
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 ]--

Quoted 177 lines
Quoted 28 lines
Quoted 28 lines
Quoted 28 lines
Quoted 11 lines · Devendra K Verma
Quoted 8 lines

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

Quoted 3 lines

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

Quoted 31 lines
Quoted 4 lines

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.

Quoted 23 lines
Quoted 23 lines
Quoted 23 lines
Quoted 23 lines

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.

---
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().

Quoted 12 lines
Quoted 12 lines
Quoted 12 lines
Quoted 12 lines
Quoted 12 lines

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().

Quoted 8 lines
Quoted 8 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

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

+++++++++++++++++++++---

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
 drivers/dma/dw-edma/dw-edma-core.h    |  1 +
 drivers/dma/dw-edma/dw-edma-pcie.c    | 46

++++++++++++++++++--

Quoted 29 lines
Quoted 29 lines

------

Quoted 28 lines
Quoted 28 lines
Quoted 3 lines
 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.

drivers/dma/dw-edma/dw-edma-core.c static int dw_edma_device_config(struct

Quoted 9 lines
 include/linux/dma/edma.h              |  1 +
 6 files changed, 132 insertions(+), 20 deletions(-)
drivers/dma/dw-edma/dw-edma-core.c +0 -0
index b43255f..d37112b 100644
@@ -223,8 +223,32 @@ static int dw_edma_device_config(struct

dma_chan *dchan,

Quoted 11 lines
drivers/dma/dw-edma/dw-edma-core.c +8 -0
                               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;
+     }
 
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines

drivers/dma/dw-edma/dw-edma-core.c static int dw_edma_device_config(struct · line 237

Quoted 4 lines
drivers/dma/dw-edma/dw-edma-core.c +3 -0
      memcpy(&chan->config, config, sizeof(*config));
+
+     /*
+      * When there is no valid LLP base address available then the

default

Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

drivers/dma/dw-edma/dw-edma-core.c static int dw_edma_device_config(struct · line 240

Quoted 3 lines
drivers/dma/dw-edma/dw-edma-core.c +3 -0
+      * DMA ops will use the non-LL mode.
+      *
+      * Cases where LL mode is enabled and client wants to use the

non-LL

Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

drivers/dma/dw-edma/dw-edma-core.c static int dw_edma_device_config(struct · line 241

Quoted 1 line
drivers/dma/dw-edma/dw-edma-core.c +1 -0
+      * mode then also client can do so via providing the

peripheral_config

Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines

drivers/dma/dw-edma/dw-edma-core.c static int dw_edma_device_config(struct · line 248

Quoted 7 lines
drivers/dma/dw-edma/dw-edma-core.c +7 -0
+      * 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))

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
drivers/dma/dw-edma/dw-edma-core.c +2 -0
+             chan->non_ll = true;
+
      chan->configured = true;
 
Quoted 34 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 3 lines
drivers/dma/dw-edma/dw-edma-core.c +0 -0
      return 0;
@@ -353,7 +377,7 @@ static void

dw_edma_device_issue_pending(struct

dma_chan *dchan)

Quoted 1 line

struct dw_edma_chan *chan = dchan2dw_edma_chan(xfer-

dchan);

Quoted 28 lines
Quoted 28 lines
Quoted 22 lines
Quoted 9 lines
      enum dma_transfer_direction dir = xfer->direction;
      struct scatterlist *sg = NULL;
drivers/dma/dw-edma/dw-edma-core.c +1 -1
-     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)

drivers/dma/dw-edma/dw-edma-core.c

Quoted 11 lines
      if (unlikely(!desc))
              goto err_alloc;
drivers/dma/dw-edma/dw-edma-core.c +5 -3
-     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;
+     }
Quoted 4 lines

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.

Quoted 9 lines
Quoted 7 lines
Quoted 4 lines

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

Quoted 9 lines

"non_ll"

Quoted 2 lines

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.

Quoted 5 lines

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/[email protected]/T/#t

Quoted 4 lines

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.

Quoted 9 lines

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.

Quoted 40 lines
Quoted 25 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines

...

Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 6 lines
Quoted 2 lines

+
+ ll_block->bar);

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

Quoted 7 lines
Quoted 4 lines
Quoted 1 line

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.

Quoted 4 lines

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.

Quoted 7 lines

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.

Quoted 7 lines

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.

Quoted 4 lines

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.

Quoted 66 lines
Quoted 44 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

              ll_region->paddr += ll_block->off;
              ll_region->sz = ll_block->sz;

...

Quoted 19 lines
Quoted 19 lines
Quoted 13 lines
Quoted 13 lines
Quoted 2 lines

+static void dw_hdma_v0_core_non_ll_start(struct dw_edma_chunk

*chunk)

drivers/dma/dw-edma/dw-edma-core.c

Quoted 7 lines
drivers/dma/dw-edma/dw-edma-core.c +7 -0
+{
+     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.

Quoted 4 lines
Quoted 2 lines

Frank

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

Quoted 7 lines
Quoted 3 lines

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.

Quoted 5 lines

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.

Quoted 9 lines
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.

(quoted diff) +5 -0
+       /*
+        * 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.
+        */
Quoted 5 lines

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

Quoted 92 lines
Quoted 92 lines
Quoted 92 lines
Quoted 92 lines
Quoted 54 lines
Quoted 54 lines
Quoted 54 lines
drivers/dma/dw-edma/dw-edma-core.c +50 -0
+             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;
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
drivers/dma/dw-edma/dw-edma-core.c +6 -0
+
+     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)

{

Quoted 29 lines
Quoted 29 lines
Quoted 29 lines
      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

drivers/dma/dw-edma/dw-hdma-v0-regs.h +1 -0
@@ -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)
(quoted diff) +0 -0

b/include/linux/dma/edma.h index 3080747..78ce31b 100644

include/linux/dma/edma.h +1 -1
@@ -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

#16 lore raw

RE: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode

[AMD Official Use Only - AMD Internal Distribution Only]

Quoted 8 lines

-----Original Message-----
From: Frank Li <[email protected]>
Sent: Tuesday, January 27, 2026 12:30 AM
To: Verma, Devendra <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; linux- [email protected]; Simek, Michal <[email protected]>
Subject: Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode

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

Quoted 224 lines
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

AMD MDB IP supports Linked List (LL) mode as well as non-LL

mode.

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 7 lines

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

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 3 lines

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.

Quoted 15 lines
Quoted 15 lines
Quoted 15 lines
Quoted 14 lines
Quoted 10 lines

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?

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

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.

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

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,

Quoted 27 lines
Quoted 27 lines
Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
Quoted 11 lines

and if user wants to use non-LL mode then user can do so via configuring the peripheral_config param of dma_slave_config.

---
Changes in v8
Cosmetic change related to comment and code.

Changes in v7
No change

Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines

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().

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

Comments follow the 80-column guideline.

Changes in v4
No change

Changes in v3
No change

Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines

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().

Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

Corrected the typo raised in review.

---
 drivers/dma/dw-edma/dw-edma-core.c    | 42

+++++++++++++++++++++---

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
 drivers/dma/dw-edma/dw-edma-core.h    |  1 +
 drivers/dma/dw-edma/dw-edma-pcie.c    | 46
Quoted 8 lines
Quoted 8 lines

++++++++++++++++++--

Quoted 7 lines
Quoted 7 lines

------

Quoted 6 lines
Quoted 6 lines
Quoted 3 lines
 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.

Quoted 96 lines
Quoted 96 lines
Quoted 24 lines
Quoted 24 lines
Quoted 24 lines
Quoted 24 lines
Quoted 11 lines
 include/linux/dma/edma.h              |  1 +
 6 files changed, 132 insertions(+), 20 deletions(-)
drivers/dma/dw-edma/dw-edma-core.c +0 -0
index b43255f..d37112b 100644
@@ -223,8 +223,32 @@ static int

dw_edma_device_config(struct

dma_chan *dchan,

Quoted 12 lines

struct dma_slave_config *config) {
struct dw_edma_chan *chan =
dchan2dw_edma_chan(dchan);

drivers/dma/dw-edma/dw-edma-core.c +8 -0
+     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;
+     }
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

drivers/dma/dw-edma/dw-edma-core.c

Quoted 5 lines
      memcpy(&chan->config, config, sizeof(*config));
drivers/dma/dw-edma/dw-edma-core.c +4 -0
+
+     /*
+      * When there is no valid LLP base address available
+ then the

default

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines

drivers/dma/dw-edma/dw-edma-core.c

Quoted 4 lines
drivers/dma/dw-edma/dw-edma-core.c +4 -0
+      * DMA ops will use the non-LL mode.
+      *
+      * Cases where LL mode is enabled and client wants
+ to use the

non-LL

Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

+ * mode then also client can do so via providing the

peripheral_config

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

drivers/dma/dw-edma/dw-edma-core.c

Quoted 8 lines
drivers/dma/dw-edma/dw-edma-core.c +8 -0
+      * 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))

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
drivers/dma/dw-edma/dw-edma-core.c +2 -0
+             chan->non_ll = true;
+
      chan->configured = true;
Quoted 34 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 3 lines
      return 0;
drivers/dma/dw-edma/dw-edma-core.c +0 -0
@@ -353,7 +377,7 @@ static void

dw_edma_device_issue_pending(struct

dma_chan *dchan)

Quoted 1 line

struct dw_edma_chan *chan = dchan2dw_edma_chan(xfer-

dchan);

Quoted 28 lines
Quoted 28 lines
Quoted 21 lines
Quoted 8 lines
      enum dma_transfer_direction dir = xfer->direction;
      struct scatterlist *sg = NULL;
drivers/dma/dw-edma/dw-edma-core.c +1 -1
-     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)

drivers/dma/dw-edma/dw-edma-core.c

Quoted 11 lines
      if (unlikely(!desc))
              goto err_alloc;
drivers/dma/dw-edma/dw-edma-core.c +5 -3
-     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;
+     }
Quoted 4 lines

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.

Quoted 10 lines
Quoted 7 lines
Quoted 4 lines

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"

Quoted 8 lines
Quoted 8 lines
Quoted 3 lines

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.

Quoted 4 lines
Quoted 1 line

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.

Quoted 3 lines

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-
[email protected]/T/#t

Quoted 5 lines

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.

Quoted 6 lines

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

Quoted 8 lines

case of LL mode also we can have a single descriptor as similar to non-LL mode.

Quoted 2 lines

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.

Quoted 49 lines
Quoted 17 lines
Quoted 17 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines

...

Quoted 12 lines
Quoted 11 lines
Quoted 8 lines
Quoted 6 lines
Quoted 2 lines

+
+ 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

Quoted 8 lines
Quoted 8 lines
Quoted 3 lines

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.

Quoted 4 lines

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.

Quoted 7 lines
Quoted 7 lines

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.

Quoted 10 lines
Quoted 1 line

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.

Quoted 3 lines

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.

Quoted 1 line
Quoted 8 lines
Quoted 1 line

This also increases the work related to testing and maintaining multiple

patches.

Quoted 3 lines

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.

Quoted 4 lines
Quoted 2 lines

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:

Quoted 1 line
Quoted 9 lines

https://lore.kernel.org/all/SA1PR12MB8120341DFFD56D90EAD70EDE9514A@
SA1

Quoted 2 lines

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

Quoted 78 lines
Quoted 49 lines
Quoted 49 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines


              ll_region->paddr += ll_block->off;
              ll_region->sz = ll_block->sz;

...

Quoted 21 lines
Quoted 21 lines
Quoted 4 lines
Quoted 4 lines

drivers/dma/dw-edma/dw-edma-core.c

Quoted 3 lines
drivers/dma/dw-edma/dw-edma-core.c +2 -0
+static void dw_hdma_v0_core_non_ll_start(struct
+dw_edma_chunk

*chunk)

Quoted 11 lines
Quoted 11 lines

drivers/dma/dw-edma/dw-edma-core.c

Quoted 8 lines
drivers/dma/dw-edma/dw-edma-core.c +8 -0
+{
+     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.

Quoted 4 lines
Quoted 2 lines

Frank

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

Quoted 8 lines
Quoted 3 lines

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.

Quoted 6 lines

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.

Quoted 6 lines
Quoted 6 lines
list_for_each_entry() {
        ...
        readl_timeout()
}

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

small.

Quoted 10 lines
Quoted 3 lines

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.

Quoted 8 lines
(quoted diff) +5 -0
+       /*
+        * 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.

Quoted 2 lines

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

Quoted 8 lines

entry alone.

Quoted 1 line

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.

Quoted 92 lines
Quoted 92 lines
Quoted 92 lines
Quoted 92 lines
Quoted 92 lines
Quoted 54 lines
Quoted 54 lines
Quoted 54 lines
drivers/dma/dw-edma/dw-edma-core.c +50 -0
+             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;
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
drivers/dma/dw-edma/dw-edma-core.c +6 -0
+
+     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)

{

Quoted 29 lines
Quoted 29 lines
Quoted 29 lines
      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

drivers/dma/dw-edma/dw-hdma-v0-regs.h +1 -0
@@ -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)
(quoted diff) +0 -0

b/include/linux/dma/edma.h index 3080747..78ce31b 100644

include/linux/dma/edma.h +1 -1
@@ -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

#17 lore raw

Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode

Quoted 246 lines · Devendra

[AMD Official Use Only - AMD Internal Distribution Only]

Quoted 8 lines

-----Original Message-----
From: Frank Li <[email protected]>
Sent: Tuesday, January 27, 2026 12:30 AM
To: Verma, Devendra <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; linux- [email protected]; Simek, Michal <[email protected]>
Subject: Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode

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

Quoted 224 lines
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

AMD MDB IP supports Linked List (LL) mode as well as non-LL

mode.

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 7 lines

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

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 3 lines

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.

Quoted 15 lines
Quoted 15 lines
Quoted 15 lines
Quoted 14 lines
Quoted 10 lines

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?

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

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.

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

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,

Quoted 27 lines
Quoted 27 lines
Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
Quoted 11 lines

and if user wants to use non-LL mode then user can do so via configuring the peripheral_config param of dma_slave_config.

---
Changes in v8
Cosmetic change related to comment and code.

Changes in v7
No change

Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines

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().

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

Comments follow the 80-column guideline.

Changes in v4
No change

Changes in v3
No change

Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines

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().

Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

Corrected the typo raised in review.

---
 drivers/dma/dw-edma/dw-edma-core.c    | 42

+++++++++++++++++++++---

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
 drivers/dma/dw-edma/dw-edma-core.h    |  1 +
 drivers/dma/dw-edma/dw-edma-pcie.c    | 46
Quoted 8 lines
Quoted 8 lines

++++++++++++++++++--

Quoted 7 lines
Quoted 7 lines

------

Quoted 6 lines
Quoted 6 lines
Quoted 3 lines
 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.

Quoted 96 lines
Quoted 96 lines
Quoted 24 lines
Quoted 24 lines
Quoted 24 lines
Quoted 24 lines
Quoted 11 lines
 include/linux/dma/edma.h              |  1 +
 6 files changed, 132 insertions(+), 20 deletions(-)
drivers/dma/dw-edma/dw-edma-core.c +0 -0
index b43255f..d37112b 100644
@@ -223,8 +223,32 @@ static int

dw_edma_device_config(struct

dma_chan *dchan,

Quoted 12 lines

struct dma_slave_config *config) {
struct dw_edma_chan *chan =
dchan2dw_edma_chan(dchan);

drivers/dma/dw-edma/dw-edma-core.c +8 -0
+     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;
+     }
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

drivers/dma/dw-edma/dw-edma-core.c

Quoted 5 lines
      memcpy(&chan->config, config, sizeof(*config));
drivers/dma/dw-edma/dw-edma-core.c +4 -0
+
+     /*
+      * When there is no valid LLP base address available
+ then the

default

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines

drivers/dma/dw-edma/dw-edma-core.c

Quoted 4 lines
drivers/dma/dw-edma/dw-edma-core.c +4 -0
+      * DMA ops will use the non-LL mode.
+      *
+      * Cases where LL mode is enabled and client wants
+ to use the

non-LL

Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

+ * mode then also client can do so via providing the

peripheral_config

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

drivers/dma/dw-edma/dw-edma-core.c

Quoted 8 lines
drivers/dma/dw-edma/dw-edma-core.c +8 -0
+      * 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))

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
drivers/dma/dw-edma/dw-edma-core.c +2 -0
+             chan->non_ll = true;
+
      chan->configured = true;
Quoted 34 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 3 lines
      return 0;
drivers/dma/dw-edma/dw-edma-core.c +0 -0
@@ -353,7 +377,7 @@ static void

dw_edma_device_issue_pending(struct

dma_chan *dchan)

Quoted 1 line

struct dw_edma_chan *chan = dchan2dw_edma_chan(xfer-

dchan);

Quoted 28 lines
Quoted 28 lines
Quoted 21 lines
Quoted 8 lines
      enum dma_transfer_direction dir = xfer->direction;
      struct scatterlist *sg = NULL;
drivers/dma/dw-edma/dw-edma-core.c +1 -1
-     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)

drivers/dma/dw-edma/dw-edma-core.c

Quoted 11 lines
      if (unlikely(!desc))
              goto err_alloc;
drivers/dma/dw-edma/dw-edma-core.c +5 -3
-     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;
+     }
Quoted 4 lines

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.

Quoted 10 lines
Quoted 7 lines
Quoted 4 lines

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"

Quoted 8 lines
Quoted 8 lines
Quoted 3 lines

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.

Quoted 4 lines
Quoted 1 line

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.

Quoted 3 lines

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-
[email protected]/T/#t

Quoted 5 lines

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.

Quoted 6 lines

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

Quoted 8 lines

case of LL mode also we can have a single descriptor as similar to non-LL mode.

Quoted 2 lines

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.

Quoted 14 lines

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

Quoted 267 lines

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.

Quoted 49 lines
Quoted 17 lines
Quoted 17 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines

...

Quoted 12 lines
Quoted 11 lines
Quoted 8 lines
Quoted 6 lines
Quoted 2 lines

+
+ 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

Quoted 8 lines
Quoted 8 lines
Quoted 3 lines

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.

Quoted 4 lines

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.

Quoted 7 lines
Quoted 7 lines

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.

Quoted 10 lines
Quoted 1 line

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.

Quoted 3 lines

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.

Quoted 1 line
Quoted 8 lines
Quoted 1 line

This also increases the work related to testing and maintaining multiple

patches.

Quoted 3 lines

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.

Quoted 4 lines
Quoted 2 lines

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:

Quoted 1 line
Quoted 9 lines

https://lore.kernel.org/all/SA1PR12MB8120341DFFD56D90EAD70EDE9514A@
SA1

Quoted 2 lines

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

Quoted 78 lines
Quoted 49 lines
Quoted 49 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines


              ll_region->paddr += ll_block->off;
              ll_region->sz = ll_block->sz;

...

Quoted 21 lines
Quoted 21 lines
Quoted 4 lines
Quoted 4 lines

drivers/dma/dw-edma/dw-edma-core.c

Quoted 3 lines
drivers/dma/dw-edma/dw-edma-core.c +2 -0
+static void dw_hdma_v0_core_non_ll_start(struct
+dw_edma_chunk

*chunk)

Quoted 11 lines
Quoted 11 lines

drivers/dma/dw-edma/dw-edma-core.c

Quoted 8 lines
drivers/dma/dw-edma/dw-edma-core.c +8 -0
+{
+     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.

Quoted 4 lines
Quoted 2 lines

Frank

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

Quoted 8 lines
Quoted 3 lines

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.

Quoted 6 lines

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.

Quoted 6 lines
Quoted 6 lines
list_for_each_entry() {
        ...
        readl_timeout()
}

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

small.

Quoted 10 lines
Quoted 3 lines

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.

Quoted 8 lines
(quoted diff) +5 -0
+       /*
+        * 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.

Quoted 2 lines

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

Quoted 8 lines

entry alone.

Quoted 1 line

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.

Quoted 92 lines
Quoted 92 lines
Quoted 92 lines
Quoted 92 lines
Quoted 92 lines
Quoted 54 lines
Quoted 54 lines
Quoted 54 lines
drivers/dma/dw-edma/dw-edma-core.c +50 -0
+             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;
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
drivers/dma/dw-edma/dw-edma-core.c +6 -0
+
+     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)

{

Quoted 29 lines
Quoted 29 lines
Quoted 29 lines
      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

drivers/dma/dw-edma/dw-hdma-v0-regs.h +1 -0
@@ -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)
(quoted diff) +0 -0

b/include/linux/dma/edma.h index 3080747..78ce31b 100644

include/linux/dma/edma.h +1 -1
@@ -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

#18 lore raw

RE: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode

[AMD Official Use Only - AMD Internal Distribution Only]

Quoted 8 lines

-----Original Message-----
From: Frank Li <[email protected]>
Sent: Wednesday, January 28, 2026 8:38 PM
To: Verma, Devendra <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; linux- [email protected]; Simek, Michal <[email protected]>
Subject: Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode

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

Quoted 276 lines
Quoted 7 lines
Quoted 7 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

AMD MDB IP supports Linked List (LL) mode as well as non-LL

mode.

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines

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

Quoted 24 lines
Quoted 24 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 7 lines
Quoted 3 lines

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.

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

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.

Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines

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

Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 4 lines

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.

Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

be using the non-LL mode only. This, the default non-LL

mode,

Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

is not applicable for Synopsys IP with the current code

addition.

Quoted 5 lines
Quoted 5 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

- If the default mode is LL-mode, for both AMD
(Xilinx) and

Synosys,

Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

and if user wants to use non-LL mode then user can do so

via

Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

configuring the peripheral_config param of

dma_slave_config.

Quoted 129 lines
Quoted 129 lines
Quoted 26 lines
Quoted 26 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines

Signed-off-by: Devendra K Verma
<[email protected]>
---
Changes in v8
Cosmetic change related to comment and code.

Changes in v7
No change

Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines

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().

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

Comments follow the 80-column guideline.

Changes in v4
No change

Changes in v3
No change

Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines

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().

Quoted 7 lines
Quoted 7 lines
Quoted 6 lines
Quoted 6 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

Corrected the typo raised in review.

---
 drivers/dma/dw-edma/dw-edma-core.c    | 42

+++++++++++++++++++++---

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
 drivers/dma/dw-edma/dw-edma-core.h    |  1 +
 drivers/dma/dw-edma/dw-edma-pcie.c    | 46

++++++++++++++++++--

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

------

Quoted 7 lines
Quoted 7 lines
Quoted 3 lines
 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.

Quoted 80 lines
Quoted 80 lines
Quoted 24 lines
Quoted 24 lines
Quoted 24 lines
Quoted 24 lines
Quoted 11 lines
 include/linux/dma/edma.h              |  1 +
 6 files changed, 132 insertions(+), 20 deletions(-)
drivers/dma/dw-edma/dw-edma-core.c +0 -0
index b43255f..d37112b 100644
@@ -223,8 +223,32 @@ static int

dw_edma_device_config(struct

dma_chan *dchan,

Quoted 12 lines

struct dma_slave_config *config) {
struct dw_edma_chan *chan =
dchan2dw_edma_chan(dchan);

drivers/dma/dw-edma/dw-edma-core.c +8 -0
+     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;
+     }
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

drivers/dma/dw-edma/dw-edma-core.c

Quoted 5 lines
      memcpy(&chan->config, config, sizeof(*config));
drivers/dma/dw-edma/dw-edma-core.c +4 -0
+
+     /*
+      * When there is no valid LLP base address
+ available then the

default

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines

drivers/dma/dw-edma/dw-edma-core.c

Quoted 4 lines
drivers/dma/dw-edma/dw-edma-core.c +4 -0
+      * DMA ops will use the non-LL mode.
+      *
+      * Cases where LL mode is enabled and client
+ wants to use the

non-LL

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

+ * mode then also client can do so via providing
+ the

peripheral_config

Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines

drivers/dma/dw-edma/dw-edma-core.c

Quoted 9 lines
drivers/dma/dw-edma/dw-edma-core.c +9 -0
+      * 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))

Quoted 32 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 7 lines
drivers/dma/dw-edma/dw-edma-core.c +2 -0
+             chan->non_ll = true;
+
      chan->configured = true;
 
      return 0;
@@ -353,7 +377,7 @@ static void

dw_edma_device_issue_pending(struct

dma_chan *dchan)

Quoted 2 lines

struct dw_edma_chan *chan =
dchan2dw_edma_chan(xfer-

dchan);

Quoted 21 lines
Quoted 21 lines
Quoted 21 lines
Quoted 8 lines
      enum dma_transfer_direction dir = xfer->direction;
      struct scatterlist *sg = NULL;
drivers/dma/dw-edma/dw-edma-core.c +1 -1
-     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)

Quoted 11 lines
      if (unlikely(!desc))
              goto err_alloc;
drivers/dma/dw-edma/dw-edma-core.c +5 -3
-     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;
+     }
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

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

entry).

Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
Quoted 11 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 3 lines

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.

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines

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.

Quoted 29 lines
Quoted 29 lines
Quoted 10 lines
Quoted 10 lines
Quoted 6 lines
Quoted 3 lines

You can use new variable, such as ll_avail.

In order to separate out the execution paths a new meaningful variable

"non_ll"

Quoted 3 lines

is used. The variable "non_ll" alone is sufficient. Using another variable along side "non_ll" for the similar purpose may not have any

Quoted 6 lines
Quoted 6 lines

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.

Quoted 5 lines
Quoted 1 line

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.

Quoted 4 lines

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.

Quoted 8 lines
Quoted 8 lines

I optimzied it to allow dynatmic appended dma descriptors.

https://lore.kernel.org/dmaengine/20260109-edma_dymatic-v1-0-
[email protected]/T/#t

Quoted 3 lines

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.

Quoted 16 lines
Quoted 16 lines
Quoted 3 lines

However, the requirement of ll_avail does not help for the supporting the

non-LL mode.

Quoted 7 lines

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.

Quoted 2 lines

Please check the function dw_hdma_v0_core_start() in this review where the decision is taken Based on non_ll flag.

Quoted 7 lines
Quoted 3 lines

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

Quoted 1 line

name non_ll is being used for driving the non-LL mode not only in this file

but also

Quoted 1 line

in the files relevant to HDMA. This flag gives the clear indication of LL vs

non-LL mode.

Quoted 1 line

In the function dw_hdma_v0_core_start(), non_ll decided the mode to

choose.

Quoted 1 line

2. The use of "ll_avail" is ambiguous for both the modes. First, it is

associated with LL mode only.

Quoted 1 line

It will be used for optimizing / testing the Controller performance based

on the

Quoted 1 line

number of descriptors available on the Device DDR side which is for LL

mode. So when

Quoted 1 line

it is being used for LL mode then it is obvious that it excludes any use for

non-LL mode.

Quoted 1 line

In the API dw_edma_device_transfer(), the ll_avail will be used for

creating the bursts.

Quoted 1 line

If ll_avail = 1 in the above API, then it is ambiguous whether it is creating

Quoted 9 lines

the burst for

Quoted 1 line

LL or non-LL mode. In the above API, the non_ll is sufficient to have the

LL mode

Quoted 1 line

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.

Quoted 6 lines
(quoted diff) +5 -0
+               /*
+                * 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.
+                */
Quoted 7 lines

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.

Quoted 4 lines

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.

Quoted 287 lines

Frank

Quoted 59 lines

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.

Quoted 54 lines
Quoted 20 lines
Quoted 20 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines

...

Quoted 15 lines
Quoted 11 lines
Quoted 8 lines
Quoted 6 lines
Quoted 2 lines

+
+ 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

Quoted 6 lines
Quoted 6 lines

function.

Quoted 5 lines

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.

Quoted 7 lines
Quoted 7 lines

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.

Quoted 10 lines
Quoted 1 line

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.

Quoted 4 lines

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.

Quoted 3 lines

This also increases the work related to testing and maintaining multiple

patches.

Quoted 15 lines
Quoted 6 lines
Quoted 3 lines

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.

Quoted 6 lines
Quoted 3 lines

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:

Quoted 1 line

https://lore.kernel.org/all/SA1PR12MB8120341DFFD56D90EAD70EDE9514A@

Quoted 7 lines
Quoted 7 lines

SA1

Quoted 2 lines

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.

Quoted 5 lines
Quoted 1 line

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.

Quoted 68 lines

Regards,
Devendra

Quoted 64 lines
Quoted 49 lines
Quoted 49 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines


              ll_region->paddr += ll_block->off;
              ll_region->sz = ll_block->sz;

...

Quoted 21 lines
Quoted 21 lines
Quoted 4 lines
Quoted 4 lines

drivers/dma/dw-edma/dw-edma-core.c

Quoted 3 lines
drivers/dma/dw-edma/dw-edma-core.c +2 -0
+static void dw_hdma_v0_core_non_ll_start(struct
+dw_edma_chunk

*chunk)

Quoted 11 lines
Quoted 11 lines

drivers/dma/dw-edma/dw-edma-core.c

Quoted 8 lines
drivers/dma/dw-edma/dw-edma-core.c +8 -0
+{
+     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.

Quoted 4 lines
Quoted 2 lines

Frank

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

Quoted 8 lines
Quoted 3 lines

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.

Quoted 6 lines

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.

Quoted 7 lines
Quoted 7 lines
list_for_each_entry() {
        ...
        readl_timeout()
}

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

small.

Quoted 7 lines
Quoted 3 lines

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.

Quoted 48 lines
Quoted 18 lines
Quoted 4 lines

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.

Quoted 9 lines
(quoted diff) +5 -0
+       /*
+        * 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.

Quoted 3 lines

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

Quoted 9 lines

entry alone.

Quoted 2 lines

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.

Quoted 18 lines
Quoted 18 lines
Quoted 18 lines
Quoted 18 lines
Quoted 18 lines
Quoted 18 lines
Quoted 18 lines
Quoted 18 lines
drivers/dma/dw-edma/dw-edma-core.c +14 -0
+             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));
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

drivers/dma/dw-edma/dw-edma-core.c

Quoted 8 lines
drivers/dma/dw-edma/dw-edma-core.c +8 -0
+
+             /* 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)

|

Quoted 70 lines
Quoted 70 lines
Quoted 70 lines
Quoted 70 lines
Quoted 70 lines
Quoted 70 lines
Quoted 31 lines
Quoted 31 lines
Quoted 31 lines
drivers/dma/dw-edma/dw-edma-core.c +31 -0
+                             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)
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
drivers/dma/dw-edma/dw-edma-core.c +5 -0
+             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)

{

Quoted 30 lines
Quoted 30 lines
Quoted 30 lines
      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

drivers/dma/dw-edma/dw-hdma-v0-regs.h +1 -0
@@ -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)
(quoted diff) +0 -0

b/include/linux/dma/edma.h index 3080747..78ce31b
100644

include/linux/dma/edma.h +1 -1
@@ -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

#19 lore raw

RE: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode

[AMD Official Use Only - AMD Internal Distribution Only]

Hi Frank

Any update on this review?

Regards,
Devendra

Quoted 682 lines

-----Original Message-----
From: Verma, Devendra <[email protected]>
Sent: Thursday, January 29, 2026 5:26 PM
To: Frank Li <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; linux- [email protected]; Simek, Michal <[email protected]>; Verma, Devendra <[email protected]>
Subject: RE: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode

[AMD Official Use Only - AMD Internal Distribution Only]

Quoted 8 lines

-----Original Message-----
From: Frank Li <[email protected]>
Sent: Wednesday, January 28, 2026 8:38 PM
To: Verma, Devendra <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; linux- [email protected]; Simek, Michal <[email protected]>
Subject: Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode

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

Quoted 10 lines
Quoted 8 lines
Quoted 8 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

AMD MDB IP supports Linked List (LL) mode as well as non-LL

mode.

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

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

Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

the device side DDR is not configured, then the IP can still

be

Quoted 227 lines
Quoted 23 lines
Quoted 23 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 6 lines
Quoted 2 lines

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.

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

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.

Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines

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

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 4 lines

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.

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines

be using the non-LL mode only. This, the default
non-LL

mode,

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

is not applicable for Synopsys IP with the current
code

addition.

Quoted 6 lines
Quoted 6 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

- If the default mode is LL-mode, for both AMD
(Xilinx) and

Synosys,

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

and if user wants to use non-LL mode then user can
do so

via

Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

configuring the peripheral_config param of

dma_slave_config.

Quoted 131 lines
Quoted 131 lines
Quoted 27 lines
Quoted 27 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines

Signed-off-by: Devendra K Verma
<[email protected]>
---
Changes in v8
Cosmetic change related to comment and code.

Changes in v7
No change

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

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().

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

Comments follow the 80-column guideline.

Changes in v4
No change

Changes in v3
No change

Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines

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().

Quoted 7 lines
Quoted 7 lines
Quoted 6 lines
Quoted 6 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

Corrected the typo raised in review.

---
 drivers/dma/dw-edma/dw-edma-core.c    | 42

+++++++++++++++++++++---

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
 drivers/dma/dw-edma/dw-edma-core.h    |  1 +
 drivers/dma/dw-edma/dw-edma-pcie.c    | 46

++++++++++++++++++--

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

------

Quoted 7 lines
Quoted 7 lines
Quoted 3 lines
 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.

Quoted 81 lines
Quoted 81 lines
Quoted 24 lines
Quoted 24 lines
Quoted 24 lines
Quoted 24 lines
Quoted 11 lines
 include/linux/dma/edma.h              |  1 +
 6 files changed, 132 insertions(+), 20 deletions(-)
drivers/dma/dw-edma/dw-edma-core.c +0 -0
index b43255f..d37112b 100644
@@ -223,8 +223,32 @@ static int

dw_edma_device_config(struct

dma_chan *dchan,

Quoted 12 lines

struct dma_slave_config *config) {
struct dw_edma_chan *chan =
dchan2dw_edma_chan(dchan);

drivers/dma/dw-edma/dw-edma-core.c +8 -0
+     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;
+     }
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines

drivers/dma/dw-edma/dw-edma-core.c

Quoted 6 lines
      memcpy(&chan->config, config,

sizeof(*config));

drivers/dma/dw-edma/dw-edma-core.c +4 -0
+
+     /*
+      * When there is no valid LLP base address
+ available then the

default

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines

drivers/dma/dw-edma/dw-edma-core.c

Quoted 4 lines
drivers/dma/dw-edma/dw-edma-core.c +4 -0
+      * DMA ops will use the non-LL mode.
+      *
+      * Cases where LL mode is enabled and client
+ wants to use the

non-LL

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

+ * mode then also client can do so via
+ providing the

peripheral_config

Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines

drivers/dma/dw-edma/dw-edma-core.c

Quoted 9 lines
drivers/dma/dw-edma/dw-edma-core.c +9 -0
+      * 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))

Quoted 32 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 7 lines
drivers/dma/dw-edma/dw-edma-core.c +2 -0
+             chan->non_ll = true;
+
      chan->configured = true;
 
      return 0;
@@ -353,7 +377,7 @@ static void

dw_edma_device_issue_pending(struct

dma_chan *dchan)

Quoted 2 lines

struct dw_edma_chan *chan =
dchan2dw_edma_chan(xfer-

dchan);

Quoted 21 lines
Quoted 21 lines
Quoted 21 lines
Quoted 8 lines
      enum dma_transfer_direction dir = xfer->direction;
      struct scatterlist *sg = NULL;
drivers/dma/dw-edma/dw-edma-core.c +1 -1
-     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)

Quoted 11 lines
      if (unlikely(!desc))
              goto err_alloc;
drivers/dma/dw-edma/dw-edma-core.c +5 -3
-     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;
+     }
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

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

entry).

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 3 lines

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

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

The ll_max is defined for the session till the driver is loaded in the

kernel.

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

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.

Quoted 31 lines
Quoted 31 lines
Quoted 10 lines
Quoted 10 lines
Quoted 6 lines
Quoted 3 lines

You can use new variable, such as ll_avail.

In order to separate out the execution paths a new meaningful variable

"non_ll"

Quoted 3 lines

is used. The variable "non_ll" alone is sufficient. Using another variable along side "non_ll" for the similar purpose may not have any

Quoted 6 lines
Quoted 6 lines

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.

Quoted 5 lines
Quoted 1 line

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.

Quoted 4 lines

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.

Quoted 14 lines
Quoted 7 lines
Quoted 7 lines

https://lore.kernel.org/dmaengine/20260109-edma_dymatic-v1-0-
[email protected]/T/#t

Quoted 4 lines

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.

Quoted 6 lines
Quoted 6 lines
Quoted 3 lines

However, the requirement of ll_avail does not help for the supporting the

non-LL mode.

Quoted 2 lines

For non-LL mode to use:
1) Either LL mode shall be not available, as it can happen for the Xilinx

IP.

Quoted 46 lines
Quoted 10 lines
Quoted 10 lines
Quoted 5 lines

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.

Quoted 2 lines

Please check the function dw_hdma_v0_core_start() in this review where the decision is taken Based on non_ll flag.

Quoted 8 lines
Quoted 3 lines

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

Quoted 2 lines

name non_ll is being used for driving the non-LL mode not only
in this file

but also

Quoted 2 lines

in the files relevant to HDMA. This flag gives the clear
indication of LL vs

non-LL mode.

Quoted 2 lines

In the function dw_hdma_v0_core_start(), non_ll decided the mode
to

choose.

Quoted 2 lines

2. The use of "ll_avail" is ambiguous for both the modes. First, it is

associated with LL mode only.

Quoted 2 lines

It will be used for optimizing / testing the Controller
performance based

on the

Quoted 2 lines

number of descriptors available on the Device DDR side which is
for LL

mode. So when

Quoted 2 lines

it is being used for LL mode then it is obvious that it excludes
any use for

non-LL mode.

Quoted 2 lines

In the API dw_edma_device_transfer(), the ll_avail will be used
for

creating the bursts.

Quoted 2 lines

If ll_avail = 1 in the above API, then it is ambiguous whether
it is creating

the burst for

Quoted 9 lines
Quoted 2 lines

LL or non-LL mode. In the above API, the non_ll is sufficient
to have the

LL mode

Quoted 1 line

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.

Quoted 6 lines
(quoted diff) +5 -0
+               /*
+                * 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.
+                */
Quoted 7 lines

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.

Quoted 4 lines

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.

Quoted 78 lines

Frank

Quoted 75 lines

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.

Quoted 52 lines
Quoted 21 lines
Quoted 21 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines

...

Quoted 16 lines
Quoted 12 lines
Quoted 8 lines
Quoted 6 lines
Quoted 2 lines

+
+ 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

Quoted 6 lines
Quoted 6 lines

function.

Quoted 5 lines

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.

Quoted 7 lines
Quoted 7 lines

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.

Quoted 10 lines
Quoted 1 line

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.

Quoted 4 lines

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.

Quoted 1 line
Quoted 9 lines
Quoted 2 lines

This also increases the work related to testing and maintaining multiple

patches.

Quoted 3 lines

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.

Quoted 6 lines
Quoted 3 lines

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:

Quoted 1 line

https://lore.kernel.org/all/SA1PR12MB8120341DFFD56D90EAD70EDE9514A@

Quoted 217 lines
Quoted 8 lines
Quoted 8 lines

SA1

Quoted 2 lines

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.

Quoted 6 lines
Quoted 1 line

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.

Quoted 71 lines

Regards,
Devendra

Quoted 67 lines
Quoted 51 lines
Quoted 51 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines


              ll_region->paddr += ll_block->off;
              ll_region->sz = ll_block->sz;

...

Quoted 22 lines
Quoted 22 lines
Quoted 4 lines
Quoted 4 lines

drivers/dma/dw-edma/dw-edma-core.c

Quoted 3 lines
drivers/dma/dw-edma/dw-edma-core.c +2 -0
+static void dw_hdma_v0_core_non_ll_start(struct
+dw_edma_chunk

*chunk)

Quoted 12 lines
Quoted 4 lines
Quoted 4 lines
drivers/dma/dw-edma/dw-edma-core.c +4 -0
+{
+     struct dw_edma_chan *chan = chunk->chan;
+     struct dw_edma *dw = chan->dw;
+     struct dw_edma_burst *child;
Quoted 8 lines

drivers/dma/dw-edma/dw-edma-core.c

Quoted 5 lines
drivers/dma/dw-edma/dw-edma-core.c +5 -0
+     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.

Quoted 4 lines
Quoted 2 lines

Frank

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

Quoted 8 lines
Quoted 3 lines

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.

Quoted 7 lines

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.

Quoted 7 lines
Quoted 7 lines
list_for_each_entry() {
        ...
        readl_timeout()
}

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

small.

Quoted 8 lines
Quoted 3 lines

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.

Quoted 49 lines
Quoted 18 lines
Quoted 4 lines

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.

Quoted 9 lines
(quoted diff) +5 -0
+       /*
+        * 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.

Quoted 3 lines

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

Quoted 9 lines

entry alone.

Quoted 2 lines

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.

Quoted 19 lines
Quoted 19 lines
Quoted 19 lines
Quoted 19 lines
Quoted 19 lines
Quoted 19 lines
Quoted 19 lines
Quoted 19 lines
drivers/dma/dw-edma/dw-edma-core.c +15 -0
+             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));
+
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

drivers/dma/dw-edma/dw-edma-core.c

Quoted 8 lines
drivers/dma/dw-edma/dw-edma-core.c +8 -0
+             /* 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)

|

Quoted 71 lines
Quoted 71 lines
Quoted 71 lines
Quoted 71 lines
Quoted 71 lines
Quoted 71 lines
Quoted 32 lines
Quoted 32 lines
Quoted 32 lines
drivers/dma/dw-edma/dw-edma-core.c +32 -0
+                             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)
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
drivers/dma/dw-edma/dw-edma-core.c +5 -0
+             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)

{

Quoted 30 lines
Quoted 30 lines
Quoted 30 lines
      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

drivers/dma/dw-edma/dw-hdma-v0-regs.h +1 -0
@@ -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)
(quoted diff) +0 -0

b/include/linux/dma/edma.h index 3080747..78ce31b
100644

include/linux/dma/edma.h +1 -1
@@ -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

#20 lore raw

Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode

Quoted 377 lines · Devendra

[AMD Official Use Only - AMD Internal Distribution Only]

Hi Frank

Any update on this review?

Regards,
Devendra

Quoted 368 lines

-----Original Message-----
From: Verma, Devendra <[email protected]>
Sent: Thursday, January 29, 2026 5:26 PM
To: Frank Li <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; linux- [email protected]; Simek, Michal <[email protected]>; Verma, Devendra <[email protected]>
Subject: RE: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode

[AMD Official Use Only - AMD Internal Distribution Only]

Quoted 8 lines

-----Original Message-----
From: Frank Li <[email protected]>
Sent: Wednesday, January 28, 2026 8:38 PM
To: Verma, Devendra <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; linux- [email protected]; Simek, Michal <[email protected]>
Subject: Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode

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

Quoted 10 lines
Quoted 8 lines
Quoted 8 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

AMD MDB IP supports Linked List (LL) mode as well as non-LL

mode.

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

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

Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

the device side DDR is not configured, then the IP can still

be

Quoted 227 lines
Quoted 23 lines
Quoted 23 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 6 lines
Quoted 2 lines

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.

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

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.

Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines

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

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 4 lines

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.

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines

be using the non-LL mode only. This, the default
non-LL

mode,

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

is not applicable for Synopsys IP with the current
code

addition.

Quoted 6 lines
Quoted 6 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

- If the default mode is LL-mode, for both AMD
(Xilinx) and

Synosys,

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

and if user wants to use non-LL mode then user can
do so

via

Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

configuring the peripheral_config param of

dma_slave_config.

Quoted 131 lines
Quoted 131 lines
Quoted 27 lines
Quoted 27 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines

Signed-off-by: Devendra K Verma
<[email protected]>
---
Changes in v8
Cosmetic change related to comment and code.

Changes in v7
No change

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

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().

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

Comments follow the 80-column guideline.

Changes in v4
No change

Changes in v3
No change

Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines

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().

Quoted 7 lines
Quoted 7 lines
Quoted 6 lines
Quoted 6 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

Corrected the typo raised in review.

---
 drivers/dma/dw-edma/dw-edma-core.c    | 42

+++++++++++++++++++++---

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
 drivers/dma/dw-edma/dw-edma-core.h    |  1 +
 drivers/dma/dw-edma/dw-edma-pcie.c    | 46

++++++++++++++++++--

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

------

Quoted 7 lines
Quoted 7 lines
Quoted 3 lines
 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.

Quoted 81 lines
Quoted 81 lines
Quoted 24 lines
Quoted 24 lines
Quoted 24 lines
Quoted 24 lines
Quoted 11 lines
 include/linux/dma/edma.h              |  1 +
 6 files changed, 132 insertions(+), 20 deletions(-)
drivers/dma/dw-edma/dw-edma-core.c +0 -0
index b43255f..d37112b 100644
@@ -223,8 +223,32 @@ static int

dw_edma_device_config(struct

dma_chan *dchan,

Quoted 12 lines

struct dma_slave_config *config) {
struct dw_edma_chan *chan =
dchan2dw_edma_chan(dchan);

drivers/dma/dw-edma/dw-edma-core.c +8 -0
+     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;
+     }
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines

drivers/dma/dw-edma/dw-edma-core.c

Quoted 6 lines
      memcpy(&chan->config, config,

sizeof(*config));

drivers/dma/dw-edma/dw-edma-core.c +4 -0
+
+     /*
+      * When there is no valid LLP base address
+ available then the

default

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines

drivers/dma/dw-edma/dw-edma-core.c

Quoted 4 lines
drivers/dma/dw-edma/dw-edma-core.c +4 -0
+      * DMA ops will use the non-LL mode.
+      *
+      * Cases where LL mode is enabled and client
+ wants to use the

non-LL

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

+ * mode then also client can do so via
+ providing the

peripheral_config

Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines

drivers/dma/dw-edma/dw-edma-core.c

Quoted 9 lines
drivers/dma/dw-edma/dw-edma-core.c +9 -0
+      * 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))

Quoted 32 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 7 lines
drivers/dma/dw-edma/dw-edma-core.c +2 -0
+             chan->non_ll = true;
+
      chan->configured = true;
 
      return 0;
@@ -353,7 +377,7 @@ static void

dw_edma_device_issue_pending(struct

dma_chan *dchan)

Quoted 2 lines

struct dw_edma_chan *chan =
dchan2dw_edma_chan(xfer-

dchan);

Quoted 21 lines
Quoted 21 lines
Quoted 21 lines
Quoted 8 lines
      enum dma_transfer_direction dir = xfer->direction;
      struct scatterlist *sg = NULL;
drivers/dma/dw-edma/dw-edma-core.c +1 -1
-     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)

Quoted 11 lines
      if (unlikely(!desc))
              goto err_alloc;
drivers/dma/dw-edma/dw-edma-core.c +5 -3
-     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;
+     }
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

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

entry).

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 3 lines

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

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

The ll_max is defined for the session till the driver is loaded in the

kernel.

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

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.

Quoted 31 lines
Quoted 31 lines
Quoted 10 lines
Quoted 10 lines
Quoted 6 lines
Quoted 3 lines

You can use new variable, such as ll_avail.

In order to separate out the execution paths a new meaningful variable

"non_ll"

Quoted 3 lines

is used. The variable "non_ll" alone is sufficient. Using another variable along side "non_ll" for the similar purpose may not have any

Quoted 6 lines
Quoted 6 lines

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.

Quoted 5 lines
Quoted 1 line

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.

Quoted 4 lines

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.

Quoted 14 lines
Quoted 7 lines
Quoted 7 lines

https://lore.kernel.org/dmaengine/20260109-edma_dymatic-v1-0-
[email protected]/T/#t

Quoted 4 lines

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.

Quoted 6 lines
Quoted 6 lines
Quoted 3 lines

However, the requirement of ll_avail does not help for the supporting the

non-LL mode.

Quoted 2 lines

For non-LL mode to use:
1) Either LL mode shall be not available, as it can happen for the Xilinx

IP.

Quoted 46 lines
Quoted 10 lines
Quoted 10 lines
Quoted 5 lines

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.

Quoted 2 lines

Please check the function dw_hdma_v0_core_start() in this review where the decision is taken Based on non_ll flag.

Quoted 8 lines
Quoted 3 lines

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

Quoted 2 lines

name non_ll is being used for driving the non-LL mode not only
in this file

but also

Quoted 2 lines

in the files relevant to HDMA. This flag gives the clear
indication of LL vs

non-LL mode.

Quoted 2 lines

In the function dw_hdma_v0_core_start(), non_ll decided the mode
to

choose.

Quoted 2 lines

2. The use of "ll_avail" is ambiguous for both the modes. First, it is

associated with LL mode only.

Quoted 2 lines

It will be used for optimizing / testing the Controller
performance based

on the

Quoted 2 lines

number of descriptors available on the Device DDR side which is
for LL

mode. So when

Quoted 2 lines

it is being used for LL mode then it is obvious that it excludes
any use for

non-LL mode.

Quoted 2 lines

In the API dw_edma_device_transfer(), the ll_avail will be used
for

creating the bursts.

Quoted 2 lines

If ll_avail = 1 in the above API, then it is ambiguous whether
it is creating

the burst for

Quoted 9 lines
Quoted 2 lines

LL or non-LL mode. In the above API, the non_ll is sufficient
to have the

LL mode

Quoted 1 line

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.

Quoted 6 lines
(quoted diff) +5 -0
+               /*
+                * 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.
+                */
Quoted 7 lines

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.

Quoted 2 lines
Quoted 2 lines

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)
	...
}
Quoted 3 lines
Quoted 3 lines

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.

Quoted 3 lines

The bursts associated with that chunk will

Quoted 2 lines

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/[email protected]/

Quoted 2 lines
Quoted 2 lines

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

Quoted 306 lines
Quoted 305 lines
Quoted 6 lines

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.

Quoted 78 lines

Frank

Quoted 75 lines

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.

Quoted 52 lines
Quoted 21 lines
Quoted 21 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines

...

Quoted 16 lines
Quoted 12 lines
Quoted 8 lines
Quoted 6 lines
Quoted 2 lines

+
+ 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

Quoted 6 lines
Quoted 6 lines

function.

Quoted 5 lines

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.

Quoted 7 lines
Quoted 7 lines

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.

Quoted 10 lines
Quoted 1 line

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.

Quoted 4 lines

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.

Quoted 1 line
Quoted 9 lines
Quoted 2 lines

This also increases the work related to testing and maintaining multiple

patches.

Quoted 3 lines

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.

Quoted 6 lines
Quoted 3 lines

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:

Quoted 1 line

https://lore.kernel.org/all/SA1PR12MB8120341DFFD56D90EAD70EDE9514A@

Quoted 217 lines
Quoted 8 lines
Quoted 8 lines

SA1

Quoted 2 lines

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.

Quoted 6 lines
Quoted 1 line

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.

Quoted 71 lines

Regards,
Devendra

Quoted 67 lines
Quoted 51 lines
Quoted 51 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines


              ll_region->paddr += ll_block->off;
              ll_region->sz = ll_block->sz;

...

Quoted 22 lines
Quoted 22 lines
Quoted 4 lines
Quoted 4 lines

drivers/dma/dw-edma/dw-edma-core.c

Quoted 3 lines
drivers/dma/dw-edma/dw-edma-core.c +2 -0
+static void dw_hdma_v0_core_non_ll_start(struct
+dw_edma_chunk

*chunk)

Quoted 12 lines
Quoted 4 lines
Quoted 4 lines
drivers/dma/dw-edma/dw-edma-core.c +4 -0
+{
+     struct dw_edma_chan *chan = chunk->chan;
+     struct dw_edma *dw = chan->dw;
+     struct dw_edma_burst *child;
Quoted 8 lines

drivers/dma/dw-edma/dw-edma-core.c

Quoted 5 lines
drivers/dma/dw-edma/dw-edma-core.c +5 -0
+     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.

Quoted 4 lines
Quoted 2 lines

Frank

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

Quoted 8 lines
Quoted 3 lines

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.

Quoted 7 lines

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.

Quoted 7 lines
Quoted 7 lines
list_for_each_entry() {
        ...
        readl_timeout()
}

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

small.

Quoted 8 lines
Quoted 3 lines

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.

Quoted 49 lines
Quoted 18 lines
Quoted 4 lines

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.

Quoted 9 lines
(quoted diff) +5 -0
+       /*
+        * 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.

Quoted 3 lines

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

Quoted 9 lines

entry alone.

Quoted 2 lines

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.

Quoted 19 lines
Quoted 19 lines
Quoted 19 lines
Quoted 19 lines
Quoted 19 lines
Quoted 19 lines
Quoted 19 lines
Quoted 19 lines
drivers/dma/dw-edma/dw-edma-core.c +15 -0
+             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));
+
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

drivers/dma/dw-edma/dw-edma-core.c

Quoted 8 lines
drivers/dma/dw-edma/dw-edma-core.c +8 -0
+             /* 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)

|

Quoted 71 lines
Quoted 71 lines
Quoted 71 lines
Quoted 71 lines
Quoted 71 lines
Quoted 71 lines
Quoted 32 lines
Quoted 32 lines
Quoted 32 lines
drivers/dma/dw-edma/dw-edma-core.c +32 -0
+                             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)
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
drivers/dma/dw-edma/dw-edma-core.c +5 -0
+             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)

{

Quoted 30 lines
Quoted 30 lines
Quoted 30 lines
      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

drivers/dma/dw-edma/dw-hdma-v0-regs.h +1 -0
@@ -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)
(quoted diff) +0 -0

b/include/linux/dma/edma.h index 3080747..78ce31b
100644

include/linux/dma/edma.h +1 -1
@@ -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

#21 lore raw

RE: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode

[AMD Official Use Only - AMD Internal Distribution Only]

Hi Frank

Thanks for your suggestions, please check my response inline.

Regards,
Devendra

Quoted 9 lines

-----Original Message-----
From: Frank Li <[email protected]>
Sent: Tuesday, February 3, 2026 10:23 PM
To: Verma, Devendra <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; linux- [email protected]; Simek, Michal <[email protected]>
Subject: Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode

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

Quoted 373 lines
Quoted 26 lines
Quoted 26 lines
Quoted 12 lines
Quoted 9 lines
Quoted 9 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

AMD MDB IP supports Linked List (LL) mode as
well as non-LL

mode.

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

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

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

the device side DDR is not configured, then
the IP can still

be

Quoted 13 lines
Quoted 13 lines
Quoted 13 lines
Quoted 13 lines
Quoted 13 lines
Quoted 13 lines
Quoted 13 lines
Quoted 13 lines
Quoted 6 lines
Quoted 2 lines

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

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

address.

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines

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.

Quoted 96 lines
Quoted 96 lines
Quoted 96 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines

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

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 4 lines

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.

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines

be using the non-LL mode only. This, the
default non-LL

mode,

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

is not applicable for Synopsys IP with the
current code

addition.

Quoted 6 lines
Quoted 6 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

- If the default mode is LL-mode, for both AMD
(Xilinx) and

Synosys,

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

and if user wants to use non-LL mode then user
can do so

via

Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

configuring the peripheral_config param of

dma_slave_config.

Quoted 63 lines
Quoted 63 lines
Quoted 27 lines
Quoted 27 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines

Signed-off-by: Devendra K Verma
<[email protected]>
---
Changes in v8
Cosmetic change related to comment and code.

Changes in v7
No change

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

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().

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

Comments follow the 80-column guideline.

Changes in v4
No change

Changes in v3
No change

Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines

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().

Quoted 7 lines
Quoted 7 lines
Quoted 6 lines
Quoted 6 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

Corrected the typo raised in review.
---
drivers/dma/dw-edma/dw-edma-core.c | 42

+++++++++++++++++++++---

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

drivers/dma/dw-edma/dw-edma-core.h | 1 +
drivers/dma/dw-edma/dw-edma-pcie.c | 46

++++++++++++++++++--

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

------

Quoted 7 lines
Quoted 7 lines
Quoted 3 lines

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.

Quoted 13 lines
Quoted 13 lines
Quoted 13 lines
Quoted 13 lines
Quoted 13 lines
Quoted 13 lines
Quoted 12 lines

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,

Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines

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");

Quoted 83 lines
Quoted 83 lines
Quoted 83 lines
Quoted 61 lines
Quoted 61 lines
Quoted 61 lines
Quoted 61 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines

+ return -EINVAL;
+ }

memcpy(&chan->config, config,
sizeof(*config));
+
+ /*
+ * When there is no valid LLP base address
+ available then the

default

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines

+ * DMA ops will use the non-LL mode.
+ *
+ * Cases where LL mode is enabled and
+ client wants to use the

non-LL

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

+ * mode then also client can do so via
+ providing the

peripheral_config

Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines

+ * 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))

Quoted 33 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 7 lines

+ chan->non_ll = true;
+
chan->configured = true;

return 0;
@@ -353,7 +377,7 @@ static void
dw_edma_device_issue_pending(struct

dma_chan *dchan)

Quoted 2 lines

struct dw_edma_chan *chan =
dchan2dw_edma_chan(xfer-

dchan);

Quoted 22 lines
Quoted 22 lines
Quoted 22 lines
Quoted 9 lines

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)

Quoted 11 lines

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;
+ }

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

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

entry).

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 3 lines

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

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

The ll_max is defined for the session till the driver is loaded in the

kernel.

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

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.

Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 3 lines

You can use new variable, such as ll_avail.

In order to separate out the execution paths a new meaningful variable

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

"non_ll"

Quoted 3 lines

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.

Quoted 43 lines
Quoted 43 lines
Quoted 17 lines
Quoted 17 lines
Quoted 17 lines
Quoted 2 lines
Quoted 2 lines

also ll_avail can help test corner case when ll item small. Normall case it is

hard to reach ll_max.

Quoted 5 lines
Quoted 1 line

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.

Quoted 4 lines

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.

Quoted 15 lines
Quoted 7 lines
Quoted 7 lines

https://lore.kernel.org/dmaengine/20260109-edma_dymatic-v1-0-
[email protected]/T/#t

Quoted 4 lines

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.

Quoted 7 lines
Quoted 7 lines
Quoted 3 lines

However, the requirement of ll_avail does not help for the supporting the

non-LL mode.

Quoted 3 lines

For non-LL mode to use:
1) Either LL mode shall be not available, as it can happen for the Xilinx

IP.

Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
Quoted 5 lines

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.

Quoted 2 lines

Please check the function dw_hdma_v0_core_start() in this review where the decision is taken Based on non_ll flag.

Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 3 lines

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:

Quoted 40 lines
Quoted 40 lines
Quoted 39 lines
Quoted 2 lines

1. The use of a new variable breaks the continuity for non-LL mode. The

variable

Quoted 2 lines

name non_ll is being used for driving the non-LL mode not
only in this file

but also

Quoted 2 lines

in the files relevant to HDMA. This flag gives the clear
indication of LL vs

non-LL mode.

Quoted 2 lines

In the function dw_hdma_v0_core_start(), non_ll decided the
mode to

choose.

Quoted 2 lines

2. The use of "ll_avail" is ambiguous for both the modes. First, it is

associated with LL mode only.

Quoted 2 lines

It will be used for optimizing / testing the Controller
performance based

on the

Quoted 2 lines

number of descriptors available on the Device DDR side which
is for LL

mode. So when

Quoted 2 lines

it is being used for LL mode then it is obvious that it
excludes any use for

non-LL mode.

Quoted 2 lines

In the API dw_edma_device_transfer(), the ll_avail will be
used for

creating the bursts.

Quoted 2 lines

If ll_avail = 1 in the above API, then it is ambiguous
whether it is creating

the burst for

Quoted 2 lines

LL or non-LL mode. In the above API, the non_ll is
sufficient to have the

LL mode

Quoted 1 line

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.

Quoted 7 lines
Quoted 7 lines

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.

Quoted 22 lines
Quoted 22 lines

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.

Quoted 11 lines
(quoted diff) +5 -0
+               /*
+                * 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).

Quoted 7 lines
Quoted 7 lines
Quoted 2 lines

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.

Quoted 2 lines
Quoted 2 lines

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()
{

Quoted 7 lines
        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.

Quoted 18 lines
Quoted 3 lines
Quoted 3 lines

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.

Quoted 3 lines

The bursts associated with that chunk will

Quoted 2 lines

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-
[email protected]/

Quoted 8 lines
Quoted 2 lines
Quoted 2 lines

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.

Quoted 320 lines
Quoted 7 lines
Quoted 7 lines
Quoted 6 lines

And anthor important is that,

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

Quoted 8 lines
Quoted 8 lines

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

Quoted 6 lines

Frank

Quoted 4 lines

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.

Quoted 40 lines
Quoted 40 lines
Quoted 40 lines
Quoted 40 lines
Quoted 40 lines
Quoted 23 lines
Quoted 23 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines

...

Quoted 17 lines
Quoted 8 lines
Quoted 8 lines
Quoted 7 lines
Quoted 2 lines

+
+ ll_block->bar);

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

Quoted 5 lines
Quoted 1 line

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

Quoted 6 lines
Quoted 6 lines

function.

Quoted 5 lines

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.

Quoted 7 lines
Quoted 7 lines

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.

Quoted 2 lines
Quoted 1 line
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines

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.

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines

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.

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 1 line

I request

you to review this patch to avoid additional review cycles.

Quoted 3 lines

This also increases the work related to testing and maintaining multiple

patches.

Quoted 2 lines

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.

Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 7 lines
Quoted 4 lines
Quoted 1 line

Please excuse me but this code has already

Thank you for your persevere.

Thank you for your support.

Quoted 8 lines
Quoted 8 lines
Quoted 7 lines
Quoted 6 lines
Quoted 6 lines
Quoted 3 lines

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:

Quoted 1 line

https://lore.kernel.org/all/SA1PR12MB8120341DFFD56D90EAD70EDE9514A@

Quoted 150 lines
Quoted 150 lines
Quoted 150 lines
Quoted 8 lines
Quoted 8 lines

SA1

Quoted 2 lines

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.

Quoted 6 lines
Quoted 1 line

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.

Quoted 72 lines

Regards,
Devendra

Quoted 68 lines
Quoted 52 lines
Quoted 52 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines

ll_region->paddr += ll_block->off;
ll_region->sz = ll_block->sz;

...

Quoted 24 lines
Quoted 24 lines
Quoted 4 lines
Quoted 4 lines
Quoted 3 lines

+static void dw_hdma_v0_core_non_ll_start(struct
+dw_edma_chunk

*chunk)

Quoted 12 lines
Quoted 4 lines
Quoted 4 lines

+{
+ struct dw_edma_chan *chan = chunk->chan;
+ struct dw_edma *dw = chan->dw;
+ struct dw_edma_burst *child;

Quoted 8 lines
Quoted 5 lines

+ 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.

Quoted 4 lines
Quoted 2 lines

Frank

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

Just fill one. list_for_each_entry() cause confuse.

Quoted 7 lines
Quoted 1 line

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.

Quoted 7 lines

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.

Quoted 7 lines
Quoted 7 lines
list_for_each_entry() {
        ...
        readl_timeout()
}

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

small.

Quoted 8 lines
Quoted 3 lines

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.

Quoted 52 lines
Quoted 19 lines
Quoted 4 lines

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.

Quoted 10 lines
(quoted diff) +5 -0
+       /*
+        * 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.

Quoted 3 lines

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

Quoted 9 lines

entry alone.

Quoted 2 lines

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.

Quoted 21 lines
Quoted 21 lines
Quoted 21 lines
Quoted 21 lines
Quoted 21 lines
Quoted 21 lines
Quoted 21 lines
Quoted 21 lines

+ 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));
+

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

+ /* 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)

|

Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines

+ 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)) {

Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines
Quoted 9 lines

+ 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);

Quoted 32 lines
Quoted 32 lines
Quoted 32 lines
Quoted 32 lines
Quoted 32 lines
Quoted 32 lines
Quoted 32 lines
Quoted 32 lines
Quoted 32 lines
Quoted 15 lines
Quoted 15 lines
Quoted 15 lines

+ 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)

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

+ 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)

{

Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

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>

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
Quoted 4 lines

#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)

Quoted 19 lines
Quoted 18 lines
Quoted 18 lines
Quoted 18 lines
Quoted 18 lines
Quoted 18 lines
Quoted 18 lines
Quoted 18 lines
Quoted 18 lines
Quoted 18 lines
Quoted 18 lines
Quoted 18 lines

#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

#22 lore raw

Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode

In reply to Devendra K Verma
kernel test robot noticed the following build errors:

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/[email protected]/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/[email protected]/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 <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/

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

Signature · 2 lines
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
#23 lore raw

Re: [PATCH v8 2/2] dmaengine: dw-edma: Add non-LL mode

In reply to Devendra K Verma
kernel test robot noticed the following build errors:

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/[email protected]/config)
compiler: alpha-linux-gcc (GCC) 15.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260116/[email protected]/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 <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/

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

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