The Global Block Unlock command has different names depending
on the manufacturer, but always the same command value: 0x98.
Macronix's MX25U12835F names it Gang Block Unlock, Winbond's
W25Q128FV names it Global Block Unlock and Microchip's
SST26VF064B names it Global Block Protection Unlock.
Used in the Individual Block Protection mode, which is mutually
exclusive with the Block Protection mode (BP0-3).
Signed-off-by: Tudor Ambarus <[email protected]>
Reviewed-by: Pratyush Yadav <[email protected]>
Reviewed-by: Michael Walle <[email protected]>
---
v3:
- s/Winbound/Winbond
- Add Michael's R-b tag
v2:
- s/mutual/mutually/
- set the GBULK cmd buswidth to 0 and call spi_nor_spimem_setup_op()
to update it, because the op can can be issued in QPI mode as well.
- add Pratyush's R-b tag
drivers/mtd/spi-nor/core.c | 37 +++++++++++++++++++++++++++++++++++++
drivers/mtd/spi-nor/core.h | 1 +
include/linux/mtd/spi-nor.h | 1 +
3 files changed, 39 insertions(+)
diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index 20df44b753da..e82732dd31e1 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -853,6 +853,43 @@ int spi_nor_wait_till_ready(struct spi_nor *nor)
DEFAULT_READY_WAIT_JIFFIES);
}
+/**
+ * spi_nor_global_block_unlock() - Unlock Global Block Protection.
+ * @nor: pointer to 'struct spi_nor'.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+int spi_nor_global_block_unlock(struct spi_nor *nor)
+{
+ int ret;
+
+ ret = spi_nor_write_enable(nor);
+ if (ret)
+ return ret;
+
+ if (nor->spimem) {
+ struct spi_mem_op op =
+ SPI_MEM_OP(SPI_MEM_OP_CMD(SPINOR_OP_GBULK, 0),
+ SPI_MEM_OP_NO_ADDR,
+ SPI_MEM_OP_NO_DUMMY,
+ SPI_MEM_OP_NO_DATA);
+
+ spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
+
+ ret = spi_mem_exec_op(nor->spimem, &op);
+ } else {
+ ret = spi_nor_controller_ops_write_reg(nor, SPINOR_OP_GBULK,
+ NULL, 0);
+ }
+
+ if (ret) {
+ dev_dbg(nor->dev, "error %d on Global Block Unlock\n", ret);
+ return ret;
+ }
+
+ return spi_nor_wait_till_ready(nor);
+}
+
/**
* spi_nor_write_sr() - Write the Status Register.
* @nor: pointer to 'struct spi_nor'.
diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
index d631ee299de3..eb26796db026 100644
--- a/drivers/mtd/spi-nor/core.h
+++ b/drivers/mtd/spi-nor/core.h
@@ -434,6 +434,7 @@ int spi_nor_write_disable(struct spi_nor *nor);
int spi_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable);
int spi_nor_write_ear(struct spi_nor *nor, u8 ear);
int spi_nor_wait_till_ready(struct spi_nor *nor);
+int spi_nor_global_block_unlock(struct spi_nor *nor);
int spi_nor_lock_and_prep(struct spi_nor *nor);
void spi_nor_unlock_and_unprep(struct spi_nor *nor);
int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor);
diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h
index d13958de6d8a..a0d572855444 100644
--- a/include/linux/mtd/spi-nor.h
+++ b/include/linux/mtd/spi-nor.h
@@ -53,6 +53,7 @@
#define SPINOR_OP_WREAR 0xc5 /* Write Extended Address Register */
#define SPINOR_OP_SRSTEN 0x66 /* Software Reset Enable */
#define SPINOR_OP_SRST 0x99 /* Software Reset */
+#define SPINOR_OP_GBULK 0x98 /* Global Block Unlock */
/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
#define SPINOR_OP_READ_4B 0x13 /* Read data bytes (low frequency) */
--
2.25.1
Even if sst26vf shares the SPINOR_OP_GBULK opcode with
Macronix (ex. MX25U12835F) and Winbound (ex. W25Q128FV),
it has its own Individual Block Protection scheme, which
is also capable to read-lock individual parameter blocks.
Thus the sst26vf's Individual Block Protection scheme will
reside in the sst.c manufacturer driver.
Add support to unlock the entire flash memory. The device
is write-protected by default after a power-on reset cycle
(volatile software protection), in order to avoid inadvertent
writes during power-up. Could do an erase, write, read back,
and compare when MTD_SPI_NOR_SWP_DISABLE_ON_VOLATILE=y.
Signed-off-by: Tudor Ambarus <[email protected]>
---
v3:
- s/Winbound/Winbond/
- read CR.BPNV and check if we can really unlock the entire flash array
- sst26vf_unlock: return -EINVAL instead of -EOPNOTSUPP when caller
asks to unlock a range/granularity that we can't unlock
v2: v2: s/!ofs/ofs == 0/
drivers/mtd/spi-nor/core.c | 2 +-
drivers/mtd/spi-nor/core.h | 1 +
drivers/mtd/spi-nor/sst.c | 52 ++++++++++++++++++++++++++++++++++++--
3 files changed, 52 insertions(+), 3 deletions(-)
diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
index e82732dd31e1..c50b30fbb912 100644
--- a/drivers/mtd/spi-nor/core.c
+++ b/drivers/mtd/spi-nor/core.c
@@ -465,7 +465,7 @@ static int spi_nor_read_fsr(struct spi_nor *nor, u8 *fsr)
*
* Return: 0 on success, -errno otherwise.
*/
-static int spi_nor_read_cr(struct spi_nor *nor, u8 *cr)
+int spi_nor_read_cr(struct spi_nor *nor, u8 *cr)
{
int ret;
diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
index eb26796db026..4a3f7f150b5d 100644
--- a/drivers/mtd/spi-nor/core.h
+++ b/drivers/mtd/spi-nor/core.h
@@ -441,6 +441,7 @@ int spi_nor_sr1_bit6_quad_enable(struct spi_nor *nor);
int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor);
int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor);
int spi_nor_read_sr(struct spi_nor *nor, u8 *sr);
+int spi_nor_read_cr(struct spi_nor *nor, u8 *cr);
int spi_nor_write_sr(struct spi_nor *nor, const u8 *sr, size_t len);
int spi_nor_write_sr_and_check(struct spi_nor *nor, u8 sr1);
diff --git a/drivers/mtd/spi-nor/sst.c b/drivers/mtd/spi-nor/sst.c
index 00e48da0744a..980f4c09c91d 100644
--- a/drivers/mtd/spi-nor/sst.c
+++ b/drivers/mtd/spi-nor/sst.c
@@ -8,6 +8,53 @@
#include "core.h"
+#define SST26VF_CR_BPNV BIT(3)
+
+static int sst26vf_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
+{
+ return -EOPNOTSUPP;
+}
+
+static int sst26vf_unlock(struct spi_nor *nor, loff_t ofs, uint64_t len)
+{
+ int ret;
+
+ /* We only support unlocking the entire flash array. */
+ if (ofs != 0 || len != nor->params->size)
+ return -EINVAL;
+
+ ret = spi_nor_read_cr(nor, nor->bouncebuf);
+ if (ret)
+ return ret;
+
+ if (!(nor->bouncebuf[0] & SST26VF_CR_BPNV)) {
+ dev_dbg(nor->dev, "Any block has been permanently locked\n");
+ return -EINVAL;
+ }
+
+ return spi_nor_global_block_unlock(nor);
+}
+
+static int sst26vf_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t len)
+{
+ return -EOPNOTSUPP;
+}
+
+static const struct spi_nor_locking_ops sst26vf_locking_ops = {
+ .lock = sst26vf_lock,
+ .unlock = sst26vf_unlock,
+ .is_locked = sst26vf_is_locked,
+};
+
+static void sst26vf_default_init(struct spi_nor *nor)
+{
+ nor->params->locking_ops = &sst26vf_locking_ops;
+}
+
+static const struct spi_nor_fixups sst26vf_fixups = {
+ .default_init = sst26vf_default_init,
+};
+
static const struct flash_info sst_parts[] = {
/* SST -- large erase sizes are "overlays", "sectors" are 4K */
{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8,
@@ -39,8 +86,9 @@ static const struct flash_info sst_parts[] = {
{ "sst26vf016b", INFO(0xbf2641, 0, 64 * 1024, 32,
SECT_4K | SPI_NOR_DUAL_READ) },
{ "sst26vf064b", INFO(0xbf2643, 0, 64 * 1024, 128,
- SECT_4K | SPI_NOR_DUAL_READ |
- SPI_NOR_QUAD_READ) },
+ SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
+ SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
+ .fixups = &sst26vf_fixups },
};
static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
--
2.25.1
Am 2021-01-21 12:05, schrieb Tudor Ambarus:
> Even if sst26vf shares the SPINOR_OP_GBULK opcode with
> Macronix (ex. MX25U12835F) and Winbound (ex. W25Q128FV),
> it has its own Individual Block Protection scheme, which
> is also capable to read-lock individual parameter blocks.
> Thus the sst26vf's Individual Block Protection scheme will
> reside in the sst.c manufacturer driver.
>
> Add support to unlock the entire flash memory. The device
> is write-protected by default after a power-on reset cycle
> (volatile software protection), in order to avoid inadvertent
> writes during power-up. Could do an erase, write, read back,
> and compare when MTD_SPI_NOR_SWP_DISABLE_ON_VOLATILE=y.
>
> Signed-off-by: Tudor Ambarus <[email protected]>
Reviewed-by: Michael Walle <[email protected]>
> ---
> v3:
> - s/Winbound/Winbond/
> - read CR.BPNV and check if we can really unlock the entire flash array
> - sst26vf_unlock: return -EINVAL instead of -EOPNOTSUPP when caller
> asks to unlock a range/granularity that we can't unlock
>
> v2: v2: s/!ofs/ofs == 0/
>
> drivers/mtd/spi-nor/core.c | 2 +-
> drivers/mtd/spi-nor/core.h | 1 +
> drivers/mtd/spi-nor/sst.c | 52 ++++++++++++++++++++++++++++++++++++--
> 3 files changed, 52 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c
> index e82732dd31e1..c50b30fbb912 100644
> --- a/drivers/mtd/spi-nor/core.c
> +++ b/drivers/mtd/spi-nor/core.c
> @@ -465,7 +465,7 @@ static int spi_nor_read_fsr(struct spi_nor *nor, u8
> *fsr)
> *
> * Return: 0 on success, -errno otherwise.
> */
> -static int spi_nor_read_cr(struct spi_nor *nor, u8 *cr)
> +int spi_nor_read_cr(struct spi_nor *nor, u8 *cr)
> {
> int ret;
>
> diff --git a/drivers/mtd/spi-nor/core.h b/drivers/mtd/spi-nor/core.h
> index eb26796db026..4a3f7f150b5d 100644
> --- a/drivers/mtd/spi-nor/core.h
> +++ b/drivers/mtd/spi-nor/core.h
> @@ -441,6 +441,7 @@ int spi_nor_sr1_bit6_quad_enable(struct spi_nor
> *nor);
> int spi_nor_sr2_bit1_quad_enable(struct spi_nor *nor);
> int spi_nor_sr2_bit7_quad_enable(struct spi_nor *nor);
> int spi_nor_read_sr(struct spi_nor *nor, u8 *sr);
> +int spi_nor_read_cr(struct spi_nor *nor, u8 *cr);
> int spi_nor_write_sr(struct spi_nor *nor, const u8 *sr, size_t len);
> int spi_nor_write_sr_and_check(struct spi_nor *nor, u8 sr1);
>
> diff --git a/drivers/mtd/spi-nor/sst.c b/drivers/mtd/spi-nor/sst.c
> index 00e48da0744a..980f4c09c91d 100644
> --- a/drivers/mtd/spi-nor/sst.c
> +++ b/drivers/mtd/spi-nor/sst.c
> @@ -8,6 +8,53 @@
>
> #include "core.h"
>
> +#define SST26VF_CR_BPNV BIT(3)
> +
> +static int sst26vf_lock(struct spi_nor *nor, loff_t ofs, uint64_t len)
> +{
> + return -EOPNOTSUPP;
> +}
> +
> +static int sst26vf_unlock(struct spi_nor *nor, loff_t ofs, uint64_t
> len)
> +{
> + int ret;
> +
> + /* We only support unlocking the entire flash array. */
> + if (ofs != 0 || len != nor->params->size)
> + return -EINVAL;
> +
> + ret = spi_nor_read_cr(nor, nor->bouncebuf);
> + if (ret)
> + return ret;
> +
> + if (!(nor->bouncebuf[0] & SST26VF_CR_BPNV)) {
> + dev_dbg(nor->dev, "Any block has been permanently locked\n");
> + return -EINVAL;
> + }
> +
> + return spi_nor_global_block_unlock(nor);
> +}
> +
> +static int sst26vf_is_locked(struct spi_nor *nor, loff_t ofs, uint64_t
> len)
> +{
> + return -EOPNOTSUPP;
> +}
> +
> +static const struct spi_nor_locking_ops sst26vf_locking_ops = {
> + .lock = sst26vf_lock,
> + .unlock = sst26vf_unlock,
> + .is_locked = sst26vf_is_locked,
> +};
> +
> +static void sst26vf_default_init(struct spi_nor *nor)
> +{
> + nor->params->locking_ops = &sst26vf_locking_ops;
> +}
> +
> +static const struct spi_nor_fixups sst26vf_fixups = {
> + .default_init = sst26vf_default_init,
> +};
> +
> static const struct flash_info sst_parts[] = {
> /* SST -- large erase sizes are "overlays", "sectors" are 4K */
> { "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8,
> @@ -39,8 +86,9 @@ static const struct flash_info sst_parts[] = {
> { "sst26vf016b", INFO(0xbf2641, 0, 64 * 1024, 32,
> SECT_4K | SPI_NOR_DUAL_READ) },
> { "sst26vf064b", INFO(0xbf2643, 0, 64 * 1024, 128,
> - SECT_4K | SPI_NOR_DUAL_READ |
> - SPI_NOR_QUAD_READ) },
> + SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
> + SPI_NOR_HAS_LOCK | SPI_NOR_SWP_IS_VOLATILE)
> + .fixups = &sst26vf_fixups },
> };
>
> static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
On Thu, 21 Jan 2021 13:05:45 +0200, Tudor Ambarus wrote:
> The Global Block Unlock command has different names depending
> on the manufacturer, but always the same command value: 0x98.
> Macronix's MX25U12835F names it Gang Block Unlock, Winbond's
> W25Q128FV names it Global Block Unlock and Microchip's
> SST26VF064B names it Global Block Protection Unlock.
>
> Used in the Individual Block Protection mode, which is mutually
> exclusive with the Block Protection mode (BP0-3).
Applied to spi-nor/next, thanks!
[1/2] mtd: spi-nor: Add Global Block Unlock command
https://git.kernel.org/mtd/c/a7a5acba0e06
[2/2] mtd: spi-nor: sst: Add support for Global Unlock on sst26vf
https://git.kernel.org/mtd/c/75386810d3a6
Best regards,
--
Tudor Ambarus <[email protected]>