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