[U-Boot] [PATCH] driver: spi: add spansion s25fs-s family protect/unprotect
Yunhui Cui
B56489 at freescale.com
Mon Aug 15 08:12:54 CEST 2016
From: Yunhui Cui <yunhui.cui at nxp.com>
In order to support spansion s25fs512s flash protect/unprotect:
[1] Fill callbak flash->lock/unlock/is_locked by spansion_lock/
unlock/is_locked.
[2] Achieve protect/unprotected by operating sr1nv, cr1nv.
Signed-off-by: Yunhui Cui <yunhui.cui at nxp.com>
---
drivers/mtd/spi/spi_flash.c | 195 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 195 insertions(+)
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
index 64d4e0f..446e6e3 100644
--- a/drivers/mtd/spi/spi_flash.c
+++ b/drivers/mtd/spi/spi_flash.c
@@ -839,6 +839,194 @@ int stm_unlock(struct spi_flash *flash, u32 ofs, size_t len)
}
#endif
+#if defined(CONFIG_SPI_FLASH_SPANSION)
+/*
+ * Return 1 if the entire region is locked, 0 otherwise
+ */
+static int spansion_is_locked_sr(struct spi_flash *flash, u32 ofs, u32 len,
+ u8 sr)
+{
+ loff_t lock_offs;
+ u64 lock_len;
+
+ stm_get_locked_range(flash, sr, &lock_offs, &lock_len);
+
+ return (ofs + len <= lock_offs + lock_len) && (ofs >= lock_offs);
+}
+
+/*
+ * Check if a region of the flash is (completely) locked. See spansion_lock()
+ * for more info.
+ *
+ * Returns 1 if entire region is locked, 0 if any portion is unlocked, and
+ * negative on errors.
+ */
+int spansion_is_locked(struct spi_flash *flash, u32 ofs, size_t len)
+{
+ u8 cmd[4];
+ u32 sr1nv_offset = 0x0;
+ u8 sr1nv;
+ int ret;
+
+ cmd[0] = CMD_SPANSION_RDAR;
+ cmd[1] = sr1nv_offset >> 16;
+ cmd[2] = sr1nv_offset >> 8;
+ cmd[3] = sr1nv_offset >> 0;
+
+ ret = spi_flash_cmd_read(flash->spi, cmd, 4, &sr1nv, 1);
+ if (ret)
+ return -EIO;
+
+ return spansion_is_locked_sr(flash, ofs, len, sr1nv);
+}
+
+/*
+ * Lock a region of the flash. Compatible with Spansion s25fs-s family flash.
+ * Supports only the block protection bits BP{0,1,2} in the Status Register-1
+ * Non-Volatile(SR1NV).
+ *
+ * Sample table portion for 64MB flash (S25FS512S):
+ * Configuration Register-1 Non-Volatile(CR1NV[5])== 0
+ *
+ * | BP2 | BP1 | BP0 | Prot Length | Protected Portion
+ * ------------------------------------------------------------
+ * | 0 | 0 | 0 | NONE | NONE
+ * | 0 | 0 | 1 | 1 MB | Upper 1/64
+ * | 0 | 1 | 0 | 2 MB | Upper 1/32
+ * | 0 | 1 | 1 | 4 MB | Upper 1/16
+ * | 1 | 0 | 0 | 8 MB | Upper 1/8
+ * | 1 | 0 | 1 | 16 MB | Upper 1/4
+ * | 1 | 1 | 0 | 32 MB | Upper 1/2
+ * | 1 | 1 | 1 | 64 MB | ALL
+ *
+ * When CR1NV[5] == 1, the Lower memory array are protected.
+ *
+ * Returns negative on errors, 0 on success.
+ */
+int spansion_lock(struct spi_flash *flash, u32 ofs, size_t len)
+{
+ u8 status_old, status_new;
+ u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
+ u8 shift = ffs(mask) - 1, pow, val;
+ int ret;
+ u8 cmd[4];
+ u32 sr1nv_offset = 0x0;
+ u8 sr1nv;
+
+ cmd[0] = CMD_SPANSION_RDAR;
+ cmd[1] = sr1nv_offset >> 16;
+ cmd[2] = sr1nv_offset >> 8;
+ cmd[3] = sr1nv_offset >> 0;
+
+ ret = spi_flash_cmd_read(flash->spi, cmd, 4, &sr1nv, 1);
+ if (ret)
+ return -EIO;
+ status_old = sr1nv;
+
+ /* SPI NOR always locks to the end */
+ if (ofs + len != flash->size) {
+ /* Does combined region extend to end? */
+ if (!stm_is_locked_sr(flash, ofs + len, flash->size - ofs - len,
+ status_old))
+ return -EINVAL;
+ len = flash->size - ofs;
+ }
+
+ /*
+ * Need smallest pow such that:
+ *
+ * 1 / (2^pow) <= (len / size)
+ *
+ * so (assuming power-of-2 size) we do:
+ *
+ * pow = ceil(log2(size / len)) = log2(size) - floor(log2(len))
+ */
+ pow = ilog2(flash->size) - ilog2(len);
+ val = mask - (pow << shift);
+ if (val & ~mask)
+ return -EINVAL;
+
+ /* Don't "lock" with no region! */
+ if (!(val & mask))
+ return -EINVAL;
+
+ status_new = (status_old & ~mask) | val;
+
+ /* Only modify protection if it will not unlock other areas */
+ if ((status_new & mask) <= (status_old & mask))
+ return -EINVAL;
+
+ cmd[0] = CMD_SPANSION_WRAR;
+ ret = spi_flash_cmd_write(flash->spi, cmd, 4, &status_new, 1);
+ if (ret)
+ return -EIO;
+
+ return 0;
+}
+
+/*
+ * Unlock a region of the flash. See spansion_lock() for more info
+ *
+ * Returns negative on errors, 0 on success.
+ */
+int spansion_unlock(struct spi_flash *flash, u32 ofs, size_t len)
+{
+ uint8_t status_old, status_new;
+ u8 mask = SR_BP2 | SR_BP1 | SR_BP0;
+ u8 shift = ffs(mask) - 1, pow, val;
+ int ret;
+
+ u8 cmd[4];
+ u32 sr1nv_offset = 0x0;
+ u8 sr1nv;
+
+ cmd[0] = CMD_SPANSION_RDAR;
+ cmd[1] = sr1nv_offset >> 16;
+ cmd[2] = sr1nv_offset >> 8;
+ cmd[3] = sr1nv_offset >> 0;
+
+ ret = spi_flash_cmd_read(flash->spi, cmd, 4, &sr1nv, 1);
+ if (ret)
+ return -EIO;
+ status_old = sr1nv;
+
+ /* Cannot unlock; would unlock larger region than requested */
+ if (spansion_is_locked_sr(flash, ofs - flash->erase_size,
+ flash->erase_size, status_old))
+ return -EINVAL;
+ /*
+ * Need largest pow such that:
+ *
+ * 1 / (2^pow) >= (len / size)
+ *
+ * so (assuming power-of-2 size) we do:
+ *
+ * pow = floor(log2(size / len)) = log2(size) - ceil(log2(len))
+ */
+ pow = ilog2(flash->size) - order_base_2(flash->size - (ofs + len));
+ if (ofs + len == flash->size) {
+ val = 0; /* fully unlocked */
+ } else {
+ val = mask - (pow << shift);
+ /* Some power-of-two sizes are not supported */
+ if (val & ~mask)
+ return -EINVAL;
+ }
+ status_new = (status_old & ~mask) | val;
+
+ /* Only modify protection if it will not lock other areas */
+ if ((status_new & mask) >= (status_old & mask))
+ return -EINVAL;
+
+ cmd[0] = CMD_SPANSION_WRAR;
+ ret = spi_flash_cmd_write(flash->spi, cmd, 4, &status_new, 1);
+ if (ret)
+ return -EIO;
+
+ return 0;
+}
+#endif
+
#ifdef CONFIG_SPI_FLASH_MACRONIX
static int macronix_quad_enable(struct spi_flash *flash)
@@ -1134,6 +1322,13 @@ int spi_flash_scan(struct spi_flash *flash)
flash->flash_is_locked = stm_is_locked;
#endif
break;
+#if defined(CONFIG_SPI_FLASH_SPANSION)
+ case SPI_FLASH_CFI_MFR_SPANSION:
+ flash->flash_lock = spansion_lock;
+ flash->flash_unlock = spansion_unlock;
+ flash->flash_is_locked = spansion_is_locked;
+#endif
+ break;
default:
debug("SF: Lock ops not supported for %02x flash\n", idcode[0]);
}
--
2.1.0.27.g96db324
More information about the U-Boot
mailing list