Skip to contentSkip to conversation

Add AMD MDB Endpoint and non-LL mode Support

PATCH RESEND Superseded Devendra K Verma 4 participants 18 messages Nov 21, 2025 – Dec 12, 2025

Subsystems (3)

v6 of 15 Superseded Latest is v15 →

lore
Share & tools
lore
#0 lore raw

v6 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    |  41 ++++++++-
 drivers/dma/dw-edma/dw-edma-core.h    |   1 +
 drivers/dma/dw-edma/dw-edma-pcie.c    | 169 ++++++++++++++++++++++++++++++++--
 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, 260 insertions(+), 14 deletions(-)
Signature · 1 line
1.8.3.1
#1 lore raw

PATCH RESEND v6 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 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 | 139 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 137 insertions(+), 2 deletions(-)
drivers/dma/dw-edma/dw-edma-pcie.c +137 -2
index 3371e0a7..3d7247c 100644
@@ -14,15 +14,31 @@
 #include <linux/pci-epf.h>
 #include <linux/msi.h>
 #include <linux/bitfield.h>
+#include <linux/sizes.h>
 
 #include "dw-edma-core.h"
 
+/* Synopsys */
 #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)
 
+/* AMD MDB (Xilinx) specific defines */
+#define DW_PCIE_XILINX_MDB_VSEC_DMA_ID		0x6
+#define DW_PCIE_XILINX_MDB_VSEC_ID		0x20
+#define PCI_DEVICE_ID_AMD_MDB_B054		0xb054
+#define DW_PCIE_AMD_MDB_INVALID_ADDR		(~0ULL)
+#define DW_PCIE_XILINX_LL_OFF_GAP		0x200000
+#define DW_PCIE_XILINX_LL_SIZE			0x800
+#define DW_PCIE_XILINX_DT_OFF_GAP		0x100000
+#define DW_PCIE_XILINX_DT_SIZE			0x800
+#define DW_PCIE_XILINX_MDB_VSEC_HDR_ID		0x20
+#define DW_PCIE_XILINX_MDB_VSEC_REV		0x1
+#define DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_HIGH	0xc
+#define DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_LOW	0x8
+
 #define DW_BLOCK(a, b, c) \
 	{ \
 		.bar = a, \
@@ -50,6 +66,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 +107,64 @@ struct dw_edma_pcie_data {
 	.rd_ch_cnt			= 2,
 };
 
+static const struct dw_edma_pcie_data amd_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);
@@ -120,9 +195,24 @@ static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
 	u32 val, map;
 	u16 vsec;
 	u64 off;
+	int cap;
+
+	/*
+	 * Synopsys and AMD (Xilinx) use the same VSEC ID for the purpose
+	 * of map, channel counts, etc.
+	 */
+	switch (pdev->vendor) {
+	case PCI_VENDOR_ID_SYNOPSYS:
+		cap = DW_PCIE_VSEC_DMA_ID;
+		break;
+	case PCI_VENDOR_ID_XILINX:
+		cap = DW_PCIE_XILINX_MDB_VSEC_DMA_ID;
+		break;
+	default:
+		return;
+	}
 
-	vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_SYNOPSYS,
-					DW_PCIE_VSEC_DMA_ID);
+	vsec = pci_find_vsec_capability(pdev, pdev->vendor, cap);
 	if (!vsec)
 		return;
 
@@ -155,6 +245,28 @@ static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
 	off <<= 32;
 	off |= val;
 	pdata->rg.off = off;
+
+	/* Xilinx specific VSEC capability */
+	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 + PCI_VNDR_HEADER, &val);
+	if (PCI_VNDR_HEADER_ID(val) != DW_PCIE_XILINX_MDB_VSEC_HDR_ID ||
+	    PCI_VNDR_HEADER_REV(val) != DW_PCIE_XILINX_MDB_VSEC_REV)
+		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,
@@ -179,6 +291,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
 	}
 
 	memcpy(vsec_data, pdata, sizeof(struct dw_edma_pcie_data));
+	vsec_data->devmem_phys_off = DW_PCIE_AMD_MDB_INVALID_ADDR;
 
 	/*
 	 * Tries to find if exists a PCIe Vendor-Specific Extended Capability
@@ -186,6 +299,26 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
 	 */
 	dw_edma_pcie_get_vsec_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_AMD_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 amd_mdb_data.
+		 */
+		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 */
 	mask = BIT(vsec_data->rg.bar);
 	for (i = 0; i < vsec_data->wr_ch_cnt; i++) {
@@ -367,6 +500,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_AMD_MDB_B054),
+	  (kernel_ulong_t)&amd_mdb_data },
 	{ }
 };
 MODULE_DEVICE_TABLE(pci, dw_edma_pcie_id_table);
Signature · 1 line
1.8.3.1
#2 lore raw

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

[AMD Official Use Only - AMD Internal Distribution Only]

Hi All

Could you all please review the following patch?

Regards,
Dev

Quoted 278 lines

-----Original Message-----
From: Devendra K Verma <[email protected]>
Sent: Friday, November 21, 2025 5:05 PM
To: [email protected]; [email protected]; [email protected]
Cc: [email protected]; [email protected]; linux- [email protected]; Simek, Michal <[email protected]>; Verma, Devendra <[email protected]>
Subject: [PATCH RESEND v6 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 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 | 139
++++++++++++++++++++++++++++++++++++-
 1 file changed, 137 insertions(+), 2 deletions(-)
drivers/dma/dw- renamed from drivers/dma/dw-edma/dw-edma-pcie.c +0 -0

edma/dw-edma-pcie.c
index 3371e0a7..3d7247c 100644

drivers/dma/dw-edma/dw-edma-pcie.c +32 -0
@@ -14,15 +14,31 @@
 #include <linux/pci-epf.h>
 #include <linux/msi.h>
 #include <linux/bitfield.h>
+#include <linux/sizes.h>
 
 #include "dw-edma-core.h"
 
+/* Synopsys */
 #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)
 
+/* AMD MDB (Xilinx) specific defines */
+#define DW_PCIE_XILINX_MDB_VSEC_DMA_ID               0x6
+#define DW_PCIE_XILINX_MDB_VSEC_ID           0x20
+#define PCI_DEVICE_ID_AMD_MDB_B054           0xb054
+#define DW_PCIE_AMD_MDB_INVALID_ADDR         (~0ULL)
+#define DW_PCIE_XILINX_LL_OFF_GAP            0x200000
+#define DW_PCIE_XILINX_LL_SIZE                       0x800
+#define DW_PCIE_XILINX_DT_OFF_GAP            0x100000
+#define DW_PCIE_XILINX_DT_SIZE                       0x800
+#define DW_PCIE_XILINX_MDB_VSEC_HDR_ID               0x20
+#define DW_PCIE_XILINX_MDB_VSEC_REV          0x1
+#define DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_HIGH       0xc
+#define DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_LOW        0x8
+
 #define DW_BLOCK(a, b, c) \
      { \
              .bar = a, \
@@ -50,6 +66,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
+107,64 @@ struct dw_edma_pcie_data {
      .rd_ch_cnt                      = 2,
 };
 
+static const struct dw_edma_pcie_data amd_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,

drivers/dma/dw-edma/dw-edma-pcie.c +43 -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); @@ -120,9 +195,24 @@

static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

      u32 val, map;
      u16 vsec;
      u64 off;
drivers/dma/dw-edma/dw-edma-pcie.c +26 -2
+     int cap;
+
+     /*
+      * Synopsys and AMD (Xilinx) use the same VSEC ID for the purpose
+      * of map, channel counts, etc.
+      */
+     switch (pdev->vendor) {
+     case PCI_VENDOR_ID_SYNOPSYS:
+             cap = DW_PCIE_VSEC_DMA_ID;
+             break;
+     case PCI_VENDOR_ID_XILINX:
+             cap = DW_PCIE_XILINX_MDB_VSEC_DMA_ID;
+             break;
+     default:
+             return;
+     }
 
-     vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_SYNOPSYS,
-                                     DW_PCIE_VSEC_DMA_ID);
+     vsec = pci_find_vsec_capability(pdev, pdev->vendor, cap);
      if (!vsec)
              return;
@@ -155,6 +245,28 @@ static void
 dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
      off <<= 32;
      off |= val;
      pdata->rg.off = off;
+
+     /* Xilinx specific VSEC capability */
+     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 + PCI_VNDR_HEADER, &val);
+     if (PCI_VNDR_HEADER_ID(val) !=

DW_PCIE_XILINX_MDB_VSEC_HDR_ID ||

drivers/dma/dw-edma/dw-edma-pcie.c +5 -0
+         PCI_VNDR_HEADER_REV(val) != DW_PCIE_XILINX_MDB_VSEC_REV)
+             return;
+
+     pci_read_config_dword(pdev,
+                           vsec +

DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_HIGH,

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,

drivers/dma/dw-edma/dw-edma-pcie.c +4 -0
+                           &val);
+     off <<= 32;
+     off |= val;
+     pdata->devmem_phys_off = off;
 }
 
 static int dw_edma_pcie_probe(struct pci_dev *pdev, @@ -179,6 +291,7 @@

static int dw_edma_pcie_probe(struct pci_dev *pdev,

      }

      memcpy(vsec_data, pdata, sizeof(struct dw_edma_pcie_data));

+ vsec_data->devmem_phys_off =
DW_PCIE_AMD_MDB_INVALID_ADDR;

/*
* Tries to find if exists a PCIe Vendor-Specific Extended Capability

drivers/dma/dw-edma/dw-edma-pcie.c +20 -0
@@ -186,6 +299,26 @@ static int dw_edma_pcie_probe(struct pci_dev
 *pdev,
       */
      dw_edma_pcie_get_vsec_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_AMD_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 amd_mdb_data.
+              */
+             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 */
      mask = BIT(vsec_data->rg.bar);
      for (i = 0; i < vsec_data->wr_ch_cnt; i++) { @@ -367,6 +500,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_AMD_MDB_B054),
+       (kernel_ulong_t)&amd_mdb_data },
      { }
 };
 MODULE_DEVICE_TABLE(pci, dw_edma_pcie_id_table);
--

1.8.3.1

#3 lore raw

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

In reply to Devendra K Verma
Quoted 161 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 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 | 139 ++++++++++++++++++++++++++++++++++++-
 1 file changed, 137 insertions(+), 2 deletions(-)
drivers/dma/dw-edma/dw-edma-pcie.c +75 -0
index 3371e0a7..3d7247c 100644
@@ -14,15 +14,31 @@
 #include <linux/pci-epf.h>
 #include <linux/msi.h>
 #include <linux/bitfield.h>
+#include <linux/sizes.h>
 
 #include "dw-edma-core.h"
 
+/* Synopsys */
 #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)
 
+/* AMD MDB (Xilinx) specific defines */
+#define DW_PCIE_XILINX_MDB_VSEC_DMA_ID		0x6
+#define DW_PCIE_XILINX_MDB_VSEC_ID		0x20
+#define PCI_DEVICE_ID_AMD_MDB_B054		0xb054
+#define DW_PCIE_AMD_MDB_INVALID_ADDR		(~0ULL)
+#define DW_PCIE_XILINX_LL_OFF_GAP		0x200000
+#define DW_PCIE_XILINX_LL_SIZE			0x800
+#define DW_PCIE_XILINX_DT_OFF_GAP		0x100000
+#define DW_PCIE_XILINX_DT_SIZE			0x800
+#define DW_PCIE_XILINX_MDB_VSEC_HDR_ID		0x20
+#define DW_PCIE_XILINX_MDB_VSEC_REV		0x1
+#define DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_HIGH	0xc
+#define DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_LOW	0x8
+
 #define DW_BLOCK(a, b, c) \
 	{ \
 		.bar = a, \
@@ -50,6 +66,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 +107,64 @@ struct dw_edma_pcie_data {
 	.rd_ch_cnt			= 2,
 };
 
+static const struct dw_edma_pcie_data amd_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);

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

Quoted 7 lines
drivers/dma/dw-edma/dw-edma-pcie.c +4 -0
@@ -120,9 +195,24 @@ static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
 	u32 val, map;
 	u16 vsec;
 	u64 off;
+	int cap;
+
+	/*
+	 * Synopsys and AMD (Xilinx) use the same VSEC ID for the purpose

Same or different?

Quoted 57 lines
drivers/dma/dw-edma/dw-edma-pcie.c +36 -2
+	 * of map, channel counts, etc.
+	 */
+	switch (pdev->vendor) {
+	case PCI_VENDOR_ID_SYNOPSYS:
+		cap = DW_PCIE_VSEC_DMA_ID;
+		break;
+	case PCI_VENDOR_ID_XILINX:
+		cap = DW_PCIE_XILINX_MDB_VSEC_DMA_ID;
+		break;
+	default:
+		return;
+	}
 
-	vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_SYNOPSYS,
-					DW_PCIE_VSEC_DMA_ID);
+	vsec = pci_find_vsec_capability(pdev, pdev->vendor, cap);
 	if (!vsec)
 		return;
 
@@ -155,6 +245,28 @@ static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
 	off <<= 32;
 	off |= val;
 	pdata->rg.off = off;
+
+	/* Xilinx specific VSEC capability */
+	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 + PCI_VNDR_HEADER, &val);
+	if (PCI_VNDR_HEADER_ID(val) != DW_PCIE_XILINX_MDB_VSEC_HDR_ID ||
+	    PCI_VNDR_HEADER_REV(val) != DW_PCIE_XILINX_MDB_VSEC_REV)
+		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,
@@ -179,6 +291,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
 	}
 
 	memcpy(vsec_data, pdata, sizeof(struct dw_edma_pcie_data));
+	vsec_data->devmem_phys_off = DW_PCIE_AMD_MDB_INVALID_ADDR;
 
 	/*
 	 * Tries to find if exists a PCIe Vendor-Specific Extended Capability

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

Quoted 10 lines
drivers/dma/dw-edma/dw-edma-pcie.c +7 -0
@@ -186,6 +299,26 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
 	 */
 	dw_edma_pcie_get_vsec_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_AMD_MDB_INVALID_ADDR)
+			return -ENOMEM;

Move this check to dw_edma_pcie_get_vsec_dma_data() and return -ENOMEM directly.

- Mani

Signature · 1 line
மணிவண்ணன் சதாசிவம்
#4 lore raw

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

[AMD Official Use Only - AMD Internal Distribution Only]

Hi Manivannan

Please check my response inline.

Regards,
Devendra

Quoted 179 lines

-----Original Message-----
From: Manivannan Sadhasivam <[email protected]>
Sent: Monday, December 8, 2025 10:55 AM
To: Verma, Devendra <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; Simek, Michal <[email protected]>
Subject: Re: [PATCH RESEND v6 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 103 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 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 | 139
++++++++++++++++++++++++++++++++++++-
 1 file changed, 137 insertions(+), 2 deletions(-)
drivers/dma/dw-edma/dw-edma-pcie.c +22 -0
index 3371e0a7..3d7247c 100644
@@ -14,15 +14,31 @@
 #include <linux/pci-epf.h>
 #include <linux/msi.h>
 #include <linux/bitfield.h>
+#include <linux/sizes.h>
 
 #include "dw-edma-core.h"
 
+/* Synopsys */
 #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)
 
+/* AMD MDB (Xilinx) specific defines */
+#define DW_PCIE_XILINX_MDB_VSEC_DMA_ID               0x6
+#define DW_PCIE_XILINX_MDB_VSEC_ID           0x20
+#define PCI_DEVICE_ID_AMD_MDB_B054           0xb054
+#define DW_PCIE_AMD_MDB_INVALID_ADDR         (~0ULL)
+#define DW_PCIE_XILINX_LL_OFF_GAP            0x200000
+#define DW_PCIE_XILINX_LL_SIZE                       0x800
+#define DW_PCIE_XILINX_DT_OFF_GAP            0x100000
+#define DW_PCIE_XILINX_DT_SIZE                       0x800
+#define DW_PCIE_XILINX_MDB_VSEC_HDR_ID               0x20
+#define DW_PCIE_XILINX_MDB_VSEC_REV          0x1
+#define DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_HIGH       0xc
+#define DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_LOW        0x8
+
 #define DW_BLOCK(a, b, c) \
      { \
              .bar = a, \
@@ -50,6 +66,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
+107,64 @@ struct dw_edma_pcie_data {
      .rd_ch_cnt                      = 2,
 };
 
+static const struct dw_edma_pcie_data amd_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 50 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); @@ -120,9 +195,24 @@
static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
      u32 val, map;
      u16 vsec;
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 6 lines
      u64 off;
drivers/dma/dw-edma/dw-edma-pcie.c +5 -0
+     int cap;
+
+     /*
+      * Synopsys and AMD (Xilinx) use the same VSEC ID for the
+ purpose

Same or different?

It is the same capability for which Synosys and AMD (Xilinx) share the common value.

Quoted 70 lines

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

Quoted 21 lines
drivers/dma/dw-edma/dw-edma-pcie.c +13 -2
+      * of map, channel counts, etc.
+      */
+     switch (pdev->vendor) {
+     case PCI_VENDOR_ID_SYNOPSYS:
+             cap = DW_PCIE_VSEC_DMA_ID;
+             break;
+     case PCI_VENDOR_ID_XILINX:
+             cap = DW_PCIE_XILINX_MDB_VSEC_DMA_ID;
+             break;
+     default:
+             return;
+     }
 
-     vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_SYNOPSYS,
-                                     DW_PCIE_VSEC_DMA_ID);
+     vsec = pci_find_vsec_capability(pdev, pdev->vendor, cap);
      if (!vsec)
              return;
@@ -155,6 +245,28 @@ 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 12 lines
drivers/dma/dw-edma/dw-edma-pcie.c +9 -0
      off <<= 32;
      off |= val;
      pdata->rg.off = off;
+
+     /* Xilinx specific VSEC capability */
+     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 + PCI_VNDR_HEADER, &val);
+     if (PCI_VNDR_HEADER_ID(val) != DW_PCIE_XILINX_MDB_VSEC_HDR_ID
||
> +         PCI_VNDR_HEADER_REV(val) != DW_PCIE_XILINX_MDB_VSEC_REV)
> +             return;
Quoted 15 lines
drivers/dma/dw-edma/dw-edma-pcie.c +11 -0
+
+     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, @@ -179,6 +291,7

@@ static int dw_edma_pcie_probe(struct pci_dev *pdev,

Quoted 8 lines
      }

      memcpy(vsec_data, pdata, sizeof(struct dw_edma_pcie_data));

+ vsec_data->devmem_phys_off = DW_PCIE_AMD_MDB_INVALID_ADDR;

/*
* Tries to find if exists a PCIe Vendor-Specific Extended
Capability @@ -186,6 +299,26 @@ 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
       */
      dw_edma_pcie_get_vsec_dma_data(pdev, vsec_data);
drivers/dma/dw-edma/dw-edma-pcie.c +6 -0
+     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 ==
Quoted 5 lines

DW_PCIE_AMD_MDB_INVALID_ADDR)

Quoted 1 line

+ return -ENOMEM;

Move this check to dw_edma_pcie_get_vsec_dma_data() and return - ENOMEM directly.

In the subsequent patch of the same series (2/2), based on this check, instead of returning, a flag is set based on the retrieval of the address offset, which decides the actual flow for non-LL mode.
For the similar functionality as above, dw_edma_pcie_get_vsec_dma_data() may require multiple changes alongside a comparison at the current location, be it error check or address value check to provide the almost similar functionality for non-LL mode.

Please let me know your thoughts on the above two approaches.

Quoted 5 lines

- Mani

--
மணிவண்ணன் சதாசிவம்

#5 lore raw

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

Quoted 197 lines · Devendra

[AMD Official Use Only - AMD Internal Distribution Only]

Hi Manivannan

Please check my response inline.

Regards,
Devendra

Quoted 179 lines

-----Original Message-----
From: Manivannan Sadhasivam <[email protected]>
Sent: Monday, December 8, 2025 10:55 AM
To: Verma, Devendra <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; Simek, Michal <[email protected]>
Subject: Re: [PATCH RESEND v6 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 103 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 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 | 139
++++++++++++++++++++++++++++++++++++-
 1 file changed, 137 insertions(+), 2 deletions(-)
drivers/dma/dw-edma/dw-edma-pcie.c +22 -0
index 3371e0a7..3d7247c 100644
@@ -14,15 +14,31 @@
 #include <linux/pci-epf.h>
 #include <linux/msi.h>
 #include <linux/bitfield.h>
+#include <linux/sizes.h>
 
 #include "dw-edma-core.h"
 
+/* Synopsys */
 #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)
 
+/* AMD MDB (Xilinx) specific defines */
+#define DW_PCIE_XILINX_MDB_VSEC_DMA_ID               0x6
+#define DW_PCIE_XILINX_MDB_VSEC_ID           0x20
+#define PCI_DEVICE_ID_AMD_MDB_B054           0xb054
+#define DW_PCIE_AMD_MDB_INVALID_ADDR         (~0ULL)
+#define DW_PCIE_XILINX_LL_OFF_GAP            0x200000
+#define DW_PCIE_XILINX_LL_SIZE                       0x800
+#define DW_PCIE_XILINX_DT_OFF_GAP            0x100000
+#define DW_PCIE_XILINX_DT_SIZE                       0x800
+#define DW_PCIE_XILINX_MDB_VSEC_HDR_ID               0x20
+#define DW_PCIE_XILINX_MDB_VSEC_REV          0x1
+#define DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_HIGH       0xc
+#define DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG_LOW        0x8
+
 #define DW_BLOCK(a, b, c) \
      { \
              .bar = a, \
@@ -50,6 +66,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
+107,64 @@ struct dw_edma_pcie_data {
      .rd_ch_cnt                      = 2,
 };
 
+static const struct dw_edma_pcie_data amd_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 50 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); @@ -120,9 +195,24 @@
static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
      u32 val, map;
      u16 vsec;
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 6 lines
      u64 off;
drivers/dma/dw-edma/dw-edma-pcie.c +5 -0
+     int cap;
+
+     /*
+      * Synopsys and AMD (Xilinx) use the same VSEC ID for the
+ purpose

Same or different?

Quoted 2 lines

It is the same capability for which Synosys and AMD (Xilinx) share the common value.

This is confusing. You are searching for either DW_PCIE_VSEC_DMA_ID or DW_PCIE_XILINX_MDB_VSEC_DMA_ID below, which means that the VSEC IDs are different.

Quoted 22 lines
Quoted 22 lines

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

Quoted 21 lines
drivers/dma/dw-edma/dw-edma-pcie.c +13 -2
+      * of map, channel counts, etc.
+      */
+     switch (pdev->vendor) {
+     case PCI_VENDOR_ID_SYNOPSYS:
+             cap = DW_PCIE_VSEC_DMA_ID;
+             break;
+     case PCI_VENDOR_ID_XILINX:
+             cap = DW_PCIE_XILINX_MDB_VSEC_DMA_ID;
+             break;
+     default:
+             return;
+     }
 
-     vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_SYNOPSYS,
-                                     DW_PCIE_VSEC_DMA_ID);
+     vsec = pci_find_vsec_capability(pdev, pdev->vendor, cap);
      if (!vsec)
              return;
@@ -155,6 +245,28 @@ static void

dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

Quoted 12 lines
Quoted 4 lines
Quoted 4 lines
drivers/dma/dw-edma/dw-edma-pcie.c +1 -0
      off <<= 32;
      off |= val;
      pdata->rg.off = off;
+
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
+     /* Xilinx specific VSEC capability */
+     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 + PCI_VNDR_HEADER, &val);
+     if (PCI_VNDR_HEADER_ID(val) != DW_PCIE_XILINX_MDB_VSEC_HDR_ID

Also, this check is pointless as it essentially duplicates the check inside pci_find_vsec_capability(). If pci_find_vsec_capability() returns a vsec cap offset, it means, it has *found* the supplied VSEC ID. And there is no separate DW_PCIE_XILINX_MDB_VSEC_HDR_ID. It is exactly the same as DW_PCIE_XILINX_MDB_VSEC_ID.

Quoted 42 lines
Quoted 36 lines
||
> +         PCI_VNDR_HEADER_REV(val) != DW_PCIE_XILINX_MDB_VSEC_REV)
> +             return;
Quoted 15 lines
drivers/dma/dw-edma/dw-edma-pcie.c +11 -0
+
+     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, @@ -179,6 +291,7

@@ static int dw_edma_pcie_probe(struct pci_dev *pdev,

Quoted 8 lines
      }

      memcpy(vsec_data, pdata, sizeof(struct dw_edma_pcie_data));

+ vsec_data->devmem_phys_off = DW_PCIE_AMD_MDB_INVALID_ADDR;

/*
* Tries to find if exists a PCIe Vendor-Specific Extended
Capability @@ -186,6 +299,26 @@ 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
       */
      dw_edma_pcie_get_vsec_dma_data(pdev, vsec_data);
drivers/dma/dw-edma/dw-edma-pcie.c +6 -0
+     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 ==
Quoted 5 lines

DW_PCIE_AMD_MDB_INVALID_ADDR)

Quoted 1 line

+ return -ENOMEM;

Move this check to dw_edma_pcie_get_vsec_dma_data() and return - ENOMEM directly.

Quoted 8 lines

In the subsequent patch of the same series (2/2), based on this check, instead of returning, a flag is set based on the retrieval of the address offset, which decides the actual flow for non-LL mode.
For the similar functionality as above, dw_edma_pcie_get_vsec_dma_data() may require multiple changes alongside a comparison at the current location, be it error check or address value check to provide the almost similar functionality for non-LL mode.

Fair enough.

- Mani

Signature · 1 line
மணிவண்ணன் சதாசிவம்
#6 lore raw

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

Quoted 22 lines · Manivannan Sadhasivam
Quoted 21 lines · Devendra
Quoted 21 lines

-----Original Message-----
From: Manivannan Sadhasivam <[email protected]>

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

drivers/dma/dw-edma/dw-edma-pcie.c +3 -0
+/* Synopsys */
 #define DW_PCIE_VSEC_DMA_ID                  0x6
 
+/* AMD MDB (Xilinx) specific defines */
+#define DW_PCIE_XILINX_MDB_VSEC_DMA_ID               0x6

static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

+ * Synopsys and AMD (Xilinx) use the same VSEC ID for the
+ purpose

Quoted 8 lines
Quoted 4 lines
Quoted 1 line

Same or different?

It is the same capability for which Synosys and AMD (Xilinx) share the common value.

This is confusing. You are searching for either DW_PCIE_VSEC_DMA_ID or DW_PCIE_XILINX_MDB_VSEC_DMA_ID below, which means that the VSEC IDs are different.

This is perennially confusing.

Since this is a "Vendor-Specific" (not a "Designated Vendor-Specific") capability, we must search for the per-vendor VSEC ID, i.e., DW_PCIE_VSEC_DMA_ID for PCI_VENDOR_ID_SYNOPSYS devices or DW_PCIE_XILINX_MDB_VSEC_DMA_ID for PCI_VENDOR_ID_XILINX devices.

The fact that DW_PCIE_VSEC_DMA_ID == DW_PCIE_XILINX_MDB_VSEC_DMA_ID is a coincidence and is not relevant to the code. The comment that "Synopsys and AMD (Xilinx) use the same VSEC ID for the purpose" should be removed because it adds confusion and the code doesn't rely on that.

However, the subsequent code DOES rely on the fact that the Synopsys and the Xilinx VSECs have the same *registers* at the same offsets:

        pci_read_config_dword(pdev, vsec + 0x8, &val);
        map = FIELD_GET(DW_PCIE_VSEC_DMA_MAP, val);
        pdata->rg.bar = FIELD_GET(DW_PCIE_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));
        pdata->rd_ch_cnt = min_t(u16, pdata->rd_ch_cnt,
                                 FIELD_GET(DW_PCIE_VSEC_DMA_RD_CH, val));

        pci_read_config_dword(pdev, vsec + 0x14, &val);
        off = val;

        pci_read_config_dword(pdev, vsec + 0x10, &val);

The VSEC ID *values* are not relevant, but the fact that the registers in the Synopsys and the Xilinx capabilities are identical *is* important and not obvious, so if you share the code that reads those registers, there should be a comment about that.

The normal way to use VSEC is to look for a capability for a single vendor and interpret it using code for that specific vendor. I think I would split this into separate dw_edma_pcie_get_synopsys_dma_data() and dw_edma_pcie_get_xilinx_dma_data() functions to make it obvious that these are indeed controlled by different vendors, e.g.,

dw_edma_pcie_get_synopsys_dma_data()

  {
    vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_SYNOPSYS,
                                    DW_PCIE_VSEC_DMA_ID);
    if (!vsec)
      return;

    pci_read_config_dword(pdev, vsec + 0x8, &val);
    ...

}

dw_edma_pcie_get_xilinx_dma_data()

  {
    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 + 0x8, &val);
    ...

    vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_XILINX,
                                    DW_PCIE_XILINX_MDB_VSEC_ID);
    if (!vsec)
      return;
    ...

}

It's safe to call both of them for all devices like this:

  dw_edma_pcie_probe
  {
    ...
    dw_edma_pcie_get_synopsys_dma_data(pdev, vsec_data);
    dw_edma_pcie_get_xilinx_dma_data(pdev, vsec_data);
    ...
  }

Most of the code in dw_edma_pcie_get_synopsys_dma_data() would be duplicated, but that's OK and acknowledges the fact that Synopsys and Xilinx can evolve that VSEC independently.

Bjorn

#7 lore raw

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

[AMD Official Use Only - AMD Internal Distribution Only]

Quoted 129 lines

-----Original Message-----
From: Bjorn Helgaas <[email protected]>
Sent: Wednesday, December 10, 2025 10:02 PM
To: Manivannan Sadhasivam <[email protected]>
Cc: Verma, Devendra <[email protected]>; [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; Simek, Michal <[email protected]>
Subject: Re: [PATCH RESEND v6 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 3 lines · Manivannan Sadhasivam
Quoted 2 lines · Devendra
Quoted 2 lines

-----Original Message-----
From: Manivannan Sadhasivam <[email protected]> On Fri, Nov 21,

2025

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

drivers/dma/dw-edma/dw-edma-pcie.c +3 -0
+/* Synopsys */
 #define DW_PCIE_VSEC_DMA_ID                  0x6
 
+/* AMD MDB (Xilinx) specific defines */
+#define DW_PCIE_XILINX_MDB_VSEC_DMA_ID               0x6

static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

+ * Synopsys and AMD (Xilinx) use the same VSEC ID for the
+ purpose

Quoted 7 lines
Quoted 4 lines
Quoted 1 line

Same or different?

It is the same capability for which Synosys and AMD (Xilinx) share the common value.

This is confusing. You are searching for either DW_PCIE_VSEC_DMA_ID or DW_PCIE_XILINX_MDB_VSEC_DMA_ID below, which means that the VSEC

IDs

Quoted 1 line

are different.

This is perennially confusing.

Since this is a "Vendor-Specific" (not a "Designated Vendor-Specific") capability, we must search for the per-vendor VSEC ID, i.e., DW_PCIE_VSEC_DMA_ID for PCI_VENDOR_ID_SYNOPSYS devices or DW_PCIE_XILINX_MDB_VSEC_DMA_ID for PCI_VENDOR_ID_XILINX devices.

The fact that DW_PCIE_VSEC_DMA_ID == DW_PCIE_XILINX_MDB_VSEC_DMA_ID is a coincidence and is not relevant to the code. The comment that "Synopsys and AMD (Xilinx) use the same VSEC ID for the purpose"
should be removed because it adds confusion and the code doesn't rely on that.

However, the subsequent code DOES rely on the fact that the Synopsys and the Xilinx VSECs have the same *registers* at the same offsets:

        pci_read_config_dword(pdev, vsec + 0x8, &val);
        map = FIELD_GET(DW_PCIE_VSEC_DMA_MAP, val);
        pdata->rg.bar = FIELD_GET(DW_PCIE_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));
        pdata->rd_ch_cnt = min_t(u16, pdata->rd_ch_cnt,
                                 FIELD_GET(DW_PCIE_VSEC_DMA_RD_CH, val));

        pci_read_config_dword(pdev, vsec + 0x14, &val);
        off = val;

        pci_read_config_dword(pdev, vsec + 0x10, &val);

The VSEC ID *values* are not relevant, but the fact that the registers in the Synopsys and the Xilinx capabilities are identical *is* important and not obvious, so if you share the code that reads those registers, there should be a comment about that.

The normal way to use VSEC is to look for a capability for a single vendor and interpret it using code for that specific vendor. I think I would split this into separate dw_edma_pcie_get_synopsys_dma_data()
and dw_edma_pcie_get_xilinx_dma_data() functions to make it obvious that these are indeed controlled by different vendors, e.g.,

dw_edma_pcie_get_synopsys_dma_data()

  {
    vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_SYNOPSYS,
                                    DW_PCIE_VSEC_DMA_ID);
    if (!vsec)
      return;

    pci_read_config_dword(pdev, vsec + 0x8, &val);
    ...

}

dw_edma_pcie_get_xilinx_dma_data()

  {
    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 + 0x8, &val);
    ...

    vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_XILINX,
                                    DW_PCIE_XILINX_MDB_VSEC_ID);
    if (!vsec)
      return;
    ...

}

It's safe to call both of them for all devices like this:

  dw_edma_pcie_probe
  {
    ...
    dw_edma_pcie_get_synopsys_dma_data(pdev, vsec_data);
    dw_edma_pcie_get_xilinx_dma_data(pdev, vsec_data);
    ...
  }
Quoted 5 lines

Most of the code in dw_edma_pcie_get_synopsys_dma_data() would be duplicated, but that's OK and acknowledges the fact that Synopsys and Xilinx can evolve that VSEC independently.

Bjorn

I guess, with some code duplication and separation of functionality for different vendors shall be OK to reduce the confusion otherwise; we are going in circles with this particular piece of code.
Thanks for the suggestion, in the next review will update the code.

Regards,
Devendra

#8 lore raw

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

In reply to Bjorn Helgaas
Quoted 112 lines · Bjorn Helgaas
Quoted 22 lines · Manivannan Sadhasivam
Quoted 21 lines · Devendra
Quoted 21 lines

-----Original Message-----
From: Manivannan Sadhasivam <[email protected]>

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

drivers/dma/dw-edma/dw-edma-pcie.c +3 -0
+/* Synopsys */
 #define DW_PCIE_VSEC_DMA_ID                  0x6
 
+/* AMD MDB (Xilinx) specific defines */
+#define DW_PCIE_XILINX_MDB_VSEC_DMA_ID               0x6

static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,

+ * Synopsys and AMD (Xilinx) use the same VSEC ID for the
+ purpose

Quoted 8 lines
Quoted 4 lines
Quoted 1 line

Same or different?

It is the same capability for which Synosys and AMD (Xilinx) share the common value.

This is confusing. You are searching for either DW_PCIE_VSEC_DMA_ID or DW_PCIE_XILINX_MDB_VSEC_DMA_ID below, which means that the VSEC IDs are different.

This is perennially confusing.

Since this is a "Vendor-Specific" (not a "Designated Vendor-Specific") capability, we must search for the per-vendor VSEC ID, i.e., DW_PCIE_VSEC_DMA_ID for PCI_VENDOR_ID_SYNOPSYS devices or DW_PCIE_XILINX_MDB_VSEC_DMA_ID for PCI_VENDOR_ID_XILINX devices.

The fact that DW_PCIE_VSEC_DMA_ID == DW_PCIE_XILINX_MDB_VSEC_DMA_ID is a coincidence and is not relevant to the code. The comment that "Synopsys and AMD (Xilinx) use the same VSEC ID for the purpose" should be removed because it adds confusion and the code doesn't rely on that.

However, the subsequent code DOES rely on the fact that the Synopsys and the Xilinx VSECs have the same *registers* at the same offsets:

        pci_read_config_dword(pdev, vsec + 0x8, &val);
        map = FIELD_GET(DW_PCIE_VSEC_DMA_MAP, val);
        pdata->rg.bar = FIELD_GET(DW_PCIE_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));
        pdata->rd_ch_cnt = min_t(u16, pdata->rd_ch_cnt,
                                 FIELD_GET(DW_PCIE_VSEC_DMA_RD_CH, val));

        pci_read_config_dword(pdev, vsec + 0x14, &val);
        off = val;

        pci_read_config_dword(pdev, vsec + 0x10, &val);

The VSEC ID *values* are not relevant, but the fact that the registers in the Synopsys and the Xilinx capabilities are identical *is* important and not obvious, so if you share the code that reads those registers, there should be a comment about that.

The normal way to use VSEC is to look for a capability for a single vendor and interpret it using code for that specific vendor. I think I would split this into separate dw_edma_pcie_get_synopsys_dma_data() and dw_edma_pcie_get_xilinx_dma_data() functions to make it obvious that these are indeed controlled by different vendors, e.g.,

dw_edma_pcie_get_synopsys_dma_data()

  {
    vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_SYNOPSYS,
                                    DW_PCIE_VSEC_DMA_ID);
    if (!vsec)
      return;

    pci_read_config_dword(pdev, vsec + 0x8, &val);
    ...

}

dw_edma_pcie_get_xilinx_dma_data()

  {
    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 + 0x8, &val);
    ...

    vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_XILINX,
                                    DW_PCIE_XILINX_MDB_VSEC_ID);
    if (!vsec)
      return;
    ...

}

It's safe to call both of them for all devices like this:

  dw_edma_pcie_probe
  {
    ...
    dw_edma_pcie_get_synopsys_dma_data(pdev, vsec_data);
    dw_edma_pcie_get_xilinx_dma_data(pdev, vsec_data);
    ...
  }
Quoted 4 lines

Most of the code in dw_edma_pcie_get_synopsys_dma_data() would be duplicated, but that's OK and acknowledges the fact that Synopsys and Xilinx can evolve that VSEC independently.

Yes, it will make it clear and obvious!

- Mani

Signature · 1 line
மணிவண்ணன் சதாசிவம்
#9 lore raw

PATCH RESEND v6 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:
- When a valid physical base address is not configured via the Xilinx VSEC capability then the IP can still be used in non-LL mode. The default mode for all the DMA transactions and for all the DMA channels then is non-LL mode.
- When a valid physical base address is configured but the client wants to use the non-LL mode for DMA transactions then also the flexibility is provided via the peripheral_config struct member of dma_slave_config. In this case the channels can be individually configured in non-LL mode. This use case is desirable for single DMA transfer of a chunk, this saves the effort of preparing the Link List. This particular scenario is applicable to AMD as well as Synopsys IP.

---
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    | 41 ++++++++++++++++++++---
 drivers/dma/dw-edma/dw-edma-core.h    |  1 +
 drivers/dma/dw-edma/dw-edma-pcie.c    | 44 +++++++++++++++++--------
 drivers/dma/dw-edma/dw-hdma-v0-core.c | 61 ++++++++++++++++++++++++++++++++++-
 drivers/dma/dw-edma/dw-hdma-v0-regs.h |  1 +
 include/linux/dma/edma.h              |  1 +
 6 files changed, 130 insertions(+), 19 deletions(-)
drivers/dma/dw-edma/dw-edma-core.c +36 -5
index b43255f..60a3279 100644
@@ -223,8 +223,31 @@ 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 +376,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 +442,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 +475,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 +31 -13

110 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
#10 lore raw

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

[AMD Official Use Only - AMD Internal Distribution Only]

Hi All

Could you all please review the following patch?

Regards,
Dev

Quoted 400 lines

-----Original Message-----
From: Devendra K Verma <[email protected]>
Sent: Friday, November 21, 2025 5:05 PM
To: [email protected]; [email protected]; [email protected]
Cc: [email protected]; [email protected]; linux- [email protected]; Simek, Michal <[email protected]>; Verma, Devendra <[email protected]>
Subject: [PATCH RESEND v6 2/2] dmaengine: dw-edma: Add non-LL mode

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:
- When a valid physical base address is not configured via the Xilinx VSEC capability then the IP can still be used in non-LL mode. The default mode for all the DMA transactions and for all the DMA channels then is non-LL mode.
- When a valid physical base address is configured but the client wants to use the non-LL mode for DMA transactions then also the flexibility is provided via the peripheral_config struct member of dma_slave_config. In this case the channels can be individually configured in non-LL mode. This use case is desirable for single DMA transfer of a chunk, this saves the effort of preparing the Link List. This particular scenario is applicable to AMD as well as Synopsys IP.

---
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    | 41 ++++++++++++++++++++---
 drivers/dma/dw-edma/dw-edma-core.h    |  1 +
 drivers/dma/dw-edma/dw-edma-pcie.c    | 44 +++++++++++++++++--------
 drivers/dma/dw-edma/dw-hdma-v0-core.c | 61
++++++++++++++++++++++++++++++++++-
 drivers/dma/dw-edma/dw-hdma-v0-regs.h |  1 +
 include/linux/dma/edma.h              |  1 +
 6 files changed, 130 insertions(+), 19 deletions(-)
drivers/dma/dw- renamed from drivers/dma/dw-edma/dw-edma-core.c +0 -0

edma/dw-edma-core.c
index b43255f..60a3279 100644

drivers/dma/dw-edma/dw-edma-core.c +23 -0
@@ -223,8 +223,31 @@ 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;
drivers/dma/dw-edma/dw-edma-core.c +1 -1
@@ -353,7 +376,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;
drivers/dma/dw-edma/dw-edma-core.c +5 -3
@@ -419,9 +442,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;
drivers/dma/dw-edma/dw-edma-core.c +7 -1
@@ -450,7 +475,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- renamed from drivers/dma/dw-edma/dw-edma-core.h +0 -0

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- renamed from drivers/dma/dw-edma/dw-edma-pcie.c +0 -0

edma/dw-edma-pcie.c
index 3d7247c..e7e95df 100644

drivers/dma/dw-edma/dw-edma-pcie.c +13 -1
@@ -269,6 +269,15 @@ static void
 dw_edma_pcie_get_vsec_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)  { @@ -278,6
+287,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)
@@ -302,21 +312,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_AMD_MDB_INVALID_ADDR)

drivers/dma/dw-edma/dw-edma-pcie.c +3 -6
-                     return -ENOMEM;
+                     non_ll = true;
 
              /*
               * Configure the channel LL and data blocks if number of
               * channels enabled in VSEC capability are more than the
               * channels configured in amd_mdb_data.
               */
-             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);
+             if (!non_ll)
+                     dw_edma_set_chan_region_offset(vsec_data, BAR_2,

0,
+
DW_PCIE_XILINX_LL_OFF_GAP,

drivers/dma/dw-edma/dw-edma-pcie.c +2 -0
+                                                    DW_PCIE_XILINX_LL_SIZE,
+

DW_PCIE_XILINX_DT_OFF_GAP,
+

DW_PCIE_XILINX_DT_SIZE);
      }

      /* Mapping PCI BAR regions */
drivers/dma/dw-edma/dw-edma-pcie.c +1 -0
@@ -364,6 +377,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; @@ -372,7 +386,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 +2 -3
-     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]; @@
-383,7 +397,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,

drivers/dma/dw-edma/dw-edma-pcie.c +4 -2
+                                                      ll_block->bar);
              ll_region->paddr += ll_block->off;
              ll_region->sz = ll_block->sz;
@@ -392,12 +407,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]; @@ -
408,7 +424,8 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
                      return -ENOMEM;

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

vsec_data,

drivers/dma/dw-edma/dw-edma-pcie.c +3 -1
+                                                      ll_block->bar);
              ll_region->paddr += ll_block->off;
              ll_region->sz = ll_block->sz;
@@ -417,7 +434,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- renamed from drivers/dma/dw-edma/dw-hdma-v0-core.c +0 -0

edma/dw-hdma-v0-core.c
index e3f8db4..ee31c9a 100644

drivers/dma/dw-edma/dw-hdma-v0-core.c +60 -1
@@ -225,7 +225,7 @@ static void dw_hdma_v0_sync_ll_data(struct
 dw_edma_chunk *chunk)
              readl(chunk->ll_region.vaddr.io);
 }
 
-static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool
 first)
+static void dw_hdma_v0_core_ll_start(struct dw_edma_chunk *chunk, bool
+first)
 {
      struct dw_edma_chan *chan = chunk->chan;
      struct dw_edma *dw = chan->dw;
@@ -263,6 +263,65 @@ static void dw_hdma_v0_core_start(struct
 dw_edma_chunk *chunk, bool first)
      SET_CH_32(dw, chan->dir, chan->id, doorbell,
 HDMA_V0_DOORBELL_START);  }
 
+static void dw_hdma_v0_core_non_ll_start(struct dw_edma_chunk *chunk)
 {
+     struct dw_edma_chan *chan = chunk->chan;
+     struct dw_edma *dw = chan->dw;
+     struct dw_edma_burst *child;
+     u32 val;
+
+     list_for_each_entry(child, &chunk->burst->list, list) {
+             SET_CH_32(dw, chan->dir, chan->id, ch_en,
 HDMA_V0_CH_EN);
+
+             /* Source address */
+             SET_CH_32(dw, chan->dir, chan->id, sar.lsb,
+                       lower_32_bits(child->sar));
+             SET_CH_32(dw, chan->dir, chan->id, sar.msb,
+                       upper_32_bits(child->sar));
+
+             /* Destination address */
+             SET_CH_32(dw, chan->dir, chan->id, dar.lsb,
+                       lower_32_bits(child->dar));
+             SET_CH_32(dw, chan->dir, chan->id, dar.msb,
+                       upper_32_bits(child->dar));
+
+             /* Transfer size */
+             SET_CH_32(dw, chan->dir, chan->id, transfer_size, child->sz);
+
+             /* Interrupt setup */
+             val = GET_CH_32(dw, chan->dir, chan->id, int_setup) |
+                             HDMA_V0_STOP_INT_MASK |
+                             HDMA_V0_ABORT_INT_MASK |
+                             HDMA_V0_LOCAL_STOP_INT_EN |
+                             HDMA_V0_LOCAL_ABORT_INT_EN;
+
+             if (!(dw->chip->flags & DW_EDMA_CHIP_LOCAL)) {
+                     val |= HDMA_V0_REMOTE_STOP_INT_EN |
+                            HDMA_V0_REMOTE_ABORT_INT_EN;
+             }
+
+             SET_CH_32(dw, chan->dir, chan->id, int_setup, val);
+
+             /* Channel control setup */
+             val = GET_CH_32(dw, chan->dir, chan->id, control1);
+             val &= ~HDMA_V0_LINKLIST_EN;
+             SET_CH_32(dw, chan->dir, chan->id, control1, val);
+
+             SET_CH_32(dw, chan->dir, chan->id, doorbell,
+                       HDMA_V0_DOORBELL_START);
+     }
+}
+
+static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool
+first) {
+     struct dw_edma_chan *chan = chunk->chan;
+
+     if (!chan->non_ll)
+             dw_hdma_v0_core_ll_start(chunk, first);
+     else
+             dw_hdma_v0_core_non_ll_start(chunk);
+}
+
 static void dw_hdma_v0_core_ch_config(struct dw_edma_chan *chan)  {
      struct dw_edma *dw = chan->dw;
drivers/dma/dw- renamed from drivers/dma/dw-edma/dw-hdma-v0-regs.h +0 -0

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 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 RESEND v6 2/2] dmaengine: dw-edma: Add non-LL mode

Quoted 5 lines · Devendra

[AMD Official Use Only - AMD Internal Distribution Only]

Hi All

Could you all please review the following patch?

No need to remind people, your patch will be reviewed.

Quoted 14 lines

Regards,
Dev

Quoted 10 lines

-----Original Message-----
From: Devendra K Verma <[email protected]>
Sent: Friday, November 21, 2025 5:05 PM
To: [email protected]; [email protected]; [email protected]
Cc: [email protected]; [email protected]; linux- [email protected]; Simek, Michal <[email protected]>; Verma, Devendra <[email protected]>
Subject: [PATCH RESEND v6 2/2] dmaengine: dw-edma: Add non-LL mode

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

Is this non-LL mode some official name ? (e.g. in the datasheet or official product name )
Because having a 'bool non-LL' as false making it in a LL mode adds a double negation that is difficult to follow.

Quoted 17 lines
Quoted 17 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:
- When a valid physical base address is not configured via the Xilinx VSEC capability then the IP can still be used in non-LL mode. The default mode for all the DMA transactions and for all the DMA channels then is non-LL mode.
- When a valid physical base address is configured but the client wants to use the non-LL mode for DMA transactions then also the flexibility is provided via the peripheral_config struct member of dma_slave_config. In this case the channels can be individually configured in non-LL mode. This use case is desirable for single DMA transfer of a chunk, this saves the effort of preparing the Link List. This particular scenario is applicable to AMD as well as Synopsys IP.

[...]

#12 lore raw

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

[AMD Official Use Only - AMD Internal Distribution Only]

Hi Eugen
Please check my response inline.

Regards,
Devendra

Quoted 35 lines

-----Original Message-----
From: Eugen Hristev <[email protected]>
Sent: Wednesday, December 3, 2025 2:54 PM
To: Verma, Devendra <[email protected]>; [email protected]; [email protected]; [email protected]
Cc: [email protected]; [email protected]; linux- [email protected]; Simek, Michal <[email protected]>
Subject: Re: [PATCH RESEND v6 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 5 lines · Devendra

[AMD Official Use Only - AMD Internal Distribution Only]

Hi All

Could you all please review the following patch?

No need to remind people, your patch will be reviewed.

Quoted 13 lines

Regards,
Dev

Quoted 9 lines

-----Original Message-----
From: Devendra K Verma <[email protected]>
Sent: Friday, November 21, 2025 5:05 PM
To: [email protected]; [email protected]; [email protected]
Cc: [email protected]; [email protected]; linux- [email protected]; Simek, Michal <[email protected]>; Verma, Devendra <[email protected]>
Subject: [PATCH RESEND v6 2/2] dmaengine: dw-edma: Add non-LL mode

Quoted 5 lines
Quoted 1 line
Quoted 1 line

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

Is this non-LL mode some official name ? (e.g. in the datasheet or official product name ) Because having a 'bool non-LL' as false making it in a LL mode adds a double negation that is difficult to follow.

In the Designware databook for PCIe Express controller, provided by Synopsis, Linked List mode is referred to LL mode and non-LL mode where no Linked List is used. Hope this clarifies the doubt.

Quoted 19 lines
Quoted 17 lines
Quoted 17 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:
- When a valid physical base address is not configured via the Xilinx VSEC capability then the IP can still be used in non-LL mode. The default mode for all the DMA transactions and for all the DMA channels then is non-LL mode.
- When a valid physical base address is configured but the client wants to use the non-LL mode for DMA transactions then also the flexibility is provided via the peripheral_config struct member of dma_slave_config. In this case the channels can be individually configured in non-LL mode. This use case is desirable for single DMA transfer of a chunk, this saves the effort of preparing the Link List. This particular scenario is applicable to AMD as well as Synopsys IP.

[...]

#13 lore raw

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

In reply to Devendra K Verma
Quoted 8 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:
- When a valid physical base address is not configured via the Xilinx VSEC capability then the IP can still be used in non-LL mode. The default mode for all the DMA transactions and for all the DMA channels then is non-LL mode.

Quoted 9 lines

- When a valid physical base address is configured but the client wants to use the non-LL mode for DMA transactions then also the flexibility is provided via the peripheral_config struct member of dma_slave_config. In this case the channels can be individually configured in non-LL mode. This use case is desirable for single DMA transfer of a chunk, this saves the effort of preparing the Link List. This particular scenario is applicable to AMD as well as Synopsys IP.

Which in-kernel DMA client is using this non-LL mode?

- Mani

Quoted 349 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(). 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    | 41 ++++++++++++++++++++---
 drivers/dma/dw-edma/dw-edma-core.h    |  1 +
 drivers/dma/dw-edma/dw-edma-pcie.c    | 44 +++++++++++++++++--------
 drivers/dma/dw-edma/dw-hdma-v0-core.c | 61 ++++++++++++++++++++++++++++++++++-
 drivers/dma/dw-edma/dw-hdma-v0-regs.h |  1 +
 include/linux/dma/edma.h              |  1 +
 6 files changed, 130 insertions(+), 19 deletions(-)
drivers/dma/dw-edma/dw-edma-core.c +36 -5
index b43255f..60a3279 100644
@@ -223,8 +223,31 @@ 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 +376,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 +442,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 +475,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 +31 -13
index 3d7247c..e7e95df 100644
@@ -269,6 +269,15 @@ static void dw_edma_pcie_get_vsec_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)
 {
@@ -278,6 +287,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)
@@ -302,21 +312,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_AMD_MDB_INVALID_ADDR)
-			return -ENOMEM;
+			non_ll = true;
 
 		/*
 		 * Configure the channel LL and data blocks if number of
 		 * channels enabled in VSEC capability are more than the
 		 * channels configured in amd_mdb_data.
 		 */
-		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);
+		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 */
@@ -364,6 +377,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;
@@ -372,7 +386,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];
@@ -383,7 +397,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;
 
@@ -392,12 +407,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];
@@ -408,7 +424,8 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
 			return -ENOMEM;
 
 		ll_region->vaddr.io += ll_block->off;
-		ll_region->paddr = pci_bus_address(pdev, ll_block->bar);
+		ll_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
+							 ll_block->bar);
 		ll_region->paddr += ll_block->off;
 		ll_region->sz = ll_block->sz;
 
@@ -417,7 +434,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 +60 -1
index e3f8db4..ee31c9a 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);
 }
 
+static void dw_hdma_v0_core_non_ll_start(struct dw_edma_chunk *chunk)
+{
+	struct dw_edma_chan *chan = chunk->chan;
+	struct dw_edma *dw = chan->dw;
+	struct dw_edma_burst *child;
+	u32 val;
+
+	list_for_each_entry(child, &chunk->burst->list, list) {
+		SET_CH_32(dw, chan->dir, chan->id, ch_en, HDMA_V0_CH_EN);
+
+		/* Source address */
+		SET_CH_32(dw, chan->dir, chan->id, sar.lsb,
+			  lower_32_bits(child->sar));
+		SET_CH_32(dw, chan->dir, chan->id, sar.msb,
+			  upper_32_bits(child->sar));
+
+		/* Destination address */
+		SET_CH_32(dw, chan->dir, chan->id, dar.lsb,
+			  lower_32_bits(child->dar));
+		SET_CH_32(dw, chan->dir, chan->id, dar.msb,
+			  upper_32_bits(child->dar));
+
+		/* Transfer size */
+		SET_CH_32(dw, chan->dir, chan->id, transfer_size, child->sz);
+
+		/* Interrupt setup */
+		val = GET_CH_32(dw, chan->dir, chan->id, int_setup) |
+				HDMA_V0_STOP_INT_MASK |
+				HDMA_V0_ABORT_INT_MASK |
+				HDMA_V0_LOCAL_STOP_INT_EN |
+				HDMA_V0_LOCAL_ABORT_INT_EN;
+
+		if (!(dw->chip->flags & DW_EDMA_CHIP_LOCAL)) {
+			val |= HDMA_V0_REMOTE_STOP_INT_EN |
+			       HDMA_V0_REMOTE_ABORT_INT_EN;
+		}
+
+		SET_CH_32(dw, chan->dir, chan->id, int_setup, val);
+
+		/* Channel control setup */
+		val = GET_CH_32(dw, chan->dir, chan->id, control1);
+		val &= ~HDMA_V0_LINKLIST_EN;
+		SET_CH_32(dw, chan->dir, chan->id, control1, val);
+
+		SET_CH_32(dw, chan->dir, chan->id, doorbell,
+			  HDMA_V0_DOORBELL_START);
+	}
+}
+
+static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
+{
+	struct dw_edma_chan *chan = chunk->chan;
+
+	if (!chan->non_ll)
+		dw_hdma_v0_core_ll_start(chunk, first);
+	else
+		dw_hdma_v0_core_non_ll_start(chunk);
+}
+
 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 -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
Signature · 1 line
மணிவண்ணன் சதாசிவம்
#14 lore raw

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

[AMD Official Use Only - AMD Internal Distribution Only]

Hi Manivannan

Please check my response inline.

Regards,
Devendra

Quoted 32 lines

-----Original Message-----
From: Manivannan Sadhasivam <[email protected]>
Sent: Monday, December 8, 2025 11:00 AM
To: Verma, Devendra <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; Simek, Michal <[email protected]>
Subject: Re: [PATCH RESEND v6 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 8 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:
- When a valid physical base address is not configured via the Xilinx VSEC capability then the IP can still be used in non-LL mode. The default mode for all the DMA transactions and for all the DMA channels then is non-LL mode.

Quoted 9 lines

- When a valid physical base address is configured but the client wants to use the non-LL mode for DMA transactions then also the flexibility is provided via the peripheral_config struct member of dma_slave_config. In this case the channels can be individually configured in non-LL mode. This use case is desirable for single DMA transfer of a chunk, this saves the effort of preparing the Link List. This particular scenario is applicable to AMD as well as Synopsys IP.

Quoted 4 lines

Which in-kernel DMA client is using this non-LL mode?

- Mani

Existing dma client application(s) can use the non-LL mode for the AMD (Xilinx) use case with the help of a simple peripheral_config variable which is part of the dma_slave_config structure.
Though, no driver is using the non-LL mode as non-LL mode is not available in the current code.
Based on the following input (peripheral_config) from client in dw_edma_device_config(), non-LL mode can be decided by the controller driver.
+ if (config->peripheral_config)
+ non_ll = *(int *)config->peripheral_config;

Quoted 371 lines

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

Quoted 40 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(). 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    | 41 ++++++++++++++++++++---
 drivers/dma/dw-edma/dw-edma-core.h    |  1 +
 drivers/dma/dw-edma/dw-edma-pcie.c    | 44 +++++++++++++++++--------
 drivers/dma/dw-edma/dw-hdma-v0-core.c | 61
++++++++++++++++++++++++++++++++++-
 drivers/dma/dw-edma/dw-hdma-v0-regs.h |  1 +
 include/linux/dma/edma.h              |  1 +
 6 files changed, 130 insertions(+), 19 deletions(-)
drivers/dma/dw-edma/dw-edma-core.c +0 -0
index b43255f..60a3279 100644
@@ -223,8 +223,31 @@ 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 31 lines
drivers/dma/dw-edma/dw-edma-core.c +23 -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 +376,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 +442,11 @@ static void dw_edma_device_issue_pending(struct

dma_chan *dchan)

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

Quoted 48 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;
+     }
 
      if (xfer->type == EDMA_XFER_INTERLEAVED) {
              src_addr = xfer->xfer.il->src_start; @@ -450,7 +475,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 3d7247c..e7e95df 100644
@@ -269,6 +269,15 @@ static void

dw_edma_pcie_get_vsec_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)  { @@
-278,6 +287,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)
@@ -302,21 +312,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 -1
      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_AMD_MDB_INVALID_ADDR)

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

Quoted 24 lines
drivers/dma/dw-edma/dw-edma-pcie.c +8 -6
-                     return -ENOMEM;
+                     non_ll = true;
 
              /*
               * Configure the channel LL and data blocks if number of
               * channels enabled in VSEC capability are more than the
               * channels configured in amd_mdb_data.
               */
-             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);
+             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 */
@@ -364,6 +377,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; @@ -372,7 +386,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];
@@ -383,7 +397,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;
 
@@ -392,12 +407,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];
@@ -408,7 +424,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;
 
@@ -417,7 +434,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..ee31c9a 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)

Quoted 88 lines
drivers/dma/dw-edma/dw-hdma-v0-core.c +60 -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) {
+             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_ll_start(chunk, first);
+     else
+             dw_hdma_v0_core_non_ll_start(chunk);
+}
+
 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 */
--
Quoted 2 lines

1.8.3.1

--
மணிவண்ணன் சதாசிவம்

#15 lore raw

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

Quoted 41 lines · Devendra

[AMD Official Use Only - AMD Internal Distribution Only]

Hi Manivannan

Please check my response inline.

Regards,
Devendra

Quoted 32 lines

-----Original Message-----
From: Manivannan Sadhasivam <[email protected]>
Sent: Monday, December 8, 2025 11:00 AM
To: Verma, Devendra <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; Simek, Michal <[email protected]>
Subject: Re: [PATCH RESEND v6 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 17 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:
- When a valid physical base address is not configured via the Xilinx VSEC capability then the IP can still be used in non-LL mode. The default mode for all the DMA transactions and for all the DMA channels then is non-LL mode.
- When a valid physical base address is configured but the client wants to use the non-LL mode for DMA transactions then also the flexibility is provided via the peripheral_config struct member of dma_slave_config. In this case the channels can be individually configured in non-LL mode. This use case is desirable for single DMA transfer of a chunk, this saves the effort of preparing the Link List. This particular scenario is applicable to AMD as well as Synopsys IP.

Quoted 8 lines
Quoted 4 lines

Which in-kernel DMA client is using this non-LL mode?

- Mani

Existing dma client application(s) can use the non-LL mode for the AMD (Xilinx) use case with the help of a simple peripheral_config variable which is part of the dma_slave_config structure.

There is no existing client driver making use of this non-LL mode. So essentially, this patch is introducing the dead code.

Quoted 2 lines

Though, no driver is using the non-LL mode as non-LL mode is not available in the current code.

Then introduce non-LL mode when such client drivers start using it.

- Mani

Quoted 376 lines

Based on the following input (peripheral_config) from client in dw_edma_device_config(), non-LL mode can be decided by the controller driver.

drivers/dma/dw-edma/dw-edma-core.c +2 -0
+     if (config->peripheral_config)
+             non_ll = *(int *)config->peripheral_config;
Quoted 372 lines

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

Quoted 41 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(). 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    | 41 ++++++++++++++++++++---
 drivers/dma/dw-edma/dw-edma-core.h    |  1 +
 drivers/dma/dw-edma/dw-edma-pcie.c    | 44 +++++++++++++++++--------
 drivers/dma/dw-edma/dw-hdma-v0-core.c | 61
++++++++++++++++++++++++++++++++++-
 drivers/dma/dw-edma/dw-hdma-v0-regs.h |  1 +
 include/linux/dma/edma.h              |  1 +
 6 files changed, 130 insertions(+), 19 deletions(-)
drivers/dma/dw-edma/dw-edma-core.c +0 -0
index b43255f..60a3279 100644
@@ -223,8 +223,31 @@ 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 31 lines
drivers/dma/dw-edma/dw-edma-core.c +23 -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 +376,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 +442,11 @@ static void dw_edma_device_issue_pending(struct

dma_chan *dchan)

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

Quoted 48 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;
+     }
 
      if (xfer->type == EDMA_XFER_INTERLEAVED) {
              src_addr = xfer->xfer.il->src_start; @@ -450,7 +475,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 3d7247c..e7e95df 100644
@@ -269,6 +269,15 @@ static void

dw_edma_pcie_get_vsec_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)  { @@
-278,6 +287,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)
@@ -302,21 +312,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 -1
      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_AMD_MDB_INVALID_ADDR)

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

Quoted 24 lines
drivers/dma/dw-edma/dw-edma-pcie.c +8 -6
-                     return -ENOMEM;
+                     non_ll = true;
 
              /*
               * Configure the channel LL and data blocks if number of
               * channels enabled in VSEC capability are more than the
               * channels configured in amd_mdb_data.
               */
-             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);
+             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 */
@@ -364,6 +377,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; @@ -372,7 +386,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];
@@ -383,7 +397,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;
 
@@ -392,12 +407,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];
@@ -408,7 +424,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;
 
@@ -417,7 +434,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..ee31c9a 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)

Quoted 88 lines
drivers/dma/dw-edma/dw-hdma-v0-core.c +60 -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) {
+             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_ll_start(chunk, first);
+     else
+             dw_hdma_v0_core_non_ll_start(chunk);
+}
+
 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 */
--
Quoted 2 lines

1.8.3.1

--
மணிவண்ணன் சதாசிவம்

Signature · 1 line
மணிவண்ணன் சதாசிவம்
#16 lore raw

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

[AMD Official Use Only - AMD Internal Distribution Only]

Hi Manivannan

Quoted 68 lines

-----Original Message-----
From: Manivannan Sadhasivam <[email protected]>
Sent: Wednesday, December 10, 2025 6:59 PM
To: Verma, Devendra <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; Simek, Michal <[email protected]>
Subject: Re: [PATCH RESEND v6 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 42 lines · Devendra

[AMD Official Use Only - AMD Internal Distribution Only]

Hi Manivannan

Please check my response inline.

Regards,
Devendra

Quoted 33 lines

-----Original Message-----
From: Manivannan Sadhasivam <[email protected]>
Sent: Monday, December 8, 2025 11:00 AM
To: Verma, Devendra <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; Simek, Michal <[email protected]>
Subject: Re: [PATCH RESEND v6 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 17 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:
- When a valid physical base address is not configured via the Xilinx VSEC capability then the IP can still be used in non-LL mode. The default mode for all the DMA transactions and for all the DMA channels then is non-LL mode.
- When a valid physical base address is configured but the client wants to use the non-LL mode for DMA transactions then also the flexibility is provided via the peripheral_config struct member of dma_slave_config. In this case the channels can be individually configured in non-LL mode. This use case is desirable for single DMA transfer of a chunk, this saves the effort of preparing the Link List. This particular scenario is applicable to AMD as well as Synopsys IP.

Quoted 8 lines
Quoted 4 lines

Which in-kernel DMA client is using this non-LL mode?

- Mani

Existing dma client application(s) can use the non-LL mode for the AMD (Xilinx) use case with the help of a simple peripheral_config variable which is part of the dma_slave_config structure.

There is no existing client driver making use of this non-LL mode. So essentially, this patch is introducing the dead code.

Quoted 7 lines
Quoted 2 lines

Though, no driver is using the non-LL mode as non-LL mode is not available in the current code.

Then introduce non-LL mode when such client drivers start using it.

- Mani

Please excuse me as I left out a use case related to AMD MDB controller.
The AMD MDB controller has a valid use-case for non-LL mode when the base physical address offset of the controller side DDR is not available / configured. In the absence of this offset the controller is not usable as the default mode enabled is LL mode. With the introduction of non-LL mode, if the offset is not configured then also controller can be used for the DMA transactions. The choice of non-LL or LL mode is use-case specific. The following snippet handles that scenario in the
dw_edma_device_config() call:
+ if (chan->dw->chip->non_ll || (!chan->dw->chip->non_ll && non_ll))
+ chan->non_ll = true;

In my previous response, I explained that it is the optional configuration using which DMA clients can use the non-LL mode when the default mode used is LL mode.

I hope this clarifies, the code is not dead code rather it has use case for AMD MDB *and* an option to use non-LL mode for both Synopsys and AMD controller when LL mode is default mode. The default non-LL mode case is not available for Synopsys.

Regards,
Devendra

Quoted 396 lines
Quoted 2 lines

Based on the following input (peripheral_config) from client in dw_edma_device_config(), non-LL mode can be decided by the controller

driver.

Quoted 24 lines
drivers/dma/dw-edma/dw-edma-core.c +2 -0
+     if (config->peripheral_config)
+             non_ll = *(int *)config->peripheral_config;
Quoted 22 lines
Quoted 22 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(). 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    | 41

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

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

----

Quoted 69 lines
Quoted 69 lines

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

Quoted 12 lines
 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, 130 insertions(+), 19 deletions(-)
drivers/dma/dw-edma/dw-edma-core.c +0 -0
index b43255f..60a3279 100644
@@ -223,8 +223,31 @@ static int dw_edma_device_config(struct

dma_chan *dchan,

Quoted 31 lines
drivers/dma/dw-edma/dw-edma-core.c +23 -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 +376,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 +442,11 @@ static void

dw_edma_device_issue_pending(struct

dma_chan *dchan)

Quoted 12 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 3 lines
Quoted 3 lines
Quoted 3 lines
      if (xfer->type == EDMA_XFER_INTERLEAVED) {
              src_addr = xfer->xfer.il->src_start; @@ -450,7

+475,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 3d7247c..e7e95df 100644
@@ -269,6 +269,15 @@ static void

dw_edma_pcie_get_vsec_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)  { @@
-278,6 +287,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
@@ -302,21 +312,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 -1
      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_AMD_MDB_INVALID_ADDR)

Quoted 24 lines
drivers/dma/dw-edma/dw-edma-pcie.c +9 -6
-                     return -ENOMEM;
+                     non_ll = true;
 
              /*
               * Configure the channel LL and data blocks if number of
               * channels enabled in VSEC capability are more than the
               * channels configured in amd_mdb_data.
               */
-             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);
+             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 */ @@ -364,6 +377,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; @@ -372,7 +386,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]; @@ -383,7 +397,8 @@ static int dw_edma_pcie_probe(struct pci_dev

*pdev,

Quoted 10 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;
Quoted 1 line
Quoted 1 line

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

Quoted 1 line
drivers/dma/dw-edma/dw-edma-pcie.c +0 -0
@@ -392,12 +407,13 @@ static int dw_edma_pcie_probe(struct

pci_dev

Quoted 120 lines
Quoted 120 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]; @@ -408,7 +424,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;
@@ -417,7 +434,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..ee31c9a 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 54 lines
drivers/dma/dw-edma/dw-hdma-v0-core.c +54 -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) {
+             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

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
+
+     if (!chan->non_ll)
+             dw_hdma_v0_core_ll_start(chunk, first);
+     else
+             dw_hdma_v0_core_non_ll_start(chunk);
+}
+
 static void dw_hdma_v0_core_ch_config(struct dw_edma_chan *chan)

{

Quoted 25 lines
Quoted 25 lines
Quoted 25 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 -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;
 };
 
Quoted 7 lines
Quoted 7 lines
Quoted 4 lines
include/linux/dma/edma.h +0 -1
 /* Export to the platform drivers */
--

1.8.3.1

--
மணிவண்ணன் சதாசிவம்

--
மணிவண்ணன் சதாசிவம்

#17 lore raw

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

Quoted 91 lines · Devendra

[AMD Official Use Only - AMD Internal Distribution Only]

Hi Manivannan

Quoted 68 lines

-----Original Message-----
From: Manivannan Sadhasivam <[email protected]>
Sent: Wednesday, December 10, 2025 6:59 PM
To: Verma, Devendra <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; Simek, Michal <[email protected]>
Subject: Re: [PATCH RESEND v6 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 42 lines · Devendra

[AMD Official Use Only - AMD Internal Distribution Only]

Hi Manivannan

Please check my response inline.

Regards,
Devendra

Quoted 33 lines

-----Original Message-----
From: Manivannan Sadhasivam <[email protected]>
Sent: Monday, December 8, 2025 11:00 AM
To: Verma, Devendra <[email protected]>
Cc: [email protected]; [email protected]; [email protected]; [email protected]; [email protected]; Simek, Michal <[email protected]>
Subject: Re: [PATCH RESEND v6 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 17 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:
- When a valid physical base address is not configured via the Xilinx VSEC capability then the IP can still be used in non-LL mode. The default mode for all the DMA transactions and for all the DMA channels then is non-LL mode.
- When a valid physical base address is configured but the client wants to use the non-LL mode for DMA transactions then also the flexibility is provided via the peripheral_config struct member of dma_slave_config. In this case the channels can be individually configured in non-LL mode. This use case is desirable for single DMA transfer of a chunk, this saves the effort of preparing the Link List. This particular scenario is applicable to AMD as well as Synopsys IP.

Quoted 8 lines
Quoted 4 lines

Which in-kernel DMA client is using this non-LL mode?

- Mani

Existing dma client application(s) can use the non-LL mode for the AMD (Xilinx) use case with the help of a simple peripheral_config variable which is part of the dma_slave_config structure.

There is no existing client driver making use of this non-LL mode. So essentially, this patch is introducing the dead code.

Quoted 7 lines
Quoted 2 lines

Though, no driver is using the non-LL mode as non-LL mode is not available in the current code.

Then introduce non-LL mode when such client drivers start using it.

- Mani

Please excuse me as I left out a use case related to AMD MDB controller.
The AMD MDB controller has a valid use-case for non-LL mode when the base physical address offset of the controller side DDR is not available / configured. In the absence of this offset the controller is not usable as the default mode enabled is LL mode. With the introduction of non-LL mode, if the offset is not configured then also controller can be used for the DMA transactions. The choice of non-LL or LL mode is use-case specific. The following snippet handles that scenario in the
dw_edma_device_config() call:

drivers/dma/dw-edma/dw-edma-core.c +2 -0
+     if (chan->dw->chip->non_ll || (!chan->dw->chip->non_ll && non_ll))
+             chan->non_ll = true;
Quoted 7 lines

In my previous response, I explained that it is the optional configuration using which DMA clients can use the non-LL mode when the default mode used is LL mode.

I hope this clarifies, the code is not dead code rather it has use case for AMD MDB *and* an option to use non-LL mode for both Synopsys and AMD controller when LL mode is default mode. The default non-LL mode case is not available for Synopsys.

Ok, thanks for clarification. If you've explained this in the commit message in the first place, it would've helped me.

- Mani

Signature · 1 line
மணிவண்ணன் சதாசிவம்