[PATCH v2 01/19] dmaengine: dw-edma: Add dw_edma_core_ll_cur_idx() to get current LL entry index

From: Koichiro Den

Date: Thu Jul 23 2026 - 04:47:36 EST


From: Frank Li <Frank.Li@xxxxxxx>

Add dw_edma_core_ll_cur_idx() to get the current LL entry index and
prepare for dynamic addition of DMA requests while the DMA engine is
running.

Signed-off-by: Frank Li <Frank.Li@xxxxxxx>
Co-developed-by: Koichiro Den <den@xxxxxxxxxxxxx>
Signed-off-by: Koichiro Den <den@xxxxxxxxxxxxx>
---
Based on Frank's original submission:
https://lore.kernel.org/dmaengine/20260109-edma_dymatic-v1-1-9a98c9c98536@xxxxxxx/

Changes in v2:
- Handle zero low words and low-word wrap without reading DMA_LLP.MSB
on the normal path, and clarify the lost-context comments.

drivers/dma/dw-edma/dw-edma-core.h | 10 ++++++++++
drivers/dma/dw-edma/dw-edma-v0-core.c | 19 +++++++++++++++++++
drivers/dma/dw-edma/dw-hdma-v0-core.c | 19 +++++++++++++++++++
3 files changed, 48 insertions(+)

diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
index f6a5ad317567..e50fa7965778 100644
--- a/drivers/dma/dw-edma/dw-edma-core.h
+++ b/drivers/dma/dw-edma/dw-edma-core.h
@@ -138,6 +138,7 @@ struct dw_edma_core_ops {
void (*ll_data)(struct dw_edma_chan *chan, struct dw_edma_burst *burst,
u32 idx, bool cb, bool irq);
void (*ll_link)(struct dw_edma_chan *chan, u32 idx, bool cb, u64 addr);
+ int (*ll_cur_idx)(struct dw_edma_chan *chan);
void (*ch_doorbell)(struct dw_edma_chan *chan);
void (*ch_enable)(struct dw_edma_chan *chan);
void (*ch_config)(struct dw_edma_chan *chan);
@@ -181,6 +182,15 @@ struct dw_edma_chan *dchan2dw_edma_chan(struct dma_chan *dchan)
return vc2dw_edma_chan(to_virt_chan(dchan));
}

+/*
+ * Return the current LL entry index. A negative value means that the channel
+ * context is not initialized or was lost after a link reset.
+ */
+static inline int dw_edma_core_ll_cur_idx(struct dw_edma_chan *chan)
+{
+ return chan->dw->core->ll_cur_idx(chan);
+}
+
static inline u64 dw_edma_core_get_ll_paddr(struct dw_edma_chan *chan)
{
if (chan->dir == EDMA_DIR_WRITE)
diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
index abc1bd4c0be2..ba84811234e1 100644
--- a/drivers/dma/dw-edma/dw-edma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
@@ -616,6 +616,24 @@ static void dw_edma_v0_core_ch_doorbell(struct dw_edma_chan *chan)
FIELD_PREP(EDMA_V0_DOORBELL_CH_MASK, chan->id));
}

+static int dw_edma_v0_core_ll_cur_idx(struct dw_edma_chan *chan)
+{
+ u32 base, val;
+
+ val = GET_CH_32(chan->dw, chan->dir, chan->id, llp.lsb);
+ base = lower_32_bits(dw_edma_core_get_ll_paddr(chan));
+
+ /*
+ * An all-zero pointer means that the channel context is unavailable,
+ * so an LL region starting at DMA address zero is not supported.
+ */
+ if (!val && !GET_CH_32(chan->dw, chan->dir, chan->id, llp.msb))
+ return -EINVAL;
+
+ /* u32 subtraction handles a low-word wrap at a 4 GiB boundary. */
+ return (val - base) / EDMA_LL_SZ;
+}
+
/* eDMA debugfs callbacks */
static void dw_edma_v0_core_debugfs_on(struct dw_edma *dw)
{
@@ -650,6 +668,7 @@ static const struct dw_edma_core_ops dw_edma_v0_core = {
.handle_int = dw_edma_v0_core_handle_int,
.ll_data = dw_edma_v0_core_ll_data,
.ll_link = dw_edma_v0_core_ll_link,
+ .ll_cur_idx = dw_edma_v0_core_ll_cur_idx,
.ch_doorbell = dw_edma_v0_core_ch_doorbell,
.ch_enable = dw_edma_v0_core_ch_enable,
.ch_config = dw_edma_v0_core_ch_config,
diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.c b/drivers/dma/dw-edma/dw-hdma-v0-core.c
index 36ee72efcd31..2fc7f8e85b43 100644
--- a/drivers/dma/dw-edma/dw-hdma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c
@@ -384,6 +384,24 @@ static void dw_hdma_v0_core_ch_doorbell(struct dw_edma_chan *chan)
SET_CH_32(dw, chan->dir, chan->id, doorbell, HDMA_V0_DOORBELL_START);
}

+static int dw_hdma_v0_core_ll_cur_idx(struct dw_edma_chan *chan)
+{
+ u32 base, val;
+
+ val = GET_CH_32(chan->dw, chan->dir, chan->id, llp.lsb);
+ base = lower_32_bits(dw_edma_core_get_ll_paddr(chan));
+
+ /*
+ * An all-zero pointer means that the channel context is unavailable,
+ * so an LL region starting at DMA address zero is not supported.
+ */
+ if (!val && !GET_CH_32(chan->dw, chan->dir, chan->id, llp.msb))
+ return -EINVAL;
+
+ /* u32 subtraction handles a low-word wrap at a 4 GiB boundary. */
+ return (val - base) / EDMA_LL_SZ;
+}
+
/* HDMA debugfs callbacks */
static void dw_hdma_v0_core_debugfs_on(struct dw_edma *dw)
{
@@ -406,6 +424,7 @@ static const struct dw_edma_core_ops dw_hdma_v0_core = {
.non_ll_start = dw_hdma_v0_core_non_ll_start,
.ll_data = dw_hdma_v0_core_ll_data,
.ll_link = dw_hdma_v0_core_ll_link,
+ .ll_cur_idx = dw_hdma_v0_core_ll_cur_idx,
.ch_doorbell = dw_hdma_v0_core_ch_doorbell,
.ch_enable = dw_hdma_v0_core_ch_enable,
.ch_config = dw_hdma_v0_core_ch_config,
--
2.51.0