[PATCH 12/17] dmaengine: dw-edma: Clear LL data entries on reset
From: Koichiro Den
Date: Mon Jun 15 2026 - 11:44:23 EST
The LL memory has always been reused, but the circular LL ring makes old
data entries reachable again after the software state is reset.
Resetting ll_head, ll_end and cb is not enough if a data entry from an
earlier lap still carries a control word with a CB value matching the
next lap.
Add a core operation to clear a data entry control word, and scrub all
data entries when the LL ring is reset.
Only the control word needs to be cleared. The reset path starts the
next ring cycle with CB=1, so a zeroed control word leaves stale data
entries with CB cleared and prevents them from being consumed before
software rewrites the slot with the current CB.
Signed-off-by: Koichiro Den <den@xxxxxxxxxxxxx>
---
drivers/dma/dw-edma/dw-edma-core.c | 5 +++++
drivers/dma/dw-edma/dw-edma-core.h | 6 ++++++
drivers/dma/dw-edma/dw-edma-v0-core.c | 16 ++++++++++++++++
drivers/dma/dw-edma/dw-hdma-v0-core.c | 16 ++++++++++++++++
4 files changed, 43 insertions(+)
diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index a289d8f8cc17..e76d8e0c6fa8 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -63,9 +63,14 @@ static void vchan_free_desc(struct virt_dma_desc *vdesc)
static void dw_edma_core_reset_ll(struct dw_edma_chan *chan)
{
+ u32 i;
+
chan->ll_head = 0;
chan->ll_end = 0;
chan->ll_done = 0;
+ /* Drop stale CB bits before reusing the circular LL ring. */
+ for (i = 0; i < chan->ll_max - 1; i++)
+ dw_edma_core_ll_clear(chan, i);
chan->cb = true;
dw_edma_core_ll_link(chan, chan->ll_max - 1, chan->cb,
diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
index 9bd0a5f2f08b..1252d264c1ca 100644
--- a/drivers/dma/dw-edma/dw-edma-core.h
+++ b/drivers/dma/dw-edma/dw-edma-core.h
@@ -150,6 +150,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);
+ void (*ll_clear)(struct dw_edma_chan *chan, u32 idx);
int (*ll_cur_idx)(struct dw_edma_chan *chan);
bool (*ll_irq)(struct dw_edma_desc *desc, u32 i, u32 free);
void (*ch_doorbell)(struct dw_edma_chan *chan);
@@ -255,6 +256,11 @@ dw_edma_core_ll_link(struct dw_edma_chan *chan, u32 idx, bool cb, u64 addr)
chan->dw->core->ll_link(chan, idx, cb, addr);
}
+static inline void dw_edma_core_ll_clear(struct dw_edma_chan *chan, u32 idx)
+{
+ chan->dw->core->ll_clear(chan, idx);
+}
+
static inline void dw_edma_core_ch_doorbell(struct dw_edma_chan *chan)
{
chan->dw->core->ch_doorbell(chan);
diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
index dfe0483896d3..265eefbf2ead 100644
--- a/drivers/dma/dw-edma/dw-edma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
@@ -495,6 +495,21 @@ dw_edma_v0_core_ll_link(struct dw_edma_chan *chan, u32 idx, bool cb, u64 addr)
dw_edma_v0_write_ll_link(chan, idx, control, chan->ll_region.paddr);
}
+static void dw_edma_v0_core_ll_clear(struct dw_edma_chan *chan, u32 idx)
+{
+ ptrdiff_t ofs = idx * sizeof(struct dw_edma_v0_lli);
+
+ if (chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
+ struct dw_edma_v0_lli *lli = chan->ll_region.vaddr.mem + ofs;
+
+ lli->control = 0;
+ } else {
+ struct dw_edma_v0_lli __iomem *lli = chan->ll_region.vaddr.io + ofs;
+
+ writel(0, &lli->control);
+ }
+}
+
static void dw_edma_v0_core_ch_doorbell(struct dw_edma_chan *chan)
{
struct dw_edma *dw = chan->dw;
@@ -544,6 +559,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_clear = dw_edma_v0_core_ll_clear,
.ll_cur_idx = dw_edma_v0_core_ll_cur_idx,
.ll_irq = dw_edma_v0_core_ll_irq,
.ch_doorbell = dw_edma_v0_core_ch_doorbell,
diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.c b/drivers/dma/dw-edma/dw-hdma-v0-core.c
index 9f5b11350f23..ad5e8201eb63 100644
--- a/drivers/dma/dw-edma/dw-hdma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c
@@ -319,6 +319,21 @@ dw_hdma_v0_core_ll_link(struct dw_edma_chan *chan, u32 idx, bool cb, u64 addr)
dw_hdma_v0_write_ll_link(chan, idx, control, chan->ll_region.paddr);
}
+static void dw_hdma_v0_core_ll_clear(struct dw_edma_chan *chan, u32 idx)
+{
+ ptrdiff_t ofs = idx * sizeof(struct dw_hdma_v0_lli);
+
+ if (chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
+ struct dw_hdma_v0_lli *lli = chan->ll_region.vaddr.mem + ofs;
+
+ lli->control = 0;
+ } else {
+ struct dw_hdma_v0_lli __iomem *lli = chan->ll_region.vaddr.io + ofs;
+
+ writel(0, &lli->control);
+ }
+}
+
static void dw_hdma_v0_core_ch_doorbell(struct dw_edma_chan *chan)
{
struct dw_edma *dw = chan->dw;
@@ -385,6 +400,7 @@ static const struct dw_edma_core_ops dw_hdma_v0_core = {
.handle_int = dw_hdma_v0_core_handle_int,
.ll_data = dw_hdma_v0_core_ll_data,
.ll_link = dw_hdma_v0_core_ll_link,
+ .ll_clear = dw_hdma_v0_core_ll_clear,
.ll_cur_idx = dw_hdma_v0_core_ll_cur_idx,
.ll_irq = dw_hdma_v0_core_ll_irq,
.ch_doorbell = dw_hdma_v0_core_ch_doorbell,
--
2.51.0