Skip to contentSkip to conversation

Add Loongson-2K0300 APB DMA mux support

PATCH Superseded Binbin Zhou 5 participants 15 messages Sep 22 – Oct 4

Subsystems (2)

v1 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

cover Add Loongson-2K0300 APB DMA mux support

Hi all:

Loongson-2K0300 integrates a CMC DMA controller with 8 physical channels. Peripheral DMA requests are not hard-wired to these channels; they are routed through the CHIP_CTRL13/14/15 DMA_MAP fields.

This series adds support for that DMA request router.

Patch 1 adds the DT binding constants for the peripheral request IDs. Patch 2 adds the DT binding documentation for the DMA mux. Patch 3 adds the driver, Kconfig symbol and Makefile entry.

The driver registers with of_dma_router_register(). In the route allocation callback it validates the <request> <channel> <flags> specifier, programs the SoC-specific route register, and forwards <channel> <flags> to the parent CMC DMA controller.

Thanks.
Binbin.

Binbin Zhou (3):
dt-bindings: dma: loongson: Add Loongson-2 DMA mux request IDs
dt-bindings: dma: loongson: Add Loongson-2K0300 DMA mux support
dmaengine: loongson: Add Loongson-2K0300 APB DMA mux driver

 .../dma/loongson,ls2k0300-dmamux.yaml         |  49 +++
 MAINTAINERS                                   |   3 +
 drivers/dma/loongson/Kconfig                  |  13 +
 drivers/dma/loongson/Makefile                 |   1 +
 drivers/dma/loongson/loongson2-apb-dmamux.c   | 380 ++++++++++++++++++
 .../dt-bindings/dma/loongson,ls2k-dmamux.h    |  56 +++
 6 files changed, 502 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.yaml
 create mode 100644 drivers/dma/loongson/loongson2-apb-dmamux.c
 create mode 100644 include/dt-bindings/dma/loongson,ls2k-dmamux.h

base-commit: 0a8dda0a15d3926422d286567f945a05328a4ac6

Signature · 1 line
2.52.0
#1 lore raw

PATCH 1/3 dt-bindings: dma: loongson: Add Loongson-2 DMA mux request IDs

Add DT binding constants for the Loongson-2 DMA mux.

The header defines the peripheral request IDs used in the DMA specifier <request channel flags> for Loongson-2K0300, covering UART0-9, I2C0-3, SPI2/3, I2S, ADC and CAN0-3. It also documents the channel-pair semantics for peripherals that use paired DMA channels.

---
 MAINTAINERS                                   |  1 +
 .../dt-bindings/dma/loongson,ls2k-dmamux.h    | 56 +++++++++++++++++++
 2 files changed, 57 insertions(+)
 create mode 100644 include/dt-bindings/dma/loongson,ls2k-dmamux.h
MAINTAINERS +1 -0
index c31b81eb42d7..b021fb623446 100644
@@ -15347,6 +15347,7 @@ 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
+F:	include/dt-bindings/dma/loongson,ls2k-dmamux.h
 
 LOONGSON LS2X I2C DRIVER
 M:	Binbin Zhou <[email protected]>
include/dt-bindings/dma/loongson,ls2k-dmamux.h new file +56 -0

56 lines not on this page · raw message

Signature · 1 line
2.52.0
#2 lore raw

Re: [PATCH 1/3] dt-bindings: dma: loongson: Add Loongson-2 DMA mux request IDs

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [Medium] dma: loongson: hardcoding physical DMA channel in device tree - [Low] dma: loongson: expose…

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] dma: loongson: hardcoding physical DMA channel in device tree
- [Low] dma: loongson: expose LS2K0300_DMA_REQ_MAX in binding header
--

commit e4e9feb04bfe5cf0cae531423dc7021c3d20e99c
Author: Binbin Zhou <[email protected]>

dt-bindings: dma: loongson: Add Loongson-2 DMA mux request IDs

Add DT binding constants for the Loongson-2 DMA mux. It covers UART, I2C, SPI, I2S, ADC, and CAN peripherals for Loongson-2K0300. It also documents channel-pair semantics for peripherals using paired channels.

Quoted 6 lines
include/dt-bindings/dma/loongson,ls2k-dmamux.h new file +0 -0
new file mode 100644
index 0000000000000..dcc8df86befbb

include/dt-bindings/dma/loongson,ls2k-dmamux.h line 8

Quoted 8 lines
include/dt-bindings/dma/loongson,ls2k-dmamux.h new file +8 -0
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * Loongson-2 SoC DMA Mux dt-bindings
+ *
+ * DMA specifier format:
+ *   dmas = <&dma_mux <request> <channel> <flags>>;
+ *
+ * The <channel> cell selects a physical CMC DMA channel (0-7)

[Severity: Medium]
Does this design hardcode software resource allocation policy into the hardware description?

Device trees should describe immutable hardware wiring rather than software policy. Since the Loongson DMA mux is flexible (e.g., UART0 can be mapped to one of 4 channel pairs via a 2-bit register field), shouldn't the OS DMA engine dynamically allocate an available channel from a pool and program the mux accordingly?

Statically assigning the channel in the DT shifts this policy to the hardware description, preventing dynamic allocation and risking runtime resource conflicts if multiple peripherals concurrently request the same statically assigned channel.

Quoted 6 lines
include/dt-bindings/dma/loongson,ls2k-dmamux.h new file +6 -0
+ * For peripherals using channel pairs (UART, I2C, SPI2/3, I2S),
+ * the channel number selects the pair: 0/1 -> pair 0, 2/3 -> pair 1,
+ * 4/5 -> pair 2, 6/7 -> pair 3.
+ *
+ * Copyright (C) 2026 Loongson Technology Corporation Limited
+ */

[ ... ]

include/dt-bindings/dma/loongson,ls2k-dmamux.h line 54

Quoted 8 lines
include/dt-bindings/dma/loongson,ls2k-dmamux.h new file +8 -0
+/* CAN-FD controllers */
+#define LS2K0300_DMA_CAN0		18
+#define LS2K0300_DMA_CAN1		19
+#define LS2K0300_DMA_CAN2		20
+#define LS2K0300_DMA_CAN3		21
+
+/* Total number of 2K0300 DMA requests */
+#define LS2K0300_DMA_REQ_MAX		22

[Severity: Low]
Is it safe to expose LS2K0300_DMA_REQ_MAX in a device tree binding header?

Device tree binding headers define the stable ABI between the hardware description and the operating system. Exposing a _MAX macro is a known DT anti-pattern.

If future hardware revisions or bindings add new request IDs, updating the _MAX macro breaks backward compatibility with older compiled device trees or drivers that rely on the previous value, potentially leading to out-of-bounds accesses or ABI mismatches.

Should this macro be removed from the ABI header to prevent misuse?

Quoted 2 lines
include/dt-bindings/dma/loongson,ls2k-dmamux.h new file +2 -0
+
+#endif /* _DT_BINDINGS_DMA_LOONGSON_LS2K_DMAMUX_H */
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=1
#3 lore raw

Re: [PATCH 1/3] dt-bindings: dma: loongson: Add Loongson-2 DMA mux request IDs

In reply to Binbin Zhou
Quoted 76 lines · Binbin Zhou

Add DT binding constants for the Loongson-2 DMA mux.

The header defines the peripheral request IDs used in the DMA specifier <request channel flags> for Loongson-2K0300, covering UART0-9, I2C0-3, SPI2/3, I2S, ADC and CAN0-3. It also documents the channel-pair semantics for peripherals that use paired DMA channels.

---
 MAINTAINERS                                   |  1 +
 .../dt-bindings/dma/loongson,ls2k-dmamux.h    | 56 +++++++++++++++++++
 2 files changed, 57 insertions(+)
 create mode 100644 include/dt-bindings/dma/loongson,ls2k-dmamux.h
MAINTAINERS +1 -0
index c31b81eb42d7..b021fb623446 100644
@@ -15347,6 +15347,7 @@ 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
+F:	include/dt-bindings/dma/loongson,ls2k-dmamux.h
 
 LOONGSON LS2X I2C DRIVER
 M:	Binbin Zhou <[email protected]>
include/dt-bindings/dma/loongson,ls2k-dmamux.h new file +44 -0
new file mode 100644
index 000000000000..dcc8df86befb
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+/*
+ * Loongson-2 SoC DMA Mux dt-bindings
+ *
+ * DMA specifier format:
+ *   dmas = <&dma_mux <request> <channel> <flags>>;
+ *
+ * The <channel> cell selects a physical CMC DMA channel (0-7)
+ * For peripherals using channel pairs (UART, I2C, SPI2/3, I2S),
+ * the channel number selects the pair: 0/1 -> pair 0, 2/3 -> pair 1,
+ * 4/5 -> pair 2, 6/7 -> pair 3.
+ *
+ * Copyright (C) 2026 Loongson Technology Corporation Limited
+ */
+
+#ifndef _DT_BINDINGS_DMA_LOONGSON_LS2K_DMAMUX_H
+#define _DT_BINDINGS_DMA_LOONGSON_LS2K_DMAMUX_H
+
+/* UART controllers */
+#define LS2K0300_DMA_UART0		0
+#define LS2K0300_DMA_UART1		1
+#define LS2K0300_DMA_UART2		2
+#define LS2K0300_DMA_UART3		3
+#define LS2K0300_DMA_UART4		4
+#define LS2K0300_DMA_UART5		5
+#define LS2K0300_DMA_UART6		6
+#define LS2K0300_DMA_UART7		7
+#define LS2K0300_DMA_UART8		8
+#define LS2K0300_DMA_UART9		9
+
+/* I2C controllers */
+#define LS2K0300_DMA_I2C0		10
+#define LS2K0300_DMA_I2C1		11
+#define LS2K0300_DMA_I2C2		12
+#define LS2K0300_DMA_I2C3		13
+
+/* SPI controllers (IO mode) */
+#define LS2K0300_DMA_SPI2		14
+#define LS2K0300_DMA_SPI3		15
+
+/* I2S controller */
+#define LS2K0300_DMA_I2S		16
+
+/* ADC controller */

include/dt-bindings/dma/loongson,ls2k-dmamux.h line 54

Quoted 10 lines
include/dt-bindings/dma/loongson,ls2k-dmamux.h new file +10 -0
+#define LS2K0300_DMA_ADC		17
+
+/* CAN-FD controllers */
+#define LS2K0300_DMA_CAN0		18
+#define LS2K0300_DMA_CAN1		19
+#define LS2K0300_DMA_CAN2		20
+#define LS2K0300_DMA_CAN3		21
+
+/* Total number of 2K0300 DMA requests */
+#define LS2K0300_DMA_REQ_MAX		22

REQ_MAX is not ABI and your driver have not use it.

Frnak

Quoted 5 lines
include/dt-bindings/dma/loongson,ls2k-dmamux.h new file +2 -1
+
+#endif /* _DT_BINDINGS_DMA_LOONGSON_LS2K_DMAMUX_H */
--

2.52.0

#4 lore raw

PATCH 2/3 dt-bindings: dma: loongson: Add Loongson-2K0300 DMA mux support

In reply to Binbin Zhou

The DMA mux on Loongson-2K0300 is not a standalone hardware block. It is implemented as part of the chip configuration registers CHIP_CTRL13/14/15, which route peripheral DMA requests to the physical channels of the CMC DMA controller.

Document this as a DMA router. The #dma-cells = <3> specifier contains the peripheral request ID, the target CMC DMA channel, and channel configuration flags forwarded to the parent DMA controller.

---
 .../dma/loongson,ls2k0300-dmamux.yaml         | 49 +++++++++++++++++++
 MAINTAINERS                                   |  1 +
 2 files changed, 50 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.yaml
Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.yaml new file +49 -0
new file mode 100644
index 000000000000..12b8f8af51e9
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/dma/loongson,ls2k0300-dmamux.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Loongson-2 APB DMA MUX (DMA request router)
+
+maintainers:
+  - Binbin Zhou <[email protected]>
+
+allOf:
+  - $ref: /schemas/dma/dma-router.yaml#
+
+properties:
+  compatible:
+    const: loongson,ls2k0300-dmamux
+
+  reg:
+    maxItems: 1
+
+  '#dma-cells':
+    const: 3
+    description: |
+      Each cell represents the following:
+      1. The peripheral request id (see
+         include/dt-bindings/dma/loongson,ls2k-dmamux.h)
+      2. The physical CMC DMA channel (0-7) that is passed to the real
+         DMA controller
+      3. Bitfield representing DMA channel configuration that is passed
+         to the real DMA controller
+
+required:
+  - compatible
+  - reg
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/dma/loongson,ls2k-dmamux.h>
+    dma-router@16000134 {
+        compatible = "loongson,ls2k0300-dmamux";
+        reg = <0x16000134 0xc>;
+        #dma-cells = <3>;
+        dma-masters = <&dma>;
+        dma-requests = <22>;
+    };
+...
MAINTAINERS +1 -0
index b021fb623446..dc976938c58c 100644
@@ -15344,6 +15344,7 @@ M:	Binbin Zhou <[email protected]>
 L:	[email protected]
 S:	Maintained
 F:	Documentation/devicetree/bindings/dma/loongson,ls2k0300-dma.yaml
+F:	Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.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
Signature · 1 line
2.52.0
#5 lore raw

Re: [PATCH 2/3] dt-bindings: dma: loongson: Add Loongson-2K0300 DMA mux support

Quoted 41 lines · Binbin Zhou

The DMA mux on Loongson-2K0300 is not a standalone hardware block. It is implemented as part of the chip configuration registers CHIP_CTRL13/14/15, which route peripheral DMA requests to the physical channels of the CMC DMA controller.

Document this as a DMA router. The #dma-cells = <3> specifier contains the peripheral request ID, the target CMC DMA channel, and channel configuration flags forwarded to the parent DMA controller.

---
 .../dma/loongson,ls2k0300-dmamux.yaml         | 49 +++++++++++++++++++
 MAINTAINERS                                   |  1 +
 2 files changed, 50 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.yaml
Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.yaml new file +19 -0
new file mode 100644
index 000000000000..12b8f8af51e9
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/dma/loongson,ls2k0300-dmamux.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Loongson-2 APB DMA MUX (DMA request router)
+
+maintainers:
+  - Binbin Zhou <[email protected]>
+
+allOf:
+  - $ref: /schemas/dma/dma-router.yaml#
+
+properties:
+  compatible:
+    const: loongson,ls2k0300-dmamux
+
+  reg:

Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.yaml line 29

Quoted 10 lines
Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.yaml new file +10 -0
+    maxItems: 1
+
+  '#dma-cells':
+    const: 3
+    description: |
+      Each cell represents the following:
+      1. The peripheral request id (see
+         include/dt-bindings/dma/loongson,ls2k-dmamux.h)
+      2. The physical CMC DMA channel (0-7) that is passed to the real
+         DMA controller

Do you have restriction between request id and channel? Does any request id route to any channel?

Frank

Quoted 35 lines
Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.yaml new file +20 -0
+      3. Bitfield representing DMA channel configuration that is passed
+         to the real DMA controller
+
+required:
+  - compatible
+  - reg
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/dma/loongson,ls2k-dmamux.h>
+    dma-router@16000134 {
+        compatible = "loongson,ls2k0300-dmamux";
+        reg = <0x16000134 0xc>;
+        #dma-cells = <3>;
+        dma-masters = <&dma>;
+        dma-requests = <22>;
+    };
+...
MAINTAINERS +1 -1
index b021fb623446..dc976938c58c 100644
@@ -15344,6 +15344,7 @@ M:	Binbin Zhou <[email protected]>
 L:	[email protected]
 S:	Maintained
 F:	Documentation/devicetree/bindings/dma/loongson,ls2k0300-dma.yaml
+F:	Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.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
--

2.52.0

#6 lore raw

Re: [PATCH 2/3] dt-bindings: dma: loongson: Add Loongson-2K0300 DMA mux support

Hi Frank:

Thanks for your reply.

Quoted 48 lines · Frank Li
Quoted 46 lines · Binbin Zhou

The DMA mux on Loongson-2K0300 is not a standalone hardware block. It is implemented as part of the chip configuration registers CHIP_CTRL13/14/15, which route peripheral DMA requests to the physical channels of the CMC DMA controller.

Document this as a DMA router. The #dma-cells = <3> specifier contains the peripheral request ID, the target CMC DMA channel, and channel configuration flags forwarded to the parent DMA controller.

---
 .../dma/loongson,ls2k0300-dmamux.yaml         | 49 +++++++++++++++++++
 MAINTAINERS                                   |  1 +
 2 files changed, 50 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.yaml
Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.yaml new file +24 -0
new file mode 100644
index 000000000000..12b8f8af51e9
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/dma/loongson,ls2k0300-dmamux.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Loongson-2 APB DMA MUX (DMA request router)
+
+maintainers:
+  - Binbin Zhou <[email protected]>
+
+allOf:
+  - $ref: /schemas/dma/dma-router.yaml#
+
+properties:
+  compatible:
+    const: loongson,ls2k0300-dmamux
+
+  reg:
+    maxItems: 1
+
+  '#dma-cells':
+    const: 3
+    description: |
Quoted 8 lines

Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.yaml line 29

Quoted 5 lines
Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.yaml new file +5 -0
+      Each cell represents the following:
+      1. The peripheral request id (see
+         include/dt-bindings/dma/loongson,ls2k-dmamux.h)
+      2. The physical CMC DMA channel (0-7) that is passed to the real
+         DMA controller

Do you have restriction between request id and channel? Does any request id route to any channel?

Yes, according to the description of this register’s bit fields in the manual, DMA channels are not mapped arbitrarily.

We can broadly categorize them as follows:
1. First, most peripherals that use this DMA controller request adjacent DMA channels (TX/RX) in pairs.
Let’s take I2S as an example.
Bits [17:16] at address 0x16000138 controls the I2S DMA channel mapping:
00: Channels 0, 1
01: Channels 2, 3
10: Channels 4, 5
11: Channels 6, 7
2. The SM/DES/AES module uses only channels 0 and 1, corresponding to the bit field values 00 and 01, respectively.
3. The can/adc module is single-channel, so each bit in the corresponding bit field corresponds to channels 0–7.

Quoted 29 lines

Frank

Quoted 27 lines
Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.yaml new file +20 -0
+      3. Bitfield representing DMA channel configuration that is passed
+         to the real DMA controller
+
+required:
+  - compatible
+  - reg
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/dma/loongson,ls2k-dmamux.h>
+    dma-router@16000134 {
+        compatible = "loongson,ls2k0300-dmamux";
+        reg = <0x16000134 0xc>;
+        #dma-cells = <3>;
+        dma-masters = <&dma>;
+        dma-requests = <22>;
+    };
+...
MAINTAINERS +0 -0
index b021fb623446..dc976938c58c 100644
@@ -15344,6 +15344,7 @@ M:    Binbin Zhou <[email protected]>
 L:   [email protected]
 S:   Maintained
Quoted 8 lines
Quoted 8 lines
MAINTAINERS +1 -1
 F:   Documentation/devicetree/bindings/dma/loongson,ls2k0300-dma.yaml
+F:   Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.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
--

2.52.0

--
Thanks.
Binbin

#7 lore raw

Re: [PATCH 2/3] dt-bindings: dma: loongson: Add Loongson-2K0300 DMA mux support

Quoted 65 lines · Binbin Zhou

Hi Frank:

Thanks for your reply.

Quoted 48 lines · Frank Li
Quoted 46 lines · Binbin Zhou

The DMA mux on Loongson-2K0300 is not a standalone hardware block. It is implemented as part of the chip configuration registers CHIP_CTRL13/14/15, which route peripheral DMA requests to the physical channels of the CMC DMA controller.

Document this as a DMA router. The #dma-cells = <3> specifier contains the peripheral request ID, the target CMC DMA channel, and channel configuration flags forwarded to the parent DMA controller.

---
 .../dma/loongson,ls2k0300-dmamux.yaml         | 49 +++++++++++++++++++
 MAINTAINERS                                   |  1 +
 2 files changed, 50 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.yaml
Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.yaml new file +24 -0
new file mode 100644
index 000000000000..12b8f8af51e9
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/dma/loongson,ls2k0300-dmamux.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Loongson-2 APB DMA MUX (DMA request router)
+
+maintainers:
+  - Binbin Zhou <[email protected]>
+
+allOf:
+  - $ref: /schemas/dma/dma-router.yaml#
+
+properties:
+  compatible:
+    const: loongson,ls2k0300-dmamux
+
+  reg:
+    maxItems: 1
+
+  '#dma-cells':
+    const: 3
+    description: |
Quoted 8 lines

Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.yaml line 29

Quoted 5 lines
Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.yaml new file +5 -0
+      Each cell represents the following:
+      1. The peripheral request id (see
+         include/dt-bindings/dma/loongson,ls2k-dmamux.h)
+      2. The physical CMC DMA channel (0-7) that is passed to the real
+         DMA controller

Do you have restriction between request id and channel? Does any request id route to any channel?

Yes, according to the description of this register’s bit fields in the manual, DMA channels are not mapped arbitrarily.

Quoted 13 lines

We can broadly categorize them as follows:
1. First, most peripherals that use this DMA controller request adjacent DMA channels (TX/RX) in pairs.
Let’s take I2S as an example.
Bits [17:16] at address 0x16000138 controls the I2S DMA channel mapping:
00: Channels 0, 1
01: Channels 2, 3
10: Channels 4, 5
11: Channels 6, 7
2. The SM/DES/AES module uses only channels 0 and 1, corresponding to the bit field values 00 and 01, respectively.
3. The can/adc module is single-channel, so each bit in the corresponding bit field corresponds to channels 0–7.

Does channel dynmatic allocated or have to static assign, if channel dynmatic allocated, needn't arg2. If static assign, it'd better descript clean about restriction between arg1 and arg2.

Frank

Quoted 42 lines
Quoted 30 lines

Frank

Quoted 27 lines
Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.yaml new file +20 -0
+      3. Bitfield representing DMA channel configuration that is passed
+         to the real DMA controller
+
+required:
+  - compatible
+  - reg
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/dma/loongson,ls2k-dmamux.h>
+    dma-router@16000134 {
+        compatible = "loongson,ls2k0300-dmamux";
+        reg = <0x16000134 0xc>;
+        #dma-cells = <3>;
+        dma-masters = <&dma>;
+        dma-requests = <22>;
+    };
+...
MAINTAINERS +0 -0
index b021fb623446..dc976938c58c 100644
@@ -15344,6 +15344,7 @@ M:    Binbin Zhou <[email protected]>
 L:   [email protected]
 S:   Maintained
Quoted 8 lines
Quoted 8 lines
MAINTAINERS +1 -1
 F:   Documentation/devicetree/bindings/dma/loongson,ls2k0300-dma.yaml
+F:   Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.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
--

2.52.0

--
Thanks.
Binbin

#8 lore raw

Re: [PATCH 2/3] dt-bindings: dma: loongson: Add Loongson-2K0300 DMA mux support

Hi Frank:

Quoted 81 lines · Frank Li
Quoted 65 lines · Binbin Zhou

Hi Frank:

Thanks for your reply.

Quoted 48 lines · Frank Li
Quoted 46 lines · Binbin Zhou

The DMA mux on Loongson-2K0300 is not a standalone hardware block. It is implemented as part of the chip configuration registers CHIP_CTRL13/14/15, which route peripheral DMA requests to the physical channels of the CMC DMA controller.

Document this as a DMA router. The #dma-cells = <3> specifier contains the peripheral request ID, the target CMC DMA channel, and channel configuration flags forwarded to the parent DMA controller.

---
 .../dma/loongson,ls2k0300-dmamux.yaml         | 49 +++++++++++++++++++
 MAINTAINERS                                   |  1 +
 2 files changed, 50 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.yaml
Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.yaml new file +24 -0
new file mode 100644
index 000000000000..12b8f8af51e9
@@ -0,0 +1,49 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/dma/loongson,ls2k0300-dmamux.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Loongson-2 APB DMA MUX (DMA request router)
+
+maintainers:
+  - Binbin Zhou <[email protected]>
+
+allOf:
+  - $ref: /schemas/dma/dma-router.yaml#
+
+properties:
+  compatible:
+    const: loongson,ls2k0300-dmamux
+
+  reg:
+    maxItems: 1
+
+  '#dma-cells':
+    const: 3
+    description: |
Quoted 8 lines

Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.yaml line 29

Quoted 5 lines
Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.yaml new file +5 -0
+      Each cell represents the following:
+      1. The peripheral request id (see
+         include/dt-bindings/dma/loongson,ls2k-dmamux.h)
+      2. The physical CMC DMA channel (0-7) that is passed to the real
+         DMA controller

Do you have restriction between request id and channel? Does any request id route to any channel?

Yes, according to the description of this register’s bit fields in the manual, DMA channels are not mapped arbitrarily.

Quoted 13 lines

We can broadly categorize them as follows:
1. First, most peripherals that use this DMA controller request adjacent DMA channels (TX/RX) in pairs.
Let’s take I2S as an example.
Bits [17:16] at address 0x16000138 controls the I2S DMA channel mapping:
00: Channels 0, 1
01: Channels 2, 3
10: Channels 4, 5
11: Channels 6, 7
2. The SM/DES/AES module uses only channels 0 and 1, corresponding to the bit field values 00 and 01, respectively.
3. The can/adc module is single-channel, so each bit in the corresponding bit field corresponds to channels 0–7.

Quoted 3 lines

Does channel dynmatic allocated or have to static assign, if channel dynmatic allocated, needn't arg2. If static assign, it'd better descript clean about restriction between arg1 and arg2.

No, it needs to be statically assigned based on the register configuration.

Let me think about how to describe this better. First, both arg1 and arg2 are required; they fully configure the register. arg1 determines the range of the bit field, and arg2 determines the value of that bit field.
At the same time, arg2 and arg3 are passed to the DMA controller itself to complete the relevant initialization.

Is this description acceptable?

'#dma-cells':
const: 3
description: |
Each cell represents the following:
1. Peripheral request ID: This describes the corresponding bit field for that peripheral in the configuration register; see ( include/dt-bindings/dma/loongson,ls2k-dmamux.h) for details.
2. Physical DMA channel (0–7): This not only represents the DMA channel used by the peripheral but is also converted into a value for the corresponding bit field in the configuration register, indicating the legitimacy of that peripheral using that channel. Specifically:
1. I2S/SPI/UART/I2C: Require adjacent channels in pairs; valid
values are 0/1, 2/3, 4/5, 6/7;
2. SM3/SM4/DES/AES: Single channel; valid values are limited to 0/1;
3. CAN/ADC: Single channel; valid values range from 0 to 7.
3. DMA Channel Configuration: A 32-bit mask used to specify device-specific DMA channel configurations.
- Bit 6: Peripheral Address Increment Mode
0x0: Disabled
0x1: Enabled
- Bit 7: Memory Address Increment Mode
0x0: Disabled
0x1: Enabled
- Bits 12–13: Priority
0x0: Low
0x1: Medium
0x2: High
0x3: Very High
.........

Quoted 45 lines

Frank

Quoted 42 lines
Quoted 30 lines

Frank

Quoted 27 lines
Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.yaml new file +20 -0
+      3. Bitfield representing DMA channel configuration that is passed
+         to the real DMA controller
+
+required:
+  - compatible
+  - reg
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/dma/loongson,ls2k-dmamux.h>
+    dma-router@16000134 {
+        compatible = "loongson,ls2k0300-dmamux";
+        reg = <0x16000134 0xc>;
+        #dma-cells = <3>;
+        dma-masters = <&dma>;
+        dma-requests = <22>;
+    };
+...
MAINTAINERS +0 -0
index b021fb623446..dc976938c58c 100644
@@ -15344,6 +15344,7 @@ M:    Binbin Zhou <[email protected]>
 L:   [email protected]
 S:   Maintained
Quoted 8 lines
Quoted 8 lines
MAINTAINERS +1 -1
 F:   Documentation/devicetree/bindings/dma/loongson,ls2k0300-dma.yaml
+F:   Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.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
--

2.52.0

--
Thanks.
Binbin

Signature · 2 lines
Thanks.
Binbin
#9 lore raw

PATCH 3/3 dmaengine: loongson: Add Loongson-2K0300 APB DMA mux driver

In reply to Binbin Zhou

The Loongson-2K0300 DMA mux is not a standalone DMA controller. It is implemented as a set of routing fields in the chip global configuration registers CHIP_CTRL13/14/15, which select which physical channel of the on-chip CMC DMA controller handles each peripheral DMA request.

Add a driver that exposes these routing fields as an OF DMA router. The router's allocation callback validates the <request> <channel> <flags> specifier, programs the corresponding CHIP_CTRL register field, and then rewrites the specifier to <channel> <flags> so that the parent CMC DMA controller's xlate is used.

---
 MAINTAINERS                                 |   1 +
 drivers/dma/loongson/Kconfig                |  13 +
 drivers/dma/loongson/Makefile               |   1 +
 drivers/dma/loongson/loongson2-apb-dmamux.c | 380 ++++++++++++++++++++
 4 files changed, 395 insertions(+)
 create mode 100644 drivers/dma/loongson/loongson2-apb-dmamux.c
MAINTAINERS +1 -0
index dc976938c58c..82b32e14581f 100644
@@ -15348,6 +15348,7 @@ F:	Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.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
+F:	drivers/dma/loongson/loongson2-apb-dmamux.c
 F:	include/dt-bindings/dma/loongson,ls2k-dmamux.h
 
 LOONGSON LS2X I2C DRIVER
drivers/dma/loongson/Kconfig +13 -0
index c4e62dce5d4f..18edf06ef64e 100644
@@ -38,4 +38,17 @@ config LOONGSON2_APB_CMC_DMA
 	  which has 4/8 channels internally, enabling bidirectional data transfer
 	  between devices and memory.
 
+config LOONGSON2_APB_DMAMUX
+	tristate "Loongson2 APB DMA Router (dmamux) support"
+	depends on LOONGSON2_APB_CMC_DMA
+	help
+	  Support for the DMA Mux controller found on Loongson-2 SoCs.
+
+	  On Loongson-2K0300, the DMA Mux routes peripheral DMA requests to
+	  one of the 8 physical channels of the on-chip CMC DMA controller
+	  by programming the CHIP_CTRL13/14/15 registers.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called loongson2-apb-dmamux.
+
 endif
drivers/dma/loongson/Makefile +1 -0
index 48c19781e729..517826a003ac 100644
@@ -2,3 +2,4 @@
 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
+obj-$(CONFIG_LOONGSON2_APB_DMAMUX) += loongson2-apb-dmamux.o
drivers/dma/loongson/loongson2-apb-dmamux.c new file +380 -0

380 lines not on this page · raw message

Signature · 1 line
2.52.0
#10 lore raw

Re: [PATCH 3/3] dmaengine: loongson: Add Loongson-2K0300 APB DMA mux driver

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: - [High] dmaengine: loongson: fix UAF of parent DMA node on route allocation - [High] dmaengine: loongson…

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] dmaengine: loongson: fix UAF of parent DMA node on route allocation
- [High] dmaengine: loongson: fix NULL pointer dereference on routing error paths
- [Low] dmaengine: loongson: remove unused 'shift' field from mapping structure --

commit c46c8efe97ecad5eafdc1a9b6a37374efa172f8a
Author: Binbin Zhou <[email protected]>

dmaengine: loongson: Add Loongson-2K0300 APB DMA mux driver

This commit adds a DMA mux driver for the Loongson-2K0300 SoC. It exposes the routing fields in the chip global configuration registers as an OF DMA router, which validates the specifier, programs the route, and forwards the translation to the parent CMC DMA controller.

Quoted 5 lines
drivers/dma/loongson/loongson2-apb-dmamux.c new file +0 -0
new file mode 100644
index 0000000000000..b6f1f4112d612

[ ... ]

drivers/dma/loongson/loongson2-apb-dmamux.c line 99

Quoted 4 lines
drivers/dma/loongson/loongson2-apb-dmamux.c new file +4 -0
+struct loongson2_dmamux_map {
+	u32 reg;
+	u32 mask;
+	u8 shift;

[Severity: Low]
Is this shift field needed? Code inspection shows it is initialized in the ls2k0300_dmamux_maps array for all entries but is never read.

The routing logic in loongson2_dmamux_set_route() correctly manages the shift operation implicitly using the field_prep() macro, which extracts the shift directly from the compile-time constant mask.

Quoted 4 lines
drivers/dma/loongson/loongson2-apb-dmamux.c new file +4 -0
+	enum loongson2_dmamux_type type;
+	u32 high_reg;
+	u8 high_shift;
+};

[ ... ]

Quoted 3 lines
drivers/dma/loongson/loongson2-apb-dmamux.c new file +3 -0
+static void *loongson2_dmamux_route_allocate(struct of_phandle_args *dma_spec,
+					     struct of_dma *ofdma)
+{

[ ... ]

drivers/dma/loongson/loongson2-apb-dmamux.c line 302

Quoted 10 lines
drivers/dma/loongson/loongson2-apb-dmamux.c new file +10 -0
+	/* Program the SoC-specific route register */
+	ret = loongson2_dmamux_set_route(mux, request, channel);
+	if (ret)
+		return ERR_PTR(ret);
+
+	/*
+	 * Rewrite dma_spec so the kernel router framework uses it to look
+	 * up the parent DMA controller and invoke its xlate.
+	 */
+	dma_spec->np = mux->dma_np;

[Severity: High]
Does this need to increment the reference count on the cached device node?

Looking at the OF DMA framework, of_dma_router_xlate() explicitly calls of_node_put(dma_spec_target.np) after translating the channel request. Since this assignment directly copies the pointer without taking a reference via of_node_get(), will each DMA request unconditionally decrement the reference count of the cached parent device node, leading to a premature release?

Quoted 6 lines
drivers/dma/loongson/loongson2-apb-dmamux.c new file +6 -0
+	dma_spec->args[0] = channel;
+	dma_spec->args[1] = flags;
+	dma_spec->args_count = mux->config->parent_args_count;
+
+	return NULL;
+}

[ ... ]

Quoted 2 lines
drivers/dma/loongson/loongson2-apb-dmamux.c new file +2 -0
+static int loongson2_dmamux_probe(struct platform_device *pdev)
+{

[ ... ]

drivers/dma/loongson/loongson2-apb-dmamux.c line 335

Quoted 2 lines
drivers/dma/loongson/loongson2-apb-dmamux.c new file +2 -0
+	mux->dma_router.dev = dev;
+	mux->dma_router.route_free = NULL;

[Severity: High]
Will setting this callback to NULL cause a panic on error paths?

In the framework's of_dma_router_xlate() function, if the parent DMA controller is not yet available (triggering -EPROBE_DEFER), the framework unconditionally executes ofdma->dma_router->route_free(...) without checking if the callback exists.

Could this crash the kernel if the parent CMC DMA controller probes after the peripheral?

Quoted 5 lines
drivers/dma/loongson/loongson2-apb-dmamux.c new file +5 -0
+
+	platform_set_drvdata(pdev, mux);
+
+	ret = of_dma_router_register(dev->of_node, loongson2_dmamux_route_allocate,
+				     &mux->dma_router);
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
#11 lore raw

Re: [PATCH 3/3] dmaengine: loongson: Add Loongson-2K0300 APB DMA mux driver

In reply to Binbin Zhou

Hi, Binbin,

Quoted 38 lines · Binbin Zhou

The Loongson-2K0300 DMA mux is not a standalone DMA controller. It is implemented as a set of routing fields in the chip global configuration registers CHIP_CTRL13/14/15, which select which physical channel of the on-chip CMC DMA controller handles each peripheral DMA request.

Add a driver that exposes these routing fields as an OF DMA router. The router's allocation callback validates the <request> <channel> <flags> specifier, programs the corresponding CHIP_CTRL register field, and then rewrites the specifier to <channel> <flags> so that the parent CMC DMA controller's xlate is used.

---
 MAINTAINERS                                 |   1 +
 drivers/dma/loongson/Kconfig                |  13 +
 drivers/dma/loongson/Makefile               |   1 +
 drivers/dma/loongson/loongson2-apb-dmamux.c | 380 ++++++++++++++++++++
 4 files changed, 395 insertions(+)
 create mode 100644 drivers/dma/loongson/loongson2-apb-dmamux.c
MAINTAINERS +1 -0
index dc976938c58c..82b32e14581f 100644
@@ -15348,6 +15348,7 @@ F:      Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.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
+F:     drivers/dma/loongson/loongson2-apb-dmamux.c
 F:     include/dt-bindings/dma/loongson,ls2k-dmamux.h
 
 LOONGSON LS2X I2C DRIVER
drivers/dma/loongson/Kconfig +0 -0
index c4e62dce5d4f..18edf06ef64e 100644

drivers/dma/loongson/Kconfig config LOONGSON2_APB_CMC_DMA · line 43

Quoted 6 lines
drivers/dma/loongson/Kconfig +3 -0
@@ -38,4 +38,17 @@ config LOONGSON2_APB_CMC_DMA
          which has 4/8 channels internally, enabling bidirectional data transfer
          between devices and memory.
 
+config LOONGSON2_APB_DMAMUX
+       tristate "Loongson2 APB DMA Router (dmamux) support"
+       depends on LOONGSON2_APB_CMC_DMA

If LOONGSON2_APB_DMAMUX is bound to LOONGSON2_APB_CMC_DMA (currently it is true but I'm not sure in future), just use LOONGSON2_APB_CMC_DMA in Makefile to compile both loongson2-apb-cmc-dma.c and loongson2-apb-dmamux.c.

Huacai

Quoted 409 lines
drivers/dma/loongson/Kconfig +10 -0
+       help
+         Support for the DMA Mux controller found on Loongson-2 SoCs.
+
+         On Loongson-2K0300, the DMA Mux routes peripheral DMA requests to
+         one of the 8 physical channels of the on-chip CMC DMA controller
+         by programming the CHIP_CTRL13/14/15 registers.
+
+         To compile this driver as a module, choose M here: the module
+         will be called loongson2-apb-dmamux.
+
 endif
drivers/dma/loongson/Makefile +1 -0
index 48c19781e729..517826a003ac 100644
@@ -2,3 +2,4 @@
 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
+obj-$(CONFIG_LOONGSON2_APB_DMAMUX) += loongson2-apb-dmamux.o
drivers/dma/loongson/loongson2-apb-dmamux.c new file +380 -1
new file mode 100644
index 000000000000..b6f1f4112d61
@@ -0,0 +1,380 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Loongson2 SoC APB DMA Mux Controller
+ *
+ * Copyright (C) 2026 Loongson Technology Corporation Limited
+ *
+ * Device tree bindings:
+ *
+ *   2K0300:
+ *     dma_mux: dma-mux@16000134 {
+ *             compatible = "loongson,ls2k0300-dmamux";
+ *             reg = <0x0 0x16000134 0x0 0xc>;
+ *             #dma-cells = <3>;
+ *             dma-masters = <&dma>;
+ *             dma-requests = <22>;
+ *     };
+ *
+ * DMA specifier: <request> <channel> <flags>
+ *   - request: peripheral request ID (LS2K0300_DMA_* or LS2K1000_DMA_*)
+ *   - channel: target physical DMA channel or controller index
+ *   - flags:   channel configuration flags forwarded to the parent DMA
+ *              controller
+ */
+
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/dmaengine.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_dma.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/dma/loongson,ls2k-dmamux.h>
+
+/* Loongson-2K0300 registers definitions */
+#define LS2K0300_CHIP_CTRL13           0x00
+#define LS2K0300_CHIP_CTRL14           0x04
+#define LS2K0300_CHIP_CTRL15           0x08
+
+/* CHIP_CTRL13 bitfields */
+#define CTRL13_UART0_DMA_MAP           GENMASK(17, 16)
+#define CTRL13_UART1_DMA_MAP           GENMASK(19, 18)
+#define CTRL13_UART2_DMA_MAP           GENMASK(21, 20)
+#define CTRL13_UART3_DMA_MAP           GENMASK(23, 22)
+#define CTRL13_UART4_DMA_MAP           GENMASK(25, 24)
+#define CTRL13_UART5_DMA_MAP           GENMASK(27, 26)
+#define CTRL13_UART6_DMA_MAP           GENMASK(29, 28)
+#define CTRL13_UART7_DMA_MAP           GENMASK(31, 30)
+
+/* CHIP_CTRL14 bitfields */
+#define CTRL14_UART8_DMA_MAP           GENMASK(1, 0)
+#define CTRL14_UART9_DMA_MAP           GENMASK(3, 2)
+#define CTRL14_I2C0_DMA_MAP            GENMASK(5, 4)
+#define CTRL14_I2C1_DMA_MAP            GENMASK(7, 6)
+#define CTRL14_I2C2_DMA_MAP            GENMASK(9, 8)
+#define CTRL14_I2C3_DMA_MAP            GENMASK(11, 10)
+#define CTRL14_SPI2_DMA_MAP            GENMASK(13, 12)
+#define CTRL14_SPI3_DMA_MAP            GENMASK(15, 14)
+#define CTRL14_I2S_DMA_MAP             GENMASK(17, 16)
+#define CTRL14_ADC_DMA_MAP             GENMASK(20, 18)
+#define CTRL14_CAN0_DMA_MAP            GENMASK(23, 21)
+#define CTRL14_CAN1_DMA_MAP            GENMASK(26, 24)
+#define CTRL14_CAN2_DMA_MAP            GENMASK(29, 27)
+#define CTRL14_CAN3_DMA_MAP            GENMASK(31, 30)
+
+/* CHIP_CTRL15 bitfields */
+#define CTRL15_CAN3_DMA_MAP_HI         BIT(0)
+
+#define LS2K0300_DMA_MAX_CHANNEL       7
+
+/**
+ * enum loongson2_dmamux_type - Type of the mapping field
+ * @LOONGSON_DMAMUX_PAIR: 2-bit field, value selects a channel pair
+ *                        (0/1, 2/3, 4/5, 6/7) — used on 2K0300.
+ * @LOONGSON_DMAMUX_SINGLE: 3-bit field, value selects a single channel
+ *                          or controller index directly.
+ */
+enum loongson2_dmamux_type {
+       LOONGSON_DMAMUX_PAIR,
+       LOONGSON_DMAMUX_SINGLE,
+};
+
+/**
+ * struct loongson2_dmamux_map - DMA request to register bitfield mapping
+ * @reg: Register offset from the controller base
+ * @mask: Bitfield mask within the register
+ * @shift: Starting bit position of the bitfield
+ * @type: Mapping type (pair or single)
+ * @high_reg: Optional extension register offset (0 if unused)
+ * @high_shift: Bit position in the extension register
+ */
+struct loongson2_dmamux_map {
+       u32 reg;
+       u32 mask;
+       u8 shift;
+       enum loongson2_dmamux_type type;
+       u32 high_reg;
+       u8 high_shift;
+};
+
+/**
+ * struct loongson2_dmamux_config - SoC-specific DMA mux configuration
+ * @maps: Pointer to the request mapping table
+ * @num_maps: Number of entries in @maps
+ * @max_channel: Highest valid channel/controller index
+ * @parent_args_count: Number of cells to write to dma_spec->args[]
+ *                     when forwarding to the parent xlate.
+ */
+struct loongson2_dmamux_config {
+       const struct loongson2_dmamux_map *maps;
+       u32 num_maps;
+       u32 max_channel;
+       u32 parent_args_count;
+};
+
+/**
+ * struct loongson2_dmamux - DMA mux controller state
+ * @dev: Back pointer to the platform device
+ * @base: I/O-mapped register base
+ * @lock: Protects register read-modify-write sequences
+ * @dma_np: The parent DMA controller device nodes
+ * @dma_router: Router structure passed to of_dma_router_register()
+ * @config: SoC-specific configuration
+ */
+struct loongson2_dmamux {
+       struct device *dev;
+       void __iomem *base;
+       struct mutex lock;      /* Protects register read-modify-write sequences */
+       struct device_node *dma_np;
+       struct dma_router dma_router;
+       const struct loongson2_dmamux_config *config;
+};
+
+/* Loongson-2K0300 mapping table */
+static const struct loongson2_dmamux_map ls2k0300_dmamux_maps[] = {
+       [LS2K0300_DMA_UART0] = { LS2K0300_CHIP_CTRL13, CTRL13_UART0_DMA_MAP, 16,
+                                LOONGSON_DMAMUX_PAIR,   0, 0 },
+       [LS2K0300_DMA_UART1] = { LS2K0300_CHIP_CTRL13, CTRL13_UART1_DMA_MAP, 18,
+                                LOONGSON_DMAMUX_PAIR,   0, 0 },
+       [LS2K0300_DMA_UART2] = { LS2K0300_CHIP_CTRL13, CTRL13_UART2_DMA_MAP, 20,
+                                LOONGSON_DMAMUX_PAIR,   0, 0 },
+       [LS2K0300_DMA_UART3] = { LS2K0300_CHIP_CTRL13, CTRL13_UART3_DMA_MAP, 22,
+                                LOONGSON_DMAMUX_PAIR,   0, 0 },
+       [LS2K0300_DMA_UART4] = { LS2K0300_CHIP_CTRL13, CTRL13_UART4_DMA_MAP, 24,
+                                LOONGSON_DMAMUX_PAIR,   0, 0 },
+       [LS2K0300_DMA_UART5] = { LS2K0300_CHIP_CTRL13, CTRL13_UART5_DMA_MAP, 26,
+                                LOONGSON_DMAMUX_PAIR,   0, 0 },
+       [LS2K0300_DMA_UART6] = { LS2K0300_CHIP_CTRL13, CTRL13_UART6_DMA_MAP, 28,
+                                LOONGSON_DMAMUX_PAIR,   0, 0 },
+       [LS2K0300_DMA_UART7] = { LS2K0300_CHIP_CTRL13, CTRL13_UART7_DMA_MAP, 30,
+                                LOONGSON_DMAMUX_PAIR,   0, 0 },
+       [LS2K0300_DMA_UART8] = { LS2K0300_CHIP_CTRL14, CTRL14_UART8_DMA_MAP,  0,
+                                LOONGSON_DMAMUX_PAIR,   0, 0 },
+       [LS2K0300_DMA_UART9] = { LS2K0300_CHIP_CTRL14, CTRL14_UART9_DMA_MAP,  2,
+                                LOONGSON_DMAMUX_PAIR,   0, 0 },
+       [LS2K0300_DMA_I2C0]  = { LS2K0300_CHIP_CTRL14, CTRL14_I2C0_DMA_MAP,   4,
+                                LOONGSON_DMAMUX_PAIR,   0, 0 },
+       [LS2K0300_DMA_I2C1]  = { LS2K0300_CHIP_CTRL14, CTRL14_I2C1_DMA_MAP,   6,
+                                LOONGSON_DMAMUX_PAIR,   0, 0 },
+       [LS2K0300_DMA_I2C2]  = { LS2K0300_CHIP_CTRL14, CTRL14_I2C2_DMA_MAP,   8,
+                                LOONGSON_DMAMUX_PAIR,   0, 0 },
+       [LS2K0300_DMA_I2C3]  = { LS2K0300_CHIP_CTRL14, CTRL14_I2C3_DMA_MAP,  10,
+                                LOONGSON_DMAMUX_PAIR,   0, 0 },
+       [LS2K0300_DMA_SPI2]  = { LS2K0300_CHIP_CTRL14, CTRL14_SPI2_DMA_MAP,  12,
+                                LOONGSON_DMAMUX_PAIR,   0, 0 },
+       [LS2K0300_DMA_SPI3]  = { LS2K0300_CHIP_CTRL14, CTRL14_SPI3_DMA_MAP,  14,
+                                LOONGSON_DMAMUX_PAIR,   0, 0 },
+       [LS2K0300_DMA_I2S]   = { LS2K0300_CHIP_CTRL14, CTRL14_I2S_DMA_MAP,   16,
+                                LOONGSON_DMAMUX_PAIR,   0, 0 },
+       [LS2K0300_DMA_ADC]   = { LS2K0300_CHIP_CTRL14, CTRL14_ADC_DMA_MAP,   18,
+                                LOONGSON_DMAMUX_SINGLE, 0, 0 },
+       [LS2K0300_DMA_CAN0]  = { LS2K0300_CHIP_CTRL14, CTRL14_CAN0_DMA_MAP,  21,
+                                LOONGSON_DMAMUX_SINGLE, 0, 0 },
+       [LS2K0300_DMA_CAN1]  = { LS2K0300_CHIP_CTRL14, CTRL14_CAN1_DMA_MAP,  24,
+                                LOONGSON_DMAMUX_SINGLE, 0, 0 },
+       [LS2K0300_DMA_CAN2]  = { LS2K0300_CHIP_CTRL14, CTRL14_CAN2_DMA_MAP,  27,
+                                LOONGSON_DMAMUX_SINGLE, 0, 0 },
+       [LS2K0300_DMA_CAN3]  = { LS2K0300_CHIP_CTRL14, CTRL14_CAN3_DMA_MAP,  30,
+                                LOONGSON_DMAMUX_SINGLE, LS2K0300_CHIP_CTRL15, 0 },
+};
+
+static const struct loongson2_dmamux_config ls2k0300_dmamux_config = {
+       .maps                   = ls2k0300_dmamux_maps,
+       .num_maps               = ARRAY_SIZE(ls2k0300_dmamux_maps),
+       .max_channel            = LS2K0300_DMA_MAX_CHANNEL,
+       .parent_args_count      = 2,
+};
+
+static inline u32 loongson2_dmamux_read(struct loongson2_dmamux *mux, u32 reg)
+{
+       return readl(mux->base + reg);
+}
+
+static inline void loongson2_dmamux_write(struct loongson2_dmamux *mux, u32 reg, u32 val)
+{
+       writel(val, mux->base + reg);
+}
+
+/**
+ * loongson2_dmamux_set_route() - Program the DMA request route
+ * @mux:     DMA mux controller
+ * @request: Peripheral DMA request ID
+ * @channel: Target physical DMA channel or controller index
+ *
+ * Computes the register value according to the mapping type:
+ *   - PAIR   : channel / 2 selects the pair index
+ *   - SINGLE : channel is written directly
+ *
+ * Return: 0 on success, negative error code otherwise.
+ */
+static int loongson2_dmamux_set_route(struct loongson2_dmamux *mux, u32 request, u32 channel)
+{
+       const struct loongson2_dmamux_map *map;
+       u32 reg_val, route_val, high_val = 0;
+
+       if (request >= mux->config->num_maps)
+               return -EINVAL;
+
+       if (channel > mux->config->max_channel)
+               return -EINVAL;
+
+       map = &mux->config->maps[request];
+
+       switch (map->type) {
+       case LOONGSON_DMAMUX_PAIR:
+               route_val = channel / 2;
+               break;
+       case LOONGSON_DMAMUX_SINGLE:
+               route_val = channel;
+               if (map->high_reg)
+                       high_val = (channel >> 2) & 0x1;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       mutex_lock(&mux->lock);
+
+       /* Write the primary register field */
+       reg_val = loongson2_dmamux_read(mux, map->reg);
+       reg_val &= ~map->mask;
+       reg_val |= field_prep(map->mask, route_val);
+       loongson2_dmamux_write(mux, map->reg, reg_val);
+
+       /* Write the extension register field if present */
+       if (map->high_reg) {
+               reg_val = loongson2_dmamux_read(mux, map->high_reg);
+               reg_val &= ~BIT(map->high_shift);
+               reg_val |= high_val << map->high_shift;
+               loongson2_dmamux_write(mux, map->high_reg, reg_val);
+       }
+
+       mutex_unlock(&mux->lock);
+
+       return 0;
+}
+
+/**
+ * loongson2_dmamux_route_allocate() - DMA router allocation callback
+ * @dma_spec: OF DMA specifier <request> <channel> <flags>
+ * @ofdma:    Router's of_dma structure; ofdma->dma_router points to our
+ *            struct dma_router embedded in struct loongson2_dmamux.
+ *
+ * Return: NULL as route_data (nothing to free), or an ERR_PTR on failure.
+ */
+static void *loongson2_dmamux_route_allocate(struct of_phandle_args *dma_spec,
+                                            struct of_dma *ofdma)
+{
+       struct loongson2_dmamux *mux;
+       u32 request, channel, flags;
+       int ret;
+
+       if (!ofdma->dma_router)
+               return ERR_PTR(-EINVAL);
+
+       mux = container_of(ofdma->dma_router, struct loongson2_dmamux, dma_router);
+
+       if (dma_spec->args_count < 3)
+               return ERR_PTR(-EINVAL);
+
+       request = dma_spec->args[0];
+       channel = dma_spec->args[1];
+       flags = dma_spec->args[2];
+
+       if (request >= mux->config->num_maps ||
+           channel > mux->config->max_channel)
+               return ERR_PTR(-EINVAL);
+
+       /* Program the SoC-specific route register */
+       ret = loongson2_dmamux_set_route(mux, request, channel);
+       if (ret)
+               return ERR_PTR(ret);
+
+       /*
+        * Rewrite dma_spec so the kernel router framework uses it to look
+        * up the parent DMA controller and invoke its xlate.
+        */
+       dma_spec->np = mux->dma_np;
+       dma_spec->args[0] = channel;
+       dma_spec->args[1] = flags;
+       dma_spec->args_count = mux->config->parent_args_count;
+
+       return NULL;
+}
+
+static int loongson2_dmamux_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct loongson2_dmamux *mux;
+       int ret;
+
+       mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
+       if (!mux)
+               return -ENOMEM;
+
+       mux->dev = dev;
+       mux->config = &ls2k0300_dmamux_config;
+       mutex_init(&mux->lock);
+
+       mux->base = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(mux->base))
+               return PTR_ERR(mux->base);
+
+       /* Parse parent DMA controller nodes */
+       mux->dma_np = of_parse_phandle(dev->of_node, "dma-masters", 0);
+       if (!mux->dma_np)
+               return dev_err_probe(dev, -ENODEV,
+                                    "Failed to find parent DMA controller\n");
+
+       mux->dma_router.dev = dev;
+       mux->dma_router.route_free = NULL;
+
+       platform_set_drvdata(pdev, mux);
+
+       ret = of_dma_router_register(dev->of_node, loongson2_dmamux_route_allocate,
+                                    &mux->dma_router);
+       if (ret) {
+               of_node_put(mux->dma_np);
+               return dev_err_probe(dev, ret,
+                                    "Failed to register DMA router\n");
+       }
+
+       dev_info(dev,
+                "Loongson2 DMA Mux Controller registered (max_channel=%u, requests=%u)\n",
+                mux->config->max_channel, mux->config->num_maps);
+
+       return 0;
+}
+
+static void loongson2_dmamux_remove(struct platform_device *pdev)
+{
+       struct loongson2_dmamux *mux = platform_get_drvdata(pdev);
+
+       of_dma_controller_free(pdev->dev.of_node);
+       of_node_put(mux->dma_np);
+}
+
+static const struct of_device_id loongson2_dmamux_of_match[] = {
+       { .compatible = "loongson,ls2k0300-dmamux" },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, loongson2_dmamux_of_match);
+
+static struct platform_driver loongson2_dmamux_driver = {
+       .driver = {
+               .name = "loongson2-dmamux",
+               .of_match_table = loongson2_dmamux_of_match,
+       },
+       .probe = loongson2_dmamux_probe,
+       .remove = loongson2_dmamux_remove,
+};
+module_platform_driver(loongson2_dmamux_driver);
+
+MODULE_DESCRIPTION("Loongson SoC DMA Mux Controller driver");
+MODULE_AUTHOR("Loongson Technology Corporation Limited");
+MODULE_LICENSE("GPL");
--

2.52.0

#12 lore raw

Re: [PATCH 3/3] dmaengine: loongson: Add Loongson-2K0300 APB DMA mux driver

In reply to Binbin Zhou
Quoted 381 lines · Binbin Zhou

The Loongson-2K0300 DMA mux is not a standalone DMA controller. It is implemented as a set of routing fields in the chip global configuration registers CHIP_CTRL13/14/15, which select which physical channel of the on-chip CMC DMA controller handles each peripheral DMA request.

Add a driver that exposes these routing fields as an OF DMA router. The router's allocation callback validates the <request> <channel> <flags> specifier, programs the corresponding CHIP_CTRL register field, and then rewrites the specifier to <channel> <flags> so that the parent CMC DMA controller's xlate is used.

---
 MAINTAINERS                                 |   1 +
 drivers/dma/loongson/Kconfig                |  13 +
 drivers/dma/loongson/Makefile               |   1 +
 drivers/dma/loongson/loongson2-apb-dmamux.c | 380 ++++++++++++++++++++
 4 files changed, 395 insertions(+)
 create mode 100644 drivers/dma/loongson/loongson2-apb-dmamux.c
MAINTAINERS +1 -0
index dc976938c58c..82b32e14581f 100644
@@ -15348,6 +15348,7 @@ F:	Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.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
+F:	drivers/dma/loongson/loongson2-apb-dmamux.c
 F:	include/dt-bindings/dma/loongson,ls2k-dmamux.h
 
 LOONGSON LS2X I2C DRIVER
drivers/dma/loongson/Kconfig +13 -0
index c4e62dce5d4f..18edf06ef64e 100644
@@ -38,4 +38,17 @@ config LOONGSON2_APB_CMC_DMA
 	  which has 4/8 channels internally, enabling bidirectional data transfer
 	  between devices and memory.
 
+config LOONGSON2_APB_DMAMUX
+	tristate "Loongson2 APB DMA Router (dmamux) support"
+	depends on LOONGSON2_APB_CMC_DMA
+	help
+	  Support for the DMA Mux controller found on Loongson-2 SoCs.
+
+	  On Loongson-2K0300, the DMA Mux routes peripheral DMA requests to
+	  one of the 8 physical channels of the on-chip CMC DMA controller
+	  by programming the CHIP_CTRL13/14/15 registers.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called loongson2-apb-dmamux.
+
 endif
drivers/dma/loongson/Makefile +1 -0
index 48c19781e729..517826a003ac 100644
@@ -2,3 +2,4 @@
 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
+obj-$(CONFIG_LOONGSON2_APB_DMAMUX) += loongson2-apb-dmamux.o
drivers/dma/loongson/loongson2-apb-dmamux.c new file +312 -0
new file mode 100644
index 000000000000..b6f1f4112d61
@@ -0,0 +1,380 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Loongson2 SoC APB DMA Mux Controller
+ *
+ * Copyright (C) 2026 Loongson Technology Corporation Limited
+ *
+ * Device tree bindings:
+ *
+ *   2K0300:
+ *     dma_mux: dma-mux@16000134 {
+ *             compatible = "loongson,ls2k0300-dmamux";
+ *             reg = <0x0 0x16000134 0x0 0xc>;
+ *             #dma-cells = <3>;
+ *             dma-masters = <&dma>;
+ *             dma-requests = <22>;
+ *     };
+ *
+ * DMA specifier: <request> <channel> <flags>
+ *   - request: peripheral request ID (LS2K0300_DMA_* or LS2K1000_DMA_*)
+ *   - channel: target physical DMA channel or controller index
+ *   - flags:   channel configuration flags forwarded to the parent DMA
+ *              controller
+ */
+
+#include <linux/bitfield.h>
+#include <linux/device.h>
+#include <linux/dmaengine.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_dma.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/dma/loongson,ls2k-dmamux.h>
+
+/* Loongson-2K0300 registers definitions */
+#define LS2K0300_CHIP_CTRL13		0x00
+#define LS2K0300_CHIP_CTRL14		0x04
+#define LS2K0300_CHIP_CTRL15		0x08
+
+/* CHIP_CTRL13 bitfields */
+#define CTRL13_UART0_DMA_MAP		GENMASK(17, 16)
+#define CTRL13_UART1_DMA_MAP		GENMASK(19, 18)
+#define CTRL13_UART2_DMA_MAP		GENMASK(21, 20)
+#define CTRL13_UART3_DMA_MAP		GENMASK(23, 22)
+#define CTRL13_UART4_DMA_MAP		GENMASK(25, 24)
+#define CTRL13_UART5_DMA_MAP		GENMASK(27, 26)
+#define CTRL13_UART6_DMA_MAP		GENMASK(29, 28)
+#define CTRL13_UART7_DMA_MAP		GENMASK(31, 30)
+
+/* CHIP_CTRL14 bitfields */
+#define CTRL14_UART8_DMA_MAP		GENMASK(1, 0)
+#define CTRL14_UART9_DMA_MAP		GENMASK(3, 2)
+#define CTRL14_I2C0_DMA_MAP		GENMASK(5, 4)
+#define CTRL14_I2C1_DMA_MAP		GENMASK(7, 6)
+#define CTRL14_I2C2_DMA_MAP		GENMASK(9, 8)
+#define CTRL14_I2C3_DMA_MAP		GENMASK(11, 10)
+#define CTRL14_SPI2_DMA_MAP		GENMASK(13, 12)
+#define CTRL14_SPI3_DMA_MAP		GENMASK(15, 14)
+#define CTRL14_I2S_DMA_MAP		GENMASK(17, 16)
+#define CTRL14_ADC_DMA_MAP		GENMASK(20, 18)
+#define CTRL14_CAN0_DMA_MAP		GENMASK(23, 21)
+#define CTRL14_CAN1_DMA_MAP		GENMASK(26, 24)
+#define CTRL14_CAN2_DMA_MAP		GENMASK(29, 27)
+#define CTRL14_CAN3_DMA_MAP		GENMASK(31, 30)
+
+/* CHIP_CTRL15 bitfields */
+#define CTRL15_CAN3_DMA_MAP_HI		BIT(0)
+
+#define LS2K0300_DMA_MAX_CHANNEL	7
+
+/**
+ * enum loongson2_dmamux_type - Type of the mapping field
+ * @LOONGSON_DMAMUX_PAIR: 2-bit field, value selects a channel pair
+ *                        (0/1, 2/3, 4/5, 6/7) — used on 2K0300.
+ * @LOONGSON_DMAMUX_SINGLE: 3-bit field, value selects a single channel
+ *                          or controller index directly.
+ */
+enum loongson2_dmamux_type {
+	LOONGSON_DMAMUX_PAIR,
+	LOONGSON_DMAMUX_SINGLE,
+};
+
+/**
+ * struct loongson2_dmamux_map - DMA request to register bitfield mapping
+ * @reg: Register offset from the controller base
+ * @mask: Bitfield mask within the register
+ * @shift: Starting bit position of the bitfield
+ * @type: Mapping type (pair or single)
+ * @high_reg: Optional extension register offset (0 if unused)
+ * @high_shift: Bit position in the extension register
+ */
+struct loongson2_dmamux_map {
+	u32 reg;
+	u32 mask;
+	u8 shift;
+	enum loongson2_dmamux_type type;
+	u32 high_reg;
+	u8 high_shift;
+};
+
+/**
+ * struct loongson2_dmamux_config - SoC-specific DMA mux configuration
+ * @maps: Pointer to the request mapping table
+ * @num_maps: Number of entries in @maps
+ * @max_channel: Highest valid channel/controller index
+ * @parent_args_count: Number of cells to write to dma_spec->args[]
+ *                     when forwarding to the parent xlate.
+ */
+struct loongson2_dmamux_config {
+	const struct loongson2_dmamux_map *maps;
+	u32 num_maps;
+	u32 max_channel;
+	u32 parent_args_count;
+};
+
+/**
+ * struct loongson2_dmamux - DMA mux controller state
+ * @dev: Back pointer to the platform device
+ * @base: I/O-mapped register base
+ * @lock: Protects register read-modify-write sequences
+ * @dma_np: The parent DMA controller device nodes
+ * @dma_router: Router structure passed to of_dma_router_register()
+ * @config: SoC-specific configuration
+ */
+struct loongson2_dmamux {
+	struct device *dev;
+	void __iomem *base;
+	struct mutex lock;	/* Protects register read-modify-write sequences */
+	struct device_node *dma_np;
+	struct dma_router dma_router;
+	const struct loongson2_dmamux_config *config;
+};
+
+/* Loongson-2K0300 mapping table */
+static const struct loongson2_dmamux_map ls2k0300_dmamux_maps[] = {
+	[LS2K0300_DMA_UART0] = { LS2K0300_CHIP_CTRL13, CTRL13_UART0_DMA_MAP, 16,
+				 LOONGSON_DMAMUX_PAIR,   0, 0 },
+	[LS2K0300_DMA_UART1] = { LS2K0300_CHIP_CTRL13, CTRL13_UART1_DMA_MAP, 18,
+				 LOONGSON_DMAMUX_PAIR,   0, 0 },
+	[LS2K0300_DMA_UART2] = { LS2K0300_CHIP_CTRL13, CTRL13_UART2_DMA_MAP, 20,
+				 LOONGSON_DMAMUX_PAIR,   0, 0 },
+	[LS2K0300_DMA_UART3] = { LS2K0300_CHIP_CTRL13, CTRL13_UART3_DMA_MAP, 22,
+				 LOONGSON_DMAMUX_PAIR,   0, 0 },
+	[LS2K0300_DMA_UART4] = { LS2K0300_CHIP_CTRL13, CTRL13_UART4_DMA_MAP, 24,
+				 LOONGSON_DMAMUX_PAIR,   0, 0 },
+	[LS2K0300_DMA_UART5] = { LS2K0300_CHIP_CTRL13, CTRL13_UART5_DMA_MAP, 26,
+				 LOONGSON_DMAMUX_PAIR,   0, 0 },
+	[LS2K0300_DMA_UART6] = { LS2K0300_CHIP_CTRL13, CTRL13_UART6_DMA_MAP, 28,
+				 LOONGSON_DMAMUX_PAIR,   0, 0 },
+	[LS2K0300_DMA_UART7] = { LS2K0300_CHIP_CTRL13, CTRL13_UART7_DMA_MAP, 30,
+				 LOONGSON_DMAMUX_PAIR,   0, 0 },
+	[LS2K0300_DMA_UART8] = { LS2K0300_CHIP_CTRL14, CTRL14_UART8_DMA_MAP,  0,
+				 LOONGSON_DMAMUX_PAIR,   0, 0 },
+	[LS2K0300_DMA_UART9] = { LS2K0300_CHIP_CTRL14, CTRL14_UART9_DMA_MAP,  2,
+				 LOONGSON_DMAMUX_PAIR,   0, 0 },
+	[LS2K0300_DMA_I2C0]  = { LS2K0300_CHIP_CTRL14, CTRL14_I2C0_DMA_MAP,   4,
+				 LOONGSON_DMAMUX_PAIR,   0, 0 },
+	[LS2K0300_DMA_I2C1]  = { LS2K0300_CHIP_CTRL14, CTRL14_I2C1_DMA_MAP,   6,
+				 LOONGSON_DMAMUX_PAIR,   0, 0 },
+	[LS2K0300_DMA_I2C2]  = { LS2K0300_CHIP_CTRL14, CTRL14_I2C2_DMA_MAP,   8,
+				 LOONGSON_DMAMUX_PAIR,   0, 0 },
+	[LS2K0300_DMA_I2C3]  = { LS2K0300_CHIP_CTRL14, CTRL14_I2C3_DMA_MAP,  10,
+				 LOONGSON_DMAMUX_PAIR,   0, 0 },
+	[LS2K0300_DMA_SPI2]  = { LS2K0300_CHIP_CTRL14, CTRL14_SPI2_DMA_MAP,  12,
+				 LOONGSON_DMAMUX_PAIR,   0, 0 },
+	[LS2K0300_DMA_SPI3]  = { LS2K0300_CHIP_CTRL14, CTRL14_SPI3_DMA_MAP,  14,
+				 LOONGSON_DMAMUX_PAIR,   0, 0 },
+	[LS2K0300_DMA_I2S]   = { LS2K0300_CHIP_CTRL14, CTRL14_I2S_DMA_MAP,   16,
+				 LOONGSON_DMAMUX_PAIR,   0, 0 },
+	[LS2K0300_DMA_ADC]   = { LS2K0300_CHIP_CTRL14, CTRL14_ADC_DMA_MAP,   18,
+				 LOONGSON_DMAMUX_SINGLE, 0, 0 },
+	[LS2K0300_DMA_CAN0]  = { LS2K0300_CHIP_CTRL14, CTRL14_CAN0_DMA_MAP,  21,
+				 LOONGSON_DMAMUX_SINGLE, 0, 0 },
+	[LS2K0300_DMA_CAN1]  = { LS2K0300_CHIP_CTRL14, CTRL14_CAN1_DMA_MAP,  24,
+				 LOONGSON_DMAMUX_SINGLE, 0, 0 },
+	[LS2K0300_DMA_CAN2]  = { LS2K0300_CHIP_CTRL14, CTRL14_CAN2_DMA_MAP,  27,
+				 LOONGSON_DMAMUX_SINGLE, 0, 0 },
+	[LS2K0300_DMA_CAN3]  = { LS2K0300_CHIP_CTRL14, CTRL14_CAN3_DMA_MAP,  30,
+				 LOONGSON_DMAMUX_SINGLE, LS2K0300_CHIP_CTRL15, 0 },
+};
+
+static const struct loongson2_dmamux_config ls2k0300_dmamux_config = {
+	.maps			= ls2k0300_dmamux_maps,
+	.num_maps		= ARRAY_SIZE(ls2k0300_dmamux_maps),
+	.max_channel		= LS2K0300_DMA_MAX_CHANNEL,
+	.parent_args_count	= 2,
+};
+
+static inline u32 loongson2_dmamux_read(struct loongson2_dmamux *mux, u32 reg)
+{
+	return readl(mux->base + reg);
+}
+
+static inline void loongson2_dmamux_write(struct loongson2_dmamux *mux, u32 reg, u32 val)
+{
+	writel(val, mux->base + reg);
+}
+
+/**
+ * loongson2_dmamux_set_route() - Program the DMA request route
+ * @mux:     DMA mux controller
+ * @request: Peripheral DMA request ID
+ * @channel: Target physical DMA channel or controller index
+ *
+ * Computes the register value according to the mapping type:
+ *   - PAIR   : channel / 2 selects the pair index
+ *   - SINGLE : channel is written directly
+ *
+ * Return: 0 on success, negative error code otherwise.
+ */
+static int loongson2_dmamux_set_route(struct loongson2_dmamux *mux, u32 request, u32 channel)
+{
+	const struct loongson2_dmamux_map *map;
+	u32 reg_val, route_val, high_val = 0;
+
+	if (request >= mux->config->num_maps)
+		return -EINVAL;
+
+	if (channel > mux->config->max_channel)
+		return -EINVAL;
+
+	map = &mux->config->maps[request];
+
+	switch (map->type) {
+	case LOONGSON_DMAMUX_PAIR:
+		route_val = channel / 2;
+		break;
+	case LOONGSON_DMAMUX_SINGLE:
+		route_val = channel;
+		if (map->high_reg)
+			high_val = (channel >> 2) & 0x1;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	mutex_lock(&mux->lock);
+
+	/* Write the primary register field */
+	reg_val = loongson2_dmamux_read(mux, map->reg);
+	reg_val &= ~map->mask;
+	reg_val |= field_prep(map->mask, route_val);
+	loongson2_dmamux_write(mux, map->reg, reg_val);
+
+	/* Write the extension register field if present */
+	if (map->high_reg) {
+		reg_val = loongson2_dmamux_read(mux, map->high_reg);
+		reg_val &= ~BIT(map->high_shift);
+		reg_val |= high_val << map->high_shift;
+		loongson2_dmamux_write(mux, map->high_reg, reg_val);
+	}
+
+	mutex_unlock(&mux->lock);
+
+	return 0;
+}
+
+/**
+ * loongson2_dmamux_route_allocate() - DMA router allocation callback
+ * @dma_spec: OF DMA specifier <request> <channel> <flags>
+ * @ofdma:    Router's of_dma structure; ofdma->dma_router points to our
+ *            struct dma_router embedded in struct loongson2_dmamux.
+ *
+ * Return: NULL as route_data (nothing to free), or an ERR_PTR on failure.
+ */
+static void *loongson2_dmamux_route_allocate(struct of_phandle_args *dma_spec,
+					     struct of_dma *ofdma)
+{
+	struct loongson2_dmamux *mux;
+	u32 request, channel, flags;
+	int ret;
+
+	if (!ofdma->dma_router)
+		return ERR_PTR(-EINVAL);
+
+	mux = container_of(ofdma->dma_router, struct loongson2_dmamux, dma_router);
+
+	if (dma_spec->args_count < 3)
+		return ERR_PTR(-EINVAL);
+
+	request = dma_spec->args[0];
+	channel = dma_spec->args[1];
+	flags = dma_spec->args[2];
+
+	if (request >= mux->config->num_maps ||
+	    channel > mux->config->max_channel)
+		return ERR_PTR(-EINVAL);
+
+	/* Program the SoC-specific route register */
+	ret = loongson2_dmamux_set_route(mux, request, channel);
+	if (ret)
+		return ERR_PTR(ret);
+
+	/*
+	 * Rewrite dma_spec so the kernel router framework uses it to look
+	 * up the parent DMA controller and invoke its xlate.
+	 */
+	dma_spec->np = mux->dma_np;
+	dma_spec->args[0] = channel;
+	dma_spec->args[1] = flags;
+	dma_spec->args_count = mux->config->parent_args_count;
+
+	return NULL;
+}
+
+static int loongson2_dmamux_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;

drivers/dma/loongson/loongson2-apb-dmamux.c line 322

Quoted 10 lines
drivers/dma/loongson/loongson2-apb-dmamux.c new file +10 -0
+	struct loongson2_dmamux *mux;
+	int ret;
+
+	mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
+	if (!mux)
+		return -ENOMEM;
+
+	mux->dev = dev;
+	mux->config = &ls2k0300_dmamux_config;
+	mutex_init(&mux->lock);

use devm_mutex_init();

Quoted 26 lines
drivers/dma/loongson/loongson2-apb-dmamux.c new file +26 -0
+
+	mux->base = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(mux->base))
+		return PTR_ERR(mux->base);
+
+	/* Parse parent DMA controller nodes */
+	mux->dma_np = of_parse_phandle(dev->of_node, "dma-masters", 0);
+	if (!mux->dma_np)
+		return dev_err_probe(dev, -ENODEV,
+				     "Failed to find parent DMA controller\n");
+
+	mux->dma_router.dev = dev;
+	mux->dma_router.route_free = NULL;
+
+	platform_set_drvdata(pdev, mux);
+
+	ret = of_dma_router_register(dev->of_node, loongson2_dmamux_route_allocate,
+				     &mux->dma_router);
+	if (ret) {
+		of_node_put(mux->dma_np);
+		return dev_err_probe(dev, ret,
+				     "Failed to register DMA router\n");
+	}
+
+	dev_info(dev,
+		 "Loongson2 DMA Mux Controller registered (max_channel=%u, requests=%u)\n",

drivers/dma/loongson/loongson2-apb-dmamux.c line 358

Quoted 10 lines
drivers/dma/loongson/loongson2-apb-dmamux.c new file +10 -0
+		 mux->config->max_channel, mux->config->num_maps);
+
+	return 0;
+}
+
+static void loongson2_dmamux_remove(struct platform_device *pdev)
+{
+	struct loongson2_dmamux *mux = platform_get_drvdata(pdev);
+
+	of_dma_controller_free(pdev->dev.of_node);

of_dma_router_free() is match of_dma_router_register() even through it is the same.

Frank

Quoted 25 lines
drivers/dma/loongson/loongson2-apb-dmamux.c new file +22 -1
+	of_node_put(mux->dma_np);
+}
+
+static const struct of_device_id loongson2_dmamux_of_match[] = {
+	{ .compatible = "loongson,ls2k0300-dmamux" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, loongson2_dmamux_of_match);
+
+static struct platform_driver loongson2_dmamux_driver = {
+	.driver = {
+		.name = "loongson2-dmamux",
+		.of_match_table = loongson2_dmamux_of_match,
+	},
+	.probe = loongson2_dmamux_probe,
+	.remove = loongson2_dmamux_remove,
+};
+module_platform_driver(loongson2_dmamux_driver);
+
+MODULE_DESCRIPTION("Loongson SoC DMA Mux Controller driver");
+MODULE_AUTHOR("Loongson Technology Corporation Limited");
+MODULE_LICENSE("GPL");
--

2.52.0

#13 lore raw
In reply to Binbin Zhou
Quoted 8 lines · Binbin Zhou

Hi all:

Loongson-2K0300 integrates a CMC DMA controller with 8 physical channels. Peripheral DMA requests are not hard-wired to these channels; they are routed through the CHIP_CTRL13/14/15 DMA_MAP fields.

This series adds support for that DMA request router.

Quoted 8 lines

Patch 1 adds the DT binding constants for the peripheral request IDs. Patch 2 adds the DT binding documentation for the DMA mux. Patch 3 adds the driver, Kconfig symbol and Makefile entry.

The driver registers with of_dma_router_register(). In the route allocation callback it validates the <request> <channel> <flags> specifier, programs the SoC-specific route register, and forwards <channel> <flags> to the parent CMC DMA controller.

Is it possible use standard mux interface?

drivers/mux/mmio.c

Frank

Quoted 25 lines

Thanks.
Binbin.

Binbin Zhou (3):
dt-bindings: dma: loongson: Add Loongson-2 DMA mux request IDs
dt-bindings: dma: loongson: Add Loongson-2K0300 DMA mux support
dmaengine: loongson: Add Loongson-2K0300 APB DMA mux driver

 .../dma/loongson,ls2k0300-dmamux.yaml         |  49 +++
 MAINTAINERS                                   |   3 +
 drivers/dma/loongson/Kconfig                  |  13 +
 drivers/dma/loongson/Makefile                 |   1 +
 drivers/dma/loongson/loongson2-apb-dmamux.c   | 380 ++++++++++++++++++
 .../dt-bindings/dma/loongson,ls2k-dmamux.h    |  56 +++
 6 files changed, 502 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.yaml
 create mode 100644 drivers/dma/loongson/loongson2-apb-dmamux.c
 create mode 100644 include/dt-bindings/dma/loongson,ls2k-dmamux.h

base-commit: 0a8dda0a15d3926422d286567f945a05328a4ac6 --
2.52.0

#14 lore raw

Hi Frank:

Quoted 14 lines · Frank Li
Quoted 12 lines · Binbin Zhou

Hi all:

Loongson-2K0300 integrates a CMC DMA controller with 8 physical channels. Peripheral DMA requests are not hard-wired to these channels; they are routed through the CHIP_CTRL13/14/15 DMA_MAP fields.

This series adds support for that DMA request router.

Patch 1 adds the DT binding constants for the peripheral request IDs. Patch 2 adds the DT binding documentation for the DMA mux. Patch 3 adds the driver, Kconfig symbol and Makefile entry.

Quoted 8 lines
Quoted 4 lines

The driver registers with of_dma_router_register(). In the route allocation callback it validates the <request> <channel> <flags> specifier, programs the SoC-specific route register, and forwards <channel> <flags> to the parent CMC DMA controller.

Is it possible use standard mux interface?

drivers/mux/mmio.c

It does not appear to be reusable.
The mmio.c file appears to only involve setting bit fields in registers, but here, not only do we need to set the bit fields, but we also need to pass additional parameters to the DMA controller.

Take i2s as an example:

&i2s {
        status = “okay”;

        pinctrl-0 = <&i2s_pins>;
        pinctrl-names = “default”;

        dmas = <&dma_router LS2K0300_DMA_I2S 3 0xbf>,
                    <&dma_router LS2K0300_DMA_I2S 2 0xaf>;
        dma-names = “tx”, “rx”;
};

We need to pass the last two parameters <3, 0xbf>/<2, 0xaf> to loongson2-apb-cmc-dma to complete the relevant configuration.

Quoted 27 lines

Frank

Quoted 25 lines

Thanks.
Binbin.

Binbin Zhou (3):
dt-bindings: dma: loongson: Add Loongson-2 DMA mux request IDs
dt-bindings: dma: loongson: Add Loongson-2K0300 DMA mux support
dmaengine: loongson: Add Loongson-2K0300 APB DMA mux driver

 .../dma/loongson,ls2k0300-dmamux.yaml         |  49 +++
 MAINTAINERS                                   |   3 +
 drivers/dma/loongson/Kconfig                  |  13 +
 drivers/dma/loongson/Makefile                 |   1 +
 drivers/dma/loongson/loongson2-apb-dmamux.c   | 380 ++++++++++++++++++
 .../dt-bindings/dma/loongson,ls2k-dmamux.h    |  56 +++
 6 files changed, 502 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.yaml
 create mode 100644 drivers/dma/loongson/loongson2-apb-dmamux.c
 create mode 100644 include/dt-bindings/dma/loongson,ls2k-dmamux.h

base-commit: 0a8dda0a15d3926422d286567f945a05328a4ac6 --
2.52.0

Signature · 2 lines
Thanks.
Binbin