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

Reply via email to