Seiji Ainoguchi / SerialFlash

Dependents:   SerialFlashTest

Fork of SerialFlash by Seiji Ainoguchi

M25PDeviceImpl.cpp

Committer:
s_ain
Date:
2011-03-02
Revision:
2:6f8ab876b516
Parent:
1:385965a14c7c

File content as of revision 2:6f8ab876b516:

#include "ISPICommand.h"
#include <string.h>
#include "M25PDeviceImpl.h"

const M25PDeviceImpl::DeviceProperty* M25PDeviceImpl::findMatchDevice(ISPICommand* pSPICommand)
{
    int manufacturerId;
    int memoryType;
    int memoryCapacity;
    readId(pSPICommand, manufacturerId, memoryType, memoryCapacity);

    struct SupportedDevice
    {
        int manufacturerId;
        int memoryType;
        int memoryCapacity;
        DeviceProperty property;
    } static const supportedDevices[] =
    {
        //M25P16 16MBit (2MB)
        { 0x20, 0x20, 0x15,
            {
              "M25P16",
              16384 * 1024 / 8,
              0xe3,
            },
        },
        //M25P80 8MBit (1MB)
        { 0x20, 0x20, 0x14,
            {
              "M25P80",
              8192 * 1024 / 8,
              0xe3,
           },
        },
    };
    int count = sizeof(supportedDevices) / sizeof(supportedDevices[0]);
    for (int i = 0; i < count; ++i)
    {
        const SupportedDevice& device = supportedDevices[i];
        if (device.manufacturerId == manufacturerId && device.memoryType == memoryType && device.memoryCapacity == memoryCapacity)
        {
            return &device.property;
        }
    }
    return NULL;
}

bool M25PDeviceImpl::IsSupported(ISPICommand* pSPICommand)
{
    return findMatchDevice(pSPICommand) != NULL;
}

M25PDeviceImpl* M25PDeviceImpl::Create(ISPICommand* pSPICommand)
{
    const DeviceProperty* property = findMatchDevice(pSPICommand);
    if (property == NULL)
    {
        return NULL;
    }
    return new M25PDeviceImpl(pSPICommand, *property);
}

M25PDeviceImpl::M25PDeviceImpl(ISPICommand* pSPICommand, const DeviceProperty& property)
 : _pSPICommand(pSPICommand)
 , _property(property)
{
    clearBlockProtection();
}

M25PDeviceImpl::~M25PDeviceImpl(void)
{

}

void M25PDeviceImpl::readId(ISPICommand* pSPICommand, int& manufacturerId, int& memoryType, int& memoryCapacity)
{
    const static int DefaultOperationFrequency = 20000000;
    pSPICommand->SetMaxFrequency(DefaultOperationFrequency);

    unsigned char id[3];
    pSPICommand->Read(0x9f, id, sizeof(id));
    manufacturerId = id[0];
    memoryType = id[1];
    memoryCapacity = id[2];
}

int M25PDeviceImpl::readStatusRegister(void)
{
    unsigned char status;
    _pSPICommand->Read(0x05, &status, 1);
    return status;
}

void M25PDeviceImpl::writeStatusRegister(int value)
{
    char vb = static_cast<char>(value);
    _pSPICommand->Write(0x01, &vb, 1);
}

void M25PDeviceImpl::writeEnable()
{
    _pSPICommand->Write(0x06);
}

void M25PDeviceImpl::writeDisable()
{
    _pSPICommand->Write(0x04);
}

void M25PDeviceImpl::clearBlockProtection(void)
{
    writeEnable();

    int status = readStatusRegister();
    status &= _property.blockProtectionMask;
    writeStatusRegister(status);
}

std::string M25PDeviceImpl::GetDeviceName(void) const
{
    return _property.deviceName;
}

int M25PDeviceImpl::GetCapacity(void) const
{
    return _property.capacity;
}

int M25PDeviceImpl::Read(int address, void* buffer, int length)
{
    if (address >= GetCapacity())
    {
        return 0;
    }
    if (address + length > GetCapacity())
    {
        length = GetCapacity() - address;
    }
    if (length == 0)
    {
        return 0;
    }

    char param[] = { 0, 0, 0, 0xff };
    fillAddress(param, address);
    _pSPICommand->Read(0x0b, param, sizeof(param), buffer, length);
    return length;
}

void M25PDeviceImpl::fillAddress(char* pBuffer, int address)
{
    *(pBuffer + 0) = (address & 0xff0000) >> 16;
    *(pBuffer + 1) = (address & 0x00ff00) >> 8;
    *(pBuffer + 2) = (address & 0x0000ff);
}

int M25PDeviceImpl::Write(int address, const void* buffer, int length)
{
    if (address >= GetCapacity())
    {
        return 0;
    }
    if (address + length > GetCapacity())
    {
        length = GetCapacity() - address;
    }
    if (length == 0)
    {
        return 0;
    }

    int result = length;

    const int pageSize = 256;
    const int pageSizeMask = pageSize - 1;
    const int pageAddressMask = ~pageSizeMask;

    const unsigned char* p = static_cast<const unsigned char*>(buffer);
    if ((address & pageSizeMask) != 0)
    {
        int readLen = address & pageSizeMask;
        int copyLen = pageSize - readLen;
        if (copyLen > length)
        {
            copyLen = length;
        }
        char buf[pageSize];
        int writeAddress = address & pageAddressMask;
        Read(writeAddress, buf, readLen);
        memcpy(&buf[address & pageSizeMask], buffer, copyLen);
        pageProgram(writeAddress, buf);
        p += readLen;
        address += pageSize - readLen;
        length -= copyLen;
    }
    while (length >= pageSize)
    {
        pageProgram(address, p);
        address += pageSize;
        p += pageSize;
        length -= pageSize;
    }
    if (length != 0)
    {
        char buf[pageSize];
        memcpy(buf, p, length);
        memset(&buf[length], 0xff, pageSize - length);
        pageProgram(address, buf);
    }
    return result;
}

void M25PDeviceImpl::BulkErase()
{
    writeEnable();

    _pSPICommand->Write(0xc7);

    while (readStatusRegister() & 0x01)
        ;

    clearBlockProtection();
}

void M25PDeviceImpl::pageProgram(int address, const void* buffer)
{
    writeEnable();

    char param[256 + 3];
    fillAddress(param, address);
    memcpy(param + 3, buffer, 256);
    _pSPICommand->Write(0x02, param, sizeof(param));

    while (readStatusRegister() & 0x01)
        ;
}