[PATCH v4 01/24] dmaengine: dw-edma: Add dw_edma_core_ll_cur_idx() to get current LL entry index
Koichiro Den <[email protected]> Wed, 29 Jul 2026 23:30:13 +0900
| Newsgroups | org.kernel.vger.dmaengine,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
From: Frank Li <[email protected]> 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 <[email protected]> Reviewed-by: Frank Li <[email protected]> Co-developed-by: Koichiro Den <[email protected]> Signed-off-by: Koichiro Den <[email protected]> --- Changes in v4: - Document that LL regions must not cross a 4 GiB boundary instead of claiming low-word wrap support. (Sashiko) 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 +++++++++++++++++++ include/linux/dma/edma.h | 2 +- 4 files changed, 49 insertions(+), 1 deletion(-) 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..7b9a95ecedbb 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)); + + /* + * LL regions stay within one 4 GiB address window. If the low word is + * zero, use the high word to distinguish a nonzero boundary address + * from an unprogrammed all-zero context. + */ + if (!val && !GET_CH_32(chan->dw, chan->dir, chan->id, llp.msb)) + return -EINVAL; + + 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..d849220f3b5d 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)); + + /* + * LL regions stay within one 4 GiB address window. If the low word is + * zero, use the high word to distinguish a nonzero boundary address + * from an unprogrammed all-zero context. + */ + if (!val && !GET_CH_32(chan->dw, chan->dir, chan->id, llp.msb)) + return -EINVAL; + + 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, diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h index 3c8e2ef9dee0..a4e5aab8fddc 100644 --- a/include/linux/dma/edma.h +++ b/include/linux/dma/edma.h @@ -130,7 +130,7 @@ struct dw_edma_chip { u16 ll_wr_cnt; u16 ll_rd_cnt; - /* link list address */ + /* Linked-list regions must not cross a 4 GiB address boundary. */ struct dw_edma_region ll_region_wr[HDMA_MAX_WR_CH]; struct dw_edma_region ll_region_rd[HDMA_MAX_RD_CH]; -- 2.51.0