klipper/lib/bossac/src/EefcFlash.cpp

364 lines
9.7 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// BOSSA
//
// Copyright (c) 2011-2018, ShumaTech
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the <organization> nor the
// names of its contributors may be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
///////////////////////////////////////////////////////////////////////////////
#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
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 user,
uint32_t stack,
uint32_t regs,
bool canBrownout)
: Flash(samba, name, addr, pages, size, planes, lockRegions, user, stack),
_regs(regs), _canBrownout(canBrownout), _eraseAuto(true)
{
assert(planes == 1 || planes == 2);
assert(pages <= 4096);
assert(lockRegions <= 256);
// SAM3 Errata (FWS must be 6)
_samba.writeWord(EEFC0_FMR, 0x6 << 8);
if (planes == 2)
_samba.writeWord(EEFC1_FMR, 0x6 << 8);
}
EefcFlash::~EefcFlash()
{
}
void
EefcFlash::eraseAll(uint32_t offset)
{
// Do a full chip erase if the offset is 0
if (offset == 0)
{
waitFSR();
writeFCR0(EEFC_FCMD_EA, 0);
if (_planes == 2)
{
waitFSR();
writeFCR1(EEFC_FCMD_EA, 0);
}
// Erase all can take an exceptionally long time on some devices
// so wait on FSR for up to 30 seconds
waitFSR(30);
}
// Else we must do it by pages
else
{
// Offset must be on an erase page boundary
if (offset % (_size * PagesPerErase))
throw FlashEraseError();
// Erase each PagesPerErase set of pages
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)));
}
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::writePage(uint32_t page)
{
if (page >= _pages)
throw FlashPageError();
_wordCopy.setDstAddr(_addr + page * _size);
_wordCopy.setSrcAddr(_onBufferA ? _pageBufferA : _pageBufferB);
_onBufferA = !_onBufferA;
waitFSR();
_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();
// The SAM3 firmware has a bug where it returns all zeros for reads
// directly from the flash so instead, we copy the flash page to
// SRAM and read it from there.
_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);
}