Flash handler for M25P* chips with no Device ID.

Dependencies:   RTOS_SPI_Clean

Fork of flash25spi by Klaus Steinhammer

flash25spi.cpp

Committer:
Tomo2k
Date:
2014-08-19
Revision:
12:63399e648bfc
Parent:
8:7b09546cb412

File content as of revision 12:63399e648bfc:

/* This library is based on the Ser25lcxxx library by Hendrik Lipka
* It was adapted to flash memory chips on 19.2.2011 by Klaus Steinhammer

* It was further adapted to M25P* SPI Flash memory chips that do not
* support the "RDID" device ID instructions on 25.03.2014 by Richard Thompson
*
* The BSD license also applies to this code - have fun.
*/

/*
* Ser25lcxxx library
* Copyright (c) 2010 Hendrik Lipka
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/


#include "flash25spi.h"
#include "wait_api.h"

//#define DEBUG

struct dataBase {
    uint8_t signature;
    size_t memsize;
    size_t sectorsize;
    size_t pagesize;
};

const struct dataBase devices[] = {
    //signature,   memsize,    sectorsize, pagesize
    {   0x10,       0x20000,    0x8000,     0x100   },  // M25P10-A Numonyx/ST
    {   0x11,       0x40000,    0x10000,    0x100   },  // M25P20   Numonyx/ST/Micron
    {   0x12,       0x80000,    0x10000,    0x100   },  // M25P40   Numonyx/ST
    {   0x00,       0x00,       0x00,       0x00},      // end of table
};

// Instruction Set
const uint8_t FLASH_WREN = 0x06;    // Write Enable
//const uint8_t FLASH_WRDI = 0x04;    // Write Disable (Unused)
//const uint8_t FLASH_RDID = 0x9F;    // Read Device IDentification (not supported)
const uint8_t FLASH_RDSR = 0x05;    // Read Status Register
//const uint8_t FLASH_WRSR = 0x01;    // Write Status Register (not used)
const uint8_t FLASH_READ = 0x03;    // Read Data Bytes
//const uint8_t FLASH_FAST_READ = 0x0B;   // Read Data Bytes Faster
const uint8_t FLASH_PP = 0x02;      // Page Program (max pagesize bytes)
const uint8_t FLASH_SE = 0xD8;      // Sector Erase
const uint8_t FLASH_BE = 0xC7;      // Bulk Erase (wipe complete system)
const uint8_t FLASH_DP = 0xB9;      // Deep Power Down (shutdown into very-low-power)
const uint8_t FLASH_RES = 0xAB;     // Wake Up and Read Electronic Signature

#define HIGH(x) ((x&0xff0000)>>16)
#define MID(x) ((x&0xff00)>>8)
#define LOW(x) (x&0xff)


FlashM25PSpi::FlashM25PSpi(RTOS_SPI *spi, PinName enable, int bits, int mode) :
    _spi(spi)
    , _enable(enable)
    , _size(0)
{
    if (bits) _spi->format(bits, mode);

    _enable = 1;
    if (osKernelRunning()) {
        Thread::wait(1);
    } else {
        wait_us(1000);
    }
    _enable = 0;
    wait_us(1);
    uint8_t chipSig;
    // Reset and Ask for chip signature
    _spi->write(FLASH_RES);
    _spi->write(0); // Dummy writes
    _spi->write(0); // Dummy writes
    _spi->write(0); // Dummy writes
    chipSig = _spi->write(0); // Get Electronic Signature
    wait_us(1);
    _enable = 1;

    unsigned int i = 0;
    while (_size == 0) {
        if (devices[i].memsize == 0) {  // Nobody makes a memory of zero size
#ifdef DEBUG
            printf("\r\nUnknown Flash Memory signature: %xh\r\n", chipSig);
#endif
            return;
        }
        if (chipSig == devices[i].signature) {
            _size=devices[i].memsize;
            _sectorSize=devices[i].sectorsize;
            _pageSize=devices[i].pagesize;
#ifdef DEBUG
            printf("\r\nFlash Memory Sig:%Xh : %u bytes Org: %x, %x\r\n",
                   chipSig, _size, _sectorSize, _pageSize);
#endif
        } else
            i++;
    }
}

FlashM25PSpi::~FlashM25PSpi()
{
    // Wait until chip finishes cycle
//    waitForWrite();
    // Shutdown the flash chip into lowest-power mode
    _enable = 0;
    wait_us(1);
    _spi->write(FLASH_DP);
    wait_us(1);
    _enable = 1;
}

bool FlashM25PSpi::read(uint32_t startAddr, void* destination, size_t len)
{
    // Check size
    if (startAddr + len > _size)
        return false;
    _enable = 0;
    wait_us(1);

    // Send address
    _spi->write(FLASH_READ);
    _spi->write(HIGH(startAddr));
    _spi->write(MID(startAddr));
    _spi->write(LOW(startAddr));

    uint8_t dummy = 0;

    // Bulk read into destination buffer (DMA)
    // Be sure to write the same dummy databyte repeatedly
    _spi->bulkReadWrite((uint8_t*)destination, &dummy, len, false);

    wait_us(1);
    _enable = 1;
    return true;
}

bool FlashM25PSpi::write(uint32_t startAddr, const void* data, size_t len)
{
    if (startAddr + len > _size)
        return false;

    // Cast to uint8_t
    uint8_t *data8 = (uint8_t*)data;

    size_t ofs = 0;
    while (ofs < len) {
        // calculate amount of data to write into current page
        size_t pageLen = _pageSize - ((startAddr + ofs) % _pageSize);
        if (ofs + pageLen > len)
            pageLen = len - ofs;
        // write single page
        if (!writePage(startAddr + ofs, data8 + ofs, pageLen))
            return false;   // Oh dear
        // and switch to next page
        ofs += pageLen;
    }
    return true;
}

bool FlashM25PSpi::writePage(uint32_t startAddr, const uint8_t* data, size_t len)
{
    enableWrite();

    _enable = 0;
    wait_us(1);

    _spi->write(FLASH_PP);
    _spi->write(HIGH(startAddr));
    _spi->write(MID(startAddr));
    _spi->write(LOW(startAddr));

    // Bulk write using DMA
    _spi->bulkWrite(data, len);

    wait_us(1);
    // disable to start physical write
    _enable = 1;

    waitForWrite();

    return true;
}

void FlashM25PSpi::eraseSector(uint32_t addr)
{
    addr &= ~(_sectorSize-1);

    enableWrite();
    _enable = 0;
    wait_us(1);
    _spi->write(FLASH_SE);
    _spi->write(HIGH(addr));
    _spi->write(MID(addr));
    _spi->write(LOW(addr));
    wait_us(1);
    _enable = 1;
    waitForWrite();
}

void FlashM25PSpi::eraseMem()
{
    enableWrite();
    _enable = 0;
    wait_us(1);
    _spi->write(FLASH_BE);
    wait_us(1);
    _enable = 1;
    waitForWrite();
}

int FlashM25PSpi::readStatus()
{
    _enable = 0;
    wait_us(1);
    _spi->write(FLASH_RDSR);
    int status=_spi->write(0x00);
    wait_us(1);
    _enable = 1;
    return status;
}

void FlashM25PSpi::waitForWrite()
{
    while (true) {
        // Allow something else to do some work
        Thread::yield();
        if (0 == (readStatus()&1)) return;
    }
}

void FlashM25PSpi::enableWrite()
{
    _enable = 0;
    wait_us(1);
    _spi->write(FLASH_WREN);
    wait_us(1);
    _enable = 1;
}