* [PATCH v2 1/3] spi: cadence_qspi: enable all clocks
2026-08-14 18:28 [PATCH v2 0/3] spi: cadence_qspi: clock enable, compatible, write protect Ralph Siemsen
@ 2026-08-14 18:28 ` Ralph Siemsen
2026-08-14 22:03 ` Marek Vasut via U-Boot
2026-08-14 18:28 ` [PATCH v2 2/3] spi: cadence_qspi: add RZ/N1 compatible Ralph Siemsen
2026-08-14 18:28 ` [PATCH v2 3/3] spi: cadence_qspi: Use APB write protection Ralph Siemsen
2 siblings, 1 reply; 7+ messages in thread
From: Ralph Siemsen @ 2026-08-14 18:28 UTC (permalink / raw)
To: u-boot
Cc: Tom Rini, Michal Simek, Marek Vasut, Padmarao Begari,
Anshul Dalal, Neha Malcom Francis, Johan Jonker, Quentin Schulz,
Patrice Chotard, Ralph Siemsen
On certain devices (such as Renesas RZ/N1) it is necessary to
enable the clock(s) before accessing the QSPI controller registers.
As there can be multiple clocks, use clk_get_bulk() to obtain clock(s)
and clk_enable_bulk() to turn them on. Warn if clk_enable_bulk() fails,
but keep going so as not to break existing users.
As before, it is assumed that the first clock is the reference clock,
so the order in the devicetree matters.
Signed-off-by: Ralph Siemsen <ralph.siemsen@linaro.org>
---
Changes in v2:
- patch was split from the series "Renesas RZ/N1 additional drivers"
https://lore.kernel.org/u-boot/20260731-rzn1-2026-07-v1-2-af2ce80db9d8@linaro.org/
- move clock enable before clk_get_rate()
- handle multiple clocks using clk_get_bulk() and clk_enable_bulk()
---
drivers/spi/cadence_qspi.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/drivers/spi/cadence_qspi.c b/drivers/spi/cadence_qspi.c
index 984d4a39ded..0f70493dea4 100644
--- a/drivers/spi/cadence_qspi.c
+++ b/drivers/spi/cadence_qspi.c
@@ -208,7 +208,7 @@ static int cadence_spi_probe(struct udevice *bus)
{
struct cadence_spi_plat *plat = dev_get_plat(bus);
struct cadence_spi_priv *priv = dev_get_priv(bus);
- struct clk clk;
+ struct clk_bulk clks;
int ret;
priv->regbase = plat->regbase;
@@ -240,7 +240,7 @@ static int cadence_spi_probe(struct udevice *bus)
ZYNQMP_PM_REQUEST_ACK_NO, 0, 0, NULL);
if (priv->ref_clk_hz == 0) {
- ret = clk_get_by_index(bus, 0, &clk);
+ ret = clk_get_bulk(bus, &clks);
if (ret) {
#ifdef CONFIG_HAS_CQSPI_REF_CLK
priv->ref_clk_hz = CONFIG_CQSPI_REF_CLK;
@@ -250,7 +250,11 @@ static int cadence_spi_probe(struct udevice *bus)
return ret;
#endif
} else {
- priv->ref_clk_hz = clk_get_rate(&clk);
+ ret = clk_enable_bulk(&clks);
+ if (ret)
+ dev_warn(bus, "failed clk_enable_bulk\n");
+
+ priv->ref_clk_hz = clk_get_rate(&clks.clks[0]);
if (IS_ERR_VALUE(priv->ref_clk_hz))
return priv->ref_clk_hz;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* Re: [PATCH v2 1/3] spi: cadence_qspi: enable all clocks
2026-08-14 18:28 ` [PATCH v2 1/3] spi: cadence_qspi: enable all clocks Ralph Siemsen
@ 2026-08-14 22:03 ` Marek Vasut via U-Boot
2026-08-15 14:09 ` Ralph Siemsen
0 siblings, 1 reply; 7+ messages in thread
From: Marek Vasut via U-Boot @ 2026-08-14 22:03 UTC (permalink / raw)
To: Ralph Siemsen, u-boot
Cc: Tom Rini, Michal Simek, Marek Vasut, Padmarao Begari,
Anshul Dalal, Neha Malcom Francis, Johan Jonker, Quentin Schulz,
Patrice Chotard
On 8/14/26 8:28 PM, Ralph Siemsen wrote:
[...]
> @@ -250,7 +250,11 @@ static int cadence_spi_probe(struct udevice *bus)
> return ret;
> #endif
> } else {
> - priv->ref_clk_hz = clk_get_rate(&clk);
> + ret = clk_enable_bulk(&clks);
Some sort of clk_disable_bulk() seems to be missing.
Fail path which disables the clock seems to be missing too in this function.
> + if (ret)
> + dev_warn(bus, "failed clk_enable_bulk\n");
> +
> + priv->ref_clk_hz = clk_get_rate(&clks.clks[0]);
Can the refclock be second or later (not first) clock in the list of
multiple clock in DT ? If yes, then the clk_get_rate[0] will return
bogus rate for another clock.
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [PATCH v2 1/3] spi: cadence_qspi: enable all clocks
2026-08-14 22:03 ` Marek Vasut via U-Boot
@ 2026-08-15 14:09 ` Ralph Siemsen
0 siblings, 0 replies; 7+ messages in thread
From: Ralph Siemsen @ 2026-08-15 14:09 UTC (permalink / raw)
To: Marek Vasut
Cc: u-boot, Tom Rini, Michal Simek, Marek Vasut, Padmarao Begari,
Anshul Dalal, Neha Malcom Francis, Johan Jonker, Quentin Schulz,
Patrice Chotard
On Sat, Aug 15, 2026 at 12:03:55AM +0200, Marek Vasut wrote:
>On 8/14/26 8:28 PM, Ralph Siemsen wrote:
>
>[...]
>
>>@@ -250,7 +250,11 @@ static int cadence_spi_probe(struct udevice *bus)
>> return ret;
>> #endif
>> } else {
>>- priv->ref_clk_hz = clk_get_rate(&clk);
>>+ ret = clk_enable_bulk(&clks);
>
>Some sort of clk_disable_bulk() seems to be missing.
>
>Fail path which disables the clock seems to be missing too in this function.
Ack for both.
>>+ if (ret)
>>+ dev_warn(bus, "failed clk_enable_bulk\n");
>>+
>>+ priv->ref_clk_hz = clk_get_rate(&clks.clks[0]);
>
>Can the refclock be second or later (not first) clock in the list of
>multiple clock in DT ? If yes, then the clk_get_rate[0] will return
>bogus rate for another clock.
The right solution is to get the clock by name. But once again we have a
pile of existing boards which may not have the correct name ("ref") in
their device tree. So I went with the simple option of continuing to
assume that the first clock is the refclock.
Ralph
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 2/3] spi: cadence_qspi: add RZ/N1 compatible
2026-08-14 18:28 [PATCH v2 0/3] spi: cadence_qspi: clock enable, compatible, write protect Ralph Siemsen
2026-08-14 18:28 ` [PATCH v2 1/3] spi: cadence_qspi: enable all clocks Ralph Siemsen
@ 2026-08-14 18:28 ` Ralph Siemsen
2026-08-14 18:28 ` [PATCH v2 3/3] spi: cadence_qspi: Use APB write protection Ralph Siemsen
2 siblings, 0 replies; 7+ messages in thread
From: Ralph Siemsen @ 2026-08-14 18:28 UTC (permalink / raw)
To: u-boot
Cc: Tom Rini, Michal Simek, Marek Vasut, Padmarao Begari,
Anshul Dalal, Neha Malcom Francis, Johan Jonker, Quentin Schulz,
Patrice Chotard, Ralph Siemsen
The Renesas RZ/N1 contains a QSPI controller based on Cadence IP.
Add its compatible string as per linux commit
2b97f5cd1a95 ("spi: dt-bindings: cdns,qspi-nor: Add Renesas
RZ/N1D400 to the list")
Signed-off-by: Ralph Siemsen <ralph.siemsen@linaro.org>
---
drivers/spi/cadence_qspi.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/spi/cadence_qspi.c b/drivers/spi/cadence_qspi.c
index 0f70493dea4..12ac64c9e08 100644
--- a/drivers/spi/cadence_qspi.c
+++ b/drivers/spi/cadence_qspi.c
@@ -505,6 +505,9 @@ static const struct udevice_id cadence_spi_ids[] = {
.compatible = "xlnx,versal-ospi-1.0",
.data = (ulong)&cdns_xilinx_qspi,
},
+ {
+ .compatible = "renesas,rzn1-qspi",
+ },
{ }
};
--
2.55.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH v2 3/3] spi: cadence_qspi: Use APB write protection
2026-08-14 18:28 [PATCH v2 0/3] spi: cadence_qspi: clock enable, compatible, write protect Ralph Siemsen
2026-08-14 18:28 ` [PATCH v2 1/3] spi: cadence_qspi: enable all clocks Ralph Siemsen
2026-08-14 18:28 ` [PATCH v2 2/3] spi: cadence_qspi: add RZ/N1 compatible Ralph Siemsen
@ 2026-08-14 18:28 ` Ralph Siemsen
2026-08-14 22:04 ` Marek Vasut via U-Boot
2 siblings, 1 reply; 7+ messages in thread
From: Ralph Siemsen @ 2026-08-14 18:28 UTC (permalink / raw)
To: u-boot
Cc: Tom Rini, Michal Simek, Marek Vasut, Padmarao Begari,
Anshul Dalal, Neha Malcom Francis, Johan Jonker, Quentin Schulz,
Patrice Chotard, Ralph Siemsen
Add an option to make use of hardware write protection in the QSPI
controller. This helps prevent flash corruption due to stray writes.
No stray writes have been observed, this is defensive code.
When enabled, the controller is configured to block writes to flash. Due
to known errata [1] the full 32-bit address range is protected, even
though the flash occupies only a portion.
The protection gets temporarily lifted while intentionally writing
to flash, for example using "sf write" command.
[1] https://www.renesas.com/en/document/tcu/qspi-contoller-issue
Signed-off-by: Ralph Siemsen <ralph.siemsen@linaro.org>
---
Changes in v2:
- patch was split from the series "Renesas RZ/N1 additional drivers"
https://lore.kernel.org/u-boot/20260731-rzn1-2026-07-v1-3-af2ce80db9d8@linaro.org/
- combine the enable()/disable() into a single function
- move the IS_ENABLED() check inside the function
- reword the commit message
- considering to drop this feature, since Linux disables it
---
drivers/spi/Kconfig | 11 +++++++++++
drivers/spi/cadence_qspi.h | 6 ++++++
drivers/spi/cadence_qspi_apb.c | 23 +++++++++++++++++++++++
3 files changed, 40 insertions(+)
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 007ad5e7733..19f8d48507b 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -168,6 +168,17 @@ config CADENCE_QSPI
used to access the SPI NOR flash on platforms embedding this
Cadence IP core.
+config CADENCE_QSPI_WRITE_PROTECT
+ bool "Cadence QSPI write protection"
+ depends on CADENCE_QSPI
+ help
+ Enable hardware write protection in the QSPI controller. Helps avoid
+ flash corruption due to stray writes. The direct access region is kept
+ locked by default, and only gets unlocked during "sf write".
+
+ This is similar to SYS_FLASH_PROTECTION however blocking is done by
+ the controller, rather than by the flash device.
+
config HAS_CQSPI_REF_CLK
bool "Cadence QSPI static reference clock"
depends on CADENCE_QSPI
diff --git a/drivers/spi/cadence_qspi.h b/drivers/spi/cadence_qspi.h
index 1e9081c2d17..6b3ed46977a 100644
--- a/drivers/spi/cadence_qspi.h
+++ b/drivers/spi/cadence_qspi.h
@@ -119,6 +119,12 @@
#define CQSPI_REG_IRQSTATUS 0x40
#define CQSPI_REG_IRQMASK 0x44
+#define CQSPI_REG_LOWER_WRITE_PROTECT 0x50
+#define CQSPI_REG_UPPER_WRITE_PROTECT 0x54
+
+#define CQSPI_REG_WRITE_PROTECT_CTRL 0x58
+#define CQSPI_REG_WRPROT_ENABLE BIT(1)
+
#define CQSPI_REG_INDIRECTRD 0x60
#define CQSPI_REG_INDIRECTRD_START BIT(0)
#define CQSPI_REG_INDIRECTRD_CANCEL BIT(1)
diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c
index 0d4bc685f5d..fc4649916ad 100644
--- a/drivers/spi/cadence_qspi_apb.c
+++ b/drivers/spi/cadence_qspi_apb.c
@@ -325,6 +325,15 @@ void cadence_qspi_apb_delay(void *reg_base,
cadence_qspi_apb_controller_enable(reg_base);
}
+static void cadence_qspi_apb_write_protect(void *reg_base, bool enable)
+{
+ if (!IS_ENABLED(CONFIG_CADENCE_QSPI_WRITE_PROTECT))
+ return;
+
+ writel(enable ? CQSPI_REG_WRPROT_ENABLE : 0,
+ reg_base + CQSPI_REG_WRITE_PROTECT_CTRL);
+}
+
void cadence_qspi_apb_controller_init(struct cadence_spi_priv *priv)
{
unsigned reg;
@@ -346,6 +355,18 @@ void cadence_qspi_apb_controller_init(struct cadence_spi_priv *priv)
/* Indirect mode configurations */
writel(priv->fifo_depth / 2, priv->regbase + CQSPI_REG_SRAMPARTITION);
+ if (IS_ENABLED(CONFIG_CADENCE_QSPI_WRITE_PROTECT)) {
+ /*
+ * Enable AHB write protection, to reduce the chance of corrupting
+ * flash memory due to a stray write within the DAC region.
+ * Protect the entire address range, regardless of the flash size.
+ * During "sf write" the protection will be temporarily disabled.
+ */
+ writel(0, priv->regbase + CQSPI_REG_LOWER_WRITE_PROTECT);
+ writel(~0, priv->regbase + CQSPI_REG_UPPER_WRITE_PROTECT);
+ cadence_qspi_apb_write_protect(priv->regbase, true);
+ }
+
/* Disable all interrupts */
writel(0, priv->regbase + CQSPI_REG_IRQMASK);
@@ -955,7 +976,9 @@ int cadence_qspi_apb_write_execute(struct cadence_spi_priv *priv,
*/
cadence_qspi_apb_enable_linear_mode(true);
if (!priv->dtr && priv->use_dac_mode && (to + len < priv->ahbsize)) {
+ cadence_qspi_apb_write_protect(priv->regbase, false);
memcpy_toio(priv->ahbbase + to, buf, len);
+ cadence_qspi_apb_write_protect(priv->regbase, true);
if (!cadence_qspi_wait_idle(priv->regbase))
return -EIO;
return 0;
--
2.55.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* Re: [PATCH v2 3/3] spi: cadence_qspi: Use APB write protection
2026-08-14 18:28 ` [PATCH v2 3/3] spi: cadence_qspi: Use APB write protection Ralph Siemsen
@ 2026-08-14 22:04 ` Marek Vasut via U-Boot
0 siblings, 0 replies; 7+ messages in thread
From: Marek Vasut via U-Boot @ 2026-08-14 22:04 UTC (permalink / raw)
To: Ralph Siemsen, u-boot
Cc: Tom Rini, Michal Simek, Marek Vasut, Padmarao Begari,
Anshul Dalal, Neha Malcom Francis, Johan Jonker, Quentin Schulz,
Patrice Chotard
On 8/14/26 8:28 PM, Ralph Siemsen wrote:
> Add an option to make use of hardware write protection in the QSPI
> controller. This helps prevent flash corruption due to stray writes.
> No stray writes have been observed, this is defensive code.
>
> When enabled, the controller is configured to block writes to flash. Due
> to known errata [1] the full 32-bit address range is protected, even
> though the flash occupies only a portion.
>
> The protection gets temporarily lifted while intentionally writing
> to flash, for example using "sf write" command.
>
> [1] https://www.renesas.com/en/document/tcu/qspi-contoller-issue
>
> Signed-off-by: Ralph Siemsen <ralph.siemsen@linaro.org>
Reviewed-by: Marek Vasut <marek.vasut+renesas@mailbox.org>
^ permalink raw reply [flat|nested] 7+ messages in thread