* [PATCH v5 0/2] spi: ma35d1-qspi: Add Nuvoton MA35D1 QSPI controller
@ 2026-07-31 2:38 Chi-Wen Weng
2026-07-31 2:38 ` [PATCH v5 1/2] dt-bindings: spi: nuvoton,ma35d1-qspi: Add Nuvoton MA35D1 QSPI Chi-Wen Weng
` (2 more replies)
0 siblings, 3 replies; 14+ messages in thread
From: Chi-Wen Weng @ 2026-07-31 2:38 UTC (permalink / raw)
To: broonie, robh, krzk+dt, conor+dt
Cc: linux-arm-kernel, linux-spi, devicetree, linux-kernel, cwweng,
cwweng.linux
From: Chi-Wen Weng <cwweng@nuvoton.com>
Add devicetree binding and SPI controller driver support for the Quad SPI
controller found in Nuvoton MA35D1 SoCs.
The controller provides two native chip selects and supports standard SPI
and spi-mem operations using single, dual and quad bus widths.
This initial implementation uses 8-bit PIO transfers and supports
per-operation clock configuration, clock and reset handling, and direct
execution of SPI memory operations. DMA and active-high chip select support
are intentionally left out of the initial driver.
Changes in v5:
* Changed dummy cycles to use output direction in both the direct
spi-mem path and the generic SPI transfer path.
Changes in v4:
* Added minimum 1 and default 2 constraints for num-cs in the devicetree
binding.
* Made the driver read num-cs and fall back to two chip selects when the
property is absent.
* Dropped SPI_CS_HIGH support from the initial driver.
* Kept separate chip-select helpers for the SPI core and direct spi-mem
paths because their boolean arguments have different semantics.
* Protected SSCTL register read-modify-write operations with a spinlock.
* Limited spi-mem operations and individual SPI transfers to 4 KiB.
* Limited complete SPI messages to 8 KiB.
* Restricted the initial driver to 8-bit words.
* Added defensive transfer-size validation to keep the PIO polling path
bounded.
* Kept bidirectional data pins in input mode during dual and quad dummy
transfers in the generic spi-mem fallback path.
* Treated zero tx_nbits and rx_nbits values as single-lane transfers in
the generic SPI path.
Changes in v3:
* Fixed the DCO mismatch by using a consistent author and Signed-off-by
address.
* Added spi_controller_mem_caps and enabled per-operation frequency
handling for spi-mem operations.
* Added a short delay after requesting FIFO reset before polling reset
completion.
* Documented that the controller generates one RX FIFO entry for each TX
word in single, dual-output and quad-output modes.
* Added the missing linux/delay.h include.
* Removed unused register bit definitions.
Changes in v2:
* Updated patch subject lines to follow SPI subsystem style.
* Added a commit message to the devicetree binding patch.
* Added ARCH_MA35 || COMPILE_TEST dependency to Kconfig.
* Expanded the Kconfig help text.
* Converted the driver file header to C++-style comments.
* Added reset control handling.
* Added the resets property to the binding.
* Added a num-cs constraint to the binding.
* Removed the flash child node from the binding example.
* Used op->max_freq for spi-mem operations.
* Split low-level chip-select register handling from the SPI core callback.
* Limited spi-mem command opcode length to one byte.
* Forced spi-mem operations to use 8-bit words.
* Avoided driving bidirectional data pins during dummy cycles.
* Drained RX FIFO entries during transmit-only transfers.
* Rejected invalid chip-select numbers.
* Rejected unsupported dual and quad full-duplex generic SPI transfers.
* Fixed checkpatch style issues.
Chi-Wen Weng (2):
dt-bindings: spi: nuvoton,ma35d1-qspi: Add Nuvoton MA35D1 QSPI
spi: ma35d1-qspi: Add Nuvoton MA35D1 QSPI controller support
.../bindings/spi/nuvoton,ma35d1-qspi.yaml | 63 ++
drivers/spi/Kconfig | 10 +
drivers/spi/Makefile | 1 +
drivers/spi/spi-ma35d1-qspi.c | 657 ++++++++++++++++++
4 files changed, 731 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] 14+ messages in thread* [PATCH v5 1/2] dt-bindings: spi: nuvoton,ma35d1-qspi: Add Nuvoton MA35D1 QSPI 2026-07-31 2:38 [PATCH v5 0/2] spi: ma35d1-qspi: Add Nuvoton MA35D1 QSPI controller Chi-Wen Weng @ 2026-07-31 2:38 ` Chi-Wen Weng 2026-07-31 20:28 ` Mark Brown 2026-07-31 2:38 ` [PATCH v5 2/2] spi: ma35d1-qspi: Add Nuvoton MA35D1 QSPI controller support Chi-Wen Weng 2026-07-31 20:29 ` [PATCH v5 0/2] spi: ma35d1-qspi: Add Nuvoton MA35D1 QSPI controller Mark Brown 2 siblings, 1 reply; 14+ messages in thread From: Chi-Wen Weng @ 2026-07-31 2:38 UTC (permalink / raw) To: broonie, robh, krzk+dt, conor+dt Cc: linux-arm-kernel, linux-spi, devicetree, linux-kernel, cwweng, cwweng.linux, Krzysztof Kozlowski From: Chi-Wen Weng <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. Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com> Signed-off-by: Chi-Wen Weng <cwweng@nuvoton.com> --- .../bindings/spi/nuvoton,ma35d1-qspi.yaml | 63 +++++++++++++++++++ 1 file changed, 63 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..377ccf4fb224 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/nuvoton,ma35d1-qspi.yaml @@ -0,0 +1,63 @@ +# 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: /schemas/spi/spi-controller.yaml# + +properties: + compatible: + const: nuvoton,ma35d1-qspi + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + resets: + maxItems: 1 + + num-cs: + minimum: 1 + maximum: 2 + default: 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 [flat|nested] 14+ messages in thread
* Re: [PATCH v5 1/2] dt-bindings: spi: nuvoton,ma35d1-qspi: Add Nuvoton MA35D1 QSPI 2026-07-31 2:38 ` [PATCH v5 1/2] dt-bindings: spi: nuvoton,ma35d1-qspi: Add Nuvoton MA35D1 QSPI Chi-Wen Weng @ 2026-07-31 20:28 ` Mark Brown 2026-08-03 2:06 ` Chi-Wen Weng ` (2 more replies) 0 siblings, 3 replies; 14+ messages in thread From: Mark Brown @ 2026-07-31 20:28 UTC (permalink / raw) To: Chi-Wen Weng Cc: robh, krzk+dt, conor+dt, linux-arm-kernel, linux-spi, devicetree, linux-kernel, cwweng, Krzysztof Kozlowski [-- Attachment #1: Type: text/plain, Size: 497 bytes --] On Fri, Jul 31, 2026 at 10:38:17AM +0800, Chi-Wen Weng wrote: > From: Chi-Wen Weng <cwweng@nuvoton.com> > > Add a devicetree binding for the Quad SPI controller found in > Nuvoton MA35D1 SoCs. This leaves the generic binding's support for GPIO chip selects enabled but the driver doesn't set use_gpio_descriptors so they don't actually work. Either the binding or the driver needs an update. Otherwise things seem mostly OK so I'll apply, please send an incremental patch for this. [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 488 bytes --] ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v5 1/2] dt-bindings: spi: nuvoton,ma35d1-qspi: Add Nuvoton MA35D1 QSPI 2026-07-31 20:28 ` Mark Brown @ 2026-08-03 2:06 ` Chi-Wen Weng 2026-08-03 6:24 ` [PATCH v2] spi: ma35d1-qspi: Enable GPIO chip selects Chi-Wen Weng 2026-08-03 7:49 ` [PATCH v3 0/2] spi: ma35d1-qspi: Enable GPIO chip-select support Chi-Wen Weng 2 siblings, 0 replies; 14+ messages in thread From: Chi-Wen Weng @ 2026-08-03 2:06 UTC (permalink / raw) To: Mark Brown Cc: robh, krzk+dt, conor+dt, linux-arm-kernel, linux-spi, devicetree, linux-kernel, cwweng, Krzysztof Kozlowski Mark Brown 於 2026/8/1 上午 04:28 寫道: > On Fri, Jul 31, 2026 at 10:38:17AM +0800, Chi-Wen Weng wrote: >> From: Chi-Wen Weng <cwweng@nuvoton.com> >> >> Add a devicetree binding for the Quad SPI controller found in >> Nuvoton MA35D1 SoCs. > This leaves the generic binding's support for GPIO chip selects enabled > but the driver doesn't set use_gpio_descriptors so they don't actually > work. Either the binding or the driver needs an update. Otherwise > things seem mostly OK so I'll apply, please send an incremental patch > for this. Hi Mark, Thanks for pointing this out and for applying the series. The current driver only supports the controller's native chip selects, so I will send an incremental binding patch to disallow the cs-gpios property. Best regards, Chi-Wen ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v2] spi: ma35d1-qspi: Enable GPIO chip selects 2026-07-31 20:28 ` Mark Brown 2026-08-03 2:06 ` Chi-Wen Weng @ 2026-08-03 6:24 ` Chi-Wen Weng 2026-08-03 7:49 ` [PATCH v3 0/2] spi: ma35d1-qspi: Enable GPIO chip-select support Chi-Wen Weng 2 siblings, 0 replies; 14+ messages in thread From: Chi-Wen Weng @ 2026-08-03 6:24 UTC (permalink / raw) To: broonie, robh, krzk+dt, conor+dt Cc: linux-arm-kernel, linux-spi, devicetree, linux-kernel, cwweng, cwweng.linux From: Chi-Wen Weng <cwweng@nuvoton.com> The generic SPI controller binding allows GPIO-based chip selects, but the MA35D1 QSPI driver does not currently enable the SPI core's GPIO descriptor handling. Enable GPIO chip-select support and specify that the controller provides two native chip selects. Add a setup callback to reject active-high polarity only for native chip selects, while allowing GPIO chip selects to use the polarity handled by the GPIO subsystem. This keeps the binding capable of describing valid board wiring while preserving the active-low limitation of the controller's native chip selects. Signed-off-by: Chi-Wen Weng <cwweng@nuvoton.com> --- Changes in v2: - Enable GPIO descriptor-based chip-select handling in the driver instead of disallowing cs-gpios in the binding. - Set the maximum number of native chip selects to two. - Reject active-high polarity only for native chip selects. drivers/spi/spi-ma35d1-qspi.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/spi/spi-ma35d1-qspi.c b/drivers/spi/spi-ma35d1-qspi.c index 541d5d72484c..60e082de58f5 100644 --- a/drivers/spi/spi-ma35d1-qspi.c +++ b/drivers/spi/spi-ma35d1-qspi.c @@ -430,13 +430,24 @@ static void nuvoton_qspi_set_cs_level(struct nuvoton_qspi *qspi, spin_unlock_irqrestore(&qspi->ssctl_lock, flags); } +static int nuvoton_qspi_setup(struct spi_device *spi) +{ + if (!spi_get_csgpiod(spi, 0) && (spi->mode & SPI_CS_HIGH)) { + dev_err(&spi->dev, + "active-high native chip select is not supported\n"); + return -EINVAL; + } + + return 0; +} + static void nuvoton_qspi_set_cs(struct spi_device *spi, bool level) { struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller); /* - * The SPI core passes the physical CS level to ->set_cs(). This - * initial driver only supports active-low native chip selects. + * The SPI core passes the physical CS level to ->set_cs(). Native + * chip selects are active low. */ nuvoton_qspi_set_cs_level(qspi, spi_get_chipselect(spi, 0), !level); } @@ -613,10 +624,13 @@ static int nuvoton_qspi_probe(struct platform_device *pdev) num_cs); ctlr->num_chipselect = num_cs; + ctlr->max_native_cs = NUVOTON_QSPI_MAX_NUM_CS; + ctlr->use_gpio_descriptors = true; ctlr->max_transfer_size = nuvoton_qspi_max_transfer_size; ctlr->max_message_size = nuvoton_qspi_max_message_size; ctlr->mem_ops = &nuvoton_qspi_mem_ops; ctlr->mem_caps = &nuvoton_qspi_mem_caps; + ctlr->setup = nuvoton_qspi_setup; ctlr->set_cs = nuvoton_qspi_set_cs; ctlr->transfer_one = nuvoton_qspi_transfer_one; ctlr->bits_per_word_mask = SPI_BPW_MASK(8); -- 2.25.1 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH v3 0/2] spi: ma35d1-qspi: Enable GPIO chip-select support 2026-07-31 20:28 ` Mark Brown 2026-08-03 2:06 ` Chi-Wen Weng 2026-08-03 6:24 ` [PATCH v2] spi: ma35d1-qspi: Enable GPIO chip selects Chi-Wen Weng @ 2026-08-03 7:49 ` Chi-Wen Weng 2026-08-03 7:49 ` [PATCH v3 1/2] dt-bindings: spi: nuvoton,ma35d1-qspi: Allow additional GPIO chip selects Chi-Wen Weng ` (2 more replies) 2 siblings, 3 replies; 14+ messages in thread From: Chi-Wen Weng @ 2026-08-03 7:49 UTC (permalink / raw) To: broonie, robh, krzk+dt, conor+dt Cc: linux-arm-kernel, linux-spi, devicetree, linux-kernel, cwweng, cwweng.linux From: Chi-Wen Weng <cwweng@nuvoton.com> The generic SPI controller binding allows GPIO-based chip selects, but the MA35D1 QSPI driver currently supports only the controller's two native chip selects. This series enables GPIO descriptor-based chip-select handling in the driver and updates the binding so that num-cs can describe the total number of native and GPIO chip selects. Patch 1 removes the native hardware limit from the binding's num-cs property. Patch 2 enables GPIO chip selects in the driver while retaining the hardware limit of two native chip selects. Changes in v3: * Split the binding and driver changes into a two-patch series. * Allow num-cs to exceed the two native chip selects when GPIO chip selects are present. * Remove the driver's upper limit of two from num-cs. * Rename the native chip-select limit for clarity. * Reject out-of-range native chip-select indices in the setup callback. * Retain active-high support for GPIO chip selects only. Changes in v2: * Enable GPIO descriptor-based chip-select handling in the driver instead of disallowing cs-gpios in the binding. * Set the maximum number of native chip selects to two. * Reject active-high polarity only for native chip selects. Chi-Wen Weng (2): dt-bindings: spi: nuvoton,ma35d1-qspi: Allow additional GPIO chip selects spi: ma35d1-qspi: Enable GPIO chip selects .../bindings/spi/nuvoton,ma35d1-qspi.yaml | 1 - drivers/spi/spi-ma35d1-qspi.c | 32 ++++++++++++++++--- 2 files changed, 28 insertions(+), 5 deletions(-) -- 2.25.1 ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v3 1/2] dt-bindings: spi: nuvoton,ma35d1-qspi: Allow additional GPIO chip selects 2026-08-03 7:49 ` [PATCH v3 0/2] spi: ma35d1-qspi: Enable GPIO chip-select support Chi-Wen Weng @ 2026-08-03 7:49 ` Chi-Wen Weng 2026-08-04 7:05 ` Krzysztof Kozlowski 2026-08-03 7:49 ` [PATCH v3 2/2] spi: ma35d1-qspi: Enable " Chi-Wen Weng 2026-08-03 12:30 ` [PATCH v3 0/2] spi: ma35d1-qspi: Enable GPIO chip-select support Mark Brown 2 siblings, 1 reply; 14+ messages in thread From: Chi-Wen Weng @ 2026-08-03 7:49 UTC (permalink / raw) To: broonie, robh, krzk+dt, conor+dt Cc: linux-arm-kernel, linux-spi, devicetree, linux-kernel, cwweng, cwweng.linux From: Chi-Wen Weng <cwweng@nuvoton.com> The MA35D1 QSPI controller provides two native chip selects, but the generic SPI controller binding defines num-cs as the total number of native and GPIO-based chip selects. Remove the maximum value of two from num-cs so that board device trees can describe additional GPIO chip selects while retaining two as the default chip-select count. Signed-off-by: Chi-Wen Weng <cwweng@nuvoton.com> --- Documentation/devicetree/bindings/spi/nuvoton,ma35d1-qspi.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/Documentation/devicetree/bindings/spi/nuvoton,ma35d1-qspi.yaml b/Documentation/devicetree/bindings/spi/nuvoton,ma35d1-qspi.yaml index 377ccf4fb224..f837367e6d5f 100644 --- a/Documentation/devicetree/bindings/spi/nuvoton,ma35d1-qspi.yaml +++ b/Documentation/devicetree/bindings/spi/nuvoton,ma35d1-qspi.yaml @@ -30,7 +30,6 @@ properties: num-cs: minimum: 1 - maximum: 2 default: 2 required: -- 2.25.1 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH v3 1/2] dt-bindings: spi: nuvoton,ma35d1-qspi: Allow additional GPIO chip selects 2026-08-03 7:49 ` [PATCH v3 1/2] dt-bindings: spi: nuvoton,ma35d1-qspi: Allow additional GPIO chip selects Chi-Wen Weng @ 2026-08-04 7:05 ` Krzysztof Kozlowski 2026-08-04 7:33 ` Chi-Wen Weng 0 siblings, 1 reply; 14+ messages in thread From: Krzysztof Kozlowski @ 2026-08-04 7:05 UTC (permalink / raw) To: Chi-Wen Weng Cc: broonie, robh, krzk+dt, conor+dt, linux-arm-kernel, linux-spi, devicetree, linux-kernel, cwweng On Mon, Aug 03, 2026 at 03:49:12PM +0800, Chi-Wen Weng wrote: > From: Chi-Wen Weng <cwweng@nuvoton.com> > > The MA35D1 QSPI controller provides two native chip selects, but the > generic SPI controller binding defines num-cs as the total number of > native and GPIO-based chip selects. > > Remove the maximum value of two from num-cs so that board device trees > can describe additional GPIO chip selects while retaining two as the > default chip-select count. > > Signed-off-by: Chi-Wen Weng <cwweng@nuvoton.com> > --- > Documentation/devicetree/bindings/spi/nuvoton,ma35d1-qspi.yaml | 1 - > 1 file changed, 1 deletion(-) > > diff --git a/Documentation/devicetree/bindings/spi/nuvoton,ma35d1-qspi.yaml b/Documentation/devicetree/bindings/spi/nuvoton,ma35d1-qspi.yaml > index 377ccf4fb224..f837367e6d5f 100644 > --- a/Documentation/devicetree/bindings/spi/nuvoton,ma35d1-qspi.yaml > +++ b/Documentation/devicetree/bindings/spi/nuvoton,ma35d1-qspi.yaml > @@ -30,7 +30,6 @@ properties: > > num-cs: > minimum: 1 > - maximum: 2 > default: 2 Isn't num-cs the number of native chip selects? Best regards, Krzysztof ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v3 1/2] dt-bindings: spi: nuvoton,ma35d1-qspi: Allow additional GPIO chip selects 2026-08-04 7:05 ` Krzysztof Kozlowski @ 2026-08-04 7:33 ` Chi-Wen Weng 0 siblings, 0 replies; 14+ messages in thread From: Chi-Wen Weng @ 2026-08-04 7:33 UTC (permalink / raw) To: Krzysztof Kozlowski Cc: broonie, robh, krzk+dt, conor+dt, linux-arm-kernel, linux-spi, devicetree, linux-kernel, cwweng Krzysztof Kozlowski 於 2026/8/4 下午 03:05 寫道: > On Mon, Aug 03, 2026 at 03:49:12PM +0800, Chi-Wen Weng wrote: >> From: Chi-Wen Weng <cwweng@nuvoton.com> >> >> The MA35D1 QSPI controller provides two native chip selects, but the >> generic SPI controller binding defines num-cs as the total number of >> native and GPIO-based chip selects. >> >> Remove the maximum value of two from num-cs so that board device trees >> can describe additional GPIO chip selects while retaining two as the >> default chip-select count. >> >> Signed-off-by: Chi-Wen Weng <cwweng@nuvoton.com> >> --- >> Documentation/devicetree/bindings/spi/nuvoton,ma35d1-qspi.yaml | 1 - >> 1 file changed, 1 deletion(-) >> >> diff --git a/Documentation/devicetree/bindings/spi/nuvoton,ma35d1-qspi.yaml b/Documentation/devicetree/bindings/spi/nuvoton,ma35d1-qspi.yaml >> index 377ccf4fb224..f837367e6d5f 100644 >> --- a/Documentation/devicetree/bindings/spi/nuvoton,ma35d1-qspi.yaml >> +++ b/Documentation/devicetree/bindings/spi/nuvoton,ma35d1-qspi.yaml >> @@ -30,7 +30,6 @@ properties: >> >> num-cs: >> minimum: 1 >> - maximum: 2 >> default: 2 > Isn't num-cs the number of native chip selects? > > Best regards, > Krzysztof Hi Krzysztof, Yes, you are right. I interpreted num-cs as the final number of native and GPIO chip selects based on the generic binding description. However, the native chip-select count should remain two here, and the SPI core will expand num_chipselect automatically when the cs-gpios array contains additional entries. Therefore, the maximum value of two should remain in the MA35D1 binding. I will drop this binding change. Thanks for the clarification. Best regards, Chi-Wen ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v3 2/2] spi: ma35d1-qspi: Enable GPIO chip selects 2026-08-03 7:49 ` [PATCH v3 0/2] spi: ma35d1-qspi: Enable GPIO chip-select support Chi-Wen Weng 2026-08-03 7:49 ` [PATCH v3 1/2] dt-bindings: spi: nuvoton,ma35d1-qspi: Allow additional GPIO chip selects Chi-Wen Weng @ 2026-08-03 7:49 ` Chi-Wen Weng 2026-08-03 12:30 ` [PATCH v3 0/2] spi: ma35d1-qspi: Enable GPIO chip-select support Mark Brown 2 siblings, 0 replies; 14+ messages in thread From: Chi-Wen Weng @ 2026-08-03 7:49 UTC (permalink / raw) To: broonie, robh, krzk+dt, conor+dt Cc: linux-arm-kernel, linux-spi, devicetree, linux-kernel, cwweng, cwweng.linux From: Chi-Wen Weng <cwweng@nuvoton.com> The generic SPI controller binding allows GPIO-based chip selects, but the MA35D1 QSPI driver does not currently enable the SPI core's GPIO descriptor handling. Enable GPIO chip-select support and distinguish the total number of chip selects from the controller's two native chip selects. Allow num-cs to include additional GPIO chip selects and set max_native_cs to the native hardware limit. Add a setup callback that rejects chip-select indices beyond the native limit when no GPIO descriptor is present. Also reject active-high polarity only for native chip selects, while allowing GPIO chip-select polarity to be handled by the GPIO subsystem. Signed-off-by: Chi-Wen Weng <cwweng@nuvoton.com> --- drivers/spi/spi-ma35d1-qspi.c | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/drivers/spi/spi-ma35d1-qspi.c b/drivers/spi/spi-ma35d1-qspi.c index 541d5d72484c..f893a4160528 100644 --- a/drivers/spi/spi-ma35d1-qspi.c +++ b/drivers/spi/spi-ma35d1-qspi.c @@ -61,7 +61,7 @@ #define NUVOTON_QSPI_STATUS_RXEMPTY_MASK BIT(8) /* Receive FIFO Empty */ #define NUVOTON_QSPI_STATUS_BUSY_MASK BIT(0) /* Busy Status */ -#define NUVOTON_QSPI_MAX_NUM_CS 2 +#define NUVOTON_QSPI_MAX_NATIVE_CS 2 #define NUVOTON_QSPI_DEFAULT_NUM_CS 2 #define NUVOTON_QSPI_DEFAULT_BPW 8 /* Bound PIO operations to avoid long atomic polling loops. */ @@ -430,13 +430,34 @@ static void nuvoton_qspi_set_cs_level(struct nuvoton_qspi *qspi, spin_unlock_irqrestore(&qspi->ssctl_lock, flags); } +static int nuvoton_qspi_setup(struct spi_device *spi) +{ + unsigned int cs = spi_get_chipselect(spi, 0); + + if (spi_get_csgpiod(spi, 0)) + return 0; + + if (cs >= NUVOTON_QSPI_MAX_NATIVE_CS) { + dev_err(&spi->dev, "invalid native chip select %u\n", cs); + return -EINVAL; + } + + if (spi->mode & SPI_CS_HIGH) { + dev_err(&spi->dev, + "active-high native chip select is not supported\n"); + return -EINVAL; + } + + return 0; +} + static void nuvoton_qspi_set_cs(struct spi_device *spi, bool level) { struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller); /* - * The SPI core passes the physical CS level to ->set_cs(). This - * initial driver only supports active-low native chip selects. + * The SPI core passes the physical CS level to ->set_cs(). Native + * chip selects are active low. */ nuvoton_qspi_set_cs_level(qspi, spi_get_chipselect(spi, 0), !level); } @@ -608,15 +629,18 @@ static int nuvoton_qspi_probe(struct platform_device *pdev) if (ret && ret != -EINVAL) return dev_err_probe(dev, ret, "failed to read num-cs\n"); - if (!num_cs || num_cs > NUVOTON_QSPI_MAX_NUM_CS) + if (!num_cs) return dev_err_probe(dev, -EINVAL, "invalid num-cs %u\n", num_cs); ctlr->num_chipselect = num_cs; + ctlr->max_native_cs = NUVOTON_QSPI_MAX_NATIVE_CS; + ctlr->use_gpio_descriptors = true; ctlr->max_transfer_size = nuvoton_qspi_max_transfer_size; ctlr->max_message_size = nuvoton_qspi_max_message_size; ctlr->mem_ops = &nuvoton_qspi_mem_ops; ctlr->mem_caps = &nuvoton_qspi_mem_caps; + ctlr->setup = nuvoton_qspi_setup; ctlr->set_cs = nuvoton_qspi_set_cs; ctlr->transfer_one = nuvoton_qspi_transfer_one; ctlr->bits_per_word_mask = SPI_BPW_MASK(8); -- 2.25.1 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH v3 0/2] spi: ma35d1-qspi: Enable GPIO chip-select support 2026-08-03 7:49 ` [PATCH v3 0/2] spi: ma35d1-qspi: Enable GPIO chip-select support Chi-Wen Weng 2026-08-03 7:49 ` [PATCH v3 1/2] dt-bindings: spi: nuvoton,ma35d1-qspi: Allow additional GPIO chip selects Chi-Wen Weng 2026-08-03 7:49 ` [PATCH v3 2/2] spi: ma35d1-qspi: Enable " Chi-Wen Weng @ 2026-08-03 12:30 ` Mark Brown 2026-08-04 1:57 ` Chi-Wen Weng 2 siblings, 1 reply; 14+ messages in thread From: Mark Brown @ 2026-08-03 12: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: 572 bytes --] On Mon, Aug 03, 2026 at 03:49:11PM +0800, Chi-Wen Weng wrote: > From: Chi-Wen Weng <cwweng@nuvoton.com> > > The generic SPI controller binding allows GPIO-based chip selects, but > the MA35D1 QSPI driver currently supports only the controller's two > native chip selects. Please don't send new patches in reply to old patches or serieses, this makes it harder for both people and tools to understand what is going on - it can bury things in mailboxes and make it difficult to keep track of what current patches are, both for the new patches and the old ones. [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 488 bytes --] ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v3 0/2] spi: ma35d1-qspi: Enable GPIO chip-select support 2026-08-03 12:30 ` [PATCH v3 0/2] spi: ma35d1-qspi: Enable GPIO chip-select support Mark Brown @ 2026-08-04 1:57 ` Chi-Wen Weng 0 siblings, 0 replies; 14+ messages in thread From: Chi-Wen Weng @ 2026-08-04 1:57 UTC (permalink / raw) To: Mark Brown Cc: robh, krzk+dt, conor+dt, linux-arm-kernel, linux-spi, devicetree, linux-kernel, cwweng Mark Brown 於 2026/8/3 下午 08:30 寫道: > On Mon, Aug 03, 2026 at 03:49:11PM +0800, Chi-Wen Weng wrote: >> From: Chi-Wen Weng <cwweng@nuvoton.com> >> >> The generic SPI controller binding allows GPIO-based chip selects, but >> the MA35D1 QSPI driver currently supports only the controller's two >> native chip selects. > Please don't send new patches in reply to old patches or serieses, this > makes it harder for both people and tools to understand what is going > on - it can bury things in mailboxes and make it difficult to keep track > of what current patches are, both for the new patches and the old ones. Hi Mark, Understood. I will send future revisions and new patch series as new top-level threads rather than replying to older patches or series. Sorry for the confusion, and thanks for the clarification. Best regards, Chi-Wen ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v5 2/2] spi: ma35d1-qspi: Add Nuvoton MA35D1 QSPI controller support 2026-07-31 2:38 [PATCH v5 0/2] spi: ma35d1-qspi: Add Nuvoton MA35D1 QSPI controller Chi-Wen Weng 2026-07-31 2:38 ` [PATCH v5 1/2] dt-bindings: spi: nuvoton,ma35d1-qspi: Add Nuvoton MA35D1 QSPI Chi-Wen Weng @ 2026-07-31 2:38 ` Chi-Wen Weng 2026-07-31 20:29 ` [PATCH v5 0/2] spi: ma35d1-qspi: Add Nuvoton MA35D1 QSPI controller Mark Brown 2 siblings, 0 replies; 14+ messages in thread From: Chi-Wen Weng @ 2026-07-31 2:38 UTC (permalink / raw) To: broonie, robh, krzk+dt, conor+dt Cc: linux-arm-kernel, linux-spi, devicetree, linux-kernel, cwweng, cwweng.linux From: Chi-Wen Weng <cwweng@nuvoton.com> Add SPI controller driver support for the Quad SPI controller found in Nuvoton MA35D1 SoCs. The controller provides two native chip selects and supports standard SPI and spi-mem operations, including single-bit full-duplex transfers and dual or quad half-duplex transfers. Implement 8-bit PIO transfers, per-operation clock configuration, clock and reset handling, and direct execution of SPI memory operations. Limit individual transfers to 4 KiB and SPI messages to 8 KiB to keep the PIO polling path bounded. Read the optional num-cs property and fall back to the hardware default of two chip selects when it is not present. Active-high chip select and DMA transfers are not supported by this initial implementation. Signed-off-by: Chi-Wen Weng <cwweng@nuvoton.com> --- drivers/spi/Kconfig | 10 + drivers/spi/Makefile | 1 + drivers/spi/spi-ma35d1-qspi.c | 657 ++++++++++++++++++++++++++++++++++ 3 files changed, 668 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..541d5d72484c --- /dev/null +++ b/drivers/spi/spi-ma35d1-qspi.c @@ -0,0 +1,657 @@ +// 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/delay.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/reset.h> +#include <linux/sizes.h> +#include <linux/spi/spi.h> +#include <linux/spi/spi-mem.h> +#include <linux/spinlock.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_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_SPIENSTS_MASK BIT(15) /* QSPI Enable Status */ +#define NUVOTON_QSPI_STATUS_RXEMPTY_MASK BIT(8) /* Receive FIFO Empty */ +#define NUVOTON_QSPI_STATUS_BUSY_MASK BIT(0) /* Busy Status */ + +#define NUVOTON_QSPI_MAX_NUM_CS 2 +#define NUVOTON_QSPI_DEFAULT_NUM_CS 2 +#define NUVOTON_QSPI_DEFAULT_BPW 8 +/* Bound PIO operations to avoid long atomic polling loops. */ +#define NUVOTON_QSPI_MAX_TRANSFER_SIZE SZ_4K +#define NUVOTON_QSPI_MAX_MESSAGE_SIZE SZ_8K +#define NUVOTON_QSPI_TIMEOUT_US 10000 + +struct nuvoton_qspi { + void __iomem *regs; + struct clk *clk; + struct device *dev; + + /* Protects read-modify-write accesses to the SSCTL register. */ + spinlock_t ssctl_lock; + u32 speed_hz; +}; + +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); + + /* + * Give the controller a short time to latch the FIFO reset request + * before polling the reset status bit. + */ + udelay(1); + + 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) +{ + if (bpw != NUVOTON_QSPI_DEFAULT_BPW) + return -EINVAL; + + nuvoton_qspi_update_bits(qspi, NUVOTON_QSPI_CTL_OFFSET, + NUVOTON_QSPI_CTL_DWIDTH_MASK | + NUVOTON_QSPI_CTL_REORDER_MASK, + FIELD_PREP(NUVOTON_QSPI_CTL_DWIDTH_MASK, 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_byte(const void *txbuf, unsigned int idx) +{ + if (!txbuf) + return 0; + + return ((const u8 *)txbuf)[idx]; +} + +static void nuvoton_qspi_rx_byte(void *rxbuf, unsigned int idx, u32 val) +{ + if (rxbuf) + ((u8 *)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 i; + u32 val; + int ret; + + if (!len) + return 0; + + if (len > NUVOTON_QSPI_MAX_TRANSFER_SIZE) + return -EMSGSIZE; + + ret = nuvoton_qspi_reset_fifo(qspi); + if (ret) { + dev_err(qspi->dev, "FIFO reset timed out\n"); + return ret; + } + + /* + * Use conservative byte-by-byte PIO access. This keeps the initial driver + * simple and avoids relying on FIFO threshold interrupts or DMA support. + * + * The MA35D1 QSPI controller pushes one RX FIFO entry for each TX byte in + * single, dual-output and quad-output modes. Drain RX after every TX byte + * and discard the value for TX-only transfers to avoid RX FIFO overflow. + */ + for (i = 0; i < len; 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_byte(txbuf, i), + 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_byte(rxbuf, i, val); + } + + 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 size_t nuvoton_qspi_max_transfer_size(struct spi_device *spi) +{ + return NUVOTON_QSPI_MAX_TRANSFER_SIZE; +} + +static size_t nuvoton_qspi_max_message_size(struct spi_device *spi) +{ + return NUVOTON_QSPI_MAX_MESSAGE_SIZE; +} + +static int nuvoton_qspi_mem_adjust_op_size(struct spi_mem *mem, + struct spi_mem_op *op) +{ + if (op->data.nbytes > NUVOTON_QSPI_MAX_TRANSFER_SIZE) + op->data.nbytes = NUVOTON_QSPI_MAX_TRANSFER_SIZE; + + return 0; +} + +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) +{ + unsigned long flags; + 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; + } + + spin_lock_irqsave(&qspi->ssctl_lock, flags); + + 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); + + spin_unlock_irqrestore(&qspi->ssctl_lock, flags); +} + +static void nuvoton_qspi_set_cs(struct spi_device *spi, bool level) +{ + struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller); + + /* + * The SPI core passes the physical CS level to ->set_cs(). This + * initial driver only supports active-low native chip selects. + */ + nuvoton_qspi_set_cs_level(qspi, spi_get_chipselect(spi, 0), !level); +} + +static void nuvoton_qspi_mem_set_cs(struct spi_device *spi, bool assert) +{ + struct nuvoton_qspi *qspi = spi_controller_get_devdata(spi->controller); + + /* The direct spi-mem path passes a logical assertion state. */ + 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_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_mem_set_cs(spi, false); + + return ret; +} + +static const struct spi_controller_mem_ops nuvoton_qspi_mem_ops = { + .adjust_op_size = nuvoton_qspi_mem_adjust_op_size, + .supports_op = nuvoton_qspi_mem_supports_op, + .exec_op = nuvoton_qspi_mem_exec_op, +}; + +static const struct spi_controller_mem_caps nuvoton_qspi_mem_caps = { + .per_op_freq = true, +}; + +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); + unsigned int tx_nbits = xfer->tx_nbits ?: SPI_NBITS_SINGLE; + unsigned int rx_nbits = xfer->rx_nbits ?: SPI_NBITS_SINGLE; + 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 && + (tx_nbits != SPI_NBITS_SINGLE || + rx_nbits != SPI_NBITS_SINGLE)) + return -EOPNOTSUPP; + + if (xfer->tx_buf) { + dir = SPI_MEM_DATA_OUT; + + if (tx_nbits == SPI_NBITS_QUAD) + buswidth = 4; + else if (tx_nbits == SPI_NBITS_DUAL) + buswidth = 2; + } else if (xfer->rx_buf) { + if (rx_nbits == SPI_NBITS_QUAD) + buswidth = 4; + else if (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; + u32 num_cs = NUVOTON_QSPI_DEFAULT_NUM_CS; + 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; + spin_lock_init(&qspi->ssctl_lock); + + 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"); + + ret = device_property_read_u32(dev, "num-cs", &num_cs); + if (ret && ret != -EINVAL) + return dev_err_probe(dev, ret, "failed to read num-cs\n"); + + if (!num_cs || num_cs > NUVOTON_QSPI_MAX_NUM_CS) + return dev_err_probe(dev, -EINVAL, "invalid num-cs %u\n", + num_cs); + + ctlr->num_chipselect = num_cs; + ctlr->max_transfer_size = nuvoton_qspi_max_transfer_size; + ctlr->max_message_size = nuvoton_qspi_max_message_size; + ctlr->mem_ops = &nuvoton_qspi_mem_ops; + ctlr->mem_caps = &nuvoton_qspi_mem_caps; + ctlr->set_cs = nuvoton_qspi_set_cs; + ctlr->transfer_one = nuvoton_qspi_transfer_one; + ctlr->bits_per_word_mask = SPI_BPW_MASK(8); + 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] 14+ messages in thread
* Re: [PATCH v5 0/2] spi: ma35d1-qspi: Add Nuvoton MA35D1 QSPI controller 2026-07-31 2:38 [PATCH v5 0/2] spi: ma35d1-qspi: Add Nuvoton MA35D1 QSPI controller Chi-Wen Weng 2026-07-31 2:38 ` [PATCH v5 1/2] dt-bindings: spi: nuvoton,ma35d1-qspi: Add Nuvoton MA35D1 QSPI Chi-Wen Weng 2026-07-31 2:38 ` [PATCH v5 2/2] spi: ma35d1-qspi: Add Nuvoton MA35D1 QSPI controller support Chi-Wen Weng @ 2026-07-31 20:29 ` Mark Brown 2 siblings, 0 replies; 14+ messages in thread From: Mark Brown @ 2026-07-31 20:29 UTC (permalink / raw) To: robh, krzk+dt, conor+dt, Chi-Wen Weng Cc: linux-arm-kernel, linux-spi, devicetree, linux-kernel, cwweng On Fri, 31 Jul 2026 10:38:16 +0800, Chi-Wen Weng wrote: > spi: ma35d1-qspi: Add Nuvoton MA35D1 QSPI controller > > From: Chi-Wen Weng <cwweng@nuvoton.com> > > Add devicetree binding and SPI controller driver support for the Quad SPI > controller found in Nuvoton MA35D1 SoCs. > > [...] Applied to https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git for-7.3 Thanks! [1/2] dt-bindings: spi: nuvoton,ma35d1-qspi: Add Nuvoton MA35D1 QSPI https://git.kernel.org/broonie/spi/c/d458c752cf6c [2/2] spi: ma35d1-qspi: Add Nuvoton MA35D1 QSPI controller support https://git.kernel.org/broonie/spi/c/78b16af159ae All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted. You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed. If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced. Please add any relevant lists and maintainers to the CCs when replying to this mail. Thanks, Mark ^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2026-08-04 7:33 UTC | newest] Thread overview: 14+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-07-31 2:38 [PATCH v5 0/2] spi: ma35d1-qspi: Add Nuvoton MA35D1 QSPI controller Chi-Wen Weng 2026-07-31 2:38 ` [PATCH v5 1/2] dt-bindings: spi: nuvoton,ma35d1-qspi: Add Nuvoton MA35D1 QSPI Chi-Wen Weng 2026-07-31 20:28 ` Mark Brown 2026-08-03 2:06 ` Chi-Wen Weng 2026-08-03 6:24 ` [PATCH v2] spi: ma35d1-qspi: Enable GPIO chip selects Chi-Wen Weng 2026-08-03 7:49 ` [PATCH v3 0/2] spi: ma35d1-qspi: Enable GPIO chip-select support Chi-Wen Weng 2026-08-03 7:49 ` [PATCH v3 1/2] dt-bindings: spi: nuvoton,ma35d1-qspi: Allow additional GPIO chip selects Chi-Wen Weng 2026-08-04 7:05 ` Krzysztof Kozlowski 2026-08-04 7:33 ` Chi-Wen Weng 2026-08-03 7:49 ` [PATCH v3 2/2] spi: ma35d1-qspi: Enable " Chi-Wen Weng 2026-08-03 12:30 ` [PATCH v3 0/2] spi: ma35d1-qspi: Enable GPIO chip-select support Mark Brown 2026-08-04 1:57 ` Chi-Wen Weng 2026-07-31 2:38 ` [PATCH v5 2/2] spi: ma35d1-qspi: Add Nuvoton MA35D1 QSPI controller support Chi-Wen Weng 2026-07-31 20:29 ` [PATCH v5 0/2] spi: ma35d1-qspi: Add Nuvoton MA35D1 QSPI controller Mark Brown
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox