1
0
Fork 0

sf: Add SPI NOR protection mechanism

Many SPI flashes have protection bits (BP2, BP1 and BP0) in the
status register that can protect selected regions of the SPI NOR.

Take these bits into account when performing erase operations,
making sure that the protected areas are skipped.

Tested on a mx6qsabresd:

=> sf probe
SF: Detected M25P32 with page size 256 Bytes, erase size 64 KiB, total 4 MiB
=> sf protect lock  0x3f0000 0x10000
=> sf erase 0x3f0000 0x10000
offset 0x3f0000 is protected and cannot be erased
SF: 65536 bytes @ 0x3f0000 Erased: ERROR
=> sf protect unlock  0x3f0000 0x10000
=> sf erase 0x3f0000 0x10000
SF: 65536 bytes @ 0x3f0000 Erased: OK

Signed-off-by: Fabio Estevam <fabio.estevam@freescale.com>
[re-worked to fit the lock common to dm and non-dm]
Signed-off-by: Jagan Teki <jteki@openedev.com>
Reviewed-by: Tom Rini <trini@konsulko.com>
Reviewed-by: Heiko Schocher <hs@denx.de>
Reviewed-by: Jagan Teki <jteki@openedev.com>
utp
Fabio Estevam 2015-11-05 12:43:42 -02:00 committed by Tom Rini
parent 41b358d7a7
commit c3c016cf75
5 changed files with 88 additions and 2 deletions

View File

@ -348,6 +348,37 @@ static int do_spi_flash_erase(int argc, char * const argv[])
return ret == 0 ? 0 : 1;
}
static int do_spi_protect(int argc, char * const argv[])
{
int ret = 0;
loff_t start, len;
bool prot = false;
if (argc != 4)
return -1;
if (!str2off(argv[2], &start)) {
puts("start sector is not a valid number\n");
return 1;
}
if (!str2off(argv[3], &len)) {
puts("len is not a valid number\n");
return 1;
}
if (strcmp(argv[1], "lock") == 0)
prot = true;
else if (strcmp(argv[1], "unlock") == 0)
prot = false;
else
return -1; /* Unknown parameter */
ret = spi_flash_protect(flash, start, len, prot);
return ret == 0 ? 0 : 1;
}
#ifdef CONFIG_CMD_SF_TEST
enum {
STAGE_ERASE,
@ -540,6 +571,8 @@ static int do_spi_flash(cmd_tbl_t *cmdtp, int flag, int argc,
ret = do_spi_flash_read_write(argc, argv);
else if (strcmp(cmd, "erase") == 0)
ret = do_spi_flash_erase(argc, argv);
else if (strcmp(cmd, "protect") == 0)
ret = do_spi_protect(argc, argv);
#ifdef CONFIG_CMD_SF_TEST
else if (!strcmp(cmd, "test"))
ret = do_spi_flash_test(argc, argv);
@ -579,5 +612,7 @@ U_BOOT_CMD(
"sf update addr offset|partition len - erase and write `len' bytes from memory\n"
" at `addr' to flash at `offset'\n"
" or to start of mtd `partition'\n"
"sf protect lock/unlock sector len - protect/unprotect 'len' bytes starting\n"
" at address 'sector'\n"
SF_TEST_HELP
);

View File

@ -176,6 +176,15 @@ int spi_flash_cmd_read_status(struct spi_flash *flash, u8 *rs);
/* Program the status register */
int spi_flash_cmd_write_status(struct spi_flash *flash, u8 ws);
/* Lock stmicro spi flash region */
int stm_lock(struct spi_flash *flash, u32 ofs, size_t len);
/* Unlock stmicro spi flash region */
int stm_unlock(struct spi_flash *flash, u32 ofs, size_t len);
/* Check if a stmicro spi flash region is completely locked */
int stm_is_locked(struct spi_flash *flash, u32 ofs, size_t len);
/* Read the config register */
int spi_flash_cmd_read_config(struct spi_flash *flash, u8 *rc);

View File

@ -268,6 +268,11 @@ int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len)
return -1;
}
if (flash->flash_is_locked(flash, offset, len) > 0) {
printf("offset 0x%x is protected and cannot be erased\n", offset);
return -EINVAL;
}
cmd[0] = flash->erase_cmd;
while (len) {
erase_addr = offset;
@ -310,6 +315,11 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
page_size = flash->page_size;
if (flash->flash_is_locked(flash, offset, len) > 0) {
printf("offset 0x%x is protected and cannot be written\n", offset);
return -EINVAL;
}
cmd[0] = flash->write_cmd;
for (actual = 0; actual < len; actual += chunk_len) {
write_addr = offset;
@ -589,7 +599,7 @@ static void stm_get_locked_range(struct spi_flash *flash, u8 sr, loff_t *ofs,
/*
* Return 1 if the entire region is locked, 0 otherwise
*/
static int stm_is_locked_sr(struct spi_flash *flash, loff_t ofs, u32 len,
static int stm_is_locked_sr(struct spi_flash *flash, u32 ofs, u32 len,
u8 sr)
{
loff_t lock_offs;
@ -607,7 +617,7 @@ static int stm_is_locked_sr(struct spi_flash *flash, loff_t ofs, u32 len,
* Returns 1 if entire region is locked, 0 if any portion is unlocked, and
* negative on errors.
*/
int stm_is_locked(struct spi_flash *flash, loff_t ofs, u32 len)
int stm_is_locked(struct spi_flash *flash, u32 ofs, size_t len)
{
int status;
u8 sr;

View File

@ -182,6 +182,19 @@ static int spi_flash_validate_params(struct spi_slave *spi, u8 *idcode,
flash->read = spi_flash_cmd_read_ops;
#endif
/* lock hooks are flash specific - assign them based on idcode0 */
switch (idcode[0]) {
#ifdef CONFIG_SPI_FLASH_STMICRO
case SPI_FLASH_CFI_MFR_STMICRO:
flash->flash_lock = stm_lock;
flash->flash_unlock = stm_unlock;
flash->flash_is_locked = stm_is_locked;
#endif
break;
default:
debug("SF: Lock ops not supported for %02x flash\n", idcode[0]);
}
/* Compute the flash size */
flash->shift = (flash->dual_flash & SF_DUAL_PARALLEL_FLASH) ? 1 : 0;
/*

View File

@ -54,6 +54,9 @@ struct spi_slave;
* @write_cmd: Write cmd - page and quad program.
* @dummy_byte: Dummy cycles for read operation.
* @memory_map: Address of read-only SPI flash access
* @flash_lock: lock a region of the SPI Flash
* @flash_unlock: unlock a region of the SPI Flash
* @flash_is_locked: check if a region of the SPI Flash is completely locked
* @read: Flash read ops: Read len bytes at offset into buf
* Supported cmds: Fast Array Read
* @write: Flash write ops: Write len bytes from buf into offset
@ -87,6 +90,10 @@ struct spi_flash {
u8 dummy_byte;
void *memory_map;
int (*flash_lock)(struct spi_flash *flash, u32 ofs, size_t len);
int (*flash_unlock)(struct spi_flash *flash, u32 ofs, size_t len);
int (*flash_is_locked)(struct spi_flash *flash, u32 ofs, size_t len);
#ifndef CONFIG_DM_SPI_FLASH
/*
* These are not strictly needed for driver model, but keep them here
@ -227,6 +234,18 @@ static inline int spi_flash_erase(struct spi_flash *flash, u32 offset,
}
#endif
static inline int spi_flash_protect(struct spi_flash *flash, u32 ofs, u32 len,
bool prot)
{
if (!flash->flash_lock)
return -EOPNOTSUPP;
if (prot)
return flash->flash_lock(flash, ofs, len);
else
return flash->flash_unlock(flash, ofs, len);
}
void spi_boot(void) __noreturn;
void spi_spl_load_image(uint32_t offs, unsigned int size, void *vdst);