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>
diff --git a/drivers/mtd/spi/sf_internal.h b/drivers/mtd/spi/sf_internal.h
index ac493f1..8793f18 100644
--- a/drivers/mtd/spi/sf_internal.h
+++ b/drivers/mtd/spi/sf_internal.h
@@ -176,6 +176,15 @@
 /* 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);
 
diff --git a/drivers/mtd/spi/sf_ops.c b/drivers/mtd/spi/sf_ops.c
index 6c87ba6..d832464 100644
--- a/drivers/mtd/spi/sf_ops.c
+++ b/drivers/mtd/spi/sf_ops.c
@@ -268,6 +268,11 @@
 		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 @@
 
 	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 @@
 /*
  * 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 @@
  * 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;
diff --git a/drivers/mtd/spi/sf_probe.c b/drivers/mtd/spi/sf_probe.c
index c000c53..bc05d30 100644
--- a/drivers/mtd/spi/sf_probe.c
+++ b/drivers/mtd/spi/sf_probe.c
@@ -182,6 +182,19 @@
 	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;
 	/*