|
@@ -565,6 +565,96 @@ time_out:
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+static int m25p80_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
|
|
+{
|
|
|
+ struct m25p *flash = mtd_to_m25p(mtd);
|
|
|
+ uint32_t offset = ofs;
|
|
|
+ uint8_t status_old, status_new;
|
|
|
+ int res = 0;
|
|
|
+
|
|
|
+ mutex_lock(&flash->lock);
|
|
|
+ /* Wait until finished previous command */
|
|
|
+ if (wait_till_ready(flash)) {
|
|
|
+ res = 1;
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+
|
|
|
+ status_old = read_sr(flash);
|
|
|
+
|
|
|
+ if (offset < flash->mtd.size-(flash->mtd.size/2))
|
|
|
+ status_new = status_old | SR_BP2 | SR_BP1 | SR_BP0;
|
|
|
+ else if (offset < flash->mtd.size-(flash->mtd.size/4))
|
|
|
+ status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1;
|
|
|
+ else if (offset < flash->mtd.size-(flash->mtd.size/8))
|
|
|
+ status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0;
|
|
|
+ else if (offset < flash->mtd.size-(flash->mtd.size/16))
|
|
|
+ status_new = (status_old & ~(SR_BP0|SR_BP1)) | SR_BP2;
|
|
|
+ else if (offset < flash->mtd.size-(flash->mtd.size/32))
|
|
|
+ status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0;
|
|
|
+ else if (offset < flash->mtd.size-(flash->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(flash);
|
|
|
+ if (write_sr(flash, status_new) < 0) {
|
|
|
+ res = 1;
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+err: mutex_unlock(&flash->lock);
|
|
|
+ return res;
|
|
|
+}
|
|
|
+
|
|
|
+static int m25p80_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
|
|
|
+{
|
|
|
+ struct m25p *flash = mtd_to_m25p(mtd);
|
|
|
+ uint32_t offset = ofs;
|
|
|
+ uint8_t status_old, status_new;
|
|
|
+ int res = 0;
|
|
|
+
|
|
|
+ mutex_lock(&flash->lock);
|
|
|
+ /* Wait until finished previous command */
|
|
|
+ if (wait_till_ready(flash)) {
|
|
|
+ res = 1;
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+
|
|
|
+ status_old = read_sr(flash);
|
|
|
+
|
|
|
+ if (offset+len > flash->mtd.size-(flash->mtd.size/64))
|
|
|
+ status_new = status_old & ~(SR_BP2|SR_BP1|SR_BP0);
|
|
|
+ else if (offset+len > flash->mtd.size-(flash->mtd.size/32))
|
|
|
+ status_new = (status_old & ~(SR_BP2|SR_BP1)) | SR_BP0;
|
|
|
+ else if (offset+len > flash->mtd.size-(flash->mtd.size/16))
|
|
|
+ status_new = (status_old & ~(SR_BP2|SR_BP0)) | SR_BP1;
|
|
|
+ else if (offset+len > flash->mtd.size-(flash->mtd.size/8))
|
|
|
+ status_new = (status_old & ~SR_BP2) | SR_BP1 | SR_BP0;
|
|
|
+ else if (offset+len > flash->mtd.size-(flash->mtd.size/4))
|
|
|
+ status_new = (status_old & ~(SR_BP0|SR_BP1)) | SR_BP2;
|
|
|
+ else if (offset+len > flash->mtd.size-(flash->mtd.size/2))
|
|
|
+ status_new = (status_old & ~SR_BP1) | SR_BP2 | SR_BP0;
|
|
|
+ else
|
|
|
+ status_new = (status_old & ~SR_BP0) | SR_BP2 | SR_BP1;
|
|
|
+
|
|
|
+ /* 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(flash);
|
|
|
+ if (write_sr(flash, status_new) < 0) {
|
|
|
+ res = 1;
|
|
|
+ goto err;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+err: mutex_unlock(&flash->lock);
|
|
|
+ return res;
|
|
|
+}
|
|
|
+
|
|
|
/****************************************************************************/
|
|
|
|
|
|
/*
|
|
@@ -899,6 +989,12 @@ static int m25p_probe(struct spi_device *spi)
|
|
|
flash->mtd._erase = m25p80_erase;
|
|
|
flash->mtd._read = m25p80_read;
|
|
|
|
|
|
+ /* flash protection support for STmicro chips */
|
|
|
+ if (JEDEC_MFR(info->jedec_id) == CFI_MFR_ST) {
|
|
|
+ flash->mtd._lock = m25p80_lock;
|
|
|
+ flash->mtd._unlock = m25p80_unlock;
|
|
|
+ }
|
|
|
+
|
|
|
/* sst flash chips use AAI word program */
|
|
|
if (JEDEC_MFR(info->jedec_id) == CFI_MFR_SST)
|
|
|
flash->mtd._write = sst_write;
|