openFPGALoader/src/spiFlash.cpp

1009 lines
24 KiB
C++

// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (C) 2019 Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>
*/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <cmath>
#include <map>
#include <iostream>
#include "progressBar.hpp"
#include "display.hpp"
#include "spiFlash.hpp"
#include "spiFlashdb.hpp"
#include "spiInterface.hpp"
/* read/write status register : 0B addr + 0 dummy */
#define FLASH_WRSR 0x01
#define FLASH_RDSR 0x05
# define FLASH_RDSR_WIP (0x01)
# define FLASH_RDSR_WEL (0x02)
/* flash program */
#define FLASH_PP 0x02
/* flash program with 4-byte address */
#define FLASH_4PP 0x12
/* read memory */
#define FLASH_READ 0x03
/* read memory with 4-byte address */
#define FLASH_4READ 0x13
/* write [en|dis]able : 0B addr + 0 dummy */
#define FLASH_WRDIS 0x04
#define FLASH_WREN 0x06
/* sector (4Kb) erase */
#define FLASH_SE 0x20
/* sector (4k) erase with 4-byte address*/
#define FLASH_4SE 0x21
/* read configuration register */
#define FLASH_RDCR 0x35
/* write function register (at least ISSI) */
#define FLASH_WRFR 0x42
/* read function register (at least ISSI) */
#define FLASH_RDFR 0x48
/* Read OTP : 3 B addr + 8 clk cycle*/
#define FLASH_ROTP 0x4B
/* block (32Kb) erase */
#define FLASH_BE32 0x52
/* block (32Kb) erase with 4-byte address */
#define FLASH_4BE32 0x5C
#define FLASH_POWER_UP 0xAB
#define FLASH_POWER_DOWN 0xB9
/* read/write non volatile register: 0B addr + 0 dummy */
#define FLASH_RDNVCR 0xB5
#define FLASH_WRNVCR 0xB1
/* read/write volatile register */
#define FLASH_RDVCR 0x85
#define FLASH_WRVCR 0x81
/* bulk erase */
#define FLASH_CE 0xC7
/* block (64Kb) erase */
#define FLASH_BE64 0xD8
/* block (64Kb) erase with 4-byte address */
#define FLASH_4BE64 0xDC
/* read/write lock register : 3B addr + 0 dummy */
#define FLASH_WRLR 0xE5
#define FLASH_RDLR 0xE8
/* read/clear flag status register : 0B addr + 0 dummy */
#define FLASH_CLFSR 0x50
#define FLASH_RFSR 0x70
/* */
#define FLASH_WRVECR 0x61
#define FLASH_RDVECR 0x65
/* reset-enable + reset */
#define FLASH_RSTEN 0x66
#define FLASH_RST 0x99
/* microchip SST26VF032B / SST26VF032BA */
/* Read Block Protection Register */
#define FLASH_RBPR 0x72
/* Global Block Protection unlock */
#define FLASH_ULBPR 0x98
SPIFlash::SPIFlash(SPIInterface *spi, bool unprotect, int8_t verbose):
_spi(spi), _verbose(verbose), _jedec_id(0),
_flash_model(NULL), _unprotect(unprotect)
{
reset();
power_up();
read_id();
}
int SPIFlash::bulk_erase()
{
int ret, ret2 = 0;
uint32_t timeout=1000000;
uint8_t bp = get_bp();
if (bp != 0) {
if (!_unprotect) {
printError("Error: Can't erase flash: block protection is set");
printError(" can't unlock without --unprotect-flash");
return -1;
}
if ((ret = disable_protection()) != 0)
return ret;
}
if ((ret = write_enable()) != 0)
return ret;
ret2 = _spi->spi_put(FLASH_CE, NULL, NULL, 0);
if (ret2 == 0)
ret2 = _spi->spi_wait(FLASH_RDSR, FLASH_RDSR_WIP, 0x00, timeout);
if (bp != 0)
ret = enable_protection(bp);
return ret | ret2;
}
/* sector -> subsector for micron */
int SPIFlash::sector_erase(int addr)
{
uint8_t tx[5];
uint32_t len = 0;
uint8_t cmd = (addr <= 0xffffff) ? FLASH_SE : FLASH_4SE;
tx[len++] = cmd;
if (cmd == FLASH_4SE)
tx[len++] = static_cast<uint8_t>(0xff & (addr >> 24));
tx[len++] = static_cast<uint8_t>(0xff & (addr >> 16));
tx[len++] = static_cast<uint8_t>(0xff & (addr >> 8));
tx[len++] = static_cast<uint8_t>(0xff & (addr ));
_spi->spi_put(tx, NULL, len);
return 0;
}
int SPIFlash::block32_erase(int addr)
{
uint8_t tx[5];
uint32_t len = 0;
uint8_t cmd = (addr <= 0xffffff) ? FLASH_BE32 : FLASH_4BE32;
tx[len++] = cmd;
if (cmd == FLASH_4BE32)
tx[len++] = static_cast<uint8_t>(0xff & (addr >> 24));
tx[len++] = static_cast<uint8_t>(0xff & (addr >> 16));
tx[len++] = static_cast<uint8_t>(0xff & (addr >> 8));
tx[len++] = static_cast<uint8_t>(0xff & (addr ));
_spi->spi_put(tx, NULL, len);
return 0;
}
/* block64 -> sector for micron */
int SPIFlash::block64_erase(int addr)
{
uint8_t tx[5];
uint32_t len = 0;
uint8_t cmd = (addr <= 0xffffff) ? FLASH_BE64 : FLASH_4BE64;
tx[len++] = cmd;
if (cmd == FLASH_4BE64)
tx[len++] = static_cast<uint8_t>(0xff & (addr >> 24));
tx[len++] = static_cast<uint8_t>(0xff & (addr >> 16));
tx[len++] = static_cast<uint8_t>(0xff & (addr >> 8));
tx[len++] = static_cast<uint8_t>(0xff & (addr ));
_spi->spi_put(tx, NULL, len);
return 0;
}
int SPIFlash::sectors_erase(int base_addr, int size)
{
// check if chip support sector and subsector erase
bool subsector_rdy = false, sector_rdy = true;
if (_flash_model) {
if (_flash_model->subsector_erase)
subsector_rdy = true;
if (!_flash_model->sector_erase)
sector_rdy = false;
}
int ret = 0;
int start_addr = base_addr;
/* compute end_addr to be multiple of 4Kb */
int end_addr = (base_addr + size + 0xfff) & ~0xfff;
if (!subsector_rdy)
end_addr = (base_addr + size + 0xffff) & ~0xffff;
ProgressBar progress("Erasing", end_addr, 50, _verbose < 0);
/* start with block size (64Kb) */
int step = 0x10000;
if (!sector_rdy)
step = 0x1000;
for (int addr = start_addr; addr < end_addr; addr += step) {
if (write_enable() == -1) {
ret = -1;
break;
}
/* if block erase + addr end out of end_addr -> use sector_erase (4Kb) */
if (!sector_rdy || (addr + step > end_addr && subsector_rdy)) {
step = 0x1000;
ret = sector_erase(addr);
} else {
ret = block64_erase(addr);
}
if (ret == -1) {
break;
}
if (_spi->spi_wait(FLASH_RDSR, FLASH_RDSR_WIP, 0x00, 100000, false) == -1) {
ret = -1;
break;
}
progress.display(addr);
}
if (ret == 0)
progress.done();
else
progress.fail();
return ret;
}
int SPIFlash::write_page(int addr, const uint8_t *data, int len)
{
uint32_t addr_len;
uint8_t write_cmd;
uint32_t i = 0;
if (addr <= 0xffffff) {
addr_len = 3;
write_cmd = FLASH_PP;
} else {
addr_len = 4;
write_cmd = FLASH_4PP;
}
uint8_t tx[len+addr_len];
if (write_cmd == FLASH_4PP)
tx[i++] = (uint8_t)(0xff & (addr >> 24));
tx[i++] = (uint8_t)(0xff & (addr >> 16));
tx[i++] = (uint8_t)(0xff & (addr >> 8));
tx[i++] = (uint8_t)(0xff & (addr ));
memcpy(tx+addr_len, data, len);
if (write_enable() == -1)
return -1;
_spi->spi_put(write_cmd, tx, NULL, len+addr_len);
return _spi->spi_wait(FLASH_RDSR, FLASH_RDSR_WIP, 0x00, 1000);
}
int SPIFlash::read(int base_addr, uint8_t *data, int len)
{
uint32_t addr_len;
uint8_t read_cmd;
uint32_t i = 0;
if (base_addr <= 0xffffff) {
addr_len = 3;
read_cmd = FLASH_READ;
} else {
addr_len = 4;
read_cmd = FLASH_4READ;
}
uint8_t tx[len+addr_len];
uint8_t rx[len+addr_len];
if (read_cmd == FLASH_4READ)
tx[i++] = (uint8_t)(0xff & (base_addr >> 24));
tx[i++] = (uint8_t)(0xff & (base_addr >> 16));
tx[i++] = (uint8_t)(0xff & (base_addr >> 8));
tx[i++] = (uint8_t)(0xff & (base_addr ));
int ret = _spi->spi_put(read_cmd, tx, rx, len+addr_len);
if (ret == 0)
memcpy(data, rx+addr_len, len);
else
printf("error\n");
return ret;
}
bool SPIFlash::dump(const std::string &filename, const int &base_addr,
const int &len, int rd_burst)
{
if (rd_burst == 0)
rd_burst = len;
/* segfault with buffer > 1M */
if (rd_burst > 0x100000)
rd_burst = 0x100000;
std::string data;
data.resize(rd_burst);
printInfo("dump flash (May take time)");
printInfo("Open dump file ", false);
FILE *fd = fopen(filename.c_str(), "wb");
if (!fd) {
printError("FAIL");
return false;
} else {
printSuccess("DONE");
}
ProgressBar progress("Read flash ", len, 50, false);
for (int i = 0; i < len; i += rd_burst) {
if (rd_burst + i > len)
rd_burst = len - i;
if (0 != read(base_addr + i, (uint8_t*)&data[0], rd_burst)) {
progress.fail();
printError("Failed to read flash");
fclose(fd);
return false;
}
fwrite(data.c_str(), sizeof(uint8_t), rd_burst, fd);
progress.display(i);
}
progress.done();
fclose(fd);
return true;
}
int SPIFlash::erase_and_prog(int base_addr, const uint8_t *data, int len)
{
if (_jedec_id == 0) {
try {
read_id();
} catch(std::exception &e) {
printError(e.what());
return -1;
}
}
bool must_relock = false; // used to relock after write;
/* microchip SST26VF032B have global lock set
* at powerup. global unlock must be send unconditionally
* with or without block protection
*/
if (_jedec_id == 0xbf2642bf) { // microchip SST26VF032B
if (!global_unlock())
return -1;
}
/* check Block Protect Bits (hide WIP/WEN bits) */
uint8_t status = read_status_reg() & ~0x03;
if (_verbose > 0)
display_status_reg(status);
/* if known chip */
if (_flash_model) {
/* check if offset + len fit in flash */
if ((unsigned int)(base_addr + len) > (_flash_model->nr_sector * 0x10000)) {
printError("flash overflow");
return -1;
}
// if device has block protect
if (_flash_model->bp_len != 0) {
/* compute protected area */
int8_t tb = get_tb();
if (tb == -1)
return -1;
std::map<std::string, uint32_t> lock_len = bp_to_len(status, tb);
printf("%08x %08x %08x %02x\n", base_addr,
lock_len["start"], lock_len["end"], status);
/* if some blocks are locked */
if (lock_len["start"] != 0 || lock_len["end"] != 0) {
/* if overlap */
if (tb == 1) { // bottom blocks are protected
// check if start is in protected blocks
if ((uint32_t)base_addr <= lock_len["end"])
must_relock = true;
} else { // top blocks
if ((uint32_t)(base_addr + len) >= lock_len["start"])
must_relock = true;
}
}
/* ISSI IS25LP032 seems have a bug:
* block protection is always in top mode regardless of
* the TB bit: if write is not at offset 0 -> force unlock
*/
if ((_jedec_id >> 8) == 0x9d6016 && tb == 1 && base_addr != 0) {
_unprotect = true;
must_relock = true;
}
/* ST M25P16 has not TB bit:
* block protection is always in top mode:
* if write is not at offset 0 -> force unlock
*/
if ((_jedec_id >> 8) == 0x202015 && tb == 1 && base_addr != 0) {
_unprotect = true;
must_relock = true;
}
}
} else { // unknown chip: basic test
printWarn("flash chip unknown: use basic protection detection");
if ((status & 0x1c) != 0)
must_relock = true;
}
/* if it's needs to unlock */
if (must_relock) {
printf("unlock blocks\n");
if (!_unprotect) {
printError("Error: block protection is set");
printError(" can't unlock without --unprotect-flash");
return -1;
} else {
if (disable_protection() != 0)
return -1;
}
}
/* Now we can erase sector and write new data */
ProgressBar progress("Writing", len, 50, _verbose < 0);
if (sectors_erase(base_addr, len) == -1)
return -1;
const uint8_t *ptr = data;
int size = 0;
for (int addr = 0; addr < len; addr += size, ptr+=size) {
size = (addr + 256 > len)?(len-addr) : 256;
if ((_jedec_id >> 8) == 0xbf258d) {
size = 1;
}
if (write_page(base_addr + addr, ptr, size) == -1)
return -1;
progress.display(addr);
}
progress.done();
/* and if required: relock blocks */
if (must_relock) {
enable_protection(status);
if (_verbose > 0)
display_status_reg(read_status_reg());
}
return 0;
}
int SPIFlash::erase_and_prog2(int base_addr, const uint8_t *data, int len)
{
printf("len: %d base_addr %08x\n", len, base_addr);
/* Now we can erase sector and write new data */
ProgressBar progress("Writing", len, 50, _verbose < 0);
const uint8_t *ptr = data;
int size = 0;
uint8_t buffer[0x100];
for (int sect_addr = 0; sect_addr < len; sect_addr += 0x1000) {
bool must_erase = true;
bool must_write = true;
bool all_same = true;
size = (sect_addr + 0x1000 > len)?(len-sect_addr) : 0x1000;
uint8_t first_c = ptr[0];
for (int p = 1; p < size; p++) {
if (first_c != ptr[p]) {
all_same = false;
break;
}
}
if (all_same) {
must_erase = first_c == 0xff;
must_write = first_c == 0x00;
}
if (must_erase) {
/* erase 4K (ie 256B * 16 pages) */
sector_erase(sect_addr); // erase 4K
}
for (int page_addr = 0; page_addr < 0x1000; page_addr += 0x100) {
uint32_t addr = sect_addr + page_addr;
size = (addr + 256 > len)?(len-addr) : 256;
memset(buffer, 0xff, 0x100);
if (addr > len) {
memcpy(buffer, ptr, size);
ptr += size;
}
printf("sect_addr %08x page_addr %08x addr %08x\n", sect_addr, page_addr, addr+base_addr);
if (must_write) {
if (write_page(addr + base_addr, buffer, 0x100) == -1)
return -1;
}
}
progress.display(sect_addr);
}
progress.done();
return 0;
}
bool SPIFlash::verify(const int &base_addr, const uint8_t *data,
const int &len, int rd_burst)
{
if (rd_burst == 0) {
rd_burst = len;
if (rd_burst > 65536)
rd_burst = 65536;
}
printInfo("Verifying write (May take time)");
std::string verify_data;
verify_data.resize(rd_burst);
ProgressBar progress("Read flash ", len, 50, false);
for (int i = 0; i < len; i += rd_burst) {
if (rd_burst + i > len)
rd_burst = len - i;
if (0 != read(base_addr + i, (uint8_t*)&verify_data[0], rd_burst)) {
progress.fail();
printError("Failed to read flash");
return false;
}
for (int ii = 0; ii < rd_burst; ii++) {
if ((uint8_t)verify_data[ii] != data[i+ii]) {
progress.fail();
printError("Verification failed at " +
std::to_string(base_addr + i + ii));
return false;
}
}
progress.display(i);
}
progress.done();
return true;
}
void SPIFlash::reset()
{
uint8_t data[8];
memset(data, 0xff, 8);
_spi->spi_put(0xff, data, NULL, 8);
_spi->spi_put(FLASH_RSTEN, NULL, NULL, 0);
_spi->spi_put(FLASH_RST, NULL, NULL, 0);
}
void SPIFlash::read_id()
{
int len = 4;
uint8_t rx[512];
bool has_edid = false;
_spi->spi_put(0x9F, NULL, rx, 4);
_jedec_id = 0;
for (int i=0; i < 4; i++) {
_jedec_id = _jedec_id << 8;
_jedec_id |= (0x00ff & (int)rx[i]);
if (_verbose > 0)
printf("%x ", rx[i]);
}
/* something wrong with read */
if ((_jedec_id >> 8) == 0xffff)
throw std::runtime_error("Read ID failed");
if (_verbose > 0)
printf("read %x\n", _jedec_id);
auto t = flash_list.find(_jedec_id >> 8);
if (t != flash_list.end()) {
_flash_model = &(*t).second;
char content[256];
snprintf(content, 256, "Detected: %s %s %u sectors size: %uMb",
_flash_model->manufacturer.c_str(), _flash_model->model.c_str(),
_flash_model->nr_sector, _flash_model->nr_sector * 0x80000 / 1048576);
printInfo(content);
} else {
/* read extended */
/*if ((_jedec_id & 0xff) != 0) {
has_edid = true;
len += (_jedec_id & 0x0ff);
_spi->spi_put(0x9F, NULL, rx, len);
}*/
/* must be 0x20BA1810 ... */
printf("Detail: \n");
printf("Jedec ID : %02x\n", rx[0]);
printf("memory type : %02x\n", rx[1]);
printf("memory capacity : %02x\n", rx[2]);
if (has_edid) {
printf("EDID + CFD length : %02x\n", rx[3]);
printf("EDID : %02x%02x\n", rx[5], rx[4]);
printf("CFD : ");
if (_verbose > 0) {
for (int i = 6; i < len; i++)
printf("%02x ", rx[i]);
printf("\n");
} else {
printf("\n");
}
}
}
}
void SPIFlash::display_status_reg(uint8_t reg)
{
uint8_t tb, bp;
if (!_flash_model) {
tb = (reg >> 5) & 0x01;
bp = (((reg >> 6) & 0x01) << 3) | ((reg >> 2) & 0x07);
} else if (_flash_model->bp_len == 0) {
tb = 0;
bp = 0;
} else {
tb = (reg & _flash_model->tb_offset) ? 1 : 0;
bp = 0;
for (int i = 0; i < _flash_model->bp_len; i++)
if (reg & _flash_model->bp_offset[i])
bp |= 1 << i;
}
// status register
printf("RDSR : %02x\n", reg);
if ((_jedec_id >> 8) != 0xBF2642) {
printf("WIP : %d\n", reg&0x01);
printf("WEL : %d\n", (reg>>1)&0x01);
printf("BP : %x\n", bp);
if ((_jedec_id >> 8) != 0x9d60) {
printf("TB : %d\n", tb);
} else { // ISSI IS25LP
printf("QE : %d\n", ((reg >> 6) & 0x01));
}
printf("SRWD : %d\n", ((reg >> 7) & 0x01));
} else {
printf("BUSY : %d\n", (reg >> 0) & 0x01);
printf("WEL : %d\n", (reg >> 1) & 0x01);
printf("WSE : %d\n", (reg >> 2) & 0x01);
printf("WSP : %d\n", (reg >> 3) & 0x01);
printf("WPLD : %d\n", (reg >> 4) & 0x01);
printf("SEC : %d\n", (reg >> 5) & 0x01);
printf("BUSY : %d\n", (reg >> 7) & 0x01);
}
/* function register */
switch (_jedec_id >> 8) {
case 0x9d60:
_spi->spi_put(FLASH_RDFR, NULL, &reg, 1);
printf("\nFunction Register\n");
printf("RDFR : %02x\n", reg);
printf("RES : %d\n", ((reg >> 0) & 0x01));
printf("TBS : %d\n", ((reg >> 1) & 0x01));
printf("PSUS : %d\n", ((reg >> 2) & 0x01));
printf("ESUS : %d\n", ((reg >> 3) & 0x01));
printf("IRL : %x\n", ((reg >> 4) & 0x0f));
break;
case 0x010216:
_spi->spi_put(FLASH_RDCR, NULL, &reg, 1);
printf("\nConfiguration Register\n");
printf("RDCR : %02x\n", reg);
printf("FREEZE : %d\n", ((reg >> 0) & 0x01));
printf("QUAD : %d\n", ((reg >> 1) & 0x01));
printf("TBPARM : %d\n", ((reg >> 2) & 0x01));
printf("BPNV : %d\n", ((reg >> 3) & 0x01));
printf("TBPROT : %d\n", ((reg >> 5) & 0x01));
break;
}
}
uint8_t SPIFlash::read_status_reg()
{
uint8_t rx;
_spi->spi_put(FLASH_RDSR, NULL, &rx, 1);
return rx;
}
uint16_t SPIFlash::readNonVolatileCfgReg()
{
uint8_t rx[2];
_spi->spi_put(FLASH_RDNVCR, NULL, rx, 2);
if (_verbose > 0)
printf("Non Volatile %x %x\n", rx[0], rx[1]);
return (rx[1] << 8) | rx[0];
}
uint16_t SPIFlash::readVolatileCfgReg()
{
uint8_t rx[2];
_spi->spi_put(FLASH_RDVCR, NULL, rx, 2);
if (_verbose > 0)
printf("Volatile %x %x\n", rx[0], rx[1]);
return (rx[1] << 8) | rx[0];
}
void SPIFlash::power_up()
{
_spi->spi_put(FLASH_POWER_UP, NULL, NULL, 0);
}
void SPIFlash::power_down()
{
_spi->spi_put(FLASH_POWER_DOWN, NULL, NULL, 0);
}
int SPIFlash::write_enable()
{
_spi->spi_put(FLASH_WREN, NULL, NULL, 0);
/* wait WEL */
if (_spi->spi_wait(FLASH_RDSR, FLASH_RDSR_WEL, FLASH_RDSR_WEL, 1000)) {
printf("write en: Error\n");
return -1;
}
return 0;
}
int SPIFlash::write_disable()
{
_spi->spi_put(FLASH_WRDIS, NULL, NULL, 0);
/* wait ! WEL */
int ret = _spi->spi_wait(FLASH_RDSR, FLASH_RDSR_WEL, 0x00, 1000);
if (ret == -1)
printf("write disable: Error\n");
else if (_verbose > 0)
printf("write disable: Success\n");
return ret;
}
int SPIFlash::disable_protection()
{
// nothing to do
if (_flash_model && _flash_model->bp_len == 0)
return 0;
uint8_t data = 0x00;
if (write_enable() == -1)
return -1;
_spi->spi_put(FLASH_WRSR, &data, NULL, 1);
if (_spi->spi_wait(FLASH_RDSR, 0xff, 0, 1000) < 0)
return -1;
/* read status */
if (read_status_reg() != 0) {
std::cout << "disable protection failed" << std::endl;
return -1;
}
return 0;
}
/* write protect code to status register
* no check for TB
*/
int SPIFlash::enable_protection(uint8_t protect_code)
{
// known device but no bp
if (_flash_model && _flash_model->bp_len == 0) {
printWarn("device has no block protection");
return -1;
}
/* enable write (required to access WRSR) */
if (write_enable() == -1) {
printError("Error: can't enable write");
return -1;
}
/* write status register and wait until Flash idle */
_spi->spi_put(FLASH_WRSR, &protect_code, NULL, 1);
if (_spi->spi_wait(FLASH_RDSR, 0xff, protect_code, 1000) < 0) {
printError("Error: enable protection failed\n");
return -1;
}
/* check status register update */
if (read_status_reg() != protect_code) {
printError("disable protection failed");
return -1;
}
if (_verbose > 0)
display_status_reg(read_status_reg());
return 0;
}
int SPIFlash::enable_protection(uint32_t length)
{
/* flash device is not listed: can't know BPx position, nor
* TB offset, nor TB non-volatile vs OTP */
if (!_flash_model) {
printError("unknown spi flash model: can't lock sectors");
return -1;
}
// if device has no block protect
if (_flash_model->bp_len == 0) {
printWarn("device has no block protection");
return -1;
}
/* convert number of sectors to bp[3:0] mask */
uint8_t bp = len_to_bp(length);
/* TB bit is OTP: this modification can't be revert!
* check if tb is already set and if not warn
* current (temporary) policy: do nothing
*/
if (_flash_model->tb_otp && _flash_model->tb_register != NONER) {
uint8_t tb = get_tb();
/* check if TB is set */
if (tb == 0) {
printError("TOP/BOTTOM bit is OTP: can't write this bit");
return -1;
}
}
/* spansion devices have only one instruction to write
* both status register and configuration register
* we have to write 2 bytes:
* 0: status register
* 1: configuration register
*/
if ((_jedec_id >> 8) == 0x010216) {
int ret = 0;
uint8_t status;
_spi->spi_put(FLASH_RDCR, NULL, &status, 1);
uint8_t cfg[2] = {bp, status};
cfg[1] |= _flash_model->tb_offset;
_spi->spi_put(FLASH_WRSR, cfg, NULL, 2);
if (_spi->spi_wait(FLASH_RDSR, 0x03, 0, 1000) < 0) {
printError("Error: enable protection failed\n");
return -1;
}
return ret;
}
/* if TB is located in status register -> set to 1 */
if (_flash_model->tb_register == STATR)
bp |= _flash_model->tb_offset;
/* update status register */
int ret = enable_protection(bp);
/* No TB available -> nothing to do */
if (_flash_model->tb_register == NONER)
return ret;
/* tb is in different register */
if (_flash_model->tb_register != STATR) {
if (ret == -1) // check if enable_protection has failed
return ret;
/* update register */
uint8_t reg_wr, reg_rd, val;
if (_flash_model->tb_register == FUNCR) {
val = _flash_model->tb_offset;
reg_wr = FLASH_WRFR;
reg_rd = FLASH_RDFR;
} else {
printError("Unknown TOP/BOTTOM register");
return -1;
}
/* write status register and wait until Flash idle */
_spi->spi_put(reg_wr, &val, NULL, 1);
if (_spi->spi_wait(FLASH_RDSR, 0x03, 0, 1000) < 0) {
printError("Error: enable protection failed\n");
return -1;
}
uint8_t rd_val;
_spi->spi_put(reg_rd, NULL, &rd_val, 1);
if (rd_val != val) {
printError("failed to update TB bit");
return -1;
}
}
return ret;
}
/* retrieve TB (Top/Bottom) bit from register */
int8_t SPIFlash::get_tb()
{
uint8_t status;
/* read TB: not always in status register */
switch (_flash_model->tb_register) {
case STATR: // status register
status = read_status_reg();
break;
case FUNCR: // function register
_spi->spi_put(FLASH_RDFR, NULL, &status, 1);
break;
case CONFR: // function register
_spi->spi_put(FLASH_RDCR, NULL, &status, 1);
break;
case NONER: // no TB bit
return 0;
break;
default: // unknown
printError("Unknown Top/Bottom register");
return -1;
}
return (status & _flash_model->tb_offset) ? 1 : 0;
}
/* read status register and extract bp area */
uint8_t SPIFlash::get_bp()
{
uint8_t mask = 0;
uint8_t status = read_status_reg();
if (!_flash_model) {
mask = 0x1C;
} else {
for (int i = 0; i < _flash_model->bp_len; i++)
mask |= _flash_model->bp_offset[i];
}
return (status & mask);
}
/* convert bp area (status register) to len in byte */
std::map<std::string, uint32_t> SPIFlash::bp_to_len(uint8_t bp, uint8_t tb)
{
std::map<std::string, uint32_t> protect_area;
protect_area["start"] = 0;
protect_area["end"] = 0;
/* 0 -> no block protected */
if (bp == 0)
return protect_area;
/* reconstruct code based on each BPx bit */
uint8_t tmp = 0;
for (int i = 0; i < 4; i++)
if ((bp & _flash_model->bp_offset[i]))
tmp |= (1 << i);
/* bp code is 2^(bp-1) blocks */
uint16_t nr_sectors = (1 << (tmp-1));
printf("nr_sectors : %d\n", nr_sectors);
uint32_t len = nr_sectors * 0x10000;
if (tb == 1) {
protect_area["start"] = 0;
protect_area["end"] = len - 1;
} else {
protect_area["end"] = (_flash_model->nr_sector * 0x10000) - 1;
protect_area["start"] = (protect_area["end"] + 1) - len;
}
return protect_area;
}
/* convert len (in byte) to bp (block protection) */
uint8_t SPIFlash::len_to_bp(uint32_t len)
{
/* 0 -> no block to protect */
if (len == 0)
return 0;
/* round and divide by sector size */
len = ((len + 0xffff) & ~0xffff) / 0x10000;
/* convert size to basic BP code */
uint8_t bp = 1 + static_cast<int>(ceil(log2(len)));
/* reconstruct code based on each BPx bit */
uint8_t tmp = 0;
for (int i = 0; i < 4; i++)
if (bp & (1 << i))
tmp |= _flash_model->bp_offset[i];
return tmp;
}
/* microchip SST26VF032B has a dedicated register
* to read sectors (un)lock status and another one to unlock
* sectors
*/
bool SPIFlash::global_unlock()
{
if (write_enable() != 0)
return false;
_spi->spi_put(FLASH_ULBPR, NULL, NULL, 0);
if (_spi->spi_wait(FLASH_RDSR, 0xff, 0, 1000) < 0)
return false;
/* check if all sectors are unlocked */
uint8_t rx2[10];
_spi->spi_put(FLASH_RBPR, NULL, rx2, 10);
printf("Non Volatile\n");
for (int i = 0; i < 10; i++) {
if (rx2[i] != 0)
return false;
}
return true;
}