From: Claudiu Beznea Date: Tue, 26 May 2026 08:47:02 +0000 (+0300) Subject: dmaengine: sh: rz-dmac: Refactor pause/resume code X-Git-Tag: v7.2-rc1~55^2~35 X-Git-Url: http://git.ipfire.org/gitweb/?a=commitdiff_plain;h=dc86e47ca9b1021e258c366a5a9aa15d71c814a5;p=thirdparty%2Fkernel%2Flinux.git dmaengine: sh: rz-dmac: Refactor pause/resume code Subsequent patches will add suspend/resume and cyclic DMA support to the rz-dmac driver. This support needs to work on SoCs where power to most components (including DMA) is turned off during system suspend. For this, some channels (for example cyclic ones) may need to be paused and resumed manually by the DMA driver during system suspend/resume. Refactor the pause/resume support so the same code can be reused in the system suspend/resume path. Tested-by: John Madieu Signed-off-by: Claudiu Beznea Tested-by: Tommaso Merciai Link: https://patch.msgid.link/20260526084710.3491480-11-claudiu.beznea@kernel.org Signed-off-by: Vinod Koul --- diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c index 1f884ec101f80..557364443a5fd 100644 --- a/drivers/dma/sh/rz-dmac.c +++ b/drivers/dma/sh/rz-dmac.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -63,6 +64,14 @@ struct rz_dmac_desc { #define to_rz_dmac_desc(d) container_of(d, struct rz_dmac_desc, vd) +/** + * enum rz_dmac_chan_status: RZ DMAC channel status + * @RZ_DMAC_CHAN_STATUS_PAUSED: Channel is paused though DMA engine callbacks + */ +enum rz_dmac_chan_status { + RZ_DMAC_CHAN_STATUS_PAUSED, +}; + struct rz_dmac_chan { struct virt_dma_chan vc; void __iomem *ch_base; @@ -74,6 +83,8 @@ struct rz_dmac_chan { dma_addr_t src_per_address; dma_addr_t dst_per_address; + unsigned long status; + u32 chcfg; u32 chctrl; int mid_rid; @@ -491,6 +502,8 @@ static void rz_dmac_free_chan_resources(struct dma_chan *chan) channel->mid_rid = -EINVAL; } + channel->status = 0; + spin_unlock_irqrestore(&channel->vc.lock, flags); vchan_free_chan_resources(&channel->vc); @@ -589,6 +602,9 @@ static int rz_dmac_terminate_all(struct dma_chan *chan) } vchan_get_all_descriptors(&channel->vc, &head); + + channel->status = 0; + spin_unlock_irqrestore(&channel->vc.lock, flags); vchan_dma_desc_free_list(&channel->vc, &head); @@ -795,35 +811,70 @@ static enum dma_status rz_dmac_tx_status(struct dma_chan *chan, return status; } -static int rz_dmac_device_pause(struct dma_chan *chan) +static int rz_dmac_device_pause_set(struct rz_dmac_chan *channel, + unsigned long set_bitmask) { - struct rz_dmac_chan *channel = to_rz_dmac_chan(chan); + int ret = 0; u32 val; - guard(spinlock_irqsave)(&channel->vc.lock); + lockdep_assert_held(&channel->vc.lock); if (!rz_dmac_chan_is_enabled(channel)) return 0; + if (rz_dmac_chan_is_paused(channel)) + goto set_bit; + rz_dmac_ch_writel(channel, CHCTRL_SETSUS, CHCTRL, 1); - return read_poll_timeout_atomic(rz_dmac_ch_readl, val, - (val & CHSTAT_SUS), 1, 1024, - false, channel, CHSTAT, 1); + ret = read_poll_timeout_atomic(rz_dmac_ch_readl, val, + (val & CHSTAT_SUS), 1, 1024, false, + channel, CHSTAT, 1); + +set_bit: + channel->status |= set_bitmask; + + return ret; } -static int rz_dmac_device_resume(struct dma_chan *chan) +static int rz_dmac_device_pause(struct dma_chan *chan) { struct rz_dmac_chan *channel = to_rz_dmac_chan(chan); - u32 val; guard(spinlock_irqsave)(&channel->vc.lock); + return rz_dmac_device_pause_set(channel, BIT(RZ_DMAC_CHAN_STATUS_PAUSED)); +} + +static int rz_dmac_device_resume_set(struct rz_dmac_chan *channel, + unsigned long clear_bitmask) +{ + int ret = 0; + u32 val; + + lockdep_assert_held(&channel->vc.lock); + /* Do not check CHSTAT_SUS but rely on HW capabilities. */ rz_dmac_ch_writel(channel, CHCTRL_CLRSUS, CHCTRL, 1); - return read_poll_timeout_atomic(rz_dmac_ch_readl, val, - !(val & CHSTAT_SUS), 1, 1024, - false, channel, CHSTAT, 1); + ret = read_poll_timeout_atomic(rz_dmac_ch_readl, val, + !(val & CHSTAT_SUS), 1, 1024, false, + channel, CHSTAT, 1); + + channel->status &= ~clear_bitmask; + + return ret; +} + +static int rz_dmac_device_resume(struct dma_chan *chan) +{ + struct rz_dmac_chan *channel = to_rz_dmac_chan(chan); + + guard(spinlock_irqsave)(&channel->vc.lock); + + if (!(channel->status & BIT(RZ_DMAC_CHAN_STATUS_PAUSED))) + return 0; + + return rz_dmac_device_resume_set(channel, BIT(RZ_DMAC_CHAN_STATUS_PAUSED)); } /*