summaryrefslogtreecommitdiffstats
path: root/drivers/mtd/spi-nor
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2015-07-14 15:59:47 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2015-08-28 11:11:46 +0200
commit311f02fbe4f00cafbb365ef76b8ad2231a2934cd (patch)
treeeda6ef99a24ea6d46ca56a66c72b6f180254ef17 /drivers/mtd/spi-nor
parent3e1adbf832b8f420906e6d5cbf5bec7617545c7e (diff)
downloadbarebox-311f02fbe4f00cafbb365ef76b8ad2231a2934cd.tar.gz
barebox-311f02fbe4f00cafbb365ef76b8ad2231a2934cd.tar.xz
mtd: spi-nor: mostly drop lock/unlock code
The lock/unlock code is broken beyond repair. First of all the algorithm doesn't work properly. SPI NOR flashes can only protect a certain amount of blocks from the end of the device which is incompatible to the protect(start,len) API we have. The algorithm tries to be clever by doing protection only when it does not protect unrelated blocks and unprotection only when it does not unprotect unrelated blocks. This breaks for example when some code protects the last blocks (which may contain the bootloader), then protects the blocks before the last ones (which may contain the environment). Then if we try to overwrite the bootloader this won't work since it would unprotect the environment aswell, so the driver will not unprotect anything resulting in a failed erase/write later. Then the protection behaviour is different between different flashes. Some have three protection bits, some have four. For some the smallest protection are is 1/16 of the device, others have 1/256 or 1/64. Some have a bit which selects the lower area instead of upper area for protection. The position of this bit differs on different flashes. This patch removes the lock code completely and always unprotects the whole device. This way we can unprotect a device for writing to it and never protect it again. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers/mtd/spi-nor')
-rw-r--r--drivers/mtd/spi-nor/spi-nor.c74
1 files changed, 8 insertions, 66 deletions
diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c
index edf0dd5f39..32501a9011 100644
--- a/drivers/mtd/spi-nor/spi-nor.c
+++ b/drivers/mtd/spi-nor/spi-nor.c
@@ -370,85 +370,27 @@ erase_err:
static int spi_nor_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
{
- struct spi_nor *nor = mtd_to_spi_nor(mtd);
- uint32_t offset = ofs;
- uint8_t status_old, status_new;
- int ret = 0;
-
- ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_LOCK);
- if (ret)
- return ret;
-
- status_old = read_sr(nor);
-
- if (offset < mtd->size - (mtd->size / 2))
- status_new = status_old | SR_BP2 | SR_BP1 | SR_BP0;
- else if (offset < mtd->size - (mtd->size / 4))
- status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1;
- else if (offset < mtd->size - (mtd->size / 8))
- status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0;
- else if (offset < mtd->size - (mtd->size / 16))
- status_new = (status_old & ~(SR_BP0 | SR_BP1)) | SR_BP2;
- else if (offset < mtd->size - (mtd->size / 32))
- status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0;
- else if (offset < mtd->size - (mtd->size / 64))
- status_new = (status_old & ~(SR_BP2 | SR_BP0)) | SR_BP1;
- else
- status_new = (status_old & ~(SR_BP2 | SR_BP1)) | SR_BP0;
-
- /* Only modify protection if it will not unlock other areas */
- if ((status_new & (SR_BP2 | SR_BP1 | SR_BP0)) >
- (status_old & (SR_BP2 | SR_BP1 | SR_BP0))) {
- write_enable(nor);
- ret = write_sr(nor, status_new);
- if (ret)
- goto err;
- }
-
-err:
- spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK);
- return ret;
+ return 0;
}
static int spi_nor_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
{
struct spi_nor *nor = mtd_to_spi_nor(mtd);
- uint32_t offset = ofs;
- uint8_t status_old, status_new;
- int ret = 0;
+ uint8_t status;
+ int ret;
ret = spi_nor_lock_and_prep(nor, SPI_NOR_OPS_UNLOCK);
if (ret)
return ret;
- status_old = read_sr(nor);
-
- if (offset+len > mtd->size - (mtd->size / 64))
- status_new = status_old & ~(SR_BP2 | SR_BP1 | SR_BP0);
- else if (offset+len > mtd->size - (mtd->size / 32))
- status_new = (status_old & ~(SR_BP2 | SR_BP1)) | SR_BP0;
- else if (offset+len > mtd->size - (mtd->size / 16))
- status_new = (status_old & ~(SR_BP2 | SR_BP0)) | SR_BP1;
- else if (offset+len > mtd->size - (mtd->size / 8))
- status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0;
- else if (offset+len > mtd->size - (mtd->size / 4))
- status_new = (status_old & ~(SR_BP0 | SR_BP1)) | SR_BP2;
- else if (offset+len > mtd->size - (mtd->size / 2))
- status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0;
- else
- status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1;
+ status = read_sr(nor);
+ status &= ~(SR_BP2 | SR_BP1 | SR_BP0);
+ write_enable(nor);
- /* Only modify protection if it will not lock other areas */
- if ((status_new & (SR_BP2 | SR_BP1 | SR_BP0)) <
- (status_old & (SR_BP2 | SR_BP1 | SR_BP0))) {
- write_enable(nor);
- ret = write_sr(nor, status_new);
- if (ret)
- goto err;
- }
+ ret = write_sr(nor, status);
-err:
spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_UNLOCK);
+
return ret;
}