mtd: spi-nor: sst: Add support for Global Unlock on sst26vf

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 <tudor.ambarus@microchip.com>
Reviewed-by: Michael Walle <michael@walle.cc>
Link: https://lore.kernel.org/r/20210121110546.382633-2-tudor.ambarus@microchip.com
This commit is contained in:
Tudor Ambarus 2021-01-21 13:05:46 +02:00
parent a7a5acba0e
commit 75386810d3
3 changed files with 52 additions and 3 deletions

View file

@ -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;

View file

@ -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);

View file

@ -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,