* [PATCH v5 0/6] dmaengine: sh: rz-dmac: Add tx_status and pause/resume support
@ 2025-12-17 13:52 Claudiu
2025-12-17 13:52 ` [PATCH v5 1/6] dmaengine: sh: rz-dmac: Protect the driver specific lists Claudiu
` (5 more replies)
0 siblings, 6 replies; 11+ messages in thread
From: Claudiu @ 2025-12-17 13:52 UTC (permalink / raw)
To: vkoul, fabrizio.castro.jz, biju.das.jz, geert+renesas,
prabhakar.mahadev-lad.rj
Cc: claudiu.beznea, dmaengine, linux-kernel, Claudiu Beznea
From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
Hi,
Series adds tx_status and pause/resume support for the rz-dmac driver.
Along with it were added fixes identified while working on the above
mentioned enhancements.
Previous versions were addressed by Biju. The previous versions were
posted here:
v4: https://lore.kernel.org/all/20240628151728.84470-1-biju.das.jz@bp.renesas.com/
v3: https://lore.kernel.org/all/20230412152445.117439-1-biju.das.jz@bp.renesas.com/
v2: https://lore.kernel.org/all/20230405140842.201883-1-biju.das.jz@bp.renesas.com/
v1: https://lore.kernel.org/all/20230324094957.115071-1-biju.das.jz@bp.renesas.com/
Changes in v5:
- added patches
-- dmaengine: sh: rz-dmac: Add rz_dmac_invalidate_lmdesc()
-- dmaengine: sh: rz-dmac: Protect the driver specific lists
-- dmaengine: sh: rz-dmac: Move all CHCTRL updates under spinlock
-- dmaengine: sh: rz-dmac: Drop unnecessary local_irq_save() call
-- dmaengine: sh: rz-dmac: Add device_{pause,resume}() callbacks
-- dmaengine: sh: rz-dmac: Add rz_dmac_invalidate_lmdesc()
- for pause/resume used the DMA controller support to pause/resume
transfers compared with previous versions
- adjusted patches:
-- dmaengine: sh: rz-dmac: Add device_tx_status() callback
Thank you,
Claudiu
Biju Das (2):
dmaengine: sh: rz-dmac: Add rz_dmac_invalidate_lmdesc()
dmaengine: sh: rz-dmac: Add device_tx_status() callback
Claudiu Beznea (4):
dmaengine: sh: rz-dmac: Protect the driver specific lists
dmaengine: sh: rz-dmac: Move all CHCTRL updates under spinlock
dmaengine: sh: rz-dmac: Drop unnecessary local_irq_save() call
dmaengine: sh: rz-dmac: Add device_{pause,resume}() callbacks
drivers/dma/sh/rz-dmac.c | 265 ++++++++++++++++++++++++++++++++-------
1 file changed, 222 insertions(+), 43 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v5 1/6] dmaengine: sh: rz-dmac: Protect the driver specific lists
2025-12-17 13:52 [PATCH v5 0/6] dmaengine: sh: rz-dmac: Add tx_status and pause/resume support Claudiu
@ 2025-12-17 13:52 ` Claudiu
2025-12-17 13:52 ` [PATCH v5 2/6] dmaengine: sh: rz-dmac: Move all CHCTRL updates under spinlock Claudiu
` (4 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: Claudiu @ 2025-12-17 13:52 UTC (permalink / raw)
To: vkoul, fabrizio.castro.jz, biju.das.jz, geert+renesas,
prabhakar.mahadev-lad.rj
Cc: claudiu.beznea, dmaengine, linux-kernel, Claudiu Beznea, stable
From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
The driver lists (ld_free, ld_queue) are used in
rz_dmac_free_chan_resources(), rz_dmac_terminate_all(),
rz_dmac_issue_pending(), and rz_dmac_irq_handler_thread(), all under
the virtual channel lock. Take the same lock in rz_dmac_prep_slave_sg()
and rz_dmac_prep_dma_memcpy() as well to avoid concurrency issues, since
these functions also check whether the lists are empty and update or
remove list entries.
Fixes: 5000d37042a6 ("dmaengine: sh: Add DMAC driver for RZ/G2L SoC")
Cc: stable@vger.kernel.org
Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
Changes in v5:
- none, this patch is new
drivers/dma/sh/rz-dmac.c | 57 ++++++++++++++++++++++------------------
1 file changed, 32 insertions(+), 25 deletions(-)
diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c
index 9e5f088355e2..c8e3d9f77b8a 100644
--- a/drivers/dma/sh/rz-dmac.c
+++ b/drivers/dma/sh/rz-dmac.c
@@ -10,6 +10,7 @@
*/
#include <linux/bitfield.h>
+#include <linux/cleanup.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/interrupt.h>
@@ -448,6 +449,7 @@ static int rz_dmac_alloc_chan_resources(struct dma_chan *chan)
if (!desc)
break;
+ /* No need to lock. This is called only for the 1st client. */
list_add_tail(&desc->node, &channel->ld_free);
channel->descs_allocated++;
}
@@ -503,18 +505,21 @@ rz_dmac_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
dev_dbg(dmac->dev, "%s channel: %d src=0x%pad dst=0x%pad len=%zu\n",
__func__, channel->index, &src, &dest, len);
- if (list_empty(&channel->ld_free))
- return NULL;
+ scoped_guard(spinlock_irqsave, &channel->vc.lock) {
+ if (list_empty(&channel->ld_free))
+ return NULL;
+
+ desc = list_first_entry(&channel->ld_free, struct rz_dmac_desc, node);
- desc = list_first_entry(&channel->ld_free, struct rz_dmac_desc, node);
+ desc->type = RZ_DMAC_DESC_MEMCPY;
+ desc->src = src;
+ desc->dest = dest;
+ desc->len = len;
+ desc->direction = DMA_MEM_TO_MEM;
- desc->type = RZ_DMAC_DESC_MEMCPY;
- desc->src = src;
- desc->dest = dest;
- desc->len = len;
- desc->direction = DMA_MEM_TO_MEM;
+ list_move_tail(channel->ld_free.next, &channel->ld_queue);
+ }
- list_move_tail(channel->ld_free.next, &channel->ld_queue);
return vchan_tx_prep(&channel->vc, &desc->vd, flags);
}
@@ -530,27 +535,29 @@ rz_dmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
int dma_length = 0;
int i = 0;
- if (list_empty(&channel->ld_free))
- return NULL;
+ scoped_guard(spinlock_irqsave, &channel->vc.lock) {
+ if (list_empty(&channel->ld_free))
+ return NULL;
- desc = list_first_entry(&channel->ld_free, struct rz_dmac_desc, node);
+ desc = list_first_entry(&channel->ld_free, struct rz_dmac_desc, node);
- for_each_sg(sgl, sg, sg_len, i) {
- dma_length += sg_dma_len(sg);
- }
+ for_each_sg(sgl, sg, sg_len, i)
+ dma_length += sg_dma_len(sg);
- desc->type = RZ_DMAC_DESC_SLAVE_SG;
- desc->sg = sgl;
- desc->sgcount = sg_len;
- desc->len = dma_length;
- desc->direction = direction;
+ desc->type = RZ_DMAC_DESC_SLAVE_SG;
+ desc->sg = sgl;
+ desc->sgcount = sg_len;
+ desc->len = dma_length;
+ desc->direction = direction;
- if (direction == DMA_DEV_TO_MEM)
- desc->src = channel->src_per_address;
- else
- desc->dest = channel->dst_per_address;
+ if (direction == DMA_DEV_TO_MEM)
+ desc->src = channel->src_per_address;
+ else
+ desc->dest = channel->dst_per_address;
+
+ list_move_tail(channel->ld_free.next, &channel->ld_queue);
+ }
- list_move_tail(channel->ld_free.next, &channel->ld_queue);
return vchan_tx_prep(&channel->vc, &desc->vd, flags);
}
--
2.43.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v5 2/6] dmaengine: sh: rz-dmac: Move all CHCTRL updates under spinlock
2025-12-17 13:52 [PATCH v5 0/6] dmaengine: sh: rz-dmac: Add tx_status and pause/resume support Claudiu
2025-12-17 13:52 ` [PATCH v5 1/6] dmaengine: sh: rz-dmac: Protect the driver specific lists Claudiu
@ 2025-12-17 13:52 ` Claudiu
2025-12-17 16:16 ` Biju Das
2025-12-17 13:52 ` [PATCH v5 3/6] dmaengine: sh: rz-dmac: Drop unnecessary local_irq_save() call Claudiu
` (3 subsequent siblings)
5 siblings, 1 reply; 11+ messages in thread
From: Claudiu @ 2025-12-17 13:52 UTC (permalink / raw)
To: vkoul, fabrizio.castro.jz, biju.das.jz, geert+renesas,
prabhakar.mahadev-lad.rj
Cc: claudiu.beznea, dmaengine, linux-kernel, Claudiu Beznea, stable
From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
Both rz_dmac_disable_hw() and rz_dmac_irq_handle_channel() update the
CHCTRL registers. To avoid concurrency issues when updating these
registers, take the virtual channel lock. All other CHCTRL updates were
already protected by the same lock.
Previously, rz_dmac_disable_hw() disabled and re-enabled local IRQs, before
accessing CHCTRL registers but this does not ensure race-free access.
Remove the local IRQ disable/enable code as well.
Fixes: 5000d37042a6 ("dmaengine: sh: Add DMAC driver for RZ/G2L SoC")
Cc: stable@vger.kernel.org
Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
Changes in v5:
- none, this patch is new
drivers/dma/sh/rz-dmac.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c
index c8e3d9f77b8a..c013bf30fa5e 100644
--- a/drivers/dma/sh/rz-dmac.c
+++ b/drivers/dma/sh/rz-dmac.c
@@ -298,13 +298,10 @@ static void rz_dmac_disable_hw(struct rz_dmac_chan *channel)
{
struct dma_chan *chan = &channel->vc.chan;
struct rz_dmac *dmac = to_rz_dmac(chan->device);
- unsigned long flags;
dev_dbg(dmac->dev, "%s channel %d\n", __func__, channel->index);
- local_irq_save(flags);
rz_dmac_ch_writel(channel, CHCTRL_DEFAULT, CHCTRL, 1);
- local_irq_restore(flags);
}
static void rz_dmac_set_dmars_register(struct rz_dmac *dmac, int nr, u32 dmars)
@@ -569,8 +566,8 @@ static int rz_dmac_terminate_all(struct dma_chan *chan)
unsigned int i;
LIST_HEAD(head);
- rz_dmac_disable_hw(channel);
spin_lock_irqsave(&channel->vc.lock, flags);
+ rz_dmac_disable_hw(channel);
for (i = 0; i < DMAC_NR_LMDESC; i++)
lmdesc[i].header = 0;
@@ -721,6 +718,8 @@ static irqreturn_t rz_dmac_irq_handler(int irq, void *dev_id)
{
struct rz_dmac_chan *channel = dev_id;
+ guard(spinlock)(&channel->vc.lock);
+
if (channel) {
rz_dmac_irq_handle_channel(channel);
return IRQ_WAKE_THREAD;
--
2.43.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v5 3/6] dmaengine: sh: rz-dmac: Drop unnecessary local_irq_save() call
2025-12-17 13:52 [PATCH v5 0/6] dmaengine: sh: rz-dmac: Add tx_status and pause/resume support Claudiu
2025-12-17 13:52 ` [PATCH v5 1/6] dmaengine: sh: rz-dmac: Protect the driver specific lists Claudiu
2025-12-17 13:52 ` [PATCH v5 2/6] dmaengine: sh: rz-dmac: Move all CHCTRL updates under spinlock Claudiu
@ 2025-12-17 13:52 ` Claudiu
2025-12-17 13:52 ` [PATCH v5 4/6] dmaengine: sh: rz-dmac: Add rz_dmac_invalidate_lmdesc() Claudiu
` (2 subsequent siblings)
5 siblings, 0 replies; 11+ messages in thread
From: Claudiu @ 2025-12-17 13:52 UTC (permalink / raw)
To: vkoul, fabrizio.castro.jz, biju.das.jz, geert+renesas,
prabhakar.mahadev-lad.rj
Cc: claudiu.beznea, dmaengine, linux-kernel, Claudiu Beznea
From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
rz_dmac_enable_hw() calls local_irq_save()/local_irq_restore(), but
this is not needed because the callers of rz_dmac_enable_hw() already
protect the critical section using spin_lock_irqsave()/spin_lock_irqrestore().
Remove the local_irq_save()/local_irq_restore() calls.
Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
Changes in v5:
- none, this patch is new
drivers/dma/sh/rz-dmac.c | 5 -----
1 file changed, 5 deletions(-)
diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c
index c013bf30fa5e..ae8a4bd9d7fa 100644
--- a/drivers/dma/sh/rz-dmac.c
+++ b/drivers/dma/sh/rz-dmac.c
@@ -267,15 +267,12 @@ static void rz_dmac_enable_hw(struct rz_dmac_chan *channel)
{
struct dma_chan *chan = &channel->vc.chan;
struct rz_dmac *dmac = to_rz_dmac(chan->device);
- unsigned long flags;
u32 nxla;
u32 chctrl;
u32 chstat;
dev_dbg(dmac->dev, "%s channel %d\n", __func__, channel->index);
- local_irq_save(flags);
-
rz_dmac_lmdesc_recycle(channel);
nxla = channel->lmdesc.base_dma +
@@ -290,8 +287,6 @@ static void rz_dmac_enable_hw(struct rz_dmac_chan *channel)
rz_dmac_ch_writel(channel, CHCTRL_SWRST, CHCTRL, 1);
rz_dmac_ch_writel(channel, chctrl, CHCTRL, 1);
}
-
- local_irq_restore(flags);
}
static void rz_dmac_disable_hw(struct rz_dmac_chan *channel)
--
2.43.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v5 4/6] dmaengine: sh: rz-dmac: Add rz_dmac_invalidate_lmdesc()
2025-12-17 13:52 [PATCH v5 0/6] dmaengine: sh: rz-dmac: Add tx_status and pause/resume support Claudiu
` (2 preceding siblings ...)
2025-12-17 13:52 ` [PATCH v5 3/6] dmaengine: sh: rz-dmac: Drop unnecessary local_irq_save() call Claudiu
@ 2025-12-17 13:52 ` Claudiu
2025-12-17 13:52 ` [PATCH v5 5/6] dmaengine: sh: rz-dmac: Add device_tx_status() callback Claudiu
2025-12-17 13:52 ` [PATCH v5 6/6] dmaengine: sh: rz-dmac: Add device_{pause,resume}() callbacks Claudiu
5 siblings, 0 replies; 11+ messages in thread
From: Claudiu @ 2025-12-17 13:52 UTC (permalink / raw)
To: vkoul, fabrizio.castro.jz, biju.das.jz, geert+renesas,
prabhakar.mahadev-lad.rj
Cc: claudiu.beznea, dmaengine, linux-kernel, Claudiu Beznea
From: Biju Das <biju.das.jz@bp.renesas.com>
Add rz_dmac_invalidate_lmdesc() so that the same code can be shared
between rz_dmac_terminate_all() and rz_dmac_free_chan_resources().
Based on a patch in the BSP by Long Luu <long.luu.ur@renesas.com>.
Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
[claudiu.beznea: adjusted the commit description; defined the lmdesc
inside the for block to have more compact code]
Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
Changes in v5:
- adjusted the commit description
- defined the lmdesc inside the for block
drivers/dma/sh/rz-dmac.c | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c
index ae8a4bd9d7fa..bb5677f5a318 100644
--- a/drivers/dma/sh/rz-dmac.c
+++ b/drivers/dma/sh/rz-dmac.c
@@ -250,6 +250,13 @@ static void rz_lmdesc_setup(struct rz_dmac_chan *channel,
* Descriptors preparation
*/
+static void rz_dmac_invalidate_lmdesc(struct rz_dmac_chan *channel)
+{
+ for (struct rz_lmdesc *lmdesc = channel->lmdesc.base;
+ lmdesc < channel->lmdesc.base + DMAC_NR_LMDESC; lmdesc++)
+ lmdesc->header = 0;
+}
+
static void rz_dmac_lmdesc_recycle(struct rz_dmac_chan *channel)
{
struct rz_lmdesc *lmdesc = channel->lmdesc.head;
@@ -456,15 +463,12 @@ static void rz_dmac_free_chan_resources(struct dma_chan *chan)
{
struct rz_dmac_chan *channel = to_rz_dmac_chan(chan);
struct rz_dmac *dmac = to_rz_dmac(chan->device);
- struct rz_lmdesc *lmdesc = channel->lmdesc.base;
struct rz_dmac_desc *desc, *_desc;
unsigned long flags;
- unsigned int i;
spin_lock_irqsave(&channel->vc.lock, flags);
- for (i = 0; i < DMAC_NR_LMDESC; i++)
- lmdesc[i].header = 0;
+ rz_dmac_invalidate_lmdesc(channel);
rz_dmac_disable_hw(channel);
list_splice_tail_init(&channel->ld_active, &channel->ld_free);
@@ -556,15 +560,12 @@ rz_dmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
static int rz_dmac_terminate_all(struct dma_chan *chan)
{
struct rz_dmac_chan *channel = to_rz_dmac_chan(chan);
- struct rz_lmdesc *lmdesc = channel->lmdesc.base;
unsigned long flags;
- unsigned int i;
LIST_HEAD(head);
spin_lock_irqsave(&channel->vc.lock, flags);
rz_dmac_disable_hw(channel);
- for (i = 0; i < DMAC_NR_LMDESC; i++)
- lmdesc[i].header = 0;
+ rz_dmac_invalidate_lmdesc(channel);
list_splice_tail_init(&channel->ld_active, &channel->ld_free);
list_splice_tail_init(&channel->ld_queue, &channel->ld_free);
--
2.43.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v5 5/6] dmaengine: sh: rz-dmac: Add device_tx_status() callback
2025-12-17 13:52 [PATCH v5 0/6] dmaengine: sh: rz-dmac: Add tx_status and pause/resume support Claudiu
` (3 preceding siblings ...)
2025-12-17 13:52 ` [PATCH v5 4/6] dmaengine: sh: rz-dmac: Add rz_dmac_invalidate_lmdesc() Claudiu
@ 2025-12-17 13:52 ` Claudiu
2025-12-17 13:52 ` [PATCH v5 6/6] dmaengine: sh: rz-dmac: Add device_{pause,resume}() callbacks Claudiu
5 siblings, 0 replies; 11+ messages in thread
From: Claudiu @ 2025-12-17 13:52 UTC (permalink / raw)
To: vkoul, fabrizio.castro.jz, biju.das.jz, geert+renesas,
prabhakar.mahadev-lad.rj
Cc: claudiu.beznea, dmaengine, linux-kernel, Claudiu Beznea
From: Biju Das <biju.das.jz@bp.renesas.com>
Add support for device_tx_status() callback as it is needed for
RZ/G2L SCIFA driver.
Based on a patch in the BSP similar to rcar-dmac by
Long Luu <long.luu.ur@renesas.com>.
Signed-off-by: Biju Das <biju.das.jz@bp.renesas.com>
[claudiu.beznea:
- post-increment lmdesc in rz_dmac_get_next_lmdesc() to allow the next
pointer to advance
- use 'lmdesc->nxla != crla' comparison instead of
'!(lmdesc->nxla == crla)' in rz_dmac_calculate_residue_bytes_in_vd()
- in rz_dmac_calculate_residue_bytes_in_vd() use '++i >= DMAC_NR_LMDESC'
to verify if the full lmdesc list was checked
- drop rz_dmac_calculate_total_bytes_in_vd() and use desc->len instead
- re-arranged comments so they span fewer lines and are wrapped to ~80
characters
- use u32 for the residue value and the functions returning it
- use u32 for the variables storing register values
- fixed typos]
Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
Changes in v5:
- post-increment lmdesc in rz_dmac_get_next_lmdesc() to allow the next
pointer to advance
- use 'lmdesc->nxla != crla' comparison instead of
'!(lmdesc->nxla == crla)' in rz_dmac_calculate_residue_bytes_in_vd()
- in rz_dmac_calculate_residue_bytes_in_vd() use '++i >= DMAC_NR_LMDESC'
to verify if the full lmdesc list was checked
- drop rz_dmac_calculate_total_bytes_in_vd() and use desc->len instead
- re-arranged comments so they span fewer lines and are wrapped to ~80
characters
- use u32 for the residue value and the functions returning it
- use u32 for the variables storing register values
- fixed typos
drivers/dma/sh/rz-dmac.c | 144 ++++++++++++++++++++++++++++++++++++++-
1 file changed, 143 insertions(+), 1 deletion(-)
diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c
index bb5677f5a318..c3035b94ef2c 100644
--- a/drivers/dma/sh/rz-dmac.c
+++ b/drivers/dma/sh/rz-dmac.c
@@ -119,10 +119,12 @@ struct rz_dmac {
* Registers
*/
+#define CRTB 0x0020
#define CHSTAT 0x0024
#define CHCTRL 0x0028
#define CHCFG 0x002c
#define NXLA 0x0038
+#define CRLA 0x003c
#define DCTRL 0x0000
@@ -685,6 +687,146 @@ static void rz_dmac_device_synchronize(struct dma_chan *chan)
}
}
+static struct rz_lmdesc *
+rz_dmac_get_next_lmdesc(struct rz_lmdesc *base, struct rz_lmdesc *lmdesc)
+{
+ struct rz_lmdesc *next = ++lmdesc;
+
+ if (next >= base + DMAC_NR_LMDESC)
+ next = base;
+
+ return next;
+}
+
+static u32 rz_dmac_calculate_residue_bytes_in_vd(struct rz_dmac_chan *channel)
+{
+ struct rz_lmdesc *lmdesc = channel->lmdesc.head;
+ struct dma_chan *chan = &channel->vc.chan;
+ struct rz_dmac *dmac = to_rz_dmac(chan->device);
+ u32 residue = 0, crla, i = 0;
+
+ crla = rz_dmac_ch_readl(channel, CRLA, 1);
+ while (lmdesc->nxla != crla) {
+ lmdesc = rz_dmac_get_next_lmdesc(channel->lmdesc.base, lmdesc);
+ if (++i >= DMAC_NR_LMDESC)
+ return 0;
+ }
+
+ /* Calculate residue from next lmdesc to end of virtual desc */
+ while (lmdesc->chcfg & CHCFG_DEM) {
+ residue += lmdesc->tb;
+ lmdesc = rz_dmac_get_next_lmdesc(channel->lmdesc.base, lmdesc);
+ }
+
+ dev_dbg(dmac->dev, "%s: VD residue is %u\n", __func__, residue);
+
+ return residue;
+}
+
+static u32 rz_dmac_chan_get_residue(struct rz_dmac_chan *channel,
+ dma_cookie_t cookie)
+{
+ struct rz_dmac_desc *current_desc, *desc;
+ enum dma_status status;
+ u32 crla, crtb, i;
+
+ /* Get current processing virtual descriptor */
+ current_desc = list_first_entry(&channel->ld_active,
+ struct rz_dmac_desc, node);
+ if (!current_desc)
+ return 0;
+
+ /*
+ * If the cookie corresponds to a descriptor that has been completed
+ * there is no residue. The same check has already been performed by the
+ * caller but without holding the channel lock, so the descriptor could
+ * now be complete.
+ */
+ status = dma_cookie_status(&channel->vc.chan, cookie, NULL);
+ if (status == DMA_COMPLETE)
+ return 0;
+
+ /*
+ * If the cookie doesn't correspond to the currently processing virtual
+ * descriptor then the descriptor hasn't been processed yet, and the
+ * residue is equal to the full descriptor size. Also, a client driver
+ * is possible to call this function before rz_dmac_irq_handler_thread()
+ * runs. In this case, the running descriptor will be the next
+ * descriptor, and will appear in the done list. So, if the argument
+ * cookie matches the done list's cookie, we can assume the residue is
+ * zero.
+ */
+ if (cookie != current_desc->vd.tx.cookie) {
+ list_for_each_entry(desc, &channel->ld_free, node) {
+ if (cookie == desc->vd.tx.cookie)
+ return 0;
+ }
+
+ list_for_each_entry(desc, &channel->ld_queue, node) {
+ if (cookie == desc->vd.tx.cookie)
+ return desc->len;
+ }
+
+ list_for_each_entry(desc, &channel->ld_active, node) {
+ if (cookie == desc->vd.tx.cookie)
+ return desc->len;
+ }
+
+ /*
+ * No descriptor found for the cookie, there's thus no residue.
+ * This shouldn't happen if the calling driver passes a correct
+ * cookie value.
+ */
+ WARN(1, "No descriptor for cookie!");
+ return 0;
+ }
+
+ /*
+ * We need to read two registers. Make sure the hardware does not move
+ * to next lmdesc while reading the current lmdesc. Trying it 3 times
+ * should be enough: initial read, retry, retry for the paranoid.
+ */
+ for (i = 0; i < 3; i++) {
+ crla = rz_dmac_ch_readl(channel, CRLA, 1);
+ crtb = rz_dmac_ch_readl(channel, CRTB, 1);
+ /* Still the same? */
+ if (crla == rz_dmac_ch_readl(channel, CRLA, 1))
+ break;
+ }
+
+ WARN_ONCE(i >= 3, "residue might not be continuous!");
+
+ /*
+ * Calculate number of byte transferred in processing virtual descriptor.
+ * One virtual descriptor can have many lmdesc.
+ */
+ return crtb + rz_dmac_calculate_residue_bytes_in_vd(channel);
+}
+
+static enum dma_status rz_dmac_tx_status(struct dma_chan *chan,
+ dma_cookie_t cookie,
+ struct dma_tx_state *txstate)
+{
+ struct rz_dmac_chan *channel = to_rz_dmac_chan(chan);
+ enum dma_status status;
+ u32 residue;
+
+ status = dma_cookie_status(chan, cookie, txstate);
+ if (status == DMA_COMPLETE || !txstate)
+ return status;
+
+ scoped_guard(spinlock_irqsave, &channel->vc.lock)
+ residue = rz_dmac_chan_get_residue(channel, cookie);
+
+ /* if there's no residue, the cookie is complete */
+ if (!residue)
+ return DMA_COMPLETE;
+
+ dma_set_residue(txstate, residue);
+
+ return status;
+}
+
/*
* -----------------------------------------------------------------------------
* IRQ handling
@@ -1015,7 +1157,7 @@ static int rz_dmac_probe(struct platform_device *pdev)
engine->device_alloc_chan_resources = rz_dmac_alloc_chan_resources;
engine->device_free_chan_resources = rz_dmac_free_chan_resources;
- engine->device_tx_status = dma_cookie_status;
+ engine->device_tx_status = rz_dmac_tx_status;
engine->device_prep_slave_sg = rz_dmac_prep_slave_sg;
engine->device_prep_dma_memcpy = rz_dmac_prep_dma_memcpy;
engine->device_config = rz_dmac_config;
--
2.43.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v5 6/6] dmaengine: sh: rz-dmac: Add device_{pause,resume}() callbacks
2025-12-17 13:52 [PATCH v5 0/6] dmaengine: sh: rz-dmac: Add tx_status and pause/resume support Claudiu
` (4 preceding siblings ...)
2025-12-17 13:52 ` [PATCH v5 5/6] dmaengine: sh: rz-dmac: Add device_tx_status() callback Claudiu
@ 2025-12-17 13:52 ` Claudiu
2025-12-18 7:48 ` Claudiu Beznea
5 siblings, 1 reply; 11+ messages in thread
From: Claudiu @ 2025-12-17 13:52 UTC (permalink / raw)
To: vkoul, fabrizio.castro.jz, biju.das.jz, geert+renesas,
prabhakar.mahadev-lad.rj
Cc: claudiu.beznea, dmaengine, linux-kernel, Claudiu Beznea
From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
Add support for device_{pause, resume}() callbacks. These are required by
the RZ/G2L SCIFA driver.
Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
---
Changes in v5:
- used suspend capability of the controller to pause/resume
the transfers
drivers/dma/sh/rz-dmac.c | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c
index c3035b94ef2c..e349ade1845f 100644
--- a/drivers/dma/sh/rz-dmac.c
+++ b/drivers/dma/sh/rz-dmac.c
@@ -135,6 +135,7 @@ struct rz_dmac {
#define CHANNEL_8_15_COMMON_BASE 0x0700
#define CHSTAT_ER BIT(4)
+#define CHSTAT_SUS BIT(3)
#define CHSTAT_EN BIT(0)
#define CHCTRL_CLRINTMSK BIT(17)
@@ -827,6 +828,38 @@ static enum dma_status rz_dmac_tx_status(struct dma_chan *chan,
return status;
}
+static int rz_dmac_device_pause(struct dma_chan *chan)
+{
+ struct rz_dmac_chan *channel = to_rz_dmac_chan(chan);
+ u32 val;
+
+ scoped_guard(spinlock_irqsave, &channel->vc.lock) {
+ val = rz_dmac_ch_readl(channel, CHCTRL, 1);
+ val |= CHCTRL_CLRSUS;
+ rz_dmac_ch_writel(channel, val, CHCTRL, 1);
+ }
+
+ return read_poll_timeout_atomic(rz_dmac_ch_readl, val,
+ (val & CHSTAT_SUS), 1, 1024, false,
+ channel, CHSTAT, 1);
+}
+
+static int rz_dmac_device_resume(struct dma_chan *chan)
+{
+ struct rz_dmac_chan *channel = to_rz_dmac_chan(chan);
+ u32 val;
+
+ scoped_guard(spinlock_irqsave, &channel->vc.lock) {
+ val = rz_dmac_ch_readl(channel, CHCTRL, 1);
+ val &= ~CHCTRL_CLRSUS;
+ rz_dmac_ch_writel(channel, val, CHCTRL, 1);
+ }
+
+ return read_poll_timeout_atomic(rz_dmac_ch_readl, val,
+ !(val & CHSTAT_SUS), 1, 1024, false,
+ channel, CHSTAT, 1);
+}
+
/*
* -----------------------------------------------------------------------------
* IRQ handling
@@ -1164,6 +1197,8 @@ static int rz_dmac_probe(struct platform_device *pdev)
engine->device_terminate_all = rz_dmac_terminate_all;
engine->device_issue_pending = rz_dmac_issue_pending;
engine->device_synchronize = rz_dmac_device_synchronize;
+ engine->device_pause = rz_dmac_device_pause;
+ engine->device_resume = rz_dmac_device_resume;
engine->copy_align = DMAENGINE_ALIGN_1_BYTE;
dma_set_max_seg_size(engine->dev, U32_MAX);
--
2.43.0
^ permalink raw reply [flat|nested] 11+ messages in thread
* RE: [PATCH v5 2/6] dmaengine: sh: rz-dmac: Move all CHCTRL updates under spinlock
2025-12-17 13:52 ` [PATCH v5 2/6] dmaengine: sh: rz-dmac: Move all CHCTRL updates under spinlock Claudiu
@ 2025-12-17 16:16 ` Biju Das
2025-12-18 7:43 ` Claudiu Beznea
0 siblings, 1 reply; 11+ messages in thread
From: Biju Das @ 2025-12-17 16:16 UTC (permalink / raw)
To: Claudiu.Beznea, vkoul, Fabrizio Castro, geert+renesas,
Prabhakar Mahadev Lad
Cc: Claudiu.Beznea, dmaengine, linux-kernel, Claudiu Beznea, stable
Hi Claudiu,
> -----Original Message-----
> From: Claudiu <claudiu.beznea@tuxon.dev>
> Sent: 17 December 2025 13:52
> To: vkoul@kernel.org; Fabrizio Castro <fabrizio.castro.jz@renesas.com>; Biju Das
> <biju.das.jz@bp.renesas.com>; geert+renesas@glider.be; Prabhakar Mahadev Lad <prabhakar.mahadev-
> lad.rj@bp.renesas.com>
> Cc: Claudiu.Beznea <claudiu.beznea@tuxon.dev>; dmaengine@vger.kernel.org; linux-
> kernel@vger.kernel.org; Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>; stable@vger.kernel.org
> Subject: [PATCH v5 2/6] dmaengine: sh: rz-dmac: Move all CHCTRL updates under spinlock
>
> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>
> Both rz_dmac_disable_hw() and rz_dmac_irq_handle_channel() update the CHCTRL registers. To avoid
> concurrency issues when updating these registers, take the virtual channel lock. All other CHCTRL
> updates were already protected by the same lock.
>
> Previously, rz_dmac_disable_hw() disabled and re-enabled local IRQs, before accessing CHCTRL registers
> but this does not ensure race-free access.
Maybe I am missing some thing here about race-access:
local_irq_save(flags);
rz_dmac_ch_writel(channel, CHCTRL_DEFAULT, CHCTRL, 1);
After local_irq_save there won't be any IRQ. So how there
can be a race in IRQ handler.
rz_dmac_irq_handle_channel(){
rz_dmac_ch_writel(channel, chctrl | CHCTRL_CLREND, CHCTRL, 1);
}
Now adding a vc lock inside interrupt handler the IRQ routine is serialized
Cheers,
Biju
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v5 2/6] dmaengine: sh: rz-dmac: Move all CHCTRL updates under spinlock
2025-12-17 16:16 ` Biju Das
@ 2025-12-18 7:43 ` Claudiu Beznea
2025-12-18 8:30 ` Biju Das
0 siblings, 1 reply; 11+ messages in thread
From: Claudiu Beznea @ 2025-12-18 7:43 UTC (permalink / raw)
To: Biju Das, vkoul, Fabrizio Castro, geert+renesas, Prabhakar Mahadev Lad
Cc: dmaengine, linux-kernel, Claudiu Beznea, stable
Hi, Biju,
On 12/17/25 18:16, Biju Das wrote:
> Hi Claudiu,
>
>> -----Original Message-----
>> From: Claudiu <claudiu.beznea@tuxon.dev>
>> Sent: 17 December 2025 13:52
>> To: vkoul@kernel.org; Fabrizio Castro <fabrizio.castro.jz@renesas.com>; Biju Das
>> <biju.das.jz@bp.renesas.com>; geert+renesas@glider.be; Prabhakar Mahadev Lad <prabhakar.mahadev-
>> lad.rj@bp.renesas.com>
>> Cc: Claudiu.Beznea <claudiu.beznea@tuxon.dev>; dmaengine@vger.kernel.org; linux-
>> kernel@vger.kernel.org; Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>; stable@vger.kernel.org
>> Subject: [PATCH v5 2/6] dmaengine: sh: rz-dmac: Move all CHCTRL updates under spinlock
>>
>> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>>
>> Both rz_dmac_disable_hw() and rz_dmac_irq_handle_channel() update the CHCTRL registers. To avoid
>> concurrency issues when updating these registers, take the virtual channel lock. All other CHCTRL
>> updates were already protected by the same lock.
>>
>> Previously, rz_dmac_disable_hw() disabled and re-enabled local IRQs, before accessing CHCTRL registers
>> but this does not ensure race-free access.
>
> Maybe I am missing some thing here about race-access:
>
> local_irq_save(flags);
> rz_dmac_ch_writel(channel, CHCTRL_DEFAULT, CHCTRL, 1);
>
> After local_irq_save there won't be any IRQ. So how there
> can be a race in IRQ handler.
My point was to address races that may happen b/w different cores trying
to set CHCTRL. E.g.:
core0: take the IRQ and set CHCTRL
core1: call rz_dmac_issue_pending() -> rz_dmac_xfer_desc() ->
rz_dmac_enable_hw() -> set CHCTRL
However, looking again though the HW manual, the CHCTRL returns zero
when it is read, for each individual bit. Thus, there is no need for any
kind of locking around this register. Also, read-modify-write approach
when updating settings though it is not needed.
I'll adjust it in the next version.
Thank you,
Claudiu
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v5 6/6] dmaengine: sh: rz-dmac: Add device_{pause,resume}() callbacks
2025-12-17 13:52 ` [PATCH v5 6/6] dmaengine: sh: rz-dmac: Add device_{pause,resume}() callbacks Claudiu
@ 2025-12-18 7:48 ` Claudiu Beznea
0 siblings, 0 replies; 11+ messages in thread
From: Claudiu Beznea @ 2025-12-18 7:48 UTC (permalink / raw)
To: vkoul, fabrizio.castro.jz, biju.das.jz, geert+renesas,
prabhakar.mahadev-lad.rj
Cc: dmaengine, linux-kernel, Claudiu Beznea
Hi,
On 12/17/25 15:52, Claudiu wrote:
> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
>
> Add support for device_{pause, resume}() callbacks. These are required by
> the RZ/G2L SCIFA driver.
>
> Signed-off-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> ---
>
> Changes in v5:
> - used suspend capability of the controller to pause/resume
> the transfers
>
> drivers/dma/sh/rz-dmac.c | 35 +++++++++++++++++++++++++++++++++++
> 1 file changed, 35 insertions(+)
>
> diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c
> index c3035b94ef2c..e349ade1845f 100644
> --- a/drivers/dma/sh/rz-dmac.c
> +++ b/drivers/dma/sh/rz-dmac.c
> @@ -135,6 +135,7 @@ struct rz_dmac {
> #define CHANNEL_8_15_COMMON_BASE 0x0700
>
> #define CHSTAT_ER BIT(4)
> +#define CHSTAT_SUS BIT(3)
> #define CHSTAT_EN BIT(0)
>
> #define CHCTRL_CLRINTMSK BIT(17)
> @@ -827,6 +828,38 @@ static enum dma_status rz_dmac_tx_status(struct dma_chan *chan,
> return status;
> }
>
> +static int rz_dmac_device_pause(struct dma_chan *chan)
> +{
> + struct rz_dmac_chan *channel = to_rz_dmac_chan(chan);
> + u32 val;
> +
> + scoped_guard(spinlock_irqsave, &channel->vc.lock) {
> + val = rz_dmac_ch_readl(channel, CHCTRL, 1);
> + val |= CHCTRL_CLRSUS;
This is wrong, I overlooked it. It should have set the CHCTRL_SETSUS bit.
> + rz_dmac_ch_writel(channel, val, CHCTRL, 1);
> + }
> +
> + return read_poll_timeout_atomic(rz_dmac_ch_readl, val,
> + (val & CHSTAT_SUS), 1, 1024, false,
> + channel, CHSTAT, 1);
> +}
> +
> +static int rz_dmac_device_resume(struct dma_chan *chan)
> +{
> + struct rz_dmac_chan *channel = to_rz_dmac_chan(chan);
> + u32 val;
> +
> + scoped_guard(spinlock_irqsave, &channel->vc.lock) {
> + val = rz_dmac_ch_readl(channel, CHCTRL, 1);
> + val &= ~CHCTRL_CLRSUS;
Same here. It should have set the CHCTRL_CLRSUS bit.
I'll update it in the next version.
Thank you,
Claudiu
^ permalink raw reply [flat|nested] 11+ messages in thread
* RE: [PATCH v5 2/6] dmaengine: sh: rz-dmac: Move all CHCTRL updates under spinlock
2025-12-18 7:43 ` Claudiu Beznea
@ 2025-12-18 8:30 ` Biju Das
0 siblings, 0 replies; 11+ messages in thread
From: Biju Das @ 2025-12-18 8:30 UTC (permalink / raw)
To: Claudiu.Beznea, vkoul, Fabrizio Castro, geert+renesas,
Prabhakar Mahadev Lad
Cc: dmaengine, linux-kernel, Claudiu Beznea, stable
Hi Claudiu,
> -----Original Message-----
> From: Claudiu Beznea <claudiu.beznea@tuxon.dev>
> Sent: 18 December 2025 07:44
> Subject: Re: [PATCH v5 2/6] dmaengine: sh: rz-dmac: Move all CHCTRL updates under spinlock
>
> Hi, Biju,
>
> On 12/17/25 18:16, Biju Das wrote:
> > Hi Claudiu,
> >
> >> -----Original Message-----
> >> From: Claudiu <claudiu.beznea@tuxon.dev>
> >> Sent: 17 December 2025 13:52
> >> To: vkoul@kernel.org; Fabrizio Castro
> >> <fabrizio.castro.jz@renesas.com>; Biju Das
> >> <biju.das.jz@bp.renesas.com>; geert+renesas@glider.be; Prabhakar
> >> Mahadev Lad <prabhakar.mahadev- lad.rj@bp.renesas.com>
> >> Cc: Claudiu.Beznea <claudiu.beznea@tuxon.dev>;
> >> dmaengine@vger.kernel.org; linux- kernel@vger.kernel.org; Claudiu
> >> Beznea <claudiu.beznea.uj@bp.renesas.com>; stable@vger.kernel.org
> >> Subject: [PATCH v5 2/6] dmaengine: sh: rz-dmac: Move all CHCTRL
> >> updates under spinlock
> >>
> >> From: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com>
> >>
> >> Both rz_dmac_disable_hw() and rz_dmac_irq_handle_channel() update the
> >> CHCTRL registers. To avoid concurrency issues when updating these
> >> registers, take the virtual channel lock. All other CHCTRL updates were already protected by the
> same lock.
> >>
> >> Previously, rz_dmac_disable_hw() disabled and re-enabled local IRQs,
> >> before accessing CHCTRL registers but this does not ensure race-free access.
> >
> > Maybe I am missing some thing here about race-access:
> >
> > local_irq_save(flags);
> > rz_dmac_ch_writel(channel, CHCTRL_DEFAULT, CHCTRL, 1);
> >
> > After local_irq_save there won't be any IRQ. So how there can be a
> > race in IRQ handler.
>
> My point was to address races that may happen b/w different cores trying to set CHCTRL. E.g.:
>
> core0: take the IRQ and set CHCTRL
> core1: call rz_dmac_issue_pending() -> rz_dmac_xfer_desc() ->
> rz_dmac_enable_hw() -> set CHCTRL
For that you could use a new lock. Currently all the calls are serialized using vc_lock
In irqhandler and irqhandler unnecessarily keeps spinning as the chctrl register is the only shared
Data in interrupt handler.
>
> However, looking again though the HW manual, the CHCTRL returns zero when it is read, for each
> individual bit. Thus, there is no need for any kind of locking around this register. Also, read-
> modify-write approach when updating settings though it is not needed.
But still you need a lock other than vc_lock as CHCTRL is shared register between IRQhandler and tasklet/process context
Cheers,
Biju
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2025-12-18 8:30 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-12-17 13:52 [PATCH v5 0/6] dmaengine: sh: rz-dmac: Add tx_status and pause/resume support Claudiu
2025-12-17 13:52 ` [PATCH v5 1/6] dmaengine: sh: rz-dmac: Protect the driver specific lists Claudiu
2025-12-17 13:52 ` [PATCH v5 2/6] dmaengine: sh: rz-dmac: Move all CHCTRL updates under spinlock Claudiu
2025-12-17 16:16 ` Biju Das
2025-12-18 7:43 ` Claudiu Beznea
2025-12-18 8:30 ` Biju Das
2025-12-17 13:52 ` [PATCH v5 3/6] dmaengine: sh: rz-dmac: Drop unnecessary local_irq_save() call Claudiu
2025-12-17 13:52 ` [PATCH v5 4/6] dmaengine: sh: rz-dmac: Add rz_dmac_invalidate_lmdesc() Claudiu
2025-12-17 13:52 ` [PATCH v5 5/6] dmaengine: sh: rz-dmac: Add device_tx_status() callback Claudiu
2025-12-17 13:52 ` [PATCH v5 6/6] dmaengine: sh: rz-dmac: Add device_{pause,resume}() callbacks Claudiu
2025-12-18 7:48 ` Claudiu Beznea
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®