aboutsummaryrefslogtreecommitdiffstats
path: root/lib/bossac/src/EefcFlash.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/bossac/src/EefcFlash.cpp')
-rw-r--r--lib/bossac/src/EefcFlash.cpp363
1 files changed, 363 insertions, 0 deletions
diff --git a/lib/bossac/src/EefcFlash.cpp b/lib/bossac/src/EefcFlash.cpp
new file mode 100644
index 00000000..988c3b7e
--- /dev/null
+++ b/lib/bossac/src/EefcFlash.cpp
@@ -0,0 +1,363 @@
+///////////////////////////////////////////////////////////////////////////////
+// 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);
+}