diff --git a/doxyfile_options b/doxyfile_options index fb8d1f7bca6..b3d9e2bfea6 100644 --- a/doxyfile_options +++ b/doxyfile_options @@ -2093,6 +2093,7 @@ PREDEFINED = DOXYGEN_ONLY \ DEVICE_SPI \ DEVICE_SPI_ASYNCH \ DEVICE_SPISLAVE \ + DEVICE_QSPI \ DEVICE_STORAGE \ "MBED_DEPRECATED_SINCE(d, m)=" \ "MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, M)=" \ diff --git a/doxygen_options.json b/doxygen_options.json index 6884a64883c..971748f3338 100644 --- a/doxygen_options.json +++ b/doxygen_options.json @@ -6,7 +6,7 @@ "SEARCH_INCLUDES": "YES", "INCLUDE_PATH": "", "INCLUDE_FILE_PATTERNS": "", - "PREDEFINED": "DOXYGEN_ONLY DEVICE_ANALOGIN DEVICE_ANALOGOUT DEVICE_CAN DEVICE_CRC DEVICE_ETHERNET DEVICE_EMAC DEVICE_FLASH DEVICE_I2C DEVICE_I2CSLAVE DEVICE_I2C_ASYNCH DEVICE_INTERRUPTIN DEVICE_ITM DEVICE_LPTICKER DEVICE_PORTIN DEVICE_PORTINOUT DEVICE_PORTOUT DEVICE_PWMOUT DEVICE_RTC DEVICE_TRNG DEVICE_SERIAL DEVICE_SERIAL_ASYNCH DEVICE_SERIAL_FC DEVICE_SLEEP DEVICE_SPI DEVICE_SPI_ASYNCH DEVICE_SPISLAVE DEVICE_STORAGE \"MBED_DEPRECATED_SINCE(f, g)=\" \"MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, M)=\" \"MBED_DEPRECATED(s)=\"", + "PREDEFINED": "DOXYGEN_ONLY DEVICE_ANALOGIN DEVICE_ANALOGOUT DEVICE_CAN DEVICE_CRC DEVICE_ETHERNET DEVICE_EMAC DEVICE_FLASH DEVICE_I2C DEVICE_I2CSLAVE DEVICE_I2C_ASYNCH DEVICE_INTERRUPTIN DEVICE_ITM DEVICE_LPTICKER DEVICE_PORTIN DEVICE_PORTINOUT DEVICE_PORTOUT DEVICE_PWMOUT DEVICE_RTC DEVICE_TRNG DEVICE_SERIAL DEVICE_SERIAL_ASYNCH DEVICE_SERIAL_FC DEVICE_SLEEP DEVICE_SPI DEVICE_SPI_ASYNCH DEVICE_SPISLAVE DEVICE_QSPI DEVICE_STORAGE \"MBED_DEPRECATED_SINCE(f, g)=\" \"MBED_ENABLE_IF_CALLBACK_COMPATIBLE(F, M)=\" \"MBED_DEPRECATED(s)=\"", "EXPAND_AS_DEFINED": "", "SKIP_FUNCTION_MACROS": "NO", "STRIP_CODE_COMMENTS": "NO", diff --git a/drivers/QSPI.cpp b/drivers/QSPI.cpp new file mode 100644 index 00000000000..36cad333e1c --- /dev/null +++ b/drivers/QSPI.cpp @@ -0,0 +1,285 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2013 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "drivers/QSPI.h" +#include "platform/mbed_critical.h" +#include + +#if DEVICE_QSPI + +namespace mbed { + +QSPI* QSPI::_owner = NULL; +SingletonPtr QSPI::_mutex; + +QSPI::QSPI(PinName io0, PinName io1, PinName io2, PinName io3, PinName sclk, PinName ssel, int mode) : _qspi() +{ + _qspi_io0 = io0; + _qspi_io1 = io1; + _qspi_io2 = io2; + _qspi_io3 = io3; + _qspi_clk = sclk; + _qspi_cs = ssel; + _inst_width = QSPI_CFG_BUS_SINGLE; + _address_width = QSPI_CFG_BUS_SINGLE; + _address_size = QSPI_CFG_ADDR_SIZE_24; + _alt_width = QSPI_CFG_BUS_SINGLE; + _alt_size = QSPI_CFG_ALT_SIZE_8; + _data_width = QSPI_CFG_BUS_SINGLE; + _num_dummy_cycles = 0; + _mode = mode; + _hz = ONE_MHZ; + _initialized = false; + + //Go ahead init the device here with the default config + _initialize(); +} + +qspi_status_t QSPI::configure_format(qspi_bus_width_t inst_width, qspi_bus_width_t address_width, qspi_address_size_t address_size, qspi_bus_width_t alt_width, qspi_alt_size_t alt_size, qspi_bus_width_t data_width, int dummy_cycles) +{ + qspi_status_t ret_status = QSPI_STATUS_OK; + + lock(); + _inst_width = inst_width; + _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 ret_status; +} + +qspi_status_t QSPI::set_frequency(int hz) +{ + qspi_status_t ret_status = QSPI_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 (QSPI_STATUS_OK != qspi_frequency(&_qspi, _hz)) { + ret_status = QSPI_STATUS_ERROR; + } + } else { + _acquire(); + } + unlock(); + } else { + ret_status = QSPI_STATUS_ERROR; + } + + return ret_status; +} + +qspi_status_t QSPI::read(unsigned int address, char *rx_buffer, size_t *rx_length) +{ + qspi_status_t ret_status = QSPI_STATUS_ERROR; + + if (_initialized) { + if ((rx_length != NULL) && (rx_buffer != NULL)) { + if (*rx_length != 0) { + lock(); + if (true == _acquire()) { + _build_qspi_command(-1, address, -1); + if (QSPI_STATUS_OK == qspi_read(&_qspi, &_qspi_command, rx_buffer, rx_length)) { + ret_status = QSPI_STATUS_OK; + } + } + unlock(); + } + } else { + ret_status = QSPI_STATUS_INVALID_PARAMETER; + } + } + + return ret_status; +} + +qspi_status_t QSPI::write(unsigned int address, const char *tx_buffer, size_t *tx_length) +{ + qspi_status_t ret_status = QSPI_STATUS_ERROR; + + if (_initialized) { + if ((tx_length != NULL) && (tx_buffer != NULL)) { + if (*tx_length != 0) { + lock(); + if (true == _acquire()) { + _build_qspi_command(-1, address, -1); + if (QSPI_STATUS_OK == qspi_write(&_qspi, &_qspi_command, tx_buffer, tx_length)) { + ret_status = QSPI_STATUS_OK; + } + } + unlock(); + } + } else { + ret_status = QSPI_STATUS_INVALID_PARAMETER; + } + } + + return ret_status; +} + +qspi_status_t QSPI::read(unsigned int instruction, unsigned int alt, unsigned int address, char *rx_buffer, size_t *rx_length) +{ + qspi_status_t ret_status = QSPI_STATUS_ERROR; + + if (_initialized) { + if ( (rx_length != NULL) && (rx_buffer != NULL) ) { + if (*rx_length != 0) { + lock(); + if ( true == _acquire()) { + _build_qspi_command(instruction, address, alt); + if (QSPI_STATUS_OK == qspi_read(&_qspi, &_qspi_command, rx_buffer, rx_length)) { + ret_status = QSPI_STATUS_OK; + } + } + unlock(); + } + } else { + ret_status = QSPI_STATUS_INVALID_PARAMETER; + } + } + + return ret_status; +} + +qspi_status_t QSPI::write(unsigned int instruction, unsigned int alt, unsigned int address, const char *tx_buffer, size_t *tx_length) +{ + qspi_status_t ret_status = QSPI_STATUS_ERROR; + + if (_initialized) { + if ( (tx_length != NULL) && (tx_buffer != NULL) ) { + if (*tx_length != 0) { + lock(); + if (true == _acquire()) { + _build_qspi_command(instruction, address, alt); + if (QSPI_STATUS_OK == qspi_write(&_qspi, &_qspi_command, tx_buffer, tx_length)) { + ret_status = QSPI_STATUS_OK; + } + } + unlock(); + } + } else { + ret_status = QSPI_STATUS_INVALID_PARAMETER; + } + } + + return ret_status; +} + +qspi_status_t QSPI::command_transfer(unsigned int instruction, int address, const char *tx_buffer, size_t tx_length, const char *rx_buffer, size_t rx_length) +{ + qspi_status_t ret_status = QSPI_STATUS_ERROR; + + if (_initialized) { + lock(); + if (true == _acquire()) { + _build_qspi_command(instruction, address, -1); //We just need the command + if (QSPI_STATUS_OK == qspi_command_transfer(&_qspi, &_qspi_command, (const void *)tx_buffer, tx_length, (void *)rx_buffer, rx_length)) { + ret_status = QSPI_STATUS_OK; + } + } + unlock(); + } + + return ret_status; +} + +void QSPI::lock() +{ + _mutex->lock(); +} + +void QSPI::unlock() +{ + _mutex->unlock(); +} + +// Note: Private helper function to initialize qspi HAL +bool QSPI::_initialize() +{ + if (_mode != 0 && _mode != 1) + return QSPI_STATUS_INVALID_PARAMETER; + + qspi_status_t ret = qspi_init(&_qspi, _qspi_io0, _qspi_io1, _qspi_io2, _qspi_io3, _qspi_clk, _qspi_cs, _hz, _mode ); + if (QSPI_STATUS_OK == ret) { + _initialized = true; + } else { + _initialized = false; + } + + return _initialized; +} + +// Note: Private function with no locking +bool QSPI::_acquire() +{ + if (_owner != this) { + //This will set freq as well + _initialize(); + _owner = this; + } + + return _initialized; +} + +void QSPI::_build_qspi_command(int instruction, int address, int alt) +{ + memset( &_qspi_command, 0, sizeof(qspi_command_t) ); + //Set up instruction phase parameters + _qspi_command.instruction.bus_width = _inst_width; + if (instruction != -1) { + _qspi_command.instruction.value = instruction; + _qspi_command.instruction.disabled = false; + } else { + _qspi_command.instruction.disabled = true; + } + + //Set up address phase parameters + _qspi_command.address.bus_width = _address_width; + _qspi_command.address.size = _address_size; + if (address != -1) { + _qspi_command.address.value = address; + _qspi_command.address.disabled = false; + } else { + _qspi_command.address.disabled = true; + } + + //Set up alt phase parameters + _qspi_command.alt.bus_width = _alt_width; + _qspi_command.alt.size = _alt_size; + if (alt != -1) { + _qspi_command.alt.value = alt; + _qspi_command.alt.disabled = false; + } else { + _qspi_command.alt.disabled = true; + } + + _qspi_command.dummy_count = _num_dummy_cycles; + + //Set up bus width for data phase + _qspi_command.data.bus_width = _data_width; +} + +} // namespace mbed + +#endif diff --git a/drivers/QSPI.h b/drivers/QSPI.h new file mode 100644 index 00000000000..ecf2d43e121 --- /dev/null +++ b/drivers/QSPI.h @@ -0,0 +1,222 @@ +/* mbed Microcontroller Library + * Copyright (c) 2006-2015 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_QSPI_H +#define MBED_QSPI_H + +#include "platform/platform.h" + +#if defined (DEVICE_QSPI) || defined(DOXYGEN_ONLY) + +#include "hal/qspi_api.h" +#include "platform/PlatformMutex.h" +#include "platform/SingletonPtr.h" +#include "platform/NonCopyable.h" + +#define ONE_MHZ 1000000 + +namespace mbed { + +/** \addtogroup drivers */ + +/** A QSPI Driver, used for communicating with QSPI slave devices + * + * The default format is set to Quad-SPI(4-4-4), and a clock frequency of 1MHz + * Most QSPI devices will also require Chip Select which is indicated by ssel. + * + * @note Synchronization level: Thread safe + * + * Example: + * @code + * // Write 4 byte array to a QSPI slave, and read the response, note that each device will have its specific read/write/alt values defined + * + * #include "mbed.h" + * + * // hardware ssel (where applicable) + * QSPI qspi_device(p5, p6, p7, p8, p9, p10); // io0, io1, io2, io3, sclk, ssel + * + * + * int main() { + * char tx_buf[] = { 0x11, 0x22, 0x33, 0x44 }; + * char rx_buf[4]; + * int buf_len = sizeof(tx_buf); + * + * int result = qspi_device.write( 0x12 , 0x100000 , 0 , tx_buf, &buf_len ); + * if( !result ) printf("Write failed"); + * int result = qspi_device.read( 0x13 , 0x100000 , 0 , rx_buf, &buf_len ); + * if( !result ) printf("Read failed"); + * + * } + * @endcode + * @ingroup drivers + */ +class QSPI : private NonCopyable { + +public: + + /** Create a QSPI 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 sclk QSPI Clock pin + * @param ssel QSPI chip select pin + * @param mode Mode specifies the SPI mode(Mode=0 uses CPOL=0, CPHA=0, Mode=1 uses CPOL=1, CPHA=1) + * default value = 0 + * + */ + QSPI(PinName io0, PinName io1, PinName io2, PinName io3, PinName sclk, PinName ssel=NC, int mode=0); + + /** Configure the data transmission format + * + * @param inst_width Bus width used by instruction phase(Valid values are 1,2,4) + * @param address_width Bus width used by address phase(Valid values are 1,2,4) + * @param address_size Size in bits used by address phase(Valid values are 8,16,24,32) + * @param alt_width Bus width used by alt phase(Valid values are 1,2,4) + * @param alt_size Size in bits used by alt phase(Valid values are 8,16,24,32) + * @param data_width Bus width used by data phase(Valid values are 1,2,4) + * @param dummy_cycles Number of dummy clock cycles to be used after alt phase + * + */ + qspi_status_t configure_format(qspi_bus_width_t inst_width, + qspi_bus_width_t address_width, + qspi_address_size_t address_size, + qspi_bus_width_t alt_width, + qspi_alt_size_t alt_size, + qspi_bus_width_t data_width, + int dummy_cycles); + + /** Set the qspi bus clock frequency + * + * @param hz SCLK frequency in hz (default = 1MHz) + * @returns + * Returns QSPI_STATUS_SUCCESS on successful, fails if the interface is already init-ed + */ + qspi_status_t set_frequency(int hz = ONE_MHZ); + + /** Read from QSPI peripheral with the preset read_instruction and alt_value + * + * @param address Address to be accessed in QSPI 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 QSPI_STATUS_SUCCESS on successful reads and QSPI_STATUS_ERROR on failed reads. + */ + qspi_status_t read(unsigned int address, char *rx_buffer, size_t *rx_length); + + /** Write to QSPI peripheral using custom write instruction + * + * @param address Address to be accessed in QSPI 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 QSPI_STATUS_SUCCESS on successful reads and QSPI_STATUS_ERROR on failed reads. + */ + qspi_status_t write(unsigned int address, const char *tx_buffer, size_t *tx_length); + + /** Read from QSPI peripheral using custom read instruction, alt values + * + * @param instruction Instruction value to be used in instruction phase + * @param alt Alt value to be used in instruction phase + * @param address Address to be accessed in QSPI 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 QSPI_STATUS_SUCCESS on successful reads and QSPI_STATUS_ERROR on failed reads. + */ + qspi_status_t read(unsigned int instruction, unsigned int alt, unsigned int address, char *rx_buffer, size_t *rx_length); + + /** Write to QSPI peripheral using custom write instruction, alt values + * + * @param instruction Instruction value to be used in instruction phase + * @param alt Alt value to be used in instruction phase + * @param address Address to be accessed in QSPI 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 QSPI_STATUS_SUCCESS on successful reads and QSPI_STATUS_ERROR on failed reads. + */ + qspi_status_t write(unsigned int instruction, unsigned int alt, unsigned 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 + * @param address Some instruction might require address. Use -1 for ignoring the address value + * @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 QSPI_STATUS_SUCCESS on successful reads and QSPI_STATUS_ERROR on failed reads. + */ + qspi_status_t command_transfer(unsigned int instruction, int address, const char *tx_buffer, size_t tx_length, const char *rx_buffer, size_t rx_length); + + /** Acquire exclusive access to this SPI bus + */ + virtual void lock(void); + + /** Release exclusive access to this SPI bus + */ + virtual void unlock(void); + +public: + virtual ~QSPI() { + } + +protected: + qspi_t _qspi; + + bool acquire(void); + static QSPI *_owner; + static SingletonPtr _mutex; + qspi_bus_width_t _inst_width; //Bus width for Instruction phase + qspi_bus_width_t _address_width; //Bus width for Address phase + qspi_address_size_t _address_size; + qspi_bus_width_t _alt_width; //Bus width for Alt phase + qspi_alt_size_t _alt_size; + qspi_bus_width_t _data_width; //Bus width for Data phase + qspi_command_t _qspi_command; //QSPI 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 _qspi_io0, _qspi_io1, _qspi_io2, _qspi_io3, _qspi_clk, _qspi_cs; //IO lines, clock and chip select + +private: + /* Private acquire function without locking/unlocking + * Implemented in order to avoid duplicate locking and boost performance + */ + bool _acquire(void); + bool _initialize(); + + /* + * This function builds the qspi command struct to be send to Hal + */ + inline void _build_qspi_command(int instruction, int address, int alt); +}; + +} // namespace mbed + +#endif + +#endif diff --git a/features/filesystem/bd/QSPIFBlockDevice.cpp b/features/filesystem/bd/QSPIFBlockDevice.cpp new file mode 100755 index 00000000000..e69f4e5d9f9 --- /dev/null +++ b/features/filesystem/bd/QSPIFBlockDevice.cpp @@ -0,0 +1,1297 @@ + +/* mbed Microcontroller Library + * Copyright (c) 2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#include "./mbed-os/drivers/qspi.h" + +#define DEVICE_QSPI 1 + +#include "QSPIFBlockDevice.h" + +/* Default QSPIF Parameters */ +/****************************/ +#define QSPIF_DEFAULT_READ_SIZE 1 +#define QSPIF_DEFAULT_PROG_SIZE 1 +#define QSPIF_DEFAULT_SE_SIZE 4096 +#define QSPI_MAX_STATUS_REGISTER_SIZE 2 +#define QSPI_STATUS_REGISTER_WRITE_TIMEOUT_MSEC 50 +#define QSPIF_DEFAULT_TIMEOUT_MSEC 1 + + +/* SFDP Header Parsing */ +/***********************/ +#define QSPIF_SFDP_HEADER_SIZE 8 +#define QSPIF_PARAM_HEADER_SIZE 8 + +/* Basic Parameters Table Parsing */ +/**********************************/ +#define SFDP_DEFAULT_BASIC_PARAMS_TABLE_SIZE_BYTES 64 /* 16 DWORDS */ +//READ Instruction support according to BUS Configuration +#define QSPIF_BASIC_PARAM_TABLE_FAST_READ_SUPPORT_BYTE 2 +#define QSPIF_BASIC_PARAM_TABLE_QPI_READ_SUPPOR_BYTE 16 +#define QSPIF_BASIC_PARAM_TABLE_444_READ_INST_BYTE 27 +#define QSPIF_BASIC_PARAM_TABLE_144_READ_INST_BYTE 9 +#define QSPIF_BASIC_PARAM_TABLE_114_READ_INST_BYTE 11 +#define QSPIF_BASIC_PARAM_TABLE_222_READ_INST_BYTE 23 +#define QSPIF_BASIC_PARAM_TABLE_122_READ_INST_BYTE 15 +#define QSPIF_BASIC_PARAM_TABLE_112_READ_INST_BYTE 13 +#define QSPIF_BASIC_PARAM_TABLE_PAGE_SIZE_BYTE 40 +// Quad Enable Params +#define QSPIF_BASIC_PARAM_TABLE_QER_BYTE 58 +#define QSPIF_BASIC_PARAM_TABLE_444_MODE_EN_SEQ_BYTE 56 +// Erase Types Params +#define QSPIF_BASIC_PARAM_ERASE_TYPE_1_BYTE 29 +#define QSPIF_BASIC_PARAM_ERASE_TYPE_2_BYTE 31 +#define QSPIF_BASIC_PARAM_ERASE_TYPE_3_BYTE 33 +#define QSPIF_BASIC_PARAM_ERASE_TYPE_4_BYTE 35 +#define QSPIF_BASIC_PARAM_ERASE_TYPE_1_SIZE_BYTE 28 +#define QSPIF_BASIC_PARAM_ERASE_TYPE_2_SIZE_BYTE 30 +#define QSPIF_BASIC_PARAM_ERASE_TYPE_3_SIZE_BYTE 32 +#define QSPIF_BASIC_PARAM_ERASE_TYPE_4_SIZE_BYTE 34 +#define QSPIF_BASIC_PARAM_4K_ERASE_TYPE_BYTE 1 + +// Debug Printouts +#define QSPIF_DEBUG_ERROR 1 +#define QSPIF_DEBUG_WARNING 2 +#define QSPIF_DEBUG_INFO 3 +#define QSPIF_DEBUG_DEBUG 4 +#define QSPIF_DEBUG_TRACE 5 +#define QSPIF_DEFAULT_DEBUG_LEVEL QSPIF_DEBUG_INFO + + + +enum qspif_default_instructions { + QSPIF_NOP = 0x00, // No operation + QSPIF_PP = 0x02, // Page Program data + QSPIF_READ = 0x03, // Read data + QSPIF_SE = 0x20, // 4KB Sector Erase + QSPIF_SFDP = 0x5a, // Read SFDP + + QSPIF_WRSR = 0x01, // Write Status/Configuration Register + QSPIF_WRDI = 0x04, // Write Disable + QSPIF_RDSR = 0x05, // Read Status Register + QSPIF_WREN = 0x06, // Write Enable + + QSPIF_RSTEN = 0x66, // Reset Enable + QSPIF_RST = 0x99, // Reset + QSPIF_RDID = 0x9f, // Read Manufacturer and JDEC Device ID +}; + +#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) + +#define QSPIF_LOG(level,...) {\ + if (level <= QSPIF_DEFAULT_DEBUG_LEVEL) {\ + char str[100];\ + sprintf(str, "\n[%s][%s:%d], ", __FILENAME__, __FUNCTION__, __LINE__);\ + sprintf(str+strlen(str),__VA_ARGS__);\ + printf(str);\ + }\ + } + +// Mutex is used for some QSPI Driver commands that must be done sequentially with no other commands in between +// e.g. (1)Set Write Enable, (2)Program, (3)Wait Memory Ready +SingletonPtr QSPIFBlockDevice::_mutex; + + +/********* Public API Functions *********/ +/****************************************/ + +QSPIFBlockDevice::QSPIFBlockDevice(PinName io0, PinName io1, PinName io2, PinName io3, PinName sclk, PinName csel, int clock_mode, int freq) + : _qspi(io0, io1, io2, io3, sclk, csel, clock_mode), _deviceSizeBytes(0) +{ + //_cs = 1; + is_initialized = false; + _minCommonEraseSize = 0; + _regions_count = 1; + _region_erase_types[0] = 0; + + //Default Bus Setup 1_1_1 with 0 dummy and mode cycles + _inst_width = QSPI_CFG_BUS_SINGLE; + _address_width = QSPI_CFG_BUS_SINGLE; + _address_size = QSPI_CFG_ADDR_SIZE_24; + _alt_width = QSPI_CFG_BUS_SINGLE; + _alt_size = QSPI_CFG_ALT_SIZE_8; + _data_width = QSPI_CFG_BUS_SINGLE; + _dummy_and_mode_cycles = 0; + + if (QSPI_STATUS_OK != _qspiSetFrequency(freq)) + { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: QSPI Set Frequency Failed"); + } +} + + +int QSPIFBlockDevice::init() +{ + + uint8_t vendor_device_ids[4]; + size_t data_length = 3; + + _mutex->lock(); + if (is_initialized == true) + { + _mutex->unlock(); + return 0; + } + + // Soft Reset + if( -1 == _resetFlashMem()) { + QSPIF_LOG(QSPIF_DEBUG_INFO,"ERROR: init - Unable to initialize flash memory, tests failed\n"); + return -1; + } else { + QSPIF_LOG(QSPIF_DEBUG_INFO,"INFO: Initialize flash memory OK\n"); + } + + + + /* Read Manufacturer ID (1byte), and Device ID (2bytes)*/ + int status = _qspiSendReadCommand(QSPIF_RDID, (char *)vendor_device_ids, 0x0 /*address*/, data_length); + if (status != QSPI_STATUS_OK) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: init - Read Vendor ID Failed"); + return status; + } + + switch (vendor_device_ids[0]) { + case 0xbf: + // SST devices come preset with block protection + // enabled for some regions, issue write disable instruction to clear + _setWriteEnable(); + //_cmdwrite(0x98, 0, 0, 0x0, NULL); + _qspiSendGeneralCommand(QSPIF_WRDI, -1, NULL, 0, NULL, 0); + + break; + } + + //Synchronize Device + if( false == _isMemReady()) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: init - _isMemReady Failed"); + return BD_ERROR_DEVICE_ERROR; + } + + + /**************************** Parse SFDP Header ***********************************/ + uint32_t basic_table_addr = NULL; + size_t basic_table_size = 0; + uint32_t sector_map_table_addr = NULL; + size_t sector_map_table_size = 0; + if ( 0 != _sfdpParseSFDPHeaders(basic_table_addr, basic_table_size, sector_map_table_addr, sector_map_table_size)) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: init - Parse SFDP Headers Failed"); + return BD_ERROR_DEVICE_ERROR; + } + + + /**************************** Parse Basic Parameters Table ***********************************/ + if ( 0 != _sfdpParseBasicParamTable(basic_table_addr, basic_table_size) ) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: init - Parse Basic Param Table Failed"); + return BD_ERROR_DEVICE_ERROR; + } + + + /**************************** Parse Sector Map Table ***********************************/ + _region_size_bytes[0] = _deviceSizeBytes; // If there's no region map, we have a single region sized the entire device size + _region_high_boundary[0] = _deviceSizeBytes - 1; + + if ( (sector_map_table_addr != NULL) && (0 != sector_map_table_size) ) { + QSPIF_LOG(QSPIF_DEBUG_INFO,"INFO: init - Parsing Sector Map Table - addr: 0x%xh, Size: %d", sector_map_table_addr, sector_map_table_size); + if ( 0 != _sfdpParseSectorMapTable(sector_map_table_addr, sector_map_table_size) ) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: init - Parse Sector Map Table Failed"); + return BD_ERROR_DEVICE_ERROR; + } + } + + + // Configure BUS Mode to 1_1_1 for all commands other than Read + _qspiConfiureFormat( QSPI_CFG_BUS_SINGLE, QSPI_CFG_BUS_SINGLE, QSPI_CFG_ADDR_SIZE_24, QSPI_CFG_BUS_SINGLE, QSPI_CFG_ALT_SIZE_8, QSPI_CFG_BUS_SINGLE, 0); + /*// Soft Reset + if( -1 == _resetFlashMem()) { + QSPIF_LOG(QSPIF_DEBUG_INFO,"ERROR: init - Unable to initialize flash memory, tests failed\n"); + return -1; + } else { + QSPIF_LOG(QSPIF_DEBUG_INFO,"INFO: Initialize flash memory OK\n"); + }*/ + + is_initialized = true; + _mutex->unlock(); + + return 0; +} + +int QSPIFBlockDevice::deinit() +{ + _mutex->lock(); + if (is_initialized == false) + { + _mutex->unlock(); + return 0; + } + qspi_status_t status = _qspiSendGeneralCommand(QSPIF_WRDI, -1, NULL, 0, NULL, 0); + int result = 0; + + if (status != QSPI_STATUS_OK) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: Write Disable failed"); + result = -1; + } + is_initialized = false; + _mutex->unlock(); + + return result; +} + + +int QSPIFBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size) +{ + + + int status = 0; + + QSPIF_LOG(QSPIF_DEBUG_INFO,"INFO Inst: 0x%xh", _readInstruction); + + _mutex->lock(); + + _qspiConfiureFormat( + _inst_width, //Bus width for Instruction phase + _address_width, //Bus width for Address phase + _address_size, + _alt_width, //Bus width for Alt phase + _alt_size, + _data_width, //Bus width for Data phase + _dummy_and_mode_cycles); + //_qspiConfiureFormat( QSPI_CFG_BUS_SINGLE, QSPI_CFG_BUS_SINGLE, QSPI_CFG_ADDR_SIZE_24, QSPI_CFG_BUS_SINGLE, QSPI_CFG_ALT_SIZE_8, QSPI_CFG_BUS_SINGLE, 8); + + if (QSPI_STATUS_OK != _qspiSendReadCommand(_readInstruction, buffer, addr, size)) { + status = -1; + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: Read failed\n"); + } + + // All commands other than Read use default 1-1-1 Bus mode (Program/Erase are constrained by flash memory performance less than that of the bus) + _qspiConfiureFormat( QSPI_CFG_BUS_SINGLE, QSPI_CFG_BUS_SINGLE, QSPI_CFG_ADDR_SIZE_24, QSPI_CFG_BUS_SINGLE, QSPI_CFG_ALT_SIZE_8, QSPI_CFG_BUS_SINGLE, 0); + + _mutex->unlock(); + return status; + +} + + + + +int QSPIFBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size) +{ + qspi_status_t result = QSPI_STATUS_OK; + bool program_failed = false; + int status = 0; + uint32_t offset = 0; + uint32_t chunk = 0; + bd_size_t writtenBytes = 0; + + while (size > 0) { + + // Write on _pageSizeBytes boundaries (Default 256 bytes a page) + offset = addr % _pageSizeBytes; + chunk = (offset + size < _pageSizeBytes) ? size : (_pageSizeBytes); + writtenBytes = chunk; + + _mutex->lock(); + + //Send WREN + if (_setWriteEnable() != 0) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: Write Enabe failed\n"); + program_failed = true; + goto Exit_Point; + } + + if( false == _isMemReady()) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: Device not ready, write failed\n"); + program_failed = true; + goto Exit_Point; + } + + result = _qspiSendProgramCommand(_progInstruction, buffer, addr, &writtenBytes); + if( (result != QSPI_STATUS_OK) || (chunk != writtenBytes) ) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: Write failed"); + program_failed = true; + goto Exit_Point; + } + + buffer = static_cast(buffer) + chunk; + addr += chunk; + size -= chunk; + + wait_ms(QSPIF_DEFAULT_TIMEOUT_MSEC); + + if( false == _isMemReady()) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: Device not ready after write, failed\n"); + program_failed = true; + goto Exit_Point; + } + _mutex->unlock(); + + + } + + Exit_Point: + if (program_failed) { + _mutex->unlock(); + status = -1; + } + + return status; +} + + +int QSPIFBlockDevice::erase(bd_addr_t addr, bd_size_t inSize) +{ + + int type = 0; + uint32_t chunk = 4096; + unsigned int curEraseInst = _eraseInstruction; + int size = (int)inSize; + bool erase_failed = false; + int status = 0; + // Find region of erased address + int region = _utilsFindAddrRegion((int)addr); + // Erase Types of selected region + uint8_t bitfield = _region_erase_types[region]; + + + while (size > 0) { + + // iterate to find next Largest erase type (1) supported by region, 2) smaller than size) + // find the matching instruction and erase size chunk for that type. + type = _utilsIterateNextLargestEraseType(bitfield, (int)size, (int)addr, _region_high_boundary[region]); + curEraseInst = _eraseTypeInstArr[type]; + chunk = _eraseTypeSizeArr[type]; + + QSPIF_LOG(QSPIF_DEBUG_DEBUG," Debug: addr: 0x%xh, size:%d, Inst: 0x%xh, chunk: %d , ", + (int)addr, (int)size, curEraseInst, chunk); + + _mutex->lock(); + + if (_setWriteEnable() != 0) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: QSPI Erase - Write Enable failed"); + erase_failed = true; + goto Exit_Point; + } + + if( false == _isMemReady()) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: QSPI Erase Device not ready - failed"); + erase_failed = true; + goto Exit_Point; + } + + if (QSPI_STATUS_OK != _qspiSendEraseCommand(curEraseInst, addr, size) ) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: QSPI Erase command failed!"); + erase_failed = true; + goto Exit_Point; + } + + addr += chunk; + size -= chunk; + + if ( (size > 0) && (addr > _region_high_boundary[region]) ) { + // erase crossed to next region + region++; + bitfield = _region_erase_types[region]; + } + wait_ms(QSPIF_DEFAULT_TIMEOUT_MSEC); + if( false == _isMemReady()) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: QSPI After Erase Device not ready - failed\n"); + erase_failed = true; + goto Exit_Point; + } + _mutex->unlock(); + + } + + +Exit_Point: + if (erase_failed) { + _mutex->unlock(); + status = -1; + } + + return status; +} + + + +bd_size_t QSPIFBlockDevice::get_read_size() const +{ + return QSPIF_DEFAULT_READ_SIZE; +} + +bd_size_t QSPIFBlockDevice::get_program_size() const +{ + return QSPIF_DEFAULT_PROG_SIZE; +} + +bd_size_t QSPIFBlockDevice::get_erase_size() const +{ + return _minCommonEraseSize; +} + + +// Find minimal erase size supported by address region +bd_size_t QSPIFBlockDevice::get_erase_size(bd_addr_t addr) +{ + // Find region of current address + int region = _utilsFindAddrRegion((int)addr); + + int minRegionEraseSize = _minCommonEraseSize; + int8_t type_mask = 0x01; + int i_ind = 0; + + if (region != -1) { + type_mask = 0x01; + + for (i_ind = 0; i_ind < 4; i_ind++) { + // loop through erase types supported by region + if (_region_erase_types[region] & type_mask) { + + minRegionEraseSize = _eraseTypeSizeArr[i_ind]; + break; + } + type_mask = type_mask << 1; + } + + if (i_ind == 4) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: no erase type was found for region addr"); + } + } + + return (bd_size_t)minRegionEraseSize; + +} + +bd_size_t QSPIFBlockDevice::size() const +{ + return _deviceSizeBytes; +} + + +/*********************************************************/ +/********** SFDP Parsing and Detection Functions *********/ +/*********************************************************/ + +int QSPIFBlockDevice::_sfdpParseSectorMapTable(uint32_t sector_map_table_addr, size_t sector_map_table_size) +{ + uint8_t sector_map_table[SFDP_DEFAULT_BASIC_PARAMS_TABLE_SIZE_BYTES]; /* Up To 16 DWORDS = 64 Bytes */ + uint32_t tmpRegionSize = 0; + int i_ind = 0; + int prevBoundary = 0; + // Default set to all type bits 1-4 are common + _minCommonEraseType = 0x0F; + + qspi_status_t status = _qspiSendReadCommand(QSPIF_SFDP, (char *)sector_map_table, sector_map_table_addr /*address*/, sector_map_table_size); + if (status != QSPI_STATUS_OK) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: init - Read SFDP First Table Failed"); + return -1; + } + + //QSPIF_LOG(QSPIF_DEBUG_DEBUG,\nDEBUG: "Read table: %x %x %x %x %x %x %x %x %x[56] %x[57] %x[58] %x[59] %x[52]\n",sector_map_table[0], + // sector_map_table[1], + // sector_map_table[2], + // sector_map_table[3], + // sector_map_table[4], + // sector_map_table[5], + // sector_map_table[6], + // sector_map_table[7], sector_map_table[56], sector_map_table[57], sector_map_table[58], sector_map_table[59], sector_map_table[52]); + + // Currently we support only Single Map Descriptor + if (! ( (sector_map_table[0] & 0x3) == 0x03 ) && (sector_map_table[1] == 0x0) ) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: Sector Map - Supporting Only Single! Map Descriptor (not map commands)"); + return -1; + } + + _regions_count = sector_map_table[2] + 1; + if (_regions_count > QSPIF_MAX_REGIONS) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: Supporting up to %d regions, current setup to %d regions - fail", QSPIF_MAX_REGIONS, _regions_count); + return -1; + } + + // Loop through Regions and set for each one: size, supported erase types, high boundary offset + // Calculate minimum Common Erase Type for all Regions + for (i_ind = 0; i_ind < _regions_count; i_ind++) { + tmpRegionSize = ((*((uint32_t *)§or_map_table[(i_ind+1)*4])) >> 8) & 0x00FFFFFF; // bits 9-32 + _region_size_bytes[i_ind] = (tmpRegionSize +1) * 256; // Region size is 0 based multiple of 256 bytes; + _region_erase_types[i_ind] = sector_map_table[(i_ind+1)*4] & 0x0F; // bits 1-4 + _minCommonEraseType &= _region_erase_types[i_ind]; + _region_high_boundary[i_ind] = (_region_size_bytes[i_ind]-1) + prevBoundary; + prevBoundary = _region_high_boundary[i_ind]+1; + } + + // Calc minimum Common Erase Size from _minCommonEraseType + uint8_t type_mask = 0x01; + for (i_ind = 0; i_ind < 4; i_ind++) { + if (_minCommonEraseType & type_mask) { + _minCommonEraseType = i_ind; + _minCommonEraseSize = _eraseTypeSizeArr[i_ind]; + break; + } + type_mask = type_mask << 1; + } + + if (i_ind == 4) { + // No common erase type was found between regions + _minCommonEraseType = 0; + _minCommonEraseSize = -1; + } + + return 0; +} + + +int QSPIFBlockDevice::_sfdpParseBasicParamTable(uint32_t basic_table_addr, size_t basic_table_size) +{ + uint8_t param_table[SFDP_DEFAULT_BASIC_PARAMS_TABLE_SIZE_BYTES]; /* Up To 16 DWORDS = 64 Bytes */ + + qspi_status_t status = _qspiSendReadCommand(QSPIF_SFDP, (char *)param_table, basic_table_addr /*address*/, basic_table_size); + if (status != QSPI_STATUS_OK) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: init - Read SFDP First Table Failed"); + return -1; + } + + QSPIF_LOG(QSPIF_DEBUG_DEBUG,"DEBUG: Read table: %x %x %x %x %x %x %x %x %x[56] %x[57] %x[58] %x[59] %x[52]\n",param_table[0], + param_table[1], + param_table[2], + param_table[3], + param_table[4], + param_table[5], + param_table[6], + param_table[7], param_table[56], param_table[57], param_table[58], param_table[59], param_table[52]); + + + // Check address size, currently only supports 3byte addresses + if ((param_table[2] & 0x4) != 0 || (param_table[7] & 0x80) != 0) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: init - verify 3byte addressing Failed"); + return -1; + } + + // Get device density (stored in bits - 1) + uint32_t density_bits = ( + (param_table[7] << 24) | + (param_table[6] << 16) | + (param_table[5] << 8 ) | + param_table[4] ); + _deviceSizeBytes = (density_bits + 1) / 8; + + // Set Default read/program/erase Instructions + _readInstruction = QSPIF_READ; + _progInstruction = QSPIF_PP; + _eraseInstruction = QSPIF_SE; + + // Set Page Size (QSPI write must be done on Page limits) + _pageSizeBytes = _sfdpDetectPageSize(param_table); + + // Detect and Set Erase Types + bool shouldSetQuadEnable = false; + bool isQPIMode = false; + _sfdpDetectEraseTypesInstAndSize(param_table, _erase4KInst, _eraseTypeInstArr, _eraseTypeSizeArr); + _eraseInstruction = _erase4KInst; + + + // Detect and Set fastest Bus mode (default 1-1-1) + _sfdpDetectBestBusReadMode(param_table, shouldSetQuadEnable, isQPIMode, _readInstruction); + + if (true == shouldSetQuadEnable) { + // Set Quad Enable and QPI Bus modes if Supported + QSPIF_LOG(QSPIF_DEBUG_INFO,"INFO: init - Setting Quad Enable"); + _sfdpSetQuadEnabled(param_table); + if (true == isQPIMode) { + QSPIF_LOG(QSPIF_DEBUG_INFO,"INFO: init - Setting QPI mode"); + _sfdpSetQPIEnabled(param_table); + } + } + return 0; +} + +int QSPIFBlockDevice::_sfdpParseSFDPHeaders(uint32_t &basic_table_addr, size_t &basic_table_size, + uint32_t §or_map_table_addr, size_t §or_map_table_size) +{ + uint8_t sfdp_header[QSPIF_SFDP_HEADER_SIZE]; + uint8_t param_header[QSPIF_PARAM_HEADER_SIZE]; + size_t data_length = QSPIF_SFDP_HEADER_SIZE; + bd_addr_t addr = 0x0; + + // Set 1-1-1 bus mode for SFDP header parsing + _qspiConfiureFormat( QSPI_CFG_BUS_SINGLE, QSPI_CFG_BUS_SINGLE, QSPI_CFG_ADDR_SIZE_24, QSPI_CFG_BUS_SINGLE, QSPI_CFG_ALT_SIZE_8, QSPI_CFG_BUS_SINGLE, 8); + + qspi_status_t status = _qspiSendReadCommand(QSPIF_SFDP, (char *)sfdp_header, addr /*address*/, data_length); + if (status != QSPI_STATUS_OK) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: init - Read SFDP Failed"); + return -1; + } + + + QSPIF_LOG(QSPIF_DEBUG_DEBUG,"DEBUG11: Read SFDP Header: %x %x %x %x %x %x %x %x\n",sfdp_header[0], + sfdp_header[1], + sfdp_header[2], + sfdp_header[3], + sfdp_header[4], + sfdp_header[5], + sfdp_header[6], + sfdp_header[7]); + + + // Verify SFDP signature for sanity + // Also check that major/minor version is acceptable + if (!(memcmp(&sfdp_header[0], "SFDP", 4) == 0 && sfdp_header[5] == 1)) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: init - _verify SFDP signature and version Failed"); + return -1; + } + else + { + QSPIF_LOG(QSPIF_DEBUG_INFO,"INFO: init - verified SFDP Signature and version Successfully"); + } + + // Discover Number of Parameter Headers + int number_of_param_headers = (int)(sfdp_header[6]) + 1; + QSPIF_LOG(QSPIF_DEBUG_DEBUG,"DEBUG: number of Param Headers: %d", number_of_param_headers); + + + addr += QSPIF_SFDP_HEADER_SIZE; + data_length = QSPIF_PARAM_HEADER_SIZE; + + // Loop over Param Headers and parse them (currently supported Basic Param Table and Sector Region Map Table) + for (int i_ind = 0; i_ind < number_of_param_headers; i_ind++) { + + status = _qspiSendReadCommand(QSPIF_SFDP, (char *)param_header, addr, data_length); + if (status != QSPI_STATUS_OK) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: init - Read Param Table %d Failed", i_ind+1); + return -1; + } + // The SFDP spec indicates the standard table is always at offset 0 + // in the parameter headers, we check just to be safe + if (param_header[2] != 1) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: Param Table %d - Major Version should be 1!", i_ind+1); + return -1; + } + + if ((param_header[0] == 0) && (param_header[7] == 0xFF)) { + // Found Basic Params Table: LSB=0x00, MSB=0xFF + QSPIF_LOG(QSPIF_DEBUG_DEBUG,"DEBUG: Found Basic Param Table at Table: %d", i_ind+1); + basic_table_addr = ( (param_header[6] << 16) | (param_header[5] << 8) | (param_header[4]) ); + // Supporting up to 64 Bytes Table (16 DWORDS) + basic_table_size = ((param_header[3]*4) < SFDP_DEFAULT_BASIC_PARAMS_TABLE_SIZE_BYTES) ? (param_header[11]*4) : 64; + + } + else if ((param_header[0] == 81) && (param_header[7] == 0xFF)) { + // Found Sector Map Table: LSB=0x81, MSB=0xFF + QSPIF_LOG(QSPIF_DEBUG_DEBUG,"DEBUG: Found Sector Map Table at Table: %d", i_ind+1); + sector_map_table_addr = ( (param_header[6] << 16) | (param_header[5] << 8) | (param_header[4]) ); + + sector_map_table_size = param_header[3] * 4; + + } + addr += QSPIF_PARAM_HEADER_SIZE; + + } + return 0; +} + + + +int QSPIFBlockDevice::_sfdpSetQPIEnabled(uint8_t *basicParamTablePtr) +{ + uint8_t config_reg[1]; + + // QPI 4-4-4 Enable Procedure is specified in 5 Bits + uint8_t en_seq_444_value = ( ((basicParamTablePtr[QSPIF_BASIC_PARAM_TABLE_444_MODE_EN_SEQ_BYTE] & 0xF0) >> 4) | ((basicParamTablePtr[QSPIF_BASIC_PARAM_TABLE_444_MODE_EN_SEQ_BYTE+1] & 0x01) << 4 )); + + switch (en_seq_444_value) { + case 1: + case 2: + QSPIF_LOG(QSPIF_DEBUG_DEBUG,"DEBUG: _setQPIEnabled - send command 38h"); + /*if (_setWriteEnable() != 0) { + printf("ERROR: Write Enabe failed\n"); + return -1; + } + if( false == _isMemReady()) { + printf("ERROR: Device not ready, write failed\n"); + return -1; + }*/ + + _qspiSendGeneralCommand(0x38, -1, NULL, 0, NULL, 0); + /*wait_ms(QSPI_STATUS_REGISTER_WRITE_TIMEOUT_MSEC); + if( false == _isMemReady()) { + printf("ERROR: Device not ready after write, failed\n"); + return -1; + }*/ + + + break; + + case 4: + QSPIF_LOG(QSPIF_DEBUG_DEBUG,"DEBUG: _setQPIEnabled - send command 35h"); + _qspiSendGeneralCommand(0x35, -1, NULL, 0, NULL, 0); + break; + + case 8: + QSPIF_LOG(QSPIF_DEBUG_DEBUG,"DEBUG: _setQPIEnabled - set config bit 6 and send command 71h"); + _qspiSendGeneralCommand(0x65, 0x800003, NULL, 0, (char *)config_reg, 1); + config_reg[1] |= 0x40; //Set Bit 6 + _qspiSendGeneralCommand(0x71, 0x800003, NULL, 0, (char *)config_reg, 1); + break; + + case 16: + QSPIF_LOG(QSPIF_DEBUG_DEBUG,"DEBUG: _setQPIEnabled - reset config bits 0-7 and send command 61h"); + _qspiSendGeneralCommand(0x65, -1, NULL, 0, (char *)config_reg, 1); + config_reg[1] &= 0x7F; //Reset Bit 7 of CR + + + + + /* + if (_setWriteEnable() != 0) { + printf("ERROR: Write Enabe failed\n"); + return -1; + } + if( false == _isMemReady()) { + printf("ERROR: Device not ready, write failed\n"); + return -1; + }*/ + + _qspiSendGeneralCommand(0x61, -1, NULL, 0, (char *)config_reg, 1); + + + /* wait_ms(QSPI_STATUS_REGISTER_WRITE_TIMEOUT_MSEC); + if( false == _isMemReady()) { + printf("ERROR: Device not ready after write, failed\n"); + return -1; + } + + + + _qspiSendGeneralCommand(0x65, -1, NULL, 0, (char *)config_reg, 1); + + if (_setWriteEnable() != 0) { + printf("ERROR: Write Enabe failed\n"); + return -1; + } + if( false == _isMemReady()) { + printf("ERROR: Device not ready, write failed\n"); + return -1; + } + config_reg[1] = 0x00; //Reset Bits 0-7 + _qspiSendGeneralCommand(0x61, -1, NULL, 0, (char *)config_reg, 1); + wait_ms(QSPI_STATUS_REGISTER_WRITE_TIMEOUT_MSEC);*/ + /* if( false == _isMemReady()) { + printf("ERROR: Device not ready after write, failed\n"); + return -1; + }*/ + + break; + + default: + QSPIF_LOG(QSPIF_DEBUG_WARNING,"WARNING: _setQPIEnabled - Unsuported En Seq 444 configuration"); + break; + + + } + + return 0; +} + + + +int QSPIFBlockDevice::_sfdpSetQuadEnabled(uint8_t *basicParamTablePtr) +{ + + int sr_read_size = QSPI_MAX_STATUS_REGISTER_SIZE; + int sr_write_size = QSPI_MAX_STATUS_REGISTER_SIZE; + + int status_reg_setup[QSPI_MAX_STATUS_REGISTER_SIZE]; + uint8_t status_reg[QSPI_MAX_STATUS_REGISTER_SIZE]; + unsigned int writeRegisterInst = QSPIF_WRSR; + unsigned int readRegisterInst = QSPIF_RDSR; + + uint8_t qer_value = (basicParamTablePtr[QSPIF_BASIC_PARAM_TABLE_QER_BYTE] & 0x70) >> 4; + + + + switch (qer_value) { + case 0: + QSPIF_LOG(QSPIF_DEBUG_DEBUG,"DEBUG: Device Does not Have a QE Bit, continue based on Read Inst"); + return 0; + + case 1: + case 4: + status_reg_setup[0] = 0; + status_reg_setup[1] = 0x02; + QSPIF_LOG(QSPIF_DEBUG_DEBUG,"DEBUG: Setting QE Bit, Bit 1 of Status Reg 2"); + break; + + case 2: + status_reg_setup[0] = 0x40; + status_reg_setup[1] = 0; + sr_write_size = 1; + sr_read_size = 1; + QSPIF_LOG(QSPIF_DEBUG_DEBUG,"DEBUG: Setting QE Bit, Bit 6 of Status Reg 1"); + break; + + case 3: + status_reg_setup[0] = 0x80; // Bit 7 of Status Reg 1 + status_reg_setup[1] = 0; + sr_write_size = 1; + sr_read_size = 1; + writeRegisterInst = 0x3E; + readRegisterInst = 0x3F; + QSPIF_LOG(QSPIF_DEBUG_DEBUG,"DEBUG: Setting QE Bit, Bit 7 of Status Reg 1"); + break; + case 5: + status_reg_setup[0] = 0; + status_reg_setup[1] = 0x02; + readRegisterInst = 0x35; + sr_read_size = 1; + QSPIF_LOG(QSPIF_DEBUG_DEBUG,"DEBUG: Setting QE Bit, Bit 1 of Status Reg 2 -special read command"); + break; + default: + QSPIF_LOG(QSPIF_DEBUG_WARNING,"WARNING: _setQuadEnable - Unsuported QER configuration"); + break; + + + } + + // Read Status Register + if (QSPI_STATUS_OK == _qspiSendGeneralCommand(readRegisterInst, -1, NULL, 0, (char *)status_reg, sr_read_size) ){ // store received values in status_value + QSPIF_LOG(QSPIF_DEBUG_DEBUG,"DEBUG: Reading Status Register Success: value = 0x%x\n", (int)status_reg[0]); + } else { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: Reading Status Register failed"); + return -1; + } + + // Set Bits for Quad Enable + status_reg[0] |= status_reg_setup[0]; + status_reg[1] |= status_reg_setup[1]; + + + // Write new Status Register Setup + if (_setWriteEnable() != 0) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: Write Enabe failed\n"); + return -1; + } + + if( false == _isMemReady()) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: Device not ready, write failed"); + return -1; + } + + if (QSPI_STATUS_OK == _qspiSendGeneralCommand(writeRegisterInst, -1, (char *)status_reg, sr_write_size, NULL, 0) ){ // Write QE to status_register + QSPIF_LOG(QSPIF_DEBUG_DEBUG,"DEBUG: _setQuadEnable - Writing Status Register Success: value = 0x%x", (int)status_reg[0]); + } else { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: _setQuadEnable - Writing Status Register failed"); + return -1; + } + + wait_ms(QSPI_STATUS_REGISTER_WRITE_TIMEOUT_MSEC); + + if( false == _isMemReady()) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: Device not ready after write, failed"); + return -1; + } + + + // For Debug + memset(status_reg, 0, QSPI_MAX_STATUS_REGISTER_SIZE); + if (QSPI_STATUS_OK == _qspiSendGeneralCommand(readRegisterInst, -1, NULL, 0, (char *)status_reg, sr_read_size) ){ // store received values in status_value + QSPIF_LOG(QSPIF_DEBUG_DEBUG,"DEBUG: Reading Status Register Success: value = 0x%x\n", (int)status_reg[0]); + } else { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: Reading Status Register failed"); + return -1; + } + + + return 0;//((status_reg[0] & QSPI_STATUS_BIT_QE) != 0 ? 0 : -1); +} + + +int QSPIFBlockDevice::_sfdpDetectPageSize(uint8_t *basicParamTablePtr) +{ + int page2PowerSize = ( (int)basicParamTablePtr[QSPIF_BASIC_PARAM_TABLE_PAGE_SIZE_BYTE]) >> 4; + int pageSize = _utilsMathPower(2, page2PowerSize); + QSPIF_LOG(QSPIF_DEBUG_DEBUG,"DEBUG: _detectPageSize - Page Size: %d", pageSize); + return pageSize; +} + + + +int QSPIFBlockDevice::_sfdpDetectEraseTypesInstAndSize(uint8_t *basicParamTablePtr, unsigned int &erase4KInst, unsigned int *eraseTypeInstArr, unsigned int *eraseTypeSizeArr) +{ + erase4KInst = 0xff; + bool found4KEraseType = false; + uint8_t bitfield = 0x01; + + // Erase 4K Inst is taken either from param table legacy 4K erase or superseded by erase Instruction for type of size 4K + erase4KInst = basicParamTablePtr[QSPIF_BASIC_PARAM_4K_ERASE_TYPE_BYTE]; + + // Loop Erase Types 1-4 + for (int i_ind=0; i_ind < 4; i_ind++) { + eraseTypeInstArr[i_ind] = 0xff; //0xFF default for unsupported type + eraseTypeSizeArr[i_ind] = _utilsMathPower(2, basicParamTablePtr[QSPIF_BASIC_PARAM_ERASE_TYPE_1_SIZE_BYTE+2*i_ind]); + if (eraseTypeSizeArr[i_ind] > 1) { + // if size==1 type is not supported + eraseTypeInstArr[i_ind] = basicParamTablePtr[QSPIF_BASIC_PARAM_ERASE_TYPE_1_BYTE+2*i_ind]; + + if ((eraseTypeSizeArr[i_ind] < _minCommonEraseSize) || (_minCommonEraseSize == 0) ) { + //Set default minimal common erase for singal region + _minCommonEraseSize = eraseTypeSizeArr[i_ind]; + } + + if (eraseTypeSizeArr[i_ind] == 4096) { + found4KEraseType = true; + if (erase4KInst != eraseTypeInstArr[i_ind]) { + //Verify 4KErase Type is identical to Legacy 4K erase type specified in Byte 1 of Param Table + erase4KInst = eraseTypeInstArr[i_ind]; + QSPIF_LOG(QSPIF_DEBUG_WARNING,"WARNING: _detectEraseTypesInstAndSize - Default 4K erase Inst is different than erase type Inst for 4K"); + + } + } + _region_erase_types[0] |= bitfield; // If there's no region map, set region "0" types as defualt; + } + + QSPIF_LOG(QSPIF_DEBUG_DEBUG,"DEBUG: Erase Type %d - Inst: 0x%xh, Size: %d", (i_ind+1), eraseTypeInstArr[i_ind], eraseTypeSizeArr[i_ind]); + bitfield = bitfield << 1; + } + + if (false == found4KEraseType) { + QSPIF_LOG(QSPIF_DEBUG_WARNING,"WARNING: Couldn't find Erase Type for 4KB size"); + } + return 0; +} + + +int QSPIFBlockDevice::_sfdpDetectBestBusReadMode(uint8_t *basicParamTablePtr, bool &setQuadEnable, bool &isQPIMode, unsigned int &readInst) { + + bool isDone = false; + + setQuadEnable = false; + isQPIMode = false; + int dummyCycles = 0; + uint8_t examinedByte = basicParamTablePtr[QSPIF_BASIC_PARAM_TABLE_QPI_READ_SUPPOR_BYTE]; + + do { // compound statement is the loop body + + + + if (examinedByte & 0x10) { + // QPI 4-4-4 Supported + readInst = basicParamTablePtr[QSPIF_BASIC_PARAM_TABLE_444_READ_INST_BYTE]; + setQuadEnable = true; + isQPIMode = true; + _dummy_and_mode_cycles = (basicParamTablePtr[QSPIF_BASIC_PARAM_TABLE_444_READ_INST_BYTE-1] >> 5) + + (basicParamTablePtr[QSPIF_BASIC_PARAM_TABLE_444_READ_INST_BYTE-1] & 0x1F); + QSPIF_LOG(QSPIF_DEBUG_DEBUG,"/nDEBUG: Read Bus Mode set to 4-4-4, Instruction: 0x%xh",_readInstruction); + //_inst_width = QSPI_CFG_BUS_QUAD; + _address_width = QSPI_CFG_BUS_QUAD; + _data_width = QSPI_CFG_BUS_QUAD; + + break; + } + + + examinedByte = basicParamTablePtr[QSPIF_BASIC_PARAM_TABLE_FAST_READ_SUPPORT_BYTE]; + if (examinedByte & 0x40) { + // Fast Read 1-4-4 Supported + readInst = basicParamTablePtr[QSPIF_BASIC_PARAM_TABLE_144_READ_INST_BYTE]; + setQuadEnable = true; + // dummy cycles + mode cycles = Dummy Cycles + _dummy_and_mode_cycles = (basicParamTablePtr[QSPIF_BASIC_PARAM_TABLE_144_READ_INST_BYTE-1] >> 5) + + (basicParamTablePtr[QSPIF_BASIC_PARAM_TABLE_144_READ_INST_BYTE-1] & 0x1F); + _address_width = QSPI_CFG_BUS_QUAD; + _data_width = QSPI_CFG_BUS_QUAD; + QSPIF_LOG(QSPIF_DEBUG_DEBUG,"/nDEBUG: Read Bus Mode set to 1-4-4, Instruction: 0x%xh",_readInstruction); + break; + } + if (examinedByte & 0x80) { + // Fast Read 1-1-4 Supported + readInst = basicParamTablePtr[QSPIF_BASIC_PARAM_TABLE_114_READ_INST_BYTE]; + setQuadEnable = true; + _dummy_and_mode_cycles = (basicParamTablePtr[QSPIF_BASIC_PARAM_TABLE_114_READ_INST_BYTE-1] >> 5) + + (basicParamTablePtr[QSPIF_BASIC_PARAM_TABLE_114_READ_INST_BYTE-1] & 0x1F); + _data_width = QSPI_CFG_BUS_QUAD; + QSPIF_LOG(QSPIF_DEBUG_DEBUG,"/nDEBUG: Read Bus Mode set to 1-1-4, Instruction: 0x%xh",_readInstruction); + break; + } + examinedByte = basicParamTablePtr[QSPIF_BASIC_PARAM_TABLE_QPI_READ_SUPPOR_BYTE]; + if (examinedByte & 0x01) { + // Fast Read 2-2-2 Supported + readInst = basicParamTablePtr[QSPIF_BASIC_PARAM_TABLE_222_READ_INST_BYTE]; + _dummy_and_mode_cycles = (basicParamTablePtr[QSPIF_BASIC_PARAM_TABLE_222_READ_INST_BYTE-1] >> 5) + + (basicParamTablePtr[QSPIF_BASIC_PARAM_TABLE_222_READ_INST_BYTE-1] & 0x1F); + _address_width = QSPI_CFG_BUS_DUAL; + _data_width = QSPI_CFG_BUS_DUAL; + QSPIF_LOG(QSPIF_DEBUG_INFO,"/nINFO: Read Bus Mode set to 2-2-2, Instruction: 0x%xh",_readInstruction); + break; + } + + examinedByte = basicParamTablePtr[QSPIF_BASIC_PARAM_TABLE_FAST_READ_SUPPORT_BYTE]; + if (examinedByte & 0x20) { + // Fast Read 1-2-2 Supported + readInst = basicParamTablePtr[QSPIF_BASIC_PARAM_TABLE_122_READ_INST_BYTE]; + _dummy_and_mode_cycles = (basicParamTablePtr[QSPIF_BASIC_PARAM_TABLE_122_READ_INST_BYTE-1] >> 5) + + (basicParamTablePtr[QSPIF_BASIC_PARAM_TABLE_122_READ_INST_BYTE-1] & 0x1F); + _address_width = QSPI_CFG_BUS_DUAL; + _data_width = QSPI_CFG_BUS_DUAL; + QSPIF_LOG(QSPIF_DEBUG_DEBUG,"/nDEBUG: Read Bus Mode set to 1-2-2, Instruction: 0x%xh",_readInstruction); + break; + } + if (examinedByte & 0x01) { + // Fast Read 1-1-2 Supported + readInst = basicParamTablePtr[QSPIF_BASIC_PARAM_TABLE_112_READ_INST_BYTE]; + _dummy_and_mode_cycles = (basicParamTablePtr[QSPIF_BASIC_PARAM_TABLE_112_READ_INST_BYTE-1] >> 5) + + (basicParamTablePtr[QSPIF_BASIC_PARAM_TABLE_112_READ_INST_BYTE-1] & 0x1F); + _data_width = QSPI_CFG_BUS_DUAL; + QSPIF_LOG(QSPIF_DEBUG_DEBUG,"/nDEBUG: Read Bus Mode set to 1-1-2, Instruction: 0x%xh",_readInstruction); + break; + } + QSPIF_LOG(QSPIF_DEBUG_DEBUG,"/nDEBUG: Read Bus Mode set to 1-1-1, Instruction: 0x%xh",_readInstruction); + isDone = true; + } + while (isDone == false); + + return 0; +} + + +int QSPIFBlockDevice::_resetFlashMem() +{ + int status = 0; + char status_value[2] = {0}; + QSPIF_LOG(QSPIF_DEBUG_ERROR,"INFO: _resetFlashMem:\n"); + //Read the Status Register from device + if (QSPI_STATUS_OK == _qspiSendGeneralCommand(QSPIF_RDSR, -1, NULL, 0, status_value, 1) ){ // store received values in status_value + QSPIF_LOG(QSPIF_DEBUG_DEBUG,"DEBUG: Reading Status Register Success: value = 0x%x\n", (int)status_value[0]); + } else { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: Reading Status Register failed\n"); + status = -1; + } + + if(0 == status) + { + //Send Reset Enable + if (QSPI_STATUS_OK == _qspiSendGeneralCommand(QSPIF_RSTEN, -1, NULL, 0, NULL, 0) ) { // store received values in status_value + QSPIF_LOG(QSPIF_DEBUG_DEBUG,"DEBUG: Sending RSTEN Success\n"); + } else { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: Sending RSTEN failed\n"); + status = -1; + } + + + if(0 == status) + { + //Send Reset + if (QSPI_STATUS_OK == _qspiSendGeneralCommand(QSPIF_RST, -1, NULL, 0, NULL, 0)) { // store received values in status_value + QSPIF_LOG(QSPIF_DEBUG_DEBUG,"DEBUG: Sending RST Success\n"); + } else { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: Sending RST failed\n"); + status = -1; + } + + _isMemReady(); + } + } + + return status; +} + + +bool QSPIFBlockDevice::_isMemReady() +{ + char status_value[2]; + int retries = 0; + bool memReady = true; + + do + { + retries++; + //Read the Status Register from device + if (QSPI_STATUS_OK != _qspiSendGeneralCommand(QSPIF_RDSR, -1, NULL, 0, status_value, 2)) { // store received values in status_value + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: Reading Status Register failed\n"); + } + } while( (status_value[0] & 0x1) != 0 && retries <10000 ); + + if((status_value[0] & 0x1) != 0) { + QSPIF_LOG(QSPIF_DEBUG_DEBUG,"ERROR: _isMemReady FALSE\n"); + memReady = false; + } + return memReady; +} + + +int QSPIFBlockDevice::_setWriteEnable() +{ + int status = 0; + if (QSPI_STATUS_OK != _qspiSendGeneralCommand(QSPIF_WREN, -1, NULL, 0, NULL, 0)) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR:Sending WREN command FAILED\n"); + status = -1; + } + return status; +} + + + +/*********************************************/ +/************* Utility Functions *************/ +/*********************************************/ +int QSPIFBlockDevice::_utilsMathPower(int base, int exp) +{ + int result = 1; + while(exp) { + result *= base; + exp--; + } + return result; +} + + +int QSPIFBlockDevice::_utilsFindAddrRegion(int offset) +{ + if ((offset > (int)_deviceSizeBytes) || (_regions_count == 0)){ + return -1; + } + + if (_regions_count == 1) { + return 0; + } + + for (int i_ind = _regions_count-2; i_ind >= 0; i_ind--) { + + if (offset > _region_high_boundary[i_ind]) { + return (i_ind+1); + } + } + return -1; + +} + + +int QSPIFBlockDevice::_utilsIterateNextLargestEraseType(uint8_t &bitfield, int size, int offset, int boundry) +{ + uint8_t type_mask = 0x08; + int i_ind = 0; + int largestEraseType = 0; + for (i_ind = 3; i_ind >= 0; i_ind--) { + if (bitfield & type_mask) { + largestEraseType = i_ind; + if ( (size > _eraseTypeSizeArr[largestEraseType]) && + ((boundry - offset) > _eraseTypeSizeArr[largestEraseType]) ) { + break; + } + else { + bitfield &= ~type_mask; + } + } + type_mask = type_mask >> 1; + } + + if (i_ind == 4) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: no erase type was found for current region addr"); + } + return largestEraseType; + +} + + +/***************************************************/ +/*********** QSPI Driver API Functions *************/ +/***************************************************/ + +qspi_status_t QSPIFBlockDevice::_qspiSetFrequency(int freq) +{ + return _qspi.set_frequency(freq); +} + + +qspi_status_t QSPIFBlockDevice::_qspiSendReadCommand(unsigned int readInst, void *buffer, bd_addr_t addr, bd_size_t size) +{ + // Check the address and size fit onto the chip. + + /* OFR_DBG */ + //MBED_ASSERT(is_valid_read(addr, size)); + + size_t bufLen = size; + + if(_qspi.read( readInst, -1, (unsigned int )addr, (char *)buffer, &bufLen) != QSPI_STATUS_OK ) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: Read failed"); + return QSPI_STATUS_ERROR; + } + + return QSPI_STATUS_OK; + +} + + +qspi_status_t QSPIFBlockDevice::_qspiSendProgramCommand(unsigned int progInst, const void *buffer, bd_addr_t addr, bd_size_t *size) +{ + // Check the address and size fit onto the chip. + //MBED_ASSERT(is_valid_program(addr, (*size))); + qspi_status_t result = QSPI_STATUS_OK; + + result = _qspi.write( progInst, -1, addr/*(((unsigned int)addr) & 0x00FFFF00)*/, (char *)buffer, (size_t *)size); + if(result != QSPI_STATUS_OK) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: QSPI Write failed"); + } + + return result; +} + + +qspi_status_t QSPIFBlockDevice::_qspiSendEraseCommand(unsigned int eraseInst, bd_addr_t addr, bd_size_t size) +{ + // Check the address and size fit onto the chip. + //MBED_ASSERT(is_valid_erase(addr, size)); + + qspi_status_t result = QSPI_STATUS_OK; + + result = _qspi.command_transfer(eraseInst, // command to send + (((int)addr) & 0x00FFF000), // Align addr to 4096 + NULL, // do not transmit + 0, // do not transmit + NULL, // just receive two bytes of data + 0); // store received values in status_value + if (QSPI_STATUS_OK != result) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR: QSPI Erase failed"); + } + + return result; + +} + + +qspi_status_t QSPIFBlockDevice::_qspiSendGeneralCommand(unsigned int instruction, bd_addr_t addr, const char *tx_buffer, size_t tx_length, const char *rx_buffer, size_t rx_length) +{ + + + qspi_status_t status = _qspi.command_transfer(instruction, (int)addr, tx_buffer, tx_length, rx_buffer, rx_length); + + if (QSPI_STATUS_OK != status) { + QSPIF_LOG(QSPIF_DEBUG_ERROR,"ERROR:Sending Generic command: %x", instruction); + } + + return status; +} + + +qspi_status_t QSPIFBlockDevice::_qspiConfiureFormat(qspi_bus_width_t inst_width, qspi_bus_width_t address_width, qspi_address_size_t address_size, qspi_bus_width_t alt_width, qspi_alt_size_t alt_size, qspi_bus_width_t data_width, int dummy_cycles) +{ + _qspi.configure_format( inst_width, address_width, address_size, alt_width, alt_size, data_width, dummy_cycles); + + return QSPI_STATUS_OK; +} + + + + + diff --git a/features/filesystem/bd/QSPIFBlockDevice.h b/features/filesystem/bd/QSPIFBlockDevice.h new file mode 100755 index 00000000000..2bcf8fab1a5 --- /dev/null +++ b/features/filesystem/bd/QSPIFBlockDevice.h @@ -0,0 +1,225 @@ +/* mbed Microcontroller Library + * Copyright (c) 2016 ARM Limited + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MBED_QSPIF_BLOCK_DEVICE_H +#define MBED_QSPIF_BLOCK_DEVICE_H + + +#define DEVICE_QSPI 1 + +#include +#include "BlockDevice.h" +//#include "./mbed-os/drivers/QSPI.h" + +#define QSPIF_MAX_REGIONS 4 + +class QSPIFBlockDevice : public BlockDevice { +public: + /** Creates a QSPIFBlockDevice on a SPI bus specified by pins + * + * @param mosi SPI master out, slave in pin + * @param miso SPI master in, slave out pin + * @param sclk SPI clock pin + * @param csel SPI chip select pin + * @param freq Clock speed of the SPI bus (defaults to 40MHz) + */ + + /** Creates a QSPIFBlockDevice on an SPI bus specified by 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 sclk QSPI Clock pin + * @param csel QSPI chip select pin + * @param clock_mode specifies the SPI Clock Polarity mode(Mode=0 uses CPOL=0, CPHA=0, Mode=1 uses CPOL=1, CPHA=1) + * default value = 0 + * @param freq Clock frequency of the SPI bus (defaults to 40MHz) + * + */ + QSPIFBlockDevice(PinName io0, PinName io1, PinName io2, PinName io3, PinName sclk, PinName csel, int clock_mode, int freq=40000000); + + /** Initialize a block device + * + * @return 0 on success or a negative error code on failure + */ + virtual int init(); + + /** Deinitialize a block device + * + * @return 0 on success or a negative error code on failure + */ + virtual int 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 0 on success, negative error code on failure + */ + virtual int read(void *buffer, bd_addr_t addr, bd_size_t size); + + /** Program blocks to a block device + * + * The blocks must have been erased prior to being programmed + * + * @param buffer Buffer of data to write to blocks + * @param addr Address of block to begin writing to + * @param size Size to write in bytes, must be a multiple of program block size + * @return 0 on success, negative error code on failure + */ + virtual int program(const void *buffer, bd_addr_t addr, bd_size_t size); + + /** Erase blocks on a block device + * + * The state of an erased block is undefined until it has been programmed + * + * @param addr Address of block to begin erasing + * @param size Size to erase in bytes, must be a multiple of erase block size + * @return 0 on success, negative error code on failure + */ + virtual int erase(bd_addr_t addr, bd_size_t size); + + /** Get the size of a readable block + * + * @return Size of a readable block in bytes + */ + virtual bd_size_t get_read_size() const; + + /** Get the size of a programable block + * + * @return Size of a programable block in bytes + * @note Must be a multiple of the read size + */ + virtual bd_size_t get_program_size() const; + + /** Get the size of a eraseable block + * + * @return Size of a eraseable block in bytes + * @note Must be a multiple of the program size + */ + virtual bd_size_t get_erase_size() const; + + /** Get the size of a eraseable block + * + * @param addr Address of block queried for erase sector size + * @return Size of a eraseable sector in bytes + * @note Must be a multiple of the program size + */ + bd_size_t get_erase_size(bd_addr_t addr); + + /** Get the total size of the underlying device + * + * @return Size of the underlying device in bytes + */ + virtual bd_size_t size() const; + +private: + // Internal functions + + /********************************/ + /* Calls to QSPI Driver APIs */ + /********************************/ + // Send Program => Write command to Driver + qspi_status_t _qspiSendProgramCommand(unsigned int progInstruction, const void *buffer, bd_addr_t addr, bd_size_t *size); + + // Send Read command to Driver + qspi_status_t _qspiSendReadCommand(unsigned int readInstruction, void *buffer, bd_addr_t addr, bd_size_t size); + + // Send Erase => command_transfer command to Driver + qspi_status_t _qspiSendEraseCommand(unsigned int eraseInstruction, bd_addr_t addr, bd_size_t size); + + // Send Generic command_transfer command to Driver + qspi_status_t _qspiSendGeneralCommand(unsigned int instructionint, bd_addr_t addr, const char *tx_buffer, size_t tx_length, const char *rx_buffer, size_t rx_length); + + // Send Bus configure_format command to Driver + qspi_status_t _qspiConfiureFormat(qspi_bus_width_t inst_width, qspi_bus_width_t address_width, qspi_address_size_t address_size, qspi_bus_width_t alt_width, qspi_alt_size_t alt_size, qspi_bus_width_t data_width, int dummy_cycles); + + // Send set_frequency command to Driver + qspi_status_t _qspiSetFrequency(int freq); + /********************************/ + + + // Verify registers and Reset Flash Memory + int _resetFlashMem(); + + // Configure Write Enable in Status Register + int _setWriteEnable(); + + // Wait on status register until write not-in-progress + bool _isMemReady(); + + + /* SFDP Detection and Parsing Functions */ + /****************************************/ + int _sfdpParseSFDPHeaders(uint32_t &basic_table_addr, size_t &basic_table_size, + uint32_t §or_map_table_addr, size_t §or_map_table_size); + int _sfdpParseBasicParamTable(uint32_t basic_table_addr, size_t basic_table_size); + int _sfdpParseSectorMapTable(uint32_t sector_map_table_addr, size_t sector_map_table_size); + int _sfdpDetectBestBusReadMode(uint8_t *basicParamTablePtr, bool &setQuadEnable, bool &isQPIMode, unsigned int &readInst); + int _sfdpSetQuadEnabled(uint8_t *basicParamTablePtr); + int _sfdpSetQPIEnabled(uint8_t *basicParamTablePtr); + int _sfdpDetectPageSize(uint8_t *basicParamTablePtr); + int _sfdpDetectEraseTypesInstAndSize(uint8_t *basicParamTablePtr, unsigned int &erase4KInst, unsigned int *eraseTypeInstArr, unsigned int *eraseTypeSizeArr); + + /* Utilities Functions */ + /***********************/ + int _utilsFindAddrRegion(int offset); + int _utilsIterateNextLargestEraseType(uint8_t &bitfield, int size, int offset, int boundry); + int _utilsMathPower(int base, int exp); + +private: + + // QSPI Driver Object + QSPI _qspi; + //DigitalOut _cs; + bool is_initialized; + static SingletonPtr _mutex; + + // Command Instructions + unsigned int _readInstruction; + unsigned int _progInstruction; + unsigned int _eraseInstruction; + unsigned int _erase4KInst; + unsigned int _eraseTypeInstArr[4]; + unsigned int _eraseTypeSizeArr[4]; + + // Sector Regions Map + int _regions_count; //number of regions + int _region_size_bytes[QSPIF_MAX_REGIONS]; //regions size + int _region_high_boundary[QSPIF_MAX_REGIONS]; //region high address offset boundary + uint8_t _region_erase_types[QSPIF_MAX_REGIONS]; //region erase type + int _minCommonEraseType; // minimal common erase size for all regions (-1 if none) + int _minCommonEraseSize; // minimal common erase size for all regions + + int _pageSizeBytes; // Page size - 256 Bytes default + bd_size_t _deviceSizeBytes; + + // Bus speed configuration + qspi_bus_width_t _inst_width; //Bus width for Instruction phase + qspi_bus_width_t _address_width; //Bus width for Address phase + qspi_address_size_t _address_size; + qspi_bus_width_t _alt_width; //Bus width for Alt phase + qspi_alt_size_t _alt_size; + qspi_bus_width_t _data_width; //Bus width for Data phase + int _dummy_and_mode_cycles; + + +}; + +#endif diff --git a/hal/qspi_api.h b/hal/qspi_api.h new file mode 100644 index 00000000000..10bd77130be --- /dev/null +++ b/hal/qspi_api.h @@ -0,0 +1,194 @@ + +/** \addtogroup hal */ +/** @{*/ +/* mbed Microcontroller Library + * Copyright (c) 2017 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_QSPI_API_H +#define MBED_QSPI_API_H + +#include "device.h" +#include + +#if DEVICE_QSPI + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \defgroup hal_qspi QSPI HAL + * @{ + */ + +/** QSPI HAL object + */ +typedef struct qspi_s qspi_t; + +/** QSPI Bus width + * + * Some parts of commands provide variable bus width + */ +typedef enum qspi_bus_width { + QSPI_CFG_BUS_SINGLE, + QSPI_CFG_BUS_DUAL, + QSPI_CFG_BUS_QUAD, +} qspi_bus_width_t; + +/** Address size in bits + */ +typedef enum qspi_address_size { + QSPI_CFG_ADDR_SIZE_8, + QSPI_CFG_ADDR_SIZE_16, + QSPI_CFG_ADDR_SIZE_24, + QSPI_CFG_ADDR_SIZE_32, +} qspi_address_size_t; + +/** Alternative size in bits + */ +typedef enum qspi_alt_size { + QSPI_CFG_ALT_SIZE_8, + QSPI_CFG_ALT_SIZE_16, + QSPI_CFG_ALT_SIZE_24, + QSPI_CFG_ALT_SIZE_32, +} qspi_alt_size_t; + +/** QSPI command + * + * Defines a frame format. It consists of instruction, address, alternative, dummy count and data + */ +typedef struct qspi_command { + struct { + qspi_bus_width_t bus_width; /**< Bus width for the instruction >*/ + uint8_t value; /**< Instruction value >*/ + bool disabled; /**< Instruction phase skipped if disabled is set to true >*/ + } instruction; + struct { + qspi_bus_width_t bus_width; /**< Bus width for the address >*/ + qspi_address_size_t size; /**< Address size >*/ + uint32_t value; /**< Address value >*/ + bool disabled; /**< Address phase skipped if disabled is set to true >*/ + } address; + struct { + qspi_bus_width_t bus_width; /**< Bus width for alternative >*/ + qspi_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 { + qspi_bus_width_t bus_width; /**< Bus width for data >*/ + } data; +} qspi_command_t; + +/** QSPI return status + */ +typedef enum qspi_status { + QSPI_STATUS_ERROR = -1, /**< Generic error >*/ + QSPI_STATUS_INVALID_PARAMETER = -2, /**< The parameter is invalid >*/ + QSPI_STATUS_OK = 0, /**< Function executed sucessfully >*/ +} qspi_status_t; + +/** Initialize QSPI peripheral. + * + * It should initialize QSPI pins (io0-io3, sclk and ssel), set frequency, clock polarity and phase mode. The clock for the peripheral should be enabled + * + * @param obj QSPI object + * @param io0 Data pin 0 + * @param io1 Data pin 1 + * @param io2 Data pin 2 + * @param io3 Data pin 3 + * @param sclk The clock pin + * @param ssel The chip select pin + * @param hz The bus frequency + * @param mode Clock polarity and phase mode (0 - 3) + * @return QSPI_STATUS_OK if initialisation successfully executed + QSPI_STATUS_INVALID_PARAMETER if invalid parameter found + QSPI_STATUS_ERROR otherwise + */ +qspi_status_t qspi_init(qspi_t *obj, PinName io0, PinName io1, PinName io2, PinName io3, PinName sclk, PinName ssel, uint32_t hz, uint8_t mode); + +/** Deinitilize QSPI peripheral + * + * It should release pins that are associated with the QSPI object, and disable clocks for QSPI peripheral module that was associated with the object + * + * @param obj QSPI object + * @return QSPI_STATUS_OK if deinitialisation successfully executed + QSPI_STATUS_INVALID_PARAMETER if invalid parameter found + QSPI_STATUS_ERROR otherwise + */ +qspi_status_t qspi_free(qspi_t *obj); + +/** Set the QSPI baud rate + * + * Actual frequency may differ from the desired frequency due to available dividers and the bus clock + * Configures the QSPI peripheral's baud rate + * @param obj The SPI object to configure + * @param hz The baud rate in Hz + * @return QSPI_STATUS_OK if frequency was set + QSPI_STATUS_INVALID_PARAMETER if invalid parameter found + QSPI_STATUS_ERROR otherwise + */ +qspi_status_t qspi_frequency(qspi_t *obj, int hz); + +/** Send a command and block of data + * + * @param obj QSPI object + * @param command QSPI command + * @param data TX buffer + * @param[in,out] length in - TX buffer length in bytes, out - number of bytes written + * @return QSPI_STATUS_OK if the data has been succesfully sent + QSPI_STATUS_INVALID_PARAMETER if invalid parameter found + QSPI_STATUS_ERROR otherwise + */ +qspi_status_t qspi_write(qspi_t *obj, const qspi_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 QSPI object + * @param command QSPI 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 QSPI_STATUS_OK if the data has been succesfully sent + QSPI_STATUS_INVALID_PARAMETER if invalid parameter found + QSPI_STATUS_ERROR otherwise + */ +qspi_status_t qspi_command_transfer(qspi_t *obj, const qspi_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 QSPI object + * @param command QSPI command + * @param data RX buffer + * @param[in,out] length in - RX buffer length in bytes, out - number of bytes read + * @return QSPI_STATUS_OK if data has been succesfully received + QSPI_STATUS_INVALID_PARAMETER if invalid parameter found + QSPI_STATUS_ERROR otherwise + */ +qspi_status_t qspi_read(qspi_t *obj, const qspi_command_t *command, void *data, size_t *length); + +/**@}*/ + +#ifdef __cplusplus +} +#endif + +#endif + +#endif + +/** @}*/ diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/TARGET_MCU_NRF52840/TARGET_NRF52840_DK/PinNames.h b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/TARGET_MCU_NRF52840/TARGET_NRF52840_DK/PinNames.h index 2a5f319abdb..4241cdcf479 100644 --- a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/TARGET_MCU_NRF52840/TARGET_NRF52840_DK/PinNames.h +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/TARGET_MCU_NRF52840/TARGET_NRF52840_DK/PinNames.h @@ -227,6 +227,13 @@ typedef enum { A4 = p30, A5 = p31, + QSPI_FLASH_IO0 = P0_20, + QSPI_FLASH_IO1 = P0_21, + QSPI_FLASH_IO2 = P0_22, + QSPI_FLASH_IO3 = P0_23, + QSPI_FLASH_SCK = P0_19, + QSPI_FLASH_CSN = P0_17, + // Not connected NC = (int)0xFFFFFFFF } PinName; diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/TARGET_MCU_NRF52840/config/sdk_config.h b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/TARGET_MCU_NRF52840/config/sdk_config.h index 834e1991563..a3cda38ffa9 100644 --- a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/TARGET_MCU_NRF52840/config/sdk_config.h +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/TARGET_MCU_NRF52840/config/sdk_config.h @@ -2748,6 +2748,136 @@ // +// QSPI_ENABLED - nrf_drv_qspi - QSPI peripheral driver. +//========================================================== +#ifndef QSPI_ENABLED +#define QSPI_ENABLED 1 +#endif +#if QSPI_ENABLED +// QSPI_CONFIG_SCK_DELAY - tSHSL, tWHSL and tSHWL in number of 16 MHz periods (62.5 ns). <0-255> + + +#ifndef QSPI_CONFIG_SCK_DELAY +#define QSPI_CONFIG_SCK_DELAY 1 +#endif + +// QSPI_CONFIG_READOC - Number of data lines and opcode used for reading. + +// <0=> FastRead +// <1=> Read2O +// <2=> Read2IO +// <3=> Read4O +// <4=> Read4IO + +#ifndef QSPI_CONFIG_READOC +#define QSPI_CONFIG_READOC 4 +#endif + +// QSPI_CONFIG_WRITEOC - Number of data lines and opcode used for writing. + +// <0=> PP +// <1=> PP2O +// <2=> PP4O +// <3=> PP4IO + +#ifndef QSPI_CONFIG_WRITEOC +#define QSPI_CONFIG_WRITEOC 3 +#endif + +// QSPI_CONFIG_ADDRMODE - Addressing mode. + +// <0=> 24bit +// <1=> 32bit + +#ifndef QSPI_CONFIG_ADDRMODE +#define QSPI_CONFIG_ADDRMODE 0 +#endif + +// QSPI_CONFIG_MODE - SPI mode. + +// <0=> Mode 0 +// <1=> Mode 1 + +#ifndef QSPI_CONFIG_MODE +#define QSPI_CONFIG_MODE 0 +#endif + +// QSPI_CONFIG_FREQUENCY - Frequency divider. + +// <0=> 32MHz/1 +// <1=> 32MHz/2 +// <2=> 32MHz/3 +// <3=> 32MHz/4 +// <4=> 32MHz/5 +// <5=> 32MHz/6 +// <6=> 32MHz/7 +// <7=> 32MHz/8 +// <8=> 32MHz/9 +// <9=> 32MHz/10 +// <10=> 32MHz/11 +// <11=> 32MHz/12 +// <12=> 32MHz/13 +// <13=> 32MHz/14 +// <14=> 32MHz/15 +// <15=> 32MHz/16 + +#ifndef QSPI_CONFIG_FREQUENCY +#define QSPI_CONFIG_FREQUENCY 1 +#endif + +// QSPI_PIN_SCK - SCK pin value. +#ifndef QSPI_PIN_SCK +#define QSPI_PIN_SCK NRF_QSPI_PIN_NOT_CONNECTED +#endif + +// QSPI_PIN_CSN - CSN pin value. +#ifndef QSPI_PIN_CSN +#define QSPI_PIN_CSN NRF_QSPI_PIN_NOT_CONNECTED +#endif + +// QSPI_PIN_IO0 - IO0 pin value. +#ifndef QSPI_PIN_IO0 +#define QSPI_PIN_IO0 NRF_QSPI_PIN_NOT_CONNECTED +#endif + +// QSPI_PIN_IO1 - IO1 pin value. +#ifndef QSPI_PIN_IO1 +#define QSPI_PIN_IO1 NRF_QSPI_PIN_NOT_CONNECTED +#endif + +// QSPI_PIN_IO2 - IO2 pin value. +#ifndef QSPI_PIN_IO2 +#define QSPI_PIN_IO2 NRF_QSPI_PIN_NOT_CONNECTED +#endif + +// QSPI_PIN_IO3 - IO3 pin value. +#ifndef QSPI_PIN_IO3 +#define QSPI_PIN_IO3 NRF_QSPI_PIN_NOT_CONNECTED +#endif + +// QSPI_CONFIG_IRQ_PRIORITY - Interrupt priority + + +// Priorities 0,2 (nRF51) and 0,1,4,5 (nRF52) are reserved for SoftDevice +// <0=> 0 (highest) +// <1=> 1 +// <2=> 2 +// <3=> 3 +// <4=> 4 +// <5=> 5 +// <6=> 6 +// <7=> 7 + +#ifndef QSPI_CONFIG_IRQ_PRIORITY +#define QSPI_CONFIG_IRQ_PRIORITY 7 +#endif + +#endif //QSPI_ENABLED + +// + +// + // TIMER_ENABLED - nrf_drv_timer - TIMER periperal driver //========================================================== #ifndef TIMER_ENABLED diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/objects.h b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/objects.h index 47e6276a829..39c4b60f678 100644 --- a/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/objects.h +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/TARGET_NRF52/objects.h @@ -142,6 +142,17 @@ struct flash_s { uint32_t placeholder; }; +#if DEVICE_QSPI + +#include "nrf_drv_qspi.h" + +struct qspi_s { + uint32_t placeholder; + //nrf_drv_qspi_config_t config; +}; + +#endif + #include "gpio_object.h" #ifdef __cplusplus diff --git a/targets/TARGET_NORDIC/TARGET_NRF5x/qspi_api.c b/targets/TARGET_NORDIC/TARGET_NRF5x/qspi_api.c new file mode 100644 index 00000000000..317cb029df8 --- /dev/null +++ b/targets/TARGET_NORDIC/TARGET_NRF5x/qspi_api.c @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2017 Nordic Semiconductor ASA + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list + * of conditions and the following disclaimer. + * + * 2. Redistributions in binary form, except as embedded into a Nordic Semiconductor ASA + * integrated circuit in a product or a software update for such product, must reproduce + * the above copyright notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of Nordic Semiconductor ASA nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific prior + * written permission. + * + * 4. This software, with or without modification, must only be used with a + * Nordic Semiconductor ASA integrated circuit. + * + * 5. Any software provided in binary or object form under this license must not be reverse + * engineered, decompiled, modified and/or disassembled. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "qspi_api.h" + +#if DEVICE_QSPI + +#include "nrf_drv_common.h" +#include "nrf_drv_qspi.h" + +/* +TODO + - config inside obj - nordic headers have some problems with inclusion + - free - is it really empty, nothing to do there? + - prepare command - support more protocols that nordic can do (now limited) + - nordic does not support + - alt + - dummy cycles +*/ + +#define MBED_HAL_QSPI_HZ_TO_CONFIG(hz) ((32000000/(hz))-1) +#define MBED_HAL_QSPI_MAX_FREQ 32000000UL + +static nrf_drv_qspi_config_t config; + +// Private helper function to track initialization +static ret_code_t _qspi_drv_init(void); + +qspi_status_t qspi_prepare_command(qspi_t *obj, const qspi_command_t *command, bool write) +{ + //Use custom command if provided by the caller + if(command->instruction.value != 0) { + //Use custom command if provided + if (write) { + config.prot_if.writeoc = (nrf_qspi_writeoc_t)command->instruction.value; + } else { + config.prot_if.readoc = (nrf_qspi_readoc_t)command->instruction.value; + } + } else { + // we need to remap to command-address-data - x_x_x + // most commmon are 1-1-1, 1-1-4, 1-4-4 + // 1-1-1 + if (command->instruction.bus_width == QSPI_CFG_BUS_SINGLE && + command->address.bus_width == QSPI_CFG_BUS_SINGLE && + command->data.bus_width == QSPI_CFG_BUS_SINGLE) { + if (write) { + config.prot_if.writeoc = NRF_QSPI_WRITEOC_PP; + } else { + config.prot_if.readoc = NRF_QSPI_READOC_FASTREAD; + } + // 1-1-4 + } else if (command->instruction.bus_width == QSPI_CFG_BUS_SINGLE && + command->address.bus_width == QSPI_CFG_BUS_SINGLE && + command->data.bus_width == QSPI_CFG_BUS_QUAD) { + // 1_1_4 + if (write) { + config.prot_if.writeoc = NRF_QSPI_WRITEOC_PP4O; + } else { + config.prot_if.readoc = NRF_QSPI_READOC_READ4O; + } + // 1-4-4 + } else if (command->instruction.bus_width == QSPI_CFG_BUS_SINGLE && + command->address.bus_width == QSPI_CFG_BUS_QUAD && + command->data.bus_width == QSPI_CFG_BUS_QUAD) { + // 1_4_4 + if (write) { + config.prot_if.writeoc = NRF_QSPI_WRITEOC_PP4IO; + } else { + config.prot_if.readoc = NRF_QSPI_READOC_READ4IO; + } + // 1-1-2 + } else if (command->instruction.bus_width == QSPI_CFG_BUS_SINGLE && + command->address.bus_width == QSPI_CFG_BUS_SINGLE && + command->data.bus_width == QSPI_CFG_BUS_DUAL) { + // 1-1-2 + if (write) { + config.prot_if.writeoc = NRF_QSPI_WRITEOC_PP2O; + } else { + config.prot_if.readoc = NRF_QSPI_READOC_READ2O; + } + // 1-2-2 + } else if (command->instruction.bus_width == QSPI_CFG_BUS_SINGLE && + command->address.bus_width == QSPI_CFG_BUS_DUAL && + command->data.bus_width == QSPI_CFG_BUS_DUAL) { + // 1-2-2 + if (write) { + //Currently NRF52840 does not define PP2IO, so use PP2O for 1-2-2 mode + config.prot_if.writeoc = NRF_QSPI_WRITEOC_PP2O; + } else { + config.prot_if.readoc = NRF_QSPI_READOC_READ2IO; + } + } + } + + qspi_status_t ret = QSPI_STATUS_OK; + // supporting only 24 or 32 bit address + if (command->address.size == QSPI_CFG_ADDR_SIZE_24) { + config.prot_if.addrmode = NRF_QSPI_ADDRMODE_24BIT; + } else if (command->address.size == QSPI_CFG_ADDR_SIZE_32) { + config.prot_if.addrmode = NRF_QSPI_ADDRMODE_32BIT; + } else { + ret = QSPI_STATUS_INVALID_PARAMETER; + } + + //Configure QSPI with new command format + if(ret == QSPI_STATUS_OK) { + ret_code_t ret_status = _qspi_drv_init(); + if (ret_status != NRF_SUCCESS ) { + if (ret_status == NRF_ERROR_INVALID_PARAM) { + return QSPI_STATUS_INVALID_PARAMETER; + } else { + return QSPI_STATUS_ERROR; + } + } + } + + return ret; +} + +qspi_status_t qspi_init(qspi_t *obj, PinName io0, PinName io1, PinName io2, PinName io3, PinName sclk, PinName ssel, uint32_t hz, uint8_t mode) +{ + (void)(obj); + if (hz > MBED_HAL_QSPI_MAX_FREQ) { + return QSPI_STATUS_INVALID_PARAMETER; + } + + // memset(config, 0, sizeof(config)); + + config.pins.sck_pin = (uint32_t)sclk; + config.pins.csn_pin = (uint32_t)ssel; + config.pins.io0_pin = (uint32_t)io0; + config.pins.io1_pin = (uint32_t)io1; + config.pins.io2_pin = (uint32_t)io2; + config.pins.io3_pin = (uint32_t)io3; + config.irq_priority = SPI_DEFAULT_CONFIG_IRQ_PRIORITY; + + config.phy_if.sck_freq = (nrf_qspi_frequency_t)MBED_HAL_QSPI_HZ_TO_CONFIG(hz), + config.phy_if.sck_delay = 0x05, + config.phy_if.dpmen = false; + config.phy_if.spi_mode = mode == 0 ? NRF_QSPI_MODE_0 : NRF_QSPI_MODE_1; + + //Use _qspi_drv_init private function to initialize + ret_code_t ret = _qspi_drv_init(); + if (ret == NRF_SUCCESS ) { + return QSPI_STATUS_OK; + } else if (ret == NRF_ERROR_INVALID_PARAM) { + return QSPI_STATUS_INVALID_PARAMETER; + } else { + return QSPI_STATUS_ERROR; + } +} + +qspi_status_t qspi_free(qspi_t *obj) +{ + (void)(obj); + // possibly here uninit from SDK driver + return QSPI_STATUS_OK; +} + +qspi_status_t qspi_frequency(qspi_t *obj, int hz) +{ + config.phy_if.sck_freq = (nrf_qspi_frequency_t)MBED_HAL_QSPI_HZ_TO_CONFIG(hz); + + // use sync version, no handler + ret_code_t ret = _qspi_drv_init(); + if (ret == NRF_SUCCESS ) { + return QSPI_STATUS_OK; + } else if (ret == NRF_ERROR_INVALID_PARAM) { + return QSPI_STATUS_INVALID_PARAMETER; + } else { + return QSPI_STATUS_ERROR; + } +} + +qspi_status_t qspi_write(qspi_t *obj, const qspi_command_t *command, const void *data, size_t *length) +{ + qspi_status_t status = qspi_prepare_command(obj, command, true); + if (status != QSPI_STATUS_OK) { + return status; + } + + // write here does not return how much it transfered, we return transfered all + ret_code_t ret = nrf_drv_qspi_write(data, *length, command->address.value); + if (ret == NRF_SUCCESS ) { + return QSPI_STATUS_OK; + } else { + return QSPI_STATUS_ERROR; + } +} + +qspi_status_t qspi_read(qspi_t *obj, const qspi_command_t *command, void *data, size_t *length) +{ + qspi_status_t status = qspi_prepare_command(obj, command, false); + if (status != QSPI_STATUS_OK) { + return status; + } + + ret_code_t ret = nrf_drv_qspi_read(data, *length, command->address.value); + if (ret == NRF_SUCCESS ) { + return QSPI_STATUS_OK; + } else { + return QSPI_STATUS_ERROR; + } +} + +qspi_status_t qspi_command_transfer(qspi_t *obj, const qspi_command_t *command, const void *tx_data, size_t tx_size, void *rx_data, size_t rx_size) +{ + ret_code_t ret_code; + uint8_t data[8]; + uint32_t data_size = tx_size + rx_size; + + nrf_qspi_cinstr_conf_t qspi_cinstr_config; + qspi_cinstr_config.opcode = command->instruction.value; + qspi_cinstr_config.io2_level = false; + qspi_cinstr_config.io3_level = false; + qspi_cinstr_config.wipwait = false; + qspi_cinstr_config.wren = false; + + if(!command->address.disabled && data_size == 0) { + // erase command with address + if (command->address.size == QSPI_CFG_ADDR_SIZE_24) { + qspi_cinstr_config.length = NRF_QSPI_CINSTR_LEN_4B; + } else if (command->address.size == QSPI_CFG_ADDR_SIZE_32) { + qspi_cinstr_config.length = NRF_QSPI_CINSTR_LEN_5B; + } else { + return QSPI_STATUS_INVALID_PARAMETER; + } + for (uint32_t i = 0; i < (uint32_t)qspi_cinstr_config.length - 1; ++i) { + data[i] = ((uint8_t *)&command->address.value)[i]; + } + } else if (data_size < 9) { + qspi_cinstr_config.length = (nrf_qspi_cinstr_len_t)(NRF_QSPI_CINSTR_LEN_1B + data_size); + // preparing data to send + for (uint32_t i = 0; i < tx_size; ++i) { + data[i] = ((uint8_t *)tx_data)[i]; + } + } else { + return QSPI_STATUS_ERROR; + } + + ret_code = nrf_drv_qspi_cinstr_xfer(&qspi_cinstr_config, data, data); + if (ret_code != NRF_SUCCESS) { + return QSPI_STATUS_ERROR; + } + + // preparing received data + for (uint32_t i = 0; i < rx_size; ++i) { + // Data is sending as a normal SPI transmission so there is one buffer to send and receive data. + ((uint8_t *)rx_data)[i] = data[i]; + } + + return QSPI_STATUS_OK; +} + +// Private helper function to track initialization +static ret_code_t _qspi_drv_init(void) +{ + static bool _initialized = false; + ret_code_t ret = NRF_ERROR_INVALID_STATE; + + if(_initialized) { + //NRF implementation prevents calling init again. But we need to call init again to program the new command settings in the IFCONFIG registers. + //So, we have to uninit qspi first and call init again. + nrf_drv_qspi_uninit(); + } + ret = nrf_drv_qspi_init(&config, NULL , NULL); + if( ret == NRF_SUCCESS ) + _initialized = true; + return ret; +} + + +#endif + +/** @}*/ diff --git a/targets/TARGET_STM/PeripheralPins.h b/targets/TARGET_STM/PeripheralPins.h index f96b84890ba..d1d5f2ea846 100644 --- a/targets/TARGET_STM/PeripheralPins.h +++ b/targets/TARGET_STM/PeripheralPins.h @@ -80,4 +80,10 @@ extern const PinMap PinMap_CAN_RD[]; extern const PinMap PinMap_CAN_TD[]; #endif +#ifdef DEVICE_QSPI +extern const PinMap PinMap_QSPI_DATA[]; +extern const PinMap PinMap_QSPI_SCLK[]; +extern const PinMap PinMap_QSPI_SSEL[]; +#endif + #endif diff --git a/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F413xH/TARGET_DISCO_F413ZH/PeripheralNames.h b/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F413xH/TARGET_DISCO_F413ZH/PeripheralNames.h index 22ad0b3f55a..3493e2cd25e 100644 --- a/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F413xH/TARGET_DISCO_F413ZH/PeripheralNames.h +++ b/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F413xH/TARGET_DISCO_F413ZH/PeripheralNames.h @@ -79,6 +79,10 @@ typedef enum { CAN_3 = (int)CAN3_BASE } CANName; +typedef enum { + QSPI_1 = (int)QSPI_R_BASE, +} QSPIName; + #ifdef __cplusplus } #endif diff --git a/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F413xH/TARGET_DISCO_F413ZH/PeripheralPins.c b/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F413xH/TARGET_DISCO_F413ZH/PeripheralPins.c index 3f5be127388..cf3b81ab5f9 100644 --- a/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F413xH/TARGET_DISCO_F413ZH/PeripheralPins.c +++ b/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F413xH/TARGET_DISCO_F413ZH/PeripheralPins.c @@ -399,3 +399,43 @@ MBED_WEAK const PinMap PinMap_CAN_TD[] = { {PG_12, CAN_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF9_CAN2)}, // Connected to WIFI_DRDY {NC, NC, 0} }; + +MBED_WEAK const PinMap PinMap_QSPI_DATA[] = { + {PA_1, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QSPI)}, // QUADSPI_BK1_IO3 + {PA_6, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QSPI)}, // QUADSPI_BK2_IO0 + {PA_7, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QSPI)}, // QUADSPI_BK2_IO1 + {PC_4, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QSPI)}, // QUADSPI_BK2_IO2 + {PC_5, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QSPI)}, // QUADSPI_BK2_IO3 + {PC_8, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QSPI)}, // QUADSPI_BK1_IO2 + {PC_9, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QSPI)}, // QUADSPI_BK1_IO0 + {PC_10, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QSPI)}, // QUADSPI_BK1_IO1 + {PD_11, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QSPI)}, // QUADSPI_BK1_IO0 + {PD_12, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QSPI)}, // QUADSPI_BK1_IO1 + {PD_13, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QSPI)}, // QUADSPI_BK1_IO3, N25Q128A13EF840F + {PE_2, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QSPI)}, // QUADSPI_BK1_IO2, N25Q128A13EF840F + {PE_7, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QSPI)}, // QUADSPI_BK2_IO0 + {PE_8, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QSPI)}, // QUADSPI_BK2_IO1 + {PE_9, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QSPI)}, // QUADSPI_BK2_IO2 + {PE_10, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QSPI)}, // QUADSPI_BK2_IO3 + {PF_6, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QSPI)}, // QUADSPI_BK1_IO3 + {PF_7, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QSPI)}, // QUADSPI_BK1_IO2 + {PF_8, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QSPI)}, // QUADSPI_BK1_IO0, N25Q128A13EF840F + {PF_9, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QSPI)}, // QUADSPI_BK1_IO1, N25Q128A13EF840F + {PG_9, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QSPI)}, // QUADSPI_BK2_IO2 + {PG_14, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QSPI)}, // QUADSPI_BK2_IO3 + {NC, NC, 0} +}; + +MBED_WEAK const PinMap PinMap_QSPI_SCLK[] = { + {PB_1, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QSPI)}, + {PB_2, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QSPI)}, // N25Q128A13EF840F + {PD_3, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QSPI)}, + {NC, NC, 0} +}; + +MBED_WEAK const PinMap PinMap_QSPI_SSEL[] = { + {PB_6, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QSPI)}, // QUADSPI_BK1_NCS + {PC_11, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QSPI)}, // QUADSPI_BK2_NCS + {PG_6, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QSPI)}, // QUADSPI_BK1_NCS, N25Q128A13EF840F + {NC, NC, 0} +}; diff --git a/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F413xH/objects.h b/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F413xH/objects.h index 9b3aa0b3f9a..85d6d2667b0 100644 --- a/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F413xH/objects.h +++ b/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F413xH/objects.h @@ -44,6 +44,10 @@ struct trng_s { RNG_HandleTypeDef handle; }; +struct qspi_s { + QSPI_HandleTypeDef handle; +}; + #include "common_objects.h" #ifdef __cplusplus diff --git a/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F469xI/TARGET_DISCO_F469NI/PeripheralNames.h b/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F469xI/TARGET_DISCO_F469NI/PeripheralNames.h index ce3afd56588..39455e98e60 100644 --- a/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F469xI/TARGET_DISCO_F469NI/PeripheralNames.h +++ b/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F469xI/TARGET_DISCO_F469NI/PeripheralNames.h @@ -92,6 +92,10 @@ typedef enum { CAN_2 = (int)CAN2_BASE } CANName; +typedef enum { + QSPI_1 = (int)QSPI_R_BASE, +} QSPIName; + #ifdef __cplusplus } #endif diff --git a/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F469xI/TARGET_DISCO_F469NI/PeripheralPins.c b/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F469xI/TARGET_DISCO_F469NI/PeripheralPins.c index 1f3690e4d0d..1e2970c0963 100644 --- a/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F469xI/TARGET_DISCO_F469NI/PeripheralPins.c +++ b/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F469xI/TARGET_DISCO_F469NI/PeripheralPins.c @@ -388,3 +388,23 @@ MBED_WEAK const PinMap PinMap_CAN_TD[] = { {PH_13, CAN_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF9_CAN1)}, // Connected to D21 {NC, NC, 0} }; + + +MBED_WEAK const PinMap PinMap_QSPI_DATA[] = { + {PF_6, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QSPI)}, + {PF_7, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QSPI)}, + {PF_8, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QSPI)}, + {PF_9, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QSPI)}, + {NC, NC, 0} +}; + +MBED_WEAK const PinMap PinMap_QSPI_SCLK[] = { + {PF_10, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QSPI)}, + {NC, NC, 0} +}; + +MBED_WEAK const PinMap PinMap_QSPI_SSEL[] = { + {PB_6, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QSPI)}, + {NC, NC, 0} +}; + diff --git a/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F469xI/TARGET_DISCO_F469NI/PinNames.h b/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F469xI/TARGET_DISCO_F469NI/PinNames.h index 5d8187d616d..58ebf4e8f63 100644 --- a/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F469xI/TARGET_DISCO_F469NI/PinNames.h +++ b/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F469xI/TARGET_DISCO_F469NI/PinNames.h @@ -406,6 +406,13 @@ typedef enum { SYS_TRACED3_ALT0 = PE_6, SYS_WKUP = PA_0, + QSPI_FLASH_IO0 = PF_8, + QSPI_FLASH_IO1 = PF_9, + QSPI_FLASH_IO2 = PF_7, + QSPI_FLASH_IO3 = PF_6, + QSPI_FLASH_SCK = PF_10, + QSPI_FLASH_CSN = PB_6, + // Not connected NC = (int)0xFFFFFFFF } PinName; diff --git a/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F469xI/objects.h b/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F469xI/objects.h index bea7fef5477..1702826fdb4 100644 --- a/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F469xI/objects.h +++ b/targets/TARGET_STM/TARGET_STM32F4/TARGET_STM32F469xI/objects.h @@ -58,6 +58,10 @@ struct trng_s { RNG_HandleTypeDef handle; }; +struct qspi_s { + QSPI_HandleTypeDef handle; +}; + #include "common_objects.h" #ifdef __cplusplus diff --git a/targets/TARGET_STM/TARGET_STM32F7/TARGET_STM32F746xG/TARGET_DISCO_F746NG/PeripheralNames.h b/targets/TARGET_STM/TARGET_STM32F7/TARGET_STM32F746xG/TARGET_DISCO_F746NG/PeripheralNames.h index 0c00d43bf56..771553bb535 100644 --- a/targets/TARGET_STM/TARGET_STM32F7/TARGET_STM32F746xG/TARGET_DISCO_F746NG/PeripheralNames.h +++ b/targets/TARGET_STM/TARGET_STM32F7/TARGET_STM32F746xG/TARGET_DISCO_F746NG/PeripheralNames.h @@ -93,6 +93,10 @@ typedef enum { CAN_2 = (int)CAN2_BASE } CANName; +typedef enum { + QSPI_1 = (int)QSPI_R_BASE, +} QSPIName; + #ifdef __cplusplus } #endif diff --git a/targets/TARGET_STM/TARGET_STM32F7/TARGET_STM32F746xG/TARGET_DISCO_F746NG/PeripheralPins.c b/targets/TARGET_STM/TARGET_STM32F7/TARGET_STM32F746xG/TARGET_DISCO_F746NG/PeripheralPins.c index 6c9c3bec71e..e5420655109 100644 --- a/targets/TARGET_STM/TARGET_STM32F7/TARGET_STM32F746xG/TARGET_DISCO_F746NG/PeripheralPins.c +++ b/targets/TARGET_STM/TARGET_STM32F7/TARGET_STM32F746xG/TARGET_DISCO_F746NG/PeripheralPins.c @@ -406,3 +406,40 @@ MBED_WEAK const PinMap PinMap_CAN_TD[] = { {PH_13, CAN_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF9_CAN1)}, // Connected to DCMI_PWR_EN {NC, NC, 0} }; + +MBED_WEAK const PinMap PinMap_QSPI_DATA[] = { +// {PA_1, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QUADSPI)}, +// {PB_6, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, +// {PC_9, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QUADSPI)}, +// {PC_10, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QUADSPI)}, +// {PC_11, QSPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QUADSPI)}, + {PD_11, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QUADSPI)}, // IO0 connected to N25Q128 + {PD_12, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QUADSPI)}, // IO1 connected to N25Q128 + {PD_13, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QUADSPI)}, // IO3 connected to N25Q128 + {PE_2, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QUADSPI)}, // IO2 connected to N25Q128 +// {PE_7, QSPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, +// {PE_8, QSPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, +// {PE_9, QSPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, +// {PE_10, QSPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, + {PF_6, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QUADSPI)}, // IO3 connected to pin A5 + {PF_7, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QUADSPI)}, // IO2 connected to pin A4 + {PF_8, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, // IO0 connected to pin A3 + {PF_9, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, // IO1 connected to pin A2 +// {PG_9, QSPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QUADSPI)}, +// {PG_14, QSPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QUADSPI)}, +// {PH_2, QSPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QUADSPI)}, +// {PH_3, QSPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QUADSPI)}, + {NC, NC, 0} +}; + +MBED_WEAK const PinMap PinMap_QSPI_SCLK[] = { + {PB_2, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QUADSPI)}, // connected to N25Q128 flash + {NC, NC, 0} +}; + +MBED_WEAK const PinMap PinMap_QSPI_SSEL[] = { + {PB_6, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, // connected to N25Q128 flash +// {PC_11, QSPI_2, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF9_QUADSPI)}, + {NC, NC, 0} +}; + diff --git a/targets/TARGET_STM/TARGET_STM32F7/TARGET_STM32F746xG/objects.h b/targets/TARGET_STM/TARGET_STM32F7/TARGET_STM32F746xG/objects.h index 28946911189..9ccc1c28d4c 100644 --- a/targets/TARGET_STM/TARGET_STM32F7/TARGET_STM32F746xG/objects.h +++ b/targets/TARGET_STM/TARGET_STM32F7/TARGET_STM32F746xG/objects.h @@ -58,6 +58,10 @@ struct trng_s { RNG_HandleTypeDef handle; }; +struct qspi_s { + QSPI_HandleTypeDef handle; +}; + #include "common_objects.h" #ifdef __cplusplus diff --git a/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L475xG/TARGET_DISCO_L475VG_IOT01A/PeripheralNames.h b/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L475xG/TARGET_DISCO_L475VG_IOT01A/PeripheralNames.h index 2a66882a1c0..04b09f65395 100644 --- a/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L475xG/TARGET_DISCO_L475VG_IOT01A/PeripheralNames.h +++ b/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L475xG/TARGET_DISCO_L475VG_IOT01A/PeripheralNames.h @@ -83,6 +83,10 @@ typedef enum { CAN_1 = (int)CAN1_BASE } CANName; +typedef enum { + QSPI_1 = (int)QSPI_R_BASE, +} QSPIName; + #ifdef __cplusplus } #endif diff --git a/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L475xG/TARGET_DISCO_L475VG_IOT01A/PeripheralPins.c b/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L475xG/TARGET_DISCO_L475VG_IOT01A/PeripheralPins.c index a21e78ce22d..502b176fa29 100644 --- a/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L475xG/TARGET_DISCO_L475VG_IOT01A/PeripheralPins.c +++ b/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L475xG/TARGET_DISCO_L475VG_IOT01A/PeripheralPins.c @@ -340,3 +340,27 @@ MBED_WEAK const PinMap PinMap_CAN_TD[] = { {PD_1, CAN_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF9_CAN1)}, // Connected to PMOD_SPI2_SCK {NC, NC, 0} }; + +MBED_WEAK const PinMap PinMap_QSPI_DATA[] = { + {PE_12, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, + {PE_13, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, + {PE_14, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, + {PE_15, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, + {PA_6, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, + {PA_7, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, + {PB_0, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, + {PB_1, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, + {NC, NC, 0} +}; + +MBED_WEAK const PinMap PinMap_QSPI_SCLK[] = { + {PE_10, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, + {PB_10, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, + {NC, NC, 0} +}; + +MBED_WEAK const PinMap PinMap_QSPI_SSEL[] = { + {PE_11, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, + {PB_11, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, + {NC, NC, 0} +}; diff --git a/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L475xG/objects.h b/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L475xG/objects.h index e68c4eb6194..249e162543f 100644 --- a/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L475xG/objects.h +++ b/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L475xG/objects.h @@ -58,6 +58,10 @@ struct trng_s { RNG_HandleTypeDef handle; }; +struct qspi_s { + QSPI_HandleTypeDef handle; +}; + #include "common_objects.h" #ifdef __cplusplus diff --git a/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L476xG/TARGET_DISCO_L476VG/PeripheralNames.h b/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L476xG/TARGET_DISCO_L476VG/PeripheralNames.h index bc8f43ffa84..553aab47cf0 100644 --- a/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L476xG/TARGET_DISCO_L476VG/PeripheralNames.h +++ b/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L476xG/TARGET_DISCO_L476VG/PeripheralNames.h @@ -83,6 +83,10 @@ typedef enum { CAN_1 = (int)CAN1_BASE } CANName; +typedef enum { + QSPI_1 = (int)QSPI_R_BASE, +} QSPIName; + #ifdef __cplusplus } #endif diff --git a/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L476xG/TARGET_DISCO_L476VG/PeripheralPins.c b/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L476xG/TARGET_DISCO_L476VG/PeripheralPins.c index 1207a190951..bafe58975e8 100644 --- a/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L476xG/TARGET_DISCO_L476VG/PeripheralPins.c +++ b/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L476xG/TARGET_DISCO_L476VG/PeripheralPins.c @@ -340,3 +340,29 @@ MBED_WEAK const PinMap PinMap_CAN_TD[] = { {PD_1, CAN_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_NOPULL, GPIO_AF9_CAN1)}, // Connected to MEMS_SCK [L3GD20_SCL/SPC] {NC, NC, 0} }; + +//*** QUADSPI *** + +MBED_WEAK const PinMap PinMap_QSPI_DATA[] = { + {PA_6, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, // IO3 not connected + {PA_7, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, // IO2 not connected + {PB_0, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, // IO1 not connected + {PB_1, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, // IO0 not connected + {PE_12, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, // IO0 connected to N25Q128 + {PE_13, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, // IO1 connected to N25Q128 + {PE_14, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, // IO2 connected to N25Q128 + {PE_15, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, // IO3 connected to N25Q128 + {NC, NC, 0} +}; + +MBED_WEAK const PinMap PinMap_QSPI_SCLK[] = { +// {PB_10, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, + {PE_10, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, // connected to N25Q128 + {NC, NC, 0} +}; + +MBED_WEAK const PinMap PinMap_QSPI_SSEL[] = { +// {PB_11, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, + {PE_11, QSPI_1, STM_PIN_DATA(STM_MODE_AF_PP, GPIO_PULLUP, GPIO_AF10_QUADSPI)}, // connected to N25Q128 + {NC, NC, 0} +}; diff --git a/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L476xG/objects.h b/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L476xG/objects.h index ece5f1679fa..95148ef88d0 100644 --- a/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L476xG/objects.h +++ b/targets/TARGET_STM/TARGET_STM32L4/TARGET_STM32L476xG/objects.h @@ -58,6 +58,10 @@ struct trng_s { RNG_HandleTypeDef handle; }; +struct qspi_s { + QSPI_HandleTypeDef handle; +}; + #include "common_objects.h" #ifdef __cplusplus diff --git a/targets/TARGET_STM/qspi_api.c b/targets/TARGET_STM/qspi_api.c new file mode 100644 index 00000000000..2069e7805c6 --- /dev/null +++ b/targets/TARGET_STM/qspi_api.c @@ -0,0 +1,292 @@ +/* mbed Microcontroller Library + * Copyright (c) 2017, ARM Limited + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#if DEVICE_QSPI + +#include "qspi_api.h" +#include "mbed_error.h" +#include "cmsis.h" +#include "pinmap.h" +#include "PeripheralPins.h" + +/* Max amount of flash size is 4Gbytes */ +/* hence 2^(31+1), then FLASH_SIZE_DEFAULT = 31<<20 */ +#define QSPI_FLASH_SIZE_DEFAULT 0x1F00000 + +void qspi_prepare_command(const qspi_command_t *command, QSPI_CommandTypeDef *st_command) +{ + // TODO: shift these around to get more dynamic mapping + switch (command->instruction.bus_width) { + case QSPI_CFG_BUS_SINGLE: + st_command->InstructionMode = QSPI_INSTRUCTION_1_LINE; + break; + case QSPI_CFG_BUS_DUAL: + st_command->InstructionMode = QSPI_INSTRUCTION_2_LINES; + break; + case QSPI_CFG_BUS_QUAD: + st_command->InstructionMode = QSPI_INSTRUCTION_4_LINES; + break; + default: + st_command->InstructionMode = QSPI_INSTRUCTION_NONE; + break; + } + + st_command->Instruction = command->instruction.value; + st_command->DummyCycles = command->dummy_count, + // these are target specific settings, use default values + st_command->SIOOMode = QSPI_SIOO_INST_EVERY_CMD; + st_command->DdrMode = QSPI_DDR_MODE_DISABLE; + st_command->DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY; + + switch (command->address.bus_width) { + case QSPI_CFG_BUS_SINGLE: + st_command->AddressMode = QSPI_ADDRESS_1_LINE; + break; + case QSPI_CFG_BUS_DUAL: + st_command->AddressMode = QSPI_ADDRESS_2_LINES; + break; + case QSPI_CFG_BUS_QUAD: + st_command->AddressMode = QSPI_ADDRESS_4_LINES; + break; + default: + st_command->AddressMode = QSPI_ADDRESS_NONE; + break; + } + + if (command->address.disabled == true) { + st_command->AddressMode = QSPI_ADDRESS_NONE; + st_command->AddressSize = 0; + } else { + st_command->Address = command->address.value; + /* command->address.size needs to be shifted by QUADSPI_CCR_ADSIZE_Pos */ + st_command->AddressSize = (command->address.size << QUADSPI_CCR_ADSIZE_Pos) & QUADSPI_CCR_ADSIZE_Msk; + } + + switch (command->alt.bus_width) { + case QSPI_CFG_BUS_SINGLE: + st_command->AlternateByteMode = QSPI_ALTERNATE_BYTES_1_LINE; + break; + case QSPI_CFG_BUS_DUAL: + st_command->AlternateByteMode = QSPI_ALTERNATE_BYTES_2_LINES; + break; + case QSPI_CFG_BUS_QUAD: + st_command->AlternateByteMode = QSPI_ALTERNATE_BYTES_4_LINES; + break; + default: + st_command->AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; + break; + } + + if (command->alt.disabled == true) { + st_command->AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; + st_command->AlternateBytesSize = 0; + } else { + st_command->AlternateBytes = command->alt.value; + /* command->AlternateBytesSize needs to be shifted by QUADSPI_CCR_ABSIZE_Pos */ + st_command->AlternateBytesSize = (command->alt.size << QUADSPI_CCR_ABSIZE_Pos) & QUADSPI_CCR_ABSIZE_Msk; + st_command->AlternateBytesSize = command->alt.size; + } + + switch (command->data.bus_width) { + case QSPI_CFG_BUS_SINGLE: + st_command->DataMode = QSPI_DATA_1_LINE; + break; + case QSPI_CFG_BUS_DUAL: + st_command->DataMode = QSPI_DATA_2_LINES; + break; + case QSPI_CFG_BUS_QUAD: + st_command->DataMode = QSPI_DATA_4_LINES; + break; + default: + st_command->DataMode = QSPI_DATA_NONE; + break; + } + + st_command->NbData = 0; +} + + +qspi_status_t qspi_init(qspi_t *obj, PinName io0, PinName io1, PinName io2, PinName io3, PinName sclk, PinName ssel, uint32_t hz, uint8_t mode) +{ + // Enable interface clock for QSPI + __HAL_RCC_QSPI_CLK_ENABLE(); + + // Reset QSPI + __HAL_RCC_QSPI_FORCE_RESET(); + __HAL_RCC_QSPI_RELEASE_RESET(); + + // Set default QSPI handle values + obj->handle.Init.ClockPrescaler = 1; + obj->handle.Init.FifoThreshold = 1; + obj->handle.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_HALFCYCLE; + obj->handle.Init.FlashSize = POSITION_VAL(QSPI_FLASH_SIZE_DEFAULT) - 1; + obj->handle.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_5_CYCLE; + obj->handle.Init.ClockMode = QSPI_CLOCK_MODE_0; +#ifdef QSPI_DUALFLASH_ENABLE + obj->handle.Init.FlashID = QSPI_FLASH_ID_1; + obj->handle.Init.DualFlash = QSPI_DUALFLASH_DISABLE; +#endif + + obj->handle.Init.ClockMode = mode == 0 ? QSPI_CLOCK_MODE_0 : QSPI_CLOCK_MODE_3; + + QSPIName qspiio0name = (QSPIName)pinmap_peripheral(io0, PinMap_QSPI_DATA); + QSPIName qspiio1name = (QSPIName)pinmap_peripheral(io1, PinMap_QSPI_DATA); + QSPIName qspiio2name = (QSPIName)pinmap_peripheral(io2, PinMap_QSPI_DATA); + QSPIName qspiio3name = (QSPIName)pinmap_peripheral(io3, PinMap_QSPI_DATA); + QSPIName qspiclkname = (QSPIName)pinmap_peripheral(sclk, PinMap_QSPI_SCLK); + QSPIName qspisselname = (QSPIName)pinmap_peripheral(ssel, PinMap_QSPI_SSEL); + + QSPIName qspi_data_first = (QSPIName)pinmap_merge(qspiio0name, qspiio1name); + QSPIName qspi_data_second = (QSPIName)pinmap_merge(qspiio2name, qspiio3name); + QSPIName qspi_data_third = (QSPIName)pinmap_merge(qspiclkname, qspisselname); + + if (qspi_data_first != qspi_data_second || qspi_data_second != qspi_data_third || + qspi_data_first != qspi_data_third) { + return QSPI_STATUS_INVALID_PARAMETER; + } + + // tested all combinations, take first + obj->handle.Instance = (QUADSPI_TypeDef *)qspi_data_first; + + // TODO pinmap here for pins (enable clock) + pinmap_pinout(io0, PinMap_QSPI_DATA); + pinmap_pinout(io1, PinMap_QSPI_DATA); + pinmap_pinout(io2, PinMap_QSPI_DATA); + pinmap_pinout(io3, PinMap_QSPI_DATA); + + pinmap_pinout(sclk, PinMap_QSPI_SCLK); + pinmap_pinout(ssel, PinMap_QSPI_SSEL); + + if (HAL_QSPI_Init(&obj->handle) != HAL_OK) { + return QSPI_STATUS_ERROR; + } + qspi_frequency(obj, hz); + return QSPI_STATUS_OK; +} + +qspi_status_t qspi_free(qspi_t *obj) +{ + // TODO + return QSPI_STATUS_ERROR; +} + +qspi_status_t qspi_frequency(qspi_t *obj, int hz) +{ + qspi_status_t status = QSPI_STATUS_OK; + + // HCLK drives QSPI + int div = HAL_RCC_GetHCLKFreq() / hz; + if (div > 256 || div < 1) { + status = QSPI_STATUS_INVALID_PARAMETER; + return status; + } + + obj->handle.Init.ClockPrescaler = div - 1; + + if (HAL_QSPI_Init(&obj->handle) != HAL_OK) { + status = QSPI_STATUS_ERROR; + } + return status; +} + +qspi_status_t qspi_write(qspi_t *obj, const qspi_command_t *command, const void *data, size_t *length) +{ + QSPI_CommandTypeDef st_command; + qspi_prepare_command(command, &st_command); + + st_command.NbData = *length; + qspi_status_t status = QSPI_STATUS_OK; + + if (HAL_QSPI_Command(&obj->handle, &st_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { + status = QSPI_STATUS_ERROR; + return status; + } + + if (HAL_QSPI_Transmit(&obj->handle, (uint8_t *)data, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { + status = QSPI_STATUS_ERROR; + } + + return status; +} + +qspi_status_t qspi_read(qspi_t *obj, const qspi_command_t *command, void *data, size_t *length) +{ + QSPI_CommandTypeDef st_command; + qspi_prepare_command(command, &st_command); + + st_command.NbData = *length; + qspi_status_t status = QSPI_STATUS_OK; + + if (HAL_QSPI_Command(&obj->handle, &st_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { + status = QSPI_STATUS_ERROR; + return status; + } + + if (HAL_QSPI_Receive(&obj->handle, data, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { + status = QSPI_STATUS_ERROR; + } + + return status; +} + +qspi_status_t qspi_command_transfer(qspi_t *obj, const qspi_command_t *command, const void *tx_data, size_t tx_size, void *rx_data, size_t rx_size) +{ + qspi_status_t status = QSPI_STATUS_OK; + + if ((tx_data == NULL || tx_size == 0) && (rx_data == NULL || rx_size == 0)) { + // only command, no rx or tx + QSPI_CommandTypeDef st_command; + qspi_prepare_command(command, &st_command); + + st_command.NbData = 1; + st_command.DataMode = QSPI_DATA_NONE; /* Instruction only */ + if (HAL_QSPI_Command(&obj->handle, &st_command, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { + status = QSPI_STATUS_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 = qspi_write(obj, command, tx_data, &tx_length); + if (status != QSPI_STATUS_OK) { + return status; + } + } + + if (rx_data != NULL && rx_size) { + size_t rx_length = rx_size; + status = qspi_read(obj, command, rx_data, &rx_length); + } + } + return status; +} + +#endif + +/** @}*/ diff --git a/targets/targets.json b/targets/targets.json index b39430eb64a..52571aa9438 100755 --- a/targets/targets.json +++ b/targets/targets.json @@ -1216,7 +1216,7 @@ }, "detect_code": ["0743"], "macros_add": ["USB_STM_HAL", "USBHOST_OTHER"], - "device_has_add": ["ANALOGOUT", "CAN", "SERIAL_ASYNCH", "SERIAL_FC", "TRNG", "FLASH"], + "device_has_add": ["ANALOGOUT", "CAN", "SERIAL_ASYNCH", "SERIAL_FC", "TRNG", "FLASH", "QSPI"], "release_versions": ["2", "5"], "device_name": "STM32F413ZH" }, @@ -1877,7 +1877,7 @@ }, "detect_code": ["0788"], "macros_add": ["USB_STM_HAL", "USBHOST_OTHER"], - "device_has_add": ["ANALOGOUT", "CAN", "SERIAL_FC", "TRNG", "FLASH"], + "device_has_add": ["ANALOGOUT", "CAN", "SERIAL_FC", "TRNG", "FLASH", "QSPI"], "release_versions": ["2", "5"], "device_name": "STM32F469NI" }, @@ -1956,7 +1956,7 @@ }, "detect_code": ["0815"], "macros_add": ["USB_STM_HAL", "USBHOST_OTHER"], - "device_has_add": ["LPTICKER", "ANALOGOUT", "CAN", "EMAC", "SERIAL_ASYNCH", "TRNG", "FLASH"], + "device_has_add": ["LPTICKER", "ANALOGOUT", "CAN", "EMAC", "SERIAL_ASYNCH", "TRNG", "FLASH", "QSPI"], "release_versions": ["2", "5"], "device_name": "STM32F746NG", "overrides": { @@ -2010,7 +2010,7 @@ "supported_form_factors": ["ARDUINO"], "detect_code": ["0764"], "macros_add": ["USBHOST_OTHER"], - "device_has_add": ["ANALOGOUT", "CAN", "SERIAL_FC", "TRNG", "FLASH"], + "device_has_add": ["ANALOGOUT", "CAN", "SERIAL_FC", "TRNG", "FLASH", "QSPI"], "release_versions": ["2", "5"], "device_name": "STM32L475VG", "bootloader_supported": true @@ -2032,7 +2032,7 @@ }, "detect_code": ["0820"], "macros_add": ["USBHOST_OTHER"], - "device_has_add": ["ANALOGOUT", "CAN", "SERIAL_FC", "TRNG", "FLASH"], + "device_has_add": ["ANALOGOUT", "CAN", "SERIAL_FC", "TRNG", "FLASH", "QSPI"], "release_versions": ["2", "5"], "device_name": "STM32L476VG", "bootloader_supported": true @@ -3776,7 +3776,8 @@ "SPI_ASYNCH", "STCLK_OFF_DURING_SLEEP", "TRNG", - "USTICKER" + "USTICKER", + "QSPI" ], "extra_labels": [ "NORDIC",