[PATCH v2 3/3] spi: cadence_qspi: Use APB write protection
Ralph Siemsen <[email protected]>
| Newsgroups | gmane.comp.boot-loaders.u-boot |
|---|---|
| Message-ID | <20260814-rzn1-2026-10-spi-v2-3-973d5fa092f2__12178.3320488007$1786732158$gmane$org@linaro.org> |
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 <[email protected]> --- Changes in v2: - patch was split from the series "Renesas RZ/N1 additional drivers" https://lore.kernel.org/u-boot/[email protected]/ - 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