Re: [RFC PATCH v3 22/35] dmaengine: dw-edma: Serialize RMW on shared interrupt registers
From: Frank Li <Frank.li@nxp.com>
Date: 2025-12-19 14:39:51
Also in:
dmaengine, linux-pci, linux-renesas-soc, lkml
On Thu, Dec 18, 2025 at 12:15:56AM +0900, Koichiro Den wrote:
The per-direction int_mask and linked_list_err_en registers are shared between all channels. Updating them requires a read-modify-write sequence, which can lose concurrent updates when multiple channels are started in parallel. This may leave interrupts masked and stall transfers under high load. Protect the RMW sequences with dw->lock. Signed-off-by: Koichiro Den <redacted> ---
I just posted a similar patch https://lore.kernel.org/imx/20251212-edma_ll-v1-1-fc863d9f5ca3@nxp.com/ (local) It change some method and I am working on add new request during dma engine running. At least, you can base on above thread. Frank
quoted hunk ↗ jump to hunk
drivers/dma/dw-edma/dw-edma-core.h | 3 ++- drivers/dma/dw-edma/dw-edma-v0-core.c | 13 ++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-)diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h index f652d2e38843..d393976a8bfc 100644 --- a/drivers/dma/dw-edma/dw-edma-core.h +++ b/drivers/dma/dw-edma/dw-edma-core.h@@ -118,7 +118,8 @@ struct dw_edma { struct dw_edma_chan *chan; - raw_spinlock_t lock; /* Only for legacy */ + /* For legacy + shared regs RMW among channels */ + raw_spinlock_t lock; struct dw_edma_chip *chip;diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c index 42a254eb9379..770b011ba3e4 100644 --- a/drivers/dma/dw-edma/dw-edma-v0-core.c +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c@@ -369,7 +369,8 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) { struct dw_edma_chan *chan = chunk->chan; struct dw_edma *dw = chan->dw; - u32 tmp; + unsigned long flags; + u32 tmp, orig; dw_edma_v0_core_write_chunk(chunk);@@ -413,7 +414,9 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) } } /* Interrupt mask/unmask - done, abort */ + raw_spin_lock_irqsave(&dw->lock, flags); tmp = GET_RW_32(dw, chan->dir, int_mask); + orig = tmp; if (chan->irq_mode == DW_EDMA_CH_IRQ_REMOTE) { tmp |= FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id)); tmp |= FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id));@@ -421,11 +424,15 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) tmp &= ~FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id)); tmp &= ~FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id)); } - SET_RW_32(dw, chan->dir, int_mask, tmp); + if (tmp != orig) + SET_RW_32(dw, chan->dir, int_mask, tmp); /* Linked list error */ tmp = GET_RW_32(dw, chan->dir, linked_list_err_en); + orig = tmp; tmp |= FIELD_PREP(EDMA_V0_LINKED_LIST_ERR_MASK, BIT(chan->id)); - SET_RW_32(dw, chan->dir, linked_list_err_en, tmp); + if (tmp != orig) + SET_RW_32(dw, chan->dir, linked_list_err_en, tmp); + raw_spin_unlock_irqrestore(&dw->lock, flags); /* Channel control */ SET_CH_32(dw, chan->dir, chan->id, ch_control1, (DW_EDMA_V0_CCS | DW_EDMA_V0_LLE)); --2.51.0