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>