| @@ -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]); | |||
| } | |||