|
|
@@ -32,6 +32,16 @@ |
|
|
|
#define CSRELEASE() DIRECT_WRITE_HIGH(cspin_basereg, cspin_bitmask) |
|
|
|
#define SPICONFIG SPISettings(50000000, MSBFIRST, SPI_MODE0) |
|
|
|
|
|
|
|
#if defined(__arc__) |
|
|
|
// Use SPI1 on Arduino 101 (accesses chip already on the board) |
|
|
|
#define SPIPORT SPI1 |
|
|
|
#elif 0 |
|
|
|
// Add cases here, if you wish to use other SPI ports... |
|
|
|
#else |
|
|
|
// Otherwise, use the normal SPI port. |
|
|
|
#define SPIPORT SPI |
|
|
|
#endif |
|
|
|
|
|
|
|
uint16_t SerialFlashChip::dirindex = 0; |
|
|
|
uint8_t SerialFlashChip::flags = 0; |
|
|
|
uint8_t SerialFlashChip::busy = 0; |
|
|
@@ -51,23 +61,23 @@ void SerialFlashChip::wait(void) |
|
|
|
uint32_t status; |
|
|
|
//Serial.print("wait-"); |
|
|
|
while (1) { |
|
|
|
SPI.beginTransaction(SPICONFIG); |
|
|
|
SPIPORT.beginTransaction(SPICONFIG); |
|
|
|
CSASSERT(); |
|
|
|
if (flags & FLAG_STATUS_CMD70) { |
|
|
|
// some Micron chips require this different |
|
|
|
// command to detect program and erase completion |
|
|
|
SPI.transfer(0x70); |
|
|
|
status = SPI.transfer(0); |
|
|
|
SPIPORT.transfer(0x70); |
|
|
|
status = SPIPORT.transfer(0); |
|
|
|
CSRELEASE(); |
|
|
|
SPI.endTransaction(); |
|
|
|
SPIPORT.endTransaction(); |
|
|
|
//Serial.printf("b=%02x.", status & 0xFF); |
|
|
|
if ((status & 0x80)) break; |
|
|
|
} else { |
|
|
|
// all others work by simply reading the status reg |
|
|
|
SPI.transfer(0x05); |
|
|
|
status = SPI.transfer(0); |
|
|
|
SPIPORT.transfer(0x05); |
|
|
|
status = SPIPORT.transfer(0); |
|
|
|
CSRELEASE(); |
|
|
|
SPI.endTransaction(); |
|
|
|
SPIPORT.endTransaction(); |
|
|
|
//Serial.printf("b=%02x.", status & 0xFF); |
|
|
|
if (!(status & 1)) break; |
|
|
|
} |
|
|
@@ -83,18 +93,18 @@ void SerialFlashChip::read(uint32_t addr, void *buf, uint32_t len) |
|
|
|
|
|
|
|
memset(p, 0, len); |
|
|
|
f = flags; |
|
|
|
SPI.beginTransaction(SPICONFIG); |
|
|
|
SPIPORT.beginTransaction(SPICONFIG); |
|
|
|
b = busy; |
|
|
|
if (b) { |
|
|
|
// read status register ... chip may no longer be busy |
|
|
|
CSASSERT(); |
|
|
|
if (flags & FLAG_STATUS_CMD70) { |
|
|
|
SPI.transfer(0x70); |
|
|
|
status = SPI.transfer(0); |
|
|
|
SPIPORT.transfer(0x70); |
|
|
|
status = SPIPORT.transfer(0); |
|
|
|
if ((status & 0x80)) b = 0; |
|
|
|
} else { |
|
|
|
SPI.transfer(0x05); |
|
|
|
status = SPI.transfer(0); |
|
|
|
SPIPORT.transfer(0x05); |
|
|
|
status = SPIPORT.transfer(0); |
|
|
|
if (!(status & 1)) b = 0; |
|
|
|
} |
|
|
|
CSRELEASE(); |
|
|
@@ -106,37 +116,37 @@ void SerialFlashChip::read(uint32_t addr, void *buf, uint32_t len) |
|
|
|
// which apparently have 2 different suspend |
|
|
|
// commands, for program vs erase |
|
|
|
CSASSERT(); |
|
|
|
SPI.transfer(0x06); // write enable (Micron req'd) |
|
|
|
SPIPORT.transfer(0x06); // write enable (Micron req'd) |
|
|
|
CSRELEASE(); |
|
|
|
delayMicroseconds(1); |
|
|
|
cmd = 0x75; //Suspend program/erase for almost all chips |
|
|
|
// but Spansion just has to be different for program suspend! |
|
|
|
if ((f & FLAG_DIFF_SUSPEND) && (b == 1)) cmd = 0x85; |
|
|
|
CSASSERT(); |
|
|
|
SPI.transfer(cmd); // Suspend command |
|
|
|
SPIPORT.transfer(cmd); // Suspend command |
|
|
|
CSRELEASE(); |
|
|
|
if (f & FLAG_STATUS_CMD70) { |
|
|
|
// Micron chips don't actually suspend until flags read |
|
|
|
CSASSERT(); |
|
|
|
SPI.transfer(0x70); |
|
|
|
SPIPORT.transfer(0x70); |
|
|
|
do { |
|
|
|
status = SPI.transfer(0); |
|
|
|
status = SPIPORT.transfer(0); |
|
|
|
} while (!(status & 0x80)); |
|
|
|
CSRELEASE(); |
|
|
|
} else { |
|
|
|
CSASSERT(); |
|
|
|
SPI.transfer(0x05); |
|
|
|
SPIPORT.transfer(0x05); |
|
|
|
do { |
|
|
|
status = SPI.transfer(0); |
|
|
|
status = SPIPORT.transfer(0); |
|
|
|
} while ((status & 0x01)); |
|
|
|
CSRELEASE(); |
|
|
|
} |
|
|
|
} else { |
|
|
|
// chip is busy with an operation that can not suspend |
|
|
|
SPI.endTransaction(); // is this a good idea? |
|
|
|
SPIPORT.endTransaction(); // is this a good idea? |
|
|
|
wait(); // should we wait without ending |
|
|
|
b = 0; // the transaction?? |
|
|
|
SPI.beginTransaction(SPICONFIG); |
|
|
|
SPIPORT.beginTransaction(SPICONFIG); |
|
|
|
} |
|
|
|
} |
|
|
|
do { |
|
|
@@ -149,14 +159,14 @@ void SerialFlashChip::read(uint32_t addr, void *buf, uint32_t len) |
|
|
|
CSASSERT(); |
|
|
|
// TODO: FIFO optimize.... |
|
|
|
if (f & FLAG_32BIT_ADDR) { |
|
|
|
SPI.transfer(0x03); |
|
|
|
SPI.transfer16(addr >> 16); |
|
|
|
SPI.transfer16(addr); |
|
|
|
SPIPORT.transfer(0x03); |
|
|
|
SPIPORT.transfer16(addr >> 16); |
|
|
|
SPIPORT.transfer16(addr); |
|
|
|
} else { |
|
|
|
SPI.transfer16(0x0300 | ((addr >> 16) & 255)); |
|
|
|
SPI.transfer16(addr); |
|
|
|
SPIPORT.transfer16(0x0300 | ((addr >> 16) & 255)); |
|
|
|
SPIPORT.transfer16(addr); |
|
|
|
} |
|
|
|
SPI.transfer(p, rdlen); |
|
|
|
SPIPORT.transfer(p, rdlen); |
|
|
|
CSRELEASE(); |
|
|
|
p += rdlen; |
|
|
|
addr += rdlen; |
|
|
@@ -164,16 +174,16 @@ void SerialFlashChip::read(uint32_t addr, void *buf, uint32_t len) |
|
|
|
} while (len > 0); |
|
|
|
if (b) { |
|
|
|
CSASSERT(); |
|
|
|
SPI.transfer(0x06); // write enable (Micron req'd) |
|
|
|
SPIPORT.transfer(0x06); // write enable (Micron req'd) |
|
|
|
CSRELEASE(); |
|
|
|
delayMicroseconds(1); |
|
|
|
cmd = 0x7A; |
|
|
|
if ((f & FLAG_DIFF_SUSPEND) && (b == 1)) cmd = 0x8A; |
|
|
|
CSASSERT(); |
|
|
|
SPI.transfer(cmd); // Resume program/erase |
|
|
|
SPIPORT.transfer(cmd); // Resume program/erase |
|
|
|
CSRELEASE(); |
|
|
|
} |
|
|
|
SPI.endTransaction(); |
|
|
|
SPIPORT.endTransaction(); |
|
|
|
} |
|
|
|
|
|
|
|
void SerialFlashChip::write(uint32_t addr, const void *buf, uint32_t len) |
|
|
@@ -184,10 +194,10 @@ void SerialFlashChip::write(uint32_t addr, const void *buf, uint32_t len) |
|
|
|
//Serial.printf("WR: addr %08X, len %d\n", addr, len); |
|
|
|
do { |
|
|
|
if (busy) wait(); |
|
|
|
SPI.beginTransaction(SPICONFIG); |
|
|
|
SPIPORT.beginTransaction(SPICONFIG); |
|
|
|
CSASSERT(); |
|
|
|
// write enable command |
|
|
|
SPI.transfer(0x06); |
|
|
|
SPIPORT.transfer(0x06); |
|
|
|
CSRELEASE(); |
|
|
|
max = 256 - (addr & 0xFF); |
|
|
|
pagelen = (len <= max) ? len : max; |
|
|
@@ -195,21 +205,21 @@ void SerialFlashChip::write(uint32_t addr, const void *buf, uint32_t len) |
|
|
|
delayMicroseconds(1); // TODO: reduce this, but prefer safety first |
|
|
|
CSASSERT(); |
|
|
|
if (flags & FLAG_32BIT_ADDR) { |
|
|
|
SPI.transfer(0x02); // program page command |
|
|
|
SPI.transfer16(addr >> 16); |
|
|
|
SPI.transfer16(addr); |
|
|
|
SPIPORT.transfer(0x02); // program page command |
|
|
|
SPIPORT.transfer16(addr >> 16); |
|
|
|
SPIPORT.transfer16(addr); |
|
|
|
} else { |
|
|
|
SPI.transfer16(0x0200 | ((addr >> 16) & 255)); |
|
|
|
SPI.transfer16(addr); |
|
|
|
SPIPORT.transfer16(0x0200 | ((addr >> 16) & 255)); |
|
|
|
SPIPORT.transfer16(addr); |
|
|
|
} |
|
|
|
addr += pagelen; |
|
|
|
len -= pagelen; |
|
|
|
do { |
|
|
|
SPI.transfer(*p++); |
|
|
|
SPIPORT.transfer(*p++); |
|
|
|
} while (--pagelen > 0); |
|
|
|
CSRELEASE(); |
|
|
|
busy = 4; |
|
|
|
SPI.endTransaction(); |
|
|
|
SPIPORT.endTransaction(); |
|
|
|
} while (len > 0); |
|
|
|
} |
|
|
|
|
|
|
@@ -232,32 +242,32 @@ void SerialFlashChip::eraseAll() |
|
|
|
if (die_index >= die_count) return; // all dies erased :-) |
|
|
|
uint8_t die_size = 2; // in 16 Mbyte units |
|
|
|
if (id[2] == 0x22) die_size = 8; |
|
|
|
SPI.beginTransaction(SPICONFIG); |
|
|
|
SPIPORT.beginTransaction(SPICONFIG); |
|
|
|
CSASSERT(); |
|
|
|
SPI.transfer(0x06); // write enable command |
|
|
|
SPIPORT.transfer(0x06); // write enable command |
|
|
|
CSRELEASE(); |
|
|
|
delayMicroseconds(1); |
|
|
|
CSASSERT(); |
|
|
|
// die erase command |
|
|
|
SPI.transfer(0xC4); |
|
|
|
SPI.transfer16((die_index * die_size) << 8); |
|
|
|
SPI.transfer16(0x0000); |
|
|
|
SPIPORT.transfer(0xC4); |
|
|
|
SPIPORT.transfer16((die_index * die_size) << 8); |
|
|
|
SPIPORT.transfer16(0x0000); |
|
|
|
CSRELEASE(); |
|
|
|
//Serial.printf("Micron erase begin\n"); |
|
|
|
flags |= (die_index + 1) << 6; |
|
|
|
} else { |
|
|
|
// All other chips support the bulk erase command |
|
|
|
SPI.beginTransaction(SPICONFIG); |
|
|
|
SPIPORT.beginTransaction(SPICONFIG); |
|
|
|
CSASSERT(); |
|
|
|
// write enable command |
|
|
|
SPI.transfer(0x06); |
|
|
|
SPIPORT.transfer(0x06); |
|
|
|
CSRELEASE(); |
|
|
|
delayMicroseconds(1); |
|
|
|
CSASSERT(); |
|
|
|
// bulk erase command |
|
|
|
SPI.transfer(0xC7); |
|
|
|
SPIPORT.transfer(0xC7); |
|
|
|
CSRELEASE(); |
|
|
|
SPI.endTransaction(); |
|
|
|
SPIPORT.endTransaction(); |
|
|
|
} |
|
|
|
busy = 3; |
|
|
|
} |
|
|
@@ -266,22 +276,22 @@ void SerialFlashChip::eraseBlock(uint32_t addr) |
|
|
|
{ |
|
|
|
uint8_t f = flags; |
|
|
|
if (busy) wait(); |
|
|
|
SPI.beginTransaction(SPICONFIG); |
|
|
|
SPIPORT.beginTransaction(SPICONFIG); |
|
|
|
CSASSERT(); |
|
|
|
SPI.transfer(0x06); // write enable command |
|
|
|
SPIPORT.transfer(0x06); // write enable command |
|
|
|
CSRELEASE(); |
|
|
|
delayMicroseconds(1); |
|
|
|
CSASSERT(); |
|
|
|
if (f & FLAG_32BIT_ADDR) { |
|
|
|
SPI.transfer(0xD8); |
|
|
|
SPI.transfer16(addr >> 16); |
|
|
|
SPI.transfer16(addr); |
|
|
|
SPIPORT.transfer(0xD8); |
|
|
|
SPIPORT.transfer16(addr >> 16); |
|
|
|
SPIPORT.transfer16(addr); |
|
|
|
} else { |
|
|
|
SPI.transfer16(0xD800 | ((addr >> 16) & 255)); |
|
|
|
SPI.transfer16(addr); |
|
|
|
SPIPORT.transfer16(0xD800 | ((addr >> 16) & 255)); |
|
|
|
SPIPORT.transfer16(addr); |
|
|
|
} |
|
|
|
CSRELEASE(); |
|
|
|
SPI.endTransaction(); |
|
|
|
SPIPORT.endTransaction(); |
|
|
|
busy = 2; |
|
|
|
} |
|
|
|
|
|
|
@@ -290,23 +300,23 @@ bool SerialFlashChip::ready() |
|
|
|
{ |
|
|
|
uint32_t status; |
|
|
|
if (!busy) return true; |
|
|
|
SPI.beginTransaction(SPICONFIG); |
|
|
|
SPIPORT.beginTransaction(SPICONFIG); |
|
|
|
CSASSERT(); |
|
|
|
if (flags & FLAG_STATUS_CMD70) { |
|
|
|
// some Micron chips require this different |
|
|
|
// command to detect program and erase completion |
|
|
|
SPI.transfer(0x70); |
|
|
|
status = SPI.transfer(0); |
|
|
|
SPIPORT.transfer(0x70); |
|
|
|
status = SPIPORT.transfer(0); |
|
|
|
CSRELEASE(); |
|
|
|
SPI.endTransaction(); |
|
|
|
SPIPORT.endTransaction(); |
|
|
|
//Serial.printf("ready=%02x\n", status & 0xFF); |
|
|
|
if ((status & 0x80) == 0) return false; |
|
|
|
} else { |
|
|
|
// all others work by simply reading the status reg |
|
|
|
SPI.transfer(0x05); |
|
|
|
status = SPI.transfer(0); |
|
|
|
SPIPORT.transfer(0x05); |
|
|
|
status = SPIPORT.transfer(0); |
|
|
|
CSRELEASE(); |
|
|
|
SPI.endTransaction(); |
|
|
|
SPIPORT.endTransaction(); |
|
|
|
//Serial.printf("ready=%02x\n", status & 0xFF); |
|
|
|
if ((status & 1)) return false; |
|
|
|
} |
|
|
@@ -339,7 +349,7 @@ bool SerialFlashChip::begin(uint8_t pin) |
|
|
|
|
|
|
|
cspin_basereg = PIN_TO_BASEREG(pin); |
|
|
|
cspin_bitmask = PIN_TO_BITMASK(pin); |
|
|
|
SPI.begin(); |
|
|
|
SPIPORT.begin(); |
|
|
|
pinMode(pin, OUTPUT); |
|
|
|
CSRELEASE(); |
|
|
|
readID(id); |
|
|
@@ -348,23 +358,23 @@ bool SerialFlashChip::begin(uint8_t pin) |
|
|
|
if (size > 16777216) { |
|
|
|
// more than 16 Mbyte requires 32 bit addresses |
|
|
|
f |= FLAG_32BIT_ADDR; |
|
|
|
SPI.beginTransaction(SPICONFIG); |
|
|
|
SPIPORT.beginTransaction(SPICONFIG); |
|
|
|
if (id[0] == ID0_SPANSION) { |
|
|
|
// spansion uses MSB of bank register |
|
|
|
CSASSERT(); |
|
|
|
SPI.transfer16(0x1780); // bank register write |
|
|
|
SPIPORT.transfer16(0x1780); // bank register write |
|
|
|
CSRELEASE(); |
|
|
|
} else { |
|
|
|
// micron & winbond & macronix use command |
|
|
|
CSASSERT(); |
|
|
|
SPI.transfer(0x06); // write enable |
|
|
|
SPIPORT.transfer(0x06); // write enable |
|
|
|
CSRELEASE(); |
|
|
|
delayMicroseconds(1); |
|
|
|
CSASSERT(); |
|
|
|
SPI.transfer(0xB7); // enter 4 byte addr mode |
|
|
|
SPIPORT.transfer(0xB7); // enter 4 byte addr mode |
|
|
|
CSRELEASE(); |
|
|
|
} |
|
|
|
SPI.endTransaction(); |
|
|
|
SPIPORT.endTransaction(); |
|
|
|
if (id[0] == ID0_MICRON) f |= FLAG_MULTI_DIE; |
|
|
|
} |
|
|
|
if (id[0] == ID0_SPANSION) { |
|
|
@@ -389,51 +399,51 @@ bool SerialFlashChip::begin(uint8_t pin) |
|
|
|
void SerialFlashChip::sleep() |
|
|
|
{ |
|
|
|
if (busy) wait(); |
|
|
|
SPI.beginTransaction(SPICONFIG); |
|
|
|
SPIPORT.beginTransaction(SPICONFIG); |
|
|
|
CSASSERT(); |
|
|
|
SPI.transfer(0xB9); // Deep power down command |
|
|
|
SPIPORT.transfer(0xB9); // Deep power down command |
|
|
|
CSRELEASE(); |
|
|
|
} |
|
|
|
|
|
|
|
void SerialFlashChip::wakeup() |
|
|
|
{ |
|
|
|
SPI.beginTransaction(SPICONFIG); |
|
|
|
SPIPORT.beginTransaction(SPICONFIG); |
|
|
|
CSASSERT(); |
|
|
|
SPI.transfer(0xAB); // Wake up from deep power down command |
|
|
|
SPIPORT.transfer(0xAB); // Wake up from deep power down command |
|
|
|
CSRELEASE(); |
|
|
|
} |
|
|
|
|
|
|
|
void SerialFlashChip::readID(uint8_t *buf) |
|
|
|
{ |
|
|
|
if (busy) wait(); |
|
|
|
SPI.beginTransaction(SPICONFIG); |
|
|
|
SPIPORT.beginTransaction(SPICONFIG); |
|
|
|
CSASSERT(); |
|
|
|
SPI.transfer(0x9F); |
|
|
|
buf[0] = SPI.transfer(0); // manufacturer ID |
|
|
|
buf[1] = SPI.transfer(0); // memory type |
|
|
|
buf[2] = SPI.transfer(0); // capacity |
|
|
|
SPIPORT.transfer(0x9F); |
|
|
|
buf[0] = SPIPORT.transfer(0); // manufacturer ID |
|
|
|
buf[1] = SPIPORT.transfer(0); // memory type |
|
|
|
buf[2] = SPIPORT.transfer(0); // capacity |
|
|
|
if (buf[0] == ID0_SPANSION) { |
|
|
|
buf[3] = SPI.transfer(0); // ID-CFI |
|
|
|
buf[4] = SPI.transfer(0); // sector size |
|
|
|
buf[3] = SPIPORT.transfer(0); // ID-CFI |
|
|
|
buf[4] = SPIPORT.transfer(0); // sector size |
|
|
|
} |
|
|
|
CSRELEASE(); |
|
|
|
SPI.endTransaction(); |
|
|
|
SPIPORT.endTransaction(); |
|
|
|
//Serial.printf("ID: %02X %02X %02X\n", buf[0], buf[1], buf[2]); |
|
|
|
} |
|
|
|
|
|
|
|
void SerialFlashChip::readSerialNumber(uint8_t *buf) //needs room for 8 bytes |
|
|
|
{ |
|
|
|
if (busy) wait(); |
|
|
|
SPI.beginTransaction(SPICONFIG); |
|
|
|
SPIPORT.beginTransaction(SPICONFIG); |
|
|
|
CSASSERT(); |
|
|
|
SPI.transfer(0x4B); |
|
|
|
SPI.transfer16(0); |
|
|
|
SPI.transfer16(0); |
|
|
|
SPIPORT.transfer(0x4B); |
|
|
|
SPIPORT.transfer16(0); |
|
|
|
SPIPORT.transfer16(0); |
|
|
|
for (int i=0; i<8; i++) { |
|
|
|
buf[i] = SPI.transfer(0); |
|
|
|
buf[i] = SPIPORT.transfer(0); |
|
|
|
} |
|
|
|
CSRELEASE(); |
|
|
|
SPI.endTransaction(); |
|
|
|
SPIPORT.endTransaction(); |
|
|
|
// Serial.printf("Serial Number: %02X %02X %02X %02X %02X %02X %02X %02X\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); |
|
|
|
} |
|
|
|
|