diff --git a/storage/blockdevice/COMPONENT_SPINAND/CMakeLists.txt b/storage/blockdevice/COMPONENT_SPINAND/CMakeLists.txt new file mode 100644 index 0000000000..fc07dbe342 --- /dev/null +++ b/storage/blockdevice/COMPONENT_SPINAND/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright (c) 2020 ARM Limited. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +target_include_directories(mbed-storage-spinand + INTERFACE + include + include/SPINAND +) + +target_sources(mbed-storage-spinand + INTERFACE + source/SPINANDBlockDevice.cpp +) diff --git a/storage/blockdevice/COMPONENT_SPINAND/include/SPINAND/.SPINANDBlockDevice.h.swo b/storage/blockdevice/COMPONENT_SPINAND/include/SPINAND/.SPINANDBlockDevice.h.swo new file mode 100644 index 0000000000..b65bdd1d72 Binary files /dev/null and b/storage/blockdevice/COMPONENT_SPINAND/include/SPINAND/.SPINANDBlockDevice.h.swo differ diff --git a/storage/blockdevice/COMPONENT_SPINAND/include/SPINAND/SPINANDBlockDevice.h b/storage/blockdevice/COMPONENT_SPINAND/include/SPINAND/SPINANDBlockDevice.h new file mode 100644 index 0000000000..1d6196f60a --- /dev/null +++ b/storage/blockdevice/COMPONENT_SPINAND/include/SPINAND/SPINANDBlockDevice.h @@ -0,0 +1,325 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_SPINAND_BLOCK_DEVICE_H +#define MBED_SPINAND_BLOCK_DEVICE_H + +#include "drivers/QSPI.h" +#include "blockdevice/BlockDevice.h" +#include "platform/Callback.h" + +#ifndef MBED_CONF_SPINAND_QSPI_IO0 +#define MBED_CONF_SPINAND_QSPI_IO0 NC +#endif +#ifndef MBED_CONF_SPINAND_QSPI_IO1 +#define MBED_CONF_SPINAND_QSPI_IO1 NC +#endif +#ifndef MBED_CONF_SPINAND_QSPI_IO2 +#define MBED_CONF_SPINAND_QSPI_IO2 NC +#endif +#ifndef MBED_CONF_SPINAND_QSPI_IO3 +#define MBED_CONF_SPINAND_QSPI_IO3 NC +#endif +#ifndef MBED_CONF_SPINAND_QSPI_SCK +#define MBED_CONF_SPINAND_QSPI_SCK NC +#endif +#ifndef MBED_CONF_SPINAND_QSPI_CSN +#define MBED_CONF_SPINAND_QSPI_CSN NC +#endif +#ifndef MBED_CONF_SPINAND_QSPI_POLARITY_MODE +#define MBED_CONF_SPINAND_QSPI_POLARITY_MODE 0 +#endif +#ifndef MBED_CONF_SPINAND_QSPI_FREQ +#define MBED_CONF_SPINAND_QSPI_FREQ 40000000 +#endif + +/** Enum spinand standard error codes + * + * @enum spinand_bd_error + */ +enum spinand_bd_error { + SPINAND_BD_ERROR_OK = 0, /*!< no error */ + SPINAND_BD_ERROR_DEVICE_ERROR = BD_ERROR_DEVICE_ERROR, /*!< device specific error -4001 */ + SPINAND_BD_ERROR_PARSING_FAILED = -4002, /* SFDP Parsing failed */ + SPINAND_BD_ERROR_READY_FAILED = -4003, /* Wait for Mem Ready failed */ + SPINAND_BD_ERROR_WREN_FAILED = -4004, /* Write Enable Failed */ + SPINAND_BD_ERROR_INVALID_ERASE_PARAMS = -4005, /* Erase command not on sector aligned addresses or exceeds device size */ + SPINAND_BD_ERROR_DEVICE_NOT_UNIQUE = -4006, /* Only one instance per csel is allowed */ + SPINAND_BD_ERROR_DEVICE_MAX_EXCEED = -4007 /* Max active SPINAND devices exceeded */ +}; + +/** Enum spinand polarity mode + * + * @enum spinand_polarity_mode + */ +enum spinand_polarity_mode { + SPINAND_POLARITY_MODE_0 = 0, /* CPOL=0, CPHA=0 */ + SPINAND_POLARITY_MODE_1 /* CPOL=1, CPHA=1 */ +}; + +#define SPINAND_MAX_ACTIVE_FLASH_DEVICES 10 + +/** BlockDevice for SPI NAND flash devices over QSPI bus + * + * @code + * // Here's an example using SPI NAND flash device on DISCO_L4R9I target + * #include "mbed.h" + * #include "SPINANDBlockDevice.h" + * + * SPINANDBlockDevice block_device(SPINAND_FLASH1_IO0, SPINAND_FLASH1_IO1, SPINAND_FLASH1_IO2, SPINAND_FLASH1_IO3, + * SPINAND_FLASH1_SCK, SPINAND_FLASH1_CSN, SPINAND_POLARITY_MODE_0, MBED_CONF_SPINAND_QSPI_FREQ); + * + * int main() + * { + * printf("SPI NAND Flash Block Device example\n"); + * + * // Initialize the SPI NAND 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("SPINAND BD size: %llu\n", block_device.size()); + * printf("SPINAND BD read size: %llu\n", block_device.get_read_size()); + * printf("SPINAND BD program size: %llu\n", block_device.get_program_size()); + * printf("SPINAND 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 SPINANDBlockDevice : public mbed::BlockDevice { +public: + /** Create SPINANDBlockDevice - An SPI NAND 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 (SPINAND_POLARITY_MODE_0/SPINAND_POLARITY_MODE_1) + * default value = 0 + * @param freq Clock frequency of the QSPI bus (defaults to 40MHz) + */ + SPINANDBlockDevice(PinName io0 = MBED_CONF_SPINAND_QSPI_IO0, + PinName io1 = MBED_CONF_SPINAND_QSPI_IO1, + PinName io2 = MBED_CONF_SPINAND_QSPI_IO2, + PinName io3 = MBED_CONF_SPINAND_QSPI_IO3, + PinName sclk = MBED_CONF_SPINAND_QSPI_SCK, + PinName csel = MBED_CONF_SPINAND_QSPI_CSN, + int clock_mode = MBED_CONF_SPINAND_QSPI_POLARITY_MODE, + int freq = MBED_CONF_SPINAND_QSPI_FREQ); + + /** Initialize a block device + * + * @return SPINAND_BD_ERROR_OK(0) - success + * SPINAND_BD_ERROR_DEVICE_ERROR - device driver transaction failed + * SPINAND_BD_ERROR_READY_FAILED - Waiting for Memory ready failed or timedout + */ + virtual int init(); + + /** Deinitialize a block device + * + * @return SPINAND_BD_ERROR_OK(0) - success + * SPINAND_BD_ERROR_DEVICE_ERROR - device driver transaction failed + */ + virtual int deinit(); + + /** Desctruct SPINANDBlockDevie + */ + ~SPINANDBlockDevice() + { + 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 SPINAND_BD_ERROR_OK(0) - success + * SPINAND_BD_ERROR_DEVICE_ERROR - device driver transaction failed + */ + virtual int read(void *buffer, mbed::bd_addr_t addr, mbed::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 SPINAND_BD_ERROR_OK(0) - success + * SPINAND_BD_ERROR_DEVICE_ERROR - device driver transaction failed + * SPINAND_BD_ERROR_READY_FAILED - Waiting for Memory ready failed or timed out + * SPINAND_BD_ERROR_WREN_FAILED - Write Enable failed + */ + virtual int program(const void *buffer, mbed::bd_addr_t addr, mbed::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 SPINAND_BD_ERROR_OK(0) - success + * SPINAND_BD_ERROR_DEVICE_ERROR - device driver transaction failed + * SPINAND_BD_ERROR_READY_FAILED - Waiting for Memory ready failed or timed out + * SPINAND_BD_ERROR_WREN_FAILED - Write Enable failed + * SPINAND_BD_ERROR_INVALID_ERASE_PARAMS - Trying to erase unaligned address or size + */ + virtual int erase(mbed::bd_addr_t addr, mbed::bd_size_t size); + + /** Get the size of a readable block + * + * @return Size of a readable block in bytes + */ + virtual mbed::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 mbed::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 mbed::bd_size_t get_erase_size() const; + virtual mbed::bd_size_t get_erase_size(bd_addr_t addr) const; + + /** Get the value of storage byte after it was erased + * + * If get_erase_value returns a non-negative byte value, the underlying + * storage is set to that value when erased, and storage containing + * that value can be programmed without another erase. + * + * @return The value of storage when erased, or -1 if you can't + * rely on the value of erased storage + */ + virtual int get_erase_value() const; + + /** Get the total size of the underlying device + * + * @return Size of the underlying device in bytes + */ + virtual mbed::bd_size_t size() const; + + /** Get the BlockDevice class type. + * + * @return A string represent the BlockDevice class type. + */ + virtual const char *get_type() const; + +private: + /********************************/ + /* Different Device Csel Mgmt */ + /********************************/ + // Add a new SPI NAND device CS to existing devices list. + // Only one SPINANDBlockDevice 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(mbed::qspi_inst_t prog_instruction, const void *buffer, + mbed::bd_addr_t addr, mbed::bd_size_t *size); + + // Send Read command to Driver + qspi_status_t _qspi_send_read_command(mbed::qspi_inst_t read_instruction, void *buffer, mbed::bd_addr_t addr, mbed::bd_size_t size); + + // Send Erase Instruction using command_transfer command to Driver + qspi_status_t _qspi_send_erase_command(mbed::qspi_inst_t erase_instruction, mbed::bd_addr_t addr, mbed::bd_size_t size); + + // Send Generic command_transfer command to Driver + qspi_status_t _qspi_send_general_command(mbed::qspi_inst_t instruction_int, mbed::bd_addr_t addr, const char *tx_buffer, + mbed::bd_size_t tx_length, const char *rx_buffer, mbed::bd_size_t rx_length); + + // Send set_frequency command to Driver + qspi_status_t _qspi_set_frequency(int freq); + + /*********************************/ + /* Flash Configuration Functions */ + /*********************************/ + + // Quad Enable in Sercurity Register + int _set_quad_enable(); + + // Clear the device's block protection + int _clear_block_protection(); + + // Configure Write Enable in Status Register + int _set_write_enable(); + + // Wait on status register until write not-in-progress + bool _is_mem_ready(); + +private: + + // QSPI Driver Object + mbed::QSPI _qspi; + + // Static List of different QSPI based Flash devices csel that already exist + // Each QSPI Flash device csel can have only 1 SPINANDBlockDevice instance + // _devices_mutex is used to lock csel list - only one SPINANDBlockDevice instance per csel is allowed + static SingletonPtr _devices_mutex; + static int _number_of_active_spinand_flash_csel; + static PinName *_active_spinand_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 + mbed::qspi_inst_t _read_instruction; + mbed::qspi_inst_t _program_instruction; + + int _freq; + + // 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 bits for address + qspi_alt_size_t _alt_size; //Number of bits for alt + bool _alt_enabled; //Whether alt is enabled + uint8_t _dummy_cycles; //Number of Dummy cycles required by Current Bus Mode + qspi_bus_width_t _data_width; //Bus width for Data phase + + uint32_t _init_ref_count; + bool _is_initialized; +}; + +#endif diff --git a/storage/blockdevice/COMPONENT_SPINAND/mbed_lib.json b/storage/blockdevice/COMPONENT_SPINAND/mbed_lib.json new file mode 100644 index 0000000000..89f69e7e8d --- /dev/null +++ b/storage/blockdevice/COMPONENT_SPINAND/mbed_lib.json @@ -0,0 +1,31 @@ +{ + "name": "spinand", + "config": { + "enable-and-reset": { + "help": "(Legacy SFDP 1.0 ONLY) Reset sequence is enable reset (0x66) then reset (0x99)", + "value": false + }, + "direct-reset": { + "help": "(Legacy SFDP 1.0 ONLY) Reset involves a single command (0xF0)", + "value": false + }, + "SPINAND_IO0": "MBED_CONF_DRIVERS_QSPI_IO0", + "SPINAND_IO1": "MBED_CONF_DRIVERS_QSPI_IO1", + "SPINAND_IO2": "MBED_CONF_DRIVERS_QSPI_IO2", + "SPINAND_IO3": "MBED_CONF_DRIVERS_QSPI_IO3", + "SPINAND_SCK": "MBED_CONF_DRIVERS_QSPI_SCK", + "SPINAND_CSN": "MBED_CONF_DRIVERS_QSPI_CSN", + "SPINAND_POLARITY_MODE": 0, + "SPINAND_FREQ": "40000000", + "SPINAND_MIN_READ_SIZE": "1", + "SPINAND_MIN_PROG_SIZE": "1", + "SPINAND_FLASH_SIZE": "2048*64*512", + "SPINAND_BLOCK_SIZE": "2048*64", + "SPINAND_PAGE_SIZE": "2048" + }, + "target_overrides": { + "MX31LF4GE4BC": { + "SPINAND_FREQ": "2000000" + } + } +} diff --git a/storage/blockdevice/COMPONENT_SPINAND/source/.SPINANDBlockDevice.cpp.swo b/storage/blockdevice/COMPONENT_SPINAND/source/.SPINANDBlockDevice.cpp.swo new file mode 100644 index 0000000000..a45aeb1e50 Binary files /dev/null and b/storage/blockdevice/COMPONENT_SPINAND/source/.SPINANDBlockDevice.cpp.swo differ diff --git a/storage/blockdevice/COMPONENT_SPINAND/source/SPINANDBlockDevice.cpp b/storage/blockdevice/COMPONENT_SPINAND/source/SPINANDBlockDevice.cpp new file mode 100644 index 0000000000..81afc212f2 --- /dev/null +++ b/storage/blockdevice/COMPONENT_SPINAND/source/SPINANDBlockDevice.cpp @@ -0,0 +1,797 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "platform/Callback.h" +#include "SPINANDBlockDevice.h" +#include +#include "rtos/ThisThread.h" + +#ifndef MBED_CONF_MBED_TRACE_ENABLE +#define MBED_CONF_MBED_TRACE_ENABLE 0 +#endif + +#include "mbed_trace.h" +#define TRACE_GROUP "SPINAND" + +using namespace std::chrono; +using namespace mbed; + +/* SPINAND Parameters */ +/****************************/ +#ifndef UINT64_MAX +#define UINT64_MAX -1 +#endif +#define QSPI_NO_ADDRESS_COMMAND UINT64_MAX +#define QSPI_ALT_DEFAULT_VALUE 0 + +// Get/Set Feature Address Definition +#define FEATURES_ADDR_BLOCK_PROTECTION 0xA0 +#define FEATURES_ADDR_SECURE_OTP 0xB0 +#define FEATURES_ADDR_STATUS 0xC0 + +// Status Register Bits +#define SPINAND_STATUS_BIT_WIP 0x1 // Write In Progress +#define SPINAND_STATUS_BIT_WEL 0x2 // Write Enable Latch +#define SPINAND_STATUS_BIT_ERASE_FAIL 0x4 // Erase failed +#define SPINAND_STATUS_BIT_PROGRAM_FAIL 0x8 // Program failed +#define SPINAND_STATUS_BIT_ECC_STATUS_MASK 0x30 // ECC status + #define SPINAND_STATUS_ECC_STATUS_NO_ERR 0x00 + #define SPINAND_STATUS_ECC_STATUS_ERR_COR 0x00 + #define SPINAND_STATUS_ECC_STATUS_ERR_NO_COR 0x00 + +// Secure OTP Register Bits +#define SPINAND_SECURE_BIT_QE 0x01 // Quad enable +#define SPINAND_SECURE_BIT_ECC_EN 0x10 // On-die ECC enable +#define SPINAND_SECURE_BIT_OTP_EN 0x40 // +#define SPINAND_SECURE_BIT_OTP_PROT 0x80 // + +// Block Protection Register Bits +#define SPINAND_BLOCK_PROT_BIT_SP 0x01 +#define SPINAND_BLOCK_PROT_BIT_COMPLE 0x02 +#define SPINAND_BLOCK_PROT_BIT_INVERT 0x04 +#define SPINAND_BLOCK_PROT_BIT_BP0 0x08 +#define SPINAND_BLOCK_PROT_BIT_BP1 0x10 +#define SPINAND_BLOCK_PROT_BIT_BP2 0x20 +#define SPINAND_BLOCK_PROT_BIT_BPRWD 0x80 +#define SPINAND_BLOCK_PROT_BIT_BP_MASK 0x38 + +#define SPINAND_BLOCK_PROT_BP_OFFSET 3 +#define SPINAND_BLOCK_PROT_COMPLE_OFFSET 1 + +#define IS_MEM_READY_MAX_RETRIES 10000 + +// General SPI NAND Flash instructions +#define SPINAND_INST_RDID 0x9F // Read Manufacturer and JDEC Device ID +#define SPINAND_INST_RSR1 0x05 // Read status register 1 + +#define SPINAND_INST_PAGE_READ 0x13 // Read data from array to cache +#define SPINAND_INST_READ_CACHE 0x03 // Read data from cache +#define SPINAND_INST_READ_CACHE2 0x3B +#define SPINAND_INST_READ_CACHE4 0x6B +#define SPINAND_INST_READ_CACHE_SEQ 0x31 +#define SPINAND_INST_READ_CACHE_END 0x3F + +#define SPINAND_INST_WREN 0x06 // Write enable +#define SPINAND_INST_WRDI 0x04 // Write disable +#define SPINAND_INST_PP_LOAD 0x02 +#define SPINAND_INST_PP_RAND_LOAD 0x84 +#define SPINAND_INST_4PP_LOAD 0x32 +#define SPINAND_INST_4PP_RAND_LOAD 0x34 +#define SPINAND_INST_PROGRAM_EXEC 0x10 +#define SPINAND_INST_BE 0xD8 + +#define SPINAND_INST_GET_FEATURE 0x0F +#define SPINAND_INST_SET_FEATURE 0x1F +#define SPINAND_INST_RESET 0xFF +#define SPINAND_INST_ECC_STAT_READ 0x7C + +// Default read/legacy erase instructions +//#define SPINAND_INST_READ_DEFAULT SPINAND_INST_READ_CACHE +//#define SPINAND_INST_READ_DEFAULT SPINAND_INST_READ_CACHE2 +#define SPINAND_INST_READ_DEFAULT SPINAND_INST_READ_CACHE4 +//#define SPINAND_INST_PROGRAM_DEFAULT SPINAND_INST_PP_LOAD +#define SPINAND_INST_PROGRAM_DEFAULT SPINAND_INST_4PP_LOAD + +#define SPINAND_BLOCK_OFFSET 0x40000 +#define SPINAND_PAGE_OFFSET 0x1000 + +#define SPI_NAND_ROW_ADDR_SIZE QSPI_CFG_ADDR_SIZE_16 +#define SPI_NAND_COLUMN_ADDR_SIZE QSPI_CFG_ADDR_SIZE_24 + +/* Init function to initialize Different Devices CS static list */ +static PinName *generate_initialized_active_spinand_csel_arr(); +// Static Members for different devices csel +// _devices_mutex is used to lock csel list - only one SPINANDBlockDevice instance per csel is allowed +SingletonPtr SPINANDBlockDevice::_devices_mutex; +int SPINANDBlockDevice::_number_of_active_spinand_flash_csel = 0; +PinName *SPINANDBlockDevice::_active_spinand_flash_csel_arr = generate_initialized_active_spinand_csel_arr(); + +/********* Public API Functions *********/ +/****************************************/ +SPINANDBlockDevice::SPINANDBlockDevice(PinName io0, PinName io1, PinName io2, PinName io3, PinName sclk, PinName csel, + int clock_mode, + int freq) + : + _qspi(io0, io1, io2, io3, sclk, csel, clock_mode), _csel(csel), _freq(freq), + _init_ref_count(0), + _is_initialized(false) +{ + _unique_device_status = add_new_csel_instance(csel); + + if (_unique_device_status == 0) { + tr_debug("Adding a new SPINANDBlockDevice csel: %d", (int)csel); + } else if (_unique_device_status == -1) { + tr_error("SPINANDBlockDevice with the same csel(%d) already exists", (int)csel); + } else { + tr_error("Too many different SPINANDBlockDevice devices - max allowed: %d", SPINAND_MAX_ACTIVE_FLASH_DEVICES); + } + + // 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_8; + _alt_size = 0; + _dummy_cycles = 8; + _data_width = QSPI_CFG_BUS_SINGLE; + + // Set default read/erase instructions + _read_instruction = SPINAND_INST_READ_DEFAULT; + _program_instruction = SPINAND_INST_PROGRAM_DEFAULT; +} + +int SPINANDBlockDevice::init() +{ + int status = SPINAND_BD_ERROR_OK; + + if (_unique_device_status == 0) { + tr_debug("SPINANDBlockDevice csel: %d", (int)_csel); + } else if (_unique_device_status == -1) { + tr_error("SPINANDBlockDevice with the same csel(%d) already exists", (int)_csel); + return SPINAND_BD_ERROR_DEVICE_NOT_UNIQUE; + } else { + tr_error("Too many different SPINANDBlockDevice devices - max allowed: %d", SPINAND_MAX_ACTIVE_FLASH_DEVICES); + return SPINAND_BD_ERROR_DEVICE_MAX_EXCEED; + } + + _mutex.lock(); + + // All commands other than Read and RSFDP use default 1-1-1 bus mode (Program/Erase are constrained by flash memory performance more than bus performance) + if (QSPI_STATUS_OK != _qspi.configure_format(QSPI_CFG_BUS_SINGLE, QSPI_CFG_BUS_SINGLE, _address_size, QSPI_CFG_BUS_SINGLE, + 0, QSPI_CFG_BUS_SINGLE, 0)) { + tr_error("_qspi_configure_format failed"); + status = SPINAND_BD_ERROR_DEVICE_ERROR; + goto exit_point; + } + + if (!_is_initialized) { + _init_ref_count = 0; + } + + _init_ref_count++; + + if (_init_ref_count != 1) { + goto exit_point; + } + + _alt_size = 0; + _dummy_cycles = 8; + if (QSPI_STATUS_OK != _qspi_set_frequency(_freq)) { + tr_error("QSPI Set Frequency Failed"); + status = SPINAND_BD_ERROR_DEVICE_ERROR; + goto exit_point; + } + + // Synchronize Device + if (false == _is_mem_ready()) { + tr_error("Init - _is_mem_ready Failed"); + status = SPINAND_BD_ERROR_READY_FAILED; + goto exit_point; + } + + if (0 != _clear_block_protection()) { + tr_error("Init - clearing block protection failed"); + status = SPINAND_BD_ERROR_PARSING_FAILED; + goto exit_point; + } + + if (_read_instruction == SPINAND_INST_READ_CACHE4) { + if (QSPI_STATUS_OK !=_set_quad_enable()) { + tr_error("SPI NAND Set Quad enable Failed"); + status = SPINAND_BD_ERROR_DEVICE_ERROR; + goto exit_point; + } + } + + _is_initialized = true; + +exit_point: + _mutex.unlock(); + + return status; +} + +int SPINANDBlockDevice::deinit() +{ + int result = SPINAND_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(SPINAND_INST_WRDI, QSPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0); + if (status != QSPI_STATUS_OK) { + tr_error("Write Disable failed"); + result = SPINAND_BD_ERROR_DEVICE_ERROR; + } + + _is_initialized = false; + + _mutex.unlock(); + + if (_unique_device_status == 0) { + remove_csel_instance(_csel); + } + + return result; +} + +int SPINANDBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size) +{ + int status = SPINAND_BD_ERROR_OK; + uint32_t offset = 0; + uint32_t chunk = 0; + bd_size_t read_bytes = 0; + + tr_debug("Read Inst: 0x%xh", _read_instruction); + + while (size > 0) { + // Read on _page_size_bytes boundaries (Default 2048 bytes a page) + offset = addr % MBED_CONF_SPINAND_SPINAND_PAGE_SIZE; + chunk = (offset + size < MBED_CONF_SPINAND_SPINAND_PAGE_SIZE) ? size : (MBED_CONF_SPINAND_SPINAND_PAGE_SIZE - offset); + read_bytes = chunk; + + _mutex.lock(); + + if (QSPI_STATUS_OK != _qspi_send_read_command(_read_instruction, buffer, addr, read_bytes)) { + tr_error("Read Command failed"); + status = SPINAND_BD_ERROR_DEVICE_ERROR; + } + + buffer = static_cast< uint8_t *>(buffer) + chunk; + addr += SPINAND_PAGE_OFFSET; + size -= chunk; + + _mutex.unlock(); + } + + return status; +} + +int SPINANDBlockDevice::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 = SPINAND_BD_ERROR_OK; + uint32_t offset = 0; + uint32_t chunk = 0; + bd_size_t written_bytes = 0; + + tr_debug("Program - Buff: %p, addr: %llu, size: %llu", buffer, addr, size); + + while (size > 0) { + // Write on _page_size_bytes boundaries (Default 2048 bytes a page) + offset = addr % MBED_CONF_SPINAND_SPINAND_PAGE_SIZE; + chunk = (offset + size < MBED_CONF_SPINAND_SPINAND_PAGE_SIZE) ? size : (MBED_CONF_SPINAND_SPINAND_PAGE_SIZE - offset); + written_bytes = chunk; + + _mutex.lock(); + + //Send WREN + if (_set_write_enable() != 0) { + tr_error("Write Enabe failed"); + program_failed = true; + status = SPINAND_BD_ERROR_WREN_FAILED; + goto exit_point; + } + + result = _qspi_send_program_command(_program_instruction, buffer, addr, &written_bytes); + if ((result != QSPI_STATUS_OK) || (chunk != written_bytes)) { + tr_error("Write failed"); + program_failed = true; + status = SPINAND_BD_ERROR_DEVICE_ERROR; + goto exit_point; + } + + buffer = static_cast(buffer) + chunk; + addr += SPINAND_PAGE_OFFSET; + size -= chunk; + + if (false == _is_mem_ready()) { + tr_error("Device not ready after write, failed"); + program_failed = true; + status = SPINAND_BD_ERROR_READY_FAILED; + goto exit_point; + } + _mutex.unlock(); + } + +exit_point: + if (program_failed) { + _mutex.unlock(); + } + + return status; +} + +int SPINANDBlockDevice::erase(bd_addr_t addr, bd_size_t size) +{ + bool erase_failed = false; + int status = SPINAND_BD_ERROR_OK; + + tr_debug("Erase - addr: %llu, size: %llu", addr, size); + + if ((addr + size) > MBED_CONF_SPINAND_SPINAND_FLASH_SIZE) { + tr_error("Erase exceeds flash device size"); + return SPINAND_BD_ERROR_INVALID_ERASE_PARAMS; + } + + if (((addr % SPINAND_BLOCK_OFFSET) != 0) || ((size % get_erase_size()) != 0)) { + tr_error("Invalid erase - unaligned address and size"); + return SPINAND_BD_ERROR_INVALID_ERASE_PARAMS; + } + + while (size > 0) { + + _mutex.lock(); + + if (_set_write_enable() != 0) { + tr_error("SPI NAND Erase Device not ready - failed"); + erase_failed = true; + status = SPINAND_BD_ERROR_WREN_FAILED; + goto exit_point; + } + + if (QSPI_STATUS_OK != _qspi_send_erase_command(SPINAND_INST_BE, addr, size)) { + tr_error("SPI NAND Erase command failed!"); + erase_failed = true; + status = SPINAND_BD_ERROR_DEVICE_ERROR; + goto exit_point; + } + + addr += SPINAND_BLOCK_OFFSET; + if(size > MBED_CONF_SPINAND_SPINAND_BLOCK_SIZE) { + size -= MBED_CONF_SPINAND_SPINAND_BLOCK_SIZE; + } else { + size = 0; + } + + if (false == _is_mem_ready()) { + tr_error("SPI NAND After Erase Device not ready - failed"); + erase_failed = true; + status = SPINAND_BD_ERROR_READY_FAILED; + goto exit_point; + } + + _mutex.unlock(); + } + +exit_point: + if (erase_failed) { + _mutex.unlock(); + } + + return status; +} + +bd_size_t SPINANDBlockDevice::get_read_size() const +{ + // Return minimum read size in bytes for the device + return MBED_CONF_SPINAND_SPINAND_MIN_READ_SIZE; +} + +bd_size_t SPINANDBlockDevice::get_program_size() const +{ + // Return minimum program/write size in bytes for the device + return MBED_CONF_SPINAND_SPINAND_MIN_PROG_SIZE; +} + +bd_size_t SPINANDBlockDevice::get_erase_size() const +{ + return MBED_CONF_SPINAND_SPINAND_BLOCK_SIZE; +} + +bd_size_t SPINANDBlockDevice::get_erase_size(bd_addr_t addr) const +{ + return MBED_CONF_SPINAND_SPINAND_BLOCK_SIZE; +} + +const char *SPINANDBlockDevice::get_type() const +{ + return "SPINAND"; +} + +bd_size_t SPINANDBlockDevice::size() const +{ + return MBED_CONF_SPINAND_SPINAND_FLASH_SIZE; +} + +int SPINANDBlockDevice::get_erase_value() const +{ + return 0xFF; +} + +/********************************/ +/* Different Device Csel Mgmt */ +/********************************/ +static PinName *generate_initialized_active_spinand_csel_arr() +{ + PinName *init_arr = new PinName[SPINAND_MAX_ACTIVE_FLASH_DEVICES]; + for (int i_ind = 0; i_ind < SPINAND_MAX_ACTIVE_FLASH_DEVICES; i_ind++) { + init_arr[i_ind] = NC; + } + return init_arr; +} + +int SPINANDBlockDevice::add_new_csel_instance(PinName csel) +{ + int status = 0; + _devices_mutex->lock(); + if (_number_of_active_spinand_flash_csel >= SPINAND_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 < SPINAND_MAX_ACTIVE_FLASH_DEVICES; i_ind++) { + if (_active_spinand_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 < SPINAND_MAX_ACTIVE_FLASH_DEVICES; i_ind++) { + if (_active_spinand_flash_csel_arr[i_ind] == NC) { + _active_spinand_flash_csel_arr[i_ind] = csel; + break; + } + } + _number_of_active_spinand_flash_csel++; + +exit_point: + _devices_mutex->unlock(); + return status; +} + +int SPINANDBlockDevice::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 < SPINAND_MAX_ACTIVE_FLASH_DEVICES; i_ind++) { + if (_active_spinand_flash_csel_arr[i_ind] == csel) { + _active_spinand_flash_csel_arr[i_ind] = NC; + if (_number_of_active_spinand_flash_csel > 0) { + _number_of_active_spinand_flash_csel--; + } + status = 0; + break; + } + } + _devices_mutex->unlock(); + return status; +} + +int SPINANDBlockDevice::_set_quad_enable() +{ + uint8_t secur_reg = 0; + + if (false == _is_mem_ready()) { + tr_error("Device not ready, set quad enable failed"); + return -1; + } + + if (QSPI_STATUS_OK != _qspi_send_general_command(SPINAND_INST_GET_FEATURE, FEATURES_ADDR_SECURE_OTP, + NULL, 0, (char *) &secur_reg, 1)) { + tr_error("Reading Security Register failed"); + } + + secur_reg |= SPINAND_SECURE_BIT_QE; + + if (QSPI_STATUS_OK != _qspi_send_general_command(SPINAND_INST_SET_FEATURE, FEATURES_ADDR_SECURE_OTP, + (char *) &secur_reg, 1, NULL, 0)) { + tr_error("Writing Security Register failed"); + } + if (QSPI_STATUS_OK != _qspi_send_general_command(SPINAND_INST_GET_FEATURE, FEATURES_ADDR_SECURE_OTP, + NULL, 0, (char *) &secur_reg, 1)) { + tr_error("Reading Security Register failed"); + } + if (false == _is_mem_ready()) { + tr_error("Device not ready, set quad enable failed"); + return -1; + } + + return 0; +} + +int SPINANDBlockDevice::_clear_block_protection() +{ + uint8_t block_protection_reg = 0; + + if (false == _is_mem_ready()) { + tr_error("Device not ready, clearing block protection failed"); + return -1; + } + + if (QSPI_STATUS_OK != _qspi_send_general_command(SPINAND_INST_GET_FEATURE, FEATURES_ADDR_BLOCK_PROTECTION, + NULL, 0, (char *) &block_protection_reg, 1)) { + tr_error("Reading Block Protection Register failed"); + } + + block_protection_reg &= ~SPINAND_BLOCK_PROT_BIT_BP_MASK; + + if (QSPI_STATUS_OK != _qspi_send_general_command(SPINAND_INST_SET_FEATURE, FEATURES_ADDR_BLOCK_PROTECTION, + (char *) &block_protection_reg, 1, NULL, 0)) { + tr_error("Writing Block Protection Register failed"); + } + if (QSPI_STATUS_OK != _qspi_send_general_command(SPINAND_INST_GET_FEATURE, FEATURES_ADDR_BLOCK_PROTECTION, + NULL, 0, (char *) &block_protection_reg, 1)) { + tr_error("Reading Block Protection Register failed"); + } + if (false == _is_mem_ready()) { + tr_error("Device not ready, clearing block protection failed"); + return -1; + } + + return 0; +} + +int SPINANDBlockDevice::_set_write_enable() +{ + // Check Status Register Busy Bit to Verify the Device isn't Busy + uint8_t status_value = 0; + int status = -1; + + do { + if (QSPI_STATUS_OK != _qspi_send_general_command(SPINAND_INST_WREN, QSPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0)) { + tr_error("Sending WREN command FAILED"); + break; + } + + if (false == _is_mem_ready()) { + tr_error("Device not ready, write failed"); + break; + } + + if (QSPI_STATUS_OK != _qspi_send_general_command(SPINAND_INST_GET_FEATURE, FEATURES_ADDR_STATUS, + NULL, 0, + (char *) &status_value, 1)) { // store received value in status_value + tr_error("Reading Status Register failed"); + } + + if ((status_value & SPINAND_STATUS_BIT_WEL) == 0) { + tr_error("_set_write_enable failed - status register 1 value: %u", status_value); + break; + } + + status = 0; + } while (false); + + return status; +} + +bool SPINANDBlockDevice::_is_mem_ready() +{ + // Check Status Register Busy Bit to Verify the Device isn't Busy + uint8_t status_value = 0; + int retries = 0; + bool mem_ready = true; + + do { + rtos::ThisThread::sleep_for(1ms); + retries++; + //Read Status Register 1 from device + if (QSPI_STATUS_OK != _qspi_send_general_command(SPINAND_INST_GET_FEATURE, FEATURES_ADDR_STATUS, + NULL, 0, + (char *) &status_value, 1)) { // store received value in status_value + tr_error("Reading Status Register failed"); + } + } while ((status_value & SPINAND_STATUS_BIT_WIP) != 0 && retries < IS_MEM_READY_MAX_RETRIES); + + if ((status_value & SPINAND_STATUS_BIT_WIP) != 0) { + tr_error("_is_mem_ready FALSE: status value = 0x%x ", status_value); + mem_ready = false; + } + return mem_ready; +} + +/***************************************************/ +/*********** QSPI Driver API Functions *************/ +/***************************************************/ +qspi_status_t SPINANDBlockDevice::_qspi_set_frequency(int freq) +{ + return _qspi.set_frequency(freq); +} + +qspi_status_t SPINANDBlockDevice::_qspi_send_read_command(qspi_inst_t read_inst, void *buffer, + bd_addr_t addr, bd_size_t size) +{ + tr_debug("Inst: 0x%xh, addr: %llu, size: %llu", read_inst, addr, size); + + size_t buf_len = size; + + // Send read command to device driver + // Read commands use the best bus mode supported by the part + qspi_status_t status = _qspi.configure_format(_inst_width, _address_width, SPI_NAND_COLUMN_ADDR_SIZE, // Alt width should be the same as address width + _address_width,_alt_size, _data_width, 0); + if (QSPI_STATUS_OK != status) { + tr_error("_qspi_configure_format failed"); + return status; + } + + if (QSPI_STATUS_OK != _qspi_send_general_command(SPINAND_INST_PAGE_READ, addr >> 12, NULL, 0, NULL, 0)) { + tr_error("Read page from array failed"); + } + + status = _qspi.configure_format(_inst_width, _address_width, _address_size, _address_width, // Alt width should be the same as address width + _alt_size, _data_width, 0); + if (QSPI_STATUS_OK != status) { + tr_error("_qspi_configure_format failed"); + return status; + } + + if (false == _is_mem_ready()) { + tr_error("Device not ready, clearing block protection failed"); + // return -1; + } + + status = _qspi.configure_format(_inst_width, _address_width, SPI_NAND_ROW_ADDR_SIZE, _address_width, // Alt width should be the same as address width + _alt_size, QSPI_CFG_BUS_QUAD, _dummy_cycles); + if (QSPI_STATUS_OK != status) { + tr_error("_qspi_configure_format failed"); + return status; + } + + // Don't check the read status until after we've configured the format back to 1-1-1, to avoid leaving the interface in an + // incorrect state if the read fails. + status = _qspi.read(read_inst, (_alt_size == 0) ? -1 : QSPI_ALT_DEFAULT_VALUE, (unsigned int)addr, (char *)buffer, &buf_len); + + // All commands other than Read use default 1-1-1 bus mode (Program/Erase are constrained by flash memory performance more than bus performance) + qspi_status_t format_status = _qspi.configure_format(QSPI_CFG_BUS_SINGLE, QSPI_CFG_BUS_SINGLE, _address_size, QSPI_CFG_BUS_SINGLE, 0, QSPI_CFG_BUS_SINGLE, 0); + if (QSPI_STATUS_OK != format_status) { + tr_error("_qspi_configure_format failed"); + return format_status; + } + + if (QSPI_STATUS_OK != status) { + tr_error("QSPI Read failed"); + return status; + } + + return QSPI_STATUS_OK; +} + +qspi_status_t SPINANDBlockDevice::_qspi_send_program_command(qspi_inst_t prog_inst, const void *buffer, + bd_addr_t addr, bd_size_t *size) +{ + tr_debug("Inst: 0x%xh, addr: %llu, size: %llu", prog_inst, addr, *size); + + // Program load commands need 16 bit row address + qspi_status_t status = _qspi.configure_format(_inst_width, _address_width, SPI_NAND_ROW_ADDR_SIZE, // Alt width should be the same as address width + _address_width, _alt_size, QSPI_CFG_BUS_QUAD, 0); + if (QSPI_STATUS_OK != status) { + tr_error("_qspi_configure_format failed"); + return status; + } + + // Send program (write) command to device driver + status = _qspi.write(prog_inst, -1, addr, (char *)buffer, (size_t *)size); + if (QSPI_STATUS_OK != status) { + tr_error("QSPI Write failed"); + return status; + } + + // Program execute command need 24 bit column address + qspi_status_t format_status = _qspi.configure_format(QSPI_CFG_BUS_SINGLE, QSPI_CFG_BUS_SINGLE, SPI_NAND_COLUMN_ADDR_SIZE, QSPI_CFG_BUS_SINGLE, + 0, QSPI_CFG_BUS_SINGLE, 0); + if (QSPI_STATUS_OK != format_status) { + tr_error("_qspi_configure_format failed"); + return format_status; + } + + // Program execute command + if (QSPI_STATUS_OK != _qspi_send_general_command(SPINAND_INST_PROGRAM_EXEC, addr >> 12, NULL, 0, NULL, 0)) { + tr_error("Read page from array failed"); + } + + status = _qspi.configure_format(_inst_width, _address_width, _address_size, _address_width, // Alt width should be the same as address width + _alt_size, _data_width, 0); + if (QSPI_STATUS_OK != status) { + tr_error("_qspi_configure_format failed"); + return status; + } + uint8_t status_value = 0; + if (QSPI_STATUS_OK != _qspi_send_general_command(SPINAND_INST_GET_FEATURE, FEATURES_ADDR_STATUS, + NULL, 0, + (char *) &status_value, 1)) { // store received value in status_value + tr_error("Reading Status Register failed"); + } + return QSPI_STATUS_OK; +} + +qspi_status_t SPINANDBlockDevice::_qspi_send_erase_command(qspi_inst_t erase_inst, bd_addr_t addr, bd_size_t size) +{ + tr_debug("Inst: 0x%xh, addr: %llu, size: %llu", erase_inst, addr, size); + + qspi_status_t status = _qspi.configure_format(_inst_width, _address_width, SPI_NAND_COLUMN_ADDR_SIZE,// Alt width should be the same as address width + _address_width, _alt_size, _data_width, 0); + if (QSPI_STATUS_OK != status) { + tr_error("_qspi_configure_format failed"); + return status; + } + + // Send erase command to driver + status = _qspi.command_transfer(erase_inst, (int) (addr >> 12), NULL, 0, NULL, 0); + + if (QSPI_STATUS_OK != status) { + tr_error("QSPI Erase failed"); + return status; + } + + status = _qspi.configure_format(_inst_width, _address_width, _address_size, _address_width, // Alt width should be the same as address width + _alt_size, _data_width, 0); + if (QSPI_STATUS_OK != status) { + tr_error("_qspi_configure_format failed"); + return status; + } + uint8_t status_value = 0; + if (QSPI_STATUS_OK != _qspi_send_general_command(SPINAND_INST_GET_FEATURE, FEATURES_ADDR_STATUS, + NULL, 0, + (char *) &status_value, 1)) { // store received value in status_value + tr_error("Reading Status Register failed"); + } + return QSPI_STATUS_OK; +} + +qspi_status_t SPINANDBlockDevice::_qspi_send_general_command(qspi_inst_t instruction, bd_addr_t addr, + const char *tx_buffer, bd_size_t tx_length, + const char *rx_buffer, bd_size_t rx_length) +{ + tr_debug("Inst: 0x%xh, addr: %llu, tx length: %llu, rx length: %llu", instruction, addr, tx_length, 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("Sending Generic command: %x", instruction); + return status; + } + + return QSPI_STATUS_OK; +} + diff --git a/storage/blockdevice/tests/TESTS/blockdevice/general_block_device/main.cpp b/storage/blockdevice/tests/TESTS/blockdevice/general_block_device/main.cpp index ff6bd9e1f8..474165e6bd 100644 --- a/storage/blockdevice/tests/TESTS/blockdevice/general_block_device/main.cpp +++ b/storage/blockdevice/tests/TESTS/blockdevice/general_block_device/main.cpp @@ -53,6 +53,10 @@ #include "FlashIAPBlockDevice.h" #endif +#if COMPONENT_SPINAND +#include "SPINANDBlockDevice.h" +#endif + // Debug available #ifndef MODE_DEBUG #define MODE_DEBUG 0 @@ -92,6 +96,7 @@ enum bd_type { sd, flashiap, ospif, + spinand, default_bd }; @@ -163,6 +168,23 @@ static BlockDevice *get_bd_instance(uint8_t bd_type) #endif break; } + case spinand: { +#if COMPONENT_SPINAND + static SPINANDBlockDevice default_bd( + MBED_CONF_SPINAND_SPINAND_IO0, + MBED_CONF_SPINAND_SPINAND_IO1, + MBED_CONF_SPINAND_SPINAND_IO2, + MBED_CONF_SPINAND_SPINAND_IO3, + MBED_CONF_SPINAND_SPINAND_SCK, + MBED_CONF_SPINAND_SPINAND_CSN, + MBED_CONF_SPINAND_SPINAND_POLARITY_MODE, + MBED_CONF_SPINAND_SPINAND_FREQ + ); + return &default_bd; +#endif + break; + } + case dataflash: { #if COMPONENT_DATAFLASH static DataFlashBlockDevice default_bd( @@ -298,7 +320,11 @@ void test_init_bd() if (curr_sector_size > max_sector_size) { max_sector_size = curr_sector_size; } +#if COMPONENT_SPINAND + start_address += 0x40000; +#else start_address += curr_sector_size; +#endif } num_of_sectors = i; } @@ -555,11 +581,19 @@ void test_contiguous_erase_write_read() // helping to avoid test timeouts. Try 256-byte chunks if contiguous_erase_size // (which should be a power of 2) is greater than that. If it's less than // that, the test finishes quickly anyway... +#if COMPONENT_SPINAND + if ((program_size < 2048) && (2048 % program_size == 0) + && (contiguous_erase_size >= 2048) && (contiguous_erase_size % 2048 == 0)) { + utest_printf("using 2048-byte write/read buffer\n"); + write_read_buf_size = 2048; + } +#else if ((program_size < 256) && (256 % program_size == 0) && (contiguous_erase_size >= 256) && (contiguous_erase_size % 256 == 0)) { utest_printf("using 256-byte write/read buffer\n"); write_read_buf_size = 256; } +#endif // Allocate write/read buffer uint8_t *write_read_buf = new (std::nothrow) uint8_t[write_read_buf_size]; @@ -572,7 +606,11 @@ void test_contiguous_erase_write_read() // Pre-fill the to-be-erased region. By pre-filling the region, // we can be sure the test will not pass if the erase doesn't work. +#if COMPONENT_SPINAND + for (bd_size_t offset = 0; start_address + offset < stop_address; offset += 0x40000) { +#else for (bd_size_t offset = 0; start_address + offset < stop_address; offset += write_read_buf_size) { +#endif for (size_t i = 0; i < write_read_buf_size; i++) { write_read_buf[i] = (uint8_t)rand(); } @@ -589,8 +627,11 @@ void test_contiguous_erase_write_read() // Loop through all write/read regions int region = 0; +#if COMPONENT_SPINAND + for (; start_address < stop_address; start_address += 0x40000) { +#else for (; start_address < stop_address; start_address += write_read_buf_size) { - +#endif // Generate test data unsigned int seed = rand(); srand(seed); @@ -790,6 +831,8 @@ void test_get_type_functionality() TEST_ASSERT_EQUAL(0, strcmp(bd_type, "SD")); #elif COMPONENT_FLASHIAP TEST_ASSERT_EQUAL(0, strcmp(bd_type, "FLASHIAP")); +#elif COMPONENT_SPINAND + TEST_ASSERT_EQUAL(0, strcmp(bd_type, "SPINAND")); #endif } @@ -848,11 +891,14 @@ int get_bd_count() #if COMPONENT_OSPIF bd_arr[count++] = ospif; //5 #endif +#if COMPONENT_SPINAND + bd_arr[count++] = spinand; //6 +#endif return count; } -static const char *prefix[] = {"SPIF ", "QSPIF ", "DATAFLASH ", "SD ", "FLASHIAP ", "OSPIF ", "DEFAULT "}; +static const char *prefix[] = {"SPIF ", "QSPIF ", "DATAFLASH ", "SD ", "FLASHIAP ", "OSPIF ","SPINAND ", "DEFAULT "}; int main() {