Kev Mann / mbed-dev-OS5_10_4
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers SPIFReducedBlockDevice.cpp Source File

SPIFReducedBlockDevice.cpp

00001 
00002 /* mbed Microcontroller Library
00003  * Copyright (c) 2018 ARM Limited
00004  *
00005  * Licensed under the Apache License, Version 2.0 (the "License");
00006  * you may not use this file except in compliance with the License.
00007  * You may obtain a copy of the License at
00008  *
00009  *     http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS,
00013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  */
00017 #include "SPIFReducedBlockDevice.h"
00018 #include "mbed_wait_api.h"
00019 
00020 // Read/write/erase sizes
00021 #define SPIF_READ_SIZE  1
00022 #define SPIF_PROG_SIZE  1
00023 #define SPIF_SE_SIZE    4096
00024 #define SPIF_TIMEOUT    10000
00025 
00026 // Debug available
00027 #define SPIF_DEBUG      0
00028 
00029 // Legacy SFDP Instruction Table.
00030 enum ops {
00031     SPIF_NOP  = 0x00, // No operation
00032     SPIF_READ = 0x03, // Read data
00033     SPIF_PROG = 0x02, // Program data
00034     SPIF_SE   = 0x20, // 4KB Sector Erase
00035     SPIF_CE   = 0xc7, // Chip Erase
00036     SPIF_SFDP = 0x5a, // Read SFDP
00037     SPIF_WREN = 0x06, // Write Enable
00038     SPIF_WRDI = 0x04, // Write Disable
00039     SPIF_RDSR = 0x05, // Read Status Register
00040     SPIF_RDID = 0x9f, // Read Manufacturer and JDEC Device ID
00041 };
00042 
00043 // Status register from RDSR
00044 // [---------| wel | wip ]
00045 // [-   6   -|  1  |  1  ]
00046 #define SPIF_WEL 0x2
00047 #define SPIF_WIP 0x1
00048 
00049 
00050 SPIFReducedBlockDevice::SPIFReducedBlockDevice(
00051     PinName mosi, PinName miso, PinName sclk, PinName cs, int freq)
00052     : _spi(mosi, miso, sclk), _cs(cs), _size(0)
00053 {
00054     _cs = 1;
00055     _spi.frequency(freq);
00056 }
00057 
00058 int SPIFReducedBlockDevice::init()
00059 {
00060     // Check for vendor specific hacks, these should move into more general
00061     // handling when possible. RDID is not used to verify a device is attached.
00062     uint8_t id[3];
00063     _cmdread(SPIF_RDID, 0, 3, 0x0, id);
00064 
00065     switch (id[0]) {
00066         case 0xbf:
00067             // SST devices come preset with block protection
00068             // enabled for some regions, issue gbpu instruction to clear
00069             _wren();
00070             _cmdwrite(0x98, 0, 0, 0x0, NULL);
00071             break;
00072     }
00073 
00074     // Check that device is doing ok
00075     int err = _sync();
00076     if (err) {
00077         return BD_ERROR_DEVICE_ERROR;
00078     }
00079 
00080     // Check JEDEC serial flash discoverable parameters for device
00081     // specific info
00082     uint8_t header[16];
00083     _cmdread(SPIF_SFDP, 4, 16, 0x0, header);
00084 
00085     // Verify SFDP signature for sanity
00086     // Also check that major/minor version is acceptable
00087     if (!(memcmp(&header[0], "SFDP", 4) == 0 && header[5] == 1)) {
00088         return BD_ERROR_DEVICE_ERROR;
00089     }
00090 
00091     // The SFDP spec indicates the standard table is always at offset 0
00092     // in the parameter headers, we check just to be safe
00093     if (!(header[8] == 0 && header[10] == 1)) {
00094         return BD_ERROR_DEVICE_ERROR;
00095     }
00096 
00097     // Parameter table pointer, spi commands are BE, SFDP is LE,
00098     // also sfdp command expects extra read wait byte
00099     // header 12-14 3 bytes building the parameter table address
00100     uint32_t table_addr = (
00101                               (header[14] << 24) |
00102                               (header[13] << 16) |
00103                               (header[12] << 8 ));
00104 
00105     uint8_t table[8];
00106     _cmdread(SPIF_SFDP, 4, 8, table_addr, table);
00107 
00108     // Check erase size, currently only supports 4kbytes
00109     if ((table[0] & 0x3) != 0x1 || table[1] != SPIF_SE) {
00110         // First byte of table, bits 0 and 1 = 0x1 indicating 4 KB Erase is supported
00111         // Second Byte of table = Sector Erase Command (0x20)
00112         return BD_ERROR_DEVICE_ERROR;
00113     }
00114 
00115     // Check address size, currently only supports 3byte addresses
00116     if ((table[2] & 0x4) != 0 || (table[7] & 0x80) != 0) {
00117         return BD_ERROR_DEVICE_ERROR;
00118     }
00119 
00120     // Get device density, stored as size in bits - 1
00121     uint32_t density = (
00122                            (table[7] << 24) |
00123                            (table[6] << 16) |
00124                            (table[5] << 8 ) |
00125                            (table[4] << 0 ));
00126     // Table bytes 5-8 : Bits 0|30 indicate Flash Density (size) in bits (divide by 8 for Bytes)
00127     _size = (density / 8) + 1;
00128 
00129     return 0;
00130 }
00131 
00132 int SPIFReducedBlockDevice::deinit()
00133 {
00134     // Latch write disable just to keep noise
00135     // from changing the device
00136     _cmdwrite(SPIF_WRDI, 0, 0, 0x0, NULL);
00137 
00138     return 0;
00139 }
00140 
00141 void SPIFReducedBlockDevice::_cmdread(
00142     uint8_t op, uint32_t addrc, uint32_t retc,
00143     uint32_t addr, uint8_t *rets)
00144 {
00145     _cs = 0;
00146     _spi.write(op);
00147 
00148     for (uint32_t i = 0; i < addrc; i++) {
00149         _spi.write(0xff & (addr >> 8 * (addrc - 1 - i)));
00150     }
00151 
00152     for (uint32_t i = 0; i < retc; i++) {
00153         rets[i] = _spi.write(0);
00154     }
00155     _cs = 1;
00156 
00157     if (SPIF_DEBUG) {
00158         printf("spif <- %02x", op);
00159         for (uint32_t i = 0; i < addrc; i++) {
00160             if (i < addrc) {
00161                 printf("%02lx", 0xff & (addr >> 8 * (addrc - 1 - i)));
00162             } else {
00163                 printf("  ");
00164             }
00165         }
00166         printf(" ");
00167         for (uint32_t i = 0; i < 16 && i < retc; i++) {
00168             printf("%02x", rets[i]);
00169         }
00170         if (retc > 16) {
00171             printf("...");
00172         }
00173         printf("\n");
00174     }
00175 }
00176 
00177 void SPIFReducedBlockDevice::_cmdwrite(
00178     uint8_t op, uint32_t addrc, uint32_t argc,
00179     uint32_t addr, const uint8_t *args)
00180 {
00181     _cs = 0;
00182     _spi.write(op);
00183 
00184     for (uint32_t i = 0; i < addrc; i++) {
00185         _spi.write(0xff & (addr >> 8 * (addrc - 1 - i)));
00186     }
00187 
00188     for (uint32_t i = 0; i < argc; i++) {
00189         _spi.write(args[i]);
00190     }
00191     _cs = 1;
00192 
00193     if (SPIF_DEBUG) {
00194         printf("spif -> %02x", op);
00195         for (uint32_t i = 0; i < addrc; i++) {
00196             if (i < addrc) {
00197                 printf("%02lx", 0xff & (addr >> 8 * (addrc - 1 - i)));
00198             } else {
00199                 printf("  ");
00200             }
00201         }
00202         printf(" ");
00203         for (uint32_t i = 0; i < 16 && i < argc; i++) {
00204             printf("%02x", args[i]);
00205         }
00206         if (argc > 16) {
00207             printf("...");
00208         }
00209         printf("\n");
00210     }
00211 }
00212 
00213 int SPIFReducedBlockDevice::_sync()
00214 {
00215     for (int i = 0; i < SPIF_TIMEOUT; i++) {
00216         // Read status register until write not-in-progress
00217         uint8_t status;
00218         _cmdread(SPIF_RDSR, 0, 1, 0x0, &status);
00219 
00220         // Check WIP bit
00221         if (!(status & SPIF_WIP)) {
00222             return 0;
00223         }
00224 
00225         wait_ms(1);
00226     }
00227 
00228     return BD_ERROR_DEVICE_ERROR;
00229 }
00230 
00231 int SPIFReducedBlockDevice::_wren()
00232 {
00233     _cmdwrite(SPIF_WREN, 0, 0, 0x0, NULL);
00234 
00235     for (int i = 0; i < SPIF_TIMEOUT; i++) {
00236         // Read status register until write latch is enabled
00237         uint8_t status;
00238         _cmdread(SPIF_RDSR, 0, 1, 0x0, &status);
00239 
00240         // Check WEL bit
00241         if (status & SPIF_WEL) {
00242             return 0;
00243         }
00244 
00245         wait_ms(1);
00246     }
00247 
00248     return BD_ERROR_DEVICE_ERROR;
00249 }
00250 
00251 int SPIFReducedBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size)
00252 {
00253     // Check the address and size fit onto the chip.
00254     MBED_ASSERT(is_valid_read(addr, size));
00255 
00256     _cmdread(SPIF_READ, 3, size, addr, static_cast<uint8_t *>(buffer));
00257     return 0;
00258 }
00259 
00260 int SPIFReducedBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size)
00261 {
00262     // Check the address and size fit onto the chip.
00263     MBED_ASSERT(is_valid_program(addr, size));
00264 
00265     while (size > 0) {
00266         int err = _wren();
00267         if (err) {
00268             return err;
00269         }
00270 
00271         // Write up to 256 bytes a page
00272         uint32_t off = addr % 256;
00273         uint32_t chunk = (off + size < 256) ? size : (256 - off);
00274         _cmdwrite(SPIF_PROG, 3, chunk, addr, static_cast<const uint8_t *>(buffer));
00275         buffer = static_cast<const uint8_t *>(buffer) + chunk;
00276         addr += chunk;
00277         size -= chunk;
00278 
00279         wait_ms(1);
00280 
00281         err = _sync();
00282         if (err) {
00283             return err;
00284         }
00285     }
00286 
00287     return 0;
00288 }
00289 
00290 int SPIFReducedBlockDevice::erase(bd_addr_t addr, bd_size_t size)
00291 {
00292     // Check the address and size fit onto the chip.
00293     MBED_ASSERT(is_valid_erase(addr, size));
00294 
00295     while (size > 0) {
00296         int err = _wren();
00297         if (err) {
00298             return err;
00299         }
00300 
00301         // Erase 4kbyte sectors
00302         uint32_t chunk = 4096;
00303         _cmdwrite(SPIF_SE, 3, 0, addr, NULL);
00304         addr += chunk;
00305         size -= chunk;
00306 
00307         err = _sync();
00308         if (err) {
00309             return err;
00310         }
00311     }
00312 
00313     return 0;
00314 }
00315 
00316 bd_size_t SPIFReducedBlockDevice::get_read_size() const
00317 {
00318     return SPIF_READ_SIZE;
00319 }
00320 
00321 bd_size_t SPIFReducedBlockDevice::get_program_size() const
00322 {
00323     return SPIF_PROG_SIZE;
00324 }
00325 
00326 bd_size_t SPIFReducedBlockDevice::get_erase_size() const
00327 {
00328     return SPIF_SE_SIZE;
00329 }
00330 
00331 bd_size_t SPIFReducedBlockDevice::get_erase_size(bd_addr_t addr) const
00332 {
00333     return SPIF_SE_SIZE;
00334 }
00335 
00336 int SPIFReducedBlockDevice::get_erase_value() const
00337 {
00338     return 0xFF;
00339 }
00340 
00341 bd_size_t SPIFReducedBlockDevice::size() const
00342 {
00343     return _size;
00344 }