* [PATCH v7 0/2] Add AMD MDB Endpoint and non-LL mode Support
@ 2025-12-12 12:20 Devendra K Verma
2025-12-12 12:20 ` [PATCH v7 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint Support Devendra K Verma
2025-12-12 12:20 ` [PATCH v7 2/2] dmaengine: dw-edma: Add non-LL mode Devendra K Verma
0 siblings, 2 replies; 8+ messages in thread
From: Devendra K Verma @ 2025-12-12 12:20 UTC (permalink / raw)
To: bhelgaas, mani, vkoul
Cc: dmaengine, linux-pci, linux-kernel, michal.simek, Devendra.Verma
This series of patch support the following:
- AMD MDB Endpoint Support, as part of this patch following are
added:
o AMD supported device ID and vendor ID (Xilinx)
o AMD MDB specific driver data
o AMD specific VSEC capabilities to retrieve the base of
phys address of MDB side DDR
o Logic to assign the offsets to LL and data blocks if
more number of channels are enabled than configured
in the given pci_data struct.
- Addition of non-LL mode
o The IP supported non-LL mode functions
o Flexibility to choose non-LL mode via dma_slave_config
param peripheral_config, by the client for all the vendors
using HDMA IP.
o Allow IP utilization if LL mode is not available
Devendra K Verma (2):
dmaengine: dw-edma: Add AMD MDB Endpoint Support
dmaengine: dw-edma: Add non-LL mode
drivers/dma/dw-edma/dw-edma-core.c | 41 ++++++-
drivers/dma/dw-edma/dw-edma-core.h | 1 +
drivers/dma/dw-edma/dw-edma-pcie.c | 199 ++++++++++++++++++++++++++++++++--
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, 289 insertions(+), 15 deletions(-)
--
1.8.3.1
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v7 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint Support
2025-12-12 12:20 [PATCH v7 0/2] Add AMD MDB Endpoint and non-LL mode Support Devendra K Verma
@ 2025-12-12 12:20 ` Devendra K Verma
2025-12-12 18:08 ` Bjorn Helgaas
2025-12-12 12:20 ` [PATCH v7 2/2] dmaengine: dw-edma: Add non-LL mode Devendra K Verma
1 sibling, 1 reply; 8+ messages in thread
From: Devendra K Verma @ 2025-12-12 12:20 UTC (permalink / raw)
To: bhelgaas, mani, vkoul
Cc: dmaengine, linux-pci, linux-kernel, michal.simek, Devendra.Verma
AMD MDB PCIe endpoint support. For AMD specific support
added the following
- AMD supported PCIe Device IDs and Vendor ID (Xilinx).
- AMD MDB specific driver data
- AMD MDB specific VSEC capability to retrieve the device DDR
base address.
Signed-off-by: Devendra K Verma <devendra.verma@amd.com>
---
Changes in v7:
Introduced vendor specific functions to retrieve the
vsec data.
Changes in v6:
Included "sizes.h" header and used the appropriate
definitions instead of constants.
Changes in v5:
Added the definitions for Xilinx specific VSEC header id,
revision, and register offsets.
Corrected the error type when no physical offset found for
device side memory.
Corrected the order of variables.
Changes in v4:
Configured 8 read and 8 write channels for Xilinx vendor
Added checks to validate vendor ID for vendor
specific vsec id.
Added Xilinx specific vendor id for vsec specific to Xilinx
Added the LL and data region offsets, size as input params to
function dw_edma_set_chan_region_offset().
Moved the LL and data region offsets assignment to function
for Xilinx specific case.
Corrected comments.
Changes in v3:
Corrected a typo when assigning AMD (Xilinx) vsec id macro
and condition check.
Changes in v2:
Reverted the devmem_phys_off type to u64.
Renamed the function appropriately to suit the
functionality for setting the LL & data region offsets.
Changes in v1:
Removed the pci device id from pci_ids.h file.
Added the vendor id macro as per the suggested method.
Changed the type of the newly added devmem_phys_off variable.
Added to logic to assign offsets for LL and data region blocks
in case more number of channels are enabled than given in
amd_mdb_data struct.
---
drivers/dma/dw-edma/dw-edma-pcie.c | 169 ++++++++++++++++++++++++++++++++++++-
1 file changed, 166 insertions(+), 3 deletions(-)
diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c b/drivers/dma/dw-edma/dw-edma-pcie.c
index 3371e0a7..9c1314b 100644
--- a/drivers/dma/dw-edma/dw-edma-pcie.c
+++ b/drivers/dma/dw-edma/dw-edma-pcie.c
@@ -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);
@@ -114,8 +189,8 @@ static u64 dw_edma_pcie_address(struct device *dev, phys_addr_t cpu_addr)
.pci_address = dw_edma_pcie_address,
};
-static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
- struct dw_edma_pcie_data *pdata)
+static void dw_edma_pcie_get_synopsys_dma_data(struct pci_dev *pdev,
+ struct dw_edma_pcie_data *pdata)
{
u32 val, map;
u16 vsec;
@@ -157,6 +232,70 @@ static void dw_edma_pcie_get_vsec_dma_data(struct pci_dev *pdev,
pdata->rg.off = off;
}
+static void dw_edma_pcie_get_xilinx_dma_data(struct pci_dev *pdev,
+ struct dw_edma_pcie_data *pdata)
+{
+ u32 val, map;
+ u16 vsec;
+ u64 off;
+
+ vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_XILINX,
+ DW_PCIE_XILINX_MDB_VSEC_DMA_ID);
+ if (!vsec)
+ return;
+
+ pci_read_config_dword(pdev, vsec + PCI_VNDR_HEADER, &val);
+ if (PCI_VNDR_HEADER_REV(val) != 0x00 ||
+ PCI_VNDR_HEADER_LEN(val) != 0x18)
+ return;
+
+ pci_dbg(pdev, "Detected PCIe Vendor-Specific Extended Capability DMA\n");
+ pci_read_config_dword(pdev, vsec + 0x8, &val);
+ map = FIELD_GET(DW_PCIE_VSEC_DMA_MAP, val);
+ if (map != EDMA_MF_EDMA_LEGACY &&
+ map != EDMA_MF_EDMA_UNROLL &&
+ map != EDMA_MF_HDMA_COMPAT &&
+ map != EDMA_MF_HDMA_NATIVE)
+ return;
+
+ pdata->mf = map;
+ pdata->rg.bar = FIELD_GET(DW_PCIE_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);
+ off <<= 32;
+ off |= val;
+ pdata->rg.off = off;
+
+ vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_XILINX,
+ DW_PCIE_XILINX_MDB_VSEC_ID);
+ if (!vsec)
+ return;
+
+ pci_read_config_dword(pdev, vsec + 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,
const struct pci_device_id *pid)
{
@@ -179,12 +318,34 @@ 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
* for the DMA, if one exists, then reconfigures it.
*/
- dw_edma_pcie_get_vsec_dma_data(pdev, vsec_data);
+ dw_edma_pcie_get_synopsys_dma_data(pdev, vsec_data);
+ dw_edma_pcie_get_xilinx_dma_data(pdev, vsec_data);
+
+ if (pdev->vendor == PCI_VENDOR_ID_XILINX) {
+ /*
+ * There is no valid address found for the LL memory
+ * space on the device side.
+ */
+ if (vsec_data->devmem_phys_off == DW_PCIE_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);
@@ -367,6 +528,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);
--
1.8.3.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v7 2/2] dmaengine: dw-edma: Add non-LL mode
2025-12-12 12:20 [PATCH v7 0/2] Add AMD MDB Endpoint and non-LL mode Support Devendra K Verma
2025-12-12 12:20 ` [PATCH v7 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint Support Devendra K Verma
@ 2025-12-12 12:20 ` Devendra K Verma
2025-12-12 18:21 ` Bjorn Helgaas
2025-12-16 12:31 ` Vinod Koul
1 sibling, 2 replies; 8+ messages in thread
From: Devendra K Verma @ 2025-12-12 12:20 UTC (permalink / raw)
To: bhelgaas, mani, vkoul
Cc: dmaengine, linux-pci, linux-kernel, michal.simek, Devendra.Verma
AMD MDB IP supports Linked List (LL) mode as well as non-LL mode.
The current code does not have the mechanisms to enable the
DMA transactions using the non-LL mode. The following two cases
are added with this patch:
- For the AMD (Xilinx) only, when a valid physical base address of
the device side DDR is not configured, then the IP can still be
used in non-LL mode. For all the channels DMA transactions will
be using the non-LL mode only. This, the default non-LL mode,
is not applicable for Synopsys IP with the current code addition.
- If the default mode is LL-mode, for both AMD (Xilinx) and Synosys,
and if user wants to use non-LL mode then user can do so via
configuring the peripheral_config param of dma_slave_config.
Signed-off-by: Devendra K Verma <devendra.verma@amd.com>
---
Changes in v7
No change
Changes in v6
Gave definition to bits used for channel configuration.
Removed the comment related to doorbell.
Changes in v5
Variable name 'nollp' changed to 'non_ll'.
In the dw_edma_device_config() WARN_ON replaced with dev_err().
Comments follow the 80-column guideline.
Changes in v4
No change
Changes in v3
No change
Changes in v2
Reverted the function return type to u64 for
dw_edma_get_phys_addr().
Changes in v1
Changed the function return type for dw_edma_get_phys_addr().
Corrected the typo raised in review.
---
drivers/dma/dw-edma/dw-edma-core.c | 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(-)
diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index b43255f..60a3279 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -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;
diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
index 71894b9..c8e3d19 100644
--- a/drivers/dma/dw-edma/dw-edma-core.h
+++ b/drivers/dma/dw-edma/dw-edma-core.h
@@ -86,6 +86,7 @@ struct dw_edma_chan {
u8 configured;
struct dma_slave_config config;
+ bool non_ll;
};
struct dw_edma_irq {
diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c b/drivers/dma/dw-edma/dw-edma-pcie.c
index 9c1314b..6c550a5 100644
--- a/drivers/dma/dw-edma/dw-edma-pcie.c
+++ b/drivers/dma/dw-edma/dw-edma-pcie.c
@@ -296,6 +296,15 @@ static void dw_edma_pcie_get_xilinx_dma_data(struct pci_dev *pdev,
pdata->devmem_phys_off = off;
}
+static u64 dw_edma_get_phys_addr(struct pci_dev *pdev,
+ struct dw_edma_pcie_data *pdata,
+ enum pci_barno bar)
+{
+ if (pdev->vendor == PCI_VENDOR_ID_XILINX)
+ return pdata->devmem_phys_off;
+ return pci_bus_address(pdev, bar);
+}
+
static int dw_edma_pcie_probe(struct pci_dev *pdev,
const struct pci_device_id *pid)
{
@@ -305,6 +314,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)
@@ -330,21 +340,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 */
@@ -392,6 +405,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;
@@ -400,7 +414,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];
@@ -411,7 +425,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;
@@ -420,12 +435,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];
@@ -436,7 +452,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;
@@ -445,7 +462,8 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev,
return -ENOMEM;
dt_region->vaddr.io += dt_block->off;
- dt_region->paddr = pci_bus_address(pdev, dt_block->bar);
+ dt_region->paddr = dw_edma_get_phys_addr(pdev, vsec_data,
+ dt_block->bar);
dt_region->paddr += dt_block->off;
dt_region->sz = dt_block->sz;
}
diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.c b/drivers/dma/dw-edma/dw-hdma-v0-core.c
index e3f8db4..ee31c9a 100644
--- a/drivers/dma/dw-edma/dw-hdma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c
@@ -225,7 +225,7 @@ static void dw_hdma_v0_sync_ll_data(struct dw_edma_chunk *chunk)
readl(chunk->ll_region.vaddr.io);
}
-static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
+static void dw_hdma_v0_core_ll_start(struct dw_edma_chunk *chunk, bool first)
{
struct dw_edma_chan *chan = chunk->chan;
struct dw_edma *dw = chan->dw;
@@ -263,6 +263,65 @@ static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
SET_CH_32(dw, chan->dir, chan->id, doorbell, HDMA_V0_DOORBELL_START);
}
+static void dw_hdma_v0_core_non_ll_start(struct dw_edma_chunk *chunk)
+{
+ struct dw_edma_chan *chan = chunk->chan;
+ struct dw_edma *dw = chan->dw;
+ struct dw_edma_burst *child;
+ u32 val;
+
+ list_for_each_entry(child, &chunk->burst->list, list) {
+ SET_CH_32(dw, chan->dir, chan->id, ch_en, HDMA_V0_CH_EN);
+
+ /* Source address */
+ SET_CH_32(dw, chan->dir, chan->id, sar.lsb,
+ lower_32_bits(child->sar));
+ SET_CH_32(dw, chan->dir, chan->id, sar.msb,
+ upper_32_bits(child->sar));
+
+ /* Destination address */
+ SET_CH_32(dw, chan->dir, chan->id, dar.lsb,
+ lower_32_bits(child->dar));
+ SET_CH_32(dw, chan->dir, chan->id, dar.msb,
+ upper_32_bits(child->dar));
+
+ /* Transfer size */
+ SET_CH_32(dw, chan->dir, chan->id, transfer_size, child->sz);
+
+ /* Interrupt setup */
+ val = GET_CH_32(dw, chan->dir, chan->id, int_setup) |
+ HDMA_V0_STOP_INT_MASK |
+ HDMA_V0_ABORT_INT_MASK |
+ HDMA_V0_LOCAL_STOP_INT_EN |
+ HDMA_V0_LOCAL_ABORT_INT_EN;
+
+ if (!(dw->chip->flags & DW_EDMA_CHIP_LOCAL)) {
+ val |= HDMA_V0_REMOTE_STOP_INT_EN |
+ HDMA_V0_REMOTE_ABORT_INT_EN;
+ }
+
+ SET_CH_32(dw, chan->dir, chan->id, int_setup, val);
+
+ /* Channel control setup */
+ val = GET_CH_32(dw, chan->dir, chan->id, control1);
+ val &= ~HDMA_V0_LINKLIST_EN;
+ SET_CH_32(dw, chan->dir, chan->id, control1, val);
+
+ SET_CH_32(dw, chan->dir, chan->id, doorbell,
+ HDMA_V0_DOORBELL_START);
+ }
+}
+
+static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
+{
+ struct dw_edma_chan *chan = chunk->chan;
+
+ if (!chan->non_ll)
+ dw_hdma_v0_core_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;
diff --git a/drivers/dma/dw-edma/dw-hdma-v0-regs.h b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
index eab5fd7..7759ba9 100644
--- a/drivers/dma/dw-edma/dw-hdma-v0-regs.h
+++ b/drivers/dma/dw-edma/dw-hdma-v0-regs.h
@@ -12,6 +12,7 @@
#include <linux/dmaengine.h>
#define HDMA_V0_MAX_NR_CH 8
+#define HDMA_V0_CH_EN BIT(0)
#define HDMA_V0_LOCAL_ABORT_INT_EN BIT(6)
#define HDMA_V0_REMOTE_ABORT_INT_EN BIT(5)
#define HDMA_V0_LOCAL_STOP_INT_EN BIT(4)
diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h
index 3080747..78ce31b 100644
--- a/include/linux/dma/edma.h
+++ b/include/linux/dma/edma.h
@@ -99,6 +99,7 @@ struct dw_edma_chip {
enum dw_edma_map_format mf;
struct dw_edma *dw;
+ bool non_ll;
};
/* Export to the platform drivers */
--
1.8.3.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v7 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint Support
2025-12-12 12:20 ` [PATCH v7 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint Support Devendra K Verma
@ 2025-12-12 18:08 ` Bjorn Helgaas
2025-12-15 11:39 ` Verma, Devendra
0 siblings, 1 reply; 8+ messages in thread
From: Bjorn Helgaas @ 2025-12-12 18:08 UTC (permalink / raw)
To: Devendra K Verma
Cc: bhelgaas, mani, vkoul, dmaengine, linux-pci, linux-kernel,
michal.simek
On Fri, Dec 12, 2025 at 05:50:55PM +0530, Devendra K Verma wrote:
> AMD MDB PCIe endpoint support. For AMD specific support
> added the following
> - AMD supported PCIe Device IDs and Vendor ID (Xilinx).
> - AMD MDB specific driver data
> - AMD MDB specific VSEC capability to retrieve the device DDR
> base address.
> ...
> +++ b/drivers/dma/dw-edma/dw-edma-pcie.c
> +/* 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)
These should include "SYNOPSYS" since they are specific to
PCI_VENDOR_ID_SYNOPSYS. Add corresponding XILINX #defines below for
the XILINX VSEC. They'll be the same masks.
> +/* 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
Looks odd to me that PCI_DEVICE_ID_AMD_MDB_B054 goes with
PCI_VENDOR_ID_XILINX. To me this would make more sense as
PCI_DEVICE_ID_XILINX_B054. Move it so it's not in the middle of the
VSEC-related things.
> +#define DW_PCIE_AMD_MDB_INVALID_ADDR (~0ULL)
It looks like this is related to the value from
DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG and is only used for Xilinx, so
should be named similarly, e.g., DW_PCIE_XILINX_MDB_INVALID_ADDR, and
moved to be next to it.
> +#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
These LL/DT gap and size #defines don't look like they're directly
related to the VSEC, so they should at least be moved after the
DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG #defines, since those *are* part of
the VSEC.
> +#define DW_PCIE_XILINX_MDB_VSEC_HDR_ID 0x20
DW_PCIE_XILINX_MDB_VSEC_HDR_ID is pointless and should be removed.
See below.
> +#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
> +static const struct dw_edma_pcie_data amd_mdb_data = {
This is a confusing mix of "xilinx" and "amd_mdb". The DEVICE_ID
#define uses "AMD_MDB". The other #defines mostly use XILINX. This
data structure uses "amd_mdb". The function uses "xilinx".
Since this patch only applies to PCI_VENDOR_ID_XILINX, I would make
this "xilinx_mdb_data". If new devices come with a different vendor
ID, e.g., AMD, you can add a corresponding block for that.
> +static void dw_edma_pcie_get_xilinx_dma_data(struct pci_dev *pdev,
> + struct dw_edma_pcie_data *pdata)
> +{
> + u32 val, map;
> + u16 vsec;
> + u64 off;
> +
> + vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_XILINX,
> + DW_PCIE_XILINX_MDB_VSEC_DMA_ID);
> + if (!vsec)
> + return;
> +
> + pci_read_config_dword(pdev, vsec + PCI_VNDR_HEADER, &val);
> + if (PCI_VNDR_HEADER_REV(val) != 0x00 ||
> + PCI_VNDR_HEADER_LEN(val) != 0x18)
> + return;
> +
> + pci_dbg(pdev, "Detected PCIe Vendor-Specific Extended Capability DMA\n");
Perhaps reword this to "Detected Xilinx Vendor-Specific Extended
Capability DMA", and the one in dw_edma_pcie_get_synopsys_dma_data()
to similarly mention "Synopsys" to reinforce the fact that these are
Xilinx-specific and Synopsys-specific.
I think the REV and LEN checks are unnecessary; see below.
> + pci_read_config_dword(pdev, vsec + 0x8, &val);
> + map = FIELD_GET(DW_PCIE_VSEC_DMA_MAP, val);
Should use XILINX #defines. Another reason for adding "SYNOPSYS" to
the #defines for the Synopsys VSEC.
> + 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_find_vsec_capability() already checks that PCI_VNDR_HEADER_ID() ==
DW_PCIE_XILINX_MDB_VSEC_ID, so there's no need to check this again.
> + PCI_VNDR_HEADER_REV(val) != DW_PCIE_XILINX_MDB_VSEC_REV)
I know this is copied from dw_edma_pcie_get_vsec_dma_data(), but I
think it's a bad idea to check for the exact revision because it
forces a change to existing, working code if there's ever a device
with a new revision of this VSEC ID.
If there are new revisions of this VSEC, they should preserve the
semantics of previous revisions. If there was a rev 0 of this VSEC, I
think we should check for PCI_VNDR_HEADER_REV() >= 1. If rev 1 was
the first revision, you could skip the check altogether.
If rev 2 *adds* new registers or functionality, we would have to add
new code to support that, and *that* code should check for
PCI_VNDR_HEADER_REV() >= 2.
I think the REV and LEN checking in dw_edma_pcie_get_vsec_dma_data()
is also too aggressive.
> static int dw_edma_pcie_probe(struct pci_dev *pdev,
> const struct pci_device_id *pid)
> {
> @@ -179,12 +318,34 @@ 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;
Seems weird to set devmem_phys_off here since it's only used for
PCI_VENDOR_ID_XILINX. Couldn't this be done in
dw_edma_pcie_get_xilinx_dma_data()?
> - dw_edma_pcie_get_vsec_dma_data(pdev, vsec_data);
> + dw_edma_pcie_get_synopsys_dma_data(pdev, vsec_data);
> + dw_edma_pcie_get_xilinx_dma_data(pdev, vsec_data);
> +
> + if (pdev->vendor == PCI_VENDOR_ID_XILINX) {
> + /*
> + * There is no valid address found for the LL memory
> + * space on the device side.
> + */
> + if (vsec_data->devmem_phys_off == DW_PCIE_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);
> + }
This PCI_VENDOR_ID_XILINX block looks like maybe it would make sense
inside dw_edma_pcie_get_xilinx_dma_data()? That function could look
like:
dw_edma_pcie_get_xilinx_dma_data(...)
{
if (pdev->vendor != PCI_VENDOR_ID_XILINX)
return;
pdata->devmem_phys_off = DW_PCIE_XILINX_MDB_INVALID_ADDR;
...
> 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 },
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v7 2/2] dmaengine: dw-edma: Add non-LL mode
2025-12-12 12:20 ` [PATCH v7 2/2] dmaengine: dw-edma: Add non-LL mode Devendra K Verma
@ 2025-12-12 18:21 ` Bjorn Helgaas
2025-12-16 10:15 ` Verma, Devendra
2025-12-16 12:31 ` Vinod Koul
1 sibling, 1 reply; 8+ messages in thread
From: Bjorn Helgaas @ 2025-12-12 18:21 UTC (permalink / raw)
To: Devendra K Verma
Cc: bhelgaas, mani, vkoul, dmaengine, linux-pci, linux-kernel,
michal.simek
On Fri, Dec 12, 2025 at 05:50:56PM +0530, Devendra K Verma wrote:
> AMD MDB IP supports Linked List (LL) mode as well as non-LL mode.
> The current code does not have the mechanisms to enable the
> DMA transactions using the non-LL mode. The following two cases
> are added with this patch:
> - For the AMD (Xilinx) only, when a valid physical base address of
> the device side DDR is not configured, then the IP can still be
> used in non-LL mode. For all the channels DMA transactions will
> be using the non-LL mode only. This, the default non-LL mode,
> is not applicable for Synopsys IP with the current code addition.
>
> - If the default mode is LL-mode, for both AMD (Xilinx) and Synosys,
> and if user wants to use non-LL mode then user can do so via
> configuring the peripheral_config param of dma_slave_config.
> ...
> +++ b/drivers/dma/dw-edma/dw-edma-core.c
> @@ -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;
Other "non_ll" uses below are bool, so a little bit of an int/bool
mix.
The name also leads to a lot of double negative use ("!non_ll", etc),
which is hard to read. I suppose "non-LL" corresponds to some spec
language, but it would be nice if we could avoid some of the negation
by testing for "ll_mode" or calling the other mode "single_burst" or
similar.
> + * 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.
Add blank line between paragraphs.
^ permalink raw reply [flat|nested] 8+ messages in thread
* RE: [PATCH v7 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint Support
2025-12-12 18:08 ` Bjorn Helgaas
@ 2025-12-15 11:39 ` Verma, Devendra
0 siblings, 0 replies; 8+ messages in thread
From: Verma, Devendra @ 2025-12-15 11:39 UTC (permalink / raw)
To: Bjorn Helgaas
Cc: bhelgaas@google.com, mani@kernel.org, vkoul@kernel.org,
dmaengine@vger.kernel.org, linux-pci@vger.kernel.org,
linux-kernel@vger.kernel.org, Simek, Michal, Verma, Devendra
[AMD Official Use Only - AMD Internal Distribution Only]
Hi Bjorn
Thank you for your illustrative inputs and suggestions.
Please find my responses inline.
Regards,
Devendra
> -----Original Message-----
> From: Bjorn Helgaas <helgaas@kernel.org>
> Sent: Friday, December 12, 2025 11:38 PM
> To: Verma, Devendra <Devendra.Verma@amd.com>
> Cc: bhelgaas@google.com; mani@kernel.org; vkoul@kernel.org;
> dmaengine@vger.kernel.org; linux-pci@vger.kernel.org; linux-
> kernel@vger.kernel.org; Simek, Michal <michal.simek@amd.com>
> Subject: Re: [PATCH v7 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint
> Support
>
> Caution: This message originated from an External Source. Use proper
> caution when opening attachments, clicking links, or responding.
>
>
> On Fri, Dec 12, 2025 at 05:50:55PM +0530, Devendra K Verma wrote:
> > AMD MDB PCIe endpoint support. For AMD specific support added the
> > following
> > - AMD supported PCIe Device IDs and Vendor ID (Xilinx).
> > - AMD MDB specific driver data
> > - AMD MDB specific VSEC capability to retrieve the device DDR
> > base address.
> > ...
>
> > +++ b/drivers/dma/dw-edma/dw-edma-pcie.c
>
> > +/* 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)
>
> These should include "SYNOPSYS" since they are specific to
> PCI_VENDOR_ID_SYNOPSYS. Add corresponding XILINX #defines below for
> the XILINX VSEC. They'll be the same masks.
>
> > +/* 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
>
> Looks odd to me that PCI_DEVICE_ID_AMD_MDB_B054 goes with
> PCI_VENDOR_ID_XILINX. To me this would make more sense as
> PCI_DEVICE_ID_XILINX_B054. Move it so it's not in the middle of the VSEC-
> related things.
>
> > +#define DW_PCIE_AMD_MDB_INVALID_ADDR (~0ULL)
>
> It looks like this is related to the value from
> DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG and is only used for Xilinx, so
> should be named similarly, e.g., DW_PCIE_XILINX_MDB_INVALID_ADDR, and
> moved to be next to it.
>
Agreed on renaming the variables to match the name with the product vendor rather
than the product owner to maintain consistency. The local defines will use the name
DW_PCIE_XILINX_MDB_<VAR-NAME>. The
> > +#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
>
> These LL/DT gap and size #defines don't look like they're directly related to
> the VSEC, so they should at least be moved after the
> DW_PCIE_XILINX_MDB_DEVMEM_OFF_REG #defines, since those *are* part
> of the VSEC.
>
> > +#define DW_PCIE_XILINX_MDB_VSEC_HDR_ID 0x20
>
> DW_PCIE_XILINX_MDB_VSEC_HDR_ID is pointless and should be removed.
> See below.
>
Agreed, this will be removed in next revision.
> > +#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
>
> > +static const struct dw_edma_pcie_data amd_mdb_data = {
>
> This is a confusing mix of "xilinx" and "amd_mdb". The DEVICE_ID #define
> uses "AMD_MDB". The other #defines mostly use XILINX. This data structure
> uses "amd_mdb". The function uses "xilinx".
>
> Since this patch only applies to PCI_VENDOR_ID_XILINX, I would make this
> "xilinx_mdb_data". If new devices come with a different vendor ID, e.g.,
> AMD, you can add a corresponding block for that.
>
Agreed for this, the variable names should have relation with the product vendor
rather than the product owner.
> > +static void dw_edma_pcie_get_xilinx_dma_data(struct pci_dev *pdev,
> > + struct dw_edma_pcie_data
> > +*pdata) {
> > + u32 val, map;
> > + u16 vsec;
> > + u64 off;
> > +
> > + vsec = pci_find_vsec_capability(pdev, PCI_VENDOR_ID_XILINX,
> > + DW_PCIE_XILINX_MDB_VSEC_DMA_ID);
> > + if (!vsec)
> > + return;
> > +
> > + pci_read_config_dword(pdev, vsec + PCI_VNDR_HEADER, &val);
> > + if (PCI_VNDR_HEADER_REV(val) != 0x00 ||
> > + PCI_VNDR_HEADER_LEN(val) != 0x18)
> > + return;
> > +
> > + pci_dbg(pdev, "Detected PCIe Vendor-Specific Extended Capability
> > + DMA\n");
>
> Perhaps reword this to "Detected Xilinx Vendor-Specific Extended Capability
> DMA", and the one in dw_edma_pcie_get_synopsys_dma_data()
> to similarly mention "Synopsys" to reinforce the fact that these are Xilinx-
> specific and Synopsys-specific.
>
> I think the REV and LEN checks are unnecessary; see below.
>
> > + pci_read_config_dword(pdev, vsec + 0x8, &val);
> > + map = FIELD_GET(DW_PCIE_VSEC_DMA_MAP, val);
>
> Should use XILINX #defines. Another reason for adding "SYNOPSYS" to the
> #defines for the Synopsys VSEC.
>
Agreed, for the reason mentioned above.
> > + 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_find_vsec_capability() already checks that PCI_VNDR_HEADER_ID() ==
> DW_PCIE_XILINX_MDB_VSEC_ID, so there's no need to check this again.
>
> > + PCI_VNDR_HEADER_REV(val) != DW_PCIE_XILINX_MDB_VSEC_REV)
>
> I know this is copied from dw_edma_pcie_get_vsec_dma_data(), but I think
> it's a bad idea to check for the exact revision because it forces a change to
> existing, working code if there's ever a device with a new revision of this VSEC
> ID.
>
> If there are new revisions of this VSEC, they should preserve the semantics of
> previous revisions. If there was a rev 0 of this VSEC, I think we should check
> for PCI_VNDR_HEADER_REV() >= 1. If rev 1 was the first revision, you could
> skip the check altogether.
>
> If rev 2 *adds* new registers or functionality, we would have to add new code
> to support that, and *that* code should check for
> PCI_VNDR_HEADER_REV() >= 2.
>
> I think the REV and LEN checking in dw_edma_pcie_get_vsec_dma_data() is
> also too aggressive.
>
Agreed. Revision and header check will be removed.
> > static int dw_edma_pcie_probe(struct pci_dev *pdev,
> > const struct pci_device_id *pid) { @@
> > -179,12 +318,34 @@ 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;
>
> Seems weird to set devmem_phys_off here since it's only used for
> PCI_VENDOR_ID_XILINX. Couldn't this be done in
> dw_edma_pcie_get_xilinx_dma_data()?
>
This will be moved to dw_edma_pcie_get_xilinx_dma_data() function.
> > - dw_edma_pcie_get_vsec_dma_data(pdev, vsec_data);
> > + dw_edma_pcie_get_synopsys_dma_data(pdev, vsec_data);
> > + dw_edma_pcie_get_xilinx_dma_data(pdev, vsec_data);
> > +
> > + if (pdev->vendor == PCI_VENDOR_ID_XILINX) {
> > + /*
> > + * There is no valid address found for the LL memory
> > + * space on the device side.
> > + */
> > + if (vsec_data->devmem_phys_off ==
> DW_PCIE_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);
> > + }
>
> This PCI_VENDOR_ID_XILINX block looks like maybe it would make sense
> inside dw_edma_pcie_get_xilinx_dma_data()? That function could look
> like:
>
> dw_edma_pcie_get_xilinx_dma_data(...)
> {
> if (pdev->vendor != PCI_VENDOR_ID_XILINX)
> return;
>
> pdata->devmem_phys_off = DW_PCIE_XILINX_MDB_INVALID_ADDR;
> ...
>
In the above suggestion, having dw_edma_set_chan_region_offset() in the
current function makes sense as when checked along patch 2/2 of the same
series. Moreover, the function is setting up some offsets, doing so in a *get*
function does not look justified just because they are related to same vendor.
Similar thing is being done when setting up the ll and dt virt / phys addresses
In the same probe() function which is after *getting* the vsec data and
then *setting* up the next data based on the retrieved info. I also followed
the similar approach, for Xilinx, of *getting* vsec data, *setting* up offsets
and then move ahead with final *settings* of ll and dt regions.
> > 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 },
^ permalink raw reply [flat|nested] 8+ messages in thread
* RE: [PATCH v7 2/2] dmaengine: dw-edma: Add non-LL mode
2025-12-12 18:21 ` Bjorn Helgaas
@ 2025-12-16 10:15 ` Verma, Devendra
0 siblings, 0 replies; 8+ messages in thread
From: Verma, Devendra @ 2025-12-16 10:15 UTC (permalink / raw)
To: Bjorn Helgaas
Cc: bhelgaas@google.com, mani@kernel.org, vkoul@kernel.org,
dmaengine@vger.kernel.org, linux-pci@vger.kernel.org,
linux-kernel@vger.kernel.org, Simek, Michal, Verma, Devendra
[AMD Official Use Only - AMD Internal Distribution Only]
> -----Original Message-----
> From: Bjorn Helgaas <helgaas@kernel.org>
> Sent: Friday, December 12, 2025 11:52 PM
> To: Verma, Devendra <Devendra.Verma@amd.com>
> Cc: bhelgaas@google.com; mani@kernel.org; vkoul@kernel.org;
> dmaengine@vger.kernel.org; linux-pci@vger.kernel.org; linux-
> kernel@vger.kernel.org; Simek, Michal <michal.simek@amd.com>
> Subject: Re: [PATCH v7 2/2] dmaengine: dw-edma: Add non-LL mode
>
> Caution: This message originated from an External Source. Use proper
> caution when opening attachments, clicking links, or responding.
>
>
> On Fri, Dec 12, 2025 at 05:50:56PM +0530, Devendra K Verma wrote:
> > AMD MDB IP supports Linked List (LL) mode as well as non-LL mode.
> > The current code does not have the mechanisms to enable the DMA
> > transactions using the non-LL mode. The following two cases are added
> > with this patch:
> > - For the AMD (Xilinx) only, when a valid physical base address of
> > the device side DDR is not configured, then the IP can still be
> > used in non-LL mode. For all the channels DMA transactions will
> > be using the non-LL mode only. This, the default non-LL mode,
> > is not applicable for Synopsys IP with the current code addition.
> >
> > - If the default mode is LL-mode, for both AMD (Xilinx) and Synosys,
> > and if user wants to use non-LL mode then user can do so via
> > configuring the peripheral_config param of dma_slave_config.
> > ...
>
> > +++ b/drivers/dma/dw-edma/dw-edma-core.c
> > @@ -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;
>
> Other "non_ll" uses below are bool, so a little bit of an int/bool mix.
>
> The name also leads to a lot of double negative use ("!non_ll", etc), which is
> hard to read. I suppose "non-LL" corresponds to some spec language, but it
> would be nice if we could avoid some of the negation by testing for "ll_mode"
> or calling the other mode "single_burst" or similar.
>
Yes, non-LL is being referred in the Synosys databook extensively to differentiate
between LL and non-LL mode.
I agree with the concern raised here but, at the moment, this is the only suitable
term that can handle the following cases:
1) Choice of variable of the DMA client to use non-LL mode,
2) Establish flow for the non-LL use-case in the driver.
Before, using the term with negation (non_ll), the possibility was explored
to use a term which does not result in double negation, eg, ll or ll_mode.
But this again breaks the above either one or both use-cases.
If using ll_mode as a variable, then with this, DMA client shall
either provide ll_mode=false or non_ll=true.
When ll_mode=false. This option would be as good as
passing a valid reference to peripheral_config pointer as
the value of ll_mode would never be used for ll_mode=true
due to default mode being LL.
On the basis of ll_mode=true, the DMA client given option, no code
is impacted with these patches.
When DMA client gives non_ll=true; this causes confusion,
DMA client does right but internally ll_mode as a variable is set
to establish the flow for non-LL mode. The different variable is
used for establishing the non-LL mode inside the driver code.
Also, it uses the combination of double negation variable.
Though, the use of non_ll, raises concern due to double
negation but its use fits the use-case from both DMA client
and in the driver to establish the non-LL flow. Additionally,
The use of non_ll also aligns with the documentation of the
vendor making it easier to follow.
Please let me know your thoughts on this.
> > + * 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.
>
> Add blank line between paragraphs.
Sure, will take care of this.
Regards,
Devendra
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v7 2/2] dmaengine: dw-edma: Add non-LL mode
2025-12-12 12:20 ` [PATCH v7 2/2] dmaengine: dw-edma: Add non-LL mode Devendra K Verma
2025-12-12 18:21 ` Bjorn Helgaas
@ 2025-12-16 12:31 ` Vinod Koul
1 sibling, 0 replies; 8+ messages in thread
From: Vinod Koul @ 2025-12-16 12:31 UTC (permalink / raw)
To: Devendra K Verma
Cc: bhelgaas, mani, dmaengine, linux-pci, linux-kernel, michal.simek
On 12-12-25, 17:50, Devendra K Verma wrote:
> AMD MDB IP supports Linked List (LL) mode as well as non-LL mode.
> The current code does not have the mechanisms to enable the
> DMA transactions using the non-LL mode. The following two cases
> are added with this patch:
> - For the AMD (Xilinx) only, when a valid physical base address of
> the device side DDR is not configured, then the IP can still be
> used in non-LL mode. For all the channels DMA transactions will
> be using the non-LL mode only. This, the default non-LL mode,
> is not applicable for Synopsys IP with the current code addition.
>
> - If the default mode is LL-mode, for both AMD (Xilinx) and Synosys,
> and if user wants to use non-LL mode then user can do so via
> configuring the peripheral_config param of dma_slave_config.
>
> Signed-off-by: Devendra K Verma <devendra.verma@amd.com>
> ---
> Changes in v7
> No change
>
> Changes in v6
> Gave definition to bits used for channel configuration.
> Removed the comment related to doorbell.
>
> Changes in v5
> Variable name 'nollp' changed to 'non_ll'.
> In the dw_edma_device_config() WARN_ON replaced with dev_err().
> Comments follow the 80-column guideline.
>
> Changes in v4
> No change
>
> Changes in v3
> No change
>
> Changes in v2
> Reverted the function return type to u64 for
> dw_edma_get_phys_addr().
>
> Changes in v1
> Changed the function return type for dw_edma_get_phys_addr().
> Corrected the typo raised in review.
> ---
> drivers/dma/dw-edma/dw-edma-core.c | 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(-)
>
> diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> index b43255f..60a3279 100644
> --- a/drivers/dma/dw-edma/dw-edma-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-core.c
> @@ -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;
> + }
Hmm, what is this config param used for. I dont like people using this
in opaque manner. Can you explain why this needs to be passed from
client. What does non ll mean and how would client decide to use this or
not..?
--
~Vinod
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2025-12-16 12:31 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-12-12 12:20 [PATCH v7 0/2] Add AMD MDB Endpoint and non-LL mode Support Devendra K Verma
2025-12-12 12:20 ` [PATCH v7 1/2] dmaengine: dw-edma: Add AMD MDB Endpoint Support Devendra K Verma
2025-12-12 18:08 ` Bjorn Helgaas
2025-12-15 11:39 ` Verma, Devendra
2025-12-12 12:20 ` [PATCH v7 2/2] dmaengine: dw-edma: Add non-LL mode Devendra K Verma
2025-12-12 18:21 ` Bjorn Helgaas
2025-12-16 10:15 ` Verma, Devendra
2025-12-16 12:31 ` Vinod Koul
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).