From 9920e63d98b1a870cb15b067cf22fd42a6a94265 Mon Sep 17 00:00:00 2001 From: Offir Kochalsky Date: Mon, 27 Aug 2018 13:02:17 +0300 Subject: [PATCH 01/12] Initial QSPIF delivery to mbed-os --- .../COMPONENT_QSPIF/QSPIFBlockDevice.cpp | 1321 +++++++++++++++++ .../COMPONENT_QSPIF/QSPIFBlockDevice.h | 309 ++++ .../blockdevice/COMPONENT_QSPIF/README.md | 46 + .../TESTS/block_device/qspif/main.cpp | 302 ++++ .../blockdevice/COMPONENT_QSPIF/mbed_lib.json | 20 + 5 files changed, 1998 insertions(+) create mode 100644 components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.cpp create mode 100644 components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h create mode 100644 components/storage/blockdevice/COMPONENT_QSPIF/README.md create mode 100644 components/storage/blockdevice/COMPONENT_QSPIF/TESTS/block_device/qspif/main.cpp create mode 100644 components/storage/blockdevice/COMPONENT_QSPIF/mbed_lib.json diff --git a/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.cpp b/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.cpp new file mode 100644 index 0000000000..188b82cab4 --- /dev/null +++ b/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.cpp @@ -0,0 +1,1321 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018 ARM Limited + * + * 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 "QSPIFBlockDevice.h" +#include +#include "mbed_wait_api.h" + +#include "mbed_trace.h" +#define TRACE_GROUP "QSPIF" +using namespace mbed; + +/* Default QSPIF Parameters */ +/****************************/ +#define QSPIF_DEFAULT_READ_SIZE 1 +#define QSPIF_DEFAULT_PROG_SIZE 1 +#define QSPIF_DEFAULT_PAGE_SIZE 256 +#define QSPIF_DEFAULT_SE_SIZE 4096 +#define QSPI_MAX_STATUS_REGISTER_SIZE 2 +#ifndef UINT64_MAX +#define UINT64_MAX -1 +#endif +#define QSPI_NO_ADDRESS_COMMAND UINT64_MAX +// Status Register Bits +#define QSPIF_STATUS_BIT_WIP 0x1 //Write In Progress +#define QSPIF_STATUS_BIT_WEL 0x2 // Write Enable Latch + + +/* SFDP Header Parsing */ +/***********************/ +#define QSPIF_SFDP_HEADER_SIZE 8 +#define QSPIF_PARAM_HEADER_SIZE 8 + +/* Basic Parameters Table Parsing */ +/**********************************/ +#define SFDP_DEFAULT_BASIC_PARAMS_TABLE_SIZE_BYTES 64 /* 16 DWORDS */ +//READ Instruction support according to BUS Configuration +#define QSPIF_BASIC_PARAM_TABLE_FAST_READ_SUPPORT_BYTE 2 +#define QSPIF_BASIC_PARAM_TABLE_QPI_READ_SUPPORT_BYTE 16 +#define QSPIF_BASIC_PARAM_TABLE_444_READ_INST_BYTE 27 +#define QSPIF_BASIC_PARAM_TABLE_144_READ_INST_BYTE 9 +#define QSPIF_BASIC_PARAM_TABLE_114_READ_INST_BYTE 11 +#define QSPIF_BASIC_PARAM_TABLE_222_READ_INST_BYTE 23 +#define QSPIF_BASIC_PARAM_TABLE_122_READ_INST_BYTE 15 +#define QSPIF_BASIC_PARAM_TABLE_112_READ_INST_BYTE 13 +#define QSPIF_BASIC_PARAM_TABLE_PAGE_SIZE_BYTE 40 +// Quad Enable Params +#define QSPIF_BASIC_PARAM_TABLE_QER_BYTE 58 +#define QSPIF_BASIC_PARAM_TABLE_444_MODE_EN_SEQ_BYTE 56 +// Erase Types Params +#define QSPIF_BASIC_PARAM_ERASE_TYPE_1_BYTE 29 +#define QSPIF_BASIC_PARAM_ERASE_TYPE_2_BYTE 31 +#define QSPIF_BASIC_PARAM_ERASE_TYPE_3_BYTE 33 +#define QSPIF_BASIC_PARAM_ERASE_TYPE_4_BYTE 35 +#define QSPIF_BASIC_PARAM_ERASE_TYPE_1_SIZE_BYTE 28 +#define QSPIF_BASIC_PARAM_ERASE_TYPE_2_SIZE_BYTE 30 +#define QSPIF_BASIC_PARAM_ERASE_TYPE_3_SIZE_BYTE 32 +#define QSPIF_BASIC_PARAM_ERASE_TYPE_4_SIZE_BYTE 34 +#define QSPIF_BASIC_PARAM_4K_ERASE_TYPE_BYTE 1 + +// Erase Types Per Region BitMask +#define ERASE_BITMASK_TYPE4 0x08 +#define ERASE_BITMASK_TYPE1 0x01 +#define ERASE_BITMASK_NONE 0x00 +#define ERASE_BITMASK_ALL 0x0F + +#define IS_MEM_READY_MAX_RETRIES 10000 + +enum qspif_default_instructions { + QSPIF_NOP = 0x00, // No operation + QSPIF_PP = 0x02, // Page Program data + QSPIF_READ = 0x03, // Read data + QSPIF_SE = 0x20, // 4KB Sector Erase + QSPIF_SFDP = 0x5a, // Read SFDP + QSPIF_WRSR = 0x01, // Write Status/Configuration Register + QSPIF_WRDI = 0x04, // Write Disable + QSPIF_RDSR = 0x05, // Read Status Register + QSPIF_WREN = 0x06, // Write Enable + QSPIF_RSTEN = 0x66, // Reset Enable + QSPIF_RST = 0x99, // Reset + QSPIF_RDID = 0x9f, // Read Manufacturer and JDEC Device ID +}; + +// Local Function +static int local_math_power(int base, int exp); + +/* Init function to initialize Different Devices CS static list */ +static PinName *generate_initialized_active_qspif_csel_arr(); +// Static Members for different devices csel +// _devices_mutex is used to lock csel list - only one QSPIFBlockDevice instance per csel is allowed +SingletonPtr QSPIFBlockDevice::_devices_mutex; +int QSPIFBlockDevice::_number_of_active_qspif_flash_csel = 0; +PinName *QSPIFBlockDevice::_active_qspif_flash_csel_arr = generate_initialized_active_qspif_csel_arr(); + +/********* Public API Functions *********/ +/****************************************/ +QSPIFBlockDevice::QSPIFBlockDevice(PinName io0, PinName io1, PinName io2, PinName io3, PinName sclk, PinName csel, + qspif_polarity_mode clock_mode, int freq) + : _qspi(io0, io1, io2, io3, sclk, csel, clock_mode), _csel(csel), _freq(freq), _device_size_bytes(0), + _init_ref_count(0), + _is_initialized(false) +{ + _unique_device_status = add_new_csel_instance(csel); + if (_unique_device_status == 0) { + tr_info("INFO: Adding a new QSPIFBlockDevice csel: %d", (int)csel); + } else if (_unique_device_status == -1) { + tr_error("ERROR: QSPIFBlockDevice with the same csel(%d) already exists", (int)csel); + } else { + tr_error("ERROR: Too many different QSPIFBlockDevice devices - max allowed: %d", QSPIF_MAX_ACTIVE_FLASH_DEVICES); + } +} + +int QSPIFBlockDevice::init() +{ + if (_unique_device_status == 0) { + tr_debug("DEBUG: QSPIFBlockDevice csel: %d", (int)_csel); + } else if (_unique_device_status == -1) { + tr_error("ERROR: QSPIFBlockDevice with the same csel(%d) already exists", (int)_csel); + return QSPIF_BD_ERROR_DEVICE_NOT_UNIQE; + } else { + tr_error("ERROR: Too many different QSPIFBlockDevice devices - max allowed: %d", QSPIF_MAX_ACTIVE_FLASH_DEVICES); + return QSPIF_BD_ERROR_DEVICE_MAX_EXCEED; + } + + uint8_t vendor_device_ids[4]; + size_t data_length = 3; + int status = QSPIF_BD_ERROR_OK; + uint32_t basic_table_addr = 0; + size_t basic_table_size = 0; + uint32_t sector_map_table_addr = 0; + size_t sector_map_table_size = 0; + int qspi_status = QSPI_STATUS_OK; + + _mutex.lock(); + + if (!_is_initialized) { + _init_ref_count = 0; + } + + _init_ref_count++; + + if (_init_ref_count != 1) { + goto exit_point; + } + + //Initialize parameters + _min_common_erase_size = 0; + _regions_count = 1; + _region_erase_types_bitfield[0] = ERASE_BITMASK_NONE; + + //Default Bus Setup 1_1_1 with 0 dummy and mode cycles + _inst_width = QSPI_CFG_BUS_SINGLE; + _address_width = QSPI_CFG_BUS_SINGLE; + _address_size = QSPI_CFG_ADDR_SIZE_24; + _data_width = QSPI_CFG_BUS_SINGLE; + _dummy_and_mode_cycles = 0; + + if (QSPI_STATUS_OK != _qspi_set_frequency(_freq)) { + tr_error("ERROR: QSPI Set Frequency Failed"); + status = QSPIF_BD_ERROR_DEVICE_ERROR; + goto exit_point; + } + + // Soft Reset + if ( -1 == _reset_flash_mem()) { + tr_error("ERROR: init - Unable to initialize flash memory, tests failed\n"); + status = QSPIF_BD_ERROR_DEVICE_ERROR; + goto exit_point; + } else { + tr_info("INFO: Initialize flash memory OK\n"); + } + + /* Read Manufacturer ID (1byte), and Device ID (2bytes)*/ + qspi_status = _qspi_send_general_command(QSPIF_RDID, QSPI_NO_ADDRESS_COMMAND, NULL, 0, (char *)vendor_device_ids, + data_length); + if (qspi_status != QSPI_STATUS_OK) { + tr_error("ERROR: init - Read Vendor ID Failed"); + status = QSPIF_BD_ERROR_DEVICE_ERROR; + goto exit_point; + } + + switch (vendor_device_ids[0]) { + case 0xbf: + // SST devices come preset with block protection + // enabled for some regions, issue write disable instruction to clear + _set_write_enable(); + _qspi_send_general_command(QSPIF_WRDI, QSPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0); + break; + } + + //Synchronize Device + if ( false == _is_mem_ready()) { + tr_error("ERROR: init - _is_mem_ready Failed"); + status = QSPIF_BD_ERROR_READY_FAILED; + goto exit_point; + } + + /**************************** Parse SFDP Header ***********************************/ + if ( 0 != _sfdp_parse_sfdp_headers(basic_table_addr, basic_table_size, sector_map_table_addr, sector_map_table_size)) { + tr_error("ERROR: init - Parse SFDP Headers Failed"); + status = QSPIF_BD_ERROR_PARSING_FAILED; + goto exit_point; + } + + + /**************************** Parse Basic Parameters Table ***********************************/ + if ( 0 != _sfdp_parse_basic_param_table(basic_table_addr, basic_table_size) ) { + tr_error("ERROR: init - Parse Basic Param Table Failed"); + status = QSPIF_BD_ERROR_PARSING_FAILED; + goto exit_point; + } + + /**************************** Parse Sector Map Table ***********************************/ + _region_size_bytes[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; + + if ( (sector_map_table_addr != 0) && (0 != sector_map_table_size) ) { + tr_info("INFO: init - Parsing Sector Map Table - addr: 0x%lxh, Size: %d", sector_map_table_addr, + sector_map_table_size); + if (0 != _sfdp_parse_sector_map_table(sector_map_table_addr, sector_map_table_size) ) { + tr_error("ERROR: init - Parse Sector Map Table Failed"); + status = QSPIF_BD_ERROR_PARSING_FAILED; + goto exit_point; + } + } + + // Configure BUS Mode to 1_1_1 for all commands other than Read + _qspi_configure_format(QSPI_CFG_BUS_SINGLE, QSPI_CFG_BUS_SINGLE, QSPI_CFG_ADDR_SIZE_24, QSPI_CFG_BUS_SINGLE, + QSPI_CFG_ALT_SIZE_8, QSPI_CFG_BUS_SINGLE, 0); + + _is_initialized = true; + +exit_point: + _mutex.unlock(); + + return status; +} + +int QSPIFBlockDevice::deinit() +{ + int result = QSPIF_BD_ERROR_OK; + + _mutex.lock(); + + if (!_is_initialized) { + _init_ref_count = 0; + _mutex.unlock(); + return result; + } + + _init_ref_count--; + + if (_init_ref_count) { + _mutex.unlock(); + return result; + } + + // Disable Device for Writing + qspi_status_t status = _qspi_send_general_command(QSPIF_WRDI, QSPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0); + if (status != QSPI_STATUS_OK) { + tr_error("ERROR: Write Disable failed"); + result = QSPIF_BD_ERROR_DEVICE_ERROR; + } + + _is_initialized = false; + + _mutex.unlock(); + + if (_unique_device_status == 0) { + remove_csel_instance(_csel); + } + + return result; +} + +int QSPIFBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size) +{ + + + int status = QSPIF_BD_ERROR_OK; + + tr_info("INFO Inst: 0x%xh", _read_instruction); + + _mutex.lock(); + + // Configure Bus for Reading + _qspi_configure_format(_inst_width, _address_width, _address_size, QSPI_CFG_BUS_SINGLE, + QSPI_CFG_ALT_SIZE_8, _data_width, _dummy_and_mode_cycles); + + if (QSPI_STATUS_OK != _qspi_send_read_command(_read_instruction, buffer, addr, size)) { + status = QSPIF_BD_ERROR_DEVICE_ERROR; + tr_error("ERROR: Read failed\n"); + } + + // All commands other than Read use default 1-1-1 Bus mode (Program/Erase are constrained by flash memory performance less than that of the bus) + _qspi_configure_format(QSPI_CFG_BUS_SINGLE, QSPI_CFG_BUS_SINGLE, QSPI_CFG_ADDR_SIZE_24, QSPI_CFG_BUS_SINGLE, + QSPI_CFG_ALT_SIZE_8, QSPI_CFG_BUS_SINGLE, 0); + + _mutex.unlock(); + return status; + +} + +int QSPIFBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size) +{ + qspi_status_t result = QSPI_STATUS_OK; + bool program_failed = false; + int status = QSPIF_BD_ERROR_OK; + uint32_t offset = 0; + uint32_t chunk = 0; + bd_size_t written_bytes = 0; + + tr_debug("DEBUG: program - Buff: 0x%lxh, addr: %llu, size: %llu", (uint32_t)buffer, addr, size); + + while (size > 0) { + + // Write on _page_size_bytes boundaries (Default 256 bytes a page) + offset = addr % _page_size_bytes; + chunk = (offset + size < _page_size_bytes) ? size : (_page_size_bytes - offset); + written_bytes = chunk; + + _mutex.lock(); + + //Send WREN + if (_set_write_enable() != 0) { + tr_error("ERROR: Write Enabe failed\n"); + program_failed = true; + status = QSPIF_BD_ERROR_WREN_FAILED; + goto exit_point; + } + + result = _qspi_send_program_command(_prog_instruction, buffer, addr, &written_bytes); + if ( (result != QSPI_STATUS_OK) || (chunk != written_bytes) ) { + tr_error("ERROR: Write failed"); + program_failed = true; + status = QSPIF_BD_ERROR_DEVICE_ERROR; + goto exit_point; + } + + buffer = static_cast(buffer) + chunk; + addr += chunk; + size -= chunk; + + if ( false == _is_mem_ready()) { + tr_error("ERROR: Device not ready after write, failed\n"); + program_failed = true; + status = QSPIF_BD_ERROR_READY_FAILED; + goto exit_point; + } + _mutex.unlock(); + } + +exit_point: + if (program_failed) { + _mutex.unlock(); + } + + return status; +} + +int QSPIFBlockDevice::erase(bd_addr_t addr, bd_size_t in_size) +{ + int type = 0; + uint32_t chunk = 4096; + unsigned int cur_erase_inst = _erase_instruction; + int size = (int)in_size; + bool erase_failed = false; + int status = QSPIF_BD_ERROR_OK; + // Find region of erased address + int region = _utils_find_addr_region(addr); + // Erase Types of selected region + uint8_t bitfield = _region_erase_types_bitfield[region]; + + tr_debug("DEBUG: erase - addr: %llu, in_size: %llu", addr, in_size); + + // For each iteration erase the largest section supported by current region + while (size > 0) { + + // 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, (int)addr, _region_high_boundary[region]); + cur_erase_inst = _erase_type_inst_arr[type]; + chunk = _erase_type_size_arr[type]; + + tr_debug("DEBUG: erase - addr: %llu, size:%d, Inst: 0x%xh, chunk: %lu , ", + addr, size, cur_erase_inst, chunk); + tr_debug("DEBUG: erase - Region: %d, Type:%d", + region, type); + + _mutex.lock(); + + if (_set_write_enable() != 0) { + tr_error("ERROR: QSPI Erase Device not ready - failed"); + erase_failed = true; + status = QSPIF_BD_ERROR_READY_FAILED; + goto exit_point; + } + + if (QSPI_STATUS_OK != _qspi_send_erase_command(cur_erase_inst, addr, size) ) { + tr_error("ERROR: QSPI Erase command failed!"); + erase_failed = true; + status = QSPIF_BD_ERROR_DEVICE_ERROR; + goto exit_point; + } + + addr += chunk; + size -= chunk; + + if ( (size > 0) && (addr > _region_high_boundary[region]) ) { + // erase crossed to next region + region++; + bitfield = _region_erase_types_bitfield[region]; + } + + if ( false == _is_mem_ready()) { + tr_error("ERROR: QSPI After Erase Device not ready - failed\n"); + erase_failed = true; + status = QSPIF_BD_ERROR_READY_FAILED; + goto exit_point; + } + + _mutex.unlock(); + } + +exit_point: + if (erase_failed) { + _mutex.unlock(); + } + + return status; +} + +bd_size_t QSPIFBlockDevice::get_read_size() const +{ + // Assuming all devices support 1byte read granularity + return QSPIF_DEFAULT_READ_SIZE; +} + +bd_size_t QSPIFBlockDevice::get_program_size() const +{ + // Assuming all devices support 1byte program granularity + return QSPIF_DEFAULT_PROG_SIZE; +} + +bd_size_t QSPIFBlockDevice::get_erase_size() const +{ + // return minimal erase size supported by all regions (0 if none exists) + return _min_common_erase_size; +} + +// Find minimal erase size supported by the region to which the address belongs to +bd_size_t QSPIFBlockDevice::get_erase_size(bd_addr_t addr) +{ + // Find region of current address + int region = _utils_find_addr_region(addr); + + int min_region_erase_size = _min_common_erase_size; + int8_t type_mask = ERASE_BITMASK_TYPE1; + int i_ind = 0; + + + if (region != -1) { + type_mask = 0x01; + + 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) { + + min_region_erase_size = _erase_type_size_arr[i_ind]; + break; + } + type_mask = type_mask << 1; + } + + if (i_ind == 4) { + tr_error("ERROR: no erase type was found for region addr"); + } + } + + return (bd_size_t)min_region_erase_size; +} + +bd_size_t QSPIFBlockDevice::size() const +{ + return _device_size_bytes; +} + +/********************************/ +/* Different Device Csel Mgmt */ +/********************************/ +static PinName *generate_initialized_active_qspif_csel_arr() +{ + PinName *init_arr = new PinName[QSPIF_MAX_ACTIVE_FLASH_DEVICES]; + for ( int i_ind = 0; i_ind < QSPIF_MAX_ACTIVE_FLASH_DEVICES; i_ind++ ) { + init_arr[i_ind] = NC; + } + return init_arr; +} + +int QSPIFBlockDevice::add_new_csel_instance(PinName csel) +{ + int status = 0; + _devices_mutex->lock(); + if (_number_of_active_qspif_flash_csel >= QSPIF_MAX_ACTIVE_FLASH_DEVICES ) { + status = -2; + goto exit_point; + } + + // verify the device is unique(no identical csel already exists) + for ( int i_ind = 0; i_ind < QSPIF_MAX_ACTIVE_FLASH_DEVICES; i_ind++ ) { + if (_active_qspif_flash_csel_arr[i_ind] == csel) { + status = -1; + goto exit_point; + } + } + + // Insert new csel into existing device list + for ( int i_ind = 0; i_ind < QSPIF_MAX_ACTIVE_FLASH_DEVICES; i_ind++ ) { + if (_active_qspif_flash_csel_arr[i_ind] == NC) { + _active_qspif_flash_csel_arr[i_ind] = csel; + break; + } + } + _number_of_active_qspif_flash_csel++; + +exit_point: + _devices_mutex->unlock(); + return status; +} + +int QSPIFBlockDevice::remove_csel_instance(PinName csel) +{ + int status = -1; + _devices_mutex->lock(); + // remove the csel from existing device list + for ( int i_ind = 0; i_ind < QSPIF_MAX_ACTIVE_FLASH_DEVICES; i_ind++ ) { + if (_active_qspif_flash_csel_arr[i_ind] == csel) { + _active_qspif_flash_csel_arr[i_ind] = NC; + if (_number_of_active_qspif_flash_csel > 0) { + _number_of_active_qspif_flash_csel--; + } + status = 0; + break; + } + } + _devices_mutex->unlock(); + return status; +} + +/*********************************************************/ +/********** SFDP Parsing and Detection Functions *********/ +/*********************************************************/ +int QSPIFBlockDevice::_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 = ERASE_BITMASK_ALL; + + + qspi_status_t status = _qspi_send_read_command(QSPIF_SFDP, (char *)sector_map_table, sector_map_table_addr /*address*/, + sector_map_table_size); + if (status != QSPI_STATUS_OK) { + tr_error("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("ERROR: Sector Map - Supporting Only Single! Map Descriptor (not map commands)"); + return -1; + } + + _regions_count = sector_map_table[2] + 1; + if (_regions_count > QSPIF_MAX_REGIONS) { + tr_error("ERROR: Supporting up to %d regions, current setup to %d regions - fail", + QSPIF_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 *)§or_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 = 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 QSPIFBlockDevice::_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 */ + + qspi_status_t status = _qspi_send_read_command(QSPIF_SFDP, (char *)param_table, basic_table_addr /*address*/, + basic_table_size); + if (status != QSPI_STATUS_OK) { + tr_error("ERROR: init - Read SFDP First Table Failed"); + return -1; + } + + // Check address size, currently only supports 3byte addresses + if ((param_table[2] & 0x4) != 0 || (param_table[7] & 0x80) != 0) { + tr_error("ERROR: init - verify 3byte addressing Failed"); + return -1; + } + + // Get device density (stored in bits - 1) + uint32_t density_bits = ( + (param_table[7] << 24) | + (param_table[6] << 16) | + (param_table[5] << 8 ) | + param_table[4] ); + _device_size_bytes = (density_bits + 1) / 8; + + // Set Default read/program/erase Instructions + _read_instruction = QSPIF_READ; + _prog_instruction = QSPIF_PP; + _erase_instruction = QSPIF_SE; + + _erase_instruction = _erase4k_inst; + + // Set Page Size (QSPI write must be done on Page limits) + _page_size_bytes = _sfdp_detect_page_size(param_table, basic_table_size); + + // Detect and Set Erase Types + bool shouldSetQuadEnable = false; + bool is_qpi_mode = false; + _sfdp_detect_erase_types_inst_and_size(param_table, basic_table_size, _erase4k_inst, _erase_type_inst_arr, + _erase_type_size_arr); + _erase_instruction = _erase4k_inst; + + + // Detect and Set fastest Bus mode (default 1-1-1) + _sfdp_detect_best_bus_read_mode(param_table, basic_table_size, shouldSetQuadEnable, is_qpi_mode, _read_instruction); + + if (true == shouldSetQuadEnable) { + // Set Quad Enable and QPI Bus modes if Supported + tr_info("INFO: init - Setting Quad Enable"); + if (0 != _sfdp_set_quad_enabled(param_table)) { + tr_error("ERROR: Device supports Quad bus, but Quad Enable Failed"); + return -1; + } + if (true == is_qpi_mode) { + tr_info("INFO: init - Setting QPI mode"); + _sfdp_set_qpi_enabled(param_table); + } + } + return 0; +} + +int QSPIFBlockDevice::_sfdp_parse_sfdp_headers(uint32_t& basic_table_addr, size_t& basic_table_size, + uint32_t& sector_map_table_addr, size_t& sector_map_table_size) +{ + uint8_t sfdp_header[QSPIF_SFDP_HEADER_SIZE]; + uint8_t param_header[QSPIF_PARAM_HEADER_SIZE]; + size_t data_length = QSPIF_SFDP_HEADER_SIZE; + bd_addr_t addr = 0x0; + + // Set 1-1-1 bus mode for SFDP header parsing + _qspi_configure_format(QSPI_CFG_BUS_SINGLE, QSPI_CFG_BUS_SINGLE, QSPI_CFG_ADDR_SIZE_24, QSPI_CFG_BUS_SINGLE, + QSPI_CFG_ALT_SIZE_8, QSPI_CFG_BUS_SINGLE, 8); + + qspi_status_t status = _qspi_send_read_command(QSPIF_SFDP, (char *)sfdp_header, addr /*address*/, data_length); + if (status != QSPI_STATUS_OK) { + tr_error("ERROR: init - Read SFDP Failed"); + return -1; + } + + // Verify SFDP signature for sanity + // Also check that major/minor version is acceptable + if (!(memcmp(&sfdp_header[0], "SFDP", 4) == 0 && sfdp_header[5] == 1)) { + tr_error("ERROR: init - _verify SFDP signature and version Failed"); + return -1; + } else { + tr_info("INFO: init - verified SFDP Signature and version Successfully"); + } + + // Discover Number of Parameter Headers + int number_of_param_headers = (int)(sfdp_header[6]) + 1; + tr_debug("DEBUG: number of Param Headers: %d", number_of_param_headers); + + + addr += QSPIF_SFDP_HEADER_SIZE; + data_length = QSPIF_PARAM_HEADER_SIZE; + + // Loop over Param Headers and parse them (currently supported Basic Param Table and Sector Region Map Table) + for (int i_ind = 0; i_ind < number_of_param_headers; i_ind++) { + + status = _qspi_send_read_command(QSPIF_SFDP, (char *)param_header, addr, data_length); + if (status != QSPI_STATUS_OK) { + tr_error("ERROR: init - Read Param Table %d Failed", i_ind + 1); + return -1; + } + + // The SFDP spec indicates the standard table is always at offset 0 + // in the parameter headers, we check just to be safe + if (param_header[2] != 1) { + tr_error("ERROR: Param Table %d - Major Version should be 1!", i_ind + 1); + return -1; + } + + if ((param_header[0] == 0) && (param_header[7] == 0xFF)) { + // Found Basic Params Table: LSB=0x00, MSB=0xFF + tr_debug("DEBUG: Found Basic Param Table at Table: %d", i_ind + 1); + basic_table_addr = ( (param_header[6] << 16) | (param_header[5] << 8) | (param_header[4]) ); + // Supporting up to 64 Bytes Table (16 DWORDS) + basic_table_size = ((param_header[3] * 4) < SFDP_DEFAULT_BASIC_PARAMS_TABLE_SIZE_BYTES) ? (param_header[3] * 4) : 64; + + } else if ((param_header[0] == 81) && (param_header[7] == 0xFF)) { + // Found Sector Map Table: LSB=0x81, MSB=0xFF + tr_debug("DEBUG: Found Sector Map Table at Table: %d", i_ind + 1); + sector_map_table_addr = ( (param_header[6] << 16) | (param_header[5] << 8) | (param_header[4]) ); + sector_map_table_size = param_header[3] * 4; + + } + addr += QSPIF_PARAM_HEADER_SIZE; + + } + return 0; +} + +int QSPIFBlockDevice::_sfdp_set_qpi_enabled(uint8_t *basic_param_table_ptr) +{ + uint8_t config_reg[1]; + + // QPI 4-4-4 Enable Procedure is specified in 5 Bits + uint8_t en_seq_444_value = ( ((basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_444_MODE_EN_SEQ_BYTE] & 0xF0) >> 4) | (( + basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_444_MODE_EN_SEQ_BYTE + 1] & 0x01) << 4 )); + + switch (en_seq_444_value) { + case 1: + case 2: + tr_debug("DEBUG: _setQPIEnabled - send command 38h"); + _qspi_send_general_command(0x38, QSPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0); + break; + + case 4: + tr_debug("DEBUG: _setQPIEnabled - send command 35h"); + _qspi_send_general_command(0x35, QSPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0); + break; + + case 8: + tr_debug("DEBUG: _setQPIEnabled - set config bit 6 and send command 71h"); + _qspi_send_general_command(0x65, 0x800003, NULL, 0, (char *)config_reg, 1); + config_reg[0] |= 0x40; //Set Bit 6 + _qspi_send_general_command(0x71, 0x800003, NULL, 0, (char *)config_reg, 1); + break; + + case 16: + tr_debug("DEBUG: _setQPIEnabled - reset config bits 0-7 and send command 61h"); + _qspi_send_general_command(0x65, QSPI_NO_ADDRESS_COMMAND, NULL, 0, (char *)config_reg, 1); + config_reg[0] &= 0x7F; //Reset Bit 7 of CR + _qspi_send_general_command(0x61, QSPI_NO_ADDRESS_COMMAND, NULL, 0, (char *)config_reg, 1); + break; + + default: + tr_warning("WARNING: _setQPIEnabled - Unsuported En Seq 444 configuration"); + break; + + } + return 0; +} + + + +int QSPIFBlockDevice::_sfdp_set_quad_enabled(uint8_t *basic_param_table_ptr) +{ + + int sr_read_size = QSPI_MAX_STATUS_REGISTER_SIZE; + int sr_write_size = QSPI_MAX_STATUS_REGISTER_SIZE; + + int status_reg_setup[QSPI_MAX_STATUS_REGISTER_SIZE] = {0}; + uint8_t status_reg[QSPI_MAX_STATUS_REGISTER_SIZE]; + unsigned int write_register_inst = QSPIF_WRSR; + unsigned int read_register_inst = QSPIF_RDSR; + + // QUAD Enable procedure is specified by 3 bits + uint8_t qer_value = (basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_QER_BYTE] & 0x70) >> 4; + + + switch (qer_value) { + case 0: + tr_debug("DEBUG: Device Does not Have a QE Bit, continue based on Read Inst"); + return 0; + + case 1: + case 4: + status_reg_setup[0] = 0; + status_reg_setup[1] = 0x02; //Bit 1 of Status Reg 2 + tr_debug("DEBUG: Setting QE Bit, Bit 1 of Status Reg 2"); + break; + + case 2: + status_reg_setup[0] = 0x40; // Bit 6 of Status Reg 1 + status_reg_setup[1] = 0; + sr_write_size = 1; + sr_read_size = 1; + tr_debug("DEBUG: Setting QE Bit, Bit 6 of Status Reg 1"); + break; + + case 3: + status_reg_setup[0] = 0x80; // Bit 7 of Status Reg 1 + status_reg_setup[1] = 0; + sr_write_size = 1; + sr_read_size = 1; + write_register_inst = 0x3E; + read_register_inst = 0x3F; + tr_debug("DEBUG: Setting QE Bit, Bit 7 of Status Reg 1"); + break; + case 5: + status_reg_setup[0] = 0; + status_reg_setup[1] = 0x02; //Bit 1 of status Reg 2 + read_register_inst = 0x35; + sr_read_size = 1; + tr_debug("DEBUG: Setting QE Bit, Bit 1 of Status Reg 2 -special read command"); + break; + default: + tr_warning("WARNING: _setQuadEnable - Unsuported QER configuration"); + break; + + + } + + // Read Status Register + if (QSPI_STATUS_OK == _qspi_send_general_command(read_register_inst, QSPI_NO_ADDRESS_COMMAND, NULL, 0, + (char *)status_reg, + sr_read_size) ) { // store received values in status_value + tr_debug("DEBUG: Reading Status Register Success: value = 0x%x\n", (int)status_reg[0]); + } else { + tr_error("ERROR: Reading Status Register failed"); + return -1; + } + + // Set Bits for Quad Enable + status_reg[0] |= status_reg_setup[0]; + status_reg[1] |= status_reg_setup[1]; + + // Write new Status Register Setup + if (_set_write_enable() != 0) { + tr_error("ERROR: Write Enabe failed\n"); + return -1; + } + + if (QSPI_STATUS_OK == _qspi_send_general_command(write_register_inst, QSPI_NO_ADDRESS_COMMAND, (char *)status_reg, + sr_write_size, NULL, + 0) ) { // Write QE to status_register + tr_debug("DEBUG: _setQuadEnable - Writing Status Register Success: value = 0x%x", + (int)status_reg[0]); + } else { + tr_error("ERROR: _setQuadEnable - Writing Status Register failed"); + return -1; + } + + if ( false == _is_mem_ready()) { + tr_error("ERROR: Device not ready after write, failed"); + return -1; + } + + + // For Debug + memset(status_reg, 0, QSPI_MAX_STATUS_REGISTER_SIZE); + if (QSPI_STATUS_OK == _qspi_send_general_command(read_register_inst, QSPI_NO_ADDRESS_COMMAND, NULL, 0, + (char *)status_reg, + sr_read_size) ) { // store received values in status_value + tr_debug("DEBUG: Reading Status Register Success: value = 0x%x\n", (int)status_reg[0]); + } else { + tr_error("ERROR: Reading Status Register failed"); + return -1; + } + + return 0; +} + +int QSPIFBlockDevice::_sfdp_detect_page_size(uint8_t *basic_param_table_ptr, int basic_param_table_size) +{ + unsigned int page_size = QSPIF_DEFAULT_PAGE_SIZE; + + if (basic_param_table_size > QSPIF_BASIC_PARAM_TABLE_PAGE_SIZE_BYTE) { + // Page Size is specified by 4 Bits (N), calculated by 2^N + int page_to_power_size = ( (int)basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_PAGE_SIZE_BYTE]) >> 4; + page_size = local_math_power(2, page_to_power_size); + tr_debug("DEBUG: Detected Page Size: %d", page_size); + } else { + tr_debug("DEBUG: Using Default Page Size: %d", page_size); + } + return page_size; +} + +int QSPIFBlockDevice::_sfdp_detect_erase_types_inst_and_size(uint8_t *basic_param_table_ptr, int basic_param_table_size, + unsigned int& erase4k_inst, + unsigned int *erase_type_inst_arr, unsigned int *erase_type_size_arr) +{ + erase4k_inst = 0xff; + bool found_4Kerase_type = false; + uint8_t bitfield = 0x01; + + // Erase 4K Inst is taken either from param table legacy 4K erase or superseded by erase Instruction for type of size 4K + erase4k_inst = basic_param_table_ptr[QSPIF_BASIC_PARAM_4K_ERASE_TYPE_BYTE]; + + if (basic_param_table_size > QSPIF_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[QSPIF_BASIC_PARAM_ERASE_TYPE_1_SIZE_BYTE + 2 * i_ind]); // Size given as 2^N + tr_info("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) { + // if size==1 type is not supported + erase_type_inst_arr[i_ind] = basic_param_table_ptr[QSPIF_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) ) { + //Set default minimal common erase for singal region + _min_common_erase_size = 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) { + found_4Kerase_type = true; + if (erase4k_inst != 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]; + tr_warning("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; + } + + tr_info("INFO: Erase Type %d - Inst: 0x%xh, Size: %d", (i_ind + 1), erase_type_inst_arr[i_ind], + erase_type_size_arr[i_ind]); + bitfield = bitfield << 1; + } + } + + if (false == found_4Kerase_type) { + tr_warning("WARNING: Couldn't find Erase Type for 4KB size"); + } + return 0; +} + +int QSPIFBlockDevice::_sfdp_detect_best_bus_read_mode(uint8_t *basic_param_table_ptr, int basic_param_table_size, + bool& set_quad_enable, + bool& is_qpi_mode, unsigned int& read_inst) +{ + set_quad_enable = false; + is_qpi_mode = false; + uint8_t examined_byte; + + do { // compound statement is the loop body + + + if (basic_param_table_size > QSPIF_BASIC_PARAM_TABLE_QPI_READ_SUPPORT_BYTE) { + + examined_byte = basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_QPI_READ_SUPPORT_BYTE]; + + if (examined_byte & 0x10) { + // QPI 4-4-4 Supported + read_inst = basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_444_READ_INST_BYTE]; + set_quad_enable = true; + is_qpi_mode = true; + _dummy_and_mode_cycles = (basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_444_READ_INST_BYTE - 1] >> 5) + + (basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_444_READ_INST_BYTE - 1] & 0x1F); + tr_debug("/nDEBUG: Read Bus Mode set to 4-4-4, Instruction: 0x%xh", _read_instruction); + //_inst_width = QSPI_CFG_BUS_QUAD; + _address_width = QSPI_CFG_BUS_QUAD; + _data_width = QSPI_CFG_BUS_QUAD; + + break; + } + } + + + examined_byte = basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_FAST_READ_SUPPORT_BYTE]; + if (examined_byte & 0x40) { + // Fast Read 1-4-4 Supported + read_inst = basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_144_READ_INST_BYTE]; + set_quad_enable = true; + // dummy cycles + mode cycles = Dummy Cycles + _dummy_and_mode_cycles = (basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_144_READ_INST_BYTE - 1] >> 5) + + (basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_144_READ_INST_BYTE - 1] & 0x1F); + _address_width = QSPI_CFG_BUS_QUAD; + _data_width = QSPI_CFG_BUS_QUAD; + tr_debug("/nDEBUG: Read Bus Mode set to 1-4-4, Instruction: 0x%xh", _read_instruction); + break; + } + if (examined_byte & 0x80) { + // Fast Read 1-1-4 Supported + read_inst = basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_114_READ_INST_BYTE]; + set_quad_enable = true; + _dummy_and_mode_cycles = (basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_114_READ_INST_BYTE - 1] >> 5) + + (basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_114_READ_INST_BYTE - 1] & 0x1F); + _data_width = QSPI_CFG_BUS_QUAD; + tr_debug("/nDEBUG: Read Bus Mode set to 1-1-4, Instruction: 0x%xh", _read_instruction); + break; + } + examined_byte = basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_QPI_READ_SUPPORT_BYTE]; + if (examined_byte & 0x01) { + // Fast Read 2-2-2 Supported + read_inst = basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_222_READ_INST_BYTE]; + _dummy_and_mode_cycles = (basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_222_READ_INST_BYTE - 1] >> 5) + + (basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_222_READ_INST_BYTE - 1] & 0x1F); + _address_width = QSPI_CFG_BUS_DUAL; + _data_width = QSPI_CFG_BUS_DUAL; + tr_info("/nINFO: Read Bus Mode set to 2-2-2, Instruction: 0x%xh", _read_instruction); + break; + } + + examined_byte = basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_FAST_READ_SUPPORT_BYTE]; + if (examined_byte & 0x20) { + // Fast Read 1-2-2 Supported + read_inst = basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_122_READ_INST_BYTE]; + _dummy_and_mode_cycles = (basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_122_READ_INST_BYTE - 1] >> 5) + + (basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_122_READ_INST_BYTE - 1] & 0x1F); + _address_width = QSPI_CFG_BUS_DUAL; + _data_width = QSPI_CFG_BUS_DUAL; + tr_debug("/nDEBUG: Read Bus Mode set to 1-2-2, Instruction: 0x%xh", _read_instruction); + break; + } + if (examined_byte & 0x01) { + // Fast Read 1-1-2 Supported + read_inst = basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_112_READ_INST_BYTE]; + _dummy_and_mode_cycles = (basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_112_READ_INST_BYTE - 1] >> 5) + + (basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_112_READ_INST_BYTE - 1] & 0x1F); + _data_width = QSPI_CFG_BUS_DUAL; + tr_debug("/nDEBUG: Read Bus Mode set to 1-1-2, Instruction: 0x%xh", _read_instruction); + break; + } + tr_debug("/nDEBUG: Read Bus Mode set to 1-1-1, Instruction: 0x%xh", _read_instruction); + } while (false); + + return 0; +} + +int QSPIFBlockDevice::_reset_flash_mem() +{ + // Perform Soft Reset of the Device prior to initialization + int status = 0; + char status_value[2] = {0}; + tr_info("INFO: _reset_flash_mem:\n"); + //Read the Status Register from device + if (QSPI_STATUS_OK == _qspi_send_general_command(QSPIF_RDSR, QSPI_NO_ADDRESS_COMMAND, NULL, 0, status_value, + 1) ) { // store received values in status_value + tr_debug("DEBUG: Reading Status Register Success: value = 0x%x\n", (int)status_value[0]); + } else { + tr_debug("ERROR: Reading Status Register failed\n"); + status = -1; + } + + if (0 == status) { + //Send Reset Enable + if (QSPI_STATUS_OK == _qspi_send_general_command(QSPIF_RSTEN, QSPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, + 0) ) { // store received values in status_value + tr_debug("DEBUG: Sending RSTEN Success\n"); + } else { + tr_error("ERROR: Sending RSTEN failed\n"); + status = -1; + } + + + if (0 == status) { + //Send Reset + if (QSPI_STATUS_OK == _qspi_send_general_command(QSPIF_RST, QSPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, + 0)) { // store received values in status_value + tr_debug("DEBUG: Sending RST Success\n"); + } else { + tr_error("ERROR: Sending RST failed\n"); + status = -1; + } + + _is_mem_ready(); + } + } + + return status; +} + +bool QSPIFBlockDevice::_is_mem_ready() +{ + // Check Status Register Busy Bit to Verify the Device isn't Busy + char status_value[2]; + int retries = 0; + bool mem_ready = true; + + do { + wait_ms(1); + retries++; + //Read the Status Register from device + if (QSPI_STATUS_OK != _qspi_send_general_command(QSPIF_RDSR, QSPI_NO_ADDRESS_COMMAND, NULL, 0, status_value, + 2)) { // store received values in status_value + tr_error("ERROR: Reading Status Register failed\n"); + } + } while ( (status_value[0] & QSPIF_STATUS_BIT_WIP) != 0 && retries < IS_MEM_READY_MAX_RETRIES ); + + if ((status_value[0] & QSPIF_STATUS_BIT_WIP) != 0) { + tr_error("ERROR: _is_mem_ready FALSE\n"); + mem_ready = false; + } + return mem_ready; +} + +int QSPIFBlockDevice::_set_write_enable() +{ + // Check Status Register Busy Bit to Verify the Device isn't Busy + char status_value[2]; + int status = -1; + + do { + if (QSPI_STATUS_OK != _qspi_send_general_command(QSPIF_WREN, QSPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0)) { + tr_error("ERROR:Sending WREN command FAILED\n"); + break; + } + + if ( false == _is_mem_ready()) { + tr_error("ERROR: Device not ready, write failed"); + break; + } + + memset(status_value, 0, 2); + if (QSPI_STATUS_OK != _qspi_send_general_command(QSPIF_RDSR, QSPI_NO_ADDRESS_COMMAND, NULL, 0, status_value, + 2)) { // store received values in status_value + tr_error("ERROR: Reading Status Register failed\n"); + break; + } + + if ((status_value[0] & QSPIF_STATUS_BIT_WEL) == 0) { + tr_error("ERROR: _set_write_enable failed\n"); + break; + } + status = 0; + } while (false); + return status; +} + +/*********************************************/ +/************* Utility Functions *************/ +/*********************************************/ +int QSPIFBlockDevice::_utils_find_addr_region(bd_size_t offset) +{ + //Find the region to which the given offset belong to + if ((offset > _device_size_bytes) || (_regions_count == 0)) { + return -1; + } + + if (_regions_count == 1) { + return 0; + } + + for (int i_ind = _regions_count - 2; i_ind >= 0; i_ind--) { + + if (offset > _region_high_boundary[i_ind]) { + return (i_ind + 1); + } + } + return -1; + +} + +int QSPIFBlockDevice::_utils_iterate_next_largest_erase_type(uint8_t& bitfield, int size, int offset, int boundry) +{ + // Iterate on all supported Erase Types of the Region to which the offset belong to. + // Iterates from highest type to lowest + uint8_t type_mask = ERASE_BITMASK_TYPE4; + int i_ind = 0; + int largest_erase_type = 0; + 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])) ) { + break; + } else { + bitfield &= ~type_mask; + } + } + type_mask = type_mask >> 1; + } + + if (i_ind == 4) { + tr_error("ERROR: no erase type was found for current region addr"); + } + return largest_erase_type; + +} + +/***************************************************/ +/*********** QSPI Driver API Functions *************/ +/***************************************************/ +qspi_status_t QSPIFBlockDevice::_qspi_set_frequency(int freq) +{ + return _qspi.set_frequency(freq); +} + +qspi_status_t QSPIFBlockDevice::_qspi_send_read_command(unsigned int read_inst, void *buffer, bd_addr_t addr, + bd_size_t size) +{ + // Send Read command to device driver + size_t buf_len = size; + + if (_qspi.read(read_inst, -1, (unsigned int )addr, (char *)buffer, &buf_len) != QSPI_STATUS_OK ) { + tr_error("ERROR: Read failed"); + return QSPI_STATUS_ERROR; + } + + return QSPI_STATUS_OK; + +} + +qspi_status_t QSPIFBlockDevice::_qspi_send_program_command(unsigned int progInst, const void *buffer, bd_addr_t addr, + bd_size_t *size) +{ + // Send Program (write) command to device driver + qspi_status_t result = QSPI_STATUS_OK; + + result = _qspi.write(progInst, -1, addr, (char *)buffer, (size_t *)size); + if (result != QSPI_STATUS_OK) { + tr_error("ERROR: QSPI Write failed"); + } + + return result; +} + +qspi_status_t QSPIFBlockDevice::_qspi_send_erase_command(unsigned int erase_inst, bd_addr_t addr, bd_size_t size) +{ + // Send Erase Instruction command to driver + qspi_status_t result = QSPI_STATUS_OK; + + tr_info("INFO: Inst: 0x%xh, addr: %llu, size: %llu", erase_inst, addr, size); + + result = _qspi.command_transfer(erase_inst, // command to send + (((int)addr) & 0x00FFF000), // Align addr to 4096 + NULL, // do not transmit + 0, // do not transmit + NULL, // just receive two bytes of data + 0); // store received values in status_value + + if (QSPI_STATUS_OK != result) { + tr_error("ERROR: QSPI Erase failed"); + } + + return result; + +} + +qspi_status_t QSPIFBlockDevice::_qspi_send_general_command(unsigned int instruction, bd_addr_t addr, + const char *tx_buffer, + size_t tx_length, const char *rx_buffer, size_t rx_length) +{ + // Send a general command Instruction to driver + qspi_status_t status = _qspi.command_transfer(instruction, (int)addr, tx_buffer, tx_length, rx_buffer, rx_length); + + if (QSPI_STATUS_OK != status) { + tr_error("ERROR:Sending Generic command: %x", instruction); + } + + return status; +} + +qspi_status_t QSPIFBlockDevice::_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) +{ + // Configure QSPI driver Bus format + qspi_status_t status = _qspi.configure_format(inst_width, address_width, address_size, alt_width, alt_size, data_width, + dummy_cycles); + + return status; +} + +/*********************************************/ +/************** Local Functions **************/ +/*********************************************/ +static int local_math_power(int base, int exp) +{ + // Integer X^Y function, used to calculate size fields given in 2^N format + int result = 1; + while (exp) { + result *= base; + exp--; + } + return result; +} + + + + diff --git a/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h b/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h new file mode 100644 index 0000000000..01b74c8f46 --- /dev/null +++ b/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h @@ -0,0 +1,309 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018 ARM Limited + * + * 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. + */ +#ifndef MBED_QSPIF_BLOCK_DEVICE_H +#define MBED_QSPIF_BLOCK_DEVICE_H + +#include "QSPI.h" +#include "BlockDevice.h" + + +namespace mbed { + +/** Enum qspif standard error codes + * + * @enum qspif_bd_error + */ +enum qspif_bd_error { + QSPIF_BD_ERROR_OK = 0, /*!< no error */ + QSPIF_BD_ERROR_DEVICE_ERROR = BD_ERROR_DEVICE_ERROR, /*!< device specific error -4001 */ + QSPIF_BD_ERROR_PARSING_FAILED = -4002, /* SFDP Parsing failed */ + QSPIF_BD_ERROR_READY_FAILED = -4003, /* Wait for Mem Ready failed */ + QSPIF_BD_ERROR_WREN_FAILED = -4004, /* Write Enable Failed */ + QSPIF_BD_ERROR_DEVICE_NOT_UNIQE = -4005, /* Only one instance per csel is allowed */ + QSPIF_BD_ERROR_DEVICE_MAX_EXCEED = -4006 /* Max active QSPIF devices exceeded */ +}; + +/** Enum qspif polarity mode + * + * @enum qspif_polarity_mode + */ +enum qspif_polarity_mode { + QSPIF_POLARITY_MODE_0 = 0, /* CPOL=0, CPHA=0 */ + QSPIF_POLARITY_MODE_1 /* CPOL=1, CPHA=1 */ +}; + +#define QSPIF_MAX_REGIONS 10 +#define MAX_NUM_OF_ERASE_TYPES 4 +#define QSPIF_MAX_ACTIVE_FLASH_DEVICES 10 + +class QSPIFBlockDevice : public BlockDevice { +public: + /** Create QSPIFBlockDevice - An SFDP based Flash Block Device over QSPI bus + * + * @param io0 1st IO pin used for sending/receiving data during data phase of a transaction + * @param io1 2nd IO pin used for sending/receiving data during data phase of a transaction + * @param io2 3rd IO pin used for sending/receiving data during data phase of a transaction + * @param io3 4th IO pin used for sending/receiving data during data phase of a transaction + * @param sclk QSPI Clock pin + * @param csel QSPI chip select pin + * @param clock_mode specifies the QSPI Clock Polarity mode (QSPIF_POLARITY_MODE_0/QSPIF_POLARITY_MODE_1) + * default value = 0 + * @param freq Clock frequency of the QSPI bus (defaults to 40MHz) + * + */ + QSPIFBlockDevice(PinName io0, PinName io1, PinName io2, PinName io3, PinName sclk, PinName csel, + qspif_polarity_mode clock_mode, + int freq = 40000000); + + + /** Initialize a block device + * + * @return QSPIF_BD_ERROR_OK(0) - success + * QSPIF_BD_ERROR_DEVICE_ERROR - device driver transaction failed + * QSPIF_BD_ERROR_READY_FAILED - Waiting for Memory ready failed or timedout + * QSPIF_BD_ERROR_PARSING_FAILED - unexpected format or values in one of the SFDP tables + */ + virtual int init(); + + /** Deinitialize a block device + * + * @return QSPIF_BD_ERROR_OK(0) - success + * QSPIF_BD_ERROR_DEVICE_ERROR - device driver transaction failed + */ + virtual int deinit(); + + /** Desctruct QSPIFBlockDevie + */ + ~QSPIFBlockDevice() {deinit();} + + /** Read blocks from a block device + * + * @param buffer Buffer to write blocks to + * @param addr Address of block to begin reading from + * @param size Size to read in bytes, must be a multiple of read block size + * @return QSPIF_BD_ERROR_OK(0) - success + * QSPIF_BD_ERROR_DEVICE_ERROR - device driver transaction failed + */ + virtual int read(void *buffer, bd_addr_t addr, bd_size_t size); + + /** Program blocks to a block device + * + * The blocks must have been erased prior to being programmed + * + * @param buffer Buffer of data to write to blocks + * @param addr Address of block to begin writing to + * @param size Size to write in bytes, must be a multiple of program block size + * @return QSPIF_BD_ERROR_OK(0) - success + * QSPIF_BD_ERROR_DEVICE_ERROR - device driver transaction failed + * QSPIF_BD_ERROR_READY_FAILED - Waiting for Memory ready failed or timed out + * QSPIF_BD_ERROR_WREN_FAILED - Write Enable failed + * QSPIF_BD_ERROR_PARSING_FAILED - unexpected format or values in one of the SFDP tables + */ + virtual int program(const void *buffer, bd_addr_t addr, bd_size_t size); + + /** Erase blocks on a block device + * + * The state of an erased block is undefined until it has been programmed + * + * @param addr Address of block to begin erasing + * @param size Size to erase in bytes, must be a multiple of erase block size + * @return QSPIF_BD_ERROR_OK(0) - success + * QSPIF_BD_ERROR_DEVICE_ERROR - device driver transaction failed + * QSPIF_BD_ERROR_READY_FAILED - Waiting for Memory ready failed or timed out + * QSPIF_BD_ERROR_WREN_FAILED - Write Enable failed + * QSPIF_BD_ERROR_PARSING_FAILED - unexpected format or values in one of the SFDP tables + */ + virtual int erase(bd_addr_t addr, bd_size_t size); + + /** Get the size of a readable block + * + * @return Size of a readable block in bytes + */ + virtual bd_size_t get_read_size() const; + + /** Get the size of a programable block + * + * @return Size of a program block size in bytes + * @note Must be a multiple of the read size + */ + virtual bd_size_t get_program_size() const; + + /** Get the size of a eraseable block + * + * @return Size of a minimal erase block, common to all regions, in bytes + * @note Must be a multiple of the program size + */ + virtual bd_size_t get_erase_size() const; + + /** Get the size of minimal eraseable sector size of given address + * + * @param addr Any address within block queried for erase sector size (can be any address within flash size offset) + * @return Size of minimal erase sector size, in given address region, in bytes + * @note Must be a multiple of the program size + */ + virtual bd_size_t get_erase_size(bd_addr_t addr); + + /** Get the total size of the underlying device + * + * @return Size of the underlying device in bytes + */ + virtual bd_size_t size() const; + +private: + // Internal functions + + + /********************************/ + /* Different Device Csel Mgmt */ + /********************************/ + // Add a new QSPI device CS to existing devices list. + // Only one QSPIFBlockDevice instance per CS is allowed + int add_new_csel_instance(PinName csel); + + // Remove device CS from existing device list upon destroying object (last deinit is called) + int remove_csel_instance(PinName csel); + + /********************************/ + /* Calls to QSPI Driver APIs */ + /********************************/ + // Send Program => Write command to Driver + qspi_status_t _qspi_send_program_command(unsigned int prog_instruction, const void *buffer, bd_addr_t addr, + bd_size_t *size); + + // Send Read command to Driver + qspi_status_t _qspi_send_read_command(unsigned int read_instruction, void *buffer, bd_addr_t addr, bd_size_t size); + + // Send Erase Instruction using command_transfer command to Driver + qspi_status_t _qspi_send_erase_command(unsigned int erase_instruction, bd_addr_t addr, bd_size_t size); + + // Send Generic command_transfer command to Driver + qspi_status_t _qspi_send_general_command(unsigned int instruction_int, bd_addr_t addr, const char *tx_buffer, + size_t tx_length, const char *rx_buffer, size_t rx_length); + + // Send Bus configure_format command to Driver + 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); + + // Send set_frequency command to Driver + qspi_status_t _qspi_set_frequency(int freq); + /********************************/ + + + // Soft Reset Flash Memory + int _reset_flash_mem(); + + // Configure Write Enable in Status Register + int _set_write_enable(); + + // Wait on status register until write not-in-progress + bool _is_mem_ready(); + + + /* SFDP Detection and Parsing Functions */ + /****************************************/ + // Parse SFDP Headers and retrieve Basic Param and Sector Map Tables (if exist) + int _sfdp_parse_sfdp_headers(uint32_t& basic_table_addr, size_t& basic_table_size, + uint32_t& sector_map_table_addr, size_t& sector_map_table_size); + + // 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 Secotr 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, bool& set_quad_enable, + bool& is_qpi_mode, unsigned int& read_inst); + + // Enable Quad mode if supported (1-1-4, 1-4-4, 4-4-4 bus modes) + int _sfdp_set_quad_enabled(uint8_t *basic_param_table_ptr); + + // Enable QPI mode (4-4-4) is supported + int _sfdp_set_qpi_enabled(uint8_t *basic_param_table_ptr); + + // Set Page size for program + int _sfdp_detect_page_size(uint8_t *basic_param_table_ptr, int basic_param_table_size); + + // Detect all supported erase types + int _sfdp_detect_erase_types_inst_and_size(uint8_t *basic_param_table_ptr, int basic_param_table_size, + unsigned int& erase4k_inst, + unsigned int *erase_type_inst_arr, unsigned int *erase_type_size_arr); + + /* Utilities Functions */ + /***********************/ + // Find the region to which the given offset belong to + int _utils_find_addr_region(bd_size_t offset); + + // Iterate on all supported Erase Types of the Region to which the offset belong to. + // Iterates from highest type to lowest + int _utils_iterate_next_largest_erase_type(uint8_t& bitfield, int size, int offset, int boundry); + +private: + // Internal Members + + // QSPI Driver Object + QSPI _qspi; + + // Static List of different QSPI based Flash devices csel that already exist + // Each QSPI Flash device csel can have only 1 QSPIFBlockDevice instance + // _devices_mutex is used to lock csel list - only one QSPIFBlockDevice instance per csel is allowed + static SingletonPtr _devices_mutex; + static int _number_of_active_qspif_flash_csel; + static PinName *_active_qspif_flash_csel_arr; + + int _unique_device_status; + PinName _csel; + + // Mutex is used to protect Flash device for some QSPI Driver commands that must be done sequentially with no other commands in between + // e.g. (1)Set Write Enable, (2)Program, (3)Wait Memory Ready + PlatformMutex _mutex; + + // Command Instructions + unsigned int _read_instruction; + unsigned int _prog_instruction; + unsigned int _erase_instruction; + unsigned int _erase4k_inst; // Legacy 4K erase instruction (default 0x20h) + + // Up To 4 Erase Types are supported by SFDP (each with its own command Instruction and Size) + unsigned 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[QSPIF_MAX_REGIONS]; //regions size in bytes + bd_size_t _region_high_boundary[QSPIF_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[QSPIF_MAX_REGIONS]; + unsigned int _min_common_erase_size; // minimal common erase size for all regions (0 if none exists) + + unsigned int _page_size_bytes; // Page size - 256 Bytes default + int _freq; + bd_size_t _device_size_bytes; + + // Bus speed configuration + qspi_bus_width_t _inst_width; //Bus width for Instruction phase + qspi_bus_width_t _address_width; //Bus width for Address phase + qspi_address_size_t _address_size; // number of bytes for address + qspi_bus_width_t _data_width; //Bus width for Data phase + int _dummy_and_mode_cycles; // Number of Dummy and Mode Bits required by Current Bus Mode + + uint32_t _init_ref_count; + bool _is_initialized; +}; + +} //namespace mbed +#endif diff --git a/components/storage/blockdevice/COMPONENT_QSPIF/README.md b/components/storage/blockdevice/COMPONENT_QSPIF/README.md new file mode 100644 index 0000000000..8808b9bd5b --- /dev/null +++ b/components/storage/blockdevice/COMPONENT_QSPIF/README.md @@ -0,0 +1,46 @@ +# Quad SPI (QSPI) Flash Block Device + +Block device driver for NOR based QSPI flash devices that support SFDP. + +NOR based QSPI flash supports up to 4bits per cycle of instruction, address and data. +SFDP based QSPI Flash supports variable bus modes (single, dual, quad), several sector erase size types, multiple regions of sector size types and more. + +SFDP JEDEC standard can be found in: +https://www.jedec.org/system/files/docs/JESD216B.pdf + +``` cpp +// QSPI SFDP Flash - Block Device example +#include "mbed.h" +#include "QSPIFBlockDevice.h" + +QSPIFBlockDevice block_device(MBED_CONF_QSPIF_QSPI_IO0,MBED_CONF_QSPIF_QSPI_IO1,MBED_CONF_QSPIF_QSPI_IO2,MBED_CONF_QSPIF_QSPI_IO3, + MBED_CONF_QSPIF_QSPI_CLK,MBED_CONF_QSPIF_QSPI_CS,0,MBED_CONF_QSPIF_QSPI_FREQ); + + +int main() { + printf("QSPI SFDP Flash Block Device example\n"); + + // Initialize the SPI flash device and print the memory layout + block_device.init(); + bd_size_t sector_size_at_address_0 = block_device.get_erase_size(0); + + printf("QSPIF BD size: %llu\n", block_device.size()); + printf("QSPIF BD read size: %llu\n", block_device.get_read_size()); + printf("QSPIF BD program size: %llu\n", block_device.get_program_size()); + + printf("QSPIF BD erase size (at address 0): %llu\n", sector_size_at_address_0); + + // Write "Hello World!" to the first block + char *buffer = (char*) malloc(sector_size_at_address_0); + sprintf(buffer, "Hello World!\n"); + block_device.erase(0, sector_size_at_address_0); + block_device.program(buffer, 0, sector_size_at_address_0); + + // Read back what was stored + block_device.read(buffer, 0, sector_size_at_address_0); + printf("%s", buffer); + + // Deinitialize the device + block_device.deinit(); +} +``` diff --git a/components/storage/blockdevice/COMPONENT_QSPIF/TESTS/block_device/qspif/main.cpp b/components/storage/blockdevice/COMPONENT_QSPIF/TESTS/block_device/qspif/main.cpp new file mode 100644 index 0000000000..9c4862b97f --- /dev/null +++ b/components/storage/blockdevice/COMPONENT_QSPIF/TESTS/block_device/qspif/main.cpp @@ -0,0 +1,302 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018 ARM Limited + * + * 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 "greentea-client/test_env.h" +#include "unity.h" +#include "utest.h" +#include "QSPIFBlockDevice.h" +#include "mbed_trace.h" +#include + +using namespace utest::v1; + +#define TEST_BLOCK_COUNT 10 +#define TEST_ERROR_MASK 16 +#define QSPIF_TEST_NUM_OF_THREADS 5 + +const struct { + const char *name; + bd_size_t (BlockDevice::*method)() const; +} ATTRS[] = { + {"read size", &BlockDevice::get_read_size}, + {"program size", &BlockDevice::get_program_size}, + {"erase size", &BlockDevice::get_erase_size}, + {"total size", &BlockDevice::size}, +}; + +static SingletonPtr _mutex; + + +// Mutex is protecting rand() per srand for buffer writing and verification. +// Mutex is also protecting printouts for clear logs. +// Mutex is NOT protecting Block Device actions: erase/program/read - which is the purpose of the multithreaded test! +void basic_erase_program_read_test(QSPIFBlockDevice& blockD, bd_size_t block_size, uint8_t *write_block, + uint8_t *read_block, unsigned addrwidth) +{ + int err = 0; + _mutex->lock(); + // Find a random block + bd_addr_t block = (rand() * block_size) % blockD.size(); + + // Use next random number as temporary seed to keep + // the address progressing in the pseudorandom sequence + unsigned seed = rand(); + + // Fill with random sequence + srand(seed); + for (bd_size_t i_ind = 0; i_ind < block_size; i_ind++) { + write_block[i_ind] = 0xff & rand(); + } + // Write, sync, and read the block + utest_printf("\ntest %0*llx:%llu...", addrwidth, block, block_size); + _mutex->unlock(); + + err = blockD.erase(block, block_size); + TEST_ASSERT_EQUAL(0, err); + + err = blockD.program(write_block, block, block_size); + TEST_ASSERT_EQUAL(0, err); + + err = blockD.read(read_block, block, block_size); + TEST_ASSERT_EQUAL(0, err); + + _mutex->lock(); + // Check that the data was unmodified + srand(seed); + int val_rand; + for (bd_size_t i_ind = 0; i_ind < block_size; i_ind++) { + val_rand = rand(); + if ( (0xff & val_rand) != read_block[i_ind] ) { + utest_printf("\n Assert Failed Buf Read - block:size: %llx:%llu \n", block, block_size); + utest_printf("\n pos: %llu, exp: %02x, act: %02x, wrt: %02x \n", i_ind, (0xff & val_rand), read_block[i_ind], + write_block[i_ind] ); + } + TEST_ASSERT_EQUAL(0xff & val_rand, read_block[i_ind]); + } + _mutex->unlock(); +} + +void test_qspif_random_program_read_erase() +{ + utest_printf("\nTest Random Program Read Erase Starts..\n"); + + QSPIFBlockDevice blockD(QSPI_FLASH1_IO0, QSPI_FLASH1_IO1, QSPI_FLASH1_IO2, QSPI_FLASH1_IO3, + QSPI_FLASH1_SCK, QSPI_FLASH1_CSN, QSPIF_POLARITY_MODE_0, MBED_CONF_QSPIF_QSPI_FREQ); + + int err = blockD.init(); + TEST_ASSERT_EQUAL(0, err); + + for (unsigned atr = 0; atr < sizeof(ATTRS) / sizeof(ATTRS[0]); atr++) { + static const char *prefixes[] = {"", "k", "M", "G"}; + for (int i_ind = 3; i_ind >= 0; i_ind--) { + bd_size_t size = (blockD.*ATTRS[atr].method)(); + if (size >= (1ULL << 10 * i_ind)) { + utest_printf("%s: %llu%sbytes (%llubytes)\n", + ATTRS[atr].name, size >> 10 * i_ind, prefixes[i_ind], size); + break; + } + } + } + + bd_size_t block_size = blockD.get_erase_size(); + unsigned addrwidth = ceil(log(float(blockD.size() - 1)) / log(float(16))) + 1; + + uint8_t *write_block = new (std::nothrow) uint8_t[block_size]; + uint8_t *read_block = new (std::nothrow) uint8_t[block_size]; + if (!write_block || !read_block) { + utest_printf("\n Not enough memory for test"); + goto end; + } + + for (int b = 0; b < TEST_BLOCK_COUNT; b++) { + basic_erase_program_read_test(blockD, block_size, write_block, read_block, addrwidth); + } + + err = blockD.deinit(); + TEST_ASSERT_EQUAL(0, err); + +end: + delete[] write_block; + delete[] read_block; +} + +void test_qspif_unaligned_program() +{ + + utest_printf("\nTest Unaligned Program Starts..\n"); + + QSPIFBlockDevice blockD(QSPI_FLASH1_IO0, QSPI_FLASH1_IO1, QSPI_FLASH1_IO2, QSPI_FLASH1_IO3, + QSPI_FLASH1_SCK, QSPI_FLASH1_CSN, QSPIF_POLARITY_MODE_0, MBED_CONF_QSPIF_QSPI_FREQ); + + int err = blockD.init(); + TEST_ASSERT_EQUAL(0, err); + + for (unsigned atr = 0; atr < sizeof(ATTRS) / sizeof(ATTRS[0]); atr++) { + static const char *prefixes[] = {"", "k", "M", "G"}; + for (int i_ind = 3; i_ind >= 0; i_ind--) { + bd_size_t size = (blockD.*ATTRS[atr].method)(); + if (size >= (1ULL << 10 * i_ind)) { + utest_printf("%s: %llu%sbytes (%llubytes)\n", + ATTRS[atr].name, size >> 10 * i_ind, prefixes[i_ind], size); + break; + } + } + } + + bd_size_t block_size = blockD.get_erase_size(); + unsigned addrwidth = ceil(log(float(blockD.size() - 1)) / log(float(16))) + 1; + + uint8_t *write_block = new (std::nothrow) uint8_t[block_size]; + uint8_t *read_block = new (std::nothrow) uint8_t[block_size]; + if (!write_block || !read_block ) { + utest_printf("\n Not enough memory for test"); + goto end; + } + + { + bd_addr_t block = (rand() * block_size) % blockD.size() + 15; + + // Use next random number as temporary seed to keep + // the address progressing in the pseudorandom sequence + unsigned seed = rand(); + + // Fill with random sequence + srand(seed); + for (bd_size_t i_ind = 0; i_ind < block_size; i_ind++) { + write_block[i_ind] = 0xff & rand(); + } + + // Write, sync, and read the block + utest_printf("\ntest %0*llx:%llu...", addrwidth, block, block_size); + + err = blockD.erase(block, block_size); + TEST_ASSERT_EQUAL(0, err); + + err = blockD.program(write_block, block, block_size); + TEST_ASSERT_EQUAL(0, err); + + err = blockD.read(read_block, block, block_size); + TEST_ASSERT_EQUAL(0, err); + + // Check that the data was unmodified + srand(seed); + for (bd_size_t i_ind = 0; i_ind < block_size; i_ind++) { + TEST_ASSERT_EQUAL(0xff & rand(), read_block[i_ind]); + } + + err = blockD.deinit(); + TEST_ASSERT_EQUAL(0, err); + } +end: + delete[] write_block; + delete[] read_block; +} + + + +static void test_qspif_thread_job(void *vBlockD/*, int thread_num*/) +{ + static int thread_num = 0; + thread_num++; + QSPIFBlockDevice *blockD = (QSPIFBlockDevice *)vBlockD; + utest_printf("\n Thread %d Started \n", thread_num); + + bd_size_t block_size = blockD->get_erase_size(); + unsigned addrwidth = ceil(log(float(blockD->size() - 1)) / log(float(16))) + 1; + + uint8_t *write_block = new (std::nothrow) uint8_t[block_size]; + uint8_t *read_block = new (std::nothrow) uint8_t[block_size]; + if (!write_block || !read_block ) { + utest_printf("\n Not enough memory for test"); + goto end; + } + + for (int b = 0; b < TEST_BLOCK_COUNT; b++) { + basic_erase_program_read_test((*blockD), block_size, write_block, read_block, addrwidth); + } + +end: + delete[] write_block; + delete[] read_block; +} + +void test_qspif_multi_threads() +{ + + utest_printf("\nTest Multi Threaded Erase/Program/Read Starts..\n"); + + QSPIFBlockDevice blockD(QSPI_FLASH1_IO0, QSPI_FLASH1_IO1, QSPI_FLASH1_IO2, QSPI_FLASH1_IO3, + QSPI_FLASH1_SCK, QSPI_FLASH1_CSN, QSPIF_POLARITY_MODE_0, MBED_CONF_QSPIF_QSPI_FREQ); + + int err = blockD.init(); + TEST_ASSERT_EQUAL(0, err); + + for (unsigned atr = 0; atr < sizeof(ATTRS) / sizeof(ATTRS[0]); atr++) { + static const char *prefixes[] = {"", "k", "M", "G"}; + for (int i_ind = 3; i_ind >= 0; i_ind--) { + bd_size_t size = (blockD.*ATTRS[atr].method)(); + if (size >= (1ULL << 10 * i_ind)) { + utest_printf("%s: %llu%sbytes (%llubytes)\n", + ATTRS[atr].name, size >> 10 * i_ind, prefixes[i_ind], size); + break; + } + } + } + + rtos::Thread qspif_bd_thread[QSPIF_TEST_NUM_OF_THREADS]; + + osStatus threadStatus; + int i_ind; + + for (i_ind = 0; i_ind < QSPIF_TEST_NUM_OF_THREADS; i_ind++) { + threadStatus = qspif_bd_thread[i_ind].start(test_qspif_thread_job, (void *)&blockD); + if (threadStatus != 0) { + utest_printf("\n Thread %d Start Failed!", i_ind + 1); + } + } + + for (i_ind = 0; i_ind < QSPIF_TEST_NUM_OF_THREADS; i_ind++) { + qspif_bd_thread[i_ind].join(); + } + + err = blockD.deinit(); + TEST_ASSERT_EQUAL(0, err); +} + + + + +// Test setup +utest::v1::status_t test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(60, "default_auto"); + return verbose_test_setup_handler(number_of_cases); +} + +Case cases[] = { + Case("Testing unaligned program blocks", test_qspif_unaligned_program), + Case("Testing read write random blocks", test_qspif_random_program_read_erase), + Case("Testing Multi Threads Erase Program Read", test_qspif_multi_threads) +}; + +Specification specification(test_setup, cases); + + +int main() +{ + mbed_trace_init(); + utest_printf("MAIN STARTS\n"); + return !Harness::run(specification); +} diff --git a/components/storage/blockdevice/COMPONENT_QSPIF/mbed_lib.json b/components/storage/blockdevice/COMPONENT_QSPIF/mbed_lib.json new file mode 100644 index 0000000000..3351003180 --- /dev/null +++ b/components/storage/blockdevice/COMPONENT_QSPIF/mbed_lib.json @@ -0,0 +1,20 @@ +{ +"name": "qspif", + "config": { + "QSPI_FREQ": "40000000" + }, + "target_overrides": { + "DISCO_F413ZH": { + "QSPI_FREQ": "80000000" + }, + "DISCO_L475VG": { + "QSPI_FREQ": "8000000" + }, + "DISCO_L476VG": { + "QSPI_FREQ": "80000000" + }, + "DISCO_F469NI": { + "QSPI_FREQ": "80000000" + } + } +} From 9d936e10aff35484b4b7bc2c5e94687e57d5b879 Mon Sep 17 00:00:00 2001 From: Offir Kochalsky Date: Mon, 27 Aug 2018 17:12:21 +0300 Subject: [PATCH 02/12] Added class example documentation --- .../COMPONENT_QSPIF/QSPIFBlockDevice.h | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h b/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h index 01b74c8f46..41297e9294 100644 --- a/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h +++ b/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h @@ -49,6 +49,44 @@ enum qspif_polarity_mode { #define MAX_NUM_OF_ERASE_TYPES 4 #define QSPIF_MAX_ACTIVE_FLASH_DEVICES 10 +/** BlockDevice for SFDP based flash devices over QSPI bus + * + * @code + * // Here's an example using QSPI flash device on DISCO_L476VG target + * #include "mbed.h" + * #include "QSPIFBlockDevice.h" + * + * QSPIFBlockDevice block_device(QSPI_FLASH1_IO0, QSPI_FLASH1_IO1, QSPI_FLASH1_IO2, QSPI_FLASH1_IO3, + * QSPI_FLASH1_SCK, QSPI_FLASH1_CSN, QSPIF_POLARITY_MODE_0, MBED_CONF_QSPIF_QSPI_FREQ); + * + * int main() + * { + * printf("QSPI SFDP Flash Block Device example\n"); + * + * // Initialize the SPI flash device and print the memory layout + * block_device.init(); + * bd_size_t sector_size_at_address_0 = block_device.get_erase_size(0); + * + * printf("QSPIF BD size: %llu\n", block_device.size()); + * printf("QSPIF BD read size: %llu\n", block_device.get_read_size()); + * printf("QSPIF BD program size: %llu\n", block_device.get_program_size()); + * printf("QSPIF BD erase size (at address 0): %llu\n", sector_size_at_address_0); + * + * // Write "Hello World!" to the first block + * char *buffer = (char *) malloc(sector_size_at_address_0); + * sprintf(buffer, "Hello World!\n"); + * block_device.erase(0, sector_size_at_address_0); + * block_device.program(buffer, 0, sector_size_at_address_0); + * + * // Read back what was stored + * block_device.read(buffer, 0, sector_size_at_address_0); + * printf("%s", buffer); + * + * // Deinitialize the device + * block_device.deinit(); + * } + * @endcode + */ class QSPIFBlockDevice : public BlockDevice { public: /** Create QSPIFBlockDevice - An SFDP based Flash Block Device over QSPI bus From 794d150a4af4514dacfe49b69292f7752a0b234e Mon Sep 17 00:00:00 2001 From: Offir Kochalsky Date: Tue, 28 Aug 2018 14:20:07 +0300 Subject: [PATCH 03/12] fix unaligned address erase problem --- .../storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.cpp b/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.cpp index 188b82cab4..e43f21da99 100644 --- a/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.cpp +++ b/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.cpp @@ -374,6 +374,7 @@ exit_point: int QSPIFBlockDevice::erase(bd_addr_t addr, bd_size_t in_size) { int type = 0; + uint32_t offset = 0; uint32_t chunk = 4096; unsigned int cur_erase_inst = _erase_instruction; int size = (int)in_size; @@ -393,7 +394,8 @@ int QSPIFBlockDevice::erase(bd_addr_t addr, bd_size_t in_size) // find the matching instruction and erase size chunk for that type. type = _utils_iterate_next_largest_erase_type(bitfield, size, (int)addr, _region_high_boundary[region]); cur_erase_inst = _erase_type_inst_arr[type]; - chunk = _erase_type_size_arr[type]; + offset = addr % _erase_type_size_arr[type]; + chunk = ( (offset + size) < _erase_type_size_arr[type]) ? size : (_erase_type_size_arr[type] - offset); tr_debug("DEBUG: erase - addr: %llu, size:%d, Inst: 0x%xh, chunk: %lu , ", addr, size, cur_erase_inst, chunk); From a60552c07dc0402b662aaa3ced9a0d4c54bba67d Mon Sep 17 00:00:00 2001 From: Offir Kochalsky Date: Tue, 9 Oct 2018 15:17:52 +0300 Subject: [PATCH 04/12] Return to 1-1-1 mode prior to setting QUAD enable --- .../COMPONENT_QSPIF/QSPIFBlockDevice.cpp | 272 +++++++++--------- .../COMPONENT_QSPIF/QSPIFBlockDevice.h | 20 +- .../blockdevice/COMPONENT_QSPIF/README.md | 55 +++- .../TESTS/block_device/qspif/main.cpp | 67 ++--- targets/targets.json | 2 + 5 files changed, 224 insertions(+), 192 deletions(-) diff --git a/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.cpp b/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.cpp index e43f21da99..2ada927b5d 100644 --- a/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.cpp +++ b/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.cpp @@ -18,8 +18,13 @@ #include #include "mbed_wait_api.h" +#ifndef MBED_CONF_MBED_TRACE_ENABLE +#define MBED_CONF_MBED_TRACE_ENABLE 0 +#endif + #include "mbed_trace.h" #define TRACE_GROUP "QSPIF" + using namespace mbed; /* Default QSPIF Parameters */ @@ -34,9 +39,8 @@ using namespace mbed; #endif #define QSPI_NO_ADDRESS_COMMAND UINT64_MAX // Status Register Bits -#define QSPIF_STATUS_BIT_WIP 0x1 //Write In Progress -#define QSPIF_STATUS_BIT_WEL 0x2 // Write Enable Latch - +#define QSPIF_STATUS_BIT_WIP 0x1 //Write In Progress +#define QSPIF_STATUS_BIT_WEL 0x2 // Write Enable Latch /* SFDP Header Parsing */ /***********************/ @@ -107,30 +111,31 @@ PinName *QSPIFBlockDevice::_active_qspif_flash_csel_arr = generate_initialized_a /********* Public API Functions *********/ /****************************************/ QSPIFBlockDevice::QSPIFBlockDevice(PinName io0, PinName io1, PinName io2, PinName io3, PinName sclk, PinName csel, - qspif_polarity_mode clock_mode, int freq) + int clock_mode, int freq) : _qspi(io0, io1, io2, io3, sclk, csel, clock_mode), _csel(csel), _freq(freq), _device_size_bytes(0), _init_ref_count(0), _is_initialized(false) { _unique_device_status = add_new_csel_instance(csel); + if (_unique_device_status == 0) { - tr_info("INFO: Adding a new QSPIFBlockDevice csel: %d", (int)csel); + tr_info("Adding a new QSPIFBlockDevice csel: %d\n", (int)csel); } else if (_unique_device_status == -1) { - tr_error("ERROR: QSPIFBlockDevice with the same csel(%d) already exists", (int)csel); + tr_error("QSPIFBlockDevice with the same csel(%d) already exists\n", (int)csel); } else { - tr_error("ERROR: Too many different QSPIFBlockDevice devices - max allowed: %d", QSPIF_MAX_ACTIVE_FLASH_DEVICES); + tr_error("Too many different QSPIFBlockDevice devices - max allowed: %d\n", QSPIF_MAX_ACTIVE_FLASH_DEVICES); } } int QSPIFBlockDevice::init() { if (_unique_device_status == 0) { - tr_debug("DEBUG: QSPIFBlockDevice csel: %d", (int)_csel); + tr_debug("QSPIFBlockDevice csel: %d", (int)_csel); } else if (_unique_device_status == -1) { - tr_error("ERROR: QSPIFBlockDevice with the same csel(%d) already exists", (int)_csel); + tr_error("QSPIFBlockDevice with the same csel(%d) already exists", (int)_csel); return QSPIF_BD_ERROR_DEVICE_NOT_UNIQE; } else { - tr_error("ERROR: Too many different QSPIFBlockDevice devices - max allowed: %d", QSPIF_MAX_ACTIVE_FLASH_DEVICES); + tr_error("Too many different QSPIFBlockDevice devices - max allowed: %d", QSPIF_MAX_ACTIVE_FLASH_DEVICES); return QSPIF_BD_ERROR_DEVICE_MAX_EXCEED; } @@ -168,29 +173,31 @@ int QSPIFBlockDevice::init() _dummy_and_mode_cycles = 0; if (QSPI_STATUS_OK != _qspi_set_frequency(_freq)) { - tr_error("ERROR: QSPI Set Frequency Failed"); + tr_error("QSPI Set Frequency Failed"); status = QSPIF_BD_ERROR_DEVICE_ERROR; goto exit_point; } // Soft Reset if ( -1 == _reset_flash_mem()) { - tr_error("ERROR: init - Unable to initialize flash memory, tests failed\n"); + tr_error("Init - Unable to initialize flash memory, tests failed"); status = QSPIF_BD_ERROR_DEVICE_ERROR; goto exit_point; } else { - tr_info("INFO: Initialize flash memory OK\n"); + tr_info("Initialize flash memory OK"); } /* Read Manufacturer ID (1byte), and Device ID (2bytes)*/ qspi_status = _qspi_send_general_command(QSPIF_RDID, QSPI_NO_ADDRESS_COMMAND, NULL, 0, (char *)vendor_device_ids, data_length); if (qspi_status != QSPI_STATUS_OK) { - tr_error("ERROR: init - Read Vendor ID Failed"); + tr_error("Init - Read Vendor ID Failed"); status = QSPIF_BD_ERROR_DEVICE_ERROR; goto exit_point; } + tr_debug("Vendor device ID = 0x%x 0x%x 0x%x 0x%x \n", vendor_device_ids[0], + vendor_device_ids[1], vendor_device_ids[2], vendor_device_ids[3]); switch (vendor_device_ids[0]) { case 0xbf: // SST devices come preset with block protection @@ -202,22 +209,21 @@ int QSPIFBlockDevice::init() //Synchronize Device if ( false == _is_mem_ready()) { - tr_error("ERROR: init - _is_mem_ready Failed"); + tr_error("Init - _is_mem_ready Failed"); status = QSPIF_BD_ERROR_READY_FAILED; goto exit_point; } /**************************** Parse SFDP Header ***********************************/ if ( 0 != _sfdp_parse_sfdp_headers(basic_table_addr, basic_table_size, sector_map_table_addr, sector_map_table_size)) { - tr_error("ERROR: init - Parse SFDP Headers Failed"); + tr_error("Init - Parse SFDP Headers Failed"); status = QSPIF_BD_ERROR_PARSING_FAILED; goto exit_point; } - /**************************** Parse Basic Parameters Table ***********************************/ if ( 0 != _sfdp_parse_basic_param_table(basic_table_addr, basic_table_size) ) { - tr_error("ERROR: init - Parse Basic Param Table Failed"); + tr_error("Init - Parse Basic Param Table Failed"); status = QSPIF_BD_ERROR_PARSING_FAILED; goto exit_point; } @@ -228,10 +234,10 @@ int QSPIFBlockDevice::init() _region_high_boundary[0] = _device_size_bytes - 1; if ( (sector_map_table_addr != 0) && (0 != sector_map_table_size) ) { - tr_info("INFO: init - Parsing Sector Map Table - addr: 0x%lxh, Size: %d", sector_map_table_addr, + tr_info("Init - Parsing Sector Map Table - addr: 0x%lxh, Size: %d", sector_map_table_addr, sector_map_table_size); if (0 != _sfdp_parse_sector_map_table(sector_map_table_addr, sector_map_table_size) ) { - tr_error("ERROR: init - Parse Sector Map Table Failed"); + tr_error("Init - Parse Sector Map Table Failed"); status = QSPIF_BD_ERROR_PARSING_FAILED; goto exit_point; } @@ -271,7 +277,7 @@ int QSPIFBlockDevice::deinit() // Disable Device for Writing qspi_status_t status = _qspi_send_general_command(QSPIF_WRDI, QSPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0); if (status != QSPI_STATUS_OK) { - tr_error("ERROR: Write Disable failed"); + tr_error("Write Disable failed"); result = QSPIF_BD_ERROR_DEVICE_ERROR; } @@ -288,11 +294,8 @@ int QSPIFBlockDevice::deinit() int QSPIFBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size) { - - int status = QSPIF_BD_ERROR_OK; - - tr_info("INFO Inst: 0x%xh", _read_instruction); + tr_info("Read Inst: 0x%xh", _read_instruction); _mutex.lock(); @@ -302,7 +305,7 @@ int QSPIFBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size) if (QSPI_STATUS_OK != _qspi_send_read_command(_read_instruction, buffer, addr, size)) { status = QSPIF_BD_ERROR_DEVICE_ERROR; - tr_error("ERROR: Read failed\n"); + tr_error("Read Command failed"); } // All commands other than Read use default 1-1-1 Bus mode (Program/Erase are constrained by flash memory performance less than that of the bus) @@ -323,10 +326,9 @@ int QSPIFBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size uint32_t chunk = 0; bd_size_t written_bytes = 0; - tr_debug("DEBUG: program - Buff: 0x%lxh, addr: %llu, size: %llu", (uint32_t)buffer, addr, size); + tr_debug("Program - Buff: 0x%lxh, addr: %llu, size: %llu", (uint32_t)buffer, addr, size); while (size > 0) { - // Write on _page_size_bytes boundaries (Default 256 bytes a page) offset = addr % _page_size_bytes; chunk = (offset + size < _page_size_bytes) ? size : (_page_size_bytes - offset); @@ -336,7 +338,7 @@ int QSPIFBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size //Send WREN if (_set_write_enable() != 0) { - tr_error("ERROR: Write Enabe failed\n"); + tr_error("Write Enabe failed"); program_failed = true; status = QSPIF_BD_ERROR_WREN_FAILED; goto exit_point; @@ -344,7 +346,7 @@ int QSPIFBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size result = _qspi_send_program_command(_prog_instruction, buffer, addr, &written_bytes); if ( (result != QSPI_STATUS_OK) || (chunk != written_bytes) ) { - tr_error("ERROR: Write failed"); + tr_error("Write failed"); program_failed = true; status = QSPIF_BD_ERROR_DEVICE_ERROR; goto exit_point; @@ -355,7 +357,7 @@ int QSPIFBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size size -= chunk; if ( false == _is_mem_ready()) { - tr_error("ERROR: Device not ready after write, failed\n"); + tr_error("Device not ready after write, failed"); program_failed = true; status = QSPIF_BD_ERROR_READY_FAILED; goto exit_point; @@ -385,11 +387,20 @@ int QSPIFBlockDevice::erase(bd_addr_t addr, bd_size_t in_size) // Erase Types of selected region uint8_t bitfield = _region_erase_types_bitfield[region]; - tr_debug("DEBUG: erase - addr: %llu, in_size: %llu", addr, in_size); + tr_debug("Erase - addr: %llu, in_size: %llu", addr, in_size); + + if ((addr + in_size) > _device_size_bytes) { + tr_error("Erase exceeds flash device size"); + return QSPIF_BD_ERROR_INVALID_ERASE_PARAMS; + } + + if ( ((addr % get_erase_size(addr)) != 0 ) || (((addr + in_size) % get_erase_size(addr + in_size - 1)) != 0 ) ) { + tr_error("Invalid erase - unaligned address and size"); + return QSPIF_BD_ERROR_INVALID_ERASE_PARAMS; + } // For each iteration erase the largest section supported by current region while (size > 0) { - // 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, (int)addr, _region_high_boundary[region]); @@ -397,22 +408,22 @@ int QSPIFBlockDevice::erase(bd_addr_t addr, bd_size_t in_size) offset = addr % _erase_type_size_arr[type]; chunk = ( (offset + size) < _erase_type_size_arr[type]) ? size : (_erase_type_size_arr[type] - offset); - tr_debug("DEBUG: erase - addr: %llu, size:%d, Inst: 0x%xh, chunk: %lu , ", + tr_debug("Erase - addr: %llu, size:%d, Inst: 0x%xh, chunk: %lu ", addr, size, cur_erase_inst, chunk); - tr_debug("DEBUG: erase - Region: %d, Type:%d", + tr_debug("Erase - Region: %d, Type:%d ", region, type); _mutex.lock(); if (_set_write_enable() != 0) { - tr_error("ERROR: QSPI Erase Device not ready - failed"); + tr_error("QSPI Erase Device not ready - failed"); erase_failed = true; status = QSPIF_BD_ERROR_READY_FAILED; goto exit_point; } if (QSPI_STATUS_OK != _qspi_send_erase_command(cur_erase_inst, addr, size) ) { - tr_error("ERROR: QSPI Erase command failed!"); + tr_error("QSPI Erase command failed!"); erase_failed = true; status = QSPIF_BD_ERROR_DEVICE_ERROR; goto exit_point; @@ -428,7 +439,7 @@ int QSPIFBlockDevice::erase(bd_addr_t addr, bd_size_t in_size) } if ( false == _is_mem_ready()) { - tr_error("ERROR: QSPI After Erase Device not ready - failed\n"); + tr_error("QSPI After Erase Device not ready - failed"); erase_failed = true; status = QSPIF_BD_ERROR_READY_FAILED; goto exit_point; @@ -488,7 +499,7 @@ bd_size_t QSPIFBlockDevice::get_erase_size(bd_addr_t addr) } if (i_ind == 4) { - tr_error("ERROR: no erase type was found for region addr"); + tr_error("No erase type was found for region addr"); } } @@ -578,19 +589,19 @@ int QSPIFBlockDevice::_sfdp_parse_sector_map_table(uint32_t sector_map_table_add qspi_status_t status = _qspi_send_read_command(QSPIF_SFDP, (char *)sector_map_table, sector_map_table_addr /*address*/, sector_map_table_size); if (status != QSPI_STATUS_OK) { - tr_error("ERROR: init - Read SFDP First Table Failed"); + 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("ERROR: Sector Map - Supporting Only Single! Map Descriptor (not map commands)"); + tr_error("Sector Map - Supporting Only Single! Map Descriptor (not map commands)"); return -1; } _regions_count = sector_map_table[2] + 1; if (_regions_count > QSPIF_MAX_REGIONS) { - tr_error("ERROR: Supporting up to %d regions, current setup to %d regions - fail", + tr_error("Supporting up to %d regions, current setup to %d regions - fail", QSPIF_MAX_REGIONS, _regions_count); return -1; } @@ -631,13 +642,13 @@ int QSPIFBlockDevice::_sfdp_parse_basic_param_table(uint32_t basic_table_addr, s qspi_status_t status = _qspi_send_read_command(QSPIF_SFDP, (char *)param_table, basic_table_addr /*address*/, basic_table_size); if (status != QSPI_STATUS_OK) { - tr_error("ERROR: init - Read SFDP First Table Failed"); + tr_error("Init - Read SFDP First Table Failed"); return -1; } // Check address size, currently only supports 3byte addresses if ((param_table[2] & 0x4) != 0 || (param_table[7] & 0x80) != 0) { - tr_error("ERROR: init - verify 3byte addressing Failed"); + tr_error("Init - verify 3byte addressing Failed"); return -1; } @@ -662,23 +673,22 @@ int QSPIFBlockDevice::_sfdp_parse_basic_param_table(uint32_t basic_table_addr, s // Detect and Set Erase Types bool shouldSetQuadEnable = false; bool is_qpi_mode = false; + _sfdp_detect_erase_types_inst_and_size(param_table, basic_table_size, _erase4k_inst, _erase_type_inst_arr, _erase_type_size_arr); _erase_instruction = _erase4k_inst; - // Detect and Set fastest Bus mode (default 1-1-1) _sfdp_detect_best_bus_read_mode(param_table, basic_table_size, shouldSetQuadEnable, is_qpi_mode, _read_instruction); - if (true == shouldSetQuadEnable) { // Set Quad Enable and QPI Bus modes if Supported - tr_info("INFO: init - Setting Quad Enable"); + tr_info("Init - Setting Quad Enable"); if (0 != _sfdp_set_quad_enabled(param_table)) { - tr_error("ERROR: Device supports Quad bus, but Quad Enable Failed"); + tr_error("Device supports Quad bus, but Quad Enable Failed"); return -1; } if (true == is_qpi_mode) { - tr_info("INFO: init - Setting QPI mode"); + tr_info("Init - Setting QPI mode"); _sfdp_set_qpi_enabled(param_table); } } @@ -699,22 +709,22 @@ int QSPIFBlockDevice::_sfdp_parse_sfdp_headers(uint32_t& basic_table_addr, size_ qspi_status_t status = _qspi_send_read_command(QSPIF_SFDP, (char *)sfdp_header, addr /*address*/, data_length); if (status != QSPI_STATUS_OK) { - tr_error("ERROR: init - Read SFDP Failed"); + tr_error("Init - Read SFDP Failed"); return -1; } // Verify SFDP signature for sanity // Also check that major/minor version is acceptable if (!(memcmp(&sfdp_header[0], "SFDP", 4) == 0 && sfdp_header[5] == 1)) { - tr_error("ERROR: init - _verify SFDP signature and version Failed"); + tr_error("Init - _verify SFDP signature and version Failed"); return -1; } else { - tr_info("INFO: init - verified SFDP Signature and version Successfully"); + tr_info("Init - verified SFDP Signature and version Successfully"); } // Discover Number of Parameter Headers int number_of_param_headers = (int)(sfdp_header[6]) + 1; - tr_debug("DEBUG: number of Param Headers: %d", number_of_param_headers); + tr_debug("Number of Param Headers: %d", number_of_param_headers); addr += QSPIF_SFDP_HEADER_SIZE; @@ -725,27 +735,27 @@ int QSPIFBlockDevice::_sfdp_parse_sfdp_headers(uint32_t& basic_table_addr, size_ status = _qspi_send_read_command(QSPIF_SFDP, (char *)param_header, addr, data_length); if (status != QSPI_STATUS_OK) { - tr_error("ERROR: init - Read Param Table %d Failed", i_ind + 1); + tr_error("Init - Read Param Table %d Failed", i_ind + 1); return -1; } // The SFDP spec indicates the standard table is always at offset 0 // in the parameter headers, we check just to be safe if (param_header[2] != 1) { - tr_error("ERROR: Param Table %d - Major Version should be 1!", i_ind + 1); + tr_error("Param Table %d - Major Version should be 1!", i_ind + 1); return -1; } if ((param_header[0] == 0) && (param_header[7] == 0xFF)) { // Found Basic Params Table: LSB=0x00, MSB=0xFF - tr_debug("DEBUG: Found Basic Param Table at Table: %d", i_ind + 1); + tr_debug("Found Basic Param Table at Table: %d", i_ind + 1); basic_table_addr = ( (param_header[6] << 16) | (param_header[5] << 8) | (param_header[4]) ); // Supporting up to 64 Bytes Table (16 DWORDS) basic_table_size = ((param_header[3] * 4) < SFDP_DEFAULT_BASIC_PARAMS_TABLE_SIZE_BYTES) ? (param_header[3] * 4) : 64; } else if ((param_header[0] == 81) && (param_header[7] == 0xFF)) { // Found Sector Map Table: LSB=0x81, MSB=0xFF - tr_debug("DEBUG: Found Sector Map Table at Table: %d", i_ind + 1); + tr_debug("Found Sector Map Table at Table: %d", i_ind + 1); sector_map_table_addr = ( (param_header[6] << 16) | (param_header[5] << 8) | (param_header[4]) ); sector_map_table_size = param_header[3] * 4; @@ -767,17 +777,17 @@ int QSPIFBlockDevice::_sfdp_set_qpi_enabled(uint8_t *basic_param_table_ptr) switch (en_seq_444_value) { case 1: case 2: - tr_debug("DEBUG: _setQPIEnabled - send command 38h"); + tr_debug("_setQPIEnabled - send command 38h"); _qspi_send_general_command(0x38, QSPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0); break; case 4: - tr_debug("DEBUG: _setQPIEnabled - send command 35h"); + tr_debug("_setQPIEnabled - send command 35h"); _qspi_send_general_command(0x35, QSPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0); break; case 8: - tr_debug("DEBUG: _setQPIEnabled - set config bit 6 and send command 71h"); + tr_debug("_setQPIEnabled - set config bit 6 and send command 71h"); _qspi_send_general_command(0x65, 0x800003, NULL, 0, (char *)config_reg, 1); config_reg[0] |= 0x40; //Set Bit 6 _qspi_send_general_command(0x71, 0x800003, NULL, 0, (char *)config_reg, 1); @@ -791,7 +801,7 @@ int QSPIFBlockDevice::_sfdp_set_qpi_enabled(uint8_t *basic_param_table_ptr) break; default: - tr_warning("WARNING: _setQPIEnabled - Unsuported En Seq 444 configuration"); + tr_warning("_setQPIEnabled - Unsuported En Seq 444 configuration"); break; } @@ -802,12 +812,11 @@ int QSPIFBlockDevice::_sfdp_set_qpi_enabled(uint8_t *basic_param_table_ptr) int QSPIFBlockDevice::_sfdp_set_quad_enabled(uint8_t *basic_param_table_ptr) { - int sr_read_size = QSPI_MAX_STATUS_REGISTER_SIZE; int sr_write_size = QSPI_MAX_STATUS_REGISTER_SIZE; - int status_reg_setup[QSPI_MAX_STATUS_REGISTER_SIZE] = {0}; - uint8_t status_reg[QSPI_MAX_STATUS_REGISTER_SIZE]; + char status_reg_setup[QSPI_MAX_STATUS_REGISTER_SIZE] = {0}; + char status_reg[QSPI_MAX_STATUS_REGISTER_SIZE] = {0}; unsigned int write_register_inst = QSPIF_WRSR; unsigned int read_register_inst = QSPIF_RDSR; @@ -817,79 +826,76 @@ int QSPIFBlockDevice::_sfdp_set_quad_enabled(uint8_t *basic_param_table_ptr) switch (qer_value) { case 0: - tr_debug("DEBUG: Device Does not Have a QE Bit, continue based on Read Inst"); + tr_debug("Device Does not Have a QE Bit, continue based on Read Inst"); return 0; case 1: case 4: - status_reg_setup[0] = 0; status_reg_setup[1] = 0x02; //Bit 1 of Status Reg 2 - tr_debug("DEBUG: Setting QE Bit, Bit 1 of Status Reg 2"); + tr_debug("Setting QE Bit, Bit 1 of Status Reg 2"); break; case 2: status_reg_setup[0] = 0x40; // Bit 6 of Status Reg 1 - status_reg_setup[1] = 0; sr_write_size = 1; - sr_read_size = 1; - tr_debug("DEBUG: Setting QE Bit, Bit 6 of Status Reg 1"); + tr_debug("Setting QE Bit, Bit 6 of Status Reg 1"); break; case 3: status_reg_setup[0] = 0x80; // Bit 7 of Status Reg 1 - status_reg_setup[1] = 0; sr_write_size = 1; - sr_read_size = 1; write_register_inst = 0x3E; read_register_inst = 0x3F; - tr_debug("DEBUG: Setting QE Bit, Bit 7 of Status Reg 1"); + tr_debug("Setting QE Bit, Bit 7 of Status Reg 1"); break; case 5: - status_reg_setup[0] = 0; - status_reg_setup[1] = 0x02; //Bit 1 of status Reg 2 + status_reg_setup[1] = 0x2; // Bit 1 of status Reg 2 read_register_inst = 0x35; sr_read_size = 1; - tr_debug("DEBUG: Setting QE Bit, Bit 1 of Status Reg 2 -special read command"); + tr_debug("Setting QE Bit, Bit 1 of Status Reg 2 -special read command"); break; default: - tr_warning("WARNING: _setQuadEnable - Unsuported QER configuration"); + tr_warning("_setQuadEnable - Unsuported QER configuration"); break; - - } + // Configure BUS Mode to 1_1_1 for all commands other than Read + _qspi_configure_format(QSPI_CFG_BUS_SINGLE, QSPI_CFG_BUS_SINGLE, QSPI_CFG_ADDR_SIZE_24, QSPI_CFG_BUS_SINGLE, + QSPI_CFG_ALT_SIZE_8, QSPI_CFG_BUS_SINGLE, 0); + // Read Status Register if (QSPI_STATUS_OK == _qspi_send_general_command(read_register_inst, QSPI_NO_ADDRESS_COMMAND, NULL, 0, - (char *)status_reg, + status_reg, sr_read_size) ) { // store received values in status_value - tr_debug("DEBUG: Reading Status Register Success: value = 0x%x\n", (int)status_reg[0]); + tr_debug("Reading Status Register Success: value = 0x%x", (int)status_reg[0]); } else { - tr_error("ERROR: Reading Status Register failed"); + tr_error("Reading Status Register failed"); return -1; } // Set Bits for Quad Enable - status_reg[0] |= status_reg_setup[0]; - status_reg[1] |= status_reg_setup[1]; + for (int i = 0; i < QSPI_MAX_STATUS_REGISTER_SIZE; i++) { + status_reg[i] |= status_reg_setup[i]; + } // Write new Status Register Setup if (_set_write_enable() != 0) { - tr_error("ERROR: Write Enabe failed\n"); + tr_error("Write Enabe failed"); return -1; } if (QSPI_STATUS_OK == _qspi_send_general_command(write_register_inst, QSPI_NO_ADDRESS_COMMAND, (char *)status_reg, sr_write_size, NULL, 0) ) { // Write QE to status_register - tr_debug("DEBUG: _setQuadEnable - Writing Status Register Success: value = 0x%x", + tr_debug("_setQuadEnable - Writing Status Register Success: value = 0x%x", (int)status_reg[0]); } else { - tr_error("ERROR: _setQuadEnable - Writing Status Register failed"); + tr_error("_setQuadEnable - Writing Status Register failed"); return -1; } if ( false == _is_mem_ready()) { - tr_error("ERROR: Device not ready after write, failed"); + tr_error("Device not ready after write, failed"); return -1; } @@ -899,9 +905,9 @@ int QSPIFBlockDevice::_sfdp_set_quad_enabled(uint8_t *basic_param_table_ptr) if (QSPI_STATUS_OK == _qspi_send_general_command(read_register_inst, QSPI_NO_ADDRESS_COMMAND, NULL, 0, (char *)status_reg, sr_read_size) ) { // store received values in status_value - tr_debug("DEBUG: Reading Status Register Success: value = 0x%x\n", (int)status_reg[0]); + tr_debug("Reading Status Register Success: value = 0x%x", (int)status_reg[0]); } else { - tr_error("ERROR: Reading Status Register failed"); + tr_error("Reading Status Register failed"); return -1; } @@ -916,9 +922,9 @@ int QSPIFBlockDevice::_sfdp_detect_page_size(uint8_t *basic_param_table_ptr, int // Page Size is specified by 4 Bits (N), calculated by 2^N int page_to_power_size = ( (int)basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_PAGE_SIZE_BYTE]) >> 4; page_size = local_math_power(2, page_to_power_size); - tr_debug("DEBUG: Detected Page Size: %d", page_size); + tr_debug("Detected Page Size: %d", page_size); } else { - tr_debug("DEBUG: Using Default Page Size: %d", page_size); + tr_debug("Using Default Page Size: %d", page_size); } return page_size; } @@ -940,7 +946,7 @@ int QSPIFBlockDevice::_sfdp_detect_erase_types_inst_and_size(uint8_t *basic_para 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[QSPIF_BASIC_PARAM_ERASE_TYPE_1_SIZE_BYTE + 2 * i_ind]); // Size given as 2^N - tr_info("DEBUG: Erase Type(A) %d - Inst: 0x%xh, Size: %d", (i_ind + 1), erase_type_inst_arr[i_ind], + tr_info("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) { // if size==1 type is not supported @@ -957,21 +963,21 @@ int QSPIFBlockDevice::_sfdp_detect_erase_types_inst_and_size(uint8_t *basic_para if (erase4k_inst != 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]; - tr_warning("WARNING: _detectEraseTypesInstAndSize - Default 4K erase Inst is different than erase type Inst for 4K"); + 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; } - tr_info("INFO: Erase Type %d - Inst: 0x%xh, Size: %d", (i_ind + 1), erase_type_inst_arr[i_ind], + 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]); bitfield = bitfield << 1; } } if (false == found_4Kerase_type) { - tr_warning("WARNING: Couldn't find Erase Type for 4KB size"); + tr_warning("Couldn't find Erase Type for 4KB size"); } return 0; } @@ -986,9 +992,7 @@ int QSPIFBlockDevice::_sfdp_detect_best_bus_read_mode(uint8_t *basic_param_table do { // compound statement is the loop body - if (basic_param_table_size > QSPIF_BASIC_PARAM_TABLE_QPI_READ_SUPPORT_BYTE) { - examined_byte = basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_QPI_READ_SUPPORT_BYTE]; if (examined_byte & 0x10) { @@ -998,7 +1002,7 @@ int QSPIFBlockDevice::_sfdp_detect_best_bus_read_mode(uint8_t *basic_param_table is_qpi_mode = true; _dummy_and_mode_cycles = (basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_444_READ_INST_BYTE - 1] >> 5) + (basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_444_READ_INST_BYTE - 1] & 0x1F); - tr_debug("/nDEBUG: Read Bus Mode set to 4-4-4, Instruction: 0x%xh", _read_instruction); + tr_debug("Read Bus Mode set to 4-4-4, Instruction: 0x%xh", _read_instruction); //_inst_width = QSPI_CFG_BUS_QUAD; _address_width = QSPI_CFG_BUS_QUAD; _data_width = QSPI_CFG_BUS_QUAD; @@ -1018,9 +1022,10 @@ int QSPIFBlockDevice::_sfdp_detect_best_bus_read_mode(uint8_t *basic_param_table + (basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_144_READ_INST_BYTE - 1] & 0x1F); _address_width = QSPI_CFG_BUS_QUAD; _data_width = QSPI_CFG_BUS_QUAD; - tr_debug("/nDEBUG: Read Bus Mode set to 1-4-4, Instruction: 0x%xh", _read_instruction); + tr_debug("Read Bus Mode set to 1-4-4, Instruction: 0x%xh", _read_instruction); break; } + if (examined_byte & 0x80) { // Fast Read 1-1-4 Supported read_inst = basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_114_READ_INST_BYTE]; @@ -1028,7 +1033,7 @@ int QSPIFBlockDevice::_sfdp_detect_best_bus_read_mode(uint8_t *basic_param_table _dummy_and_mode_cycles = (basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_114_READ_INST_BYTE - 1] >> 5) + (basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_114_READ_INST_BYTE - 1] & 0x1F); _data_width = QSPI_CFG_BUS_QUAD; - tr_debug("/nDEBUG: Read Bus Mode set to 1-1-4, Instruction: 0x%xh", _read_instruction); + tr_debug("Read Bus Mode set to 1-1-4, Instruction: 0x%xh", _read_instruction); break; } examined_byte = basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_QPI_READ_SUPPORT_BYTE]; @@ -1039,7 +1044,7 @@ int QSPIFBlockDevice::_sfdp_detect_best_bus_read_mode(uint8_t *basic_param_table + (basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_222_READ_INST_BYTE - 1] & 0x1F); _address_width = QSPI_CFG_BUS_DUAL; _data_width = QSPI_CFG_BUS_DUAL; - tr_info("/nINFO: Read Bus Mode set to 2-2-2, Instruction: 0x%xh", _read_instruction); + tr_info("Read Bus Mode set to 2-2-2, Instruction: 0x%xh", _read_instruction); break; } @@ -1051,7 +1056,7 @@ int QSPIFBlockDevice::_sfdp_detect_best_bus_read_mode(uint8_t *basic_param_table + (basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_122_READ_INST_BYTE - 1] & 0x1F); _address_width = QSPI_CFG_BUS_DUAL; _data_width = QSPI_CFG_BUS_DUAL; - tr_debug("/nDEBUG: Read Bus Mode set to 1-2-2, Instruction: 0x%xh", _read_instruction); + tr_debug("Read Bus Mode set to 1-2-2, Instruction: 0x%xh", _read_instruction); break; } if (examined_byte & 0x01) { @@ -1060,10 +1065,10 @@ int QSPIFBlockDevice::_sfdp_detect_best_bus_read_mode(uint8_t *basic_param_table _dummy_and_mode_cycles = (basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_112_READ_INST_BYTE - 1] >> 5) + (basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_112_READ_INST_BYTE - 1] & 0x1F); _data_width = QSPI_CFG_BUS_DUAL; - tr_debug("/nDEBUG: Read Bus Mode set to 1-1-2, Instruction: 0x%xh", _read_instruction); + tr_debug("Read Bus Mode set to 1-1-2, Instruction: 0x%xh", _read_instruction); break; } - tr_debug("/nDEBUG: Read Bus Mode set to 1-1-1, Instruction: 0x%xh", _read_instruction); + tr_debug("Read Bus Mode set to 1-1-1, Instruction: 0x%xh", _read_instruction); } while (false); return 0; @@ -1073,14 +1078,14 @@ int QSPIFBlockDevice::_reset_flash_mem() { // Perform Soft Reset of the Device prior to initialization int status = 0; - char status_value[2] = {0}; - tr_info("INFO: _reset_flash_mem:\n"); + char status_value[QSPI_MAX_STATUS_REGISTER_SIZE] = {0}; + tr_info("_reset_flash_mem:"); //Read the Status Register from device if (QSPI_STATUS_OK == _qspi_send_general_command(QSPIF_RDSR, QSPI_NO_ADDRESS_COMMAND, NULL, 0, status_value, - 1) ) { // store received values in status_value - tr_debug("DEBUG: Reading Status Register Success: value = 0x%x\n", (int)status_value[0]); + QSPI_MAX_STATUS_REGISTER_SIZE) ) { // store received values in status_value + tr_debug("Reading Status Register Success: value = 0x%x", (int)status_value[0]); } else { - tr_debug("ERROR: Reading Status Register failed\n"); + tr_debug("Reading Status Register failed: value = 0x%x", (int)status_value[0]); status = -1; } @@ -1088,9 +1093,9 @@ int QSPIFBlockDevice::_reset_flash_mem() //Send Reset Enable if (QSPI_STATUS_OK == _qspi_send_general_command(QSPIF_RSTEN, QSPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0) ) { // store received values in status_value - tr_debug("DEBUG: Sending RSTEN Success\n"); + tr_debug("Sending RSTEN Success"); } else { - tr_error("ERROR: Sending RSTEN failed\n"); + tr_error("Sending RSTEN failed"); status = -1; } @@ -1099,9 +1104,9 @@ int QSPIFBlockDevice::_reset_flash_mem() //Send Reset if (QSPI_STATUS_OK == _qspi_send_general_command(QSPIF_RST, QSPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0)) { // store received values in status_value - tr_debug("DEBUG: Sending RST Success\n"); + tr_debug("Sending RST Success"); } else { - tr_error("ERROR: Sending RST failed\n"); + tr_error("Sending RST failed"); status = -1; } @@ -1115,7 +1120,7 @@ int QSPIFBlockDevice::_reset_flash_mem() bool QSPIFBlockDevice::_is_mem_ready() { // Check Status Register Busy Bit to Verify the Device isn't Busy - char status_value[2]; + char status_value[QSPI_MAX_STATUS_REGISTER_SIZE]; int retries = 0; bool mem_ready = true; @@ -1123,14 +1128,15 @@ bool QSPIFBlockDevice::_is_mem_ready() wait_ms(1); retries++; //Read the Status Register from device + memset(status_value, 0xFF, QSPI_MAX_STATUS_REGISTER_SIZE); if (QSPI_STATUS_OK != _qspi_send_general_command(QSPIF_RDSR, QSPI_NO_ADDRESS_COMMAND, NULL, 0, status_value, - 2)) { // store received values in status_value - tr_error("ERROR: Reading Status Register failed\n"); + QSPI_MAX_STATUS_REGISTER_SIZE)) { // store received values in status_value + tr_error("Reading Status Register failed"); } } while ( (status_value[0] & QSPIF_STATUS_BIT_WIP) != 0 && retries < IS_MEM_READY_MAX_RETRIES ); if ((status_value[0] & QSPIF_STATUS_BIT_WIP) != 0) { - tr_error("ERROR: _is_mem_ready FALSE\n"); + tr_error("_is_mem_ready FALSE: status value = 0x%x ", (int)status_value[0]); mem_ready = false; } return mem_ready; @@ -1139,29 +1145,29 @@ bool QSPIFBlockDevice::_is_mem_ready() int QSPIFBlockDevice::_set_write_enable() { // Check Status Register Busy Bit to Verify the Device isn't Busy - char status_value[2]; + char status_value[QSPI_MAX_STATUS_REGISTER_SIZE]; int status = -1; do { if (QSPI_STATUS_OK != _qspi_send_general_command(QSPIF_WREN, QSPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0)) { - tr_error("ERROR:Sending WREN command FAILED\n"); + tr_error("Sending WREN command FAILED"); break; } if ( false == _is_mem_ready()) { - tr_error("ERROR: Device not ready, write failed"); + tr_error("Device not ready, write failed"); break; } - memset(status_value, 0, 2); + memset(status_value, 0, QSPI_MAX_STATUS_REGISTER_SIZE); if (QSPI_STATUS_OK != _qspi_send_general_command(QSPIF_RDSR, QSPI_NO_ADDRESS_COMMAND, NULL, 0, status_value, - 2)) { // store received values in status_value - tr_error("ERROR: Reading Status Register failed\n"); + QSPI_MAX_STATUS_REGISTER_SIZE)) { // store received values in status_value + tr_error("Reading Status Register failed"); break; } if ((status_value[0] & QSPIF_STATUS_BIT_WEL) == 0) { - tr_error("ERROR: _set_write_enable failed\n"); + tr_error("_set_write_enable failed"); break; } status = 0; @@ -1214,7 +1220,7 @@ int QSPIFBlockDevice::_utils_iterate_next_largest_erase_type(uint8_t& bitfield, } if (i_ind == 4) { - tr_error("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; @@ -1235,7 +1241,7 @@ qspi_status_t QSPIFBlockDevice::_qspi_send_read_command(unsigned int read_inst, size_t buf_len = size; if (_qspi.read(read_inst, -1, (unsigned int )addr, (char *)buffer, &buf_len) != QSPI_STATUS_OK ) { - tr_error("ERROR: Read failed"); + tr_error("Read failed"); return QSPI_STATUS_ERROR; } @@ -1251,7 +1257,7 @@ qspi_status_t QSPIFBlockDevice::_qspi_send_program_command(unsigned int progInst result = _qspi.write(progInst, -1, addr, (char *)buffer, (size_t *)size); if (result != QSPI_STATUS_OK) { - tr_error("ERROR: QSPI Write failed"); + tr_error("QSPI Write failed"); } return result; @@ -1262,7 +1268,7 @@ qspi_status_t QSPIFBlockDevice::_qspi_send_erase_command(unsigned int erase_inst // Send Erase Instruction command to driver qspi_status_t result = QSPI_STATUS_OK; - tr_info("INFO: Inst: 0x%xh, addr: %llu, size: %llu", erase_inst, addr, size); + tr_info("Inst: 0x%xh, addr: %llu, size: %llu", erase_inst, addr, size); result = _qspi.command_transfer(erase_inst, // command to send (((int)addr) & 0x00FFF000), // Align addr to 4096 @@ -1272,7 +1278,7 @@ qspi_status_t QSPIFBlockDevice::_qspi_send_erase_command(unsigned int erase_inst 0); // store received values in status_value if (QSPI_STATUS_OK != result) { - tr_error("ERROR: QSPI Erase failed"); + tr_error("QSPI Erase failed"); } return result; @@ -1287,7 +1293,7 @@ qspi_status_t QSPIFBlockDevice::_qspi_send_general_command(unsigned int instruct qspi_status_t status = _qspi.command_transfer(instruction, (int)addr, tx_buffer, tx_length, rx_buffer, rx_length); if (QSPI_STATUS_OK != status) { - tr_error("ERROR:Sending Generic command: %x", instruction); + tr_error("Sending Generic command: %x", instruction); } return status; @@ -1317,7 +1323,3 @@ static int local_math_power(int base, int exp) } return result; } - - - - diff --git a/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h b/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h index 41297e9294..93814de0d1 100644 --- a/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h +++ b/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h @@ -19,7 +19,6 @@ #include "QSPI.h" #include "BlockDevice.h" - namespace mbed { /** Enum qspif standard error codes @@ -27,13 +26,14 @@ namespace mbed { * @enum qspif_bd_error */ enum qspif_bd_error { - QSPIF_BD_ERROR_OK = 0, /*!< no error */ - QSPIF_BD_ERROR_DEVICE_ERROR = BD_ERROR_DEVICE_ERROR, /*!< device specific error -4001 */ - QSPIF_BD_ERROR_PARSING_FAILED = -4002, /* SFDP Parsing failed */ - QSPIF_BD_ERROR_READY_FAILED = -4003, /* Wait for Mem Ready failed */ - QSPIF_BD_ERROR_WREN_FAILED = -4004, /* Write Enable Failed */ - QSPIF_BD_ERROR_DEVICE_NOT_UNIQE = -4005, /* Only one instance per csel is allowed */ - QSPIF_BD_ERROR_DEVICE_MAX_EXCEED = -4006 /* Max active QSPIF devices exceeded */ + QSPIF_BD_ERROR_OK = 0, /*!< no error */ + QSPIF_BD_ERROR_DEVICE_ERROR = BD_ERROR_DEVICE_ERROR, /*!< device specific error -4001 */ + QSPIF_BD_ERROR_PARSING_FAILED = -4002, /* SFDP Parsing failed */ + QSPIF_BD_ERROR_READY_FAILED = -4003, /* Wait for Mem Ready failed */ + QSPIF_BD_ERROR_WREN_FAILED = -4004, /* Write Enable Failed */ + QSPIF_BD_ERROR_INVALID_ERASE_PARAMS = -4005, /* Erase command not on sector aligned addresses or exceeds device size */ + QSPIF_BD_ERROR_DEVICE_NOT_UNIQE = -4006, /* Only one instance per csel is allowed */ + QSPIF_BD_ERROR_DEVICE_MAX_EXCEED = -4007 /* Max active QSPIF devices exceeded */ }; /** Enum qspif polarity mode @@ -103,8 +103,7 @@ public: * */ QSPIFBlockDevice(PinName io0, PinName io1, PinName io2, PinName io3, PinName sclk, PinName csel, - qspif_polarity_mode clock_mode, - int freq = 40000000); + int clock_mode, int freq = 40000000); /** Initialize a block device @@ -163,6 +162,7 @@ public: * QSPIF_BD_ERROR_READY_FAILED - Waiting for Memory ready failed or timed out * QSPIF_BD_ERROR_WREN_FAILED - Write Enable failed * QSPIF_BD_ERROR_PARSING_FAILED - unexpected format or values in one of the SFDP tables + * QSPIF_BD_ERROR_INVALID_ERASE_PARAMS - Trying to erase unaligned address or size */ virtual int erase(bd_addr_t addr, bd_size_t size); diff --git a/components/storage/blockdevice/COMPONENT_QSPIF/README.md b/components/storage/blockdevice/COMPONENT_QSPIF/README.md index 8808b9bd5b..e678a2c86b 100644 --- a/components/storage/blockdevice/COMPONENT_QSPIF/README.md +++ b/components/storage/blockdevice/COMPONENT_QSPIF/README.md @@ -8,39 +8,78 @@ SFDP based QSPI Flash supports variable bus modes (single, dual, quad), several SFDP JEDEC standard can be found in: https://www.jedec.org/system/files/docs/JESD216B.pdf +### Debugging +Set `MBED_CONF_MBED_TRACE_ENABLE` as true(1) to enable logs. + +### Example + ``` cpp // QSPI SFDP Flash - Block Device example #include "mbed.h" #include "QSPIFBlockDevice.h" QSPIFBlockDevice block_device(MBED_CONF_QSPIF_QSPI_IO0,MBED_CONF_QSPIF_QSPI_IO1,MBED_CONF_QSPIF_QSPI_IO2,MBED_CONF_QSPIF_QSPI_IO3, - MBED_CONF_QSPIF_QSPI_CLK,MBED_CONF_QSPIF_QSPI_CS,0,MBED_CONF_QSPIF_QSPI_FREQ); + MBED_CONF_QSPIF_QSPI_CLK,MBED_CONF_QSPIF_QSPI_CS,QSPIF_POLARITY_MODE_0,MBED_CONF_QSPIF_QSPI_FREQ); int main() { + +#if defined(MBED_CONF_MBED_TRACE_ENABLE) && MBED_CONF_MBED_TRACE_ENABLE + mbed_trace_init(); +#endif printf("QSPI SFDP Flash Block Device example\n"); + int status = 0; // Initialize the SPI flash device and print the memory layout - block_device.init(); + status = block_device.init(); + if (0 != status) { + printf("QSPI Flash init failed \n"); + return status; + } bd_size_t sector_size_at_address_0 = block_device.get_erase_size(0); printf("QSPIF BD size: %llu\n", block_device.size()); printf("QSPIF BD read size: %llu\n", block_device.get_read_size()); printf("QSPIF BD program size: %llu\n", block_device.get_program_size()); - printf("QSPIF BD erase size (at address 0): %llu\n", sector_size_at_address_0); // Write "Hello World!" to the first block char *buffer = (char*) malloc(sector_size_at_address_0); + if (buffer == NULL) { + printf("Malloc failed\n"); + return -1; + } sprintf(buffer, "Hello World!\n"); - block_device.erase(0, sector_size_at_address_0); - block_device.program(buffer, 0, sector_size_at_address_0); + + int address = 0; + status = block_device.erase(address, sector_size_at_address_0); + if (0 != status) { + printf("QSPI Flash Erase failed, address = 0x%x size = %lld\n", address, sector_size_at_address_0); + return status; + } + + status = block_device.program(buffer, address, sector_size_at_address_0); + if (0 != status) { + printf("QSPI Flash program failed, address = 0x%x size = %lld\n", address, sector_size_at_address_0); + return status; + } // Read back what was stored - block_device.read(buffer, 0, sector_size_at_address_0); - printf("%s", buffer); + status = block_device.read(buffer, address, sector_size_at_address_0); + if (0 != status) { + printf("QSPI Flash read failed, address = 0x%x size = %lld\n", address, sector_size_at_address_0); + return status; + } + + printf("%s\n", buffer); // Deinitialize the device - block_device.deinit(); + status = block_device.deinit(); + if (0 != status) { + printf("QSPI Flash deinit failed \n"); + return status; + } + printf ("Finished \n"); + return 0; } ``` diff --git a/components/storage/blockdevice/COMPONENT_QSPIF/TESTS/block_device/qspif/main.cpp b/components/storage/blockdevice/COMPONENT_QSPIF/TESTS/block_device/qspif/main.cpp index 9c4862b97f..c792cbf507 100644 --- a/components/storage/blockdevice/COMPONENT_QSPIF/TESTS/block_device/qspif/main.cpp +++ b/components/storage/blockdevice/COMPONENT_QSPIF/TESTS/block_device/qspif/main.cpp @@ -132,10 +132,10 @@ end: delete[] read_block; } -void test_qspif_unaligned_program() +void test_qspif_unaligned_erase() { - utest_printf("\nTest Unaligned Program Starts..\n"); + utest_printf("\nTest Unaligned Erase Starts..\n"); QSPIFBlockDevice blockD(QSPI_FLASH1_IO0, QSPI_FLASH1_IO1, QSPI_FLASH1_IO2, QSPI_FLASH1_IO3, QSPI_FLASH1_SCK, QSPI_FLASH1_CSN, QSPIF_POLARITY_MODE_0, MBED_CONF_QSPIF_QSPI_FREQ); @@ -155,53 +155,42 @@ void test_qspif_unaligned_program() } } - bd_size_t block_size = blockD.get_erase_size(); + bd_addr_t addr = 0; + bd_size_t sector_erase_size = blockD.get_erase_size(addr); unsigned addrwidth = ceil(log(float(blockD.size() - 1)) / log(float(16))) + 1; - uint8_t *write_block = new (std::nothrow) uint8_t[block_size]; - uint8_t *read_block = new (std::nothrow) uint8_t[block_size]; - if (!write_block || !read_block ) { - utest_printf("\n Not enough memory for test"); - goto end; - } + utest_printf("\ntest %0*llx:%llu...", addrwidth, addr, sector_erase_size); - { - bd_addr_t block = (rand() * block_size) % blockD.size() + 15; + //unaligned start address + addr += 1; + err = blockD.erase(addr, sector_erase_size - 1); + TEST_ASSERT_EQUAL(QSPIF_BD_ERROR_INVALID_ERASE_PARAMS, err); - // Use next random number as temporary seed to keep - // the address progressing in the pseudorandom sequence - unsigned seed = rand(); + err = blockD.erase(addr, sector_erase_size); + TEST_ASSERT_EQUAL(QSPIF_BD_ERROR_INVALID_ERASE_PARAMS, err); - // Fill with random sequence - srand(seed); - for (bd_size_t i_ind = 0; i_ind < block_size; i_ind++) { - write_block[i_ind] = 0xff & rand(); - } + err = blockD.erase(addr, 1); + TEST_ASSERT_EQUAL(QSPIF_BD_ERROR_INVALID_ERASE_PARAMS, err); - // Write, sync, and read the block - utest_printf("\ntest %0*llx:%llu...", addrwidth, block, block_size); + //unaligned end address + addr = 0; - err = blockD.erase(block, block_size); - TEST_ASSERT_EQUAL(0, err); + err = blockD.erase(addr, 1); + TEST_ASSERT_EQUAL(QSPIF_BD_ERROR_INVALID_ERASE_PARAMS, err); - err = blockD.program(write_block, block, block_size); - TEST_ASSERT_EQUAL(0, err); + err = blockD.erase(addr, sector_erase_size + 1); + TEST_ASSERT_EQUAL(QSPIF_BD_ERROR_INVALID_ERASE_PARAMS, err); - err = blockD.read(read_block, block, block_size); - TEST_ASSERT_EQUAL(0, err); + //erase size exceeds flash device size + err = blockD.erase(addr, blockD.size() + 1); + TEST_ASSERT_EQUAL(QSPIF_BD_ERROR_INVALID_ERASE_PARAMS, err); - // Check that the data was unmodified - srand(seed); - for (bd_size_t i_ind = 0; i_ind < block_size; i_ind++) { - TEST_ASSERT_EQUAL(0xff & rand(), read_block[i_ind]); - } + // Valid erase + err = blockD.erase(addr, sector_erase_size); + TEST_ASSERT_EQUAL(QSPIF_BD_ERROR_OK, err); - err = blockD.deinit(); - TEST_ASSERT_EQUAL(0, err); - } -end: - delete[] write_block; - delete[] read_block; + err = blockD.deinit(); + TEST_ASSERT_EQUAL(0, err); } @@ -286,7 +275,7 @@ utest::v1::status_t test_setup(const size_t number_of_cases) } Case cases[] = { - Case("Testing unaligned program blocks", test_qspif_unaligned_program), + Case("Testing unaligned erase blocks", test_qspif_unaligned_erase), Case("Testing read write random blocks", test_qspif_random_program_read_erase), Case("Testing Multi Threads Erase Program Read", test_qspif_multi_threads) }; diff --git a/targets/targets.json b/targets/targets.json index e8859694be..7fb33f4afe 100755 --- a/targets/targets.json +++ b/targets/targets.json @@ -1233,6 +1233,7 @@ } }, "DISCO_F413ZH": { + "components": ["QSPIF"], "inherits": ["FAMILY_STM32"], "supported_form_factors": ["ARDUINO"], "core": "Cortex-M4F", @@ -2058,6 +2059,7 @@ "bootloader_supported": true }, "DISCO_L476VG": { + "components": ["QSPIF"], "inherits": ["FAMILY_STM32"], "core": "Cortex-M4F", "extra_labels_add": ["STM32L4", "STM32L476xG", "STM32L476VG"], From 52dc2e58ffbb51072401f68f3dd25a24ec4a56be Mon Sep 17 00:00:00 2001 From: Offir Kochalsky Date: Tue, 9 Oct 2018 15:37:31 +0300 Subject: [PATCH 05/12] Added more boards with QSPI component default --- targets/targets.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/targets/targets.json b/targets/targets.json index 7fb33f4afe..2b3e5433d4 100755 --- a/targets/targets.json +++ b/targets/targets.json @@ -1903,6 +1903,7 @@ "bootloader_supported": true }, "DISCO_F469NI": { + "components": ["QSPIF"], "inherits": ["FAMILY_STM32"], "supported_form_factors": ["ARDUINO"], "core": "Cortex-M4F", @@ -2036,6 +2037,7 @@ } }, "DISCO_L475VG_IOT01A": { + "components": ["QSPIF"], "inherits": ["FAMILY_STM32"], "core": "Cortex-M4F", "extra_labels_add": ["STM32L4", "STM32L475xG", "STM32L475VG"], From dc4557939f2b844aa7d20c9b2fb027679b5c8853 Mon Sep 17 00:00:00 2001 From: Offir Kochalsky Date: Wed, 10 Oct 2018 16:36:40 +0300 Subject: [PATCH 06/12] Remove DISCO-L476VG-IOT01A board from QSPI test (IOTSTOR-666 Issue) --- targets/targets.json | 1 - 1 file changed, 1 deletion(-) diff --git a/targets/targets.json b/targets/targets.json index 2b3e5433d4..ae75dbc1bd 100755 --- a/targets/targets.json +++ b/targets/targets.json @@ -2037,7 +2037,6 @@ } }, "DISCO_L475VG_IOT01A": { - "components": ["QSPIF"], "inherits": ["FAMILY_STM32"], "core": "Cortex-M4F", "extra_labels_add": ["STM32L4", "STM32L475xG", "STM32L475VG"], From 9b3bca89fd8295147155db035e366abd8b9c3c01 Mon Sep 17 00:00:00 2001 From: Offir Kochalsky Date: Sun, 14 Oct 2018 08:54:09 +0300 Subject: [PATCH 07/12] Enable first fast mode then quad for chips with low power default --- .../COMPONENT_QSPIF/QSPIFBlockDevice.cpp | 82 +++++++++++++++++-- .../COMPONENT_QSPIF/QSPIFBlockDevice.h | 4 + targets/targets.json | 1 + 3 files changed, 78 insertions(+), 9 deletions(-) diff --git a/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.cpp b/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.cpp index 2ada927b5d..c40675aba9 100644 --- a/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.cpp +++ b/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.cpp @@ -33,7 +33,7 @@ using namespace mbed; #define QSPIF_DEFAULT_PROG_SIZE 1 #define QSPIF_DEFAULT_PAGE_SIZE 256 #define QSPIF_DEFAULT_SE_SIZE 4096 -#define QSPI_MAX_STATUS_REGISTER_SIZE 2 +#define QSPI_MAX_STATUS_REGISTER_SIZE 3 #ifndef UINT64_MAX #define UINT64_MAX -1 #endif @@ -171,6 +171,9 @@ int QSPIFBlockDevice::init() _address_size = QSPI_CFG_ADDR_SIZE_24; _data_width = QSPI_CFG_BUS_SINGLE; _dummy_and_mode_cycles = 0; + _write_register_inst = QSPIF_WRSR; + _read_register_inst = QSPIF_RDSR; + if (QSPI_STATUS_OK != _qspi_set_frequency(_freq)) { tr_error("QSPI Set Frequency Failed"); @@ -681,6 +684,7 @@ int QSPIFBlockDevice::_sfdp_parse_basic_param_table(uint32_t basic_table_addr, s // Detect and Set fastest Bus mode (default 1-1-1) _sfdp_detect_best_bus_read_mode(param_table, basic_table_size, shouldSetQuadEnable, is_qpi_mode, _read_instruction); if (true == shouldSetQuadEnable) { + _enable_fast_mdoe(); // Set Quad Enable and QPI Bus modes if Supported tr_info("Init - Setting Quad Enable"); if (0 != _sfdp_set_quad_enabled(param_table)) { @@ -817,8 +821,6 @@ int QSPIFBlockDevice::_sfdp_set_quad_enabled(uint8_t *basic_param_table_ptr) char status_reg_setup[QSPI_MAX_STATUS_REGISTER_SIZE] = {0}; char status_reg[QSPI_MAX_STATUS_REGISTER_SIZE] = {0}; - unsigned int write_register_inst = QSPIF_WRSR; - unsigned int read_register_inst = QSPIF_RDSR; // QUAD Enable procedure is specified by 3 bits uint8_t qer_value = (basic_param_table_ptr[QSPIF_BASIC_PARAM_TABLE_QER_BYTE] & 0x70) >> 4; @@ -844,13 +846,13 @@ int QSPIFBlockDevice::_sfdp_set_quad_enabled(uint8_t *basic_param_table_ptr) case 3: status_reg_setup[0] = 0x80; // Bit 7 of Status Reg 1 sr_write_size = 1; - write_register_inst = 0x3E; - read_register_inst = 0x3F; + _write_register_inst = 0x3E; + _read_register_inst = 0x3F; tr_debug("Setting QE Bit, Bit 7 of Status Reg 1"); break; case 5: status_reg_setup[1] = 0x2; // Bit 1 of status Reg 2 - read_register_inst = 0x35; + _read_register_inst = 0x35; sr_read_size = 1; tr_debug("Setting QE Bit, Bit 1 of Status Reg 2 -special read command"); break; @@ -864,7 +866,7 @@ int QSPIFBlockDevice::_sfdp_set_quad_enabled(uint8_t *basic_param_table_ptr) QSPI_CFG_ALT_SIZE_8, QSPI_CFG_BUS_SINGLE, 0); // Read Status Register - if (QSPI_STATUS_OK == _qspi_send_general_command(read_register_inst, QSPI_NO_ADDRESS_COMMAND, NULL, 0, + if (QSPI_STATUS_OK == _qspi_send_general_command(_read_register_inst, QSPI_NO_ADDRESS_COMMAND, NULL, 0, status_reg, sr_read_size) ) { // store received values in status_value tr_debug("Reading Status Register Success: value = 0x%x", (int)status_reg[0]); @@ -884,7 +886,7 @@ int QSPIFBlockDevice::_sfdp_set_quad_enabled(uint8_t *basic_param_table_ptr) return -1; } - if (QSPI_STATUS_OK == _qspi_send_general_command(write_register_inst, QSPI_NO_ADDRESS_COMMAND, (char *)status_reg, + if (QSPI_STATUS_OK == _qspi_send_general_command(_write_register_inst, QSPI_NO_ADDRESS_COMMAND, (char *)status_reg, sr_write_size, NULL, 0) ) { // Write QE to status_register tr_debug("_setQuadEnable - Writing Status Register Success: value = 0x%x", @@ -902,7 +904,7 @@ int QSPIFBlockDevice::_sfdp_set_quad_enabled(uint8_t *basic_param_table_ptr) // For Debug memset(status_reg, 0, QSPI_MAX_STATUS_REGISTER_SIZE); - if (QSPI_STATUS_OK == _qspi_send_general_command(read_register_inst, QSPI_NO_ADDRESS_COMMAND, NULL, 0, + if (QSPI_STATUS_OK == _qspi_send_general_command(_read_register_inst, QSPI_NO_ADDRESS_COMMAND, NULL, 0, (char *)status_reg, sr_read_size) ) { // store received values in status_value tr_debug("Reading Status Register Success: value = 0x%x", (int)status_reg[0]); @@ -1175,6 +1177,68 @@ int QSPIFBlockDevice::_set_write_enable() return status; } +int QSPIFBlockDevice::_enable_fast_mdoe() +{ + char status_reg[QSPI_MAX_STATUS_REGISTER_SIZE] = {0}; + unsigned int read_conf_register_inst = 0x15; + char status_reg_qer_setup[QSPI_MAX_STATUS_REGISTER_SIZE] = {0}; + + status_reg_qer_setup[2] = 0x2; // Bit 1 of config Reg 2 + + // Configure BUS Mode to 1_1_1 for all commands other than Read + _qspi_configure_format(QSPI_CFG_BUS_SINGLE, QSPI_CFG_BUS_SINGLE, QSPI_CFG_ADDR_SIZE_24, QSPI_CFG_BUS_SINGLE, + QSPI_CFG_ALT_SIZE_8, QSPI_CFG_BUS_SINGLE, 0); + + // Read Status Register + if (QSPI_STATUS_OK == _qspi_send_general_command(read_conf_register_inst, QSPI_NO_ADDRESS_COMMAND, NULL, 0, + &status_reg[1], + QSPI_MAX_STATUS_REGISTER_SIZE - 1) ) { // store received values in status_value + tr_debug("Reading Config Register Success: value = 0x%x", (int)status_reg[2]); + } else { + tr_error("Reading Config Register failed"); + return -1; + } + + // Set Bits for Quad Enable + for (int i = 0; i < QSPI_MAX_STATUS_REGISTER_SIZE; i++) { + status_reg[i] |= status_reg_qer_setup[i]; + } + + // Write new Status Register Setup + if (_set_write_enable() != 0) { + tr_error("Write Enabe failed"); + return -1; + } + + if (QSPI_STATUS_OK == _qspi_send_general_command(_write_register_inst, QSPI_NO_ADDRESS_COMMAND, status_reg, + QSPI_MAX_STATUS_REGISTER_SIZE, NULL, + 0) ) { // Write Fast mode bit to status_register + tr_debug("fast mode enable - Writing Config Register Success: value = 0x%x", + (int)status_reg[2]); + } else { + tr_error("fast mode enable - Writing Config Register failed"); + return -1; + } + + if ( false == _is_mem_ready()) { + tr_error("Device not ready after write, failed"); + return -1; + } + + // For Debug + memset(status_reg, 0, QSPI_MAX_STATUS_REGISTER_SIZE); + if (QSPI_STATUS_OK == _qspi_send_general_command(read_conf_register_inst, QSPI_NO_ADDRESS_COMMAND, NULL, 0, + &status_reg[1], + QSPI_MAX_STATUS_REGISTER_SIZE - 1) ) { // store received values in status_value + tr_debug("Verifying Config Register Success: value = 0x%x", (int)status_reg[2]); + } else { + tr_error("Verifying Config Register failed"); + return -1; + } + + return 0; +} + /*********************************************/ /************* Utility Functions *************/ /*********************************************/ diff --git a/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h b/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h index 93814de0d1..863892f495 100644 --- a/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h +++ b/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h @@ -250,6 +250,8 @@ private: // Wait on status register until write not-in-progress bool _is_mem_ready(); + // Enable Fast Mode - for flash chips with low power default + int _enable_fast_mdoe(); /* SFDP Detection and Parsing Functions */ /****************************************/ @@ -315,6 +317,8 @@ private: unsigned int _prog_instruction; unsigned int _erase_instruction; unsigned int _erase4k_inst; // Legacy 4K erase instruction (default 0x20h) + unsigned int _write_register_inst; // Write status/config register instruction may vary between chips + unsigned int _read_register_inst; // Read status/config register instruction may vary between chips // Up To 4 Erase Types are supported by SFDP (each with its own command Instruction and Size) unsigned int _erase_type_inst_arr[MAX_NUM_OF_ERASE_TYPES]; diff --git a/targets/targets.json b/targets/targets.json index ae75dbc1bd..2b3e5433d4 100755 --- a/targets/targets.json +++ b/targets/targets.json @@ -2037,6 +2037,7 @@ } }, "DISCO_L475VG_IOT01A": { + "components": ["QSPIF"], "inherits": ["FAMILY_STM32"], "core": "Cortex-M4F", "extra_labels_add": ["STM32L4", "STM32L475xG", "STM32L475VG"], From 020685c47313808304c437e2335ed7ca02cfd64a Mon Sep 17 00:00:00 2001 From: Offir Kochalsky Date: Tue, 16 Oct 2018 10:15:36 +0300 Subject: [PATCH 08/12] Added error logging at set QPI --- .../COMPONENT_QSPIF/QSPIFBlockDevice.cpp | 37 ++++++++++++------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.cpp b/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.cpp index c40675aba9..5e590d7c90 100644 --- a/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.cpp +++ b/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.cpp @@ -781,33 +781,44 @@ int QSPIFBlockDevice::_sfdp_set_qpi_enabled(uint8_t *basic_param_table_ptr) switch (en_seq_444_value) { case 1: case 2: - tr_debug("_setQPIEnabled - send command 38h"); - _qspi_send_general_command(0x38, QSPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0); + tr_debug("_sfdp_set_qpi_enabled - send command 38h"); + if (QSPI_STATUS_OK != _qspi_send_general_command(0x38, QSPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0)) { + tr_error("_sfdp_set_qpi_enabled - send command 38h Failed"); + } break; case 4: - tr_debug("_setQPIEnabled - send command 35h"); - _qspi_send_general_command(0x35, QSPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0); + tr_debug("_sfdp_set_qpi_enabled - send command 35h"); + if (QSPI_STATUS_OK != _qspi_send_general_command(0x35, QSPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0)) { + tr_error("_sfdp_set_qpi_enabled - send command 35h Failed"); + } break; case 8: - tr_debug("_setQPIEnabled - set config bit 6 and send command 71h"); - _qspi_send_general_command(0x65, 0x800003, NULL, 0, (char *)config_reg, 1); + tr_debug("_sfdp_set_qpi_enabled - set config bit 6 and send command 71h"); + if (QSPI_STATUS_OK != _qspi_send_general_command(0x65, 0x800003, NULL, 0, (char *)config_reg, 1)) { + tr_error("_sfdp_set_qpi_enabled - set config bit 6 command 65h Failed"); + } config_reg[0] |= 0x40; //Set Bit 6 - _qspi_send_general_command(0x71, 0x800003, NULL, 0, (char *)config_reg, 1); + if (QSPI_STATUS_OK != _qspi_send_general_command(0x71, 0x800003, NULL, 0, (char *)config_reg, 1)) { + tr_error("_sfdp_set_qpi_enabled - send command 71h Failed"); + } break; case 16: - tr_debug("DEBUG: _setQPIEnabled - reset config bits 0-7 and send command 61h"); - _qspi_send_general_command(0x65, QSPI_NO_ADDRESS_COMMAND, NULL, 0, (char *)config_reg, 1); + tr_debug("_sfdp_set_qpi_enabled - reset config bits 0-7 and send command 61h"); + if (QSPI_STATUS_OK != _qspi_send_general_command(0x65, QSPI_NO_ADDRESS_COMMAND, NULL, 0, (char *)config_reg, 1)) { + tr_error("_sfdp_set_qpi_enabled - send command 65h Failed"); + } config_reg[0] &= 0x7F; //Reset Bit 7 of CR - _qspi_send_general_command(0x61, QSPI_NO_ADDRESS_COMMAND, NULL, 0, (char *)config_reg, 1); + if (QSPI_STATUS_OK != _qspi_send_general_command(0x61, QSPI_NO_ADDRESS_COMMAND, NULL, 0, (char *)config_reg, 1)) { + tr_error("_sfdp_set_qpi_enabled - send command 61 Failed"); + } break; default: - tr_warning("_setQPIEnabled - Unsuported En Seq 444 configuration"); + tr_warning("_sfdp_set_qpi_enabled - Unsuported En Seq 444 configuration"); break; - } return 0; } @@ -1087,7 +1098,7 @@ int QSPIFBlockDevice::_reset_flash_mem() QSPI_MAX_STATUS_REGISTER_SIZE) ) { // store received values in status_value tr_debug("Reading Status Register Success: value = 0x%x", (int)status_value[0]); } else { - tr_debug("Reading Status Register failed: value = 0x%x", (int)status_value[0]); + tr_error("Reading Status Register failed: value = 0x%x", (int)status_value[0]); status = -1; } From 23bc67dfdc1250e0a9106cdeec47249ddef9f31b Mon Sep 17 00:00:00 2001 From: Offir Kochalsky Date: Wed, 17 Oct 2018 11:16:57 +0300 Subject: [PATCH 09/12] Fix DISCO_L475VG_IOT01A name at json --- components/storage/blockdevice/COMPONENT_QSPIF/mbed_lib.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/storage/blockdevice/COMPONENT_QSPIF/mbed_lib.json b/components/storage/blockdevice/COMPONENT_QSPIF/mbed_lib.json index 3351003180..affd50bfb5 100644 --- a/components/storage/blockdevice/COMPONENT_QSPIF/mbed_lib.json +++ b/components/storage/blockdevice/COMPONENT_QSPIF/mbed_lib.json @@ -7,7 +7,7 @@ "DISCO_F413ZH": { "QSPI_FREQ": "80000000" }, - "DISCO_L475VG": { + "DISCO_L475VG_IOT01A": { "QSPI_FREQ": "8000000" }, "DISCO_L476VG": { From 908233a17798a3f0e211f291b81cf75b289b2fc5 Mon Sep 17 00:00:00 2001 From: Offir Kochalsky Date: Wed, 17 Oct 2018 14:42:21 +0300 Subject: [PATCH 10/12] Changes per Pull Request review 3 --- .../COMPONENT_QSPIF/QSPIFBlockDevice.h | 10 ++- .../blockdevice/COMPONENT_QSPIF/README.md | 85 ------------------- 2 files changed, 6 insertions(+), 89 deletions(-) delete mode 100644 components/storage/blockdevice/COMPONENT_QSPIF/README.md diff --git a/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h b/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h index 863892f495..9673317197 100644 --- a/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h +++ b/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h @@ -103,8 +103,7 @@ public: * */ QSPIFBlockDevice(PinName io0, PinName io1, PinName io2, PinName io3, PinName sclk, PinName csel, - int clock_mode, int freq = 40000000); - + int clock_mode, int freq = MBED_CONF_QSPIF_QSPI_FREQ); /** Initialize a block device * @@ -238,9 +237,10 @@ private: // Send set_frequency command to Driver qspi_status_t _qspi_set_frequency(int freq); - /********************************/ - + /*********************************/ + /* Flash Configuration Functions */ + /*********************************/ // Soft Reset Flash Memory int _reset_flash_mem(); @@ -253,6 +253,7 @@ private: // Enable Fast Mode - for flash chips with low power default int _enable_fast_mdoe(); + /****************************************/ /* SFDP Detection and Parsing Functions */ /****************************************/ // Parse SFDP Headers and retrieve Basic Param and Sector Map Tables (if exist) @@ -283,6 +284,7 @@ private: unsigned int& erase4k_inst, unsigned int *erase_type_inst_arr, unsigned int *erase_type_size_arr); + /***********************/ /* Utilities Functions */ /***********************/ // Find the region to which the given offset belong to diff --git a/components/storage/blockdevice/COMPONENT_QSPIF/README.md b/components/storage/blockdevice/COMPONENT_QSPIF/README.md deleted file mode 100644 index e678a2c86b..0000000000 --- a/components/storage/blockdevice/COMPONENT_QSPIF/README.md +++ /dev/null @@ -1,85 +0,0 @@ -# Quad SPI (QSPI) Flash Block Device - -Block device driver for NOR based QSPI flash devices that support SFDP. - -NOR based QSPI flash supports up to 4bits per cycle of instruction, address and data. -SFDP based QSPI Flash supports variable bus modes (single, dual, quad), several sector erase size types, multiple regions of sector size types and more. - -SFDP JEDEC standard can be found in: -https://www.jedec.org/system/files/docs/JESD216B.pdf - -### Debugging -Set `MBED_CONF_MBED_TRACE_ENABLE` as true(1) to enable logs. - -### Example - -``` cpp -// QSPI SFDP Flash - Block Device example -#include "mbed.h" -#include "QSPIFBlockDevice.h" - -QSPIFBlockDevice block_device(MBED_CONF_QSPIF_QSPI_IO0,MBED_CONF_QSPIF_QSPI_IO1,MBED_CONF_QSPIF_QSPI_IO2,MBED_CONF_QSPIF_QSPI_IO3, - MBED_CONF_QSPIF_QSPI_CLK,MBED_CONF_QSPIF_QSPI_CS,QSPIF_POLARITY_MODE_0,MBED_CONF_QSPIF_QSPI_FREQ); - - -int main() { - -#if defined(MBED_CONF_MBED_TRACE_ENABLE) && MBED_CONF_MBED_TRACE_ENABLE - mbed_trace_init(); -#endif - printf("QSPI SFDP Flash Block Device example\n"); - - int status = 0; - // Initialize the SPI flash device and print the memory layout - status = block_device.init(); - if (0 != status) { - printf("QSPI Flash init failed \n"); - return status; - } - bd_size_t sector_size_at_address_0 = block_device.get_erase_size(0); - - printf("QSPIF BD size: %llu\n", block_device.size()); - printf("QSPIF BD read size: %llu\n", block_device.get_read_size()); - printf("QSPIF BD program size: %llu\n", block_device.get_program_size()); - printf("QSPIF BD erase size (at address 0): %llu\n", sector_size_at_address_0); - - // Write "Hello World!" to the first block - char *buffer = (char*) malloc(sector_size_at_address_0); - if (buffer == NULL) { - printf("Malloc failed\n"); - return -1; - } - sprintf(buffer, "Hello World!\n"); - - int address = 0; - status = block_device.erase(address, sector_size_at_address_0); - if (0 != status) { - printf("QSPI Flash Erase failed, address = 0x%x size = %lld\n", address, sector_size_at_address_0); - return status; - } - - status = block_device.program(buffer, address, sector_size_at_address_0); - if (0 != status) { - printf("QSPI Flash program failed, address = 0x%x size = %lld\n", address, sector_size_at_address_0); - return status; - } - - // Read back what was stored - status = block_device.read(buffer, address, sector_size_at_address_0); - if (0 != status) { - printf("QSPI Flash read failed, address = 0x%x size = %lld\n", address, sector_size_at_address_0); - return status; - } - - printf("%s\n", buffer); - - // Deinitialize the device - status = block_device.deinit(); - if (0 != status) { - printf("QSPI Flash deinit failed \n"); - return status; - } - printf ("Finished \n"); - return 0; -} -``` From 7d05fcb788651233fb51fb9917482c28acbbdd80 Mon Sep 17 00:00:00 2001 From: Offir Kochalsky Date: Sun, 21 Oct 2018 10:29:03 +0300 Subject: [PATCH 11/12] remove mbed namespace from h file and update test --- .../storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h | 3 --- .../COMPONENT_QSPIF/TESTS/block_device/qspif/main.cpp | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h b/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h index 9673317197..7b7768066c 100644 --- a/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h +++ b/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h @@ -19,8 +19,6 @@ #include "QSPI.h" #include "BlockDevice.h" -namespace mbed { - /** Enum qspif standard error codes * * @enum qspif_bd_error @@ -349,5 +347,4 @@ private: bool _is_initialized; }; -} //namespace mbed #endif diff --git a/components/storage/blockdevice/COMPONENT_QSPIF/TESTS/block_device/qspif/main.cpp b/components/storage/blockdevice/COMPONENT_QSPIF/TESTS/block_device/qspif/main.cpp index c792cbf507..dba0d77658 100644 --- a/components/storage/blockdevice/COMPONENT_QSPIF/TESTS/block_device/qspif/main.cpp +++ b/components/storage/blockdevice/COMPONENT_QSPIF/TESTS/block_device/qspif/main.cpp @@ -18,6 +18,7 @@ #include "utest.h" #include "QSPIFBlockDevice.h" #include "mbed_trace.h" +#include "rtos/Thread.h" #include using namespace utest::v1; From 5cebe43dfc09b1d3f7205ffcae8a127d3cc13fd4 Mon Sep 17 00:00:00 2001 From: Offir Kochalsky Date: Sun, 21 Oct 2018 11:50:17 +0300 Subject: [PATCH 12/12] mbed namespace removed add mbed indicator to QSPI --- .../storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h b/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h index 7b7768066c..aad4e3fb37 100644 --- a/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h +++ b/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.h @@ -296,7 +296,7 @@ private: // Internal Members // QSPI Driver Object - QSPI _qspi; + mbed::QSPI _qspi; // Static List of different QSPI based Flash devices csel that already exist // Each QSPI Flash device csel can have only 1 QSPIFBlockDevice instance