From 08a2709993b4ad7b50467a6f11c6f260720e3046 Mon Sep 17 00:00:00 2001 From: Matthew Macovsky Date: Fri, 6 Sep 2019 18:09:21 +0100 Subject: [PATCH] Allow for arbitrary QSPI alt sizes The QSPI spec allows alt to be any size that is a multiple of the number of data lines. For example, Micron's N25Q128A uses only a single alt cycle for all read modes (1, 2, or 4 bits depending on how many data lines are in use). --- TESTS/mbed_hal/qspi/qspi_test_utils.h | 8 +- .../COMPONENT_QSPIF/QSPIFBlockDevice.cpp | 13 +- drivers/QSPI.h | 2 +- drivers/source/QSPI.cpp | 29 +++- .../direct_access_devicekey_test/main.cpp | 2 +- hal/qspi_api.h | 7 +- .../TARGET_Cypress/TARGET_PSOC6/cy_qspi_api.c | 17 +- .../psoc6csp/hal/include/cyhal_qspi.h | 11 +- .../psoc6csp/hal/src/cyhal_qspi.c | 123 +++++++++++--- targets/TARGET_STM/qspi_api.c | 154 +++++++++++++----- .../TARGET_EFM32/qspi_api.c | 6 +- 11 files changed, 266 insertions(+), 106 deletions(-) diff --git a/TESTS/mbed_hal/qspi/qspi_test_utils.h b/TESTS/mbed_hal/qspi/qspi_test_utils.h index 08f16f42c9..0c29232e51 100644 --- a/TESTS/mbed_hal/qspi/qspi_test_utils.h +++ b/TESTS/mbed_hal/qspi/qspi_test_utils.h @@ -102,10 +102,10 @@ struct Qspi { #define ADDR_SIZE_24 QSPI_CFG_ADDR_SIZE_24 #define ADDR_SIZE_32 QSPI_CFG_ADDR_SIZE_32 -#define ALT_SIZE_8 QSPI_CFG_ALT_SIZE_8 -#define ALT_SIZE_16 QSPI_CFG_ALT_SIZE_16 -#define ALT_SIZE_24 QSPI_CFG_ALT_SIZE_24 -#define ALT_SIZE_32 QSPI_CFG_ALT_SIZE_32 +#define ALT_SIZE_8 8u +#define ALT_SIZE_16 16u +#define ALT_SIZE_24 24u +#define ALT_SIZE_32 32u #define STATUS_REG QSPI_CMD_RDSR #define CONFIG_REG0 QSPI_CMD_RDCR0 diff --git a/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.cpp b/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.cpp index 7010b22cfb..bc0d424188 100644 --- a/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.cpp +++ b/components/storage/blockdevice/COMPONENT_QSPIF/QSPIFBlockDevice.cpp @@ -247,7 +247,7 @@ int QSPIFBlockDevice::init() // Configure BUS Mode to 1_1_1 for all commands other than Read _qspi_configure_format(QSPI_CFG_BUS_SINGLE, QSPI_CFG_BUS_SINGLE, QSPI_CFG_ADDR_SIZE_24, QSPI_CFG_BUS_SINGLE, - QSPI_CFG_ALT_SIZE_8, QSPI_CFG_BUS_SINGLE, 0); + 0, QSPI_CFG_BUS_SINGLE, 0); _is_initialized = true; @@ -303,7 +303,7 @@ int QSPIFBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size) // Configure Bus for Reading _qspi_configure_format(_inst_width, _address_width, _address_size, QSPI_CFG_BUS_SINGLE, - QSPI_CFG_ALT_SIZE_8, _data_width, _dummy_and_mode_cycles); + 0, _data_width, _dummy_and_mode_cycles); if (QSPI_STATUS_OK != _qspi_send_read_command(_read_instruction, buffer, addr, size)) { status = QSPIF_BD_ERROR_DEVICE_ERROR; @@ -312,7 +312,7 @@ int QSPIFBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size) // All commands other than Read use default 1-1-1 Bus mode (Program/Erase are constrained by flash memory performance less than that of the bus) _qspi_configure_format(QSPI_CFG_BUS_SINGLE, QSPI_CFG_BUS_SINGLE, QSPI_CFG_ADDR_SIZE_24, QSPI_CFG_BUS_SINGLE, - QSPI_CFG_ALT_SIZE_8, QSPI_CFG_BUS_SINGLE, 0); + 0, QSPI_CFG_BUS_SINGLE, 0); _mutex.unlock(); return status; @@ -718,7 +718,7 @@ int QSPIFBlockDevice::_sfdp_parse_sfdp_headers(uint32_t &basic_table_addr, size_ // Set 1-1-1 bus mode for SFDP header parsing _qspi_configure_format(QSPI_CFG_BUS_SINGLE, QSPI_CFG_BUS_SINGLE, QSPI_CFG_ADDR_SIZE_24, QSPI_CFG_BUS_SINGLE, - QSPI_CFG_ALT_SIZE_8, QSPI_CFG_BUS_SINGLE, 8); + 0, QSPI_CFG_BUS_SINGLE, 8); qspi_status_t status = _qspi_send_read_command(QSPIF_SFDP, (char *)sfdp_header, addr /*address*/, data_length); if (status != QSPI_STATUS_OK) { @@ -885,7 +885,7 @@ int QSPIFBlockDevice::_sfdp_set_quad_enabled(uint8_t *basic_param_table_ptr) // Configure BUS Mode to 1_1_1 for all commands other than Read _qspi_configure_format(QSPI_CFG_BUS_SINGLE, QSPI_CFG_BUS_SINGLE, QSPI_CFG_ADDR_SIZE_24, QSPI_CFG_BUS_SINGLE, - QSPI_CFG_ALT_SIZE_8, QSPI_CFG_BUS_SINGLE, 0); + 0, QSPI_CFG_BUS_SINGLE, 0); // Read Status Register if (QSPI_STATUS_OK == _qspi_send_general_command(_read_register_inst, QSPI_NO_ADDRESS_COMMAND, NULL, 0, @@ -1206,7 +1206,7 @@ int QSPIFBlockDevice::_enable_fast_mdoe() // Configure BUS Mode to 1_1_1 for all commands other than Read _qspi_configure_format(QSPI_CFG_BUS_SINGLE, QSPI_CFG_BUS_SINGLE, QSPI_CFG_ADDR_SIZE_24, QSPI_CFG_BUS_SINGLE, - QSPI_CFG_ALT_SIZE_8, QSPI_CFG_BUS_SINGLE, 0); + 0, QSPI_CFG_BUS_SINGLE, 0); // Read Status Register if (QSPI_STATUS_OK == _qspi_send_general_command(read_conf_register_inst, QSPI_NO_ADDRESS_COMMAND, NULL, 0, @@ -1306,7 +1306,6 @@ int QSPIFBlockDevice::_utils_iterate_next_largest_erase_type(uint8_t &bitfield, tr_error("No erase type was found for current region addr"); } return largest_erase_type; - } /***************************************************/ diff --git a/drivers/QSPI.h b/drivers/QSPI.h index 4ab356a009..657be50726 100644 --- a/drivers/QSPI.h +++ b/drivers/QSPI.h @@ -106,7 +106,7 @@ public: * @param address_width Bus width used by address phase(Valid values are QSPI_CFG_BUS_SINGLE, QSPI_CFG_BUS_DUAL, QSPI_CFG_BUS_QUAD) * @param address_size Size in bits used by address phase(Valid values are QSPI_CFG_ADDR_SIZE_8, QSPI_CFG_ADDR_SIZE_16, QSPI_CFG_ADDR_SIZE_24, QSPI_CFG_ADDR_SIZE_32) * @param alt_width Bus width used by alt phase(Valid values are QSPI_CFG_BUS_SINGLE, QSPI_CFG_BUS_DUAL, QSPI_CFG_BUS_QUAD) - * @param alt_size Size in bits used by alt phase(Valid values are QSPI_CFG_ALT_SIZE_8, QSPI_CFG_ALT_SIZE_16, QSPI_CFG_ALT_SIZE_24, QSPI_CFG_ALT_SIZE_32) + * @param alt_size Size in bits used by alt phase (must be a multiple of the number of bus lines indicated in alt_width) * @param data_width Bus width used by data phase(Valid values are QSPI_CFG_BUS_SINGLE, QSPI_CFG_BUS_DUAL, QSPI_CFG_BUS_QUAD) * @param dummy_cycles Number of dummy clock cycles to be used after alt phase * diff --git a/drivers/source/QSPI.cpp b/drivers/source/QSPI.cpp index c79c949e3b..839c2f1e63 100644 --- a/drivers/source/QSPI.cpp +++ b/drivers/source/QSPI.cpp @@ -26,6 +26,21 @@ namespace mbed { QSPI *QSPI::_owner = NULL; SingletonPtr QSPI::_mutex; +uint8_t convert_bus_width_to_line_count(qspi_bus_width_t width) +{ + switch (width) { + case QSPI_CFG_BUS_SINGLE: + return 1; + case QSPI_CFG_BUS_DUAL: + return 2; + case QSPI_CFG_BUS_QUAD: + return 4; + default: + // Unrecognized bus width + return 0; + } +} + QSPI::QSPI(PinName io0, PinName io1, PinName io2, PinName io3, PinName sclk, PinName ssel, int mode) : _qspi() { _qspi_io0 = io0; @@ -38,7 +53,7 @@ QSPI::QSPI(PinName io0, PinName io1, PinName io2, PinName io3, PinName sclk, Pin _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; + _alt_size = 0; _data_width = QSPI_CFG_BUS_SINGLE; _num_dummy_cycles = 0; _mode = mode; @@ -52,7 +67,14 @@ QSPI::QSPI(PinName io0, PinName io1, PinName io2, PinName io3, PinName sclk, Pin 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; + // Check that alt_size/alt_width are a valid combination + uint8_t alt_lines = convert_bus_width_to_line_count(alt_width); + if (alt_lines == 0) { + return QSPI_STATUS_ERROR; + } else if (alt_size % alt_lines != 0) { + // Invalid alt size/width combination (alt size is not a multiple of the number of bus lines used to transmit it) + return QSPI_STATUS_ERROR; + } lock(); _inst_width = inst_width; @@ -62,10 +84,9 @@ qspi_status_t QSPI::configure_format(qspi_bus_width_t inst_width, qspi_bus_width _alt_size = alt_size; _data_width = data_width; _num_dummy_cycles = dummy_cycles; - unlock(); - return ret_status; + return QSPI_STATUS_OK; } qspi_status_t QSPI::set_frequency(int hz) diff --git a/features/storage/TESTS/kvstore/direct_access_devicekey_test/main.cpp b/features/storage/TESTS/kvstore/direct_access_devicekey_test/main.cpp index ba3fb6bb63..cf6a60f9f8 100644 --- a/features/storage/TESTS/kvstore/direct_access_devicekey_test/main.cpp +++ b/features/storage/TESTS/kvstore/direct_access_devicekey_test/main.cpp @@ -255,7 +255,7 @@ void test_direct_access_to_device_inject_root() ret = devkey.device_inject_root_of_trust(key, DEVICE_KEY_16BYTE); TEST_ASSERT_EQUAL_INT(DEVICEKEY_SUCCESS, ret); - // Now use Direct Access To DeviceKey to retrieve it */ + // Now use Direct Access To DeviceKey to retrieve it uint32_t internal_start_address; uint32_t internal_rbp_size; bool is_conf_tdb_internal = false; diff --git a/hal/qspi_api.h b/hal/qspi_api.h index 8464c2d6b2..8314c5bb7f 100644 --- a/hal/qspi_api.h +++ b/hal/qspi_api.h @@ -60,12 +60,7 @@ typedef enum qspi_address_size { /** 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; +typedef uint8_t qspi_alt_size_t; /** QSPI command * diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/cy_qspi_api.c b/targets/TARGET_Cypress/TARGET_PSOC6/cy_qspi_api.c index cd28b08844..3404dcc75d 100644 --- a/targets/TARGET_Cypress/TARGET_PSOC6/cy_qspi_api.c +++ b/targets/TARGET_Cypress/TARGET_PSOC6/cy_qspi_api.c @@ -69,21 +69,6 @@ static inline cyhal_qspi_size_t cyhal_qspi_convert_addr_size(qspi_address_size_t } } -static inline cyhal_qspi_size_t cyhal_qspi_convert_alt_size(qspi_alt_size_t size) -{ - switch (size) { - case QSPI_CFG_ALT_SIZE_8: - return CYHAL_QSPI_CFG_SIZE_8; - case QSPI_CFG_ALT_SIZE_16: - return CYHAL_QSPI_CFG_SIZE_16; - case QSPI_CFG_ALT_SIZE_24: - return CYHAL_QSPI_CFG_SIZE_24; - default: // fallthrough - case QSPI_CFG_ALT_SIZE_32: - return CYHAL_QSPI_CFG_SIZE_32; - } -} - static void cyhal_qspi_convert_command(const qspi_command_t *from, cyhal_qspi_command_t *to) { to->instruction.bus_width = cyhal_qspi_convert_width(from->instruction.bus_width); @@ -94,7 +79,7 @@ static void cyhal_qspi_convert_command(const qspi_command_t *from, cyhal_qspi_co to->address.value = from->address.value; to->address.disabled = from->address.disabled; to->mode_bits.bus_width = cyhal_qspi_convert_width(from->alt.bus_width); - to->mode_bits.size = cyhal_qspi_convert_alt_size(from->alt.size); + to->mode_bits.size = from->alt.size; to->mode_bits.value = from->alt.value; to->mode_bits.disabled = from->alt.disabled; to->dummy_count = from->dummy_count; diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/psoc6csp/hal/include/cyhal_qspi.h b/targets/TARGET_Cypress/TARGET_PSOC6/psoc6csp/hal/include/cyhal_qspi.h index b3ba4d9123..bf67b38452 100644 --- a/targets/TARGET_Cypress/TARGET_PSOC6/psoc6csp/hal/include/cyhal_qspi.h +++ b/targets/TARGET_Cypress/TARGET_PSOC6/psoc6csp/hal/include/cyhal_qspi.h @@ -71,9 +71,12 @@ typedef enum { } cyhal_qspi_event_t; #define CYHAL_QSPI_RSLT_ERR_BUS_WIDTH (CY_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CYHAL_RSLT_MODULE_QSPI, 0)) /**< Bus width Error. >*/ -#define CYHAL_QSPI_RSLT_ERR_PIN (CY_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CYHAL_RSLT_MODULE_QSPI, 1)) /**< Pin related Error. >*/ -#define CYHAL_QSPI_RSLT_ERR_DATA_SEL (CY_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CYHAL_RSLT_MODULE_QSPI, 2)) /**< Data select Error. >*/ -#define CYHAL_QSPI_RSLT_ERR_INSTANCE (CY_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CYHAL_RSLT_MODULE_QSPI, 3)) /**< QSPI instance related Error. >*/ +#define CYHAL_QSPI_RSLT_ERR_SIZE (CY_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CYHAL_RSLT_MODULE_QSPI, 1)) /**< Size Error. >*/ +#define CYHAL_QSPI_RSLT_ERR_PIN (CY_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CYHAL_RSLT_MODULE_QSPI, 2)) /**< Pin related Error. >*/ +#define CYHAL_QSPI_RSLT_ERR_DATA_SEL (CY_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CYHAL_RSLT_MODULE_QSPI, 3)) /**< Data select Error. >*/ +#define CYHAL_QSPI_RSLT_ERR_INSTANCE (CY_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CYHAL_RSLT_MODULE_QSPI, 4)) /**< QSPI instance related Error. >*/ +#define CYHAL_QSPI_RSLT_ERR_ALT_SIZE_WIDTH_MISMATCH (CY_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CYHAL_RSLT_MODULE_QSPI, 5)) /**< Provided alt size is incompatible with provided alt width. >*/ +#define CYHAL_QSPI_RSLT_ERR_ALT_SIZE_DUMMY_CYCLES_MISMATCH (CY_RSLT_CREATE(CY_RSLT_TYPE_ERROR, CYHAL_RSLT_MODULE_QSPI, 6)) /**< Provided alt size is incompatible with provided number of dummy cycles (due to device-specific restrictions). >*/ /** @brief QSPI command settings */ typedef struct cyhal_qspi_command { @@ -90,7 +93,7 @@ typedef struct cyhal_qspi_command { } address; struct { cyhal_qspi_bus_width_t bus_width; /**< Bus width for mode bits >*/ - cyhal_qspi_size_t size; /**< Mode bits size >*/ + uint8_t size; /**< Mode bits size >*/ uint32_t value; /**< Mode bits value >*/ bool disabled; /**< Mode bits phase skipped if disabled is set to true >*/ } mode_bits; diff --git a/targets/TARGET_Cypress/TARGET_PSOC6/psoc6csp/hal/src/cyhal_qspi.c b/targets/TARGET_Cypress/TARGET_PSOC6/psoc6csp/hal/src/cyhal_qspi.c index 2842e62347..5c4c852a6d 100644 --- a/targets/TARGET_Cypress/TARGET_PSOC6/psoc6csp/hal/src/cyhal_qspi.c +++ b/targets/TARGET_Cypress/TARGET_PSOC6/psoc6csp/hal/src/cyhal_qspi.c @@ -292,16 +292,45 @@ static inline uint32_t get_size(cyhal_qspi_size_t hal_size) return ((uint32_t)hal_size >> 3); /* convert bits to bytes */ } +/* cyhal_qspi_bus_width_t to number of bus lines used */ +static uint8_t get_lines(cyhal_qspi_bus_width_t hal_width) +{ + uint8_t lines; + + switch (hal_width) + { + case CYHAL_QSPI_CFG_BUS_SINGLE: + lines = 1; + break; + case CYHAL_QSPI_CFG_BUS_DUAL: + lines = 2; + break; + case CYHAL_QSPI_CFG_BUS_QUAD: + lines = 4; + break; + case CYHAL_QSPI_CFG_BUS_OCTAL: + lines = 8; + break; + default: + lines = 0; + } + + return lines; +} + /* Sends QSPI command with certain set of data */ /* Address passed through 'command' is not used, instead the value in 'addr' is used. */ static cy_rslt_t qspi_command_transfer(cyhal_qspi_t *obj, const cyhal_qspi_command_t *command, - uint32_t addr, bool endOfTransfer) + uint32_t addr, bool endOfTransfer, uint8_t *dummy_cycles) { /* max address size is 4 bytes and max mode bits size is 4 bytes */ uint8_t cmd_param[8] = {0}; uint32_t start_pos = 0; uint32_t addr_size = 0; - uint32_t mode_bits_size = 0; + uint32_t mode_size = 0; + uint8_t leftover_bits = 0; + uint8_t lines = 0; + uint8_t integrated_dummy_cycles = 0; cy_en_smif_txfr_width_t bus_width = CY_SMIF_WIDTH_SINGLE; cy_stc_smif_mem_cmd_t cyhal_cmd_config; cy_rslt_t result = CY_RSLT_SUCCESS; @@ -332,22 +361,67 @@ static cy_rslt_t qspi_command_transfer(cyhal_qspi_t *obj, const cyhal_qspi_comma if (!command->address.disabled) { addr_size = get_size(command->address.size); - uint32_to_byte_array(addr, cmd_param, start_pos, addr_size); - start_pos += addr_size; - bus_width = cyhal_cmd_config.addrWidth; + if (addr_size == 0) + { + result = CYHAL_QSPI_RSLT_ERR_SIZE; + } + else + { + uint32_to_byte_array(addr, cmd_param, start_pos, addr_size); + start_pos += addr_size; + bus_width = cyhal_cmd_config.addrWidth; + } } if (!command->mode_bits.disabled) { - mode_bits_size = get_size(command->mode_bits.size); - uint32_to_byte_array(cyhal_cmd_config.mode, cmd_param, start_pos, mode_bits_size); - bus_width = cyhal_cmd_config.modeWidth; + // Mode size must be a multiple of the number of bus lines used (i.e. a whole number of cycles) + lines = get_lines(command->mode_bits.bus_width); + if (lines == 0) + { + result = CYHAL_QSPI_RSLT_ERR_BUS_WIDTH; + } + else if (command->mode_bits.size % lines != 0) + { + result = CYHAL_QSPI_RSLT_ERR_ALT_SIZE_WIDTH_MISMATCH; + } + else + { + // Round mode size up to nearest byte - unused parts of byte act as dummy cycles + mode_size = get_size(command->mode_bits.size - 1) + 1; + + // Unused bits in most significant byte of mode + leftover_bits = (mode_size << 3) - command->mode_bits.size; + if (leftover_bits != 0) + { + // Account for dummy cycles that will be spent in the mode portion of the command + integrated_dummy_cycles = (8 - (command->mode_bits.size % 8)) / lines; + if (*dummy_cycles < integrated_dummy_cycles) + { + // Not enough dummy cycles to account for a short mode + result = CYHAL_QSPI_RSLT_ERR_ALT_SIZE_DUMMY_CYCLES_MISMATCH; + } + else + { + *dummy_cycles -= integrated_dummy_cycles; + } + + // Align mode value to the end of the most significant byte + cyhal_cmd_config.mode <<= leftover_bits; + } + + uint32_to_byte_array(cyhal_cmd_config.mode, cmd_param, start_pos, mode_size); + bus_width = cyhal_cmd_config.modeWidth; + } } - uint32_t cmpltTxfr = ((endOfTransfer) ? 1UL : 0UL); - result = (cy_rslt_t)Cy_SMIF_TransmitCommand(obj->base, cyhal_cmd_config.command, - cyhal_cmd_config.cmdWidth, cmd_param, (addr_size + mode_bits_size), - bus_width, obj->slave_select, cmpltTxfr, &obj->context); + if (CY_RSLT_SUCCESS == result) + { + uint32_t cmpltTxfr = ((endOfTransfer) ? 1UL : 0UL); + result = (cy_rslt_t)Cy_SMIF_TransmitCommand(obj->base, cyhal_cmd_config.command, + cyhal_cmd_config.cmdWidth, cmd_param, (addr_size + mode_size), + bus_width, obj->slave_select, cmpltTxfr, &obj->context); + } } return result; } @@ -810,6 +884,7 @@ cy_rslt_t cyhal_qspi_read(cyhal_qspi_t *obj, const cyhal_qspi_command_t *command uint32_t chunk = 0; size_t read_bytes = *length; uint32_t addr = command->address.value; + uint8_t dummy_cycles = command->dummy_count; /* SMIF can read only up to 65536 bytes in one go. Split the larger read into multiple chunks */ while (read_bytes > 0) @@ -823,11 +898,11 @@ cy_rslt_t cyhal_qspi_read(cyhal_qspi_t *obj, const cyhal_qspi_command_t *command * to create a copy of the command object. Instead of copying the object, the address is * passed separately. */ - status = qspi_command_transfer(obj, command, addr, false); + status = qspi_command_transfer(obj, command, addr, false, &dummy_cycles); if (CY_RSLT_SUCCESS == status) { - if (command->dummy_count > 0u) + if (dummy_cycles > 0u) { status = (cy_rslt_t)Cy_SMIF_SendDummyCycles(obj->base, command->dummy_count); } @@ -852,13 +927,15 @@ cy_rslt_t cyhal_qspi_read(cyhal_qspi_t *obj, const cyhal_qspi_command_t *command cy_rslt_t cyhal_qspi_read_async(cyhal_qspi_t *obj, const cyhal_qspi_command_t *command, void *data, size_t *length) { - cy_rslt_t status = qspi_command_transfer(obj, command, command->address.value, false); - + cy_rslt_t status = CY_RSLT_SUCCESS; + uint32_t addr = command->address.value; + uint8_t dummy_cycles = command->dummy_count; + status = qspi_command_transfer(obj, command, addr, false, &dummy_cycles); if (CY_RSLT_SUCCESS == status) { if (command->dummy_count > 0u) { - status = (cy_rslt_t)Cy_SMIF_SendDummyCycles(obj->base, command->dummy_count); + status = (cy_rslt_t)Cy_SMIF_SendDummyCycles(obj->base, dummy_cycles); } if (CY_RSLT_SUCCESS == status) @@ -873,13 +950,14 @@ cy_rslt_t cyhal_qspi_read_async(cyhal_qspi_t *obj, const cyhal_qspi_command_t *c /* length can be up to 65536. */ cy_rslt_t cyhal_qspi_write(cyhal_qspi_t *obj, const cyhal_qspi_command_t *command, const void *data, size_t *length) { - cy_rslt_t status = qspi_command_transfer(obj, command, command->address.value, false); + uint8_t dummy_cycles = command->dummy_count; + cy_rslt_t status = qspi_command_transfer(obj, command, command->address.value, false, &dummy_cycles); if (CY_RSLT_SUCCESS == status) { if (command->dummy_count > 0u) { - status = (cy_rslt_t)Cy_SMIF_SendDummyCycles(obj->base, command->dummy_count); + status = (cy_rslt_t)Cy_SMIF_SendDummyCycles(obj->base, dummy_cycles); } if ((CY_SMIF_SUCCESS == status) && (*length > 0)) @@ -895,13 +973,14 @@ cy_rslt_t cyhal_qspi_write(cyhal_qspi_t *obj, const cyhal_qspi_command_t *comman /* length can be up to 65536. */ cy_rslt_t cyhal_qspi_write_async(cyhal_qspi_t *obj, const cyhal_qspi_command_t *command, const void *data, size_t *length) { - cy_rslt_t status = qspi_command_transfer(obj, command, command->address.value, false); + uint8_t dummy_cycles = command->dummy_count; + cy_rslt_t status = qspi_command_transfer(obj, command, command->address.value, false, &dummy_cycles); if (CY_RSLT_SUCCESS == status) { if (command->dummy_count > 0u) { - status = (cy_rslt_t)Cy_SMIF_SendDummyCycles(obj->base, command->dummy_count); + status = (cy_rslt_t)Cy_SMIF_SendDummyCycles(obj->base, dummy_cycles); } if ((CY_SMIF_SUCCESS == status) && (*length > 0)) @@ -922,7 +1001,7 @@ cy_rslt_t cyhal_qspi_transfer( if ((tx_data == NULL || tx_size == 0) && (rx_data == NULL || rx_size == 0)) { /* only command, no rx or tx */ - status = qspi_command_transfer(obj, command, command->address.value, true); + status = qspi_command_transfer(obj, command, command->address.value, true, NULL); } else { diff --git a/targets/TARGET_STM/qspi_api.c b/targets/TARGET_STM/qspi_api.c index 789bdaba49..e0e4fc759b 100644 --- a/targets/TARGET_STM/qspi_api.c +++ b/targets/TARGET_STM/qspi_api.c @@ -43,7 +43,7 @@ #define QSPI_FLASH_SIZE_DEFAULT 0x80000000 #if defined(OCTOSPI1) -void qspi_prepare_command(const qspi_command_t *command, OSPI_RegularCmdTypeDef *st_command) +qspi_status_t qspi_prepare_command(const qspi_command_t *command, OSPI_RegularCmdTypeDef *st_command) { debug_if(qspi_api_c_debug, "qspi_prepare_command In: instruction.value %x dummy_count %x address.bus_width %x address.disabled %x address.value %x address.size %x\n", command->instruction.value, command->dummy_count, command->address.bus_width, command->address.disabled, command->address.value, command->address.size); @@ -66,8 +66,8 @@ void qspi_prepare_command(const qspi_command_t *command, OSPI_RegularCmdTypeDef st_command->InstructionMode = HAL_OSPI_INSTRUCTION_4_LINES; break; default: - error("Command param error: wrong istruction format\n"); - break; + error("Command param error: wrong instruction format\n"); + return QSPI_STATUS_ERROR; } } @@ -99,7 +99,7 @@ void qspi_prepare_command(const qspi_command_t *command, OSPI_RegularCmdTypeDef break; default: error("Command param error: wrong address size\n"); - break; + return QSPI_STATUS_ERROR; } switch(command->address.size) { case QSPI_CFG_ADDR_SIZE_8: @@ -116,7 +116,7 @@ void qspi_prepare_command(const qspi_command_t *command, OSPI_RegularCmdTypeDef break; default: error("Command param error: wrong address size\n"); - break; + return QSPI_STATUS_ERROR; } } @@ -124,39 +124,62 @@ void qspi_prepare_command(const qspi_command_t *command, OSPI_RegularCmdTypeDef st_command->AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE; st_command->AlternateBytesSize = 0; } else { - st_command->AlternateBytes = command->alt.value; + uint8_t alt_lines = 0; switch (command->alt.bus_width) { case QSPI_CFG_BUS_SINGLE: st_command->AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_1_LINE; + alt_lines = 1; break; case QSPI_CFG_BUS_DUAL: st_command->AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_2_LINES; + alt_lines = 2; break; case QSPI_CFG_BUS_QUAD: st_command->AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_4_LINES; + alt_lines = 4; break; default: st_command->AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE; - break; + error("Command param error: invalid alt bytes mode\n"); + return QSPI_STATUS_ERROR; } - switch(command->alt.size) { - case QSPI_CFG_ALT_SIZE_8: - st_command->AlternateBytesSize = HAL_OSPI_ALTERNATE_BYTES_8_BITS; - break; - case QSPI_CFG_ALT_SIZE_16: - st_command->AlternateBytesSize = HAL_OSPI_ALTERNATE_BYTES_16_BITS; - break; - case QSPI_CFG_ALT_SIZE_24: - st_command->AlternateBytesSize = HAL_OSPI_ALTERNATE_BYTES_24_BITS; - break; - case QSPI_CFG_ALT_SIZE_32: - st_command->AlternateBytesSize = HAL_OSPI_ALTERNATE_BYTES_32_BITS; - break; - default: - st_command->AlternateBytesSize = 0; - printf("Command param error: wrong address size\n"); - break; + + // Alt size must be a multiple of the number of bus lines used (i.e. a whole number of cycles) + if (command->alt.size % alt_lines != 0) { + error("Command param error: incompatible alt size and alt bus width\n"); + return QSPI_STATUS_ERROR; } + + // Round up to nearest byte - unused parts of byte act as dummy cycles + uint32_t rounded_size = ((command->alt.size - 1) >> 3) + 1; + // Maximum of 4 alt bytes + if (rounded_size > 4) { + error("Command param error: alt size exceeds maximum of 32 bits\n"); + return QSPI_STATUS_ERROR; + } + + // Unused bits in most significant byte of alt + uint8_t leftover_bits = (rounded_size << 3) - command->alt.size; + if (leftover_bits != 0) { + // Account for dummy cycles that will be spent in the alt portion of the command + uint8_t integrated_dummy_cycles = leftover_bits / alt_lines; + if (st_command->DummyCycles < integrated_dummy_cycles) + { + // Not enough dummy cycles to account for a short alt + error("Command param error: not enough dummy cycles to make up for given alt size\n"); + return QSPI_STATUS_ERROR; + } + st_command->DummyCycles -= integrated_dummy_cycles; + + // Align alt value to the end of the most significant byte + st_command->AlternateBytes = command->alt.value << leftover_bits; + } else { + st_command->AlternateBytes = command->alt.value; + } + + /* command->AlternateBytesSize needs to be shifted by OCTOSPI_CCR_ABSIZE_Pos */ + // 0b00 = 1 byte, 0b01 = 2 bytes, 0b10 = 3 bytes, 0b11 = 4 bytes + st_command->AlternateBytesSize = ((rounded_size - 1) << OCTOSPI_CCR_ABSIZE_Pos) & OCTOSPI_CCR_ABSIZE_Msk; } switch (command->data.bus_width) { @@ -176,9 +199,11 @@ void qspi_prepare_command(const qspi_command_t *command, OSPI_RegularCmdTypeDef debug_if(qspi_api_c_debug, "qspi_prepare_command Out: InstructionMode %x Instruction %x AddressMode %x AddressSize %x Address %x DataMode %x\n", st_command->InstructionMode, st_command->Instruction, st_command->AddressMode, st_command->AddressSize, st_command->Address, st_command->DataMode); + + return QSPI_STATUS_OK; } #else /* OCTOSPI */ -void qspi_prepare_command(const qspi_command_t *command, QSPI_CommandTypeDef *st_command) +qspi_status_t qspi_prepare_command(const qspi_command_t *command, QSPI_CommandTypeDef *st_command) { debug_if(qspi_api_c_debug, "qspi_prepare_command In: instruction.value %x dummy_count %x address.bus_width %x address.disabled %x address.value %x address.size %x\n", command->instruction.value, command->dummy_count, command->address.bus_width, command->address.disabled, command->address.value, command->address.size); @@ -230,15 +255,19 @@ void qspi_prepare_command(const qspi_command_t *command, QSPI_CommandTypeDef *st st_command->AddressSize = (command->address.size << QUADSPI_CCR_ADSIZE_Pos) & QUADSPI_CCR_ADSIZE_Msk; } + uint8_t alt_lines = 0; switch (command->alt.bus_width) { case QSPI_CFG_BUS_SINGLE: st_command->AlternateByteMode = QSPI_ALTERNATE_BYTES_1_LINE; + alt_lines = 1; break; case QSPI_CFG_BUS_DUAL: st_command->AlternateByteMode = QSPI_ALTERNATE_BYTES_2_LINES; + alt_lines = 2; break; case QSPI_CFG_BUS_QUAD: st_command->AlternateByteMode = QSPI_ALTERNATE_BYTES_4_LINES; + alt_lines = 4; break; default: st_command->AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; @@ -249,10 +278,40 @@ void qspi_prepare_command(const qspi_command_t *command, QSPI_CommandTypeDef *st st_command->AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE; st_command->AlternateBytesSize = 0; } else { - st_command->AlternateBytes = command->alt.value; + // Alt size must be a multiple of the number of bus lines used (i.e. a whole number of cycles) + if ((alt_lines == 0) || (command->alt.size % alt_lines != 0)) { + return QSPI_STATUS_ERROR; + } + + // Round up to nearest byte - unused parts of byte act as dummy cycles + uint32_t rounded_size = ((command->alt.size - 1) >> 3) + 1; + // Maximum of 4 alt bytes + if (rounded_size > 4) { + return QSPI_STATUS_ERROR; + } + + // Unused bits in most significant byte of alt + uint8_t leftover_bits = (rounded_size << 3) - command->alt.size; + if (leftover_bits != 0) { + // Account for dummy cycles that will be spent in the alt portion of the command + uint8_t integrated_dummy_cycles = leftover_bits / alt_lines; + if (st_command->DummyCycles < integrated_dummy_cycles) + { + // Not enough dummy cycles to account for a short alt + return QSPI_STATUS_ERROR; + } + st_command->DummyCycles -= integrated_dummy_cycles; + + // Align alt value to the end of the most significant byte + st_command->AlternateBytes = command->alt.value << leftover_bits; + } else { + rounded_size -= 1; + 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; + // 0b00 = 1 byte, 0b01 = 2 bytes, 0b10 = 3 bytes, 0b11 = 4 bytes + st_command->AlternateBytesSize = ((rounded_size - 1) << QUADSPI_CCR_ABSIZE_Pos) & QUADSPI_CCR_ABSIZE_Msk; } switch (command->data.bus_width) { @@ -271,8 +330,11 @@ void qspi_prepare_command(const qspi_command_t *command, QSPI_CommandTypeDef *st } st_command->NbData = 0; + debug_if(qspi_api_c_debug, "qspi_prepare_command Out: InstructionMode %x Instruction %x AddressMode %x AddressSize %x Address %x DataMode %x\n", st_command->InstructionMode, st_command->Instruction, st_command->AddressMode, st_command->AddressSize, st_command->Address, st_command->DataMode); + + return QSPI_STATUS_OK; } #endif /* OCTOSPI */ @@ -586,10 +648,12 @@ qspi_status_t qspi_write(qspi_t *obj, const qspi_command_t *command, const void debug_if(qspi_api_c_debug, "qspi_write size %u\n", *length); OSPI_RegularCmdTypeDef st_command; - qspi_prepare_command(command, &st_command); + qspi_status_t status = qspi_prepare_command(command, &st_command); + if (status != QSPI_STATUS_OK) { + return status; + } st_command.NbData = *length; - qspi_status_t status = QSPI_STATUS_OK; if (HAL_OSPI_Command(&obj->handle, &st_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { debug_if(qspi_api_c_debug, "HAL_OSPI_Command error\n"); @@ -607,10 +671,12 @@ qspi_status_t qspi_write(qspi_t *obj, const qspi_command_t *command, const void 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); + qspi_status_t status = qspi_prepare_command(command, &st_command); + if (status != QSPI_STATUS_OK) { + return status; + } 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; @@ -631,10 +697,12 @@ qspi_status_t qspi_write(qspi_t *obj, const qspi_command_t *command, const void qspi_status_t qspi_read(qspi_t *obj, const qspi_command_t *command, void *data, size_t *length) { OSPI_RegularCmdTypeDef st_command; - qspi_prepare_command(command, &st_command); + qspi_status_t status = qspi_prepare_command(command, &st_command); + if (status != QSPI_STATUS_OK) { + return status; + } st_command.NbData = *length; - qspi_status_t status = QSPI_STATUS_OK; if (HAL_OSPI_Command(&obj->handle, &st_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK) { debug_if(qspi_api_c_debug, "HAL_OSPI_Command error\n"); @@ -654,10 +722,12 @@ qspi_status_t qspi_read(qspi_t *obj, const qspi_command_t *command, void *data, 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); - + qspi_status_t status = qspi_prepare_command(command, &st_command); + if (status != QSPI_STATUS_OK) { + return status; + } + 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; @@ -683,7 +753,10 @@ qspi_status_t qspi_command_transfer(qspi_t *obj, const qspi_command_t *command, if ((tx_data == NULL || tx_size == 0) && (rx_data == NULL || rx_size == 0)) { // only command, no rx or tx OSPI_RegularCmdTypeDef st_command; - qspi_prepare_command(command, &st_command); + status = qspi_prepare_command(command, &st_command); + if (status != QSPI_STATUS_OK) { + return status; + } st_command.NbData = 1; st_command.DataMode = HAL_OSPI_DATA_NONE; /* Instruction only */ @@ -720,7 +793,10 @@ qspi_status_t qspi_command_transfer(qspi_t *obj, const qspi_command_t *command, 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); + status = qspi_prepare_command(command, &st_command); + if (status != QSPI_STATUS_OK) { + return status; + } st_command.NbData = 1; st_command.DataMode = QSPI_DATA_NONE; /* Instruction only */ diff --git a/targets/TARGET_Silicon_Labs/TARGET_EFM32/qspi_api.c b/targets/TARGET_Silicon_Labs/TARGET_EFM32/qspi_api.c index 1435751b1b..75f988b6a4 100644 --- a/targets/TARGET_Silicon_Labs/TARGET_EFM32/qspi_api.c +++ b/targets/TARGET_Silicon_Labs/TARGET_EFM32/qspi_api.c @@ -33,6 +33,8 @@ #include "PeripheralPins.h" #include "pinmap_function.h" +#define SUPPORTED_ALT_SIZE 8u + 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) { @@ -268,7 +270,7 @@ qspi_status_t qspi_command_transfer(qspi_t *obj, const qspi_command_t *command, cfg.modeBitEnable = true; obj->instance->MODEBITCONFIG = command->alt.value & _QSPI_MODEBITCONFIG_MODE_MASK; - if(command->alt.size != QSPI_CFG_ALT_SIZE_8) { + if(command->alt.size != SUPPORTED_ALT_SIZE) { //do not support 'alt' bigger than 8 bit return QSPI_STATUS_INVALID_PARAMETER; } @@ -338,7 +340,7 @@ qspi_status_t qspi_read(qspi_t *obj, const qspi_command_t *command, void *data, obj->instance->DEVINSTRRDCONFIG |= QSPI_DEVINSTRRDCONFIG_MODEBITENABLE; obj->instance->MODEBITCONFIG = command->alt.value & _QSPI_MODEBITCONFIG_MODE_MASK; - if(command->alt.size != QSPI_CFG_ALT_SIZE_8) { + if(command->alt.size != SUPPORTED_ALT_SIZE) { // Do not support 'alt' bigger than 8 bit return QSPI_STATUS_INVALID_PARAMETER; }