diff --git a/os/ex/Micron/m25q.c b/os/ex/Micron/m25q.c index 9a170a17f..84a67f2cc 100644 --- a/os/ex/Micron/m25q.c +++ b/os/ex/Micron/m25q.c @@ -25,8 +25,9 @@ * @{ */ -#include "hal.h" +#include +#include "hal.h" #include "m25q.h" /*===========================================================================*/ @@ -82,11 +83,7 @@ static flash_descriptor_t descriptor = { .attributes = FLASH_ATTR_ERASED_IS_ONE | FLASH_ATTR_REWRITABLE | FLASH_ATTR_SUSPEND_ERASE_CAPABLE, .page_size = 256U, -#if M25Q_USE_SUB_SECTORS == TRUE - .sectors_count = 4096U, -#else - .sectors_count = 256U, -#endif + .sectors_count = 0U, /* It is overwritten.*/ .sectors = NULL, .sectors_size = SECTOR_SIZE, .address = 0U @@ -209,9 +206,11 @@ static const uint8_t evconf_value[1] = {0xCF}; #elif M25Q_BUS_MODE == M25Q_BUS_MODE_QSPI2L static const uint8_t evconf_value[1] = {0x8F}; #else -static const uint8_t evconf_value[1] = {0xCF};//{0x4F}; +static const uint8_t evconf_value[1] = {0x4F}; #endif +static const uint8_t flash_status[1] = {(M25Q_READ_DUMMY_CYCLES << 4U) | 0x0FU}; + #endif /* M25Q_BUS_MODE != M25Q_BUS_MODE_SPI */ /*===========================================================================*/ @@ -314,6 +313,39 @@ static void flash_cmd_receive(M25QDriver *devp, #endif } +static void flash_cmd_send(M25QDriver *devp, + uint8_t cmd, + size_t n, + const uint8_t *p) { +#if M25Q_BUS_MODE != M25Q_BUS_MODE_SPI + qspi_command_t mode; + + mode.cfg = QSPI_CFG_CMD(cmd) | +#if M25Q_BUS_MODE == M25Q_BUS_MODE_QSPI1L + QSPI_CFG_CMD_MODE_ONE_LINE | + QSPI_CFG_DATA_MODE_ONE_LINE; +#elif M25Q_BUS_MODE == M25Q_BUS_MODE_QSPI2L + QSPI_CFG_CMD_MODE_TWO_LINES | + QSPI_CFG_DATA_MODE_TWO_LINES; +#else + QSPI_CFG_CMD_MODE_FOUR_LINES | + QSPI_CFG_DATA_MODE_FOUR_LINES; + +#endif + mode.addr = 0U; + mode.alt = 0U; + qspiSend(devp->config->qspip, &mode, n, p); +#else + uint8_t buf[1]; + + spiSelect(devp->config->spip); + buf[0] = cmd; + spiSend(devp->config->spip, 1, buf); + spiSend(devp->config->spip, n, p); + spiUnselect(devp->config->spip); +#endif +} + static void flash_cmd_addr(M25QDriver *devp, uint8_t cmd, flash_address_t addr) { @@ -437,6 +469,42 @@ static void flash_cmd_addr_receive(M25QDriver *devp, #endif } +#if (M25Q_BUS_MODE != M25Q_BUS_MODE_SPI) || defined(__DOXYGEN__) +static void flash_cmd_addr_dummy_receive(M25QDriver *devp, + uint8_t cmd, + flash_address_t addr, + uint8_t dummy, + size_t n, + uint8_t *p) { + qspi_command_t mode; + + mode.cfg = QSPI_CFG_CMD(cmd) | +#if M25Q_BUS_MODE == M25Q_BUS_MODE_QSPI1L + QSPI_CFG_CMD_MODE_ONE_LINE | + QSPI_CFG_ADDR_MODE_ONE_LINE | + QSPI_CFG_ADDR_SIZE_24 | + QSPI_CFG_DUMMY_CYCLES(dummy) | + QSPI_CFG_DATA_MODE_ONE_LINE; +#elif M25Q_BUS_MODE == M25Q_BUS_MODE_QSPI2L + QSPI_CFG_CMD_MODE_TWO_LINES | + QSPI_CFG_ADDR_MODE_TWO_LINES | + QSPI_CFG_ADDR_SIZE_24 | + QSPI_CFG_DUMMY_CYCLES(dummy) | + QSPI_CFG_DATA_MODE_TWO_LINES; +#else + QSPI_CFG_CMD_MODE_FOUR_LINES | + QSPI_CFG_ADDR_MODE_FOUR_LINES | + QSPI_CFG_ADDR_SIZE_24 | + QSPI_CFG_DUMMY_CYCLES(dummy) | + QSPI_CFG_DATA_MODE_FOUR_LINES; + +#endif + mode.addr = addr; + mode.alt = 0U; + qspiReceive(devp->config->qspip, &mode, n, p); +} +#endif /* M25Q_BUS_MODE != M25Q_BUS_MODE_SPI */ + static flash_error_t flash_poll_status(M25QDriver *devp) { return FLASH_NO_ERROR; @@ -454,6 +522,38 @@ static const flash_descriptor_t *get_descriptor(void *instance) { static flash_error_t read(void *instance, flash_address_t addr, uint8_t *rp, size_t n) { + M25QDriver *devp = (M25QDriver *)instance; + + osalDbgCheck((instance != NULL) && (rp != NULL) && (n > 0U)); + osalDbgCheck((size_t)addr + n <= (size_t)descriptor.sectors_count * + (size_t)descriptor.sectors_size); + osalDbgAssert((devp->state == FLASH_READY) || (devp->state == FLASH_ERASE), + "invalid state"); + + if (devp->state == FLASH_ERASE) { + return FLASH_BUSY_ERASING; + } + + /* Bus acquired.*/ + flash_bus_acquire(devp); + + /* FLASH_READY state while the operation is performed.*/ + devp->state = FLASH_READ; + +#if M25Q_BUS_MODE != M25Q_BUS_MODE_SPI + /* Fast read command in QSPI mode.*/ + flash_cmd_addr_dummy_receive(devp, M25Q_CMD_FAST_READ, + addr, M25Q_READ_DUMMY_CYCLES, + n, rp); +#else + /* Normal read command in SPI mode.*/ +#endif + + /* Ready state again.*/ + devp->state = FLASH_READY; + + /* Bus released.*/ + flash_bus_release(devp); return FLASH_NO_ERROR; } @@ -516,6 +616,18 @@ void m25qObjectInit(M25QDriver *devp) { #endif } +static const qspi_command_t cmd_test_reset_enable_4 = { + .cfg = QSPI_CFG_ALT_MODE_FOUR_LINES, + .addr = 0, + .alt = M25Q_CMD_RESET_ENABLE +}; + +static const qspi_command_t cmd_test_reset_memory_4 = { + .cfg = QSPI_CFG_ALT_MODE_FOUR_LINES, + .addr = 0, + .alt = M25Q_CMD_RESET_MEMORY +}; + /** * @brief Configures and activates N25Q128 driver. * @@ -532,7 +644,6 @@ void m25qStart(M25QDriver *devp, const M25QConfig *config) { devp->config = config; if (devp->state == FLASH_STOP) { - uint8_t id[3]; /* Bus acquisition.*/ flash_bus_acquire(devp); @@ -546,46 +657,62 @@ void m25qStart(M25QDriver *devp, const M25QConfig *config) { /* QSPI initialization.*/ qspiStart(devp->config->qspip, devp->config->qspicfg); + /* Reading device ID and unique ID.*/ + #if M25Q_SWITCH_WIDTH == TRUE - /* Attempting a device reset with decreasing bus widths, commands + /* Attempting a device reset with different bus widths, commands shorter than 8 bits are ignored.*/ - qspiCommand(devp->config->qspip, &cmd_reset_enable_4); - qspiCommand(devp->config->qspip, &cmd_reset_memory_4); - qspiCommand(devp->config->qspip, &cmd_reset_enable_2); - qspiCommand(devp->config->qspip, &cmd_reset_memory_2); qspiCommand(devp->config->qspip, &cmd_reset_enable_1); qspiCommand(devp->config->qspip, &cmd_reset_memory_1); + qspiCommand(devp->config->qspip, &cmd_reset_enable_2); + qspiCommand(devp->config->qspip, &cmd_reset_memory_2); + qspiCommand(devp->config->qspip, &cmd_reset_enable_4); + qspiCommand(devp->config->qspip, &cmd_reset_memory_4); #endif - /* Reading device ID.*/ - qspiReceive(devp->config->qspip, &cmd_read_id, 3, id); + /* Reading device ID and unique ID.*/ + qspiReceive(devp->config->qspip, &cmd_read_id, + sizeof devp->device_id, devp->device_id); #endif /* M25Q_BUS_MODE != M25Q_BUS_MODE_SPI */ /* Checking if the device is white listed.*/ - osalDbgAssert(find_id(manufacturer_ids, sizeof manufacturer_ids, id[0]), + osalDbgAssert(find_id(manufacturer_ids, + sizeof manufacturer_ids, + devp->device_id[0]), "invalid manufacturer id"); - osalDbgAssert(find_id(memory_type_ids, sizeof memory_type_ids, id[1]), + osalDbgAssert(find_id(memory_type_ids, + sizeof memory_type_ids, + devp->device_id[1]), "invalid memory type id"); #if (M25Q_BUS_MODE != M25Q_BUS_MODE_SPI) && (M25Q_SWITCH_WIDTH == TRUE) /* Setting up final bus width.*/ qspiCommand(devp->config->qspip, &cmd_write_enable); qspiSend(devp->config->qspip, &cmd_write_evconf, 1, evconf_value); + + { + uint8_t id[3]; + + /* Reading ID again for confirmation.*/ + flash_cmd_receive(devp, M25Q_CMD_MULTIPLE_IO_READ_ID, 3, id); + + /* Checking if the device is white listed.*/ + osalDbgAssert(memcmp(id, devp->device_id, 3) == 0, + "id confirmation failed"); + } #endif - flash_cmd_receive(devp, M25Q_CMD_READ_ID, 3, id); + /* Setting up the device size.*/ + descriptor.sectors_count = (1U << (size_t)devp->device_id[2]) / SECTOR_SIZE; - flash_cmd_receive(devp, M25Q_CMD_READ_ID, 3, id); - - /* Reading device ID.*/ - qspiReceive(devp->config->qspip, &cmd_read_id, 3, id); - - /* Reset Enable command.*/ -// flash_short_cmd(devp, M25Q_CMD_RESET_ENABLE); - - /* Reset Memory command.*/ -// flash_short_cmd(devp, M25Q_CMD_RESET_MEMORY); +#if (M25Q_BUS_MODE != M25Q_BUS_MODE_SPI) + /* Setting up the dummy cycles to be used for rast read operations.*/ + flash_cmd(devp, M25Q_CMD_WRITE_ENABLE); + flash_cmd_send(devp, M25Q_CMD_WRITE_V_CONF_REGISTER, + 1, flash_status); +#endif + /* Driver in ready state.*/ devp->state = FLASH_READY; /* Bus release.*/ @@ -617,6 +744,8 @@ void m25qStop(M25QDriver *devp) { #endif devp->config = NULL; + + /* Driver stopped.*/ devp->state = FLASH_STOP; /* Bus release.*/ diff --git a/os/ex/Micron/m25q.h b/os/ex/Micron/m25q.h index 27ee06afc..865cc32ea 100644 --- a/os/ex/Micron/m25q.h +++ b/os/ex/Micron/m25q.h @@ -41,9 +41,10 @@ #define M25Q_CMD_RESET_ENABLE 0x66 #define M25Q_CMD_RESET_MEMORY 0x99 #define M25Q_CMD_READ_ID 0x9F +#define M25Q_CMD_MULTIPLE_IO_READ_ID 0xAF #define M25Q_CMD_READ_DISCOVERY_PARAMETER 0x5A #define M25Q_CMD_READ 0x03 -#define M25Q_CMD_FAST_READ 0x08 +#define M25Q_CMD_FAST_READ 0x0B #define M25Q_CMD_WRITE_ENABLE 0x06 #define M25Q_CMD_WRITE_DISABLE 0x04 #define M25Q_CMD_READ_STATUS_REGISTER 0x05 @@ -111,6 +112,15 @@ #define M25Q_BUS_MODE M25Q_BUS_MODE_QSPI4L #endif +/** + * @brief Number of dummy cycles for fast read (1..15). + * @details This is the number of dummy cycles to be used for fast read + * operations. + */ +#if !defined(M25Q_READ_DUMMY_CYCLES) || defined(__DOXYGEN__) +#define M25Q_READ_DUMMY_CYCLES 8 +#endif + /** * @brief Switch QSPI bus width on initialization. * @details A bus width initialization is performed by writing the @@ -185,6 +195,17 @@ #error "M25Q_SHARED_SPI requires SPI_USE_MUTUAL_EXCLUSION" #endif +#if (M25Q_BUS_MODE != M25Q_BUS_MODE_SPI) && \ + (M25Q_BUS_MODE != M25Q_BUS_MODE_QSPI1L) && \ + (M25Q_BUS_MODE != M25Q_BUS_MODE_QSPI2L) && \ + (M25Q_BUS_MODE != M25Q_BUS_MODE_QSPI4L) +#error "invalid M25Q_BUS_MODE selected" +#endif + +#if (M25Q_READ_DUMMY_CYCLES < 1) || (M25Q_READ_DUMMY_CYCLES > 15) +#error "invalid M25Q_READ_DUMMY_CYCLES value (1..15)" +#endif + /*===========================================================================*/ /* Driver data structures and types. */ /*===========================================================================*/ @@ -250,6 +271,10 @@ typedef struct { */ uint32_t qspi_mode; #endif + /** + * @brief Device ID and unique ID. + */ + uint8_t device_id[20]; } M25QDriver; /*===========================================================================*/ diff --git a/os/hal/ports/STM32/LLD/QUADSPIv1/hal_qspi_lld.c b/os/hal/ports/STM32/LLD/QUADSPIv1/hal_qspi_lld.c index 40df845db..f914b6163 100644 --- a/os/hal/ports/STM32/LLD/QUADSPIv1/hal_qspi_lld.c +++ b/os/hal/ports/STM32/LLD/QUADSPIv1/hal_qspi_lld.c @@ -211,6 +211,22 @@ void qspi_lld_stop(QSPIDriver *qspip) { */ void qspi_lld_command(QSPIDriver *qspip, const qspi_command_t *cmdp) { +#if STM32_USE_STM32_D1_WORKAROUND == TRUE + /* If it is a command without address and alternate phases then the command + is sent as an alternate byte, the command phase is suppressed.*/ + if ((cmdp->cfg & (QSPI_CFG_ADDR_MODE_MASK | QSPI_CFG_ALT_MODE_MASK)) == 0U) { + uint32_t cfg; + + /* The command mode field is copied in the alternate mode field. All + other fields are not used in this scenario.*/ + cfg = (cmdp->cfg & QSPI_CFG_CMD_MODE_MASK) << 6U; + + qspip->qspi->DLR = 0U; + qspip->qspi->ABR = cmdp->cfg & QSPI_CFG_CMD_MASK; + qspip->qspi->CCR = cfg; + return; + } +#endif qspip->qspi->DLR = 0U; qspip->qspi->ABR = cmdp->alt; qspip->qspi->CCR = cmdp->cfg; diff --git a/os/hal/ports/STM32/LLD/QUADSPIv1/hal_qspi_lld.h b/os/hal/ports/STM32/LLD/QUADSPIv1/hal_qspi_lld.h index c3a82d690..4465755bf 100644 --- a/os/hal/ports/STM32/LLD/QUADSPIv1/hal_qspi_lld.h +++ b/os/hal/ports/STM32/LLD/QUADSPIv1/hal_qspi_lld.h @@ -31,6 +31,17 @@ /* Driver constants. */ /*===========================================================================*/ +/** + * @name DCR register options + * @{ + */ +#define STM32_DCR_CK_MODE (1U << 0U) +#define STM32_DCR_CSHT_MASK (7U << 8U) +#define STM32_DCR_CSHT(n) ((n) << 8U) +#define STM32_DCR_FSIZE_MASK (31U << 16U) +#define STM32_DCR_FSIZE(n) ((n) << 16U) +/** @} */ + /*===========================================================================*/ /* Driver pre-compile time settings. */ /*===========================================================================*/ @@ -85,6 +96,17 @@ #if !defined(STM32_QSPI_DMA_ERROR_HOOK) || defined(__DOXYGEN__) #define STM32_QSPI_DMA_ERROR_HOOK(qspip) osalSysHalt("DMA failure") #endif + +/** + * @brief Enables a workaround for a STM32L476 QUADSPI errata. + * @details The document DM00111498 states: "QUADSPI_BK1_IO1 is always an + * input when the command is sent in dual or quad SPI mode". + * This workaround makes commands without address or data phases + * to be sent as alternate bytes. + */ +#if !defined(STM32_USE_STM32_D1_WORKAROUND) || defined(__DOXYGEN__) +#define STM32_USE_STM32_D1_WORKAROUND TRUE +#endif /** @} */ /*===========================================================================*/ diff --git a/testhal/STM32/STM32L4xx/QSPI-N25Q128/debug/QSPI-N25Q128 (OpenOCD, Flash and Run).launch b/testhal/STM32/STM32L4xx/QSPI-N25Q128/debug/QSPI-N25Q128 (OpenOCD, Flash and Run).launch index 5c5c982a2..573a54f12 100644 --- a/testhal/STM32/STM32L4xx/QSPI-N25Q128/debug/QSPI-N25Q128 (OpenOCD, Flash and Run).launch +++ b/testhal/STM32/STM32L4xx/QSPI-N25Q128/debug/QSPI-N25Q128 (OpenOCD, Flash and Run).launch @@ -33,7 +33,7 @@ - + diff --git a/testhal/STM32/STM32L4xx/QSPI-N25Q128/main.c b/testhal/STM32/STM32L4xx/QSPI-N25Q128/main.c index 5e57497fa..51abd65cd 100644 --- a/testhal/STM32/STM32L4xx/QSPI-N25Q128/main.c +++ b/testhal/STM32/STM32L4xx/QSPI-N25Q128/main.c @@ -21,7 +21,7 @@ const QSPIConfig qspicfg1 = { NULL, - 0 + STM32_DCR_FSIZE(24) | STM32_DCR_CSHT(1) }; qspi_command_t cmd_read_id = { @@ -63,6 +63,7 @@ static THD_FUNCTION(Thread1, arg) { * Application entry point. */ int main(void) { + flash_error_t err; /* * System initializations. @@ -89,8 +90,11 @@ int main(void) { */ m25qObjectInit(&m25q); m25qStart(&m25q, &m25qcfg1); -// qspiStart(&QSPID1, &qspicfg1); -// qspiReceive(&QSPID1, &cmd_read_id, 17, buffer); + + /* Reading it back.*/ + err = flashRead(&m25q, 0, buffer, 128); + if (err != FLASH_NO_ERROR) + chSysHalt("read error"); /* * Normal main() thread activity, in this demo it does nothing.