Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
a58f946edThis file is pinned to the selected release. Download the original to open it in your design tool.
// 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, ®, 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, ®, 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;
}