public inbox for linux-mips@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 0/6] dmaengine: Add Loongson Multi-Channel DMA controller support
@ 2026-03-07  3:25 Binbin Zhou
  2026-03-07  3:25 ` [PATCH v4 1/6] dmaengine: loongson: New directory for Loongson DMA controllers drivers Binbin Zhou
                   ` (7 more replies)
  0 siblings, 8 replies; 10+ messages in thread
From: Binbin Zhou @ 2026-03-07  3:25 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,
	linux-mips, Binbin Zhou

Hi all:

This patchset introduces the Loongson multi-channel DMA controller,
which is present in the Loongson-2K0300 and Loongson-2K3000 processors.

It is a multi-channel controller that enables data transfers from memory
to memory, device to memory, and memory to device, as well as channel
prioritization configurable through the channel configuration registers.

Additionally, since multiple distinct types of DMA controllers exist on
the Loongson platform, I have attempted to consolidate all Loongson DMA
drivers into a new directory named `Loongson` for easier management.

Thanks.
Binbin

===========
V4:
- Rebase on dmaengine/next tree;
- Add Reviewed-by tags from Frank and Rob, thanks;

patch(1/6):
 - Add `depends on` restrictions.

patch(6/6):
 - Move loongson2_cmc_dma_config{..} close to its users.

Link to V3:
https://lore.kernel.org/dmaengine/cover.1771989595.git.zhoubinbin@loongson.cn/

V3:
- Rebase on dmaengine/next tree;

patch(1/6):
 - Keep alphabet order;

patch(2/6):
 - Add Reviewed-by tag from Frank, thanks;

patch(3/6)/(4/6):
 - New patches, format loongson2-apb-dma driver code;

patch(5/6):
 - Add description for `interrupts` property;

patch(6/6):
 - Use ffs() helper make the code cleaner;
 - Refact loongson2_cmc_dma_chan_irq();
 - Simplify locking with guard() and scoped_guard();
 - kzalloc()->kzalloc_flex().

Link to V2:
https://lore.kernel.org/all/cover.1770605931.git.zhoubinbin@loongson.cn/

V2:
patch(1/4):
 - Update loongson1-apb-dma.c entry in MAINTAINERS.

patch(2/4):
 - New patch, use dmaenginem_async_device_register() helper.

patch(3/4):
 - `additionalProperties: false` replaced by
   `unevaluatedProperties: false`.

patch(4/4):
 - Rename filename as loongson2-apb-cmc-dma.c;
 - Rename Kconfig item as LOONGSON2_APB_CMC_DMA;
 - Rename the variable prefix as `loongson2_cmc_dma`;
 - Use dmaenginem_async_device_register() helper;
 - Drop 'dma_' prefix in struct loongson2_mdma_chan_reg;
 - Use struct_size();

Link to V1:
https://lore.kernel.org/all/cover.1770119693.git.zhoubinbin@loongson.cn/

Binbin Zhou (6):
  dmaengine: loongson: New directory for Loongson DMA controllers
    drivers
  dmaengine: loongson: loongson2-apb: Convert to
    dmaenginem_async_device_register()
  dmaengine: loongson: loongson2-apb: Convert to devm_clk_get_enabled()
  dmaengine: loongson: loongson2-apb: Simplify locking with guard() and
    scoped_guard()
  dt-bindings: dmaengine: Add Loongson Multi-Channel DMA controller
  dmaengine: loongson: New driver for the Loongson Multi-Channel DMA
    controller

 .../bindings/dma/loongson,ls2k0300-dma.yaml   |  81 ++
 MAINTAINERS                                   |   7 +-
 drivers/dma/Kconfig                           |  25 +-
 drivers/dma/Makefile                          |   3 +-
 drivers/dma/loongson/Kconfig                  |  41 +
 drivers/dma/loongson/Makefile                 |   4 +
 .../dma/{ => loongson}/loongson1-apb-dma.c    |   4 +-
 drivers/dma/loongson/loongson2-apb-cmc-dma.c  | 730 ++++++++++++++++++
 .../dma/{ => loongson}/loongson2-apb-dma.c    |  93 +--
 9 files changed, 903 insertions(+), 85 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/dma/loongson,ls2k0300-dma.yaml
 create mode 100644 drivers/dma/loongson/Kconfig
 create mode 100644 drivers/dma/loongson/Makefile
 rename drivers/dma/{ => loongson}/loongson1-apb-dma.c (99%)
 create mode 100644 drivers/dma/loongson/loongson2-apb-cmc-dma.c
 rename drivers/dma/{ => loongson}/loongson2-apb-dma.c (91%)


base-commit: c8e9b1d9febc83ee94944695a07cfd40a1b29743
-- 
2.52.0


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

* [PATCH v4 1/6] dmaengine: loongson: New directory for Loongson DMA controllers drivers
  2026-03-07  3:25 [PATCH v4 0/6] dmaengine: Add Loongson Multi-Channel DMA controller support Binbin Zhou
@ 2026-03-07  3:25 ` Binbin Zhou
  2026-03-09  5:05   ` Keguang Zhang
  2026-03-07  3:25 ` [PATCH v4 2/6] dmaengine: loongson: loongson2-apb: Convert to dmaenginem_async_device_register() Binbin Zhou
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 10+ messages in thread
From: Binbin Zhou @ 2026-03-07  3:25 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,
	linux-mips, Binbin Zhou, Frank Li

Gather the Loongson DMA controllers under drivers/dma/loongson/

Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 MAINTAINERS                                   |  3 +-
 drivers/dma/Kconfig                           | 25 ++--------------
 drivers/dma/Makefile                          |  3 +-
 drivers/dma/loongson/Kconfig                  | 30 +++++++++++++++++++
 drivers/dma/loongson/Makefile                 |  3 ++
 .../dma/{ => loongson}/loongson1-apb-dma.c    |  4 +--
 .../dma/{ => loongson}/loongson2-apb-dma.c    |  4 +--
 7 files changed, 42 insertions(+), 30 deletions(-)
 create mode 100644 drivers/dma/loongson/Kconfig
 create mode 100644 drivers/dma/loongson/Makefile
 rename drivers/dma/{ => loongson}/loongson1-apb-dma.c (99%)
 rename drivers/dma/{ => loongson}/loongson2-apb-dma.c (99%)

diff --git a/MAINTAINERS b/MAINTAINERS
index 55af015174a5..e8cd1e2dac13 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14953,7 +14953,7 @@ M:	Binbin Zhou <zhoubinbin@loongson.cn>
 L:	dmaengine@vger.kernel.org
 S:	Maintained
 F:	Documentation/devicetree/bindings/dma/loongson,ls2x-apbdma.yaml
-F:	drivers/dma/loongson2-apb-dma.c
+F:	drivers/dma/loongson/loongson2-apb-dma.c
 
 LOONGSON LS2X I2C DRIVER
 M:	Binbin Zhou <zhoubinbin@loongson.cn>
@@ -17721,6 +17721,7 @@ F:	arch/mips/boot/dts/loongson/loongson1*
 F:	arch/mips/configs/loongson1_defconfig
 F:	arch/mips/loongson32/
 F:	drivers/*/*loongson1*
+F:	drivers/dma/loongson/loongson1-apb-dma.c
 F:	drivers/mtd/nand/raw/loongson-nand-controller.c
 F:	drivers/net/ethernet/stmicro/stmmac/dwmac-loongson1.c
 F:	sound/soc/loongson/loongson1_ac97.c
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 66cda7cc9f7a..1b84c5b11654 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -376,29 +376,6 @@ config K3_DMA
 	  Support the DMA engine for Hisilicon K3 platform
 	  devices.
 
-config LOONGSON1_APB_DMA
-	tristate "Loongson1 APB DMA support"
-	depends on MACH_LOONGSON32 || COMPILE_TEST
-	select DMA_ENGINE
-	select DMA_VIRTUAL_CHANNELS
-	help
-	  This selects support for the APB DMA controller in Loongson1 SoCs,
-	  which is required by Loongson1 NAND and audio support.
-
-config LOONGSON2_APB_DMA
-	tristate "Loongson2 APB DMA support"
-	depends on LOONGARCH || COMPILE_TEST
-	select DMA_ENGINE
-	select DMA_VIRTUAL_CHANNELS
-	help
-	  Support for the Loongson2 APB DMA controller driver. The
-	  DMA controller is having single DMA channel which can be
-	  configured for different peripherals like audio, nand, sdio
-	  etc which is in APB bus.
-
-	  This DMA controller transfers data from memory to peripheral fifo.
-	  It does not support memory to memory data transfer.
-
 config LPC18XX_DMAMUX
 	bool "NXP LPC18xx/43xx DMA MUX for PL080"
 	depends on ARCH_LPC18XX || COMPILE_TEST
@@ -774,6 +751,8 @@ source "drivers/dma/fsl-dpaa2-qdma/Kconfig"
 
 source "drivers/dma/lgm/Kconfig"
 
+source "drivers/dma/loongson/Kconfig"
+
 source "drivers/dma/stm32/Kconfig"
 
 # clients
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index a54d7688392b..963b10494ee5 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -49,8 +49,6 @@ obj-$(CONFIG_INTEL_IDMA64) += idma64.o
 obj-$(CONFIG_INTEL_IOATDMA) += ioat/
 obj-y += idxd/
 obj-$(CONFIG_K3_DMA) += k3dma.o
-obj-$(CONFIG_LOONGSON1_APB_DMA) += loongson1-apb-dma.o
-obj-$(CONFIG_LOONGSON2_APB_DMA) += loongson2-apb-dma.o
 obj-$(CONFIG_LPC18XX_DMAMUX) += lpc18xx-dmamux.o
 obj-$(CONFIG_LPC32XX_DMAMUX) += lpc32xx-dmamux.o
 obj-$(CONFIG_MILBEAUT_HDMAC) += milbeaut-hdmac.o
@@ -87,6 +85,7 @@ obj-$(CONFIG_FSL_DPAA2_QDMA) += fsl-dpaa2-qdma/
 obj-$(CONFIG_INTEL_LDMA) += lgm/
 
 obj-y += amd/
+obj-y += loongson/
 obj-y += mediatek/
 obj-y += qcom/
 obj-y += stm32/
diff --git a/drivers/dma/loongson/Kconfig b/drivers/dma/loongson/Kconfig
new file mode 100644
index 000000000000..0a865a8fd3a6
--- /dev/null
+++ b/drivers/dma/loongson/Kconfig
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Loongson DMA controllers drivers
+#
+if MACH_LOONGSON32 || MACH_LOONGSON64 || COMPILE_TEST
+
+config LOONGSON1_APB_DMA
+	tristate "Loongson1 APB DMA support"
+	depends on MACH_LOONGSON32 || COMPILE_TEST
+	select DMA_ENGINE
+	select DMA_VIRTUAL_CHANNELS
+	help
+	  This selects support for the APB DMA controller in Loongson1 SoCs,
+	  which is required by Loongson1 NAND and audio support.
+
+config LOONGSON2_APB_DMA
+	tristate "Loongson2 APB DMA support"
+	depends on MACH_LOONGSON64 || COMPILE_TEST
+	select DMA_ENGINE
+	select DMA_VIRTUAL_CHANNELS
+	help
+	  Support for the Loongson2 APB DMA controller driver. The
+	  DMA controller is having single DMA channel which can be
+	  configured for different peripherals like audio, nand, sdio
+	  etc which is in APB bus.
+
+	  This DMA controller transfers data from memory to peripheral fifo.
+	  It does not support memory to memory data transfer.
+
+endif
diff --git a/drivers/dma/loongson/Makefile b/drivers/dma/loongson/Makefile
new file mode 100644
index 000000000000..6cdd08065e92
--- /dev/null
+++ b/drivers/dma/loongson/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_LOONGSON1_APB_DMA) += loongson1-apb-dma.o
+obj-$(CONFIG_LOONGSON2_APB_DMA) += loongson2-apb-dma.o
diff --git a/drivers/dma/loongson1-apb-dma.c b/drivers/dma/loongson/loongson1-apb-dma.c
similarity index 99%
rename from drivers/dma/loongson1-apb-dma.c
rename to drivers/dma/loongson/loongson1-apb-dma.c
index 2e347aba9af8..89786cbd20ab 100644
--- a/drivers/dma/loongson1-apb-dma.c
+++ b/drivers/dma/loongson/loongson1-apb-dma.c
@@ -16,8 +16,8 @@
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 
-#include "dmaengine.h"
-#include "virt-dma.h"
+#include "../dmaengine.h"
+#include "../virt-dma.h"
 
 /* Loongson-1 DMA Control Register */
 #define LS1X_DMA_CTRL		0x0
diff --git a/drivers/dma/loongson2-apb-dma.c b/drivers/dma/loongson/loongson2-apb-dma.c
similarity index 99%
rename from drivers/dma/loongson2-apb-dma.c
rename to drivers/dma/loongson/loongson2-apb-dma.c
index b981475e6779..fc7d9f4a96ec 100644
--- a/drivers/dma/loongson2-apb-dma.c
+++ b/drivers/dma/loongson/loongson2-apb-dma.c
@@ -17,8 +17,8 @@
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 
-#include "dmaengine.h"
-#include "virt-dma.h"
+#include "../dmaengine.h"
+#include "../virt-dma.h"
 
 /* Global Configuration Register */
 #define LDMA_ORDER_ERG		0x0
-- 
2.52.0


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

* [PATCH v4 2/6] dmaengine: loongson: loongson2-apb: Convert to dmaenginem_async_device_register()
  2026-03-07  3:25 [PATCH v4 0/6] dmaengine: Add Loongson Multi-Channel DMA controller support Binbin Zhou
  2026-03-07  3:25 ` [PATCH v4 1/6] dmaengine: loongson: New directory for Loongson DMA controllers drivers Binbin Zhou
@ 2026-03-07  3:25 ` Binbin Zhou
  2026-03-07  3:25 ` [PATCH v4 3/6] dmaengine: loongson: loongson2-apb: Convert to devm_clk_get_enabled() Binbin Zhou
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Binbin Zhou @ 2026-03-07  3:25 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,
	linux-mips, Binbin Zhou, Frank Li

Use the dmaenginem_async_device_register() helper function to simplify
the probe routine.

Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 drivers/dma/loongson/loongson2-apb-dma.c | 7 ++-----
 1 file changed, 2 insertions(+), 5 deletions(-)

diff --git a/drivers/dma/loongson/loongson2-apb-dma.c b/drivers/dma/loongson/loongson2-apb-dma.c
index fc7d9f4a96ec..2d16842e1658 100644
--- a/drivers/dma/loongson/loongson2-apb-dma.c
+++ b/drivers/dma/loongson/loongson2-apb-dma.c
@@ -650,21 +650,19 @@ static int ls2x_dma_probe(struct platform_device *pdev)
 	ddev->dst_addr_widths = LDMA_SLAVE_BUSWIDTHS;
 	ddev->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
 
-	ret = dma_async_device_register(&priv->ddev);
+	ret = dmaenginem_async_device_register(&priv->ddev);
 	if (ret < 0)
 		goto disable_clk;
 
 	ret = of_dma_controller_register(dev->of_node, of_dma_xlate_by_chan_id, priv);
 	if (ret < 0)
-		goto unregister_dmac;
+		goto disable_clk;
 
 	platform_set_drvdata(pdev, priv);
 
 	dev_info(dev, "Loongson LS2X APB DMA driver registered successfully.\n");
 	return 0;
 
-unregister_dmac:
-	dma_async_device_unregister(&priv->ddev);
 disable_clk:
 	clk_disable_unprepare(priv->dma_clk);
 
@@ -680,7 +678,6 @@ static void ls2x_dma_remove(struct platform_device *pdev)
 	struct ls2x_dma_priv *priv = platform_get_drvdata(pdev);
 
 	of_dma_controller_free(pdev->dev.of_node);
-	dma_async_device_unregister(&priv->ddev);
 	clk_disable_unprepare(priv->dma_clk);
 }
 
-- 
2.52.0


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

* [PATCH v4 3/6] dmaengine: loongson: loongson2-apb: Convert to devm_clk_get_enabled()
  2026-03-07  3:25 [PATCH v4 0/6] dmaengine: Add Loongson Multi-Channel DMA controller support Binbin Zhou
  2026-03-07  3:25 ` [PATCH v4 1/6] dmaengine: loongson: New directory for Loongson DMA controllers drivers Binbin Zhou
  2026-03-07  3:25 ` [PATCH v4 2/6] dmaengine: loongson: loongson2-apb: Convert to dmaenginem_async_device_register() Binbin Zhou
@ 2026-03-07  3:25 ` Binbin Zhou
  2026-03-07  3:25 ` [PATCH v4 4/6] dmaengine: loongson: loongson2-apb: Simplify locking with guard() and scoped_guard() Binbin Zhou
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Binbin Zhou @ 2026-03-07  3:25 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,
	linux-mips, Binbin Zhou, Frank Li

Use the devm_clk_get_enabled() helper function to simplify the probe
routine.

Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 drivers/dma/loongson/loongson2-apb-dma.c | 22 +++++-----------------
 1 file changed, 5 insertions(+), 17 deletions(-)

diff --git a/drivers/dma/loongson/loongson2-apb-dma.c b/drivers/dma/loongson/loongson2-apb-dma.c
index 2d16842e1658..adddfafc2f1c 100644
--- a/drivers/dma/loongson/loongson2-apb-dma.c
+++ b/drivers/dma/loongson/loongson2-apb-dma.c
@@ -616,17 +616,13 @@ static int ls2x_dma_probe(struct platform_device *pdev)
 		return dev_err_probe(dev, PTR_ERR(priv->regs),
 				     "devm_platform_ioremap_resource failed.\n");
 
-	priv->dma_clk = devm_clk_get(&pdev->dev, NULL);
+	priv->dma_clk = devm_clk_get_enabled(dev, NULL);
 	if (IS_ERR(priv->dma_clk))
-		return dev_err_probe(dev, PTR_ERR(priv->dma_clk), "devm_clk_get failed.\n");
-
-	ret = clk_prepare_enable(priv->dma_clk);
-	if (ret)
-		return dev_err_probe(dev, ret, "clk_prepare_enable failed.\n");
+		return dev_err_probe(dev, PTR_ERR(priv->dma_clk), "Couldn't start the clock.\n");
 
 	ret = ls2x_dma_chan_init(pdev, priv);
 	if (ret)
-		goto disable_clk;
+		return ret;
 
 	ddev = &priv->ddev;
 	ddev->dev = dev;
@@ -652,21 +648,16 @@ static int ls2x_dma_probe(struct platform_device *pdev)
 
 	ret = dmaenginem_async_device_register(&priv->ddev);
 	if (ret < 0)
-		goto disable_clk;
+		return dev_err_probe(dev, ret, "Failed to register DMA engine device.\n");
 
 	ret = of_dma_controller_register(dev->of_node, of_dma_xlate_by_chan_id, priv);
 	if (ret < 0)
-		goto disable_clk;
+		return dev_err_probe(dev, ret, "Failed to register dma controller.\n");
 
 	platform_set_drvdata(pdev, priv);
 
 	dev_info(dev, "Loongson LS2X APB DMA driver registered successfully.\n");
 	return 0;
-
-disable_clk:
-	clk_disable_unprepare(priv->dma_clk);
-
-	return ret;
 }
 
 /*
@@ -675,10 +666,7 @@ static int ls2x_dma_probe(struct platform_device *pdev)
  */
 static void ls2x_dma_remove(struct platform_device *pdev)
 {
-	struct ls2x_dma_priv *priv = platform_get_drvdata(pdev);
-
 	of_dma_controller_free(pdev->dev.of_node);
-	clk_disable_unprepare(priv->dma_clk);
 }
 
 static const struct of_device_id ls2x_dma_of_match_table[] = {
-- 
2.52.0


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

* [PATCH v4 4/6] dmaengine: loongson: loongson2-apb: Simplify locking with guard() and scoped_guard()
  2026-03-07  3:25 [PATCH v4 0/6] dmaengine: Add Loongson Multi-Channel DMA controller support Binbin Zhou
                   ` (2 preceding siblings ...)
  2026-03-07  3:25 ` [PATCH v4 3/6] dmaengine: loongson: loongson2-apb: Convert to devm_clk_get_enabled() Binbin Zhou
@ 2026-03-07  3:25 ` Binbin Zhou
  2026-03-07  3:25 ` [PATCH v4 5/6] dt-bindings: dmaengine: Add Loongson Multi-Channel DMA controller Binbin Zhou
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Binbin Zhou @ 2026-03-07  3:25 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,
	linux-mips, Binbin Zhou, Frank Li

Use guard() and scoped_guard() infrastructure instead of explicitly
acquiring and releasing spinlocks to simplify the code and ensure that
all locks are released properly.

Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 drivers/dma/loongson/loongson2-apb-dma.c | 62 +++++++++++-------------
 1 file changed, 29 insertions(+), 33 deletions(-)

diff --git a/drivers/dma/loongson/loongson2-apb-dma.c b/drivers/dma/loongson/loongson2-apb-dma.c
index adddfafc2f1c..aceb069e71fc 100644
--- a/drivers/dma/loongson/loongson2-apb-dma.c
+++ b/drivers/dma/loongson/loongson2-apb-dma.c
@@ -461,12 +461,11 @@ static int ls2x_dma_slave_config(struct dma_chan *chan,
 static void ls2x_dma_issue_pending(struct dma_chan *chan)
 {
 	struct ls2x_dma_chan *lchan = to_ldma_chan(chan);
-	unsigned long flags;
 
-	spin_lock_irqsave(&lchan->vchan.lock, flags);
+	guard(spinlock_irqsave)(&lchan->vchan.lock);
+
 	if (vchan_issue_pending(&lchan->vchan) && !lchan->desc)
 		ls2x_dma_start_transfer(lchan);
-	spin_unlock_irqrestore(&lchan->vchan.lock, flags);
 }
 
 /*
@@ -478,19 +477,18 @@ static void ls2x_dma_issue_pending(struct dma_chan *chan)
 static int ls2x_dma_terminate_all(struct dma_chan *chan)
 {
 	struct ls2x_dma_chan *lchan = to_ldma_chan(chan);
-	unsigned long flags;
 	LIST_HEAD(head);
 
-	spin_lock_irqsave(&lchan->vchan.lock, flags);
-	/* Setting stop cmd */
-	ls2x_dma_write_cmd(lchan, LDMA_STOP);
-	if (lchan->desc) {
-		vchan_terminate_vdesc(&lchan->desc->vdesc);
-		lchan->desc = NULL;
-	}
+	scoped_guard(spinlock_irqsave, &lchan->vchan.lock) {
+		/* Setting stop cmd */
+		ls2x_dma_write_cmd(lchan, LDMA_STOP);
+		if (lchan->desc) {
+			vchan_terminate_vdesc(&lchan->desc->vdesc);
+			lchan->desc = NULL;
+		}
 
-	vchan_get_all_descriptors(&lchan->vchan, &head);
-	spin_unlock_irqrestore(&lchan->vchan.lock, flags);
+		vchan_get_all_descriptors(&lchan->vchan, &head);
+	}
 
 	vchan_dma_desc_free_list(&lchan->vchan, &head);
 	return 0;
@@ -511,14 +509,13 @@ static void ls2x_dma_synchronize(struct dma_chan *chan)
 static int ls2x_dma_pause(struct dma_chan *chan)
 {
 	struct ls2x_dma_chan *lchan = to_ldma_chan(chan);
-	unsigned long flags;
 
-	spin_lock_irqsave(&lchan->vchan.lock, flags);
+	guard(spinlock_irqsave)(&lchan->vchan.lock);
+
 	if (lchan->desc && lchan->desc->status == DMA_IN_PROGRESS) {
 		ls2x_dma_write_cmd(lchan, LDMA_STOP);
 		lchan->desc->status = DMA_PAUSED;
 	}
-	spin_unlock_irqrestore(&lchan->vchan.lock, flags);
 
 	return 0;
 }
@@ -526,14 +523,13 @@ static int ls2x_dma_pause(struct dma_chan *chan)
 static int ls2x_dma_resume(struct dma_chan *chan)
 {
 	struct ls2x_dma_chan *lchan = to_ldma_chan(chan);
-	unsigned long flags;
 
-	spin_lock_irqsave(&lchan->vchan.lock, flags);
+	guard(spinlock_irqsave)(&lchan->vchan.lock);
+
 	if (lchan->desc && lchan->desc->status == DMA_PAUSED) {
 		lchan->desc->status = DMA_IN_PROGRESS;
 		ls2x_dma_write_cmd(lchan, LDMA_START);
 	}
-	spin_unlock_irqrestore(&lchan->vchan.lock, flags);
 
 	return 0;
 }
@@ -550,22 +546,22 @@ static irqreturn_t ls2x_dma_isr(int irq, void *dev_id)
 	struct ls2x_dma_chan *lchan = dev_id;
 	struct ls2x_dma_desc *desc;
 
-	spin_lock(&lchan->vchan.lock);
-	desc = lchan->desc;
-	if (desc) {
-		if (desc->cyclic) {
-			vchan_cyclic_callback(&desc->vdesc);
-		} else {
-			desc->status = DMA_COMPLETE;
-			vchan_cookie_complete(&desc->vdesc);
-			ls2x_dma_start_transfer(lchan);
+	scoped_guard(spinlock, &lchan->vchan.lock) {
+		desc = lchan->desc;
+		if (desc) {
+			if (desc->cyclic) {
+				vchan_cyclic_callback(&desc->vdesc);
+			} else {
+				desc->status = DMA_COMPLETE;
+				vchan_cookie_complete(&desc->vdesc);
+				ls2x_dma_start_transfer(lchan);
+			}
+
+			/* ls2x_dma_start_transfer() updates lchan->desc */
+			if (!lchan->desc)
+				ls2x_dma_write_cmd(lchan, LDMA_STOP);
 		}
-
-		/* ls2x_dma_start_transfer() updates lchan->desc */
-		if (!lchan->desc)
-			ls2x_dma_write_cmd(lchan, LDMA_STOP);
 	}
-	spin_unlock(&lchan->vchan.lock);
 
 	return IRQ_HANDLED;
 }
-- 
2.52.0


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

* [PATCH v4 5/6] dt-bindings: dmaengine: Add Loongson Multi-Channel DMA controller
  2026-03-07  3:25 [PATCH v4 0/6] dmaengine: Add Loongson Multi-Channel DMA controller support Binbin Zhou
                   ` (3 preceding siblings ...)
  2026-03-07  3:25 ` [PATCH v4 4/6] dmaengine: loongson: loongson2-apb: Simplify locking with guard() and scoped_guard() Binbin Zhou
@ 2026-03-07  3:25 ` Binbin Zhou
  2026-03-07  3:25 ` [PATCH v4 6/6] dmaengine: loongson: New driver for the " Binbin Zhou
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Binbin Zhou @ 2026-03-07  3:25 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,
	linux-mips, Binbin Zhou

The Loongson-2K0300/Loongson-2K3000 have built-in multi-channel DMA
controllers, which are similar except for some of the register offsets
and number of channels.

Obviously, this is quite different from the APB DMA controller used in
the Loongson-2K0500/Loongson-2K1000, such as the latter being a
single-channel DMA controller.

To avoid cluttering a single dt-binding file, add a new yaml file.

Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 .../bindings/dma/loongson,ls2k0300-dma.yaml   | 81 +++++++++++++++++++
 MAINTAINERS                                   |  3 +-
 2 files changed, 83 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/dma/loongson,ls2k0300-dma.yaml

diff --git a/Documentation/devicetree/bindings/dma/loongson,ls2k0300-dma.yaml b/Documentation/devicetree/bindings/dma/loongson,ls2k0300-dma.yaml
new file mode 100644
index 000000000000..c3151d806b55
--- /dev/null
+++ b/Documentation/devicetree/bindings/dma/loongson,ls2k0300-dma.yaml
@@ -0,0 +1,81 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/dma/loongson,ls2k0300-dma.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Looongson-2 Multi-Channel DMA controller
+
+description:
+  The Loongson-2 Multi-Channel DMA controller is used for transferring data
+  between system memory and the peripherals on the APB bus.
+
+maintainers:
+  - Binbin Zhou <zhoubinbin@loongson.cn>
+
+allOf:
+  - $ref: dma-controller.yaml#
+
+properties:
+  compatible:
+    enum:
+      - loongson,ls2k0300-dma
+      - loongson,ls2k3000-dma
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    description:
+      Should contain all of the per-channel DMA interrupts in ascending order
+      with respect to the DMA channel index.
+    minItems: 4
+    maxItems: 8
+
+  clocks:
+    maxItems: 1
+
+  '#dma-cells':
+    const: 2
+    description: |
+      DMA request from clients consists of 2 cells:
+        1. Channel index
+        2. Transfer request factor number, If no transfer factor, use 0.
+           The number is SoC-specific, and this should be specified with
+           relation to the device to use the DMA controller.
+
+  dma-channels:
+    enum: [4, 8]
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - '#dma-cells'
+  - dma-channels
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    #include <dt-bindings/clock/loongson,ls2k-clk.h>
+
+    dma-controller@1612c000 {
+        compatible = "loongson,ls2k0300-dma";
+        reg = <0x1612c000 0xff>;
+        interrupt-parent = <&liointc0>;
+        interrupts = <23 IRQ_TYPE_LEVEL_HIGH>,
+                     <24 IRQ_TYPE_LEVEL_HIGH>,
+                     <25 IRQ_TYPE_LEVEL_HIGH>,
+                     <26 IRQ_TYPE_LEVEL_HIGH>,
+                     <27 IRQ_TYPE_LEVEL_HIGH>,
+                     <28 IRQ_TYPE_LEVEL_HIGH>,
+                     <29 IRQ_TYPE_LEVEL_HIGH>,
+                     <30 IRQ_TYPE_LEVEL_HIGH>;
+        clocks = <&clk LS2K0300_CLK_APB_GATE>;
+        #dma-cells = <2>;
+        dma-channels = <8>;
+    };
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index e8cd1e2dac13..aea29c28d865 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14948,10 +14948,11 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/gpio/loongson,ls-gpio.yaml
 F:	drivers/gpio/gpio-loongson-64bit.c
 
-LOONGSON-2 APB DMA DRIVER
+LOONGSON-2 DMA DRIVER
 M:	Binbin Zhou <zhoubinbin@loongson.cn>
 L:	dmaengine@vger.kernel.org
 S:	Maintained
+F:	Documentation/devicetree/bindings/dma/loongson,ls2k0300-dma.yaml
 F:	Documentation/devicetree/bindings/dma/loongson,ls2x-apbdma.yaml
 F:	drivers/dma/loongson/loongson2-apb-dma.c
 
-- 
2.52.0


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

* [PATCH v4 6/6] dmaengine: loongson: New driver for the Loongson Multi-Channel DMA controller
  2026-03-07  3:25 [PATCH v4 0/6] dmaengine: Add Loongson Multi-Channel DMA controller support Binbin Zhou
                   ` (4 preceding siblings ...)
  2026-03-07  3:25 ` [PATCH v4 5/6] dt-bindings: dmaengine: Add Loongson Multi-Channel DMA controller Binbin Zhou
@ 2026-03-07  3:25 ` Binbin Zhou
  2026-03-09 13:32 ` [PATCH v4 0/6] dmaengine: Add Loongson Multi-Channel DMA controller support Huacai Chen
  2026-03-17 11:28 ` Vinod Koul
  7 siblings, 0 replies; 10+ messages in thread
From: Binbin Zhou @ 2026-03-07  3:25 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,
	linux-mips, Binbin Zhou, Frank Li

This DMA controller appears in Loongson-2K0300 and Loongson-2K3000.

It is a chain multi-channel controller that enables data transfers from
memory to memory, device to memory, and memory to device, as well as
channel prioritization configurable through the channel configuration
registers.

In addition, there are slight differences between Loongson-2K0300 and
Loongson-2K3000, such as channel register offsets and the number of
channels.

Reviewed-by: Frank Li <Frank.Li@nxp.com>
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 MAINTAINERS                                  |   1 +
 drivers/dma/loongson/Kconfig                 |  11 +
 drivers/dma/loongson/Makefile                |   1 +
 drivers/dma/loongson/loongson2-apb-cmc-dma.c | 730 +++++++++++++++++++
 4 files changed, 743 insertions(+)
 create mode 100644 drivers/dma/loongson/loongson2-apb-cmc-dma.c

diff --git a/MAINTAINERS b/MAINTAINERS
index aea29c28d865..af9fbb3b43e2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14954,6 +14954,7 @@ L:	dmaengine@vger.kernel.org
 S:	Maintained
 F:	Documentation/devicetree/bindings/dma/loongson,ls2k0300-dma.yaml
 F:	Documentation/devicetree/bindings/dma/loongson,ls2x-apbdma.yaml
+F:	drivers/dma/loongson/loongson2-apb-cmc-dma.c
 F:	drivers/dma/loongson/loongson2-apb-dma.c
 
 LOONGSON LS2X I2C DRIVER
diff --git a/drivers/dma/loongson/Kconfig b/drivers/dma/loongson/Kconfig
index 0a865a8fd3a6..c4e62dce5d4f 100644
--- a/drivers/dma/loongson/Kconfig
+++ b/drivers/dma/loongson/Kconfig
@@ -27,4 +27,15 @@ config LOONGSON2_APB_DMA
 	  This DMA controller transfers data from memory to peripheral fifo.
 	  It does not support memory to memory data transfer.
 
+config LOONGSON2_APB_CMC_DMA
+	tristate "Loongson2 Chain Multi-Channel DMA support"
+	depends on MACH_LOONGSON64 || COMPILE_TEST
+	select DMA_ENGINE
+	select DMA_VIRTUAL_CHANNELS
+	help
+	  Support for the Loongson Chain Multi-Channel DMA controller driver.
+	  It is discovered on the Loongson-2K chip (Loongson-2K0300/Loongson-2K3000),
+	  which has 4/8 channels internally, enabling bidirectional data transfer
+	  between devices and memory.
+
 endif
diff --git a/drivers/dma/loongson/Makefile b/drivers/dma/loongson/Makefile
index 6cdd08065e92..48c19781e729 100644
--- a/drivers/dma/loongson/Makefile
+++ b/drivers/dma/loongson/Makefile
@@ -1,3 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0-only
 obj-$(CONFIG_LOONGSON1_APB_DMA) += loongson1-apb-dma.o
 obj-$(CONFIG_LOONGSON2_APB_DMA) += loongson2-apb-dma.o
+obj-$(CONFIG_LOONGSON2_APB_CMC_DMA) += loongson2-apb-cmc-dma.o
diff --git a/drivers/dma/loongson/loongson2-apb-cmc-dma.c b/drivers/dma/loongson/loongson2-apb-cmc-dma.c
new file mode 100644
index 000000000000..2f2ef51e41b6
--- /dev/null
+++ b/drivers/dma/loongson/loongson2-apb-cmc-dma.c
@@ -0,0 +1,730 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Looongson-2 Chain Multi-Channel DMA Controller driver
+ *
+ * Copyright (C) 2024-2026 Loongson Technology Corporation Limited
+ */
+
+#include <linux/acpi.h>
+#include <linux/acpi_dma.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_dma.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include "../dmaengine.h"
+#include "../virt-dma.h"
+
+#define LOONGSON2_CMCDMA_ISR		0x0	/* DMA Interrupt Status Register */
+#define LOONGSON2_CMCDMA_IFCR		0x4	/* DMA Interrupt Flag Clear Register */
+#define LOONGSON2_CMCDMA_CCR		0x8	/* DMA Channel Configuration Register */
+#define LOONGSON2_CMCDMA_CNDTR		0xc	/* DMA Channel Transmit Count Register */
+#define LOONGSON2_CMCDMA_CPAR		0x10	/* DMA Channel Peripheral Address Register */
+#define LOONGSON2_CMCDMA_CMAR		0x14	/* DMA Channel Memory Address Register */
+
+/* Bitfields of DMA interrupt status register */
+#define LOONGSON2_CMCDMA_TCI		BIT(1) /* Transfer Complete Interrupt */
+#define LOONGSON2_CMCDMA_HTI		BIT(2) /* Half Transfer Interrupt */
+#define LOONGSON2_CMCDMA_TEI		BIT(3) /* Transfer Error Interrupt */
+
+#define LOONGSON2_CMCDMA_MASKI		\
+	(LOONGSON2_CMCDMA_TCI | LOONGSON2_CMCDMA_HTI | LOONGSON2_CMCDMA_TEI)
+
+/* Bitfields of DMA channel x Configuration Register */
+#define LOONGSON2_CMCDMA_CCR_EN		BIT(0) /* Stream Enable */
+#define LOONGSON2_CMCDMA_CCR_TCIE	BIT(1) /* Transfer Complete Interrupt Enable */
+#define LOONGSON2_CMCDMA_CCR_HTIE	BIT(2) /* Half Transfer Complete Interrupt Enable */
+#define LOONGSON2_CMCDMA_CCR_TEIE	BIT(3) /* Transfer Error Interrupt Enable */
+#define LOONGSON2_CMCDMA_CCR_DIR	BIT(4) /* Data Transfer Direction */
+#define LOONGSON2_CMCDMA_CCR_CIRC	BIT(5) /* Circular mode */
+#define LOONGSON2_CMCDMA_CCR_PINC	BIT(6) /* Peripheral increment mode */
+#define LOONGSON2_CMCDMA_CCR_MINC	BIT(7) /* Memory increment mode */
+#define LOONGSON2_CMCDMA_CCR_PSIZE_MASK	GENMASK(9, 8)
+#define LOONGSON2_CMCDMA_CCR_MSIZE_MASK	GENMASK(11, 10)
+#define LOONGSON2_CMCDMA_CCR_PL_MASK	GENMASK(13, 12)
+#define LOONGSON2_CMCDMA_CCR_M2M	BIT(14)
+
+#define LOONGSON2_CMCDMA_CCR_CFG_MASK	\
+	(LOONGSON2_CMCDMA_CCR_PINC | LOONGSON2_CMCDMA_CCR_MINC | LOONGSON2_CMCDMA_CCR_PL_MASK)
+
+#define LOONGSON2_CMCDMA_CCR_IRQ_MASK	\
+	(LOONGSON2_CMCDMA_CCR_TCIE | LOONGSON2_CMCDMA_CCR_HTIE | LOONGSON2_CMCDMA_CCR_TEIE)
+
+#define LOONGSON2_CMCDMA_STREAM_MASK	\
+	(LOONGSON2_CMCDMA_CCR_CFG_MASK | LOONGSON2_CMCDMA_CCR_IRQ_MASK)
+
+#define LOONGSON2_CMCDMA_BUSWIDTHS	(BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \
+					 BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \
+					 BIT(DMA_SLAVE_BUSWIDTH_4_BYTES))
+
+#define LOONSON2_CMCDMA_MAX_DATA_ITEMS	SZ_64K
+
+struct loongson2_cmc_dma_chan_reg {
+	u32 ccr;
+	u32 cndtr;
+	u32 cpar;
+	u32 cmar;
+};
+
+struct loongson2_cmc_dma_sg_req {
+	u32 len;
+	struct loongson2_cmc_dma_chan_reg chan_reg;
+};
+
+struct loongson2_cmc_dma_desc {
+	struct virt_dma_desc vdesc;
+	bool cyclic;
+	u32 num_sgs;
+	struct loongson2_cmc_dma_sg_req sg_req[] __counted_by(num_sgs);
+};
+
+struct loongson2_cmc_dma_chan {
+	struct virt_dma_chan vchan;
+	struct dma_slave_config	dma_sconfig;
+	struct loongson2_cmc_dma_desc *desc;
+	u32 id;
+	u32 irq;
+	u32 next_sg;
+	struct loongson2_cmc_dma_chan_reg chan_reg;
+};
+
+struct loongson2_cmc_dma_dev {
+	struct dma_device ddev;
+	struct clk *dma_clk;
+	void __iomem *base;
+	u32 nr_channels;
+	u32 chan_reg_offset;
+	struct loongson2_cmc_dma_chan chan[] __counted_by(nr_channels);
+};
+
+struct loongson2_cmc_dma_config {
+	u32 max_channels;
+	u32 chan_reg_offset;
+};
+
+static const struct loongson2_cmc_dma_config ls2k0300_cmc_dma_config = {
+	.max_channels = 8,
+	.chan_reg_offset = 0x14,
+};
+
+static const struct loongson2_cmc_dma_config ls2k3000_cmc_dma_config = {
+	.max_channels = 4,
+	.chan_reg_offset = 0x18,
+};
+
+static struct loongson2_cmc_dma_dev *lmdma_get_dev(struct loongson2_cmc_dma_chan *lchan)
+{
+	return container_of(lchan->vchan.chan.device, struct loongson2_cmc_dma_dev, ddev);
+}
+
+static struct loongson2_cmc_dma_chan *to_lmdma_chan(struct dma_chan *chan)
+{
+	return container_of(chan, struct loongson2_cmc_dma_chan, vchan.chan);
+}
+
+static struct loongson2_cmc_dma_desc *to_lmdma_desc(struct virt_dma_desc *vdesc)
+{
+	return container_of(vdesc, struct loongson2_cmc_dma_desc, vdesc);
+}
+
+static struct device *chan2dev(struct loongson2_cmc_dma_chan *lchan)
+{
+	return &lchan->vchan.chan.dev->device;
+}
+
+static u32 loongson2_cmc_dma_read(struct loongson2_cmc_dma_dev *lddev, u32 reg, u32 id)
+{
+	return readl(lddev->base + (reg + lddev->chan_reg_offset * id));
+}
+
+static void loongson2_cmc_dma_write(struct loongson2_cmc_dma_dev *lddev, u32 reg, u32 id, u32 val)
+{
+	writel(val, lddev->base + (reg + lddev->chan_reg_offset * id));
+}
+
+static int loongson2_cmc_dma_get_width(enum dma_slave_buswidth width)
+{
+	switch (width) {
+	case DMA_SLAVE_BUSWIDTH_1_BYTE:
+	case DMA_SLAVE_BUSWIDTH_2_BYTES:
+	case DMA_SLAVE_BUSWIDTH_4_BYTES:
+		return ffs(width) - 1;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int loongson2_cmc_dma_slave_config(struct dma_chan *chan, struct dma_slave_config *config)
+{
+	struct loongson2_cmc_dma_chan *lchan = to_lmdma_chan(chan);
+
+	memcpy(&lchan->dma_sconfig, config, sizeof(*config));
+
+	return 0;
+}
+
+static void loongson2_cmc_dma_irq_clear(struct loongson2_cmc_dma_chan *lchan, u32 flags)
+{
+	struct loongson2_cmc_dma_dev *lddev = lmdma_get_dev(lchan);
+	u32 ifcr;
+
+	ifcr = flags << (4 * lchan->id);
+	loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_IFCR, 0, ifcr);
+}
+
+static void loongson2_cmc_dma_stop(struct loongson2_cmc_dma_chan *lchan)
+{
+	struct loongson2_cmc_dma_dev *lddev = lmdma_get_dev(lchan);
+	u32 ccr;
+
+	ccr = loongson2_cmc_dma_read(lddev, LOONGSON2_CMCDMA_CCR, lchan->id);
+	ccr &= ~(LOONGSON2_CMCDMA_CCR_IRQ_MASK | LOONGSON2_CMCDMA_CCR_EN);
+	loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CCR, lchan->id, ccr);
+
+	loongson2_cmc_dma_irq_clear(lchan, LOONGSON2_CMCDMA_MASKI);
+}
+
+static int loongson2_cmc_dma_terminate_all(struct dma_chan *chan)
+{
+	struct loongson2_cmc_dma_chan *lchan = to_lmdma_chan(chan);
+
+	LIST_HEAD(head);
+
+	scoped_guard(spinlock_irqsave, &lchan->vchan.lock) {
+		if (lchan->desc) {
+			vchan_terminate_vdesc(&lchan->desc->vdesc);
+			loongson2_cmc_dma_stop(lchan);
+			lchan->desc = NULL;
+		}
+		vchan_get_all_descriptors(&lchan->vchan, &head);
+	}
+
+	vchan_dma_desc_free_list(&lchan->vchan, &head);
+
+	return 0;
+}
+
+static void loongson2_cmc_dma_synchronize(struct dma_chan *chan)
+{
+	struct loongson2_cmc_dma_chan *lchan = to_lmdma_chan(chan);
+
+	vchan_synchronize(&lchan->vchan);
+}
+
+static void loongson2_cmc_dma_start_transfer(struct loongson2_cmc_dma_chan *lchan)
+{
+	struct loongson2_cmc_dma_dev *lddev = lmdma_get_dev(lchan);
+	struct loongson2_cmc_dma_sg_req *sg_req;
+	struct loongson2_cmc_dma_chan_reg *reg;
+	struct virt_dma_desc *vdesc;
+
+	loongson2_cmc_dma_stop(lchan);
+
+	if (!lchan->desc) {
+		vdesc = vchan_next_desc(&lchan->vchan);
+		if (!vdesc)
+			return;
+
+		list_del(&vdesc->node);
+		lchan->desc = to_lmdma_desc(vdesc);
+		lchan->next_sg = 0;
+	}
+
+	if (lchan->next_sg == lchan->desc->num_sgs)
+		lchan->next_sg = 0;
+
+	sg_req = &lchan->desc->sg_req[lchan->next_sg];
+	reg = &sg_req->chan_reg;
+
+	loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CCR, lchan->id, reg->ccr);
+	loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CNDTR, lchan->id, reg->cndtr);
+	loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CPAR, lchan->id, reg->cpar);
+	loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CMAR, lchan->id, reg->cmar);
+
+	lchan->next_sg++;
+
+	/* Start DMA */
+	reg->ccr |= LOONGSON2_CMCDMA_CCR_EN;
+	loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CCR, lchan->id, reg->ccr);
+}
+
+static void loongson2_cmc_dma_configure_next_sg(struct loongson2_cmc_dma_chan *lchan)
+{
+	struct loongson2_cmc_dma_dev *lddev = lmdma_get_dev(lchan);
+	struct loongson2_cmc_dma_sg_req *sg_req;
+	u32 ccr, id = lchan->id;
+
+	if (lchan->next_sg == lchan->desc->num_sgs)
+		lchan->next_sg = 0;
+
+	/* Stop to update mem addr */
+	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);
+
+	sg_req = &lchan->desc->sg_req[lchan->next_sg];
+	loongson2_cmc_dma_write(lddev, LOONGSON2_CMCDMA_CMAR, id, sg_req->chan_reg.cmar);
+
+	/* Start transition */
+	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)
+{
+	if (!lchan->desc)
+		return;
+
+	if (lchan->desc->cyclic) {
+		vchan_cyclic_callback(&lchan->desc->vdesc);
+		/* LOONGSON2_CMCDMA_CCR_CIRC mode don't need update register */
+		if (lchan->desc->num_sgs == 1)
+			return;
+		loongson2_cmc_dma_configure_next_sg(lchan);
+		lchan->next_sg++;
+	} else {
+		if (lchan->next_sg == lchan->desc->num_sgs) {
+			vchan_cookie_complete(&lchan->desc->vdesc);
+			lchan->desc = NULL;
+		}
+		loongson2_cmc_dma_start_transfer(lchan);
+	}
+}
+
+static irqreturn_t loongson2_cmc_dma_chan_irq(int irq, void *devid)
+{
+	struct loongson2_cmc_dma_chan *lchan = devid;
+	struct loongson2_cmc_dma_dev *lddev = lmdma_get_dev(lchan);
+	struct device *dev = chan2dev(lchan);
+	u32 ists, status, ccr;
+
+	scoped_guard(spinlock, &lchan->vchan.lock) {
+		ccr = loongson2_cmc_dma_read(lddev, LOONGSON2_CMCDMA_CCR, lchan->id);
+		ists = loongson2_cmc_dma_read(lddev, LOONGSON2_CMCDMA_ISR, 0);
+		status = (ists >> (4 * lchan->id)) & LOONGSON2_CMCDMA_MASKI;
+
+		loongson2_cmc_dma_irq_clear(lchan, status);
+
+		if (status & LOONGSON2_CMCDMA_TCI) {
+			loongson2_cmc_dma_handle_chan_done(lchan);
+			status &= ~LOONGSON2_CMCDMA_TCI;
+		}
+
+		if (status & LOONGSON2_CMCDMA_HTI)
+			status &= ~LOONGSON2_CMCDMA_HTI;
+
+		if (status & LOONGSON2_CMCDMA_TEI) {
+			dev_err(dev, "DMA Transform Error.\n");
+			if (!(ccr & LOONGSON2_CMCDMA_CCR_EN))
+				dev_err(dev, "Channel disabled by HW.\n");
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+static void loongson2_cmc_dma_issue_pending(struct dma_chan *chan)
+{
+	struct loongson2_cmc_dma_chan *lchan = to_lmdma_chan(chan);
+
+	guard(spinlock_irqsave)(&lchan->vchan.lock);
+
+	if (vchan_issue_pending(&lchan->vchan) && !lchan->desc) {
+		dev_dbg(chan2dev(lchan), "vchan %pK: issued\n", &lchan->vchan);
+		loongson2_cmc_dma_start_transfer(lchan);
+	}
+}
+
+static int loongson2_cmc_dma_set_xfer_param(struct loongson2_cmc_dma_chan *lchan,
+					    enum dma_transfer_direction direction,
+					    enum dma_slave_buswidth *buswidth, u32 buf_len)
+{
+	struct dma_slave_config	sconfig = lchan->dma_sconfig;
+	struct device *dev = chan2dev(lchan);
+	int dev_width;
+	u32 ccr;
+
+	switch (direction) {
+	case DMA_MEM_TO_DEV:
+		dev_width = loongson2_cmc_dma_get_width(sconfig.dst_addr_width);
+		if (dev_width < 0) {
+			dev_err(dev, "DMA_MEM_TO_DEV bus width not supported\n");
+			return dev_width;
+		}
+		lchan->chan_reg.cpar = sconfig.dst_addr;
+		ccr = LOONGSON2_CMCDMA_CCR_DIR;
+		*buswidth = sconfig.dst_addr_width;
+		break;
+	case DMA_DEV_TO_MEM:
+		dev_width = loongson2_cmc_dma_get_width(sconfig.src_addr_width);
+		if (dev_width < 0) {
+			dev_err(dev, "DMA_DEV_TO_MEM bus width not supported\n");
+			return dev_width;
+		}
+		lchan->chan_reg.cpar = sconfig.src_addr;
+		ccr = LOONGSON2_CMCDMA_CCR_MINC;
+		*buswidth = sconfig.src_addr_width;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	ccr |= FIELD_PREP(LOONGSON2_CMCDMA_CCR_PSIZE_MASK, dev_width) |
+	       FIELD_PREP(LOONGSON2_CMCDMA_CCR_MSIZE_MASK, dev_width);
+
+	/* Set DMA control register */
+	lchan->chan_reg.ccr &= ~(LOONGSON2_CMCDMA_CCR_PSIZE_MASK | LOONGSON2_CMCDMA_CCR_MSIZE_MASK);
+	lchan->chan_reg.ccr |= ccr;
+
+	return 0;
+}
+
+static struct dma_async_tx_descriptor *
+loongson2_cmc_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, u32 sg_len,
+				enum dma_transfer_direction direction,
+				unsigned long flags, void *context)
+{
+	struct loongson2_cmc_dma_chan *lchan = to_lmdma_chan(chan);
+	struct loongson2_cmc_dma_desc *desc;
+	enum dma_slave_buswidth buswidth;
+	struct scatterlist *sg;
+	u32 num_items, i;
+	int ret;
+
+	desc = kzalloc_flex(*desc, sg_req, sg_len, GFP_NOWAIT);
+	if (!desc)
+		return ERR_PTR(-ENOMEM);
+
+	for_each_sg(sgl, sg, sg_len, i) {
+		ret = loongson2_cmc_dma_set_xfer_param(lchan, direction, &buswidth, sg_dma_len(sg));
+		if (ret)
+			return ERR_PTR(ret);
+
+		num_items = DIV_ROUND_UP(sg_dma_len(sg), buswidth);
+		if (num_items >= LOONSON2_CMCDMA_MAX_DATA_ITEMS) {
+			dev_err(chan2dev(lchan), "Number of items not supported\n");
+			kfree(desc);
+			return ERR_PTR(-EINVAL);
+		}
+
+		desc->sg_req[i].len = sg_dma_len(sg);
+		desc->sg_req[i].chan_reg.ccr = lchan->chan_reg.ccr;
+		desc->sg_req[i].chan_reg.cpar = lchan->chan_reg.cpar;
+		desc->sg_req[i].chan_reg.cmar = sg_dma_address(sg);
+		desc->sg_req[i].chan_reg.cndtr = num_items;
+	}
+
+	desc->num_sgs = sg_len;
+	desc->cyclic = false;
+
+	return vchan_tx_prep(&lchan->vchan, &desc->vdesc, flags);
+}
+
+static struct dma_async_tx_descriptor *
+loongson2_cmc_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
+				  size_t period_len, enum dma_transfer_direction direction,
+				  unsigned long flags)
+{
+	struct loongson2_cmc_dma_chan *lchan = to_lmdma_chan(chan);
+	struct loongson2_cmc_dma_desc *desc;
+	enum dma_slave_buswidth buswidth;
+	u32 num_periods, num_items, i;
+	int ret;
+
+	if (unlikely(buf_len % period_len))
+		return ERR_PTR(-EINVAL);
+
+	ret = loongson2_cmc_dma_set_xfer_param(lchan, direction, &buswidth, period_len);
+	if (ret)
+		return ERR_PTR(ret);
+
+	num_items = DIV_ROUND_UP(period_len, buswidth);
+	if (num_items >= LOONSON2_CMCDMA_MAX_DATA_ITEMS) {
+		dev_err(chan2dev(lchan), "Number of items not supported\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	/* Enable Circular mode */
+	if (buf_len == period_len)
+		lchan->chan_reg.ccr |= LOONGSON2_CMCDMA_CCR_CIRC;
+
+	num_periods = DIV_ROUND_UP(buf_len, period_len);
+	desc = kzalloc_flex(*desc, sg_req, num_periods, GFP_NOWAIT);
+	if (!desc)
+		return ERR_PTR(-ENOMEM);
+
+	for (i = 0; i < num_periods; i++) {
+		desc->sg_req[i].len = period_len;
+		desc->sg_req[i].chan_reg.ccr = lchan->chan_reg.ccr;
+		desc->sg_req[i].chan_reg.cpar = lchan->chan_reg.cpar;
+		desc->sg_req[i].chan_reg.cmar = buf_addr;
+		desc->sg_req[i].chan_reg.cndtr = num_items;
+		buf_addr += period_len;
+	}
+
+	desc->num_sgs = num_periods;
+	desc->cyclic = true;
+
+	return vchan_tx_prep(&lchan->vchan, &desc->vdesc, flags);
+}
+
+static size_t loongson2_cmc_dma_desc_residue(struct loongson2_cmc_dma_chan *lchan,
+					     struct loongson2_cmc_dma_desc *desc, u32 next_sg)
+{
+	struct loongson2_cmc_dma_dev *lddev = lmdma_get_dev(lchan);
+	u32 residue, width, ndtr, ccr, i;
+
+	ccr = loongson2_cmc_dma_read(lddev, LOONGSON2_CMCDMA_CCR, lchan->id);
+	width = FIELD_GET(LOONGSON2_CMCDMA_CCR_PSIZE_MASK, ccr);
+
+	ndtr = loongson2_cmc_dma_read(lddev, LOONGSON2_CMCDMA_CNDTR, lchan->id);
+	residue = ndtr << width;
+
+	if (lchan->desc->cyclic && next_sg == 0)
+		return residue;
+
+	for (i = next_sg; i < desc->num_sgs; i++)
+		residue += desc->sg_req[i].len;
+
+	return residue;
+}
+
+static enum dma_status loongson2_cmc_dma_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
+						   struct dma_tx_state *state)
+{
+	struct loongson2_cmc_dma_chan *lchan = to_lmdma_chan(chan);
+	struct virt_dma_desc *vdesc;
+	enum dma_status status;
+
+	status = dma_cookie_status(chan, cookie, state);
+	if (status == DMA_COMPLETE || !state)
+		return status;
+
+	scoped_guard(spinlock_irqsave, &lchan->vchan.lock) {
+		vdesc = vchan_find_desc(&lchan->vchan, cookie);
+		if (lchan->desc && cookie == lchan->desc->vdesc.tx.cookie)
+			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);
+	}
+
+	return status;
+}
+
+static void loongson2_cmc_dma_free_chan_resources(struct dma_chan *chan)
+{
+	vchan_free_chan_resources(to_virt_chan(chan));
+}
+
+static void loongson2_cmc_dma_desc_free(struct virt_dma_desc *vdesc)
+{
+	kfree(to_lmdma_desc(vdesc));
+}
+
+static bool loongson2_cmc_dma_acpi_filter(struct dma_chan *chan, void *param)
+{
+	struct loongson2_cmc_dma_chan *lchan = to_lmdma_chan(chan);
+	struct acpi_dma_spec *dma_spec = param;
+
+	memset(&lchan->chan_reg, 0, sizeof(struct loongson2_cmc_dma_chan_reg));
+	lchan->chan_reg.ccr = dma_spec->chan_id & LOONGSON2_CMCDMA_STREAM_MASK;
+
+	return true;
+}
+
+static int loongson2_cmc_dma_acpi_controller_register(struct loongson2_cmc_dma_dev *lddev)
+{
+	struct device *dev = lddev->ddev.dev;
+	struct acpi_dma_filter_info *info;
+
+	if (!is_acpi_node(dev_fwnode(dev)))
+		return 0;
+
+	info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	dma_cap_zero(info->dma_cap);
+	info->dma_cap = lddev->ddev.cap_mask;
+	info->filter_fn = loongson2_cmc_dma_acpi_filter;
+
+	return devm_acpi_dma_controller_register(dev, acpi_dma_simple_xlate, info);
+}
+
+static struct dma_chan *loongson2_cmc_dma_of_xlate(struct of_phandle_args *dma_spec,
+						   struct of_dma *ofdma)
+{
+	struct loongson2_cmc_dma_dev *lddev = ofdma->of_dma_data;
+	struct device *dev = lddev->ddev.dev;
+	struct loongson2_cmc_dma_chan *lchan;
+	struct dma_chan *chan;
+
+	if (dma_spec->args_count < 2)
+		return ERR_PTR(-EINVAL);
+
+	if (dma_spec->args[0] >= lddev->nr_channels) {
+		dev_err(dev, "Invalid channel id.\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	lchan = &lddev->chan[dma_spec->args[0]];
+	chan = dma_get_slave_channel(&lchan->vchan.chan);
+	if (!chan) {
+		dev_err(dev, "No more channels available.\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	memset(&lchan->chan_reg, 0, sizeof(struct loongson2_cmc_dma_chan_reg));
+	lchan->chan_reg.ccr = dma_spec->args[1] & LOONGSON2_CMCDMA_STREAM_MASK;
+
+	return chan;
+}
+
+static int loongson2_cmc_dma_of_controller_register(struct loongson2_cmc_dma_dev *lddev)
+{
+	struct device *dev = lddev->ddev.dev;
+
+	if (!is_of_node(dev_fwnode(dev)))
+		return 0;
+
+	return of_dma_controller_register(dev->of_node, loongson2_cmc_dma_of_xlate, lddev);
+}
+
+static int loongson2_cmc_dma_probe(struct platform_device *pdev)
+{
+	const struct loongson2_cmc_dma_config *config;
+	struct loongson2_cmc_dma_chan *lchan;
+	struct loongson2_cmc_dma_dev *lddev;
+	struct device *dev = &pdev->dev;
+	struct dma_device *ddev;
+	u32 nr_chans, i;
+	int ret;
+
+	config = (const struct loongson2_cmc_dma_config *)device_get_match_data(dev);
+	if (!config)
+		return -EINVAL;
+
+	ret = device_property_read_u32(dev, "dma-channels", &nr_chans);
+	if (ret || nr_chans > config->max_channels) {
+		dev_err(dev, "missing or invalid dma-channels property\n");
+		nr_chans = config->max_channels;
+	}
+
+	lddev = devm_kzalloc(dev, struct_size(lddev, chan, nr_chans), GFP_KERNEL);
+	if (!lddev)
+		return -ENOMEM;
+
+	lddev->base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(lddev->base))
+		return PTR_ERR(lddev->base);
+
+	platform_set_drvdata(pdev, lddev);
+	lddev->nr_channels = nr_chans;
+	lddev->chan_reg_offset = config->chan_reg_offset;
+
+	lddev->dma_clk = devm_clk_get_optional_enabled(dev, NULL);
+	if (IS_ERR(lddev->dma_clk))
+		return dev_err_probe(dev, PTR_ERR(lddev->dma_clk), "Failed to get dma clock\n");
+
+	ddev = &lddev->ddev;
+	ddev->dev = dev;
+
+	dma_cap_zero(ddev->cap_mask);
+	dma_cap_set(DMA_SLAVE, ddev->cap_mask);
+	dma_cap_set(DMA_PRIVATE, ddev->cap_mask);
+	dma_cap_set(DMA_CYCLIC, ddev->cap_mask);
+
+	ddev->device_free_chan_resources = loongson2_cmc_dma_free_chan_resources;
+	ddev->device_config = loongson2_cmc_dma_slave_config;
+	ddev->device_prep_slave_sg = loongson2_cmc_dma_prep_slave_sg;
+	ddev->device_prep_dma_cyclic = loongson2_cmc_dma_prep_dma_cyclic;
+	ddev->device_issue_pending = loongson2_cmc_dma_issue_pending;
+	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->max_sg_burst = LOONSON2_CMCDMA_MAX_DATA_ITEMS;
+	ddev->src_addr_widths = LOONGSON2_CMCDMA_BUSWIDTHS;
+	ddev->dst_addr_widths = LOONGSON2_CMCDMA_BUSWIDTHS;
+	ddev->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
+	INIT_LIST_HEAD(&ddev->channels);
+
+	for (i = 0; i < nr_chans; i++) {
+		lchan = &lddev->chan[i];
+
+		lchan->id = i;
+		lchan->vchan.desc_free = loongson2_cmc_dma_desc_free;
+		vchan_init(&lchan->vchan, ddev);
+	}
+
+	ret = dmaenginem_async_device_register(ddev);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to register DMA engine device.\n");
+
+	for (i = 0; i < nr_chans; i++) {
+		lchan = &lddev->chan[i];
+
+		lchan->irq = platform_get_irq(pdev, i);
+		if (lchan->irq < 0)
+			return lchan->irq;
+
+		ret = devm_request_irq(dev, lchan->irq, loongson2_cmc_dma_chan_irq, IRQF_SHARED,
+				       dev_name(chan2dev(lchan)), lchan);
+		if (ret)
+			return ret;
+	}
+
+	ret = loongson2_cmc_dma_acpi_controller_register(lddev);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to register dma controller with ACPI.\n");
+
+	ret = loongson2_cmc_dma_of_controller_register(lddev);
+	if (ret)
+		return dev_err_probe(dev, ret, "Failed to register dma controller with FDT.\n");
+
+	dev_info(dev, "Loongson-2 Multi-Channel DMA Controller registered successfully.\n");
+
+	return 0;
+}
+
+static void loongson2_cmc_dma_remove(struct platform_device *pdev)
+{
+	of_dma_controller_free(pdev->dev.of_node);
+}
+
+static const struct of_device_id loongson2_cmc_dma_of_match[] = {
+	{ .compatible = "loongson,ls2k0300-dma", .data = &ls2k0300_cmc_dma_config },
+	{ .compatible = "loongson,ls2k3000-dma", .data = &ls2k3000_cmc_dma_config },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, loongson2_cmc_dma_of_match);
+
+static const struct acpi_device_id loongson2_cmc_dma_acpi_match[] = {
+	{ "LOON0014", .driver_data = (kernel_ulong_t)&ls2k3000_cmc_dma_config },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(acpi, loongson2_cmc_dma_acpi_match);
+
+static struct platform_driver loongson2_cmc_dma_driver = {
+	.driver = {
+		.name = "loongson2-apb-cmc-dma",
+		.of_match_table = loongson2_cmc_dma_of_match,
+		.acpi_match_table = loongson2_cmc_dma_acpi_match,
+	},
+	.probe = loongson2_cmc_dma_probe,
+	.remove = loongson2_cmc_dma_remove,
+};
+module_platform_driver(loongson2_cmc_dma_driver);
+
+MODULE_DESCRIPTION("Looongson-2 Chain Multi-Channel DMA Controller driver");
+MODULE_AUTHOR("Loongson Technology Corporation Limited");
+MODULE_LICENSE("GPL");
-- 
2.52.0


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

* Re: [PATCH v4 1/6] dmaengine: loongson: New directory for Loongson DMA controllers drivers
  2026-03-07  3:25 ` [PATCH v4 1/6] dmaengine: loongson: New directory for Loongson DMA controllers drivers Binbin Zhou
@ 2026-03-09  5:05   ` Keguang Zhang
  0 siblings, 0 replies; 10+ messages in thread
From: Keguang Zhang @ 2026-03-09  5:05 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, linux-mips, Frank Li

Reviewed-by: Keguang Zhang <keguang.zhang@gmail.com>

On Sat, Mar 7, 2026 at 11:25 AM Binbin Zhou <zhoubinbin@loongson.cn> wrote:
>
> Gather the Loongson DMA controllers under drivers/dma/loongson/
>
> Reviewed-by: Frank Li <Frank.Li@nxp.com>
> Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
> ---
>  MAINTAINERS                                   |  3 +-
>  drivers/dma/Kconfig                           | 25 ++--------------
>  drivers/dma/Makefile                          |  3 +-
>  drivers/dma/loongson/Kconfig                  | 30 +++++++++++++++++++
>  drivers/dma/loongson/Makefile                 |  3 ++
>  .../dma/{ => loongson}/loongson1-apb-dma.c    |  4 +--
>  .../dma/{ => loongson}/loongson2-apb-dma.c    |  4 +--
>  7 files changed, 42 insertions(+), 30 deletions(-)
>  create mode 100644 drivers/dma/loongson/Kconfig
>  create mode 100644 drivers/dma/loongson/Makefile
>  rename drivers/dma/{ => loongson}/loongson1-apb-dma.c (99%)
>  rename drivers/dma/{ => loongson}/loongson2-apb-dma.c (99%)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 55af015174a5..e8cd1e2dac13 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -14953,7 +14953,7 @@ M:      Binbin Zhou <zhoubinbin@loongson.cn>
>  L:     dmaengine@vger.kernel.org
>  S:     Maintained
>  F:     Documentation/devicetree/bindings/dma/loongson,ls2x-apbdma.yaml
> -F:     drivers/dma/loongson2-apb-dma.c
> +F:     drivers/dma/loongson/loongson2-apb-dma.c
>
>  LOONGSON LS2X I2C DRIVER
>  M:     Binbin Zhou <zhoubinbin@loongson.cn>
> @@ -17721,6 +17721,7 @@ F:      arch/mips/boot/dts/loongson/loongson1*
>  F:     arch/mips/configs/loongson1_defconfig
>  F:     arch/mips/loongson32/
>  F:     drivers/*/*loongson1*
> +F:     drivers/dma/loongson/loongson1-apb-dma.c
>  F:     drivers/mtd/nand/raw/loongson-nand-controller.c
>  F:     drivers/net/ethernet/stmicro/stmmac/dwmac-loongson1.c
>  F:     sound/soc/loongson/loongson1_ac97.c
> diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
> index 66cda7cc9f7a..1b84c5b11654 100644
> --- a/drivers/dma/Kconfig
> +++ b/drivers/dma/Kconfig
> @@ -376,29 +376,6 @@ config K3_DMA
>           Support the DMA engine for Hisilicon K3 platform
>           devices.
>
> -config LOONGSON1_APB_DMA
> -       tristate "Loongson1 APB DMA support"
> -       depends on MACH_LOONGSON32 || COMPILE_TEST
> -       select DMA_ENGINE
> -       select DMA_VIRTUAL_CHANNELS
> -       help
> -         This selects support for the APB DMA controller in Loongson1 SoCs,
> -         which is required by Loongson1 NAND and audio support.
> -
> -config LOONGSON2_APB_DMA
> -       tristate "Loongson2 APB DMA support"
> -       depends on LOONGARCH || COMPILE_TEST
> -       select DMA_ENGINE
> -       select DMA_VIRTUAL_CHANNELS
> -       help
> -         Support for the Loongson2 APB DMA controller driver. The
> -         DMA controller is having single DMA channel which can be
> -         configured for different peripherals like audio, nand, sdio
> -         etc which is in APB bus.
> -
> -         This DMA controller transfers data from memory to peripheral fifo.
> -         It does not support memory to memory data transfer.
> -
>  config LPC18XX_DMAMUX
>         bool "NXP LPC18xx/43xx DMA MUX for PL080"
>         depends on ARCH_LPC18XX || COMPILE_TEST
> @@ -774,6 +751,8 @@ source "drivers/dma/fsl-dpaa2-qdma/Kconfig"
>
>  source "drivers/dma/lgm/Kconfig"
>
> +source "drivers/dma/loongson/Kconfig"
> +
>  source "drivers/dma/stm32/Kconfig"
>
>  # clients
> diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
> index a54d7688392b..963b10494ee5 100644
> --- a/drivers/dma/Makefile
> +++ b/drivers/dma/Makefile
> @@ -49,8 +49,6 @@ obj-$(CONFIG_INTEL_IDMA64) += idma64.o
>  obj-$(CONFIG_INTEL_IOATDMA) += ioat/
>  obj-y += idxd/
>  obj-$(CONFIG_K3_DMA) += k3dma.o
> -obj-$(CONFIG_LOONGSON1_APB_DMA) += loongson1-apb-dma.o
> -obj-$(CONFIG_LOONGSON2_APB_DMA) += loongson2-apb-dma.o
>  obj-$(CONFIG_LPC18XX_DMAMUX) += lpc18xx-dmamux.o
>  obj-$(CONFIG_LPC32XX_DMAMUX) += lpc32xx-dmamux.o
>  obj-$(CONFIG_MILBEAUT_HDMAC) += milbeaut-hdmac.o
> @@ -87,6 +85,7 @@ obj-$(CONFIG_FSL_DPAA2_QDMA) += fsl-dpaa2-qdma/
>  obj-$(CONFIG_INTEL_LDMA) += lgm/
>
>  obj-y += amd/
> +obj-y += loongson/
>  obj-y += mediatek/
>  obj-y += qcom/
>  obj-y += stm32/
> diff --git a/drivers/dma/loongson/Kconfig b/drivers/dma/loongson/Kconfig
> new file mode 100644
> index 000000000000..0a865a8fd3a6
> --- /dev/null
> +++ b/drivers/dma/loongson/Kconfig
> @@ -0,0 +1,30 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +#
> +# Loongson DMA controllers drivers
> +#
> +if MACH_LOONGSON32 || MACH_LOONGSON64 || COMPILE_TEST
> +
> +config LOONGSON1_APB_DMA
> +       tristate "Loongson1 APB DMA support"
> +       depends on MACH_LOONGSON32 || COMPILE_TEST
> +       select DMA_ENGINE
> +       select DMA_VIRTUAL_CHANNELS
> +       help
> +         This selects support for the APB DMA controller in Loongson1 SoCs,
> +         which is required by Loongson1 NAND and audio support.
> +
> +config LOONGSON2_APB_DMA
> +       tristate "Loongson2 APB DMA support"
> +       depends on MACH_LOONGSON64 || COMPILE_TEST
> +       select DMA_ENGINE
> +       select DMA_VIRTUAL_CHANNELS
> +       help
> +         Support for the Loongson2 APB DMA controller driver. The
> +         DMA controller is having single DMA channel which can be
> +         configured for different peripherals like audio, nand, sdio
> +         etc which is in APB bus.
> +
> +         This DMA controller transfers data from memory to peripheral fifo.
> +         It does not support memory to memory data transfer.
> +
> +endif
> diff --git a/drivers/dma/loongson/Makefile b/drivers/dma/loongson/Makefile
> new file mode 100644
> index 000000000000..6cdd08065e92
> --- /dev/null
> +++ b/drivers/dma/loongson/Makefile
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +obj-$(CONFIG_LOONGSON1_APB_DMA) += loongson1-apb-dma.o
> +obj-$(CONFIG_LOONGSON2_APB_DMA) += loongson2-apb-dma.o
> diff --git a/drivers/dma/loongson1-apb-dma.c b/drivers/dma/loongson/loongson1-apb-dma.c
> similarity index 99%
> rename from drivers/dma/loongson1-apb-dma.c
> rename to drivers/dma/loongson/loongson1-apb-dma.c
> index 2e347aba9af8..89786cbd20ab 100644
> --- a/drivers/dma/loongson1-apb-dma.c
> +++ b/drivers/dma/loongson/loongson1-apb-dma.c
> @@ -16,8 +16,8 @@
>  #include <linux/platform_device.h>
>  #include <linux/slab.h>
>
> -#include "dmaengine.h"
> -#include "virt-dma.h"
> +#include "../dmaengine.h"
> +#include "../virt-dma.h"
>
>  /* Loongson-1 DMA Control Register */
>  #define LS1X_DMA_CTRL          0x0
> diff --git a/drivers/dma/loongson2-apb-dma.c b/drivers/dma/loongson/loongson2-apb-dma.c
> similarity index 99%
> rename from drivers/dma/loongson2-apb-dma.c
> rename to drivers/dma/loongson/loongson2-apb-dma.c
> index b981475e6779..fc7d9f4a96ec 100644
> --- a/drivers/dma/loongson2-apb-dma.c
> +++ b/drivers/dma/loongson/loongson2-apb-dma.c
> @@ -17,8 +17,8 @@
>  #include <linux/platform_device.h>
>  #include <linux/slab.h>
>
> -#include "dmaengine.h"
> -#include "virt-dma.h"
> +#include "../dmaengine.h"
> +#include "../virt-dma.h"
>
>  /* Global Configuration Register */
>  #define LDMA_ORDER_ERG         0x0
> --
> 2.52.0
>


-- 
Best regards,

Keguang Zhang

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

* Re: [PATCH v4 0/6] dmaengine: Add Loongson Multi-Channel DMA controller support
  2026-03-07  3:25 [PATCH v4 0/6] dmaengine: Add Loongson Multi-Channel DMA controller support Binbin Zhou
                   ` (5 preceding siblings ...)
  2026-03-07  3:25 ` [PATCH v4 6/6] dmaengine: loongson: New driver for the " Binbin Zhou
@ 2026-03-09 13:32 ` Huacai Chen
  2026-03-17 11:28 ` Vinod Koul
  7 siblings, 0 replies; 10+ messages in thread
From: Huacai Chen @ 2026-03-09 13:32 UTC (permalink / raw)
  To: Binbin Zhou
  Cc: Binbin Zhou, Huacai Chen, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Vinod Koul, Frank Li, dmaengine, Xuerui Wang,
	loongarch, devicetree, Keguang Zhang, linux-mips

On Sat, Mar 7, 2026 at 11:25 AM Binbin Zhou <zhoubinbin@loongson.cn> wrote:
>
> Hi all:
>
> This patchset introduces the Loongson multi-channel DMA controller,
> which is present in the Loongson-2K0300 and Loongson-2K3000 processors.
>
> It is a multi-channel controller that enables data transfers from memory
> to memory, device to memory, and memory to device, as well as channel
> prioritization configurable through the channel configuration registers.
>
> Additionally, since multiple distinct types of DMA controllers exist on
> the Loongson platform, I have attempted to consolidate all Loongson DMA
> drivers into a new directory named `Loongson` for easier management.
For the whole series,
Reviewed-by: Huacai Chen <chenhuacai@loongson.cn>

>
> Thanks.
> Binbin
>
> ===========
> V4:
> - Rebase on dmaengine/next tree;
> - Add Reviewed-by tags from Frank and Rob, thanks;
>
> patch(1/6):
>  - Add `depends on` restrictions.
>
> patch(6/6):
>  - Move loongson2_cmc_dma_config{..} close to its users.
>
> Link to V3:
> https://lore.kernel.org/dmaengine/cover.1771989595.git.zhoubinbin@loongson.cn/
>
> V3:
> - Rebase on dmaengine/next tree;
>
> patch(1/6):
>  - Keep alphabet order;
>
> patch(2/6):
>  - Add Reviewed-by tag from Frank, thanks;
>
> patch(3/6)/(4/6):
>  - New patches, format loongson2-apb-dma driver code;
>
> patch(5/6):
>  - Add description for `interrupts` property;
>
> patch(6/6):
>  - Use ffs() helper make the code cleaner;
>  - Refact loongson2_cmc_dma_chan_irq();
>  - Simplify locking with guard() and scoped_guard();
>  - kzalloc()->kzalloc_flex().
>
> Link to V2:
> https://lore.kernel.org/all/cover.1770605931.git.zhoubinbin@loongson.cn/
>
> V2:
> patch(1/4):
>  - Update loongson1-apb-dma.c entry in MAINTAINERS.
>
> patch(2/4):
>  - New patch, use dmaenginem_async_device_register() helper.
>
> patch(3/4):
>  - `additionalProperties: false` replaced by
>    `unevaluatedProperties: false`.
>
> patch(4/4):
>  - Rename filename as loongson2-apb-cmc-dma.c;
>  - Rename Kconfig item as LOONGSON2_APB_CMC_DMA;
>  - Rename the variable prefix as `loongson2_cmc_dma`;
>  - Use dmaenginem_async_device_register() helper;
>  - Drop 'dma_' prefix in struct loongson2_mdma_chan_reg;
>  - Use struct_size();
>
> Link to V1:
> https://lore.kernel.org/all/cover.1770119693.git.zhoubinbin@loongson.cn/
>
> Binbin Zhou (6):
>   dmaengine: loongson: New directory for Loongson DMA controllers
>     drivers
>   dmaengine: loongson: loongson2-apb: Convert to
>     dmaenginem_async_device_register()
>   dmaengine: loongson: loongson2-apb: Convert to devm_clk_get_enabled()
>   dmaengine: loongson: loongson2-apb: Simplify locking with guard() and
>     scoped_guard()
>   dt-bindings: dmaengine: Add Loongson Multi-Channel DMA controller
>   dmaengine: loongson: New driver for the Loongson Multi-Channel DMA
>     controller
>
>  .../bindings/dma/loongson,ls2k0300-dma.yaml   |  81 ++
>  MAINTAINERS                                   |   7 +-
>  drivers/dma/Kconfig                           |  25 +-
>  drivers/dma/Makefile                          |   3 +-
>  drivers/dma/loongson/Kconfig                  |  41 +
>  drivers/dma/loongson/Makefile                 |   4 +
>  .../dma/{ => loongson}/loongson1-apb-dma.c    |   4 +-
>  drivers/dma/loongson/loongson2-apb-cmc-dma.c  | 730 ++++++++++++++++++
>  .../dma/{ => loongson}/loongson2-apb-dma.c    |  93 +--
>  9 files changed, 903 insertions(+), 85 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/dma/loongson,ls2k0300-dma.yaml
>  create mode 100644 drivers/dma/loongson/Kconfig
>  create mode 100644 drivers/dma/loongson/Makefile
>  rename drivers/dma/{ => loongson}/loongson1-apb-dma.c (99%)
>  create mode 100644 drivers/dma/loongson/loongson2-apb-cmc-dma.c
>  rename drivers/dma/{ => loongson}/loongson2-apb-dma.c (91%)
>
>
> base-commit: c8e9b1d9febc83ee94944695a07cfd40a1b29743
> --
> 2.52.0
>

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

* Re: [PATCH v4 0/6] dmaengine: Add Loongson Multi-Channel DMA controller support
  2026-03-07  3:25 [PATCH v4 0/6] dmaengine: Add Loongson Multi-Channel DMA controller support Binbin Zhou
                   ` (6 preceding siblings ...)
  2026-03-09 13:32 ` [PATCH v4 0/6] dmaengine: Add Loongson Multi-Channel DMA controller support Huacai Chen
@ 2026-03-17 11:28 ` Vinod Koul
  7 siblings, 0 replies; 10+ messages in thread
From: Vinod Koul @ 2026-03-17 11:28 UTC (permalink / raw)
  To: Binbin Zhou, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Frank Li, dmaengine, Huacai Chen, Binbin Zhou
  Cc: Huacai Chen, Xuerui Wang, loongarch, devicetree, Keguang Zhang,
	linux-mips


On Sat, 07 Mar 2026 11:25:09 +0800, Binbin Zhou wrote:
> This patchset introduces the Loongson multi-channel DMA controller,
> which is present in the Loongson-2K0300 and Loongson-2K3000 processors.
> 
> It is a multi-channel controller that enables data transfers from memory
> to memory, device to memory, and memory to device, as well as channel
> prioritization configurable through the channel configuration registers.
> 
> [...]

Applied, thanks!

[1/6] dmaengine: loongson: New directory for Loongson DMA controllers drivers
      commit: ffee2dc04e7e06534aaa4fd51ef89645b809b6b8
[2/6] dmaengine: loongson: loongson2-apb: Convert to dmaenginem_async_device_register()
      commit: 7d348227f4961bbf21255281438ee3aebe12830f
[3/6] dmaengine: loongson: loongson2-apb: Convert to devm_clk_get_enabled()
      commit: bdf1621a6a67b6327e2a26a1d47bffcde3be3b26
[4/6] dmaengine: loongson: loongson2-apb: Simplify locking with guard() and scoped_guard()
      commit: 9de4303fc04977d15b257726a6519caca687c43a
[5/6] dt-bindings: dmaengine: Add Loongson Multi-Channel DMA controller
      commit: 7a65e81e8e2e58b0db9f2dedda410ee2b6042859
[6/6] dmaengine: loongson: New driver for the Loongson Multi-Channel DMA controller
      commit: 1c0028e725f156ebabe68b0025f9c8e7a6170ffd

Best regards,
-- 
~Vinod



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

end of thread, other threads:[~2026-03-17 11:28 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-07  3:25 [PATCH v4 0/6] dmaengine: Add Loongson Multi-Channel DMA controller support Binbin Zhou
2026-03-07  3:25 ` [PATCH v4 1/6] dmaengine: loongson: New directory for Loongson DMA controllers drivers Binbin Zhou
2026-03-09  5:05   ` Keguang Zhang
2026-03-07  3:25 ` [PATCH v4 2/6] dmaengine: loongson: loongson2-apb: Convert to dmaenginem_async_device_register() Binbin Zhou
2026-03-07  3:25 ` [PATCH v4 3/6] dmaengine: loongson: loongson2-apb: Convert to devm_clk_get_enabled() Binbin Zhou
2026-03-07  3:25 ` [PATCH v4 4/6] dmaengine: loongson: loongson2-apb: Simplify locking with guard() and scoped_guard() Binbin Zhou
2026-03-07  3:25 ` [PATCH v4 5/6] dt-bindings: dmaengine: Add Loongson Multi-Channel DMA controller Binbin Zhou
2026-03-07  3:25 ` [PATCH v4 6/6] dmaengine: loongson: New driver for the " Binbin Zhou
2026-03-09 13:32 ` [PATCH v4 0/6] dmaengine: Add Loongson Multi-Channel DMA controller support Huacai Chen
2026-03-17 11:28 ` Vinod Koul

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