Add Loongson-2K0300 APB DMA mux support
Subsystems (3)
v2 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.
=========
V2:
Patch(1/3):
- Drop LS2K0300_DMA_REQ_MAX as it is not part of the ABI;
Patch(2/3):
- More detailed description of dma-cells;
- Add the dma_requests constraint
Patch(3/3):
- Drop loongson2_dmamux_map->shift;
- Move of_parse_phandle() into loongson2_dmamux_route_allocate() avoid UAF;
- Drop loongson2_dmamux->dma_np;
- Check dma_requests;
- Add devm_add_action_or_reset() instead of .remove();
- Replace metux() with spinlock(), which has lower overhead;
- Add loongson2_dmamux_route_free().
Link to V1:
https://lore.kernel.org/all/[email protected]/
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 | 72 ++++ MAINTAINERS | 3 + drivers/dma/loongson/Makefile | 1 + drivers/dma/loongson/loongson2-apb-dmamux.c | 398 ++++++++++++++++++ .../dt-bindings/dma/loongson,ls2k-dmamux.h | 53 +++ 5 files changed, 527 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: 1d8ae2003de08f61095e4e928b1b8cce6c23cabd
Signature · 1 line
2.52.0
PATCH v2 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 | 53 +++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 include/dt-bindings/dma/loongson,ls2k-dmamux.h
MAINTAINERS +1 -0
@@ -15354,6 +15354,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 +53 -0
53 lines not on this page · raw message
Signature · 1 line
2.52.0
Re: [PATCH v2 1/3] dt-bindings: dma: loongson: Add Loongson-2 DMA mux request IDs
Quoted 8 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.
Signed-off-by Binbin Zhou <[email protected]>
Squash this with the binding patch please.
If checkpatch told you to split this, binding headers are bindings and such that warning is a false positive.
pw-bot: changes-requested
Thanks,
Conor.
Quoted 80 lines
--- MAINTAINERS | 1 + .../dt-bindings/dma/loongson,ls2k-dmamux.h | 53 +++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 include/dt-bindings/dma/loongson,ls2k-dmamux.hMAINTAINERS +1
-0@@ -15354,6 +15354,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 +53
-0@@ -0,0 +1,53 @@ +/* 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 */ +#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 + +#endif /* _DT_BINDINGS_DMA_LOONGSON_LS2K_DMAMUX_H */ Signature · 1 line
2.52.0
PATCH v2 2/3 dt-bindings: dma: loongson: Add Loongson-2K0300 DMA mux support
In reply to Binbin ZhouThe 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 | 72 +++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 73 insertions(+) create mode 100644 Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.yaml
Documentation/devicetree/bindings/dma/loongson,ls2k0300-dmamux.yaml new file +72 -0
@@ -0,0 +1,72 @@ +# 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. 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: + - I2S/SPI/UART/I2C: Require adjacent channels in pairs; valid + values are 0/1, 2/3, 4/5, 6/7; + - SM3/SM4/DES/AES: Single channel; valid values are limited to 0/1; + - 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 + + dma-requests: + const: 22 + +required: + - compatible + - reg + - dma-requests + +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
@@ -15351,6 +15351,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
Re: [PATCH v2 2/3] dt-bindings: dma: loongson: Add Loongson-2K0300 DMA mux support
pw-bot: not-applicable
PATCH v2 3/3 dmaengine: loongson: Add Loongson-2K0300 APB DMA mux driver
In reply to Binbin ZhouThe 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/Makefile | 1 + drivers/dma/loongson/loongson2-apb-dmamux.c | 398 ++++++++++++++++++++ 3 files changed, 400 insertions(+) create mode 100644 drivers/dma/loongson/loongson2-apb-dmamux.c
MAINTAINERS +1 -0
@@ -15355,6 +15355,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/Makefile +1 -0
@@ -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_CMC_DMA) += loongson2-apb-dmamux.o
drivers/dma/loongson/loongson2-apb-dmamux.c new file +398 -0
398 lines not on this page · raw message
Signature · 1 line
2.52.0
Re: [PATCH v2 3/3] dmaengine: loongson: Add Loongson-2K0300 APB DMA mux driver
Quoted 46 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_CTRLregister field, and then rewrites the specifier to <channel> <flags> so that the parent CMC DMA controller's xlate is used.Signed-off-by Binbin Zhou <[email protected]>--- MAINTAINERS | 1 + drivers/dma/loongson/Makefile | 1 + drivers/dma/loongson/loongson2-apb-dmamux.c | 398 ++++++++++++++++++++ 3 files changed, 400 insertions(+) create mode 100644 drivers/dma/loongson/loongson2-apb-dmamux.cMAINTAINERS +1
-0@@ -15355,6 +15355,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 DRIVERdrivers/dma/loongson/Makefile +1
-0@@ -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_CMC_DMA) += loongson2-apb-dmamux.odrivers/dma/loongson/loongson2-apb-dmamux.c new file +0
-0@@ -0,0 +1,398 @@
...
Quoted 29 lines
drivers/dma/loongson/loongson2-apb-dmamux.c new file +29
-0+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 ; + } + +guard (spinlock_irqsave)(&mux->lock); + +/* Write the primary register field */
drivers/dma/loongson/loongson2-apb-dmamux.c line 248
Quoted 10 lines
drivers/dma/loongson/loongson2-apb-dmamux.c new file +10
-0+ 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;
assign_bit(map->high_shift, ®_val, high_val);
other looks good
Frank
Quoted 153 lines
drivers/dma/loongson/loongson2-apb-dmamux.c new file +150
-1+loongson2_dmamux_write (mux, map->high_reg, reg_val); + } + +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); + +/* The of_node_put() will be done in of_dma_router_xlate function */ + dma_spec->np =of_parse_phandle (ofdma->of_node,"dma-masters" ,0 ); +if (!dma_spec->np) +return ERR_PTR (-ENODEV ); + + dma_spec->args[0 ] = channel; + dma_spec->args[1 ] = flags; + dma_spec->args_count = mux->config->parent_args_count; + +return NULL ; +} + +/** +* loongson2_dmamux_route_free() - DMA router free callback +* @dev: Back pointer to the router's device +* @route_data: Router-specific data returned by route_allocate +* +* The driver does not allocate any per-route resources, so this callback +* is intentionally empty. It must still be provided because the OF DMA +* core calls it unconditionally on some error paths (e.g. -EPROBE_DEFER) +* without checking for a NULL pointer. +*/ +static void loongson2_dmamux_route_free (struct device *dev,void *route_data) +{ +} + +/** +* loongson2_dmamux_unregister_of_dma() - devm action to unregister the router +* @data: Pointer to struct loongson2_dmamux +* +* Called automatically by the device-managed resource framework both on +* probe failure and on driver removal, ensuring the OF DMA router is +* always unregistered. +*/ +static void loongson2_dmamux_unregister_of_dma (void *data) +{ +struct loongson2_dmamux *mux = data; + +of_dma_router_free (mux->dma_router.dev->of_node); +} + +static int loongson2_dmamux_probe (struct platform_device *pdev) +{ +struct device *dev = &pdev->dev; +struct loongson2_dmamux *mux; +u32 dma_requests; +int ret; + + mux =devm_kzalloc (dev,sizeof (*mux),GFP_KERNEL ); +if (!mux) +return -ENOMEM ; + + mux->base =devm_platform_ioremap_resource (pdev,0 ); +if (IS_ERR (mux->base)) +return PTR_ERR (mux->base); + +spin_lock_init (&mux->lock); + mux->config = &ls2k0300_dmamux_config; + + ret =of_property_read_u32 (dev->of_node,"dma-requests" , &dma_requests); +if (ret) +return dev_err_probe (dev, ret,"missing dma-requests property\n" ); + +if (dma_requests != mux->config->num_maps) +return dev_err_probe (dev, -EINVAL , +"dma-requests=%u mismatches driver num_maps=%u" , + dma_requests, mux->config->num_maps); + + mux->dma_router.dev = dev; + mux->dma_router.route_free = loongson2_dmamux_route_free; + +platform_set_drvdata (pdev, mux); + + ret =of_dma_router_register (dev->of_node, loongson2_dmamux_route_allocate, + &mux->dma_router); +if (ret) +return dev_err_probe (dev, ret, +"Failed to register DMA router" ); + + ret =devm_add_action_or_reset (dev, loongson2_dmamux_unregister_of_dma, mux); +if (ret) +return ret; + +dev_info (dev, +"Loongson2 DMA Mux Controller registered (max_channel=%u, requests=%u)" , + mux->config->max_channel, mux->config->num_maps); + +return 0 ; +} + +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, +}; +module_platform_driver (loongson2_dmamux_driver); + +MODULE_DESCRIPTION ("Loongson2 SoC DMA Mux Controller driver" ); +MODULE_AUTHOR ("Loongson Technology Corporation Limited" ); +MODULE_LICENSE ("GPL" ); --2.52.0
Re: [PATCH v2 3/3] dmaengine: loongson: Add Loongson-2K0300 APB DMA mux driver
Hi Frank:
Quoted 82 lines · Frank Li
Quoted 46 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_CTRLregister field, and then rewrites the specifier to <channel> <flags> so that the parent CMC DMA controller's xlate is used.Signed-off-by Binbin Zhou <[email protected]>--- MAINTAINERS | 1 + drivers/dma/loongson/Makefile | 1 + drivers/dma/loongson/loongson2-apb-dmamux.c | 398 ++++++++++++++++++++ 3 files changed, 400 insertions(+) create mode 100644 drivers/dma/loongson/loongson2-apb-dmamux.cMAINTAINERS +1
-0@@ -15355,6 +15355,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 DRIVERdrivers/dma/loongson/Makefile +1
-0@@ -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_CMC_DMA) += loongson2-apb-dmamux.odrivers/dma/loongson/loongson2-apb-dmamux.c new file +0
-0@@ -0,0 +1,398 @@...
Quoted 33 lines
drivers/dma/loongson/loongson2-apb-dmamux.c new file +33
-0+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 ; + } + +guard (spinlock_irqsave)(&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);
Quoted 8 lines
drivers/dma/loongson/loongson2-apb-dmamux.c line 248
Quoted 6 lines
drivers/dma/loongson/loongson2-apb-dmamux.c new file +6
-0+ +/* 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;
assign_bit(map->high_shift, ®_val, high_val);
I think assign_bit() here is not quite suitable, reg_val is a u32 (returned by loongson2_dmamux_read()), but assign_bit() expects a volatile unsigned long *.
How about adding a helper function:
static void loongson2_dmamux_assign_bits(struct loongson2_dmamux *priv,
unsigned int reg, u32 mask, u32 val)
{
u32 reg_val;
reg_val = loongson2_dmamux_read(priv, reg);
reg_val &= ~mask;
reg_val |= val & mask;
loongson2_dmamux_write(priv, reg, reg_val);
}Then we can call this function:
/* Primary register field */ loongson2_dmamux_assign_bits(priv, map->reg, map->mask,
field_prep(map->mask, route_val));
/* Extension register bit if present */
if (map->high_reg)
loongson2_dmamux_assign_bits(priv, map->high_reg,
BIT(map->high_shift),
high_val ? BIT(map->high_shift) : 0);Quoted 149 lines
other looks good
Frank
Quoted 145 lines
drivers/dma/loongson/loongson2-apb-dmamux.c new file +145
-0+loongson2_dmamux_write (mux, map->high_reg, reg_val); + } + +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); + +/* The of_node_put() will be done in of_dma_router_xlate function */ + dma_spec->np =of_parse_phandle (ofdma->of_node,"dma-masters" ,0 ); +if (!dma_spec->np) +return ERR_PTR (-ENODEV ); + + dma_spec->args[0 ] = channel; + dma_spec->args[1 ] = flags; + dma_spec->args_count = mux->config->parent_args_count; + +return NULL ; +} + +/** +* loongson2_dmamux_route_free() - DMA router free callback +* @dev: Back pointer to the router's device +* @route_data: Router-specific data returned by route_allocate +* +* The driver does not allocate any per-route resources, so this callback +* is intentionally empty. It must still be provided because the OF DMA +* core calls it unconditionally on some error paths (e.g. -EPROBE_DEFER) +* without checking for a NULL pointer. +*/ +static void loongson2_dmamux_route_free (struct device *dev,void *route_data) +{ +} + +/** +* loongson2_dmamux_unregister_of_dma() - devm action to unregister the router +* @data: Pointer to struct loongson2_dmamux +* +* Called automatically by the device-managed resource framework both on +* probe failure and on driver removal, ensuring the OF DMA router is +* always unregistered. +*/ +static void loongson2_dmamux_unregister_of_dma (void *data) +{ +struct loongson2_dmamux *mux = data; + +of_dma_router_free (mux->dma_router.dev->of_node); +} + +static int loongson2_dmamux_probe (struct platform_device *pdev) +{ +struct device *dev = &pdev->dev; +struct loongson2_dmamux *mux; +u32 dma_requests; +int ret; + + mux =devm_kzalloc (dev,sizeof (*mux),GFP_KERNEL ); +if (!mux) +return -ENOMEM ; + + mux->base =devm_platform_ioremap_resource (pdev,0 ); +if (IS_ERR (mux->base)) +return PTR_ERR (mux->base); + +spin_lock_init (&mux->lock); + mux->config = &ls2k0300_dmamux_config; + + ret =of_property_read_u32 (dev->of_node,"dma-requests" , &dma_requests); +if (ret) +return dev_err_probe (dev, ret,"missing dma-requests property\n" ); + +if (dma_requests != mux->config->num_maps) +return dev_err_probe (dev, -EINVAL , +"dma-requests=%u mismatches driver num_maps=%u" , + dma_requests, mux->config->num_maps); + + mux->dma_router.dev = dev; + mux->dma_router.route_free = loongson2_dmamux_route_free; + +platform_set_drvdata (pdev, mux); + + ret =of_dma_router_register (dev->of_node, loongson2_dmamux_route_allocate, + &mux->dma_router); +if (ret) +return dev_err_probe (dev, ret, +"Failed to register DMA router" ); + + ret =devm_add_action_or_reset (dev, loongson2_dmamux_unregister_of_dma, mux); +if (ret) +return ret; + +dev_info (dev, +"Loongson2 DMA Mux Controller registered (max_channel=%u, requests=%u)" , + mux->config->max_channel, mux->config->num_maps); + +return 0 ; +} + +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, +};
Quoted 8 lines
Quoted 8 lines
drivers/dma/loongson/loongson2-apb-dmamux.c new file +5
-1+module_platform_driver (loongson2_dmamux_driver); + +MODULE_DESCRIPTION ("Loongson2 SoC DMA Mux Controller driver" ); +MODULE_AUTHOR ("Loongson Technology Corporation Limited" ); +MODULE_LICENSE ("GPL" ); --2.52.0
--
Thanks.
Binbin
Frank Li
Reviewed-byRe: [PATCH v2 3/3] dmaengine: loongson: Add Loongson-2K0300 APB DMA mux driver
Quoted 102 lines · Binbin Zhou
Hi Frank:
Quoted 82 lines · Frank Li
Quoted 46 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_CTRLregister field, and then rewrites the specifier to <channel> <flags> so that the parent CMC DMA controller's xlate is used.Signed-off-by Binbin Zhou <[email protected]>--- MAINTAINERS | 1 + drivers/dma/loongson/Makefile | 1 + drivers/dma/loongson/loongson2-apb-dmamux.c | 398 ++++++++++++++++++++ 3 files changed, 400 insertions(+) create mode 100644 drivers/dma/loongson/loongson2-apb-dmamux.cMAINTAINERS +1
-0@@ -15355,6 +15355,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 DRIVERdrivers/dma/loongson/Makefile +1
-0@@ -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_CMC_DMA) += loongson2-apb-dmamux.odrivers/dma/loongson/loongson2-apb-dmamux.c new file +0
-0@@ -0,0 +1,398 @@...
Quoted 33 lines
drivers/dma/loongson/loongson2-apb-dmamux.c new file +33
-0+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 ; + } + +guard (spinlock_irqsave)(&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);Quoted 8 lines
drivers/dma/loongson/loongson2-apb-dmamux.c line 248
Quoted 6 lines
drivers/dma/loongson/loongson2-apb-dmamux.c new file +6
-0+ +/* 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;
assign_bit(map->high_shift, ®_val, high_val);I think
assign_bit()here is not quite suitable,reg_valis a u32 (returned byloongson2_dmamux_read()), butassign_bit()expects avolatile unsigned long *.How about adding a helper function:
static void loongson2_dmamux_assign_bits(struct
loongson2_dmamux*priv,
unsigned int reg, u32 mask, u32 val)
Quoted 9 lines
{
u32 reg_val;
reg_val = loongson2_dmamux_read(priv, reg);
reg_val &= ~mask;
reg_val |= val & mask;
loongson2_dmamux_write(priv, reg, reg_val);
}Okay, if only use once, old code should be fine.
Huacai Chen
maintainerRe: [PATCH v2 3/3] dmaengine: loongson: Add Loongson-2K0300 APB DMA mux driver
In reply to Binbin ZhouHi, Binbin,
Quoted 37 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_CTRLregister field, and then rewrites the specifier to <channel> <flags> so that the parent CMC DMA controller's xlate is used.Signed-off-by Binbin Zhou <[email protected]>--- MAINTAINERS | 1 + drivers/dma/loongson/Makefile | 1 + drivers/dma/loongson/loongson2-apb-dmamux.c | 398 ++++++++++++++++++++ 3 files changed, 400 insertions(+) create mode 100644 drivers/dma/loongson/loongson2-apb-dmamux.cMAINTAINERS +1
-0@@ -15355,6 +15355,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 DRIVERdrivers/dma/loongson/Makefile +0
-0
drivers/dma/loongson/Makefile line 5
Quoted 4 lines
drivers/dma/loongson/Makefile +1
-0@@ -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_CMC_DMA) += loongson2-apb-dmamux.o
Here we can use
obj-$(CONFIG_LOONGSON2_APB_CMC_DMA) += loongson2-apb-cmc-dma.o loongson2-apb-dmamux.o
Huacai
Quoted 408 lines
drivers/dma/loongson/loongson2-apb-dmamux.c new file +398
-1@@ -0,0 +1,398 @@ +// 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_*) +* - channel: target physical DMA channel +* - flags: channel configuration flags forwarded to the parent DMA +* controller +*/ + +#include <linux/bitfield.h> +#include <linux/cleanup.h> +#include <linux/device.h> +#include <linux/dmaengine.h> +#include <linux/err.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 <linux/spinlock.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). +* @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 +* @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; +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 +* @base: I/O-mapped register base +* @lock: Protects register read-modify-write sequences +* @dma_router: Router structure passed to of_dma_router_register() +* @config: SoC-specific configuration +*/ +struct loongson2_dmamux { +void __iomem *base; +spinlock_t lock;/* Protects register read-modify-write sequences */ +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 ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_UART1 ] = {LS2K0300_CHIP_CTRL13 ,CTRL13_UART1_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_UART2 ] = {LS2K0300_CHIP_CTRL13 ,CTRL13_UART2_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_UART3 ] = {LS2K0300_CHIP_CTRL13 ,CTRL13_UART3_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_UART4 ] = {LS2K0300_CHIP_CTRL13 ,CTRL13_UART4_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_UART5 ] = {LS2K0300_CHIP_CTRL13 ,CTRL13_UART5_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_UART6 ] = {LS2K0300_CHIP_CTRL13 ,CTRL13_UART6_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_UART7 ] = {LS2K0300_CHIP_CTRL13 ,CTRL13_UART7_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_UART8 ] = {LS2K0300_CHIP_CTRL14 ,CTRL14_UART8_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_UART9 ] = {LS2K0300_CHIP_CTRL14 ,CTRL14_UART9_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_I2C0 ] = {LS2K0300_CHIP_CTRL14 ,CTRL14_I2C0_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_I2C1 ] = {LS2K0300_CHIP_CTRL14 ,CTRL14_I2C1_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_I2C2 ] = {LS2K0300_CHIP_CTRL14 ,CTRL14_I2C2_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_I2C3 ] = {LS2K0300_CHIP_CTRL14 ,CTRL14_I2C3_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_SPI2 ] = {LS2K0300_CHIP_CTRL14 ,CTRL14_SPI2_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_SPI3 ] = {LS2K0300_CHIP_CTRL14 ,CTRL14_SPI3_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_I2S ] = {LS2K0300_CHIP_CTRL14 ,CTRL14_I2S_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_ADC ] = {LS2K0300_CHIP_CTRL14 ,CTRL14_ADC_DMA_MAP ,LOONGSON_DMAMUX_SINGLE , +0 ,0 }, + [LS2K0300_DMA_CAN0 ] = {LS2K0300_CHIP_CTRL14 ,CTRL14_CAN0_DMA_MAP ,LOONGSON_DMAMUX_SINGLE , +0 ,0 }, + [LS2K0300_DMA_CAN1 ] = {LS2K0300_CHIP_CTRL14 ,CTRL14_CAN1_DMA_MAP ,LOONGSON_DMAMUX_SINGLE , +0 ,0 }, + [LS2K0300_DMA_CAN2 ] = {LS2K0300_CHIP_CTRL14 ,CTRL14_CAN2_DMA_MAP ,LOONGSON_DMAMUX_SINGLE , +0 ,0 }, + [LS2K0300_DMA_CAN3 ] = {LS2K0300_CHIP_CTRL14 ,CTRL14_CAN3_DMA_MAP ,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 ; + } + +guard (spinlock_irqsave)(&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); + } + +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); + +/* The of_node_put() will be done in of_dma_router_xlate function */ + dma_spec->np =of_parse_phandle (ofdma->of_node,"dma-masters" ,0 ); +if (!dma_spec->np) +return ERR_PTR (-ENODEV ); + + dma_spec->args[0 ] = channel; + dma_spec->args[1 ] = flags; + dma_spec->args_count = mux->config->parent_args_count; + +return NULL ; +} + +/** +* loongson2_dmamux_route_free() - DMA router free callback +* @dev: Back pointer to the router's device +* @route_data: Router-specific data returned by route_allocate +* +* The driver does not allocate any per-route resources, so this callback +* is intentionally empty. It must still be provided because the OF DMA +* core calls it unconditionally on some error paths (e.g. -EPROBE_DEFER) +* without checking for a NULL pointer. +*/ +static void loongson2_dmamux_route_free (struct device *dev,void *route_data) +{ +} + +/** +* loongson2_dmamux_unregister_of_dma() - devm action to unregister the router +* @data: Pointer to struct loongson2_dmamux +* +* Called automatically by the device-managed resource framework both on +* probe failure and on driver removal, ensuring the OF DMA router is +* always unregistered. +*/ +static void loongson2_dmamux_unregister_of_dma (void *data) +{ +struct loongson2_dmamux *mux = data; + +of_dma_router_free (mux->dma_router.dev->of_node); +} + +static int loongson2_dmamux_probe (struct platform_device *pdev) +{ +struct device *dev = &pdev->dev; +struct loongson2_dmamux *mux; +u32 dma_requests; +int ret; + + mux =devm_kzalloc (dev,sizeof (*mux),GFP_KERNEL ); +if (!mux) +return -ENOMEM ; + + mux->base =devm_platform_ioremap_resource (pdev,0 ); +if (IS_ERR (mux->base)) +return PTR_ERR (mux->base); + +spin_lock_init (&mux->lock); + mux->config = &ls2k0300_dmamux_config; + + ret =of_property_read_u32 (dev->of_node,"dma-requests" , &dma_requests); +if (ret) +return dev_err_probe (dev, ret,"missing dma-requests property\n" ); + +if (dma_requests != mux->config->num_maps) +return dev_err_probe (dev, -EINVAL , +"dma-requests=%u mismatches driver num_maps=%u" , + dma_requests, mux->config->num_maps); + + mux->dma_router.dev = dev; + mux->dma_router.route_free = loongson2_dmamux_route_free; + +platform_set_drvdata (pdev, mux); + + ret =of_dma_router_register (dev->of_node, loongson2_dmamux_route_allocate, + &mux->dma_router); +if (ret) +return dev_err_probe (dev, ret, +"Failed to register DMA router" ); + + ret =devm_add_action_or_reset (dev, loongson2_dmamux_unregister_of_dma, mux); +if (ret) +return ret; + +dev_info (dev, +"Loongson2 DMA Mux Controller registered (max_channel=%u, requests=%u)" , + mux->config->max_channel, mux->config->num_maps); + +return 0 ; +} + +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, +}; +module_platform_driver (loongson2_dmamux_driver); + +MODULE_DESCRIPTION ("Loongson2 SoC DMA Mux Controller driver" ); +MODULE_AUTHOR ("Loongson Technology Corporation Limited" ); +MODULE_LICENSE ("GPL" ); --2.52.0
Re: [PATCH v2 3/3] dmaengine: loongson: Add Loongson-2K0300 APB DMA mux driver
Quoted 35 lines · Huacai Chen
Hi, Binbin,
Quoted 31 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_CTRLregister field, and then rewrites the specifier to <channel> <flags> so that the parent CMC DMA controller's xlate is used.Signed-off-by Binbin Zhou <[email protected]>--- MAINTAINERS | 1 + drivers/dma/loongson/Makefile | 1 + drivers/dma/loongson/loongson2-apb-dmamux.c | 398 ++++++++++++++++++++ 3 files changed, 400 insertions(+) create mode 100644 drivers/dma/loongson/loongson2-apb-dmamux.cMAINTAINERS +1
-0@@ -15355,6 +15355,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
Quoted 13 lines
drivers/dma/loongson/Makefile line 5
Quoted 10 lines
MAINTAINERS +0
-0LOONGSON LS2X I2C DRIVERdrivers/dma/loongson/Makefile +1
-0@@ -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_CMC_DMA) += loongson2-apb-dmamux.oHere we can use
obj-$(CONFIG_LOONGSON2_APB_CMC_DMA) += loongson2-apb-cmc-dma.o loongson2-apb-dmamux.o
OK. I will do it in the next version.
Quoted 411 lines
Huacai
Quoted 408 lines
drivers/dma/loongson/loongson2-apb-dmamux.c new file +398
-1@@ -0,0 +1,398 @@ +// 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_*) +* - channel: target physical DMA channel +* - flags: channel configuration flags forwarded to the parent DMA +* controller +*/ + +#include <linux/bitfield.h> +#include <linux/cleanup.h> +#include <linux/device.h> +#include <linux/dmaengine.h> +#include <linux/err.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 <linux/spinlock.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). +* @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 +* @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; +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 +* @base: I/O-mapped register base +* @lock: Protects register read-modify-write sequences +* @dma_router: Router structure passed to of_dma_router_register() +* @config: SoC-specific configuration +*/ +struct loongson2_dmamux { +void __iomem *base; +spinlock_t lock;/* Protects register read-modify-write sequences */ +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 ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_UART1 ] = {LS2K0300_CHIP_CTRL13 ,CTRL13_UART1_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_UART2 ] = {LS2K0300_CHIP_CTRL13 ,CTRL13_UART2_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_UART3 ] = {LS2K0300_CHIP_CTRL13 ,CTRL13_UART3_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_UART4 ] = {LS2K0300_CHIP_CTRL13 ,CTRL13_UART4_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_UART5 ] = {LS2K0300_CHIP_CTRL13 ,CTRL13_UART5_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_UART6 ] = {LS2K0300_CHIP_CTRL13 ,CTRL13_UART6_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_UART7 ] = {LS2K0300_CHIP_CTRL13 ,CTRL13_UART7_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_UART8 ] = {LS2K0300_CHIP_CTRL14 ,CTRL14_UART8_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_UART9 ] = {LS2K0300_CHIP_CTRL14 ,CTRL14_UART9_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_I2C0 ] = {LS2K0300_CHIP_CTRL14 ,CTRL14_I2C0_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_I2C1 ] = {LS2K0300_CHIP_CTRL14 ,CTRL14_I2C1_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_I2C2 ] = {LS2K0300_CHIP_CTRL14 ,CTRL14_I2C2_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_I2C3 ] = {LS2K0300_CHIP_CTRL14 ,CTRL14_I2C3_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_SPI2 ] = {LS2K0300_CHIP_CTRL14 ,CTRL14_SPI2_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_SPI3 ] = {LS2K0300_CHIP_CTRL14 ,CTRL14_SPI3_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_I2S ] = {LS2K0300_CHIP_CTRL14 ,CTRL14_I2S_DMA_MAP ,LOONGSON_DMAMUX_PAIR , +0 ,0 }, + [LS2K0300_DMA_ADC ] = {LS2K0300_CHIP_CTRL14 ,CTRL14_ADC_DMA_MAP ,LOONGSON_DMAMUX_SINGLE , +0 ,0 }, + [LS2K0300_DMA_CAN0 ] = {LS2K0300_CHIP_CTRL14 ,CTRL14_CAN0_DMA_MAP ,LOONGSON_DMAMUX_SINGLE , +0 ,0 }, + [LS2K0300_DMA_CAN1 ] = {LS2K0300_CHIP_CTRL14 ,CTRL14_CAN1_DMA_MAP ,LOONGSON_DMAMUX_SINGLE , +0 ,0 }, + [LS2K0300_DMA_CAN2 ] = {LS2K0300_CHIP_CTRL14 ,CTRL14_CAN2_DMA_MAP ,LOONGSON_DMAMUX_SINGLE , +0 ,0 }, + [LS2K0300_DMA_CAN3 ] = {LS2K0300_CHIP_CTRL14 ,CTRL14_CAN3_DMA_MAP ,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 ; + } + +guard (spinlock_irqsave)(&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); + } + +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); + +/* The of_node_put() will be done in of_dma_router_xlate function */ + dma_spec->np =of_parse_phandle (ofdma->of_node,"dma-masters" ,0 ); +if (!dma_spec->np) +return ERR_PTR (-ENODEV ); + + dma_spec->args[0 ] = channel; + dma_spec->args[1 ] = flags; + dma_spec->args_count = mux->config->parent_args_count; + +return NULL ; +} + +/** +* loongson2_dmamux_route_free() - DMA router free callback +* @dev: Back pointer to the router's device +* @route_data: Router-specific data returned by route_allocate +* +* The driver does not allocate any per-route resources, so this callback +* is intentionally empty. It must still be provided because the OF DMA +* core calls it unconditionally on some error paths (e.g. -EPROBE_DEFER) +* without checking for a NULL pointer. +*/ +static void loongson2_dmamux_route_free (struct device *dev,void *route_data) +{ +} + +/** +* loongson2_dmamux_unregister_of_dma() - devm action to unregister the router +* @data: Pointer to struct loongson2_dmamux +* +* Called automatically by the device-managed resource framework both on +* probe failure and on driver removal, ensuring the OF DMA router is +* always unregistered. +*/ +static void loongson2_dmamux_unregister_of_dma (void *data) +{ +struct loongson2_dmamux *mux = data; + +of_dma_router_free (mux->dma_router.dev->of_node); +} + +static int loongson2_dmamux_probe (struct platform_device *pdev) +{ +struct device *dev = &pdev->dev; +struct loongson2_dmamux *mux; +u32 dma_requests; +int ret; + + mux =devm_kzalloc (dev,sizeof (*mux),GFP_KERNEL ); +if (!mux) +return -ENOMEM ; + + mux->base =devm_platform_ioremap_resource (pdev,0 ); +if (IS_ERR (mux->base)) +return PTR_ERR (mux->base); + +spin_lock_init (&mux->lock); + mux->config = &ls2k0300_dmamux_config; + + ret =of_property_read_u32 (dev->of_node,"dma-requests" , &dma_requests); +if (ret) +return dev_err_probe (dev, ret,"missing dma-requests property\n" ); + +if (dma_requests != mux->config->num_maps) +return dev_err_probe (dev, -EINVAL , +"dma-requests=%u mismatches driver num_maps=%u" , + dma_requests, mux->config->num_maps); + + mux->dma_router.dev = dev; + mux->dma_router.route_free = loongson2_dmamux_route_free; + +platform_set_drvdata (pdev, mux); + + ret =of_dma_router_register (dev->of_node, loongson2_dmamux_route_allocate, + &mux->dma_router); +if (ret) +return dev_err_probe (dev, ret, +"Failed to register DMA router" ); + + ret =devm_add_action_or_reset (dev, loongson2_dmamux_unregister_of_dma, mux); +if (ret) +return ret; + +dev_info (dev, +"Loongson2 DMA Mux Controller registered (max_channel=%u, requests=%u)" , + mux->config->max_channel, mux->config->num_maps); + +return 0 ; +} + +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, +}; +module_platform_driver (loongson2_dmamux_driver); + +MODULE_DESCRIPTION ("Loongson2 SoC DMA Mux Controller driver" ); +MODULE_AUTHOR ("Loongson Technology Corporation Limited" ); +MODULE_LICENSE ("GPL" ); --2.52.0
Signature · 2 lines
Thanks. Binbin