Add detailed status register printing and unlocking for all ATMEL AT25* chips.
Some chips require EWSR before WRSR, others require WREN before WRSR, and some support both variants. Add feature_bits to select the correct SPI command, and default to EWSR.
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Index: flashrom-writeprotect_atmel_at25/flash.h =================================================================== --- flashrom-writeprotect_atmel_at25/flash.h (Revision 1109) +++ flashrom-writeprotect_atmel_at25/flash.h (Arbeitskopie) @@ -167,8 +167,9 @@
/* * How many different erase functions do we have per chip? + * Atmel AT25FS010 has 6 different functions. */ -#define NUM_ERASEFUNCTIONS 5 +#define NUM_ERASEFUNCTIONS 6
#define FEATURE_REGISTERMAP (1 << 0) #define FEATURE_BYTEWRITES (1 << 1) @@ -180,6 +181,9 @@ #define FEATURE_ADDR_2AA (1 << 2) #define FEATURE_ADDR_AAA (2 << 2) #define FEATURE_ADDR_SHIFTED (1 << 5) +#define FEATURE_WRSR_EWSR (1 << 6) +#define FEATURE_WRSR_WREN (1 << 7) +#define FEATURE_WRSR_EITHER (FEATURE_WRSR_EWSR | FEATURE_WRSR_WREN)
struct flashchip { const char *vendor; Index: flashrom-writeprotect_atmel_at25/spi25.c =================================================================== --- flashrom-writeprotect_atmel_at25/spi25.c (Revision 1109) +++ flashrom-writeprotect_atmel_at25/spi25.c (Arbeitskopie) @@ -311,8 +311,17 @@ }
/* Prettyprint the status register. Common definitions. */ -void spi_prettyprint_status_register_common(uint8_t status) +static void spi_prettyprint_status_register_welwip(uint8_t status) { + msg_cdbg("Chip status register: Write Enable Latch (WEL) is " + "%sset\n", (status & (1 << 1)) ? "" : "not "); + msg_cdbg("Chip status register: Write In Progress (WIP/BUSY) is " + "%sset\n", (status & (1 << 0)) ? "" : "not "); +} + +/* Prettyprint the status register. Common definitions. */ +static void spi_prettyprint_status_register_common(uint8_t status) +{ msg_cdbg("Chip status register: Bit 5 / Block Protect 3 (BP3) is " "%sset\n", (status & (1 << 5)) ? "" : "not "); msg_cdbg("Chip status register: Bit 4 / Block Protect 2 (BP2) is " @@ -321,10 +330,7 @@ "%sset\n", (status & (1 << 3)) ? "" : "not "); msg_cdbg("Chip status register: Bit 2 / Block Protect 0 (BP0) is " "%sset\n", (status & (1 << 2)) ? "" : "not "); - msg_cdbg("Chip status register: Write Enable Latch (WEL) is " - "%sset\n", (status & (1 << 1)) ? "" : "not "); - msg_cdbg("Chip status register: Write In Progress (WIP/BUSY) is " - "%sset\n", (status & (1 << 0)) ? "" : "not "); + spi_prettyprint_status_register_welwip(status); }
/* Prettyprint the status register. Works for @@ -337,6 +343,121 @@ spi_prettyprint_status_register_common(status); }
+/* Prettyprint the status register. Common definitions. */ +static void spi_prettyprint_status_register_at25_srplepewpp(uint8_t status) +{ + msg_cdbg("Chip status register: Sector Protection Register Lock (SRPL) " + "is %sset\n", (status & (1 << 7)) ? "" : "not "); + msg_cdbg("Chip status register: Bit 6 " + "is %sset\n", (status & (1 << 6)) ? "" : "not "); + msg_cdbg("Chip status register: Erase/Program Error (EPE) " + "is %sset\n", (status & (1 << 5)) ? "" : "not "); + msg_cdbg("Chip status register: WP# pin (WPP) " + "is %sactive\n", (status & (1 << 4)) ? "not " : ""); +} + +int spi_prettyprint_status_register_at25df(struct flashchip *flash) +{ + uint8_t status; + + status = spi_read_status_register(); + msg_cdbg("Chip status register is %02x\n", status); + + spi_prettyprint_status_register_at25_srplepewpp(status); + msg_cdbg("Chip status register: Software Protection Status (SWP): "); + switch (status & (3 << 2)) { + case 0x0: + msg_cdbg("no sectors are protected\n"); + break; + case 0x1: + msg_cdbg("some sectors are protected\n"); + /* FIXME: Read individual Sector Protection Registers. */ + break; + case 0x3: + msg_cdbg("all sectors are protected\n"); + break; + default: + msg_cdbg("reserved for future use\n"); + break; + } + spi_prettyprint_status_register_welwip(status); + return 0; +} + +int spi_prettyprint_status_register_at25df_sec(struct flashchip *flash) +{ + /* FIXME: We should check the security lockdown. */ + msg_cdbg("Ignoring security lockdown (if present)\n"); + msg_cdbg("Ignoring status register byte 2\n"); + return spi_prettyprint_status_register_at25df(flash); +} + +int spi_prettyprint_status_register_at25f(struct flashchip *flash) +{ + uint8_t status; + + status = spi_read_status_register(); + msg_cdbg("Chip status register is %02x\n", status); + + spi_prettyprint_status_register_at25_srplepewpp(status); + msg_cdbg("Chip status register: Bit 3 " + "is %sset\n", (status & (1 << 3)) ? "" : "not "); + msg_cdbg("Chip status register: Block Protect 0 (BP0) is " + "%sset, %s sectors are protected\n", + (status & (1 << 2)) ? "" : "not ", + (status & (1 << 2)) ? "all" : "no"); + spi_prettyprint_status_register_welwip(status); + return 0; +} + +int spi_prettyprint_status_register_at25fs010(struct flashchip *flash) +{ + uint8_t status; + + status = spi_read_status_register(); + msg_cdbg("Chip status register is %02x\n", status); + + msg_cdbg("Chip status register: Status Register Write Protect (WPEN) " + "is %sset\n", (status & (1 << 7)) ? "" : "not "); + msg_cdbg("Chip status register: Bit 6 / Block Protect 4 (BP4) is " + "%sset\n", (status & (1 << 6)) ? "" : "not "); + msg_cdbg("Chip status register: Bit 5 / Block Protect 3 (BP3) is " + "%sset\n", (status & (1 << 5)) ? "" : "not "); + msg_cdbg("Chip status register: Bit 4 is " + "%sset\n", (status & (1 << 4)) ? "" : "not "); + msg_cdbg("Chip status register: Bit 3 / Block Protect 1 (BP1) is " + "%sset\n", (status & (1 << 3)) ? "" : "not "); + msg_cdbg("Chip status register: Bit 2 / Block Protect 0 (BP0) is " + "%sset\n", (status & (1 << 2)) ? "" : "not "); + /* FIXME: Pretty-print detailed sector protection status. */ + spi_prettyprint_status_register_welwip(status); + return 0; +} + +int spi_prettyprint_status_register_at25fs040(struct flashchip *flash) +{ + uint8_t status; + + status = spi_read_status_register(); + msg_cdbg("Chip status register is %02x\n", status); + + msg_cdbg("Chip status register: Status Register Write Protect (WPEN) " + "is %sset\n", (status & (1 << 7)) ? "" : "not "); + msg_cdbg("Chip status register: Bit 6 / Block Protect 4 (BP4) is " + "%sset\n", (status & (1 << 6)) ? "" : "not "); + msg_cdbg("Chip status register: Bit 5 / Block Protect 3 (BP3) is " + "%sset\n", (status & (1 << 5)) ? "" : "not "); + msg_cdbg("Chip status register: Bit 4 / Block Protect 2 (BP2) is " + "%sset\n", (status & (1 << 4)) ? "" : "not "); + msg_cdbg("Chip status register: Bit 3 / Block Protect 1 (BP1) is " + "%sset\n", (status & (1 << 3)) ? "" : "not "); + msg_cdbg("Chip status register: Bit 2 / Block Protect 0 (BP0) is " + "%sset\n", (status & (1 << 2)) ? "" : "not "); + /* FIXME: Pretty-print detailed sector protection status. */ + spi_prettyprint_status_register_welwip(status); + return 0; +} + /* Prettyprint the status register. Works for * ST M25P series * MX MX25L series @@ -731,12 +852,12 @@ * This is according the SST25VF016 datasheet, who knows it is more * generic that this... */ -int spi_write_status_register(int status) +static int spi_write_status_register_ewsr(struct flashchip *flash, int status) { int result; struct spi_command cmds[] = { { - /* FIXME: WRSR requires either EWSR or WREN depending on chip type. */ + /* WRSR requires either EWSR or WREN depending on chip type. */ .writecnt = JEDEC_EWSR_OUTSIZE, .writearr = (const unsigned char[]){ JEDEC_EWSR }, .readcnt = 0, @@ -758,9 +879,59 @@ msg_cerr("%s failed during command execution\n", __func__); } + /* WRSR performs a self-timed erase before the changes take effect. */ + programmer_delay(100 * 1000); return result; }
+static int spi_write_status_register_wren(struct flashchip *flash, int status) +{ + int result; + struct spi_command cmds[] = { + { + /* WRSR requires either EWSR or WREN depending on chip type. */ + .writecnt = JEDEC_WREN_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WREN }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = JEDEC_WRSR_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WRSR, (unsigned char) status }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = 0, + .writearr = NULL, + .readcnt = 0, + .readarr = NULL, + }}; + + result = spi_send_multicommand(cmds); + if (result) { + msg_cerr("%s failed during command execution\n", + __func__); + } + /* WRSR performs a self-timed erase before the changes take effect. */ + programmer_delay(100 * 1000); + return result; +} + +static int spi_write_status_register(struct flashchip *flash, int status) +{ + int ret = 1; + + if (!(flash->feature_bits & (FEATURE_WRSR_WREN | FEATURE_WRSR_EWSR))) { + msg_cdbg("Missing status register write definition, assuming " + "EWSR is needed\n"); + flash->feature_bits |= FEATURE_WRSR_EWSR; + } + if (flash->feature_bits & FEATURE_WRSR_WREN) + ret = spi_write_status_register_wren(flash, status); + if (ret && (flash->feature_bits & FEATURE_WRSR_EWSR)) + ret = spi_write_status_register_ewsr(flash, status); + return ret; +} + int spi_byte_program(int addr, uint8_t databyte) { int result; @@ -843,29 +1014,156 @@ return result; }
+/* A generic brute-force block protection disable works like this: + * Write 0x00 to the status register. Check if any locks are still set (that + * part is chip specific). Repeat once. + */ int spi_disable_blockprotect(struct flashchip *flash) { uint8_t status; int result;
status = spi_read_status_register(); - /* If there is block protection in effect, unprotect it first. */ + /* If block protection is disabled, stop here. */ + if ((status & 0x3c) == 0) + return 0; + + msg_cdbg("Some block protection in effect, disabling\n"); + result = spi_write_status_register(flash, status & ~0x3c); + if (result) { + msg_cerr("spi_write_status_register failed\n"); + return result; + } + status = spi_read_status_register(); if ((status & 0x3c) != 0) { - msg_cdbg("Some block protection in effect, disabling\n"); - result = spi_write_status_register(status & ~0x3c); + msg_cerr("Block protection could not be disabled!\n"); + return 1; + } + return 0; +} + +int spi_disable_blockprotect_at25df(struct flashchip *flash) +{ + uint8_t status; + int result; + + status = spi_read_status_register(); + /* If block protection is disabled, stop here. */ + if ((status & (3 << 2)) == 0) + return 0; + + msg_cdbg("Some block protection in effect, disabling\n"); + if (status & (1 << 7)) { + msg_cdbg("Need to disable Sector Protection Register Lock\n"); + if ((status & (1 << 4)) == 0) { + msg_cerr("WP# pin is active, disabling " + "write protection is impossible.\n"); + return 1; + } + /* All bits except bit 7 (SPRL) are readonly. */ + result = spi_write_status_register(flash, status & ~(1 << 7)); if (result) { msg_cerr("spi_write_status_register failed\n"); return result; } - status = spi_read_status_register(); - if ((status & 0x3c) != 0) { - msg_cerr("Block protection could not be disabled!\n"); - return 1; + + } + /* Global unprotect. Make sure to mask SPRL as well. */ + result = spi_write_status_register(flash, status & ~0xbc); + if (result) { + msg_cerr("spi_write_status_register failed\n"); + return result; + } + status = spi_read_status_register(); + if ((status & (3 << 2)) != 0) { + msg_cerr("Block protection could not be disabled!\n"); + return 1; + } + return 0; +} + +int spi_disable_blockprotect_at25df_sec(struct flashchip *flash) +{ + /* FIXME: We should check the security lockdown. */ + msg_cinfo("Ignoring security lockdown (if present)\n"); + return spi_disable_blockprotect_at25df(flash); +} + +int spi_disable_blockprotect_at25f(struct flashchip *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); +} + +int spi_disable_blockprotect_at25fs010(struct flashchip *flash) +{ + uint8_t status; + int result; + + status = spi_read_status_register(); + /* If block protection is disabled, stop here. */ + if ((status & 0x6c) == 0) + return 0; + + msg_cdbg("Some block protection in effect, disabling\n"); + if (status & (1 << 7)) { + msg_cdbg("Need to disable Status Register Write Protect\n"); + /* Clear bit 7 (WPEN). */ + result = spi_write_status_register(flash, status & ~(1 << 7)); + if (result) { + msg_cerr("spi_write_status_register failed\n"); + return result; } } + /* Global unprotect. Make sure to mask WPEN as well. */ + result = spi_write_status_register(flash, status & ~0xec); + if (result) { + msg_cerr("spi_write_status_register failed\n"); + return result; + } + status = spi_read_status_register(); + if ((status & 0x6c) != 0) { + msg_cerr("Block protection could not be disabled!\n"); + return 1; + } return 0; } +int spi_disable_blockprotect_at25fs040(struct flashchip *flash) +{ + uint8_t status; + int result;
+ status = spi_read_status_register(); + /* If block protection is disabled, stop here. */ + if ((status & 0x7c) == 0) + return 0; + + msg_cdbg("Some block protection in effect, disabling\n"); + if (status & (1 << 7)) { + msg_cdbg("Need to disable Status Register Write Protect\n"); + /* Clear bit 7 (WPEN). */ + result = spi_write_status_register(flash, status & ~(1 << 7)); + if (result) { + msg_cerr("spi_write_status_register failed\n"); + return result; + } + } + /* Global unprotect. Make sure to mask WPEN as well. */ + result = spi_write_status_register(flash, status & ~0xfc); + if (result) { + msg_cerr("spi_write_status_register failed\n"); + return result; + } + status = spi_read_status_register(); + if ((status & 0x7c) != 0) { + msg_cerr("Block protection could not be disabled!\n"); + return 1; + } + return 0; +} + int spi_nbyte_read(int address, uint8_t *bytes, int len) { const unsigned char cmd[JEDEC_READ_OUTSIZE] = { Index: flashrom-writeprotect_atmel_at25/flashchips.c =================================================================== --- flashrom-writeprotect_atmel_at25/flashchips.c (Revision 1109) +++ flashrom-writeprotect_atmel_at25/flashchips.c (Arbeitskopie) @@ -313,6 +313,7 @@ .model_id = AT_25DF021, .total_size = 256, .page_size = 256, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, @@ -335,7 +336,8 @@ .block_erase = spi_block_erase_c7, } }, - .unlock = spi_disable_blockprotect, + .printlock = spi_prettyprint_status_register_at25df, + .unlock = spi_disable_blockprotect_at25df, .write = spi_chip_write_256, .read = spi_chip_read, }, @@ -348,6 +350,7 @@ .model_id = AT_25DF041A, .total_size = 512, .page_size = 256, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, @@ -370,7 +373,8 @@ .block_erase = spi_block_erase_c7, } }, - .unlock = spi_disable_blockprotect, + .printlock = spi_prettyprint_status_register_at25df, + .unlock = spi_disable_blockprotect_at25df, .write = spi_chip_write_256, .read = spi_chip_read, }, @@ -383,6 +387,7 @@ .model_id = AT_25DF081, .total_size = 1024, .page_size = 256, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, @@ -405,19 +410,58 @@ .block_erase = spi_block_erase_c7, } }, - .unlock = spi_disable_blockprotect, + .printlock = spi_prettyprint_status_register_at25df, + .unlock = spi_disable_blockprotect_at25df, .write = spi_chip_write_256, .read = spi_chip_read, },
{ .vendor = "Atmel", + .name = "AT25DF081A", + .bustype = CHIP_BUSTYPE_SPI, + .manufacture_id = ATMEL_ID, + .model_id = AT_25DF081A, + .total_size = 1024, + .page_size = 256, + .feature_bits = FEATURE_WRSR_WREN, + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, + .block_erasers = + { + { + .eraseblocks = { {4 * 1024, 256} }, + .block_erase = spi_block_erase_20, + }, { + .eraseblocks = { {32 * 1024, 32} }, + .block_erase = spi_block_erase_52, + }, { + .eraseblocks = { {64 * 1024, 16} }, + .block_erase = spi_block_erase_d8, + }, { + .eraseblocks = { {1024 * 1024, 1} }, + .block_erase = spi_block_erase_60, + }, { + .eraseblocks = { {1024 * 1024, 1} }, + .block_erase = spi_block_erase_c7, + } + }, + .printlock = spi_prettyprint_status_register_at25df_sec, + .unlock = spi_disable_blockprotect_at25df_sec, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, + + { + .vendor = "Atmel", .name = "AT25DF161", .bustype = CHIP_BUSTYPE_SPI, .manufacture_id = ATMEL_ID, .model_id = AT_25DF161, .total_size = 2048, .page_size = 256, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, @@ -440,7 +484,8 @@ .block_erase = spi_block_erase_c7, } }, - .unlock = spi_disable_blockprotect, + .printlock = spi_prettyprint_status_register_at25df_sec, + .unlock = spi_disable_blockprotect_at25df_sec, .write = spi_chip_write_256, .read = spi_chip_read, }, @@ -453,6 +498,7 @@ .model_id = AT_25DF321, .total_size = 4096, .page_size = 256, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_OK_PRW, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, @@ -475,7 +521,8 @@ .block_erase = spi_block_erase_c7, } }, - .unlock = spi_disable_blockprotect, + .printlock = spi_prettyprint_status_register_at25df, + .unlock = spi_disable_blockprotect_at25df, .write = spi_chip_write_256, .read = spi_chip_read, }, @@ -488,6 +535,7 @@ .model_id = AT_25DF321A, .total_size = 4096, .page_size = 256, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, @@ -510,7 +558,8 @@ .block_erase = spi_block_erase_c7, } }, - .unlock = spi_disable_blockprotect, + .printlock = spi_prettyprint_status_register_at25df_sec, + .unlock = spi_disable_blockprotect_at25df_sec, .write = spi_chip_write_256, .read = spi_chip_read, }, @@ -523,6 +572,7 @@ .model_id = AT_25DF641, .total_size = 8192, .page_size = 256, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, @@ -545,19 +595,58 @@ .block_erase = spi_block_erase_c7, } }, - .unlock = spi_disable_blockprotect, + .printlock = spi_prettyprint_status_register_at25df_sec, + .unlock = spi_disable_blockprotect_at25df_sec, .write = spi_chip_write_256, .read = spi_chip_read, },
{ .vendor = "Atmel", + .name = "AT25DQ161", + .bustype = CHIP_BUSTYPE_SPI, + .manufacture_id = ATMEL_ID, + .model_id = AT_25DQ161, + .total_size = 2048, + .page_size = 256, + .feature_bits = FEATURE_WRSR_WREN, + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, + .block_erasers = + { + { + .eraseblocks = { {4 * 1024, 512} }, + .block_erase = spi_block_erase_20, + }, { + .eraseblocks = { {32 * 1024, 64} }, + .block_erase = spi_block_erase_52, + }, { + .eraseblocks = { {64 * 1024, 32} }, + .block_erase = spi_block_erase_d8, + }, { + .eraseblocks = { {2 * 1024 * 1024, 1} }, + .block_erase = spi_block_erase_60, + }, { + .eraseblocks = { {2 * 1024 * 1024, 1} }, + .block_erase = spi_block_erase_c7, + } + }, + .printlock = spi_prettyprint_status_register_at25df_sec, + .unlock = spi_disable_blockprotect_at25df_sec, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, + + { + .vendor = "Atmel", .name = "AT25F512B", .bustype = CHIP_BUSTYPE_SPI, .manufacture_id = ATMEL_ID, .model_id = AT_25F512B, .total_size = 64, .page_size = 256, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, @@ -580,7 +669,8 @@ .block_erase = spi_block_erase_c7, } }, - .unlock = spi_disable_blockprotect, + .printlock = spi_prettyprint_status_register_at25f, + .unlock = spi_disable_blockprotect_at25f, .write = spi_chip_write_256, .read = spi_chip_read, }, @@ -602,6 +692,9 @@ .eraseblocks = { {4 * 1024, 32} }, .block_erase = spi_block_erase_20, }, { + .eraseblocks = { {4 * 1024, 32} }, + .block_erase = spi_block_erase_d7, + }, { .eraseblocks = { {32 * 1024, 4} }, .block_erase = spi_block_erase_52, }, { @@ -615,7 +708,8 @@ .block_erase = spi_block_erase_c7, } }, - .unlock = spi_disable_blockprotect, + .printlock = spi_prettyprint_status_register_at25fs010, + .unlock = spi_disable_blockprotect_at25fs010, .write = spi_chip_write_256, .read = spi_chip_read, }, @@ -650,7 +744,8 @@ .block_erase = spi_block_erase_c7, } }, - .unlock = spi_disable_blockprotect, + .printlock = spi_prettyprint_status_register_at25fs040, + .unlock = spi_disable_blockprotect_at25fs040, .write = spi_chip_write_256, .read = spi_chip_read, }, Index: flashrom-writeprotect_atmel_at25/flashchips.h =================================================================== --- flashrom-writeprotect_atmel_at25/flashchips.h (Revision 1109) +++ flashrom-writeprotect_atmel_at25/flashchips.h (Arbeitskopie) @@ -110,12 +110,19 @@ #define AT_25DF021 0x4300 #define AT_25DF041A 0x4401 #define AT_25DF081 0x4502 +#define AT_25DF081A 0x4501 /* Yes, 81A has a lower number than 81 */ #define AT_25DF161 0x4602 #define AT_25DF321 0x4700 /* Same as 26DF321 */ #define AT_25DF321A 0x4701 #define AT_25DF641 0x4800 -#define AT_25F512A 0x65 /* Needs special RDID. AT25F512A_RDID 15 1d */ +#define AT_25DQ161 0x8600 +#define AT25F512 /* No device ID found in datasheet. Vendor ID + * can be read with AT25F512A_RDID */ +#define AT_25F512A 0x65 /* Needs AT25F512A_RDID */ #define AT_25F512B 0x6500 +#define AT25F1024 /* No device ID found in datasheet. Vendor ID + * can be read with AT25F512A_RDID */ +#define AT_25F1024A 0x60 /* Needs AT25F512A_RDID */ #define AT_25FS010 0x6601 #define AT_25FS040 0x6604 #define AT_26DF041 0x4400 Index: flashrom-writeprotect_atmel_at25/spi.c =================================================================== --- flashrom-writeprotect_atmel_at25/spi.c (Revision 1109) +++ flashrom-writeprotect_atmel_at25/spi.c (Arbeitskopie) @@ -29,8 +29,6 @@
enum spi_controller spi_controller = SPI_CONTROLLER_NONE;
-void spi_prettyprint_status_register(struct flashchip *flash); - const struct spi_programmer spi_programmer[] = { { /* SPI_CONTROLLER_NONE */ .command = NULL, Index: flashrom-writeprotect_atmel_at25/spi.h =================================================================== --- flashrom-writeprotect_atmel_at25/spi.h (Revision 1109) +++ flashrom-writeprotect_atmel_at25/spi.h (Arbeitskopie) @@ -31,7 +31,7 @@ #define JEDEC_RDID_INSIZE 0x03
/* AT25F512A has bit 3 as don't care bit in commands */ -#define AT25F512A_RDID 0x15 +#define AT25F512A_RDID 0x15 /* 0x15 or 0x1d */ #define AT25F512A_RDID_OUTSIZE 0x01 #define AT25F512A_RDID_INSIZE 0x02
@@ -123,5 +123,6 @@ #define SPI_INVALID_OPCODE -2 #define SPI_INVALID_ADDRESS -3 #define SPI_INVALID_LENGTH -4 +#define SPI_FLASHROM_BUG -5
#endif /* !__SPI_H__ */ Index: flashrom-writeprotect_atmel_at25/chipdrivers.h =================================================================== --- flashrom-writeprotect_atmel_at25/chipdrivers.h (Revision 1109) +++ flashrom-writeprotect_atmel_at25/chipdrivers.h (Arbeitskopie) @@ -47,7 +47,17 @@ int spi_chip_write_256_new(struct flashchip *flash, uint8_t *buf, int start, int len); int spi_chip_read(struct flashchip *flash, uint8_t *buf, int start, int len); uint8_t spi_read_status_register(void); +int spi_prettyprint_status_register_at25df(struct flashchip *flash); +int spi_prettyprint_status_register_at25df_sec(struct flashchip *flash); +int spi_prettyprint_status_register_at25f(struct flashchip *flash); +int spi_prettyprint_status_register_at25fs010(struct flashchip *flash); +int spi_prettyprint_status_register_at25fs040(struct flashchip *flash); int spi_disable_blockprotect(struct flashchip *flash); +int spi_disable_blockprotect_at25df(struct flashchip *flash); +int spi_disable_blockprotect_at25df_sec(struct flashchip *flash); +int spi_disable_blockprotect_at25f(struct flashchip *flash); +int spi_disable_blockprotect_at25fs010(struct flashchip *flash); +int spi_disable_blockprotect_at25fs040(struct flashchip *flash); int spi_byte_program(int addr, uint8_t databyte); int spi_nbyte_program(int addr, uint8_t *bytes, int len); int spi_nbyte_read(int addr, uint8_t *bytes, int len);
Next try, with one status register printing corner case fixed and with a better changelog.
Add detailed status register printing and unlocking for all ATMEL AT25* chips.
Add support for Atmel AT25DF081A and AT25DQ161.
Some chips require EWSR before WRSR, others require WREN before WRSR, and some support both variants. Add feature_bits to select the correct SPI command, and default to EWSR.
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Index: flashrom-writeprotect_atmel_at25/flash.h =================================================================== --- flashrom-writeprotect_atmel_at25/flash.h (Revision 1109) +++ flashrom-writeprotect_atmel_at25/flash.h (Arbeitskopie) @@ -167,8 +167,9 @@
/* * How many different erase functions do we have per chip? + * Atmel AT25FS010 has 6 different functions. */ -#define NUM_ERASEFUNCTIONS 5 +#define NUM_ERASEFUNCTIONS 6
#define FEATURE_REGISTERMAP (1 << 0) #define FEATURE_BYTEWRITES (1 << 1) @@ -180,6 +181,9 @@ #define FEATURE_ADDR_2AA (1 << 2) #define FEATURE_ADDR_AAA (2 << 2) #define FEATURE_ADDR_SHIFTED (1 << 5) +#define FEATURE_WRSR_EWSR (1 << 6) +#define FEATURE_WRSR_WREN (1 << 7) +#define FEATURE_WRSR_EITHER (FEATURE_WRSR_EWSR | FEATURE_WRSR_WREN)
struct flashchip { const char *vendor; Index: flashrom-writeprotect_atmel_at25/spi25.c =================================================================== --- flashrom-writeprotect_atmel_at25/spi25.c (Revision 1109) +++ flashrom-writeprotect_atmel_at25/spi25.c (Arbeitskopie) @@ -311,8 +311,17 @@ }
/* Prettyprint the status register. Common definitions. */ -void spi_prettyprint_status_register_common(uint8_t status) +static void spi_prettyprint_status_register_welwip(uint8_t status) { + msg_cdbg("Chip status register: Write Enable Latch (WEL) is " + "%sset\n", (status & (1 << 1)) ? "" : "not "); + msg_cdbg("Chip status register: Write In Progress (WIP/BUSY) is " + "%sset\n", (status & (1 << 0)) ? "" : "not "); +} + +/* Prettyprint the status register. Common definitions. */ +static void spi_prettyprint_status_register_common(uint8_t status) +{ msg_cdbg("Chip status register: Bit 5 / Block Protect 3 (BP3) is " "%sset\n", (status & (1 << 5)) ? "" : "not "); msg_cdbg("Chip status register: Bit 4 / Block Protect 2 (BP2) is " @@ -321,10 +330,7 @@ "%sset\n", (status & (1 << 3)) ? "" : "not "); msg_cdbg("Chip status register: Bit 2 / Block Protect 0 (BP0) is " "%sset\n", (status & (1 << 2)) ? "" : "not "); - msg_cdbg("Chip status register: Write Enable Latch (WEL) is " - "%sset\n", (status & (1 << 1)) ? "" : "not "); - msg_cdbg("Chip status register: Write In Progress (WIP/BUSY) is " - "%sset\n", (status & (1 << 0)) ? "" : "not "); + spi_prettyprint_status_register_welwip(status); }
/* Prettyprint the status register. Works for @@ -337,6 +343,121 @@ spi_prettyprint_status_register_common(status); }
+/* Prettyprint the status register. Common definitions. */ +static void spi_prettyprint_status_register_at25_srplepewpp(uint8_t status) +{ + msg_cdbg("Chip status register: Sector Protection Register Lock (SRPL) " + "is %sset\n", (status & (1 << 7)) ? "" : "not "); + msg_cdbg("Chip status register: Bit 6 " + "is %sset\n", (status & (1 << 6)) ? "" : "not "); + msg_cdbg("Chip status register: Erase/Program Error (EPE) " + "is %sset\n", (status & (1 << 5)) ? "" : "not "); + msg_cdbg("Chip status register: WP# pin (WPP) " + "is %sactive\n", (status & (1 << 4)) ? "not " : ""); +} + +int spi_prettyprint_status_register_at25df(struct flashchip *flash) +{ + uint8_t status; + + status = spi_read_status_register(); + msg_cdbg("Chip status register is %02x\n", status); + + spi_prettyprint_status_register_at25_srplepewpp(status); + msg_cdbg("Chip status register: Software Protection Status (SWP): "); + switch (status & (3 << 2)) { + case 0x0 << 2: + msg_cdbg("no sectors are protected\n"); + break; + case 0x1 << 2: + msg_cdbg("some sectors are protected\n"); + /* FIXME: Read individual Sector Protection Registers. */ + break; + case 0x3 << 2: + msg_cdbg("all sectors are protected\n"); + break; + default: + msg_cdbg("reserved for future use\n"); + break; + } + spi_prettyprint_status_register_welwip(status); + return 0; +} + +int spi_prettyprint_status_register_at25df_sec(struct flashchip *flash) +{ + /* FIXME: We should check the security lockdown. */ + msg_cdbg("Ignoring security lockdown (if present)\n"); + msg_cdbg("Ignoring status register byte 2\n"); + return spi_prettyprint_status_register_at25df(flash); +} + +int spi_prettyprint_status_register_at25f(struct flashchip *flash) +{ + uint8_t status; + + status = spi_read_status_register(); + msg_cdbg("Chip status register is %02x\n", status); + + spi_prettyprint_status_register_at25_srplepewpp(status); + msg_cdbg("Chip status register: Bit 3 " + "is %sset\n", (status & (1 << 3)) ? "" : "not "); + msg_cdbg("Chip status register: Block Protect 0 (BP0) is " + "%sset, %s sectors are protected\n", + (status & (1 << 2)) ? "" : "not ", + (status & (1 << 2)) ? "all" : "no"); + spi_prettyprint_status_register_welwip(status); + return 0; +} + +int spi_prettyprint_status_register_at25fs010(struct flashchip *flash) +{ + uint8_t status; + + status = spi_read_status_register(); + msg_cdbg("Chip status register is %02x\n", status); + + msg_cdbg("Chip status register: Status Register Write Protect (WPEN) " + "is %sset\n", (status & (1 << 7)) ? "" : "not "); + msg_cdbg("Chip status register: Bit 6 / Block Protect 4 (BP4) is " + "%sset\n", (status & (1 << 6)) ? "" : "not "); + msg_cdbg("Chip status register: Bit 5 / Block Protect 3 (BP3) is " + "%sset\n", (status & (1 << 5)) ? "" : "not "); + msg_cdbg("Chip status register: Bit 4 is " + "%sset\n", (status & (1 << 4)) ? "" : "not "); + msg_cdbg("Chip status register: Bit 3 / Block Protect 1 (BP1) is " + "%sset\n", (status & (1 << 3)) ? "" : "not "); + msg_cdbg("Chip status register: Bit 2 / Block Protect 0 (BP0) is " + "%sset\n", (status & (1 << 2)) ? "" : "not "); + /* FIXME: Pretty-print detailed sector protection status. */ + spi_prettyprint_status_register_welwip(status); + return 0; +} + +int spi_prettyprint_status_register_at25fs040(struct flashchip *flash) +{ + uint8_t status; + + status = spi_read_status_register(); + msg_cdbg("Chip status register is %02x\n", status); + + msg_cdbg("Chip status register: Status Register Write Protect (WPEN) " + "is %sset\n", (status & (1 << 7)) ? "" : "not "); + msg_cdbg("Chip status register: Bit 6 / Block Protect 4 (BP4) is " + "%sset\n", (status & (1 << 6)) ? "" : "not "); + msg_cdbg("Chip status register: Bit 5 / Block Protect 3 (BP3) is " + "%sset\n", (status & (1 << 5)) ? "" : "not "); + msg_cdbg("Chip status register: Bit 4 / Block Protect 2 (BP2) is " + "%sset\n", (status & (1 << 4)) ? "" : "not "); + msg_cdbg("Chip status register: Bit 3 / Block Protect 1 (BP1) is " + "%sset\n", (status & (1 << 3)) ? "" : "not "); + msg_cdbg("Chip status register: Bit 2 / Block Protect 0 (BP0) is " + "%sset\n", (status & (1 << 2)) ? "" : "not "); + /* FIXME: Pretty-print detailed sector protection status. */ + spi_prettyprint_status_register_welwip(status); + return 0; +} + /* Prettyprint the status register. Works for * ST M25P series * MX MX25L series @@ -731,12 +852,12 @@ * This is according the SST25VF016 datasheet, who knows it is more * generic that this... */ -int spi_write_status_register(int status) +static int spi_write_status_register_ewsr(struct flashchip *flash, int status) { int result; struct spi_command cmds[] = { { - /* FIXME: WRSR requires either EWSR or WREN depending on chip type. */ + /* WRSR requires either EWSR or WREN depending on chip type. */ .writecnt = JEDEC_EWSR_OUTSIZE, .writearr = (const unsigned char[]){ JEDEC_EWSR }, .readcnt = 0, @@ -758,9 +879,59 @@ msg_cerr("%s failed during command execution\n", __func__); } + /* WRSR performs a self-timed erase before the changes take effect. */ + programmer_delay(100 * 1000); return result; }
+static int spi_write_status_register_wren(struct flashchip *flash, int status) +{ + int result; + struct spi_command cmds[] = { + { + /* WRSR requires either EWSR or WREN depending on chip type. */ + .writecnt = JEDEC_WREN_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WREN }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = JEDEC_WRSR_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WRSR, (unsigned char) status }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = 0, + .writearr = NULL, + .readcnt = 0, + .readarr = NULL, + }}; + + result = spi_send_multicommand(cmds); + if (result) { + msg_cerr("%s failed during command execution\n", + __func__); + } + /* WRSR performs a self-timed erase before the changes take effect. */ + programmer_delay(100 * 1000); + return result; +} + +static int spi_write_status_register(struct flashchip *flash, int status) +{ + int ret = 1; + + if (!(flash->feature_bits & (FEATURE_WRSR_WREN | FEATURE_WRSR_EWSR))) { + msg_cdbg("Missing status register write definition, assuming " + "EWSR is needed\n"); + flash->feature_bits |= FEATURE_WRSR_EWSR; + } + if (flash->feature_bits & FEATURE_WRSR_WREN) + ret = spi_write_status_register_wren(flash, status); + if (ret && (flash->feature_bits & FEATURE_WRSR_EWSR)) + ret = spi_write_status_register_ewsr(flash, status); + return ret; +} + int spi_byte_program(int addr, uint8_t databyte) { int result; @@ -843,29 +1014,156 @@ return result; }
+/* A generic brute-force block protection disable works like this: + * Write 0x00 to the status register. Check if any locks are still set (that + * part is chip specific). Repeat once. + */ int spi_disable_blockprotect(struct flashchip *flash) { uint8_t status; int result;
status = spi_read_status_register(); - /* If there is block protection in effect, unprotect it first. */ + /* If block protection is disabled, stop here. */ + if ((status & 0x3c) == 0) + return 0; + + msg_cdbg("Some block protection in effect, disabling\n"); + result = spi_write_status_register(flash, status & ~0x3c); + if (result) { + msg_cerr("spi_write_status_register failed\n"); + return result; + } + status = spi_read_status_register(); if ((status & 0x3c) != 0) { - msg_cdbg("Some block protection in effect, disabling\n"); - result = spi_write_status_register(status & ~0x3c); + msg_cerr("Block protection could not be disabled!\n"); + return 1; + } + return 0; +} + +int spi_disable_blockprotect_at25df(struct flashchip *flash) +{ + uint8_t status; + int result; + + status = spi_read_status_register(); + /* If block protection is disabled, stop here. */ + if ((status & (3 << 2)) == 0) + return 0; + + msg_cdbg("Some block protection in effect, disabling\n"); + if (status & (1 << 7)) { + msg_cdbg("Need to disable Sector Protection Register Lock\n"); + if ((status & (1 << 4)) == 0) { + msg_cerr("WP# pin is active, disabling " + "write protection is impossible.\n"); + return 1; + } + /* All bits except bit 7 (SPRL) are readonly. */ + result = spi_write_status_register(flash, status & ~(1 << 7)); if (result) { msg_cerr("spi_write_status_register failed\n"); return result; } - status = spi_read_status_register(); - if ((status & 0x3c) != 0) { - msg_cerr("Block protection could not be disabled!\n"); - return 1; + + } + /* Global unprotect. Make sure to mask SPRL as well. */ + result = spi_write_status_register(flash, status & ~0xbc); + if (result) { + msg_cerr("spi_write_status_register failed\n"); + return result; + } + status = spi_read_status_register(); + if ((status & (3 << 2)) != 0) { + msg_cerr("Block protection could not be disabled!\n"); + return 1; + } + return 0; +} + +int spi_disable_blockprotect_at25df_sec(struct flashchip *flash) +{ + /* FIXME: We should check the security lockdown. */ + msg_cinfo("Ignoring security lockdown (if present)\n"); + return spi_disable_blockprotect_at25df(flash); +} + +int spi_disable_blockprotect_at25f(struct flashchip *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); +} + +int spi_disable_blockprotect_at25fs010(struct flashchip *flash) +{ + uint8_t status; + int result; + + status = spi_read_status_register(); + /* If block protection is disabled, stop here. */ + if ((status & 0x6c) == 0) + return 0; + + msg_cdbg("Some block protection in effect, disabling\n"); + if (status & (1 << 7)) { + msg_cdbg("Need to disable Status Register Write Protect\n"); + /* Clear bit 7 (WPEN). */ + result = spi_write_status_register(flash, status & ~(1 << 7)); + if (result) { + msg_cerr("spi_write_status_register failed\n"); + return result; } } + /* Global unprotect. Make sure to mask WPEN as well. */ + result = spi_write_status_register(flash, status & ~0xec); + if (result) { + msg_cerr("spi_write_status_register failed\n"); + return result; + } + status = spi_read_status_register(); + if ((status & 0x6c) != 0) { + msg_cerr("Block protection could not be disabled!\n"); + return 1; + } return 0; } +int spi_disable_blockprotect_at25fs040(struct flashchip *flash) +{ + uint8_t status; + int result;
+ status = spi_read_status_register(); + /* If block protection is disabled, stop here. */ + if ((status & 0x7c) == 0) + return 0; + + msg_cdbg("Some block protection in effect, disabling\n"); + if (status & (1 << 7)) { + msg_cdbg("Need to disable Status Register Write Protect\n"); + /* Clear bit 7 (WPEN). */ + result = spi_write_status_register(flash, status & ~(1 << 7)); + if (result) { + msg_cerr("spi_write_status_register failed\n"); + return result; + } + } + /* Global unprotect. Make sure to mask WPEN as well. */ + result = spi_write_status_register(flash, status & ~0xfc); + if (result) { + msg_cerr("spi_write_status_register failed\n"); + return result; + } + status = spi_read_status_register(); + if ((status & 0x7c) != 0) { + msg_cerr("Block protection could not be disabled!\n"); + return 1; + } + return 0; +} + int spi_nbyte_read(int address, uint8_t *bytes, int len) { const unsigned char cmd[JEDEC_READ_OUTSIZE] = { Index: flashrom-writeprotect_atmel_at25/flashchips.c =================================================================== --- flashrom-writeprotect_atmel_at25/flashchips.c (Revision 1109) +++ flashrom-writeprotect_atmel_at25/flashchips.c (Arbeitskopie) @@ -313,6 +313,7 @@ .model_id = AT_25DF021, .total_size = 256, .page_size = 256, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, @@ -335,7 +336,8 @@ .block_erase = spi_block_erase_c7, } }, - .unlock = spi_disable_blockprotect, + .printlock = spi_prettyprint_status_register_at25df, + .unlock = spi_disable_blockprotect_at25df, .write = spi_chip_write_256, .read = spi_chip_read, }, @@ -348,6 +350,7 @@ .model_id = AT_25DF041A, .total_size = 512, .page_size = 256, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, @@ -370,7 +373,8 @@ .block_erase = spi_block_erase_c7, } }, - .unlock = spi_disable_blockprotect, + .printlock = spi_prettyprint_status_register_at25df, + .unlock = spi_disable_blockprotect_at25df, .write = spi_chip_write_256, .read = spi_chip_read, }, @@ -383,6 +387,7 @@ .model_id = AT_25DF081, .total_size = 1024, .page_size = 256, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, @@ -405,19 +410,58 @@ .block_erase = spi_block_erase_c7, } }, - .unlock = spi_disable_blockprotect, + .printlock = spi_prettyprint_status_register_at25df, + .unlock = spi_disable_blockprotect_at25df, .write = spi_chip_write_256, .read = spi_chip_read, },
{ .vendor = "Atmel", + .name = "AT25DF081A", + .bustype = CHIP_BUSTYPE_SPI, + .manufacture_id = ATMEL_ID, + .model_id = AT_25DF081A, + .total_size = 1024, + .page_size = 256, + .feature_bits = FEATURE_WRSR_WREN, + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, + .block_erasers = + { + { + .eraseblocks = { {4 * 1024, 256} }, + .block_erase = spi_block_erase_20, + }, { + .eraseblocks = { {32 * 1024, 32} }, + .block_erase = spi_block_erase_52, + }, { + .eraseblocks = { {64 * 1024, 16} }, + .block_erase = spi_block_erase_d8, + }, { + .eraseblocks = { {1024 * 1024, 1} }, + .block_erase = spi_block_erase_60, + }, { + .eraseblocks = { {1024 * 1024, 1} }, + .block_erase = spi_block_erase_c7, + } + }, + .printlock = spi_prettyprint_status_register_at25df_sec, + .unlock = spi_disable_blockprotect_at25df_sec, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, + + { + .vendor = "Atmel", .name = "AT25DF161", .bustype = CHIP_BUSTYPE_SPI, .manufacture_id = ATMEL_ID, .model_id = AT_25DF161, .total_size = 2048, .page_size = 256, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, @@ -440,7 +484,8 @@ .block_erase = spi_block_erase_c7, } }, - .unlock = spi_disable_blockprotect, + .printlock = spi_prettyprint_status_register_at25df_sec, + .unlock = spi_disable_blockprotect_at25df_sec, .write = spi_chip_write_256, .read = spi_chip_read, }, @@ -453,6 +498,7 @@ .model_id = AT_25DF321, .total_size = 4096, .page_size = 256, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_OK_PRW, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, @@ -475,7 +521,8 @@ .block_erase = spi_block_erase_c7, } }, - .unlock = spi_disable_blockprotect, + .printlock = spi_prettyprint_status_register_at25df, + .unlock = spi_disable_blockprotect_at25df, .write = spi_chip_write_256, .read = spi_chip_read, }, @@ -488,6 +535,7 @@ .model_id = AT_25DF321A, .total_size = 4096, .page_size = 256, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, @@ -510,7 +558,8 @@ .block_erase = spi_block_erase_c7, } }, - .unlock = spi_disable_blockprotect, + .printlock = spi_prettyprint_status_register_at25df_sec, + .unlock = spi_disable_blockprotect_at25df_sec, .write = spi_chip_write_256, .read = spi_chip_read, }, @@ -523,6 +572,7 @@ .model_id = AT_25DF641, .total_size = 8192, .page_size = 256, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, @@ -545,19 +595,58 @@ .block_erase = spi_block_erase_c7, } }, - .unlock = spi_disable_blockprotect, + .printlock = spi_prettyprint_status_register_at25df_sec, + .unlock = spi_disable_blockprotect_at25df_sec, .write = spi_chip_write_256, .read = spi_chip_read, },
{ .vendor = "Atmel", + .name = "AT25DQ161", + .bustype = CHIP_BUSTYPE_SPI, + .manufacture_id = ATMEL_ID, + .model_id = AT_25DQ161, + .total_size = 2048, + .page_size = 256, + .feature_bits = FEATURE_WRSR_WREN, + .tested = TEST_UNTESTED, + .probe = probe_spi_rdid, + .probe_timing = TIMING_ZERO, + .block_erasers = + { + { + .eraseblocks = { {4 * 1024, 512} }, + .block_erase = spi_block_erase_20, + }, { + .eraseblocks = { {32 * 1024, 64} }, + .block_erase = spi_block_erase_52, + }, { + .eraseblocks = { {64 * 1024, 32} }, + .block_erase = spi_block_erase_d8, + }, { + .eraseblocks = { {2 * 1024 * 1024, 1} }, + .block_erase = spi_block_erase_60, + }, { + .eraseblocks = { {2 * 1024 * 1024, 1} }, + .block_erase = spi_block_erase_c7, + } + }, + .printlock = spi_prettyprint_status_register_at25df_sec, + .unlock = spi_disable_blockprotect_at25df_sec, + .write = spi_chip_write_256, + .read = spi_chip_read, + }, + + { + .vendor = "Atmel", .name = "AT25F512B", .bustype = CHIP_BUSTYPE_SPI, .manufacture_id = ATMEL_ID, .model_id = AT_25F512B, .total_size = 64, .page_size = 256, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, @@ -580,7 +669,8 @@ .block_erase = spi_block_erase_c7, } }, - .unlock = spi_disable_blockprotect, + .printlock = spi_prettyprint_status_register_at25f, + .unlock = spi_disable_blockprotect_at25f, .write = spi_chip_write_256, .read = spi_chip_read, }, @@ -602,6 +692,9 @@ .eraseblocks = { {4 * 1024, 32} }, .block_erase = spi_block_erase_20, }, { + .eraseblocks = { {4 * 1024, 32} }, + .block_erase = spi_block_erase_d7, + }, { .eraseblocks = { {32 * 1024, 4} }, .block_erase = spi_block_erase_52, }, { @@ -615,7 +708,8 @@ .block_erase = spi_block_erase_c7, } }, - .unlock = spi_disable_blockprotect, + .printlock = spi_prettyprint_status_register_at25fs010, + .unlock = spi_disable_blockprotect_at25fs010, .write = spi_chip_write_256, .read = spi_chip_read, }, @@ -650,7 +744,8 @@ .block_erase = spi_block_erase_c7, } }, - .unlock = spi_disable_blockprotect, + .printlock = spi_prettyprint_status_register_at25fs040, + .unlock = spi_disable_blockprotect_at25fs040, .write = spi_chip_write_256, .read = spi_chip_read, }, Index: flashrom-writeprotect_atmel_at25/flashchips.h =================================================================== --- flashrom-writeprotect_atmel_at25/flashchips.h (Revision 1109) +++ flashrom-writeprotect_atmel_at25/flashchips.h (Arbeitskopie) @@ -110,12 +110,19 @@ #define AT_25DF021 0x4300 #define AT_25DF041A 0x4401 #define AT_25DF081 0x4502 +#define AT_25DF081A 0x4501 /* Yes, 81A has a lower number than 81 */ #define AT_25DF161 0x4602 #define AT_25DF321 0x4700 /* Same as 26DF321 */ #define AT_25DF321A 0x4701 #define AT_25DF641 0x4800 -#define AT_25F512A 0x65 /* Needs special RDID. AT25F512A_RDID 15 1d */ +#define AT_25DQ161 0x8600 +#define AT25F512 /* No device ID found in datasheet. Vendor ID + * can be read with AT25F512A_RDID */ +#define AT_25F512A 0x65 /* Needs AT25F512A_RDID */ #define AT_25F512B 0x6500 +#define AT25F1024 /* No device ID found in datasheet. Vendor ID + * can be read with AT25F512A_RDID */ +#define AT_25F1024A 0x60 /* Needs AT25F512A_RDID */ #define AT_25FS010 0x6601 #define AT_25FS040 0x6604 #define AT_26DF041 0x4400 Index: flashrom-writeprotect_atmel_at25/spi.c =================================================================== --- flashrom-writeprotect_atmel_at25/spi.c (Revision 1109) +++ flashrom-writeprotect_atmel_at25/spi.c (Arbeitskopie) @@ -29,8 +29,6 @@
enum spi_controller spi_controller = SPI_CONTROLLER_NONE;
-void spi_prettyprint_status_register(struct flashchip *flash); - const struct spi_programmer spi_programmer[] = { { /* SPI_CONTROLLER_NONE */ .command = NULL, Index: flashrom-writeprotect_atmel_at25/spi.h =================================================================== --- flashrom-writeprotect_atmel_at25/spi.h (Revision 1109) +++ flashrom-writeprotect_atmel_at25/spi.h (Arbeitskopie) @@ -31,7 +31,7 @@ #define JEDEC_RDID_INSIZE 0x03
/* AT25F512A has bit 3 as don't care bit in commands */ -#define AT25F512A_RDID 0x15 +#define AT25F512A_RDID 0x15 /* 0x15 or 0x1d */ #define AT25F512A_RDID_OUTSIZE 0x01 #define AT25F512A_RDID_INSIZE 0x02
@@ -123,5 +123,6 @@ #define SPI_INVALID_OPCODE -2 #define SPI_INVALID_ADDRESS -3 #define SPI_INVALID_LENGTH -4 +#define SPI_FLASHROM_BUG -5
#endif /* !__SPI_H__ */ Index: flashrom-writeprotect_atmel_at25/chipdrivers.h =================================================================== --- flashrom-writeprotect_atmel_at25/chipdrivers.h (Revision 1109) +++ flashrom-writeprotect_atmel_at25/chipdrivers.h (Arbeitskopie) @@ -47,7 +47,17 @@ int spi_chip_write_256_new(struct flashchip *flash, uint8_t *buf, int start, int len); int spi_chip_read(struct flashchip *flash, uint8_t *buf, int start, int len); uint8_t spi_read_status_register(void); +int spi_prettyprint_status_register_at25df(struct flashchip *flash); +int spi_prettyprint_status_register_at25df_sec(struct flashchip *flash); +int spi_prettyprint_status_register_at25f(struct flashchip *flash); +int spi_prettyprint_status_register_at25fs010(struct flashchip *flash); +int spi_prettyprint_status_register_at25fs040(struct flashchip *flash); int spi_disable_blockprotect(struct flashchip *flash); +int spi_disable_blockprotect_at25df(struct flashchip *flash); +int spi_disable_blockprotect_at25df_sec(struct flashchip *flash); +int spi_disable_blockprotect_at25f(struct flashchip *flash); +int spi_disable_blockprotect_at25fs010(struct flashchip *flash); +int spi_disable_blockprotect_at25fs040(struct flashchip *flash); int spi_byte_program(int addr, uint8_t databyte); int spi_nbyte_program(int addr, uint8_t *bytes, int len); int spi_nbyte_read(int addr, uint8_t *bytes, int len);
Attached as requested: flashrom -p internal:laptop=force_I_want_a_brick -V using flashrom trunk 1109 patched with latest Rayer patch and Atmel write protection patch
On 7/26/10 5:47 PM, Carl-Daniel Hailfinger wrote:
Next try, with one status register printing corner case fixed and with a better changelog.
Add detailed status register printing and unlocking for all ATMEL AT25* chips.
Add support for Atmel AT25DF081A and AT25DQ161.
Some chips require EWSR before WRSR, others require WREN before WRSR, and some support both variants. Add feature_bits to select the correct SPI command, and default to EWSR.
Signed-off-by: Carl-Daniel Hailfingerc-d.hailfinger.devel.2006@gmx.net
Index: flashrom-writeprotect_atmel_at25/flash.h
--- flashrom-writeprotect_atmel_at25/flash.h (Revision 1109) +++ flashrom-writeprotect_atmel_at25/flash.h (Arbeitskopie) @@ -167,8 +167,9 @@
/*
- How many different erase functions do we have per chip?
*/
- Atmel AT25FS010 has 6 different functions.
-#define NUM_ERASEFUNCTIONS 5 +#define NUM_ERASEFUNCTIONS 6
#define FEATURE_REGISTERMAP (1<< 0) #define FEATURE_BYTEWRITES (1<< 1) @@ -180,6 +181,9 @@ #define FEATURE_ADDR_2AA (1<< 2) #define FEATURE_ADDR_AAA (2<< 2) #define FEATURE_ADDR_SHIFTED (1<< 5) +#define FEATURE_WRSR_EWSR (1<< 6) +#define FEATURE_WRSR_WREN (1<< 7) +#define FEATURE_WRSR_EITHER (FEATURE_WRSR_EWSR | FEATURE_WRSR_WREN)
struct flashchip { const char *vendor; Index: flashrom-writeprotect_atmel_at25/spi25.c =================================================================== --- flashrom-writeprotect_atmel_at25/spi25.c (Revision 1109) +++ flashrom-writeprotect_atmel_at25/spi25.c (Arbeitskopie) @@ -311,8 +311,17 @@ }
/* Prettyprint the status register. Common definitions. */ -void spi_prettyprint_status_register_common(uint8_t status) +static void spi_prettyprint_status_register_welwip(uint8_t status) {
- msg_cdbg("Chip status register: Write Enable Latch (WEL) is "
"%sset\n", (status& (1<< 1)) ? "" : "not ");
- msg_cdbg("Chip status register: Write In Progress (WIP/BUSY) is "
"%sset\n", (status& (1<< 0)) ? "" : "not ");
+}
+/* Prettyprint the status register. Common definitions. */ +static void spi_prettyprint_status_register_common(uint8_t status) +{ msg_cdbg("Chip status register: Bit 5 / Block Protect 3 (BP3) is " "%sset\n", (status& (1<< 5)) ? "" : "not "); msg_cdbg("Chip status register: Bit 4 / Block Protect 2 (BP2) is " @@ -321,10 +330,7 @@ "%sset\n", (status& (1<< 3)) ? "" : "not "); msg_cdbg("Chip status register: Bit 2 / Block Protect 0 (BP0) is " "%sset\n", (status& (1<< 2)) ? "" : "not ");
- msg_cdbg("Chip status register: Write Enable Latch (WEL) is "
"%sset\n", (status& (1<< 1)) ? "" : "not ");
- msg_cdbg("Chip status register: Write In Progress (WIP/BUSY) is "
"%sset\n", (status& (1<< 0)) ? "" : "not ");
spi_prettyprint_status_register_welwip(status); }
/* Prettyprint the status register. Works for
@@ -337,6 +343,121 @@ spi_prettyprint_status_register_common(status); }
+/* Prettyprint the status register. Common definitions. */ +static void spi_prettyprint_status_register_at25_srplepewpp(uint8_t status) +{
- msg_cdbg("Chip status register: Sector Protection Register Lock (SRPL) "
"is %sset\n", (status& (1<< 7)) ? "" : "not ");
- msg_cdbg("Chip status register: Bit 6 "
"is %sset\n", (status& (1<< 6)) ? "" : "not ");
- msg_cdbg("Chip status register: Erase/Program Error (EPE) "
"is %sset\n", (status& (1<< 5)) ? "" : "not ");
- msg_cdbg("Chip status register: WP# pin (WPP) "
"is %sactive\n", (status& (1<< 4)) ? "not " : "");
+}
+int spi_prettyprint_status_register_at25df(struct flashchip *flash) +{
- uint8_t status;
- status = spi_read_status_register();
- msg_cdbg("Chip status register is %02x\n", status);
- spi_prettyprint_status_register_at25_srplepewpp(status);
- msg_cdbg("Chip status register: Software Protection Status (SWP): ");
- switch (status& (3<< 2)) {
- case 0x0<< 2:
msg_cdbg("no sectors are protected\n");
break;
- case 0x1<< 2:
msg_cdbg("some sectors are protected\n");
/* FIXME: Read individual Sector Protection Registers. */
break;
- case 0x3<< 2:
msg_cdbg("all sectors are protected\n");
break;
- default:
msg_cdbg("reserved for future use\n");
break;
- }
- spi_prettyprint_status_register_welwip(status);
- return 0;
+}
+int spi_prettyprint_status_register_at25df_sec(struct flashchip *flash) +{
- /* FIXME: We should check the security lockdown. */
- msg_cdbg("Ignoring security lockdown (if present)\n");
- msg_cdbg("Ignoring status register byte 2\n");
- return spi_prettyprint_status_register_at25df(flash);
+}
+int spi_prettyprint_status_register_at25f(struct flashchip *flash) +{
- uint8_t status;
- status = spi_read_status_register();
- msg_cdbg("Chip status register is %02x\n", status);
- spi_prettyprint_status_register_at25_srplepewpp(status);
- msg_cdbg("Chip status register: Bit 3 "
"is %sset\n", (status& (1<< 3)) ? "" : "not ");
- msg_cdbg("Chip status register: Block Protect 0 (BP0) is "
"%sset, %s sectors are protected\n",
(status& (1<< 2)) ? "" : "not ",
(status& (1<< 2)) ? "all" : "no");
- spi_prettyprint_status_register_welwip(status);
- return 0;
+}
+int spi_prettyprint_status_register_at25fs010(struct flashchip *flash) +{
- uint8_t status;
- status = spi_read_status_register();
- msg_cdbg("Chip status register is %02x\n", status);
- msg_cdbg("Chip status register: Status Register Write Protect (WPEN) "
"is %sset\n", (status& (1<< 7)) ? "" : "not ");
- msg_cdbg("Chip status register: Bit 6 / Block Protect 4 (BP4) is "
"%sset\n", (status& (1<< 6)) ? "" : "not ");
- msg_cdbg("Chip status register: Bit 5 / Block Protect 3 (BP3) is "
"%sset\n", (status& (1<< 5)) ? "" : "not ");
- msg_cdbg("Chip status register: Bit 4 is "
"%sset\n", (status& (1<< 4)) ? "" : "not ");
- msg_cdbg("Chip status register: Bit 3 / Block Protect 1 (BP1) is "
"%sset\n", (status& (1<< 3)) ? "" : "not ");
- msg_cdbg("Chip status register: Bit 2 / Block Protect 0 (BP0) is "
"%sset\n", (status& (1<< 2)) ? "" : "not ");
- /* FIXME: Pretty-print detailed sector protection status. */
- spi_prettyprint_status_register_welwip(status);
- return 0;
+}
+int spi_prettyprint_status_register_at25fs040(struct flashchip *flash) +{
- uint8_t status;
- status = spi_read_status_register();
- msg_cdbg("Chip status register is %02x\n", status);
- msg_cdbg("Chip status register: Status Register Write Protect (WPEN) "
"is %sset\n", (status& (1<< 7)) ? "" : "not ");
- msg_cdbg("Chip status register: Bit 6 / Block Protect 4 (BP4) is "
"%sset\n", (status& (1<< 6)) ? "" : "not ");
- msg_cdbg("Chip status register: Bit 5 / Block Protect 3 (BP3) is "
"%sset\n", (status& (1<< 5)) ? "" : "not ");
- msg_cdbg("Chip status register: Bit 4 / Block Protect 2 (BP2) is "
"%sset\n", (status& (1<< 4)) ? "" : "not ");
- msg_cdbg("Chip status register: Bit 3 / Block Protect 1 (BP1) is "
"%sset\n", (status& (1<< 3)) ? "" : "not ");
- msg_cdbg("Chip status register: Bit 2 / Block Protect 0 (BP0) is "
"%sset\n", (status& (1<< 2)) ? "" : "not ");
- /* FIXME: Pretty-print detailed sector protection status. */
- spi_prettyprint_status_register_welwip(status);
- return 0;
+}
- /* Prettyprint the status register. Works for
- ST M25P series
- MX MX25L series
@@ -731,12 +852,12 @@
- This is according the SST25VF016 datasheet, who knows it is more
- generic that this...
*/ -int spi_write_status_register(int status) +static int spi_write_status_register_ewsr(struct flashchip *flash, int status) { int result; struct spi_command cmds[] = { {
- /* FIXME: WRSR requires either EWSR or WREN depending on chip type. */
- /* WRSR requires either EWSR or WREN depending on chip type. */ .writecnt = JEDEC_EWSR_OUTSIZE, .writearr = (const unsigned char[]){ JEDEC_EWSR }, .readcnt = 0,
@@ -758,9 +879,59 @@ msg_cerr("%s failed during command execution\n", __func__); }
- /* WRSR performs a self-timed erase before the changes take effect. */
- programmer_delay(100 * 1000); return result; }
+static int spi_write_status_register_wren(struct flashchip *flash, int status) +{
- int result;
- struct spi_command cmds[] = {
- {
- /* WRSR requires either EWSR or WREN depending on chip type. */
.writecnt = JEDEC_WREN_OUTSIZE,
.writearr = (const unsigned char[]){ JEDEC_WREN },
.readcnt = 0,
.readarr = NULL,
- }, {
.writecnt = JEDEC_WRSR_OUTSIZE,
.writearr = (const unsigned char[]){ JEDEC_WRSR, (unsigned char) status },
.readcnt = 0,
.readarr = NULL,
- }, {
.writecnt = 0,
.writearr = NULL,
.readcnt = 0,
.readarr = NULL,
- }};
- result = spi_send_multicommand(cmds);
- if (result) {
msg_cerr("%s failed during command execution\n",
__func__);
- }
- /* WRSR performs a self-timed erase before the changes take effect. */
- programmer_delay(100 * 1000);
- return result;
+}
+static int spi_write_status_register(struct flashchip *flash, int status) +{
- int ret = 1;
- if (!(flash->feature_bits& (FEATURE_WRSR_WREN | FEATURE_WRSR_EWSR))) {
msg_cdbg("Missing status register write definition, assuming "
"EWSR is needed\n");
flash->feature_bits |= FEATURE_WRSR_EWSR;
- }
- if (flash->feature_bits& FEATURE_WRSR_WREN)
ret = spi_write_status_register_wren(flash, status);
- if (ret&& (flash->feature_bits& FEATURE_WRSR_EWSR))
ret = spi_write_status_register_ewsr(flash, status);
- return ret;
+}
- int spi_byte_program(int addr, uint8_t databyte) { int result;
@@ -843,29 +1014,156 @@ return result; }
+/* A generic brute-force block protection disable works like this:
- Write 0x00 to the status register. Check if any locks are still set (that
- part is chip specific). Repeat once.
*/ int spi_disable_blockprotect(struct flashchip *flash) { uint8_t status; int result;
status = spi_read_status_register();
- /* If there is block protection in effect, unprotect it first. */
- /* If block protection is disabled, stop here. */
- if ((status& 0x3c) == 0)
return 0;
- msg_cdbg("Some block protection in effect, disabling\n");
- result = spi_write_status_register(flash, status& ~0x3c);
- if (result) {
msg_cerr("spi_write_status_register failed\n");
return result;
- }
- status = spi_read_status_register(); if ((status& 0x3c) != 0) {
msg_cdbg("Some block protection in effect, disabling\n");
result = spi_write_status_register(status& ~0x3c);
msg_cerr("Block protection could not be disabled!\n");
return 1;
- }
- return 0;
+}
+int spi_disable_blockprotect_at25df(struct flashchip *flash) +{
- uint8_t status;
- int result;
- status = spi_read_status_register();
- /* If block protection is disabled, stop here. */
- if ((status& (3<< 2)) == 0)
return 0;
- msg_cdbg("Some block protection in effect, disabling\n");
- if (status& (1<< 7)) {
msg_cdbg("Need to disable Sector Protection Register Lock\n");
if ((status& (1<< 4)) == 0) {
msg_cerr("WP# pin is active, disabling "
"write protection is impossible.\n");
return 1;
}
/* All bits except bit 7 (SPRL) are readonly. */
if (result) { msg_cerr("spi_write_status_register failed\n"); return result; }result = spi_write_status_register(flash, status& ~(1<< 7));
status = spi_read_status_register();
if ((status& 0x3c) != 0) {
msg_cerr("Block protection could not be disabled!\n");
return 1;
- }
- /* Global unprotect. Make sure to mask SPRL as well. */
- result = spi_write_status_register(flash, status& ~0xbc);
- if (result) {
msg_cerr("spi_write_status_register failed\n");
return result;
- }
- status = spi_read_status_register();
- if ((status& (3<< 2)) != 0) {
msg_cerr("Block protection could not be disabled!\n");
return 1;
- }
- return 0;
+}
+int spi_disable_blockprotect_at25df_sec(struct flashchip *flash) +{
- /* FIXME: We should check the security lockdown. */
- msg_cinfo("Ignoring security lockdown (if present)\n");
- return spi_disable_blockprotect_at25df(flash);
+}
+int spi_disable_blockprotect_at25f(struct flashchip *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);
+}
+int spi_disable_blockprotect_at25fs010(struct flashchip *flash) +{
- uint8_t status;
- int result;
- status = spi_read_status_register();
- /* If block protection is disabled, stop here. */
- if ((status& 0x6c) == 0)
return 0;
- msg_cdbg("Some block protection in effect, disabling\n");
- if (status& (1<< 7)) {
msg_cdbg("Need to disable Status Register Write Protect\n");
/* Clear bit 7 (WPEN). */
result = spi_write_status_register(flash, status& ~(1<< 7));
if (result) {
msg_cerr("spi_write_status_register failed\n");
} }return result;
- /* Global unprotect. Make sure to mask WPEN as well. */
- result = spi_write_status_register(flash, status& ~0xec);
- if (result) {
msg_cerr("spi_write_status_register failed\n");
return result;
- }
- status = spi_read_status_register();
- if ((status& 0x6c) != 0) {
msg_cerr("Block protection could not be disabled!\n");
return 1;
- } return 0; }
+int spi_disable_blockprotect_at25fs040(struct flashchip *flash) +{
uint8_t status;
int result;
status = spi_read_status_register();
/* If block protection is disabled, stop here. */
if ((status& 0x7c) == 0)
return 0;
msg_cdbg("Some block protection in effect, disabling\n");
if (status& (1<< 7)) {
msg_cdbg("Need to disable Status Register Write Protect\n");
/* Clear bit 7 (WPEN). */
result = spi_write_status_register(flash, status& ~(1<< 7));
if (result) {
msg_cerr("spi_write_status_register failed\n");
return result;
}
}
/* Global unprotect. Make sure to mask WPEN as well. */
result = spi_write_status_register(flash, status& ~0xfc);
if (result) {
msg_cerr("spi_write_status_register failed\n");
return result;
}
status = spi_read_status_register();
if ((status& 0x7c) != 0) {
msg_cerr("Block protection could not be disabled!\n");
return 1;
}
return 0;
+}
- int spi_nbyte_read(int address, uint8_t *bytes, int len) { const unsigned char cmd[JEDEC_READ_OUTSIZE] = {
Index: flashrom-writeprotect_atmel_at25/flashchips.c
--- flashrom-writeprotect_atmel_at25/flashchips.c (Revision 1109) +++ flashrom-writeprotect_atmel_at25/flashchips.c (Arbeitskopie) @@ -313,6 +313,7 @@ .model_id = AT_25DF021, .total_size = 256, .page_size = 256,
.tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO,.feature_bits = FEATURE_WRSR_WREN,
@@ -335,7 +336,8 @@ .block_erase = spi_block_erase_c7, } },
.unlock = spi_disable_blockprotect,
.printlock = spi_prettyprint_status_register_at25df,
.write = spi_chip_write_256, .read = spi_chip_read, },.unlock = spi_disable_blockprotect_at25df,
@@ -348,6 +350,7 @@ .model_id = AT_25DF041A, .total_size = 512, .page_size = 256,
.tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO,.feature_bits = FEATURE_WRSR_WREN,
@@ -370,7 +373,8 @@ .block_erase = spi_block_erase_c7, } },
.unlock = spi_disable_blockprotect,
.printlock = spi_prettyprint_status_register_at25df,
.write = spi_chip_write_256, .read = spi_chip_read, },.unlock = spi_disable_blockprotect_at25df,
@@ -383,6 +387,7 @@ .model_id = AT_25DF081, .total_size = 1024, .page_size = 256,
.tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO,.feature_bits = FEATURE_WRSR_WREN,
@@ -405,19 +410,58 @@ .block_erase = spi_block_erase_c7, } },
.unlock = spi_disable_blockprotect,
.printlock = spi_prettyprint_status_register_at25df,
.unlock = spi_disable_blockprotect_at25df,
.write = spi_chip_write_256, .read = spi_chip_read, },
{ .vendor = "Atmel",
.name = "AT25DF081A",
.bustype = CHIP_BUSTYPE_SPI,
.manufacture_id = ATMEL_ID,
.model_id = AT_25DF081A,
.total_size = 1024,
.page_size = 256,
.feature_bits = FEATURE_WRSR_WREN,
.tested = TEST_UNTESTED,
.probe = probe_spi_rdid,
.probe_timing = TIMING_ZERO,
.block_erasers =
{
{
.eraseblocks = { {4 * 1024, 256} },
.block_erase = spi_block_erase_20,
}, {
.eraseblocks = { {32 * 1024, 32} },
.block_erase = spi_block_erase_52,
}, {
.eraseblocks = { {64 * 1024, 16} },
.block_erase = spi_block_erase_d8,
}, {
.eraseblocks = { {1024 * 1024, 1} },
.block_erase = spi_block_erase_60,
}, {
.eraseblocks = { {1024 * 1024, 1} },
.block_erase = spi_block_erase_c7,
}
},
.printlock = spi_prettyprint_status_register_at25df_sec,
.unlock = spi_disable_blockprotect_at25df_sec,
.write = spi_chip_write_256,
.read = spi_chip_read,
},
{
.vendor = "Atmel",
.name = "AT25DF161", .bustype = CHIP_BUSTYPE_SPI, .manufacture_id = ATMEL_ID, .model_id = AT_25DF161, .total_size = 2048, .page_size = 256,
.feature_bits = FEATURE_WRSR_WREN,
.tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO,
@@ -440,7 +484,8 @@ .block_erase = spi_block_erase_c7, } },
.unlock = spi_disable_blockprotect,
.printlock = spi_prettyprint_status_register_at25df_sec,
.write = spi_chip_write_256, .read = spi_chip_read, },.unlock = spi_disable_blockprotect_at25df_sec,
@@ -453,6 +498,7 @@ .model_id = AT_25DF321, .total_size = 4096, .page_size = 256,
.tested = TEST_OK_PRW, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO,.feature_bits = FEATURE_WRSR_WREN,
@@ -475,7 +521,8 @@ .block_erase = spi_block_erase_c7, } },
.unlock = spi_disable_blockprotect,
.printlock = spi_prettyprint_status_register_at25df,
.write = spi_chip_write_256, .read = spi_chip_read, },.unlock = spi_disable_blockprotect_at25df,
@@ -488,6 +535,7 @@ .model_id = AT_25DF321A, .total_size = 4096, .page_size = 256,
.tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO,.feature_bits = FEATURE_WRSR_WREN,
@@ -510,7 +558,8 @@ .block_erase = spi_block_erase_c7, } },
.unlock = spi_disable_blockprotect,
.printlock = spi_prettyprint_status_register_at25df_sec,
.write = spi_chip_write_256, .read = spi_chip_read, },.unlock = spi_disable_blockprotect_at25df_sec,
@@ -523,6 +572,7 @@ .model_id = AT_25DF641, .total_size = 8192, .page_size = 256,
.tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO,.feature_bits = FEATURE_WRSR_WREN,
@@ -545,19 +595,58 @@ .block_erase = spi_block_erase_c7, } },
.unlock = spi_disable_blockprotect,
.printlock = spi_prettyprint_status_register_at25df_sec,
.unlock = spi_disable_blockprotect_at25df_sec,
.write = spi_chip_write_256, .read = spi_chip_read, },
{ .vendor = "Atmel",
.name = "AT25DQ161",
.bustype = CHIP_BUSTYPE_SPI,
.manufacture_id = ATMEL_ID,
.model_id = AT_25DQ161,
.total_size = 2048,
.page_size = 256,
.feature_bits = FEATURE_WRSR_WREN,
.tested = TEST_UNTESTED,
.probe = probe_spi_rdid,
.probe_timing = TIMING_ZERO,
.block_erasers =
{
{
.eraseblocks = { {4 * 1024, 512} },
.block_erase = spi_block_erase_20,
}, {
.eraseblocks = { {32 * 1024, 64} },
.block_erase = spi_block_erase_52,
}, {
.eraseblocks = { {64 * 1024, 32} },
.block_erase = spi_block_erase_d8,
}, {
.eraseblocks = { {2 * 1024 * 1024, 1} },
.block_erase = spi_block_erase_60,
}, {
.eraseblocks = { {2 * 1024 * 1024, 1} },
.block_erase = spi_block_erase_c7,
}
},
.printlock = spi_prettyprint_status_register_at25df_sec,
.unlock = spi_disable_blockprotect_at25df_sec,
.write = spi_chip_write_256,
.read = spi_chip_read,
},
{
.vendor = "Atmel",
.name = "AT25F512B", .bustype = CHIP_BUSTYPE_SPI, .manufacture_id = ATMEL_ID, .model_id = AT_25F512B, .total_size = 64, .page_size = 256,
.feature_bits = FEATURE_WRSR_WREN,
.tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO,
@@ -580,7 +669,8 @@ .block_erase = spi_block_erase_c7, } },
.unlock = spi_disable_blockprotect,
.printlock = spi_prettyprint_status_register_at25f,
.write = spi_chip_write_256, .read = spi_chip_read, },.unlock = spi_disable_blockprotect_at25f,
@@ -602,6 +692,9 @@ .eraseblocks = { {4 * 1024, 32} }, .block_erase = spi_block_erase_20, }, {
.eraseblocks = { {4 * 1024, 32} },
.block_erase = spi_block_erase_d7,
}, { .eraseblocks = { {32 * 1024, 4} }, .block_erase = spi_block_erase_52, }, {
@@ -615,7 +708,8 @@ .block_erase = spi_block_erase_c7, } },
.unlock = spi_disable_blockprotect,
.printlock = spi_prettyprint_status_register_at25fs010,
.write = spi_chip_write_256, .read = spi_chip_read, },.unlock = spi_disable_blockprotect_at25fs010,
@@ -650,7 +744,8 @@ .block_erase = spi_block_erase_c7, } },
.unlock = spi_disable_blockprotect,
.printlock = spi_prettyprint_status_register_at25fs040,
.write = spi_chip_write_256, .read = spi_chip_read, },.unlock = spi_disable_blockprotect_at25fs040,
Index: flashrom-writeprotect_atmel_at25/flashchips.h
--- flashrom-writeprotect_atmel_at25/flashchips.h (Revision 1109) +++ flashrom-writeprotect_atmel_at25/flashchips.h (Arbeitskopie) @@ -110,12 +110,19 @@ #define AT_25DF021 0x4300 #define AT_25DF041A 0x4401 #define AT_25DF081 0x4502 +#define AT_25DF081A 0x4501 /* Yes, 81A has a lower number than 81 */ #define AT_25DF161 0x4602 #define AT_25DF321 0x4700 /* Same as 26DF321 */ #define AT_25DF321A 0x4701 #define AT_25DF641 0x4800 -#define AT_25F512A 0x65 /* Needs special RDID. AT25F512A_RDID 15 1d */ +#define AT_25DQ161 0x8600 +#define AT25F512 /* No device ID found in datasheet. Vendor ID
* can be read with AT25F512A_RDID */
+#define AT_25F512A 0x65 /* Needs AT25F512A_RDID */ #define AT_25F512B 0x6500 +#define AT25F1024 /* No device ID found in datasheet. Vendor ID
* can be read with AT25F512A_RDID */
+#define AT_25F1024A 0x60 /* Needs AT25F512A_RDID */ #define AT_25FS010 0x6601 #define AT_25FS040 0x6604 #define AT_26DF041 0x4400 Index: flashrom-writeprotect_atmel_at25/spi.c =================================================================== --- flashrom-writeprotect_atmel_at25/spi.c (Revision 1109) +++ flashrom-writeprotect_atmel_at25/spi.c (Arbeitskopie) @@ -29,8 +29,6 @@
enum spi_controller spi_controller = SPI_CONTROLLER_NONE;
-void spi_prettyprint_status_register(struct flashchip *flash);
- const struct spi_programmer spi_programmer[] = { { /* SPI_CONTROLLER_NONE */ .command = NULL,
Index: flashrom-writeprotect_atmel_at25/spi.h
--- flashrom-writeprotect_atmel_at25/spi.h (Revision 1109) +++ flashrom-writeprotect_atmel_at25/spi.h (Arbeitskopie) @@ -31,7 +31,7 @@ #define JEDEC_RDID_INSIZE 0x03
/* AT25F512A has bit 3 as don't care bit in commands */ -#define AT25F512A_RDID 0x15 +#define AT25F512A_RDID 0x15 /* 0x15 or 0x1d */ #define AT25F512A_RDID_OUTSIZE 0x01 #define AT25F512A_RDID_INSIZE 0x02
@@ -123,5 +123,6 @@ #define SPI_INVALID_OPCODE -2 #define SPI_INVALID_ADDRESS -3 #define SPI_INVALID_LENGTH -4 +#define SPI_FLASHROM_BUG -5
#endif /* !__SPI_H__ */ Index: flashrom-writeprotect_atmel_at25/chipdrivers.h =================================================================== --- flashrom-writeprotect_atmel_at25/chipdrivers.h (Revision 1109) +++ flashrom-writeprotect_atmel_at25/chipdrivers.h (Arbeitskopie) @@ -47,7 +47,17 @@ int spi_chip_write_256_new(struct flashchip *flash, uint8_t *buf, int start, int len); int spi_chip_read(struct flashchip *flash, uint8_t *buf, int start, int len); uint8_t spi_read_status_register(void); +int spi_prettyprint_status_register_at25df(struct flashchip *flash); +int spi_prettyprint_status_register_at25df_sec(struct flashchip *flash); +int spi_prettyprint_status_register_at25f(struct flashchip *flash); +int spi_prettyprint_status_register_at25fs010(struct flashchip *flash); +int spi_prettyprint_status_register_at25fs040(struct flashchip *flash); int spi_disable_blockprotect(struct flashchip *flash); +int spi_disable_blockprotect_at25df(struct flashchip *flash); +int spi_disable_blockprotect_at25df_sec(struct flashchip *flash); +int spi_disable_blockprotect_at25f(struct flashchip *flash); +int spi_disable_blockprotect_at25fs010(struct flashchip *flash); +int spi_disable_blockprotect_at25fs040(struct flashchip *flash); int spi_byte_program(int addr, uint8_t databyte); int spi_nbyte_program(int addr, uint8_t *bytes, int len); int spi_nbyte_read(int addr, uint8_t *bytes, int len);
On Tue, Jul 27, 2010 at 12:47:47AM +0200, Carl-Daniel Hailfinger wrote:
Next try, with one status register printing corner case fixed and with a better changelog.
Add detailed status register printing and unlocking for all ATMEL AT25* chips.
Add support for Atmel AT25DF081A and AT25DQ161.
Some chips require EWSR before WRSR, others require WREN before WRSR, and some support both variants. Add feature_bits to select the correct SPI command, and default to EWSR.
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Looks good.
Acked-by: Uwe Hermann uwe@hermann-uwe.de
I have to admit that I did _not_ cross-check the bits against the datasheet, though. It's compile-tested, however.
- /* WRSR performs a self-timed erase before the changes take effect. */
- programmer_delay(100 * 1000);
Why 100ms here? Is that mentioned in some datasheet or standardized or estimated? Is it the same for all chips?
- result = spi_send_multicommand(cmds);
- if (result) {
msg_cerr("%s failed during command execution\n",
__func__);
- }
- /* WRSR performs a self-timed erase before the changes take effect. */
- programmer_delay(100 * 1000);
Ditto.
Uwe.
On 29.07.2010 14:12, Uwe Hermann wrote:
On Tue, Jul 27, 2010 at 12:47:47AM +0200, Carl-Daniel Hailfinger wrote:
Next try, with one status register printing corner case fixed and with a better changelog.
Add detailed status register printing and unlocking for all ATMEL AT25* chips.
Add support for Atmel AT25DF081A and AT25DQ161.
Some chips require EWSR before WRSR, others require WREN before WRSR, and some support both variants. Add feature_bits to select the correct SPI command, and default to EWSR.
Signed-off-by: Carl-Daniel Hailfinger c-d.hailfinger.devel.2006@gmx.net
Looks good.
Acked-by: Uwe Hermann uwe@hermann-uwe.de
Thanks.
I have to admit that I did _not_ cross-check the bits against the datasheet, though. It's compile-tested, however.
- /* WRSR performs a self-timed erase before the changes take effect. */
- programmer_delay(100 * 1000);
Why 100ms here? Is that mentioned in some datasheet or standardized or estimated? Is it the same for all chips?
I've seen 60-85 ms delays in various datasheets. Some chips may require more. 100 ms should be a safe bet for now, but I will send a followup patch which adds WIP polling.
- result = spi_send_multicommand(cmds);
- if (result) {
msg_cerr("%s failed during command execution\n",
__func__);
- }
- /* WRSR performs a self-timed erase before the changes take effect. */
- programmer_delay(100 * 1000);
Ditto.
Please see above.
Thanks for the review, committed in r1115.
Regards, Carl-Daniel