++

Fork of SerialFlash by tom dunigan

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers SerialFlash.cpp Source File

SerialFlash.cpp

00001 /* SerialFlash Library - for filesystem-like access to SPI Serial Flash memory
00002  * https://github.com/PaulStoffregen/SerialFlash
00003  * Copyright (C) 2015, Paul Stoffregen, paul@pjrc.com
00004  *
00005  * Development of this library was funded by PJRC.COM, LLC by sales of Teensy.
00006  * Please support PJRC's efforts to develop open source software by purchasing
00007  * Teensy or other genuine PJRC products.
00008  *
00009  * Permission is hereby granted, free of charge, to any person obtaining a copy
00010  * of this software and associated documentation files (the "Software"), to deal
00011  * in the Software without restriction, including without limitation the rights
00012  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00013  * copies of the Software, and to permit persons to whom the Software is
00014  * furnished to do so, subject to the following conditions:
00015  *
00016  * The above copyright notice, development funding notice, and this permission
00017  * notice shall be included in all copies or substantial portions of the Software.
00018  *
00019  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00020  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00021  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00022  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00023  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00024  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
00025  * THE SOFTWARE.
00026  */
00027 #include "mbed.h"
00028 #include "SerialFlash.h"
00029 
00030 static SPI spi(PB_15,PB_14,PB_13); // mosi, miso, sclk
00031    DigitalOut cspin(PB_12);
00032 
00033 #define CSASSERT()  cspin = 0
00034 #define CSRELEASE() cspin = 1
00035 
00036 uint16_t SerialFlashChip::dirindex = 0;
00037 uint8_t SerialFlashChip::flags = 0;
00038 uint8_t SerialFlashChip::busy = 0;
00039 
00040 
00041 #define FLAG_32BIT_ADDR     0x01    // larger than 16 MByte address
00042 #define FLAG_STATUS_CMD70   0x02    // requires special busy flag check
00043 #define FLAG_DIFF_SUSPEND   0x04    // uses 2 different suspend commands
00044 #define FLAG_MULTI_DIE      0x08    // multiple die, don't read cross 32M barrier
00045 #define FLAG_256K_BLOCKS    0x10    // has 256K erase blocks
00046 #define FLAG_DIE_MASK       0xC0    // top 2 bits count during multi-die erase
00047 
00048 void SerialFlashChip::wait(void)
00049 {
00050     uint32_t status;
00051     //Serial.print("wait-");
00052     while (1) {
00053         CSASSERT();
00054         if (flags & FLAG_STATUS_CMD70) {
00055             // some Micron chips require this different
00056             // command to detect program and erase completion
00057             spi.write(0x70);
00058             status = spi.write(0);
00059             CSRELEASE();
00060             //Serial.printf("b=%02x.", status & 0xFF);
00061             if ((status & 0x80)) break;
00062         } else {
00063             // all others work by simply reading the status reg
00064             spi.write(0x05);
00065             status = spi.write(0);
00066             CSRELEASE();
00067             //Serial.printf("b=%02x.", status & 0xFF);
00068             if (!(status & 1)) break;
00069         }
00070     }
00071     busy = 0;
00072     //Serial.println();
00073 }
00074 
00075 void SerialFlashChip::read(uint32_t addr, void *buf, uint32_t len)
00076 {
00077     uint8_t *p = (uint8_t *)buf;
00078     uint8_t b, f, status, cmd;
00079 
00080     memset(p, 0, len);
00081     f = flags;
00082     b = busy;
00083     if (b) {
00084         // read status register ... chip may no longer be busy
00085         CSASSERT();
00086         if (flags & FLAG_STATUS_CMD70) {
00087             spi.write(0x70);
00088             status = spi.write(0);
00089             if ((status & 0x80)) b = 0;
00090         } else {
00091             spi.write(0x05);
00092             status = spi.write(0);
00093             if (!(status & 1)) b = 0;
00094         }
00095         CSRELEASE();
00096         if (b == 0) {
00097             // chip is no longer busy :-)
00098             busy = 0;
00099         } else if (b < 3) {
00100             // TODO: this may not work on Spansion chips
00101             // which apparently have 2 different suspend
00102             // commands, for program vs erase
00103             CSASSERT();
00104             spi.write(0x06); // write enable (Micron req'd)
00105             CSRELEASE();
00106             wait_us(1);
00107             cmd = 0x75; //Suspend program/erase for almost all chips
00108             // but Spansion just has to be different for program suspend!
00109             if ((f & FLAG_DIFF_SUSPEND) && (b == 1)) cmd = 0x85;
00110             CSASSERT();
00111             spi.write(cmd); // Suspend command
00112             CSRELEASE();
00113             if (f & FLAG_STATUS_CMD70) {
00114                 // Micron chips don't actually suspend until flags read
00115                 CSASSERT();
00116                 spi.write(0x70);
00117                 do {
00118                     status = spi.write(0);
00119                 } while (!(status & 0x80));
00120                 CSRELEASE();
00121             } else {
00122                 CSASSERT();
00123                 spi.write(0x05);
00124                 do {
00125                     status = spi.write(0);
00126                 } while ((status & 0x01));
00127                 CSRELEASE();
00128             }
00129         } else {
00130             // chip is busy with an operation that can not suspend
00131             wait();         // should we wait without ending
00132             b = 0;          // the transaction??
00133         }
00134     }
00135     do {
00136         uint32_t rdlen = len;
00137         if (f & FLAG_MULTI_DIE) {
00138             if ((addr & 0xFE000000) != ((addr + len - 1) & 0xFE000000)) {
00139                 rdlen = 0x2000000 - (addr & 0x1FFFFFF);
00140             }
00141         }
00142         CSASSERT();
00143         // TODO: FIFO optimize....
00144         if (f & FLAG_32BIT_ADDR) {
00145             spi.write(0x03);
00146             spi.write(addr >> 24);
00147             spi.write(addr >> 16);
00148             spi.write(addr >> 8);
00149             spi.write(addr);
00150         } else {
00151             spi.write(3);
00152             spi.write(addr >> 16);
00153             spi.write(addr >> 8);
00154             spi.write(addr);
00155         }
00156         uint32_t i = rdlen;  // need block transfer
00157         while(i>0){
00158             *p++ = spi.write(0);
00159             i--;
00160         }
00161         CSRELEASE();
00162         addr += rdlen;
00163         len -= rdlen;
00164     } while (len > 0);
00165     if (b) {
00166         CSASSERT();
00167         spi.write(0x06); // write enable (Micron req'd)
00168         CSRELEASE();
00169         wait_us(1);
00170         cmd = 0x7A;
00171         if ((f & FLAG_DIFF_SUSPEND) && (b == 1)) cmd = 0x8A;
00172         CSASSERT();
00173         spi.write(cmd); // Resume program/erase
00174         CSRELEASE();
00175     }
00176 }
00177 
00178 void SerialFlashChip::write(uint32_t addr, const void *buf, uint32_t len)
00179 {
00180     const uint8_t *p = (const uint8_t *)buf;
00181     uint32_t max, pagelen;
00182 
00183      //Serial.printf("WR: addr %08X, len %d\n", addr, len);
00184     do {
00185         if (busy) wait();
00186         CSASSERT();
00187         // write enable command
00188         spi.write(0x06);
00189         CSRELEASE();
00190         max = 256 - (addr & 0xFF);
00191         pagelen = (len <= max) ? len : max;
00192          //Serial.printf("WR: addr %08X, pagelen %d\n", addr, pagelen);
00193         wait_us(1); // TODO: reduce this, but prefer safety first
00194         CSASSERT();
00195         if (flags & FLAG_32BIT_ADDR) {
00196             spi.write(0x02); // program page command
00197             spi.write(addr >> 24);
00198             spi.write(addr >> 16);
00199             spi.write(addr >> 8);
00200             spi.write(addr);
00201         } else {
00202             spi.write(2);
00203             spi.write(addr >> 16);
00204             spi.write(addr >> 8);
00205             spi.write(addr);
00206         }
00207         addr += pagelen;
00208         len -= pagelen;
00209         do {
00210             spi.write(*p++);
00211         } while (--pagelen > 0);
00212         CSRELEASE();
00213         busy = 4;
00214     } while (len > 0);
00215 }
00216 
00217 void SerialFlashChip::eraseAll()
00218 {
00219     if (busy) wait();
00220     uint8_t id[5];
00221     readID(id);
00222     //Serial.printf("ID: %02X %02X %02X\n", id[0], id[1], id[2]);
00223     if (id[0] == 0x20 && id[2] >= 0x20 && id[2] <= 0x22) {
00224         // Micron's multi-die chips require special die erase commands
00225         //  N25Q512A    20 BA 20  2 dies  32 Mbyte/die   65 nm transitors
00226         //  N25Q00AA    20 BA 21  4 dies  32 Mbyte/die   65 nm transitors
00227         //  MT25QL02GC  20 BA 22  2 dies  128 Mbyte/die  45 nm transitors
00228         uint8_t die_count = 2;
00229         if (id[2] == 0x21) die_count = 4;
00230         uint8_t die_index = flags >> 6;
00231          //Serial.printf("Micron die erase %d\n", die_index);
00232         flags &= 0x3F;
00233         if (die_index >= die_count) return; // all dies erased :-)
00234         uint8_t die_size = 2;  // in 16 Mbyte units
00235         if (id[2] == 0x22) die_size = 8;
00236         CSASSERT();
00237         spi.write(0x06); // write enable command
00238         CSRELEASE();
00239          wait_us(1);
00240         CSASSERT();
00241         // die erase command
00242         spi.write(0xC4);
00243         spi.write((die_index * die_size) );
00244         spi.write(0);
00245         spi.write(0);
00246         spi.write(0);
00247         CSRELEASE();
00248          //Serial.printf("Micron erase begin\n");
00249         flags |= (die_index + 1) << 6;
00250     } else {
00251         // All other chips support the bulk erase command
00252         CSASSERT();
00253         // write enable command
00254         spi.write(0x06);
00255         CSRELEASE();
00256          wait_us(1);
00257         CSASSERT();
00258         // bulk erase command
00259         spi.write(0xC7);
00260         CSRELEASE();
00261     }
00262     busy = 3;
00263 }
00264 
00265 void SerialFlashChip::eraseBlock(uint32_t addr)
00266 {
00267     uint8_t f = flags;
00268     if (busy) wait();
00269     CSASSERT();
00270     spi.write(0x06); // write enable command
00271     CSRELEASE();
00272      wait_us(1);
00273     CSASSERT();
00274     if (f & FLAG_32BIT_ADDR) {
00275         spi.write(0xD8);
00276         spi.write(addr >> 24);
00277         spi.write(addr >> 16);
00278         spi.write(addr >> 8);
00279         spi.write(addr);
00280     } else {
00281         spi.write(0xD8);
00282         spi.write(addr >> 16);
00283         spi.write(addr >> 8);
00284         spi.write(addr);
00285     }
00286     CSRELEASE();
00287     busy = 2;
00288 }
00289 
00290 
00291 bool SerialFlashChip::ready()
00292 {
00293     uint32_t status;
00294     if (!busy) return true;
00295     CSASSERT();
00296     if (flags & FLAG_STATUS_CMD70) {
00297         // some Micron chips require this different
00298         // command to detect program and erase completion
00299         spi.write(0x70);
00300         status = spi.write(0);
00301         CSRELEASE();
00302         //Serial.printf("ready=%02x\n", status & 0xFF);
00303         if ((status & 0x80) == 0) return false;
00304     } else {
00305         // all others work by simply reading the status reg
00306         spi.write(0x05);
00307         status = spi.write(0);
00308         CSRELEASE();
00309         //Serial.printf("ready=%02x\n", status & 0xFF);
00310         if ((status & 1)) return false;
00311     }
00312     busy = 0;
00313     if (flags & 0xC0) {
00314         // continue a multi-die erase
00315         eraseAll();
00316         return false;
00317     }
00318     return true;
00319 }
00320 
00321 
00322 #define ID0_WINBOND 0xEF
00323 #define ID0_SPANSION    0x01
00324 #define ID0_MICRON  0x20
00325 #define ID0_MACRONIX    0xC2
00326 #define ID0_SST     0xBF
00327 
00328 //#define FLAG_32BIT_ADDR   0x01    // larger than 16 MByte address
00329 //#define FLAG_STATUS_CMD70 0x02    // requires special busy flag check
00330 //#define FLAG_DIFF_SUSPEND 0x04    // uses 2 different suspend commands
00331 //#define FLAG_256K_BLOCKS  0x10    // has 256K erase blocks
00332 
00333 bool SerialFlashChip::begin(uint8_t pin)
00334 {
00335     uint8_t id[5];
00336     uint8_t f;
00337     uint32_t size;
00338 
00339     spi.frequency(30000000);   // max
00340     CSRELEASE();
00341     readID(id);
00342     f = 0;
00343     size = capacity(id);
00344     if (size > 16777216) {
00345         // more than 16 Mbyte requires 32 bit addresses
00346         f |= FLAG_32BIT_ADDR;
00347         if (id[0] == ID0_SPANSION) {
00348             // spansion uses MSB of bank register
00349             CSASSERT();
00350             spi.write(0x17); // bank register write
00351             spi.write(0x80);
00352             CSRELEASE();
00353         } else {
00354             // micron & winbond & macronix use command
00355             CSASSERT();
00356             spi.write(0x06); // write enable
00357             CSRELEASE();
00358             wait_us(1);
00359             CSASSERT();
00360             spi.write(0xB7); // enter 4 byte addr mode
00361             CSRELEASE();
00362         }
00363         if (id[0] == ID0_MICRON) f |= FLAG_MULTI_DIE;
00364     }
00365     if (id[0] == ID0_SPANSION) {
00366         // Spansion has separate suspend commands
00367         f |= FLAG_DIFF_SUSPEND;
00368         if (!id[4]) {
00369             // Spansion chips with id[4] == 0 use 256K sectors
00370             f |= FLAG_256K_BLOCKS;
00371         }
00372     }
00373     if (id[0] == ID0_MICRON) {
00374         // Micron requires busy checks with a different command
00375         f |= FLAG_STATUS_CMD70; // TODO: all or just multi-die chips?
00376     }
00377     flags = f;
00378     readID(id);
00379     return true;
00380 }
00381 
00382 // chips tested: https://github.com/PaulStoffregen/SerialFlash/pull/12#issuecomment-169596992
00383 //
00384 void SerialFlashChip::sleep()
00385 {
00386     if (busy) wait();
00387     CSASSERT();
00388     spi.write(0xB9); // Deep power down command
00389     CSRELEASE();
00390 }
00391 
00392 void SerialFlashChip::wakeup()
00393 {
00394     CSASSERT();
00395     spi.write(0xAB); // Wake up from deep power down command
00396     CSRELEASE();
00397 }
00398 
00399 void SerialFlashChip::readID(uint8_t *buf)
00400 {
00401     if (busy) wait();
00402     CSASSERT();
00403     spi.write(0x9F);
00404     buf[0] = spi.write(0); // manufacturer ID
00405     buf[1] = spi.write(0); // memory type
00406     buf[2] = spi.write(0); // capacity
00407     if (buf[0] == ID0_SPANSION) {
00408         buf[3] = spi.write(0); // ID-CFI
00409         buf[4] = spi.write(0); // sector size
00410     }
00411     CSRELEASE();
00412     //Serial.printf("ID: %02X %02X %02X\n", buf[0], buf[1], buf[2]);
00413 }
00414 
00415 void SerialFlashChip::readSerialNumber(uint8_t *buf) //needs room for 8 bytes
00416 {
00417     if (busy) wait();
00418     CSASSERT();
00419     spi.write(0x4B);         
00420     spi.write(0);  
00421     spi.write(0);
00422     spi.write(0);
00423     spi.write(0);
00424     for (int i=0; i<8; i++) {       
00425         buf[i] = spi.write(0);
00426     }
00427     CSRELEASE();
00428 //  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]);
00429 }
00430 
00431 uint32_t SerialFlashChip::capacity(const uint8_t *id)
00432 {
00433     uint32_t n = 1048576; // unknown chips, default to 1 MByte
00434 
00435     if (id[2] >= 16 && id[2] <= 31) {
00436         n = 1ul << id[2];
00437     } else
00438     if (id[2] >= 32 && id[2] <= 37) {
00439         n = 1ul << (id[2] - 6);
00440     }
00441     //Serial.printf("capacity %lu\n", n);
00442     return n;
00443 }
00444 
00445 uint32_t SerialFlashChip::blockSize()
00446 {
00447     // Spansion chips >= 512 mbit use 256K sectors
00448     if (flags & FLAG_256K_BLOCKS) return 262144;
00449     // everything else seems to have 64K sectors
00450     return 65536;
00451 }
00452 
00453 
00454 
00455 
00456 /*
00457 Chip        Uniform Sector Erase
00458         20/21   52  D8/DC
00459         -----   --  -----
00460 W25Q64CV    4   32  64
00461 W25Q128FV   4   32  64
00462 S25FL127S           64
00463 N25Q512A    4       64
00464 N25Q00AA    4       64
00465 S25FL512S           256
00466 SST26VF032  4
00467 */
00468 
00469 
00470 
00471 //          size    sector          busy    pgm/erase   chip
00472 // Part         Mbyte   kbyte   ID bytes    cmd suspend     erase
00473 // ----         ----    -----   --------    --- -------     -----
00474 // Winbond W25Q64CV     8   64  EF 40 17
00475 // Winbond W25Q128FV    16  64  EF 40 18    05  single      60 & C7
00476 // Winbond W25Q256FV    32  64  EF 40 19    
00477 // Spansion S25FL064A   8   ?   01 02 16
00478 // Spansion S25FL127S   16  64  01 20 18    05
00479 // Spansion S25FL128P   16  64  01 20 18
00480 // Spansion S25FL256S   32  64  01 02 19    05          60 & C7
00481 // Spansion S25FL512S   64  256 01 02 20
00482 // Macronix MX25L12805D 16  ?   C2 20 18
00483 // Macronix MX66L51235F 64      C2 20 1A
00484 // Numonyx M25P128  16  ?   20 20 18
00485 // Micron M25P80    1   ?   20 20 14
00486 // Micron N25Q128A  16  64  20 BA 18
00487 // Micron N25Q512A  64  ?   20 BA 20    70  single      C4 x2
00488 // Micron N25Q00AA  128 64  20 BA 21        single      C4 x4
00489 // Micron MT25QL02GC    256 64  20 BA 22    70          C4 x2
00490 // SST SST25WF010   1/8 ?   BF 25 02
00491 // SST SST25WF020   1/4 ?   BF 25 03
00492 // SST SST25WF040   1/2 ?   BF 25 04
00493 // SST SST25VF016B  1   ?   BF 25 41
00494 // SST26VF016           ?   BF 26 01
00495 // SST26VF032           ?   BF 26 02
00496 // SST25VF032       4   64  BF 25 4A
00497 // SST26VF064       8   ?   BF 26 43
00498 // LE25U40CMC       1/2 64  62 06 13
00499 
00500 SerialFlashChip SerialFlash;