SPIFBlockDevice: Consolidates SFDP info data

This far all SFDP Sector Map Table related data has been found in small
pieces inside SPIFBlockDevice. Purpose was to consolidate the data
under one SFDP structure where all the information gathered from
SFDP tables is stored.

More generic version of a function used for parsing a Sector Map Table
was taken into use to avoid duplicate code. The implementation taken
into use is the one which got split from QSPIFBlockDevice and resides
now under the SFDP module.
pull/12318/head
Veijo Pesonen 2020-01-22 13:43:04 +02:00
parent 83c0fdf19f
commit 57722666a1
3 changed files with 68 additions and 130 deletions

View File

@ -107,9 +107,9 @@ SPIFBlockDevice::SPIFBlockDevice(
_write_dummy_and_mode_cycles = 0;
_dummy_and_mode_cycles = _read_dummy_and_mode_cycles;
_min_common_erase_size = 0;
_regions_count = 1;
_region_erase_types_bitfield[0] = SFDP_ERASE_BITMASK_NONE;
_sfdp_info.smtbl.regions_min_common_erase_size = 0;
_sfdp_info.smtbl.region_cnt = 1;
_sfdp_info.smtbl.region_erase_types_bitfld[0] = SFDP_ERASE_BITMASK_NONE;
if (SPIF_BD_ERROR_OK != _spi_set_frequency(freq)) {
tr_error("SPI Set Frequency Failed");
@ -190,14 +190,15 @@ int SPIFBlockDevice::init()
}
/**************************** Parse Sector Map Table ***********************************/
_region_size_bytes[0] =
_sfdp_info.smtbl.region_size[0] =
_device_size_bytes; // If there's no region map, we have a single region sized the entire device size
_region_high_boundary[0] = _device_size_bytes - 1;
_sfdp_info.smtbl.region_high_boundary[0] = _device_size_bytes - 1;
if ((hdr_info.smtbl.addr != 0) && (0 != hdr_info.smtbl.size)) {
tr_debug("init - Parsing Sector Map Table - addr: 0x%" PRIx32 "h, Size: %d", hdr_info.smtbl.addr,
hdr_info.smtbl.size);
if (0 != _sfdp_parse_sector_map_table(hdr_info.smtbl.addr, hdr_info.smtbl.size)) {
if (sfdp_parse_sector_map_table(callback(this, &SPIFBlockDevice::_spi_send_read_sfdp_command),
_sfdp_info.smtbl) < 0) {
tr_error("init - Parse Sector Map Table Failed");
status = SPIF_BD_ERROR_PARSING_FAILED;
goto exit_point;
@ -341,13 +342,13 @@ int SPIFBlockDevice::erase(bd_addr_t addr, bd_size_t in_size)
bool erase_failed = false;
int status = SPIF_BD_ERROR_OK;
// Find region of erased address
int region = _utils_find_addr_region(addr);
int region = _utils_find_addr_region(addr, _sfdp_info.smtbl);
if (region < 0) {
tr_error("no region found for address %llu", addr);
return SPIF_BD_ERROR_INVALID_ERASE_PARAMS;
}
// Erase Types of selected region
uint8_t bitfield = _region_erase_types_bitfield[region];
uint8_t bitfield = _sfdp_info.smtbl.region_erase_types_bitfld[region];
tr_debug("erase - addr: %llu, in_size: %llu", addr, in_size);
@ -366,10 +367,11 @@ int SPIFBlockDevice::erase(bd_addr_t addr, bd_size_t in_size)
// iterate to find next Largest erase type ( a. supported by region, b. smaller than size)
// find the matching instruction and erase size chunk for that type.
type = _utils_iterate_next_largest_erase_type(bitfield, size, (unsigned int)addr, _region_high_boundary[region]);
cur_erase_inst = _erase_type_inst_arr[type];
offset = addr % _erase_type_size_arr[type];
chunk = ((offset + size) < _erase_type_size_arr[type]) ? size : (_erase_type_size_arr[type] - offset);
type = _utils_iterate_next_largest_erase_type(bitfield, size, (unsigned int)addr, region, _sfdp_info.smtbl);
cur_erase_inst = _sfdp_info.smtbl.erase_type_inst_arr[type];
offset = addr % _sfdp_info.smtbl.erase_type_size_arr[type];
chunk = ((offset + size) < _sfdp_info.smtbl.erase_type_size_arr[type]) ?
size : (_sfdp_info.smtbl.erase_type_size_arr[type] - offset);
tr_debug("erase - addr: %llu, size:%d, Inst: 0x%xh, chunk: %" PRIu32 " , ",
addr, size, cur_erase_inst, chunk);
@ -390,10 +392,10 @@ int SPIFBlockDevice::erase(bd_addr_t addr, bd_size_t in_size)
addr += chunk;
size -= chunk;
if ((size > 0) && (addr > _region_high_boundary[region])) {
if ((size > 0) && (addr > _sfdp_info.smtbl.region_high_boundary[region])) {
// erase crossed to next region
region++;
bitfield = _region_erase_types_bitfield[region];
bitfield = _sfdp_info.smtbl.region_erase_types_bitfld[region];
}
if (false == _is_mem_ready()) {
@ -429,16 +431,16 @@ bd_size_t SPIFBlockDevice::get_program_size() const
bd_size_t SPIFBlockDevice::get_erase_size() const
{
// return minimal erase size supported by all regions (0 if none exists)
return _min_common_erase_size;
return _sfdp_info.smtbl.regions_min_common_erase_size;
}
// Find minimal erase size supported by the region to which the address belongs to
bd_size_t SPIFBlockDevice::get_erase_size(bd_addr_t addr) const
{
// Find region of current address
int region = _utils_find_addr_region(addr);
int region = _utils_find_addr_region(addr, _sfdp_info.smtbl);
unsigned int min_region_erase_size = _min_common_erase_size;
unsigned int min_region_erase_size = _sfdp_info.smtbl.regions_min_common_erase_size;
int8_t type_mask = SFDP_ERASE_BITMASK_TYPE1;
int i_ind = 0;
@ -447,9 +449,9 @@ bd_size_t SPIFBlockDevice::get_erase_size(bd_addr_t addr) const
for (i_ind = 0; i_ind < 4; i_ind++) {
// loop through erase types bitfield supported by region
if (_region_erase_types_bitfield[region] & type_mask) {
if (_sfdp_info.smtbl.region_erase_types_bitfld[region] & type_mask) {
min_region_erase_size = _erase_type_size_arr[i_ind];
min_region_erase_size = _sfdp_info.smtbl.erase_type_size_arr[i_ind];
break;
}
type_mask = type_mask << 1;
@ -618,65 +620,6 @@ spif_bd_error SPIFBlockDevice::_spi_send_general_command(int instruction, bd_add
/*********************************************************/
/********** SFDP Parsing and Detection Functions *********/
/*********************************************************/
int SPIFBlockDevice::_sfdp_parse_sector_map_table(uint32_t sector_map_table_addr, size_t sector_map_table_size)
{
uint8_t sector_map_table[SFDP_DEFAULT_BASIC_PARAMS_TABLE_SIZE_BYTES]; /* Up To 16 DWORDS = 64 Bytes */
uint32_t tmp_region_size = 0;
int i_ind = 0;
int prev_boundary = 0;
// Default set to all type bits 1-4 are common
int min_common_erase_type_bits = SFDP_ERASE_BITMASK_ALL;
spif_bd_error status = _spi_send_read_command(SPIF_SFDP, sector_map_table, sector_map_table_addr /*address*/,
sector_map_table_size);
if (status != SPIF_BD_ERROR_OK) {
tr_error("init - Read SFDP First Table Failed");
return -1;
}
// Currently we support only Single Map Descriptor
if (!((sector_map_table[0] & 0x3) == 0x03) && (sector_map_table[1] == 0x0)) {
tr_error("Sector Map - Supporting Only Single! Map Descriptor (not map commands)");
return -1;
}
_regions_count = sector_map_table[2] + 1;
if (_regions_count > SPIF_MAX_REGIONS) {
tr_error("Supporting up to %d regions, current setup to %d regions - fail",
SPIF_MAX_REGIONS, _regions_count);
return -1;
}
// Loop through Regions and set for each one: size, supported erase types, high boundary offset
// Calculate minimum Common Erase Type for all Regions
for (i_ind = 0; i_ind < _regions_count; i_ind++) {
tmp_region_size = ((*((uint32_t *)&sector_map_table[(i_ind + 1) * 4])) >> 8) & 0x00FFFFFF; // bits 9-32
_region_size_bytes[i_ind] = (tmp_region_size + 1) * 256; // Region size is 0 based multiple of 256 bytes;
_region_erase_types_bitfield[i_ind] = sector_map_table[(i_ind + 1) * 4] & 0x0F; // bits 1-4
min_common_erase_type_bits &= _region_erase_types_bitfield[i_ind];
_region_high_boundary[i_ind] = (_region_size_bytes[i_ind] - 1) + prev_boundary;
prev_boundary = _region_high_boundary[i_ind] + 1;
}
// Calc minimum Common Erase Size from min_common_erase_type_bits
uint8_t type_mask = SFDP_ERASE_BITMASK_TYPE1;
for (i_ind = 0; i_ind < 4; i_ind++) {
if (min_common_erase_type_bits & type_mask) {
_min_common_erase_size = _erase_type_size_arr[i_ind];
break;
}
type_mask = type_mask << 1;
}
if (i_ind == 4) {
// No common erase type was found between regions
_min_common_erase_size = 0;
}
return 0;
}
int SPIFBlockDevice::_sfdp_parse_basic_param_table(uint32_t basic_table_addr, size_t basic_table_size)
{
uint8_t param_table[SFDP_DEFAULT_BASIC_PARAMS_TABLE_SIZE_BYTES]; /* Up To 16 DWORDS = 64 Bytes */
@ -713,8 +656,7 @@ int SPIFBlockDevice::_sfdp_parse_basic_param_table(uint32_t basic_table_addr, si
_page_size_bytes = _sfdp_detect_page_size(param_table, basic_table_size);
// Detect and Set Erase Types
_sfdp_detect_erase_types_inst_and_size(param_table, basic_table_size, _erase4k_inst, _erase_type_inst_arr,
_erase_type_size_arr);
_sfdp_detect_erase_types_inst_and_size(param_table, basic_table_size, _erase4k_inst, _sfdp_info.smtbl);
_erase_instruction = _erase4k_inst;
// Detect and Set fastest Bus mode (default 1-1-1)
@ -745,7 +687,7 @@ unsigned int SPIFBlockDevice::_sfdp_detect_page_size(uint8_t *basic_param_table_
int SPIFBlockDevice::_sfdp_detect_erase_types_inst_and_size(uint8_t *basic_param_table_ptr, int basic_param_table_size,
int &erase4k_inst,
int *erase_type_inst_arr, unsigned int *erase_type_size_arr)
sfdp_smtbl_info &smtbl)
{
erase4k_inst = 0xff;
bool found_4Kerase_type = false;
@ -757,34 +699,36 @@ int SPIFBlockDevice::_sfdp_detect_erase_types_inst_and_size(uint8_t *basic_param
if (basic_param_table_size > SPIF_BASIC_PARAM_ERASE_TYPE_1_SIZE_BYTE) {
// Loop Erase Types 1-4
for (int i_ind = 0; i_ind < 4; i_ind++) {
erase_type_inst_arr[i_ind] = 0xff; //0xFF default for unsupported type
erase_type_size_arr[i_ind] = local_math_power(2,
basic_param_table_ptr[SPIF_BASIC_PARAM_ERASE_TYPE_1_SIZE_BYTE + 2 * i_ind]); // Size given as 2^N
tr_debug("Erase Type(A) %d - Inst: 0x%xh, Size: %d", (i_ind + 1), erase_type_inst_arr[i_ind],
erase_type_size_arr[i_ind]);
if (erase_type_size_arr[i_ind] > 1) {
smtbl.erase_type_inst_arr[i_ind] = 0xff; //0xFF default for unsupported type
smtbl.erase_type_size_arr[i_ind] = local_math_power(
2, basic_param_table_ptr[SPIF_BASIC_PARAM_ERASE_TYPE_1_SIZE_BYTE + 2 * i_ind]); // Size given as 2^N
tr_debug("Erase Type(A) %d - Inst: 0x%xh, Size: %d", (i_ind + 1), smtbl.erase_type_inst_arr[i_ind],
smtbl.erase_type_size_arr[i_ind]);
if (smtbl.erase_type_size_arr[i_ind] > 1) {
// if size==1 type is not supported
erase_type_inst_arr[i_ind] = basic_param_table_ptr[SPIF_BASIC_PARAM_ERASE_TYPE_1_BYTE + 2 * i_ind];
smtbl.erase_type_inst_arr[i_ind] =
basic_param_table_ptr[SPIF_BASIC_PARAM_ERASE_TYPE_1_BYTE + 2 * i_ind];
if ((erase_type_size_arr[i_ind] < _min_common_erase_size) || (_min_common_erase_size == 0)) {
if ((smtbl.erase_type_size_arr[i_ind] < smtbl.regions_min_common_erase_size)
|| (smtbl.regions_min_common_erase_size == 0)) {
//Set default minimal common erase for singal region
_min_common_erase_size = erase_type_size_arr[i_ind];
smtbl.regions_min_common_erase_size = smtbl.erase_type_size_arr[i_ind];
}
// SFDP standard requires 4K Erase type to exist and its instruction to be identical to legacy field erase instruction
if (erase_type_size_arr[i_ind] == 4096) {
if (smtbl.erase_type_size_arr[i_ind] == 4096) {
found_4Kerase_type = true;
if (erase4k_inst != erase_type_inst_arr[i_ind]) {
if (erase4k_inst != smtbl.erase_type_inst_arr[i_ind]) {
//Verify 4KErase Type is identical to Legacy 4K erase type specified in Byte 1 of Param Table
erase4k_inst = erase_type_inst_arr[i_ind];
erase4k_inst = smtbl.erase_type_inst_arr[i_ind];
tr_warning("_detectEraseTypesInstAndSize - Default 4K erase Inst is different than erase type Inst for 4K");
}
}
_region_erase_types_bitfield[0] |= bitfield; // If there's no region map, set region "0" types bitfield as defualt;
smtbl.region_erase_types_bitfld[0] |= bitfield; // no region map, set region "0" types bitfield as default
}
tr_info("Erase Type %d - Inst: 0x%xh, Size: %d", (i_ind + 1),
erase_type_inst_arr[i_ind], erase_type_size_arr[i_ind]);
smtbl.erase_type_inst_arr[i_ind], smtbl.erase_type_size_arr[i_ind]);
bitfield = bitfield << 1;
}
}
@ -912,20 +856,20 @@ int SPIFBlockDevice::_set_write_enable()
/*********************************************/
/************* Utility Functions *************/
/*********************************************/
int SPIFBlockDevice::_utils_find_addr_region(bd_size_t offset) const
int SPIFBlockDevice::_utils_find_addr_region(bd_size_t offset, const sfdp_smtbl_info &smtbl) const
{
//Find the region to which the given offset belong to
if ((offset > _device_size_bytes) || (_regions_count == 0)) {
if ((offset > _device_size_bytes) || (smtbl.region_cnt == 0)) {
return -1;
}
if (_regions_count == 1) {
if (smtbl.region_cnt == 1) {
return 0;
}
for (int i_ind = _regions_count - 2; i_ind >= 0; i_ind--) {
for (int i_ind = smtbl.region_cnt - 2; i_ind >= 0; i_ind--) {
if (offset > _region_high_boundary[i_ind]) {
if (offset > smtbl.region_high_boundary[i_ind]) {
return (i_ind + 1);
}
}
@ -933,7 +877,11 @@ int SPIFBlockDevice::_utils_find_addr_region(bd_size_t offset) const
}
int SPIFBlockDevice::_utils_iterate_next_largest_erase_type(uint8_t &bitfield, int size, int offset, int boundry)
int SPIFBlockDevice::_utils_iterate_next_largest_erase_type(uint8_t &bitfield,
int size,
int offset,
int region,
sfdp_smtbl_info &smtbl)
{
// Iterate on all supported Erase Types of the Region to which the offset belong to.
// Iterates from highest type to lowest
@ -943,8 +891,9 @@ int SPIFBlockDevice::_utils_iterate_next_largest_erase_type(uint8_t &bitfield, i
for (i_ind = 3; i_ind >= 0; i_ind--) {
if (bitfield & type_mask) {
largest_erase_type = i_ind;
if ((size > (int)(_erase_type_size_arr[largest_erase_type])) &&
((boundry - offset) > (int)(_erase_type_size_arr[largest_erase_type]))) {
if ((size > (int)(smtbl.erase_type_size_arr[largest_erase_type])) &&
((_sfdp_info.smtbl.region_high_boundary[region] - offset)
> (int)(smtbl.erase_type_size_arr[largest_erase_type]))) {
break;
} else {
bitfield &= ~type_mask;
@ -954,10 +903,9 @@ int SPIFBlockDevice::_utils_iterate_next_largest_erase_type(uint8_t &bitfield, i
}
if (i_ind == 4) {
tr_error("no erase type was found for current region addr");
tr_error("No erase type was found for current region addr");
}
return largest_erase_type;
}
/*********************************************/

View File

@ -52,9 +52,6 @@ enum spif_bd_error {
};
#define SPIF_MAX_REGIONS 10
#define MAX_NUM_OF_ERASE_TYPES 4
/** BlockDevice for SFDP based flash devices over SPI bus
*
* @code
@ -222,6 +219,10 @@ private:
// Internal functions
// SFDP helpers
friend int mbed::sfdp_parse_headers(mbed::Callback<int(bd_addr_t, void *, bd_size_t)> sfdp_reader,
mbed::sfdp_hdr_info &hdr_info);
/****************************************/
/* SFDP Detection and Parsing Functions */
/****************************************/
@ -234,9 +235,6 @@ private:
// Parse and Detect required Basic Parameters from Table
int _sfdp_parse_basic_param_table(uint32_t basic_table_addr, size_t basic_table_size);
// Parse and read information required by Regions Sector Map
int _sfdp_parse_sector_map_table(uint32_t sector_map_table_addr, size_t sector_map_table_size);
// Detect fastest read Bus mode supported by device
int _sfdp_detect_best_bus_read_mode(uint8_t *basic_param_table_ptr, int basic_param_table_size, int &read_inst);
@ -246,17 +244,21 @@ private:
// Detect all supported erase types
int _sfdp_detect_erase_types_inst_and_size(uint8_t *basic_param_table_ptr, int basic_param_table_size,
int &erase4k_inst,
int *erase_type_inst_arr, unsigned int *erase_type_size_arr);
mbed::sfdp_smtbl_info &smtbl);
/***********************/
/* Utilities Functions */
/***********************/
// Find the region to which the given offset belongs to
int _utils_find_addr_region(bd_size_t offset) const;
int _utils_find_addr_region(bd_size_t offset, const mbed::sfdp_smtbl_info &smtbl) const;
// Iterate on all supported Erase Types of the Region to which the offset belongs to.
// Iterates from highest type to lowest
int _utils_iterate_next_largest_erase_type(uint8_t &bitfield, int size, int offset, int boundry);
int _utils_iterate_next_largest_erase_type(uint8_t &bitfield,
int size,
int offset,
int region,
mbed::sfdp_smtbl_info &smtbl);
/********************************/
/* Calls to SPI Driver APIs */
@ -304,22 +306,8 @@ private:
int _erase_instruction;
int _erase4k_inst; // Legacy 4K erase instruction (default 0x20h)
// SFDP helpers
friend int mbed::sfdp_parse_headers(mbed::Callback<int(bd_addr_t, void *, bd_size_t)> sfdp_reader,
mbed::sfdp_hdr_info &hdr_info);
// Up To 4 Erase Types are supported by SFDP (each with its own command Instruction and Size)
int _erase_type_inst_arr[MAX_NUM_OF_ERASE_TYPES];
unsigned int _erase_type_size_arr[MAX_NUM_OF_ERASE_TYPES];
// Sector Regions Map
int _regions_count; //number of regions
int _region_size_bytes[SPIF_MAX_REGIONS]; //regions size in bytes
bd_size_t _region_high_boundary[SPIF_MAX_REGIONS]; //region high address offset boundary
//Each Region can support a bit combination of any of the 4 Erase Types
uint8_t _region_erase_types_bitfield[SPIF_MAX_REGIONS];
unsigned int _min_common_erase_size; // minimal common erase size for all regions (0 if none exists)
// Data extracted from the devices SFDP structure
mbed::sfdp_hdr_info _sfdp_info;
unsigned int _page_size_bytes; // Page size - 256 Bytes default
bd_size_t _device_size_bytes;

View File

@ -31,6 +31,8 @@ static const int SFDP_SECTOR_MAP_MAX_REGIONS = 10;
// Erase Types Per Region BitMask
static const int SFDP_ERASE_BITMASK_TYPE4 = 0x08;
static const int SFDP_ERASE_BITMASK_TYPE3 = 0x04;
static const int SFDP_ERASE_BITMASK_TYPE2 = 0x02;
static const int SFDP_ERASE_BITMASK_TYPE1 = 0x01;
static const int SFDP_ERASE_BITMASK_NONE = 0x00;
static const int SFDP_ERASE_BITMASK_ALL = 0x0F;