234 lines
6.3 KiB
Diff
234 lines
6.3 KiB
Diff
|
From 986172ece10eee928ce66597d76f3f40ac3d25f7 Mon Sep 17 00:00:00 2001
|
||
|
From: Yunhui Cui <yunhui.cui@nxp.com>
|
||
|
Date: Mon, 8 Aug 2016 14:24:13 +0800
|
||
|
Subject: [PATCH 86/93] driver: spi: add spansion s25fs-s family
|
||
|
protect/unprotect
|
||
|
|
||
|
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@nxp.com>
|
||
|
---
|
||
|
drivers/mtd/spi/spi_flash.c | 194 +++++++++++++++++++++++++++++++++++++++++++
|
||
|
1 file changed, 194 insertions(+)
|
||
|
|
||
|
diff --git a/drivers/mtd/spi/spi_flash.c b/drivers/mtd/spi/spi_flash.c
|
||
|
index e04bd55..87a92e9 100644
|
||
|
--- a/drivers/mtd/spi/spi_flash.c
|
||
|
+++ b/drivers/mtd/spi/spi_flash.c
|
||
|
@@ -877,6 +877,193 @@ 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;
|
||
|
+ u32 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 spi_flash_set_qeb_mxic(struct spi_flash *flash)
|
||
|
@@ -1132,6 +1319,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]);
|
||
|
}
|
||
|
--
|
||
|
1.7.9.5
|
||
|
|