Nico Huber has uploaded this change for review. ( https://review.coreboot.org/23694
Change subject: Add support for unlock and write to Atmel AT26F040 ......................................................................
Add support for unlock and write to Atmel AT26F040
Add support for unlock and write to Atmel AT26F040 SPI 512k chips.
Tested using a bus pirate only.
I'm unavailble for a couple of weeks imminently, but will reply on return if pos...
Cheers,
Tim.
~# flashrom -c AT26F004 -p buspirate_spi:dev=/dev/ttyUSB0 -Vw rr620WDA-read-rom-image flashrom v0.9.3-r1349 on Linux 2.6.32-5-686 (i686), built with libpci 3.1.7, GCC 4.4.5, little endian flashrom is free software, get the source code at http://www.flashrom.org
Calibrating delay loop... OS timer resolution is 1 usecs, 1088M loops per second, 10 myus = 10 us, 100 myus = 95 us, 1000 myus = 945 us, 10000 myus = 9495 us, 4 myus = 5 us, OK. Initializing buspirate_spi programmer SPI speed is 8MHz Raw bitbang mode version 1 Raw SPI mode version 1 Probing for Atmel AT26F004, 512 kB: probe_spi_rdid_generic: id1 0x1f, id2 0x400 Chip status register is 1c Found chip "Atmel AT26F004" (512 kB, SPI) on buspirate_spi.
=== This flash part has status UNTESTED for operations: PROBE READ ERASE WRITE The test status of this chip may have been updated in the latest development version of flashrom. If you are running the latest development version, please email a report to flashrom@flashrom.org if any of the above operations work correctly for you with this flash part. Please include the flashrom output with the additional -V option for all operations you tested (-V, -Vr, -Vw, -VE), and mention which mainboard or programmer you tested. Please mention your board in the subject line. Thanks for your help! Some block protection in effect, disabling All write protection disabled! Reading old flash chip contents... Erasing and writing flash chip... Looking at blockwise erase function 0... trying... 0x000000-0x000fff:EW, 0x001000-0x001fff:W, 0x002000-0x002fff:S, 0x003000-0x003fff:S, 0x004000-0x004fff:S, 0x005000-0x005fff:S, 0x006000-0x006fff:S, 0x007000-0x007fff:S, 0x008000-0x008fff:S, 0x009000-0x009fff:S, 0x00a000-0x00afff:S, 0x00b000-0x00bfff:S, 0x00c000-0x00cfff:W, 0x00d000-0x00dfff:S, 0x00e000-0x00efff:S, 0x00f000-0x00ffff:S, 0x010000-0x010fff:EW, 0x011000-0x011fff:EW, 0x012000-0x012fff:EW, 0x013000-0x013fff:EW, 0x014000-0x014fff:EW, 0x015000-0x015fff:W, 0x016000-0x016fff:W, 0x017000-0x017fff:S, 0x018000-0x018fff:S, 0x019000-0x019fff:S, 0x01a000-0x01afff:S, 0x01b000-0x01bfff:S, 0x01c000-0x01cfff:S, 0x01d000-0x01dfff:S, 0x01e000-0x01efff:S, 0x01f000-0x01ffff:S, 0x020000-0x020fff:EW, 0x021000-0x021fff:W, 0x022000-0x022fff:W, 0x023000-0x023fff:W, 0x024000-0x024fff:W, 0x025000-0x025fff:W, 0x026000-0x026fff:W, 0x027000-0x027fff:W, 0x028000-0x028fff:W, 0x029000-0x029fff:W, 0x02a000-0x02aff f:W, 0x02b000-0x02bfff:W, 0x02c000-0x02cfff:W, 0x02d000-0x02dfff:W, 0x02e000-0x02efff:W, 0x02f000-0x02ffff:W, 0x030000-0x030fff:W, 0x031000-0x031fff:W, 0x032000-0x032fff:W, 0x033000-0x033fff:W, 0x034000-0x034fff:W, 0x035000-0x035fff:W, 0x036000-0x036fff:W, 0x037000-0x037fff:W, 0x038000-0x038fff:W, 0x039000-0x039fff:W, 0x03a000-0x03afff:W, 0x03b000-0x03bfff:W, 0x03c000-0x03cfff:W, 0x03d000-0x03dfff:S, 0x03e000-0x03efff:S, 0x03f000-0x03ffff:S, 0x040000-0x040fff:S, 0x041000-0x041fff:S, 0x042000-0x042fff:S, 0x043000-0x043fff:S, 0x044000-0x044fff:S, 0x045000-0x045fff:S, 0x046000-0x046fff:S, 0x047000-0x047fff:S, 0x048000-0x048fff:S, 0x049000-0x049fff:S, 0x04a000-0x04afff:S, 0x04b000-0x04bfff:S, 0x04c000-0x04cfff:S, 0x04d000-0x04dfff:S, 0x04e000-0x04efff:S, 0x04f000-0x04ffff:S, 0x050000-0x050fff:S, 0x051000-0x051fff:S, 0x052000-0x052fff:S, 0x053000-0x053fff:S, 0x054000-0x054fff:S, 0x055000-0x055fff:S, 0x056000-0x056fff:S, 0x057000-0x057fff:S, 0x058000-0x058fff:S, 0x059000-0x059fff: S, 0x05a000-0x05afff:S, 0x05b000-0x05bfff:W, 0x05c000-0x05cfff:W, 0x05d000-0x05dfff:S, 0x05e000-0x05efff:S, 0x05f000-0x05ffff:S, 0x060000-0x060fff:S, 0x061000-0x061fff:S, 0x062000-0x062fff:S, 0x063000-0x063fff:S, 0x064000-0x064fff:S, 0x065000-0x065fff:S, 0x066000-0x066fff:S, 0x067000-0x067fff:S, 0x068000-0x068fff:S, 0x069000-0x069fff:S, 0x06a000-0x06afff:S, 0x06b000-0x06bfff:S, 0x06c000-0x06cfff:S, 0x06d000-0x06dfff:S, 0x06e000-0x06efff:S, 0x06f000-0x06ffff:S, 0x070000-0x070fff:S, 0x071000-0x071fff:S, 0x072000-0x072fff:S, 0x073000-0x073fff:S, 0x074000-0x074fff:S, 0x075000-0x075fff:S, 0x076000-0x076fff:S, 0x077000-0x077fff:S, 0x078000-0x078fff:S, 0x079000-0x079fff:S, 0x07a000-0x07afff:S, 0x07b000-0x07bfff:S, 0x07c000-0x07cfff:S, 0x07d000-0x07dfff:S, 0x07e000-0x07efff:S, 0x07f000-0x07ffff:S
Done. Verifying flash... VERIFIED. Raw bitbang mode version 1 Bus Pirate shutdown completed.
Change-Id: I88d0901eaec29c190d481f8d06097d4978a03840 Signed-off-by: Tim Small tim@seoss.co.uk --- M at25.c M chipdrivers.h M flashchips.c M spi25.c 4 files changed, 168 insertions(+), 1 deletion(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/94/23694/1
diff --git a/at25.c b/at25.c index e55b007..bb735cb 100644 --- a/at25.c +++ b/at25.c @@ -215,6 +215,8 @@ /* spi_disable_blockprotect_at25df is not really the right way to do * this, but the side effects of said function work here as well. */ + /* FIXME? This does a global unlock, but at25f don't have this command? + */ return spi_disable_blockprotect_at25df(flash); }
@@ -285,3 +287,71 @@ } return 0; } + + +int spi_disable_blockprotect_at26f040(struct flashchip *flash) +{ + uint8_t status; + uint32_t i; + int result; + + /* See Page 19 * + * http://www.atmel.com/dyn/resources/prod_documents/doc3588.pdf */ + + /* Write Protect Pin status: 0 == cannot set SPRL to 0 (unlocked) */ + const uint8_t stat_bit_WPP = (1 << 4); + + /* Software Protection Status: 00 == all sectors unprotected */ + const uint8_t stat_bit_SWP = (3 << 2); + + /* Sector Protection registers locked: 0 == unlocked. * + * Cannot be set to 0 if WPP is 1 (hardware lock). */ + const uint8_t stat_bit_SPRL = (1 << 7); + + /* Page 6 */ + const uint8_t at26f004_unprotect_sector = 0x39; + + status = spi_read_status_register(); + /* If block protection is disabled, OK to go ahead, stop here. */ + if ((status & stat_bit_SWP) == 0) + return 0; + + msg_cdbg("Some block protection in effect, disabling\n"); + if (status & stat_bit_SPRL) { + msg_cdbg("Need to disable Sector Protection Register Lock\n"); + if ((status & stat_bit_WPP) == 0) { + msg_cerr("The chips write-protect pin is being " + "asserted (driven low) by external hardware." + "\nWriting is impossible :-(.\n"); + return 1; + } + /* All bits except bit 7 (SPRL) are readonly. */ + /* Unlock sector protection registers. */ + result = spi_write_status_register(flash, status & ~stat_bit_SPRL); + if (result) { + msg_cerr("spi_write_status_register failed\n"); + return result; + } + + } + /* Call unprotect sector on each sector. We need to give an address */ + /* within each sector, and the smallest sector is 8KB. */ + for (i = 0; i < 0x80000; i += 0x1000) { + const unsigned char cmd[4] = { at26f004_unprotect_sector, + (i >> 16) & 0xff, + (i >> 8) & 0xff, + i & 0xff}; + /* set the chip's "WEL" write latch - WREN (Write Enable) */ + spi_write_enable(); + spi_send_command(sizeof(cmd), 0, cmd, NULL); + } + status = spi_read_status_register(); + if ((status & stat_bit_SWP) != 0) { + msg_cerr("Block protection could not be disabled!\n"); + return 1; + } else { + msg_cerr("All write protection disabled!\n"); + } + + return 0; +} diff --git a/chipdrivers.h b/chipdrivers.h index 92ddbea..38f2001 100644 --- a/chipdrivers.h +++ b/chipdrivers.h @@ -55,6 +55,7 @@ int spi_read_chunked(struct flashchip *flash, uint8_t *buf, int start, int len, int chunksize); int spi_write_chunked(struct flashchip *flash, uint8_t *buf, int start, int len, int chunksize); int spi_aai_write(struct flashchip *flash, uint8_t *buf, int start, int len); +int spi_at26_write_sequential(struct flashchip *flash, uint8_t *buf, int start, int len);
/* a25.c */ int spi_prettyprint_status_register_amic_a25l05p(struct flashchip *flash); @@ -74,6 +75,7 @@ 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_disable_blockprotect_at26f040(struct flashchip *flash);
/* 82802ab.c */ uint8_t wait_82802ab(struct flashchip *flash); diff --git a/flashchips.c b/flashchips.c index 1b5c88a..0ca4346 100644 --- a/flashchips.c +++ b/flashchips.c @@ -1883,6 +1883,7 @@ }, .printlock = spi_prettyprint_status_register_atmel_at26df081a, .unlock = spi_disable_blockprotect, + /* Should support spi_at26_write_sequential too, but it's untested */ .write = spi_chip_write_256, .read = spi_chip_read, .voltage = {2700, 3600}, @@ -1914,6 +1915,7 @@ .model_id = ATMEL_AT26F004, .total_size = 512, .page_size = 256, + .feature_bits = FEATURE_WRSR_WREN, .tested = TEST_UNTESTED, .probe = probe_spi_rdid, .probe_timing = TIMING_ZERO, @@ -1936,7 +1938,9 @@ .block_erase = spi_block_erase_c7, } }, - .write = NULL /* Incompatible Page write */, + .unlock = spi_disable_blockprotect_at26f040, + /* also supports spi_chip_write_1, but nothing else */ + .write = spi_at26_write_sequential, .read = spi_chip_read, .voltage = {2700, 3600}, }, diff --git a/spi25.c b/spi25.c index 937ee5d..89e32ae 100644 --- a/spi25.c +++ b/spi25.c @@ -1192,3 +1192,94 @@
return 0; } + + +/* This is for Atmel chips only (AT26F004, AT26DF161A etc.) - the 25F004 only + * does either this mode, or byte-at-a-time. */ +int spi_at26_write_sequential(struct flashchip *flash, uint8_t *buf, int start, int len) +{ + /* ref http://www.atmel.com/dyn/resources/prod_documents/doc3588.pdf + * page 9 etc. */ + const int AT26_SEQ_BYTE_WRITE_OUTSIZE = 5; + const int AT26_SEQ_BYTE_WRITE_CONT_OUTSIZE = 2; + const int AT26_SEQ_BYTE_WRITE_CMD = 0xaf; + uint32_t pos = start; + int result; + unsigned char cmd[4] = { + AT26_SEQ_BYTE_WRITE_CMD + }; + struct spi_command cmds[] = { + { + .writecnt = JEDEC_WREN_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WREN }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = AT26_SEQ_BYTE_WRITE_OUTSIZE, + .writearr = (const unsigned char[]){ + AT26_SEQ_BYTE_WRITE_CMD, + (start >> 16) & 0xff, + (start >> 8) & 0xff, + (start & 0xff), + buf[0] + }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = 0, + .writearr = NULL, + .readcnt = 0, + .readarr = NULL, + }}; + + switch (spi_programmer->type) { +#if CONFIG_INTERNAL == 1 +#if defined(__i386__) || defined(__x86_64__) + case SPI_CONTROLLER_IT87XX: + case SPI_CONTROLLER_WBSIO: + msg_perr("%s: impossible with this SPI controller," + " degrading to byte program\n", __func__); + return spi_chip_write_1(flash, buf, start, len); +#endif +#endif + default: + break; + } + + cmds[1].writearr = (const unsigned char[]){ + 0xAF, + (pos >> 16) & 0xff, + (pos >> 8) & 0xff, + (pos & 0xff), + buf[pos - start] + }; + + + result = spi_send_multicommand(cmds); + if (result) { + msg_cerr("%s failed during start command execution\n", + __func__); + /* FIXME: Should we send WRDI here as well to make sure the chip + * is not in seuquential wirte mode? + */ + return result; + } + while (spi_read_status_register() & JEDEC_RDSR_BIT_WIP) + programmer_delay(5); + + pos ++; + + while (pos < start + len) { + cmd[1] = buf[pos++ - start]; + spi_send_command(AT26_SEQ_BYTE_WRITE_CONT_OUTSIZE, 0, cmd, NULL); + while (spi_read_status_register() & JEDEC_RDSR_BIT_WIP) + programmer_delay(5); + } + + /* Use WRDI to exit sequential write mode. This needs to be done before + * issuing any other non-sequential-byte-write commands. + */ + spi_write_disable(); + + return 0; +}