* [PATCH v1 1/5] dt-bindings: soc: cix: Describe the Sky1 AUDSS DMA IRQ controller
2026-10-23 9:30 [PATCH v1 0/5] soc: cix: Add the Sky1 AUDSS DMA-350 interrupt path Jelly Jia
@ 2026-10-23 9:30 ` Jelly Jia
2026-10-23 9:30 ` [PATCH v1 2/5] clk: cix: Handle Sky1 AUDSS DMA interrupt gates as an irqchip Jelly Jia
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Jelly Jia @ 2026-10-23 9:30 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 | 44 ++++++++++++++++++-
1 file changed, 42 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..a296b59516f2 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,40 @@ 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>
+
+ gic: interrupt-controller {
+ #address-cells = <2>;
+ #interrupt-cells = <4>;
+ interrupt-controller;
+ };
+
+ 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;
+ interrupt-parent = <&gic>;
+ 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] 6+ messages in thread* [PATCH v1 2/5] clk: cix: Handle Sky1 AUDSS DMA interrupt gates as an irqchip
2026-10-23 9:30 [PATCH v1 0/5] soc: cix: Add the Sky1 AUDSS DMA-350 interrupt path Jelly Jia
2026-10-23 9:30 ` [PATCH v1 1/5] dt-bindings: soc: cix: Describe the Sky1 AUDSS DMA IRQ controller Jelly Jia
@ 2026-10-23 9:30 ` Jelly Jia
2026-10-23 9:30 ` [PATCH v1 3/5] dt-bindings: dma: arm,dma-350: Document dma-channel-mask Jelly Jia
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Jelly Jia @ 2026-10-23 9:30 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. Call
each enabled child's DMA handler to check its channel-local status. Use
the untracked flow handler so inactive children returning IRQ_NONE do not
accumulate spurious-IRQ counts.
Clear the AP gates before installing the chained handler, then enable
them only as Linux requests channel interrupts. Clear them again during
teardown and include the register in the runtime and system sleep save
list. Register irqchip cleanup after runtime-PM cleanup so devres removes
the irqchip while the CRU register block is still clocked.
Signed-off-by: Jelly Jia <Jelly.Jia@cixtech.com>
---
drivers/clk/cix/clk-sky1-audss.c | 122 +++++++++++++++++++++++++++++++
1 file changed, 122 insertions(+)
diff --git a/drivers/clk/cix/clk-sky1-audss.c b/drivers/clk/cix/clk-sky1-audss.c
index e096b8121df3..789cbba631d1 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,6 +23,9 @@
#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 SKY1_AUDSS_CLK_PARENTS_CNT 4
@@ -28,6 +36,7 @@ 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 },
};
@@ -529,6 +538,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 +939,110 @@ 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,
+ .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_untracked_irq);
+ 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;
+
+ 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);
+ irq_domain_remove(priv->irq_domain);
+}
+
+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;
+
+ priv->irq_domain = irq_domain_add_linear(dev->of_node,
+ SKY1_AUDSS_DMAC_IRQS,
+ &sky1_audss_irq_ops, priv);
+ if (!priv->irq_domain)
+ return -ENOMEM;
+
+ ret = regmap_update_bits(priv->regmap_cru, INFO_AP_IRQC,
+ INFO_AP_IRQC_DMAC_MASK, 0);
+ if (ret) {
+ irq_domain_remove(priv->irq_domain);
+ return ret;
+ }
+
+ priv->parent_irq = irq;
+ 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 +1225,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;
--
2.54.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH v1 3/5] dt-bindings: dma: arm,dma-350: Document dma-channel-mask
2026-10-23 9:30 [PATCH v1 0/5] soc: cix: Add the Sky1 AUDSS DMA-350 interrupt path Jelly Jia
2026-10-23 9:30 ` [PATCH v1 1/5] dt-bindings: soc: cix: Describe the Sky1 AUDSS DMA IRQ controller Jelly Jia
2026-10-23 9:30 ` [PATCH v1 2/5] clk: cix: Handle Sky1 AUDSS DMA interrupt gates as an irqchip Jelly Jia
@ 2026-10-23 9:30 ` Jelly Jia
2026-10-23 9:30 ` [PATCH v1 4/5] dmaengine: arm-dma350: Honour dma-channel-mask Jelly Jia
2026-10-23 9:30 ` [PATCH v1 5/5] arm64: dts: cix: Add the Sky1 AUDSS DMA-350 integration Jelly Jia
4 siblings, 0 replies; 6+ messages in thread
From: Jelly Jia @ 2026-10-23 9:30 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
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.
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] 6+ messages in thread* [PATCH v1 4/5] dmaengine: arm-dma350: Honour dma-channel-mask
2026-10-23 9:30 [PATCH v1 0/5] soc: cix: Add the Sky1 AUDSS DMA-350 interrupt path Jelly Jia
` (2 preceding siblings ...)
2026-10-23 9:30 ` [PATCH v1 3/5] dt-bindings: dma: arm,dma-350: Document dma-channel-mask Jelly Jia
@ 2026-10-23 9:30 ` Jelly Jia
2026-10-23 9:30 ` [PATCH v1 5/5] arm64: dts: cix: Add the Sky1 AUDSS DMA-350 integration Jelly Jia
4 siblings, 0 replies; 6+ messages in thread
From: Jelly Jia @ 2026-10-23 9:30 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 9cb5b2f03113..32ec6f061ee9 100644
--- a/drivers/dma/arm-dma350.c
+++ b/drivers/dma/arm-dma350.c
@@ -1205,9 +1205,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))
@@ -1285,6 +1286,19 @@ static int d350_probe(struct platform_device *pdev)
dmac->dma.device_config = d350_config;
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);
@@ -1294,6 +1308,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] 6+ messages in thread* [PATCH v1 5/5] arm64: dts: cix: Add the Sky1 AUDSS DMA-350 integration
2026-10-23 9:30 [PATCH v1 0/5] soc: cix: Add the Sky1 AUDSS DMA-350 interrupt path Jelly Jia
` (3 preceding siblings ...)
2026-10-23 9:30 ` [PATCH v1 4/5] dmaengine: arm-dma350: Honour dma-channel-mask Jelly Jia
@ 2026-10-23 9:30 ` Jelly Jia
4 siblings, 0 replies; 6+ messages in thread
From: Jelly Jia @ 2026-10-23 9:30 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] 6+ messages in thread