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).
pull/11696/head
Matthew Macovsky 2019-09-06 18:09:21 +01:00 committed by adbridge
parent 03affe94d8
commit 08a2709993
11 changed files with 266 additions and 106 deletions

View File

@ -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

View File

@ -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;
}
/***************************************************/

View File

@ -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
*

View File

@ -26,6 +26,21 @@ namespace mbed {
QSPI *QSPI::_owner = NULL;
SingletonPtr<PlatformMutex> 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)

View File

@ -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;

View File

@ -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
*

View File

@ -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;

View File

@ -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;

View File

@ -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
{

View File

@ -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 */

View File

@ -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;
}