mirror of https://github.com/ARMmbed/mbed-os.git
Merge pull request #14989 from LDong-Arm/sfdp_sector_maps_multi
SFDP: Add support for multiple configurations and sector mapspull/15065/head
commit
756830e776
|
@ -34,6 +34,7 @@ target_sources(mbed-core
|
|||
source/I2CSlave.cpp
|
||||
source/InterruptIn.cpp
|
||||
source/MbedCRC.cpp
|
||||
source/OSPI.cpp
|
||||
source/PortIn.cpp
|
||||
source/PortInOut.cpp
|
||||
source/PortOut.cpp
|
||||
|
|
|
@ -115,12 +115,7 @@ public:
|
|||
QSPI(const qspi_pinmap_t &pinmap, int mode = 0);
|
||||
QSPI(const qspi_pinmap_t &&, int = 0) = delete; // prevent passing of temporary objects
|
||||
|
||||
virtual ~QSPI()
|
||||
{
|
||||
lock();
|
||||
qspi_free(&_qspi);
|
||||
unlock();
|
||||
}
|
||||
virtual ~QSPI();
|
||||
|
||||
/** Configure the data transmission format
|
||||
*
|
||||
|
|
|
@ -92,6 +92,13 @@ QSPI::QSPI(const qspi_pinmap_t &pinmap, int mode) : _qspi()
|
|||
MBED_ASSERT(success);
|
||||
}
|
||||
|
||||
QSPI::~QSPI()
|
||||
{
|
||||
lock();
|
||||
qspi_free(&_qspi);
|
||||
unlock();
|
||||
}
|
||||
|
||||
qspi_status_t QSPI::configure_format(qspi_bus_width_t inst_width, qspi_bus_width_t address_width, qspi_address_size_t address_size, qspi_bus_width_t alt_width, qspi_alt_size_t alt_size, qspi_bus_width_t data_width, int dummy_cycles)
|
||||
{
|
||||
// Check that alt_size/alt_width are a valid combination
|
||||
|
|
|
@ -5,6 +5,7 @@ if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING)
|
|||
if(BUILD_GREENTEA_TESTS)
|
||||
# add greentea test
|
||||
else()
|
||||
add_subdirectory(COMPONENT_QSPIF)
|
||||
add_subdirectory(tests/UNITTESTS)
|
||||
endif()
|
||||
endif()
|
||||
|
|
|
@ -327,7 +327,9 @@ private:
|
|||
mbed::bd_size_t tx_length, const char *rx_buffer, mbed::bd_size_t rx_length);
|
||||
|
||||
// Send command to read from the SFDP table
|
||||
int _ospi_send_read_sfdp_command(mbed::bd_addr_t addr, void *rx_buffer, mbed::bd_size_t rx_length);
|
||||
int _ospi_send_read_sfdp_command(mbed::bd_addr_t addr, mbed::sfdp_cmd_addr_size_t addr_size,
|
||||
uint8_t inst, uint8_t dummy_cycles,
|
||||
void *rx_buffer, mbed::bd_size_t rx_length);
|
||||
|
||||
// Read the contents of status registers 1 and 2 into a buffer (buffer must have a length of 2)
|
||||
ospi_status_t _ospi_read_status_registers(uint8_t *reg_buffer);
|
||||
|
@ -366,11 +368,11 @@ private:
|
|||
/* SFDP Detection and Parsing Functions */
|
||||
/****************************************/
|
||||
// Parse and Detect required Basic Parameters from Table
|
||||
int _sfdp_parse_basic_param_table(mbed::Callback<int(mbed::bd_addr_t, void *, mbed::bd_size_t)> sfdp_reader,
|
||||
int _sfdp_parse_basic_param_table(mbed::Callback<int(mbed::bd_addr_t, mbed::sfdp_cmd_addr_size_t, uint8_t, uint8_t, void *, mbed::bd_size_t)> sfdp_reader,
|
||||
mbed::sfdp_hdr_info &sfdp_info);
|
||||
|
||||
// Parse and Detect 4-Byte Address Instruction Parameters from Table
|
||||
int _sfdp_parse_4_byte_inst_table(mbed::Callback<int(mbed::bd_addr_t, void *, mbed::bd_size_t)> sfdp_reader,
|
||||
int _sfdp_parse_4_byte_inst_table(mbed::Callback<int(mbed::bd_addr_t, mbed::sfdp_cmd_addr_size_t, uint8_t, uint8_t, void *, mbed::bd_size_t)> sfdp_reader,
|
||||
mbed::sfdp_hdr_info &sfdp_info);
|
||||
|
||||
// Detect the soft reset protocol and reset - returns error if soft reset is not supported
|
||||
|
|
|
@ -921,12 +921,19 @@ int OSPIFBlockDevice::remove_csel_instance(PinName csel)
|
|||
/*********************************************************/
|
||||
/********** SFDP Parsing and Detection Functions *********/
|
||||
/*********************************************************/
|
||||
int OSPIFBlockDevice::_sfdp_parse_basic_param_table(Callback<int(bd_addr_t, void *, bd_size_t)> sfdp_reader,
|
||||
int OSPIFBlockDevice::_sfdp_parse_basic_param_table(Callback<int(bd_addr_t, mbed::sfdp_cmd_addr_size_t, uint8_t, uint8_t, void *, bd_size_t)> sfdp_reader,
|
||||
sfdp_hdr_info &sfdp_info)
|
||||
{
|
||||
uint8_t param_table[SFDP_BASIC_PARAMS_TBL_SIZE]; /* Up To 20 DWORDS = 80 Bytes */
|
||||
|
||||
int status = sfdp_reader(sfdp_info.bptbl.addr, param_table, sfdp_info.bptbl.size);
|
||||
int status = sfdp_reader(
|
||||
sfdp_info.bptbl.addr,
|
||||
SFDP_READ_CMD_ADDR_TYPE,
|
||||
SFDP_READ_CMD_INST,
|
||||
SFDP_READ_CMD_DUMMY_CYCLES,
|
||||
param_table,
|
||||
sfdp_info.bptbl.size
|
||||
);
|
||||
if (status != OSPI_STATUS_OK) {
|
||||
tr_error("Init - Read SFDP First Table Failed");
|
||||
return -1;
|
||||
|
@ -1383,12 +1390,19 @@ int OSPIFBlockDevice::_sfdp_detect_reset_protocol_and_reset(uint8_t *basic_param
|
|||
return status;
|
||||
}
|
||||
|
||||
int OSPIFBlockDevice::_sfdp_parse_4_byte_inst_table(Callback<int(bd_addr_t, void *, bd_size_t)> sfdp_reader,
|
||||
int OSPIFBlockDevice::_sfdp_parse_4_byte_inst_table(Callback<int(mbed::bd_addr_t, mbed::sfdp_cmd_addr_size_t, uint8_t, uint8_t, void *, mbed::bd_size_t)> sfdp_reader,
|
||||
sfdp_hdr_info &sfdp_info)
|
||||
{
|
||||
uint8_t four_byte_inst_table[SFDP_DEFAULT_4_BYTE_INST_TABLE_SIZE_BYTES]; /* Up To 2 DWORDS = 8 Bytes */
|
||||
|
||||
int status = sfdp_reader(sfdp_info.fbatbl.addr, four_byte_inst_table, sfdp_info.fbatbl.size);
|
||||
int status = sfdp_reader(
|
||||
sfdp_info.fbatbl.addr,
|
||||
SFDP_READ_CMD_ADDR_TYPE,
|
||||
SFDP_READ_CMD_INST,
|
||||
SFDP_READ_CMD_DUMMY_CYCLES,
|
||||
four_byte_inst_table,
|
||||
sfdp_info.fbatbl.size
|
||||
);
|
||||
if (status != OSPI_STATUS_OK) {
|
||||
tr_error("Init - Read SFDP Four Byte Inst Table Failed");
|
||||
return -1;
|
||||
|
@ -1823,13 +1837,39 @@ ospi_status_t OSPIFBlockDevice::_ospi_send_general_command(ospi_inst_t instructi
|
|||
return OSPI_STATUS_OK;
|
||||
}
|
||||
|
||||
int OSPIFBlockDevice::_ospi_send_read_sfdp_command(bd_addr_t addr, void *rx_buffer, bd_size_t rx_length)
|
||||
int OSPIFBlockDevice::_ospi_send_read_sfdp_command(mbed::bd_addr_t addr, mbed::sfdp_cmd_addr_size_t addr_size,
|
||||
uint8_t inst, uint8_t dummy_cycles,
|
||||
void *rx_buffer, mbed::bd_size_t rx_length)
|
||||
{
|
||||
size_t rx_len = rx_length;
|
||||
uint8_t *rx_buffer_tmp = (uint8_t *)rx_buffer;
|
||||
|
||||
// Set default here to avoid uninitialized variable warning
|
||||
ospi_address_size_t address_size = _address_size;
|
||||
int address = addr;
|
||||
switch (addr_size) {
|
||||
case SFDP_CMD_ADDR_3_BYTE:
|
||||
address_size = OSPI_CFG_ADDR_SIZE_24;
|
||||
break;
|
||||
case SFDP_CMD_ADDR_4_BYTE:
|
||||
address_size = OSPI_CFG_ADDR_SIZE_32;
|
||||
break;
|
||||
case SFDP_CMD_ADDR_SIZE_VARIABLE: // use current setting
|
||||
break;
|
||||
case SFDP_CMD_ADDR_NONE: // no address in command
|
||||
address = static_cast<int>(OSPI_NO_ADDRESS_COMMAND);
|
||||
break;
|
||||
default:
|
||||
tr_error("Invalid SFDP command address size: 0x%02X", addr_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (dummy_cycles == SFDP_CMD_DUMMY_CYCLES_VARIABLE) {
|
||||
// use current setting
|
||||
dummy_cycles = _dummy_cycles;
|
||||
}
|
||||
|
||||
// SFDP read instruction requires 1-1-1 bus mode with 8 dummy cycles and a 3-byte address
|
||||
ospi_status_t status = _ospi.configure_format(OSPI_CFG_BUS_SINGLE, OSPI_CFG_INST_SIZE_8, OSPI_CFG_BUS_SINGLE, OSPI_CFG_ADDR_SIZE_24, OSPI_CFG_BUS_SINGLE, 0, OSPI_CFG_BUS_SINGLE, OSPIF_RSFDP_DUMMY_CYCLES);
|
||||
ospi_status_t status = _ospi.configure_format(OSPI_CFG_BUS_SINGLE, OSPI_CFG_INST_SIZE_8, OSPI_CFG_BUS_SINGLE, address_size, OSPI_CFG_BUS_SINGLE, 0, OSPI_CFG_BUS_SINGLE, dummy_cycles);
|
||||
if (OSPI_STATUS_OK != status) {
|
||||
tr_error("_ospi_configure_format failed");
|
||||
return status;
|
||||
|
@ -1837,7 +1877,8 @@ int OSPIFBlockDevice::_ospi_send_read_sfdp_command(bd_addr_t addr, void *rx_buff
|
|||
|
||||
// Don't check the read status until after we've configured the format back to 1-1-1, to avoid leaving the interface in an
|
||||
// incorrect state if the read fails.
|
||||
status = _ospi.read(OSPIF_INST_RSFDP, -1, (unsigned int) addr, (char *) rx_buffer, &rx_len);
|
||||
size_t rx_len = rx_length;
|
||||
status = _ospi.read(inst, -1, address, static_cast<char *>(rx_buffer), &rx_len);
|
||||
|
||||
ospi_status_t format_status = _ospi.configure_format(OSPI_CFG_BUS_SINGLE, OSPI_CFG_INST_SIZE_8, OSPI_CFG_BUS_SINGLE, _address_size, OSPI_CFG_BUS_SINGLE, 0, OSPI_CFG_BUS_SINGLE, 0);
|
||||
// All commands other than Read and RSFDP use default 1-1-1 bus mode (Program/Erase are constrained by flash memory performance more than bus performance)
|
||||
|
|
|
@ -11,3 +11,7 @@ target_sources(mbed-storage-qspif
|
|||
INTERFACE
|
||||
source/QSPIFBlockDevice.cpp
|
||||
)
|
||||
|
||||
if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND BUILD_TESTING)
|
||||
add_subdirectory(UNITTESTS)
|
||||
endif()
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
*
|
|
@ -0,0 +1,39 @@
|
|||
# Copyright (c) 2021 Arm Limited. All rights reserved.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
include(GoogleTest)
|
||||
|
||||
add_executable(qspif-unittest)
|
||||
|
||||
target_compile_definitions(qspif-unittest
|
||||
PRIVATE
|
||||
DEVICE_QSPI=1
|
||||
MBED_CONF_QSPIF_QSPI_MIN_READ_SIZE=1
|
||||
MBED_CONF_QSPIF_QSPI_MIN_PROG_SIZE=1
|
||||
)
|
||||
|
||||
target_include_directories(qspif-unittest
|
||||
PRIVATE
|
||||
${mbed-os_SOURCE_DIR}/storage/blockdevice/COMPONENT_QSPIF/include
|
||||
)
|
||||
|
||||
target_sources(qspif-unittest
|
||||
PRIVATE
|
||||
${mbed-os_SOURCE_DIR}/storage/blockdevice/COMPONENT_QSPIF/source/QSPIFBlockDevice.cpp
|
||||
test_QSPIFBlockDevice.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(qspif-unittest
|
||||
PRIVATE
|
||||
mbed-headers-blockdevice
|
||||
mbed-headers-drivers
|
||||
mbed-headers-platform
|
||||
mbed-headers-rtos
|
||||
mbed-stubs-blockdevice
|
||||
mbed-stubs-platform
|
||||
mbed-stubs-drivers
|
||||
mbed-stubs-rtos
|
||||
gmock_main
|
||||
)
|
||||
|
||||
gtest_discover_tests(qspif-unittest PROPERTIES LABELS "storage")
|
|
@ -0,0 +1,336 @@
|
|||
/* Copyright (c) 2021 Arm Limited
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
#include "QSPIF/QSPIFBlockDevice.h"
|
||||
|
||||
// S25FS512S's CR3NV register needs a quirk
|
||||
static const int S25FS512S_CR1NV = 0x2;
|
||||
static const int S25FS512S_CR3NV = 0x4;
|
||||
|
||||
// JEDEC Manufacturer and Device ID, set by the test cases
|
||||
static uint8_t const *device_id;
|
||||
|
||||
namespace mbed {
|
||||
|
||||
/**
|
||||
* Fake implementation of mbed::QSPI class for QSPIFBlockDevice unit tests.
|
||||
*
|
||||
* Note: Data output by this is either dummy or based on Cypress S25FS512S.
|
||||
* We can't use gMock for this because
|
||||
* - The entire mbed::QSPI is non-virtual. Mocks are derived classes, and
|
||||
* when a derived class instance is used as an instance of its base class,
|
||||
* non-virtual members of the base class get used.
|
||||
* - QSPIFBlockDevice's member _qspi (an instance of mbed::QSPI) is a value
|
||||
* instead of a reference, in order to support initializing from pins directly.
|
||||
* Also, mbed::QSPI is declared as NonCopyable whose copy-constructor is deleted,
|
||||
* so we can't copy a preexisting mbed::QSPI instance into QSPIFBlockDevice.
|
||||
* There's no way to dependency inject a mock instance.
|
||||
*/
|
||||
QSPI::QSPI(PinName io0, PinName io1, PinName io2, PinName io3, PinName sclk, PinName ssel, int mode) : _qspi()
|
||||
{
|
||||
}
|
||||
|
||||
QSPI::~QSPI()
|
||||
{
|
||||
}
|
||||
|
||||
qspi_status_t QSPI::configure_format(qspi_bus_width_t inst_width, qspi_bus_width_t address_width, qspi_address_size_t address_size, qspi_bus_width_t alt_width, qspi_alt_size_t alt_size, qspi_bus_width_t data_width, int dummy_cycles)
|
||||
{
|
||||
return QSPI_STATUS_OK;
|
||||
}
|
||||
|
||||
qspi_status_t QSPI::set_frequency(int hz)
|
||||
{
|
||||
return QSPI_STATUS_OK;
|
||||
}
|
||||
|
||||
qspi_status_t QSPI::read(int address, char *rx_buffer, size_t *rx_length)
|
||||
{
|
||||
return QSPI_STATUS_OK;
|
||||
}
|
||||
|
||||
qspi_status_t QSPI::write(int address, const char *tx_buffer, size_t *tx_length)
|
||||
{
|
||||
return QSPI_STATUS_OK;
|
||||
}
|
||||
|
||||
qspi_status_t QSPI::read(qspi_inst_t instruction, int alt, int address, char *rx_buffer, size_t *rx_length)
|
||||
{
|
||||
if (!rx_buffer) {
|
||||
return QSPI_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
switch (address) {
|
||||
// CR1NV and CR3NV registers' factory default values are both 0x0
|
||||
case S25FS512S_CR1NV:
|
||||
case S25FS512S_CR3NV:
|
||||
if (!rx_length || *rx_length < 1) {
|
||||
return QSPI_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
rx_buffer[0] = 0x00;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (instruction) {
|
||||
case mbed::SFDP_READ_CMD_INST: // read SFDP table
|
||||
if (!rx_length || *rx_length < SFDP_BASIC_PARAMS_TBL_SIZE) {
|
||||
return QSPI_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
// set dummy data (zeros), to avoid warning of uninitialized
|
||||
// data from Valgrind and inconsistent test behavior.
|
||||
memset(rx_buffer, 0x00, *rx_length);
|
||||
// soft reset needs one instruction to enable reset,
|
||||
// another instruction to perform reset
|
||||
rx_buffer[61] = 0x30;
|
||||
break;
|
||||
}
|
||||
|
||||
return QSPI_STATUS_OK;
|
||||
}
|
||||
|
||||
qspi_status_t QSPI::write(qspi_inst_t instruction, int alt, int address, const char *tx_buffer, size_t *tx_length)
|
||||
{
|
||||
return QSPI_STATUS_OK;
|
||||
}
|
||||
|
||||
qspi_status_t QSPI::command_transfer(qspi_inst_t instruction, int address, const char *tx_buffer, size_t tx_length, const char *rx_buffer, size_t rx_length)
|
||||
{
|
||||
static char status_registers[2];
|
||||
switch (instruction) {
|
||||
case 0x01: // write status registers
|
||||
if (!tx_buffer || tx_length < 2) {
|
||||
return QSPI_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
memcpy(status_registers, tx_buffer, tx_length);
|
||||
break;
|
||||
case 0x05: // read status register 1
|
||||
if (!rx_buffer || rx_length < 1) {
|
||||
return QSPI_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
const_cast<char *>(rx_buffer)[0] = status_registers[0];
|
||||
break;
|
||||
case 0x35: // read status register 2
|
||||
if (!rx_buffer || rx_length < 1) {
|
||||
return QSPI_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
const_cast<char *>(rx_buffer)[0] = status_registers[1];
|
||||
break;
|
||||
case 0x06: // set write enabled bit
|
||||
status_registers[0] |= 0x2;
|
||||
break;
|
||||
case 0x9F: // read Manufacturer and JDEC Device ID
|
||||
assert(nullptr != device_id); // Test cases should set device_id
|
||||
if (!rx_buffer || rx_length < 3) {
|
||||
return QSPI_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
memcpy(const_cast<char *>(rx_buffer), device_id, 3);
|
||||
break;
|
||||
}
|
||||
return QSPI_STATUS_OK;
|
||||
}
|
||||
|
||||
void QSPI::lock()
|
||||
{
|
||||
}
|
||||
|
||||
void QSPI::unlock()
|
||||
{
|
||||
}
|
||||
|
||||
bool QSPI::_initialize()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QSPI::_initialize_direct()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void QSPI::_build_qspi_command(qspi_inst_t instruction, int address, int alt)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Fake implementation of SFDP functions.
|
||||
* They can't be mocked with gMock which supports only class member functions,
|
||||
* not free/global functions.
|
||||
*/
|
||||
static Callback<int(bd_addr_t, sfdp_cmd_addr_size_t, uint8_t, uint8_t, void *, bd_size_t)> test_sfdp_reader;
|
||||
|
||||
int sfdp_parse_headers(Callback<int(bd_addr_t, sfdp_cmd_addr_size_t, uint8_t, uint8_t, void *, bd_size_t)> sfdp_reader, sfdp_hdr_info &sfdp_info)
|
||||
{
|
||||
// The SFDP Basic Parameters Table size is used as a QSPI buffer
|
||||
// size, so it must be set.
|
||||
sfdp_info.bptbl.size = SFDP_BASIC_PARAMS_TBL_SIZE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sfdp_parse_sector_map_table(Callback<int(bd_addr_t, sfdp_cmd_addr_size_t, uint8_t, uint8_t, void *, bd_size_t)> sfdp_reader, sfdp_hdr_info &sfdp_info)
|
||||
{
|
||||
// The real implementation of this function queries
|
||||
// the CR3NV register on S25FS512S.
|
||||
test_sfdp_reader = sfdp_reader;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t sfdp_detect_page_size(uint8_t *bptbl_ptr, size_t bptbl_size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sfdp_detect_erase_types_inst_and_size(uint8_t *bptbl_ptr, sfdp_hdr_info &sfdp_info)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sfdp_find_addr_region(bd_addr_t offset, const sfdp_hdr_info &sfdp_info)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sfdp_iterate_next_largest_erase_type(uint8_t bitfield,
|
||||
bd_size_t size,
|
||||
bd_addr_t offset,
|
||||
int region,
|
||||
const sfdp_smptbl_info &smptbl)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sfdp_detect_device_density(uint8_t *bptbl_ptr, sfdp_bptbl_info &bptbl_info)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sfdp_detect_addressability(uint8_t *bptbl_ptr, sfdp_bptbl_info &bptbl_info)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace mbed
|
||||
|
||||
|
||||
class TestQSPIFBlockDevice : public testing::Test {
|
||||
protected:
|
||||
TestQSPIFBlockDevice()
|
||||
: bd(PinName(0), PinName(1), PinName(2), PinName(3), PinName(4), PinName(5)) // fake pins
|
||||
{
|
||||
}
|
||||
|
||||
virtual void TearDown()
|
||||
{
|
||||
device_id = nullptr;
|
||||
}
|
||||
|
||||
QSPIFBlockDevice bd;
|
||||
};
|
||||
|
||||
/**
|
||||
* Test that the quirk on the reported values of the CR1NV and CR3NV
|
||||
* registers is applied when the flash chip is S25FS512S.
|
||||
*/
|
||||
TEST_F(TestQSPIFBlockDevice, TestS25FS512SQuirkApplied)
|
||||
{
|
||||
// Use flash chip S25FS512S
|
||||
const uint8_t id_S25FS512S[] { 0x01, 0x02, 0x20 };
|
||||
device_id = id_S25FS512S;
|
||||
EXPECT_EQ(QSPIF_BD_ERROR_OK, bd.init());
|
||||
|
||||
const uint8_t id_N25Q128A[] { 0x20, 0xBB, 0x18 };
|
||||
|
||||
// Read the CR1NV register
|
||||
uint8_t CR1NV;
|
||||
EXPECT_EQ(QSPIF_BD_ERROR_OK, mbed::test_sfdp_reader(
|
||||
S25FS512S_CR1NV,
|
||||
mbed::SFDP_CMD_ADDR_SIZE_VARIABLE,
|
||||
0x65,
|
||||
mbed::SFDP_CMD_DUMMY_CYCLES_VARIABLE,
|
||||
&CR1NV,
|
||||
sizeof(CR1NV)));
|
||||
|
||||
// Read the CR3NV register
|
||||
uint8_t CR3NV;
|
||||
EXPECT_EQ(QSPIF_BD_ERROR_OK, mbed::test_sfdp_reader(
|
||||
S25FS512S_CR3NV,
|
||||
mbed::SFDP_CMD_ADDR_SIZE_VARIABLE,
|
||||
0x65,
|
||||
mbed::SFDP_CMD_DUMMY_CYCLES_VARIABLE,
|
||||
&CR3NV,
|
||||
sizeof(CR3NV)));
|
||||
|
||||
// The hardware value of CR3NV[1] is 0 but S25FS512S's SFDP table
|
||||
// expects it to be 1.
|
||||
EXPECT_EQ(0x2, CR3NV & 0x02);
|
||||
|
||||
// The factory default configuration is CR1NV[2] == 0 and CR3NV[3] == 0
|
||||
// (eight 4KB sectors overlaying the start of the flash) but we treat
|
||||
// the flash chip as if CR1NV[2] == 0 and CR3NV[3] == 1 (no overlaying 4KB
|
||||
// sectors), as Mbed OS does not support this type of flash layout.
|
||||
EXPECT_EQ(0x0, CR1NV & 0x4);
|
||||
EXPECT_EQ(0x8, CR3NV & 0x8);
|
||||
|
||||
// Deinitialization
|
||||
EXPECT_EQ(QSPIF_BD_ERROR_OK, bd.deinit());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the quirk on the reported values of the CR1NV and CR3NV
|
||||
* registers is not applied when the flash chip is not S25FS512S.
|
||||
* Note: Other flash chips most likely do not have CR1NV or CR3NV, but
|
||||
* they might have something else readable at the same addresses.
|
||||
*/
|
||||
TEST_F(TestQSPIFBlockDevice, TestS25FS512SQuirkNotApplied)
|
||||
{
|
||||
// Use flash chip N25Q128A
|
||||
const uint8_t id_N25Q128A[] { 0x20, 0xBB, 0x18 };
|
||||
device_id = id_N25Q128A;
|
||||
EXPECT_EQ(QSPIF_BD_ERROR_OK, bd.init());
|
||||
|
||||
// Read the value at the address of S25FS512S's CR1NV register,
|
||||
// assuming this address is readable.
|
||||
uint8_t CR1NV;
|
||||
EXPECT_EQ(QSPIF_BD_ERROR_OK, mbed::test_sfdp_reader(
|
||||
S25FS512S_CR1NV,
|
||||
mbed::SFDP_CMD_ADDR_SIZE_VARIABLE,
|
||||
0x65,
|
||||
mbed::SFDP_CMD_DUMMY_CYCLES_VARIABLE,
|
||||
&CR1NV,
|
||||
sizeof(CR1NV)));
|
||||
|
||||
// Read the value at the address of S25FS512S's CR3NV register,
|
||||
// assuming this address is readable.
|
||||
uint8_t CR3NV;
|
||||
EXPECT_EQ(QSPIF_BD_ERROR_OK, mbed::test_sfdp_reader(
|
||||
S25FS512S_CR3NV,
|
||||
mbed::SFDP_CMD_ADDR_SIZE_VARIABLE,
|
||||
0x65,
|
||||
mbed::SFDP_CMD_DUMMY_CYCLES_VARIABLE,
|
||||
&CR3NV,
|
||||
sizeof(CR3NV)));
|
||||
|
||||
// Values (0) reported by the fake QSPI::read() should be unmodifed
|
||||
EXPECT_EQ(0, CR1NV);
|
||||
EXPECT_EQ(0, CR3NV);
|
||||
|
||||
// Deinitialization
|
||||
EXPECT_EQ(QSPIF_BD_ERROR_OK, bd.deinit());
|
||||
}
|
|
@ -277,7 +277,9 @@ private:
|
|||
mbed::bd_size_t tx_length, const char *rx_buffer, mbed::bd_size_t rx_length);
|
||||
|
||||
// Send command to read from the SFDP table
|
||||
int _qspi_send_read_sfdp_command(mbed::bd_addr_t addr, void *rx_buffer, mbed::bd_size_t rx_length);
|
||||
int _qspi_send_read_sfdp_command(mbed::bd_addr_t addr, mbed::sfdp_cmd_addr_size_t addr_size,
|
||||
uint8_t inst, uint8_t dummy_cycles,
|
||||
void *rx_buffer, mbed::bd_size_t rx_length);
|
||||
|
||||
// Read the contents of status registers 1 and 2 into a buffer (buffer must have a length of 2)
|
||||
qspi_status_t _qspi_read_status_registers(uint8_t *reg_buffer);
|
||||
|
@ -313,7 +315,7 @@ private:
|
|||
/* SFDP Detection and Parsing Functions */
|
||||
/****************************************/
|
||||
// Parse and Detect required Basic Parameters from Table
|
||||
int _sfdp_parse_basic_param_table(mbed::Callback<int(mbed::bd_addr_t, void *, mbed::bd_size_t)> sfdp_reader,
|
||||
int _sfdp_parse_basic_param_table(mbed::Callback<int(bd_addr_t, mbed::sfdp_cmd_addr_size_t, uint8_t, uint8_t, void *, bd_size_t)> sfdp_reader,
|
||||
mbed::sfdp_hdr_info &sfdp_info);
|
||||
|
||||
// Detect the soft reset protocol and reset - returns error if soft reset is not supported
|
||||
|
@ -374,6 +376,9 @@ private:
|
|||
|
||||
bool _needs_fast_mode;
|
||||
|
||||
// S25FS512S needs a quirk
|
||||
bool _S25FS512S_quirk;
|
||||
|
||||
// Clear block protection
|
||||
qspif_clear_protection_method_t _clear_protection_method;
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
#include "blockdevice/internal/SFDP.h"
|
||||
#include "platform/Callback.h"
|
||||
#include "QSPIFBlockDevice.h"
|
||||
#include "QSPIF/QSPIFBlockDevice.h"
|
||||
#include <string.h>
|
||||
#include "rtos/ThisThread.h"
|
||||
|
||||
|
@ -25,7 +25,7 @@
|
|||
#define MBED_CONF_MBED_TRACE_ENABLE 0
|
||||
#endif
|
||||
|
||||
#include "mbed_trace.h"
|
||||
#include "mbed-trace/mbed_trace.h"
|
||||
#define TRACE_GROUP "QSPIF"
|
||||
|
||||
using namespace std::chrono;
|
||||
|
@ -176,6 +176,9 @@ QSPIFBlockDevice::QSPIFBlockDevice(PinName io0, PinName io1, PinName io2, PinNam
|
|||
// Set default 4-byte addressing extension register write instruction
|
||||
_attempt_4_byte_addressing = true;
|
||||
_4byte_msb_reg_write_inst = QSPIF_INST_4BYTE_REG_WRITE_DEFAULT;
|
||||
|
||||
// Quirk for Cypress S25FS512S
|
||||
_S25FS512S_quirk = false;
|
||||
}
|
||||
|
||||
int QSPIFBlockDevice::init()
|
||||
|
@ -614,12 +617,19 @@ int QSPIFBlockDevice::remove_csel_instance(PinName csel)
|
|||
/*********************************************************/
|
||||
/********** SFDP Parsing and Detection Functions *********/
|
||||
/*********************************************************/
|
||||
int QSPIFBlockDevice::_sfdp_parse_basic_param_table(Callback<int(bd_addr_t, void *, bd_size_t)> sfdp_reader,
|
||||
int QSPIFBlockDevice::_sfdp_parse_basic_param_table(Callback<int(bd_addr_t, mbed::sfdp_cmd_addr_size_t, uint8_t, uint8_t, void *, bd_size_t)> sfdp_reader,
|
||||
sfdp_hdr_info &sfdp_info)
|
||||
{
|
||||
uint8_t param_table[SFDP_BASIC_PARAMS_TBL_SIZE]; /* Up To 20 DWORDS = 80 Bytes */
|
||||
|
||||
int status = sfdp_reader(sfdp_info.bptbl.addr, param_table, sfdp_info.bptbl.size);
|
||||
int status = sfdp_reader(
|
||||
sfdp_info.bptbl.addr,
|
||||
SFDP_READ_CMD_ADDR_TYPE,
|
||||
SFDP_READ_CMD_INST,
|
||||
SFDP_READ_CMD_DUMMY_CYCLES,
|
||||
param_table,
|
||||
sfdp_info.bptbl.size
|
||||
);
|
||||
if (status != QSPI_STATUS_OK) {
|
||||
tr_error("Init - Read SFDP First Table Failed");
|
||||
return -1;
|
||||
|
@ -1092,6 +1102,23 @@ int QSPIFBlockDevice::_handle_vendor_quirks()
|
|||
tr_debug("Applying quirks for ISSI");
|
||||
_num_status_registers = 1;
|
||||
break;
|
||||
case 0x01:
|
||||
if (vendor_device_ids[1] == 0x02 && vendor_device_ids[2] == 0x20) {
|
||||
tr_debug("Applying quirks for Cypress S25FS512S");
|
||||
// On a Cypress S25FS512S flash chip
|
||||
// * The SFDP table expects the register bitfield CR3NV[1] to be 1
|
||||
// but its actual value on the hardware is 0. In order for SFDP parsing
|
||||
// to work, the quirk reports CR3NV[1] as 1.
|
||||
// * All three possible configurations support 256KB sectors across
|
||||
// the entire chip. But when CR3NV[3] is 0, eight 4KB sectors overlay
|
||||
// either the first 32KB or the last 32KB of the chip, whereas when
|
||||
// CR3NV[3] is 1 there are no overlaying 4KB sectors. Mbed OS can't
|
||||
// handle this type of overlay, so the quirk reports CR3NV[3] as 1 to
|
||||
// let the code treat the chip as if it has no overlay. (Also CR1NV[2]
|
||||
// is required to be 0 when CR3NV[3] is 1.)
|
||||
_S25FS512S_quirk = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1405,23 +1432,53 @@ qspi_status_t QSPIFBlockDevice::_qspi_send_general_command(qspi_inst_t instructi
|
|||
return QSPI_STATUS_OK;
|
||||
}
|
||||
|
||||
int QSPIFBlockDevice::_qspi_send_read_sfdp_command(bd_addr_t addr, void *rx_buffer, bd_size_t rx_length)
|
||||
int QSPIFBlockDevice::_qspi_send_read_sfdp_command(mbed::bd_addr_t addr, mbed::sfdp_cmd_addr_size_t addr_size,
|
||||
uint8_t inst, uint8_t dummy_cycles,
|
||||
void *rx_buffer, mbed::bd_size_t rx_length)
|
||||
{
|
||||
size_t rx_len = rx_length;
|
||||
// Set default here to avoid uninitialized variable warning
|
||||
qspi_address_size_t address_size = _address_size;
|
||||
int address = addr;
|
||||
switch (addr_size) {
|
||||
case SFDP_CMD_ADDR_3_BYTE:
|
||||
address_size = QSPI_CFG_ADDR_SIZE_24;
|
||||
break;
|
||||
case SFDP_CMD_ADDR_4_BYTE:
|
||||
address_size = QSPI_CFG_ADDR_SIZE_32;
|
||||
break;
|
||||
case SFDP_CMD_ADDR_SIZE_VARIABLE: // use current setting
|
||||
break;
|
||||
case SFDP_CMD_ADDR_NONE: // no address in command
|
||||
address = static_cast<int>(QSPI_NO_ADDRESS_COMMAND);
|
||||
break;
|
||||
default:
|
||||
tr_error("Invalid SFDP command address size: 0x%02X", addr_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// SFDP read instruction requires 1-1-1 bus mode with 8 dummy cycles and a 3-byte address
|
||||
qspi_status_t status = _qspi.configure_format(QSPI_CFG_BUS_SINGLE, QSPI_CFG_BUS_SINGLE, QSPI_CFG_ADDR_SIZE_24, QSPI_CFG_BUS_SINGLE, 0, QSPI_CFG_BUS_SINGLE, QSPIF_RSFDP_DUMMY_CYCLES);
|
||||
if (dummy_cycles == SFDP_CMD_DUMMY_CYCLES_VARIABLE) {
|
||||
// use current setting
|
||||
dummy_cycles = _dummy_cycles;
|
||||
}
|
||||
|
||||
qspi_status_t status = _qspi.configure_format(QSPI_CFG_BUS_SINGLE, QSPI_CFG_BUS_SINGLE,
|
||||
address_size, QSPI_CFG_BUS_SINGLE,
|
||||
0, QSPI_CFG_BUS_SINGLE, dummy_cycles);
|
||||
if (QSPI_STATUS_OK != status) {
|
||||
tr_error("_qspi_configure_format failed");
|
||||
return status;
|
||||
}
|
||||
|
||||
// Don't check the read status until after we've configured the format back to 1-1-1, to avoid leaving the interface in an
|
||||
// incorrect state if the read fails.
|
||||
status = _qspi.read(QSPIF_INST_RSFDP, -1, (unsigned int) addr, (char *) rx_buffer, &rx_len);
|
||||
// Don't check the read status until after we've configured the format back to 1-1-1,
|
||||
// to avoid leaving the interface in an incorrect state if the read fails.
|
||||
size_t rx_len = rx_length;
|
||||
status = _qspi.read(inst, -1, address, static_cast<char *>(rx_buffer), &rx_len);
|
||||
|
||||
qspi_status_t format_status = _qspi.configure_format(QSPI_CFG_BUS_SINGLE, QSPI_CFG_BUS_SINGLE, _address_size, QSPI_CFG_BUS_SINGLE, 0, QSPI_CFG_BUS_SINGLE, 0);
|
||||
// All commands other than Read and RSFDP use default 1-1-1 bus mode (Program/Erase are constrained by flash memory performance more than bus performance)
|
||||
qspi_status_t format_status = _qspi.configure_format(QSPI_CFG_BUS_SINGLE, QSPI_CFG_BUS_SINGLE,
|
||||
address_size, QSPI_CFG_BUS_SINGLE,
|
||||
0, QSPI_CFG_BUS_SINGLE, 0);
|
||||
// All commands other than Read and RSFDP use default 1-1-1 bus mode
|
||||
// (Program/Erase are constrained by flash memory performance more than bus performance)
|
||||
if (QSPI_STATUS_OK != format_status) {
|
||||
tr_error("_qspi_configure_format failed");
|
||||
return format_status;
|
||||
|
@ -1432,6 +1489,20 @@ int QSPIFBlockDevice::_qspi_send_read_sfdp_command(bd_addr_t addr, void *rx_buff
|
|||
return status;
|
||||
}
|
||||
|
||||
// Handle S25FS512S quirk.
|
||||
const mbed::bd_addr_t S25FS512S_CR1NV = 0x2;
|
||||
const mbed::bd_addr_t S25FS512S_CR3NV = 0x4;
|
||||
if (_S25FS512S_quirk) {
|
||||
if (addr == S25FS512S_CR3NV) {
|
||||
// If we reach here, rx_buffer is guaranteed to be non-null
|
||||
// because it's been checked by _qspi.read() above.
|
||||
static_cast<uint8_t *>(rx_buffer)[0] |= (1 << 1);
|
||||
static_cast<uint8_t *>(rx_buffer)[0] |= (1 << 3);
|
||||
} else if (addr == S25FS512S_CR1NV) {
|
||||
static_cast<uint8_t *>(rx_buffer)[0] &= ~(1 << 2);
|
||||
}
|
||||
}
|
||||
|
||||
return QSPI_STATUS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -221,10 +221,12 @@ private:
|
|||
/* SFDP Detection and Parsing Functions */
|
||||
/****************************************/
|
||||
// Send SFDP Read command to Driver
|
||||
int _spi_send_read_sfdp_command(mbed::bd_addr_t addr, void *rx_buffer, mbed::bd_size_t rx_length);
|
||||
int _spi_send_read_sfdp_command(mbed::bd_addr_t addr, mbed::sfdp_cmd_addr_size_t addr_size,
|
||||
uint8_t inst, uint8_t dummy_cycles,
|
||||
void *rx_buffer, mbed::bd_size_t rx_length);
|
||||
|
||||
// Parse and Detect required Basic Parameters from Table
|
||||
int _sfdp_parse_basic_param_table(mbed::Callback<int(mbed::bd_addr_t, void *, mbed::bd_size_t)> sfdp_reader,
|
||||
int _sfdp_parse_basic_param_table(mbed::Callback<int(bd_addr_t, mbed::sfdp_cmd_addr_size_t, uint8_t, uint8_t, void *, bd_size_t)> sfdp_reader,
|
||||
mbed::sfdp_hdr_info &hdr_info);
|
||||
|
||||
// Detect fastest read Bus mode supported by device
|
||||
|
|
|
@ -497,13 +497,32 @@ spif_bd_error SPIFBlockDevice::_spi_send_read_command(int read_inst, uint8_t *bu
|
|||
return SPIF_BD_ERROR_OK;
|
||||
}
|
||||
|
||||
int SPIFBlockDevice::_spi_send_read_sfdp_command(bd_addr_t addr, void *rx_buffer, bd_size_t rx_length)
|
||||
int SPIFBlockDevice::_spi_send_read_sfdp_command(mbed::bd_addr_t addr, mbed::sfdp_cmd_addr_size_t addr_size,
|
||||
uint8_t inst, uint8_t dummy_cycles,
|
||||
void *rx_buffer, mbed::bd_size_t rx_length)
|
||||
{
|
||||
// Set 1-1-1 bus mode for SFDP header parsing
|
||||
// Initial SFDP read tables are read with 8 dummy cycles
|
||||
_dummy_and_mode_cycles = 8;
|
||||
switch (addr_size) {
|
||||
case SFDP_CMD_ADDR_3_BYTE:
|
||||
_address_size = SPIF_ADDR_SIZE_3_BYTES;
|
||||
break;
|
||||
case SFDP_CMD_ADDR_4_BYTE:
|
||||
_address_size = SPIF_ADDR_SIZE_4_BYTES;
|
||||
break;
|
||||
case SFDP_CMD_ADDR_SIZE_VARIABLE: // use current setting
|
||||
break;
|
||||
case SFDP_CMD_ADDR_NONE: // no address in command
|
||||
addr = static_cast<int>(SPI_NO_ADDRESS_COMMAND);
|
||||
break;
|
||||
default:
|
||||
tr_error("Invalid SFDP command address size: 0x%02X", addr_size);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int status = _spi_send_read_command(SPIF_SFDP, (uint8_t *)rx_buffer, addr, rx_length);
|
||||
if (dummy_cycles != SFDP_CMD_DUMMY_CYCLES_VARIABLE) {
|
||||
_dummy_and_mode_cycles = dummy_cycles;
|
||||
}
|
||||
|
||||
int status = _spi_send_read_command(inst, static_cast<uint8_t *>(rx_buffer), addr, rx_length);
|
||||
if (status < 0) {
|
||||
tr_error("_spi_send_read_sfdp_command failed");
|
||||
}
|
||||
|
@ -588,12 +607,19 @@ spif_bd_error SPIFBlockDevice::_spi_send_general_command(int instruction, bd_add
|
|||
/*********************************************************/
|
||||
/********** SFDP Parsing and Detection Functions *********/
|
||||
/*********************************************************/
|
||||
int SPIFBlockDevice::_sfdp_parse_basic_param_table(Callback<int(bd_addr_t, void *, bd_size_t)> sfdp_reader,
|
||||
mbed::sfdp_hdr_info &sfdp_info)
|
||||
int SPIFBlockDevice::_sfdp_parse_basic_param_table(Callback<int(bd_addr_t, mbed::sfdp_cmd_addr_size_t, uint8_t, uint8_t, void *, bd_size_t)> sfdp_reader,
|
||||
sfdp_hdr_info &sfdp_info)
|
||||
{
|
||||
uint8_t param_table[SFDP_BASIC_PARAMS_TBL_SIZE]; /* Up To 20 DWORDS = 80 Bytes */
|
||||
|
||||
int status = sfdp_reader(sfdp_info.bptbl.addr, param_table, sfdp_info.bptbl.size);
|
||||
int status = sfdp_reader(
|
||||
sfdp_info.bptbl.addr,
|
||||
SFDP_READ_CMD_ADDR_TYPE,
|
||||
SFDP_READ_CMD_INST,
|
||||
SFDP_READ_CMD_DUMMY_CYCLES,
|
||||
param_table,
|
||||
sfdp_info.bptbl.size
|
||||
);
|
||||
if (status != SPIF_BD_ERROR_OK) {
|
||||
tr_error("init - Read SFDP First Table Failed");
|
||||
return -1;
|
||||
|
|
|
@ -47,6 +47,22 @@ constexpr int SFDP_ERASE_BITMASK_ALL = 0x0F; ///< Erase type All
|
|||
|
||||
constexpr int SFDP_MAX_NUM_OF_ERASE_TYPES = 4; ///< Maximum number of different erase types (erase granularity)
|
||||
|
||||
// Size of a command specified by SFDP
|
||||
enum sfdp_cmd_addr_size_t {
|
||||
SFDP_CMD_ADDR_NONE = 0x00, // No address in command
|
||||
SFDP_CMD_ADDR_3_BYTE = 0x01, // 3-byte address
|
||||
SFDP_CMD_ADDR_4_BYTE = 0x02, // 4-byte address
|
||||
SFDP_CMD_ADDR_SIZE_VARIABLE = 0x03 // Address size from current setting
|
||||
};
|
||||
|
||||
// Parameters for SFDP Read command
|
||||
constexpr sfdp_cmd_addr_size_t SFDP_READ_CMD_ADDR_TYPE = SFDP_CMD_ADDR_3_BYTE; // Read SFDP has 3-byte address
|
||||
constexpr uint8_t SFDP_READ_CMD_INST = 0x5A; // Read SFDP instruction
|
||||
constexpr uint8_t SFDP_READ_CMD_DUMMY_CYCLES = 8; // READ SFDP dummy cycles
|
||||
|
||||
// Special value from SFDP for using dummy cycles from current setting
|
||||
constexpr uint8_t SFDP_CMD_DUMMY_CYCLES_VARIABLE = 0xF;
|
||||
|
||||
/** JEDEC Basic Flash Parameter Table info */
|
||||
struct sfdp_bptbl_info {
|
||||
uint32_t addr; ///< Address
|
||||
|
@ -92,7 +108,7 @@ struct sfdp_hdr_info {
|
|||
*
|
||||
* @return MBED_SUCCESS on success, negative error code on failure
|
||||
*/
|
||||
int sfdp_parse_headers(Callback<int(bd_addr_t, void *, bd_size_t)> sfdp_reader, sfdp_hdr_info &sfdp_info);
|
||||
int sfdp_parse_headers(Callback<int(bd_addr_t, sfdp_cmd_addr_size_t, uint8_t, uint8_t, void *, bd_size_t)> sfdp_reader, sfdp_hdr_info &sfdp_info);
|
||||
|
||||
/** Parse Sector Map Parameter Table
|
||||
* Retrieves the table from a device and parses the information contained by the table
|
||||
|
@ -102,7 +118,7 @@ int sfdp_parse_headers(Callback<int(bd_addr_t, void *, bd_size_t)> sfdp_reader,
|
|||
*
|
||||
* @return MBED_SUCCESS on success, negative error code on failure
|
||||
*/
|
||||
int sfdp_parse_sector_map_table(Callback<int(bd_addr_t, void *, bd_size_t)> sfdp_reader, sfdp_hdr_info &sfdp_info);
|
||||
int sfdp_parse_sector_map_table(Callback<int(bd_addr_t, sfdp_cmd_addr_size_t, uint8_t, uint8_t, void *, bd_size_t)> sfdp_reader, sfdp_hdr_info &sfdp_info);
|
||||
|
||||
/** Detect page size used for writing on flash
|
||||
*
|
||||
|
|
|
@ -181,7 +181,7 @@ int sfdp_parse_single_param_header(sfdp_prm_hdr *phdr_ptr, sfdp_hdr_info &hdr_in
|
|||
return 0;
|
||||
}
|
||||
|
||||
int sfdp_parse_headers(Callback<int(bd_addr_t, void *, bd_size_t)> sfdp_reader, sfdp_hdr_info &sfdp_info)
|
||||
int sfdp_parse_headers(Callback<int(bd_addr_t, sfdp_cmd_addr_size_t, uint8_t, uint8_t, void *, bd_size_t)> sfdp_reader, sfdp_hdr_info &sfdp_info)
|
||||
{
|
||||
bd_addr_t addr = 0x0;
|
||||
int number_of_param_headers = 0;
|
||||
|
@ -191,7 +191,14 @@ int sfdp_parse_headers(Callback<int(bd_addr_t, void *, bd_size_t)> sfdp_reader,
|
|||
data_length = SFDP_HEADER_SIZE;
|
||||
uint8_t sfdp_header[SFDP_HEADER_SIZE];
|
||||
|
||||
int status = sfdp_reader(addr, sfdp_header, data_length);
|
||||
int status = sfdp_reader(
|
||||
addr,
|
||||
SFDP_READ_CMD_ADDR_TYPE,
|
||||
SFDP_READ_CMD_INST,
|
||||
SFDP_READ_CMD_DUMMY_CYCLES,
|
||||
sfdp_header,
|
||||
data_length
|
||||
);
|
||||
if (status < 0) {
|
||||
tr_error("Retrieving SFDP Header failed");
|
||||
return -1;
|
||||
|
@ -213,7 +220,14 @@ int sfdp_parse_headers(Callback<int(bd_addr_t, void *, bd_size_t)> sfdp_reader,
|
|||
|
||||
// Loop over Param Headers and parse them (currently supports Basic Param Table and Sector Region Map Table)
|
||||
for (int idx = 0; idx < number_of_param_headers; idx++) {
|
||||
status = sfdp_reader(addr, param_header, data_length);
|
||||
status = sfdp_reader(
|
||||
addr,
|
||||
SFDP_READ_CMD_ADDR_TYPE,
|
||||
SFDP_READ_CMD_INST,
|
||||
SFDP_READ_CMD_DUMMY_CYCLES,
|
||||
param_header,
|
||||
data_length
|
||||
);
|
||||
if (status < 0) {
|
||||
tr_error("Retrieving a parameter header %d failed", idx + 1);
|
||||
return -1;
|
||||
|
@ -231,7 +245,111 @@ int sfdp_parse_headers(Callback<int(bd_addr_t, void *, bd_size_t)> sfdp_reader,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int sfdp_parse_sector_map_table(Callback<int(bd_addr_t, void *, bd_size_t)> sfdp_reader, sfdp_hdr_info &sfdp_info)
|
||||
static constexpr size_t min_descriptor_size = 8; // two DWORDs
|
||||
|
||||
static inline bool is_last_descriptor(const uint8_t *descriptor)
|
||||
{
|
||||
// Last descriptor of the current type (detection command/sector map)
|
||||
MBED_ASSERT(nullptr != descriptor);
|
||||
return descriptor[0] & 0x01;
|
||||
}
|
||||
|
||||
static inline bool is_sector_map_descriptor(const uint8_t *descriptor)
|
||||
{
|
||||
// true - sector map descriptor
|
||||
// false - configuration detection command descriptor
|
||||
MBED_ASSERT(nullptr != descriptor);
|
||||
return descriptor[0] & 0x02;
|
||||
}
|
||||
|
||||
static int sfdp_detect_sector_map_configuration(
|
||||
Callback<int(bd_addr_t, sfdp_cmd_addr_size_t, uint8_t, uint8_t, void *, bd_size_t)> sfdp_reader,
|
||||
sfdp_hdr_info &sfdp_info,
|
||||
uint8_t *&descriptor,
|
||||
const uint8_t *table_end,
|
||||
uint8_t &config)
|
||||
{
|
||||
config = 0;
|
||||
|
||||
// If the table starts with a sector map descriptor instead of a configuration
|
||||
// detection command descriptor, this device has only one configuration (i.e. is
|
||||
// not configurable) with ID equal to 0.
|
||||
if (is_sector_map_descriptor(descriptor)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Loop through all configuration detection descriptors and run detection commands
|
||||
while (!is_sector_map_descriptor(descriptor) && (descriptor + min_descriptor_size <= table_end)) {
|
||||
uint8_t instruction = descriptor[1];
|
||||
uint8_t dummy_cycles = descriptor[2] & 0x0F;
|
||||
auto addr_size = static_cast<sfdp_cmd_addr_size_t>(descriptor[2] >> 6);
|
||||
uint8_t mask = descriptor[3];
|
||||
uint32_t cmd_addr;
|
||||
memcpy(&cmd_addr, &descriptor[4], sizeof(cmd_addr)); // last 32 bits of the descriptor
|
||||
|
||||
uint8_t rx;
|
||||
int status = sfdp_reader(cmd_addr, addr_size, instruction, dummy_cycles, &rx, sizeof(rx));
|
||||
if (status < 0) {
|
||||
tr_error("Sector Map: Configuration detection command failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Shift existing bits to the left, so we can add the newly detected bit
|
||||
config <<= 1;
|
||||
|
||||
// The mask may apply to any bit of rx, so we can't directly combine
|
||||
// (rx & mask) with config. Instead, treat (rx & mask) as a boolean.
|
||||
if (rx & mask) {
|
||||
config |= 0x01;
|
||||
}
|
||||
|
||||
if (is_last_descriptor(descriptor)) {
|
||||
// We've processed the last configuration detection command descriptor
|
||||
descriptor += min_descriptor_size; // Increment the descriptor for the caller
|
||||
return 0;
|
||||
}
|
||||
descriptor += min_descriptor_size; // next descriptor
|
||||
}
|
||||
|
||||
tr_error("Sector Map: Incomplete configuration detection command descriptors");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int sfdp_locate_sector_map_by_config(
|
||||
const uint8_t config,
|
||||
sfdp_hdr_info &sfdp_info,
|
||||
uint8_t *&descriptor,
|
||||
const uint8_t *table_end)
|
||||
{
|
||||
// The size of a sector map descriptor depends on the number of regions. Before
|
||||
// the number of regions is calculated, use the minimum possible size in the a loop condition.
|
||||
while (is_sector_map_descriptor(descriptor) && (descriptor + min_descriptor_size <= table_end)) {
|
||||
size_t regions = descriptor[2] + 1; // Region ID starts at 0
|
||||
size_t current_descriptor_size = (1 /*header*/ + regions) * 4 /*DWORD size*/;
|
||||
if (descriptor + current_descriptor_size > table_end) {
|
||||
tr_error("Sector Map: Incomplete sector map descriptor at the end of the table");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (descriptor[1] == config) {
|
||||
// matching sector map found
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (is_last_descriptor(descriptor)) {
|
||||
// We've processed the last sector map descriptor
|
||||
tr_error("Sector Map: Failed to find a sector map that matches the current configuration");
|
||||
return -1;
|
||||
}
|
||||
|
||||
descriptor += current_descriptor_size; // next descriptor
|
||||
}
|
||||
|
||||
tr_error("Sector Map: Incomplete sector map descriptors");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int sfdp_parse_sector_map_table(Callback<int(bd_addr_t, sfdp_cmd_addr_size_t, uint8_t, uint8_t, void *, bd_size_t)> sfdp_reader, sfdp_hdr_info &sfdp_info)
|
||||
{
|
||||
uint32_t tmp_region_size = 0;
|
||||
uint8_t type_mask;
|
||||
|
@ -254,9 +372,9 @@ int sfdp_parse_sector_map_table(Callback<int(bd_addr_t, void *, bd_size_t)> sfdp
|
|||
* - sector map configuration detection commands
|
||||
* - configurations
|
||||
* - regions in each configuration
|
||||
* is variable -> the size of this table is variable
|
||||
* are variable -> the size of this table is variable
|
||||
*/
|
||||
auto smptbl_buff = std::make_unique<uint8_t[]>(sfdp_info.smptbl.size);
|
||||
auto smptbl_buff = std::unique_ptr<uint8_t[]>(new (std::nothrow) uint8_t[sfdp_info.smptbl.size]);
|
||||
if (!smptbl_buff) {
|
||||
tr_error("Failed to allocate memory");
|
||||
return -1;
|
||||
|
@ -264,19 +382,39 @@ int sfdp_parse_sector_map_table(Callback<int(bd_addr_t, void *, bd_size_t)> sfdp
|
|||
|
||||
tr_debug("Parsing Sector Map Table - addr: 0x%" PRIx32 ", Size: %d", sfdp_info.smptbl.addr, sfdp_info.smptbl.size);
|
||||
|
||||
int status = sfdp_reader(sfdp_info.smptbl.addr, smptbl_buff.get(), sfdp_info.smptbl.size);
|
||||
int status = sfdp_reader(
|
||||
sfdp_info.smptbl.addr,
|
||||
SFDP_READ_CMD_ADDR_TYPE,
|
||||
SFDP_READ_CMD_INST,
|
||||
SFDP_READ_CMD_DUMMY_CYCLES,
|
||||
smptbl_buff.get(),
|
||||
sfdp_info.smptbl.size
|
||||
);
|
||||
if (status < 0) {
|
||||
tr_error("Sector Map: Table retrieval failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Currently we support only Single Map Descriptor
|
||||
if (!((smptbl_buff[0] & 0x3) == 0x03) && (smptbl_buff[1] == 0x0)) {
|
||||
tr_error("Sector Map: Supporting Only Single Map Descriptor (not map commands)");
|
||||
return -1;
|
||||
uint8_t *table = smptbl_buff.get();
|
||||
uint8_t *descriptor = table;
|
||||
|
||||
// Detect which configuration is in use
|
||||
uint8_t active_config_id = 0x00;
|
||||
status = sfdp_detect_sector_map_configuration(sfdp_reader, sfdp_info, descriptor, table + sfdp_info.smptbl.size, active_config_id);
|
||||
if (status != 0) {
|
||||
tr_error("Failed to detect sector map configuration");
|
||||
return status;
|
||||
}
|
||||
|
||||
sfdp_info.smptbl.region_cnt = smptbl_buff[2] + 1;
|
||||
// Locate the sector map for the configuration
|
||||
status = sfdp_locate_sector_map_by_config(active_config_id, sfdp_info, descriptor, table + sfdp_info.smptbl.size);
|
||||
if (status != 0) {
|
||||
tr_error("Failed to locate a matching sector map");
|
||||
return status;
|
||||
}
|
||||
|
||||
// Find the number of regions from the sector map
|
||||
sfdp_info.smptbl.region_cnt = descriptor[2] + 1;
|
||||
if (sfdp_info.smptbl.region_cnt > SFDP_SECTOR_MAP_MAX_REGIONS) {
|
||||
tr_error("Sector Map: Supporting up to %d regions, current setup to %d regions - fail",
|
||||
SFDP_SECTOR_MAP_MAX_REGIONS,
|
||||
|
@ -284,13 +422,13 @@ int sfdp_parse_sector_map_table(Callback<int(bd_addr_t, void *, bd_size_t)> sfdp
|
|||
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
|
||||
// Loop through the regions and set for each one: size, supported erase types, high boundary offset
|
||||
// Calculate the minimum common erase type for all regions
|
||||
for (auto idx = 0; idx < sfdp_info.smptbl.region_cnt; idx++) {
|
||||
tmp_region_size = ((*((uint32_t *)&smptbl_buff[(idx + 1) * 4])) >> 8) & 0x00FFFFFF; // bits 9-32
|
||||
tmp_region_size = ((*((uint32_t *)&descriptor[(idx + 1) * 4])) >> 8) & 0x00FFFFFF; // bits 9-32
|
||||
sfdp_info.smptbl.region_size[idx] = (tmp_region_size + 1) * 256; // Region size is 0 based multiple of 256 bytes;
|
||||
|
||||
sfdp_info.smptbl.region_erase_types_bitfld[idx] = smptbl_buff[(idx + 1) * 4] & 0x0F; // bits 1-4
|
||||
sfdp_info.smptbl.region_erase_types_bitfld[idx] = descriptor[(idx + 1) * 4] & 0x0F; // bits 1-4
|
||||
|
||||
min_common_erase_type_bits &= sfdp_info.smptbl.region_erase_types_bitfld[idx];
|
||||
|
||||
|
|
|
@ -19,62 +19,90 @@
|
|||
#include "blockdevice/internal/SFDP.h"
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::Expectation;
|
||||
using ::testing::MockFunction;
|
||||
using ::testing::Return;
|
||||
|
||||
// The following data is used by multiple test cases.
|
||||
|
||||
/**
|
||||
* The Sector Map Parameter Table of Cypress S25FS512S:
|
||||
* https://www.cypress.com/file/216376/download Table 71.
|
||||
*/
|
||||
static const mbed::bd_addr_t sector_map_start_addr = 0xD81000;
|
||||
static const mbed::bd_addr_t register_CR1NV = 0x000002;
|
||||
static const mbed::bd_addr_t register_CR3NV = 0x000004;
|
||||
static const uint8_t sector_map_multiple_descriptors[] = {
|
||||
// Detect 1
|
||||
0xFC, 0x65, 0xFF, 0x08,
|
||||
0x04, 0x00, 0x00, 0x00,
|
||||
|
||||
// Detect 2
|
||||
0xFC, 0x65, 0xFF, 0x04,
|
||||
0x02, 0x00, 0x00, 0x00,
|
||||
|
||||
// Detect 3
|
||||
0xFD, 0x65, 0xFF, 0x02,
|
||||
0x04, 0x00, 0x00, 0x00,
|
||||
|
||||
// Config 1
|
||||
0xFE, 0x01, 0x02, 0xFF, // header
|
||||
0xF1, 0x7F, 0x00, 0x00, // region 0
|
||||
0xF4, 0x7F, 0x03, 0x00, // region 1
|
||||
0xF4, 0xFF, 0xFB, 0x03, // region 2
|
||||
|
||||
// No Config 2
|
||||
|
||||
// Config 3
|
||||
0xFE, 0x03, 0x02, 0xFF, // header
|
||||
0xF4, 0xFF, 0xFB, 0x03, // region 0
|
||||
0xF4, 0x7F, 0x03, 0x00, // region 1
|
||||
0xF1, 0x7F, 0x00, 0x00, // region 2
|
||||
|
||||
// Config 4
|
||||
0xFF, 0x05, 0x00, 0xFF, // header
|
||||
0xF4, 0xFF, 0xFF, 0x03 // region 0
|
||||
};
|
||||
|
||||
/**
|
||||
* Based on Cypress S25FS512S, modified to have one descriptor,
|
||||
* three regions for test purpose.
|
||||
*/
|
||||
static const uint8_t sector_map_single_descriptor[] {
|
||||
0xFF, 0x01, 0x02, 0xFF, // header, highest region = 0x02
|
||||
0xFF, 0x00, 0x02, 0xFF, // header, highest region = 0x02
|
||||
0xF1, 0x7F, 0x00, 0x00, // region 0
|
||||
0xF4, 0x7F, 0x03, 0x00, // region 1
|
||||
0xF4, 0xFF, 0xFB, 0x03 // region 2
|
||||
};
|
||||
|
||||
/**
|
||||
* Based on Cypress S25FS512S, modified to have one descriptor,
|
||||
* twelve regions for test purpose.
|
||||
*/
|
||||
static const uint8_t sector_map_single_descriptor_twelve_regions[] {
|
||||
0xFF, 0x01, 0x0B, 0xFF, // header, highest region = 0x0B
|
||||
0xF1, 0x7F, 0x00, 0x00, // region 0
|
||||
0xF4, 0x7F, 0x03, 0x00, // region 1
|
||||
0xF4, 0xFF, 0xFB, 0x03, // region 2
|
||||
0xF1, 0x7F, 0x00, 0x00, // region 3
|
||||
0xF4, 0x7F, 0x03, 0x00, // region 4
|
||||
0xF4, 0xFF, 0xFB, 0x03, // region 5
|
||||
0xF1, 0x7F, 0x00, 0x00, // region 6
|
||||
0xF4, 0x7F, 0x03, 0x00, // region 7
|
||||
0xF4, 0xFF, 0xFB, 0x03, // region 8
|
||||
0xF1, 0x7F, 0x00, 0x00, // region 9
|
||||
0xF4, 0x7F, 0x03, 0x00, // region 10
|
||||
0xF4, 0xFF, 0xFB, 0x03, // region 11
|
||||
};
|
||||
|
||||
class TestSFDP : public testing::Test {
|
||||
|
||||
public:
|
||||
mbed::Callback<int(mbed::bd_addr_t, void*, bd_size_t)> sfdp_reader_callback;
|
||||
mbed::Callback<int(mbed::bd_addr_t, mbed::sfdp_cmd_addr_size_t, uint8_t, uint8_t, void*, bd_size_t)> sfdp_reader_callback;
|
||||
|
||||
protected:
|
||||
TestSFDP() : sfdp_reader_callback(this, &TestSFDP::sfdp_reader) {};
|
||||
|
||||
int sfdp_reader(mbed::bd_addr_t addr, void *buff, bd_size_t buff_size)
|
||||
int sfdp_reader(mbed::bd_addr_t addr, mbed::sfdp_cmd_addr_size_t addr_size, uint8_t instr, uint8_t cycles, void *buff, bd_size_t buff_size)
|
||||
{
|
||||
int mock_return = sfdp_reader_mock.Call(addr, buff, buff_size);
|
||||
int mock_return = sfdp_reader_mock.Call(addr, addr_size, instr, cycles, buff, buff_size);
|
||||
if (mock_return != 0) {
|
||||
return mock_return;
|
||||
}
|
||||
|
||||
memcpy(buff, sector_descriptors, sector_descriptors_size);
|
||||
// The following register values give Configuration ID = 0x03.
|
||||
uint8_t *out = static_cast<uint8_t*>(buff);
|
||||
switch (addr) {
|
||||
case sector_map_start_addr:
|
||||
memcpy(buff, sector_descriptors, sector_descriptors_size);
|
||||
break;
|
||||
case register_CR1NV:
|
||||
out[0] = 0x04;
|
||||
break;
|
||||
case register_CR3NV:
|
||||
out[0] = 0x02;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
|
@ -87,7 +115,7 @@ protected:
|
|||
sector_descriptors_size = table_size;
|
||||
}
|
||||
|
||||
MockFunction<int(mbed::bd_addr_t, void*, bd_size_t)> sfdp_reader_mock;
|
||||
MockFunction<int(mbed::bd_addr_t, uint8_t, uint8_t, uint8_t, void*, bd_size_t)> sfdp_reader_mock;
|
||||
const uint8_t *sector_descriptors;
|
||||
bd_size_t sector_descriptors_size;
|
||||
};
|
||||
|
@ -191,7 +219,7 @@ TEST_F(TestSFDP, TestNoSectorMap)
|
|||
header_info.bptbl.device_size_bytes = device_size;
|
||||
|
||||
// No need to read anything
|
||||
EXPECT_CALL(sfdp_reader_mock, Call(_, _, _)).Times(0);
|
||||
EXPECT_CALL(sfdp_reader_mock, Call(_, _, _, _, _, _)).Times(0);
|
||||
|
||||
EXPECT_EQ(0, sfdp_parse_sector_map_table(sfdp_reader_callback, header_info));
|
||||
|
||||
|
@ -208,9 +236,17 @@ TEST_F(TestSFDP, TestSingleSectorConfig)
|
|||
mbed::sfdp_hdr_info header_info;
|
||||
set_sector_map_param_table(header_info.smptbl, sector_map_single_descriptor, sizeof(sector_map_single_descriptor));
|
||||
|
||||
EXPECT_CALL(sfdp_reader_mock, Call(sector_map_start_addr, _, sizeof(sector_map_single_descriptor)))
|
||||
.Times(1)
|
||||
.WillOnce(Return(0));
|
||||
EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(
|
||||
sector_map_start_addr,
|
||||
mbed::SFDP_READ_CMD_ADDR_TYPE,
|
||||
mbed::SFDP_READ_CMD_INST,
|
||||
mbed::SFDP_READ_CMD_DUMMY_CYCLES,
|
||||
_,
|
||||
sizeof(sector_map_single_descriptor)
|
||||
)
|
||||
).Times(1).WillOnce(Return(0));
|
||||
|
||||
EXPECT_EQ(0, sfdp_parse_sector_map_table(sfdp_reader_callback, header_info));
|
||||
|
||||
|
@ -241,9 +277,17 @@ TEST_F(TestSFDP, TestSFDPReadFailure)
|
|||
mbed::sfdp_hdr_info header_info;
|
||||
set_sector_map_param_table(header_info.smptbl, sector_map_single_descriptor, sizeof(sector_map_single_descriptor));
|
||||
|
||||
EXPECT_CALL(sfdp_reader_mock, Call(sector_map_start_addr, _, sizeof(sector_map_single_descriptor)))
|
||||
.Times(1)
|
||||
.WillOnce(Return(-1)); // Emulate read failure
|
||||
EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(
|
||||
sector_map_start_addr,
|
||||
mbed::SFDP_READ_CMD_ADDR_TYPE,
|
||||
mbed::SFDP_READ_CMD_INST,
|
||||
mbed::SFDP_READ_CMD_DUMMY_CYCLES,
|
||||
_,
|
||||
sizeof(sector_map_single_descriptor)
|
||||
)
|
||||
).Times(1).WillOnce(Return(-1)); // Emulate read failure
|
||||
|
||||
EXPECT_EQ(-1, sfdp_parse_sector_map_table(sfdp_reader_callback, header_info));
|
||||
}
|
||||
|
@ -254,6 +298,26 @@ TEST_F(TestSFDP, TestSFDPReadFailure)
|
|||
*/
|
||||
TEST_F(TestSFDP, TestMoreRegionsThanSupported)
|
||||
{
|
||||
/**
|
||||
* Based on Cypress S25FS512S, modified to have one descriptor,
|
||||
* twelve regions for test purpose.
|
||||
*/
|
||||
const uint8_t sector_map_single_descriptor_twelve_regions[] {
|
||||
0xFF, 0x00, 0x0B, 0xFF, // header, highest region = 0x0B
|
||||
0xF1, 0x7F, 0x00, 0x00, // region 0
|
||||
0xF4, 0x7F, 0x03, 0x00, // region 1
|
||||
0xF4, 0xFF, 0xFB, 0x03, // region 2
|
||||
0xF1, 0x7F, 0x00, 0x00, // region 3
|
||||
0xF4, 0x7F, 0x03, 0x00, // region 4
|
||||
0xF4, 0xFF, 0xFB, 0x03, // region 5
|
||||
0xF1, 0x7F, 0x00, 0x00, // region 6
|
||||
0xF4, 0x7F, 0x03, 0x00, // region 7
|
||||
0xF4, 0xFF, 0xFB, 0x03, // region 8
|
||||
0xF1, 0x7F, 0x00, 0x00, // region 9
|
||||
0xF4, 0x7F, 0x03, 0x00, // region 10
|
||||
0xF4, 0xFF, 0xFB, 0x03, // region 11
|
||||
};
|
||||
|
||||
mbed::sfdp_hdr_info header_info;
|
||||
set_sector_map_param_table(
|
||||
header_info.smptbl,
|
||||
|
@ -261,9 +325,305 @@ TEST_F(TestSFDP, TestMoreRegionsThanSupported)
|
|||
sizeof(sector_map_single_descriptor_twelve_regions)
|
||||
);
|
||||
|
||||
EXPECT_CALL(sfdp_reader_mock, Call(sector_map_start_addr, _, sizeof(sector_map_single_descriptor_twelve_regions)))
|
||||
.Times(1)
|
||||
.WillOnce(Return(0));
|
||||
EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(
|
||||
sector_map_start_addr,
|
||||
mbed::SFDP_READ_CMD_ADDR_TYPE,
|
||||
mbed::SFDP_READ_CMD_INST,
|
||||
mbed::SFDP_READ_CMD_DUMMY_CYCLES,
|
||||
_,
|
||||
sizeof(sector_map_single_descriptor_twelve_regions)
|
||||
)
|
||||
).Times(1).WillOnce(Return(0));
|
||||
|
||||
EXPECT_EQ(-1, sfdp_parse_sector_map_table(sfdp_reader_callback, header_info));
|
||||
}
|
||||
|
||||
/**
|
||||
* When a Sector Map Parameter Table has multiple configuration detection
|
||||
* commands and sector maps, sfdp_parse_sector_map_table() runs all commands
|
||||
* to find the active configuration and selects the matching sector map.
|
||||
*/
|
||||
TEST_F(TestSFDP, TestMultipleSectorConfigs)
|
||||
{
|
||||
mbed::sfdp_hdr_info header_info;
|
||||
set_sector_map_param_table(
|
||||
header_info.smptbl,
|
||||
sector_map_multiple_descriptors,
|
||||
sizeof(sector_map_multiple_descriptors)
|
||||
);
|
||||
|
||||
// First call: get all detection command and sector map descriptors
|
||||
Expectation call_1 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(
|
||||
sector_map_start_addr,
|
||||
mbed::SFDP_READ_CMD_ADDR_TYPE,
|
||||
mbed::SFDP_READ_CMD_INST,
|
||||
mbed::SFDP_READ_CMD_DUMMY_CYCLES,
|
||||
_,
|
||||
sizeof(sector_map_multiple_descriptors)
|
||||
)
|
||||
).Times(1).WillOnce(Return(0));
|
||||
|
||||
// Second call: detect bit-0 of configuration
|
||||
Expectation call_2 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(register_CR3NV, mbed::SFDP_CMD_ADDR_SIZE_VARIABLE, 0x65, mbed::SFDP_CMD_DUMMY_CYCLES_VARIABLE, _, 1)
|
||||
).Times(1).After(call_1).WillOnce(Return(0));
|
||||
|
||||
// Third call: detect bit-1 of configuration
|
||||
Expectation call_3 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(register_CR1NV, mbed::SFDP_CMD_ADDR_SIZE_VARIABLE, 0x65, mbed::SFDP_CMD_DUMMY_CYCLES_VARIABLE, _, 1)
|
||||
).Times(1).After(call_2).WillOnce(Return(0));
|
||||
|
||||
// Fourth call: detect bit-2 of configuration
|
||||
Expectation call_4 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(register_CR3NV, mbed::SFDP_CMD_ADDR_SIZE_VARIABLE, 0x65, mbed::SFDP_CMD_DUMMY_CYCLES_VARIABLE, _, 1)
|
||||
).Times(1).After(call_3).WillOnce(Return(0));
|
||||
|
||||
EXPECT_EQ(0, sfdp_parse_sector_map_table(sfdp_reader_callback, header_info));
|
||||
|
||||
// Expecting sector map for Configuration ID = 0x03:
|
||||
// Three regions
|
||||
EXPECT_EQ(3, header_info.smptbl.region_cnt);
|
||||
|
||||
// Region 0: erase type 3 (256KB erase)
|
||||
// Range: first 64 MB minus 256 KB
|
||||
EXPECT_EQ(64_MB - 256_KB, header_info.smptbl.region_size[0]);
|
||||
EXPECT_EQ(64_MB - 256_KB - 1_B, header_info.smptbl.region_high_boundary[0]);
|
||||
EXPECT_EQ(1 << (3 - 1), header_info.smptbl.region_erase_types_bitfld[0]);
|
||||
|
||||
// Region 1: erase type 3 (256KB erase, which also covers 32KB from Region 2)
|
||||
// Range: between Region 0 and Region 2
|
||||
EXPECT_EQ(256_KB - 32_KB, header_info.smptbl.region_size[1]);
|
||||
EXPECT_EQ(64_MB - 32_KB - 1_B, header_info.smptbl.region_high_boundary[1]);
|
||||
EXPECT_EQ(1 << (3 - 1), header_info.smptbl.region_erase_types_bitfld[1]);
|
||||
|
||||
// Region 2: erase type 1 (4KB erase)
|
||||
// Range: last 32 KB
|
||||
EXPECT_EQ(32_KB, header_info.smptbl.region_size[2]);
|
||||
EXPECT_EQ(64_MB - 1_B, header_info.smptbl.region_high_boundary[2]);
|
||||
EXPECT_EQ(1 << (1 - 1), header_info.smptbl.region_erase_types_bitfld[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* When a Sector Map Parameter Table has multiple configuration detection
|
||||
* commands and sector maps, but one of the detection commands returns
|
||||
* an error (e.g. due to a bus fault).
|
||||
*/
|
||||
TEST_F(TestSFDP, TestConfigDetectCmdFailure)
|
||||
{
|
||||
mbed::sfdp_hdr_info header_info;
|
||||
set_sector_map_param_table(
|
||||
header_info.smptbl,
|
||||
sector_map_multiple_descriptors,
|
||||
sizeof(sector_map_multiple_descriptors)
|
||||
);
|
||||
|
||||
// First call: get all detection command and sector map descriptors
|
||||
Expectation call_1 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(
|
||||
sector_map_start_addr,
|
||||
mbed::SFDP_READ_CMD_ADDR_TYPE,
|
||||
mbed::SFDP_READ_CMD_INST,
|
||||
mbed::SFDP_READ_CMD_DUMMY_CYCLES,
|
||||
_,
|
||||
sizeof(sector_map_multiple_descriptors)
|
||||
)
|
||||
).Times(1).WillOnce(Return(0));
|
||||
|
||||
// Second call: detect bit-0 of configuration
|
||||
Expectation call_2 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(register_CR3NV, mbed::SFDP_CMD_ADDR_SIZE_VARIABLE, 0x65, mbed::SFDP_CMD_DUMMY_CYCLES_VARIABLE, _, 1)
|
||||
).Times(1).After(call_1).WillOnce(Return(0));
|
||||
|
||||
// Third call: detect bit-1 of configuration (failed)
|
||||
Expectation call_3 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(register_CR1NV, mbed::SFDP_CMD_ADDR_SIZE_VARIABLE, 0x65, mbed::SFDP_CMD_DUMMY_CYCLES_VARIABLE, _, 1)
|
||||
).Times(1).After(call_2).WillOnce(Return(-1)); // Emulate command failure
|
||||
|
||||
// No further calls after failure
|
||||
Expectation call_4 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(_, _, _, _, _, _)
|
||||
).Times(0).After(call_3);
|
||||
|
||||
EXPECT_EQ(-1, sfdp_parse_sector_map_table(sfdp_reader_callback, header_info));
|
||||
}
|
||||
|
||||
/**
|
||||
* When a Sector Map Parameter Table has multiple configuration detection
|
||||
* commands and sector maps, but no detection command is declared as the
|
||||
* last command.
|
||||
* Note: This means either reading went wrong, or the SFDP data is inconsistent
|
||||
* possibly due to hardware manufactured with wrong data. When the latter happens in
|
||||
* practice, the solution is to let the block device apply a device-specific quirk
|
||||
* and supply "corrected" SFDP data in its callback.
|
||||
*/
|
||||
TEST_F(TestSFDP, TestConfigIncompleteDetectCommands)
|
||||
{
|
||||
const uint8_t table_incomplete_detect_commands[] = {
|
||||
// Detect 1
|
||||
0xFC, 0x65, 0xFF, 0x08,
|
||||
0x04, 0x00, 0x00, 0x00,
|
||||
|
||||
// Detect 2
|
||||
0xFC, 0x65, 0xFF, 0x04,
|
||||
0x02, 0x00, 0x00, 0x00,
|
||||
|
||||
// Detect 3
|
||||
// Removed to trigger a parsing error
|
||||
|
||||
// Config 1
|
||||
0xFE, 0x01, 0x02, 0xFF, // header
|
||||
0xF1, 0x7F, 0x00, 0x00, // region 0
|
||||
0xF4, 0x7F, 0x03, 0x00, // region 1
|
||||
0xF4, 0xFF, 0xFB, 0x03, // region 2
|
||||
|
||||
// No Config 2
|
||||
|
||||
// Config 3
|
||||
0xFE, 0x03, 0x02, 0xFF, // header
|
||||
0xF4, 0xFF, 0xFB, 0x03, // region 0
|
||||
0xF4, 0x7F, 0x03, 0x00, // region 1
|
||||
0xF1, 0x7F, 0x00, 0x00, // region 2
|
||||
|
||||
// Config 4
|
||||
0xFF, 0x05, 0x00, 0xFF, // header
|
||||
0xF4, 0xFF, 0xFF, 0x03 // region 0
|
||||
};
|
||||
|
||||
mbed::sfdp_hdr_info header_info;
|
||||
set_sector_map_param_table(
|
||||
header_info.smptbl,
|
||||
table_incomplete_detect_commands,
|
||||
sizeof(table_incomplete_detect_commands)
|
||||
);
|
||||
|
||||
// First call: get all detection command and sector map descriptors
|
||||
Expectation call_1 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(
|
||||
sector_map_start_addr,
|
||||
mbed::SFDP_READ_CMD_ADDR_TYPE,
|
||||
mbed::SFDP_READ_CMD_INST,
|
||||
mbed::SFDP_READ_CMD_DUMMY_CYCLES,
|
||||
_,
|
||||
sizeof(table_incomplete_detect_commands)
|
||||
)
|
||||
).Times(1).WillOnce(Return(0));
|
||||
|
||||
// Second call: detect bit-0 of configuration
|
||||
Expectation call_2 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(register_CR3NV, mbed::SFDP_CMD_ADDR_SIZE_VARIABLE, 0x65, mbed::SFDP_CMD_DUMMY_CYCLES_VARIABLE, _, 1)
|
||||
).Times(1).After(call_1).WillOnce(Return(0));
|
||||
|
||||
// Third call: detect bit-1 of configuration
|
||||
Expectation call_3 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(register_CR1NV, mbed::SFDP_CMD_ADDR_SIZE_VARIABLE, 0x65, mbed::SFDP_CMD_DUMMY_CYCLES_VARIABLE, _, 1)
|
||||
).Times(1).After(call_2).WillOnce(Return(0));
|
||||
|
||||
// No further calls - incomplete detect command
|
||||
Expectation call_4 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(_, _, _, _, _, _)
|
||||
).Times(0).After(call_3);
|
||||
|
||||
EXPECT_EQ(-1, sfdp_parse_sector_map_table(sfdp_reader_callback, header_info));
|
||||
}
|
||||
|
||||
/**
|
||||
* When a Sector Map Parameter Table has multiple configuration detection
|
||||
* commands and sector maps, but no sector map matches the active
|
||||
* configuration.
|
||||
* Note: This means either detection went wrong, or the SFDP data is inconsistent
|
||||
* possibly due to hardware manufactured with wrong data. When the latter happens in
|
||||
* practice, the solution is to let the block device apply a device-specific quirk
|
||||
* and supply "corrected" SFDP data in its callback.
|
||||
*/
|
||||
TEST_F(TestSFDP, TestConfigNoMatchingSectorMap)
|
||||
{
|
||||
const uint8_t table_no_matching_sector_map[] = {
|
||||
// Detect 1
|
||||
0xFC, 0x65, 0xFF, 0x08,
|
||||
0x04, 0x00, 0x00, 0x00,
|
||||
|
||||
// Detect 2
|
||||
0xFC, 0x65, 0xFF, 0x04,
|
||||
0x02, 0x00, 0x00, 0x00,
|
||||
|
||||
// Detect 3
|
||||
0xFD, 0x65, 0xFF, 0x02,
|
||||
0x04, 0x00, 0x00, 0x00,
|
||||
|
||||
// Config 1
|
||||
0xFE, 0x01, 0x02, 0xFF, // header
|
||||
0xF1, 0x7F, 0x00, 0x00, // region 0
|
||||
0xF4, 0x7F, 0x03, 0x00, // region 1
|
||||
0xF4, 0xFF, 0xFB, 0x03, // region 2
|
||||
|
||||
// No Config 2
|
||||
|
||||
// Config 3
|
||||
// The active configuration (for test purpose) is 0x03 which should match header[1],
|
||||
// but we change the latter to 0x02 to trigger a parsing error.
|
||||
0xFE, 0x02, 0x02, 0xFF, // header
|
||||
0xF4, 0xFF, 0xFB, 0x03, // region 0
|
||||
0xF4, 0x7F, 0x03, 0x00, // region 1
|
||||
0xF1, 0x7F, 0x00, 0x00, // region 2
|
||||
|
||||
// Config 4
|
||||
0xFF, 0x05, 0x00, 0xFF, // header
|
||||
0xF4, 0xFF, 0xFF, 0x03 // region 0
|
||||
};
|
||||
|
||||
mbed::sfdp_hdr_info header_info;
|
||||
set_sector_map_param_table(
|
||||
header_info.smptbl,
|
||||
table_no_matching_sector_map,
|
||||
sizeof(table_no_matching_sector_map)
|
||||
);
|
||||
|
||||
// First call: get all detection command and sector map descriptors
|
||||
Expectation call_1 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(
|
||||
sector_map_start_addr,
|
||||
mbed::SFDP_READ_CMD_ADDR_TYPE,
|
||||
mbed::SFDP_READ_CMD_INST,
|
||||
mbed::SFDP_READ_CMD_DUMMY_CYCLES,
|
||||
_,
|
||||
sizeof(table_no_matching_sector_map)
|
||||
)
|
||||
).Times(1).WillOnce(Return(0));
|
||||
|
||||
// Second call: detect bit-0 of configuration
|
||||
Expectation call_2 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(register_CR3NV, mbed::SFDP_CMD_ADDR_SIZE_VARIABLE, 0x65, mbed::SFDP_CMD_DUMMY_CYCLES_VARIABLE, _, 1)
|
||||
).Times(1).After(call_1).WillOnce(Return(0));
|
||||
|
||||
// Third call: detect bit-1 of configuration
|
||||
Expectation call_3 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(register_CR1NV, mbed::SFDP_CMD_ADDR_SIZE_VARIABLE, 0x65, mbed::SFDP_CMD_DUMMY_CYCLES_VARIABLE, _, 1)
|
||||
).Times(1).After(call_2).WillOnce(Return(0));
|
||||
|
||||
// Fourth call: detect bit-2 of configuration
|
||||
Expectation call_4 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(register_CR3NV, mbed::SFDP_CMD_ADDR_SIZE_VARIABLE, 0x65, mbed::SFDP_CMD_DUMMY_CYCLES_VARIABLE, _, 1)
|
||||
).Times(1).After(call_3).WillOnce(Return(0));
|
||||
|
||||
// Failed to find a sector map for the active configuration.
|
||||
EXPECT_EQ(-1, sfdp_parse_sector_map_table(sfdp_reader_callback, header_info));
|
||||
}
|
||||
|
|
|
@ -7849,12 +7849,8 @@
|
|||
"CYW43XXX",
|
||||
"UDB_SDIO_P12"
|
||||
],
|
||||
"components_remove": [
|
||||
"QSPIF"
|
||||
],
|
||||
"device_has_remove": [
|
||||
"ANALOGOUT",
|
||||
"QSPI"
|
||||
"ANALOGOUT"
|
||||
],
|
||||
"extra_labels_add": [
|
||||
"PSOC6_01",
|
||||
|
|
Loading…
Reference in New Issue