213 lines
4.7 KiB
C++
213 lines
4.7 KiB
C++
#include "Flash.hpp"
|
|
|
|
#include "FreeRTOS.h"
|
|
#include "task.h"
|
|
#include <cstring>
|
|
|
|
#define LOG_LEVEL LOG_LEVEL_INFO
|
|
#define LOG_MODULE "Flash"
|
|
#include "Log.h"
|
|
|
|
bool Flash::isPresent() {
|
|
CS(false);
|
|
// read JEDEC ID
|
|
uint8_t send[4] = {0x9F};
|
|
uint8_t recv[4];
|
|
HAL_SPI_TransmitReceive(spi, send, recv, 4, 100);
|
|
CS(true);
|
|
// Check against valid manufacturer IDs
|
|
constexpr uint8_t valid_ids[] = {0xEF, 0x68, 0x9D};
|
|
bool valid = false;
|
|
for (uint8_t i = 0; i < sizeof(valid_ids); i++) {
|
|
if (recv[1] == valid_ids[i]) {
|
|
valid = true;
|
|
break;
|
|
}
|
|
}
|
|
return valid;
|
|
}
|
|
|
|
void Flash::read(uint32_t address, uint16_t length, void *dest) {
|
|
initiateRead(address);
|
|
// read data
|
|
HAL_SPI_Receive(spi, (uint8_t*) dest, length, 1000);
|
|
CS(true);
|
|
}
|
|
|
|
bool Flash::write(uint32_t address, uint16_t length, void *src) {
|
|
if(address % PageSize != 0 || length%PageSize != 0) {
|
|
// only writes to complete pages allowed
|
|
LOG_ERR("Invalid write address/size: %lu/%u", address, length);
|
|
return false;
|
|
}
|
|
address &= 0x00FFFFFF;
|
|
LOG_DEBUG("Writing %u bytes to address %lu", length, address);
|
|
while(length > 0) {
|
|
EnableWrite();
|
|
CS(false);
|
|
uint8_t cmd[4] = {
|
|
0x02,
|
|
(uint8_t) (address >> 16) & 0xFF,
|
|
(uint8_t) (address >> 8) & 0xFF,
|
|
(uint8_t) (address & 0xFF),
|
|
};
|
|
// issue write command
|
|
HAL_SPI_Transmit(spi, cmd, 4, 100);
|
|
// write data
|
|
HAL_SPI_Transmit(spi, (uint8_t*) src, 256, 1000);
|
|
CS(true);
|
|
if(!WaitBusy(20)) {
|
|
LOG_ERR("Write timed out");
|
|
return false;
|
|
}
|
|
// Verify
|
|
uint8_t buf[256];
|
|
read(address, 256, buf);
|
|
if(memcmp(src, buf, 256)) {
|
|
LOG_ERR("Verification error");
|
|
return false;
|
|
}
|
|
address += 256;
|
|
length -= 256;
|
|
src += 256;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void Flash::EnableWrite() {
|
|
CS(false);
|
|
// enable write latch
|
|
uint8_t wel = 0x06;
|
|
HAL_SPI_Transmit(spi, &wel, 1, 100);
|
|
CS(true);
|
|
}
|
|
|
|
bool Flash::eraseChip() {
|
|
LOG_INFO("Erasing chip...");
|
|
EnableWrite();
|
|
CS(false);
|
|
uint8_t chip_erase = 0x60;
|
|
HAL_SPI_Transmit(spi, &chip_erase, 1, 100);
|
|
CS(true);
|
|
return WaitBusy(25000);
|
|
}
|
|
|
|
bool Flash::eraseSector(uint32_t address) {
|
|
// align address with sector address
|
|
address -= address % SectorSize;
|
|
LOG_INFO("Erasing sector at %lu", address);
|
|
EnableWrite();
|
|
CS(false);
|
|
uint8_t cmd[4] = {
|
|
0x20,
|
|
(uint8_t) (address >> 16) & 0xFF,
|
|
(uint8_t) (address >> 8) & 0xFF,
|
|
(uint8_t) (address & 0xFF),
|
|
};
|
|
HAL_SPI_Transmit(spi, cmd, 4, 100);
|
|
CS(true);
|
|
return WaitBusy(25000);
|
|
}
|
|
|
|
bool Flash::erase32Block(uint32_t address) {
|
|
// align address with block address
|
|
address -= address % Block32Size;
|
|
LOG_INFO("Erasing 32kB block at %lu", address);
|
|
EnableWrite();
|
|
CS(false);
|
|
uint8_t cmd[4] = {
|
|
0x52,
|
|
(uint8_t) (address >> 16) & 0xFF,
|
|
(uint8_t) (address >> 8) & 0xFF,
|
|
(uint8_t) (address & 0xFF),
|
|
};
|
|
HAL_SPI_Transmit(spi, cmd, 4, 100);
|
|
CS(true);
|
|
return WaitBusy(25000);
|
|
}
|
|
|
|
bool Flash::erase64Block(uint32_t address) {
|
|
// align address with block address
|
|
address -= address % Block64Size;
|
|
LOG_INFO("Erasing 64kB block at %lu", address);
|
|
EnableWrite();
|
|
CS(false);
|
|
uint8_t cmd[4] = {
|
|
0xD8,
|
|
(uint8_t) (address >> 16) & 0xFF,
|
|
(uint8_t) (address >> 8) & 0xFF,
|
|
(uint8_t) (address & 0xFF),
|
|
};
|
|
HAL_SPI_Transmit(spi, cmd, 4, 100);
|
|
CS(true);
|
|
return WaitBusy(25000);
|
|
}
|
|
|
|
void Flash::initiateRead(uint32_t address) {
|
|
address &= 0x00FFFFFF;
|
|
CS(false);
|
|
uint8_t cmd[4] = {
|
|
0x03,
|
|
(uint8_t) (address >> 16) & 0xFF,
|
|
(uint8_t) (address >> 8) & 0xFF,
|
|
(uint8_t) (address & 0xFF),
|
|
};
|
|
// issue read command
|
|
HAL_SPI_Transmit(spi, cmd, 4, 100);
|
|
}
|
|
|
|
bool Flash::WaitBusy(uint32_t timeout) {
|
|
uint32_t starttime = HAL_GetTick();
|
|
CS(false);
|
|
uint8_t readStatus1 = 0x05;
|
|
HAL_SPI_Transmit(spi, &readStatus1, 1, 100);
|
|
do {
|
|
vTaskDelay(1);
|
|
uint8_t status1;
|
|
HAL_SPI_Receive(spi, &status1, 1, 100);
|
|
if (!(status1 & 0x01)) {
|
|
CS(true);
|
|
return true;
|
|
}
|
|
} while (HAL_GetTick() - starttime < timeout);
|
|
// timed out
|
|
CS(true);
|
|
LOG_ERR("Timeout occured");
|
|
return false;
|
|
}
|
|
|
|
bool Flash::eraseRange(uint32_t start, uint32_t len) {
|
|
if(start % SectorSize != 0) {
|
|
LOG_ERR("Start address of range has to be sector aligned (is %lu)", start);
|
|
return false;
|
|
}
|
|
if(len % SectorSize != 0) {
|
|
LOG_ERR("Length of range has to be multiple of sector size (is %lu)", len);
|
|
return false;
|
|
}
|
|
uint32_t erased_len = 0;
|
|
while(erased_len < len) {
|
|
uint32_t remaining = len - erased_len;
|
|
if(remaining >= Block64Size && start % Block64Size == 0) {
|
|
erase64Block(start);
|
|
erased_len += Block64Size;
|
|
start += Block64Size;
|
|
continue;
|
|
}
|
|
if(remaining >= Block32Size && start % Block32Size == 0) {
|
|
erase32Block(start);
|
|
erased_len += Block32Size;
|
|
start += Block32Size;
|
|
continue;
|
|
}
|
|
if(remaining >= SectorSize && start % SectorSize == 0) {
|
|
eraseSector(start);
|
|
erased_len += SectorSize;
|
|
start += SectorSize;
|
|
continue;
|
|
}
|
|
// Should never get here
|
|
}
|
|
return true;
|
|
}
|