Refactor Winbond W39*, ST M50*, PMC Pm49*, SST 49LF00*, ... locking Unlock ST M50FW002 correctly.
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net ---
Rebased, slightly refined and also a bit checked :)
IMHO TODOs: - separate/different file for the *regspace2* functions - write_lockbits_49lfxxxc: this looks overly complicated, maybe try to use a scheme similar to unlock_stm50fw002, but with taking the total size into account by making the first sector size filling the chip up or so... - the RWLOCK and LOCKDOWN defines should be in chipdrivers.h because other files need to know about it!
82802ab.c | 6 +++--- chipdrivers.h | 3 +++ flashchips.c | 9 +++++--- pm49fl00x.c | 20 +++++++---------- sst49lfxxxc.c | 22 +++++-------------- stm50flw0x0x.c | 48 +++++++++++++++++++++++------------------ w39.c | 66 ++++++++++++++++++++++++++++++++++++++++---------------- 7 files changed, 100 insertions(+), 74 deletions(-)
diff --git a/82802ab.c b/82802ab.c index 608995d..c06568f 100644 --- a/82802ab.c +++ b/82802ab.c @@ -109,11 +109,11 @@ uint8_t wait_82802ab(struct flashctx *flash)
int unlock_82802ab(struct flashctx *flash) { - int i; - //chipaddr wrprotect = flash->virtual_registers + page + 2; + unsigned int i;
for (i = 0; i < flash->chip->total_size * 1024; i+= flash->chip->page_size) - chip_writeb(flash, 0, flash->virtual_registers + i + 2); + if (changelock_regspace2_block(flash, i, 0x00)) + return -1;
return 0; } diff --git a/chipdrivers.h b/chipdrivers.h index b43e16e..d5facc6 100644 --- a/chipdrivers.h +++ b/chipdrivers.h @@ -150,6 +150,8 @@ int printlock_w39v080fa_dual(struct flashctx *flash); int unlock_w39v040fb(struct flashctx *flash); int unlock_w39v080fa(struct flashctx *flash); int printlock_at49f(struct flashctx *flash); +int printlock_regspace2_block(struct flashctx *flash, int offset); +int changelock_regspace2_block(const struct flashctx *flash, int offset, uint8_t bits);
/* w29ee011.c */ int probe_w29ee011(struct flashctx *flash); @@ -157,6 +159,7 @@ int probe_w29ee011(struct flashctx *flash); /* stm50flw0x0x.c */ int erase_sector_stm50flw0x0x(struct flashctx *flash, unsigned int block, unsigned int blocksize); int unlock_stm50flw0x0x(struct flashctx *flash); +int unlock_stm50fw002(struct flashctx *flash);
/* en29lv640b.c */ int probe_en29lv640b(struct flashctx *flash); diff --git a/flashchips.c b/flashchips.c index 8fe50bd..7c4e827 100644 --- a/flashchips.c +++ b/flashchips.c @@ -8151,9 +8151,9 @@ const struct flashchip flashchips[] = { .total_size = 256, .page_size = 64 * 1024, .feature_bits = FEATURE_REGISTERMAP, - .tested = TEST_UNTESTED, + .tested = TEST_OK_PREW, .probe = probe_82802ab, - .probe_timing = TIMING_IGNORED, /* routine doesn't use probe_timing (sst49lfxxxc.c) */ + .probe_timing = TIMING_IGNORED, /* routine doesn't use probe_timing (82802ab.c) */ .block_erasers = { { @@ -8164,9 +8164,12 @@ const struct flashchip flashchips[] = { {16 * 1024, 1}, }, .block_erase = erase_block_82802ab, + }, { + .eraseblocks = { {256 * 1024, 1}, }, + .block_erase = NULL, /* Only in A/A mux mode */ } }, - .unlock = unlock_stm50flw0x0x, + .unlock = unlock_stm50fw002, .write = write_82802ab, .read = read_memmapped, .voltage = {3000, 3600}, /* Also has 12V fast program & erase */ diff --git a/pm49fl00x.c b/pm49fl00x.c index fe28d2b..2f81f1f 100644 --- a/pm49fl00x.c +++ b/pm49fl00x.c @@ -21,33 +21,29 @@ */
#include "flash.h" +#include "chipdrivers.h"
-static void write_lockbits_49fl00x(const struct flashctx *flash, - unsigned int size, unsigned char bits, - unsigned int block_size) +static int write_lockbits_49fl00x(const struct flashctx *flash, unsigned char bits, unsigned int block_size) { - unsigned int i, left = size; - chipaddr bios = flash->virtual_registers; + unsigned int i, left = flash->chip->total_size * 1024;
for (i = 0; left >= block_size; i++, left -= block_size) { /* pm49fl002 */ if (block_size == 16384 && i % 2) continue;
- chip_writeb(flash, bits, bios + (i * block_size) + 2); + if (changelock_regspace2_block(flash, i * block_size, bits)) + return -1; } + return 0; }
int unlock_49fl00x(struct flashctx *flash) { - write_lockbits_49fl00x(flash, flash->chip->total_size * 1024, 0, - flash->chip->page_size); - return 0; + return write_lockbits_49fl00x(flash, 0, flash->chip->page_size); }
int lock_49fl00x(struct flashctx *flash) { - write_lockbits_49fl00x(flash, flash->chip->total_size * 1024, 1, - flash->chip->page_size); - return 0; + return write_lockbits_49fl00x(flash, 1, flash->chip->page_size); } diff --git a/sst49lfxxxc.c b/sst49lfxxxc.c index bb21559..14063cb 100644 --- a/sst49lfxxxc.c +++ b/sst49lfxxxc.c @@ -23,18 +23,6 @@ #include "flash.h" #include "chipdrivers.h"
-static int write_lockbits_block_49lfxxxc(struct flashctx *flash, - unsigned long address, - unsigned char bits) -{ - unsigned long lock = flash->virtual_registers + address + 2; - msg_cdbg("lockbits at address=0x%08lx is 0x%01x\n", lock, - chip_readb(flash, lock)); - chip_writeb(flash, bits, lock); - - return 0; -} - static int write_lockbits_49lfxxxc(struct flashctx *flash, unsigned char bits) { chipaddr registers = flash->virtual_registers; @@ -43,16 +31,16 @@ static int write_lockbits_49lfxxxc(struct flashctx *flash, unsigned char bits)
msg_cdbg("\nbios=0x%08lx\n", registers); for (i = 0; left > 65536; i++, left -= 65536) { - write_lockbits_block_49lfxxxc(flash, i * 65536, bits); + changelock_regspace2_block(flash, i * 65536, bits); } address = i * 65536; - write_lockbits_block_49lfxxxc(flash, address, bits); + changelock_regspace2_block(flash, address, bits); address += 32768; - write_lockbits_block_49lfxxxc(flash, address, bits); + changelock_regspace2_block(flash, address, bits); address += 8192; - write_lockbits_block_49lfxxxc(flash, address, bits); + changelock_regspace2_block(flash, address, bits); address += 8192; - write_lockbits_block_49lfxxxc(flash, address, bits); + changelock_regspace2_block(flash, address, bits);
return 0; } diff --git a/stm50flw0x0x.c b/stm50flw0x0x.c index e6c7c05..a710b8f 100644 --- a/stm50flw0x0x.c +++ b/stm50flw0x0x.c @@ -3,6 +3,7 @@ * * Copyright (C) 2008 Claus Gindhart claus.gindhart@kontron.com * Copyright (C) 2009 Sean Nelson audiohacked@gmail.com + * Copyright (C) 2011 Carl-Daniel Hailfinger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -36,10 +37,8 @@ * The ST M50FLW080B and STM50FLW080B chips have to be unlocked, * before you can erase them or write to them. */ -static int unlock_block_stm50flw0x0x(struct flashctx *flash, int offset) +static int unlock_block_stm50flw0x0x(struct flashctx *flash, unsigned int offset) { - chipaddr wrprotect = flash->virtual_registers + 2; - static const uint8_t unlock_sector = 0x00; int j;
/* @@ -58,25 +57,11 @@ static int unlock_block_stm50flw0x0x(struct flashctx *flash, int offset) || (offset == 0xF0000)) {
// unlock each 4k-sector - for (j = 0; j < 0x10000; j += 0x1000) { - msg_cdbg("unlocking at 0x%x\n", offset + j); - chip_writeb(flash, unlock_sector, - wrprotect + offset + j); - if (chip_readb(flash, wrprotect + offset + j) != - unlock_sector) { - msg_cerr("Cannot unlock sector @ 0x%x\n", - offset + j); + for (j = 0; j < 0x10000; j += 0x1000) + if (changelock_regspace2_block(flash, offset + j, 0x00)) return -1; - } - } - } else { - msg_cdbg("unlocking at 0x%x\n", offset); - chip_writeb(flash, unlock_sector, wrprotect + offset); - if (chip_readb(flash, wrprotect + offset) != unlock_sector) { - msg_cerr("Cannot unlock sector @ 0x%x\n", offset); + } else if (changelock_regspace2_block(flash, offset, 0x00)) return -1; - } - }
return 0; } @@ -86,7 +71,7 @@ int unlock_stm50flw0x0x(struct flashctx *flash) int i;
for (i = 0; i < flash->chip->total_size * 1024; i+= flash->chip->page_size) { - if(unlock_block_stm50flw0x0x(flash, i)) { + if (unlock_block_stm50flw0x0x(flash, i)) { msg_cerr("UNLOCK FAILED!\n"); return -1; } @@ -95,6 +80,27 @@ int unlock_stm50flw0x0x(struct flashctx *flash) return 0; }
+/* FIXME: Should this be moved to a generic walk_unlockregions()? */ +int unlock_stm50fw002(struct flashctx *flash) +{ + static const struct eraseblock unlockregions[4] = { + {64 * 1024, 3}, + {32 * 1024, 1}, + {8 * 1024, 2}, + {16 * 1024, 1}}; + int i, j; + int addr = 0; + + for (i = 0; i < 4; i++) { + for (j = 0; j < unlockregions[i].count; j++) { + if (changelock_regspace2_block(flash, addr, 0x00)) + return 1; + addr += unlockregions[i].size; + } + } + return 0; +} + /* This function is unused. */ int erase_sector_stm50flw0x0x(struct flashctx *flash, unsigned int sector, unsigned int sectorsize) diff --git a/w39.c b/w39.c index da61d23..e903e24 100644 --- a/w39.c +++ b/w39.c @@ -21,14 +21,15 @@
#include "flash.h"
-static int printlock_w39_fwh_block(struct flashctx *flash, unsigned int offset) +#define RWLOCK ((1 << 2) | (1 << 0)) +#define LOCKDOWN (1 << 1) + +static int printlock_regspace2_block(struct flashctx *flash, unsigned int offset) { chipaddr wrprotect = flash->virtual_registers + offset + 2; - uint8_t locking; - - locking = chip_readb(flash, wrprotect); + uint8_t state = chip_readb(flash, wrprotect); msg_cdbg("Lock status of block at 0x%08x is ", offset); - switch (locking & 0x7) { + switch (state & 0x7) { case 0: msg_cdbg("Full Access.\n"); break; @@ -36,7 +37,7 @@ static int printlock_w39_fwh_block(struct flashctx *flash, unsigned int offset) msg_cdbg("Write Lock (Default State).\n"); break; case 2: - msg_cdbg("Locked Open (Full Access, Lock Down).\n"); + msg_cdbg("Locked Open (Full Access, Locked Down).\n"); break; case 3: msg_cerr("Error: Write Lock, Locked Down.\n"); @@ -56,24 +57,53 @@ static int printlock_w39_fwh_block(struct flashctx *flash, unsigned int offset) }
/* Read or write lock present? */ - return (locking & ((1 << 2) | (1 << 0))) ? -1 : 0; + return (state & RWLOCK) ? -1 : 0; }
-static int unlock_w39_fwh_block(struct flashctx *flash, unsigned int offset) +int changelock_regspace2_block(const struct flashctx *flash, unsigned int offset, uint8_t new_bits) { chipaddr wrprotect = flash->virtual_registers + offset + 2; - uint8_t locking; + uint8_t old;
- locking = chip_readb(flash, wrprotect); - /* Read or write lock present? */ - if (locking & ((1 << 2) | (1 << 0))) { + if (new_bits & 0xf8) { // FIXME: define a mask? + msg_cerr("Invalid locking change 0x%02x requested at 0x%08x! " + "Please report a bug at flashrom@flashrom.org\n", + new_bits, offset); + return -1; + } + old = chip_readb(flash, wrprotect); + /* Did we request a change of read/write/lockdown? */ + if (((old ^ new_bits) & (RWLOCK | LOCKDOWN)) == 0) { + msg_cdbg2("Locking status at 0x%08x not changed\n", offset); + return 0; + } + /* Change read or write lock? */ + if ((old ^ new_bits) & RWLOCK) { /* Lockdown active? */ - if (locking & (1 << 1)) { - msg_cerr("Can't unlock block at 0x%08x!\n", offset); + if (old & LOCKDOWN) { + msg_cerr("Can't change locking status at 0x%08x due to lockdown!\n", offset); return -1; } else { - msg_cdbg("Unlocking block at 0x%08x\n", offset); - chip_writeb(flash, 0, wrprotect); + /* Do not lockdown yet. */ + msg_cdbg("Changing locking status at 0x%08x to 0x%02x\n", offset, new_bits & RWLOCK); + chip_writeb(flash, new_bits & RWLOCK, wrprotect); + if (chip_readb(flash, wrprotect) != (new_bits & RWLOCK)) { + msg_cerr("Locking status change FAILED at 0x%08x!\n", offset); + return -1; + } + } + } + if ((old & LOCKDOWN) && !(new_bits & LOCKDOWN)) { + msg_cerr("Lockdown can't be removed at 0x%08x!\n", offset); + /* FIXME: Is this really an error? */ + return -1; + } + if (!(old & LOCKDOWN) && (new_bits & LOCKDOWN)) { + msg_cdbg("Enabling lockdown at 0x%08x\n", offset); + chip_writeb(flash, new_bits, wrprotect); + if (chip_readb(flash, wrprotect) != new_bits) { + msg_cerr("Lockdown status change FAILED at 0x%08x!\n", offset); + return -1; } }
@@ -143,7 +173,7 @@ static int printlock_w39_fwh(struct flashctx *flash) /* Print lock status of the complete chip */ for (i = 0; i < total_size; i += flash->chip->page_size) - ret |= printlock_w39_fwh_block(flash, i); + ret |= printlock_regspace2_block(flash, i);
return ret; } @@ -154,7 +184,7 @@ static int unlock_w39_fwh(struct flashctx *flash) /* Unlock the complete chip */ for (i = 0; i < total_size; i += flash->chip->page_size) - if (unlock_w39_fwh_block(flash, i)) + if (changelock_regspace2_block(flash, i, 0x00)) return -1;
return 0;