[PATCH v4 16/24] dmaengine: dw-edma: Use HDMA watermarks as progress events
Koichiro Den <[email protected]> Wed, 29 Jul 2026 23:30:28 +0900
| Newsgroups | org.kernel.vger.dmaengine,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
HDMA reports running LL progress through WATERMARK and channel stop through STOP. Wire watermark setup, clearing, MSI programming, and LWIE/RWIE into the core interrupt path. Record WATERMARK as progress; STOP remains a stopped event. DWC PCIe Controller Databook 6.10a, Section 7.2.1, permits recycling LLEs through the watermark LLP. Testing on a 6.30a integration found that boundary unsafe for DMAengine completion: clients released mappings while hardware still accessed them. Keep completion one entry behind the raw LLP. Once the channel has stopped, LLP points to the next entry and needs no such margin. Leave EDMA_REQ_STOP and EDMA_REQ_PAUSE to the STOP path instead of handling their watermark as normal running progress. The eDMA path is unchanged. Signed-off-by: Koichiro Den <[email protected]> --- Changes in v4: - No changes. drivers/dma/dw-edma/dw-hdma-v0-core.c | 69 +++++++++++++++++++++++---- drivers/dma/dw-edma/dw-hdma-v0-regs.h | 1 + 2 files changed, 60 insertions(+), 10 deletions(-) diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.c b/drivers/dma/dw-edma/dw-hdma-v0-core.c index 0ed4e1b8240a..bfb9f15ac245 100644 --- a/drivers/dma/dw-edma/dw-hdma-v0-core.c +++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c @@ -53,7 +53,8 @@ static u32 dw_hdma_v0_core_int_setup(struct dw_edma_chan *chan, u32 val) { val &= ~(HDMA_V0_LOCAL_ABORT_INT_EN | HDMA_V0_REMOTE_ABORT_INT_EN | HDMA_V0_LOCAL_STOP_INT_EN | HDMA_V0_REMOTE_STOP_INT_EN | - HDMA_V0_ABORT_INT_MASK | HDMA_V0_STOP_INT_MASK); + HDMA_V0_ABORT_INT_MASK | HDMA_V0_WATERMARK_INT_MASK | + HDMA_V0_STOP_INT_MASK); /* * HDMA_INT_STATUS.STOP and .ABORT are latched only when LSIE and @@ -63,10 +64,14 @@ static u32 dw_hdma_v0_core_int_setup(struct dw_edma_chan *chan, u32 val) */ val |= HDMA_V0_LOCAL_ABORT_INT_EN | HDMA_V0_LOCAL_STOP_INT_EN; - if (chan->irq_mode == DW_EDMA_CH_IRQ_REMOTE) + if (chan->irq_mode == DW_EDMA_CH_IRQ_REMOTE) { + if (!chan->non_ll) + val |= HDMA_V0_WATERMARK_INT_MASK; + val |= HDMA_V0_REMOTE_ABORT_INT_EN | HDMA_V0_REMOTE_STOP_INT_EN | HDMA_V0_ABORT_INT_MASK | HDMA_V0_STOP_INT_MASK; + } return val; } @@ -76,10 +81,12 @@ static void dw_hdma_v0_core_ch_off(struct dw_edma *dw, enum dw_edma_dir dir, u16 id) { SET_CH_32(dw, dir, id, int_setup, - HDMA_V0_STOP_INT_MASK | HDMA_V0_ABORT_INT_MASK); + HDMA_V0_STOP_INT_MASK | HDMA_V0_WATERMARK_INT_MASK | + HDMA_V0_ABORT_INT_MASK); SET_CH_32(dw, dir, id, ch_en, 0); SET_CH_32(dw, dir, id, int_clear, - HDMA_V0_STOP_INT_MASK | HDMA_V0_ABORT_INT_MASK); + HDMA_V0_STOP_INT_MASK | HDMA_V0_WATERMARK_INT_MASK | + HDMA_V0_ABORT_INT_MASK); } static void dw_hdma_v0_core_off(struct dw_edma *dw) @@ -90,18 +97,26 @@ static void dw_hdma_v0_core_off(struct dw_edma *dw) dir = EDMA_DIR_WRITE; for (id = 0; id < dw->wr_ch_cnt; id++) { SET_CH_32(dw, dir, id, int_setup, - HDMA_V0_STOP_INT_MASK | HDMA_V0_ABORT_INT_MASK); + HDMA_V0_STOP_INT_MASK | + HDMA_V0_WATERMARK_INT_MASK | + HDMA_V0_ABORT_INT_MASK); SET_CH_32(dw, dir, id, int_clear, - HDMA_V0_STOP_INT_MASK | HDMA_V0_ABORT_INT_MASK); + HDMA_V0_STOP_INT_MASK | + HDMA_V0_WATERMARK_INT_MASK | + HDMA_V0_ABORT_INT_MASK); SET_CH_32(dw, dir, id, ch_en, 0); } dir = EDMA_DIR_READ; for (id = 0; id < dw->rd_ch_cnt; id++) { SET_CH_32(dw, dir, id, int_setup, - HDMA_V0_STOP_INT_MASK | HDMA_V0_ABORT_INT_MASK); + HDMA_V0_STOP_INT_MASK | + HDMA_V0_WATERMARK_INT_MASK | + HDMA_V0_ABORT_INT_MASK); SET_CH_32(dw, dir, id, int_clear, - HDMA_V0_STOP_INT_MASK | HDMA_V0_ABORT_INT_MASK); + HDMA_V0_STOP_INT_MASK | + HDMA_V0_WATERMARK_INT_MASK | + HDMA_V0_ABORT_INT_MASK); SET_CH_32(dw, dir, id, ch_en, 0); } } @@ -158,6 +173,22 @@ static void dw_hdma_v0_core_clear_done_int(struct dw_edma_chan *chan) SET_CH_32(dw, chan->dir, chan->id, int_clear, HDMA_V0_STOP_INT_MASK); } +static void dw_hdma_v0_core_clear_watermark_int(struct dw_edma_chan *chan) +{ + struct dw_edma *dw = chan->dw; + + SET_CH_32(dw, chan->dir, chan->id, int_clear, + HDMA_V0_WATERMARK_INT_MASK); +} + +static void dw_hdma_v0_core_clear_ll_int(struct dw_edma_chan *chan) +{ + struct dw_edma *dw = chan->dw; + + SET_CH_32(dw, chan->dir, chan->id, int_clear, + HDMA_V0_STOP_INT_MASK | HDMA_V0_WATERMARK_INT_MASK); +} + static void dw_hdma_v0_core_clear_abort_int(struct dw_edma_chan *chan) { struct dw_edma *dw = chan->dw; @@ -214,6 +245,12 @@ dw_hdma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir, dw_hdma_v0_core_clear_done_int(chan); } + if (FIELD_GET(HDMA_V0_WATERMARK_INT_MASK, val)) { + if (!(events & DW_EDMA_IRQ_STOP)) + events |= DW_EDMA_IRQ_PROGRESS; + dw_hdma_v0_core_clear_watermark_int(chan); + } + if (FIELD_GET(HDMA_V0_ABORT_INT_MASK, val)) { events |= DW_EDMA_IRQ_ABORT; dw_edma_abort_event_mark(chan); @@ -282,7 +319,7 @@ static void dw_hdma_v0_core_ch_enable(struct dw_edma_chan *chan) /* Enable engine */ SET_CH_32(dw, chan->dir, chan->id, ch_en, BIT(0)); - /* Interrupt unmask - stop, abort */ + /* Interrupt unmask - stop, watermark, abort */ tmp = GET_CH_32(dw, chan->dir, chan->id, int_setup); tmp = dw_hdma_v0_core_int_setup(chan, tmp); SET_CH_32(dw, chan->dir, chan->id, int_setup, tmp); @@ -343,6 +380,11 @@ static void dw_hdma_v0_core_ch_config(struct dw_edma_chan *chan) /* MSI done addr - low, high */ SET_CH_32(dw, chan->dir, chan->id, msi_stop.lsb, chan->msi.address_lo); SET_CH_32(dw, chan->dir, chan->id, msi_stop.msb, chan->msi.address_hi); + /* MSI watermark addr - low, high */ + SET_CH_32(dw, chan->dir, chan->id, msi_watermark.lsb, + chan->msi.address_lo); + SET_CH_32(dw, chan->dir, chan->id, msi_watermark.msb, + chan->msi.address_hi); /* MSI abort addr - low, high */ SET_CH_32(dw, chan->dir, chan->id, msi_abort.lsb, chan->msi.address_lo); SET_CH_32(dw, chan->dir, chan->id, msi_abort.msb, chan->msi.address_hi); @@ -362,6 +404,13 @@ dw_hdma_v0_core_ll_data(struct dw_edma_chan *chan, struct dw_edma_burst *burst, if (cb) control |= DW_HDMA_V0_CB; + if (irq) { + control |= DW_HDMA_V0_LWIE; + + if (chan->irq_mode == DW_EDMA_CH_IRQ_REMOTE) + control |= DW_HDMA_V0_RWIE; + } + dw_hdma_v0_write_ll_data(chan, idx, control, burst->sz, burst->sar, burst->dar); } @@ -443,7 +492,7 @@ static const struct dw_edma_core_ops dw_hdma_v0_core = { .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_clear = dw_hdma_v0_core_clear_done_int, + .ll_irq_clear = dw_hdma_v0_core_clear_ll_int, .ch_doorbell = dw_hdma_v0_core_ch_doorbell, .ch_enable = dw_hdma_v0_core_ch_enable, .ch_config = dw_hdma_v0_core_ch_config, diff --git a/drivers/dma/dw-edma/dw-hdma-v0-regs.h b/drivers/dma/dw-edma/dw-hdma-v0-regs.h index 2124c162a62f..60dc054adafa 100644 --- a/drivers/dma/dw-edma/dw-hdma-v0-regs.h +++ b/drivers/dma/dw-edma/dw-hdma-v0-regs.h @@ -18,6 +18,7 @@ #define HDMA_V0_LOCAL_STOP_INT_EN BIT(4) #define HDMA_V0_REMOTE_STOP_INT_EN BIT(3) #define HDMA_V0_ABORT_INT_MASK BIT(2) +#define HDMA_V0_WATERMARK_INT_MASK BIT(1) #define HDMA_V0_STOP_INT_MASK BIT(0) #define HDMA_V0_LINKLIST_EN BIT(0) #define HDMA_V0_CONSUMER_CYCLE_STAT BIT(1) -- 2.51.0