#include "EefcFlash.h"
#include <assert.h>
#include <unistd.h>
#include <stdio.h>
#define EEFC_KEY 0x5a
#define EEFC0_FMR (_regs + 0x00)
#define EEFC0_FCR (_regs + 0x04)
#define EEFC0_FSR (_regs + 0x08)
#define EEFC0_FRR (_regs + 0x0C)
#define EEFC1_FMR (_regs + 0x200)
#define EEFC1_FCR (_regs + 0x204)
#define EEFC1_FSR (_regs + 0x208)
#define EEFC1_FRR (_regs + 0x20C)
#define EEFC_FCMD_GETD 0x0
#define EEFC_FCMD_WP 0x1
#define EEFC_FCMD_WPL 0x2
#define EEFC_FCMD_EWP 0x3
#define EEFC_FCMD_EWPL 0x4
#define EEFC_FCMD_EA 0x5
#define EEFC_FCMD_EPA 0x7
#define EEFC_FCMD_SLB 0x8
#define EEFC_FCMD_CLB 0x9
#define EEFC_FCMD_GLB 0xa
#define EEFC_FCMD_SGPB 0xb
#define EEFC_FCMD_CGPB 0xc
#define EEFC_FCMD_GGPB 0xd
#define EEFC_FCMD_STUI 0xe
#define EEFC_FCMD_SPUI 0xf
#define EEFC_FCMD_GCALB 0x10
#define EEFC_FCMD_ES 0x11
#define EEFC_FCMD_WUS 0x12
#define EEFC_FCMD_EUS 0x13
#define EEFC_FCMD_STUS 0x14
#define EEFC_FCMD_SPUS 0x15
const uint32_t
EefcFlash::PagesPerErase = 8;
EefcFlash::EefcFlash(Samba& samba,
const std::string& name,
uint32_t addr,
uint32_t pages,
uint32_t size,
uint32_t planes,
uint32_t lockRegions,
uint32_t uniqueIdWords,
uint32_t user,
uint32_t stack,
uint32_t regs,
bool canBrownout)
: Flash(samba, name, addr, pages, size, planes, lockRegions, user, stack),
_regs(regs), _uniqueIdWords(uniqueIdWords), _canBrownout(canBrownout), _eraseAuto(true)
{
assert(planes == 1 || planes == 2);
assert(pages <= 4096);
assert(lockRegions <= 256);
_samba.writeWord(EEFC0_FMR, 0x6 << 8);
if (planes == 2)
_samba.writeWord(EEFC1_FMR, 0x6 << 8);
}
EefcFlash::~EefcFlash()
{
}
void
EefcFlash::eraseAll(uint32_t offset)
{
if (offset == 0)
{
waitFSR();
writeFCR0(EEFC_FCMD_EA, 0);
if (_planes == 2)
{
waitFSR();
writeFCR1(EEFC_FCMD_EA, 0);
}
waitFSR(30);
}
else
{
if (offset % (_size * PagesPerErase))
throw FlashEraseError();
for (uint32_t pageNum = offset / _size; pageNum < _pages; pageNum += PagesPerErase)
{
if (_planes == 1 || pageNum < _pages / 2)
{
waitFSR();
writeFCR0(EEFC_FCMD_EPA, pageNum | 0x1);
}
else
{
waitFSR();
writeFCR1(EEFC_FCMD_EPA, (pageNum % (_pages / 2)) | 0x1);
}
}
}
}
void
EefcFlash::eraseAuto(bool enable)
{
_eraseAuto = enable;
}
std::vector<bool>
EefcFlash::getLockRegions()
{
std::vector<bool> regions(_lockRegions);
uint32_t frr;
uint32_t bit;
waitFSR();
for (uint32_t region = 0; region < _lockRegions; region++)
{
if (_planes == 2 && region >= _lockRegions / 2)
{
bit = region - _lockRegions / 2;
writeFCR1(EEFC_FCMD_GLB, 0);
waitFSR();
frr = readFRR1();
while (bit >= 32)
{
frr = readFRR1();
bit -= 32;
}
regions[region] = (frr & (1 << bit)) != 0;
}
else
{
bit = region;
writeFCR0(EEFC_FCMD_GLB, 0);
waitFSR();
frr = readFRR0();
while (bit >= 32)
{
frr = readFRR0();
bit -= 32;
}
regions[region] = (frr & (1 << bit)) != 0;
}
}
return regions;
}
bool
EefcFlash::getSecurity()
{
waitFSR();
writeFCR0(EEFC_FCMD_GGPB, 0);
waitFSR();
return (readFRR0() & (1 << 0));
}
bool
EefcFlash::getBod()
{
if (!_canBrownout)
return false;
waitFSR();
writeFCR0(EEFC_FCMD_GGPB, 0);
waitFSR();
return (readFRR0() & (1 << 1));
}
bool
EefcFlash::getBor()
{
if (!_canBrownout)
return false;
waitFSR();
writeFCR0(EEFC_FCMD_GGPB, 0);
waitFSR();
return (readFRR0() & (1 << 2));
}
bool
EefcFlash::getBootFlash()
{
waitFSR();
writeFCR0(EEFC_FCMD_GGPB, 0);
waitFSR();
return (readFRR0() & (1 << (_canBrownout ? 3 : 1)));
}
std::vector<uint32_t>
EefcFlash::getUniqueId()
{
std::vector<uint32_t> value;
if (_uniqueIdWords == 0)
return value;
waitFSR();
writeFCR0(EEFC_FCMD_STUI, 0);
uint32_t addr = address();
for (uint8_t word = 0; word < _uniqueIdWords; word++)
{
value.push_back(_samba.readWord(addr + word * 4));
}
writeFCR0(EEFC_FCMD_SPUI, 0);
waitFSR();
return value;
}
void
EefcFlash::writeOptions()
{
if (canBootFlash() && _bootFlash.isDirty() && _bootFlash.get() != getBootFlash())
{
waitFSR();
writeFCR0(_bootFlash.get() ? EEFC_FCMD_SGPB : EEFC_FCMD_CGPB, (canBod() ? 3 : 1));
}
if (canBor() && _bor.isDirty() && _bor.get() != getBor())
{
waitFSR();
writeFCR0(_bor.get() ? EEFC_FCMD_SGPB : EEFC_FCMD_CGPB, 2);
}
if (canBod() && _bod.isDirty() && _bod.get() != getBod())
{
waitFSR();
writeFCR0(_bod.get() ? EEFC_FCMD_SGPB : EEFC_FCMD_CGPB, 1);
}
if (_regions.isDirty())
{
uint32_t page;
std::vector<bool> current;
if (_regions.get().size() > _lockRegions)
throw FlashRegionError();
current = getLockRegions();
for (uint32_t region = 0; region < _lockRegions; region++)
{
if (_regions.get()[region] != current[region])
{
if (_planes == 2 && region >= _lockRegions / 2)
{
page = (region - _lockRegions / 2) * _pages / _lockRegions;
waitFSR();
writeFCR1(_regions.get()[region] ? EEFC_FCMD_SLB : EEFC_FCMD_CLB, page);
}
else
{
page = region * _pages / _lockRegions;
waitFSR();
writeFCR0(_regions.get()[region] ? EEFC_FCMD_SLB : EEFC_FCMD_CLB, page);
}
}
}
}
if (_security.isDirty() && _security.get() == true && _security.get() != getSecurity())
{
waitFSR();
writeFCR0(EEFC_FCMD_SGPB, 0);
}
}
void
EefcFlash::ready()
{
waitFSR();
}
void
EefcFlash::writePage(uint32_t page)
{
if (page >= _pages)
throw FlashPageError();
_wordCopy.setDstAddr(_addr + page * _size);
_wordCopy.setSrcAddr(_onBufferA ? _pageBufferA : _pageBufferB);
_onBufferA = !_onBufferA;
try
{
waitFSR();
}
catch (FlashCmdError& exc)
{
if (page > 0)
{
printf("\nNOTE: Some chip families may not support auto-erase on all flash regions.\n");
printf(" Try erasing the flash first (bossash), or erasing at the same time (bossac).");
fflush(stdout);
}
throw;
}
_wordCopy.runv();
if (_planes == 2 && page >= _pages / 2)
writeFCR1(_eraseAuto ? EEFC_FCMD_EWP : EEFC_FCMD_WP, page - _pages / 2);
else
writeFCR0(_eraseAuto ? EEFC_FCMD_EWP : EEFC_FCMD_WP, page);
}
void
EefcFlash::readPage(uint32_t page, uint8_t* data)
{
if (page >= _pages)
throw FlashPageError();
_wordCopy.setDstAddr(_onBufferA ? _pageBufferA : _pageBufferB);
_wordCopy.setSrcAddr(_addr + page * _size);
waitFSR();
_wordCopy.runv();
_samba.read(_onBufferA ? _pageBufferA : _pageBufferB, data, _size);
}
void
EefcFlash::waitFSR(int seconds)
{
int tries = seconds * 1000;
uint32_t fsr0;
uint32_t fsr1 = 0x1;
while (tries-- > 0)
{
fsr0 = _samba.readWord(EEFC0_FSR);
if (fsr0 & 0x2)
throw FlashCmdError();
if (fsr0 & 0x4)
throw FlashLockError();
if (_planes == 2)
{
fsr1 = _samba.readWord(EEFC1_FSR);
if (fsr1 & 0x2)
throw FlashCmdError();
if (fsr1 & 0x4)
throw FlashLockError();
}
if (fsr0 & fsr1 & 0x1)
break;
usleep(1000);
}
if (tries == 0)
throw FlashTimeoutError();
}
void
EefcFlash::writeFCR0(uint8_t cmd, uint32_t arg)
{
_samba.writeWord(EEFC0_FCR, (EEFC_KEY << 24) | (arg << 8) | cmd);
}
void
EefcFlash::writeFCR1(uint8_t cmd, uint32_t arg)
{
_samba.writeWord(EEFC1_FCR, (EEFC_KEY << 24) | (arg << 8) | cmd);
}
uint32_t
EefcFlash::readFRR0()
{
return _samba.readWord(EEFC0_FRR);
}
uint32_t
EefcFlash::readFRR1()
{
return _samba.readWord(EEFC1_FRR);
}