* [PATCH 0/2] Add support for nuvoton ma35d1 qspi controller
@ 2026-06-03 4:35 Chi-Wen Weng
2026-06-03 4:35 ` [PATCH 1/2] dt-bindings: spi: Add for Nuvoton MA35D1 SoC QSPI Controller Chi-Wen Weng
2026-06-03 4:35 ` [PATCH 2/2] spi: Add Nuvoton MA35D1 QSPI controller support Chi-Wen Weng
0 siblings, 2 replies; 13+ messages in thread
From: Chi-Wen Weng @ 2026-06-03 4:35 UTC (permalink / raw)
To: broonie, robh, krzk+dt, conor+dt
Cc: linux-arm-kernel, linux-spi, devicetree, linux-kernel, cwweng,
Chi-Wen Weng
This patch series adds qspi driver for the nuvoton ma35 ARMv8 SoC.
It includes DT binding documentation and the ma35d1 qspi driver.
Chi-Wen Weng (2):
dt-bindings: spi: Add for Nuvoton MA35D1 SoC QSPI Controller
spi: Add Nuvoton MA35D1 QSPI controller support
.../bindings/spi/nuvoton,ma35d1-qspi.yaml | 61 ++
drivers/spi/Kconfig | 6 +
drivers/spi/Makefile | 1 +
drivers/spi/spi-ma35d1-qspi.c | 579 ++++++++++++++++++
4 files changed, 647 insertions(+)
create mode 100644 Documentation/devicetree/bindings/spi/nuvoton,ma35d1-qspi.yaml
create mode 100644 drivers/spi/spi-ma35d1-qspi.c
--
2.25.1
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 1/2] dt-bindings: spi: Add for Nuvoton MA35D1 SoC QSPI Controller
2026-06-03 4:35 [PATCH 0/2] Add support for nuvoton ma35d1 qspi controller Chi-Wen Weng
@ 2026-06-03 4:35 ` Chi-Wen Weng
2026-06-03 4:47 ` sashiko-bot
` (2 more replies)
2026-06-03 4:35 ` [PATCH 2/2] spi: Add Nuvoton MA35D1 QSPI controller support Chi-Wen Weng
1 sibling, 3 replies; 13+ messages in thread
From: Chi-Wen Weng @ 2026-06-03 4:35 UTC (permalink / raw)
To: broonie, robh, krzk+dt, conor+dt
Cc: linux-arm-kernel, linux-spi, devicetree, linux-kernel, cwweng,
Chi-Wen Weng
Signed-off-by: Chi-Wen Weng <cwweng.linux@gmail.com>
---
.../bindings/spi/nuvoton,ma35d1-qspi.yaml | 61 +++++++++++++++++++
1 file changed, 61 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..f7b9cb52d8e5
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/nuvoton,ma35d1-qspi.yaml
@@ -0,0 +1,61 @@
+# 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
+
+required:
+ - compatible
+ - reg
+ - clocks
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/nuvoton,ma35d1-clk.h>
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ spi@40680000 {
+ compatible = "nuvoton,ma35d1-qspi";
+ reg = <0x0 0x40680000 0x0 0x100>;
+ interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk QSPI0_GATE>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ flash@0 {
+ compatible = "jedec,spi-nor";
+ spi-max-frequency = <30000000>;
+ reg = <0>;
+ spi-rx-bus-width = <4>;
+ spi-tx-bus-width = <1>;
+ };
+ };
+ };
+
--
2.25.1
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 2/2] spi: Add Nuvoton MA35D1 QSPI controller support
2026-06-03 4:35 [PATCH 0/2] Add support for nuvoton ma35d1 qspi controller Chi-Wen Weng
2026-06-03 4:35 ` [PATCH 1/2] dt-bindings: spi: Add for Nuvoton MA35D1 SoC QSPI Controller Chi-Wen Weng
@ 2026-06-03 4:35 ` Chi-Wen Weng
2026-06-03 4:58 ` sashiko-bot
2026-06-03 9:30 ` Mark Brown
1 sibling, 2 replies; 13+ messages in thread
From: Chi-Wen Weng @ 2026-06-03 4:35 UTC (permalink / raw)
To: broonie, robh, krzk+dt, conor+dt
Cc: linux-arm-kernel, linux-spi, devicetree, linux-kernel, cwweng,
Chi-Wen Weng
Signed-off-by: Chi-Wen Weng <cwweng.linux@gmail.com>
---
drivers/spi/Kconfig | 6 +
drivers/spi/Makefile | 1 +
drivers/spi/spi-ma35d1-qspi.c | 579 ++++++++++++++++++++++++++++++++++
3 files changed, 586 insertions(+)
create mode 100644 drivers/spi/spi-ma35d1-qspi.c
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index b563f49e2197..1fde55abc36a 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -694,6 +694,12 @@ 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"
+ help
+ This driver provides support for Nuvoton MA35D1
+ QSPI controller in master mode.
+
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..ed1615bf5684 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..119c06464cb0
--- /dev/null
+++ b/drivers/spi/spi-ma35d1-qspi.c
@@ -0,0 +1,579 @@
+// 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/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,
+ struct spi_transfer *xfer)
+{
+ struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller);
+ u32 speed_hz = spi->max_speed_hz;
+ u32 mode = spi->mode & SPI_MODE_X_MASK;
+ u8 bpw = spi->bits_per_word;
+ u32 ctl = 0;
+ int ret;
+
+ if (!bpw)
+ bpw = NUVOTON_QSPI_DEFAULT_BPW;
+
+ if (xfer) {
+ if (xfer->bits_per_word)
+ bpw = xfer->bits_per_word;
+ if (xfer->speed_hz)
+ speed_hz = xfer->speed_hz;
+ }
+
+ 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);
+
+ if (rxbuf) {
+ 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);
+ 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->addr.nbytes > 4)
+ return false;
+
+ return true;
+}
+
+static void nuvoton_qspi_set_cs(struct spi_device *spi, bool enable)
+{
+ struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller);
+ unsigned int cs = spi_get_chipselect(spi, 0);
+ u32 mask;
+ u32 val;
+
+ if (cs == 0)
+ mask = NUVOTON_QSPI_SSCTL_SS0_MASK;
+ else
+ mask = NUVOTON_QSPI_SSCTL_SS1_MASK;
+
+ val = nuvoton_qspi_read(qspi, NUVOTON_QSPI_SSCTL_OFFSET);
+
+ /* SPI core passes enable=true when CS is asserted (typically active-low) */
+ if (enable)
+ val |= mask;
+ else
+ val &= ~mask;
+
+ nuvoton_qspi_write(qspi, val, NUVOTON_QSPI_SSCTL_OFFSET);
+}
+
+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 addr[4];
+ int ret;
+ int i;
+
+ ret = nuvoton_qspi_setup_transfer(spi, NULL);
+ if (ret)
+ return ret;
+
+ nuvoton_qspi_set_cs(spi, true);
+
+ nuvoton_qspi_set_bus_width(qspi, op->cmd.buswidth, SPI_MEM_DATA_OUT);
+ ret = nuvoton_qspi_txrx(qspi, &op->cmd.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_OUT);
+ 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_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);
+ if (ret)
+ return ret;
+
+ 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;
+ 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);
+
+ 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");
+
+ 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 [flat|nested] 13+ messages in thread
* Re: [PATCH 1/2] dt-bindings: spi: Add for Nuvoton MA35D1 SoC QSPI Controller
2026-06-03 4:35 ` [PATCH 1/2] dt-bindings: spi: Add for Nuvoton MA35D1 SoC QSPI Controller Chi-Wen Weng
@ 2026-06-03 4:47 ` sashiko-bot
2026-06-03 9:04 ` Mark Brown
2026-06-03 15:24 ` Conor Dooley
2 siblings, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-06-03 4:47 UTC (permalink / raw)
To: Chi-Wen Weng; +Cc: conor+dt, robh, devicetree
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [High] Sending `op->cmd.opcode` by address fails on Big-Endian architectures and ignores opcode length.
- [High] Failure to unconditionally drain the RX FIFO during TX-only transfers can stall the SPI controller.
- [Medium] Silent mapping of invalid chip select numbers to `SS1`.
- [Medium] Missing `num-cs` constraint in the device tree binding allows invalid configurations.
- [Medium] Missing 'resets' property in the YAML schema properties list.
--
commit 4a6810e7446469e1e2c0b488d225f75f8420f815
Author: Chi-Wen Weng <cwweng.linux@gmail.com>
dt-bindings: spi: Add for Nuvoton MA35D1 SoC QSPI Controller
This series introduces the device tree bindings and driver support for the
Nuvoton MA35D1 Quad SPI controller. The driver implements standard master
mode operations, quad I/O, and SPI memory operations.
> 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 0000000000000..f7b9cb52d8e59
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/spi/nuvoton,ma35d1-qspi.yaml
[ ... ]
> +properties:
> + compatible:
> + const: nuvoton,ma35d1-qspi
> +
> + reg:
> + maxItems: 1
[Severity: Medium]
Should the 'resets' property be included in this schema?
Since the MA35D1 hardware contains reset control lines for QSPI, omitting
'resets' means any board device tree that accurately specifies the hardware's
reset line will fail schema validation due to unevaluatedProperties: false.
[Severity: Medium]
Does this binding need a 'num-cs' constraint?
The MA35D1 controller physically supports a maximum of 2 chip selects. Because
the binding references spi-controller.yaml without capping num-cs, a device
tree specifying num-cs = <4> will pass validation and could cause
out-of-bounds indexing in the driver.
[ ... ]
> diff --git a/drivers/spi/spi-ma35d1-qspi.c b/drivers/spi/spi-ma35d1-qspi.c
> new file mode 100644
> index 0000000000000..119c06464cb01
> --- /dev/null
> +++ b/drivers/spi/spi-ma35d1-qspi.c
[ ... ]
> +static int nuvoton_qspi_txrx(struct nuvoton_qspi *qspi, const void *txbuf,
> + void *rxbuf, unsigned int len)
> +{
[ ... ]
> + 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);
> +
> + if (rxbuf) {
> + 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);
> + nuvoton_qspi_rx_word(rxbuf, i, val, qspi->bits_per_word);
> + }
> + }
[Severity: High]
Can this stall the SPI controller on large TX-only transfers?
Because SPI is a full-duplex shift register, every byte transmitted shifts a
byte into the RX FIFO. By only reading the RX FIFO when rxbuf is not NULL, a
long TX-only operation (like a 256-byte page program) will fill and overflow
the RX FIFO.
On many controllers this stalls the shift register and causes a hang or timeout
in nuvoton_qspi_wait_tx_not_full(). Should the RX FIFO be unconditionally
drained on every cycle?
[ ... ]
> +static void nuvoton_qspi_set_cs(struct spi_device *spi, bool enable)
> +{
> + struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller);
> + unsigned int cs = spi_get_chipselect(spi, 0);
> + u32 mask;
> + u32 val;
> +
> + if (cs == 0)
> + mask = NUVOTON_QSPI_SSCTL_SS0_MASK;
> + else
> + mask = NUVOTON_QSPI_SSCTL_SS1_MASK;
[Severity: Medium]
Is it safe to assume any non-zero chip select maps to SS1?
If an incorrect device tree specifies num-cs = <4>, the SPI core might pass
cs >= 2 here. This branch will silently map all invalid chip selects to
SS1_MASK.
If another peripheral is connected to SS1, could this cause a bus collision?
[ ... ]
> +static int nuvoton_qspi_mem_exec_op(struct spi_mem *mem,
> + const struct spi_mem_op *op)
> +{
[ ... ]
> + nuvoton_qspi_set_cs(spi, true);
> +
> + nuvoton_qspi_set_bus_width(qspi, op->cmd.buswidth, SPI_MEM_DATA_OUT);
> + ret = nuvoton_qspi_txrx(qspi, &op->cmd.opcode, NULL, 1);
[Severity: High]
What happens here on big-endian systems or with multi-byte opcodes?
op->cmd.opcode is a 16-bit integer, but it's passed by address with a
hardcoded length of 1. On big-endian systems, reading a 16-bit integer via a
byte pointer reads the most significant byte (which is 0x00 for a typical
1-byte opcode like 0x03), breaking flash communication.
Additionally, this ignores op->cmd.nbytes, which would break operations that
require more than 1 byte for the opcode.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260603043551.1062112-1-cwweng.linux@gmail.com?part=1
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 2/2] spi: Add Nuvoton MA35D1 QSPI controller support
2026-06-03 4:35 ` [PATCH 2/2] spi: Add Nuvoton MA35D1 QSPI controller support Chi-Wen Weng
@ 2026-06-03 4:58 ` sashiko-bot
2026-06-03 9:30 ` Mark Brown
1 sibling, 0 replies; 13+ messages in thread
From: sashiko-bot @ 2026-06-03 4:58 UTC (permalink / raw)
To: Chi-Wen Weng; +Cc: conor+dt, devicetree, robh
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [High] Passing a pointer to `op->cmd.opcode` (a `u16`) to a buffer-oriented transfer function causes command corruption on Big-Endian systems.
- [High] Failure to drain the RX FIFO during TX-only transfers causes the SPI controller to stall or drop data due to overflow.
- [High] The driver configures the bidirectional data pins as outputs during SPI memory dummy cycles, risking bus contention.
- [Medium] Memory operations (`spi_mem_exec_op`) will fail with `-EINVAL` if the client device configures a 16-bit or 32-bit default word size.
- [Low] The Kconfig symbol `SPI_MA35D1_QSPI` is missing architecture dependencies.
--
commit 6406cd6c82b9bd8e78df612f2de82f09e50e1f89
Author: Chi-Wen Weng <cwweng.linux@gmail.com>
Subject: spi: Add Nuvoton MA35D1 QSPI controller support
This commit adds support for the Nuvoton MA35D1 QSPI controller in master mode.
> diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
> index 957c3e065b839..7e67eebba2427 100644
> --- a/drivers/spi/Kconfig
> +++ b/drivers/spi/Kconfig
> @@ -695,6 +695,12 @@ 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"
[Severity: Low]
Should this config symbol include architecture dependencies, such as
depends on ARCH_MA35 || COMPILE_TEST?
Adding dependencies would help prevent cluttering the configuration menu for
users building kernels for unrelated hardware architectures.
> + help
> + This driver provides support for Nuvoton MA35D1
> + QSPI controller in master mode.
[ ... ]
> diff --git a/drivers/spi/spi-ma35d1-qspi.c b/drivers/spi/spi-ma35d1-qspi.c
> new file mode 100644
> index 0000000000000..119c06464cb01
> --- /dev/null
> +++ b/drivers/spi/spi-ma35d1-qspi.c
[ ... ]
> +static int nuvoton_qspi_txrx(struct nuvoton_qspi *qspi, const void *txbuf,
> + void *rxbuf, unsigned int len)
> +{
[ ... ]
> + 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);
> +
> + if (rxbuf) {
> + 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);
> + nuvoton_qspi_rx_word(rxbuf, i, val, qspi->bits_per_word);
> + }
> + }
[Severity: High]
Does skipping the RX FIFO read when rxbuf is NULL cause the RX FIFO to overflow
during TX-only transfers?
In standard SPI operations, every transmitted word also shifts a word into
the RX FIFO. Because this loop never empties the RX FIFO in transmit-only cases
(like a flash Page Program), could the FIFO rapidly fill up and cause the SPI
controller state machine to stall or drop data?
A dummy read might be needed for every word written, even when rxbuf is not
provided.
[ ... ]
> +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 addr[4];
> + int ret;
> + int i;
> +
> + ret = nuvoton_qspi_setup_transfer(spi, NULL);
[Severity: Medium]
Will this inherit the default word size from the SPI device?
If a client device configures a 16-bit or 32-bit default word size,
bytes_per_word will become 2 or 4. When the driver attempts to transmit the
1-byte command phase below (len = 1), nuvoton_qspi_txrx() will execute its
modulus check and return -EINVAL.
Could this explicitly enforce an 8-bit word size for memory operations instead
of relying on the device's default?
> + if (ret)
> + return ret;
> +
> + nuvoton_qspi_set_cs(spi, true);
> +
> + nuvoton_qspi_set_bus_width(qspi, op->cmd.buswidth, SPI_MEM_DATA_OUT);
> + ret = nuvoton_qspi_txrx(qspi, &op->cmd.opcode, NULL, 1);
[Severity: High]
Could passing the address of op->cmd.opcode cause command corruption on
big-endian systems?
The op->cmd.opcode field is defined as a u16 in struct spi_mem_op. When passed
directly to nuvoton_qspi_txrx() as a byte buffer (const void *txbuf), the
function will read the first byte at the lowest memory address.
On big-endian platforms, this reads the most significant byte (0x00) instead
of the actual command byte, which would break SPI flash communication.
> + if (ret)
> + goto out_deassert_cs;
[ ... ]
> + if (op->dummy.nbytes) {
> + nuvoton_qspi_set_bus_width(qspi, op->dummy.buswidth,
> + SPI_MEM_DATA_OUT);
[Severity: High]
Is it safe to hardcode SPI_MEM_DATA_OUT for the dummy phase?
For Dual or Quad I/O modes (buswidth > 1), setting the data direction to output
configures the SPI data pins to actively drive the bus. However, dummy cycles
in SPI flash protocols provide a high-impedance turnaround time so the flash
chip can prepare to drive the bus for the data input phase.
Actively driving the lines from the host controller during this phase might
lead to direct bus contention.
> + ret = nuvoton_qspi_txrx(qspi, NULL, NULL, op->dummy.nbytes);
> + if (ret)
> + goto out_deassert_cs;
> + }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260603043551.1062112-1-cwweng.linux@gmail.com?part=2
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 1/2] dt-bindings: spi: Add for Nuvoton MA35D1 SoC QSPI Controller
2026-06-03 4:35 ` [PATCH 1/2] dt-bindings: spi: Add for Nuvoton MA35D1 SoC QSPI Controller Chi-Wen Weng
2026-06-03 4:47 ` sashiko-bot
@ 2026-06-03 9:04 ` Mark Brown
2026-06-03 9:29 ` Chi-Wen Weng
2026-06-03 15:24 ` Conor Dooley
2 siblings, 1 reply; 13+ messages in thread
From: Mark Brown @ 2026-06-03 9:04 UTC (permalink / raw)
To: Chi-Wen Weng
Cc: robh, krzk+dt, conor+dt, linux-arm-kernel, linux-spi, devicetree,
linux-kernel, cwweng
[-- Attachment #1: Type: text/plain, Size: 448 bytes --]
On Wed, Jun 03, 2026 at 12:35:50PM +0800, Chi-Wen Weng wrote:
> Signed-off-by: Chi-Wen Weng <cwweng.linux@gmail.com>
Please submit patches using subject lines reflecting the style for the
subsystem, this makes it easier for people to identify relevant patches.
Look at what existing commits in the area you're changing are doing and
make sure your subject lines visually resemble what they're doing.
There's no need to resubmit to fix this alone.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 1/2] dt-bindings: spi: Add for Nuvoton MA35D1 SoC QSPI Controller
2026-06-03 9:04 ` Mark Brown
@ 2026-06-03 9:29 ` Chi-Wen Weng
0 siblings, 0 replies; 13+ messages in thread
From: Chi-Wen Weng @ 2026-06-03 9:29 UTC (permalink / raw)
To: Mark Brown
Cc: robh, krzk+dt, conor+dt, linux-arm-kernel, linux-spi, devicetree,
linux-kernel, cwweng
Hi Mark,
Thanks, I will fix the subject lines in the next version.
Chi-Wen Weng
Mark Brown 於 2026/6/3 下午 05:04 寫道:
> On Wed, Jun 03, 2026 at 12:35:50PM +0800, Chi-Wen Weng wrote:
>> Signed-off-by: Chi-Wen Weng <cwweng.linux@gmail.com>
> Please submit patches using subject lines reflecting the style for the
> subsystem, this makes it easier for people to identify relevant patches.
> Look at what existing commits in the area you're changing are doing and
> make sure your subject lines visually resemble what they're doing.
> There's no need to resubmit to fix this alone.
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 2/2] spi: Add Nuvoton MA35D1 QSPI controller support
2026-06-03 4:35 ` [PATCH 2/2] spi: Add Nuvoton MA35D1 QSPI controller support Chi-Wen Weng
2026-06-03 4:58 ` sashiko-bot
@ 2026-06-03 9:30 ` Mark Brown
2026-06-03 11:03 ` Chi-Wen Weng
1 sibling, 1 reply; 13+ messages in thread
From: Mark Brown @ 2026-06-03 9:30 UTC (permalink / raw)
To: Chi-Wen Weng
Cc: robh, krzk+dt, conor+dt, linux-arm-kernel, linux-spi, devicetree,
linux-kernel, cwweng
[-- Attachment #1: Type: text/plain, Size: 1904 bytes --]
On Wed, Jun 03, 2026 at 12:35:51PM +0800, Chi-Wen Weng wrote:
> +config SPI_MA35D1_QSPI
> + tristate "Nuvoton MA35D1 QSPI Controller"
> + help
> + This driver provides support for Nuvoton MA35D1
> + QSPI controller in master mode.
> +
Other drivers for this SoC seem to have ARCH_MA35 || COMPILE_TEST?
> @@ -0,0 +1,579 @@
> +// 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>
> + */
Please make the entire comment a C++ one so things look more
intentional.
> +static void nuvoton_qspi_set_cs(struct spi_device *spi, bool enable)
> +{
> + struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller);
> + unsigned int cs = spi_get_chipselect(spi, 0);
> + u32 mask;
> + u32 val;
> +
> + if (cs == 0)
> + mask = NUVOTON_QSPI_SSCTL_SS0_MASK;
> + else
> + mask = NUVOTON_QSPI_SSCTL_SS1_MASK;
> +
> + val = nuvoton_qspi_read(qspi, NUVOTON_QSPI_SSCTL_OFFSET);
> +
> + /* SPI core passes enable=true when CS is asserted (typically active-low) */
> + if (enable)
> + val |= mask;
> + else
> + val &= ~mask;
> +
> + nuvoton_qspi_write(qspi, val, NUVOTON_QSPI_SSCTL_OFFSET);
> +}
Note that the core deals with SPI_CS_HIGH, the driver doesn't need to...
> +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 addr[4];
> + int ret;
> + int i;
> +
> + ret = nuvoton_qspi_setup_transfer(spi, NULL);
> + if (ret)
> + return ret;
This uses spi->max_speed_hz but spi_mem configures p->max_freq which you
should use (it might be lower).
> +
> + nuvoton_qspi_set_cs(spi, true);
...except where you're calling in directly at which point the driver
needs to figure this out.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 2/2] spi: Add Nuvoton MA35D1 QSPI controller support
2026-06-03 9:30 ` Mark Brown
@ 2026-06-03 11:03 ` Chi-Wen Weng
0 siblings, 0 replies; 13+ messages in thread
From: Chi-Wen Weng @ 2026-06-03 11:03 UTC (permalink / raw)
To: Mark Brown
Cc: robh, krzk+dt, conor+dt, linux-arm-kernel, linux-spi, devicetree,
linux-kernel, cwweng
Hi Mark,
Thanks for the review.
I will address these in v2:
- add "depends on ARCH_MA35 || COMPILE_TEST" to the Kconfig entry;
- convert the file header to // comments;
- split the low-level CS register update from the SPI core .set_cs()
callback, and make the spi-mem direct CS path handle SPI_CS_HIGH
explicitly;
- use op->max_freq for spi-mem operations instead of spi->max_speed_hz.
I will also fix the subject lines in the next version.
Best regards,
Chi-Wen Weng
Mark Brown 於 2026/6/3 下午 05:30 寫道:
> On Wed, Jun 03, 2026 at 12:35:51PM +0800, Chi-Wen Weng wrote:
>
>> +config SPI_MA35D1_QSPI
>> + tristate "Nuvoton MA35D1 QSPI Controller"
>> + help
>> + This driver provides support for Nuvoton MA35D1
>> + QSPI controller in master mode.
>> +
> Other drivers for this SoC seem to have ARCH_MA35 || COMPILE_TEST?
>
>> @@ -0,0 +1,579 @@
>> +// 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>
>> + */
> Please make the entire comment a C++ one so things look more
> intentional.
>
>> +static void nuvoton_qspi_set_cs(struct spi_device *spi, bool enable)
>> +{
>> + struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller);
>> + unsigned int cs = spi_get_chipselect(spi, 0);
>> + u32 mask;
>> + u32 val;
>> +
>> + if (cs == 0)
>> + mask = NUVOTON_QSPI_SSCTL_SS0_MASK;
>> + else
>> + mask = NUVOTON_QSPI_SSCTL_SS1_MASK;
>> +
>> + val = nuvoton_qspi_read(qspi, NUVOTON_QSPI_SSCTL_OFFSET);
>> +
>> + /* SPI core passes enable=true when CS is asserted (typically active-low) */
>> + if (enable)
>> + val |= mask;
>> + else
>> + val &= ~mask;
>> +
>> + nuvoton_qspi_write(qspi, val, NUVOTON_QSPI_SSCTL_OFFSET);
>> +}
> Note that the core deals with SPI_CS_HIGH, the driver doesn't need to...
>
>> +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 addr[4];
>> + int ret;
>> + int i;
>> +
>> + ret = nuvoton_qspi_setup_transfer(spi, NULL);
>> + if (ret)
>> + return ret;
> This uses spi->max_speed_hz but spi_mem configures p->max_freq which you
> should use (it might be lower).
>
>> +
>> + nuvoton_qspi_set_cs(spi, true);
> ...except where you're calling in directly at which point the driver
> needs to figure this out.
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 1/2] dt-bindings: spi: Add for Nuvoton MA35D1 SoC QSPI Controller
2026-06-03 4:35 ` [PATCH 1/2] dt-bindings: spi: Add for Nuvoton MA35D1 SoC QSPI Controller Chi-Wen Weng
2026-06-03 4:47 ` sashiko-bot
2026-06-03 9:04 ` Mark Brown
@ 2026-06-03 15:24 ` Conor Dooley
2026-06-04 7:07 ` Chi-Wen Weng
2 siblings, 1 reply; 13+ messages in thread
From: Conor Dooley @ 2026-06-03 15:24 UTC (permalink / raw)
To: Chi-Wen Weng
Cc: broonie, robh, krzk+dt, conor+dt, linux-arm-kernel, linux-spi,
devicetree, linux-kernel, cwweng
[-- Attachment #1: Type: text/plain, Size: 2441 bytes --]
On Wed, Jun 03, 2026 at 12:35:50PM +0800, Chi-Wen Weng wrote:
> Signed-off-by: Chi-Wen Weng <cwweng.linux@gmail.com>
Missing commit message for one, but why can't your Nuvoton mail be used
here?
Sashiko had two comments about resets and num-cs that looked valid.
> ---
> .../bindings/spi/nuvoton,ma35d1-qspi.yaml | 61 +++++++++++++++++++
> 1 file changed, 61 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..f7b9cb52d8e5
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/spi/nuvoton,ma35d1-qspi.yaml
> @@ -0,0 +1,61 @@
> +# 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
> +
> +required:
> + - compatible
> + - reg
> + - clocks
> +
> +unevaluatedProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/interrupt-controller/arm-gic.h>
> + #include <dt-bindings/clock/nuvoton,ma35d1-clk.h>
> +
> + soc {
> + #address-cells = <2>;
> + #size-cells = <2>;
> +
> + spi@40680000 {
> + compatible = "nuvoton,ma35d1-qspi";
> + reg = <0x0 0x40680000 0x0 0x100>;
> + interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>;
> + clocks = <&clk QSPI0_GATE>;
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + flash@0 {
Drop this flash node, it serves no purpose here.
pw-bot: changes-requested
Thanks,
Conor.
> + compatible = "jedec,spi-nor";
> + spi-max-frequency = <30000000>;
> + reg = <0>;
> + spi-rx-bus-width = <4>;
> + spi-tx-bus-width = <1>;
> + };
> + };
> + };
> +
> --
> 2.25.1
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 1/2] dt-bindings: spi: Add for Nuvoton MA35D1 SoC QSPI Controller
2026-06-03 15:24 ` Conor Dooley
@ 2026-06-04 7:07 ` Chi-Wen Weng
2026-06-04 8:55 ` Conor Dooley
0 siblings, 1 reply; 13+ messages in thread
From: Chi-Wen Weng @ 2026-06-04 7:07 UTC (permalink / raw)
To: Conor Dooley
Cc: broonie, robh, krzk+dt, conor+dt, linux-arm-kernel, linux-spi,
devicetree, linux-kernel, cwweng
Hi Conor,
Thanks for the review.
> Missing commit message for one, but why can't your Nuvoton mail be used
> here?
I apologize for the missing commit message; I will add a proper
description in v2.
Regarding the email address, my Nuvoton mail adds a corporate
confidentiality disclaimer to outgoing
external mail, so I use my personal address for sending kernel patches.
> Sashiko had two comments about resets and num-cs that looked valid.
Noted. I will add the `num-cs` and `resets` properties in v2.
> Drop this flash node, it serves no purpose here.
Understood, I will remove the flash child node from the example in v2.
Thanks,
Chi-Wen Weng
Conor Dooley 於 2026/6/3 下午 11:24 寫道:
> On Wed, Jun 03, 2026 at 12:35:50PM +0800, Chi-Wen Weng wrote:
>> Signed-off-by: Chi-Wen Weng <cwweng.linux@gmail.com>
> Missing commit message for one, but why can't your Nuvoton mail be used
> here?
>
> Sashiko had two comments about resets and num-cs that looked valid.
>
>> ---
>> .../bindings/spi/nuvoton,ma35d1-qspi.yaml | 61 +++++++++++++++++++
>> 1 file changed, 61 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..f7b9cb52d8e5
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/spi/nuvoton,ma35d1-qspi.yaml
>> @@ -0,0 +1,61 @@
>> +# 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
>> +
>> +required:
>> + - compatible
>> + - reg
>> + - clocks
>> +
>> +unevaluatedProperties: false
>> +
>> +examples:
>> + - |
>> + #include <dt-bindings/interrupt-controller/arm-gic.h>
>> + #include <dt-bindings/clock/nuvoton,ma35d1-clk.h>
>> +
>> + soc {
>> + #address-cells = <2>;
>> + #size-cells = <2>;
>> +
>> + spi@40680000 {
>> + compatible = "nuvoton,ma35d1-qspi";
>> + reg = <0x0 0x40680000 0x0 0x100>;
>> + interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>;
>> + clocks = <&clk QSPI0_GATE>;
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> +
>> + flash@0 {
> Drop this flash node, it serves no purpose here.
>
> pw-bot: changes-requested
>
> Thanks,
> Conor.
>
>> + compatible = "jedec,spi-nor";
>> + spi-max-frequency = <30000000>;
>> + reg = <0>;
>> + spi-rx-bus-width = <4>;
>> + spi-tx-bus-width = <1>;
>> + };
>> + };
>> + };
>> +
>> --
>> 2.25.1
>>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 1/2] dt-bindings: spi: Add for Nuvoton MA35D1 SoC QSPI Controller
2026-06-04 7:07 ` Chi-Wen Weng
@ 2026-06-04 8:55 ` Conor Dooley
2026-06-04 9:25 ` Chi-Wen Weng
0 siblings, 1 reply; 13+ messages in thread
From: Conor Dooley @ 2026-06-04 8:55 UTC (permalink / raw)
To: Chi-Wen Weng
Cc: broonie, robh, krzk+dt, conor+dt, linux-arm-kernel, linux-spi,
devicetree, linux-kernel, cwweng
[-- Attachment #1: Type: text/plain, Size: 787 bytes --]
On Thu, Jun 04, 2026 at 03:07:31PM +0800, Chi-Wen Weng wrote:
> Hi Conor,
>
> Thanks for the review.
>
> > Missing commit message for one, but why can't your Nuvoton mail be used
> > here?
>
> I apologize for the missing commit message; I will add a proper description
> in v2.
>
> Regarding the email address, my Nuvoton mail adds a corporate
> confidentiality disclaimer to outgoing
> external mail, so I use my personal address for sending kernel patches.
This prevents you sending with your work email account, but you can still
set your commit author to your Nuvoton address FWIW.
> Conor Dooley 於 2026/6/3 下午 11:24 寫道:
> > On Wed, Jun 03, 2026 at 12:35:50PM +0800, Chi-Wen Weng wrote:
> > > Signed-off-by: Chi-Wen Weng <cwweng.linux@gmail.com>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 1/2] dt-bindings: spi: Add for Nuvoton MA35D1 SoC QSPI Controller
2026-06-04 8:55 ` Conor Dooley
@ 2026-06-04 9:25 ` Chi-Wen Weng
0 siblings, 0 replies; 13+ messages in thread
From: Chi-Wen Weng @ 2026-06-04 9:25 UTC (permalink / raw)
To: Conor Dooley
Cc: broonie, robh, krzk+dt, conor+dt, linux-arm-kernel, linux-spi,
devicetree, linux-kernel, cwweng
Hi Conor,
Thanks for the clarification.
I will keep using my personal mail account for sending the patches to
avoid the corporate confidentiality footer, but I will set the commit
author and Signed-off-by to my Nuvoton address in v2.
Best regards,
Chi-Wen
Conor Dooley 於 2026/6/4 下午 04:55 寫道:
> On Thu, Jun 04, 2026 at 03:07:31PM +0800, Chi-Wen Weng wrote:
>> Hi Conor,
>>
>> Thanks for the review.
>>
>>> Missing commit message for one, but why can't your Nuvoton mail be used
>>> here?
>> I apologize for the missing commit message; I will add a proper description
>> in v2.
>>
>> Regarding the email address, my Nuvoton mail adds a corporate
>> confidentiality disclaimer to outgoing
>> external mail, so I use my personal address for sending kernel patches.
> This prevents you sending with your work email account, but you can still
> set your commit author to your Nuvoton address FWIW.
>
>> Conor Dooley 於 2026/6/3 下午 11:24 寫道:
>>> On Wed, Jun 03, 2026 at 12:35:50PM +0800, Chi-Wen Weng wrote:
>>>> Signed-off-by: Chi-Wen Weng <cwweng.linux@gmail.com>
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2026-06-04 9:25 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-03 4:35 [PATCH 0/2] Add support for nuvoton ma35d1 qspi controller Chi-Wen Weng
2026-06-03 4:35 ` [PATCH 1/2] dt-bindings: spi: Add for Nuvoton MA35D1 SoC QSPI Controller Chi-Wen Weng
2026-06-03 4:47 ` sashiko-bot
2026-06-03 9:04 ` Mark Brown
2026-06-03 9:29 ` Chi-Wen Weng
2026-06-03 15:24 ` Conor Dooley
2026-06-04 7:07 ` Chi-Wen Weng
2026-06-04 8:55 ` Conor Dooley
2026-06-04 9:25 ` Chi-Wen Weng
2026-06-03 4:35 ` [PATCH 2/2] spi: Add Nuvoton MA35D1 QSPI controller support Chi-Wen Weng
2026-06-03 4:58 ` sashiko-bot
2026-06-03 9:30 ` Mark Brown
2026-06-03 11:03 ` Chi-Wen Weng
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox