Prevent random memory writes from corrupting flash memory. When using the Direct Access Controller (DAC), the QSPI flash appears as a memory-mapped device. A stray write within the DAC region can corrupt flash contents.
To prevent this, enable the hardware write protection feature. Due to known errata [1] of the QSPI controller, the entire address range is protected, and only gets unlocked during intentional QSPI writes. [1] https://www.renesas.com/en/document/tcu/qspi-contoller-issue Signed-off-by: Ralph Siemsen <[email protected]> --- drivers/spi/Kconfig | 11 +++++++++++ drivers/spi/cadence_qspi.h | 6 ++++++ drivers/spi/cadence_qspi_apb.c | 27 +++++++++++++++++++++++++++ 3 files changed, 44 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..353b57a46af 100644 --- a/drivers/spi/cadence_qspi_apb.c +++ b/drivers/spi/cadence_qspi_apb.c @@ -325,6 +325,17 @@ void cadence_qspi_apb_delay(void *reg_base, cadence_qspi_apb_controller_enable(reg_base); } +static void cadence_qspi_apb_write_protect_enable(void *reg_base) +{ + writel(CQSPI_REG_WRPROT_ENABLE, + reg_base + CQSPI_REG_WRITE_PROTECT_CTRL); +} + +static void cadence_qspi_apb_write_protect_disable(void *reg_base) +{ + writel(0, reg_base + CQSPI_REG_WRITE_PROTECT_CTRL); +} + void cadence_qspi_apb_controller_init(struct cadence_spi_priv *priv) { unsigned reg; @@ -346,6 +357,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_enable(priv->regbase); + } + /* Disable all interrupts */ writel(0, priv->regbase + CQSPI_REG_IRQMASK); @@ -955,7 +978,11 @@ 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)) { + if (IS_ENABLED(CONFIG_CADENCE_QSPI_WRITE_PROTECT)) + cadence_qspi_apb_write_protect_disable(priv->regbase); memcpy_toio(priv->ahbbase + to, buf, len); + if (IS_ENABLED(CONFIG_CADENCE_QSPI_WRITE_PROTECT)) + cadence_qspi_apb_write_protect_enable(priv->regbase); if (!cadence_qspi_wait_idle(priv->regbase)) return -EIO; return 0; -- 2.55.0
