Patchwork Add support for 4-address-byte commands and block-erase 0xDC

login
register
about
Submitter mark marshall
Date 2013-03-28 12:14:13
Message ID <CAD4b4W+Qq5kw1iGuESh_4Q2j-MKwq8mikjFsF5yCavvyNg37OA@mail.gmail.com>
Download mbox | patch
Permalink /patch/3897/
State New
Headers show

Comments

mark marshall - 2013-03-28 12:14:13
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(-)

  * 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)

Patch

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