Flash handler for M25P* chips with no Device ID.
Fork of flash25spi by
flash25spi.cpp@10:52b0df37bd21, 2014-06-28 (annotated)
- Committer:
- Tomo2k
- Date:
- Sat Jun 28 06:56:54 2014 +0000
- Revision:
- 10:52b0df37bd21
- Parent:
- 8:7b09546cb412
Updated to public edition of RTOS_SPI
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
stonie | 0:d07f90d3c670 | 1 | /* This library is based on the Ser25lcxxx library by Hendrik Lipka |
stonie | 0:d07f90d3c670 | 2 | * It was adapted to flash memory chips on 19.2.2011 by Klaus Steinhammer |
Tomo2k | 3:318fabd6708c | 3 | |
Tomo2k | 3:318fabd6708c | 4 | * It was further adapted to M25P* SPI Flash memory chips that do not |
Tomo2k | 3:318fabd6708c | 5 | * support the "RDID" device ID instructions on 25.03.2014 by Richard Thompson |
Tomo2k | 3:318fabd6708c | 6 | * |
Tomo2k | 3:318fabd6708c | 7 | * The BSD license also applies to this code - have fun. |
stonie | 0:d07f90d3c670 | 8 | */ |
stonie | 0:d07f90d3c670 | 9 | |
stonie | 0:d07f90d3c670 | 10 | /* |
stonie | 0:d07f90d3c670 | 11 | * Ser25lcxxx library |
stonie | 0:d07f90d3c670 | 12 | * Copyright (c) 2010 Hendrik Lipka |
Tomo2k | 3:318fabd6708c | 13 | * |
stonie | 0:d07f90d3c670 | 14 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
stonie | 0:d07f90d3c670 | 15 | * of this software and associated documentation files (the "Software"), to deal |
stonie | 0:d07f90d3c670 | 16 | * in the Software without restriction, including without limitation the rights |
stonie | 0:d07f90d3c670 | 17 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
stonie | 0:d07f90d3c670 | 18 | * copies of the Software, and to permit persons to whom the Software is |
stonie | 0:d07f90d3c670 | 19 | * furnished to do so, subject to the following conditions: |
Tomo2k | 3:318fabd6708c | 20 | * |
stonie | 0:d07f90d3c670 | 21 | * The above copyright notice and this permission notice shall be included in |
stonie | 0:d07f90d3c670 | 22 | * all copies or substantial portions of the Software. |
Tomo2k | 3:318fabd6708c | 23 | * |
stonie | 0:d07f90d3c670 | 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
stonie | 0:d07f90d3c670 | 25 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
stonie | 0:d07f90d3c670 | 26 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
stonie | 0:d07f90d3c670 | 27 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
stonie | 0:d07f90d3c670 | 28 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
stonie | 0:d07f90d3c670 | 29 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
stonie | 0:d07f90d3c670 | 30 | * THE SOFTWARE. |
stonie | 0:d07f90d3c670 | 31 | */ |
stonie | 0:d07f90d3c670 | 32 | |
stonie | 0:d07f90d3c670 | 33 | |
stonie | 0:d07f90d3c670 | 34 | #include "flash25spi.h" |
stonie | 0:d07f90d3c670 | 35 | #include "wait_api.h" |
stonie | 0:d07f90d3c670 | 36 | |
Tomo2k | 4:af870c53c0e9 | 37 | //#define DEBUG |
stonie | 0:d07f90d3c670 | 38 | |
stonie | 0:d07f90d3c670 | 39 | struct dataBase { |
Tomo2k | 3:318fabd6708c | 40 | uint8_t signature; |
Tomo2k | 3:318fabd6708c | 41 | size_t memsize; |
Tomo2k | 3:318fabd6708c | 42 | size_t sectorsize; |
Tomo2k | 3:318fabd6708c | 43 | size_t pagesize; |
stonie | 0:d07f90d3c670 | 44 | }; |
stonie | 0:d07f90d3c670 | 45 | |
stonie | 0:d07f90d3c670 | 46 | const struct dataBase devices[] = { |
Tomo2k | 3:318fabd6708c | 47 | //signature, memsize, sectorsize, pagesize |
Tomo2k | 3:318fabd6708c | 48 | { 0x10, 0x20000, 0x8000, 0x100 }, // M25P10-A Numonyx/ST |
Tomo2k | 7:fae78b14f38f | 49 | { 0x11, 0x40000, 0x10000, 0x100 }, // M25P20 Numonyx/ST/Micron |
Tomo2k | 3:318fabd6708c | 50 | { 0x12, 0x80000, 0x10000, 0x100 }, // M25P40 Numonyx/ST |
Tomo2k | 3:318fabd6708c | 51 | { 0x00, 0x00, 0x00, 0x00}, // end of table |
stonie | 0:d07f90d3c670 | 52 | }; |
stonie | 0:d07f90d3c670 | 53 | |
Tomo2k | 3:318fabd6708c | 54 | // Instruction Set |
Tomo2k | 3:318fabd6708c | 55 | const uint8_t FLASH_WREN = 0x06; // Write Enable |
Tomo2k | 3:318fabd6708c | 56 | //const uint8_t FLASH_WRDI = 0x04; // Write Disable (Unused) |
Tomo2k | 3:318fabd6708c | 57 | //const uint8_t FLASH_RDID = 0x9F; // Read Device IDentification (not supported) |
Tomo2k | 3:318fabd6708c | 58 | const uint8_t FLASH_RDSR = 0x05; // Read Status Register |
Tomo2k | 8:7b09546cb412 | 59 | //const uint8_t FLASH_WRSR = 0x01; // Write Status Register (not used) |
Tomo2k | 3:318fabd6708c | 60 | const uint8_t FLASH_READ = 0x03; // Read Data Bytes |
Tomo2k | 3:318fabd6708c | 61 | //const uint8_t FLASH_FAST_READ = 0x0B; // Read Data Bytes Faster |
Tomo2k | 3:318fabd6708c | 62 | const uint8_t FLASH_PP = 0x02; // Page Program (max pagesize bytes) |
Tomo2k | 3:318fabd6708c | 63 | const uint8_t FLASH_SE = 0xD8; // Sector Erase |
Tomo2k | 3:318fabd6708c | 64 | const uint8_t FLASH_BE = 0xC7; // Bulk Erase (wipe complete system) |
Tomo2k | 3:318fabd6708c | 65 | const uint8_t FLASH_DP = 0xB9; // Deep Power Down (shutdown into very-low-power) |
Tomo2k | 3:318fabd6708c | 66 | const uint8_t FLASH_RES = 0xAB; // Wake Up and Read Electronic Signature |
stonie | 0:d07f90d3c670 | 67 | |
stonie | 0:d07f90d3c670 | 68 | #define HIGH(x) ((x&0xff0000)>>16) |
stonie | 0:d07f90d3c670 | 69 | #define MID(x) ((x&0xff00)>>8) |
stonie | 0:d07f90d3c670 | 70 | #define LOW(x) (x&0xff) |
stonie | 0:d07f90d3c670 | 71 | |
Tomo2k | 8:7b09546cb412 | 72 | |
Tomo2k | 8:7b09546cb412 | 73 | FlashM25PSpi::FlashM25PSpi(RTOS_SPI *spi, PinName enable, int bits, int mode) : |
Tomo2k | 3:318fabd6708c | 74 | _spi(spi) |
Tomo2k | 3:318fabd6708c | 75 | , _enable(enable) |
Tomo2k | 3:318fabd6708c | 76 | , _size(0) |
Tomo2k | 3:318fabd6708c | 77 | { |
Tomo2k | 8:7b09546cb412 | 78 | if (bits) _spi->format(bits, mode); |
Tomo2k | 3:318fabd6708c | 79 | |
Tomo2k | 3:318fabd6708c | 80 | _enable = 1; |
Tomo2k | 8:7b09546cb412 | 81 | if (osKernelRunning()) { |
Tomo2k | 8:7b09546cb412 | 82 | Thread::wait(1); |
Tomo2k | 8:7b09546cb412 | 83 | } else { |
Tomo2k | 8:7b09546cb412 | 84 | wait_us(1000); |
Tomo2k | 8:7b09546cb412 | 85 | } |
Tomo2k | 3:318fabd6708c | 86 | _enable = 0; |
stonie | 0:d07f90d3c670 | 87 | wait_us(1); |
Tomo2k | 3:318fabd6708c | 88 | uint8_t chipSig; |
Tomo2k | 3:318fabd6708c | 89 | // Reset and Ask for chip signature |
Tomo2k | 3:318fabd6708c | 90 | _spi->write(FLASH_RES); |
Tomo2k | 3:318fabd6708c | 91 | _spi->write(0); // Dummy writes |
Tomo2k | 3:318fabd6708c | 92 | _spi->write(0); // Dummy writes |
Tomo2k | 3:318fabd6708c | 93 | _spi->write(0); // Dummy writes |
Tomo2k | 3:318fabd6708c | 94 | chipSig = _spi->write(0); // Get Electronic Signature |
stonie | 0:d07f90d3c670 | 95 | wait_us(1); |
Tomo2k | 3:318fabd6708c | 96 | _enable = 1; |
stonie | 0:d07f90d3c670 | 97 | |
Tomo2k | 3:318fabd6708c | 98 | unsigned int i = 0; |
stonie | 0:d07f90d3c670 | 99 | while (_size == 0) { |
Tomo2k | 3:318fabd6708c | 100 | if (devices[i].memsize == 0) { // Nobody makes a memory of zero size |
stonie | 0:d07f90d3c670 | 101 | #ifdef DEBUG |
Tomo2k | 4:af870c53c0e9 | 102 | printf("\r\nUnknown Flash Memory signature: %xh\r\n", chipSig); |
stonie | 0:d07f90d3c670 | 103 | #endif |
stonie | 0:d07f90d3c670 | 104 | return; |
stonie | 0:d07f90d3c670 | 105 | } |
Tomo2k | 3:318fabd6708c | 106 | if (chipSig == devices[i].signature) { |
Tomo2k | 3:318fabd6708c | 107 | _size=devices[i].memsize; |
Tomo2k | 3:318fabd6708c | 108 | _sectorSize=devices[i].sectorsize; |
Tomo2k | 3:318fabd6708c | 109 | _pageSize=devices[i].pagesize; |
stonie | 0:d07f90d3c670 | 110 | #ifdef DEBUG |
Tomo2k | 4:af870c53c0e9 | 111 | printf("\r\nFlash Memory Sig:%Xh : %u bytes Org: %x, %x\r\n", |
Tomo2k | 4:af870c53c0e9 | 112 | chipSig, _size, _sectorSize, _pageSize); |
stonie | 0:d07f90d3c670 | 113 | #endif |
Tomo2k | 3:318fabd6708c | 114 | } else |
stonie | 0:d07f90d3c670 | 115 | i++; |
stonie | 0:d07f90d3c670 | 116 | } |
stonie | 0:d07f90d3c670 | 117 | } |
stonie | 0:d07f90d3c670 | 118 | |
Tomo2k | 3:318fabd6708c | 119 | FlashM25PSpi::~FlashM25PSpi() |
Tomo2k | 3:318fabd6708c | 120 | { |
Tomo2k | 8:7b09546cb412 | 121 | // Wait until chip finishes cycle |
Tomo2k | 8:7b09546cb412 | 122 | // waitForWrite(); |
Tomo2k | 3:318fabd6708c | 123 | // Shutdown the flash chip into lowest-power mode |
Tomo2k | 3:318fabd6708c | 124 | _enable = 0; |
Tomo2k | 3:318fabd6708c | 125 | wait_us(1); |
Tomo2k | 3:318fabd6708c | 126 | _spi->write(FLASH_DP); |
Tomo2k | 3:318fabd6708c | 127 | wait_us(1); |
Tomo2k | 3:318fabd6708c | 128 | _enable = 1; |
stonie | 0:d07f90d3c670 | 129 | } |
stonie | 0:d07f90d3c670 | 130 | |
Tomo2k | 3:318fabd6708c | 131 | bool FlashM25PSpi::read(uint32_t startAddr, void* destination, size_t len) |
Tomo2k | 3:318fabd6708c | 132 | { |
Tomo2k | 3:318fabd6708c | 133 | // Check size |
Tomo2k | 3:318fabd6708c | 134 | if (startAddr + len > _size) |
Tomo2k | 3:318fabd6708c | 135 | return false; |
Tomo2k | 3:318fabd6708c | 136 | _enable = 0; |
stonie | 0:d07f90d3c670 | 137 | wait_us(1); |
Tomo2k | 3:318fabd6708c | 138 | |
Tomo2k | 3:318fabd6708c | 139 | // Send address |
Tomo2k | 3:318fabd6708c | 140 | _spi->write(FLASH_READ); |
Tomo2k | 3:318fabd6708c | 141 | _spi->write(HIGH(startAddr)); |
Tomo2k | 3:318fabd6708c | 142 | _spi->write(MID(startAddr)); |
Tomo2k | 3:318fabd6708c | 143 | _spi->write(LOW(startAddr)); |
Tomo2k | 3:318fabd6708c | 144 | |
Tomo2k | 8:7b09546cb412 | 145 | uint8_t dummy = 0; |
Tomo2k | 8:7b09546cb412 | 146 | |
Tomo2k | 8:7b09546cb412 | 147 | // Bulk read into destination buffer (DMA) |
Tomo2k | 8:7b09546cb412 | 148 | // Be sure to write the same dummy databyte repeatedly |
Tomo2k | 8:7b09546cb412 | 149 | _spi->bulkReadWrite((uint8_t*)destination, &dummy, len, false); |
Tomo2k | 8:7b09546cb412 | 150 | |
stonie | 0:d07f90d3c670 | 151 | wait_us(1); |
Tomo2k | 3:318fabd6708c | 152 | _enable = 1; |
Tomo2k | 3:318fabd6708c | 153 | return true; |
stonie | 0:d07f90d3c670 | 154 | } |
stonie | 0:d07f90d3c670 | 155 | |
Tomo2k | 3:318fabd6708c | 156 | bool FlashM25PSpi::write(uint32_t startAddr, const void* data, size_t len) |
Tomo2k | 3:318fabd6708c | 157 | { |
Tomo2k | 3:318fabd6708c | 158 | if (startAddr + len > _size) |
stonie | 2:14c5db5e54df | 159 | return false; |
stonie | 0:d07f90d3c670 | 160 | |
Tomo2k | 8:7b09546cb412 | 161 | // Cast to uint8_t |
Tomo2k | 8:7b09546cb412 | 162 | uint8_t *data8 = (uint8_t*)data; |
Tomo2k | 4:af870c53c0e9 | 163 | |
Tomo2k | 3:318fabd6708c | 164 | size_t ofs = 0; |
Tomo2k | 3:318fabd6708c | 165 | while (ofs < len) { |
stonie | 0:d07f90d3c670 | 166 | // calculate amount of data to write into current page |
Tomo2k | 3:318fabd6708c | 167 | size_t pageLen = _pageSize - ((startAddr + ofs) % _pageSize); |
Tomo2k | 3:318fabd6708c | 168 | if (ofs + pageLen > len) |
Tomo2k | 3:318fabd6708c | 169 | pageLen = len - ofs; |
stonie | 0:d07f90d3c670 | 170 | // write single page |
Tomo2k | 3:318fabd6708c | 171 | if (!writePage(startAddr + ofs, data8 + ofs, pageLen)) |
Tomo2k | 3:318fabd6708c | 172 | return false; // Oh dear |
stonie | 0:d07f90d3c670 | 173 | // and switch to next page |
Tomo2k | 3:318fabd6708c | 174 | ofs += pageLen; |
stonie | 0:d07f90d3c670 | 175 | } |
stonie | 0:d07f90d3c670 | 176 | return true; |
stonie | 0:d07f90d3c670 | 177 | } |
stonie | 0:d07f90d3c670 | 178 | |
Tomo2k | 8:7b09546cb412 | 179 | bool FlashM25PSpi::writePage(uint32_t startAddr, const uint8_t* data, size_t len) |
Tomo2k | 3:318fabd6708c | 180 | { |
stonie | 0:d07f90d3c670 | 181 | enableWrite(); |
stonie | 0:d07f90d3c670 | 182 | |
Tomo2k | 3:318fabd6708c | 183 | _enable = 0; |
stonie | 0:d07f90d3c670 | 184 | wait_us(1); |
stonie | 0:d07f90d3c670 | 185 | |
Tomo2k | 3:318fabd6708c | 186 | _spi->write(FLASH_PP); |
Tomo2k | 3:318fabd6708c | 187 | _spi->write(HIGH(startAddr)); |
Tomo2k | 3:318fabd6708c | 188 | _spi->write(MID(startAddr)); |
Tomo2k | 3:318fabd6708c | 189 | _spi->write(LOW(startAddr)); |
stonie | 0:d07f90d3c670 | 190 | |
Tomo2k | 8:7b09546cb412 | 191 | // Bulk write using DMA |
Tomo2k | 8:7b09546cb412 | 192 | _spi->bulkWrite(data, len); |
Tomo2k | 8:7b09546cb412 | 193 | |
stonie | 0:d07f90d3c670 | 194 | wait_us(1); |
stonie | 0:d07f90d3c670 | 195 | // disable to start physical write |
Tomo2k | 3:318fabd6708c | 196 | _enable = 1; |
Tomo2k | 3:318fabd6708c | 197 | |
stonie | 0:d07f90d3c670 | 198 | waitForWrite(); |
stonie | 0:d07f90d3c670 | 199 | |
stonie | 0:d07f90d3c670 | 200 | return true; |
stonie | 0:d07f90d3c670 | 201 | } |
stonie | 0:d07f90d3c670 | 202 | |
Tomo2k | 6:94558d4243f8 | 203 | void FlashM25PSpi::eraseSector(uint32_t addr) |
Tomo2k | 3:318fabd6708c | 204 | { |
stonie | 0:d07f90d3c670 | 205 | addr &= ~(_sectorSize-1); |
stonie | 0:d07f90d3c670 | 206 | |
stonie | 0:d07f90d3c670 | 207 | enableWrite(); |
Tomo2k | 3:318fabd6708c | 208 | _enable = 0; |
stonie | 0:d07f90d3c670 | 209 | wait_us(1); |
Tomo2k | 3:318fabd6708c | 210 | _spi->write(FLASH_SE); |
stonie | 0:d07f90d3c670 | 211 | _spi->write(HIGH(addr)); |
stonie | 0:d07f90d3c670 | 212 | _spi->write(MID(addr)); |
stonie | 0:d07f90d3c670 | 213 | _spi->write(LOW(addr)); |
stonie | 0:d07f90d3c670 | 214 | wait_us(1); |
Tomo2k | 3:318fabd6708c | 215 | _enable = 1; |
stonie | 0:d07f90d3c670 | 216 | waitForWrite(); |
stonie | 0:d07f90d3c670 | 217 | } |
stonie | 0:d07f90d3c670 | 218 | |
Tomo2k | 3:318fabd6708c | 219 | void FlashM25PSpi::eraseMem() |
Tomo2k | 3:318fabd6708c | 220 | { |
stonie | 0:d07f90d3c670 | 221 | enableWrite(); |
Tomo2k | 3:318fabd6708c | 222 | _enable = 0; |
stonie | 0:d07f90d3c670 | 223 | wait_us(1); |
Tomo2k | 3:318fabd6708c | 224 | _spi->write(FLASH_BE); |
stonie | 0:d07f90d3c670 | 225 | wait_us(1); |
Tomo2k | 3:318fabd6708c | 226 | _enable = 1; |
stonie | 0:d07f90d3c670 | 227 | waitForWrite(); |
stonie | 0:d07f90d3c670 | 228 | } |
stonie | 0:d07f90d3c670 | 229 | |
Tomo2k | 3:318fabd6708c | 230 | int FlashM25PSpi::readStatus() |
Tomo2k | 3:318fabd6708c | 231 | { |
Tomo2k | 3:318fabd6708c | 232 | _enable = 0; |
stonie | 0:d07f90d3c670 | 233 | wait_us(1); |
Tomo2k | 3:318fabd6708c | 234 | _spi->write(FLASH_RDSR); |
stonie | 0:d07f90d3c670 | 235 | int status=_spi->write(0x00); |
stonie | 0:d07f90d3c670 | 236 | wait_us(1); |
Tomo2k | 3:318fabd6708c | 237 | _enable = 1; |
stonie | 0:d07f90d3c670 | 238 | return status; |
stonie | 0:d07f90d3c670 | 239 | } |
stonie | 0:d07f90d3c670 | 240 | |
Tomo2k | 3:318fabd6708c | 241 | void FlashM25PSpi::waitForWrite() |
Tomo2k | 3:318fabd6708c | 242 | { |
stonie | 0:d07f90d3c670 | 243 | while (true) { |
Tomo2k | 8:7b09546cb412 | 244 | // Allow something else to do some work |
Tomo2k | 8:7b09546cb412 | 245 | Thread::yield(); |
Tomo2k | 8:7b09546cb412 | 246 | if (0 == (readStatus()&1)) return; |
stonie | 0:d07f90d3c670 | 247 | } |
stonie | 0:d07f90d3c670 | 248 | } |
stonie | 0:d07f90d3c670 | 249 | |
Tomo2k | 3:318fabd6708c | 250 | void FlashM25PSpi::enableWrite() |
stonie | 0:d07f90d3c670 | 251 | { |
Tomo2k | 3:318fabd6708c | 252 | _enable = 0; |
stonie | 0:d07f90d3c670 | 253 | wait_us(1); |
Tomo2k | 3:318fabd6708c | 254 | _spi->write(FLASH_WREN); |
stonie | 0:d07f90d3c670 | 255 | wait_us(1); |
Tomo2k | 3:318fabd6708c | 256 | _enable = 1; |
stonie | 0:d07f90d3c670 | 257 | } |