| #define CSRELEASE() DIRECT_WRITE_HIGH(cspin_basereg, cspin_bitmask) | #define CSRELEASE() DIRECT_WRITE_HIGH(cspin_basereg, cspin_bitmask) | ||||
| #define SPICONFIG SPISettings(50000000, MSBFIRST, SPI_MODE0) | #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; | uint16_t SerialFlashChip::dirindex = 0; | ||||
| uint8_t SerialFlashChip::flags = 0; | uint8_t SerialFlashChip::flags = 0; | ||||
| uint8_t SerialFlashChip::busy = 0; | uint8_t SerialFlashChip::busy = 0; | ||||
| uint32_t status; | uint32_t status; | ||||
| //Serial.print("wait-"); | //Serial.print("wait-"); | ||||
| while (1) { | while (1) { | ||||
| SPI.beginTransaction(SPICONFIG); | |||||
| SPIPORT.beginTransaction(SPICONFIG); | |||||
| CSASSERT(); | CSASSERT(); | ||||
| if (flags & FLAG_STATUS_CMD70) { | if (flags & FLAG_STATUS_CMD70) { | ||||
| // some Micron chips require this different | // some Micron chips require this different | ||||
| // command to detect program and erase completion | // command to detect program and erase completion | ||||
| SPI.transfer(0x70); | |||||
| status = SPI.transfer(0); | |||||
| SPIPORT.transfer(0x70); | |||||
| status = SPIPORT.transfer(0); | |||||
| CSRELEASE(); | CSRELEASE(); | ||||
| SPI.endTransaction(); | |||||
| SPIPORT.endTransaction(); | |||||
| //Serial.printf("b=%02x.", status & 0xFF); | //Serial.printf("b=%02x.", status & 0xFF); | ||||
| if ((status & 0x80)) break; | if ((status & 0x80)) break; | ||||
| } else { | } else { | ||||
| // all others work by simply reading the status reg | // all others work by simply reading the status reg | ||||
| SPI.transfer(0x05); | |||||
| status = SPI.transfer(0); | |||||
| SPIPORT.transfer(0x05); | |||||
| status = SPIPORT.transfer(0); | |||||
| CSRELEASE(); | CSRELEASE(); | ||||
| SPI.endTransaction(); | |||||
| SPIPORT.endTransaction(); | |||||
| //Serial.printf("b=%02x.", status & 0xFF); | //Serial.printf("b=%02x.", status & 0xFF); | ||||
| if (!(status & 1)) break; | if (!(status & 1)) break; | ||||
| } | } | ||||
| memset(p, 0, len); | memset(p, 0, len); | ||||
| f = flags; | f = flags; | ||||
| SPI.beginTransaction(SPICONFIG); | |||||
| SPIPORT.beginTransaction(SPICONFIG); | |||||
| b = busy; | b = busy; | ||||
| if (b) { | if (b) { | ||||
| // read status register ... chip may no longer be busy | // read status register ... chip may no longer be busy | ||||
| CSASSERT(); | CSASSERT(); | ||||
| if (flags & FLAG_STATUS_CMD70) { | if (flags & FLAG_STATUS_CMD70) { | ||||
| SPI.transfer(0x70); | |||||
| status = SPI.transfer(0); | |||||
| SPIPORT.transfer(0x70); | |||||
| status = SPIPORT.transfer(0); | |||||
| if ((status & 0x80)) b = 0; | if ((status & 0x80)) b = 0; | ||||
| } else { | } else { | ||||
| SPI.transfer(0x05); | |||||
| status = SPI.transfer(0); | |||||
| SPIPORT.transfer(0x05); | |||||
| status = SPIPORT.transfer(0); | |||||
| if (!(status & 1)) b = 0; | if (!(status & 1)) b = 0; | ||||
| } | } | ||||
| CSRELEASE(); | CSRELEASE(); | ||||
| // which apparently have 2 different suspend | // which apparently have 2 different suspend | ||||
| // commands, for program vs erase | // commands, for program vs erase | ||||
| CSASSERT(); | CSASSERT(); | ||||
| SPI.transfer(0x06); // write enable (Micron req'd) | |||||
| SPIPORT.transfer(0x06); // write enable (Micron req'd) | |||||
| CSRELEASE(); | CSRELEASE(); | ||||
| delayMicroseconds(1); | delayMicroseconds(1); | ||||
| cmd = 0x75; //Suspend program/erase for almost all chips | cmd = 0x75; //Suspend program/erase for almost all chips | ||||
| // but Spansion just has to be different for program suspend! | // but Spansion just has to be different for program suspend! | ||||
| if ((f & FLAG_DIFF_SUSPEND) && (b == 1)) cmd = 0x85; | if ((f & FLAG_DIFF_SUSPEND) && (b == 1)) cmd = 0x85; | ||||
| CSASSERT(); | CSASSERT(); | ||||
| SPI.transfer(cmd); // Suspend command | |||||
| SPIPORT.transfer(cmd); // Suspend command | |||||
| CSRELEASE(); | CSRELEASE(); | ||||
| if (f & FLAG_STATUS_CMD70) { | if (f & FLAG_STATUS_CMD70) { | ||||
| // Micron chips don't actually suspend until flags read | // Micron chips don't actually suspend until flags read | ||||
| CSASSERT(); | CSASSERT(); | ||||
| SPI.transfer(0x70); | |||||
| SPIPORT.transfer(0x70); | |||||
| do { | do { | ||||
| status = SPI.transfer(0); | |||||
| status = SPIPORT.transfer(0); | |||||
| } while (!(status & 0x80)); | } while (!(status & 0x80)); | ||||
| CSRELEASE(); | CSRELEASE(); | ||||
| } else { | } else { | ||||
| CSASSERT(); | CSASSERT(); | ||||
| SPI.transfer(0x05); | |||||
| SPIPORT.transfer(0x05); | |||||
| do { | do { | ||||
| status = SPI.transfer(0); | |||||
| status = SPIPORT.transfer(0); | |||||
| } while ((status & 0x01)); | } while ((status & 0x01)); | ||||
| CSRELEASE(); | CSRELEASE(); | ||||
| } | } | ||||
| } else { | } else { | ||||
| // chip is busy with an operation that can not suspend | // 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 | wait(); // should we wait without ending | ||||
| b = 0; // the transaction?? | b = 0; // the transaction?? | ||||
| SPI.beginTransaction(SPICONFIG); | |||||
| SPIPORT.beginTransaction(SPICONFIG); | |||||
| } | } | ||||
| } | } | ||||
| do { | do { | ||||
| CSASSERT(); | CSASSERT(); | ||||
| // TODO: FIFO optimize.... | // TODO: FIFO optimize.... | ||||
| if (f & FLAG_32BIT_ADDR) { | 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 { | } 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(); | CSRELEASE(); | ||||
| p += rdlen; | p += rdlen; | ||||
| addr += rdlen; | addr += rdlen; | ||||
| } while (len > 0); | } while (len > 0); | ||||
| if (b) { | if (b) { | ||||
| CSASSERT(); | CSASSERT(); | ||||
| SPI.transfer(0x06); // write enable (Micron req'd) | |||||
| SPIPORT.transfer(0x06); // write enable (Micron req'd) | |||||
| CSRELEASE(); | CSRELEASE(); | ||||
| delayMicroseconds(1); | delayMicroseconds(1); | ||||
| cmd = 0x7A; | cmd = 0x7A; | ||||
| if ((f & FLAG_DIFF_SUSPEND) && (b == 1)) cmd = 0x8A; | if ((f & FLAG_DIFF_SUSPEND) && (b == 1)) cmd = 0x8A; | ||||
| CSASSERT(); | CSASSERT(); | ||||
| SPI.transfer(cmd); // Resume program/erase | |||||
| SPIPORT.transfer(cmd); // Resume program/erase | |||||
| CSRELEASE(); | CSRELEASE(); | ||||
| } | } | ||||
| SPI.endTransaction(); | |||||
| SPIPORT.endTransaction(); | |||||
| } | } | ||||
| void SerialFlashChip::write(uint32_t addr, const void *buf, uint32_t len) | void SerialFlashChip::write(uint32_t addr, const void *buf, uint32_t len) | ||||
| //Serial.printf("WR: addr %08X, len %d\n", addr, len); | //Serial.printf("WR: addr %08X, len %d\n", addr, len); | ||||
| do { | do { | ||||
| if (busy) wait(); | if (busy) wait(); | ||||
| SPI.beginTransaction(SPICONFIG); | |||||
| SPIPORT.beginTransaction(SPICONFIG); | |||||
| CSASSERT(); | CSASSERT(); | ||||
| // write enable command | // write enable command | ||||
| SPI.transfer(0x06); | |||||
| SPIPORT.transfer(0x06); | |||||
| CSRELEASE(); | CSRELEASE(); | ||||
| max = 256 - (addr & 0xFF); | max = 256 - (addr & 0xFF); | ||||
| pagelen = (len <= max) ? len : max; | pagelen = (len <= max) ? len : max; | ||||
| delayMicroseconds(1); // TODO: reduce this, but prefer safety first | delayMicroseconds(1); // TODO: reduce this, but prefer safety first | ||||
| CSASSERT(); | CSASSERT(); | ||||
| if (flags & FLAG_32BIT_ADDR) { | 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 { | } else { | ||||
| SPI.transfer16(0x0200 | ((addr >> 16) & 255)); | |||||
| SPI.transfer16(addr); | |||||
| SPIPORT.transfer16(0x0200 | ((addr >> 16) & 255)); | |||||
| SPIPORT.transfer16(addr); | |||||
| } | } | ||||
| addr += pagelen; | addr += pagelen; | ||||
| len -= pagelen; | len -= pagelen; | ||||
| do { | do { | ||||
| SPI.transfer(*p++); | |||||
| SPIPORT.transfer(*p++); | |||||
| } while (--pagelen > 0); | } while (--pagelen > 0); | ||||
| CSRELEASE(); | CSRELEASE(); | ||||
| busy = 4; | busy = 4; | ||||
| SPI.endTransaction(); | |||||
| SPIPORT.endTransaction(); | |||||
| } while (len > 0); | } while (len > 0); | ||||
| } | } | ||||
| if (die_index >= die_count) return; // all dies erased :-) | if (die_index >= die_count) return; // all dies erased :-) | ||||
| uint8_t die_size = 2; // in 16 Mbyte units | uint8_t die_size = 2; // in 16 Mbyte units | ||||
| if (id[2] == 0x22) die_size = 8; | if (id[2] == 0x22) die_size = 8; | ||||
| SPI.beginTransaction(SPICONFIG); | |||||
| SPIPORT.beginTransaction(SPICONFIG); | |||||
| CSASSERT(); | CSASSERT(); | ||||
| SPI.transfer(0x06); // write enable command | |||||
| SPIPORT.transfer(0x06); // write enable command | |||||
| CSRELEASE(); | CSRELEASE(); | ||||
| delayMicroseconds(1); | delayMicroseconds(1); | ||||
| CSASSERT(); | CSASSERT(); | ||||
| // die erase command | // 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(); | CSRELEASE(); | ||||
| //Serial.printf("Micron erase begin\n"); | //Serial.printf("Micron erase begin\n"); | ||||
| flags |= (die_index + 1) << 6; | flags |= (die_index + 1) << 6; | ||||
| } else { | } else { | ||||
| // All other chips support the bulk erase command | // All other chips support the bulk erase command | ||||
| SPI.beginTransaction(SPICONFIG); | |||||
| SPIPORT.beginTransaction(SPICONFIG); | |||||
| CSASSERT(); | CSASSERT(); | ||||
| // write enable command | // write enable command | ||||
| SPI.transfer(0x06); | |||||
| SPIPORT.transfer(0x06); | |||||
| CSRELEASE(); | CSRELEASE(); | ||||
| delayMicroseconds(1); | delayMicroseconds(1); | ||||
| CSASSERT(); | CSASSERT(); | ||||
| // bulk erase command | // bulk erase command | ||||
| SPI.transfer(0xC7); | |||||
| SPIPORT.transfer(0xC7); | |||||
| CSRELEASE(); | CSRELEASE(); | ||||
| SPI.endTransaction(); | |||||
| SPIPORT.endTransaction(); | |||||
| } | } | ||||
| busy = 3; | busy = 3; | ||||
| } | } | ||||
| { | { | ||||
| uint8_t f = flags; | uint8_t f = flags; | ||||
| if (busy) wait(); | if (busy) wait(); | ||||
| SPI.beginTransaction(SPICONFIG); | |||||
| SPIPORT.beginTransaction(SPICONFIG); | |||||
| CSASSERT(); | CSASSERT(); | ||||
| SPI.transfer(0x06); // write enable command | |||||
| SPIPORT.transfer(0x06); // write enable command | |||||
| CSRELEASE(); | CSRELEASE(); | ||||
| delayMicroseconds(1); | delayMicroseconds(1); | ||||
| CSASSERT(); | CSASSERT(); | ||||
| if (f & FLAG_32BIT_ADDR) { | 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 { | } else { | ||||
| SPI.transfer16(0xD800 | ((addr >> 16) & 255)); | |||||
| SPI.transfer16(addr); | |||||
| SPIPORT.transfer16(0xD800 | ((addr >> 16) & 255)); | |||||
| SPIPORT.transfer16(addr); | |||||
| } | } | ||||
| CSRELEASE(); | CSRELEASE(); | ||||
| SPI.endTransaction(); | |||||
| SPIPORT.endTransaction(); | |||||
| busy = 2; | busy = 2; | ||||
| } | } | ||||
| { | { | ||||
| uint32_t status; | uint32_t status; | ||||
| if (!busy) return true; | if (!busy) return true; | ||||
| SPI.beginTransaction(SPICONFIG); | |||||
| SPIPORT.beginTransaction(SPICONFIG); | |||||
| CSASSERT(); | CSASSERT(); | ||||
| if (flags & FLAG_STATUS_CMD70) { | if (flags & FLAG_STATUS_CMD70) { | ||||
| // some Micron chips require this different | // some Micron chips require this different | ||||
| // command to detect program and erase completion | // command to detect program and erase completion | ||||
| SPI.transfer(0x70); | |||||
| status = SPI.transfer(0); | |||||
| SPIPORT.transfer(0x70); | |||||
| status = SPIPORT.transfer(0); | |||||
| CSRELEASE(); | CSRELEASE(); | ||||
| SPI.endTransaction(); | |||||
| SPIPORT.endTransaction(); | |||||
| //Serial.printf("ready=%02x\n", status & 0xFF); | //Serial.printf("ready=%02x\n", status & 0xFF); | ||||
| if ((status & 0x80) == 0) return false; | if ((status & 0x80) == 0) return false; | ||||
| } else { | } else { | ||||
| // all others work by simply reading the status reg | // all others work by simply reading the status reg | ||||
| SPI.transfer(0x05); | |||||
| status = SPI.transfer(0); | |||||
| SPIPORT.transfer(0x05); | |||||
| status = SPIPORT.transfer(0); | |||||
| CSRELEASE(); | CSRELEASE(); | ||||
| SPI.endTransaction(); | |||||
| SPIPORT.endTransaction(); | |||||
| //Serial.printf("ready=%02x\n", status & 0xFF); | //Serial.printf("ready=%02x\n", status & 0xFF); | ||||
| if ((status & 1)) return false; | if ((status & 1)) return false; | ||||
| } | } | ||||
| cspin_basereg = PIN_TO_BASEREG(pin); | cspin_basereg = PIN_TO_BASEREG(pin); | ||||
| cspin_bitmask = PIN_TO_BITMASK(pin); | cspin_bitmask = PIN_TO_BITMASK(pin); | ||||
| SPI.begin(); | |||||
| SPIPORT.begin(); | |||||
| pinMode(pin, OUTPUT); | pinMode(pin, OUTPUT); | ||||
| CSRELEASE(); | CSRELEASE(); | ||||
| readID(id); | readID(id); | ||||
| if (size > 16777216) { | if (size > 16777216) { | ||||
| // more than 16 Mbyte requires 32 bit addresses | // more than 16 Mbyte requires 32 bit addresses | ||||
| f |= FLAG_32BIT_ADDR; | f |= FLAG_32BIT_ADDR; | ||||
| SPI.beginTransaction(SPICONFIG); | |||||
| SPIPORT.beginTransaction(SPICONFIG); | |||||
| if (id[0] == ID0_SPANSION) { | if (id[0] == ID0_SPANSION) { | ||||
| // spansion uses MSB of bank register | // spansion uses MSB of bank register | ||||
| CSASSERT(); | CSASSERT(); | ||||
| SPI.transfer16(0x1780); // bank register write | |||||
| SPIPORT.transfer16(0x1780); // bank register write | |||||
| CSRELEASE(); | CSRELEASE(); | ||||
| } else { | } else { | ||||
| // micron & winbond & macronix use command | // micron & winbond & macronix use command | ||||
| CSASSERT(); | CSASSERT(); | ||||
| SPI.transfer(0x06); // write enable | |||||
| SPIPORT.transfer(0x06); // write enable | |||||
| CSRELEASE(); | CSRELEASE(); | ||||
| delayMicroseconds(1); | delayMicroseconds(1); | ||||
| CSASSERT(); | CSASSERT(); | ||||
| SPI.transfer(0xB7); // enter 4 byte addr mode | |||||
| SPIPORT.transfer(0xB7); // enter 4 byte addr mode | |||||
| CSRELEASE(); | CSRELEASE(); | ||||
| } | } | ||||
| SPI.endTransaction(); | |||||
| SPIPORT.endTransaction(); | |||||
| if (id[0] == ID0_MICRON) f |= FLAG_MULTI_DIE; | if (id[0] == ID0_MICRON) f |= FLAG_MULTI_DIE; | ||||
| } | } | ||||
| if (id[0] == ID0_SPANSION) { | if (id[0] == ID0_SPANSION) { | ||||
| void SerialFlashChip::sleep() | void SerialFlashChip::sleep() | ||||
| { | { | ||||
| if (busy) wait(); | if (busy) wait(); | ||||
| SPI.beginTransaction(SPICONFIG); | |||||
| SPIPORT.beginTransaction(SPICONFIG); | |||||
| CSASSERT(); | CSASSERT(); | ||||
| SPI.transfer(0xB9); // Deep power down command | |||||
| SPIPORT.transfer(0xB9); // Deep power down command | |||||
| CSRELEASE(); | CSRELEASE(); | ||||
| } | } | ||||
| void SerialFlashChip::wakeup() | void SerialFlashChip::wakeup() | ||||
| { | { | ||||
| SPI.beginTransaction(SPICONFIG); | |||||
| SPIPORT.beginTransaction(SPICONFIG); | |||||
| CSASSERT(); | CSASSERT(); | ||||
| SPI.transfer(0xAB); // Wake up from deep power down command | |||||
| SPIPORT.transfer(0xAB); // Wake up from deep power down command | |||||
| CSRELEASE(); | CSRELEASE(); | ||||
| } | } | ||||
| void SerialFlashChip::readID(uint8_t *buf) | void SerialFlashChip::readID(uint8_t *buf) | ||||
| { | { | ||||
| if (busy) wait(); | if (busy) wait(); | ||||
| SPI.beginTransaction(SPICONFIG); | |||||
| SPIPORT.beginTransaction(SPICONFIG); | |||||
| CSASSERT(); | 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) { | 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(); | CSRELEASE(); | ||||
| SPI.endTransaction(); | |||||
| SPIPORT.endTransaction(); | |||||
| //Serial.printf("ID: %02X %02X %02X\n", buf[0], buf[1], buf[2]); | //Serial.printf("ID: %02X %02X %02X\n", buf[0], buf[1], buf[2]); | ||||
| } | } | ||||
| void SerialFlashChip::readSerialNumber(uint8_t *buf) //needs room for 8 bytes | void SerialFlashChip::readSerialNumber(uint8_t *buf) //needs room for 8 bytes | ||||
| { | { | ||||
| if (busy) wait(); | if (busy) wait(); | ||||
| SPI.beginTransaction(SPICONFIG); | |||||
| SPIPORT.beginTransaction(SPICONFIG); | |||||
| CSASSERT(); | 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++) { | for (int i=0; i<8; i++) { | ||||
| buf[i] = SPI.transfer(0); | |||||
| buf[i] = SPIPORT.transfer(0); | |||||
| } | } | ||||
| CSRELEASE(); | 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]); | // 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]); | ||||
| } | } | ||||