diff --git a/drivers/include/drivers/OSPI.h b/drivers/include/drivers/OSPI.h new file mode 100644 index 0000000..d10ef80 --- /dev/null +++ b/drivers/include/drivers/OSPI.h @@ -0,0 +1,272 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2020 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_OSPI_H +#define MBED_OSPI_H + +#include "platform/platform.h" + +#if DEVICE_OSPI || defined(DOXYGEN_ONLY) + +#include "hal/ospi_api.h" +#include "platform/PlatformMutex.h" +#include "platform/SingletonPtr.h" +#include "platform/NonCopyable.h" + +#define ONE_MHZ 1000000 + +#define OSPI_NO_INST (-1) + +namespace mbed { +/** \defgroup drivers-public-api-spi SPI + * \ingroup drivers-public-api + */ + +/** + * \defgroup drivers_OSPI OSPI class + * \ingroup drivers-public-api-spi + * @{ + */ + +/** Type representing a OSPI instruction + */ +typedef int ospi_inst_t; + +/** A OSPI Driver, used for communicating with OSPI slave devices + * + * The default format is set to OCTO-SPI(1-1-1), and a clock frequency of 1MHz + * Most OSPI devices will also require Chip Select which is indicated by ssel. + * + * @note Synchronization level: Thread safe + * + * Example: + * @code + * // Write 4 byte array to a OSPI slave, and read the response, note that each device will have its specific read/write/alt values defined + * + * #include "mbed.h" + * + * #define CMD_WRITE 0x02 + * #define CMD_READ 0x03 + * #define ADDRESS 0x1000 + * + * // hardware ssel (where applicable) + * OSPI ospi_device(OSPI_FLASH1_IO0, OSPI_FLASH1_IO1, OSPI_FLASH1_IO2, OSPI_FLASH1_IO3, OSPI_FLASH1_IO4, OSPI_FLASH1_IO5, OSPI_FLASH1_IO6, + * OSPI_FLASH1_SCK, OSPI_FLASH1_CSN, OSPI_FLASH1_DQS); // io0, io1, io2, io3, io4, io5, io6, io7, sclk, ssel, dqs + * + * + * int main() { + * char tx_buf[] = { 0x11, 0x22, 0x33, 0x44 }; + * char rx_buf[4]; + * int buf_len = sizeof(tx_buf); + * + * ospi_status_t result = ospi_device.write(CMD_WRITE, 0, ADDRESS, tx_buf, &buf_len); + * if (result != OSPI_STATUS_OK) { + * printf("Write failed"); + * } + * result = ospi_device.read(CMD_READ, 0, ADDRESS, rx_buf, &buf_len); + * if (result != OSPI_STATUS_OK) { + * printf("Read failed"); + * } + * + * } + * @endcode + */ +class OSPI : private NonCopyable { + +public: + + /** Create a OSPI master connected to the specified pins + * + * io0-io3 is used to specify the Pins used for Quad SPI mode + * + * @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 io4 5th IO pin used for sending/receiving data during data phase of a transaction + * @param io5 6th IO pin used for sending/receiving data during data phase of a transaction + * @param io6 7th IO pin used for sending/receiving data during data phase of a transaction + * @param io7 8th IO pin used for sending/receiving data during data phase of a transaction + * @param sclk OSPI Clock pin + * @param ssel OSPI chip select pin + * @param dqs OSPI dqs pin + * @param mode Clock polarity and phase mode (0 - 3) of SPI + * (Default: Mode=0 uses CPOL=0, CPHA=0, Mode=1 uses CPOL=1, CPHA=1) + * + */ + OSPI(PinName io0, PinName io1, PinName io2, PinName io3, PinName io4, PinName io5, PinName io6, PinName io7, + PinName sclk, PinName ssel = NC, PinName dqs = NC, int mode = 0); + + /** Create a OSPI master connected to the specified pins + * + * io0-io3 is used to specify the Pins used for Quad SPI mode + * + * @param pinmap reference to structure which holds static pinmap + * @param mode Clock polarity and phase mode (0 - 3) of SPI + * (Default: Mode=0 uses CPOL=0, CPHA=0, Mode=1 uses CPOL=1, CPHA=1) + * + */ + OSPI(const ospi_pinmap_t &pinmap, int mode = 0); + OSPI(const ospi_pinmap_t &&, int = 0) = delete; // prevent passing of temporary objects + + virtual ~OSPI() + { + } + + /** Configure the data transmission format + * + * @param inst_width Bus width used by instruction phase(Valid values are OSPI_CFG_BUS_SINGLE, OSPI_CFG_BUS_DUAL, OSPI_CFG_BUS_QUAD) + * @param inst_size Size in bits used by instruction phase + * @param address_width Bus width used by address phase(Valid values are OSPI_CFG_BUS_SINGLE, OSPI_CFG_BUS_DUAL, OSPI_CFG_BUS_QUAD) + * @param address_size Size in bits used by address phase(Valid values are OSPI_CFG_ADDR_SIZE_8, OSPI_CFG_ADDR_SIZE_16, OSPI_CFG_ADDR_SIZE_24, OSPI_CFG_ADDR_SIZE_32) + * @param alt_width Bus width used by alt phase(Valid values are OSPI_CFG_BUS_SINGLE, OSPI_CFG_BUS_DUAL, OSPI_CFG_BUS_QUAD) + * @param alt_size Size in bits used by alt phase (must be a multiple of the number of bus lines indicated in alt_width) + * @param data_width Bus width used by data phase(Valid values are OSPI_CFG_BUS_SINGLE, OSPI_CFG_BUS_DUAL, OSPI_CFG_BUS_QUAD) + * @param dummy_cycles Number of dummy clock cycles to be used after alt phase + * + */ + ospi_status_t configure_format(ospi_bus_width_t inst_width, + ospi_inst_size_t inst_size, + ospi_bus_width_t address_width, + ospi_address_size_t address_size, + ospi_bus_width_t alt_width, + ospi_alt_size_t alt_size, + ospi_bus_width_t data_width, + int dummy_cycles); + + /** Set the ospi bus clock frequency + * + * @param hz SCLK frequency in hz (default = 1MHz) + * @returns + * Returns OSPI_STATUS_SUCCESS on successful, fails if the interface is already init-ed + */ + ospi_status_t set_frequency(int hz = ONE_MHZ); + + /** Read from OSPI peripheral with the preset read_instruction and alt_value + * + * @param address Address to be accessed in OSPI peripheral + * @param rx_buffer Buffer for data to be read from the peripheral + * @param rx_length Pointer to a variable containing the length of rx_buffer, and on return this variable will be updated with the actual number of bytes read + * + * @returns + * Returns OSPI_STATUS_SUCCESS on successful reads and OSPI_STATUS_ERROR on failed reads. + */ + ospi_status_t read(int address, char *rx_buffer, size_t *rx_length); + + /** Write to OSPI peripheral using custom write instruction + * + * @param address Address to be accessed in OSPI peripheral + * @param tx_buffer Buffer containing data to be sent to peripheral + * @param tx_length Pointer to a variable containing the length of data to be transmitted, and on return this variable will be updated with the actual number of bytes written + * + * @returns + * Returns OSPI_STATUS_SUCCESS on successful reads and OSPI_STATUS_ERROR on failed reads. + */ + ospi_status_t write(int address, const char *tx_buffer, size_t *tx_length); + + /** Read from OSPI peripheral using custom read instruction, alt values + * + * @param instruction Instruction value to be used in instruction phase. Use OSPI_NO_INST to skip the instruction phase + * @param alt Alt value to be used in Alternate-byte phase. Use -1 for ignoring Alternate-byte phase + * @param address Address to be accessed in OSPI peripheral + * @param rx_buffer Buffer for data to be read from the peripheral + * @param rx_length Pointer to a variable containing the length of rx_buffer, and on return this variable will be updated with the actual number of bytes read + * + * @returns + * Returns OSPI_STATUS_SUCCESS on successful reads and OSPI_STATUS_ERROR on failed reads. + */ + ospi_status_t read(ospi_inst_t instruction, int alt, int address, char *rx_buffer, size_t *rx_length); + + /** Write to OSPI peripheral using custom write instruction, alt values + * + * @param instruction Instruction value to be used in instruction phase. Use OSPI_NO_INST to skip the instruction phase + * @param alt Alt value to be used in Alternate-byte phase. Use -1 for ignoring Alternate-byte phase + * @param address Address to be accessed in OSPI peripheral + * @param tx_buffer Buffer containing data to be sent to peripheral + * @param tx_length Pointer to a variable containing the length of data to be transmitted, and on return this variable will be updated with the actual number of bytes written + * + * @returns + * Returns OSPI_STATUS_SUCCESS on successful reads and OSPI_STATUS_ERROR on failed reads. + */ + ospi_status_t write(ospi_inst_t instruction, int alt, int address, const char *tx_buffer, size_t *tx_length); + + /** Perform a transaction to write to an address(a control register) and get the status results + * + * @param instruction Instruction value to be used in instruction phase. Use OSPI_NO_INST to skip the instruction phase + * @param address Some instruction might require address. Use -1 if no address + * @param tx_buffer Buffer containing data to be sent to peripheral + * @param tx_length Pointer to a variable containing the length of data to be transmitted, and on return this variable will be updated with the actual number of bytes written + * @param rx_buffer Buffer for data to be read from the peripheral + * @param rx_length Pointer to a variable containing the length of rx_buffer, and on return this variable will be updated with the actual number of bytes read + * + * @returns + * Returns OSPI_STATUS_SUCCESS on successful reads and OSPI_STATUS_ERROR on failed reads. + */ + ospi_status_t command_transfer(ospi_inst_t instruction, int address, const char *tx_buffer, size_t tx_length, const char *rx_buffer, size_t rx_length); + +#if !defined(DOXYGEN_ONLY) +protected: + /** Acquire exclusive access to this SPI bus + */ + virtual void lock(void); + + /** Release exclusive access to this SPI bus + */ + virtual void unlock(void); + + ospi_t _ospi; + + bool acquire(void); + static OSPI *_owner; + static SingletonPtr _mutex; + ospi_bus_width_t _inst_width; //Bus width for Instruction phase + ospi_inst_size_t _inst_size; //Instruction Size + ospi_bus_width_t _address_width; //Bus width for Address phase + ospi_address_size_t _address_size; + ospi_bus_width_t _alt_width; //Bus width for Alt phase + ospi_alt_size_t _alt_size; + ospi_bus_width_t _data_width; //Bus width for Data phase + ospi_command_t _ospi_command; //OSPI Hal command struct + unsigned int _num_dummy_cycles; //Number of dummy cycles to be used + int _hz; //Bus Frequency + int _mode; //SPI mode + bool _initialized; + PinName _ospi_io0, _ospi_io1, _ospi_io2, _ospi_io3, _ospi_io4, _ospi_io5, _ospi_io6, _ospi_io7, _ospi_clk, _ospi_cs, _ospi_dqs; //IO lines, clock, chip select and dqs + const ospi_pinmap_t *_static_pinmap; + bool (OSPI::* _init_func)(void); + +private: + /* Private acquire function without locking/unlocking + * Implemented in order to avoid duplicate locking and boost performance + */ + bool _acquire(void); + bool _initialize(); + bool _initialize_direct(); + + /* + * This function builds the ospi command struct to be send to Hal + */ + inline void _build_ospi_command(ospi_inst_t instruction, int address, int alt); +#endif +}; + +/** @}*/ + +} // namespace mbed + +#endif + +#endif diff --git a/drivers/include/drivers/internal/SFDP.h b/drivers/include/drivers/internal/SFDP.h index 9e3c759..ab0931b 100644 --- a/drivers/include/drivers/internal/SFDP.h +++ b/drivers/include/drivers/internal/SFDP.h @@ -68,10 +68,18 @@ unsigned int erase_type_size_arr[SFDP_MAX_NUM_OF_ERASE_TYPES]; ///< Erase sizes for all different erase types }; +/** JEDEC 4-byte Address Instruction Parameter Table info */ +struct sfdp_fbatbl_info { + uint32_t addr; ///< Address + size_t size; ///< Size + int erase_type_4_byte_inst_arr[SFDP_MAX_NUM_OF_ERASE_TYPES]; ///< // Up To 4 Erase Types are supported by SFDP (each with its own command Instruction and Size) +}; + /** SFDP JEDEC Parameter Table info */ struct sfdp_hdr_info { sfdp_bptbl_info bptbl; sfdp_smptbl_info smptbl; + sfdp_fbatbl_info fbatbl; }; /** Parse SFDP Database diff --git a/drivers/mbed_lib.json b/drivers/mbed_lib.json index 614baa9..4e54889 100644 --- a/drivers/mbed_lib.json +++ b/drivers/mbed_lib.json @@ -41,6 +41,50 @@ "qspi_csn": { "help": "QSPI chip select pin", "value": "QSPI_FLASH1_CSN" + }, + "ospi_io0": { + "help": "OSPI data I/O 0 pin", + "value": "OSPI_FLASH1_IO0" + }, + "ospi_io1": { + "help": "OSPI data I/O 1 pin", + "value": "OSPI_FLASH1_IO1" + }, + "ospi_io2": { + "help": "OSPI data I/O 2 pin", + "value": "OSPI_FLASH1_IO2" + }, + "ospi_io3": { + "help": "OSPI data I/O 3 pin", + "value": "OSPI_FLASH1_IO3" + }, + "ospi_io4": { + "help": "OSPI data I/O 4 pin", + "value": "OSPI_FLASH1_IO4" + }, + "ospi_io5": { + "help": "OSPI data I/O 5 pin", + "value": "OSPI_FLASH1_IO5" + }, + "ospi_io6": { + "help": "OSPI data I/O 6 pin", + "value": "OSPI_FLASH1_IO6" + }, + "ospi_io7": { + "help": "OSPI data I/O 7 pin", + "value": "OSPI_FLASH1_IO7" + }, + "ospi_sck": { + "help": "OSPI clock pin", + "value": "OSPI_FLASH1_SCK" + }, + "ospi_csn": { + "help": "OSPI chip select pin", + "value": "OSPI_FLASH1_CSN" + }, + "ospi_dqs": { + "help": "OSPI dqs pin", + "value": "OSPI_FLASH1_DQS" } } } diff --git a/drivers/source/OSPI.cpp b/drivers/source/OSPI.cpp new file mode 100644 index 0000000..73c80ad --- /dev/null +++ b/drivers/source/OSPI.cpp @@ -0,0 +1,378 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2020 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 "drivers/OSPI.h" +#include "platform/mbed_critical.h" +#include + +#if DEVICE_OSPI + +namespace mbed { + +OSPI *OSPI::_owner = NULL; +SingletonPtr OSPI::_mutex; + +uint8_t convert_bus_width_to_line_count(ospi_bus_width_t width) +{ + switch (width) { + case OSPI_CFG_BUS_SINGLE: + return 1; + case OSPI_CFG_BUS_DUAL: + return 2; + case OSPI_CFG_BUS_QUAD: + return 4; + case OSPI_CFG_BUS_OCTA: + case OSPI_CFG_BUS_OCTA_DTR: + return 8; + default: + // Unrecognized bus width + return 0; + } +} + +OSPI::OSPI(PinName io0, PinName io1, PinName io2, PinName io3, PinName io4, PinName io5, PinName io6, PinName io7, + PinName sclk, PinName ssel, PinName dqs, int mode) : _ospi() +{ + _ospi_io0 = io0; + _ospi_io1 = io1; + _ospi_io2 = io2; + _ospi_io3 = io3; + _ospi_io4 = io4; + _ospi_io5 = io5; + _ospi_io6 = io6; + _ospi_io7 = io7; + _ospi_clk = sclk; + _ospi_cs = ssel; + _ospi_dqs = dqs; + _static_pinmap = NULL; + _inst_width = OSPI_CFG_BUS_SINGLE; + _inst_size = OSPI_CFG_INST_SIZE_8; + _address_width = OSPI_CFG_BUS_SINGLE; + _address_size = OSPI_CFG_ADDR_SIZE_24; + _alt_width = OSPI_CFG_BUS_SINGLE; + _alt_size = 0; + _data_width = OSPI_CFG_BUS_SINGLE; + _num_dummy_cycles = 0; + _mode = mode; + _hz = ONE_MHZ; + _initialized = false; + _init_func = &OSPI::_initialize; + + //Go ahead init the device here with the default config + bool success = (this->*_init_func)(); + MBED_ASSERT(success); +} + +OSPI::OSPI(const ospi_pinmap_t &pinmap, int mode) : _ospi() +{ + _ospi_io0 = pinmap.data0_pin; + _ospi_io1 = pinmap.data1_pin; + _ospi_io2 = pinmap.data2_pin; + _ospi_io3 = pinmap.data3_pin; + _ospi_io4 = pinmap.data4_pin; + _ospi_io5 = pinmap.data5_pin; + _ospi_io6 = pinmap.data6_pin; + _ospi_io7 = pinmap.data7_pin; + _ospi_clk = pinmap.sclk_pin; + _ospi_cs = pinmap.ssel_pin; + _ospi_dqs = pinmap.dqs_pin; + _static_pinmap = &pinmap; + _inst_width = OSPI_CFG_BUS_SINGLE; + _inst_size = OSPI_CFG_INST_SIZE_8; + _address_width = OSPI_CFG_BUS_SINGLE; + _address_size = OSPI_CFG_ADDR_SIZE_24; + _alt_width = OSPI_CFG_BUS_SINGLE; + _alt_size = OSPI_CFG_ALT_SIZE_8; + _data_width = OSPI_CFG_BUS_SINGLE; + _num_dummy_cycles = 0; + _mode = mode; + _hz = ONE_MHZ; + _initialized = false; + _init_func = &OSPI::_initialize_direct; + + //Go ahead init the device here with the default config + bool success = (this->*_init_func)(); + MBED_ASSERT(success); +} + +ospi_status_t OSPI::configure_format(ospi_bus_width_t inst_width, ospi_inst_size_t inst_size, + ospi_bus_width_t address_width, ospi_address_size_t address_size, + ospi_bus_width_t alt_width, ospi_alt_size_t alt_size, + ospi_bus_width_t data_width, int dummy_cycles) +{ + // Check that alt_size/alt_width are a valid combination + uint8_t alt_lines = convert_bus_width_to_line_count(alt_width); + if (alt_lines == 0) { + return OSPI_STATUS_ERROR; + } else if (alt_size % alt_lines != 0) { + // Invalid alt size/width combination (alt size is not a multiple of the number of bus lines used to transmit it) + return OSPI_STATUS_ERROR; + } + + lock(); + _inst_width = inst_width; + _inst_size = inst_size; + _address_width = address_width; + _address_size = address_size; + _alt_width = alt_width; + _alt_size = alt_size; + _data_width = data_width; + _num_dummy_cycles = dummy_cycles; + unlock(); + + return OSPI_STATUS_OK; +} + +ospi_status_t OSPI::set_frequency(int hz) +{ + ospi_status_t ret_status = OSPI_STATUS_OK; + + if (_initialized) { + lock(); + _hz = hz; + //If the same owner, just change freq. + //Otherwise we may have to change mode as well, so call _acquire + if (_owner == this) { + if (OSPI_STATUS_OK != ospi_frequency(&_ospi, _hz)) { + ret_status = OSPI_STATUS_ERROR; + } + } else { + _acquire(); + } + unlock(); + } else { + ret_status = OSPI_STATUS_ERROR; + } + + return ret_status; +} + +ospi_status_t OSPI::read(int address, char *rx_buffer, size_t *rx_length) +{ + ospi_status_t ret_status = OSPI_STATUS_ERROR; + + if (_initialized) { + if ((rx_length != NULL) && (rx_buffer != NULL)) { + if (*rx_length != 0) { + lock(); + if (true == _acquire()) { + _build_ospi_command(OSPI_NO_INST, address, -1); + if (OSPI_STATUS_OK == ospi_read(&_ospi, &_ospi_command, rx_buffer, rx_length)) { + ret_status = OSPI_STATUS_OK; + } + } + unlock(); + } + } else { + ret_status = OSPI_STATUS_INVALID_PARAMETER; + } + } + + return ret_status; +} + +ospi_status_t OSPI::write(int address, const char *tx_buffer, size_t *tx_length) +{ + ospi_status_t ret_status = OSPI_STATUS_ERROR; + + if (_initialized) { + if ((tx_length != NULL) && (tx_buffer != NULL)) { + if (*tx_length != 0) { + lock(); + if (true == _acquire()) { + _build_ospi_command(OSPI_NO_INST, address, -1); + if (OSPI_STATUS_OK == ospi_write(&_ospi, &_ospi_command, tx_buffer, tx_length)) { + ret_status = OSPI_STATUS_OK; + } + } + unlock(); + } + } else { + ret_status = OSPI_STATUS_INVALID_PARAMETER; + } + } + + return ret_status; +} + +ospi_status_t OSPI::read(ospi_inst_t instruction, int alt, int address, char *rx_buffer, size_t *rx_length) +{ + ospi_status_t ret_status = OSPI_STATUS_ERROR; + + if (_initialized) { + if ((rx_length != NULL) && (rx_buffer != NULL)) { + if (*rx_length != 0) { + lock(); + if (true == _acquire()) { + _build_ospi_command(instruction, address, alt); + if (OSPI_STATUS_OK == ospi_read(&_ospi, &_ospi_command, rx_buffer, rx_length)) { + ret_status = OSPI_STATUS_OK; + } + } + unlock(); + } + } else { + ret_status = OSPI_STATUS_INVALID_PARAMETER; + } + } + + return ret_status; +} + +ospi_status_t OSPI::write(ospi_inst_t instruction, int alt, int address, const char *tx_buffer, size_t *tx_length) +{ + ospi_status_t ret_status = OSPI_STATUS_ERROR; + + if (_initialized) { + if ((tx_length != NULL) && (tx_buffer != NULL)) { + if (*tx_length != 0) { + lock(); + if (true == _acquire()) { + _build_ospi_command(instruction, address, alt); + if (OSPI_STATUS_OK == ospi_write(&_ospi, &_ospi_command, tx_buffer, tx_length)) { + ret_status = OSPI_STATUS_OK; + } + } + unlock(); + } + } else { + ret_status = OSPI_STATUS_INVALID_PARAMETER; + } + } + + return ret_status; +} + +ospi_status_t OSPI::command_transfer(ospi_inst_t instruction, int address, const char *tx_buffer, size_t tx_length, const char *rx_buffer, size_t rx_length) +{ + ospi_status_t ret_status = OSPI_STATUS_ERROR; + + if (_initialized) { + lock(); + if (true == _acquire()) { + _build_ospi_command(instruction, address, -1); //We just need the command + if (OSPI_STATUS_OK == ospi_command_transfer(&_ospi, &_ospi_command, (const void *)tx_buffer, tx_length, (void *)rx_buffer, rx_length)) { + ret_status = OSPI_STATUS_OK; + } + } + unlock(); + } + + return ret_status; +} + +void OSPI::lock() +{ + _mutex->lock(); +} + +void OSPI::unlock() +{ + _mutex->unlock(); +} + +// Note: Private helper function to initialize ospi HAL +bool OSPI::_initialize() +{ + if (_mode != 0 && _mode != 1) { + _initialized = false; + return _initialized; + } + + ospi_status_t ret = ospi_init(&_ospi, _ospi_io0, _ospi_io1, _ospi_io2, _ospi_io3, _ospi_io4, _ospi_io5, _ospi_io6, _ospi_io7, _ospi_clk, _ospi_cs, _ospi_dqs, _hz, _mode); + if (OSPI_STATUS_OK == ret) { + _initialized = true; + _owner = this; + } else { + _initialized = false; + } + + return _initialized; +} + +// Note: Private helper function to initialize ospi HAL +bool OSPI::_initialize_direct() +{ + if (_mode != 0 && _mode != 1) { + _initialized = false; + return _initialized; + } + + ospi_status_t ret = ospi_init_direct(&_ospi, _static_pinmap, _hz, _mode); + if (OSPI_STATUS_OK == ret) { + _initialized = true; + _owner = this; + } else { + _initialized = false; + } + + return _initialized; +} + +// Note: Private function with no locking +bool OSPI::_acquire() +{ + if (_owner != this) { + //This will set freq as well + (this->*_init_func)(); + _owner = this; + } + + return _initialized; +} + +void OSPI::_build_ospi_command(ospi_inst_t instruction, int address, int alt) +{ + memset(&_ospi_command, 0, sizeof(ospi_command_t)); + //Set up instruction phase parameters + _ospi_command.instruction.bus_width = _inst_width; + if (instruction != OSPI_NO_INST) { + _ospi_command.instruction.value = instruction; + _ospi_command.instruction.disabled = false; + } else { + _ospi_command.instruction.disabled = true; + } + + //Set up address phase parameters + _ospi_command.address.bus_width = _address_width; + _ospi_command.address.size = _address_size; + if (address != -1) { + _ospi_command.address.value = address; + _ospi_command.address.disabled = false; + } else { + _ospi_command.address.disabled = true; + } + + //Set up alt phase parameters + _ospi_command.alt.bus_width = _alt_width; + _ospi_command.alt.size = _alt_size; + if (alt != -1) { + _ospi_command.alt.value = alt; + _ospi_command.alt.disabled = false; + } else { + _ospi_command.alt.disabled = true; + } + + _ospi_command.dummy_count = _num_dummy_cycles; + + //Set up bus width for data phase + _ospi_command.data.bus_width = _data_width; +} + +} // namespace mbed + +#endif diff --git a/drivers/source/SFDP.cpp b/drivers/source/SFDP.cpp index 15ed551..b83ecd7 100644 --- a/drivers/source/SFDP.cpp +++ b/drivers/source/SFDP.cpp @@ -24,7 +24,7 @@ #include "platform/mbed_error.h" #include "drivers/internal/SFDP.h" -#if (DEVICE_SPI || DEVICE_QSPI) +#if (DEVICE_SPI || DEVICE_QSPI || DEVICE_OSPI) #include "mbed-trace/mbed_trace.h" #define TRACE_GROUP "SFDP" @@ -130,13 +130,15 @@ hdr_info.smptbl.addr = sfdp_get_param_tbl_ptr(phdr_ptr->DWORD2); hdr_info.smptbl.size = phdr_ptr->P_LEN * 4; break; + case 0x84: + tr_info("Parameter header: 4-byte Address Instruction"); + hdr_info.fbatbl.addr = sfdp_get_param_tbl_ptr(phdr_ptr->DWORD2); + hdr_info.fbatbl.size = phdr_ptr->P_LEN * 4; + break; /* Unsupported */ case 0x03: tr_info("UNSUPPORTED:Parameter header: Replay Protected Monotonic Counters"); break; - case 0x84: - tr_info("UNSUPPORTED:Parameter header: 4-byte Address Instruction"); - break; case 0x05: tr_info("UNSUPPORTED:Parameter header: eXtended Serial Peripheral Interface (xSPI) Profile 1.0"); break; @@ -437,7 +439,7 @@ return 0; } -#if DEVICE_QSPI +#if (DEVICE_QSPI || DEVICE_OSPI) int sfdp_detect_addressability(uint8_t *bptbl_ptr, sfdp_bptbl_info &bptbl_info) { // Check that density is not greater than 4 gigabits (i.e. that addressing beyond 4 bytes is not required) @@ -460,4 +462,4 @@ #endif } /* namespace mbed */ -#endif /* (DEVICE_SPI || DEVICE_QSPI) */ +#endif /* (DEVICE_SPI || DEVICE_QSPI || DEVICE_OSPI) */ diff --git a/hal/include/hal/ospi_api.h b/hal/include/hal/ospi_api.h new file mode 100644 index 0000000..fd9f651 --- /dev/null +++ b/hal/include/hal/ospi_api.h @@ -0,0 +1,354 @@ + +/** \addtogroup hal */ +/** @{*/ +/* mbed Microcontroller Library + * Copyright (c) 2020 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_OSPI_API_H +#define MBED_OSPI_API_H + +#include "device.h" +#include "pinmap.h" +#include + +#if DEVICE_OSPI + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \defgroup hal_ospi OSPI HAL + * @{ + */ + +/** OSPI HAL object + */ +typedef struct ospi_s ospi_t; + +typedef struct { + int peripheral; + PinName data0_pin; + int data0_function; + PinName data1_pin; + int data1_function; + PinName data2_pin; + int data2_function; + PinName data3_pin; + int data3_function; + PinName data4_pin; + int data4_function; + PinName data5_pin; + int data5_function; + PinName data6_pin; + int data6_function; + PinName data7_pin; + int data7_function; + PinName sclk_pin; + int sclk_function; + PinName ssel_pin; + int ssel_function; + PinName dqs_pin; + int dqs_function; + +} ospi_pinmap_t; + +/** OSPI Bus width + * + * Some parts of commands provide variable bus width + */ +typedef enum ospi_bus_width { + OSPI_CFG_BUS_SINGLE, + OSPI_CFG_BUS_DUAL, + OSPI_CFG_BUS_QUAD, + OSPI_CFG_BUS_OCTA, + OSPI_CFG_BUS_OCTA_DTR, +} ospi_bus_width_t; + +/** Instruction size in bits + */ +typedef enum ospi_inst_size { + OSPI_CFG_INST_SIZE_8, /* 1 byte for SPI mode */ + OSPI_CFG_INST_SIZE_16, /* 2 byte for OPI mode */ +} ospi_inst_size_t; + +/** Address size in bits + */ +typedef enum ospi_address_size { + OSPI_CFG_ADDR_SIZE_8, + OSPI_CFG_ADDR_SIZE_16, + OSPI_CFG_ADDR_SIZE_24, + OSPI_CFG_ADDR_SIZE_32, +} ospi_address_size_t; + +/** Alternative size in bits + */ +typedef uint8_t ospi_alt_size_t; + +// The following defines are provided for backwards compatibilty. New code should explicitly +// specify the required number of alt bits. +#define OSPI_CFG_ALT_SIZE_8 8u +#define OSPI_CFG_ALT_SIZE_16 16u +#define OSPI_CFG_ALT_SIZE_24 24u +#define OSPI_CFG_ALT_SIZE_32 32u + +/** OSPI command + * + * Defines a frame format. It consists of instruction, address, alternative, dummy count and data + */ +typedef struct ospi_command { + struct { + ospi_bus_width_t bus_width; /**< Bus width for the instruction >*/ + ospi_inst_size_t size; /**< Inst size >*/ + uint32_t value; /**< Instruction value >*/ + bool disabled; /**< Instruction phase skipped if disabled is set to true >*/ + } instruction; + struct { + ospi_bus_width_t bus_width; /**< Bus width for the address >*/ + ospi_address_size_t size; /**< Address size >*/ + uint32_t value; /**< Address value >*/ + bool disabled; /**< Address phase skipped if disabled is set to true >*/ + } address; + struct { + ospi_bus_width_t bus_width; /**< Bus width for alternative >*/ + ospi_alt_size_t size; /**< Alternative size >*/ + uint32_t value; /**< Alternative value >*/ + bool disabled; /**< Alternative phase skipped if disabled is set to true >*/ + } alt; + uint8_t dummy_count; /**< Dummy cycles count >*/ + struct { + ospi_bus_width_t bus_width; /**< Bus width for data >*/ + } data; +} ospi_command_t; + +/** OSPI return status + */ +typedef enum ospi_status { + OSPI_STATUS_ERROR = -1, /**< Generic error >*/ + OSPI_STATUS_INVALID_PARAMETER = -2, /**< The parameter is invalid >*/ + OSPI_STATUS_OK = 0, /**< Function executed sucessfully >*/ +} ospi_status_t; + +/** Initialize OSPI peripheral. + * + * It should initialize OSPI pins (io0-io7, sclk, ssel and dqs), set frequency, clock polarity and phase mode. The clock for the peripheral should be enabled + * + * @param obj OSPI object + * @param io0 Data pin 0 + * @param io1 Data pin 1 + * @param io2 Data pin 2 + * @param io3 Data pin 3 + * @param io4 Data pin 4 + * @param io5 Data pin 5 + * @param io6 Data pin 6 + * @param io7 Data pin 7 + * @param sclk The clock pin + * @param ssel The chip select pin + * @param dqs The chip dqs pin + * @param hz The bus frequency + * @param mode Clock polarity and phase mode (0 - 3) + * @return OSPI_STATUS_OK if initialisation successfully executed + OSPI_STATUS_INVALID_PARAMETER if invalid parameter found + OSPI_STATUS_ERROR otherwise + */ +ospi_status_t ospi_init(ospi_t *obj, PinName io0, PinName io1, PinName io2, PinName io3, PinName io4, PinName io5, PinName io6, PinName io7, + PinName sclk, PinName ssel, PinName dqs, uint32_t hz, uint8_t mode); + +/** Initialize OSPI peripheral. + * + * It should initialize OSPI pins (io0-io7, sclk, ssel and dqs), set frequency, clock polarity and phase mode. The clock for the peripheral should be enabled + * + * @param obj OSPI object + * @param pinmap pointer to structure which holds static pinmap + * @param hz The bus frequency + * @param mode Clock polarity and phase mode (0 - 3) + * @return OSPI_STATUS_OK if initialisation successfully executed + OSPI_STATUS_INVALID_PARAMETER if invalid parameter found + OSPI_STATUS_ERROR otherwise + */ +ospi_status_t ospi_init_direct(ospi_t *obj, const ospi_pinmap_t *pinmap, uint32_t hz, uint8_t mode); + +/** Deinitialize OSPI peripheral + * + * It should release pins that are associated with the OSPI object, and disable clocks for OSPI peripheral module that was associated with the object + * + * @param obj OSPI object + * @return OSPI_STATUS_OK if deinitialisation successfully executed + OSPI_STATUS_INVALID_PARAMETER if invalid parameter found + OSPI_STATUS_ERROR otherwise + */ +ospi_status_t ospi_free(ospi_t *obj); + +/** Set the OSPI baud rate + * + * Actual frequency may differ from the desired frequency due to available dividers and the bus clock + * Configures the OSPI peripheral's baud rate + * @param obj The SPI object to configure + * @param hz The baud rate in Hz + * @return OSPI_STATUS_OK if frequency was set + OSPI_STATUS_INVALID_PARAMETER if invalid parameter found + OSPI_STATUS_ERROR otherwise + */ +ospi_status_t ospi_frequency(ospi_t *obj, int hz); + +/** Send a command and block of data + * + * @param obj OSPI object + * @param command OSPI command + * @param data TX buffer + * @param[in,out] length in - TX buffer length in bytes, out - number of bytes written + * @return OSPI_STATUS_OK if the data has been succesfully sent + OSPI_STATUS_INVALID_PARAMETER if invalid parameter found + OSPI_STATUS_ERROR otherwise + */ +ospi_status_t ospi_write(ospi_t *obj, const ospi_command_t *command, const void *data, size_t *length); + +/** Send a command (and optionally data) and get the response. Can be used to send/receive device specific commands + * + * @param obj OSPI object + * @param command OSPI command + * @param tx_data TX buffer + * @param tx_size TX buffer length in bytes + * @param rx_data RX buffer + * @param rx_size RX buffer length in bytes + * @return OSPI_STATUS_OK if the data has been succesfully sent + OSPI_STATUS_INVALID_PARAMETER if invalid parameter found + OSPI_STATUS_ERROR otherwise + */ +ospi_status_t ospi_command_transfer(ospi_t *obj, const ospi_command_t *command, const void *tx_data, size_t tx_size, void *rx_data, size_t rx_size); + +/** Receive a command and block of data + * + * @param obj OSPI object + * @param command OSPI command + * @param data RX buffer + * @param[in,out] length in - RX buffer length in bytes, out - number of bytes read + * @return OSPI_STATUS_OK if data has been succesfully received + OSPI_STATUS_INVALID_PARAMETER if invalid parameter found + OSPI_STATUS_ERROR otherwise + */ +ospi_status_t ospi_read(ospi_t *obj, const ospi_command_t *command, void *data, size_t *length); + +/** Get the pins that support OSPI SCLK + * + * Return a PinMap array of pins that support OSPI SCLK in + * master mode. The array is terminated with {NC, NC, 0}. + * + * @return PinMap array + */ +const PinMap *ospi_master_sclk_pinmap(void); + +/** Get the pins that support OSPI SSEL + * + * Return a PinMap array of pins that support OSPI SSEL in + * master mode. The array is terminated with {NC, NC, 0}. + * + * @return PinMap array + */ +const PinMap *ospi_master_ssel_pinmap(void); + +/** Get the pins that support OSPI DQS + * + * Return a PinMap array of pins that support OSPI DQS in + * master mode. The array is terminated with {NC, NC, 0}. + * + * @return PinMap array + */ +const PinMap *ospi_master_dqs_pinmap(void); + +/** Get the pins that support OSPI DATA0 + * + * Return a PinMap array of pins that support OSPI DATA0 in + * master mode. The array is terminated with {NC, NC, 0}. + * + * @return PinMap array + */ +const PinMap *ospi_master_data0_pinmap(void); + +/** Get the pins that support OSPI DATA1 + * + * Return a PinMap array of pins that support OSPI DATA1 in + * master mode. The array is terminated with {NC, NC, 0}. + * + * @return PinMap array + */ +const PinMap *ospi_master_data1_pinmap(void); + +/** Get the pins that support OSPI DATA2 + * + * Return a PinMap array of pins that support OSPI DATA2 in + * master mode. The array is terminated with {NC, NC, 0}. + * + * @return PinMap array + */ +const PinMap *ospi_master_data2_pinmap(void); + +/** Get the pins that support OSPI DATA3 + * + * Return a PinMap array of pins that support OSPI DATA3 in + * master mode. The array is terminated with {NC, NC, 0}. + * + * @return PinMap array + */ +const PinMap *ospi_master_data3_pinmap(void); + +/** Get the pins that support OSPI DATA4 + * + * Return a PinMap array of pins that support OSPI DATA4 in + * master mode. The array is terminated with {NC, NC, 0}. + * + * @return PinMap array + */ +const PinMap *ospi_master_data4_pinmap(void); + +/** Get the pins that support OSPI DATA5 + * + * Return a PinMap array of pins that support OSPI DATA5 in + * master mode. The array is terminated with {NC, NC, 0}. + * + * @return PinMap array + */ +const PinMap *ospi_master_data5_pinmap(void); + +/** Get the pins that support OSPI DATA6 + * + * Return a PinMap array of pins that support OSPI DATA6 in + * master mode. The array is terminated with {NC, NC, 0}. + * + * @return PinMap array + */ +const PinMap *ospi_master_data6_pinmap(void); + +/** Get the pins that support OSPI DATA7 + * + * Return a PinMap array of pins that support OSPI DATA7 in + * master mode. The array is terminated with {NC, NC, 0}. + * + * @return PinMap array + */ +const PinMap *ospi_master_data7_pinmap(void); + +/**@}*/ + +#ifdef __cplusplus +} +#endif + +#endif + +#endif + +/** @}*/ diff --git a/hal/include/hal/static_pinmap.h b/hal/include/hal/static_pinmap.h index fafed9c..ef196a8 100644 --- a/hal/include/hal/static_pinmap.h +++ b/hal/include/hal/static_pinmap.h @@ -25,6 +25,7 @@ #include "i2c_api.h" #include "serial_api.h" #include "qspi_api.h" +#include "ospi_api.h" #include "can_api.h" #include @@ -293,6 +294,106 @@ } #endif //DEVICE_QSPI +#if defined(DEVICE_OSPI) && defined(PINMAP_OSPI_DATA0) && defined(PINMAP_OSPI_DATA1) && defined(PINMAP_OSPI_DATA2) && defined(PINMAP_OSPI_DATA3) && defined(PINMAP_OSPI_DATA4) && defined(PINMAP_OSPI_DATA5) && defined(PINMAP_OSPI_DATA6) && defined(PINMAP_OSPI_DATA7) && defined(PINMAP_OSPI_SCLK) && defined(PINMAP_OSPI_SSEL) && defined(PINMAP_OSPI_DQS) +MSTD_CONSTEXPR_FN_14 ospi_pinmap_t get_ospi_pinmap(const PinName data0, const PinName data1, const PinName data2, const PinName data3, const PinName data4, const PinName data5, const PinName data6, const PinName data7, const PinName sclk, const PinName ssel, const PinName dqs) +{ + const PinMap *data0_map = nullptr; + for (const PinMap &pinmap : PINMAP_OSPI_DATA0) { + if (pinmap.pin == data0) { + data0_map = &pinmap; + break; + } + } + + const PinMap *data1_map = nullptr; + for (const PinMap &pinmap : PINMAP_OSPI_DATA1) { + if (pinmap.pin == data1) { + data1_map = &pinmap; + break; + } + } + + const PinMap *data2_map = nullptr; + for (const PinMap &pinmap : PINMAP_OSPI_DATA2) { + if (pinmap.pin == data2) { + data2_map = &pinmap; + break; + } + } + + const PinMap *data3_map = nullptr; + for (const PinMap &pinmap : PINMAP_OSPI_DATA3) { + if (pinmap.pin == data3) { + data3_map = &pinmap; + break; + } + } + + const PinMap *data4_map = nullptr; + for (const PinMap &pinmap : PINMAP_OSPI_DATA4) { + if (pinmap.pin == data4) { + data4_map = &pinmap; + break; + } + } + + const PinMap *data5_map = nullptr; + for (const PinMap &pinmap : PINMAP_OSPI_DATA5) { + if (pinmap.pin == data5) { + data5_map = &pinmap; + break; + } + } + + const PinMap *data6_map = nullptr; + for (const PinMap &pinmap : PINMAP_OSPI_DATA6) { + if (pinmap.pin == data6) { + data6_map = &pinmap; + break; + } + } + + const PinMap *data7_map = nullptr; + for (const PinMap &pinmap : PINMAP_OSPI_DATA7) { + if (pinmap.pin == data7) { + data7_map = &pinmap; + break; + } + } + + const PinMap *sclk_map = nullptr; + for (const PinMap &pinmap : PINMAP_OSPI_SCLK) { + if (pinmap.pin == sclk) { + sclk_map = &pinmap; + break; + } + } + + const PinMap *ssel_map = nullptr; + for (const PinMap &pinmap : PINMAP_OSPI_SSEL) { + if (pinmap.pin == ssel) { + ssel_map = &pinmap; + break; + } + } + + const PinMap *dqs_map = nullptr; + for (const PinMap &pinmap : PINMAP_OSPI_DQS) { + if (pinmap.pin == dqs) { + dqs_map = &pinmap; + break; + } + } + + + if (!data0_map || !data1_map || !data2_map || !data3_map || !data4_map || !data5_map || !data6_map || !data7_map || !sclk_map || !ssel_map || !dqs_map || data0_map->peripheral != data1_map->peripheral || data0_map->peripheral != data2_map->peripheral || data0_map->peripheral != data3_map->peripheral || data0_map->peripheral != data4_map->peripheral || data0_map->peripheral != data5_map->peripheral || data0_map->peripheral != data6_map->peripheral || data0_map->peripheral != data7_map->peripheral || data0_map->peripheral != sclk_map->peripheral || data0_map->peripheral != ssel_map->peripheral || data0_map->peripheral != dqs_map->peripheral) { + return {(int) NC, NC, (int) NC, NC, (int) NC, NC, (int) NC, NC, (int) NC, NC, (int) NC, NC, (int) NC, NC, (int) NC, NC, (int) NC}; + } + + return {data0_map->peripheral, data0_map->pin, data0_map->function, data1_map->pin, data1_map->function, data2_map->pin, data2_map->function, data3_map->pin, data3_map->function, data4_map->pin, data4_map->function, data5_map->pin, data5_map->function, data6_map->pin, data6_map->function, data7_map->pin, data7_map->function, sclk_map->pin, sclk_map->function, ssel_map->pin, ssel_map->function, dqs_map->pin, dqs_map->function}; +} +#endif //DEVICE_OSPI + #else // STATIC_PINMAP_READY #if DEVICE_PWMOUT @@ -358,6 +459,13 @@ } #endif //DEVICE_QSPI +#if DEVICE_OSPI +MSTD_CONSTEXPR_FN_14 ospi_pinmap_t get_ospi_pinmap(const PinName data0, const PinName data1, const PinName data2, const PinName data3, const PinName data4, const PinName data5, const PinName data6, const PinName data7, const PinName sclk, const PinName ssel, const PinName dqs) +{ + return {(int) NC, data0, (int) NC, data1, (int) NC, data2, (int) NC, data3, (int) NC, data4, (int) NC, data5, (int) NC, data6, (int) NC, data7, (int) NC, sclk, (int) NC, ssel, (int) NC, dqs, (int) NC}; +} +#endif //DEVICE_OSPI + #endif // STATIC_PINMAP_READY #endif // STATIC_PINMAP_H diff --git a/hal/source/static_pinmap.cpp b/hal/source/static_pinmap.cpp index ba1f958..fe004c3 100644 --- a/hal/source/static_pinmap.cpp +++ b/hal/source/static_pinmap.cpp @@ -93,4 +93,11 @@ } #endif +#if DEVICE_OSPI +MBED_WEAK ospi_status_t ospi_init_direct(ospi_t *obj, const ospi_pinmap_t *pinmap, uint32_t hz, uint8_t mode) +{ + return ospi_init(obj, pinmap->data0_pin, pinmap->data1_pin, pinmap->data2_pin, pinmap->data3_pin, pinmap->data4_pin, pinmap->data5_pin, pinmap->data6_pin, pinmap->data7_pin, pinmap->sclk_pin, pinmap->ssel_pin, pinmap->dqs_pin, hz, mode); +} +#endif + #endif diff --git a/hal/tests/TESTS/mbed_hal/ospi/flash_configs/MX25LM51245G_config.h b/hal/tests/TESTS/mbed_hal/ospi/flash_configs/MX25LM51245G_config.h new file mode 100644 index 0000000..b808cef --- /dev/null +++ b/hal/tests/TESTS/mbed_hal/ospi/flash_configs/MX25LM51245G_config.h @@ -0,0 +1,222 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018-2020 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_OSPI_FLASH_MX25LM51245G_H +#define MBED_OSPI_FLASH_MX25LM51245G_H + + +#define OSPI_FLASH_CHIP_STRING "macronix MX25LM51245G" + +#define OSPI_CMD_RDID 0x9F +// Command for reading status register +#define OSPI_CMD_RDSR 0x05 +// Command for reading configuration register +#define OSPI_CMD_RDCR0 0x15 +#define OSPI_CMD_RDCR2 0x71 +// Command for writing status/configuration register +#define OSPI_CMD_WRSR 0x01 + +#define OSPI_CMD_WRCR2 0x72 + +// Command for reading security register +#define OSPI_CMD_RDSCUR 0x2B + +// Command for setting Reset Enable +#define OSPI_CMD_RSTEN 0x66 +// Command for setting Reset +#define OSPI_CMD_RST 0x99 + +// Command for setting write enable +#define OSPI_CMD_WREN 0x06 +// Command for setting write disable +#define OSPI_CMD_WRDI 0x04 + +// WRSR operations max time [us] (datasheet max time + 15%) +#define OSPI_WRSR_MAX_TIME 34500 // 30ms +// general wait max time [us] +#define OSPI_WAIT_MAX_TIME 100000 // 100ms + + +// Commands for writing (page programming) +// Only single/octal mode supported with this memory +// So only single 1-1-1 mode in this OSPI config +//#define OSPI_CMD_WRITE_1IO 0x02 // 1-1-1 mode +#define OSPI_CMD_WRITE_1IO 0x12 // 4 byte addr 1-1-1 mode +#define OSPI_CMD_WRITE_OPI 0x12 // 8-8-8 mode + +// write operations max time [us] (datasheet max time + 15%) +#define OSPI_PAGE_PROG_MAX_TIME 11500 // 10ms + +#define OSPI_PAGE_SIZE 256 // 256B +#define OSPI_SECTOR_SIZE 4096 // 4kB +#define OSPI_SECTOR_COUNT 2048 + +// Commands for reading +// Only single/octal mode supported with this memory +// So only single 1-1-1 mode in this OSPI config +#define OSPI_CMD_READ_1IO_FAST 0x0B // 1-1-1 mode +//#define OSPI_CMD_READ_1IO 0x03 // 1-1-1 mode +#define OSPI_CMD_READ_1IO 0x13 // 4 byte addr 1-1-1 mode +#define OSPI_CMD_READ_OPI 0xEC // 8-8-8 mode +#define OSPI_CMD_READ_DOPI 0xEE // 8D-8D-8D mode + +#define OSPI_READ_1IO_DUMMY_CYCLE 0 +#define OSPI_READ_FAST_DUMMY_CYCLE 8 +#define OSPI_READ_8IO_DUMMY_CYCLE 20 + +// Commands for erasing +#define OSPI_CMD_ERASE_SECTOR 0x21 // 4kB +#define OSPI_CMD_ERASE_SECTOR_4B 0x21 // 4kB +//#define OSPI_CMD_ERASE_BLOCK_32 // not supported, only ersae block 64 +#define OSPI_CMD_ERASE_BLOCK_64 0xD8 // 64kB +#define OSPI_CMD_ERASE_CHIP 0x60 // or 0xC7 + +// erase operations max time [us] (datasheet max time + 15%) +#define OSPI_ERASE_SECTOR_MAX_TIME 480000 // 400 ms +#define OSPI_ERASE_BLOCK_64_MAX_TIME 2400000 // 2s + +// max frequency for basic rw operation (for fast mode) +#define OSPI_COMMON_MAX_FREQUENCY 66000000 + +#define OSPI_STATUS_REG_SIZE 2 //2 ?? +#define OSPI_CONFIG_REG_0_SIZE 1 +#define OSPI_CONFIG_REG_1_SIZE 2 +#define OSPI_CONFIG_REG_2_SIZE 2 //1 byte in spi and sopi mode;2 byte in dopi +#define OSPI_SECURITY_REG_SIZE 1 +#define OSPI_MAX_REG_SIZE 2 + +// status register +#define STATUS_BIT_WIP (1 << 0) // write in progress bit +#define STATUS_BIT_WEL (1 << 1) // write enable latch +#define STATUS_BIT_BP0 (1 << 2) // +#define STATUS_BIT_BP1 (1 << 3) // +#define STATUS_BIT_BP2 (1 << 4) // +#define STATUS_BIT_BP3 (1 << 5) // +//#define STATUS_BIT_QE (1 << 6) // Not supported +//#define STATUS_BIT_SRWD (1 << 7) // Not supported + +// configuration register 0 +// bit 0, 1, 2, 4, 5, 7 reserved +#define CONFIG0_BIT_TB (1 << 3) // Top/Bottom area protect + +#define CONFIG2_OPI_EN_ADDR 0x00000000 + +// configuration register 2 +#define CONFIG2_BIT_SOPI_EN (1 << 0) // sopi mode enable +#define CONFIG2_BIT_DOPI_EN (1 << 1) // dopi mode enable +#define CONFIG2_SPI_EN 0 // spi mode enable + +#define OCTA_ENABLE() \ + \ + uint8_t reg_data[OSPI_CONFIG_REG_2_SIZE]; \ + \ + memset(reg_data, 0, OSPI_CONFIG_REG_2_SIZE); \ + if (write_enable(ospi) != OSPI_STATUS_OK) { \ + return OSPI_STATUS_ERROR; \ + } \ + \ + reg_data[0] = CONFIG2_BIT_SOPI_EN; \ + ospi.cmd.configure(MODE_1_1_1, ADDR_SIZE_32, ALT_SIZE_8); \ + if (write_config_register_2(OSPI_CMD_WRCR2, CONFIG2_OPI_EN_ADDR, \ + reg_data, 1, ospi) != OSPI_STATUS_OK) { \ + return OSPI_STATUS_ERROR; \ + } \ + ospi.cmd.configure(MODE_8_8_8, ADDR_SIZE_32, ALT_SIZE_8); \ + WAIT_FOR(WRSR_MAX_TIME, ospi); \ + memset(reg_data, 0, OSPI_CONFIG_REG_2_SIZE); \ + \ + if (read_config_register_2(OSPI_CMD_RDCR2, CONFIG2_OPI_EN_ADDR, \ + reg_data, OSPI_CONFIG_REG_2_SIZE, ospi) != OSPI_STATUS_OK) { \ + return OSPI_STATUS_ERROR; \ + } \ + return (reg_data[0] == CONFIG2_BIT_SOPI_EN ? \ + OSPI_STATUS_OK : OSPI_STATUS_ERROR) + + +#define OCTA_DISABLE() \ + \ + uint8_t reg_data[OSPI_CONFIG_REG_2_SIZE]; \ + \ + memset(reg_data, 0, OSPI_CONFIG_REG_2_SIZE); \ + if (write_enable(ospi) != OSPI_STATUS_OK) { \ + return OSPI_STATUS_ERROR; \ + } \ + \ + reg_data[0] = CONFIG2_SPI_EN; \ + if (write_config_register_2(OSPI_CMD_WRCR2, CONFIG2_OPI_EN_ADDR, \ + reg_data, 1, ospi) != OSPI_STATUS_OK) { \ + return OSPI_STATUS_ERROR; \ + } \ + ospi.cmd.configure(MODE_1_1_1, ADDR_SIZE_32, ALT_SIZE_8); \ + WAIT_FOR(WRSR_MAX_TIME, ospi); \ + memset(reg_data, 0, OSPI_CONFIG_REG_2_SIZE); \ + if (read_config_register_2(OSPI_CMD_RDCR2, CONFIG2_OPI_EN_ADDR, \ + reg_data, 1, ospi) != OSPI_STATUS_OK) { \ + return OSPI_STATUS_ERROR; \ + } \ + return (reg_data[0] == CONFIG2_SPI_EN ? \ + OSPI_STATUS_OK : OSPI_STATUS_ERROR) + +#define OCTA_DTR_ENABLE() \ + \ + uint8_t reg_data[OSPI_CONFIG_REG_2_SIZE]; \ + \ + memset(reg_data, 0, OSPI_CONFIG_REG_2_SIZE); \ + if (write_enable(ospi) != OSPI_STATUS_OK) { \ + return OSPI_STATUS_ERROR; \ + } \ + \ + reg_data[0] = CONFIG2_BIT_DOPI_EN; \ + ospi.cmd.configure(MODE_1_1_1, ADDR_SIZE_32, ALT_SIZE_8); \ + if (write_config_register_2(OSPI_CMD_WRCR2, CONFIG2_OPI_EN_ADDR, \ + reg_data, 1, ospi) != OSPI_STATUS_OK) { \ + return OSPI_STATUS_ERROR; \ + } \ + ospi.cmd.configure(MODE_8D_8D_8D, ADDR_SIZE_32, ALT_SIZE_8); \ + WAIT_FOR(WRSR_MAX_TIME, ospi); \ + memset(reg_data, 0, OSPI_CONFIG_REG_2_SIZE); \ + if (read_config_register_2(OSPI_CMD_RDCR2, CONFIG2_OPI_EN_ADDR, \ + reg_data, OSPI_CONFIG_REG_2_SIZE, ospi) != OSPI_STATUS_OK) { \ + return OSPI_STATUS_ERROR; \ + } \ + return (reg_data[0] == CONFIG2_BIT_DOPI_EN ? \ + OSPI_STATUS_OK : OSPI_STATUS_ERROR) + + +#define OCTA_DTR_DISABLE() \ + \ + uint8_t reg_data[OSPI_CONFIG_REG_2_SIZE]; \ + \ + memset(reg_data, 0, OSPI_CONFIG_REG_2_SIZE); \ + if (write_enable(ospi) != OSPI_STATUS_OK) { \ + return OSPI_STATUS_ERROR; \ + } \ + \ + reg_data[0] = CONFIG2_SPI_EN; \ + if (write_config_register_2(OSPI_CMD_WRCR2, CONFIG2_OPI_EN_ADDR, \ + reg_data, OSPI_CONFIG_REG_2_SIZE, ospi) != OSPI_STATUS_OK) { \ + return OSPI_STATUS_ERROR; \ + } \ + ospi.cmd.configure(MODE_1_1_1, ADDR_SIZE_32, ALT_SIZE_8); \ + WAIT_FOR(WRSR_MAX_TIME, ospi); \ + memset(reg_data, 0, OSPI_CONFIG_REG_2_SIZE); \ + if (read_config_register_2(OSPI_CMD_RDCR2, CONFIG2_OPI_EN_ADDR, \ + reg_data, OSPI_CONFIG_REG_2_SIZE, ospi) != OSPI_STATUS_OK) { \ + return OSPI_STATUS_ERROR; \ + } \ + return (reg_data[0] == CONFIG2_SPI_EN ? \ + OSPI_STATUS_OK : OSPI_STATUS_ERROR) + +#endif // MBED_OSPI_FLASH_MX25LM51245G_H diff --git a/hal/tests/TESTS/mbed_hal/ospi/flash_configs/flash_configs.h b/hal/tests/TESTS/mbed_hal/ospi/flash_configs/flash_configs.h new file mode 100644 index 0000000..d7872bd --- /dev/null +++ b/hal/tests/TESTS/mbed_hal/ospi/flash_configs/flash_configs.h @@ -0,0 +1,26 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018-2020 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_FLASH_CONFIGS_H +#define MBED_FLASH_CONFIGS_H + +#if defined(TARGET_MX25LM51245G) +#include "MX25LM51245G_config.h" +#endif + +#endif // MBED_FLASH_CONFIGS_H + diff --git a/hal/tests/TESTS/mbed_hal/ospi/main.cpp b/hal/tests/TESTS/mbed_hal/ospi/main.cpp new file mode 100644 index 0000000..ab96608 --- /dev/null +++ b/hal/tests/TESTS/mbed_hal/ospi/main.cpp @@ -0,0 +1,645 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018-2020 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. + */ + +#if !DEVICE_OSPI +#error [NOT_SUPPORTED] OSPI not supported for this target +#else + +#include "utest/utest.h" +#include "unity/unity.h" +#include "greentea-client/test_env.h" +#include "ospi_test.h" +#include "ospi_test_utils.h" + +#include "mbed.h" +#include "ospi_api.h" +#include "hal/us_ticker_api.h" + + +#if !defined(OSPI_FLASH_CHIP_STRING) +#error [NOT_SUPPORTED] OSPI test not supported for this target +#else + +using namespace utest::v1; + +// uncomment to enable verbose mode +//#define OSPI_TEST_LOG_DATA +//#define OSPI_TEST_LOG_FLASH_TIME +//#define OSPI_TEST_LOG_FLASH_STATUS + + + +#ifndef OSPI_MIN_FREQUENCY +#define OSPI_MIN_FREQUENCY 1000000 +#endif + +// max write size is usually page size +#define DATA_SIZE_256 (OSPI_PAGE_SIZE) +#define DATA_SIZE_1024 (OSPI_PAGE_SIZE * 4) + +uint8_t tx_buf[DATA_SIZE_1024]; +uint8_t rx_buf[DATA_SIZE_1024]; + + +// write address should be page aligned +#define TEST_FLASH_ADDRESS 0x0 + +#define TEST_REPEAT_SINGLE 1 +#define TEST_REPEAT_MULTIPLE 4 + +// write block of data in single write operation +#define WRITE_SINGLE 1 +// write block of data in adjacent locations in multiple write operations +#define WRITE_MULTIPLE 4 + +// read block of data in single read operation +#define READ_SINGLE 1 +// read block of data in adjacent locations in multiple read operations +#define READ_MULTIPLE 4 + + +// some target defines OSPI pins as integers thus conversion needed +#define OPIN_0 static_cast(MBED_CONF_DRIVERS_OSPI_IO0) +#define OPIN_1 static_cast(MBED_CONF_DRIVERS_OSPI_IO1) +#define OPIN_2 static_cast(MBED_CONF_DRIVERS_OSPI_IO2) +#define OPIN_3 static_cast(MBED_CONF_DRIVERS_OSPI_IO3) +#define OPIN_4 static_cast(MBED_CONF_DRIVERS_OSPI_IO4) +#define OPIN_5 static_cast(MBED_CONF_DRIVERS_OSPI_IO5) +#define OPIN_6 static_cast(MBED_CONF_DRIVERS_OSPI_IO6) +#define OPIN_7 static_cast(MBED_CONF_DRIVERS_OSPI_IO7) +#define QSCK static_cast(MBED_CONF_DRIVERS_OSPI_SCK) +#define QCSN static_cast(MBED_CONF_DRIVERS_OSPI_CSN) +#define DQS static_cast(MBED_CONF_DRIVERS_OSPI_DQS) + +static uint32_t gen_flash_address() +{ + srand(ticker_read(get_us_ticker_data())); + uint32_t address = (((uint32_t)rand()) % OSPI_SECTOR_COUNT) * OSPI_SECTOR_SIZE; + address &= 0xFFFFFF; // Ensure address is within 24 bits so as to not have to deal with 4-byte addressing + return address; +} + +static void log_data(const char *str, uint8_t *data, uint32_t size) +{ + utest_printf("%s: ", str); + for (uint32_t j = 0; j < size; j++) { + utest_printf("%02X ", data[j]); + } + utest_printf("\r\n"); +} + + +static void _ospi_write_read_test(Ospi &ospi, ospi_bus_width_t write_inst_width, ospi_bus_width_t write_addr_width, + ospi_bus_width_t write_data_width, ospi_bus_width_t write_alt_width, uint32_t write_cmd, + ospi_address_size_t write_addr_size, ospi_alt_size_t write_alt_size, + uint32_t write_count, ospi_bus_width_t read_inst_width, ospi_bus_width_t read_addr_width, + ospi_bus_width_t read_data_width, ospi_bus_width_t read_alt_width, uint32_t read_cmd, + int read_dummy_cycles, ospi_address_size_t read_addr_size, ospi_alt_size_t read_alt_size, + uint32_t read_count, uint32_t test_count, uint32_t data_size, + uint32_t flash_addr) +{ + ospi_status_t ret = OSPI_STATUS_OK; + + Timer timer; + int erase_time = 0, write_time = 0, read_time = 0; + size_t buf_len = data_size; + + for (uint32_t tc = 0; tc < test_count; tc++) { + + srand(ticker_read(get_us_ticker_data())); + for (uint32_t i = 0; i < data_size; i++) { + tx_buf[i] = (uint8_t)(rand() & 0xFF); + } + + ret = write_enable(ospi); + TEST_ASSERT_EQUAL(OSPI_STATUS_OK, ret); + + timer.reset(); + timer.start(); + + ret = erase(SECTOR_ERASE, flash_addr, ospi); + TEST_ASSERT_EQUAL(OSPI_STATUS_OK, ret); + WAIT_FOR(SECTOR_ERASE_MAX_TIME, ospi); + + timer.stop(); + erase_time = timer.read_us(); + + // switching to extended-SPI/DPI/QPI mode here for write operation + // for DPI/QPI ospi.cmd is automatically switched to 2_2_2/4_4_4 mode + ret = mode_enable(ospi, write_inst_width, write_addr_width, write_data_width); + TEST_ASSERT_EQUAL(OSPI_STATUS_OK, ret); + + const uint32_t write_size = data_size / write_count; + for (uint32_t wc = 0, write_start = flash_addr; wc < write_count; wc++, write_start += write_size) { + ret = write_enable(ospi); + TEST_ASSERT_EQUAL(OSPI_STATUS_OK, ret); + + timer.reset(); + timer.start(); + + buf_len = write_size; + ospi.cmd.configure(write_inst_width, write_addr_width, write_data_width, write_alt_width, write_addr_size, write_alt_size); + ospi.cmd.build(write_cmd, write_start); + ret = ospi_write(&ospi.handle, ospi.cmd.get(), tx_buf + wc * write_size, &buf_len); + TEST_ASSERT_EQUAL(OSPI_STATUS_OK, ret); + TEST_ASSERT_EQUAL(write_size, buf_len); + + if (is_extended_mode(write_inst_width, write_addr_width, write_data_width)) { + // on some flash chips in extended-SPI mode, control commands works only in 1-1-1 mode + // so switching back to 1-1-1 mode + ospi.cmd.configure(MODE_1_1_1, ADDR_SIZE_32, ALT_SIZE_8); + } + + WAIT_FOR(PAGE_PROG_MAX_TIME, ospi); + + timer.stop(); + write_time = timer.read_us(); + } + + // switching back to single channel SPI + ret = mode_disable(ospi, write_inst_width, write_addr_width, write_data_width); + TEST_ASSERT_EQUAL(OSPI_STATUS_OK, ret); + + // switching to extended-SPI/DPI/QPI mode here for read operation + // for DPI/QPI ospi.cmd is automatically switched to 2_2_2/4_4_4 mode + ret = mode_enable(ospi, read_inst_width, read_addr_width, read_data_width); + TEST_ASSERT_EQUAL(OSPI_STATUS_OK, ret); + + memset(rx_buf, 0, sizeof(rx_buf)); + const uint32_t read_size = data_size / read_count; + ospi.cmd.configure(read_inst_width, read_addr_width, read_data_width, read_alt_width, read_addr_size, read_alt_size, read_dummy_cycles); + for (uint32_t rc = 0, read_start = flash_addr; rc < read_count; rc++, read_start += read_size) { + timer.reset(); + timer.start(); + + buf_len = read_size; + ospi.cmd.build(read_cmd, read_start); + ret = ospi_read(&ospi.handle, ospi.cmd.get(), rx_buf + rc * read_size, &buf_len); + TEST_ASSERT_EQUAL(OSPI_STATUS_OK, ret); + TEST_ASSERT_EQUAL(read_size, buf_len); + + timer.stop(); + read_time = timer.read_us(); + } + ospi.cmd.set_dummy_cycles(0); + + if (is_extended_mode(read_inst_width, read_addr_width, read_data_width)) { + // on some flash chips in extended-SPI mode, control commands works only in 1-1-1 mode + // so switching back to 1-1-1 mode + ospi.cmd.configure(MODE_1_1_1, ADDR_SIZE_32, ALT_SIZE_8); + } + + // switching back to single channel SPI + ret = mode_disable(ospi, read_inst_width, read_addr_width, read_data_width); + TEST_ASSERT_EQUAL(OSPI_STATUS_OK, ret); + + for (uint32_t i = 0; i < data_size; i++) { + if (tx_buf[i] != rx_buf[i]) { + log_data("tx data", tx_buf, data_size); + log_data("rx data", rx_buf, data_size); + utest_printf("erase/write/read time: %d/%d/%d [us]\r\n", erase_time, write_time, read_time); + TEST_ASSERT_EQUAL(tx_buf[i], rx_buf[i]); + } + } + +#ifdef OSPI_TEST_LOG_FLASH_TIME + utest_printf("erase/write/read time: %d/%d/%d [us]\r\n", erase_time, write_time, read_time); +#endif + +#ifdef OSPI_TEST_LOG_DATA + log_data("tx data", tx_buf, data_size); + log_data("rx data", rx_buf, data_size); + utest_printf("rx/tx data match\r\n"); +#endif + } +} + + +template < ospi_bus_width_t write_inst_width, + ospi_bus_width_t write_addr_width, + ospi_bus_width_t write_data_width, + ospi_bus_width_t write_alt_width, + unsigned int write_cmd, + ospi_address_size_t write_addr_size, + ospi_alt_size_t write_alt_size, + uint32_t write_count, + ospi_bus_width_t read_inst_width, + ospi_bus_width_t read_addr_width, + ospi_bus_width_t read_data_width, + ospi_bus_width_t read_alt_width, + unsigned int read_cmd, + int read_dummy_cycles, + ospi_address_size_t read_addr_size, + ospi_alt_size_t read_alt_size, + int frequency, + uint32_t read_count, + uint32_t test_count, + uint32_t data_size, + uint32_t flash_addr> +void ospi_write_read_test(void) +{ + ospi_status_t ret; + Ospi ospi; + + uint32_t addr = flash_addr; + if (addr == 0) { + // if no specified address selected, use random one to extend flash life + addr = gen_flash_address(); + } + + ospi_init(&ospi.handle, OPIN_0, OPIN_1, OPIN_2, OPIN_3, OPIN_4, OPIN_5, OPIN_6, OPIN_7, QSCK, QCSN, DQS, OSPI_COMMON_MAX_FREQUENCY, 0); + + ret = ospi_frequency(&ospi.handle, frequency); + TEST_ASSERT_EQUAL(OSPI_STATUS_OK, ret); + + ospi.cmd.configure(MODE_1_1_1, ADDR_SIZE_32, ALT_SIZE_8); + flash_init(ospi); + + // switch memory to high performance mode (if available) + ret = fast_mode_enable(ospi); + TEST_ASSERT_EQUAL(OSPI_STATUS_OK, ret); + +#ifdef OSPI_TEST_LOG_FLASH_STATUS + log_register(STATUS_REG, OSPI_STATUS_REG_SIZE, ospi, "Status register"); + log_register(CONFIG_REG0, OSPI_CONFIG_REG_0_SIZE, ospi, "Config register 0"); +#ifdef CONFIG_REG1 + log_register(CONFIG_REG1, OSPI_CONFIG_REG_1_SIZE, ospi, "Config register 1"); +#endif +#ifdef CONFIG_REG2 + log_register(CONFIG_REG2, OSPI_CONFIG_REG_2_SIZE, ospi, "Config register 2"); +#endif +#endif + + _ospi_write_read_test(ospi, write_inst_width, write_addr_width, write_data_width, write_alt_width, write_cmd, + write_addr_size, write_alt_size, write_count, read_inst_width, + read_addr_width, read_data_width, read_alt_width, read_cmd, read_dummy_cycles, + read_addr_size, read_alt_size, read_count, test_count, + data_size, addr); + + ret = fast_mode_disable(ospi); + TEST_ASSERT_EQUAL(OSPI_STATUS_OK, ret); + + ospi_free(&ospi.handle); +} + + +void ospi_init_free_test(void) +{ + Ospi ospi; + ospi_status_t ret; + + ret = ospi_init(&ospi.handle, OPIN_0, OPIN_1, OPIN_2, OPIN_3, OPIN_4, OPIN_5, OPIN_6, OPIN_7, QSCK, QCSN, DQS, OSPI_COMMON_MAX_FREQUENCY, 0); + TEST_ASSERT_EQUAL(OSPI_STATUS_OK, ret); + ret = ospi_free(&ospi.handle); + TEST_ASSERT_EQUAL(OSPI_STATUS_OK, ret); + + + ret = ospi_init(&ospi.handle, OPIN_0, OPIN_1, OPIN_2, OPIN_3, OPIN_4, OPIN_5, OPIN_6, OPIN_7, QSCK, QCSN, DQS, OSPI_COMMON_MAX_FREQUENCY, 0); + TEST_ASSERT_EQUAL(OSPI_STATUS_OK, ret); + ret = ospi_free(&ospi.handle); + TEST_ASSERT_EQUAL(OSPI_STATUS_OK, ret); + + + ret = ospi_init(&ospi.handle, OPIN_0, OPIN_1, OPIN_2, OPIN_3, OPIN_4, OPIN_5, OPIN_6, OPIN_7, QSCK, QCSN, DQS, OSPI_COMMON_MAX_FREQUENCY, 0); + TEST_ASSERT_EQUAL(OSPI_STATUS_OK, ret); + ret = ospi_free(&ospi.handle); + TEST_ASSERT_EQUAL(OSPI_STATUS_OK, ret); + + + ret = ospi_init(&ospi.handle, OPIN_0, OPIN_1, OPIN_2, OPIN_3, OPIN_4, OPIN_5, OPIN_6, OPIN_7, QSCK, QCSN, DQS, OSPI_COMMON_MAX_FREQUENCY, 0); + TEST_ASSERT_EQUAL(OSPI_STATUS_OK, ret); + + // check if the memory is working properly + ospi.cmd.configure(MODE_1_1_1, ADDR_SIZE_32, ALT_SIZE_8); + flash_init(ospi); + +#ifdef OSPI_TEST_LOG_FLASH_STATUS + log_register(STATUS_REG, OSPI_STATUS_REG_SIZE, ospi, "Status register"); + log_register(CONFIG_REG0, OSPI_CONFIG_REG_0_SIZE, ospi, "Config register 0"); +#ifdef CONFIG_REG1 + log_register(CONFIG_REG1, OSPI_CONFIG_REG_1_SIZE, ospi, "Config register 1"); +#endif +#ifdef CONFIG_REG2 + log_register(CONFIG_REG2, OSPI_CONFIG_REG_2_SIZE, ospi, "Config register 2"); +#endif +#endif + + _ospi_write_read_test(ospi, WRITE_1_1_1, ADDR_SIZE_32, ALT_SIZE_8, WRITE_SINGLE, READ_1_1_1, ADDR_SIZE_32, ALT_SIZE_8, READ_SINGLE, TEST_REPEAT_SINGLE, DATA_SIZE_256, TEST_FLASH_ADDRESS); + ospi_free(&ospi.handle); +} + + +void ospi_frequency_test(void) +{ + Ospi ospi; + ospi_status_t ret; + int freq = OSPI_COMMON_MAX_FREQUENCY; + + ret = ospi_init(&ospi.handle, OPIN_0, OPIN_1, OPIN_2, OPIN_3, OPIN_4, OPIN_5, OPIN_6, OPIN_7, QSCK, QCSN, DQS, freq, 0); + TEST_ASSERT_EQUAL(OSPI_STATUS_OK, ret); + + while (ret == OSPI_STATUS_OK && freq >= OSPI_MIN_FREQUENCY) { + // check if the memory is working properly + ospi.cmd.configure(MODE_1_1_1, ADDR_SIZE_32, ALT_SIZE_8); + ret = ospi_frequency(&ospi.handle, freq); + flash_init(ospi); + _ospi_write_read_test(ospi, WRITE_1_1_1, ADDR_SIZE_32, ALT_SIZE_8, WRITE_SINGLE, READ_1_1_1, ADDR_SIZE_32, ALT_SIZE_8, READ_SINGLE, TEST_REPEAT_SINGLE, DATA_SIZE_256, TEST_FLASH_ADDRESS); + + utest_printf("frequency setting %d [Hz] - OK\r\n", freq); + + freq /= 2; + } + + ospi_free(&ospi.handle); +} + + +void ospi_memory_id_test() +{ + utest_printf("*** %s memory config loaded ***\r\n", OSPI_FLASH_CHIP_STRING); +} + + +Case cases[] = { + Case("ospi memory id test", ospi_memory_id_test), + Case("ospi init/free test", ospi_init_free_test), + Case("ospi frequency setting test", ospi_frequency_test), + // read/x1 write/x1 - read/write block of data in single write/read operation + // read/x4 write/x4 - read/write block of data in adjacent locations in multiple write/read operations + // repeat/xN - test repeat count (new data pattern each time) + // 1-1-1 - single channel SPI + // 1-1-2 - Dual data (extended SPI) + // 1-2-2 - Dual I/O (extended SPI) + // 1-1-4 - Quad data (extended SPI) + // 1-4-4 - Quad I/O (extended SPI) + // 2-2-2 - DPI (multi-channel SPI) + // 4-4-4 - QPI (multi-channel SPI) + Case("ospi write(1-1-1)/x1 read(1-1-1)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-1)/x4 read(1-1-1)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-1)/x1 read(1-1-1)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-1)/x1 read(1-1-1)/x1 repeat/x4 test", ospi_write_read_test), +#ifdef READ_1_1_2 + Case("ospi write(1-1-1)/x1 read(1-1-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-1)/x4 read(1-1-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-1)/x1 read(1-1-2)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-1)/x1 read(1-1-2)/x1 repeat/x4 test", ospi_write_read_test), +#endif +#ifdef READ_1_2_2 + Case("ospi write(1-1-1)/x1 read(1-2-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-1)/x4 read(1-2-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-1)/x1 read(1-2-2)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-1)/x1 read(1-2-2)/x1 repeat/x4 test", ospi_write_read_test), +#endif +#ifdef READ_2_2_2 + Case("ospi write(1-1-1)/x1 read(2-2-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-1)/x4 read(2-2-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-1)/x1 read(2-2-2)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-1)/x1 read(2-2-2)/x1 repeat/x4 test", ospi_write_read_test), +#endif +#ifdef READ_1_1_4 + Case("ospi write(1-1-1)/x1 read(1-1-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-1)/x4 read(1-1-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-1)/x1 read(1-1-4)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-1)/x1 read(1-1-4)/x1 repeat/x4 test", ospi_write_read_test), +#endif +#ifdef READ_1_4_4 + Case("ospi write(1-1-1)/x1 read(1-4-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-1)/x4 read(1-4-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-1)/x1 read(1-4-4)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-1)/x1 read(1-4-4)/x1 repeat/x4 test", ospi_write_read_test), +#endif +#ifdef READ_4_4_4 + Case("ospi write(1-1-1)/x1 read(4-4-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-1)/x4 read(4-4-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-1)/x1 read(4-4-4)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-1)/x1 read(4-4-4)/x1 repeat/x4 test", ospi_write_read_test), +#endif + +#ifdef WRITE_1_2_2 + Case("ospi write(1-2-2)/x1 read(1-1-1)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-2-2)/x4 read(1-1-1)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-2-2)/x1 read(1-1-1)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-2-2)/x1 read(1-1-1)/x1 repeat/x4 test", ospi_write_read_test), + Case("ospi write(1-2-2)/x1 read(1-1-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-2-2)/x4 read(1-1-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-2-2)/x1 read(1-1-2)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-2-2)/x1 read(1-1-2)/x1 repeat/x4 test", ospi_write_read_test), + Case("ospi write(1-2-2)/x1 read(1-2-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-2-2)/x4 read(1-2-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-2-2)/x1 read(1-2-2)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-2-2)/x1 read(1-2-2)/x1 repeat/x4 test", ospi_write_read_test), +#ifdef READ_2_2_2 + Case("ospi write(1-2-2)/x1 read(2-2-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-2-2)/x4 read(2-2-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-2-2)/x1 read(2-2-2)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-2-2)/x1 read(2-2-2)/x1 repeat/x4 test", ospi_write_read_test), +#endif +#ifdef READ_1_1_4 + Case("ospi write(1-2-2)/x1 read(1-1-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-2-2)/x4 read(1-1-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-2-2)/x1 read(1-1-4)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-2-2)/x1 read(1-1-4)/x1 repeat/x4 test", ospi_write_read_test), +#endif + Case("ospi write(1-2-2)/x1 read(1-4-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-2-2)/x4 read(1-4-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-2-2)/x1 read(1-4-4)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-2-2)/x1 read(1-4-4)/x1 repeat/x4 test", ospi_write_read_test), +#ifdef READ_4_4_4 + Case("ospi write(1-2-2)/x1 read(4-4-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-2-2)/x4 read(4-4-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-2-2)/x1 read(4-4-4)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-2-2)/x1 read(4-4-4)/x1 repeat/x4 test", ospi_write_read_test), +#endif +#endif + +#ifdef WRITE_2_2_2 + Case("ospi write(2-2-2)/x1 read(1-1-1)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(2-2-2)/x4 read(1-1-1)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(2-2-2)/x1 read(1-1-1)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(2-2-2)/x1 read(1-1-1)/x1 repeat/x4 test", ospi_write_read_test), + Case("ospi write(2-2-2)/x1 read(1-1-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(2-2-2)/x4 read(1-1-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(2-2-2)/x1 read(1-1-2)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(2-2-2)/x1 read(1-1-2)/x1 repeat/x4 test", ospi_write_read_test), + Case("ospi write(2-2-2)/x1 read(1-2-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(2-2-2)/x4 read(1-2-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(2-2-2)/x1 read(1-2-2)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(2-2-2)/x1 read(1-2-2)/x1 repeat/x4 test", ospi_write_read_test), +#ifdef READ_2_2_2 + Case("ospi write(2-2-2)/x1 read(2-2-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(2-2-2)/x4 read(2-2-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(2-2-2)/x1 read(2-2-2)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(2-2-2)/x1 read(2-2-2)/x1 repeat/x4 test", ospi_write_read_test), +#endif +#ifdef READ_1_1_4 + Case("ospi write(2-2-2)/x1 read(1-1-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(2-2-2)/x4 read(1-1-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(2-2-2)/x1 read(1-1-4)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(2-2-2)/x1 read(1-1-4)/x1 repeat/x4 test", ospi_write_read_test), +#endif + Case("ospi write(2-2-2)/x1 read(1-4-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(2-2-2)/x4 read(1-4-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(2-2-2)/x1 read(1-4-4)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(2-2-2)/x1 read(1-4-4)/x1 repeat/x4 test", ospi_write_read_test), +#ifdef READ_4_4_4 + Case("ospi write(2-2-2)/x1 read(4-4-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(2-2-2)/x4 read(4-4-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(2-2-2)/x1 read(4-4-4)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(2-2-2)/x1 read(4-4-4)/x1 repeat/x4 test", ospi_write_read_test), +#endif +#endif + +#ifdef WRITE_1_1_4 + Case("ospi write(1-1-4)/x1 read(1-1-1)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-4)/x4 read(1-1-1)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-4)/x1 read(1-1-1)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-4)/x1 read(1-1-1)/x1 repeat/x4 test", ospi_write_read_test), + Case("ospi write(1-1-4)/x1 read(1-1-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-4)/x4 read(1-1-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-4)/x1 read(1-1-2)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-4)/x1 read(1-1-2)/x1 repeat/x4 test", ospi_write_read_test), + Case("ospi write(1-1-4)/x1 read(1-2-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-4)/x4 read(1-2-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-4)/x1 read(1-2-2)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-4)/x1 read(1-2-2)/x1 repeat/x4 test", ospi_write_read_test), +#ifdef READ_2_2_2 + Case("ospi write(1-1-4)/x1 read(2-2-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-4)/x4 read(2-2-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-4)/x1 read(2-2-2)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-4)/x1 read(2-2-2)/x1 repeat/x4 test", ospi_write_read_test), +#endif +#ifdef READ_1_1_4 + Case("ospi write(1-1-4)/x1 read(1-1-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-4)/x4 read(1-1-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-4)/x1 read(1-1-4)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-4)/x1 read(1-1-4)/x1 repeat/x4 test", ospi_write_read_test), +#endif + Case("ospi write(1-1-4)/x1 read(1-4-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-4)/x4 read(1-4-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-4)/x1 read(1-4-4)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-4)/x1 read(1-4-4)/x1 repeat/x4 test", ospi_write_read_test), +#ifdef READ_4_4_4 + Case("ospi write(1-1-4)/x1 read(4-4-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-4)/x4 read(4-4-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-4)/x1 read(4-4-4)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-1-4)/x1 read(4-4-4)/x1 repeat/x4 test", ospi_write_read_test), +#endif +#endif + +#ifdef WRITE_1_4_4 + Case("ospi write(1-4-4)/x1 read(1-1-1)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-4-4)/x4 read(1-1-1)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-4-4)/x1 read(1-1-1)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-4-4)/x1 read(1-1-1)/x1 repeat/x4 test", ospi_write_read_test), + Case("ospi write(1-4-4)/x1 read(1-1-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-4-4)/x4 read(1-1-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-4-4)/x1 read(1-1-2)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-4-4)/x1 read(1-1-2)/x1 repeat/x4 test", ospi_write_read_test), + Case("ospi write(1-4-4)/x1 read(1-2-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-4-4)/x4 read(1-2-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-4-4)/x1 read(1-2-2)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-4-4)/x1 read(1-2-2)/x1 repeat/x4 test", ospi_write_read_test), +#ifdef READ_2_2_2 + Case("ospi write(1-4-4)/x1 read(2-2-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-4-4)/x4 read(2-2-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-4-4)/x1 read(2-2-2)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-4-4)/x1 read(2-2-2)/x1 repeat/x4 test", ospi_write_read_test), +#endif +#ifdef READ_1_1_4 + Case("ospi write(1-4-4)/x1 read(1-1-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-4-4)/x4 read(1-1-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-4-4)/x1 read(1-1-4)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-4-4)/x1 read(1-1-4)/x1 repeat/x4 test", ospi_write_read_test), +#endif + Case("ospi write(1-4-4)/x1 read(1-4-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-4-4)/x4 read(1-4-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-4-4)/x1 read(1-4-4)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-4-4)/x1 read(1-4-4)/x1 repeat/x4 test", ospi_write_read_test), +#ifdef READ_4_4_4 + Case("ospi write(1-4-4)/x1 read(4-4-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-4-4)/x4 read(4-4-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-4-4)/x1 read(4-4-4)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(1-4-4)/x1 read(4-4-4)/x1 repeat/x4 test", ospi_write_read_test), +#endif +#endif + +#ifdef WRITE_4_4_4 + Case("ospi write(4-4-4)/x1 read(1-1-1)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(4-4-4)/x4 read(1-1-1)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(4-4-4)/x1 read(1-1-1)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(4-4-4)/x1 read(1-1-1)/x1 repeat/x4 test", ospi_write_read_test), + Case("ospi write(4-4-4)/x1 read(1-1-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(4-4-4)/x4 read(1-1-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(4-4-4)/x1 read(1-1-2)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(4-4-4)/x1 read(1-1-2)/x1 repeat/x4 test", ospi_write_read_test), + Case("ospi write(4-4-4)/x1 read(1-2-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(4-4-4)/x4 read(1-2-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(4-4-4)/x1 read(1-2-2)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(4-4-4)/x1 read(1-2-2)/x1 repeat/x4 test", ospi_write_read_test), +#ifdef READ_2_2_2 + Case("ospi write(4-4-4)/x1 read(2-2-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(4-4-4)/x4 read(2-2-2)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(4-4-4)/x1 read(2-2-2)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(4-4-4)/x1 read(2-2-2)/x1 repeat/x4 test", ospi_write_read_test), +#endif +#ifdef READ_1_1_4 + Case("ospi write(4-4-4)/x1 read(1-1-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(4-4-4)/x4 read(1-1-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(4-4-4)/x1 read(1-1-4)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(4-4-4)/x1 read(1-1-4)/x1 repeat/x4 test", ospi_write_read_test), +#endif + Case("ospi write(4-4-4)/x1 read(1-4-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(4-4-4)/x4 read(1-4-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(4-4-4)/x1 read(1-4-4)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(4-4-4)/x1 read(1-4-4)/x1 repeat/x4 test", ospi_write_read_test), +#ifdef READ_4_4_4 + Case("ospi write(4-4-4)/x1 read(4-4-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(4-4-4)/x4 read(4-4-4)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(4-4-4)/x1 read(4-4-4)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(4-4-4)/x1 read(4-4-4)/x1 repeat/x4 test", ospi_write_read_test), +#endif +#endif +#ifdef READ_8_8_8 + Case("ospi write(8_8_8)/x1 read(8_8_8)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(8_8_8)/x4 read(8_8_8)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(8_8_8)/x1 read(8_8_8)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(8_8_8)/x1 read(8_8_8)/x1 repeat/x4 test", ospi_write_read_test), +#endif + +#ifdef READ_8D_8D_8D + Case("ospi write(8D_8D_8D)/x1 read(8D_8D_8D)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(8D_8D_8D)/x4 read(8D_8D_8D)/x1 repeat/x1 test", ospi_write_read_test), + Case("ospi write(8D_8D_8D)/x1 read(8D_8D_8D)/x4 repeat/x1 test", ospi_write_read_test), + Case("ospi write(8D_8D_8D)/x1 read(8D_8D_8D)/x1 repeat/x4 test", ospi_write_read_test), +#endif +}; + +utest::v1::status_t greentea_test_setup(const size_t number_of_cases) +{ + GREENTEA_SETUP(180, "default_auto"); + return greentea_test_setup_handler(number_of_cases); +} + +Specification specification(greentea_test_setup, cases, greentea_test_teardown_handler); + +int main() +{ + Harness::run(specification); +} + +#endif // !defined(OSPI_FLASH_CHIP_STRING) +#endif // !DEVICE_OSPI diff --git a/hal/tests/TESTS/mbed_hal/ospi/ospi_test.h b/hal/tests/TESTS/mbed_hal/ospi/ospi_test.h new file mode 100644 index 0000000..716e888 --- /dev/null +++ b/hal/tests/TESTS/mbed_hal/ospi/ospi_test.h @@ -0,0 +1,102 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018-2020 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. + */ + +/** \addtogroup hal_ospi + * @{ + * \defgroup hal_ospi_tests Tests + * OSPI tests of the HAL. + * @{ + */ +#ifndef MBED_OSPI_TEST_H +#define MBED_OSPI_TEST_H + +#include "ospi_test_utils.h" +#include "ospi_api.h" + + +#if DEVICE_OSPI + +/** Test that ospi_init/ospi_free can be called multiple times. + * + * Given board provides OSPI. + * When ospi_init/ospi_free is called multiple times. + * Then ospi_init/ospi_free are successfully performed (no exception is generated). + * + */ +void ospi_init_free_test(void); + +/** Test ospi frequency setting. + * + * Given board provides OSPI, with OSPI already initialized. + * When set OSPI frequency. + * Then freguency setting is successfully performed (no exception is generated). + * + */ +void ospi_frequency_test(void); + +/** Template for write/read tests + * + * Test single write/read operation of a block of data to/from the specific memory address + * Given board provides OSPI, with OSPI already initialized. + * When perform write and then read operations. + * Then data is successfully written and then read (no exception is generated) and the read data is valid. + * + * Test multiple write/read operation of a block of data to/from the same specific memory address + * Given board provides OSPI, with OSPI already initialized. + * When perform write and then read operations. + * Then data is successfully written and then read (no exception is generated) and the read data is valid. + * + * Test multiple adjacent write and single read operation of a block of data to/from the specific memory address + * Given board provides OSPI, with OSPI already initialized. + * When perform write and then read operations. + * Then data is successfully written and then read (no exception is generated) and the read data is valid. + * + * Test single write and multiple adjacent read operation of a block of data to/from the specific memory address + * Given board provides OSPI, with OSPI already initialized. + * When perform write and then read operations. + * Then data is successfully written and then read (no exception is generated) and the read data is valid. + * + */ +template < ospi_bus_width_t write_inst_width, + ospi_bus_width_t write_addr_width, + ospi_bus_width_t write_data_width, + ospi_bus_width_t write_alt_width, + unsigned int write_cmd, + ospi_address_size_t write_addr_size, + ospi_alt_size_t write_alt_size, + uint32_t write_count, + ospi_bus_width_t read_inst_width, + ospi_bus_width_t read_addr_width, + ospi_bus_width_t read_data_width, + ospi_bus_width_t read_alt_width, + unsigned int read_cmd, + int read_dummy_cycles, + ospi_address_size_t read_addr_size, + ospi_alt_size_t read_alt_size, + int frequency, + uint32_t read_count, + uint32_t test_count, + uint32_t data_size, + uint32_t flash_addr> +void ospi_write_read_test(void); + +#endif + +#endif + +/** @}*/ +/** @}*/ diff --git a/hal/tests/TESTS/mbed_hal/ospi/ospi_test_utils.cpp b/hal/tests/TESTS/mbed_hal/ospi/ospi_test_utils.cpp new file mode 100644 index 0000000..7eaa68e --- /dev/null +++ b/hal/tests/TESTS/mbed_hal/ospi/ospi_test_utils.cpp @@ -0,0 +1,415 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018-2020 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 "ospi_test_utils.h" + +#include "utest/utest.h" + +#include "hal/ospi_api.h" +#include "hal/us_ticker_api.h" +#include "unity/unity.h" + +#include // for memset + +#include "flash_configs/flash_configs.h" +#include "mbed.h" + +static ospi_status_t extended_enable(Ospi &ospi); +static ospi_status_t extended_disable(Ospi &ospi); +static ospi_status_t dual_enable(Ospi &ospi); +static ospi_status_t dual_disable(Ospi &ospi); +static ospi_status_t quad_enable(Ospi &ospi); +static ospi_status_t quad_disable(Ospi &ospi); +static ospi_status_t octa_enable(Ospi &ospi); +static ospi_status_t octa_disable(Ospi &ospi); +static ospi_status_t octa_dtr_enable(Ospi &ospi); +static ospi_status_t octa_dtr_disable(Ospi &ospi); + +void OspiCommand::configure(ospi_bus_width_t inst_width, ospi_bus_width_t addr_width, + ospi_bus_width_t data_width, ospi_bus_width_t alt_width, + ospi_address_size_t addr_size, ospi_alt_size_t alt_size, + int dummy_cycles) +{ + memset(&_cmd, 0, sizeof(ospi_command_t)); + _cmd.instruction.disabled = _cmd.address.disabled = _cmd.alt.disabled = true; + + _cmd.instruction.bus_width = inst_width; + _cmd.address.bus_width = addr_width; + _cmd.address.size = addr_size; + _cmd.alt.bus_width = alt_width; + _cmd.alt.size = alt_size; + _cmd.data.bus_width = data_width; + _cmd.dummy_count = dummy_cycles; +} + +void OspiCommand::set_dummy_cycles(int dummy_cycles) +{ + _cmd.dummy_count = dummy_cycles; +} + +void OspiCommand::build(int instruction, int address, int alt) +{ + _cmd.instruction.disabled = (instruction == -1); + if (!_cmd.instruction.disabled) { + _cmd.instruction.value = instruction; + } + + _cmd.address.disabled = (address == OSPI_NONE); + if (!_cmd.address.disabled) { + _cmd.address.value = address; + } + + _cmd.alt.disabled = (alt == OSPI_NONE); + if (!_cmd.alt.disabled) { + _cmd.alt.value = alt; + } + + if ((_cmd.instruction.bus_width == OSPI_CFG_BUS_OCTA) || (_cmd.instruction.bus_width == OSPI_CFG_BUS_OCTA_DTR)) { + if (instruction == STATUS_REG) { + _cmd.address.disabled = 0; + _cmd.address.value = 0; + _cmd.dummy_count = 4; + } else if (instruction == OSPI_CMD_RDCR2) { + _cmd.dummy_count = 4; + } else if ((instruction == OSPI_CMD_READ_OPI) || (instruction == OSPI_CMD_READ_DOPI)) { + _cmd.dummy_count = 20; + } else { + _cmd.dummy_count = 0; + } + } +} + +ospi_command_t *OspiCommand::get() +{ + return &_cmd; +} + + +ospi_status_t read_register(uint32_t cmd, uint8_t *buf, uint32_t size, Ospi &q) +{ + q.cmd.build(cmd); + return ospi_command_transfer(&q.handle, q.cmd.get(), NULL, 0, buf, size); +} + +ospi_status_t write_register(uint32_t cmd, uint8_t *buf, uint32_t size, Ospi &q) +{ + q.cmd.build(cmd); + return ospi_command_transfer(&q.handle, q.cmd.get(), buf, size, NULL, 0); +} + +ospi_status_t read_config_register_2(uint32_t cmd, uint32_t addr, uint8_t *buf, uint32_t size, Ospi &q) +{ + q.cmd.build(cmd, addr); + return ospi_command_transfer(&q.handle, q.cmd.get(), NULL, 0, buf, size); +} + +ospi_status_t write_config_register_2(uint32_t cmd, uint32_t addr, uint8_t *buf, uint32_t size, Ospi &q) +{ + q.cmd.build(cmd, addr); + return ospi_command_transfer(&q.handle, q.cmd.get(), buf, size, NULL, 0); +} + +OspiStatus flash_wait_for(uint32_t time_us, Ospi &ospi) +{ + uint8_t reg[OSPI_STATUS_REG_SIZE]; + ospi_status_t ret; + uint32_t curr_time; + + const ticker_data_t *const ticker = get_us_ticker_data(); + const uint32_t start = ticker_read(ticker); + + memset(reg, 255, OSPI_STATUS_REG_SIZE); + do { + ret = read_register(STATUS_REG, reg, OSPI_STATUS_REG_SIZE, ospi); + curr_time = ticker_read(ticker); + } while (((reg[0] & STATUS_BIT_WIP) != 0) && ((curr_time - start) < time_us)); + + if (((reg[0] & STATUS_BIT_WIP) == 0) && (ret == OSPI_STATUS_OK)) { + return sOK; + } else if (ret != OSPI_STATUS_OK) { + return sError; + } else if ((curr_time - start) >= time_us) { + return sTimeout; + } + return sUnknown; +} + +void flash_init(Ospi &ospi) +{ + uint8_t status[OSPI_STATUS_REG_SIZE]; + ospi_status_t ret; + + ospi.cmd.build(OSPI_CMD_RDSR); + ret = ospi_command_transfer(&ospi.handle, ospi.cmd.get(), NULL, 0, status, OSPI_STATUS_REG_SIZE); + TEST_ASSERT_EQUAL(OSPI_STATUS_OK, ret); + + // Only do reset enable if device needs it + if (OSPI_CMD_RSTEN != 0) { + ospi.cmd.build(OSPI_CMD_RSTEN); + ret = ospi_command_transfer(&ospi.handle, ospi.cmd.get(), NULL, 0, NULL, 0); + TEST_ASSERT_EQUAL(OSPI_STATUS_OK, ret); + + WAIT_FOR(WRSR_MAX_TIME, ospi); + } + + ospi.cmd.build(OSPI_CMD_RST); + ret = ospi_command_transfer(&ospi.handle, ospi.cmd.get(), NULL, 0, NULL, 0); + TEST_ASSERT_EQUAL(OSPI_STATUS_OK, ret); + + WAIT_FOR(WAIT_MAX_TIME, ospi); + + // Zero out status register to attempt to clear block protection bits + uint8_t blanks[OSPI_STATUS_REG_SIZE] = {0}; + + ospi.cmd.build(OSPI_CMD_WREN); + ret = ospi_command_transfer(&ospi.handle, ospi.cmd.get(), NULL, 0, NULL, 0); + TEST_ASSERT_EQUAL(OSPI_STATUS_OK, ret); + + ospi.cmd.build(OSPI_CMD_WRSR); + ret = ospi_command_transfer(&ospi.handle, ospi.cmd.get(), blanks, 1, NULL, 0); + TEST_ASSERT_EQUAL(OSPI_STATUS_OK, ret); + + WAIT_FOR(WRSR_MAX_TIME, ospi); +} + + +ospi_status_t write_enable(Ospi &ospi) +{ + uint8_t reg[OSPI_STATUS_REG_SIZE]; + ospi.cmd.build(OSPI_CMD_WREN); + + if (ospi_command_transfer(&ospi.handle, ospi.cmd.get(), NULL, 0, NULL, 0) != OSPI_STATUS_OK) { + return OSPI_STATUS_ERROR; + } + WAIT_FOR(WRSR_MAX_TIME, ospi); + + memset(reg, 0, OSPI_STATUS_REG_SIZE); + if (read_register(STATUS_REG, reg, OSPI_STATUS_REG_SIZE, ospi) != OSPI_STATUS_OK) { + return OSPI_STATUS_ERROR; + } + + return ((reg[0] & STATUS_BIT_WEL) != 0 ? OSPI_STATUS_OK : OSPI_STATUS_ERROR); +} + +ospi_status_t write_disable(Ospi &ospi) +{ + uint8_t reg[OSPI_STATUS_REG_SIZE]; + ospi.cmd.build(OSPI_CMD_WRDI); + + if (ospi_command_transfer(&ospi.handle, ospi.cmd.get(), NULL, 0, NULL, 0) != OSPI_STATUS_OK) { + return OSPI_STATUS_ERROR; + } + WAIT_FOR(WRSR_MAX_TIME, ospi); + + memset(reg, 0, OSPI_STATUS_REG_SIZE); + if (read_register(STATUS_REG, reg, OSPI_STATUS_REG_SIZE, ospi) != OSPI_STATUS_OK) { + return OSPI_STATUS_ERROR; + } + + return ((reg[0] & STATUS_BIT_WEL) == 0 ? OSPI_STATUS_OK : OSPI_STATUS_ERROR); +} + +void log_register(uint32_t cmd, uint32_t reg_size, Ospi &ospi, const char *str) +{ + ospi_status_t ret; + static uint8_t reg[OSPI_MAX_REG_SIZE]; + + ret = read_register(cmd, reg, reg_size, ospi); + TEST_ASSERT_EQUAL(OSPI_STATUS_OK, ret); + + for (uint32_t j = 0; j < reg_size; j++) { + utest_printf("%s byte %u (MSB first): ", str != NULL ? str : "", j); + for (int i = 0; i < 8; i++) { + utest_printf("%s ", ((reg[j] & (1 << (7 - i))) & 0xFF) == 0 ? "0" : "1"); + } + utest_printf("\r\n"); + } +} + +ospi_status_t erase(uint32_t erase_cmd, uint32_t flash_addr, Ospi &ospi) +{ + ospi.cmd.build(erase_cmd, flash_addr); + return ospi_command_transfer(&ospi.handle, ospi.cmd.get(), NULL, 0, NULL, 0); +} + +ospi_status_t mode_enable(Ospi &ospi, ospi_bus_width_t inst_width, ospi_bus_width_t addr_width, ospi_bus_width_t data_width) +{ + if (is_extended_mode(inst_width, addr_width, data_width)) { + return extended_enable(ospi); + } else if (is_dual_mode(inst_width, addr_width, data_width)) { + return dual_enable(ospi); + } else if (is_quad_mode(inst_width, addr_width, data_width)) { + return quad_enable(ospi); + } else if (is_octa_mode(inst_width, addr_width, data_width)) { + return octa_enable(ospi); + } else if (is_octa_dtr_mode(inst_width, addr_width, data_width)) { + return octa_dtr_enable(ospi); + } else { + return OSPI_STATUS_OK; + } +} + +ospi_status_t mode_disable(Ospi &ospi, ospi_bus_width_t inst_width, ospi_bus_width_t addr_width, ospi_bus_width_t data_width) +{ + if (is_extended_mode(inst_width, addr_width, data_width)) { + return extended_disable(ospi); + } else if (is_dual_mode(inst_width, addr_width, data_width)) { + return dual_disable(ospi); + } else if (is_quad_mode(inst_width, addr_width, data_width)) { + return quad_disable(ospi); + } else if (is_octa_mode(inst_width, addr_width, data_width)) { + return octa_disable(ospi); + } else if (is_octa_dtr_mode(inst_width, addr_width, data_width)) { + return octa_dtr_disable(ospi); + } else { + return OSPI_STATUS_OK; + } +} + +static ospi_status_t extended_enable(Ospi &ospi) +{ +#ifdef EXTENDED_SPI_ENABLE + EXTENDED_SPI_ENABLE(); +#else + return OSPI_STATUS_OK; +#endif +} + +static ospi_status_t extended_disable(Ospi &ospi) +{ +#ifdef EXTENDED_SPI_DISABLE + EXTENDED_SPI_DISABLE(); +#else + return OSPI_STATUS_OK; +#endif +} + +static ospi_status_t dual_enable(Ospi &ospi) +{ +#ifdef DUAL_ENABLE + DUAL_ENABLE(); +#else + return OSPI_STATUS_OK; +#endif +} + +static ospi_status_t dual_disable(Ospi &ospi) +{ +#ifdef DUAL_DISABLE + DUAL_DISABLE(); +#else + return OSPI_STATUS_OK; +#endif + +} + +static ospi_status_t quad_enable(Ospi &ospi) +{ +#ifdef QUAD_ENABLE + QUAD_ENABLE(); +#else + return OSPI_STATUS_OK; +#endif +} + +static ospi_status_t quad_disable(Ospi &ospi) +{ +#ifdef QUAD_DISABLE + QUAD_DISABLE(); +#else + return OSPI_STATUS_OK; +#endif +} + +static ospi_status_t octa_enable(Ospi &ospi) +{ +#ifdef OCTA_ENABLE + OCTA_ENABLE(); +#else + return OSPI_STATUS_OK; +#endif +} + +static ospi_status_t octa_disable(Ospi &ospi) +{ +#ifdef OCTA_DISABLE + OCTA_DISABLE(); +#else + return OSPI_STATUS_OK; +#endif +} + +static ospi_status_t octa_dtr_enable(Ospi &ospi) +{ +#ifdef OCTA_DTR_ENABLE + OCTA_DTR_ENABLE(); +#else + return OSPI_STATUS_OK; +#endif +} + +static ospi_status_t octa_dtr_disable(Ospi &ospi) +{ +#ifdef OCTA_DTR_DISABLE + OCTA_DTR_DISABLE(); +#else + return OSPI_STATUS_OK; +#endif +} + +ospi_status_t fast_mode_enable(Ospi &ospi) +{ +#ifdef FAST_MODE_ENABLE + FAST_MODE_ENABLE(); +#else + return OSPI_STATUS_OK; +#endif +} + +ospi_status_t fast_mode_disable(Ospi &ospi) +{ +#ifdef FAST_MODE_DISABLE + FAST_MODE_DISABLE(); +#else + return OSPI_STATUS_OK; +#endif +} + +bool is_extended_mode(ospi_bus_width_t inst_width, ospi_bus_width_t addr_width, ospi_bus_width_t data_width) +{ + return (inst_width == OSPI_CFG_BUS_SINGLE) && ((addr_width != OSPI_CFG_BUS_SINGLE) || (data_width != OSPI_CFG_BUS_SINGLE)); +} + +bool is_dual_mode(ospi_bus_width_t inst_width, ospi_bus_width_t addr_width, ospi_bus_width_t data_width) +{ + return (inst_width == OSPI_CFG_BUS_DUAL) && (addr_width == OSPI_CFG_BUS_DUAL) && (data_width == OSPI_CFG_BUS_DUAL); +} + +bool is_quad_mode(ospi_bus_width_t inst_width, ospi_bus_width_t addr_width, ospi_bus_width_t data_width) +{ + return (inst_width == OSPI_CFG_BUS_QUAD) && (addr_width == OSPI_CFG_BUS_QUAD) && (data_width == OSPI_CFG_BUS_QUAD); +} + +bool is_octa_mode(ospi_bus_width_t inst_width, ospi_bus_width_t addr_width, ospi_bus_width_t data_width) +{ + return (inst_width == OSPI_CFG_BUS_OCTA) && (addr_width == OSPI_CFG_BUS_OCTA) && (data_width == OSPI_CFG_BUS_OCTA); +} + +bool is_octa_dtr_mode(ospi_bus_width_t inst_width, ospi_bus_width_t addr_width, ospi_bus_width_t data_width) +{ + return (inst_width == OSPI_CFG_BUS_OCTA_DTR) && (addr_width == OSPI_CFG_BUS_OCTA_DTR) && (data_width == OSPI_CFG_BUS_OCTA_DTR); +} diff --git a/hal/tests/TESTS/mbed_hal/ospi/ospi_test_utils.h b/hal/tests/TESTS/mbed_hal/ospi/ospi_test_utils.h new file mode 100644 index 0000000..0fca1ed --- /dev/null +++ b/hal/tests/TESTS/mbed_hal/ospi/ospi_test_utils.h @@ -0,0 +1,186 @@ +/* mbed Microcontroller Library + * Copyright (c) 2018-2020 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_OSPI_TEST_UTILS_H +#define MBED_OSPI_TEST_UTILS_H + +#include "flash_configs/flash_configs.h" +#include "unity/unity.h" +#include "hal/ospi_api.h" +#define OSPI_NONE (-1) + +enum OspiStatus { + sOK, + sError, + sTimeout, + sUnknown +}; + +class OspiCommand { +public: + void configure(ospi_bus_width_t inst_width, ospi_bus_width_t addr_width, ospi_bus_width_t data_width, + ospi_bus_width_t alt_width, ospi_address_size_t addr_size, ospi_alt_size_t alt_size, + int dummy_cycles = 0); + + void set_dummy_cycles(int dummy_cycles); + + void build(int instruction, int address = OSPI_NONE, int alt = OSPI_NONE); + + ospi_command_t *get(); + +private: + ospi_command_t _cmd; +}; + +struct Ospi { + ospi_t handle; + OspiCommand cmd; +}; + +// MODE_Command_Address_Data_Alt +#define MODE_1_1_1 OSPI_CFG_BUS_SINGLE, OSPI_CFG_BUS_SINGLE, OSPI_CFG_BUS_SINGLE, OSPI_CFG_BUS_SINGLE +#define MODE_1_1_2 OSPI_CFG_BUS_SINGLE, OSPI_CFG_BUS_SINGLE, OSPI_CFG_BUS_DUAL, OSPI_CFG_BUS_DUAL +#define MODE_1_2_2 OSPI_CFG_BUS_SINGLE, OSPI_CFG_BUS_DUAL, OSPI_CFG_BUS_DUAL, OSPI_CFG_BUS_DUAL +#define MODE_2_2_2 OSPI_CFG_BUS_DUAL, OSPI_CFG_BUS_DUAL, OSPI_CFG_BUS_DUAL, OSPI_CFG_BUS_DUAL +#define MODE_1_1_4 OSPI_CFG_BUS_SINGLE, OSPI_CFG_BUS_SINGLE, OSPI_CFG_BUS_QUAD, OSPI_CFG_BUS_QUAD +#define MODE_1_4_4 OSPI_CFG_BUS_SINGLE, OSPI_CFG_BUS_QUAD, OSPI_CFG_BUS_QUAD, OSPI_CFG_BUS_QUAD +#define MODE_4_4_4 OSPI_CFG_BUS_QUAD, OSPI_CFG_BUS_QUAD, OSPI_CFG_BUS_QUAD, OSPI_CFG_BUS_QUAD +#define MODE_8_8_8 OSPI_CFG_BUS_OCTA, OSPI_CFG_BUS_OCTA, OSPI_CFG_BUS_OCTA, OSPI_CFG_BUS_OCTA +#define MODE_8D_8D_8D OSPI_CFG_BUS_OCTA_DTR, OSPI_CFG_BUS_OCTA_DTR, OSPI_CFG_BUS_OCTA_DTR, OSPI_CFG_BUS_OCTA_DTR + + +#define WRITE_1_1_1 MODE_1_1_1, OSPI_CMD_WRITE_1IO +#ifdef OSPI_CMD_WRITE_2IO +#define WRITE_1_2_2 MODE_1_2_2, OSPI_CMD_WRITE_2IO +#endif +#ifdef OSPI_CMD_WRITE_1I4O // Quad page program - command: 0x32 +#define WRITE_1_1_4 MODE_1_1_4, OSPI_CMD_WRITE_1I4O +#endif +#ifdef OSPI_CMD_WRITE_4IO +#define WRITE_1_4_4 MODE_1_4_4, OSPI_CMD_WRITE_4IO +#endif +#ifdef OSPI_CMD_WRITE_DPI +#define WRITE_2_2_2 MODE_2_2_2, OSPI_CMD_WRITE_DPI +#endif +#ifdef OSPI_CMD_WRITE_QPI +#define WRITE_4_4_4 MODE_4_4_4, OSPI_CMD_WRITE_QPI +#endif +#ifdef OSPI_CMD_WRITE_OPI +#define WRITE_8_8_8 MODE_8_8_8, OSPI_CMD_WRITE_OPI +#endif +#ifdef OSPI_CMD_WRITE_OPI +#define WRITE_8D_8D_8D MODE_8D_8D_8D, OSPI_CMD_WRITE_OPI +#endif + +#define READ_1_1_1 MODE_1_1_1, OSPI_CMD_READ_1IO, OSPI_READ_1IO_DUMMY_CYCLE +#ifdef OSPI_CMD_READ_1I2O +#define READ_1_1_2 MODE_1_1_2, OSPI_CMD_READ_1I2O, OSPI_READ_1I2O_DUMMY_CYCLE +#endif +#ifdef OSPI_CMD_READ_2IO +#define READ_1_2_2 MODE_1_2_2, OSPI_CMD_READ_2IO, OSPI_READ_2IO_DUMMY_CYCLE +#endif +#ifdef OSPI_CMD_READ_1I4O +#define READ_1_1_4 MODE_1_1_4, OSPI_CMD_READ_1I4O, OSPI_READ_1I4O_DUMMY_CYCLE +#endif +#ifdef OSPI_CMD_READ_4IO +#define READ_1_4_4 MODE_1_4_4, OSPI_CMD_READ_4IO, OSPI_READ_4IO_DUMMY_CYCLE +#endif +#ifdef OSPI_CMD_READ_OPI +#define READ_8_8_8 MODE_8_8_8, OSPI_CMD_READ_OPI, OSPI_READ_8IO_DUMMY_CYCLE +#endif +#ifdef OSPI_CMD_READ_DOPI +#define READ_8D_8D_8D MODE_8D_8D_8D, OSPI_CMD_READ_DOPI, OSPI_READ_8IO_DUMMY_CYCLE +#endif + +#ifdef OSPI_CMD_READ_DPI +#define READ_2_2_2 MODE_2_2_2, OSPI_CMD_READ_DPI, OSPI_READ_2IO_DUMMY_CYCLE +#endif +#ifdef OSPI_CMD_READ_QPI +#define READ_4_4_4 MODE_4_4_4, OSPI_CMD_READ_QPI, OSPI_READ_4IO_DUMMY_CYCLE +#endif + +#define ADDR_SIZE_8 OSPI_CFG_ADDR_SIZE_8 +#define ADDR_SIZE_16 OSPI_CFG_ADDR_SIZE_16 +#define ADDR_SIZE_24 OSPI_CFG_ADDR_SIZE_24 +#define ADDR_SIZE_32 OSPI_CFG_ADDR_SIZE_32 + +#define ALT_SIZE_8 OSPI_CFG_ALT_SIZE_8 +#define ALT_SIZE_16 OSPI_CFG_ALT_SIZE_16 +#define ALT_SIZE_24 OSPI_CFG_ALT_SIZE_24 +#define ALT_SIZE_32 OSPI_CFG_ALT_SIZE_32 + +#define STATUS_REG OSPI_CMD_RDSR +#define CONFIG_REG0 OSPI_CMD_RDCR0 +#ifdef OSPI_CMD_RDCR1 +#define CONFIG_REG1 OSPI_CMD_RDCR1 +#endif +#ifdef OSPI_CMD_RDCR2 +#define CONFIG_REG2 OSPI_CMD_RDCR2 +#endif +#define SECURITY_REG OSPI_CMD_RDSCUR + +#ifndef OSPI_CONFIG_REG_1_SIZE +#define OSPI_CONFIG_REG_1_SIZE 0 +#endif + +#ifndef OSPI_CONFIG_REG_2_SIZE +#define OSPI_CONFIG_REG_2_SIZE 0 +#endif + + +#define SECTOR_ERASE OSPI_CMD_ERASE_SECTOR +#define BLOCK_ERASE OSPI_CMD_ERASE_BLOCK_64 + + +#define SECTOR_ERASE_MAX_TIME OSPI_ERASE_SECTOR_MAX_TIME +#define BLOCK32_ERASE_MAX_TIME OSPI_ERASE_BLOCK_32_MAX_TIME +#define BLOCK64_ERASE_MAX_TIME OSPI_ERASE_BLOCK_64_MAX_TIME +#define PAGE_PROG_MAX_TIME OSPI_PAGE_PROG_MAX_TIME +#define WRSR_MAX_TIME OSPI_WRSR_MAX_TIME +#define WAIT_MAX_TIME OSPI_WAIT_MAX_TIME + + + +ospi_status_t read_register(uint32_t cmd, uint8_t *buf, uint32_t size, Ospi &q); +ospi_status_t write_register(uint32_t cmd, uint8_t *buf, uint32_t size, Ospi &q); + +OspiStatus flash_wait_for(uint32_t time_us, Ospi &ospi); + +void flash_init(Ospi &ospi); + +ospi_status_t write_enable(Ospi &ospi); +ospi_status_t write_disable(Ospi &ospi); + +void log_register(uint32_t cmd, uint32_t reg_size, Ospi &ospi, const char *str = NULL); + +ospi_status_t mode_enable(Ospi &ospi, ospi_bus_width_t inst_width, ospi_bus_width_t addr_width, ospi_bus_width_t data_width); +ospi_status_t mode_disable(Ospi &ospi, ospi_bus_width_t inst_width, ospi_bus_width_t addr_width, ospi_bus_width_t data_width); + +ospi_status_t fast_mode_enable(Ospi &ospi); +ospi_status_t fast_mode_disable(Ospi &ospi); + +ospi_status_t erase(uint32_t erase_cmd, uint32_t flash_addr, Ospi &ospi); + +bool is_extended_mode(ospi_bus_width_t inst_width, ospi_bus_width_t addr_width, ospi_bus_width_t data_width); +bool is_dual_mode(ospi_bus_width_t inst_width, ospi_bus_width_t addr_width, ospi_bus_width_t data_width); +bool is_quad_mode(ospi_bus_width_t inst_width, ospi_bus_width_t addr_width, ospi_bus_width_t data_width); +bool is_octa_mode(ospi_bus_width_t inst_width, ospi_bus_width_t addr_width, ospi_bus_width_t data_width); +bool is_octa_dtr_mode(ospi_bus_width_t inst_width, ospi_bus_width_t addr_width, ospi_bus_width_t data_width); + +#define WAIT_FOR(timeout, q) TEST_ASSERT_EQUAL_MESSAGE(sOK, flash_wait_for(timeout, q), "flash_wait_for failed!!!") + + +#endif // MBED_OSPI_TEST_UTILS_H diff --git a/storage/blockdevice/COMPONENT_OSPIF/include/QSPIF/MX25LM51245G_config.h b/storage/blockdevice/COMPONENT_OSPIF/include/QSPIF/MX25LM51245G_config.h new file mode 100644 index 0000000..2eeb4f2 --- /dev/null +++ b/storage/blockdevice/COMPONENT_OSPIF/include/QSPIF/MX25LM51245G_config.h @@ -0,0 +1,45 @@ +/* mbed Microcontroller Library + * Copyright (c) 2020 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_OSPI_FLASH_MX25LM51245G_H +#define MBED_OSPI_FLASH_MX25LM51245G_H + + +#define OSPI_FLASH_CHIP_STRING "macronix MX25LM51245G" + +// This is a workaround, +// The sfdp parameter values in Macronix old octaflash(include the MX25LM51245G on L4R9I_DISCO) are all 0xFF, +// so we need to define the parameter values by software to support SFDP parsing. +// The code below can be removed when users test with the new flash. +#define NEED_DEFINE_SFDP_PARA + +#ifdef NEED_DEFINE_SFDP_PARA +uint8_t _sfdp_head_table[32] = {0x53, 0x46, 0x44, 0x50, 0x06, 0x01, 0x02, 0xFF, 0x00, 0x06, 0x01, + 0x10, 0x30, 0x00, 0x00, 0xFF, 0xC2, 0x00, 0x01, 0x04, 0x10, 0x01, + 0x00, 0xFF, 0x84, 0x00, 0x01, 0x02, 0xC0, 0x00, 0x00, 0xFF + }; +uint8_t _sfdp_basic_param_table[64] = {0x30, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x14, 0xEC, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0C, 0x20, + 0x10, 0xDC, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x50, 0xF9, 0x80 + }; +uint8_t _sfdp_4_byte_inst_table[8] = {0x7F, 0xEF, 0xFF, 0xFF, 0x21, 0x5C, 0xDC, 0x14}; +#endif + +#endif // MBED_OSPI_FLASH_MX25LM51245G_H diff --git a/storage/blockdevice/COMPONENT_OSPIF/include/QSPIF/OSPIFBlockDevice.h b/storage/blockdevice/COMPONENT_OSPIF/include/QSPIF/OSPIFBlockDevice.h new file mode 100644 index 0000000..ed61604 --- /dev/null +++ b/storage/blockdevice/COMPONENT_OSPIF/include/QSPIF/OSPIFBlockDevice.h @@ -0,0 +1,454 @@ +/* mbed Microcontroller Library + * Copyright (c) 2020 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_OSPIF_BLOCK_DEVICE_H +#define MBED_OSPIF_BLOCK_DEVICE_H + +#include "drivers/OSPI.h" +#include "drivers/internal/SFDP.h" +#include "blockdevice/BlockDevice.h" +#include "platform/Callback.h" + +#ifndef MBED_CONF_OSPIF_OSPI_IO0 +#define MBED_CONF_OSPIF_OSPI_IO0 NC +#endif +#ifndef MBED_CONF_OSPIF_OSPI_IO1 +#define MBED_CONF_OSPIF_OSPI_IO1 NC +#endif +#ifndef MBED_CONF_OSPIF_OSPI_IO2 +#define MBED_CONF_OSPIF_OSPI_IO2 NC +#endif +#ifndef MBED_CONF_OSPIF_OSPI_IO3 +#define MBED_CONF_OSPIF_OSPI_IO3 NC +#endif +#ifndef MBED_CONF_OSPIF_OSPI_IO4 +#define MBED_CONF_OSPIF_OSPI_IO4 NC +#endif +#ifndef MBED_CONF_OSPIF_OSPI_IO5 +#define MBED_CONF_OSPIF_OSPI_IO5 NC +#endif +#ifndef MBED_CONF_OSPIF_OSPI_IO6 +#define MBED_CONF_OSPIF_OSPI_IO6 NC +#endif +#ifndef MBED_CONF_OSPIF_OSPI_IO7 +#define MBED_CONF_OSPIF_OSPI_IO7 NC +#endif +#ifndef MBED_CONF_OSPIF_OSPI_SCK +#define MBED_CONF_OSPIF_OSPI_SCK NC +#endif +#ifndef MBED_CONF_OSPIF_OSPI_CSN +#define MBED_CONF_OSPIF_OSPI_CSN NC +#endif +#ifndef MBED_CONF_OSPIF_OSPI_DQS +#define MBED_CONF_OSPIF_OSPI_DQS NC +#endif +#ifndef MBED_CONF_OSPIF_OSPI_POLARITY_MODE +#define MBED_CONF_OSPIF_OSPI_POLARITY_MODE 0 +#endif +#ifndef MBED_CONF_OSPIF_OSPI_FREQ +#define MBED_CONF_OSPIF_OSPI_FREQ 40000000 +#endif + +/** Enum ospif standard error codes + * + * @enum ospif_bd_error + */ +enum ospif_bd_error { + OSPIF_BD_ERROR_OK = 0, /*!< no error */ + OSPIF_BD_ERROR_DEVICE_ERROR = BD_ERROR_DEVICE_ERROR, /*!< device specific error -4001 */ + OSPIF_BD_ERROR_PARSING_FAILED = -4002, /* SFDP Parsing failed */ + OSPIF_BD_ERROR_READY_FAILED = -4003, /* Wait for Mem Ready failed */ + OSPIF_BD_ERROR_WREN_FAILED = -4004, /* Write Enable Failed */ + OSPIF_BD_ERROR_INVALID_ERASE_PARAMS = -4005, /* Erase command not on sector aligned addresses or exceeds device size */ + OSPIF_BD_ERROR_DEVICE_NOT_UNIQUE = -4006, /* Only one instance per csel is allowed */ + OSPIF_BD_ERROR_DEVICE_MAX_EXCEED = -4007 /* Max active OSPIF devices exceeded */ +}; + +enum _mode { + SPI, + SOPI, + DOPI +}; + +/** Enum ospif polarity mode + * + * @enum ospif_polarity_mode + */ +enum ospif_polarity_mode { + OSPIF_POLARITY_MODE_0 = 0, /* CPOL=0, CPHA=0 */ + OSPIF_POLARITY_MODE_1 /* CPOL=1, CPHA=1 */ +}; + +#define OSPIF_MAX_ACTIVE_FLASH_DEVICES 10 + +/** BlockDevice for SFDP based flash devices over OSPI bus + * + * @code + * // Here's an example using OSPI flash device on NUCLEO_L4R9I target + * #include "mbed.h" + * #include "OSPIFBlockDevice.h" + * + * OSPIFBlockDevice block_device(OSPI_FLASH1_IO0, OSPI_FLASH1_IO1, OSPI_FLASH1_IO2, OSPI_FLASH1_IO3, OSPI_FLASH1_IO4, OSPI_FLASH1_IO5, OSPI_FLASH1_IO6, OSPI_FLASH1_IO7 + * OSPI_FLASH1_SCK, OSPI_FLASH1_CSN, OSPI_FLASH1_DQS, OSPIF_POLARITY_MODE_0, MBED_CONF_OSPIF_OSPI_FREQ); + * + * int main() + * { + * printf("OSPI SFDP Flash Block Device example\n"); + * + * // Initialize the OSPI 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("OSPIF BD size: %llu\n", block_device.size()); + * printf("OSPIF BD read size: %llu\n", block_device.get_read_size()); + * printf("OSPIF BD program size: %llu\n", block_device.get_program_size()); + * printf("OSPIF 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 OSPIFBlockDevice : public mbed::BlockDevice { +public: + /** Create OSPIFBlockDevice - An SFDP based Flash Block Device over OSPI 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 io4 5th IO pin used for sending/receiving data during data phase of a transaction + * @param io5 6th IO pin used for sending/receiving data during data phase of a transaction + * @param io6 7th IO pin used for sending/receiving data during data phase of a transaction + * @param io7 8th IO pin used for sending/receiving data during data phase of a transaction + * @param sclk OSPI Clock pin + * @param csel OSPI chip select pin + * @param dqs OSPI dqs pin + * @param clock_mode specifies the OSPI Clock Polarity mode (OSPIF_POLARITY_MODE_0/OSPIF_POLARITY_MODE_1) + * default value = 0 + * @param freq Clock frequency of the OSPI bus (defaults to 40MHz) + */ + OSPIFBlockDevice(PinName io0 = MBED_CONF_OSPIF_OSPI_IO0, + PinName io1 = MBED_CONF_OSPIF_OSPI_IO1, + PinName io2 = MBED_CONF_OSPIF_OSPI_IO2, + PinName io3 = MBED_CONF_OSPIF_OSPI_IO3, + PinName io4 = MBED_CONF_OSPIF_OSPI_IO4, + PinName io5 = MBED_CONF_OSPIF_OSPI_IO5, + PinName io6 = MBED_CONF_OSPIF_OSPI_IO6, + PinName io7 = MBED_CONF_OSPIF_OSPI_IO7, + PinName sclk = MBED_CONF_OSPIF_OSPI_SCK, + PinName csel = MBED_CONF_OSPIF_OSPI_CSN, + PinName dqs = MBED_CONF_OSPIF_OSPI_DQS, + int clock_mode = MBED_CONF_OSPIF_OSPI_POLARITY_MODE, + int freq = MBED_CONF_OSPIF_OSPI_FREQ); + + /** Initialize a block device + * + * @return OSPIF_BD_ERROR_OK(0) - success + * OSPIF_BD_ERROR_DEVICE_ERROR - device driver transaction failed + * OSPIF_BD_ERROR_READY_FAILED - Waiting for Memory ready failed or timedout + * OSPIF_BD_ERROR_PARSING_FAILED - unexpected format or values in one of the SFDP tables + */ + virtual int init(); + + /** Deinitialize a block device + * + * @return OSPIF_BD_ERROR_OK(0) - success + * OSPIF_BD_ERROR_DEVICE_ERROR - device driver transaction failed + */ + virtual int deinit(); + + /** Desctruct OSPIFBlockDevie + */ + ~OSPIFBlockDevice() + { + 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 OSPIF_BD_ERROR_OK(0) - success + * OSPIF_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 OSPIF_BD_ERROR_OK(0) - success + * OSPIF_BD_ERROR_DEVICE_ERROR - device driver transaction failed + * OSPIF_BD_ERROR_READY_FAILED - Waiting for Memory ready failed or timed out + * OSPIF_BD_ERROR_WREN_FAILED - Write Enable failed + * OSPIF_BD_ERROR_PARSING_FAILED - unexpected format or values in one of the SFDP tables + */ + 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 OSPIF_BD_ERROR_OK(0) - success + * OSPIF_BD_ERROR_DEVICE_ERROR - device driver transaction failed + * OSPIF_BD_ERROR_READY_FAILED - Waiting for Memory ready failed or timed out + * OSPIF_BD_ERROR_WREN_FAILED - Write Enable failed + * OSPIF_BD_ERROR_PARSING_FAILED - unexpected format or values in one of the SFDP tables + * OSPIF_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; + + /** 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 mbed::bd_size_t get_erase_size(mbed::bd_addr_t addr); + + /** 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; + + /** Change the operation mode(SPI, SOPI or DOPI) of flash. + * + * @return OSPIF_BD_ERROR_OK(0) - success + * OSPIF_BD_ERROR_DEVICE_ERROR - device driver transaction failed + * OSPIF_BD_ERROR_READY_FAILED - Waiting for Memory ready failed or timed out + * OSPIF_BD_ERROR_WREN_FAILED - Write Enable failed + * OSPIF_BD_ERROR_PARSING_FAILED - unexpected format or values in one of the SFDP tables + * OSPIF_BD_ERROR_INVALID_ERASE_PARAMS - Trying to erase unaligned address or size + */ + virtual int change_mode(int mode); + +private: + /********************************/ + /* Different Device Csel Mgmt */ + /********************************/ + // Add a new OSPI device CS to existing devices list. + // Only one OSPIFBlockDevice 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 OSPI Driver APIs */ + /********************************/ + // Send Program/Write command to Driver + ospi_status_t _ospi_send_program_command(mbed::ospi_inst_t prog_instruction, const void *buffer, + mbed::bd_addr_t addr, mbed::bd_size_t *size); + + // Send Read command to Driver + ospi_status_t _ospi_send_read_command(mbed::ospi_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 + ospi_status_t _ospi_send_erase_command(mbed::ospi_inst_t erase_instruction, mbed::bd_addr_t addr, mbed::bd_size_t size); + + // Send Generic command_transfer command to Driver + ospi_status_t _ospi_send_general_command(mbed::ospi_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 command to read from the SFDP table + int _ospi_send_read_sfdp_command(mbed::bd_addr_t addr, void *rx_buffer, mbed::bd_size_t rx_length); + + // Read the contents of status registers 1 and 2 into a buffer (buffer must have a length of 2) + ospi_status_t _ospi_read_status_registers(uint8_t *reg_buffer); + + // Set the contents of status registers 1 and 2 from a buffer (buffer must have a length of 2) + ospi_status_t _ospi_write_status_registers(uint8_t *reg_buffer); + + // Send set_frequency command to Driver + ospi_status_t _ospi_set_frequency(int freq); + + // Update the 4-byte addressing extension register with the MSB of the address if it is in use + ospi_status_t _ospi_update_4byte_ext_addr_reg(bd_addr_t addr); + + /*********************************/ + /* Flash Configuration Functions */ + /*********************************/ + // 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(); + + // Enable Fast Mode - for flash chips with low power default + int _enable_fast_mode(); + + // Enable OPI Mode + int _enable_opi_mdoe(); + + // Query vendor ID and handle special behavior that isn't covered by SFDP data + int _handle_vendor_quirks(); + + /****************************************/ + /* SFDP Detection and Parsing Functions */ + /****************************************/ + // Parse and Detect required Basic Parameters from Table + int _sfdp_parse_basic_param_table(mbed::Callback sfdp_reader, + mbed::sfdp_hdr_info &sfdp_info); + + // Parse and Detect 4-Byte Address Instruction Parameters from Table + int _sfdp_parse_4_byte_inst_table(mbed::Callback sfdp_reader, + mbed::sfdp_hdr_info &sfdp_info); + + // Detect the soft reset protocol and reset - returns error if soft reset is not supported + int _sfdp_detect_reset_protocol_and_reset(uint8_t *basic_param_table_ptr); + + // 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, bool &is_opi_mode); + + // 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) + int _sfdp_set_qpi_enabled(uint8_t *basic_param_table_ptr); + + // Detect 4-byte addressing mode and enable it if supported + int _sfdp_detect_and_enable_4byte_addressing(uint8_t *basic_param_table_ptr, int basic_param_table_size); + +private: + enum ospif_clear_protection_method_t { + OSPIF_BP_ULBPR, // Issue global protection unlock instruction + OSPIF_BP_CLEAR_SR, // Clear protection bits in status register 1 + }; + + // OSPI Driver Object + mbed::OSPI _ospi; + + // Static List of different OSPI based Flash devices csel that already exist + // Each OSPI Flash device csel can have only 1 OSPIFBlockDevice instance + // _devices_mutex is used to lock csel list - only one OSPIFBlockDevice instance per csel is allowed + static SingletonPtr _devices_mutex; + static int _number_of_active_ospif_flash_csel; + static PinName *_active_ospif_flash_csel_arr; + + int _unique_device_status; + PinName _csel; + + // Mutex is used to protect Flash device for some OSPI 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::ospi_inst_t _read_instruction; + mbed::ospi_inst_t _prog_instruction; + mbed::ospi_inst_t _legacy_erase_instruction; + + // Status register write/read instructions + unsigned int _num_status_registers; + mbed::ospi_inst_t _write_status_reg_2_inst; + mbed::ospi_inst_t _read_status_reg_2_inst; // If three registers, this instruction reads the latter two + + // Attempt to enable 4-byte addressing. True by default, but may be disabled for some vendors + bool _attempt_4_byte_addressing; + // 4-byte addressing extension register write instruction + mbed::ospi_inst_t _4byte_msb_reg_write_inst; + + // Flash support 4-Byte instructions,like READ4B(0x13). + bool _support_4_byte_inst; + + // Quad mode enable status register and bit + int _quad_enable_register_idx; + int _quad_enable_bit; + + bool _needs_fast_mode; + + // Clear block protection + ospif_clear_protection_method_t _clear_protection_method; + + // Data extracted from the devices SFDP structure + mbed::sfdp_hdr_info _sfdp_info; + + unsigned int _page_size_bytes; // Page size - 256 Bytes default + int _freq; + + // Bus speed configuration + ospi_bus_width_t _inst_width; //Bus width for Instruction phase + ospi_inst_size_t _inst_size; //Instruction Size + ospi_bus_width_t _address_width; //Bus width for Address phase + ospi_address_size_t _address_size; //Number of bits for address + ospi_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 + ospi_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_OSPIF/mbed_lib.json b/storage/blockdevice/COMPONENT_OSPIF/mbed_lib.json new file mode 100644 index 0000000..14b1d43 --- /dev/null +++ b/storage/blockdevice/COMPONENT_OSPIF/mbed_lib.json @@ -0,0 +1,33 @@ +{ + "name": "ospif", + "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 + }, + "OSPI_IO0": "MBED_CONF_DRIVERS_OSPI_IO0", + "OSPI_IO1": "MBED_CONF_DRIVERS_OSPI_IO1", + "OSPI_IO2": "MBED_CONF_DRIVERS_OSPI_IO2", + "OSPI_IO3": "MBED_CONF_DRIVERS_OSPI_IO3", + "OSPI_IO4": "MBED_CONF_DRIVERS_OSPI_IO4", + "OSPI_IO5": "MBED_CONF_DRIVERS_OSPI_IO5", + "OSPI_IO6": "MBED_CONF_DRIVERS_OSPI_IO6", + "OSPI_IO7": "MBED_CONF_DRIVERS_OSPI_IO7", + "OSPI_SCK": "MBED_CONF_DRIVERS_OSPI_SCK", + "OSPI_CSN": "MBED_CONF_DRIVERS_OSPI_CSN", + "OSPI_DQS": "MBED_CONF_DRIVERS_OSPI_DQS", + "OSPI_POLARITY_MODE": 0, + "OSPI_FREQ": "40000000", + "OSPI_MIN_READ_SIZE": "1", + "OSPI_MIN_PROG_SIZE": "256" + }, + "target_overrides": { + "MX25LM51245G": { + "OSPI_FREQ": "66000000" + } + } +} diff --git a/storage/blockdevice/COMPONENT_OSPIF/source/OSPIFBlockDevice.cpp b/storage/blockdevice/COMPONENT_OSPIF/source/OSPIFBlockDevice.cpp new file mode 100644 index 0000000..8952927 --- /dev/null +++ b/storage/blockdevice/COMPONENT_OSPIF/source/OSPIFBlockDevice.cpp @@ -0,0 +1,1847 @@ +/* mbed Microcontroller Library + * Copyright (c) 2020 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 "drivers/internal/SFDP.h" +#include "platform/Callback.h" +#include "OSPIFBlockDevice.h" +#include +#include "rtos/ThisThread.h" +#if defined(TARGET_MX25LM51245G) +#include "MX25LM51245G_config.h" +#endif + +#ifndef MBED_CONF_MBED_TRACE_ENABLE +#define MBED_CONF_MBED_TRACE_ENABLE 0 +#endif + +#include "mbed_trace.h" +#define TRACE_GROUP "OSPIF" + +using namespace mbed; + +/* Default OSPIF Parameters */ +/****************************/ +#define OSPIF_DEFAULT_SE_SIZE 4096 +// The SFDP spec only defines two status registers. But some devices, +// have three "status-like" registers (one status, two config) +#define OSPI_MAX_STATUS_REGISTERS 3 +#define OSPI_DEFAULT_STATUS_REGISTERS 2 +#ifndef UINT64_MAX +#define UINT64_MAX -1 +#endif +#define OSPI_NO_ADDRESS_COMMAND UINT64_MAX +#define OSPI_ALT_DEFAULT_VALUE 0 +// Status Register Bits +#define OSPIF_STATUS_BIT_WIP 0x1 // Write In Progress +#define OSPIF_STATUS_BIT_WEL 0x2 // Write Enable Latch +#define OSPIF_NO_QUAD_ENABLE (-1) + +// Configuration Register2 address +#define OSPIF_CR2_OPI_EN_ADDR 0x00000000 + +/* SFDP Header Parsing */ +/***********************/ +#define OSPIF_RSFDP_DUMMY_CYCLES 8 + +/* Basic Parameters Table Parsing */ +/**********************************/ +//READ Instruction support according to BUS Configuration +#define OSPIF_BASIC_PARAM_TABLE_FAST_READ_SUPPORT_BYTE 2 +#define OSPIF_BASIC_PARAM_TABLE_QPI_READ_SUPPORT_BYTE 16 +#define OSPIF_BASIC_PARAM_TABLE_OPI_READ_SUPPORT_BYTE 17 +#define OSPIF_BASIC_PARAM_TABLE_888_READ_INST_BYTE 19 +#define OSPIF_BASIC_PARAM_TABLE_444_READ_INST_BYTE 27 +#define OSPIF_BASIC_PARAM_TABLE_144_READ_INST_BYTE 9 +#define OSPIF_BASIC_PARAM_TABLE_114_READ_INST_BYTE 11 +#define OSPIF_BASIC_PARAM_TABLE_222_READ_INST_BYTE 23 +#define OSPIF_BASIC_PARAM_TABLE_122_READ_INST_BYTE 15 +#define OSPIF_BASIC_PARAM_TABLE_112_READ_INST_BYTE 13 +#define OSPIF_BASIC_PARAM_TABLE_PAGE_SIZE_BYTE 40 +// Quad Enable Params +#define OSPIF_BASIC_PARAM_TABLE_QER_BYTE 58 +#define OSPIF_BASIC_PARAM_TABLE_444_MODE_EN_SEQ_BYTE 56 +// Erase Types Params +#define OSPIF_BASIC_PARAM_TABLE_ERASE_TYPE_1_BYTE 29 +#define OSPIF_BASIC_PARAM_TABLE_ERASE_TYPE_2_BYTE 31 +#define OSPIF_BASIC_PARAM_TABLE_ERASE_TYPE_3_BYTE 33 +#define OSPIF_BASIC_PARAM_TABLE_ERASE_TYPE_4_BYTE 35 +#define OSPIF_BASIC_PARAM_TABLE_ERASE_TYPE_1_SIZE_BYTE 28 +#define OSPIF_BASIC_PARAM_TABLE_ERASE_TYPE_2_SIZE_BYTE 30 +#define OSPIF_BASIC_PARAM_TABLE_ERASE_TYPE_3_SIZE_BYTE 32 +#define OSPIF_BASIC_PARAM_TABLE_ERASE_TYPE_4_SIZE_BYTE 34 +#define OSPIF_BASIC_PARAM_4K_ERASE_TYPE_BYTE 1 + +#define OSPIF_BASIC_PARAM_TABLE_SOFT_RESET_BYTE 61 +#define OSPIF_BASIC_PARAM_TABLE_4BYTE_ADDR_BYTE 63 + +#define SOFT_RESET_RESET_INST_BITMASK 0b001000 +#define SOFT_RESET_ENABLE_AND_RESET_INST_BITMASK 0b010000 + +// 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 + +// 4-Byte Addressing Support Bitmasks +#define FOURBYTE_ADDR_B7_BITMASK 0b00000001 +#define FOURBYTE_ADDR_B7_WREN_BITMASK 0b00000010 +#define FOURBYTE_ADDR_EXT_ADDR_REG_BITMASK 0b00000100 +#define FOURBYTE_ADDR_BANK_REG_BITMASK 0b00001000 +#define FOURBYTE_ADDR_CONF_REG_BITMASK 0b00010000 +#define FOURBYTE_ADDR_INSTS_BITMASK 0b00100000 +#define FOURBYTE_ADDR_ALWAYS_BITMASK 0b01000000 + +/* 4-Byte Instruction Table Parsing */ +/**********************************/ +#define SFDP_DEFAULT_4_BYTE_INST_TABLE_SIZE_BYTES 8 /* 2 DWORDS */ + +#define OSPIF_4_BYTE_INST_TABLE_FAST_READ_SUPPORT_BYTE 0 +#define OSPIF_4_BYTE_INST_TABLE_ERASE_SUPPORT_BYTE 1 +#define OSPIF_4_BYTE_INST_TABLE_INST_ERASE_TYPE_1_BYTE 4 +#define OSPIF_4_BYTE_INST_TABLE_INST_ERASE_TYPE_2_BYTE 5 +#define OSPIF_4_BYTE_INST_TABLE_INST_ERASE_TYPE_3_BYTE 6 +#define OSPIF_4_BYTE_INST_TABLE_INST_ERASE_TYPE_4_BYTE 7 + +// 4-Byte Instruction Support Bitmasks +#define FOURBYTE_INST_1_1_1_READ_BITMASK 0b00000001 +#define FOURBYTE_INST_1_1_2_FAST_READ_BITMASK 0b00000010 +#define FOURBYTE_INST_1_2_2_FAST_READ_BITMASK 0b00000100 +#define FOURBYTE_INST_1_1_4_FAST_READ_BITMASK 0b00001000 +#define FOURBYTE_INST_1_4_4_FAST_READ_BITMASK 0b00010000 +#define FOURBYTE_INST_ERASE_TYPE_1_BITMASK 0b00000010 +#define FOURBYTE_INST_ERASE_TYPE_2_BITMASK 0b00000100 +#define FOURBYTE_INST_ERASE_TYPE_3_BITMASK 0b00001000 +#define FOURBYTE_INST_ERASE_TYPE_4_BITMASK 0b00010000 + +#define IS_MEM_READY_MAX_RETRIES 10000 + + +// General OSPI instructions +#define OSPIF_INST_WSR1 0x01 // Write status register 1 +#define OSPIF_INST_PROG 0x02 // Page program +#define OSPIF_INST_WRDI 0x04 // Write disable +#define OSPIF_INST_RSR1 0x05 // Read status register 1 +#define OSPIF_INST_WREN 0x06 // Write enable +#define OSPIF_INST_RSFDP 0x5A // Read SFDP +#define OSPIF_INST_RDID 0x9F // Read Manufacturer and JDEC Device ID +#define DTROSPIF_INST_READ_OCTA_STR 0xEC // Read data in SOPI mode +#define DTROSPIF_INST_READ_OCTA_DTR 0xEE // Read data in DOPI mode +#define OSPIF_INST_WRCR2 0x72 // Write Configuration Register2 +#define OSPIF_INST_RDCR2 0x71 // Read Configuration Register2 + +//Macronix flash 4-Byte address instructions +#define OSPIF_INST_READ_4B 0x13 // Read data with 4 byte address +#define OSPIF_INST_PROG_4B 0x12 // Page program with 4 byte address + +// Device-specific instructions +#define OSPIF_INST_ULBPR 0x98 // Clear all write-protection bits in the Block-Protection register +#define OSPIF_INST_RDCR 0x15 // Read the two control registers + +// Default read/legacy erase instructions +#define OSPIF_INST_READ_DEFAULT 0x03 +#define OSPIF_INST_LEGACY_ERASE_DEFAULT OSPI_NO_INST + +// Default status register 2 read/write instructions +#define OSPIF_INST_WSR2_DEFAULT OSPI_NO_INST +#define OSPIF_INST_RSR2_DEFAULT 0x35 + +// Default 4-byte extended addressing register write instruction +#define OSPIF_INST_4BYTE_REG_WRITE_DEFAULT OSPI_NO_INST + + +// Length of data returned from RDID instruction +#define OSPI_RDID_DATA_LENGTH 3 + + +/* Init function to initialize Different Devices CS static list */ +static PinName *generate_initialized_active_ospif_csel_arr(); +// Static Members for different devices csel +// _devices_mutex is used to lock csel list - only one OSPIFBlockDevice instance per csel is allowed +SingletonPtr OSPIFBlockDevice::_devices_mutex; +int OSPIFBlockDevice::_number_of_active_ospif_flash_csel = 0; +PinName *OSPIFBlockDevice::_active_ospif_flash_csel_arr = generate_initialized_active_ospif_csel_arr(); + +/********* Public API Functions *********/ +/****************************************/ +OSPIFBlockDevice::OSPIFBlockDevice(PinName io0, PinName io1, PinName io2, PinName io3, PinName io4, PinName io5, PinName io6, PinName io7, PinName sclk, PinName csel, PinName dqs, + int clock_mode, int freq) + : _ospi(io0, io1, io2, io3, io4, io5, io6, io7, sclk, csel, dqs, 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 OSPIFBlockDevice csel: %d", (int)csel); + } else if (_unique_device_status == -1) { + tr_error("OSPIFBlockDevice with the same csel(%d) already exists", (int)csel); + } else { + tr_error("Too many different OSPIFBlockDevice devices - max allowed: %d", OSPIF_MAX_ACTIVE_FLASH_DEVICES); + } + + // Initialize parameters + _sfdp_info.bptbl.legacy_erase_instruction = OSPIF_INST_LEGACY_ERASE_DEFAULT; + _sfdp_info.bptbl.device_size_bytes = 0; + _sfdp_info.smptbl.regions_min_common_erase_size = 0; + _sfdp_info.smptbl.region_cnt = 1; + _sfdp_info.smptbl.region_erase_types_bitfld[0] = SFDP_ERASE_BITMASK_NONE; + + // Until proven otherwise, assume no quad enable + _quad_enable_register_idx = OSPIF_NO_QUAD_ENABLE; + _quad_enable_bit = OSPIF_NO_QUAD_ENABLE; + _needs_fast_mode = false; + + // Default Bus Setup 1_1_1 with 0 dummy and mode cycles + _inst_width = OSPI_CFG_BUS_SINGLE; + _inst_size = OSPI_CFG_INST_SIZE_16; + _address_width = OSPI_CFG_BUS_SINGLE; + _address_size = OSPI_CFG_ADDR_SIZE_24; + _alt_size = 0; + _dummy_cycles = 0; + _data_width = OSPI_CFG_BUS_SINGLE; + + // Set default read/erase instructions + _read_instruction = OSPIF_INST_READ_DEFAULT; + _prog_instruction = OSPIF_INST_PROG; + _legacy_erase_instruction = OSPIF_INST_LEGACY_ERASE_DEFAULT; + + _num_status_registers = OSPI_DEFAULT_STATUS_REGISTERS; + // Set default status register 2 write/read instructions + _write_status_reg_2_inst = OSPIF_INST_WSR2_DEFAULT; + _read_status_reg_2_inst = OSPIF_INST_RSR2_DEFAULT; + + _clear_protection_method = OSPIF_BP_CLEAR_SR; + + // Set default 4-byte addressing extension register write instruction + _attempt_4_byte_addressing = true; + _4byte_msb_reg_write_inst = OSPIF_INST_4BYTE_REG_WRITE_DEFAULT; + _support_4_byte_inst = false; +} + +int OSPIFBlockDevice::init() +{ + int status = OSPIF_BD_ERROR_OK; + + if (_unique_device_status == 0) { + tr_debug("OSPIFBlockDevice csel: %d", (int)_csel); + } else if (_unique_device_status == -1) { + tr_error("OSPIFBlockDevice with the same csel(%d) already exists", (int)_csel); + return OSPIF_BD_ERROR_DEVICE_NOT_UNIQUE; + } else { + tr_error("Too many different OSPIFBlockDevice devices - max allowed: %d", OSPIF_MAX_ACTIVE_FLASH_DEVICES); + return OSPIF_BD_ERROR_DEVICE_MAX_EXCEED; + } + + _mutex.lock(); + + if (!_is_initialized) { + _init_ref_count = 0; + } + + _init_ref_count++; + + if (_init_ref_count != 1) { + goto exit_point; + } + + + // 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 (OSPI_STATUS_OK != _ospi.configure_format(OSPI_CFG_BUS_SINGLE, OSPI_CFG_INST_SIZE_8, OSPI_CFG_BUS_SINGLE, _address_size, OSPI_CFG_BUS_SINGLE, + 0, OSPI_CFG_BUS_SINGLE, 0)) { + tr_error("_ospi_configure_format failed"); + status = OSPIF_BD_ERROR_DEVICE_ERROR; + goto exit_point; + } + + _alt_size = 0; + _dummy_cycles = 0; + if (OSPI_STATUS_OK != _ospi_set_frequency(_freq)) { + tr_error("OSPI Set Frequency Failed"); + status = OSPIF_BD_ERROR_DEVICE_ERROR; + goto exit_point; + } + + // Synchronize Device + if (false == _is_mem_ready()) { + tr_error("Init - _is_mem_ready Failed"); + status = OSPIF_BD_ERROR_READY_FAILED; + goto exit_point; + } + + if (_handle_vendor_quirks() < 0) { + tr_error("Init - Could not read vendor id"); + status = OSPIF_BD_ERROR_DEVICE_ERROR; + goto exit_point; + } + + /**************************** Parse SFDP data ***********************************/ + { + _sfdp_info.bptbl.addr = 0x0; + _sfdp_info.bptbl.size = 0; + _sfdp_info.smptbl.addr = 0x0; + _sfdp_info.smptbl.size = 0; + + if (sfdp_parse_headers(callback(this, &OSPIFBlockDevice::_ospi_send_read_sfdp_command), _sfdp_info) < 0) { + tr_error("Init - Parse SFDP Headers Failed"); + status = OSPIF_BD_ERROR_PARSING_FAILED; + goto exit_point; + } + + /************************ Parse 4-Byte Address Instructions Table ************************/ + if (_sfdp_parse_4_byte_inst_table(callback(this, &OSPIFBlockDevice::_ospi_send_read_sfdp_command), + _sfdp_info) < 0) { + tr_error("Init - Parse 4-Byte Address Instructions Table Failed"); + status = OSPIF_BD_ERROR_PARSING_FAILED; + goto exit_point; + } + + if (_sfdp_parse_basic_param_table(callback(this, &OSPIFBlockDevice::_ospi_send_read_sfdp_command), + _sfdp_info) < 0) { + tr_error("Init - Parse Basic Param Table Failed"); + status = OSPIF_BD_ERROR_PARSING_FAILED; + goto exit_point; + } + + if (sfdp_parse_sector_map_table(callback(this, &OSPIFBlockDevice::_ospi_send_read_sfdp_command), _sfdp_info) < 0) { + tr_error("Init - Parse Sector Map Table Failed"); + status = OSPIF_BD_ERROR_PARSING_FAILED; + goto exit_point; + } + } + + if (0 != _clear_block_protection()) { + tr_error("Init - clearing block protection failed"); + status = OSPIF_BD_ERROR_PARSING_FAILED; + goto exit_point; + } + + _is_initialized = true; + +exit_point: + _mutex.unlock(); + + return status; +} + +int OSPIFBlockDevice::deinit() +{ + int result = OSPIF_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; + } + + change_mode(SPI); + + // Disable Device for Writing + ospi_status_t status = _ospi_send_general_command(OSPIF_INST_WRDI, OSPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0); + if (status != OSPI_STATUS_OK) { + tr_error("Write Disable failed"); + result = OSPIF_BD_ERROR_DEVICE_ERROR; + } + + _is_initialized = false; + + _mutex.unlock(); + + if (_unique_device_status == 0) { + remove_csel_instance(_csel); + } + + return result; +} + +int OSPIFBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size) +{ + int status = OSPIF_BD_ERROR_OK; + tr_debug("Read Inst: 0x%xh", _read_instruction); + + _mutex.lock(); + + // In DOPI mode, the number of read data should be even + if (size % 2 != 0) { + size ++; + } + + if (OSPI_STATUS_OK != _ospi_send_read_command(_read_instruction, buffer, addr, size)) { + tr_error("Read Command failed"); + status = OSPIF_BD_ERROR_DEVICE_ERROR; + } + + _mutex.unlock(); + + return status; + +} + +int OSPIFBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size) +{ + ospi_status_t result = OSPI_STATUS_OK; + bool program_failed = false; + int status = OSPIF_BD_ERROR_OK; + uint32_t offset = 0; + uint32_t chunk = 0; + bd_size_t written_bytes = 0; + + 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); + written_bytes = chunk; + + _mutex.lock(); + + //Send WREN + if (_set_write_enable() != 0) { + tr_error("Write Enabe failed"); + program_failed = true; + status = OSPIF_BD_ERROR_WREN_FAILED; + goto exit_point; + } + + //_prog_instruction can be OSPIF_INST_PROG or OSPIF_INST_PROG_4B + result = _ospi_send_program_command(_prog_instruction, buffer, addr, &written_bytes); + if ((result != OSPI_STATUS_OK) || (chunk != written_bytes)) { + tr_error("Write failed"); + program_failed = true; + status = OSPIF_BD_ERROR_DEVICE_ERROR; + goto exit_point; + } + + buffer = static_cast(buffer) + chunk; + addr += chunk; + size -= chunk; + + if (false == _is_mem_ready()) { + tr_error("Device not ready after write, failed"); + program_failed = true; + status = OSPIF_BD_ERROR_READY_FAILED; + goto exit_point; + } + _mutex.unlock(); + } + +exit_point: + if (program_failed) { + _mutex.unlock(); + } + + return status; +} + +int OSPIFBlockDevice::erase(bd_addr_t addr, bd_size_t in_size) +{ + int type = 0; + uint32_t offset = 0; + uint32_t chunk = 4096; + ospi_inst_t cur_erase_inst = OSPI_NO_INST; + int size = (int)in_size; + bool erase_failed = false; + int status = OSPIF_BD_ERROR_OK; + // Find region of erased address + int region = sfdp_find_addr_region(addr, _sfdp_info); + // Erase Types of selected region + uint8_t bitfield = _sfdp_info.smptbl.region_erase_types_bitfld[region]; + + tr_debug("Erase - addr: %llu, in_size: %llu", addr, in_size); + + if ((addr + in_size) > _sfdp_info.bptbl.device_size_bytes) { + tr_error("Erase exceeds flash device size"); + return OSPIF_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 OSPIF_BD_ERROR_INVALID_ERASE_PARAMS; + } + + // For each iteration erase the largest section supported by current region + while (size > 0) { + unsigned int eu_size; + if (_sfdp_info.bptbl.legacy_erase_instruction == OSPI_NO_INST) { + // Iterate to find next largest erase type that is a) supported by region, and b) smaller than size. + // Find the matching instruction and erase size chunk for that type. + type = sfdp_iterate_next_largest_erase_type(bitfield, size, (int)addr, + region, + _sfdp_info.smptbl); + //cur_erase_inst should be 4-Byte erase instruction for Macronix octa flash OPI mode + cur_erase_inst = (_address_size == OSPI_CFG_ADDR_SIZE_32) ? _sfdp_info.fbatbl.erase_type_4_byte_inst_arr[type] : _sfdp_info.smptbl.erase_type_inst_arr[type]; + eu_size = _sfdp_info.smptbl.erase_type_size_arr[type]; + } else { + // Must use legacy 4k erase instruction + cur_erase_inst = _sfdp_info.bptbl.legacy_erase_instruction; + eu_size = OSPIF_DEFAULT_SE_SIZE; + } + offset = addr % eu_size; + chunk = ((offset + size) < eu_size) ? size : (eu_size - offset); + + tr_debug("Erase - addr: %llu, size:%d, Inst: 0x%xh, chunk: %lu ", + addr, size, cur_erase_inst, chunk); + tr_debug("Erase - Region: %d, Type:%d ", + region, type); + + _mutex.lock(); + + if (_set_write_enable() != 0) { + tr_error("OSPI Erase Device not ready - failed"); + erase_failed = true; + status = OSPIF_BD_ERROR_WREN_FAILED; + goto exit_point; + } + + if (OSPI_STATUS_OK != _ospi_send_erase_command(cur_erase_inst, addr, size)) { + tr_error("OSPI Erase command failed!"); + erase_failed = true; + status = OSPIF_BD_ERROR_DEVICE_ERROR; + goto exit_point; + } + + addr += chunk; + size -= chunk; + + if ((size > 0) && (addr > _sfdp_info.smptbl.region_high_boundary[region])) { + // erase crossed to next region + region++; + bitfield = _sfdp_info.smptbl.region_erase_types_bitfld[region]; + } + + if (false == _is_mem_ready()) { + tr_error("OSPI After Erase Device not ready - failed"); + erase_failed = true; + status = OSPIF_BD_ERROR_READY_FAILED; + goto exit_point; + } + + _mutex.unlock(); + } + +exit_point: + if (erase_failed) { + _mutex.unlock(); + } + + return status; +} + +bd_size_t OSPIFBlockDevice::get_read_size() const +{ + // Return minimum read size in bytes for the device + return MBED_CONF_OSPIF_OSPI_MIN_READ_SIZE; +} + +bd_size_t OSPIFBlockDevice::get_program_size() const +{ + // Return minimum program/write size in bytes for the device + return MBED_CONF_OSPIF_OSPI_MIN_PROG_SIZE; +} + +bd_size_t OSPIFBlockDevice::get_erase_size() const +{ + // return minimal erase size supported by all regions (0 if none exists) + return _sfdp_info.smptbl.regions_min_common_erase_size; +} + +const char *OSPIFBlockDevice::get_type() const +{ + return "OSPIF"; +} + +// Find minimal erase size supported by the region to which the address belongs to +bd_size_t OSPIFBlockDevice::get_erase_size(bd_addr_t addr) +{ + // If the legacy erase instruction is in use, the erase size is uniformly 4k + if (_sfdp_info.bptbl.legacy_erase_instruction != OSPI_NO_INST) { + return OSPIF_DEFAULT_SE_SIZE; + } + + // Find region of current address + int region = sfdp_find_addr_region(addr, _sfdp_info); + + int min_region_erase_size = _sfdp_info.smptbl.regions_min_common_erase_size; + int8_t type_mask = SFDP_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 (_sfdp_info.smptbl.region_erase_types_bitfld[region] & type_mask) { + + min_region_erase_size = _sfdp_info.smptbl.erase_type_size_arr[i_ind]; + break; + } + type_mask = type_mask << 1; + } + + if (i_ind == 4) { + tr_error("No erase type was found for region addr"); + } + } + + return (bd_size_t)min_region_erase_size; +} + +bd_size_t OSPIFBlockDevice::size() const +{ + return _sfdp_info.bptbl.device_size_bytes; +} + +int OSPIFBlockDevice::get_erase_value() const +{ + return 0xFF; +} + +int OSPIFBlockDevice::change_mode(int mode) +{ + int status = OSPIF_BD_ERROR_OK; + char config_reg2 = 0; + + if (((mode == SPI) && (_read_instruction == OSPIF_INST_READ_4B)) || + ((mode == SOPI) && (_read_instruction == DTROSPIF_INST_READ_OCTA_STR)) || + ((mode == DOPI) && (_read_instruction == DTROSPIF_INST_READ_OCTA_DTR))) { + tr_debug("Flash does not need change mode"); + } + + if (mode == SOPI) { + if ((_read_instruction != OSPIF_INST_READ_4B) && (_read_instruction != OSPIF_INST_READ_DEFAULT)) { //change mode from DOPI to SPI + // Write new Status Register Setup + if (_set_write_enable() != 0) { + tr_error("Write Enabe failed"); + return -1; + } + + config_reg2 = 0x00; + + if (OSPI_STATUS_OK == _ospi_send_general_command(OSPIF_INST_WRCR2, OSPIF_CR2_OPI_EN_ADDR, &config_reg2, + 1, NULL, 0)) { + tr_debug(" Writing Config Register2 Success: value = 0x%x", config_reg2); + } else { + tr_error(" Writing Config Register2 Failed"); + return -1; + } + + // Configure BUS Mode to 1_1_1 + _ospi.configure_format(OSPI_CFG_BUS_SINGLE, OSPI_CFG_INST_SIZE_8, OSPI_CFG_BUS_SINGLE, OSPI_CFG_ADDR_SIZE_32, + OSPI_CFG_BUS_SINGLE, 0, OSPI_CFG_BUS_SINGLE, 0); + } + + _ospi.configure_format(OSPI_CFG_BUS_SINGLE, OSPI_CFG_INST_SIZE_8, OSPI_CFG_BUS_SINGLE, OSPI_CFG_ADDR_SIZE_32, + OSPI_CFG_BUS_SINGLE, 0, OSPI_CFG_BUS_SINGLE, 0); + // Write new Status Register Setup + if (_set_write_enable() != 0) { + tr_error("Write Enabe failed"); + return -1; + } + + config_reg2 = 0x01; + + if (OSPI_STATUS_OK == _ospi_send_general_command(OSPIF_INST_WRCR2, OSPIF_CR2_OPI_EN_ADDR, &config_reg2, + 1, NULL, 0)) { + tr_debug("OPI mode enable - Writing Config Register2 Success: value = 0x%x", config_reg2); + } else { + tr_error("OPI mode enable - Writing Config Register2 failed"); + return -1; + } + + _read_instruction = DTROSPIF_INST_READ_OCTA_STR; + _dummy_cycles = 20; + + _inst_width = OSPI_CFG_BUS_OCTA; + _inst_size = OSPI_CFG_INST_SIZE_16; + _address_width = OSPI_CFG_BUS_OCTA; + _address_size = OSPI_CFG_ADDR_SIZE_32; + _data_width = OSPI_CFG_BUS_OCTA; + + _ospi.configure_format(_inst_width, _inst_size, _address_width, _address_size, OSPI_CFG_BUS_SINGLE, + 0, _data_width, 0); + } else if (mode == DOPI) { + if ((_read_instruction != OSPIF_INST_READ_4B) && (_read_instruction != OSPIF_INST_READ_DEFAULT)) {//chang mode from SOPI to SPI + // Write new Status Register Setup + if (_set_write_enable() != 0) { + tr_error("Write Enabe failed"); + return -1; + } + + config_reg2 = 0x00; + + if (OSPI_STATUS_OK == _ospi_send_general_command(OSPIF_INST_WRCR2, OSPIF_CR2_OPI_EN_ADDR, &config_reg2, + 1, NULL, 0)) { + tr_debug("OPI mode enable - Writing Config Register2 Success: value = 0x%x", config_reg2); + } else { + tr_error("OPI mode enable - Writing Config Register2 failed"); + return -1; + } + + // Configure BUS Mode to 1_1_1 + _ospi.configure_format(OSPI_CFG_BUS_SINGLE, OSPI_CFG_INST_SIZE_8, OSPI_CFG_BUS_SINGLE, OSPI_CFG_ADDR_SIZE_32, + OSPI_CFG_BUS_SINGLE, 0, OSPI_CFG_BUS_SINGLE, 0); + } + + _ospi.configure_format(OSPI_CFG_BUS_SINGLE, OSPI_CFG_INST_SIZE_8, OSPI_CFG_BUS_SINGLE, OSPI_CFG_ADDR_SIZE_32, + OSPI_CFG_BUS_SINGLE, 0, OSPI_CFG_BUS_SINGLE, 0); + // Write new Status Register Setup + if (_set_write_enable() != 0) { + tr_error("Write Enabe failed"); + return -1; + } + + config_reg2 = 0x02; + + if (OSPI_STATUS_OK == _ospi_send_general_command(OSPIF_INST_WRCR2, OSPIF_CR2_OPI_EN_ADDR, &config_reg2, + 1, NULL, 0)) { + tr_debug("OPI mode enable - Writing Config Register2 Success: value = 0x%x", config_reg2); + } else { + tr_error("OPI mode enable - Writing Config Register2 failed"); + return -1; + } + + _read_instruction = DTROSPIF_INST_READ_OCTA_DTR; + _dummy_cycles = 20; + + _inst_width = OSPI_CFG_BUS_OCTA_DTR; + _inst_size = OSPI_CFG_INST_SIZE_16; + _address_width = OSPI_CFG_BUS_OCTA_DTR; + _address_size = OSPI_CFG_ADDR_SIZE_32; + _data_width = OSPI_CFG_BUS_OCTA_DTR; + + _ospi.configure_format(_inst_width, _inst_size, _address_width, _address_size, OSPI_CFG_BUS_SINGLE, + 0, _data_width, 0); + } else if (mode == SPI) { + // Write new Status Register Setup + if (_set_write_enable() != 0) { + tr_error("Write Enabe failed"); + return -1; + } + + config_reg2 = 0x00; + + if (OSPI_STATUS_OK == _ospi_send_general_command(OSPIF_INST_WRCR2, OSPIF_CR2_OPI_EN_ADDR, &config_reg2, + 1, NULL, 0)) { + tr_debug("OPI mode enable - Writing Config Register2 Success: value = 0x%x", config_reg2); + } else { + tr_error("OPI mode enable - Writing Config Register2 failed"); + return -1; + } + _read_instruction = OSPIF_INST_READ_4B; + _dummy_cycles = 0; + + _inst_width = OSPI_CFG_BUS_SINGLE; + _inst_size = OSPI_CFG_INST_SIZE_8; + _address_width = OSPI_CFG_BUS_SINGLE; + _address_size = OSPI_CFG_ADDR_SIZE_32; + _data_width = OSPI_CFG_BUS_SINGLE; + + _ospi.configure_format(_inst_width, _inst_size, _address_width, _address_size, OSPI_CFG_BUS_SINGLE, + 0, _data_width, _dummy_cycles); + } + return status; +} + + +/********************************/ +/* Different Device Csel Mgmt */ +/********************************/ +static PinName *generate_initialized_active_ospif_csel_arr() +{ + PinName *init_arr = new PinName[OSPIF_MAX_ACTIVE_FLASH_DEVICES]; + for (int i_ind = 0; i_ind < OSPIF_MAX_ACTIVE_FLASH_DEVICES; i_ind++) { + init_arr[i_ind] = NC; + } + return init_arr; +} + +int OSPIFBlockDevice::add_new_csel_instance(PinName csel) +{ + int status = 0; + _devices_mutex->lock(); + if (_number_of_active_ospif_flash_csel >= OSPIF_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 < OSPIF_MAX_ACTIVE_FLASH_DEVICES; i_ind++) { + if (_active_ospif_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 < OSPIF_MAX_ACTIVE_FLASH_DEVICES; i_ind++) { + if (_active_ospif_flash_csel_arr[i_ind] == NC) { + _active_ospif_flash_csel_arr[i_ind] = csel; + break; + } + } + _number_of_active_ospif_flash_csel++; + +exit_point: + _devices_mutex->unlock(); + return status; +} + +int OSPIFBlockDevice::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 < OSPIF_MAX_ACTIVE_FLASH_DEVICES; i_ind++) { + if (_active_ospif_flash_csel_arr[i_ind] == csel) { + _active_ospif_flash_csel_arr[i_ind] = NC; + if (_number_of_active_ospif_flash_csel > 0) { + _number_of_active_ospif_flash_csel--; + } + status = 0; + break; + } + } + _devices_mutex->unlock(); + return status; +} + +/*********************************************************/ +/********** SFDP Parsing and Detection Functions *********/ +/*********************************************************/ +int OSPIFBlockDevice::_sfdp_parse_basic_param_table(Callback sfdp_reader, + sfdp_hdr_info &sfdp_info) +{ + uint8_t param_table[SFDP_BASIC_PARAMS_TBL_SIZE]; /* Up To 20 DWORDS = 80 Bytes */ + + int status = sfdp_reader(sfdp_info.bptbl.addr, param_table, sfdp_info.bptbl.size); + if (status != OSPI_STATUS_OK) { + tr_error("Init - Read SFDP First Table Failed"); + return -1; + } + + // Check that density is not greater than 4 gigabits (i.e. that addressing beyond 4 bytes is not required) + if (sfdp_detect_addressability(param_table, _sfdp_info.bptbl) < 0) { + tr_error("Verify 4byte addressing failed"); + return -1; + } + + if (sfdp_detect_device_density(param_table, _sfdp_info.bptbl) < 0) { + tr_error("Detecting device density failed"); + return -1; + } + + // Set Page Size (OSPI write must be done on Page limits) + _page_size_bytes = sfdp_detect_page_size(param_table, sfdp_info.bptbl.size); + + if (_sfdp_detect_reset_protocol_and_reset(param_table) != OSPIF_BD_ERROR_OK) { + tr_error("Init - Detecting reset protocol/resetting failed"); + return -1; + } + + // Detect and Set Erase Types + bool shouldSetQuadEnable = false; + bool is_qpi_mode = false; + bool is_opi_mode = false; + + if (sfdp_detect_erase_types_inst_and_size(param_table, _sfdp_info) < 0) { + tr_error("Init - Detecting erase types instructions/sizes failed"); + return -1; + } + + // Detect and Set fastest Bus mode (default 1-1-1) + _sfdp_detect_best_bus_read_mode(param_table, sfdp_info.bptbl.size, shouldSetQuadEnable, is_qpi_mode, is_opi_mode); + if (true == is_opi_mode) { + change_mode(DOPI); + tr_debug("Init - Setting DOPI mode"); + } + if (true == shouldSetQuadEnable) { + if (_needs_fast_mode) { + _enable_fast_mode(); + } + // Set Quad Enable and QPI Bus modes if Supported + tr_debug("Init - Setting Quad Enable"); + if (0 != _sfdp_set_quad_enabled(param_table)) { + tr_error("Device supports Quad bus, but Quad Enable Failed"); + return -1; + } + if (true == is_qpi_mode) { + tr_debug("Init - Setting QPI mode"); + _sfdp_set_qpi_enabled(param_table); + } + } + + if (false == _is_mem_ready()) { + tr_error("Init - _is_mem_ready Failed"); + return -1; + } + + return 0; +} + +int OSPIFBlockDevice::_sfdp_set_quad_enabled(uint8_t *basic_param_table_ptr) +{ + uint8_t status_reg_setup[OSPI_MAX_STATUS_REGISTERS] = {0}; + uint8_t status_regs[OSPI_MAX_STATUS_REGISTERS] = {0}; + + // QUAD Enable procedure is specified by 3 bits + uint8_t qer_value = (basic_param_table_ptr[OSPIF_BASIC_PARAM_TABLE_QER_BYTE] & 0x70) >> 4; + + switch (qer_value) { + case 0: + tr_debug("Device Does not Have a QE Bit, continue based on Read Inst"); + return 0; + case 1: + case 4: + // Bit 1 of Status Reg 2 + _quad_enable_register_idx = 1; + _quad_enable_bit = 1; + tr_debug("Setting QE Bit, Bit 1 of Status Reg 2"); + break; + case 2: + // Bit 6 of Status Reg 1 + _quad_enable_register_idx = 0; + _quad_enable_bit = 6; + tr_debug("Setting QE Bit, Bit 6 of Status Reg 1"); + break; + case 3: + // Bit 7 of Status Reg 1 + _quad_enable_register_idx = 0; + _quad_enable_bit = 7; + _write_status_reg_2_inst = 0x3E; + _read_status_reg_2_inst = 0x3F; + tr_debug("Setting QE Bit, Bit 7 of Status Reg 1"); + break; + case 5: + // Bit 1 of status Reg 2 + _quad_enable_register_idx = 1; + _quad_enable_bit = 1; + tr_debug("Setting QE Bit, Bit 1 of Status Reg 2"); + break; + default: + tr_warning("Unsupported QER configuration"); + return 0; + } + + if (_quad_enable_register_idx != OSPIF_NO_QUAD_ENABLE && _quad_enable_bit != OSPIF_NO_QUAD_ENABLE) { + status_reg_setup[_quad_enable_register_idx] = 1 << _quad_enable_bit; + } + + // Read existing status register values + _ospi_read_status_registers(status_regs); + + // Set Bits for Quad Enable + for (int i = 0; i < OSPI_MAX_STATUS_REGISTERS; i++) { + status_regs[i] |= status_reg_setup[i]; + } + + // Write new Status Register Setup + _ospi_write_status_registers(status_regs); + + if (false == _is_mem_ready()) { + tr_error("Device not ready after write, failed"); + return -1; + } + + // For Debug + memset(status_regs, 0, OSPI_MAX_STATUS_REGISTERS); + _ospi_read_status_registers(status_regs); + if (((status_regs[0] & status_reg_setup[0]) | (status_regs[1] & status_reg_setup[1])) == 0) { + tr_error("Status register not set correctly"); + return -1; + } + + return 0; +} + +int OSPIFBlockDevice::_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[OSPIF_BASIC_PARAM_TABLE_444_MODE_EN_SEQ_BYTE] & 0xF0) >> 4) | (( + basic_param_table_ptr[OSPIF_BASIC_PARAM_TABLE_444_MODE_EN_SEQ_BYTE + 1] & 0x01) << 4)); + + switch (en_seq_444_value) { + case 1: + case 2: + tr_debug("_sfdp_set_qpi_enabled - send command 38h"); + if (OSPI_STATUS_OK != _ospi_send_general_command(0x38, OSPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0)) { + tr_error("_sfdp_set_qpi_enabled - send command 38h Failed"); + } + break; + + case 4: + tr_debug("_sfdp_set_qpi_enabled - send command 35h"); + if (OSPI_STATUS_OK != _ospi_send_general_command(0x35, OSPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0)) { + tr_error("_sfdp_set_qpi_enabled - send command 35h Failed"); + } + break; + + case 8: + tr_debug("_sfdp_set_qpi_enabled - set config bit 6 and send command 71h"); + if (OSPI_STATUS_OK != _ospi_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 + if (OSPI_STATUS_OK != _ospi_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("_sfdp_set_qpi_enabled - reset config bits 0-7 and send command 61h"); + if (OSPI_STATUS_OK != _ospi_send_general_command(0x65, OSPI_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 + if (OSPI_STATUS_OK != _ospi_send_general_command(0x61, OSPI_NO_ADDRESS_COMMAND, NULL, 0, (char *)config_reg, 1)) { + tr_error("_sfdp_set_qpi_enabled - send command 61 Failed"); + } + break; + + default: + tr_warning("_sfdp_set_qpi_enabled - Unsupported En Seq 444 configuration"); + break; + } + return 0; +} + +int OSPIFBlockDevice::_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, bool &is_opi_mode) +{ + set_quad_enable = false; + is_qpi_mode = false; + is_opi_mode = false; + uint8_t examined_byte; + + do { // compound statement is the loop body + if (basic_param_table_size > OSPIF_BASIC_PARAM_TABLE_OPI_READ_SUPPORT_BYTE) { + examined_byte = basic_param_table_ptr[OSPIF_BASIC_PARAM_TABLE_OPI_READ_SUPPORT_BYTE]; + tr_debug("\r\n examined_byte, : 0x%xh", examined_byte); + if (examined_byte & 0x01) { + // OPI 8-8-8 Supported + is_opi_mode = true; + _dummy_cycles = (basic_param_table_ptr[OSPIF_BASIC_PARAM_TABLE_888_READ_INST_BYTE - 1] >> 5) + + (basic_param_table_ptr[OSPIF_BASIC_PARAM_TABLE_888_READ_INST_BYTE - 1] & 0x1F); + tr_debug("Read Bus Mode set to 8-8-8, Instruction: 0x%xh", _read_instruction); + break; + } + } + + if (basic_param_table_size > OSPIF_BASIC_PARAM_TABLE_QPI_READ_SUPPORT_BYTE) { + examined_byte = basic_param_table_ptr[OSPIF_BASIC_PARAM_TABLE_QPI_READ_SUPPORT_BYTE]; + if (examined_byte & 0x10) { + // QPI 4-4-4 Supported + _read_instruction = basic_param_table_ptr[OSPIF_BASIC_PARAM_TABLE_444_READ_INST_BYTE]; + set_quad_enable = true; + is_qpi_mode = true; + _dummy_cycles = basic_param_table_ptr[OSPIF_BASIC_PARAM_TABLE_444_READ_INST_BYTE - 1] & 0x1F; + uint8_t mode_cycles = basic_param_table_ptr[OSPIF_BASIC_PARAM_TABLE_444_READ_INST_BYTE - 1] >> 5; + _alt_size = mode_cycles * 4; + tr_debug("Read Bus Mode set to 4-4-4, Instruction: 0x%xh", _read_instruction); + _address_width = OSPI_CFG_BUS_QUAD; + _data_width = OSPI_CFG_BUS_QUAD; + break; + } + } + + examined_byte = basic_param_table_ptr[OSPIF_BASIC_PARAM_TABLE_FAST_READ_SUPPORT_BYTE]; + if (examined_byte & 0x20) { + // Fast Read 1-4-4 Supported + _read_instruction = basic_param_table_ptr[OSPIF_BASIC_PARAM_TABLE_144_READ_INST_BYTE]; + set_quad_enable = true; + _dummy_cycles = basic_param_table_ptr[OSPIF_BASIC_PARAM_TABLE_144_READ_INST_BYTE - 1] & 0x1F; + uint8_t mode_cycles = basic_param_table_ptr[OSPIF_BASIC_PARAM_TABLE_144_READ_INST_BYTE - 1] >> 5; + _alt_size = mode_cycles * 4; + _address_width = OSPI_CFG_BUS_QUAD; + _data_width = OSPI_CFG_BUS_QUAD; + tr_debug("Read Bus Mode set to 1-4-4, Instruction: 0x%xh", _read_instruction); + break; + } + + if (examined_byte & 0x40) { + // Fast Read 1-1-4 Supported + _read_instruction = basic_param_table_ptr[OSPIF_BASIC_PARAM_TABLE_114_READ_INST_BYTE]; + set_quad_enable = true; + _dummy_cycles = basic_param_table_ptr[OSPIF_BASIC_PARAM_TABLE_114_READ_INST_BYTE - 1] & 0x1F; + uint8_t mode_cycles = basic_param_table_ptr[OSPIF_BASIC_PARAM_TABLE_114_READ_INST_BYTE - 1] >> 5; + _alt_size = mode_cycles; + _data_width = OSPI_CFG_BUS_QUAD; + tr_debug("Read Bus Mode set to 1-1-4, Instruction: 0x%xh", _read_instruction); + break; + } + examined_byte = basic_param_table_ptr[OSPIF_BASIC_PARAM_TABLE_QPI_READ_SUPPORT_BYTE]; + if (examined_byte & 0x01) { + // Fast Read 2-2-2 Supported + _read_instruction = basic_param_table_ptr[OSPIF_BASIC_PARAM_TABLE_222_READ_INST_BYTE]; + _dummy_cycles = basic_param_table_ptr[OSPIF_BASIC_PARAM_TABLE_222_READ_INST_BYTE - 1] & 0x1F; + uint8_t mode_cycles = basic_param_table_ptr[OSPIF_BASIC_PARAM_TABLE_222_READ_INST_BYTE - 1] >> 5; + _alt_size = mode_cycles * 2; + _address_width = OSPI_CFG_BUS_DUAL; + _data_width = OSPI_CFG_BUS_DUAL; + tr_debug("Read Bus Mode set to 2-2-2, Instruction: 0x%xh", _read_instruction); + break; + } + + examined_byte = basic_param_table_ptr[OSPIF_BASIC_PARAM_TABLE_FAST_READ_SUPPORT_BYTE]; + if (examined_byte & 0x10) { + // Fast Read 1-2-2 Supported + _read_instruction = basic_param_table_ptr[OSPIF_BASIC_PARAM_TABLE_122_READ_INST_BYTE]; + _dummy_cycles = basic_param_table_ptr[OSPIF_BASIC_PARAM_TABLE_122_READ_INST_BYTE - 1] & 0x1F; + uint8_t mode_cycles = basic_param_table_ptr[OSPIF_BASIC_PARAM_TABLE_122_READ_INST_BYTE - 1] >> 5; + _alt_size = mode_cycles * 2; + _address_width = OSPI_CFG_BUS_DUAL; + _data_width = OSPI_CFG_BUS_DUAL; + tr_debug("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_instruction = basic_param_table_ptr[OSPIF_BASIC_PARAM_TABLE_112_READ_INST_BYTE]; + _dummy_cycles = basic_param_table_ptr[OSPIF_BASIC_PARAM_TABLE_112_READ_INST_BYTE - 1] & 0x1F; + uint8_t mode_cycles = basic_param_table_ptr[OSPIF_BASIC_PARAM_TABLE_112_READ_INST_BYTE - 1] >> 5; + _alt_size = mode_cycles; + _data_width = OSPI_CFG_BUS_DUAL; + tr_debug("Read Bus Mode set to 1-1-2, Instruction: 0x%xh", _read_instruction); + break; + } + _read_instruction = OSPIF_INST_READ_DEFAULT; + tr_debug("Read Bus Mode set to 1-1-1, Instruction: 0x%xh", _read_instruction); + } while (false); + + return 0; +} + +int OSPIFBlockDevice::_sfdp_detect_and_enable_4byte_addressing(uint8_t *basic_param_table_ptr, int basic_param_table_size) +{ + int status = OSPIF_BD_ERROR_OK; + ospi_status_t ospi_status = OSPI_STATUS_OK; + + // Always enable 4-byte addressing if possible + if (basic_param_table_size > OSPIF_BASIC_PARAM_TABLE_4BYTE_ADDR_BYTE) { + uint8_t examined_byte = basic_param_table_ptr[OSPIF_BASIC_PARAM_TABLE_4BYTE_ADDR_BYTE]; + + if (examined_byte & FOURBYTE_ADDR_ALWAYS_BITMASK) { + // No need to do anything if 4-byte addressing is always enabled + _address_size = OSPI_CFG_ADDR_SIZE_32; + } else if (examined_byte & FOURBYTE_ADDR_B7_BITMASK) { + // Issue instruction B7h to enable 4-byte addressing + ospi_status = _ospi_send_general_command(0xB7, OSPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0); + status = (ospi_status == OSPI_STATUS_OK) ? OSPIF_BD_ERROR_OK : OSPIF_BD_ERROR_PARSING_FAILED; + if (status == OSPIF_BD_ERROR_OK) { + _address_size = OSPI_CFG_ADDR_SIZE_32; + } + } else if (examined_byte & FOURBYTE_ADDR_B7_WREN_BITMASK) { + // Issue WREN and then instruction B7h to enable 4-byte addressing + if (_set_write_enable() == 0) { + ospi_status = _ospi_send_general_command(0xB7, OSPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0); + status = (ospi_status == OSPI_STATUS_OK) ? OSPIF_BD_ERROR_OK : OSPIF_BD_ERROR_PARSING_FAILED; + + if (status == OSPIF_BD_ERROR_OK) { + _address_size = OSPI_CFG_ADDR_SIZE_32; + } + } else { + tr_error("Write enable failed"); + status = OSPIF_BD_ERROR_WREN_FAILED; + } + } else if (examined_byte & FOURBYTE_ADDR_CONF_REG_BITMASK) { + // Write 1 to bit 0 of a configuration register to enable 4-byte addressing + // Write to register with instruction B1h, read from register with instruction B5h + uint8_t conf_register = 0; + ospi_status = _ospi_send_general_command(0xB5, OSPI_NO_ADDRESS_COMMAND, NULL, 0, (char *) &conf_register, 1); + status = (ospi_status == OSPI_STATUS_OK) ? OSPIF_BD_ERROR_OK : OSPIF_BD_ERROR_PARSING_FAILED; + + if (status == OSPIF_BD_ERROR_OK) { + conf_register |= 0b00000001; + if (_set_write_enable() == 0) { + ospi_status_t ospi_status = _ospi_send_general_command(0xB1, OSPI_NO_ADDRESS_COMMAND, (char *) &conf_register, 1, NULL, 0); + status = (ospi_status == OSPI_STATUS_OK) ? OSPIF_BD_ERROR_OK : OSPIF_BD_ERROR_PARSING_FAILED; + + if (status == OSPIF_BD_ERROR_OK) { + _address_size = OSPI_CFG_ADDR_SIZE_32; + } + } else { + tr_error("Write enable failed"); + status = OSPIF_BD_ERROR_WREN_FAILED; + } + } + } else if (examined_byte & FOURBYTE_ADDR_BANK_REG_BITMASK) { + // Write 1 to bit 7 of a bank register to enable 4-byte addressing + // Write to register with instruction 17h, read from register with instruction 16h + uint8_t to_write = 0b10000000; + ospi_status = _ospi_send_general_command(0x17, OSPI_NO_ADDRESS_COMMAND, (char *) &to_write, 1, NULL, 0); + status = (ospi_status == OSPI_STATUS_OK) ? OSPIF_BD_ERROR_OK : OSPIF_BD_ERROR_PARSING_FAILED; + if (status == OSPIF_BD_ERROR_OK) { + _address_size = OSPI_CFG_ADDR_SIZE_32; + } + } else if (examined_byte & FOURBYTE_ADDR_EXT_ADDR_REG_BITMASK) { + // Extended address register stores most significant byte of a 4-byte address + // Instructions are sent with the lower 3 bytes of the address + // Write to register with instruction C5h, read from register with instruction C8h + _4byte_msb_reg_write_inst = 0xC5; + _address_size = OSPI_CFG_ADDR_SIZE_24; + } else if (_support_4_byte_inst == true) { + // Extended address register stores most significant byte of a 4-byte address + _address_size = OSPI_CFG_ADDR_SIZE_32; + switch (_read_instruction) { + case OSPIF_INST_READ_DEFAULT: + _read_instruction = OSPIF_INST_READ_4B; + break; + case 0xc2: + break; + } + _prog_instruction = OSPIF_INST_PROG_4B; + } else { + // Either part specific instructions are required to use 4-byte addressing or it isn't supported, so use 3-byte addressing instead + tr_debug("_sfdp_detect_and_enable_4byte_addressing - 4-byte addressing not supported, falling back to 3-byte addressing"); + _address_size = OSPI_CFG_ADDR_SIZE_24; + } + + if (_address_size == OSPI_CFG_ADDR_SIZE_32) { + // Update 1-1-1 format to match new address size + if (OSPI_STATUS_OK != _ospi.configure_format(OSPI_CFG_BUS_SINGLE, OSPI_CFG_INST_SIZE_8, OSPI_CFG_BUS_SINGLE, _address_size, OSPI_CFG_BUS_SINGLE, + 0, OSPI_CFG_BUS_SINGLE, 0)) { + tr_error("_ospi_configure_format failed"); + status = OSPIF_BD_ERROR_DEVICE_ERROR; + } + } + } + + return status; +} + +#if MBED_CONF_OSPIF_ENABLE_AND_RESET && MBED_CONF_OSPIF_DIRECT_RESET +#error "ospif.enable-and-reset and ospif.direct-reset cannot be both true!" +#endif + +#define RESET_SEQUENCE_FROM_SFDP ( !MBED_CONF_OSPIF_ENABLE_AND_RESET && !MBED_CONF_OSPIF_DIRECT_RESET ) + + +int OSPIFBlockDevice::_sfdp_detect_reset_protocol_and_reset(uint8_t *basic_param_table_ptr) +{ + int status = OSPIF_BD_ERROR_OK; + +#if RESET_SEQUENCE_FROM_SFDP + uint8_t examined_byte = basic_param_table_ptr[OSPIF_BASIC_PARAM_TABLE_SOFT_RESET_BYTE]; + + // Ignore bit indicating need to exit 0-4-4 mode - should not enter 0-4-4 mode from OSPIFBlockDevice + if (examined_byte & SOFT_RESET_RESET_INST_BITMASK) { +#endif + +#if !MBED_CONF_OSPIF_ENABLE_AND_RESET // i.e. direct reset, or determined from SFDP + // Issue instruction 0xF0 to reset the device + ospi_status_t ospi_status = _ospi_send_general_command(0xF0, OSPI_NO_ADDRESS_COMMAND, // Send reset instruction + NULL, 0, NULL, 0); + status = (ospi_status == OSPI_STATUS_OK) ? OSPIF_BD_ERROR_OK : OSPIF_BD_ERROR_PARSING_FAILED; +#endif + +#if RESET_SEQUENCE_FROM_SFDP + } else if (examined_byte & SOFT_RESET_ENABLE_AND_RESET_INST_BITMASK) { +#endif + +#if !MBED_CONF_OSPIF_DIRECT_RESET // i.e. enable and reset, or determined from SFDP + // Issue instruction 66h to enable resets on the device + // Then issue instruction 99h to reset the device + ospi_status_t ospi_status = _ospi_send_general_command(0x66, OSPI_NO_ADDRESS_COMMAND, // Send reset enable instruction + NULL, 0, NULL, 0); + if (ospi_status == OSPI_STATUS_OK) { + ospi_status = _ospi_send_general_command(0x99, OSPI_NO_ADDRESS_COMMAND, // Send reset instruction + NULL, 0, NULL, 0); + } + status = (ospi_status == OSPI_STATUS_OK) ? OSPIF_BD_ERROR_OK : OSPIF_BD_ERROR_PARSING_FAILED; +#endif + +#if RESET_SEQUENCE_FROM_SFDP + } else { + // Soft reset either is not supported or requires direct control over data lines + tr_error("Failed to determine soft reset sequence. If your device has a legacy SFDP table, please manually set enable-and-reset or direct-reset."); + + status = OSPIF_BD_ERROR_PARSING_FAILED; + } +#endif + + if (status == OSPIF_BD_ERROR_OK) { + if (false == _is_mem_ready()) { + tr_error("Device not ready, reset failed"); + status = OSPIF_BD_ERROR_READY_FAILED; + } + } + + return status; +} + +int OSPIFBlockDevice::_sfdp_parse_4_byte_inst_table(Callback sfdp_reader, + sfdp_hdr_info &sfdp_info) +{ + uint8_t four_byte_inst_table[SFDP_DEFAULT_4_BYTE_INST_TABLE_SIZE_BYTES]; /* Up To 2 DWORDS = 8 Bytes */ + + int status = sfdp_reader(sfdp_info.fbatbl.addr, four_byte_inst_table, sfdp_info.fbatbl.size); + if (status != OSPI_STATUS_OK) { + tr_error("Init - Read SFDP Four Byte Inst Table Failed"); + return -1; + } + + // Support 4-Byte address instructions + if (four_byte_inst_table[OSPIF_4_BYTE_INST_TABLE_FAST_READ_SUPPORT_BYTE] & FOURBYTE_INST_1_1_1_READ_BITMASK) { + _support_4_byte_inst = true; + } + + // Detect erase types 4-Byte instructions + if (sfdp_info.fbatbl.size > OSPIF_4_BYTE_INST_TABLE_INST_ERASE_TYPE_1_BYTE) { + // Loop Erase Types 1-4 + for (int i_ind = 0; i_ind < 4; i_ind++) { + sfdp_info.fbatbl.erase_type_4_byte_inst_arr[i_ind] = OSPI_NO_INST; // Default for unsupported type + if (four_byte_inst_table[OSPIF_4_BYTE_INST_TABLE_ERASE_SUPPORT_BYTE] & (FOURBYTE_INST_ERASE_TYPE_1_BITMASK << i_ind)) { + sfdp_info.fbatbl.erase_type_4_byte_inst_arr[i_ind] = four_byte_inst_table[OSPIF_4_BYTE_INST_TABLE_INST_ERASE_TYPE_1_BYTE + i_ind]; + } + tr_debug("Erase Type %d - Inst: 0x%xh", (i_ind + 1), sfdp_info.fbatbl.erase_type_4_byte_inst_arr[i_ind]); + } + } + + return 0; +} + +int OSPIFBlockDevice::_handle_vendor_quirks() +{ + uint8_t vendor_device_ids[OSPI_RDID_DATA_LENGTH] = {0}; + /* Read Manufacturer ID (1byte), and Device ID (2bytes) */ + ospi_status_t status = _ospi_send_general_command(OSPIF_INST_RDID, OSPI_NO_ADDRESS_COMMAND, + NULL, 0, + (char *) vendor_device_ids, OSPI_RDID_DATA_LENGTH); + if (OSPI_STATUS_OK != status) { + tr_error("Read Vendor ID Failed"); + return -1; + } + + tr_debug("Vendor device ID = 0x%x 0x%x 0x%x", vendor_device_ids[0], vendor_device_ids[1], vendor_device_ids[2]); + + switch (vendor_device_ids[0]) { + case 0xbf: + // SST devices come preset with block protection + // enabled for some regions, issue global protection unlock to clear + tr_debug("Applying quirks for SST"); + _clear_protection_method = OSPIF_BP_ULBPR; + break; + case 0xc2: + // Macronix devices have several quirks: + // 1. Have one status register and 2 config registers, with a nonstandard instruction for reading the config registers + // 2. Require setting a "fast mode" bit in config register 2 to operate at higher clock rates + // 3. Should never attempt to enable 4-byte addressing (it causes reads and writes to fail) + tr_debug("Applying quirks for macronix"); + _needs_fast_mode = true; + _num_status_registers = 3; + _read_status_reg_2_inst = OSPIF_INST_RDCR; + break; + } + + return 0; +} + +int OSPIFBlockDevice::_clear_block_protection() +{ + uint8_t status_regs[OSPI_MAX_STATUS_REGISTERS] = {0}; + + if (false == _is_mem_ready()) { + tr_error("Device not ready, clearing block protection failed"); + return -1; + } + ospi_status_t status; + switch (_clear_protection_method) { + case OSPIF_BP_ULBPR: + tr_debug("Clearing block protection via ULBPR"); + // SST devices come preset with block protection + // enabled for some regions, issue global protection unlock to clear + if (0 != _set_write_enable()) { + tr_error("Write enable failed"); + return -1; + } + status = _ospi_send_general_command(OSPIF_INST_ULBPR, OSPI_NO_ADDRESS_COMMAND, NULL, 0, NULL, 0); + if (OSPI_STATUS_OK != status) { + tr_error("Global block protection unlock failed"); + return -1; + } + break; + case OSPIF_BP_CLEAR_SR: + // For all other devices, to clear the block protection bits clear all bits + // in status register 1 that aren't the WIP or WEL bits, or the QE bit (if it is in SR 1) + tr_debug("Clearing block protection via status register protection bits"); + status = _ospi_read_status_registers(status_regs); + if (OSPI_STATUS_OK != status) { + tr_error("_clear_block_protection - Status register read failed"); + return -1; + } + uint8_t status_mask = (OSPIF_STATUS_BIT_WIP | OSPIF_STATUS_BIT_WEL); + if (_quad_enable_register_idx == 0) { + status_mask |= 1 << _quad_enable_bit; + } + status_regs[0] &= status_mask; + status = _ospi_write_status_registers(status_regs); + if (OSPI_STATUS_OK != status) { + tr_error("__clear_block_protection - Status register write failed"); + return -1; + } + break; + } + + if (false == _is_mem_ready()) { + tr_error("Device not ready, clearing block protection failed"); + return -1; + } + + return 0; +} + +int OSPIFBlockDevice::_set_write_enable() +{ + // Check Status Register Busy Bit to Verify the Device isn't Busy + uint16_t status_value = 0; + int status = -1; + + do { + if (OSPI_STATUS_OK != _ospi_send_general_command(OSPIF_INST_WREN, OSPI_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; + } + //the number of read byte need to be even in octa flash DOPI mode + if (OSPI_STATUS_OK != _ospi_send_general_command(OSPIF_INST_RSR1, OSPI_NO_ADDRESS_COMMAND, + NULL, 0, + (char *) &status_value, 2)) { + tr_error("Reading Status Register 1 failed"); + break; + } + + if ((status_value & OSPIF_STATUS_BIT_WEL) == 0) { + tr_error("_set_write_enable failed - status register 1 value: %u", status_value); + break; + } + + status = 0; + } while (false); + + return status; +} + +int OSPIFBlockDevice::_enable_fast_mode() +{ + tr_debug("enabling fast mode"); + MBED_ASSERT(_num_status_registers == 3); // Make sure the register for fast mode enable exists + uint8_t status_reg[OSPI_MAX_STATUS_REGISTERS] = {0}; + + // Bit 1 of config reg 2 (aka "status register 3" in our generic register representation) + const int QER_REG_IDX = 2; + const int QER_REG_VALUE = 0x2; + + // Configure BUS Mode to 1_1_1 for all commands other than Read + if (OSPI_STATUS_OK != _ospi.configure_format(OSPI_CFG_BUS_SINGLE, OSPI_CFG_INST_SIZE_8, OSPI_CFG_BUS_SINGLE, OSPI_CFG_ADDR_SIZE_24, OSPI_CFG_BUS_SINGLE, + 0, OSPI_CFG_BUS_SINGLE, 0)) { + tr_error("_ospi_configure_format failed"); + return -1; + + } + + if (OSPI_STATUS_OK == _ospi_read_status_registers(status_reg)) { + tr_debug("Reading Config Register Success: value = 0x%x", status_reg[2]); + } else { + tr_error("Reading Config Register failed"); + return -1; + } + + // Set Bits for Quad Enable + status_reg[QER_REG_IDX] |= QER_REG_VALUE; + + // Write new Status Register Setup + if (_set_write_enable() != 0) { + tr_error("Write Enable failed"); + return -1; + } + + if (OSPI_STATUS_OK == _ospi_write_status_registers(status_reg)) { + 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, OSPI_MAX_STATUS_REGISTERS); + if (OSPI_STATUS_OK == _ospi_read_status_registers(status_reg)) { + tr_debug("Verifying Register Success: status = 0x%x config 1 = 0x%x config 2 = 0x%x", (int)status_reg[0], (int)status_reg[1], (int)status_reg[2]); + } else { + tr_error("Verifying Config Register failed"); + return -1; + } + + return 0; +} + +bool OSPIFBlockDevice::_is_mem_ready() +{ + // Check Status Register Busy Bit to Verify the Device isn't Busy + uint16_t status_value = 0; + int retries = 0; + bool mem_ready = true; + + do { + rtos::ThisThread::sleep_for(1); + retries++; + //Read Status Register 1 from device, the number of read byte need to be even in octa flash DOPI mode + if (OSPI_STATUS_OK != _ospi_send_general_command(OSPIF_INST_RSR1, OSPI_NO_ADDRESS_COMMAND, + NULL, 0, + (char *) &status_value, OSPI_DEFAULT_STATUS_REGISTERS)) { // store received value in status_value + tr_error("Reading Status Register failed"); + } + } while ((status_value & OSPIF_STATUS_BIT_WIP) != 0 && retries < IS_MEM_READY_MAX_RETRIES); + + if ((status_value & OSPIF_STATUS_BIT_WIP) != 0) { + tr_error("_is_mem_ready FALSE: status value = 0x%x ", status_value); + mem_ready = false; + } + return mem_ready; +} + +/***************************************************/ +/*********** OSPI Driver API Functions *************/ +/***************************************************/ +ospi_status_t OSPIFBlockDevice::_ospi_set_frequency(int freq) +{ + return _ospi.set_frequency(freq); +} + +ospi_status_t OSPIFBlockDevice::_ospi_update_4byte_ext_addr_reg(bd_addr_t addr) +{ + ospi_status_t status = OSPI_STATUS_OK; + // Only update register if in the extended address register mode + if (_4byte_msb_reg_write_inst != OSPI_NO_INST) { + // Set register to the most significant byte of the address + uint8_t most_significant_byte = addr >> 24; + if (_set_write_enable() == 0) { + status = _ospi.command_transfer(_4byte_msb_reg_write_inst, (int) OSPI_NO_ADDRESS_COMMAND, + (char *) &most_significant_byte, 1, + NULL, 0); + } else { + tr_error("Write enable failed"); + status = OSPI_STATUS_ERROR; + } + } else if ((_address_size != OSPI_CFG_ADDR_SIZE_32) && (addr != OSPI_NO_ADDRESS_COMMAND) && (addr >= (1 << 24))) { + tr_error("Attempted to use 4-byte address but 4-byte addressing is not supported"); + status = OSPI_STATUS_ERROR; + } + return status; +} + +ospi_status_t OSPIFBlockDevice::_ospi_send_read_command(ospi_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; + + ospi_status_t status = _ospi_update_4byte_ext_addr_reg(addr); + if (OSPI_STATUS_OK != status) { + tr_error("OSPI Read - Updating 4-byte addressing extended address register failed"); + return status; + } + + // Send read command to device driver + // Read commands use the best bus mode supported by the part + status = _ospi.configure_format(_inst_width, _inst_size, _address_width, _address_size, _address_width, // Alt width should be the same as address width + _alt_size, _data_width, _dummy_cycles); + if (OSPI_STATUS_OK != status) { + tr_error("_ospi_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 = _ospi.read(read_inst, (_alt_size == 0) ? -1 : OSPI_ALT_DEFAULT_VALUE, (unsigned int)addr, (char *)buffer, &buf_len); + + status = _ospi.configure_format(_inst_width, _inst_size, _address_width, _address_size, _address_width, // Alt width should be the same as address width + _alt_size, _data_width, 0); + if (OSPI_STATUS_OK != status) { + tr_error("_ospi_configure_format failed"); + return status; + } + + if (OSPI_STATUS_OK != status) { + tr_error("OSPI Read failed"); + return status; + } + + return OSPI_STATUS_OK; +} + +ospi_status_t OSPIFBlockDevice::_ospi_send_program_command(ospi_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); + + ospi_status_t status = _ospi_update_4byte_ext_addr_reg(addr); + if (OSPI_STATUS_OK != status) { + tr_error("OSPI Write - Updating 4-byte addressing extended address register failed"); + return status; + } + + // Send program (write) command to device driver + status = _ospi.write(prog_inst, -1, addr, (char *)buffer, (size_t *)size); + if (OSPI_STATUS_OK != status) { + tr_error("OSPI Write failed"); + return status; + } + + return OSPI_STATUS_OK; +} + +ospi_status_t OSPIFBlockDevice::_ospi_send_erase_command(ospi_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); + + ospi_status_t status = _ospi_update_4byte_ext_addr_reg(addr); + if (OSPI_STATUS_OK != status) { + tr_error("OSPI Erase - Updating 4-byte addressing extended address register failed"); + return status; + } + + // Send erase command to driver + status = _ospi.command_transfer(erase_inst, (int) addr, NULL, 0, NULL, 0); + + if (OSPI_STATUS_OK != status) { + tr_error("OSPI Erase failed"); + return status; + } + + return OSPI_STATUS_OK; +} + +ospi_status_t OSPIFBlockDevice::_ospi_send_general_command(ospi_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); + + ospi_status_t status = _ospi_update_4byte_ext_addr_reg(addr); + if (OSPI_STATUS_OK != status) { + tr_error("OSPI Generic command - Updating 4-byte addressing extended address register failed"); + return status; + } + + if ((_inst_width == OSPI_CFG_BUS_OCTA) || (_inst_width == OSPI_CFG_BUS_OCTA_DTR)) { + if ((instruction == OSPIF_INST_RSR1) || (instruction == OSPIF_INST_RDID) || + (instruction == OSPIF_INST_RDCR2) || (instruction == OSPIF_INST_RDCR)) { + _ospi.configure_format(_inst_width, _inst_size, _address_width, _address_size, OSPI_CFG_BUS_SINGLE, + 0, _data_width, 4); + addr = 0; + } else if ((instruction == OSPIF_INST_WSR1)) { + addr = 0; + } + } + + // Send a general command instruction to driver + status = _ospi.command_transfer(instruction, (int)addr, tx_buffer, tx_length, rx_buffer, rx_length); + if (OSPI_STATUS_OK != status) { + tr_error("Sending Generic command: %x", instruction); + return status; + } + + if (((_inst_width == OSPI_CFG_BUS_OCTA) || (_inst_width == OSPI_CFG_BUS_OCTA_DTR)) && + ((instruction == OSPIF_INST_RSR1) || (instruction == OSPIF_INST_RDID) || + (instruction == OSPIF_INST_RDCR2) || (instruction == OSPIF_INST_RDCR))) { + _ospi.configure_format(_inst_width, _inst_size, _address_width, _address_size, OSPI_CFG_BUS_SINGLE, + 0, _data_width, 0); + } + + return OSPI_STATUS_OK; +} + +int OSPIFBlockDevice::_ospi_send_read_sfdp_command(bd_addr_t addr, void *rx_buffer, bd_size_t rx_length) +{ + size_t rx_len = rx_length; + uint8_t *rx_buffer_tmp = (uint8_t *)rx_buffer; + + // SFDP read instruction requires 1-1-1 bus mode with 8 dummy cycles and a 3-byte address + ospi_status_t status = _ospi.configure_format(OSPI_CFG_BUS_SINGLE, OSPI_CFG_INST_SIZE_8, OSPI_CFG_BUS_SINGLE, OSPI_CFG_ADDR_SIZE_24, OSPI_CFG_BUS_SINGLE, 0, OSPI_CFG_BUS_SINGLE, OSPIF_RSFDP_DUMMY_CYCLES); + if (OSPI_STATUS_OK != status) { + tr_error("_ospi_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 = _ospi.read(OSPIF_INST_RSFDP, -1, (unsigned int) addr, (char *) rx_buffer, &rx_len); + + ospi_status_t format_status = _ospi.configure_format(OSPI_CFG_BUS_SINGLE, OSPI_CFG_INST_SIZE_8, OSPI_CFG_BUS_SINGLE, _address_size, OSPI_CFG_BUS_SINGLE, 0, OSPI_CFG_BUS_SINGLE, 0); + // All commands other than Read and RSFDP use default 1-1-1 bus mode (Program/Erase are constrained by flash memory performance more than bus performance) + if (OSPI_STATUS_OK != format_status) { + tr_error("_ospi_configure_format failed"); + return format_status; + } + + if (OSPI_STATUS_OK != status) { + tr_error("Sending SFDP read instruction"); + return status; + } + + // This is a workaround, + // The sfdp parameter values in Macronix old octaflash(include the MX25LM51245G on L4R9I_DISCO) are all 0xFF, + // so we need to define the parameter values by software to support SFDP parsing. + // The code below can be removed when users test with the new flash. +#ifdef NEED_DEFINE_SFDP_PARA + if (!(memcmp(rx_buffer, "SFDP", 4) == 0)) { + if (addr < 30) { + for (uint8_t i_ind = 0; i_ind < rx_len; i_ind++) { + *(rx_buffer_tmp + i_ind) = _sfdp_head_table[i_ind + addr]; + } + } else if (addr == 0x30) { + for (uint8_t i_ind = 0; i_ind < rx_len; i_ind++) { + *(rx_buffer_tmp + i_ind) = _sfdp_basic_param_table[i_ind]; + } + } else if (addr == 0xc0) { + for (uint8_t i_ind = 0; i_ind < rx_len; i_ind++) { + *(rx_buffer_tmp + i_ind) = _sfdp_4_byte_inst_table[i_ind]; + } + } + } +#endif + + return OSPI_STATUS_OK; +} + +ospi_status_t OSPIFBlockDevice::_ospi_read_status_registers(uint8_t *reg_buffer) +{ + // Read Status Register 1, the number of read byte need to be even in octa flash DOPI mode + ospi_status_t status = _ospi_send_general_command(OSPIF_INST_RSR1, OSPI_NO_ADDRESS_COMMAND, + NULL, 0, + (char *) ®_buffer[0], OSPI_DEFAULT_STATUS_REGISTERS); + if (OSPI_STATUS_OK == status) { + tr_debug("Reading Status Register 1 Success: value = 0x%x", (int) reg_buffer[0]); + } else { + tr_error("Reading Status Register 1 failed"); + return status; + } + + // Read Status Register 2 (and beyond, if applicable) + unsigned int read_length = _num_status_registers - 1; // We already read status reg 1 above + status = _ospi_send_general_command(_read_status_reg_2_inst, OSPI_NO_ADDRESS_COMMAND, + NULL, 0, + (char *) ®_buffer[1], read_length); + if (OSPI_STATUS_OK == status) { + tr_debug("Reading Status Register 2 Success: value = 0x%x", (int) reg_buffer[1]); + if (_num_status_registers > 2) { + tr_debug("Reading Register 3 Success: value = 0x%x", (int) reg_buffer[2]); + } + } else { + tr_error("Reading Status Register 2 failed"); + return status; + } + + return OSPI_STATUS_OK; +} + +ospi_status_t OSPIFBlockDevice::_ospi_write_status_registers(uint8_t *reg_buffer) +{ + ospi_status_t status; + + if (_write_status_reg_2_inst == OSPI_NO_INST) { + // Status registers are written on different data bytes of the same command + if (_set_write_enable() != 0) { + tr_error("Write Enable failed"); + return OSPI_STATUS_ERROR; + } + //for octa flash SOPI mode, WRSR just write 1 byte + if (_read_instruction == DTROSPIF_INST_READ_OCTA_STR) { + status = _ospi_send_general_command(OSPIF_INST_WSR1, OSPI_NO_ADDRESS_COMMAND, + (char *) reg_buffer, 1, + NULL, 0); + //for octa flash DOPI mode, WRSR just write 1 byte, but the byte number need to even because of protocol + } else if (_read_instruction == DTROSPIF_INST_READ_OCTA_DTR) { + status = _ospi_send_general_command(OSPIF_INST_WSR1, OSPI_NO_ADDRESS_COMMAND, + (char *) reg_buffer, 2, + NULL, 0); + } else { + status = _ospi_send_general_command(OSPIF_INST_WSR1, OSPI_NO_ADDRESS_COMMAND, + (char *) reg_buffer, _num_status_registers, + NULL, 0); + } + if (OSPI_STATUS_OK == status) { + tr_debug("Writing Status Registers Success: reg 1 value = 0x%x, reg 2 value = 0x%x", + (int) reg_buffer[0], (int) reg_buffer[1]); + if (_num_status_registers > 2) { + tr_debug("Writing Register 3 Success: value = 0x%x", (int) reg_buffer[2]); + } + } else { + tr_error("Writing Status Registers failed"); + return status; + } + } else { + // Status registers are written using different commands + MBED_ASSERT(_num_status_registers == 2); // This flow doesn't support a nonstandard third status/config register + + // Write status register 1 + if (_set_write_enable() != 0) { + tr_error("Write Enable failed"); + return OSPI_STATUS_ERROR; + } + status = _ospi_send_general_command(OSPIF_INST_WSR1, OSPI_NO_ADDRESS_COMMAND, + (char *) ®_buffer[0], 1, + NULL, 0); + if (OSPI_STATUS_OK == status) { + tr_debug("Writing Status Register 1 Success: value = 0x%x", + (int) reg_buffer[0]); + } else { + tr_error("Writing Status Register 1 failed"); + return status; + } + + // Write status register 2 + if (_set_write_enable() != 0) { + tr_error("Write Enable failed"); + return OSPI_STATUS_ERROR; + } + status = _ospi_send_general_command(_write_status_reg_2_inst, OSPI_NO_ADDRESS_COMMAND, + (char *) ®_buffer[0], 1, + NULL, 0); + if (OSPI_STATUS_OK == status) { + tr_debug("Writing Status Register 2 Success: value = 0x%x", + (int) reg_buffer[1]); + } else { + tr_error("Writing Status Register 2 failed"); + return status; + } + } + + return OSPI_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 236db4f..61fa31c 100644 --- a/storage/blockdevice/tests/TESTS/blockdevice/general_block_device/main.cpp +++ b/storage/blockdevice/tests/TESTS/blockdevice/general_block_device/main.cpp @@ -36,6 +36,10 @@ #include "QSPIFBlockDevice.h" #endif +#if COMPONENT_OSPIF +#include "OSPIFBlockDevice.h" +#endif + #if COMPONENT_DATAFLASH #include "DataFlashBlockDevice.h" #endif @@ -86,10 +90,11 @@ dataflash, sd, flashiap, + ospif, default_bd }; -uint8_t bd_arr[5] = {0}; +uint8_t bd_arr[6] = {0}; static uint8_t test_iteration = 0; @@ -136,6 +141,27 @@ #endif break; } + case ospif: { +#if COMPONENT_OSPIF + static OSPIFBlockDevice default_bd( + MBED_CONF_OSPIF_OSPI_IO0, + MBED_CONF_OSPIF_OSPI_IO1, + MBED_CONF_OSPIF_OSPI_IO2, + MBED_CONF_OSPIF_OSPI_IO3, + MBED_CONF_OSPIF_OSPI_IO4, + MBED_CONF_OSPIF_OSPI_IO5, + MBED_CONF_OSPIF_OSPI_IO6, + MBED_CONF_OSPIF_OSPI_IO7, + MBED_CONF_OSPIF_OSPI_SCK, + MBED_CONF_OSPIF_OSPI_CSN, + MBED_CONF_OSPIF_OSPI_DQS, + MBED_CONF_OSPIF_OSPI_POLARITY_MODE, + MBED_CONF_OSPIF_OSPI_FREQ + ); + return &default_bd; +#endif + break; + } case dataflash: { #if COMPONENT_DATAFLASH static DataFlashBlockDevice default_bd( @@ -746,13 +772,15 @@ #if COMPONENT_QSPIF TEST_ASSERT_EQUAL(0, strcmp(bd_type, "QSPIF")); +#elif COMPONENT_OSPIF + TEST_ASSERT_EQUAL(0, strcmp(bd_type, "OSPIF")); #elif COMPONENT_SPIF TEST_ASSERT_EQUAL(0, strcmp(bd_type, "SPIF")); #elif COMPONENT_DATAFLASH TEST_ASSERT_EQUAL(0, strcmp(bd_type, "DATAFLASH")); #elif COMPONENT_SD TEST_ASSERT_EQUAL(0, strcmp(bd_type, "SD")); -#elif COMPONET_FLASHIAP +#elif COMPONENT_FLASHIAP TEST_ASSERT_EQUAL(0, strcmp(bd_type, "FLASHIAP")); #endif } @@ -809,11 +837,14 @@ #if COMPONENT_FLASHIAP bd_arr[count++] = flashiap; //4 #endif +#if COMPONENT_OSPIF + bd_arr[count++] = ospif; //5 +#endif return count; } -static const char *prefix[] = {"SPIF ", "QSPIF ", "DATAFLASH ", "SD ", "FLASHIAP ", "DEFAULT "}; +static const char *prefix[] = {"SPIF ", "QSPIF ", "DATAFLASH ", "SD ", "FLASHIAP ", "OSPIF ", "DEFAULT "}; int main() { diff --git a/storage/kvstore/kv_config/source/kv_config.cpp b/storage/kvstore/kv_config/source/kv_config.cpp index b100df9..187ec74 100644 --- a/storage/kvstore/kv_config/source/kv_config.cpp +++ b/storage/kvstore/kv_config/source/kv_config.cpp @@ -43,6 +43,10 @@ #include "SPIFBlockDevice.h" #endif +#if COMPONENT_OSPIF +#include "OSPIFBlockDevice.h" +#endif + #if COMPONENT_DATAFLASH #include "DataFlashBlockDevice.h" #endif @@ -79,7 +83,7 @@ * MBED_CONF_STORAGE_TDB_EXTERNAL_EXTERNAL_SIZE - Size of the external blockdevice in bytes or NULL for * max possible size. * MBED_CONF_STORAGE_TDB_EXTERNAL_EXTERNAL_BASE_ADDRESS - The block device start address. - * MBED_CONF_STORAGE_TDB_EXTERNAL_EXTERNAL_BLOCK_DEVICE - Alowed vlaues are: default, SPIF, DATAFASH, QSPIF or SD + * MBED_CONF_STORAGE_TDB_EXTERNAL_EXTERNAL_BLOCK_DEVICE - Alowed vlaues are: default, SPIF, DATAFASH, QSPIF, OSPIF or SD * @returns 0 on success or negative value on failure. */ int _storage_config_TDB_EXTERNAL(); @@ -92,7 +96,7 @@ * MBED_CONF_STORAGE_TDB_EXTERNAL_NO_RBP_EXTERNAL_SIZE - Size of the external blockdevice in bytes * or NULL for max possible size. * MBED_CONF_STORAGE_TDB_EXTERNAL_NO_RBP_EXTERNAL_BASE_ADDRESS - The block device start address - * MBED_CONF_STORAGE_TDB_EXTERNAL_NO_RBP_EXTERNAL_BLOCK_DEVICE - Alowed vlaues are: default, SPIF, DATAFASH, QSPIF or SD + * MBED_CONF_STORAGE_TDB_EXTERNAL_NO_RBP_EXTERNAL_BLOCK_DEVICE - Alowed vlaues are: default, SPIF, DATAFASH, QSPIF, OSPIF or SD * @returns 0 on success or negative value on failure. */ int _storage_config_TDB_EXTERNAL_NO_RBP(); @@ -109,7 +113,7 @@ * flash - rbp_internal_size. * MBED_CONF_STORAGE_FILESYSTEM_INTERNAL_BASE_ADDRESS - The satrt address of the internal FlashIAPBlockDevice. * MBED_CONF_STORAGE_FILESYSTEM_FILESYSTEM - Allowed values are: default, FAT or LITTLE - * MBED_CONF_STORAGE_FILESYSTEM_BLOCKDEVICE - Allowed values are: default, SPIF, DATAFASH, QSPIF or SD + * MBED_CONF_STORAGE_FILESYSTEM_BLOCKDEVICE - Allowed values are: default, SPIF, DATAFASH, QSPIF, OSPIF or SD * MBED_CONF_STORAGE_FILESYSTEM_EXTERNAL_SIZE - External Blockdevice size in bytes or NULL for max possible size. * MBED_CONF_STORAGE_FILESYSTEM_EXTERNAL_BASE_ADDRESS - The block device start address. * MBED_CONF_STORAGE_FILESYSTEM_MOUNT_POINT - Where to mount the filesystem @@ -125,7 +129,7 @@ * filesystem with default blockdevice unless differently configured. * The following is a list of configuration parameter: * MBED_CONF_STORAGE_FILESYSTEM_NO_RBP_FILESYSTEM - Allowed values are: default, FAT or LITTLE - * MBED_CONF_STORAGE_FILESYSTEM_NO_RBP_BLOCKDEVICE - Allowed values are: default, SPIF, DATAFASH, QSPIF or SD + * MBED_CONF_STORAGE_FILESYSTEM_NO_RBP_BLOCKDEVICE - Allowed values are: default, SPIF, DATAFASH, QSPIF, OSPIF or SD * MBED_CONF_STORAGE_FILESYSTEM_NO_RBP_EXTERNAL_SIZE - Blockdevice size in bytes. or NULL for max possible size. * MBED_CONF_STORAGE_FILESYSTEM_NO_RBP_EXTERNAL_BASE_ADDRESS - The block device start address. * MBED_CONF_STORAGE_FILESYSTEM_NO_RBP_MOUNT_POINT - Where to mount the filesystem @@ -256,7 +260,7 @@ FileSystem *_get_filesystem_default(const char *mount) { -#if COMPONENT_QSPIF || COMPONENT_SPIF || COMPONENT_DATAFLASH +#if COMPONENT_QSPIF || COMPONENT_SPIF || COMPONENT_DATAFLASH || COMPONENT_OSPIF return _get_filesystem_LITTLE(mount); #elif COMPONENT_SD return _get_filesystem_FAT(mount); @@ -375,6 +379,55 @@ #endif } +BlockDevice *_get_blockdevice_OSPIF(bd_addr_t start_address, bd_size_t size) +{ +#if COMPONENT_OSPIF + + bd_addr_t aligned_end_address; + bd_addr_t aligned_start_address; + + static OSPIFBlockDevice bd( + MBED_CONF_OSPIF_OSPI_IO0, + MBED_CONF_OSPIF_OSPI_IO1, + MBED_CONF_OSPIF_OSPI_IO2, + MBED_CONF_OSPIF_OSPI_IO3, + MBED_CONF_OSPIF_OSPI_IO4, + MBED_CONF_OSPIF_OSPI_IO5, + MBED_CONF_OSPIF_OSPI_IO6, + MBED_CONF_OSPIF_OSPI_IO7, + MBED_CONF_OSPIF_OSPI_SCK, + MBED_CONF_OSPIF_OSPI_CSN, + MBED_CONF_OSPIF_OSPI_DQS, + MBED_CONF_OSPIF_OSPI_POLARITY_MODE, + MBED_CONF_OSPIF_OSPI_FREQ + ); + + if (bd.init() != MBED_SUCCESS) { + tr_error("KV Config: OSPIFBlockDevice init fail"); + return NULL; + } + + if (start_address == 0 && size == 0) { + return &bd; + } + + //If address and size were specified use SlicingBlockDevice to get the correct block device size and start address. + if (_get_addresses(&bd, start_address, size, &aligned_start_address, &aligned_end_address) != 0) { + tr_error("KV Config: Fail to get addresses for SlicingBlockDevice."); + return NULL; + } + + start_address = aligned_start_address; + size = aligned_end_address - aligned_start_address; + + static SlicingBlockDevice sbd(&bd, aligned_start_address, aligned_end_address); + return &sbd; + +#else + return NULL; +#endif +} + BlockDevice *_get_blockdevice_DATAFLASH(bd_addr_t start_address, bd_size_t size) { #if COMPONENT_DATAFLASH @@ -491,6 +544,8 @@ return _get_blockdevice_QSPIF(start_address, size); #elif COMPONENT_SPIF return _get_blockdevice_SPIF(start_address, size); +#elif COMPONENT_OSPIF + return _get_blockdevice_OSPIF(start_address, size); #elif COMPONENT_DATAFLASH return _get_blockdevice_DATAFLASH(start_address, size); #elif COMPONENT_SD diff --git a/storage/platform/source/PlatformStorage.cpp b/storage/platform/source/PlatformStorage.cpp index 20daad0..2371daf 100644 --- a/storage/platform/source/PlatformStorage.cpp +++ b/storage/platform/source/PlatformStorage.cpp @@ -28,6 +28,10 @@ #include "QSPIFBlockDevice.h" #endif +#if COMPONENT_OSPIF +#include "OSPIFBlockDevice.h" +#endif + #if COMPONENT_DATAFLASH #include "DataFlashBlockDevice.h" #endif @@ -75,6 +79,12 @@ return &default_bd; +#elif COMPONENT_OSPIF + + static OSPIFBlockDevice default_bd; + + return &default_bd; + #elif COMPONENT_DATAFLASH static DataFlashBlockDevice default_bd; @@ -140,7 +150,7 @@ MBED_WEAK FileSystem *FileSystem::get_default_instance() { -#if COMPONENT_SPIF || COMPONENT_QSPIF || COMPONENT_DATAFLASH +#if COMPONENT_SPIF || COMPONENT_QSPIF || COMPONENT_OSPIF || COMPONENT_DATAFLASH static LittleFileSystem flash("flash", BlockDevice::get_default_instance()); flash.set_as_default(); diff --git a/targets/TARGET_STM/PeripheralPins.h b/targets/TARGET_STM/PeripheralPins.h index ddbfcb4..fd7b7c3 100644 --- a/targets/TARGET_STM/PeripheralPins.h +++ b/targets/TARGET_STM/PeripheralPins.h @@ -90,6 +90,21 @@ extern const PinMap PinMap_QSPI_SSEL[]; #endif +//*** OSPI *** +#if DEVICE_OSPI +extern const PinMap PinMap_OSPI_DATA0[]; +extern const PinMap PinMap_OSPI_DATA1[]; +extern const PinMap PinMap_OSPI_DATA2[]; +extern const PinMap PinMap_OSPI_DATA3[]; +extern const PinMap PinMap_OSPI_DATA4[]; +extern const PinMap PinMap_OSPI_DATA5[]; +extern const PinMap PinMap_OSPI_DATA6[]; +extern const PinMap PinMap_OSPI_DATA7[]; +extern const PinMap PinMap_OSPI_DQS[]; +extern const PinMap PinMap_OSPI_SCLK[]; +extern const PinMap PinMap_OSPI_SSEL[]; +#endif + //*** USB *** #define USE_USB_NO_OTG 0 #define USE_USB_OTG_FS 1 diff --git a/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L4R9xI/TARGET_DISCO_L4R9I/PeripheralNames.h b/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L4R9xI/TARGET_DISCO_L4R9I/PeripheralNames.h index 65e8740..3cd14c5 100644 --- a/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L4R9xI/TARGET_DISCO_L4R9I/PeripheralNames.h +++ b/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L4R9xI/TARGET_DISCO_L4R9I/PeripheralNames.h @@ -89,6 +89,11 @@ } QSPIName; typedef enum { + OSPI_1 = (int)OCTOSPI1_R_BASE, + OSPI_2 = (int)OCTOSPI2_R_BASE +} OSPIName; + +typedef enum { USB_FS = (int)USB_OTG_FS_PERIPH_BASE, } USBName; diff --git a/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L4R9xI/TARGET_DISCO_L4R9I/PeripheralPins.c b/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L4R9xI/TARGET_DISCO_L4R9I/PeripheralPins.c index 0e9dd3d..fb35840 100644 --- a/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L4R9xI/TARGET_DISCO_L4R9I/PeripheralPins.c +++ b/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L4R9xI/TARGET_DISCO_L4R9I/PeripheralPins.c @@ -447,6 +447,87 @@ {NC, NC, 0} }; +//*** OCTOSPI *** + +MBED_WEAK const PinMap PinMap_OSPI_DATA0[] = { + {PB_1, OSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_OCTOSPIM_P1)}, // OCTOSPIM_P1_IO0 + {PE_12, OSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_OCTOSPIM_P1)}, // OCTOSPIM_P1_IO0 // Connected to D9 + {PF_0, OSPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_OCTOSPIM_P2)}, // OCTOSPIM_P2_IO0 // Connected to PSRAM_A0 + {PI_11, OSPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_OCTOSPIM_P2)}, // OCTOSPIM_P2_IO0 // Connected to OCTOSPIM_P2_IO0 + {NC, NC, 0} +}; + +MBED_WEAK const PinMap PinMap_OSPI_DATA1[] = { + {PB_0, OSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_OCTOSPIM_P1)}, // OCTOSPIM_P1_IO1 // Connected to ARD_A3 + {PE_13, OSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_OCTOSPIM_P1)}, // OCTOSPIM_P1_IO1 // Connected to D10 + {PF_1, OSPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_OCTOSPIM_P2)}, // OCTOSPIM_P2_IO1 // Connected to PSRAM_A1 + {PI_10, OSPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_OCTOSPIM_P2)}, // OCTOSPIM_P2_IO1 // Connected to OCTOSPIM_P2_IO1 + {NC, NC, 0} +}; + +MBED_WEAK const PinMap PinMap_OSPI_DATA2[] = { + {PA_7, OSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_OCTOSPIM_P1)}, // OCTOSPIM_P1_IO2 // Connected to ARD_A0 + {PE_14, OSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_OCTOSPIM_P1)}, // OCTOSPIM_P1_IO2 // Connected to D11 + {PF_2, OSPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_OCTOSPIM_P2)}, // OCTOSPIM_P2_IO2 // Connected to PSRAM_A2 + {PI_9, OSPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_OCTOSPIM_P2)}, // OCTOSPIM_P2_IO2 // Connected to OCTOSPIM_P2_IO2 + {NC, NC, 0} +}; + +MBED_WEAK const PinMap PinMap_OSPI_DATA3[] = { + {PA_6, OSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_OCTOSPIM_P1)}, // OCTOSPIM_P1_IO3 // Connected to SPI2_CS + {PE_15, OSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_OCTOSPIM_P1)}, // OCTOSPIM_P1_IO3 // Connected to D12 + {PF_3, OSPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_OCTOSPIM_P2)}, // OCTOSPIM_P2_IO3 // Connected to PSRAM_A3 + {PH_8, OSPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_OCTOSPIM_P2)}, // OCTOSPIM_P2_IO3 // Connected to OCTOSPI_P2_IO3 + {NC, NC, 0} +}; + +MBED_WEAK const PinMap PinMap_OSPI_DATA4[] = { + {PH_9, OSPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_OCTOSPIM_P2)}, // OCTOSPIM_P2_IO4 // Connected to OCTOSPI_P2_IO4 + {NC, NC, 0} +}; + +MBED_WEAK const PinMap PinMap_OSPI_DATA5[] = { + {PH_10, OSPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_OCTOSPIM_P2)}, // OCTOSPIM_P2_IO5 // Connected to OCTOSPI_P2_IO5 + {NC, NC, 0} +}; + +MBED_WEAK const PinMap PinMap_OSPI_DATA6[] = { + {PG_9, OSPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_OCTOSPIM_P2)}, // OCTOSPIM_P2_IO6 // Connected to OCTOSPI_P2_IO6 + {NC, NC, 0} +}; + +MBED_WEAK const PinMap PinMap_OSPI_DATA7[] = { + {PG_10, OSPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_OCTOSPIM_P2)}, // OCTOSPIM_P2_IO7 // Connected to OCTOSPI_P2_IO7 + {NC, NC, 0} +}; + +MBED_WEAK const PinMap PinMap_OSPI_DQS[] = { + {PG_15, OSPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_OCTOSPIM_P2)}, // OCTOSPIM_P2_DQS // Connected to OCTOSPI_P2_DQS + {NC, NC, 0} +}; + +MBED_WEAK const PinMap PinMap_OSPI_SCLK[] = { +// {PA_3, OSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_OCTOSPIM_P1)}, // OCTOSPIM_P1_CLK // Connected to STDIO_UART_RX + {PB_10, OSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_OCTOSPIM_P1)}, // OCTOSPIM_P1_CLK // Connected to USART3_TX + {PE_10, OSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_OCTOSPIM_P1)}, // OCTOSPIM_P1_CLK // Connected to D7 + {PF_4, OSPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_OCTOSPIM_P2)}, // OCTOSPIM_P2_CLK // Connected to PSRAM_A4 + {PF_10, OSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF3_OCTOSPIM_P1)}, // OCTOSPIM_P1_CLK + {PI_6, OSPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_OCTOSPIM_P2)}, // OCTOSPIM_P2_CLK // Connected to OCTOSPIM_P2_CLK + {NC, NC, 0} +}; + +MBED_WEAK const PinMap PinMap_OSPI_SSEL[] = { +// {PA_2, OSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_OCTOSPIM_P1)}, // OCTOSPIM_P1_NCS // Connected to STDIO_UART_TX + {PA_4, OSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF3_OCTOSPIM_P1)}, // OCTOSPIM_P1_NCS // Connected to DCMI_HSYNC + {PB_11, OSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_OCTOSPIM_P1)}, // OCTOSPIM_P1_NCS // Connected to USART3_RX + {PC_11, OSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_OCTOSPIM_P1)}, // OCTOSPIM_P1_NCS // Connected to uSD_D3 + {PD_3, OSPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_OCTOSPIM_P2)}, // OCTOSPIM_P2_NCS // Connected to PSRAM_CLK + {PE_11, OSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_OCTOSPIM_P1)}, // OCTOSPIM_P1_NCS // Connected to D8 + {PG_12, OSPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_OCTOSPIM_P2)}, // OCTOSPIM_P2_NCS // Connected to OCTOSPIM_P2_CS + {PI_5, OSPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF5_OCTOSPIM_P2)}, // OCTOSPIM_P2_NCS // Connected to DCMI_VSYNC + {NC, NC, 0} +}; + //*** USBDEVICE *** MBED_WEAK const PinMap PinMap_USB_FS[] = { diff --git a/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L4R9xI/TARGET_DISCO_L4R9I/PinNames.h b/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L4R9xI/TARGET_DISCO_L4R9I/PinNames.h index b25dd15..38a2d7e 100644 --- a/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L4R9xI/TARGET_DISCO_L4R9I/PinNames.h +++ b/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L4R9xI/TARGET_DISCO_L4R9I/PinNames.h @@ -358,6 +358,19 @@ QSPI_FLASH1_SCK = PI_6, QSPI_FLASH1_CSN = PG_12, + /**** OSPI FLASH pins ****/ + OSPI_FLASH1_IO0 = PI_11, + OSPI_FLASH1_IO1 = PI_10, + OSPI_FLASH1_IO2 = PI_9, + OSPI_FLASH1_IO3 = PH_8, + OSPI_FLASH1_IO4 = PH_9, + OSPI_FLASH1_IO5 = PH_10, + OSPI_FLASH1_IO6 = PG_9, + OSPI_FLASH1_IO7 = PG_10, + OSPI_FLASH1_DQS = PG_15, + OSPI_FLASH1_SCK = PI_6, + OSPI_FLASH1_CSN = PG_12, + /**** STMOD+ pins ****/ STMOD_1 = PA_6, STMOD_2 = PB_10, diff --git a/targets/TARGET_STM/TARGET_STM32L4/common_objects.h b/targets/TARGET_STM/TARGET_STM32L4/common_objects.h index bbbcb52..f5e3f7d 100644 --- a/targets/TARGET_STM/TARGET_STM32L4/common_objects.h +++ b/targets/TARGET_STM/TARGET_STM32L4/common_objects.h @@ -168,6 +168,24 @@ }; #endif +#if DEVICE_OSPI +struct ospi_s { + OSPI_HandleTypeDef handle; + OSPIName ospi; + PinName io0; + PinName io1; + PinName io2; + PinName io3; + PinName io4; + PinName io5; + PinName io6; + PinName io7; + PinName sclk; + PinName ssel; + PinName dqs; +}; +#endif + #define HAL_CRC_IS_SUPPORTED(polynomial, width) ((width) == 7 || (width) == 8 || (width) == 16 || (width) == 32) #endif diff --git a/targets/TARGET_STM/ospi_api.c b/targets/TARGET_STM/ospi_api.c new file mode 100644 index 0000000..0564434 --- /dev/null +++ b/targets/TARGET_STM/ospi_api.c @@ -0,0 +1,625 @@ +/* + * Copyright (c) 2020, Arm Limited and affiliates. + * Copyright (c) 2020, STMicroelectronics. + * 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. + */ + +#if DEVICE_OSPI + +#include "ospi_api.h" +#include "mbed_error.h" +#include "mbed_debug.h" +#include "cmsis.h" +#include "pinmap.h" +#include "PeripheralPins.h" + +#include "mbed-trace/mbed_trace.h" + +#define TRACE_GROUP "STOS" + +// activate / de-activate debug +#define ospi_api_c_debug 0 + +/* Max amount of flash size is 4Gbytes */ +/* hence 2^(31+1), then FLASH_SIZE_DEFAULT = 1<<31 */ +#define OSPI_FLASH_SIZE_DEFAULT 0x80000000 + +static uint32_t get_alt_bytes_size(const uint32_t num_bytes) +{ + switch (num_bytes) { + case 1: + return HAL_OSPI_ALTERNATE_BYTES_8_BITS; + case 2: + return HAL_OSPI_ALTERNATE_BYTES_16_BITS; + case 3: + return HAL_OSPI_ALTERNATE_BYTES_24_BITS; + case 4: + return HAL_OSPI_ALTERNATE_BYTES_32_BITS; + } + error("Invalid alt bytes size"); + return 0xFFFFFFFF; +} + + +ospi_status_t ospi_prepare_command(const ospi_command_t *command, OSPI_RegularCmdTypeDef *st_command) +{ + debug_if(ospi_api_c_debug, "ospi_prepare_command In: instruction.value %x dummy_count %x address.bus_width %x address.disabled %x address.value %x address.size %x\n", + command->instruction.value, command->dummy_count, command->address.bus_width, command->address.disabled, command->address.value, command->address.size); + + st_command->FlashId = HAL_OSPI_FLASH_ID_1; + + if (command->instruction.disabled == true) { + st_command->InstructionMode = HAL_OSPI_INSTRUCTION_NONE; + st_command->Instruction = 0; + } else { + st_command->Instruction = ((command->instruction.bus_width == OSPI_CFG_BUS_OCTA) || (command->instruction.bus_width == OSPI_CFG_BUS_OCTA_DTR)) + ? command->instruction.value << 8 | (0xFF - command->instruction.value) : command->instruction.value; + switch (command->instruction.bus_width) { + case OSPI_CFG_BUS_SINGLE: + st_command->InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE; + break; + case OSPI_CFG_BUS_DUAL: + st_command->InstructionMode = HAL_OSPI_INSTRUCTION_2_LINES; + break; + case OSPI_CFG_BUS_QUAD: + st_command->InstructionMode = HAL_OSPI_INSTRUCTION_4_LINES; + break; + case OSPI_CFG_BUS_OCTA: + case OSPI_CFG_BUS_OCTA_DTR: + st_command->InstructionMode = HAL_OSPI_INSTRUCTION_8_LINES; + break; + default: + error("Command param error: wrong instruction format\n"); + return OSPI_STATUS_ERROR; + } + } + + st_command->InstructionSize = (st_command->InstructionMode == HAL_OSPI_INSTRUCTION_8_LINES) ? HAL_OSPI_INSTRUCTION_16_BITS : HAL_OSPI_INSTRUCTION_8_BITS; + st_command->InstructionDtrMode = (command->instruction.bus_width == OSPI_CFG_BUS_OCTA_DTR) ? HAL_OSPI_INSTRUCTION_DTR_ENABLE :HAL_OSPI_INSTRUCTION_DTR_DISABLE; + st_command->DummyCycles = command->dummy_count; + // these are target specific settings, use default values + st_command->SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD; + st_command->DataDtrMode = (command->instruction.bus_width == OSPI_CFG_BUS_OCTA_DTR) ? HAL_OSPI_DATA_DTR_ENABLE : HAL_OSPI_DATA_DTR_DISABLE; + st_command->AddressDtrMode = (command->instruction.bus_width == OSPI_CFG_BUS_OCTA_DTR) ? HAL_OSPI_ADDRESS_DTR_ENABLE : HAL_OSPI_ADDRESS_DTR_DISABLE; + st_command->AlternateBytesDtrMode = (command->instruction.bus_width == OSPI_CFG_BUS_OCTA_DTR) ? HAL_OSPI_ALTERNATE_BYTES_DTR_ENABLE : HAL_OSPI_ALTERNATE_BYTES_DTR_DISABLE; + st_command->DQSMode = (command->instruction.bus_width == OSPI_CFG_BUS_OCTA_DTR) ? HAL_OSPI_DQS_ENABLE :HAL_OSPI_DQS_DISABLE; + + st_command->OperationType = HAL_OSPI_OPTYPE_COMMON_CFG; + if (command->address.disabled == true) { + st_command->AddressMode = HAL_OSPI_ADDRESS_NONE; + st_command->AddressSize = 0; + } else { + st_command->Address = command->address.value; + switch (command->address.bus_width) { + case OSPI_CFG_BUS_SINGLE: + st_command->AddressMode = HAL_OSPI_ADDRESS_1_LINE; + break; + case OSPI_CFG_BUS_DUAL: + st_command->AddressMode = HAL_OSPI_ADDRESS_2_LINES; + break; + case OSPI_CFG_BUS_QUAD: + st_command->AddressMode = HAL_OSPI_ADDRESS_4_LINES; + break; + case OSPI_CFG_BUS_OCTA: + case OSPI_CFG_BUS_OCTA_DTR: + st_command->AddressMode = HAL_OSPI_ADDRESS_8_LINES; + break; + default: + error("Command param error: wrong address size\n"); + return OSPI_STATUS_ERROR; + } + switch (command->address.size) { + case OSPI_CFG_ADDR_SIZE_8: + st_command->AddressSize = HAL_OSPI_ADDRESS_8_BITS; + break; + case OSPI_CFG_ADDR_SIZE_16: + st_command->AddressSize = HAL_OSPI_ADDRESS_16_BITS; + break; + case OSPI_CFG_ADDR_SIZE_24: + st_command->AddressSize = HAL_OSPI_ADDRESS_24_BITS; + break; + case OSPI_CFG_ADDR_SIZE_32: + st_command->AddressSize = HAL_OSPI_ADDRESS_32_BITS; + break; + default: + error("Command param error: wrong address size\n"); + return OSPI_STATUS_ERROR; + } + } + + if (command->alt.disabled == true) { + st_command->AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE; + st_command->AlternateBytesSize = 0; + } else { + uint8_t alt_lines = 0; + switch (command->alt.bus_width) { + case OSPI_CFG_BUS_SINGLE: + st_command->AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_1_LINE; + alt_lines = 1; + break; + case OSPI_CFG_BUS_DUAL: + st_command->AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_2_LINES; + alt_lines = 2; + break; + case OSPI_CFG_BUS_QUAD: + st_command->AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_4_LINES; + alt_lines = 4; + break; + case OSPI_CFG_BUS_OCTA: + case OSPI_CFG_BUS_OCTA_DTR: + st_command->AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_8_LINES; + alt_lines = 8; + break; + default: + st_command->AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE; + error("Command param error: invalid alt bytes mode\n"); + return OSPI_STATUS_ERROR; + } + + // Alt size must be a multiple of the number of bus lines used (i.e. a whole number of cycles) + if (command->alt.size % alt_lines != 0) { + error("Command param error: incompatible alt size and alt bus width\n"); + return OSPI_STATUS_ERROR; + } + + // Round up to nearest byte - unused parts of byte act as dummy cycles + uint32_t alt_bytes = ((command->alt.size - 1) >> 3) + 1; + // Maximum of 4 alt bytes + if (alt_bytes > 4) { + error("Command param error: alt size exceeds maximum of 32 bits\n"); + return OSPI_STATUS_ERROR; + } + + // Unused bits in most significant byte of alt + uint8_t leftover_bits = (alt_bytes << 3) - command->alt.size; + if (leftover_bits != 0) { + // Account for dummy cycles that will be spent in the alt portion of the command + uint8_t integrated_dummy_cycles = leftover_bits / alt_lines; + if (st_command->DummyCycles < integrated_dummy_cycles) { + // Not enough dummy cycles to account for a short alt + error("Command param error: not enough dummy cycles to make up for given alt size\n"); + return OSPI_STATUS_ERROR; + } + st_command->DummyCycles -= integrated_dummy_cycles; + + // Align alt value to the end of the most significant byte + st_command->AlternateBytes = command->alt.value << leftover_bits; + } else { + st_command->AlternateBytes = command->alt.value; + } + + st_command->AlternateBytesSize = get_alt_bytes_size(alt_bytes); + } + + switch (command->data.bus_width) { + case OSPI_CFG_BUS_SINGLE: + st_command->DataMode = HAL_OSPI_DATA_1_LINE; + break; + case OSPI_CFG_BUS_DUAL: + st_command->DataMode = HAL_OSPI_DATA_2_LINES; + break; + case OSPI_CFG_BUS_QUAD: + st_command->DataMode = HAL_OSPI_DATA_4_LINES; + break; + case OSPI_CFG_BUS_OCTA: + case OSPI_CFG_BUS_OCTA_DTR: + st_command->DataMode = HAL_OSPI_DATA_8_LINES; + break; + default: + st_command->DataMode = HAL_OSPI_DATA_NONE; + break; + } + + debug_if(ospi_api_c_debug, "ospi_prepare_command Out: InstructionMode %x Instruction %x AddressMode %x AddressSize %x Address %x DataMode %x\n", + st_command->InstructionMode, st_command->Instruction, st_command->AddressMode, st_command->AddressSize, st_command->Address, st_command->DataMode); + + return OSPI_STATUS_OK; +} + + +#if STATIC_PINMAP_READY +#define OSPI_INIT_DIRECT ospi_init_direct +ospi_status_t ospi_init_direct(ospi_t *obj, const ospi_pinmap_t *pinmap, uint32_t hz, uint8_t mode) +#else +#define OSPI_INIT_DIRECT _ospi_init_direct +static ospi_status_t _ospi_init_direct(ospi_t *obj, const ospi_pinmap_t *pinmap, uint32_t hz, uint8_t mode) +#endif +{ + tr_info("ospi_init mode %u", mode); + + // Reset handle internal state + obj->handle.State = HAL_OSPI_STATE_RESET; + + // Set default OCTOSPI handle values + obj->handle.Init.DualQuad = HAL_OSPI_DUALQUAD_DISABLE; +//#if defined(TARGET_MX25LM512451G) + // obj->handle.Init.MemoryType = HAL_OSPI_MEMTYPE_MACRONIX; // Read sequence in DTR mode: D1-D0-D3-D2 +//#else + obj->handle.Init.MemoryType = HAL_OSPI_MEMTYPE_MICRON; // Read sequence in DTR mode: D0-D1-D2-D3 +//#endif + obj->handle.Init.ClockPrescaler = 4; // default value, will be overwritten in ospi_frequency + obj->handle.Init.FifoThreshold = 4; + obj->handle.Init.SampleShifting = HAL_OSPI_SAMPLE_SHIFTING_NONE; + obj->handle.Init.DeviceSize = POSITION_VAL(OSPI_FLASH_SIZE_DEFAULT) - 1; + obj->handle.Init.ChipSelectHighTime = 3; + obj->handle.Init.FreeRunningClock = HAL_OSPI_FREERUNCLK_DISABLE; + obj->handle.Init.WrapSize = HAL_OSPI_WRAP_NOT_SUPPORTED; + obj->handle.Init.ClockMode = mode == 0 ? HAL_OSPI_CLOCK_MODE_0 : HAL_OSPI_CLOCK_MODE_3; + obj->handle.Init.DelayHoldQuarterCycle = HAL_OSPI_DHQC_ENABLE; + obj->handle.Init.ChipSelectBoundary = 0; +#if defined(HAL_OSPI_DELAY_BLOCK_USED) // STM32L5 + obj->handle.Init.DelayBlockBypass = HAL_OSPI_DELAY_BLOCK_USED; +#endif + + // tested all combinations, take first + obj->ospi = pinmap->peripheral; + +#if defined(OCTOSPI1) + if (obj->ospi == OSPI_1) { + obj->handle.Instance = OCTOSPI1; + } +#endif +#if defined(OCTOSPI2) + if (obj->ospi == OSPI_2) { + obj->handle.Instance = OCTOSPI2; + } +#endif + +#if defined(OCTOSPI1) + if (obj->ospi == OSPI_1) { + __HAL_RCC_OSPI1_CLK_ENABLE(); + __HAL_RCC_OSPI1_FORCE_RESET(); + __HAL_RCC_OSPI1_RELEASE_RESET(); + } +#endif +#if defined(OCTOSPI2) + if (obj->ospi == OSPI_2) { + __HAL_RCC_OSPI2_CLK_ENABLE(); + __HAL_RCC_OSPI2_FORCE_RESET(); + __HAL_RCC_OSPI2_RELEASE_RESET(); + } +#endif + + // pinmap for pins (enable clock) + obj->io0 = pinmap->data0_pin; + pin_function(pinmap->data0_pin, pinmap->data0_function); + pin_mode(pinmap->data0_pin, PullNone); + obj->io1 = pinmap->data1_pin; + pin_function(pinmap->data1_pin, pinmap->data1_function); + pin_mode(pinmap->data1_pin, PullNone); + obj->io2 = pinmap->data2_pin; + pin_function(pinmap->data2_pin, pinmap->data2_function); + pin_mode(pinmap->data2_pin, PullNone); + obj->io3 = pinmap->data3_pin; + pin_function(pinmap->data3_pin, pinmap->data3_function); + pin_mode(pinmap->data3_pin, PullNone); + obj->io4 = pinmap->data4_pin; + pin_function(pinmap->data4_pin, pinmap->data4_function); + pin_mode(pinmap->data4_pin, PullNone); + obj->io5 = pinmap->data5_pin; + pin_function(pinmap->data5_pin, pinmap->data5_function); + pin_mode(pinmap->data5_pin, PullNone); + obj->io6 = pinmap->data6_pin; + pin_function(pinmap->data6_pin, pinmap->data6_function); + pin_mode(pinmap->data6_pin, PullNone); + obj->io7 = pinmap->data7_pin; + pin_function(pinmap->data7_pin, pinmap->data7_function); + pin_mode(pinmap->data7_pin, PullNone); + + obj->sclk = pinmap->sclk_pin; + pin_function(pinmap->sclk_pin, pinmap->sclk_function); + pin_mode(pinmap->sclk_pin, PullNone); + obj->ssel = pinmap->ssel_pin; + pin_function(pinmap->ssel_pin, pinmap->ssel_function); + pin_mode(pinmap->ssel_pin, PullNone); + obj->dqs = pinmap->dqs_pin; + pin_function(pinmap->dqs_pin, pinmap->dqs_function); + pin_mode(pinmap->dqs_pin, PullNone); + +#if defined(OCTOSPI2) + __HAL_RCC_OSPIM_CLK_ENABLE(); + + OSPIM_CfgTypeDef OSPIM_Cfg_Struct = {0}; + + /* The OctoSPI IO Manager OCTOSPIM configuration is supported in a simplified mode in mbed-os + * OSPI1 signals are mapped to port 1 and OSPI2 signals are mapped to port 2. + * This is coded in this way in PeripheralPins.c */ + if (obj->ospi == OSPI_1) { + OSPIM_Cfg_Struct.ClkPort = 1; + OSPIM_Cfg_Struct.DQSPort = 1; + OSPIM_Cfg_Struct.NCSPort = 1; + OSPIM_Cfg_Struct.IOLowPort = HAL_OSPIM_IOPORT_1_LOW; + OSPIM_Cfg_Struct.IOHighPort = HAL_OSPIM_IOPORT_1_HIGH; + } else { + OSPIM_Cfg_Struct.ClkPort = 2; + OSPIM_Cfg_Struct.DQSPort = 2; + OSPIM_Cfg_Struct.NCSPort = 2; + OSPIM_Cfg_Struct.IOLowPort = HAL_OSPIM_IOPORT_2_LOW; + OSPIM_Cfg_Struct.IOHighPort = HAL_OSPIM_IOPORT_2_HIGH; + } + + if (HAL_OSPIM_Config(&obj->handle, &OSPIM_Cfg_Struct, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { + tr_error("HAL_OSPIM_Config error"); + return OSPI_STATUS_ERROR; + } +#endif + + return ospi_frequency(obj, hz); +} + +ospi_status_t ospi_init(ospi_t *obj, PinName io0, PinName io1, PinName io2, PinName io3, PinName io4, PinName io5, PinName io6, PinName io7, + PinName sclk, PinName ssel, PinName dqs, uint32_t hz, uint8_t mode) +{ + OSPIName ospiio0name = (OSPIName)pinmap_peripheral(io0, PinMap_OSPI_DATA0); + OSPIName ospiio1name = (OSPIName)pinmap_peripheral(io1, PinMap_OSPI_DATA1); + OSPIName ospiio2name = (OSPIName)pinmap_peripheral(io2, PinMap_OSPI_DATA2); + OSPIName ospiio3name = (OSPIName)pinmap_peripheral(io3, PinMap_OSPI_DATA3); + OSPIName ospiio4name = (OSPIName)pinmap_peripheral(io4, PinMap_OSPI_DATA4); + OSPIName ospiio5name = (OSPIName)pinmap_peripheral(io5, PinMap_OSPI_DATA5); + OSPIName ospiio6name = (OSPIName)pinmap_peripheral(io6, PinMap_OSPI_DATA6); + OSPIName ospiio7name = (OSPIName)pinmap_peripheral(io7, PinMap_OSPI_DATA7); + OSPIName ospiclkname = (OSPIName)pinmap_peripheral(sclk, PinMap_OSPI_SCLK); + OSPIName ospisselname = (OSPIName)pinmap_peripheral(ssel, PinMap_OSPI_SSEL); + OSPIName ospidqsname = (OSPIName)pinmap_peripheral(dqs, PinMap_OSPI_DQS); + + OSPIName ospi_data_first = (OSPIName)pinmap_merge(ospiio0name, ospiio1name); + OSPIName ospi_data_second = (OSPIName)pinmap_merge(ospiio2name, ospiio3name); + OSPIName ospi_data_third = (OSPIName)pinmap_merge(ospiclkname, ospisselname); + + if (ospi_data_first != ospi_data_second || ospi_data_second != ospi_data_third || + ospi_data_first != ospi_data_third) { + return OSPI_STATUS_INVALID_PARAMETER; + } + + int peripheral = (int)ospi_data_first; + int function_io0 = (int)pinmap_find_function(io0, PinMap_OSPI_DATA0); + int function_io1 = (int)pinmap_find_function(io1, PinMap_OSPI_DATA1); + int function_io2 = (int)pinmap_find_function(io2, PinMap_OSPI_DATA2); + int function_io3 = (int)pinmap_find_function(io3, PinMap_OSPI_DATA3); + int function_io4 = (int)pinmap_find_function(io4, PinMap_OSPI_DATA4); + int function_io5 = (int)pinmap_find_function(io5, PinMap_OSPI_DATA5); + int function_io6 = (int)pinmap_find_function(io6, PinMap_OSPI_DATA6); + int function_io7 = (int)pinmap_find_function(io7, PinMap_OSPI_DATA7); + + int function_sclk = (int)pinmap_find_function(sclk, PinMap_OSPI_SCLK); + int function_ssel = (int)pinmap_find_function(ssel, PinMap_OSPI_SSEL); + int function_dqs = (int)pinmap_find_function(dqs, PinMap_OSPI_DQS); + + const ospi_pinmap_t static_pinmap = {peripheral, io0, function_io0, io1, function_io1, io2, function_io2, io3, function_io3, + io4, function_io4, io5, function_io5, io6, function_io6, io7, function_io7, + sclk, function_sclk, ssel, function_ssel, dqs, function_dqs}; + + return OSPI_INIT_DIRECT(obj, &static_pinmap, hz, mode); +} + + +ospi_status_t ospi_free(ospi_t *obj) +{ + tr_info("ospi_free"); + if (HAL_OSPI_DeInit(&obj->handle) != HAL_OK) { + return OSPI_STATUS_ERROR; + } + +#if defined(OCTOSPI1) + if (obj->ospi == OSPI_1) { + __HAL_RCC_OSPI1_FORCE_RESET(); + __HAL_RCC_OSPI1_CLK_DISABLE(); + } +#endif +#if defined(OCTOSPI2) + if (obj->ospi == OSPI_2) { + __HAL_RCC_OSPI2_FORCE_RESET(); + __HAL_RCC_OSPI2_CLK_DISABLE(); + } +#endif + + // Configure GPIOs + pin_function(obj->io0, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0)); + pin_function(obj->io1, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0)); + pin_function(obj->io2, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0)); + pin_function(obj->io3, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0)); + pin_function(obj->io4, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0)); + pin_function(obj->io5, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0)); + pin_function(obj->io6, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0)); + pin_function(obj->io7, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0)); + pin_function(obj->sclk, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0)); + pin_function(obj->ssel, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0)); + pin_function(obj->dqs, STM_PIN_DATA(STM_MODE_INPUT, GPIO_NOPULL, 0)); + + (void)(obj); + return OSPI_STATUS_OK; +} + + +ospi_status_t ospi_frequency(ospi_t *obj, int hz) +{ + tr_info("ospi_frequency hz %d", hz); + ospi_status_t status = OSPI_STATUS_OK; + + /* HCLK drives OSPI. OSPI clock depends on prescaler value: + * 0: Freq = HCLK + * 1: Freq = HCLK/2 + * ... + * 255: Freq = HCLK/256 (minimum value) + */ + + int div = HAL_RCC_GetHCLKFreq() / hz; + if (div > 255) { + div = 255; + } else { + if (div == 1) { + div = div + 1; + } + } + + obj->handle.Init.ClockPrescaler = div; + + if (HAL_OSPI_Init(&obj->handle) != HAL_OK) { + tr_error("HAL_OSPI_Init error"); + status = OSPI_STATUS_ERROR; + } + + return status; +} + + +ospi_status_t ospi_write(ospi_t *obj, const ospi_command_t *command, const void *data, size_t *length) +{ + debug_if(ospi_api_c_debug, "ospi_write size %u\n", *length); + + OSPI_RegularCmdTypeDef st_command; + ospi_status_t status = ospi_prepare_command(command, &st_command); + if (status != OSPI_STATUS_OK) { + return status; + } + + st_command.NbData = *length; + + if (HAL_OSPI_Command(&obj->handle, &st_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { + tr_error("HAL_OSPI_Command error"); + status = OSPI_STATUS_ERROR; + } else { + if (HAL_OSPI_Transmit(&obj->handle, (uint8_t *)data, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { + tr_error("HAL_OSPI_Transmit error"); + status = OSPI_STATUS_ERROR; + } + } + + return status; +} + +ospi_status_t ospi_read(ospi_t *obj, const ospi_command_t *command, void *data, size_t *length) +{ + OSPI_RegularCmdTypeDef st_command; + ospi_status_t status = ospi_prepare_command(command, &st_command); + if (status != OSPI_STATUS_OK) { + return status; + } + + st_command.NbData = *length; + + if (HAL_OSPI_Command(&obj->handle, &st_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { + tr_error("HAL_OSPI_Command error"); + status = OSPI_STATUS_ERROR; + } else { + if (HAL_OSPI_Receive(&obj->handle, data, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { + tr_error("HAL_OSPI_Receive error %d", obj->handle.ErrorCode); + status = OSPI_STATUS_ERROR; + } + } + + debug_if(ospi_api_c_debug, "ospi_read size %u\n", *length); + + return status; +} + +ospi_status_t ospi_command_transfer(ospi_t *obj, const ospi_command_t *command, const void *tx_data, size_t tx_size, void *rx_data, size_t rx_size) +{ + tr_info("ospi_command_transfer tx %u rx %u command %#04x", tx_size, rx_size, command->instruction.value); + ospi_status_t status = OSPI_STATUS_OK; + + if ((tx_data == NULL || tx_size == 0) && (rx_data == NULL || rx_size == 0)) { + // only command, no rx or tx + OSPI_RegularCmdTypeDef st_command; + status = ospi_prepare_command(command, &st_command); + if (status != OSPI_STATUS_OK) { + return status; + } + + st_command.NbData = 1; + st_command.DataMode = HAL_OSPI_DATA_NONE; /* Instruction only */ + if (HAL_OSPI_Command(&obj->handle, &st_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { + status = OSPI_STATUS_ERROR; + tr_error("HAL_OSPI_Command error"); + return status; + } + } else { + // often just read a register, check if we need to transmit anything prior reading + if (tx_data != NULL && tx_size) { + size_t tx_length = tx_size; + status = ospi_write(obj, command, tx_data, &tx_length); + if (status != OSPI_STATUS_OK) { + tr_error("qspi_write error"); + return status; + } + } + + if (rx_data != NULL && rx_size) { + size_t rx_length = rx_size; + status = ospi_read(obj, command, rx_data, &rx_length); + } + } + return status; +} + + +const PinMap *ospi_master_sclk_pinmap() +{ + return PinMap_OSPI_SCLK; +} + +const PinMap *ospi_master_ssel_pinmap() +{ + return PinMap_OSPI_SSEL; +} + +const PinMap *ospi_master_dqs_pinmap() +{ + return PinMap_OSPI_DQS; +} + +const PinMap *ospi_master_data0_pinmap() +{ + return PinMap_OSPI_DATA0; +} + +const PinMap *ospi_master_data1_pinmap() +{ + return PinMap_OSPI_DATA1; +} + +const PinMap *ospi_master_data2_pinmap() +{ + return PinMap_OSPI_DATA2; +} + +const PinMap *ospi_master_data3_pinmap() +{ + return PinMap_OSPI_DATA3; +} + +const PinMap *ospi_master_data4_pinmap() +{ + return PinMap_OSPI_DATA4; +} + +const PinMap *ospi_master_data5_pinmap() +{ + return PinMap_OSPI_DATA5; +} + +const PinMap *ospi_master_data6_pinmap() +{ + return PinMap_OSPI_DATA6; +} + +const PinMap *ospi_master_data7_pinmap() +{ + return PinMap_OSPI_DATA7; +} + +#endif + +/** @}*/ diff --git a/targets/targets.json b/targets/targets.json index a624b2d..2af940e 100644 --- a/targets/targets.json +++ b/targets/targets.json @@ -3316,6 +3316,7 @@ "MX25LM51245G" ], "components_add": [ + "OSPIF", "FLASHIAP" ], "macros_add": [ @@ -3326,7 +3327,7 @@ "0774" ], "device_has_add": [ - "QSPI", + "OSPI", "USBDEVICE" ] },