All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Jelly Jia" <Jelly.Jia@cixtech.com>
Cc: devicetree@vger.kernel.org, dmaengine@vger.kernel.org,
	linux-clk@vger.kernel.org, robh@kernel.org, conor+dt@kernel.org,
	vkoul@kernel.org, Frank.Li@kernel.org
Subject: Re: [PATCH v1 5/5] arm64: dts: cix: Add the Sky1 AUDSS DMA-350 integration
Date: Fri, 23 Oct 2026 10:28:35 +0000	[thread overview]
Message-ID: <sashiko-outbox-182685@kernel.org> (raw)
In-Reply-To: <20261023093045.3248630-6-Jelly.Jia@cixtech.com>

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [High] clk: cix: unclocked MMIO access in sky1_audss_irq_teardown() and IRQ callbacks
- [High] clk: cix: IRQ storm in sky1_audss_irq_handler() due to handle_untracked_irq()
- [High] clk: cix: invalid wait context in sky1_audss_irq_handler() via non-raw regmap
- [High] clk: cix: missing virq disposal before irq_domain_remove() in teardown
- [High] arm64: dts: cix: audss_dmac reset AUDSS_SW_RST lost across AUDSS CRU suspend
--

commit 5fc04062b961f79314208fe23d9fe7413f932103
Author: Jelly Jia <Jelly.Jia@cixtech.com>

arm64: dts: cix: Add the Sky1 AUDSS DMA-350 integration

This commit adds the audss-dma-bus and audss_dmac nodes for the Sky1 AUDSS
DMA-350 controller and wires its clocks, resets, and channel interrupts
through the audss_cru controller.

> diff --git a/arch/arm64/boot/dts/cix/sky1.dtsi b/arch/arm64/boot/dts/cix/sky1.dtsi
> index cd498ee7e9a76..6ca94f606463c 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>;

[Severity: High]
Will the AUDSS_DMAC_SW_RST reset state for audss_dmac survive a system
suspend and resume cycle of audss_cru?

In drivers/dma/arm-dma350.c, d350_probe() deasserts the reset once at probe
time and the driver defines no power management callbacks:

drivers/dma/arm-dma350.c:d350_probe() {
	reset = devm_reset_control_get_optional_exclusive_deasserted(dev, NULL);
	if (IS_ERR(reset))
		return dev_err_probe(dev, PTR_ERR(reset),
				     "failed to deassert reset\n");
}

That call goes through sky1_audss_reset_deassert() -> sky1_audss_reset_set()
in drivers/reset/reset-sky1-audss.c to set BIT(15) in AUDSS_SW_RST (0x78).

However, when audss_cru suspends via sky1_audss_clk_runtime_suspend() in
drivers/clk/cix/clk-sky1-audss.c, it saves only the registers listed in
sky1_reg_save[] before asserting priv->rst_noc:

drivers/clk/cix/clk-sky1-audss.c:
static u32 sky1_reg_save[][2] = {
	{ INFO_HIFI0,  0 },
	{ INFO_CLK_GATE,  0 },
	{ INFO_CLK_DIV, 0 },
	{ INFO_CLK_MUX, 0 },
	{ INFO_AP_IRQC, 0 },
	{ INFO_MCLK, 0 },
};

Because AUDSS_SW_RST (0x78) is not included in sky1_reg_save[], asserting
priv->rst_noc and powering down SKY1_PD_AUDIO resets AUDSS_SW_RST, leaving
audss_dmac held in hardware reset after sky1_audss_clk_runtime_resume()
runs.

> +			};
> +		};
> +
>  		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>;

[Severity: High]
Can wiring audss_cru as an interrupt-controller trigger unclocked register
accesses to INFO_AP_IRQC while audss_cru is runtime-suspended and held in
reset?

In drivers/clk/cix/clk-sky1-audss.c, sky1_audss_clk_probe() initializes the
irqchip and then drops the runtime PM reference:

drivers/clk/cix/clk-sky1-audss.c:sky1_audss_clk_probe() {
	/* 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);
}

Whenever no consumer clock is prepared, sky1_audss_clk_runtime_suspend()
asserts priv->rst_noc and disables the parent clocks via
sky1_audss_clks_disable(priv):

drivers/clk/cix/clk-sky1-audss.c:sky1_audss_clk_runtime_suspend() {
	...
	reset_control_assert(priv->rst_noc);

	sky1_audss_clks_disable(priv);
}

During driver unbind in __device_release_driver(),
device_links_unbind_consumers() unbinds audss_dmac (unpreparing
CLK_DMAC_AXI), and pm_runtime_put_sync() suspends audss_cru before
device_unbind_cleanup() -> devres_release_all() runs
sky1_audss_irq_teardown():

drivers/clk/cix/clk-sky1-audss.c:sky1_audss_irq_teardown() {
	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);
}

In addition, sky1_audss_irq_init() does not call irq_domain_set_pm_device()
and leaves GIC_SPI 230 active while suspended, so sky1_audss_irq_teardown(),
sky1_audss_irq_mask(), sky1_audss_irq_unmask(), or sky1_audss_irq_handler()
can access INFO_AP_IRQC while priv->rst_noc is asserted and the parent
clocks are disabled.

[Severity: High]
Could routing the level-high GIC_SPI 230 interrupt through
sky1_audss_irq_handler() and handle_untracked_irq() cause an interrupt storm
or spurious interrupt replay?

In drivers/clk/cix/clk-sky1-audss.c, sky1_audss_irq_chip provides only
.irq_mask and .irq_unmask, and sky1_audss_irq_map() sets
handle_untracked_irq without IRQ_DISABLE_UNLAZY or IRQ_NOTHREAD:

drivers/clk/cix/clk-sky1-audss.c:
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;
}

When sky1_audss_irq_handler() runs, it reads the gate mask in INFO_AP_IRQC
and dispatches every unmasked channel bit:

drivers/clk/cix/clk-sky1-audss.c:sky1_audss_irq_handler() {
	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);
	}
}

Because handle_untracked_irq() in kernel/irq/chip.c returns early when
!irq_can_handle(desc) without calling mask_irq(desc), and does not mask
IRQS_ONESHOT interrupts:

1. If disable_irq() or suspend_device_irqs() lazily disables a channel, its
   bit in INFO_AP_IRQC stays set. If that channel asserts its level-high
   interrupt, handle_untracked_irq() neither masks INFO_AP_IRQC nor runs
   d350_irq() to clear CH_STATUS, leaving GIC_SPI 230 asserted in an
   infinite hard-IRQ loop.
2. Under CONFIG_PREEMPT_RT or threadirqs, request_irq() in
   d350_alloc_chan_resources() is force-threaded with IRQF_ONESHOT, and
   handle_untracked_irq() leaves the interrupt unmasked while waking the
   IRQ thread, re-triggering GIC_SPI 230 continuously.
3. When any enabled channel fires, sky1_audss_irq_handler() calls
   generic_handle_domain_irq() for every lazily-disabled sibling channel
   still enabled in INFO_AP_IRQC, which sets IRQS_PENDING on idle channels
   and causes a spurious replay on enable_irq() or resume_device_irqs().

[Severity: High]
Does accessing priv->regmap_cru from sky1_audss_irq_mask(),
sky1_audss_irq_unmask(), and sky1_audss_irq_handler() trigger an invalid
wait context warning?

In drivers/clk/cix/clk-sky1-audss.c, sky1_audss_regmap_config does not set
.use_raw_spinlock = true:

drivers/clk/cix/clk-sky1-audss.c:
static const struct regmap_config sky1_audss_regmap_config = {
	.reg_bits = 32,
	.val_bits = 32,
	.reg_stride = 4,
};

As a result, __regmap_init() configures priv->regmap_cru to use a non-raw
spinlock_t (which becomes a sleeping lock on CONFIG_PREEMPT_RT). When
audss_dmac requests or handles its channel interrupts:

- sky1_audss_irq_mask() and sky1_audss_irq_unmask() call
  regmap_update_bits(priv->regmap_cru, INFO_AP_IRQC, ...) while the IRQ core
  holds desc->lock (a raw_spinlock_t).
- sky1_audss_irq_handler() runs as a chained handler in hard-IRQ context and
  calls regmap_read(priv->regmap_cru, INFO_AP_IRQC, &val).

Should sky1_audss_regmap_config set .use_raw_spinlock = true so that
priv->regmap_cru can be safely accessed from raw_spinlock_t and hard-IRQ
contexts?

[Severity: High]
Does sky1_audss_irq_teardown() leak the mapped virq descriptors and leave
dangling pointers when audss_cru is unbound?

When d350_probe() calls platform_get_irq() for the 8 audss_dmac channels,
of_irq_get() -> irq_create_of_mapping() populates priv->irq_domain's
revmap_tree and invokes sky1_audss_irq_map(), storing pointers to priv,
priv->irq_domain, and sky1_audss_irq_chip in each irq_desc.

However, sky1_audss_irq_teardown() calls irq_domain_remove() without first
disposing the mappings via irq_dispose_mapping():

drivers/clk/cix/clk-sky1-audss.c:sky1_audss_irq_teardown() {
	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);
}

In kernel/irq/irqdomain.c, irq_domain_remove() triggers
WARN_ON(!radix_tree_empty(&domain->revmap_tree)) and frees priv->irq_domain,
leaving the 8 allocated irq_desc descriptors in sparse_irqs with dangling
references to the freed domain, devm-freed priv data, and module rodata if
/proc/interrupts or /sys/kernel/irq/<virq>/chip_name is subsequently read.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261023093045.3248630-1-Jelly.Jia@cixtech.com?part=5

      reply	other threads:[~2026-10-23 10:28 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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-26  8:12   ` Krzysztof Kozlowski
2026-10-29  5:25     ` 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:53   ` sashiko-bot
2026-10-23  9:30 ` [PATCH v1 3/5] dt-bindings: dma: arm,dma-350: Document dma-channel-mask Jelly Jia
2026-10-26  8:13   ` Krzysztof Kozlowski
2026-10-23  9:30 ` [PATCH v1 4/5] dmaengine: arm-dma350: Honour dma-channel-mask Jelly Jia
2026-10-28 16:47   ` Frank Li
2026-10-29  5:18     ` Jelly Jia
2026-10-23  9:30 ` [PATCH v1 5/5] arm64: dts: cix: Add the Sky1 AUDSS DMA-350 integration Jelly Jia
2026-10-23 10:28   ` sashiko-bot [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-182685@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=Frank.Li@kernel.org \
    --cc=Jelly.Jia@cixtech.com \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=dmaengine@vger.kernel.org \
    --cc=linux-clk@vger.kernel.org \
    --cc=robh@kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=vkoul@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.