mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v2 0/5] soc: cix: Add the Sky1 AUDSS DMA-350 interrupt path
@ 2026-11-04 12:44 Jelly Jia
  2026-11-04 12:44 ` [PATCH v2 1/5] dt-bindings: soc: cix: Describe the Sky1 AUDSS DMA IRQ controller Jelly Jia
                   ` (4 more replies)
  0 siblings, 5 replies; 7+ messages in thread
From: Jelly Jia @ 2026-11-04 12:44 UTC (permalink / raw)
  To: vkoul, robh, krzk+dt, conor+dt, robin.murphy, sboyd, bmasney+clk,
	jbrunet+clk
  Cc: devicetree, Frank.Li, cix-kernel-upstream, dmaengine,
	linux-arm-kernel, linux-kernel, Jelly.Jia, linux-clk,
	joakim.zhang

Hello,

This series adds the CIX Sky1 AUDSS DMA-350 and its interrupt path through
the audio subsystem CRU. The DMA, CRU and DT changes are posted together
because the controller depends on the CRU interrupt gates, while
dma-channel-mask describes which channels Linux may use. Keeping them
together allows the complete interrupt path to be reviewed. If separate
submissions are preferred, they can be split in the next revision. No
audio client drivers are added here.

It depends on the companion v5 series, "dmaengine: arm-dma350: Add slave
support and Sky1 integration", for slave/cyclic transfers and optional
clock and reset support. Apply that series first.

The CRU gates the AP interrupt path for each of the eight DMA channels
and combines enabled inputs onto GIC SPI 230. Channels may be reserved
for firmware or the DSP; dma-channel-mask limits those available to Linux.
The AP gate bits control interrupt delivery, not channel ownership, and
this series does not implement runtime AP/DSP ownership transfer.

Conor suggested interrupt-map when the routing was described as being
configured by firmware. This series instead manages each AP gate via
irq_mask and irq_unmask. A static interrupt-map alone would translate
child specifiers to the shared GIC SPI but would not provide that gate
control. Model the CRU as an irqchip so the register owner manages the
gates, without CRU register accesses in the DMA driver. Feedback on this
choice of irqchip rather than a static interrupt-map is welcome.

The gate register exposes no per-channel pending status. The chained
handler polls every enabled child, and each DMA channel handler checks
its own status. The children use level flow handling with IRQ_IS_POLLED
so disabled and oneshot-threaded interrupts remain masked, while inactive
channels returning IRQ_NONE do not accumulate spurious-IRQ counts.

The AUDSS DT describes a 1 MiB peripheral window and a 512 MiB DRAM window
with dma-ranges. The prerequisite series allocates linked commands from
a coherent DMA pool, so successfully allocated commands are reachable
through the controller's DMA address windows.

Changes since v1:
- Drop the unrelated GIC node from the binding example. (Krzysztof.)
- Retain Krzysztof's Reviewed-by for the dma-channel-mask binding. DMA-350
  supports one to eight channels, so the single-cell mask is retained.
  (Frank's array-handling question.)
- Use raw regmap locking, explicit irq_disable and level flow handling
  with polled children, including the oneshot-threaded case. (Sashiko.)
- Dispose of child IRQ mappings before removing the domain and select
  IRQ_DOMAIN for COMPILE_TEST configurations. (Sashiko.)
- Associate the domain with the PM device, protect teardown register
  accesses, quiesce the parent IRQ before runtime suspend and use noirq
  system sleep callbacks. Save and restore software reset state as part
  of the CRU context. (Sashiko.)
- Rebase onto Linux 7.4-rc1 and use irq_domain_create_linear().

Signed-off-by: Jelly Jia <Jelly.Jia@cixtech.com>

Jelly Jia (5):
  dt-bindings: soc: cix: Describe the Sky1 AUDSS DMA IRQ controller
  clk: cix: Handle Sky1 AUDSS DMA interrupt gates as an irqchip
  dt-bindings: dma: arm,dma-350: Document dma-channel-mask
  dmaengine: arm-dma350: Honour dma-channel-mask
  arm64: dts: cix: Add the Sky1 AUDSS DMA-350 integration

 .../devicetree/bindings/dma/arm,dma-350.yaml  |   3 +
 .../bindings/soc/cix/cix,sky1-audss-cru.yaml  |  37 +++-
 arch/arm64/boot/dts/cix/sky1.dtsi             |  26 ++-
 drivers/clk/cix/Kconfig                       |   1 +
 drivers/clk/cix/clk-sky1-audss.c              | 181 +++++++++++++++++-
 drivers/dma/arm-dma350.c                      |  20 +-
 6 files changed, 261 insertions(+), 7 deletions(-)


base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
prerequisite-patch-id: aad1bc3c30a4825048143a3e0395e696201548e9
prerequisite-patch-id: d7f5243feddb5f16231302bd4c948d359ca92ac1
prerequisite-patch-id: ddc9ee115f12a7d2ee7ceff7e0485e7166581756
prerequisite-patch-id: 3d5fb2f7a5b2da42cb013d1c2e0a6e5eac5ffd6a
prerequisite-patch-id: 0e54074dcfd104480233bb076d2424e6ba1bd25e
-- 
2.54.0


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

* [PATCH v2 1/5] dt-bindings: soc: cix: Describe the Sky1 AUDSS DMA IRQ controller
  2026-11-04 12:44 [PATCH v2 0/5] soc: cix: Add the Sky1 AUDSS DMA-350 interrupt path Jelly Jia
@ 2026-11-04 12:44 ` Jelly Jia
  2026-11-04 12:44 ` [PATCH v2 2/5] clk: cix: Handle Sky1 AUDSS DMA interrupt gates as an irqchip Jelly Jia
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 7+ messages in thread
From: Jelly Jia @ 2026-11-04 12:44 UTC (permalink / raw)
  To: vkoul, robh, krzk+dt, conor+dt, robin.murphy, sboyd, bmasney+clk,
	jbrunet+clk
  Cc: devicetree, Frank.Li, cix-kernel-upstream, dmaengine,
	linux-arm-kernel, linux-kernel, Jelly.Jia, linux-clk,
	joakim.zhang

The AUDSS CRU contains a separate AP interrupt gate for each of the eight
DMA-350 channels. The enabled inputs are combined onto one GIC interrupt.
These gates are part of the CRU register block, not the DMA controller.

Describe the CRU as an interrupt-controller in addition to its clock and
reset functions. A child interrupt specifier contains the DMA channel
index, matching the corresponding gate bit; the parent interrupt is the
combined GIC line.

Signed-off-by: Jelly Jia <Jelly.Jia@cixtech.com>
---
 .../bindings/soc/cix/cix,sky1-audss-cru.yaml  | 37 ++++++++++++++++++-
 1 file changed, 35 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/soc/cix/cix,sky1-audss-cru.yaml b/Documentation/devicetree/bindings/soc/cix/cix,sky1-audss-cru.yaml
index 50dd0593e1d9..aa0c557abec0 100644
--- a/Documentation/devicetree/bindings/soc/cix/cix,sky1-audss-cru.yaml
+++ b/Documentation/devicetree/bindings/soc/cix/cix,sky1-audss-cru.yaml
@@ -28,6 +28,12 @@ description: |
   The SoC syscon NoC (or bus) reset is described via resets. The audio
   subsystem power domain is described via power-domains.
 
+  The same register block contains the eight DMA-350 channel interrupt
+  gates at offset 0x54. When the node is an interrupt-controller, the
+  cell is the DMA channel index (0 through 7). The corresponding gate
+  bit is unmasked while that interrupt is requested, and the eight
+  inputs are combined onto the parent GIC interrupt.
+
 properties:
   compatible:
     const: cix,sky1-audss-cru
@@ -45,6 +51,18 @@ properties:
     description:
       Reset indices are defined in include/dt-bindings/reset/cix,sky1-audss-cru.h.
 
+  interrupt-controller: true
+
+  '#interrupt-cells':
+    const: 1
+    description:
+      The cell is the DMA-350 channel index, 0 through 7.
+
+  interrupts:
+    maxItems: 1
+    description:
+      Combined DMA-350 channel interrupt after the CRU gates.
+
   clocks:
     items:
       - description: I2S parent clock for sampling rates multiple of 8kHz.
@@ -75,18 +93,33 @@ required:
   - power-domains
   - resets
 
+allOf:
+  - if:
+      required:
+        - interrupt-controller
+    then:
+      required:
+        - '#interrupt-cells'
+        - interrupts
+
 additionalProperties: false
 
 examples:
   - |
-    audss_cru: clock-controller@7110000 {
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/interrupt-controller/irq.h>
+
+    clock-controller@7110000 {
         compatible = "cix,sky1-audss-cru";
         reg = <0x7110000 0x10000>;
         #clock-cells = <1>;
-        #reset-cells = <1>;
         clocks = <&scmi_clk 76>, <&scmi_clk 78>,
                  <&scmi_clk 70>, <&scmi_clk 71>;
         clock-names = "x8k", "x11k", "sys", "48m";
+        #interrupt-cells = <1>;
+        interrupt-controller;
+        interrupts = <GIC_SPI 230 IRQ_TYPE_LEVEL_HIGH 0>;
         power-domains = <&smc_devpd 0>;
+        #reset-cells = <1>;
         resets = <&s5_syscon 31>;
     };
-- 
2.54.0


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

* [PATCH v2 2/5] clk: cix: Handle Sky1 AUDSS DMA interrupt gates as an irqchip
  2026-11-04 12:44 [PATCH v2 0/5] soc: cix: Add the Sky1 AUDSS DMA-350 interrupt path Jelly Jia
  2026-11-04 12:44 ` [PATCH v2 1/5] dt-bindings: soc: cix: Describe the Sky1 AUDSS DMA IRQ controller Jelly Jia
@ 2026-11-04 12:44 ` Jelly Jia
  2026-11-04 12:44 ` [PATCH v2 3/5] dt-bindings: dma: arm,dma-350: Document dma-channel-mask Jelly Jia
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 7+ messages in thread
From: Jelly Jia @ 2026-11-04 12:44 UTC (permalink / raw)
  To: vkoul, robh, krzk+dt, conor+dt, robin.murphy, sboyd, bmasney+clk,
	jbrunet+clk
  Cc: devicetree, Frank.Li, cix-kernel-upstream, dmaengine,
	linux-arm-kernel, linux-kernel, Jelly.Jia, linux-clk,
	joakim.zhang

The AUDSS CRU INFO_AP_IRQC register gates each DMA-350 channel interrupt
onto the shared AP GIC line. Manage these gates with an irqchip in the
CRU driver, keeping access to CRU registers out of the DMA driver. Mask
and unmask the corresponding AP gate through the IRQ core; this controls
interrupt delivery, not AP/DSP channel ownership.

The register exposes gate state but no per-channel pending status. Poll
each enabled child's DMA handler to check its channel-local status. Use
level flow handling so disabled and oneshot-threaded interrupts remain
masked, and mark the children as polled because IRQ_NONE is expected
when a different channel caused the parent interrupt.

Use a raw regmap lock for the hard-IRQ callbacks and keep the CRU powered
while its IRQ resources are in use or being torn down. Quiesce the parent
IRQ before runtime suspend and move system sleep handling to noirq.
Save and restore the interrupt gates and software reset state with the
other CRU context. Dispose of child mappings before removing the domain.

Signed-off-by: Jelly Jia <Jelly.Jia@cixtech.com>
---
 drivers/clk/cix/Kconfig          |   1 +
 drivers/clk/cix/clk-sky1-audss.c | 181 ++++++++++++++++++++++++++++++-
 2 files changed, 179 insertions(+), 3 deletions(-)

diff --git a/drivers/clk/cix/Kconfig b/drivers/clk/cix/Kconfig
index b057ef85fab6..de463ae63cb4 100644
--- a/drivers/clk/cix/Kconfig
+++ b/drivers/clk/cix/Kconfig
@@ -6,6 +6,7 @@ config CLK_SKY1_AUDSS
 	tristate "Cixtech Sky1 Audio Subsystem Clock Driver"
 	depends on ARCH_CIX || COMPILE_TEST
 	select AUXILIARY_BUS
+	select IRQ_DOMAIN
 	select REGMAP_MMIO
 	select RESET_CONTROLLER
 	help
diff --git a/drivers/clk/cix/clk-sky1-audss.c b/drivers/clk/cix/clk-sky1-audss.c
index e096b8121df3..9c8457334e17 100644
--- a/drivers/clk/cix/clk-sky1-audss.c
+++ b/drivers/clk/cix/clk-sky1-audss.c
@@ -2,9 +2,14 @@
 // Copyright 2026 Cix Technology Group Co., Ltd.
 
 #include <linux/auxiliary_bus.h>
+#include <linux/bits.h>
 #include <linux/clk.h>
 #include <linux/clk-provider.h>
 #include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
@@ -18,7 +23,11 @@
 #define INFO_CLK_GATE				0x10
 #define INFO_CLK_DIV				0x14
 #define INFO_CLK_MUX				0x18
+#define INFO_AP_IRQC				0x54
+#define INFO_AP_IRQC_DMAC_MASK			GENMASK(7, 0)
+#define SKY1_AUDSS_DMAC_IRQS			8
 #define INFO_MCLK				0x70
+#define INFO_SW_RESETN				0x78
 
 #define SKY1_AUDSS_CLK_PARENTS_CNT		4
 #define SKY1_AUDSS_NUM_CLKS			(CLK_MCLK4 + 1)
@@ -28,7 +37,9 @@ static u32 sky1_reg_save[][2] = {
 	{ INFO_CLK_GATE,  0 },
 	{ INFO_CLK_DIV, 0 },
 	{ INFO_CLK_MUX, 0 },
+	{ INFO_AP_IRQC, 0 },
 	{ INFO_MCLK, 0 },
+	{ INFO_SW_RESETN, 0 },
 };
 
 static const char * const sky1_audss_clk_names[SKY1_AUDSS_CLK_PARENTS_CNT] = {
@@ -519,6 +530,7 @@ static const struct regmap_config sky1_audss_regmap_config = {
 	.reg_bits = 32,
 	.val_bits = 32,
 	.reg_stride = 4,
+	.use_raw_spinlock = true,
 };
 
 struct sky1_audss_clks_priv {
@@ -529,6 +541,8 @@ struct sky1_audss_clks_priv {
 	const struct sky1_audss_clks_devtype_data *devtype_data;
 	spinlock_t lock;
 	struct clk_hw_onecell_data *clk_data;
+	struct irq_domain *irq_domain;
+	int parent_irq;
 };
 
 #if IS_ENABLED(CONFIG_RESET_SKY1_AUDSS)
@@ -928,6 +942,147 @@ static int sky1_audss_clks_enable(struct sky1_audss_clks_priv *priv)
 	return err;
 }
 
+static void sky1_audss_irq_mask(struct irq_data *d)
+{
+	struct sky1_audss_clks_priv *priv = irq_data_get_irq_chip_data(d);
+
+	regmap_update_bits(priv->regmap_cru, INFO_AP_IRQC,
+			   BIT(d->hwirq), 0);
+}
+
+static void sky1_audss_irq_unmask(struct irq_data *d)
+{
+	struct sky1_audss_clks_priv *priv = irq_data_get_irq_chip_data(d);
+
+	regmap_update_bits(priv->regmap_cru, INFO_AP_IRQC,
+			   BIT(d->hwirq), BIT(d->hwirq));
+}
+
+static struct irq_chip sky1_audss_irq_chip = {
+	.name		= "sky1-audss-dmac",
+	.irq_mask	= sky1_audss_irq_mask,
+	.irq_unmask	= sky1_audss_irq_unmask,
+	.irq_disable	= sky1_audss_irq_mask,
+	.flags		= IRQCHIP_SKIP_SET_WAKE,
+};
+
+static int sky1_audss_irq_map(struct irq_domain *d, unsigned int irq,
+			      irq_hw_number_t hwirq)
+{
+	if (hwirq >= SKY1_AUDSS_DMAC_IRQS)
+		return -EINVAL;
+
+	irq_set_chip_data(irq, d->host_data);
+	irq_set_chip_and_handler(irq, &sky1_audss_irq_chip,
+				 handle_level_irq);
+	/*
+	 * The CRU exposes gates, not per-channel pending status. The parent
+	 * handler polls every enabled channel, so IRQ_NONE is expected and
+	 * must not trigger spurious interrupt detection. Keep level flow
+	 * control so oneshot IRQ threads run with their channel gate masked.
+	 */
+	irq_set_status_flags(irq, IRQ_LEVEL | IRQ_IS_POLLED);
+	irq_set_noprobe(irq);
+
+	return 0;
+}
+
+static const struct irq_domain_ops sky1_audss_irq_ops = {
+	.map	= sky1_audss_irq_map,
+	.xlate	= irq_domain_xlate_onecell,
+};
+
+static void sky1_audss_irq_handler(struct irq_desc *desc)
+{
+	struct sky1_audss_clks_priv *priv = irq_desc_get_handler_data(desc);
+	struct irq_chip *chip = irq_desc_get_chip(desc);
+	unsigned long enabled;
+	unsigned int hwirq;
+	u32 val;
+
+	chained_irq_enter(chip, desc);
+
+	if (!regmap_read(priv->regmap_cru, INFO_AP_IRQC, &val)) {
+		enabled = val & INFO_AP_IRQC_DMAC_MASK;
+		for_each_set_bit(hwirq, &enabled, SKY1_AUDSS_DMAC_IRQS)
+			generic_handle_domain_irq(priv->irq_domain, hwirq);
+	}
+
+	chained_irq_exit(chip, desc);
+}
+
+static void sky1_audss_irq_teardown(void *data)
+{
+	struct sky1_audss_clks_priv *priv = data;
+	unsigned int hwirq;
+	unsigned int virq;
+	int ret;
+
+	ret = pm_runtime_resume_and_get(priv->dev);
+	if (ret < 0)
+		dev_warn(priv->dev, "failed to resume CRU for IRQ teardown: %d\n", ret);
+
+	disable_irq(priv->parent_irq);
+	if (ret >= 0)
+		regmap_update_bits(priv->regmap_cru, INFO_AP_IRQC,
+				   INFO_AP_IRQC_DMAC_MASK, 0);
+	irq_set_chained_handler_and_data(priv->parent_irq, NULL, NULL);
+	synchronize_irq(priv->parent_irq);
+	irq_clear_status_flags(priv->parent_irq, IRQ_DISABLE_UNLAZY);
+	priv->parent_irq = 0;
+
+	for (hwirq = 0; hwirq < SKY1_AUDSS_DMAC_IRQS; hwirq++) {
+		virq = irq_find_mapping(priv->irq_domain, hwirq);
+		if (!virq)
+			continue;
+
+		/* Disassociation must not access an unpowered CRU either. */
+		if (ret < 0)
+			irq_set_chip(virq, NULL);
+		irq_dispose_mapping(virq);
+	}
+
+	irq_domain_remove(priv->irq_domain);
+	priv->irq_domain = NULL;
+	if (ret >= 0)
+		pm_runtime_put_sync(priv->dev);
+}
+
+static int sky1_audss_irq_init(struct platform_device *pdev,
+			       struct sky1_audss_clks_priv *priv)
+{
+	struct device *dev = &pdev->dev;
+	int ret;
+	int irq;
+
+	if (!of_property_read_bool(dev->of_node, "interrupt-controller"))
+		return 0;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	ret = regmap_update_bits(priv->regmap_cru, INFO_AP_IRQC,
+				 INFO_AP_IRQC_DMAC_MASK, 0);
+	if (ret)
+		return ret;
+
+	priv->irq_domain = irq_domain_create_linear(of_fwnode_handle(dev->of_node),
+						    SKY1_AUDSS_DMAC_IRQS,
+						    &sky1_audss_irq_ops, priv);
+	if (!priv->irq_domain)
+		return -ENOMEM;
+
+	irq_domain_set_pm_device(priv->irq_domain, dev);
+
+	priv->parent_irq = irq;
+	/* The chained handler cannot implement lazy-disable masking. */
+	irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY);
+	irq_set_chained_handler_and_data(irq, sky1_audss_irq_handler, priv);
+
+	return devm_add_action_or_reset(dev, sky1_audss_irq_teardown, priv);
+}
+
 static void sky1_audss_clks_disable(struct sky1_audss_clks_priv *priv)
 {
 	const struct sky1_audss_clks_devtype_data *devtype_data = priv->devtype_data;
@@ -1110,6 +1265,13 @@ static int sky1_audss_clk_probe(struct platform_device *pdev)
 		goto fail_pm;
 	}
 
+	/* Tear the irqchip down before the runtime-PM cleanup disables clocks. */
+	ret = sky1_audss_irq_init(pdev, priv);
+	if (ret) {
+		dev_err_probe(dev, ret, "failed to set up AUDSS DMA IRQs\n");
+		goto fail_pm;
+	}
+
 	pm_runtime_put_sync(dev);
 
 	return 0;
@@ -1125,6 +1287,11 @@ static int __maybe_unused sky1_audss_clk_runtime_suspend(struct device *dev)
 	const struct sky1_audss_clks_devtype_data *devtype_data = priv->devtype_data;
 	unsigned long flags;
 	int i;
+	int ret;
+
+	/* Quiesce the chained handler before registers become inaccessible. */
+	if (priv->parent_irq)
+		disable_irq(priv->parent_irq);
 
 	spin_lock_irqsave(&priv->lock, flags);
 	for (i = 0; i < devtype_data->reg_save_size; i++)
@@ -1132,7 +1299,12 @@ static int __maybe_unused sky1_audss_clk_runtime_suspend(struct device *dev)
 			    devtype_data->reg_save[i][0], &devtype_data->reg_save[i][1]);
 	spin_unlock_irqrestore(&priv->lock, flags);
 
-	reset_control_assert(priv->rst_noc);
+	ret = reset_control_assert(priv->rst_noc);
+	if (ret) {
+		if (priv->parent_irq)
+			enable_irq(priv->parent_irq);
+		return ret;
+	}
 
 	sky1_audss_clks_disable(priv);
 
@@ -1164,14 +1336,17 @@ static int __maybe_unused sky1_audss_clk_runtime_resume(struct device *dev)
 			     devtype_data->reg_save[i][0], devtype_data->reg_save[i][1]);
 	spin_unlock_irqrestore(&priv->lock, flags);
 
+	if (priv->parent_irq)
+		enable_irq(priv->parent_irq);
+
 	return 0;
 }
 
 static const struct dev_pm_ops sky1_audss_clk_pm_ops = {
 	SET_RUNTIME_PM_OPS(sky1_audss_clk_runtime_suspend,
 			   sky1_audss_clk_runtime_resume, NULL)
-	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
-				pm_runtime_force_resume)
+	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+				      pm_runtime_force_resume)
 };
 
 static const struct sky1_audss_clks_devtype_data sky1_devtype_data = {
-- 
2.54.0


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

* [PATCH v2 3/5] dt-bindings: dma: arm,dma-350: Document dma-channel-mask
  2026-11-04 12:44 [PATCH v2 0/5] soc: cix: Add the Sky1 AUDSS DMA-350 interrupt path Jelly Jia
  2026-11-04 12:44 ` [PATCH v2 1/5] dt-bindings: soc: cix: Describe the Sky1 AUDSS DMA IRQ controller Jelly Jia
  2026-11-04 12:44 ` [PATCH v2 2/5] clk: cix: Handle Sky1 AUDSS DMA interrupt gates as an irqchip Jelly Jia
@ 2026-11-04 12:44 ` Jelly Jia
  2026-11-04 12:44 ` [PATCH v2 4/5] dmaengine: arm-dma350: Honour dma-channel-mask Jelly Jia
  2026-11-04 12:44 ` [PATCH v2 5/5] arm64: dts: cix: Add the Sky1 AUDSS DMA-350 integration Jelly Jia
  4 siblings, 0 replies; 7+ messages in thread
From: Jelly Jia @ 2026-11-04 12:44 UTC (permalink / raw)
  To: vkoul, robh, krzk+dt, conor+dt, robin.murphy, sboyd, bmasney+clk,
	jbrunet+clk
  Cc: devicetree, Frank.Li, cix-kernel-upstream, dmaengine,
	linux-arm-kernel, linux-kernel, Jelly.Jia, linux-clk,
	joakim.zhang, Krzysztof Kozlowski

Some DMA-350 integrations reserve channels for firmware or another
processor. Allow the standard dma-channel-mask property so firmware can
describe which channels Linux may use.

DMA-350 implements at most eight channels, so one 32-bit cell is enough;
limit the property to a single cell.

Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Signed-off-by: Jelly Jia <Jelly.Jia@cixtech.com>
---
 Documentation/devicetree/bindings/dma/arm,dma-350.yaml | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/Documentation/devicetree/bindings/dma/arm,dma-350.yaml b/Documentation/devicetree/bindings/dma/arm,dma-350.yaml
index 43279878207c..98e7bb651210 100644
--- a/Documentation/devicetree/bindings/dma/arm,dma-350.yaml
+++ b/Documentation/devicetree/bindings/dma/arm,dma-350.yaml
@@ -43,6 +43,9 @@ properties:
     description:
       Optional reset line, where the integration exposes one.
 
+  dma-channel-mask:
+    maxItems: 1
+
 required:
   - compatible
   - reg
-- 
2.54.0


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

* [PATCH v2 4/5] dmaengine: arm-dma350: Honour dma-channel-mask
  2026-11-04 12:44 [PATCH v2 0/5] soc: cix: Add the Sky1 AUDSS DMA-350 interrupt path Jelly Jia
                   ` (2 preceding siblings ...)
  2026-11-04 12:44 ` [PATCH v2 3/5] dt-bindings: dma: arm,dma-350: Document dma-channel-mask Jelly Jia
@ 2026-11-04 12:44 ` Jelly Jia
  2026-11-04 19:44   ` Frank Li
  2026-11-04 12:44 ` [PATCH v2 5/5] arm64: dts: cix: Add the Sky1 AUDSS DMA-350 integration Jelly Jia
  4 siblings, 1 reply; 7+ messages in thread
From: Jelly Jia @ 2026-11-04 12:44 UTC (permalink / raw)
  To: vkoul, robh, krzk+dt, conor+dt, robin.murphy, sboyd, bmasney+clk,
	jbrunet+clk
  Cc: devicetree, Frank.Li, cix-kernel-upstream, dmaengine,
	linux-arm-kernel, linux-kernel, Jelly.Jia, linux-clk,
	joakim.zhang

DMA-350 channels may be reserved for firmware or another processor.
Read the optional dma-channel-mask property and register only the channels
whose bits are set. If the property is absent, preserve the existing
behaviour and expose every hardware channel.

Skip masked channels before accessing their channel registers or
initializing their interrupts and virt-dma state. This avoids clearing
or configuring channels reserved for firmware or another processor.

Signed-off-by: Jelly Jia <Jelly.Jia@cixtech.com>
---
 drivers/dma/arm-dma350.c | 20 +++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/drivers/dma/arm-dma350.c b/drivers/dma/arm-dma350.c
index 2f22f8a0ab47..b24e569a1dd8 100644
--- a/drivers/dma/arm-dma350.c
+++ b/drivers/dma/arm-dma350.c
@@ -1253,9 +1253,10 @@ static int d350_probe(struct platform_device *pdev)
 	struct clk *clk;
 	struct d350 *dmac;
 	void __iomem *base;
+	u32 chan_mask = U32_MAX;
 	u32 reg;
 	int ret, nchan, dw, aw, r, p;
-	bool coherent, memset;
+	bool coherent, has_chan_mask, memset;
 
 	clk = devm_clk_get_optional_enabled(dev, NULL);
 	if (IS_ERR(clk))
@@ -1328,6 +1329,19 @@ static int d350_probe(struct platform_device *pdev)
 	dmac->dma.device_caps = d350_caps;
 	INIT_LIST_HEAD(&dmac->dma.channels);
 
+	/*
+	 * dma-channel-mask is optional. Firmware-reserved channels stay
+	 * clear; if the property is absent, Linux may use every channel.
+	 */
+	has_chan_mask = device_property_present(dev, "dma-channel-mask");
+	if (has_chan_mask) {
+		ret = device_property_read_u32(dev, "dma-channel-mask",
+					       &chan_mask);
+		if (ret)
+			return dev_err_probe(dev, ret,
+					     "failed to read dma-channel-mask\n");
+	}
+
 	reg = readl_relaxed(base + DMANSECCTRL + NSEC_CTRL);
 	writel_relaxed(reg | INTREN_ANYCHINTR_EN,
 		       base + DMANSECCTRL + NSEC_CTRL);
@@ -1337,6 +1351,10 @@ static int d350_probe(struct platform_device *pdev)
 	for (int i = 0; i < nchan; i++) {
 		struct d350_chan *dch = &dmac->channels[i];
 
+		if (has_chan_mask &&
+		    (i >= 32 || !(chan_mask & BIT(i))))
+			continue;
+
 		dch->base = base + DMACH(i);
 		writel_relaxed(CH_CMD_CLEAR, dch->base + CH_CMD);
 
-- 
2.54.0


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

* [PATCH v2 5/5] arm64: dts: cix: Add the Sky1 AUDSS DMA-350 integration
  2026-11-04 12:44 [PATCH v2 0/5] soc: cix: Add the Sky1 AUDSS DMA-350 interrupt path Jelly Jia
                   ` (3 preceding siblings ...)
  2026-11-04 12:44 ` [PATCH v2 4/5] dmaengine: arm-dma350: Honour dma-channel-mask Jelly Jia
@ 2026-11-04 12:44 ` Jelly Jia
  4 siblings, 0 replies; 7+ messages in thread
From: Jelly Jia @ 2026-11-04 12:44 UTC (permalink / raw)
  To: vkoul, robh, krzk+dt, conor+dt, robin.murphy, sboyd, bmasney+clk,
	jbrunet+clk
  Cc: devicetree, Frank.Li, cix-kernel-upstream, dmaengine,
	linux-arm-kernel, linux-kernel, Jelly.Jia, linux-clk,
	joakim.zhang

The AUDSS DMA-350 master sees the CPU physical peripheral window at
0x07010000 through DMA address 0x20000000, and DRAM at CPU physical
0xc0000000 through DMA address 0x30000000. Describe the 1 MiB peripheral
window and 512 MiB DRAM window with dma-ranges on the parent bus, and add
the controller's clock and reset.

Route the eight channel interrupts through the AUDSS CRU, whose AP gates
combine them onto GIC SPI 230. List child interrupts in hardware channel
order and set dma-channel-mask to 0xff to make all eight channels
available to Linux.

Signed-off-by: Jelly Jia <Jelly.Jia@cixtech.com>
---
 arch/arm64/boot/dts/cix/sky1.dtsi | 26 +++++++++++++++++++++++++-
 1 file changed, 25 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/boot/dts/cix/sky1.dtsi b/arch/arm64/boot/dts/cix/sky1.dtsi
index cd498ee7e9a7..6ca94f606463 100644
--- a/arch/arm64/boot/dts/cix/sky1.dtsi
+++ b/arch/arm64/boot/dts/cix/sky1.dtsi
@@ -588,17 +588,41 @@ mbox_pm2ap: mailbox@65a0080 {
 			cix,mbox-dir = "rx";
 		};
 
+		audss-dma-bus {
+			compatible = "simple-bus";
+			ranges;
+			#address-cells = <2>;
+			dma-ranges = <0x0 0x20000000 0x0 0x07010000 0x0 0x00100000>,
+				     <0x0 0x30000000 0x0 0xc0000000 0x0 0x20000000>;
+			#size-cells = <2>;
+
+			audss_dmac: dma-controller@7010000 {
+				compatible = "arm,dma-350";
+				reg = <0x0 0x07010000 0x0 0x10000>;
+				clocks = <&audss_cru CLK_DMAC_AXI>;
+				#dma-cells = <1>;
+				dma-channel-mask = <0xff>;
+				interrupt-parent = <&audss_cru>;
+				interrupts = <0>, <1>, <2>, <3>,
+					     <4>, <5>, <6>, <7>;
+				resets = <&audss_cru AUDSS_DMAC_SW_RST>;
+			};
+		};
+
 		audss_cru: clock-controller@7110000 {
 			compatible = "cix,sky1-audss-cru";
 			reg = <0x0 0x07110000 0x0 0x10000>;
 			#clock-cells = <1>;
-			#reset-cells = <1>;
 			clocks = <&scmi_clk CLK_TREE_AUDIO_CLK0>,
 				 <&scmi_clk CLK_TREE_AUDIO_CLK2>,
 				 <&scmi_clk CLK_TREE_AUDIO_CLK4>,
 				 <&scmi_clk CLK_TREE_AUDIO_CLK5>;
 			clock-names = "x8k", "x11k", "sys", "48m";
+			#interrupt-cells = <1>;
+			interrupt-controller;
+			interrupts = <GIC_SPI 230 IRQ_TYPE_LEVEL_HIGH 0>;
 			power-domains = <&smc_devpd SKY1_PD_AUDIO>;
+			#reset-cells = <1>;
 			resets = <&s5_syscon SKY1_AUDIO_HIFI5_NOC_RESET_N>;
 		};
 
-- 
2.54.0


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

* Re: [PATCH v2 4/5] dmaengine: arm-dma350: Honour dma-channel-mask
  2026-11-04 12:44 ` [PATCH v2 4/5] dmaengine: arm-dma350: Honour dma-channel-mask Jelly Jia
@ 2026-11-04 19:44   ` Frank Li
  0 siblings, 0 replies; 7+ messages in thread
From: Frank Li @ 2026-11-04 19:44 UTC (permalink / raw)
  To: Jelly Jia
  Cc: vkoul, robh, krzk+dt, conor+dt, robin.murphy, sboyd, bmasney+clk,
	jbrunet+clk, devicetree, Frank.Li, cix-kernel-upstream,
	dmaengine, linux-arm-kernel, linux-kernel, linux-clk,
	joakim.zhang

On Wed, Nov 04, 2026 at 08:44:36PM +0800, Jelly Jia wrote:
> DMA-350 channels may be reserved for firmware or another processor.
> Read the optional dma-channel-mask property and register only the channels
> whose bits are set. If the property is absent, preserve the existing
> behaviour and expose every hardware channel.
>
> Skip masked channels before accessing their channel registers or
> initializing their interrupts and virt-dma state. This avoids clearing
> or configuring channels reserved for firmware or another processor.
>
> Signed-off-by: Jelly Jia <Jelly.Jia@cixtech.com>
> ---
>  drivers/dma/arm-dma350.c | 20 +++++++++++++++++++-
>  1 file changed, 19 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/dma/arm-dma350.c b/drivers/dma/arm-dma350.c
> index 2f22f8a0ab47..b24e569a1dd8 100644
> --- a/drivers/dma/arm-dma350.c
> +++ b/drivers/dma/arm-dma350.c
> @@ -1253,9 +1253,10 @@ static int d350_probe(struct platform_device *pdev)
>  	struct clk *clk;
>  	struct d350 *dmac;
>  	void __iomem *base;
> +	u32 chan_mask = U32_MAX;
>  	u32 reg;
>  	int ret, nchan, dw, aw, r, p;
> -	bool coherent, memset;
> +	bool coherent, has_chan_mask, memset;
>
>  	clk = devm_clk_get_optional_enabled(dev, NULL);
>  	if (IS_ERR(clk))
> @@ -1328,6 +1329,19 @@ static int d350_probe(struct platform_device *pdev)
>  	dmac->dma.device_caps = d350_caps;
>  	INIT_LIST_HEAD(&dmac->dma.channels);
>
> +	/*
> +	 * dma-channel-mask is optional. Firmware-reserved channels stay
> +	 * clear; if the property is absent, Linux may use every channel.
> +	 */
> +	has_chan_mask = device_property_present(dev, "dma-channel-mask");
> +	if (has_chan_mask) {
> +		ret = device_property_read_u32(dev, "dma-channel-mask",
> +					       &chan_mask);
> +		if (ret)
> +			return dev_err_probe(dev, ret,
> +					     "failed to read dma-channel-mask\n");
> +	}
> +
>  	reg = readl_relaxed(base + DMANSECCTRL + NSEC_CTRL);
>  	writel_relaxed(reg | INTREN_ANYCHINTR_EN,
>  		       base + DMANSECCTRL + NSEC_CTRL);
> @@ -1337,6 +1351,10 @@ static int d350_probe(struct platform_device *pdev)
>  	for (int i = 0; i < nchan; i++) {
>  		struct d350_chan *dch = &dmac->channels[i];
>
> +		if (has_chan_mask &&
> +		    (i >= 32 || !(chan_mask & BIT(i))))
> +			continue;
> +


max nchan is 8 (in your cover letter), why need check i >= 8 here?

Frank

>  		dch->base = base + DMACH(i);
>  		writel_relaxed(CH_CMD_CLEAR, dch->base + CH_CMD);
>
> --
> 2.54.0
>

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

end of thread, other threads:[~2026-11-04 19:44 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-04 12:44 [PATCH v2 0/5] soc: cix: Add the Sky1 AUDSS DMA-350 interrupt path Jelly Jia
2026-11-04 12:44 ` [PATCH v2 1/5] dt-bindings: soc: cix: Describe the Sky1 AUDSS DMA IRQ controller Jelly Jia
2026-11-04 12:44 ` [PATCH v2 2/5] clk: cix: Handle Sky1 AUDSS DMA interrupt gates as an irqchip Jelly Jia
2026-11-04 12:44 ` [PATCH v2 3/5] dt-bindings: dma: arm,dma-350: Document dma-channel-mask Jelly Jia
2026-11-04 12:44 ` [PATCH v2 4/5] dmaengine: arm-dma350: Honour dma-channel-mask Jelly Jia
2026-11-04 19:44   ` Frank Li
2026-11-04 12:44 ` [PATCH v2 5/5] arm64: dts: cix: Add the Sky1 AUDSS DMA-350 integration Jelly Jia

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

all inboxes | Powered by JetHome®