[PATCH v1 4/5] dmaengine: cix-sky1-dma350: Add Sky1 integration driver

Jelly Jia Jelly.Jia at cixtech.com
Sun Sep 6 20:34:03 PDT 2026


Add a CIX Sky1 integration driver for Arm DMA-350 instances.

The driver manages clocks, reset, optional interrupt routing, optional
reserved memory attachment, and populates the child Arm DMA-350
controller.

Signed-off-by: Jelly Jia <Jelly.Jia at cixtech.com>
---
 MAINTAINERS                   |   1 +
 drivers/dma/Kconfig           |  15 +++
 drivers/dma/Makefile          |   1 +
 drivers/dma/cix-sky1-dma350.c | 206 ++++++++++++++++++++++++++++++++++
 4 files changed, 223 insertions(+)
 create mode 100644 drivers/dma/cix-sky1-dma350.c

diff --git a/MAINTAINERS b/MAINTAINERS
index feff8f9ecb2b..8d972fe2c994 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2828,6 +2828,7 @@ F:	Documentation/devicetree/bindings/arm/cix.yaml
 F:	Documentation/devicetree/bindings/dma/cix,sky1-dma350.yaml
 F:	Documentation/devicetree/bindings/mailbox/cix,sky1-mbox.yaml
 F:	arch/arm64/boot/dts/cix/
+F:	drivers/dma/cix-sky1-dma350.c
 F:	drivers/mailbox/cix-mailbox.c
 K:	\bcix\b
 
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index ae6a682c9f76..51c881dfa6d3 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -147,6 +147,21 @@ config DMA_BCM2835
 	select DMA_ENGINE
 	select DMA_VIRTUAL_CHANNELS
 
+config CIX_SKY1_DMA350
+	tristate "CIX Sky1 DMA-350 integration support"
+	depends on OF && ARM_DMA350
+	depends on OF_RESERVED_MEM
+	depends on ARCH_CIX || COMPILE_TEST
+	default ARCH_CIX
+	select MFD_SYSCON
+	help
+	  Enable support for the CIX Sky1 integration around Arm
+	  DMA-350 controllers.
+
+	  This driver owns SoC integration resources such as clocks, resets,
+	  reserved memory attachment, and interrupt routing registers, while the
+	  child controller is handled by the generic Arm DMA-350 driver.
+
 config DMA_JZ4780
 	tristate "JZ4780 DMA support"
 	depends on MIPS || COMPILE_TEST
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 14aa086629d5..a69964251f68 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_AT_XDMAC) += at_xdmac.o
 obj-$(CONFIG_AXI_DMAC) += dma-axi-dmac.o
 obj-$(CONFIG_BCM_SBA_RAID) += bcm-sba-raid.o
 obj-$(CONFIG_DMA_BCM2835) += bcm2835-dma.o
+obj-$(CONFIG_CIX_SKY1_DMA350) += cix-sky1-dma350.o
 obj-$(CONFIG_DMA_JZ4780) += dma-jz4780.o
 obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o
 obj-$(CONFIG_DMA_SUN4I) += sun4i-dma.o
diff --git a/drivers/dma/cix-sky1-dma350.c b/drivers/dma/cix-sky1-dma350.c
new file mode 100644
index 000000000000..5904453f3481
--- /dev/null
+++ b/drivers/dma/cix-sky1-dma350.c
@@ -0,0 +1,206 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * CIX Sky1 DMA-350 integration driver
+ */
+
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#define SKY1_DMA350_CRU_DMAC_AP_IRQ	0x54
+#define SKY1_DMA350_IRQ_ROUTE_MASK	0xff
+
+struct cix_sky1_dma350 {
+	struct clk_bulk_data *clks;
+	struct reset_control *reset;
+	struct regmap *irq_router;
+	struct device *rmem_dev;
+	int num_clks;
+};
+
+static int cix_sky1_dma350_route_irqs(struct device *dev)
+{
+	struct cix_sky1_dma350 *data = dev_get_drvdata(dev);
+
+	if (!data->irq_router)
+		return 0;
+
+	return regmap_update_bits(data->irq_router,
+				  SKY1_DMA350_CRU_DMAC_AP_IRQ,
+				  SKY1_DMA350_IRQ_ROUTE_MASK,
+				  SKY1_DMA350_IRQ_ROUTE_MASK);
+}
+
+static int cix_sky1_dma350_enable_resources(struct device *dev)
+{
+	struct cix_sky1_dma350 *data = dev_get_drvdata(dev);
+	int ret;
+
+	ret = clk_bulk_prepare_enable(data->num_clks, data->clks);
+	if (ret)
+		return ret;
+
+	ret = reset_control_reset(data->reset);
+	if (ret)
+		goto err_disable_clks;
+
+	ret = cix_sky1_dma350_route_irqs(dev);
+	if (ret)
+		goto err_disable_clks;
+
+	return 0;
+
+err_disable_clks:
+	clk_bulk_disable_unprepare(data->num_clks, data->clks);
+	return ret;
+}
+
+static void cix_sky1_dma350_disable_resources(struct device *dev)
+{
+	struct cix_sky1_dma350 *data = dev_get_drvdata(dev);
+
+	reset_control_assert(data->reset);
+	clk_bulk_disable_unprepare(data->num_clks, data->clks);
+}
+
+static int cix_sky1_dma350_attach_reserved_mem(struct device *dev)
+{
+	struct cix_sky1_dma350 *data = dev_get_drvdata(dev);
+	struct platform_device *child_pdev;
+	struct device_node *child_np;
+	int ret;
+
+	if (!of_property_present(dev->of_node, "memory-region"))
+		return 0;
+
+	child_np = of_get_compatible_child(dev->of_node, "arm,dma-350");
+	if (!child_np)
+		return -ENODEV;
+
+	child_pdev = of_find_device_by_node(child_np);
+	of_node_put(child_np);
+	if (!child_pdev)
+		return -EPROBE_DEFER;
+
+	/*
+	 * Reserved memory is attached after the child has probed. This relies
+	 * on arm-dma350 not allocating coherent command buffers in probe;
+	 * those allocations happen per descriptor at prep time.
+	 */
+	ret = of_reserved_mem_device_init_by_idx(&child_pdev->dev,
+						 dev->of_node, 0);
+	if (ret) {
+		put_device(&child_pdev->dev);
+		return ret;
+	}
+
+	data->rmem_dev = &child_pdev->dev;
+
+	return 0;
+}
+
+static void cix_sky1_dma350_release_reserved_mem(struct device *dev)
+{
+	struct cix_sky1_dma350 *data = dev_get_drvdata(dev);
+
+	if (data->rmem_dev) {
+		of_reserved_mem_device_release(data->rmem_dev);
+		put_device(data->rmem_dev);
+		data->rmem_dev = NULL;
+	}
+}
+
+static int cix_sky1_dma350_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct cix_sky1_dma350 *data;
+	int ret;
+
+	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, data);
+
+	data->num_clks = devm_clk_bulk_get_all(dev, &data->clks);
+	if (data->num_clks < 0)
+		return dev_err_probe(dev, data->num_clks,
+				     "failed to get clocks\n");
+
+	data->reset = devm_reset_control_get_optional_exclusive(dev, NULL);
+	if (IS_ERR(data->reset))
+		return dev_err_probe(dev, PTR_ERR(data->reset),
+				     "failed to get reset\n");
+
+	data->irq_router = syscon_regmap_lookup_by_phandle_optional(dev->of_node,
+								    "cix,irq-router");
+	if (IS_ERR(data->irq_router))
+		return dev_err_probe(dev, PTR_ERR(data->irq_router),
+				     "failed to get IRQ router\n");
+
+	ret = cix_sky1_dma350_enable_resources(dev);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to enable resources\n");
+
+	ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
+	if (ret)
+		goto err_disable_resources;
+
+	ret = cix_sky1_dma350_attach_reserved_mem(dev);
+	if (ret)
+		goto err_depopulate;
+
+	return 0;
+
+err_depopulate:
+	of_platform_depopulate(dev);
+err_disable_resources:
+	cix_sky1_dma350_disable_resources(dev);
+	return dev_err_probe(dev, ret, "failed to initialize child devices\n");
+}
+
+static void cix_sky1_dma350_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	of_platform_depopulate(dev);
+	cix_sky1_dma350_release_reserved_mem(dev);
+	cix_sky1_dma350_disable_resources(dev);
+}
+
+static int __maybe_unused cix_sky1_dma350_resume_noirq(struct device *dev)
+{
+	return cix_sky1_dma350_route_irqs(dev);
+}
+
+static const struct dev_pm_ops cix_sky1_dma350_pm = {
+	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(NULL, cix_sky1_dma350_resume_noirq)
+};
+
+static const struct of_device_id cix_sky1_dma350_of_match[] = {
+	{ .compatible = "cix,sky1-dma350" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, cix_sky1_dma350_of_match);
+
+static struct platform_driver cix_sky1_dma350_driver = {
+	.probe = cix_sky1_dma350_probe,
+	.remove = cix_sky1_dma350_remove,
+	.driver = {
+		.name = "cix-sky1-dma350",
+		.of_match_table = cix_sky1_dma350_of_match,
+		.pm = pm_sleep_ptr(&cix_sky1_dma350_pm),
+	},
+};
+module_platform_driver(cix_sky1_dma350_driver);
+
+MODULE_AUTHOR("Jelly Jia <Jelly.Jia at cixtech.com>");
+MODULE_DESCRIPTION("CIX Sky1 DMA-350 integration driver");
+MODULE_LICENSE("GPL");
-- 
2.54.0




More information about the linux-arm-kernel mailing list