summaryrefslogtreecommitdiffstats
path: root/spi25_statusreg.c
diff options
context:
space:
mode:
Diffstat (limited to 'spi25_statusreg.c')
-rw-r--r--spi25_statusreg.c61
1 files changed, 41 insertions, 20 deletions
diff --git a/spi25_statusreg.c b/spi25_statusreg.c
index 53952fe..a6a4607 100644
--- a/spi25_statusreg.c
+++ b/spi25_statusreg.c
@@ -126,11 +126,24 @@ uint8_t spi_read_status_register(struct flashctx *flash)
/* A generic block protection disable.
* Tests if a protection is enabled with the block protection mask (bp_mask) and returns success otherwise.
* Tests if the register bits are locked with the lock_mask (lock_mask).
- * Tests if a hardware protection is active (i.e. low) with the write protection mask (wp_mask) and bails out
- * in that case.
- * Finally tries to disable engaged protections and checks if any locks are still set.
+ * Tests if a hardware protection is active (i.e. low pin/high bit value) with the write protection mask
+ * (wp_mask) and bails out in that case.
+ * If there are register lock bits set we try to disable them by unsetting those bits of the previous register
+ * contents that are set in the lock_mask. We then check if removing the lock bits has worked and continue as if
+ * they never had been engaged:
+ * If the lock bits are out of the way try to disable engaged protections.
+ * To support uncommon global unprotects (e.g. on most AT2[56]xx1(A)) unprotect_mask can be used to force
+ * bits to 0 additionally to those set in bp_mask and lock_mask. Only bits set in unprotect_mask are potentially
+ * preserved when doing the final unprotect.
+ *
+ * To sum up:
+ * bp_mask: set those bits that correspond to the bits in the status register that indicate an active protection
+ * (which should be unset after this function returns).
+ * lock_mask: set the bits that correspond to the bits that lock changing the bits above.
+ * wp_mask: set the bits that correspond to bits indicating non-software revocable protections.
+ * unprotect_mask: set the bits that should be preserved if possible when unprotecting.
*/
-static int spi_disable_blockprotect_generic(struct flashctx *flash, uint8_t bp_mask, uint8_t lock_mask, uint8_t wp_mask)
+static int spi_disable_blockprotect_generic(struct flashctx *flash, uint8_t bp_mask, uint8_t lock_mask, uint8_t wp_mask, uint8_t unprotect_mask)
{
uint8_t status;
int result;
@@ -154,10 +167,15 @@ static int spi_disable_blockprotect_generic(struct flashctx *flash, uint8_t bp_m
msg_cerr("spi_write_status_register failed.\n");
return result;
}
+ status = spi_read_status_register(flash);
+ if ((status & lock_mask) != 0) {
+ msg_cerr("Unsetting lock bit(s) failed.\n");
+ return 1;
+ }
msg_cdbg("done.\n");
}
/* Global unprotect. Make sure to mask the register lock bit as well. */
- result = spi_write_status_register(flash, status & ~(bp_mask | lock_mask));
+ result = spi_write_status_register(flash, status & ~(bp_mask | lock_mask) & unprotect_mask);
if (result) {
msg_cerr("spi_write_status_register failed.\n");
return result;
@@ -165,6 +183,7 @@ static int spi_disable_blockprotect_generic(struct flashctx *flash, uint8_t bp_m
status = spi_read_status_register(flash);
if ((status & bp_mask) != 0) {
msg_cerr("Block protection could not be disabled!\n");
+ flash->chip->printlock(flash);
return 1;
}
msg_cdbg("disabled.\n");
@@ -174,7 +193,7 @@ static int spi_disable_blockprotect_generic(struct flashctx *flash, uint8_t bp_m
/* A common block protection disable that tries to unset the status register bits masked by 0x3C. */
int spi_disable_blockprotect(struct flashctx *flash)
{
- return spi_disable_blockprotect_generic(flash, 0x3C, 0, 0);
+ return spi_disable_blockprotect_generic(flash, 0x3C, 0, 0, 0xFF);
}
@@ -488,49 +507,51 @@ int spi_prettyprint_status_register_at26df081a(struct flashctx *flash)
return 0;
}
-int spi_disable_blockprotect_at25df(struct flashctx *flash)
+/* Some Atmel DataFlash chips support per sector protection bits and the write protection bits in the status
+ * register do indicate if none, some or all sectors are protected. It is possible to globally (un)lock all
+ * sectors at once by writing 0 not only the protection bits (2 and 3) but also completely unrelated bits (4 and
+ * 5) which normally are not touched.
+ * Affected are all known Atmel chips matched by AT2[56]D[FLQ]..1A? but the AT26DF041. */
+int spi_disable_blockprotect_at2x_global_unprotect(struct flashctx *flash)
{
- return spi_disable_blockprotect_generic(flash, 0x0C, 1 << 7, 1 << 4);
+ return spi_disable_blockprotect_generic(flash, 0x0C, 1 << 7, 1 << 4, 0x00);
}
-int spi_disable_blockprotect_at25df_sec(struct flashctx *flash)
+int spi_disable_blockprotect_at2x_global_unprotect_sec(struct flashctx *flash)
{
/* FIXME: We should check the security lockdown. */
msg_cinfo("Ignoring security lockdown (if present)\n");
- return spi_disable_blockprotect_at25df(flash);
+ return spi_disable_blockprotect_at2x_global_unprotect(flash);
}
int spi_disable_blockprotect_at25f(struct flashctx *flash)
{
- return spi_disable_blockprotect_generic(flash, 0x0C, 1 << 7, 0);
+ return spi_disable_blockprotect_generic(flash, 0x0C, 1 << 7, 0, 0xFF);
}
int spi_disable_blockprotect_at25f512a(struct flashctx *flash)
{
- return spi_disable_blockprotect_generic(flash, 0x04, 1 << 7, 0);
+ return spi_disable_blockprotect_generic(flash, 0x04, 1 << 7, 0, 0xFF);
}
int spi_disable_blockprotect_at25f512b(struct flashctx *flash)
{
- /* spi_disable_blockprotect_at25df is not really the right way to do
- * this, but the side effects of said function work here as well.
- */
- return spi_disable_blockprotect_at25df(flash);
+ return spi_disable_blockprotect_generic(flash, 0x04, 1 << 7, 1 << 4, 0xFF);
}
int spi_disable_blockprotect_at25f4096(struct flashctx *flash)
{
- return spi_disable_blockprotect_generic(flash, 0x1C, 1 << 7, 0);
+ return spi_disable_blockprotect_generic(flash, 0x1C, 1 << 7, 0, 0xFF);
}
int spi_disable_blockprotect_at25fs010(struct flashctx *flash)
{
- return spi_disable_blockprotect_generic(flash, 0x6C, 1 << 7, 0);
+ return spi_disable_blockprotect_generic(flash, 0x6C, 1 << 7, 0, 0xFF);
}
int spi_disable_blockprotect_at25fs040(struct flashctx *flash)
{
- return spi_disable_blockprotect_generic(flash, 0x7C, 1 << 7, 0);
+ return spi_disable_blockprotect_generic(flash, 0x7C, 1 << 7, 0, 0xFF);
}
/* === Intel === */
@@ -538,7 +559,7 @@ int spi_disable_blockprotect_at25fs040(struct flashctx *flash)
/* TODO: Clear P_FAIL and E_FAIL with Clear SR Fail Flags Command (30h) here? */
int spi_disable_blockprotect_s33(struct flashctx *flash)
{
- return spi_disable_blockprotect_generic(flash, 0x1C, 1 << 7, 0);
+ return spi_disable_blockprotect_generic(flash, 0x1C, 1 << 7, 0, 0xFF);
}
int spi_prettyprint_status_register_s33(struct flashctx *flash)
OpenPOWER on IntegriCloud