spiFlash: enable/disable protection. now handle correctly device with protection enabled
This commit is contained in:
parent
21b44fc22f
commit
b25e2e0125
212
src/spiFlash.cpp
212
src/spiFlash.cpp
|
|
@ -8,6 +8,7 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <map>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include "progressBar.hpp"
|
#include "progressBar.hpp"
|
||||||
|
|
@ -28,6 +29,8 @@
|
||||||
#define FLASH_WREN 0x06
|
#define FLASH_WREN 0x06
|
||||||
/* sector (4Kb) erase */
|
/* sector (4Kb) erase */
|
||||||
#define FLASH_SE 0x20
|
#define FLASH_SE 0x20
|
||||||
|
/* read configuration register */
|
||||||
|
#define FLASH_RDCR 0x35
|
||||||
/* write function register (at least ISSI) */
|
/* write function register (at least ISSI) */
|
||||||
#define FLASH_WRFR 0x42
|
#define FLASH_WRFR 0x42
|
||||||
/* read function register (at least ISSI) */
|
/* read function register (at least ISSI) */
|
||||||
|
|
@ -64,10 +67,12 @@
|
||||||
/* Global Block Protection unlock */
|
/* Global Block Protection unlock */
|
||||||
#define FLASH_ULBPR 0x98
|
#define FLASH_ULBPR 0x98
|
||||||
|
|
||||||
SPIFlash::SPIFlash(SPIInterface *spi, int8_t verbose):
|
SPIFlash::SPIFlash(SPIInterface *spi, bool unprotect, int8_t verbose):
|
||||||
_spi(spi), _verbose(verbose), _jedec_id(0),
|
_spi(spi), _verbose(verbose), _jedec_id(0),
|
||||||
_flash_model(NULL)
|
_flash_model(NULL), _unprotect(unprotect)
|
||||||
{
|
{
|
||||||
|
reset();
|
||||||
|
power_up();
|
||||||
read_id();
|
read_id();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -130,8 +135,8 @@ int SPIFlash::sectors_erase(int base_addr, int size)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if block erase + addr end out of end_addr -> use sector_erase */
|
/* if block erase + addr end out of end_addr -> use sector_erase (4Kb) */
|
||||||
if (addr + 0x10000 > end_addr) {
|
if (addr + 0x10000 > end_addr && _flash_model && _flash_model->subsector_erase) {
|
||||||
step = 0x1000;
|
step = 0x1000;
|
||||||
ret = sector_erase(addr);
|
ret = sector_erase(addr);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -236,19 +241,75 @@ int SPIFlash::erase_and_prog(int base_addr, uint8_t *data, int len)
|
||||||
{
|
{
|
||||||
if (_jedec_id == 0)
|
if (_jedec_id == 0)
|
||||||
read_id();
|
read_id();
|
||||||
|
bool must_relock = false; // used to relock after write;
|
||||||
|
|
||||||
/* check Block Protect Bits */
|
/* microchip SST26VF032B have global lock set
|
||||||
if (_jedec_id == 0xbf2642bf) { // microchip SST26VF032B
|
* at powerup. global unlock must be send inconditionally
|
||||||
|
* with or without block protection
|
||||||
|
*/
|
||||||
|
if (_jedec_id == 0xbf2642bf) { // microchip SST26VF032B
|
||||||
if (!global_unlock())
|
if (!global_unlock())
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
}
|
||||||
uint8_t status = read_status_reg();
|
/* check Block Protect Bits (hide WIP/WEN bits) */
|
||||||
if ((status & 0x1c) !=0) {
|
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) - 1) {
|
||||||
|
printError("flash overflow");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
} 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)
|
if (disable_protection() != 0)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Now we can erase sector and write new data */
|
||||||
ProgressBar progress("Writing", len, 50, _verbose < 0);
|
ProgressBar progress("Writing", len, 50, _verbose < 0);
|
||||||
if (sectors_erase(base_addr, len) == -1)
|
if (sectors_erase(base_addr, len) == -1)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -262,6 +323,13 @@ int SPIFlash::erase_and_prog(int base_addr, uint8_t *data, int len)
|
||||||
progress.display(addr);
|
progress.display(addr);
|
||||||
}
|
}
|
||||||
progress.done();
|
progress.done();
|
||||||
|
|
||||||
|
/* and if required: relock blocks */
|
||||||
|
if (must_relock) {
|
||||||
|
enable_protection(status);
|
||||||
|
if (_verbose > 0)
|
||||||
|
display_status_reg(read_status_reg());
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -376,12 +444,41 @@ void SPIFlash::display_status_reg(uint8_t reg)
|
||||||
if (reg & _flash_model->bp_offset[i])
|
if (reg & _flash_model->bp_offset[i])
|
||||||
bp |= 1 << i;
|
bp |= 1 << i;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("RDSR : %02x\n", reg);
|
printf("RDSR : %02x\n", reg);
|
||||||
printf("WIP : %d\n", reg&0x01);
|
printf("WIP : %d\n", reg&0x01);
|
||||||
printf("WEL : %d\n", (reg>>1)&0x01);
|
printf("WEL : %d\n", (reg>>1)&0x01);
|
||||||
printf("BP : %x\n", bp);
|
printf("BP : %x\n", bp);
|
||||||
printf("TB : %d\n", tb);
|
if ((_jedec_id >> 8) != 0x9d60) {
|
||||||
printf("SRWD : %d\n", (((reg>>7)&0x01)));
|
printf("TB : %d\n", tb);
|
||||||
|
} else { // ISSI IS25LP
|
||||||
|
printf("QE : %d\n", ((reg >> 6) & 0x01));
|
||||||
|
}
|
||||||
|
printf("SRWD : %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 SPIFlash::read_status_reg()
|
||||||
|
|
@ -456,8 +553,9 @@ int SPIFlash::disable_protection()
|
||||||
if (read_status_reg() != 0) {
|
if (read_status_reg() != 0) {
|
||||||
std::cout << "disable protection failed" << std::endl;
|
std::cout << "disable protection failed" << std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
} else
|
}
|
||||||
return 0;
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* write protect code to status register
|
/* write protect code to status register
|
||||||
|
|
@ -489,7 +587,7 @@ int SPIFlash::enable_protection(uint8_t protect_code)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SPIFlash::enable_protection(int length)
|
int SPIFlash::enable_protection(uint32_t length)
|
||||||
{
|
{
|
||||||
/* flash device is not listed: can't know BPx position, nor
|
/* flash device is not listed: can't know BPx position, nor
|
||||||
* TB offset, nor TB non-volatile vs OTP */
|
* TB offset, nor TB non-volatile vs OTP */
|
||||||
|
|
@ -506,27 +604,35 @@ int SPIFlash::enable_protection(int length)
|
||||||
* current (temporary) policy: do nothing
|
* current (temporary) policy: do nothing
|
||||||
*/
|
*/
|
||||||
if (_flash_model->tb_otp) {
|
if (_flash_model->tb_otp) {
|
||||||
uint8_t status;
|
uint8_t tb = get_tb();
|
||||||
/* red TB: not aloways 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;
|
|
||||||
default: // unknown
|
|
||||||
printError("Unknown Top/Bottom register");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check if TB is set */
|
/* check if TB is set */
|
||||||
if (!(status & _flash_model->tb_otp)) {
|
if (tb == 0) {
|
||||||
printError("TOP/BOTTOM bit is OTP: can't write this bit");
|
printError("TOP/BOTTOM bit is OTP: can't write this bit");
|
||||||
return -1;
|
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 TB is located in status register -> set to 1 */
|
||||||
if (_flash_model->tb_register == STATR)
|
if (_flash_model->tb_register == STATR)
|
||||||
bp |= _flash_model->tb_offset;
|
bp |= _flash_model->tb_offset;
|
||||||
|
|
@ -555,8 +661,9 @@ int SPIFlash::enable_protection(int length)
|
||||||
printError("Error: enable protection failed\n");
|
printError("Error: enable protection failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
_spi->spi_put(reg_rd, &val, NULL, 1);
|
uint8_t rd_val;
|
||||||
if (reg_rd != val) {
|
_spi->spi_put(reg_rd, NULL, &rd_val, 1);
|
||||||
|
if (rd_val != val) {
|
||||||
printError("failed to update TB bit");
|
printError("failed to update TB bit");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -565,12 +672,38 @@ int SPIFlash::enable_protection(int length)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* convert bp area (status register) to len in byte */
|
/* retrieve TB (Top/Bottom) bit from register */
|
||||||
uint32_t SPIFlash::bp_to_len(uint8_t bp)
|
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;
|
||||||
|
default: // unknown
|
||||||
|
printError("Unknown Top/Bottom register");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (status & _flash_model->tb_offset) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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 */
|
/* 0 -> no block protected */
|
||||||
if (bp == 0)
|
if (bp == 0)
|
||||||
return 0;
|
return protect_area;
|
||||||
|
|
||||||
/* reconstruct code based on each BPx bit */
|
/* reconstruct code based on each BPx bit */
|
||||||
uint8_t tmp = 0;
|
uint8_t tmp = 0;
|
||||||
|
|
@ -579,8 +712,17 @@ uint32_t SPIFlash::bp_to_len(uint8_t bp)
|
||||||
tmp |= (1 << i);
|
tmp |= (1 << i);
|
||||||
/* bp code is 2^(bp-1) blocks */
|
/* bp code is 2^(bp-1) blocks */
|
||||||
uint16_t nr_sectors = (1 << (tmp-1));
|
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 nr_sectors * 0x10000;
|
return protect_area;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* convert len (in byte) to bp (block protection) */
|
/* convert len (in byte) to bp (block protection) */
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
#ifndef SRC_SPIFLASH_HPP_
|
#ifndef SRC_SPIFLASH_HPP_
|
||||||
#define SRC_SPIFLASH_HPP_
|
#define SRC_SPIFLASH_HPP_
|
||||||
|
|
||||||
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "spiInterface.hpp"
|
#include "spiInterface.hpp"
|
||||||
|
|
@ -13,11 +14,11 @@
|
||||||
|
|
||||||
class SPIFlash {
|
class SPIFlash {
|
||||||
public:
|
public:
|
||||||
SPIFlash(SPIInterface *spi, int8_t verbose);
|
SPIFlash(SPIInterface *spi, bool unprotect, int8_t verbose);
|
||||||
/* power */
|
/* power */
|
||||||
virtual void power_up();
|
virtual void power_up();
|
||||||
virtual void power_down();
|
virtual void power_down();
|
||||||
void reset();
|
virtual void reset();
|
||||||
/* protection */
|
/* protection */
|
||||||
int write_enable();
|
int write_enable();
|
||||||
int write_disable();
|
int write_disable();
|
||||||
|
|
@ -37,7 +38,7 @@ class SPIFlash {
|
||||||
* \param[in] length: TODO
|
* \param[in] length: TODO
|
||||||
* \return -1 if write enable or enabling failed
|
* \return -1 if write enable or enabling failed
|
||||||
*/
|
*/
|
||||||
int enable_protection(int len);
|
int enable_protection(uint32_t len);
|
||||||
/*!
|
/*!
|
||||||
* \brief unlock all sectors: specific to
|
* \brief unlock all sectors: specific to
|
||||||
* Microchip SST26VF032B / SST26VF032BA
|
* Microchip SST26VF032B / SST26VF032BA
|
||||||
|
|
@ -94,17 +95,28 @@ class SPIFlash {
|
||||||
uint8_t read_status_reg();
|
uint8_t read_status_reg();
|
||||||
/* display/info */
|
/* display/info */
|
||||||
void display_status_reg(uint8_t reg);
|
void display_status_reg(uint8_t reg);
|
||||||
|
void display_status_reg() {display_status_reg(read_status_reg());}
|
||||||
virtual void read_id();
|
virtual void read_id();
|
||||||
uint16_t readNonVolatileCfgReg();
|
uint16_t readNonVolatileCfgReg();
|
||||||
uint16_t readVolatileCfgReg();
|
uint16_t readVolatileCfgReg();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
/*!
|
||||||
|
* \brief retrieve TB (Top/Bottom) bit from one register
|
||||||
|
* (depends on flash)
|
||||||
|
* \return -1 if unknown register, 1 or 0 otherwise
|
||||||
|
*/
|
||||||
|
int8_t get_tb();
|
||||||
|
|
||||||
|
public:
|
||||||
/*!
|
/*!
|
||||||
* \brief convert block protect to len in byte
|
* \brief convert block protect to len in byte
|
||||||
* \param[in] bp: block protection
|
* \param[in] bp: block protection
|
||||||
* \return protect area in byte
|
* \return protect area in byte
|
||||||
*/
|
*/
|
||||||
uint32_t bp_to_len(uint8_t bp);
|
std::map<std::string, uint32_t> bp_to_len(uint8_t bp, uint8_t tb);
|
||||||
|
|
||||||
|
protected:
|
||||||
/*!
|
/*!
|
||||||
* \brief convert len (in byte) to corresponding block protect
|
* \brief convert len (in byte) to corresponding block protect
|
||||||
* \param[in] len: len in byte
|
* \param[in] len: len in byte
|
||||||
|
|
@ -116,6 +128,7 @@ class SPIFlash {
|
||||||
int8_t _verbose;
|
int8_t _verbose;
|
||||||
uint32_t _jedec_id; /**< CHIP ID */
|
uint32_t _jedec_id; /**< CHIP ID */
|
||||||
flash_t *_flash_model; /**< detect flash model */
|
flash_t *_flash_model; /**< detect flash model */
|
||||||
|
bool _unprotect; /**< allows to unprotect memory before write */
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SRC_SPIFLASH_HPP_
|
#endif // SRC_SPIFLASH_HPP_
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue