To support flashes that are larger than 16 MBytes we need to support '4-byte-address' program and erase commands.
Signed-off-by: Mark Marshall mark.marshall@omicron.at --- chipdrivers.h | 1 + spi.h | 13 +++++ spi25.c | 175 +++++++++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 147 insertions(+), 42 deletions(-)
diff --git a/chipdrivers.h b/chipdrivers.h index f0223ae..244d421 100644 --- a/chipdrivers.h +++ b/chipdrivers.h @@ -47,6 +47,7 @@ int spi_block_erase_52(struct flashctx *flash, unsigned int addr, unsigned int b int spi_block_erase_81(struct flashctx *flash, unsigned int addr, unsigned int blocklen); int spi_block_erase_d7(struct flashctx *flash, unsigned int addr, unsigned int blocklen); int spi_block_erase_d8(struct flashctx *flash, unsigned int addr, unsigned int blocklen); +int spi_block_erase_dc(struct flashctx *flash, unsigned int addr, unsigned int blocklen); int spi_block_erase_60(struct flashctx *flash, unsigned int addr, unsigned int blocklen); int spi_block_erase_62(struct flashctx *flash, unsigned int addr, unsigned int blocklen); int spi_block_erase_c7(struct flashctx *flash, unsigned int addr, unsigned int blocklen); diff --git a/spi.h b/spi.h index 297125e..359756a 100644 --- a/spi.h +++ b/spi.h @@ -101,6 +101,11 @@ #define JEDEC_BE_D7_OUTSIZE 0x04 #define JEDEC_BE_D7_INSIZE 0x00
+/* Block Erase 0xdc is supported by Spansion chips. */ +#define JEDEC_BE_DC 0xdc +#define JEDEC_BE_DC_OUTSIZE 0x05 +#define JEDEC_BE_DC_INSIZE 0x00 + /* Sector Erase 0x20 is supported by Macronix/SST chips. */ #define JEDEC_SE 0x20 #define JEDEC_SE_OUTSIZE 0x04 @@ -131,11 +136,19 @@ #define JEDEC_READ_OUTSIZE 0x04 /* JEDEC_READ_INSIZE : any length */
+#define JEDEC_4_READ 0x13 +#define JEDEC_4_READ_OUTSIZE 0x05 +/* JEDEC_4_READ_INSIZE : any length */ + /* Write memory byte */ #define JEDEC_BYTE_PROGRAM 0x02 #define JEDEC_BYTE_PROGRAM_OUTSIZE 0x05 #define JEDEC_BYTE_PROGRAM_INSIZE 0x00
+#define JEDEC_4_BYTE_PROGRAM 0x12 +#define JEDEC_4_BYTE_PROGRAM_OUTSIZE 0x06 +#define JEDEC_4_BYTE_PROGRAM_INSIZE 0x00 + /* Write AAI word (SST25VF080B) */ #define JEDEC_AAI_WORD_PROGRAM 0xad #define JEDEC_AAI_WORD_PROGRAM_OUTSIZE 0x06 diff --git a/spi25.c b/spi25.c index 911dc4e..3beab3a 100644 --- a/spi25.c +++ b/spi25.c @@ -453,6 +453,50 @@ int spi_block_erase_52(struct flashctx *flash, unsigned int addr, return 0; }
+/* Block size is usually 256k */ +int spi_block_erase_dc(struct flashctx *flash, unsigned int addr, + unsigned int blocklen) +{ + int result; + struct spi_command cmds[] = { + { + .writecnt = JEDEC_WREN_OUTSIZE, + .writearr = (const unsigned char[]){ JEDEC_WREN }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = JEDEC_BE_DC_OUTSIZE, + .writearr = (const unsigned char[]){ + JEDEC_BE_DC, + (addr >> 24) & 0xff, + (addr >> 16) & 0xff, + (addr >> 8) & 0xff, + (addr & 0xff) + }, + .readcnt = 0, + .readarr = NULL, + }, { + .writecnt = 0, + .writearr = NULL, + .readcnt = 0, + .readarr = NULL, + }}; + + result = spi_send_multicommand(flash, cmds); + if (result) { + msg_cerr("%s failed during command execution at address 0x%x\n", + __func__, addr); + return result; + } + /* Wait until the Write-In-Progress bit is cleared. + * This usually takes 100-4000 ms, so wait in 100 ms steps. + */ + while (spi_read_status_register(flash) & SPI_SR_WIP) + programmer_delay(100 * 1000); + /* FIXME: Check the status register for errors. */ + return 0; +} + /* Block size is usually * 64k for Macronix * 32k for SST @@ -719,6 +763,8 @@ erasefunc_t *spi_get_erasefn_from_opcode(uint8_t opcode) return &spi_block_erase_d7; case 0xd8: return &spi_block_erase_d8; + case 0xdc: + return &spi_block_erase_dc; default: msg_cinfo("%s: unknown erase opcode (0x%02x). Please report " "this at flashrom@flashrom.org\n", __func__, opcode); @@ -730,6 +776,7 @@ int spi_byte_program(struct flashctx *flash, unsigned int addr, uint8_t databyte) { int result; + uint8_t cmd[JEDEC_4_BYTE_PROGRAM_OUTSIZE]; struct spi_command cmds[] = { { .writecnt = JEDEC_WREN_OUTSIZE, @@ -737,14 +784,8 @@ int spi_byte_program(struct flashctx *flash, unsigned int addr, .readcnt = 0, .readarr = NULL, }, { - .writecnt = JEDEC_BYTE_PROGRAM_OUTSIZE, - .writearr = (const unsigned char[]){ - JEDEC_BYTE_PROGRAM, - (addr >> 16) & 0xff, - (addr >> 8) & 0xff, - (addr & 0xff), - databyte - }, + .writecnt = 0, + .writearr = NULL, .readcnt = 0, .readarr = NULL, }, { @@ -754,6 +795,32 @@ int spi_byte_program(struct flashctx *flash, unsigned int addr, .readarr = NULL, }};
+ /* We always use the 4-byte commands for chips bigger than 16 + * MB as we don't know what's in the 'bank address + * register'. */ + if (flash->chip->total_size > 16 * 1024) { + + cmds[1].writecnt = JEDEC_4_BYTE_PROGRAM_OUTSIZE; + cmds[1].writearr = cmd; + + cmd[0] = JEDEC_4_BYTE_PROGRAM; + cmd[1] = (addr >> 24) & 0xff; + cmd[2] = (addr >> 16) & 0xff; + cmd[3] = (addr >> 8) & 0xff; + cmd[4] = (addr >> 0) & 0xff; + cmd[5] = databyte; + } else { + + cmds[1].writecnt = JEDEC_BYTE_PROGRAM_OUTSIZE; + cmds[1].writearr = cmd; + + cmd[0] = JEDEC_BYTE_PROGRAM; + cmd[1] = (addr >> 16) & 0xff; + cmd[2] = (addr >> 8) & 0xff; + cmd[3] = (addr >> 0) & 0xff; + cmd[4] = databyte; + } + result = spi_send_multicommand(flash, cmds); if (result) { msg_cerr("%s failed during command execution at address 0x%x\n", @@ -766,13 +833,7 @@ int spi_nbyte_program(struct flashctx *flash, unsigned int addr, uint8_t *bytes, unsigned int len) { int result; - /* FIXME: Switch to malloc based on len unless that kills speed. */ - unsigned char cmd[JEDEC_BYTE_PROGRAM_OUTSIZE - 1 + 256] = { - JEDEC_BYTE_PROGRAM, - (addr >> 16) & 0xff, - (addr >> 8) & 0xff, - (addr >> 0) & 0xff, - }; + uint8_t cmd[JEDEC_4_BYTE_PROGRAM_OUTSIZE - 1 + 512]; struct spi_command cmds[] = { { .writecnt = JEDEC_WREN_OUTSIZE, @@ -780,8 +841,8 @@ int spi_nbyte_program(struct flashctx *flash, unsigned int addr, uint8_t *bytes, .readcnt = 0, .readarr = NULL, }, { - .writecnt = JEDEC_BYTE_PROGRAM_OUTSIZE - 1 + len, - .writearr = cmd, + .writecnt = 0, + .writearr = NULL, .readcnt = 0, .readarr = NULL, }, { @@ -795,33 +856,69 @@ int spi_nbyte_program(struct flashctx *flash, unsigned int addr, uint8_t *bytes, msg_cerr("%s called for zero-length write\n", __func__); return 1; } - if (len > 256) { + if (len > 512) { msg_cerr("%s called for too long a write\n", __func__); return 1; }
- memcpy(&cmd[4], bytes, len); + /* We always use the 4-byte commands for chips bigger than 16 + * MB as we don't know what's in the 'bank address + * register'. */ + if (flash->chip->total_size > 16 * 1024) { + cmds[1].writecnt = JEDEC_4_BYTE_PROGRAM_OUTSIZE - 1 + len; + cmds[1].writearr = cmd; + + cmd[0] = JEDEC_4_BYTE_PROGRAM; + cmd[1] = (addr >> 24) & 0xff; + cmd[2] = (addr >> 16) & 0xff; + cmd[3] = (addr >> 8) & 0xff; + cmd[4] = (addr >> 0) & 0xff; + + memcpy(&cmd[5], bytes, len); + } else { + cmds[1].writecnt = JEDEC_BYTE_PROGRAM_OUTSIZE - 1 + len; + cmds[1].writearr = cmd; + + cmd[0] = JEDEC_BYTE_PROGRAM; + cmd[1] = (addr >> 16) & 0xff; + cmd[2] = (addr >> 8) & 0xff; + cmd[3] = (addr >> 0) & 0xff; + + memcpy(&cmd[4], bytes, len); + }
result = spi_send_multicommand(flash, cmds); if (result) { msg_cerr("%s failed during command execution at address 0x%x\n", __func__, addr); } + return result; }
int spi_nbyte_read(struct flashctx *flash, unsigned int address, uint8_t *bytes, unsigned int len) { - const unsigned char cmd[JEDEC_READ_OUTSIZE] = { - JEDEC_READ, - (address >> 16) & 0xff, - (address >> 8) & 0xff, - (address >> 0) & 0xff, - }; + unsigned char cmd[JEDEC_4_READ_OUTSIZE]; + unsigned int cmd_len; + + if (flash->chip->total_size > 16 * 1024) { + cmd[0] = JEDEC_4_READ; + cmd[1] = (address >> 24) & 0xff; + cmd[2] = (address >> 16) & 0xff; + cmd[3] = (address >> 8) & 0xff; + cmd[4] = (address >> 0) & 0xff; + cmd_len = JEDEC_4_READ_OUTSIZE; + } else { + cmd[0] = JEDEC_READ; + cmd[1] = (address >> 16) & 0xff; + cmd[2] = (address >> 8) & 0xff; + cmd[3] = (address >> 0) & 0xff; + cmd_len = JEDEC_READ_OUTSIZE; + }
/* Send Read */ - return spi_send_command(flash, sizeof(cmd), len, cmd, bytes); + return spi_send_command(flash, cmd_len, len, cmd, bytes); }
/* @@ -873,7 +970,7 @@ int spi_write_chunked(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len, unsigned int chunksize) { int rc = 0; - unsigned int i, j, starthere, lenhere, towrite; + unsigned int pos, j, lenhere, towrite, page_end; /* FIXME: page_size is the wrong variable. We need max_writechunk_size * in struct flashctx to do this properly. All chips using * spi_chip_write_256 have page_size set to max_writechunk_size, so @@ -881,24 +978,18 @@ int spi_write_chunked(struct flashctx *flash, uint8_t *buf, unsigned int start, */ unsigned int page_size = flash->chip->page_size;
- /* Warning: This loop has a very unusual condition and body. - * The loop needs to go through each page with at least one affected - * byte. The lowest page number is (start / page_size) since that - * division rounds down. The highest page number we want is the page - * where the last byte of the range lives. That last byte has the - * address (start + len - 1), thus the highest page number is - * (start + len - 1) / page_size. Since we want to include that last - * page as well, the loop condition uses <=. - */ - for (i = start / page_size; i <= (start + len - 1) / page_size; i++) { - /* Byte position of the first byte in the range in this page. */ - /* starthere is an offset to the base address of the chip. */ - starthere = max(start, i * page_size); + if (page_size & (page_size - 1)) { + msg_cerr("page_size not a power of 2 (%u)?\n", page_size); + } + + for (pos = start; pos < (start + len); pos += lenhere) { + /* This is the end of the current page (start of the next page). */ + page_end = (pos & ~(page_size - 1)) + page_size; /* Length of bytes in the range in this page. */ - lenhere = min(start + len, (i + 1) * page_size) - starthere; + lenhere = min(start + len, page_end) - pos; for (j = 0; j < lenhere; j += chunksize) { towrite = min(chunksize, lenhere - j); - rc = spi_nbyte_program(flash, starthere + j, buf + starthere - start + j, towrite); + rc = spi_nbyte_program(flash, pos + j, buf + pos - start + j, towrite); if (rc) break; while (spi_read_status_register(flash) & SPI_SR_WIP)