[U-Boot] [PATCH v5 3/3] spi: Add SPI NOR protection mechanism

Fabio Estevam festevam at gmail.com
Thu Oct 1 20:31:19 CEST 2015


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 and
write 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 at freescale.com>
---
Changes since v4:
- Use sf protect lock/unlock (Jagan)
- Remove SPI_FLASH_STM_PROTECT (Jagan)
- Do not remove definitions from drivers/mtd/spi/sf_internal.h (Jagan)
- Move other prototypes to drivers/mtd/spi/sf_internal.h (Jagan)

 common/cmd_sf.c               | 35 +++++++++++++++++++++++++++++++++++
 drivers/mtd/spi/sf_internal.h |  8 ++++++++
 drivers/mtd/spi/sf_ops.c      | 33 +++++++++++++++++++++++++++++++++
 drivers/mtd/spi/sf_probe.c    | 27 +++++++++++++++++++++++++++
 include/spi_flash.h           |  7 +++++++
 5 files changed, 110 insertions(+)

diff --git a/common/cmd_sf.c b/common/cmd_sf.c
index ac7f5df..42862d9 100644
--- a/common/cmd_sf.c
+++ b/common/cmd_sf.c
@@ -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
 );
diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h
index 9c95d56..33be598 100644
--- a/drivers/mtd/spi/sf_internal.h
+++ b/drivers/mtd/spi/sf_internal.h
@@ -168,6 +168,10 @@ 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);
 
+int stm_is_locked(struct spi_flash *nor, loff_t ofs, u32 len);
+int stm_lock(struct spi_flash *nor, u32 ofs, u32 len);
+int stm_unlock(struct spi_flash *nor, u32 ofs, u32 len);
+
 /* Read the config register */
 int spi_flash_cmd_read_config(struct spi_flash *flash, u8 *rc);
 
@@ -222,6 +226,10 @@ int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd,
 int spi_flash_cmd_read_ops(struct spi_flash *flash, u32 offset,
 		size_t len, void *data);
 
+int spi_flash_cmd_lock_ops(struct spi_flash *flash, u32 offset, size_t len);
+int spi_flash_cmd_unlock_ops(struct spi_flash *flash, u32 offset, size_t len);
+int spi_flash_cmd_is_locked_ops(struct spi_flash *flash, u32 offset, size_t len);
+
 #ifdef CONFIG_SPI_FLASH_MTD
 int spi_flash_mtd_register(struct spi_flash *flash);
 void spi_flash_mtd_unregister(void);
diff --git a/drivers/mtd/spi/sf_ops.c b/drivers/mtd/spi/sf_ops.c
index 0f7a31d..d2b9ddc 100644
--- a/drivers/mtd/spi/sf_ops.c
+++ b/drivers/mtd/spi/sf_ops.c
@@ -276,6 +276,11 @@ int spi_flash_cmd_erase_ops(struct spi_flash *flash, u32 offset, size_t len)
 		return -1;
 	}
 
+	if (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;
@@ -318,6 +323,11 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
 
 	page_size = flash->page_size;
 
+	if (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;
@@ -356,6 +366,21 @@ int spi_flash_cmd_write_ops(struct spi_flash *flash, u32 offset,
 	return ret;
 }
 
+int spi_flash_cmd_lock_ops(struct spi_flash *flash, u32 offset, size_t len)
+{
+	return stm_lock(flash, offset, len);
+}
+
+int spi_flash_cmd_unlock_ops(struct spi_flash *flash, u32 offset, size_t len)
+{
+	return stm_unlock(flash, offset, len);
+}
+
+int spi_flash_cmd_is_locked_ops(struct spi_flash *flash, u32 offset, size_t len)
+{
+	return stm_is_locked(flash, offset, len);
+}
+
 int spi_flash_read_common(struct spi_flash *flash, const u8 *cmd,
 		size_t cmd_len, void *data, size_t data_len)
 {
@@ -761,3 +786,11 @@ int stm_unlock(struct spi_flash *nor, u32 ofs, u32 len)
 	return 0;
 }
 #endif  /* CONFIG_SPI_FLASH_STMICRO */
+
+int spi_flash_protect(struct spi_flash *flash, loff_t ofs, u32 len, bool prot)
+{
+	if (prot)
+		return flash->lock(flash, ofs, len);
+	else
+		return flash->unlock(flash, ofs, len);
+}
diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c
index 954376d..6da9d6c 100644
--- a/drivers/mtd/spi/sf_probe.c
+++ b/drivers/mtd/spi/sf_probe.c
@@ -149,6 +149,9 @@ static int spi_flash_validate_params(struct spi_slave *spi, u8 *idcode,
 #endif
 	flash->erase = spi_flash_cmd_erase_ops;
 	flash->read = spi_flash_cmd_read_ops;
+	flash->lock = spi_flash_cmd_lock_ops;
+	flash->unlock = spi_flash_cmd_unlock_ops;
+	flash->is_locked = spi_flash_cmd_is_locked_ops;
 #endif
 
 	/* Compute the flash size */
@@ -469,6 +472,27 @@ int spi_flash_std_erase(struct udevice *dev, u32 offset, size_t len)
 	return spi_flash_cmd_erase_ops(flash, offset, len);
 }
 
+int spi_flash_std_lock(struct udevice *dev, u32 offset, size_t len)
+{
+	struct spi_flash *flash = dev_get_uclass_priv(dev);
+
+	return spi_flash_cmd_lock_ops(flash, offset, len);
+}
+
+int spi_flash_std_unlock(struct udevice *dev, u32 offset, size_t len)
+{
+	struct spi_flash *flash = dev_get_uclass_priv(dev);
+
+	return spi_flash_cmd_unlock_ops(flash, offset, len);
+}
+
+int spi_flash_std_is_locked(struct udevice *dev, u32 offset, size_t len)
+{
+	struct spi_flash *flash = dev_get_uclass_priv(dev);
+
+	return spi_flash_cmd_is_locked_ops(flash, offset, len);
+}
+
 int spi_flash_std_probe(struct udevice *dev)
 {
 	struct spi_slave *slave = dev_get_parentdata(dev);
@@ -485,6 +509,9 @@ static const struct dm_spi_flash_ops spi_flash_std_ops = {
 	.read = spi_flash_std_read,
 	.write = spi_flash_std_write,
 	.erase = spi_flash_std_erase,
+	.lock = spi_flash_std_lock,
+	.unlock = spi_flash_std_unlock,
+	.is_locked = spi_flash_std_is_locked,
 };
 
 static const struct udevice_id spi_flash_std_ids[] = {
diff --git a/include/spi_flash.h b/include/spi_flash.h
index 3b2d555..3a9b399 100644
--- a/include/spi_flash.h
+++ b/include/spi_flash.h
@@ -105,6 +105,9 @@ struct spi_flash {
 	int (*write)(struct spi_flash *flash, u32 offset, size_t len,
 			const void *buf);
 	int (*erase)(struct spi_flash *flash, u32 offset, size_t len);
+	int (*lock)(struct spi_flash *flash, u32 offset, size_t len);
+	int (*unlock)(struct spi_flash *flash, u32 offset, size_t len);
+	int (*is_locked)(struct spi_flash *flash, u32 offset, size_t len);
 #endif
 };
 
@@ -113,6 +116,9 @@ struct dm_spi_flash_ops {
 	int (*write)(struct udevice *dev, u32 offset, size_t len,
 		     const void *buf);
 	int (*erase)(struct udevice *dev, u32 offset, size_t len);
+	int (*lock)(struct udevice *dev, u32 offset, size_t len);
+	int (*unlock)(struct udevice *dev, u32 offset, size_t len);
+	int (*is_locked)(struct udevice *dev, u32 offset, size_t len);
 };
 
 /* Access the serial operations for a device */
@@ -229,6 +235,7 @@ static inline int spi_flash_erase(struct spi_flash *flash, u32 offset,
 }
 #endif
 
+int spi_flash_protect(struct spi_flash *flash, loff_t ofs, u32 len, bool prot);
 void spi_boot(void) __noreturn;
 void spi_spl_load_image(uint32_t offs, unsigned int size, void *vdst);
 
-- 
1.9.1



More information about the U-Boot mailing list