LoongArch architecture development
 help / color / mirror / Atom feed
* [PATCH 0/4] loongson2-apb-cmc-dma: Fixes issues and pause/resume support
@ 2026-10-26  4:31 Binbin Zhou
  2026-10-26  4:31 ` [PATCH 1/4] dmaengine: loongson: loongson2-apb-cmc-dma: Fix cyclic residue wrap NULL dereference Binbin Zhou
                   ` (3 more replies)
  0 siblings, 4 replies; 7+ messages in thread
From: Binbin Zhou @ 2026-10-26  4:31 UTC (permalink / raw)
  To: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vinod Koul, Frank Li, dmaengine
  Cc: Huacai Chen, Xuerui Wang, loongarch, devicetree, Keguang Zhang,
	Binbin Zhou

Hi all:

This series fixes three bugs in the Loongson 2 APB CMC DMA driver and
adds pause/resume support on top.

Thanks.
Binbin

Binbin Zhou (4):
  dmaengine: loongson: loongson2-apb-cmc-dma: Fix cyclic residue wrap
    NULL dereference
  dmaengine: loongson: loongson2-apb-cmc-dma: Fix residue for queued
    descriptors
  dmaengine: loongson: loongson2-apb-cmc-dma: Program CNDTR when
    advancing sg
  dmaengine: loongson: loongson2-apb-cmc-dma: Add pause/resume support

 drivers/dma/loongson/loongson2-apb-cmc-dma.c | 134 +++++++++++++++++--
 1 file changed, 124 insertions(+), 10 deletions(-)


base-commit: aff5e6a77fc69a7c490246465940d18422d7c631
-- 
2.52.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/4] dmaengine: loongson: loongson2-apb-cmc-dma: Fix cyclic residue wrap NULL dereference
  2026-10-26  4:31 [PATCH 0/4] loongson2-apb-cmc-dma: Fixes issues and pause/resume support Binbin Zhou
@ 2026-10-26  4:31 ` Binbin Zhou
  2026-10-26  4:31 ` [PATCH 2/4] dmaengine: loongson: loongson2-apb-cmc-dma: Fix residue for queued descriptors Binbin Zhou
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 7+ messages in thread
From: Binbin Zhou @ 2026-10-26  4:31 UTC (permalink / raw)
  To: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vinod Koul, Frank Li, dmaengine
  Cc: Huacai Chen, Xuerui Wang, loongarch, devicetree, Keguang Zhang,
	Binbin Zhou, stable

loongson2_cmc_dma_desc_residue()'s cyclic wrap special case checks
lchan->desc->cyclic instead of desc->cyclic.

When tx_status() is called for a queued descriptor while no descriptor
is active, it reaches the "else if (vdesc)" branch with lchan->desc ==
NULL and passes the queued descriptor to this helper. The helper then
dereferences lchan->desc->cyclic and oopses.

Even when lchan->desc is non-NULL, keying the check on lchan->desc is
semantically wrong: the wrap decision belongs to the descriptor being
queried, not to whatever descriptor happens to be active.

Use desc->cyclic. This fixes the NULL dereference and makes the helper
self-contained.

Cc: stable@vger.kernel.org
Fixes: 1c0028e725f1 ("dmaengine: loongson: New driver for the Loongson Multi-Channel DMA controller")
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 drivers/dma/loongson/loongson2-apb-cmc-dma.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/drivers/dma/loongson/loongson2-apb-cmc-dma.c b/drivers/dma/loongson/loongson2-apb-cmc-dma.c
index 2037120b23ca..f090fbe0b8a1 100644
--- a/drivers/dma/loongson/loongson2-apb-cmc-dma.c
+++ b/drivers/dma/loongson/loongson2-apb-cmc-dma.c
@@ -484,7 +484,19 @@ static size_t loongson2_cmc_dma_desc_residue(struct loongson2_cmc_dma_chan *lcha
 	ndtr = loongson2_cmc_dma_read(lddev, LOONGSON2_CMCDMA_CNDTR, lchan->id);
 	residue = ndtr << width;
 
-	if (lchan->desc->cyclic && next_sg == 0)
+	/*
+	 * For a cyclic descriptor that has wrapped (next_sg == 0), the
+	 * current period only has the in-flight entry left; the entries
+	 * from sg[0] onwards belong to the next period. Adding them would
+	 * make residue exceed one period.
+	 *
+	 * Use desc->cyclic, not lchan->desc->cyclic: this helper is also
+	 * reachable with a descriptor that is not lchan->desc, and only
+	 * the active-descriptor branch passes lchan->desc today. Keying
+	 * the wrap decision on the descriptor being queried keeps the
+	 * result correct and avoids depending on lchan->desc being set.
+	 */
+	if (desc->cyclic && next_sg == 0)
 		return residue;
 
 	for (i = next_sg; i < desc->num_sgs; i++)
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 2/4] dmaengine: loongson: loongson2-apb-cmc-dma: Fix residue for queued descriptors
  2026-10-26  4:31 [PATCH 0/4] loongson2-apb-cmc-dma: Fixes issues and pause/resume support Binbin Zhou
  2026-10-26  4:31 ` [PATCH 1/4] dmaengine: loongson: loongson2-apb-cmc-dma: Fix cyclic residue wrap NULL dereference Binbin Zhou
@ 2026-10-26  4:31 ` Binbin Zhou
  2026-10-28 16:06   ` Frank Li
  2026-10-26  4:31 ` [PATCH 3/4] dmaengine: loongson: loongson2-apb-cmc-dma: Program CNDTR when advancing sg Binbin Zhou
  2026-10-26  4:31 ` [PATCH 4/4] dmaengine: loongson: loongson2-apb-cmc-dma: Add pause/resume support Binbin Zhou
  3 siblings, 1 reply; 7+ messages in thread
From: Binbin Zhou @ 2026-10-26  4:31 UTC (permalink / raw)
  To: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vinod Koul, Frank Li, dmaengine
  Cc: Huacai Chen, Xuerui Wang, loongarch, devicetree, Keguang Zhang,
	Binbin Zhou, stable

tx_status() computes residue for both the active and the queued
descriptors through loongson2_cmc_dma_desc_residue(), which reads the
hardware CNDTR register. For a descriptor that has not started yet that
register does not describe its transfer, so clients can observe a
residue that does not match the amount of data still to be moved.

Report residue from the descriptor length for queued transfers, and keep
the hardware-based calculation only for the active descriptor.

Cc: stable@vger.kernel.org
Fixes: 1c0028e725f1 ("dmaengine: loongson: New driver for the Loongson Multi-Channel DMA controller")
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 drivers/dma/loongson/loongson2-apb-cmc-dma.c | 23 ++++++++++++++++----
 1 file changed, 19 insertions(+), 4 deletions(-)

diff --git a/drivers/dma/loongson/loongson2-apb-cmc-dma.c b/drivers/dma/loongson/loongson2-apb-cmc-dma.c
index f090fbe0b8a1..8b8d530d60c3 100644
--- a/drivers/dma/loongson/loongson2-apb-cmc-dma.c
+++ b/drivers/dma/loongson/loongson2-apb-cmc-dma.c
@@ -472,6 +472,17 @@ loongson2_cmc_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, si
 	return vchan_tx_prep(&lchan->vchan, &desc->vdesc, flags);
 }
 
+static size_t loongson2_cmc_dma_desc_total_len(struct loongson2_cmc_dma_desc *desc)
+{
+	size_t total = 0;
+	u32 i;
+
+	for (i = 0; i < desc->num_sgs; i++)
+		total += desc->sg_req[i].len;
+
+	return total;
+}
+
 static size_t loongson2_cmc_dma_desc_residue(struct loongson2_cmc_dma_chan *lchan,
 					     struct loongson2_cmc_dma_desc *desc, u32 next_sg)
 {
@@ -518,12 +529,16 @@ static enum dma_status loongson2_cmc_dma_tx_status(struct dma_chan *chan, dma_co
 
 	scoped_guard(spinlock_irqsave, &lchan->vchan.lock) {
 		vdesc = vchan_find_desc(&lchan->vchan, cookie);
-		if (lchan->desc && cookie == lchan->desc->vdesc.tx.cookie)
+		if (lchan->desc && cookie == lchan->desc->vdesc.tx.cookie) {
+			/* Active descriptor: compute residue from hardware progress */
 			state->residue = loongson2_cmc_dma_desc_residue(lchan, lchan->desc,
 									lchan->next_sg);
-		else if (vdesc)
-			state->residue = loongson2_cmc_dma_desc_residue(lchan,
-									to_lmdma_desc(vdesc), 0);
+		} else if (vdesc) {
+			/* Queued descriptor not yet started: residue is the total length */
+			struct loongson2_cmc_dma_desc *ldesc = to_lmdma_desc(vdesc);
+
+			state->residue = loongson2_cmc_dma_desc_total_len(ldesc);
+		}
 	}
 
 	return status;
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 3/4] dmaengine: loongson: loongson2-apb-cmc-dma: Program CNDTR when advancing sg
  2026-10-26  4:31 [PATCH 0/4] loongson2-apb-cmc-dma: Fixes issues and pause/resume support Binbin Zhou
  2026-10-26  4:31 ` [PATCH 1/4] dmaengine: loongson: loongson2-apb-cmc-dma: Fix cyclic residue wrap NULL dereference Binbin Zhou
  2026-10-26  4:31 ` [PATCH 2/4] dmaengine: loongson: loongson2-apb-cmc-dma: Fix residue for queued descriptors Binbin Zhou
@ 2026-10-26  4:31 ` Binbin Zhou
  2026-10-28 16:12   ` Frank Li
  2026-10-26  4:31 ` [PATCH 4/4] dmaengine: loongson: loongson2-apb-cmc-dma: Add pause/resume support Binbin Zhou
  3 siblings, 1 reply; 7+ messages in thread
From: Binbin Zhou @ 2026-10-26  4:31 UTC (permalink / raw)
  To: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vinod Koul, Frank Li, dmaengine
  Cc: Huacai Chen, Xuerui Wang, loongarch, devicetree, Keguang Zhang,
	Binbin Zhou, stable

When configure_next_sg() moves to the next scatterlist entry it updates
CMAR but leaves CNDTR at the value left over from the previous entry.
The controller therefore keeps using a stale transfer count, so the
hardware does not move the amount of data the descriptor describes.

Program CNDTR together with CMAR so software and hardware progress stay
in sync.

Cc: stable@vger.kernel.org
Fixes: 1c0028e725f1 ("dmaengine: loongson: New driver for the Loongson Multi-Channel DMA controller")
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 drivers/dma/loongson/loongson2-apb-cmc-dma.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/dma/loongson/loongson2-apb-cmc-dma.c b/drivers/dma/loongson/loongson2-apb-cmc-dma.c
index 8b8d530d60c3..c654e0572902 100644
--- a/drivers/dma/loongson/loongson2-apb-cmc-dma.c
+++ b/drivers/dma/loongson/loongson2-apb-cmc-dma.c
@@ -266,6 +266,7 @@ static void loongson2_cmc_dma_configure_next_sg(struct loongson2_cmc_dma_chan *l
 
 	sg_req = &lchan->desc->sg_req[lchan->next_sg];
 	loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CMAR, id, sg_req->chan_reg.cmar);
+	loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CNDTR, id, sg_req->chan_reg.cndtr);
 
 	/* Start transition */
 	ccr |= LOONGSON2_CMCDMA_CCR_EN;
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 4/4] dmaengine: loongson: loongson2-apb-cmc-dma: Add pause/resume support
  2026-10-26  4:31 [PATCH 0/4] loongson2-apb-cmc-dma: Fixes issues and pause/resume support Binbin Zhou
                   ` (2 preceding siblings ...)
  2026-10-26  4:31 ` [PATCH 3/4] dmaengine: loongson: loongson2-apb-cmc-dma: Program CNDTR when advancing sg Binbin Zhou
@ 2026-10-26  4:31 ` Binbin Zhou
  3 siblings, 0 replies; 7+ messages in thread
From: Binbin Zhou @ 2026-10-26  4:31 UTC (permalink / raw)
  To: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vinod Koul, Frank Li, dmaengine
  Cc: Huacai Chen, Xuerui Wang, loongarch, devicetree, Keguang Zhang,
	Binbin Zhou

Clients that run cyclic transfers sometimes need to stop the channel
temporarily and continue from the same position later. Without pause and
resume, such clients have to terminate and restart the transfer, which
loses the current position and can cause observable glitches or
duplicated data. In addition, advancing to the next scatterlist entry
while the client has requested a pause must not silently restart the
channel.

Provide pause/resume semantics that preserve the current hardware
position, and make scatterlist advancement honor the paused state so a
paused channel stays stopped until the client explicitly resumes it.

Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 drivers/dma/loongson/loongson2-apb-cmc-dma.c | 94 +++++++++++++++++++-
 1 file changed, 90 insertions(+), 4 deletions(-)

diff --git a/drivers/dma/loongson/loongson2-apb-cmc-dma.c b/drivers/dma/loongson/loongson2-apb-cmc-dma.c
index c654e0572902..a4153ea85462 100644
--- a/drivers/dma/loongson/loongson2-apb-cmc-dma.c
+++ b/drivers/dma/loongson/loongson2-apb-cmc-dma.c
@@ -93,6 +93,7 @@ struct loongson2_cmc_dma_chan {
 	int irq;
 	u32 next_sg;
 	struct loongson2_cmc_dma_chan_reg chan_reg;
+	bool paused;
 };
 
 struct loongson2_cmc_dma_dev {
@@ -184,6 +185,8 @@ static void loongson2_cmc_dma_stop(struct loongson2_cmc_dma_chan *lchan)
 	loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CCR, lchan->id, ccr);
 
 	loongson2_cmc_dma_irq_clear(lchan, LOONGSON2_CMCDMA_MASKI);
+
+	lchan->paused = false;
 }
 
 static int loongson2_cmc_dma_terminate_all(struct dma_chan *chan)
@@ -254,12 +257,13 @@ static void loongson2_cmc_dma_configure_next_sg(struct loongson2_cmc_dma_chan *l
 {
 	struct loongson2_cmc_dma_dev *lddev = lmdma_get_dev(lchan);
 	struct loongson2_cmc_dma_sg_req *sg_req;
+	bool keep_paused = lchan->paused;
 	u32 ccr, id = lchan->id;
 
 	if (lchan->next_sg == lchan->desc->num_sgs)
 		lchan->next_sg = 0;
 
-	/* Stop to update mem addr */
+	/* Stop to update mem addr (idempotent when already paused) */
 	ccr = loongson2_cmc_dma_read(lddev, LOONGSON2_CMCDMA_CCR, id);
 	ccr &= ~LOONGSON2_CMCDMA_CCR_EN;
 	loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CCR, id, ccr);
@@ -268,9 +272,16 @@ static void loongson2_cmc_dma_configure_next_sg(struct loongson2_cmc_dma_chan *l
 	loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CMAR, id, sg_req->chan_reg.cmar);
 	loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CNDTR, id, sg_req->chan_reg.cndtr);
 
-	/* Start transition */
-	ccr |= LOONGSON2_CMCDMA_CCR_EN;
-	loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CCR, id, ccr);
+	/*
+	 * Only re-enable when the client has not paused the channel.
+	 * A paused cyclic channel still needs its CMAR/CNDTR advanced
+	 * so that resume() starts on the right scatterlist entry, but
+	 * the enable bit must stay clear until the client asks for it.
+	 */
+	if (!keep_paused) {
+		ccr |= LOONGSON2_CMCDMA_CCR_EN;
+		loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CCR, id, ccr);
+	}
 }
 
 static void loongson2_cmc_dma_handle_chan_done(struct loongson2_cmc_dma_chan *lchan)
@@ -534,6 +545,13 @@ static enum dma_status loongson2_cmc_dma_tx_status(struct dma_chan *chan, dma_co
 			/* Active descriptor: compute residue from hardware progress */
 			state->residue = loongson2_cmc_dma_desc_residue(lchan, lchan->desc,
 									lchan->next_sg);
+			/*
+			 * A paused channel still has an active descriptor but is not making
+			 * progress. Report DMA_PAUSED so clients can distinguish it from a
+			 * running transfer.
+			 */
+			if (lchan->paused && status == DMA_IN_PROGRESS)
+				status = DMA_PAUSED;
 		} else if (vdesc) {
 			/* Queued descriptor not yet started: residue is the total length */
 			struct loongson2_cmc_dma_desc *ldesc = to_lmdma_desc(vdesc);
@@ -624,6 +642,72 @@ static int loongson2_cmc_dma_of_controller_register(struct loongson2_cmc_dma_dev
 	return of_dma_controller_register(dev->of_node, loongson2_cmc_dma_of_xlate, lddev);
 }
 
+/**
+ * loongson2_cmc_dma_pause() - Stop the channel without losing state
+ * @chan: DMA channel
+ *
+ * Clears CCR.EN so the controller stops issuing requests to the
+ * peripheral. CNDTR and CMAR keep their current values, so
+ * loongson2_cmc_dma_resume() can pick up exactly where the transfer
+ * left off. The channel remains in cyclic mode; only the enable bit is
+ * dropped, so a paused cyclic channel does not advance the write
+ * pointer until resumed.
+ *
+ * Return: 0 on success.
+ */
+static int loongson2_cmc_dma_pause(struct dma_chan *chan)
+{
+	struct loongson2_cmc_dma_chan *lchan = to_lmdma_chan(chan);
+	struct loongson2_cmc_dma_dev *lddev = lmdma_get_dev(lchan);
+	u32 ccr;
+
+	guard(spinlock_irqsave)(&lchan->vchan.lock);
+
+	if (lchan->paused || !lchan->desc)
+		return 0;
+
+	lchan->paused = true;
+
+	ccr = loongson2_cmc_dma_read(lddev, LOONGSON2_CMCDMA_CCR, lchan->id);
+	ccr &= ~LOONGSON2_CMCDMA_CCR_EN;
+	loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CCR, lchan->id, ccr);
+
+	return 0;
+}
+
+/**
+ * loongson2_cmc_dma_resume() - Restart a paused channel
+ * @chan: DMA channel
+ *
+ * Restores CCR.EN. CNDTR is left untouched, so the transfer continues
+ * from the exact word where the pause happened.
+ *
+ * Return: 0 on success.
+ */
+static int loongson2_cmc_dma_resume(struct dma_chan *chan)
+{
+	struct loongson2_cmc_dma_chan *lchan = to_lmdma_chan(chan);
+	struct loongson2_cmc_dma_dev *lddev = lmdma_get_dev(lchan);
+	u32 ccr;
+
+	guard(spinlock_irqsave)(&lchan->vchan.lock);
+
+	if (!lchan->paused)
+		return 0;
+
+	lchan->paused = false;
+
+	/* No descriptor: nothing left to run */
+	if (!lchan->desc)
+		return 0;
+
+	ccr = loongson2_cmc_dma_read(lddev, LOONGSON2_CMCDMA_CCR, lchan->id);
+	ccr |= LOONGSON2_CMCDMA_CCR_EN;
+	loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CCR, lchan->id, ccr);
+
+	return 0;
+}
+
 static int loongson2_cmc_dma_probe(struct platform_device *pdev)
 {
 	const struct loongson2_cmc_dma_config *config;
@@ -676,6 +760,8 @@ static int loongson2_cmc_dma_probe(struct platform_device *pdev)
 	ddev->device_synchronize = loongson2_cmc_dma_synchronize;
 	ddev->device_tx_status = loongson2_cmc_dma_tx_status;
 	ddev->device_terminate_all = loongson2_cmc_dma_terminate_all;
+	ddev->device_pause = loongson2_cmc_dma_pause;
+	ddev->device_resume = loongson2_cmc_dma_resume;
 
 	ddev->max_sg_burst = LOONSON2_CMCDMA_MAX_DATA_ITEMS;
 	ddev->src_addr_widths = LOONGSON2_CMCDMA_BUSWIDTHS;
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH 2/4] dmaengine: loongson: loongson2-apb-cmc-dma: Fix residue for queued descriptors
  2026-10-26  4:31 ` [PATCH 2/4] dmaengine: loongson: loongson2-apb-cmc-dma: Fix residue for queued descriptors Binbin Zhou
@ 2026-10-28 16:06   ` Frank Li
  0 siblings, 0 replies; 7+ messages in thread
From: Frank Li @ 2026-10-28 16:06 UTC (permalink / raw)
  To: Binbin Zhou
  Cc: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vinod Koul, Frank Li, dmaengine, Huacai Chen,
	Xuerui Wang, loongarch, devicetree, Keguang Zhang, stable

On Mon, Oct 26, 2026 at 12:31:21PM +0800, Binbin Zhou wrote:
> tx_status() computes residue for both the active and the queued
> descriptors through loongson2_cmc_dma_desc_residue(), which reads the
> hardware CNDTR register. For a descriptor that has not started yet that
> register does not describe its transfer, so clients can observe a
> residue that does not match the amount of data still to be moved.

why this happen, suppose write CNDTR at loongson2_cmc_dma_start_transfer()
when issue pending.

Maybe there are raise condition, between send to issue list and start_transfer()

suppose loongson2_cmc_dma_tx_status() should not find vdesc by cookie id
if issue pending have not complete yet.

Maybe move loongson2_cmc_dma_start_transfer() into lchan->vchan.lock, you
will not see this problem.

Frank
>
> Report residue from the descriptor length for queued transfers, and keep
> the hardware-based calculation only for the active descriptor.
>
> Cc: stable@vger.kernel.org
> Fixes: 1c0028e725f1 ("dmaengine: loongson: New driver for the Loongson Multi-Channel DMA controller")
> Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
> ---
>  drivers/dma/loongson/loongson2-apb-cmc-dma.c | 23 ++++++++++++++++----
>  1 file changed, 19 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/dma/loongson/loongson2-apb-cmc-dma.c b/drivers/dma/loongson/loongson2-apb-cmc-dma.c
> index f090fbe0b8a1..8b8d530d60c3 100644
> --- a/drivers/dma/loongson/loongson2-apb-cmc-dma.c
> +++ b/drivers/dma/loongson/loongson2-apb-cmc-dma.c
> @@ -472,6 +472,17 @@ loongson2_cmc_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, si
>  	return vchan_tx_prep(&lchan->vchan, &desc->vdesc, flags);
>  }
>
> +static size_t loongson2_cmc_dma_desc_total_len(struct loongson2_cmc_dma_desc *desc)
> +{
> +	size_t total = 0;
> +	u32 i;
> +
> +	for (i = 0; i < desc->num_sgs; i++)
> +		total += desc->sg_req[i].len;
> +
> +	return total;
> +}
> +
>  static size_t loongson2_cmc_dma_desc_residue(struct loongson2_cmc_dma_chan *lchan,
>  					     struct loongson2_cmc_dma_desc *desc, u32 next_sg)
>  {
> @@ -518,12 +529,16 @@ static enum dma_status loongson2_cmc_dma_tx_status(struct dma_chan *chan, dma_co
>
>  	scoped_guard(spinlock_irqsave, &lchan->vchan.lock) {
>  		vdesc = vchan_find_desc(&lchan->vchan, cookie);
> -		if (lchan->desc && cookie == lchan->desc->vdesc.tx.cookie)
> +		if (lchan->desc && cookie == lchan->desc->vdesc.tx.cookie) {
> +			/* Active descriptor: compute residue from hardware progress */
>  			state->residue = loongson2_cmc_dma_desc_residue(lchan, lchan->desc,
>  									lchan->next_sg);
> -		else if (vdesc)
> -			state->residue = loongson2_cmc_dma_desc_residue(lchan,
> -									to_lmdma_desc(vdesc), 0);
> +		} else if (vdesc) {
> +			/* Queued descriptor not yet started: residue is the total length */
> +			struct loongson2_cmc_dma_desc *ldesc = to_lmdma_desc(vdesc);
> +
> +			state->residue = loongson2_cmc_dma_desc_total_len(ldesc);
> +		}
>  	}
>
>  	return status;
> --
> 2.52.0
>

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 3/4] dmaengine: loongson: loongson2-apb-cmc-dma: Program CNDTR when advancing sg
  2026-10-26  4:31 ` [PATCH 3/4] dmaengine: loongson: loongson2-apb-cmc-dma: Program CNDTR when advancing sg Binbin Zhou
@ 2026-10-28 16:12   ` Frank Li
  0 siblings, 0 replies; 7+ messages in thread
From: Frank Li @ 2026-10-28 16:12 UTC (permalink / raw)
  To: Binbin Zhou
  Cc: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vinod Koul, Frank Li, dmaengine, Huacai Chen,
	Xuerui Wang, loongarch, devicetree, Keguang Zhang, stable

On Mon, Oct 26, 2026 at 12:31:22PM +0800, Binbin Zhou wrote:
> When configure_next_sg() moves to the next scatterlist entry it updates
> CMAR but leaves CNDTR at the value left over from the previous entry.
> The controller therefore keeps using a stale transfer count, so the
> hardware does not move the amount of data the descriptor describes.

How it works at 1c0028e725f1? not test ?

Frank

>
> Program CNDTR together with CMAR so software and hardware progress stay
> in sync.
>
> Cc: stable@vger.kernel.org
> Fixes: 1c0028e725f1 ("dmaengine: loongson: New driver for the Loongson Multi-Channel DMA controller")
> Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
> ---
>  drivers/dma/loongson/loongson2-apb-cmc-dma.c | 1 +
>  1 file changed, 1 insertion(+)
>
> diff --git a/drivers/dma/loongson/loongson2-apb-cmc-dma.c b/drivers/dma/loongson/loongson2-apb-cmc-dma.c
> index 8b8d530d60c3..c654e0572902 100644
> --- a/drivers/dma/loongson/loongson2-apb-cmc-dma.c
> +++ b/drivers/dma/loongson/loongson2-apb-cmc-dma.c
> @@ -266,6 +266,7 @@ static void loongson2_cmc_dma_configure_next_sg(struct loongson2_cmc_dma_chan *l
>
>  	sg_req = &lchan->desc->sg_req[lchan->next_sg];
>  	loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CMAR, id, sg_req->chan_reg.cmar);
> +	loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CNDTR, id, sg_req->chan_reg.cndtr);
>
>  	/* Start transition */
>  	ccr |= LOONGSON2_CMCDMA_CCR_EN;
> --
> 2.52.0
>

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-10-28 16:12 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-26  4:31 [PATCH 0/4] loongson2-apb-cmc-dma: Fixes issues and pause/resume support Binbin Zhou
2026-10-26  4:31 ` [PATCH 1/4] dmaengine: loongson: loongson2-apb-cmc-dma: Fix cyclic residue wrap NULL dereference Binbin Zhou
2026-10-26  4:31 ` [PATCH 2/4] dmaengine: loongson: loongson2-apb-cmc-dma: Fix residue for queued descriptors Binbin Zhou
2026-10-28 16:06   ` Frank Li
2026-10-26  4:31 ` [PATCH 3/4] dmaengine: loongson: loongson2-apb-cmc-dma: Program CNDTR when advancing sg Binbin Zhou
2026-10-28 16:12   ` Frank Li
2026-10-26  4:31 ` [PATCH 4/4] dmaengine: loongson: loongson2-apb-cmc-dma: Add pause/resume support Binbin Zhou

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox