diff options
author | Kevin O'Connor <kevin@koconnor.net> | 2018-08-16 17:03:23 -0400 |
---|---|---|
committer | Kevin O'Connor <kevin@koconnor.net> | 2018-08-27 08:54:43 -0400 |
commit | 2b9124f3c09731dbacaf0682f899101b6813a28c (patch) | |
tree | 98b6e3f8c90c6930e23e011f0146fec228641e06 /lib/bossac/src/D2xNvmFlash.cpp | |
parent | 79632878ac86c0bf3b9dadb719b54d90742591c5 (diff) | |
download | kutter-2b9124f3c09731dbacaf0682f899101b6813a28c.tar.gz kutter-2b9124f3c09731dbacaf0682f899101b6813a28c.tar.xz kutter-2b9124f3c09731dbacaf0682f899101b6813a28c.zip |
lib: Add bossac 1.9 code to lib directory
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
Diffstat (limited to 'lib/bossac/src/D2xNvmFlash.cpp')
-rw-r--r-- | lib/bossac/src/D2xNvmFlash.cpp | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/lib/bossac/src/D2xNvmFlash.cpp b/lib/bossac/src/D2xNvmFlash.cpp new file mode 100644 index 00000000..0974936c --- /dev/null +++ b/lib/bossac/src/D2xNvmFlash.cpp @@ -0,0 +1,347 @@ +/////////////////////////////////////////////////////////////////////////////// +// BOSSA +// +// Copyright (c) 2018, ShumaTech +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +/////////////////////////////////////////////////////////////////////////////// + +#include "D2xNvmFlash.h" + +// CMDEX field should be 0xA5 to allow execution of any command. +#define CMDEX_KEY 0xa500 + +// NVM ready bit mask +#define NVM_INT_STATUS_READY_MASK 0x1 + +// NVM status mask +#define NVM_CTRL_STATUS_MASK 0xFFEB + +#define NVM_REG_BASE 0x41004000 + +#define NVM_REG_CTRLA 0x00 +#define NVM_REG_CTRLB 0x04 +#define NVM_REG_INTFLAG 0x14 +#define NVM_REG_STATUS 0x18 +#define NVM_REG_ADDR 0x1c +#define NVM_REG_LOCK 0x20 + +#define NVM_CMD_ER 0x02 +#define NVM_CMD_WP 0x04 +#define NVM_CMD_EAR 0x05 +#define NVM_CMD_WAP 0x06 +#define NVM_CMD_LR 0x40 +#define NVM_CMD_UR 0x41 +#define NVM_CMD_SSB 0x45 +#define NVM_CMD_PBC 0x44 + +#define ERASE_ROW_PAGES 4 // pages + +// NVM User Row +#define NVM_UR_ADDR 0x804000 +#define NVM_UR_SIZE (_size * ERASE_ROW_PAGES) +#define NVM_UR_BOD33_ENABLE_OFFSET 0x1 +#define NVM_UR_BOD33_ENABLE_MASK 0x6 +#define NVM_UR_BOD33_RESET_OFFSET 0x1 +#define NVM_UR_BOD33_RESET_MASK 0x7 +#define NVM_UR_NVM_LOCK_OFFSET 0x6 + +D2xNvmFlash::D2xNvmFlash( + Samba& samba, + const std::string& name, + uint32_t pages, + uint32_t size, + uint32_t user, + uint32_t stack) + : + Flash(samba, name, 0, pages, size, 1, 16, user, stack), _eraseAuto(true) +{ +} + +D2xNvmFlash::~D2xNvmFlash() +{ +} + +void +D2xNvmFlash::erase(uint32_t offset, uint32_t size) +{ + uint32_t eraseSize = _size * ERASE_ROW_PAGES; + + // Offset must be a multiple of the erase size + if (offset % eraseSize) + throw FlashEraseError(); + + // Offset and size must be in range + if (offset + size > totalSize()) + throw FlashEraseError(); + + uint32_t eraseEnd = (offset + size + eraseSize - 1) / eraseSize; + + // Erase each erase size set of pages + for (uint32_t eraseNum = offset / eraseSize; eraseNum < eraseEnd; eraseNum++) + { + waitReady(); + + // Clear error bits + uint16_t statusReg = readReg(NVM_REG_STATUS); + writeReg(NVM_REG_STATUS, statusReg | NVM_CTRL_STATUS_MASK); + + // Issue erase command + uint32_t wordAddr = (eraseNum * eraseSize) / 2; + writeReg(NVM_REG_ADDR, wordAddr); + command(NVM_CMD_ER); + } +} + +void +D2xNvmFlash::eraseAll(uint32_t offset) +{ + // Use the extended Samba command if available + if (_samba.canChipErase()) + { + _samba.chipErase(offset); + } + else + { + erase(offset, totalSize() - offset); + } +} + +void +D2xNvmFlash::eraseAuto(bool enable) +{ + _eraseAuto = enable; +} + +std::vector<bool> +D2xNvmFlash::getLockRegions() +{ + uint8_t lockBits = 0; + uint32_t addr = NVM_UR_ADDR + NVM_UR_NVM_LOCK_OFFSET; + std::vector<bool> regions(_lockRegions); + + for (uint32_t region = 0; region < _lockRegions; region++) + { + if (region % 8 == 0) + lockBits = _samba.readByte(addr++); + regions[region] = (lockBits & (1 << (region % 8))) == 0; + } + + return regions; +} + +bool +D2xNvmFlash::getSecurity() +{ + return (readReg(NVM_REG_STATUS) & 0x100) != 0; +} + +bool +D2xNvmFlash::getBod() +{ + uint8_t byte = _samba.readByte(NVM_UR_ADDR + NVM_UR_BOD33_ENABLE_OFFSET); + + return (byte & NVM_UR_BOD33_ENABLE_MASK) != 0; +} + +bool +D2xNvmFlash::getBor() +{ + uint8_t byte = _samba.readByte(NVM_UR_ADDR + NVM_UR_BOD33_RESET_OFFSET); + + return (byte & NVM_UR_BOD33_RESET_MASK) != 0; +} + +bool +D2xNvmFlash::getBootFlash() +{ + return true; +} + +void +D2xNvmFlash::readUserRow(std::unique_ptr<uint8_t[]>& userRow) +{ + if (!userRow) + { + userRow.reset(new uint8_t[NVM_UR_SIZE]); + _samba.read(NVM_UR_ADDR, userRow.get(), NVM_UR_SIZE); + } +} + +void +D2xNvmFlash::writeOptions() +{ + std::unique_ptr<uint8_t[]> userRow; + + if (canBor() && _bor.isDirty() && _bor.get() != getBor()) + { + readUserRow(userRow); + if (_bor.get()) + userRow[NVM_UR_BOD33_RESET_OFFSET] |= NVM_UR_BOD33_RESET_MASK; + else + userRow[NVM_UR_BOD33_RESET_OFFSET] &= ~NVM_UR_BOD33_RESET_MASK; + } + if (canBod() && _bod.isDirty() && _bod.get() != getBod()) + { + readUserRow(userRow); + if (_bod.get()) + userRow[NVM_UR_BOD33_ENABLE_OFFSET] |= NVM_UR_BOD33_ENABLE_MASK; + else + userRow[NVM_UR_BOD33_ENABLE_OFFSET] &= ~NVM_UR_BOD33_ENABLE_MASK; + } + if (_regions.isDirty()) + { + // Check if any lock bits are different from the current set + std::vector<bool> current = getLockRegions(); + if (!equal(_regions.get().begin(), _regions.get().end(), current.begin())) + { + readUserRow(userRow); + + uint8_t* lockBits = &userRow[NVM_UR_NVM_LOCK_OFFSET]; + for (uint32_t region = 0; region < _regions.get().size(); region++) + { + if (_regions.get()[region]) + lockBits[region / 8] &= ~(1 << (region % 8)); + else + lockBits[region / 8] |= (1 << (region % 8)); + } + } + } + + // Erase and write the user row if modified + if (userRow) + { + // Disable cache and configure manual page write + writeReg(NVM_REG_CTRLB, readReg(NVM_REG_CTRLB) | (0x1 << 18) | (0x1 << 7)); + + // Erase user row + writeReg(NVM_REG_ADDR, NVM_UR_ADDR / 2); + command(NVM_CMD_EAR); + + // Write user row in page chunks + for (uint32_t offset = 0; offset < NVM_UR_SIZE; offset += _size) + { + // Load the buffer with the page + loadBuffer(&userRow[offset], _size); + + // Clear page buffer + command(NVM_CMD_PBC); + + // Copy page to page buffer + _wordCopy.setDstAddr(NVM_UR_ADDR + offset); + _wordCopy.setSrcAddr(_onBufferA ? _pageBufferA : _pageBufferB); + _onBufferA = !_onBufferA; + waitReady(); + _wordCopy.runv(); + + // Write the page + writeReg(NVM_REG_ADDR, (NVM_UR_ADDR + offset) / 2); + command(NVM_CMD_WAP); + } + } + + // Always do security last + if (_security.isDirty() && _security.get() == true && _security.get() != getSecurity()) + { + command(NVM_CMD_SSB); + } +} + +void +D2xNvmFlash::writePage(uint32_t page) +{ + if (page >= _pages) + { + throw FlashPageError(); + } + + // Disable cache and configure manual page write + writeReg(NVM_REG_CTRLB, readReg(NVM_REG_CTRLB) | (0x1 << 18) | (0x1 << 7)); + + // Auto-erase if writing at the start of the erase page + if (_eraseAuto && page % ERASE_ROW_PAGES == 0) + erase(page * _size, ERASE_ROW_PAGES * _size); + + // Clear page buffer + command(NVM_CMD_PBC); + + // Compute the start address. + uint32_t addr = _addr + (page * _size); + + _wordCopy.setDstAddr(addr); + _wordCopy.setSrcAddr(_onBufferA ? _pageBufferA : _pageBufferB); + _onBufferA = !_onBufferA; + waitReady(); + _wordCopy.runv(); + + writeReg(NVM_REG_ADDR, addr / 2); + command(NVM_CMD_WP); +} + +void +D2xNvmFlash::waitReady() +{ + while ((readReg(NVM_REG_INTFLAG) & 0x1) == 0); +} + +void +D2xNvmFlash::readPage(uint32_t page, uint8_t* buf) +{ + if (page >= _pages) + { + throw FlashPageError(); + } + + _samba.read(_addr + (page * _size), buf, _size); +} + +uint32_t +D2xNvmFlash::readReg(uint8_t reg) +{ + return _samba.readWord(NVM_REG_BASE + reg); +} + +void +D2xNvmFlash::writeReg(uint8_t reg, uint32_t value) +{ + _samba.writeWord(NVM_REG_BASE + reg, value); +} + +void +D2xNvmFlash::command(uint8_t cmd) +{ + waitReady(); + + writeReg(NVM_REG_CTRLA, CMDEX_KEY | cmd); + + waitReady(); + + if (readReg(NVM_REG_INTFLAG) & 0x2) + { + // Clear the error bit + writeReg(NVM_REG_INTFLAG, 0x2); + throw FlashCmdError(); + } +} + +void +D2xNvmFlash::writeBuffer(uint32_t dst_addr, uint32_t size) +{ + // Auto-erase if enabled + if (_eraseAuto) + erase(dst_addr, size); + + // Call the base class method + Flash::writeBuffer(dst_addr, size); +} |