Skip to content

Instantly share code, notes, and snippets.

@devicenull
Created January 30, 2014 21:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save devicenull/8720588 to your computer and use it in GitHub Desktop.
Save devicenull/8720588 to your computer and use it in GitHub Desktop.
diff --git a/chipdrivers.h b/chipdrivers.h
index 851e90a..3f31293 100644
--- a/chipdrivers.h
+++ b/chipdrivers.h
@@ -52,6 +52,7 @@ int spi_block_erase_c4(struct flashctx *flash, unsigned int addr, unsigned int b
int spi_block_erase_c7(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_db(struct flashctx *flash, unsigned int addr, unsigned int blocklen);
erasefunc_t *spi_get_erasefn_from_opcode(uint8_t opcode);
int spi_chip_write_1(struct flashctx *flash, uint8_t *buf, unsigned int start, unsigned int len);
diff --git a/spi.h b/spi.h
index de5b3be..63dcdf9 100644
--- a/spi.h
+++ b/spi.h
@@ -101,6 +101,11 @@
#define JEDEC_BE_D8_OUTSIZE 0x04
#define JEDEC_BE_D8_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
+
/* Block Erase 0xd7 is supported by PMC chips. */
#define JEDEC_BE_D7 0xd7
#define JEDEC_BE_D7_OUTSIZE 0x04
@@ -121,6 +126,10 @@
#define JEDEC_RDSR_OUTSIZE 0x01
#define JEDEC_RDSR_INSIZE 0x01
+#define JEDEC_4_READ 0x13
+#define JEDEC_4_READ_OUTSIZE 0x05
+/* JEDEC_4_READ_INSIZE : any length */
+
/* Status Register Bits */
#define SPI_SR_WIP (0x01 << 0)
#define SPI_SR_WEL (0x01 << 1)
@@ -146,6 +155,10 @@
#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 e001196..c1a6e0f 100644
--- a/spi25.c
+++ b/spi25.c
@@ -649,6 +649,48 @@ int spi_block_erase_db(struct flashctx *flash, unsigned int addr, unsigned int b
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;
+}
+
+
/* Sector size is usually 4k, though Macronix eliteflash has 64k */
int spi_block_erase_20(struct flashctx *flash, unsigned int addr,
unsigned int blocklen)
@@ -831,6 +873,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;
case 0xdb:
return &spi_block_erase_db;
default:
@@ -844,6 +888,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,
@@ -851,14 +896,6 @@ 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
- },
.readcnt = 0,
.readarr = NULL,
}, {
@@ -868,6 +905,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",
@@ -880,13 +943,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,
@@ -894,8 +951,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,
}, {
@@ -909,12 +966,36 @@ 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) {
@@ -927,15 +1008,27 @@ int spi_nbyte_program(struct flashctx *flash, unsigned int addr, uint8_t *bytes,
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);
}
/*
@@ -947,27 +1040,21 @@ int spi_read_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, toread;
+ unsigned int pos, j, lenhere, towrite, page_end;
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) {
- toread = min(chunksize, lenhere - j);
- rc = spi_nbyte_read(flash, starthere + j, buf + starthere - start + j, toread);
+ towrite = min(chunksize, lenhere - j);
+ rc = spi_nbyte_program(flash, pos + j, buf + pos - start + j, towrite);
if (rc)
break;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment