Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 1/2] dt-bindings: spi: nuvoton,ma35d1-qspi: Add Nuvoton MA35D1 QSPI
From: Chi-Wen Weng @ 2026-06-08  2:50 UTC (permalink / raw)
  To: broonie, robh, krzk+dt, conor+dt
  Cc: linux-arm-kernel, linux-spi, devicetree, linux-kernel, cwweng
In-Reply-To: <20260608025009.1504971-1-cwweng@nuvoton.com>

Add a devicetree binding for the Quad SPI controller found in
Nuvoton MA35D1 SoCs.

The controller supports SPI memory devices such as SPI NOR and SPI NAND
flashes. It has one register range, one clock input and one reset line,
and supports up to two chip selects.

Signed-off-by: Chi-Wen Weng <cwweng@nuvoton.com>
---
 .../bindings/spi/nuvoton,ma35d1-qspi.yaml     | 62 +++++++++++++++++++
 1 file changed, 62 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/spi/nuvoton,ma35d1-qspi.yaml

diff --git a/Documentation/devicetree/bindings/spi/nuvoton,ma35d1-qspi.yaml b/Documentation/devicetree/bindings/spi/nuvoton,ma35d1-qspi.yaml
new file mode 100644
index 000000000000..68e6d79e6b7d
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/nuvoton,ma35d1-qspi.yaml
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/spi/nuvoton,ma35d1-qspi.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Nuvoton MA35D1 Quad SPI Controller
+
+maintainers:
+  - Chi-Wen Weng <cwweng@nuvoton.com>
+
+allOf:
+  - $ref: spi-controller.yaml#
+
+properties:
+  compatible:
+    const: nuvoton,ma35d1-qspi
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  resets:
+    maxItems: 1
+
+  num-cs:
+    maximum: 2
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - resets
+
+unevaluatedProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/clock/nuvoton,ma35d1-clk.h>
+    #include <dt-bindings/reset/nuvoton,ma35d1-reset.h>
+
+    soc {
+        #address-cells = <2>;
+        #size-cells = <2>;
+
+        spi@40680000 {
+            compatible = "nuvoton,ma35d1-qspi";
+            reg = <0 0x40680000 0 0x100>;
+            interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>;
+            clocks = <&clk QSPI0_GATE>;
+            resets = <&sys MA35D1_RESET_QSPI0>;
+            #address-cells = <1>;
+            #size-cells = <0>;
+        };
+    };
+
-- 
2.25.1



^ permalink raw reply related

* [PATCH v2 2/2] spi: ma35d1-qspi: Add Nuvoton MA35D1 QSPI controller support
From: Chi-Wen Weng @ 2026-06-08  2:50 UTC (permalink / raw)
  To: broonie, robh, krzk+dt, conor+dt
  Cc: linux-arm-kernel, linux-spi, devicetree, linux-kernel, cwweng
In-Reply-To: <20260608025009.1504971-1-cwweng@nuvoton.com>

Add SPI controller driver support for the Nuvoton MA35D1 Quad SPI
controller.

The controller supports standard SPI transfers and spi-mem operations
for SPI memory devices such as SPI NOR and SPI NAND flashes. The driver
supports single, dual and quad I/O modes and uses a conservative
word-by-word PIO transfer path for the initial upstream version.

The driver also handles controller reset, clock divider programming,
chip-select control, transfer mode setup and spi-mem command/address/
dummy/data phases.

Signed-off-by: Chi-Wen Weng <cwweng@nuvoton.com>
---
 drivers/spi/Kconfig           |  10 +
 drivers/spi/Makefile          |   1 +
 drivers/spi/spi-ma35d1-qspi.c | 622 ++++++++++++++++++++++++++++++++++
 3 files changed, 633 insertions(+)
 create mode 100644 drivers/spi/spi-ma35d1-qspi.c

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index b563f49e2197..8b8297ee3f2e 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -694,6 +694,16 @@ config SPI_LJCA
 	  This driver can also be built as a module. If so, the module
 	  will be called spi-ljca.
 
+config SPI_MA35D1_QSPI
+	tristate "Nuvoton MA35D1 QSPI controller"
+	depends on ARCH_MA35 || COMPILE_TEST
+	help
+	  This enables support for the Quad SPI controller found in
+	  Nuvoton MA35D1 SoCs.
+
+	  The controller supports SPI memory devices such as SPI NOR and
+	  SPI NAND flashes in single, dual and quad I/O modes.
+
 config SPI_MESON_SPICC
 	tristate "Amlogic Meson SPICC controller"
 	depends on COMMON_CLK
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 9d36190a9884..c5bb0efd108c 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -85,6 +85,7 @@ obj-$(CONFIG_SPI_LOONGSON_CORE)		+= spi-loongson-core.o
 obj-$(CONFIG_SPI_LOONGSON_PCI)		+= spi-loongson-pci.o
 obj-$(CONFIG_SPI_LOONGSON_PLATFORM)	+= spi-loongson-plat.o
 obj-$(CONFIG_SPI_LP8841_RTC)		+= spi-lp8841-rtc.o
+obj-$(CONFIG_SPI_MA35D1_QSPI)		+= spi-ma35d1-qspi.o
 obj-$(CONFIG_SPI_MESON_SPICC)		+= spi-meson-spicc.o
 obj-$(CONFIG_SPI_MESON_SPIFC)		+= spi-meson-spifc.o
 obj-$(CONFIG_SPI_MICROCHIP_CORE_QSPI)	+= spi-microchip-core-qspi.o
diff --git a/drivers/spi/spi-ma35d1-qspi.c b/drivers/spi/spi-ma35d1-qspi.c
new file mode 100644
index 000000000000..93b60b6142e5
--- /dev/null
+++ b/drivers/spi/spi-ma35d1-qspi.c
@@ -0,0 +1,622 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+//
+// Nuvoton MA35D1 QSPI controller driver
+//
+// Copyright (c) 2026 Nuvoton Technology Corp.
+// Author: Chi-Wen Weng <cwweng@nuvoton.com>
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi-mem.h>
+
+/* Register offset definitions */
+#define NUVOTON_QSPI_CTL_OFFSET		0x00 /* Control Register, RW */
+#define NUVOTON_QSPI_CLKDIV_OFFSET	0x04 /* Clock Divider Register, RW */
+#define NUVOTON_QSPI_SSCTL_OFFSET	0x08 /* Slave Select Register, RW */
+#define NUVOTON_QSPI_FIFOCTL_OFFSET	0x10 /* FIFO Control Register, RW */
+#define NUVOTON_QSPI_STATUS_OFFSET	0x14 /* Status Register, RW */
+#define NUVOTON_QSPI_TX_OFFSET		0x20 /* Data Transmit Register, WO */
+#define NUVOTON_QSPI_RX_OFFSET		0x30 /* Data Receive Register, RO */
+
+/* QSPI Control Register bit masks */
+#define NUVOTON_QSPI_CTL_QUADIOEN_MASK	BIT(22) /* Quad I/O Mode Enable */
+#define NUVOTON_QSPI_CTL_DUALIOEN_MASK	BIT(21) /* Dual I/O Mode Enable */
+#define NUVOTON_QSPI_CTL_DATDIR_MASK	BIT(20) /* Data Port Direction Control */
+#define NUVOTON_QSPI_CTL_REORDER_MASK	BIT(19) /* Byte Reorder Function Enable */
+#define NUVOTON_QSPI_CTL_LSB_MASK	BIT(13) /* Send LSB First */
+#define NUVOTON_QSPI_CTL_DWIDTH_MASK	GENMASK(12, 8) /* Data Width */
+#define NUVOTON_QSPI_CTL_SUSPITV_MASK	GENMASK(7, 4) /* Suspend Interval */
+#define NUVOTON_QSPI_CTL_CLKPOL_MASK	BIT(3) /* Clock Polarity */
+#define NUVOTON_QSPI_CTL_TXNEG_MASK	BIT(2) /* Transmit on Negative Edge */
+#define NUVOTON_QSPI_CTL_RXNEG_MASK	BIT(1) /* Receive on Negative Edge */
+#define NUVOTON_QSPI_CTL_SPIEN_MASK	BIT(0) /* QSPI Transfer Control Enable */
+
+/* QSPI Clock Divider Register bit masks */
+#define NUVOTON_QSPI_CLKDIV_MASK	GENMASK(8, 0) /* Clock Divider */
+
+/* QSPI Slave Select Control Register bit masks */
+#define NUVOTON_QSPI_SSCTL_SSACTPOL_MASK	BIT(2) /* Slave Selection Active Polarity */
+#define NUVOTON_QSPI_SSCTL_SS1_MASK	BIT(1) /* Slave Selection 1 Control */
+#define NUVOTON_QSPI_SSCTL_SS0_MASK	BIT(0) /* Slave Selection 0 Control */
+
+/* QSPI FIFO Control Register bit masks */
+#define NUVOTON_QSPI_FIFOCTL_TXRST_MASK	BIT(1) /* Transmit Reset */
+#define NUVOTON_QSPI_FIFOCTL_RXRST_MASK	BIT(0) /* Receive Reset */
+
+/* QSPI Status Register bit masks */
+#define NUVOTON_QSPI_STATUS_TXRXRST_MASK	BIT(23) /* TX or RX Reset Status */
+#define NUVOTON_QSPI_STATUS_TXFULL_MASK	BIT(17) /* Transmit FIFO Full */
+#define NUVOTON_QSPI_STATUS_TXEMPTY_MASK	BIT(16) /* Transmit FIFO Empty */
+#define NUVOTON_QSPI_STATUS_SPIENSTS_MASK	BIT(15) /* QSPI Enable Status */
+#define NUVOTON_QSPI_STATUS_RXFULL_MASK	BIT(9) /* Receive FIFO Full */
+#define NUVOTON_QSPI_STATUS_RXEMPTY_MASK	BIT(8) /* Receive FIFO Empty */
+#define NUVOTON_QSPI_STATUS_UNITIF_MASK	BIT(1) /* Unit Transfer Interrupt Flag */
+#define NUVOTON_QSPI_STATUS_BUSY_MASK	BIT(0) /* Busy Status */
+
+#define NUVOTON_QSPI_DEFAULT_NUM_CS	2
+#define NUVOTON_QSPI_DEFAULT_BPW	8
+#define NUVOTON_QSPI_TIMEOUT_US		10000
+
+struct nuvoton_qspi {
+	void __iomem *regs;
+	struct clk *clk;
+	struct device *dev;
+	u32 speed_hz;
+	u8 bits_per_word;
+};
+
+static u32 nuvoton_qspi_read(struct nuvoton_qspi *qspi, u32 reg)
+{
+	return readl(qspi->regs + reg);
+}
+
+static void nuvoton_qspi_write(struct nuvoton_qspi *qspi, u32 val, u32 reg)
+{
+	writel(val, qspi->regs + reg);
+}
+
+static void nuvoton_qspi_update_bits(struct nuvoton_qspi *qspi, u32 reg,
+				     u32 mask, u32 val)
+{
+	u32 tmp;
+
+	tmp = nuvoton_qspi_read(qspi, reg);
+	tmp &= ~mask;
+	tmp |= val & mask;
+	nuvoton_qspi_write(qspi, tmp, reg);
+}
+
+static int nuvoton_qspi_wait_ready(struct nuvoton_qspi *qspi)
+{
+	u32 val;
+
+	return readl_poll_timeout(qspi->regs + NUVOTON_QSPI_STATUS_OFFSET,
+					 val,
+					 !(val & NUVOTON_QSPI_STATUS_BUSY_MASK),
+					 0, NUVOTON_QSPI_TIMEOUT_US);
+}
+
+static int nuvoton_qspi_reset_fifo(struct nuvoton_qspi *qspi)
+{
+	u32 val;
+
+	val = nuvoton_qspi_read(qspi, NUVOTON_QSPI_FIFOCTL_OFFSET);
+	val |= NUVOTON_QSPI_FIFOCTL_TXRST_MASK |
+	       NUVOTON_QSPI_FIFOCTL_RXRST_MASK;
+	nuvoton_qspi_write(qspi, val, NUVOTON_QSPI_FIFOCTL_OFFSET);
+
+	/* FIFO reset is extremely fast, safe to keep atomic for this micro-wait */
+	return readl_poll_timeout_atomic(qspi->regs + NUVOTON_QSPI_STATUS_OFFSET,
+					 val,
+					 !(val & NUVOTON_QSPI_STATUS_TXRXRST_MASK),
+					 1, NUVOTON_QSPI_TIMEOUT_US);
+}
+
+static int nuvoton_qspi_set_speed(struct nuvoton_qspi *qspi, u32 speed_hz)
+{
+	unsigned long clk_rate;
+	u32 div;
+
+	if (!speed_hz)
+		return -EINVAL;
+
+	if (qspi->speed_hz == speed_hz)
+		return 0;
+
+	clk_rate = clk_get_rate(qspi->clk);
+	if (!clk_rate) {
+		dev_err(qspi->dev, "failed to get clock rate\n");
+		return -EINVAL;
+	}
+
+	div = DIV_ROUND_UP(clk_rate, speed_hz) - 1;
+	if (div > FIELD_MAX(NUVOTON_QSPI_CLKDIV_MASK)) {
+		dev_err(qspi->dev, "unsupported SPI clock %u Hz\n", speed_hz);
+		return -EINVAL;
+	}
+
+	nuvoton_qspi_write(qspi, FIELD_PREP(NUVOTON_QSPI_CLKDIV_MASK, div),
+			   NUVOTON_QSPI_CLKDIV_OFFSET);
+	qspi->speed_hz = speed_hz;
+
+	return 0;
+}
+
+static int nuvoton_qspi_set_bits_per_word(struct nuvoton_qspi *qspi, u8 bpw)
+{
+	u32 val;
+
+	if (bpw != 8 && bpw != 16 && bpw != 32)
+		return -EINVAL;
+
+	if (bpw == 32)
+		val = NUVOTON_QSPI_CTL_REORDER_MASK;
+	else
+		val = FIELD_PREP(NUVOTON_QSPI_CTL_DWIDTH_MASK, bpw);
+
+	nuvoton_qspi_update_bits(qspi, NUVOTON_QSPI_CTL_OFFSET,
+				 NUVOTON_QSPI_CTL_DWIDTH_MASK |
+				 NUVOTON_QSPI_CTL_REORDER_MASK, val);
+	qspi->bits_per_word = bpw;
+
+	return 0;
+}
+
+static int nuvoton_qspi_setup_transfer(struct spi_device *spi,
+				       u32 speed_hz, u8 bpw)
+{
+	struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller);
+	u32 mode = spi->mode & SPI_MODE_X_MASK;
+	u32 ctl = 0;
+	int ret;
+
+	if (!speed_hz)
+		speed_hz = spi->max_speed_hz;
+
+	if (!bpw)
+		bpw = NUVOTON_QSPI_DEFAULT_BPW;
+
+	ret = nuvoton_qspi_set_speed(qspi, speed_hz);
+	if (ret)
+		return ret;
+
+	ret = nuvoton_qspi_set_bits_per_word(qspi, bpw);
+	if (ret)
+		return ret;
+
+	if (mode == SPI_MODE_0 || mode == SPI_MODE_3)
+		ctl |= NUVOTON_QSPI_CTL_TXNEG_MASK;
+	else
+		ctl |= NUVOTON_QSPI_CTL_RXNEG_MASK;
+
+	if (spi->mode & SPI_CPOL)
+		ctl |= NUVOTON_QSPI_CTL_CLKPOL_MASK;
+
+	if (spi->mode & SPI_LSB_FIRST)
+		ctl |= NUVOTON_QSPI_CTL_LSB_MASK;
+
+	nuvoton_qspi_update_bits(qspi, NUVOTON_QSPI_CTL_OFFSET,
+				 NUVOTON_QSPI_CTL_TXNEG_MASK |
+				 NUVOTON_QSPI_CTL_RXNEG_MASK |
+				 NUVOTON_QSPI_CTL_CLKPOL_MASK |
+				 NUVOTON_QSPI_CTL_LSB_MASK, ctl);
+
+	return 0;
+}
+
+static void nuvoton_qspi_set_bus_width(struct nuvoton_qspi *qspi,
+				       unsigned int buswidth,
+				       enum spi_mem_data_dir dir)
+{
+	u32 ctl = 0;
+
+	if (buswidth == 4)
+		ctl |= NUVOTON_QSPI_CTL_QUADIOEN_MASK;
+	else if (buswidth == 2)
+		ctl |= NUVOTON_QSPI_CTL_DUALIOEN_MASK;
+
+	if (buswidth > 1 && dir == SPI_MEM_DATA_OUT)
+		ctl |= NUVOTON_QSPI_CTL_DATDIR_MASK;
+
+	nuvoton_qspi_update_bits(qspi, NUVOTON_QSPI_CTL_OFFSET,
+				 NUVOTON_QSPI_CTL_QUADIOEN_MASK |
+				 NUVOTON_QSPI_CTL_DUALIOEN_MASK |
+				 NUVOTON_QSPI_CTL_DATDIR_MASK, ctl);
+}
+
+static u32 nuvoton_qspi_tx_word(const void *txbuf, unsigned int idx, u8 bpw)
+{
+	if (!txbuf)
+		return 0;
+
+	if (bpw <= 8)
+		return ((const u8 *)txbuf)[idx];
+	if (bpw <= 16)
+		return ((const u16 *)txbuf)[idx];
+
+	return ((const u32 *)txbuf)[idx];
+}
+
+static void nuvoton_qspi_rx_word(void *rxbuf, unsigned int idx, u32 val, u8 bpw)
+{
+	if (!rxbuf)
+		return;
+
+	if (bpw <= 8)
+		((u8 *)rxbuf)[idx] = val;
+	else if (bpw <= 16)
+		((u16 *)rxbuf)[idx] = val;
+	else
+		((u32 *)rxbuf)[idx] = val;
+}
+
+static int nuvoton_qspi_wait_tx_not_full(struct nuvoton_qspi *qspi)
+{
+	u32 val;
+
+	return readl_poll_timeout_atomic(qspi->regs + NUVOTON_QSPI_STATUS_OFFSET,
+					 val,
+					 !(val & NUVOTON_QSPI_STATUS_TXFULL_MASK),
+					 0, NUVOTON_QSPI_TIMEOUT_US);
+}
+
+static int nuvoton_qspi_wait_rx_not_empty(struct nuvoton_qspi *qspi)
+{
+	u32 val;
+
+	return readl_poll_timeout_atomic(qspi->regs + NUVOTON_QSPI_STATUS_OFFSET,
+					 val,
+					 !(val & NUVOTON_QSPI_STATUS_RXEMPTY_MASK),
+					 0, NUVOTON_QSPI_TIMEOUT_US);
+}
+
+static int nuvoton_qspi_txrx(struct nuvoton_qspi *qspi, const void *txbuf,
+			     void *rxbuf, unsigned int len)
+{
+	unsigned int bytes_per_word = DIV_ROUND_UP(qspi->bits_per_word, 8);
+	unsigned int words;
+	u32 val;
+	int ret;
+	int i;
+
+	if (!len)
+		return 0;
+
+	if (len % bytes_per_word)
+		return -EINVAL;
+
+	words = len / bytes_per_word;
+
+	ret = nuvoton_qspi_reset_fifo(qspi);
+	if (ret) {
+		dev_err(qspi->dev, "FIFO reset timed out\n");
+		return ret;
+	}
+
+	/*
+	 * Use conservative word-by-word PIO access. This keeps the initial driver
+	 * simple and avoids relying on FIFO threshold interrupts or DMA support.
+	 */
+	for (i = 0; i < words; i++) {
+		ret = nuvoton_qspi_wait_tx_not_full(qspi);
+		if (ret) {
+			dev_err(qspi->dev, "TX FIFO full timeout\n");
+			return ret;
+		}
+
+		nuvoton_qspi_write(qspi, nuvoton_qspi_tx_word(txbuf, i,
+							      qspi->bits_per_word),
+				   NUVOTON_QSPI_TX_OFFSET);
+
+		ret = nuvoton_qspi_wait_rx_not_empty(qspi);
+		if (ret) {
+			dev_err(qspi->dev, "RX FIFO empty timeout\n");
+			return ret;
+		}
+
+		val = nuvoton_qspi_read(qspi, NUVOTON_QSPI_RX_OFFSET);
+		if (rxbuf)
+			nuvoton_qspi_rx_word(rxbuf, i, val, qspi->bits_per_word);
+	}
+
+	ret = nuvoton_qspi_wait_ready(qspi);
+	if (ret)
+		dev_err(qspi->dev, "controller busy timeout\n");
+
+	return ret;
+}
+
+static int nuvoton_qspi_hw_init(struct nuvoton_qspi *qspi)
+{
+	u32 val;
+	int ret;
+
+	ret = nuvoton_qspi_set_bits_per_word(qspi, NUVOTON_QSPI_DEFAULT_BPW);
+	if (ret)
+		return ret;
+
+	nuvoton_qspi_update_bits(qspi, NUVOTON_QSPI_CTL_OFFSET,
+				 NUVOTON_QSPI_CTL_SUSPITV_MASK |
+				 NUVOTON_QSPI_CTL_TXNEG_MASK |
+				 NUVOTON_QSPI_CTL_RXNEG_MASK |
+				 NUVOTON_QSPI_CTL_CLKPOL_MASK |
+				 NUVOTON_QSPI_CTL_LSB_MASK,
+				 NUVOTON_QSPI_CTL_TXNEG_MASK);
+
+	val = nuvoton_qspi_read(qspi, NUVOTON_QSPI_CTL_OFFSET);
+	nuvoton_qspi_write(qspi, val | NUVOTON_QSPI_CTL_SPIEN_MASK,
+			   NUVOTON_QSPI_CTL_OFFSET);
+
+	ret = readl_poll_timeout(qspi->regs + NUVOTON_QSPI_STATUS_OFFSET, val,
+				 (val & NUVOTON_QSPI_STATUS_SPIENSTS_MASK),
+				 1, NUVOTON_QSPI_TIMEOUT_US);
+	if (ret) {
+		dev_err(qspi->dev, "failed to enable controller\n");
+		return ret;
+	}
+
+	ret = nuvoton_qspi_reset_fifo(qspi);
+	if (ret)
+		dev_err(qspi->dev, "FIFO reset timed out\n");
+
+	return ret;
+}
+
+static bool nuvoton_qspi_mem_supports_op(struct spi_mem *mem,
+					 const struct spi_mem_op *op)
+{
+	if (!spi_mem_default_supports_op(mem, op))
+		return false;
+
+	if (op->cmd.buswidth > 4 || op->addr.buswidth > 4 ||
+	    op->dummy.buswidth > 4 || op->data.buswidth > 4)
+		return false;
+
+	if (op->cmd.nbytes != 1)
+		return false;
+
+	if (op->addr.nbytes > 4)
+		return false;
+
+	return true;
+}
+
+static void nuvoton_qspi_set_cs_level(struct nuvoton_qspi *qspi,
+				      unsigned int cs, bool assert)
+{
+	u32 mask;
+	u32 val;
+
+	switch (cs) {
+	case 0:
+		mask = NUVOTON_QSPI_SSCTL_SS0_MASK;
+		break;
+	case 1:
+		mask = NUVOTON_QSPI_SSCTL_SS1_MASK;
+		break;
+	default:
+		dev_warn(qspi->dev, "invalid chip select %u\n", cs);
+		return;
+	}
+
+	val = nuvoton_qspi_read(qspi, NUVOTON_QSPI_SSCTL_OFFSET);
+
+	if (assert)
+		val |= mask;
+	else
+		val &= ~mask;
+
+	nuvoton_qspi_write(qspi, val, NUVOTON_QSPI_SSCTL_OFFSET);
+}
+
+static void nuvoton_qspi_set_cs(struct spi_device *spi, bool enable)
+{
+	struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller);
+
+	nuvoton_qspi_set_cs_level(qspi, spi_get_chipselect(spi, 0), enable);
+}
+
+static void nuvoton_qspi_mem_set_cs(struct spi_device *spi, bool enable)
+{
+	struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller);
+	bool assert = enable;
+
+	if (spi->mode & SPI_CS_HIGH)
+		assert = !assert;
+
+	nuvoton_qspi_set_cs_level(qspi, spi_get_chipselect(spi, 0), assert);
+}
+
+static int nuvoton_qspi_mem_exec_op(struct spi_mem *mem,
+				    const struct spi_mem_op *op)
+{
+	struct spi_device *spi = mem->spi;
+	struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller);
+	u8 opcode = op->cmd.opcode;
+	u8 addr[4];
+	int ret;
+	int i;
+
+	ret = nuvoton_qspi_setup_transfer(spi, op->max_freq, NUVOTON_QSPI_DEFAULT_BPW);
+	if (ret)
+		return ret;
+
+	nuvoton_qspi_mem_set_cs(spi, true);
+
+	nuvoton_qspi_set_bus_width(qspi, op->cmd.buswidth, SPI_MEM_DATA_OUT);
+	ret = nuvoton_qspi_txrx(qspi, &opcode, NULL, 1);
+	if (ret)
+		goto out_deassert_cs;
+
+	if (op->addr.nbytes) {
+		for (i = 0; i < op->addr.nbytes; i++)
+			addr[i] = op->addr.val >> (8 * (op->addr.nbytes - i - 1));
+
+		nuvoton_qspi_set_bus_width(qspi, op->addr.buswidth,
+					   SPI_MEM_DATA_OUT);
+		ret = nuvoton_qspi_txrx(qspi, addr, NULL, op->addr.nbytes);
+		if (ret)
+			goto out_deassert_cs;
+	}
+
+	if (op->dummy.nbytes) {
+		nuvoton_qspi_set_bus_width(qspi, op->dummy.buswidth,
+					   SPI_MEM_DATA_IN);
+		ret = nuvoton_qspi_txrx(qspi, NULL, NULL, op->dummy.nbytes);
+		if (ret)
+			goto out_deassert_cs;
+	}
+
+	if (op->data.nbytes) {
+		nuvoton_qspi_set_bus_width(qspi, op->data.buswidth,
+					   op->data.dir);
+		ret = nuvoton_qspi_txrx(qspi,
+					op->data.dir == SPI_MEM_DATA_OUT ?
+					op->data.buf.out : NULL,
+					op->data.dir == SPI_MEM_DATA_IN ?
+					op->data.buf.in : NULL,
+					op->data.nbytes);
+	}
+
+out_deassert_cs:
+	nuvoton_qspi_set_bus_width(qspi, 1, SPI_MEM_DATA_IN);
+	nuvoton_qspi_mem_set_cs(spi, false);
+
+	return ret;
+}
+
+static const struct spi_controller_mem_ops nuvoton_qspi_mem_ops = {
+	.supports_op = nuvoton_qspi_mem_supports_op,
+	.exec_op = nuvoton_qspi_mem_exec_op,
+};
+
+static int nuvoton_qspi_transfer_one(struct spi_controller *ctlr,
+				     struct spi_device *spi,
+				     struct spi_transfer *xfer)
+{
+	struct nuvoton_qspi *qspi = spi_controller_get_devdata(ctlr);
+	enum spi_mem_data_dir dir = SPI_MEM_DATA_IN;
+	unsigned int buswidth = 1;
+	int ret;
+
+	ret = nuvoton_qspi_setup_transfer(spi, xfer->speed_hz, xfer->bits_per_word);
+	if (ret)
+		return ret;
+
+	if (xfer->tx_buf && xfer->rx_buf) {
+		if (xfer->tx_nbits != SPI_NBITS_SINGLE ||
+		    xfer->rx_nbits != SPI_NBITS_SINGLE)
+			return -EOPNOTSUPP;
+	}
+
+	if (xfer->tx_buf) {
+		dir = SPI_MEM_DATA_OUT;
+		if (xfer->tx_nbits == SPI_NBITS_QUAD)
+			buswidth = 4;
+		else if (xfer->tx_nbits == SPI_NBITS_DUAL)
+			buswidth = 2;
+	} else if (xfer->rx_buf) {
+		if (xfer->rx_nbits == SPI_NBITS_QUAD)
+			buswidth = 4;
+		else if (xfer->rx_nbits == SPI_NBITS_DUAL)
+			buswidth = 2;
+	}
+
+	nuvoton_qspi_set_bus_width(qspi, buswidth, dir);
+	ret = nuvoton_qspi_txrx(qspi, xfer->tx_buf, xfer->rx_buf, xfer->len);
+	nuvoton_qspi_set_bus_width(qspi, 1, SPI_MEM_DATA_IN);
+
+	return ret;
+}
+
+static int nuvoton_qspi_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct spi_controller *ctlr;
+	struct nuvoton_qspi *qspi;
+	struct reset_control *rst;
+	int ret;
+
+	ctlr = devm_spi_alloc_host(dev, sizeof(*qspi));
+	if (!ctlr)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, ctlr);
+
+	qspi = spi_controller_get_devdata(ctlr);
+	qspi->dev = dev;
+	qspi->bits_per_word = NUVOTON_QSPI_DEFAULT_BPW;
+
+	qspi->regs = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(qspi->regs))
+		return PTR_ERR(qspi->regs);
+
+	rst = devm_reset_control_get_exclusive(dev, NULL);
+	if (IS_ERR(rst))
+		return dev_err_probe(dev, PTR_ERR(rst),
+				     "failed to get reset\n");
+
+	qspi->clk = devm_clk_get_enabled(dev, NULL);
+	if (IS_ERR(qspi->clk))
+		return dev_err_probe(dev, PTR_ERR(qspi->clk),
+				     "failed to get and enable clock\n");
+
+	ret = reset_control_assert(rst);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to assert reset\n");
+
+	udelay(2);
+
+	ret = reset_control_deassert(rst);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to deassert reset\n");
+
+	ctlr->num_chipselect = NUVOTON_QSPI_DEFAULT_NUM_CS;
+	ctlr->mem_ops = &nuvoton_qspi_mem_ops;
+	ctlr->set_cs = nuvoton_qspi_set_cs;
+	ctlr->transfer_one = nuvoton_qspi_transfer_one;
+	ctlr->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16) |
+				   SPI_BPW_MASK(32);
+	ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST |
+			  SPI_RX_DUAL | SPI_TX_DUAL |
+			  SPI_RX_QUAD | SPI_TX_QUAD;
+	ctlr->dev.of_node = dev->of_node;
+
+	ret = nuvoton_qspi_hw_init(qspi);
+	if (ret)
+		return ret;
+
+	ret = devm_spi_register_controller(dev, ctlr);
+	if (ret)
+		return dev_err_probe(dev, ret,
+				     "failed to register spi controller\n");
+
+	return 0;
+}
+
+static const struct of_device_id nuvoton_qspi_of_match[] = {
+	{ .compatible = "nuvoton,ma35d1-qspi" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, nuvoton_qspi_of_match);
+
+static struct platform_driver nuvoton_qspi_driver = {
+	.driver = {
+		.name = "ma35d1-qspi",
+		.of_match_table = nuvoton_qspi_of_match,
+	},
+	.probe = nuvoton_qspi_probe,
+};
+module_platform_driver(nuvoton_qspi_driver);
+
+MODULE_DESCRIPTION("Nuvoton MA35D1 QSPI controller driver");
+MODULE_AUTHOR("Chi-Wen Weng <cwweng@nuvoton.com>");
+MODULE_LICENSE("GPL");
-- 
2.25.1



^ permalink raw reply related

* [PATCH v1] dmaengine: qcom: hidma-mgmt: Fix sysfs cleanup on setup failure
From: Yuho Choi @ 2026-06-08  3:08 UTC (permalink / raw)
  To: Vinod Koul, Sinan Kaya
  Cc: dmaengine, Frank Li, linux-arm-kernel, linux-arm-msm,
	linux-kernel, Yuho Choi

hidma_mgmt_init_sys() creates the chanops kobject, per-channel
kobjects and sysfs files incrementally. If a later creation step fails,
the function returns without tearing down the objects already created.

Those sysfs callbacks reference devm-managed driver data. A later probe
failure can free that data while the sysfs entries and kobjects remain
registered.

Track the chanops kobject in struct hidma_mgmt_dev, unwind the sysfs
files and channel kobjects on setup failure, and register the same
cleanup with devm after successful setup.

Fixes: 7f8f209fd6e0 ("dmaengine: add Qualcomm Technologies HIDMA management driver")
Signed-off-by: Yuho Choi <dbgh9129@gmail.com>
---
 drivers/dma/qcom/hidma_mgmt.h     |  1 +
 drivers/dma/qcom/hidma_mgmt_sys.c | 65 +++++++++++++++++++++++++------
 2 files changed, 55 insertions(+), 11 deletions(-)

diff --git a/drivers/dma/qcom/hidma_mgmt.h b/drivers/dma/qcom/hidma_mgmt.h
index 30e8095988bf..4fb6759e3371 100644
--- a/drivers/dma/qcom/hidma_mgmt.h
+++ b/drivers/dma/qcom/hidma_mgmt.h
@@ -24,6 +24,7 @@ struct hidma_mgmt_dev {
 	resource_size_t addrsize;
 
 	struct kobject **chroots;
+	struct kobject *chanops;
 	struct platform_device *pdev;
 };
 
diff --git a/drivers/dma/qcom/hidma_mgmt_sys.c b/drivers/dma/qcom/hidma_mgmt_sys.c
index 930eae0a6257..280b3af6ec03 100644
--- a/drivers/dma/qcom/hidma_mgmt_sys.c
+++ b/drivers/dma/qcom/hidma_mgmt_sys.c
@@ -231,20 +231,52 @@ static int create_sysfs_entry_channel(struct hidma_mgmt_dev *mdev, char *name,
 	return sysfs_create_file(parent, &chattr->attr.attr);
 }
 
+static void hidma_mgmt_uninit_sys(struct hidma_mgmt_dev *mdev,
+				  unsigned int sysfs_count,
+				  unsigned int chroot_count)
+{
+	unsigned int i;
+
+	for (i = 0; i < sysfs_count; i++) {
+		struct attribute attr = { .name = hidma_mgmt_files[i].name };
+
+		sysfs_remove_file(&mdev->pdev->dev.kobj, &attr);
+	}
+
+	for (i = 0; i < chroot_count; i++) {
+		kobject_put(mdev->chroots[i]);
+		mdev->chroots[i] = NULL;
+	}
+
+	if (mdev->chanops) {
+		kobject_put(mdev->chanops);
+		mdev->chanops = NULL;
+	}
+}
+
+static void hidma_mgmt_uninit_sys_action(void *data)
+{
+	struct hidma_mgmt_dev *mdev = data;
+
+	hidma_mgmt_uninit_sys(mdev, ARRAY_SIZE(hidma_mgmt_files),
+			      mdev->dma_channels);
+}
+
 int hidma_mgmt_init_sys(struct hidma_mgmt_dev *mdev)
 {
+	unsigned int chroot_count = 0;
+	unsigned int sysfs_count = 0;
 	unsigned int i;
-	int rc;
 	int required;
-	struct kobject *chanops;
+	int rc;
 
 	required = sizeof(*mdev->chroots) * mdev->dma_channels;
 	mdev->chroots = devm_kmalloc(&mdev->pdev->dev, required, GFP_KERNEL);
 	if (!mdev->chroots)
 		return -ENOMEM;
 
-	chanops = kobject_create_and_add("chanops", &mdev->pdev->dev.kobj);
-	if (!chanops)
+	mdev->chanops = kobject_create_and_add("chanops", &mdev->pdev->dev.kobj);
+	if (!mdev->chanops)
 		return -ENOMEM;
 
 	/* create each channel directory here */
@@ -252,9 +284,12 @@ int hidma_mgmt_init_sys(struct hidma_mgmt_dev *mdev)
 		char name[20];
 
 		snprintf(name, sizeof(name), "chan%d", i);
-		mdev->chroots[i] = kobject_create_and_add(name, chanops);
-		if (!mdev->chroots[i])
-			return -ENOMEM;
+		mdev->chroots[i] = kobject_create_and_add(name, mdev->chanops);
+		if (!mdev->chroots[i]) {
+			rc = -ENOMEM;
+			goto err_uninit;
+		}
+		chroot_count++;
 	}
 
 	/* populate common parameters */
@@ -262,7 +297,9 @@ int hidma_mgmt_init_sys(struct hidma_mgmt_dev *mdev)
 		rc = create_sysfs_entry(mdev, hidma_mgmt_files[i].name,
 					hidma_mgmt_files[i].mode);
 		if (rc)
-			return rc;
+			goto err_uninit;
+
+		sysfs_count++;
 	}
 
 	/* populate parameters that are per channel */
@@ -271,15 +308,21 @@ int hidma_mgmt_init_sys(struct hidma_mgmt_dev *mdev)
 						(S_IRUGO | S_IWUGO), i,
 						mdev->chroots[i]);
 		if (rc)
-			return rc;
+			goto err_uninit;
 
 		rc = create_sysfs_entry_channel(mdev, "weight",
 						(S_IRUGO | S_IWUGO), i,
 						mdev->chroots[i]);
 		if (rc)
-			return rc;
+			goto err_uninit;
 	}
 
-	return 0;
+	return devm_add_action_or_reset(&mdev->pdev->dev,
+					hidma_mgmt_uninit_sys_action, mdev);
+
+err_uninit:
+	hidma_mgmt_uninit_sys(mdev, sysfs_count, chroot_count);
+
+	return rc;
 }
 EXPORT_SYMBOL_GPL(hidma_mgmt_init_sys);
-- 
2.43.0



^ permalink raw reply related

* Re: [RFC PATCH v3 6/9] iommu/rockchip: Clear AUTO_GATING bit 1 on the RK356x v1 IOMMU
From: Chaoyi Chen @ 2026-06-08  3:40 UTC (permalink / raw)
  To: Midgy Balon
  Cc: Simon Xue, tomeu, ogabbay, heiko, robh, krzk+dt, conor+dt, joro,
	will, robin.murphy, dri-devel, linux-rockchip, devicetree,
	linux-arm-kernel, iommu, linux-kernel
In-Reply-To: <0919ffcc-81c3-483a-a3ba-404b455c2040@rock-chips.com>

On 6/8/2026 9:45 AM, Chaoyi Chen wrote:
> Hi Midgy,
> 
> On 6/8/2026 5:05 AM, Midgy Balon wrote:
>> Hi Chaoyi,
>>
>>> As I said, it is v2. Could you please try using the code below instead and
>>> see if it works?
>>> [ auto_gate = read(RK_MMU_AUTO_GATING); auto_gate |= BIT(31); write(...) ]
>>
>> Thanks -- that's clearly the right shape (read-modify-write, before paging is
>> enabled, keeping the reset value instead of my clobbering 0x2).
>>
>> I rebuilt v7.1-rc6 (with the rocket RK3568 series + your per-device-ops work)
>> using your bit-31 version and tested it on a ROCK 3B: the NPU IOMMU comes up and
>> services the NPU's DMA cleanly -- the NPU probes, attaches its domain, and runs
>> repeated conv submissions with no DMA_READ_ERROR and no page-walk stall. No
>> regression from the write.
>>
>> To be precise about what I can and can't show: I tested both ways on v7.1-rc6 --
>> with your bit-31 write, and on the reset value (0x3) -- and the NPU
>> IOMMU services
>> the NPU's reads with zero faults in both cases (no DMA_READ_ERROR, no page-walk
>> stall). So I don't have a failing baseline here that bit-31 visibly
>> fixes. Is the
>> AUTO_GATING write needed on current mainline, or only under conditions I'm not
>> reproducing (a particular traffic pattern / silicon rev)? I'll keep the patch in
>> your form unless you'd prefer to drop it.
>>
>> One question so I document it correctly: what does bit 31 of RK_MMU_AUTO_GATING
>> control on the v2 block -- is it a master "disable internal auto clock-gating"
>> for the page-table walker (i.e. so a TLB-miss walk's AXI master keeps its clock
>> to completion)? The RK3568 TRM I have doesn't cover the IOMMU registers, so a
>> one-line description would let me write an accurate comment.
>>
> 
> Glad to hear this works. Please refer to the commit below.
> 
> [0]: https://github.com/rockchip-linux/kernel/commit/7f8158fb41b5cc8e738aaeebc3637c50ebd74cae
> [1]: https://github.com/rockchip-linux/kernel/commit/6a355e5f9a2069a2309e240791bc3aad63b7324e
> 

It looks like RGA needs this patch too, and it has already been merged :).

https://lore.kernel.org/all/20260428-spu-iommudtefix-v2-1-f592f579e508@pengutronix.de/

-- 
Best, 
Chaoyi


^ permalink raw reply

* [PATCH v1] ARM: l2x0: Fix OF node reference leak in l2x0_of_init
From: Yuho Choi @ 2026-06-08  4:23 UTC (permalink / raw)
  To: Russell King; +Cc: Kuninori Morimoto, linux-arm-kernel, linux-kernel, Yuho Choi

l2x0_of_init() gets the cache controller node with
of_find_matching_node(), which returns the node with a reference held.
The node is only needed while parsing the cache controller resources and
properties, but the function returns without dropping that reference on
the resource/ioremap failure paths or after __l2c_init() returns.

Use a single exit path after the node lookup and put the node before
returning.

Fixes: 8c369264b6de ("ARM: 7009/1: l2x0: Add OF based initialization")
Signed-off-by: Yuho Choi <dbgh9129@gmail.com>
---
 arch/arm/mm/cache-l2x0.c | 19 ++++++++++++++-----
 1 file changed, 14 insertions(+), 5 deletions(-)

diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c
index 470867160076..e20ac79dd500 100644
--- a/arch/arm/mm/cache-l2x0.c
+++ b/arch/arm/mm/cache-l2x0.c
@@ -1767,17 +1767,22 @@ int __init l2x0_of_init(u32 aux_val, u32 aux_mask)
 	u32 cache_id, old_aux;
 	u32 cache_level = 2;
 	bool nosync = false;
+	int ret;
 
 	np = of_find_matching_node(NULL, l2x0_ids);
 	if (!np)
 		return -ENODEV;
 
-	if (of_address_to_resource(np, 0, &res))
-		return -ENODEV;
+	if (of_address_to_resource(np, 0, &res)) {
+		ret = -ENODEV;
+		goto out_put_node;
+	}
 
 	l2x0_base = ioremap(res.start, resource_size(&res));
-	if (!l2x0_base)
-		return -ENOMEM;
+	if (!l2x0_base) {
+		ret = -ENOMEM;
+		goto out_put_node;
+	}
 
 	l2x0_saved_regs.phy_base = res.start;
 
@@ -1821,6 +1826,10 @@ int __init l2x0_of_init(u32 aux_val, u32 aux_mask)
 	else
 		cache_id = readl_relaxed(l2x0_base + L2X0_CACHE_ID);
 
-	return __l2c_init(data, aux_val, aux_mask, cache_id, nosync);
+	ret = __l2c_init(data, aux_val, aux_mask, cache_id, nosync);
+
+out_put_node:
+	of_node_put(np);
+	return ret;
 }
 #endif
-- 
2.43.0



^ permalink raw reply related

* Re: [PATCH] arm_mpam: Fix MPAMCFG_MBW_PBM register setting
From: Gavin Shan @ 2026-06-08  4:46 UTC (permalink / raw)
  To: Fenghua Yu, Ben Horgan, James Morse, Reinette Chatre,
	Catalin Marinas, Shaopeng Tan, Jesse Chick
  Cc: linux-kernel, linux-arm-kernel, Matt Ochs
In-Reply-To: <20260607050925.252475-1-fenghuay@nvidia.com>

Hi Fenghua,

On 6/7/26 3:09 PM, Fenghua Yu wrote:
> MPAMCFG_MBW_PBM is written from cfg if cfg has the MBW partition feature.
> It is reset when cfg does not have the MBW partition feature.
> 
> But the register handling is reversed. This may cause an incorrect
> register setting. For example, during an MPAM reset, reset_cfg is
> empty (no MBW partition feature set), and cfg->mbw_pbm is 0. Instead of
> resetting MPAMCFG_MBW_PBM to all 1's, the current logic will set it to
> cfg->mbw_pbm, which is 0.
> 
> Fix the issue by swapping the if/else branches.
> 
> Fixes: a1cb6577f575 ("arm_mpam: Reset when feature configuration bit unset")
> Reported-by: Matt Ochs <mochs@nvidia.com>
> Signed-off-by: Fenghua Yu <fenghuay@nvidia.com>
> ---
>   drivers/resctrl/mpam_devices.c | 4 ++--
>   1 file changed, 2 insertions(+), 2 deletions(-)
> 

The fix itself looks reasonable to me, but two questions below.

Reviewed-by: Gavin Shan <gshan@redhat.com>

> diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
> index 4b93e89c2678..d8b0383cee92 100644
> --- a/drivers/resctrl/mpam_devices.c
> +++ b/drivers/resctrl/mpam_devices.c
> @@ -1570,9 +1570,9 @@ static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid,
>   
>   	if (mpam_has_feature(mpam_feat_mbw_part, rprops)) {
>   		if (mpam_has_feature(mpam_feat_mbw_part, cfg))
> -			mpam_reset_msc_bitmap(msc, MPAMCFG_MBW_PBM, rprops->mbw_pbm_bits);
> -		else
>   			mpam_write_partsel_reg(msc, MBW_PBM, cfg->mbw_pbm);
> +		else
> +			mpam_reset_msc_bitmap(msc, MPAMCFG_MBW_PBM, rprops->mbw_pbm_bits);
>   	}
>   
>   	if (mpam_has_feature(mpam_feat_mbw_min, rprops)) {

Which machine or system where mpam_feat_mbw_part is set on RIS? As I can
remember, this feature isn't available on grace-hopper.

Besides, I don't think this feature is well handled at present because
mpam_config::mbw_pbm is only 32-bits in length, which doesn't match with
the maximal length of the bit map (4096) as documented in the spec.

Thanks,
Gavin



^ permalink raw reply

* Re: [PATCH v2 1/9] mailbox: imx: Add a channel shutdown field
From: Peng Fan @ 2026-06-08  5:01 UTC (permalink / raw)
  To: Sebastian Andrzej Siewior
  Cc: linux-kernel, imx, linux-arm-kernel, linux-remoteproc,
	linux-rt-devel, Bjorn Andersson, Clark Williams, Fabio Estevam,
	Frank Li, Jassi Brar, Mathieu Poirier, Pengutronix Kernel Team,
	Sascha Hauer, Steven Rostedt
In-Reply-To: <20260603-imx_mbox_rproc-v2-1-a0059dc3b69a@linutronix.de>

On Wed, Jun 03, 2026 at 03:05:04PM +0200, Sebastian Andrzej Siewior wrote:
>sashiko complained about possible teardown problem. The scenario
>
> CPU 0                              CPU 1
>  imx_mu_isr()                   imx_mu_shutdown()
>                                   imx_mu_xcr_rmw(priv, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx));
>    imx_mu_specific_rx()
>      imx_mu_xcr_rmw(priv, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, 0), 0);
>                                   free_irq()
>
>The RX event remains enabled because in this short window the RX event
>was disabled in ->shutdown() while the interrupt was active and then
>enabled again by the ISR while ->shutdown waited in free_irq().
>
>This race requires timing and if happens can be problematic on shared
>handlers if the "removed" channel triggers an interrupt. In this case
>the irq-core will shutdown the interrupt with the "nobody cared"
>message.
>
>Introduce imx_mu_con_priv::shutdown to signal that the channel is
>shutting down. This flag is set with the lock held (by
>imx_mu_xcr_clr_shut()). The unmask side uses imx_mu_xcr_set_act() which
>only enables the event if the channel has not been shutdown and
>serialises on the same lock.
>
>Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>

Reviewed-by: Peng Fan <peng.fan@nxp.com>


^ permalink raw reply

* Re: [PATCH v2 3/9] mailbox: imx: use devm_of_platform_populate()
From: Peng Fan @ 2026-06-08  5:05 UTC (permalink / raw)
  To: Sebastian Andrzej Siewior
  Cc: linux-kernel, imx, linux-arm-kernel, linux-remoteproc,
	linux-rt-devel, Bjorn Andersson, Clark Williams, Fabio Estevam,
	Frank Li, Jassi Brar, Mathieu Poirier, Pengutronix Kernel Team,
	Sascha Hauer, Steven Rostedt
In-Reply-To: <20260603-imx_mbox_rproc-v2-3-a0059dc3b69a@linutronix.de>

On Wed, Jun 03, 2026 at 03:05:06PM +0200, Sebastian Andrzej Siewior wrote:
>The driver uses of_platform_populate() but does not remove the added
>devices on removal. This can lead to "double devices" on module removal
>followed by adding the module again.
>
>Use devm_of_platform_populate() to remove the populated devices once the
>parent device is removed.
>
>Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>

Reviewed-by: Peng Fan <peng.fan@nxp.com>


^ permalink raw reply

* Re: [PATCH v2 2/9] mailbox: imx: Use devm_pm_runtime_enable()
From: Peng Fan @ 2026-06-08  5:06 UTC (permalink / raw)
  To: Sebastian Andrzej Siewior
  Cc: linux-kernel, imx, linux-arm-kernel, linux-remoteproc,
	linux-rt-devel, Bjorn Andersson, Clark Williams, Fabio Estevam,
	Frank Li, Jassi Brar, Mathieu Poirier, Pengutronix Kernel Team,
	Sascha Hauer, Steven Rostedt
In-Reply-To: <20260603-imx_mbox_rproc-v2-2-a0059dc3b69a@linutronix.de>

On Wed, Jun 03, 2026 at 03:05:05PM +0200, Sebastian Andrzej Siewior wrote:
>sashiko complained about early usage of the device while probe isn't
>completed. This can be mitigated by delaying the pm_runtime_enable()
>into the removal path instead doing it early. This ensures that in an
>error case the device is removed (and imx_mu_shutdown()) before
>pm_runtime_disable() so we don't have to do this manually.
>
>For the order to work, lets move devm_mbox_controller_register() until
>after the pm-runtime part. So the reverse order will be mbox-controller
>removal followed by disabling pm runtime.
>
>Use devm_pm_runtime_enable(), remove manual pm_runtime_disable()
>invocations and move the pm_runtime handling in probe before
>devm_mbox_controller_register().
>
>Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>

Reviewed-by: Peng Fan <peng.fan@nxp.com>


^ permalink raw reply

* [PATCH] firmware: arm_scmi: simplify some allocations
From: Rosen Penev @ 2026-06-08  5:07 UTC (permalink / raw)
  To: arm-scmi
  Cc: Sudeep Holla, Cristian Marussi, Kees Cook, Gustavo A. R. Silva,
	linux-arm-kernel@lists.infradead.org (moderated list:SYSTEM CONTROL & POWER/MANAGEMENT INTERFACE (SC...), linux-kernel@vger.kernel.org (open list), linux-hardening@vger.kernel.org (open list:KERNEL HARDENING (not covered by other areas):Keyword:\b__counted_by(_le|_be|_ptr)?\b)

Use flexible array members to combine allocations and remove kcalloc
usage.

Add __counted_by where appropriate for extra runtime analysis. Move
counting variable assignment after allocation before any potential array
access.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
 drivers/firmware/arm_scmi/notify.c | 42 ++++++++++++------------------
 1 file changed, 16 insertions(+), 26 deletions(-)

diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi/notify.c
index 40ec184eedae..45cbb3d2d684 100644
--- a/drivers/firmware/arm_scmi/notify.c
+++ b/drivers/firmware/arm_scmi/notify.c
@@ -209,11 +209,11 @@ struct scmi_registered_events_desc;
  * @init_work: A work item to perform final initializations of pending handlers
  * @notify_wq: A reference to the allocated Kernel cmwq
  * @pending_mtx: A mutex to protect @pending_events_handlers
+ * @pending_events_handlers: An hashtable containing all pending events'
+ *			     handlers descriptors
  * @registered_protocols: A statically allocated array containing pointers to
  *			  all the registered protocol-level specific information
  *			  related to events' handling
- * @pending_events_handlers: An hashtable containing all pending events'
- *			     handlers descriptors
  *
  * Each platform instance, represented by a handle, has its own instance of
  * the notification subsystem represented by this structure.
@@ -225,8 +225,8 @@ struct scmi_notify_instance {
 	struct workqueue_struct	*notify_wq;
 	/* lock to protect pending_events_handlers */
 	struct mutex		pending_mtx;
-	struct scmi_registered_events_desc	**registered_protocols;
 	DECLARE_HASHTABLE(pending_events_handlers, SCMI_PENDING_HASH_SZ);
+	struct scmi_registered_events_desc	*registered_protocols[];
 };

 /**
@@ -276,13 +276,13 @@ struct scmi_registered_event;
  * @eh_sz: Size of the pre-allocated buffer @eh
  * @in_flight: A reference to an in flight &struct scmi_registered_event
  * @num_events: Number of events in @registered_events
- * @registered_events: A dynamically allocated array holding all the registered
- *		       events' descriptors, whose fixed-size is determined at
- *		       compile time.
  * @registered_mtx: A mutex to protect @registered_events_handlers
  * @ph: SCMI protocol handle reference
  * @registered_events_handlers: An hashtable containing all events' handlers
  *				descriptors registered for this protocol
+ * @registered_events: A dynamically allocated array holding all the registered
+ *		       events' descriptors, whose fixed-size is determined at
+ *		       compile time.
  *
  * All protocols that register at least one event have their protocol-specific
  * information stored here, together with the embedded allocated events_queue.
@@ -302,11 +302,11 @@ struct scmi_registered_events_desc {
 	size_t				eh_sz;
 	void				*in_flight;
 	int				num_events;
-	struct scmi_registered_event	**registered_events;
 	/* mutex to protect registered_events_handlers */
 	struct mutex			registered_mtx;
 	const struct scmi_protocol_handle	*ph;
 	DECLARE_HASHTABLE(registered_events_handlers, SCMI_REGISTERED_HASH_SZ);
+	struct scmi_registered_event	*registered_events[] __counted_by(num_events);
 };

 /**
@@ -338,9 +338,9 @@ struct scmi_registered_event {
 	void		*report;
 	u32		num_sources;
 	bool		not_supported_by_platform;
-	refcount_t	*sources;
 	/* locking to serialize the access to sources */
 	struct mutex	sources_mtx;
+	refcount_t	sources[] __counted_by(num_sources);
 };

 /**
@@ -706,9 +706,12 @@ scmi_allocate_registered_events_desc(struct scmi_notify_instance *ni,
 	if (WARN_ON(ni->registered_protocols[proto_id]))
 		return ERR_PTR(-EINVAL);

-	pd = devm_kzalloc(ni->handle->dev, sizeof(*pd), GFP_KERNEL);
+	pd = devm_kzalloc(ni->handle->dev, struct_size(pd, registered_events, num_events),
+			GFP_KERNEL);
 	if (!pd)
 		return ERR_PTR(-ENOMEM);
+
+	pd->num_events = num_events;
 	pd->id = proto_id;
 	pd->ops = ops;
 	pd->ni = ni;
@@ -722,12 +725,6 @@ scmi_allocate_registered_events_desc(struct scmi_notify_instance *ni,
 		return ERR_PTR(-ENOMEM);
 	pd->eh_sz = eh_sz;

-	pd->registered_events = devm_kcalloc(ni->handle->dev, num_events,
-					     sizeof(char *), GFP_KERNEL);
-	if (!pd->registered_events)
-		return ERR_PTR(-ENOMEM);
-	pd->num_events = num_events;
-
 	/* Initialize per protocol handlers table */
 	mutex_init(&pd->registered_mtx);
 	hash_init(pd->registered_events_handlers);
@@ -800,14 +797,11 @@ int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id,
 				     GFP_KERNEL);
 		if (!r_evt)
 			return -ENOMEM;
+
+		r_evt->num_sources = num_sources;
 		r_evt->proto = pd;
 		r_evt->evt = evt;

-		r_evt->sources = devm_kcalloc(ni->handle->dev, num_sources,
-					      sizeof(refcount_t), GFP_KERNEL);
-		if (!r_evt->sources)
-			return -ENOMEM;
-		r_evt->num_sources = num_sources;
 		mutex_init(&r_evt->sources_mtx);

 		r_evt->report = devm_kzalloc(ni->handle->dev,
@@ -1666,18 +1660,14 @@ int scmi_notification_init(struct scmi_handle *handle)
 	if (!gid)
 		return -ENOMEM;

-	ni = devm_kzalloc(handle->dev, sizeof(*ni), GFP_KERNEL);
+	ni = devm_kzalloc(handle->dev, struct_size(ni, registered_protocols, SCMI_MAX_PROTO),
+			GFP_KERNEL);
 	if (!ni)
 		goto err;

 	ni->gid = gid;
 	ni->handle = handle;

-	ni->registered_protocols = devm_kcalloc(handle->dev, SCMI_MAX_PROTO,
-						sizeof(char *), GFP_KERNEL);
-	if (!ni->registered_protocols)
-		goto err;
-
 	ni->notify_wq = alloc_workqueue(dev_name(handle->dev),
 					WQ_UNBOUND | WQ_FREEZABLE | WQ_SYSFS,
 					0);
--
2.54.0



^ permalink raw reply related

* Re: [PATCH v2 4/9] mailbox: imx: Use channel index instead of zero in imx_mu_specific_rx()
From: Peng Fan @ 2026-06-08  5:11 UTC (permalink / raw)
  To: Sebastian Andrzej Siewior
  Cc: linux-kernel, imx, linux-arm-kernel, linux-remoteproc,
	linux-rt-devel, Bjorn Andersson, Clark Williams, Fabio Estevam,
	Frank Li, Jassi Brar, Mathieu Poirier, Pengutronix Kernel Team,
	Sascha Hauer, Steven Rostedt
In-Reply-To: <20260603-imx_mbox_rproc-v2-4-a0059dc3b69a@linutronix.de>

On Wed, Jun 03, 2026 at 03:05:07PM +0200, Sebastian Andrzej Siewior wrote:
>imx_mu_specific_rx() masks channel 0 and unmasks it again at the end of
>the function. Given that at startup the channel index got unmasked it
>should do the right job.
>
>This here either unmasks the actual channel or another one but should
>have no impact given that it reverses its doing at the end.
>
>Use the channel index instead of zero.
>
>Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
>---
> drivers/mailbox/imx-mailbox.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
>diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c
>index 1dda1437b87f6..d1de07cc0ed62 100644
>--- a/drivers/mailbox/imx-mailbox.c
>+++ b/drivers/mailbox/imx-mailbox.c
>@@ -380,7 +380,7 @@ static int imx_mu_specific_rx(struct imx_mu_priv *priv, struct imx_mu_con_priv *
> 
> 	data = (u32 *)priv->msg;
> 
>-	imx_mu_xcr_rmw(priv, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(priv->dcfg->type, 0));
>+	imx_mu_xcr_rmw(priv, IMX_MU_RCR, 0, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx));
> 	*data++ = imx_mu_read(priv, priv->dcfg->xRR);
> 
> 	if (priv->dcfg->type & IMX_MU_V2_S4) {
>@@ -407,7 +407,7 @@ static int imx_mu_specific_rx(struct imx_mu_priv *priv, struct imx_mu_con_priv *
> 		*data++ = imx_mu_read(priv, priv->dcfg->xRR + (i % num_rr) * 4);
> 	}
> 
>-	imx_mu_xcr_set_act(priv, cp, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, 0));
>+	imx_mu_xcr_set_act(priv, cp, IMX_MU_RCR, IMX_MU_xCR_RIEn(priv->dcfg->type, cp->idx));
> 	mbox_chan_received_data(cp->chan, (void *)priv->msg);

For specific rx channel, whether it is i.MX8 SCU or i.MX ELE, actually there is
only 1 channel as of now, but it seems better to use cp->idx in case more
channels in future.

Reviewed-by: Peng Fan <peng.fan@nxp.com>

> 
> 	return 0;
>
>-- 
>2.53.0
>


^ permalink raw reply

* [PATCH] ARM: arch/arm/kernel/signal.c: resolve set-but-not-used warning
From: Ethan Nelson-Moore @ 2026-06-08  5:09 UTC (permalink / raw)
  To: linux-arm-kernel, linux-kernel
  Cc: Ethan Nelson-Moore, Russell King, Arnd Bergmann,
	Thomas Weißschuh

If neither CONFIG_IWMMXT nor CONFIG_VFP are enabled (which is the case,
for example, in rpc_defconfig), the variable "aux" is set but not used,
which generates a warning during W=1 builds. Resolve this issue by only
defining the variable if it will be used.

Signed-off-by: Ethan Nelson-Moore <enelsonmoore@gmail.com>
---
 arch/arm/kernel/signal.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c
index 7be9188d83d9..1d015b696a76 100644
--- a/arch/arm/kernel/signal.c
+++ b/arch/arm/kernel/signal.c
@@ -140,7 +140,9 @@ static int restore_vfp_context(char __user **auxp)
 static int restore_sigframe(struct pt_regs *regs, struct sigframe __user *sf)
 {
 	struct sigcontext context;
+#if defined(CONFIG_IWMMXT) || defined(CONFIG_VFP)
 	char __user *aux;
+#endif
 	sigset_t set;
 	int err;
 
@@ -171,6 +173,7 @@ static int restore_sigframe(struct pt_regs *regs, struct sigframe __user *sf)
 
 	err |= !valid_user_regs(regs);
 
+#if defined(CONFIG_IWMMXT) || defined(CONFIG_VFP)
 	aux = (char __user *) sf->uc.uc_regspace;
 #ifdef CONFIG_IWMMXT
 	if (err == 0)
@@ -180,6 +183,7 @@ static int restore_sigframe(struct pt_regs *regs, struct sigframe __user *sf)
 	if (err == 0)
 		err |= restore_vfp_context(&aux);
 #endif
+#endif /* defined(CONFIG_IWMMXT) || defined(CONFIG_VFP) */
 
 	return err;
 }
-- 
2.43.0



^ permalink raw reply related

* Re: [PATCH v2 5/9] mailbox: imx: Start splitting the IRQ handler in primary and threaded handler
From: Peng Fan @ 2026-06-08  5:14 UTC (permalink / raw)
  To: Sebastian Andrzej Siewior
  Cc: linux-kernel, imx, linux-arm-kernel, linux-remoteproc,
	linux-rt-devel, Bjorn Andersson, Clark Williams, Fabio Estevam,
	Frank Li, Jassi Brar, Mathieu Poirier, Pengutronix Kernel Team,
	Sascha Hauer, Steven Rostedt
In-Reply-To: <20260603-imx_mbox_rproc-v2-5-a0059dc3b69a@linutronix.de>

On Wed, Jun 03, 2026 at 03:05:08PM +0200, Sebastian Andrzej Siewior wrote:
>Split the mailbox irq handling into a primary handler (imx_mu_isr()) and
>a threaded handler (imx_mu_isr_th()). The primary handler masks the
>interrupt event so the threaded handler can run without raising the
>interrupt again. The threaded handler can invoke the actuall callback in
>preemtible context.
>
>As a first step, prepare the logic and move TX handling part.
>
>Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>

Reviewed-by: Peng Fan <peng.fan@nxp.com>


^ permalink raw reply

* Re: [EXT] Re: [PATCH] arm64: dts: imx93-11x11-frdm: enable additional devices
From: Joseph Guo @ 2026-06-08  5:14 UTC (permalink / raw)
  To: Francesco Valla
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Shawn Guo,
	Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
	Daniel Baluta, devicetree, imx, linux-arm-kernel, linux-kernel,
	steven.yang
In-Reply-To: <_j4Ik3AsS5yfKb4Y7cSB1w@valla.it>

On 6/5/2026 7:36 PM, Francesco Valla wrote:
> Caution: This is an external email. Please take care when clicking links or opening attachments. When in doubt, report the message using the 'Report this email' button
> 
> 
> Hi Joseph,
> 
> On venerdì 5 giugno 2026 10:59:08 Ora legale dell’Europa centrale Joseph Guo
> wrote:
>> On Thu, Jan 15, 2026 at 06:11:34PM +0100, Francesco Valla wrote:
>>> Enable additional devices on the i.MX93 FRDM board:
>>>   - CAN port and associated transceiver
>>>   - Bluetooth portion of the IW612 chipset
>>>   - WiFi SDIO port
>>>   - user buttons
>>>
>>> The WiFi portion of the on-board IW612 chipset is still not supported
>>> upstream, but since SDIO is a discoverable bus it will be probed once it
>>> is.
>>>
>>> Signed-off-by: Francesco Valla <francesco@valla.it>
>>> ---
> [...]
> 
>>
>> Hi Francesco,
>>
>> Do you ever tried bluetooth feature? The bluetooth failed to scan with
>> 'device-wakeup-gpios' property.
>>
>> Regards,
>> Joseph
>>
> 
> Yes, Bluetooth was tested using bluetoothctl, I just briefly re-tested it
> with latest master branch (7.1.0-rc6).
> 
> Can you clarify what you mean with "The bluetooth failed to scan
> with 'device-wakeup-gpios' property."?
> 

Hi Francesco,

If 'device-wakeup-gpios' property is set. The bluetoothctl can work, but errors will show up if try to scan the bluetooth devices.

[bluetoothctl]> scan on
SetDiscoveryFilter success
Failed to start discovery: org.bluez.Error.InProgress
hci0 class of device changed: 0x000000
hci0 new_settings: bondable ssp br/edr le secure-conn cis-central cis-peripheral iso-broadcaster sync-receiver ll-privacy past-sender past-receiver
[CHG] Controller 20:BA:36:5C:B0:D8 Class: 0x00000000 (0)
[CHG] Controller 20:BA:36:5C:B0:D8 Powered: no
[CHG] Controller 20:BA:36:5C:B0:D8 Discovering: no
[CHG] Controller 20:BA:36:5C:B0:D8 PowerState: on
[bluetoothctl]> discoverable on
Failed to set discoverable on: org.bluez.Error.Failed

After remove the 'device-wakeup-gpios' node. The bluetooth can work normally.

> Thank you
> 
> Regards,
> 
> Francesco
> 
> 

-- 
Best Regards,
Joseph


^ permalink raw reply

* [PATCHv3] clk: kirkwood: use kzalloc_flex
From: Rosen Penev @ 2026-06-08  5:16 UTC (permalink / raw)
  To: linux-clk
  Cc: Andrew Lunn, Gregory Clement, Sebastian Hesselbarth,
	Michael Turquette, Stephen Boyd, Brian Masney, Kees Cook,
	Gustavo A. R. Silva,
	moderated list:ARM/Marvell Kirkwood and Armada 370, 375, 38x,...,
	open list,
	open list:KERNEL HARDENING (not covered by other areas):Keyword:b__counted_by(_le|_be|_ptr)?b

Simplify allocation by using a flexible array member and kzalloc_flex to
combine allocations.

Add __counted_by for extra runtime analysis. Move counting variable
assignment to right after allocation. kzalloc_flex does this
automatically with GCC >= 15.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
Reviewed-by: Brian Masney <bmasney@redhat.com>
---
 v3: change description slightly.
 v2: remove unused goto.
 drivers/clk/mvebu/kirkwood.c | 21 ++++++++-------------
 1 file changed, 8 insertions(+), 13 deletions(-)

diff --git a/drivers/clk/mvebu/kirkwood.c b/drivers/clk/mvebu/kirkwood.c
index ed061d82fb65..f4f62b241193 100644
--- a/drivers/clk/mvebu/kirkwood.c
+++ b/drivers/clk/mvebu/kirkwood.c
@@ -253,8 +253,8 @@ struct clk_muxing_soc_desc {

 struct clk_muxing_ctrl {
 	spinlock_t *lock;
-	struct clk **muxes;
 	int num_muxes;
+	struct clk *muxes[] __counted_by(num_muxes);
 };

 static const char *powersave_parents[] = {
@@ -297,21 +297,18 @@ static void __init kirkwood_clk_muxing_setup(struct device_node *np,
 	if (WARN_ON(!base))
 		return;

-	ctrl = kzalloc_obj(*ctrl);
-	if (WARN_ON(!ctrl))
-		goto ctrl_out;
-
-	/* lock must already be initialized */
-	ctrl->lock = &ctrl_gating_lock;
-
 	/* Count, allocate, and register clock muxes */
 	for (n = 0; desc[n].name;)
 		n++;

+	ctrl = kzalloc_flex(*ctrl, muxes, n);
+	if (WARN_ON(!ctrl))
+		goto ctrl_out;
+
 	ctrl->num_muxes = n;
-	ctrl->muxes = kzalloc_objs(struct clk *, ctrl->num_muxes);
-	if (WARN_ON(!ctrl->muxes))
-		goto muxes_out;
+
+	/* lock must already be initialized */
+	ctrl->lock = &ctrl_gating_lock;

 	for (n = 0; n < ctrl->num_muxes; n++) {
 		ctrl->muxes[n] = clk_register_mux(NULL, desc[n].name,
@@ -324,8 +321,6 @@ static void __init kirkwood_clk_muxing_setup(struct device_node *np,
 	of_clk_add_provider(np, clk_muxing_get_src, ctrl);

 	return;
-muxes_out:
-	kfree(ctrl);
 ctrl_out:
 	iounmap(base);
 }
--
2.54.0



^ permalink raw reply related

* [PATCHv2] dmaengine: st_fdma: simplify allocation
From: Rosen Penev @ 2026-06-08  5:18 UTC (permalink / raw)
  To: dmaengine
  Cc: Patrice Chotard, Vinod Koul, Frank Li, Kees Cook,
	Gustavo A. R. Silva, moderated list:ARM/STI ARCHITECTURE,
	open list,
	open list:KERNEL HARDENING (not covered by other areas):Keyword:b__counted_by(_le|_be|_ptr)?b

Use a flexible array member to combine kzalloc and kcalloc to a single
allocation.

Add __counted_by for extra runtime analysis. Assign counting variable
after allocation before any array accesses.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
 v2: update description.
 drivers/dma/st_fdma.c | 27 ++++++++-------------------
 drivers/dma/st_fdma.h |  4 ++--
 2 files changed, 10 insertions(+), 21 deletions(-)

diff --git a/drivers/dma/st_fdma.c b/drivers/dma/st_fdma.c
index d9547017f3bd..3ec0d6731b8d 100644
--- a/drivers/dma/st_fdma.c
+++ b/drivers/dma/st_fdma.c
@@ -710,16 +710,6 @@ static const struct of_device_id st_fdma_match[] = {
 };
 MODULE_DEVICE_TABLE(of, st_fdma_match);

-static int st_fdma_parse_dt(struct platform_device *pdev,
-			const struct st_fdma_driverdata *drvdata,
-			struct st_fdma_dev *fdev)
-{
-	snprintf(fdev->fw_name, FW_NAME_SIZE, "fdma_%s_%d.elf",
-		drvdata->name, drvdata->id);
-
-	return of_property_read_u32(pdev->dev.of_node, "dma-channels",
-				    &fdev->nr_channels);
-}
 #define FDMA_DMA_BUSWIDTHS	(BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \
 				 BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \
 				 BIT(DMA_SLAVE_BUSWIDTH_3_BYTES) | \
@@ -742,27 +732,26 @@ static int st_fdma_probe(struct platform_device *pdev)
 	struct st_fdma_dev *fdev;
 	struct device_node *np = pdev->dev.of_node;
 	const struct st_fdma_driverdata *drvdata;
+	u32 nr_channels;
 	int ret, i;

 	drvdata = device_get_match_data(&pdev->dev);

-	fdev = devm_kzalloc(&pdev->dev, sizeof(*fdev), GFP_KERNEL);
-	if (!fdev)
-		return -ENOMEM;
-
-	ret = st_fdma_parse_dt(pdev, drvdata, fdev);
+	ret = of_property_read_u32(pdev->dev.of_node, "dma-channels", &nr_channels);
 	if (ret) {
 		dev_err(&pdev->dev, "unable to find platform data\n");
-		goto err;
+		return ret;
 	}

-	fdev->chans = devm_kcalloc(&pdev->dev, fdev->nr_channels,
-				   sizeof(struct st_fdma_chan), GFP_KERNEL);
-	if (!fdev->chans)
+	fdev = devm_kzalloc(&pdev->dev, struct_size(fdev, chans, nr_channels), GFP_KERNEL);
+	if (!fdev)
 		return -ENOMEM;

+	fdev->nr_channels = nr_channels;
 	fdev->dev = &pdev->dev;
 	fdev->drvdata = drvdata;
+	snprintf(fdev->fw_name, FW_NAME_SIZE, "fdma_%s_%d.elf", drvdata->name, drvdata->id);
+
 	platform_set_drvdata(pdev, fdev);

 	fdev->irq = platform_get_irq(pdev, 0);
diff --git a/drivers/dma/st_fdma.h b/drivers/dma/st_fdma.h
index f1e746f7bc7d..27ded555879f 100644
--- a/drivers/dma/st_fdma.h
+++ b/drivers/dma/st_fdma.h
@@ -136,13 +136,13 @@ struct st_fdma_dev {

 	int irq;

-	struct st_fdma_chan *chans;
-
 	spinlock_t dreq_lock;
 	unsigned long dreq_mask;

 	u32 nr_channels;
 	char fw_name[FW_NAME_SIZE];
+
+	struct st_fdma_chan chans[] __counted_by(nr_channels);
 };

 /* Peripheral Registers*/
--
2.54.0



^ permalink raw reply related

* [PATCH] scsi: acornscsi: resolve set-but-not-used variable warning
From: Ethan Nelson-Moore @ 2026-06-08  5:19 UTC (permalink / raw)
  To: linux-arm-kernel, linux-scsi
  Cc: Ethan Nelson-Moore, Russell King, James E.J. Bottomley,
	Martin K. Petersen

The variable "p" in the acornscsi_info() function is set but not used,
which causes a warning with W=1 builds. Remove it to resolve this issue.

Signed-off-by: Ethan Nelson-Moore <enelsonmoore@gmail.com>
---
 drivers/scsi/arm/acornscsi.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/drivers/scsi/arm/acornscsi.c b/drivers/scsi/arm/acornscsi.c
index 79d7d7336b6a..ebf09b787dfd 100644
--- a/drivers/scsi/arm/acornscsi.c
+++ b/drivers/scsi/arm/acornscsi.c
@@ -2681,11 +2681,9 @@ static int acornscsi_host_reset(struct scsi_cmnd *SCpnt)
  */
 static const char *acornscsi_info(struct Scsi_Host *host)
 {
-    static char string[100], *p;
+    static char string[100];
 
-    p = string;
-    
-    p += sprintf(string, "%s at port %08lX irq %d v%d.%d.%d"
+    sprintf(string, "%s at port %08lX irq %d v%d.%d.%d"
 #ifdef CONFIG_SCSI_ACORNSCSI_SYNC
     " SYNC"
 #endif
-- 
2.43.0



^ permalink raw reply related

* Re: [PATCH] ARM: arch/arm/kernel/signal.c: resolve set-but-not-used warning
From: Arnd Bergmann @ 2026-06-08  5:29 UTC (permalink / raw)
  To: Ethan Nelson-Moore, linux-arm-kernel, linux-kernel
  Cc: Russell King, Thomas Weißschuh
In-Reply-To: <20260608050926.88446-1-enelsonmoore@gmail.com>

On Mon, Jun 8, 2026, at 07:09, Ethan Nelson-Moore wrote:
> If neither CONFIG_IWMMXT nor CONFIG_VFP are enabled (which is the case,
> for example, in rpc_defconfig), the variable "aux" is set but not used,
> which generates a warning during W=1 builds. Resolve this issue by only
> defining the variable if it will be used.
>
> Signed-off-by: Ethan Nelson-Moore <enelsonmoore@gmail.com>

Acked-by: Arnd Bergmann <arnd@arndb.de>


^ permalink raw reply

* Re: [PATCH v2 5/9] mailbox: imx: Start splitting the IRQ handler in primary and threaded handler
From: Peng Fan @ 2026-06-08  5:32 UTC (permalink / raw)
  To: Sebastian Andrzej Siewior
  Cc: linux-kernel, imx, linux-arm-kernel, linux-remoteproc,
	linux-rt-devel, Bjorn Andersson, Clark Williams, Fabio Estevam,
	Frank Li, Jassi Brar, Mathieu Poirier, Pengutronix Kernel Team,
	Sascha Hauer, Steven Rostedt
In-Reply-To: <20260603-imx_mbox_rproc-v2-5-a0059dc3b69a@linutronix.de>

On Wed, Jun 03, 2026 at 03:05:08PM +0200, Sebastian Andrzej Siewior wrote:
>Split the mailbox irq handling into a primary handler (imx_mu_isr()) and
>a threaded handler (imx_mu_isr_th()). The primary handler masks the
>interrupt event so the threaded handler can run without raising the
>interrupt again. The threaded handler can invoke the actuall callback in
>preemtible context.
>
>As a first step, prepare the logic and move TX handling part.
>
>Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
>---
> drivers/mailbox/imx-mailbox.c | 33 ++++++++++++++++++++++++++++++---
> 1 file changed, 30 insertions(+), 3 deletions(-)
>
>diff --git a/drivers/mailbox/imx-mailbox.c b/drivers/mailbox/imx-mailbox.c
>index d1de07cc0ed62..006aa76b74b62 100644
>--- a/drivers/mailbox/imx-mailbox.c
>+++ b/drivers/mailbox/imx-mailbox.c
>@@ -81,6 +81,7 @@ struct imx_mu_con_priv {
> 	struct mbox_chan	*chan;
> 	struct work_struct 	txdb_work;
> 	bool			shutdown;
>+	bool			pending;
> };
> 
> struct imx_mu_priv {
>@@ -539,11 +540,35 @@ static void imx_mu_txdb_work(struct work_struct *t)
> 	mbox_chan_txdone(cp->chan, 0);
> }
> 
>+static irqreturn_t imx_mu_isr_th(int irq, void *p)
>+{
>+	struct mbox_chan *chan = p;
>+	struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox);
>+	struct imx_mu_con_priv *cp = chan->con_priv;
>+
>+	if (!cp->pending)
>+		return IRQ_NONE;

Is there a chance that cp->pending could be false here?

Regards
Peng

>+
>+	switch (cp->type) {
>+	case IMX_MU_TYPE_TX:
>+		cp->pending = false;
>+		mbox_chan_txdone(chan, 0);
>+		break;
>+
>+	default:
>+		dev_warn_ratelimited(priv->dev, "Unhandled channel type %d\n",
>+				     cp->type);
>+		return IRQ_NONE;
>+	}
>+	return IRQ_HANDLED;
>+}
>+
> static irqreturn_t imx_mu_isr(int irq, void *p)
> {
> 	struct mbox_chan *chan = p;
> 	struct imx_mu_priv *priv = to_imx_mu_priv(chan->mbox);
> 	struct imx_mu_con_priv *cp = chan->con_priv;
>+	irqreturn_t ret = IRQ_HANDLED;
> 	u32 val, ctrl;
> 
> 	switch (cp->type) {
>@@ -579,7 +604,8 @@ static irqreturn_t imx_mu_isr(int irq, void *p)
> 	if ((val == IMX_MU_xSR_TEn(priv->dcfg->type, cp->idx)) &&
> 	    (cp->type == IMX_MU_TYPE_TX)) {
> 		imx_mu_xcr_rmw(priv, IMX_MU_TCR, 0, IMX_MU_xCR_TIEn(priv->dcfg->type, cp->idx));
>-		mbox_chan_txdone(chan, 0);
>+		cp->pending = true;
>+		ret = IRQ_WAKE_THREAD;
> 	} else if ((val == IMX_MU_xSR_RFn(priv->dcfg->type, cp->idx)) &&
> 		   (cp->type == IMX_MU_TYPE_RX)) {
> 		priv->dcfg->rx(priv, cp);
>@@ -594,7 +620,7 @@ static irqreturn_t imx_mu_isr(int irq, void *p)
> 	if (priv->suspend)
> 		pm_system_wakeup();
> 
>-	return IRQ_HANDLED;
>+	return ret;
> }
> 
> static int imx_mu_send_data(struct mbox_chan *chan, void *data)
>@@ -629,7 +655,8 @@ static int imx_mu_startup(struct mbox_chan *chan)
> 	if (!(priv->dcfg->type & IMX_MU_V2_IRQ))
> 		irq_flag |= IRQF_SHARED;
> 
>-	ret = request_irq(priv->irq[cp->type], imx_mu_isr, irq_flag, cp->irq_desc, chan);
>+	ret = request_threaded_irq(priv->irq[cp->type], imx_mu_isr, imx_mu_isr_th,
>+				   irq_flag, cp->irq_desc, chan);
> 	if (ret) {
> 		dev_err(priv->dev, "Unable to acquire IRQ %d\n", priv->irq[cp->type]);
> 		return ret;
>
>-- 
>2.53.0
>


^ permalink raw reply

* [PATCH] phy: apple: atc: remove stale kernel-doc for removed struct members
From: Rosen Penev @ 2026-06-08  5:35 UTC (permalink / raw)
  To: linux-phy
  Cc: Sven Peter, Janne Grunau, Neal Gompa, Vinod Koul, Neil Armstrong,
	open list:ARM/APPLE MACHINE SUPPORT,
	moderated list:ARM/APPLE MACHINE SUPPORT, open list

The @sw and @mux members were removed from struct apple_atcphy but their kernel-doc entries remained, causing kernel-doc warnings.

Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
 drivers/phy/apple/atc.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/phy/apple/atc.c b/drivers/phy/apple/atc.c
index 4156fabad742..f09305dd76d1 100644
--- a/drivers/phy/apple/atc.c
+++ b/drivers/phy/apple/atc.c
@@ -585,8 +585,6 @@ struct atcphy_mode_configuration {
  * @phys.dp: DisplayPort PHY instance
  * @phy_provider: PHY provider instance
  * @rcdev: Reset controller device
- * @sw: Type-C switch instance
- * @mux: Type-C mux instance
  * @lock: Mutex for synchronizing register access across PHY, Type-C switch/mux and reset controller
  */
 struct apple_atcphy {
-- 
2.54.0



^ permalink raw reply related

* Re: [PATCH v2 6/9] mailbox: imx: Move the RX part of the mailbox into the threaded handler
From: Peng Fan @ 2026-06-08  5:48 UTC (permalink / raw)
  To: Sebastian Andrzej Siewior
  Cc: linux-kernel, imx, linux-arm-kernel, linux-remoteproc,
	linux-rt-devel, Bjorn Andersson, Clark Williams, Fabio Estevam,
	Frank Li, Jassi Brar, Mathieu Poirier, Pengutronix Kernel Team,
	Sascha Hauer, Steven Rostedt
In-Reply-To: <20260603-imx_mbox_rproc-v2-6-a0059dc3b69a@linutronix.de>

On Wed, Jun 03, 2026 at 03:05:09PM +0200, Sebastian Andrzej Siewior wrote:
>Move RX callback handling into the threaded handler. This is similar to
>the TX side except that we explicitly mask the source interrupt in the
>primary handler and unmask it in the threaded handler again after
>success. This was done automatically in the TX part.
>
>The masking/ unmasking can be removed from imx_mu_specific_rx() since it
>already happens in the primary/ threaded handler before invoking the
>channel specific callback.
>
>Move RX channel handling into threaded handler.
>
>Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>

Reviewed-by: Peng Fan <peng.fan@nxp.com>


^ permalink raw reply

* Re: [PATCH v2 7/9] mailbox: imx: Move the RXDB part of the mailbox into the threaded handler
From: Peng Fan @ 2026-06-08  5:48 UTC (permalink / raw)
  To: Sebastian Andrzej Siewior
  Cc: linux-kernel, imx, linux-arm-kernel, linux-remoteproc,
	linux-rt-devel, Bjorn Andersson, Clark Williams, Fabio Estevam,
	Frank Li, Jassi Brar, Mathieu Poirier, Pengutronix Kernel Team,
	Sascha Hauer, Steven Rostedt
In-Reply-To: <20260603-imx_mbox_rproc-v2-7-a0059dc3b69a@linutronix.de>

On Wed, Jun 03, 2026 at 03:05:10PM +0200, Sebastian Andrzej Siewior wrote:
>Move RXDB callback handling into the threaded handler. This similar to
>the RX side except that we unmask it unconditionally in threaded
>handler.
>
>Move RXDB callback handling into the threaded handler.
>
>Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>

Reviewed-by: Peng Fan <peng.fan@nxp.com>


^ permalink raw reply

* Re: [PATCH v2 8/9] mailbox: imx: Don't force-thread the primary handler
From: Peng Fan @ 2026-06-08  5:50 UTC (permalink / raw)
  To: Sebastian Andrzej Siewior
  Cc: linux-kernel, imx, linux-arm-kernel, linux-remoteproc,
	linux-rt-devel, Bjorn Andersson, Clark Williams, Fabio Estevam,
	Frank Li, Jassi Brar, Mathieu Poirier, Pengutronix Kernel Team,
	Sascha Hauer, Steven Rostedt
In-Reply-To: <20260603-imx_mbox_rproc-v2-8-a0059dc3b69a@linutronix.de>

On Wed, Jun 03, 2026 at 03:05:11PM +0200, Sebastian Andrzej Siewior wrote:
>The primary interrupt handler (imx_mu_isr()) no longer invokes any
>callbacks it only masks the interrupt source and returns. In a
>forced-threaded environment the IRQ-core will force-thread the primary
>handler which can be avoided.
>
>The primary handler uses a spinlock_t to protect the RMW operation in
>imx_mu_xcr_rmw() - nothing that may introduce long latencies.
>
>The lock can be turned into a raw_spinlock_t and then the primary
>handler can run in hardirq context even on PREEMPT_RT skipping one
>thread.
>
>Make struct imx_mu_priv::xcr_lock a raw_spinlock_t and skip
>force-threading the primrary handler by marking it IRQF_NO_THREAD.
>
>Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>

Reviewed-by: Peng Fan <peng.fan@nxp.com>


^ permalink raw reply

* Re: [PATCH v2 9/9] remoteproc: imx_rproc: Invoke the callback directly
From: Peng Fan @ 2026-06-08  5:52 UTC (permalink / raw)
  To: Sebastian Andrzej Siewior
  Cc: linux-kernel, imx, linux-arm-kernel, linux-remoteproc,
	linux-rt-devel, Bjorn Andersson, Clark Williams, Fabio Estevam,
	Frank Li, Jassi Brar, Mathieu Poirier, Pengutronix Kernel Team,
	Sascha Hauer, Steven Rostedt
In-Reply-To: <20260603-imx_mbox_rproc-v2-9-a0059dc3b69a@linutronix.de>

On Wed, Jun 03, 2026 at 03:05:12PM +0200, Sebastian Andrzej Siewior wrote:
>The imx-mailbox driver moved the callback invocation into the threaded
>IRQ handler. This means the callback is invoked in preemptible context
>and there is no need to schedule the kworker for the
>imx_rproc_notified_idr_cb() invocation.
>
>This was tested with the rpmsg-tty driver on imx93.
>
>Remove the workqueue handling and invoke the imx_rproc_notified_idr_cb()
>callback directly.
>
>Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>

Reviewed-by: Peng Fan <peng.fan@nxp.com>


^ permalink raw reply

* Re: [PATCH] ARM: arch/arm/kernel/signal.c: resolve set-but-not-used warning
From: Baruch Siach @ 2026-06-08  5:36 UTC (permalink / raw)
  To: Ethan Nelson-Moore
  Cc: linux-arm-kernel, linux-kernel, Russell King, Arnd Bergmann,
	Thomas Weißschuh
In-Reply-To: <20260608050926.88446-1-enelsonmoore@gmail.com>

Hi Ethan,

On Sun, Jun 07 2026, Ethan Nelson-Moore wrote:

> If neither CONFIG_IWMMXT nor CONFIG_VFP are enabled (which is the case,
> for example, in rpc_defconfig), the variable "aux" is set but not used,
> which generates a warning during W=1 builds. Resolve this issue by only
> defining the variable if it will be used.
>
> Signed-off-by: Ethan Nelson-Moore <enelsonmoore@gmail.com>
> ---
>  arch/arm/kernel/signal.c | 4 ++++
>  1 file changed, 4 insertions(+)
>
> diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c
> index 7be9188d83d9..1d015b696a76 100644
> --- a/arch/arm/kernel/signal.c
> +++ b/arch/arm/kernel/signal.c
> @@ -140,7 +140,9 @@ static int restore_vfp_context(char __user **auxp)
>  static int restore_sigframe(struct pt_regs *regs, struct sigframe __user *sf)
>  {
>  	struct sigcontext context;
> +#if defined(CONFIG_IWMMXT) || defined(CONFIG_VFP)
>  	char __user *aux;

__maybe_unused is a common attribute marking local variables that are not
always used. I find it nicer than another layer of #ifdef.

baruch

> +#endif
>  	sigset_t set;
>  	int err;
>  
> @@ -171,6 +173,7 @@ static int restore_sigframe(struct pt_regs *regs, struct sigframe __user *sf)
>  
>  	err |= !valid_user_regs(regs);
>  
> +#if defined(CONFIG_IWMMXT) || defined(CONFIG_VFP)
>  	aux = (char __user *) sf->uc.uc_regspace;
>  #ifdef CONFIG_IWMMXT
>  	if (err == 0)
> @@ -180,6 +183,7 @@ static int restore_sigframe(struct pt_regs *regs, struct sigframe __user *sf)
>  	if (err == 0)
>  		err |= restore_vfp_context(&aux);
>  #endif
> +#endif /* defined(CONFIG_IWMMXT) || defined(CONFIG_VFP) */
>  
>  	return err;
>  }

-- 
                                                     ~. .~   Tk Open Systems
=}------------------------------------------------ooO--U--Ooo------------{=
   - baruch@tkos.co.il - tel: +972.52.368.4656, http://www.tkos.co.il -


^ permalink raw reply


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