diff options
author | Giovanni Di Sirio <gdisirio@gmail.com> | 2016-06-16 07:44:08 +0000 |
---|---|---|
committer | Giovanni Di Sirio <gdisirio@gmail.com> | 2016-06-16 07:44:08 +0000 |
commit | 77a63206e770cb4a73abf3063ce381fa8de4705b (patch) | |
tree | c6c33fbd512f6989c5f318e945b4a394502071b8 /os/ex/Micron/m25q.c | |
parent | c4995095eec2018618a9f96a09797ce84e470af9 (diff) | |
download | ChibiOS-77a63206e770cb4a73abf3063ce381fa8de4705b.tar.gz ChibiOS-77a63206e770cb4a73abf3063ce381fa8de4705b.tar.bz2 ChibiOS-77a63206e770cb4a73abf3063ce381fa8de4705b.zip |
git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@9627 35acf78f-673a-0410-8e92-d51de3d6d3f4
Diffstat (limited to 'os/ex/Micron/m25q.c')
-rw-r--r-- | os/ex/Micron/m25q.c | 1668 |
1 files changed, 834 insertions, 834 deletions
diff --git a/os/ex/Micron/m25q.c b/os/ex/Micron/m25q.c index 3dbb3df62..3d6de626d 100644 --- a/os/ex/Micron/m25q.c +++ b/os/ex/Micron/m25q.c @@ -1,834 +1,834 @@ -/* - ChibiOS - Copyright (C) 2016 Giovanni Di Sirio - - This file is part of ChibiOS. - - ChibiOS is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - ChibiOS is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <http://www.gnu.org/licenses/>. -*/ - -/** - * @file m25q.c - * @brief Micron serial flash driver code. - * - * @addtogroup m25q - * @{ - */ - -#include <string.h> - -#include "hal.h" -#include "m25q.h" - -/*===========================================================================*/ -/* Driver local definitions. */ -/*===========================================================================*/ - -#define PAGE_SIZE 256U -#define PAGE_MASK (PAGE_SIZE - 1U) - -#if M25Q_USE_SUB_SECTORS == TRUE -#define SECTOR_SIZE 0x00001000U -#define CMD_SECTOR_ERASE M25Q_CMD_SUBSECTOR_ERASE -#else -#define SECTOR_SIZE 0x00010000U -#define CMD_SECTOR_ERASE M25Q_CMD_SECTOR_ERASE -#endif - -/*===========================================================================*/ -/* Driver exported variables. */ -/*===========================================================================*/ - -/*===========================================================================*/ -/* Driver local variables and types. */ -/*===========================================================================*/ - -static const flash_descriptor_t *m25q_get_descriptor(void *instance); -static flash_error_t m25q_read(void *instance, flash_offset_t offset, - size_t n, uint8_t *rp); -static flash_error_t m25q_program(void *instance, flash_offset_t offset, - size_t n, const uint8_t *pp); -static flash_error_t m25q_start_erase_all(void *instance); -static flash_error_t m25q_start_erase_sector(void *instance, - flash_sector_t sector); -static flash_error_t m25q_query_erase(void *instance, uint32_t *msec); -static flash_error_t m25q_verify_erase(void *instance, flash_sector_t sector); -static flash_error_t m25q_read_sfdp(void *instance, flash_offset_t offset, - size_t n, uint8_t *rp); - -/** - * @brief Virtual methods table. - */ -static const struct M25QDriverVMT m25q_vmt = { - m25q_get_descriptor, m25q_read, m25q_program, - m25q_start_erase_all, m25q_start_erase_sector, - m25q_query_erase, m25q_verify_erase, - m25q_read_sfdp -}; - -/** - * @brief N25Q128 descriptor. - */ -static flash_descriptor_t m25q_descriptor = { - .attributes = FLASH_ATTR_ERASED_IS_ONE | FLASH_ATTR_REWRITABLE | - FLASH_ATTR_SUSPEND_ERASE_CAPABLE, - .page_size = 256U, - .sectors_count = 0U, /* It is overwritten.*/ - .sectors = NULL, - .sectors_size = SECTOR_SIZE, - .address = 0U -}; - -#if JESD216_BUS_MODE != JESD216_BUS_MODE_SPI -/* Initial M25Q_CMD_READ_ID command.*/ -static const qspi_command_t m25q_cmd_read_id = { - .cfg = QSPI_CFG_CMD(M25Q_CMD_READ_ID) | -#if M25Q_SWITCH_WIDTH == TRUE - QSPI_CFG_CMD_MODE_ONE_LINE | - QSPI_CFG_DATA_MODE_ONE_LINE, -#else -#if JESD216_BUS_MODE == JESD216_BUS_MODE_QSPI1L - QSPI_CFG_CMD_MODE_ONE_LINE | - QSPI_CFG_DATA_MODE_ONE_LINE, -#elif JESD216_BUS_MODE == JESD216_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 -#endif - .addr = 0, - .alt = 0 -}; - -/* Initial M25Q_CMD_WRITE_ENHANCED_V_CONF_REGISTER command.*/ -static const qspi_command_t m25q_cmd_write_evconf = { - .cfg = QSPI_CFG_CMD(M25Q_CMD_WRITE_ENHANCED_V_CONF_REGISTER) | -#if M25Q_SWITCH_WIDTH == TRUE - QSPI_CFG_CMD_MODE_ONE_LINE | - QSPI_CFG_DATA_MODE_ONE_LINE, -#else -#if JESD216_BUS_MODE == JESD216_BUS_MODE_QSPI1L - QSPI_CFG_CMD_MODE_ONE_LINE | - QSPI_CFG_DATA_MODE_ONE_LINE, -#elif JESD216_BUS_MODE == JESD216_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 -#endif - .addr = 0, - .alt = 0 -}; - -/* Initial M25Q_CMD_WRITE_ENABLE command.*/ -static const qspi_command_t m25q_cmd_write_enable = { - .cfg = QSPI_CFG_CMD(M25Q_CMD_WRITE_ENABLE) | -#if M25Q_SWITCH_WIDTH == TRUE - QSPI_CFG_CMD_MODE_ONE_LINE, -#else -#if JESD216_BUS_MODE == JESD216_BUS_MODE_QSPI1L - QSPI_CFG_CMD_MODE_ONE_LINE, -#elif JESD216_BUS_MODE == JESD216_BUS_MODE_QSPI2L - QSPI_CFG_CMD_MODE_TWO_LINES, -#else - QSPI_CFG_CMD_MODE_FOUR_LINES, -#endif -#endif - .addr = 0, - .alt = 0 -}; - -/* Bus width initialization.*/ -#if JESD216_BUS_MODE == JESD216_BUS_MODE_QSPI1L -static const uint8_t m25q_evconf_value[1] = {0xCF}; -#elif JESD216_BUS_MODE == JESD216_BUS_MODE_QSPI2L -static const uint8_t m25q_evconf_value[1] = {0x8F}; -#else -static const uint8_t m25q_evconf_value[1] = {0x4F}; -#endif -#endif /* JESD216_BUS_MODE != JESD216_BUS_MODE_SPI */ - -static const uint8_t m25q_manufacturer_ids[] = M25Q_SUPPORTED_MANUFACTURE_IDS; -static const uint8_t m25q_memory_type_ids[] = M25Q_SUPPORTED_MEMORY_TYPE_IDS; - -/*===========================================================================*/ -/* Driver local functions. */ -/*===========================================================================*/ - -static bool m25q_find_id(const uint8_t *set, size_t size, uint8_t element) { - size_t i; - - for (i = 0; i < size; i++) { - if (set[i] == element) { - return true; - } - } - return false; -} - -#if (JESD216_BUS_MODE != JESD216_BUS_MODE_SPI) || defined(__DOXYGEN__) -void m25q_reset_xip(M25QDriver *devp) { - static const uint8_t flash_conf[1] = { - (M25Q_READ_DUMMY_CYCLES << 4U) | 0x0FU - }; - qspi_command_t cmd; - uint8_t buf[1]; - - /* Resetting XIP mode by reading one byte without XIP confirmation bit.*/ - cmd.alt = 0xFF; - cmd.addr = 0; - cmd.cfg = QSPI_CFG_CMD_MODE_NONE | - QSPI_CFG_ADDR_SIZE_24 | -#if JESD216_BUS_MODE == JESD216_BUS_MODE_QSPI1L - QSPI_CFG_ADDR_MODE_ONE_LINE | - QSPI_CFG_DATA_MODE_ONE_LINE | -#elif JESD216_BUS_MODE == JESD216_BUS_MODE_QSPI2L - QSPI_CFG_ADDR_MODE_TWO_LINES | - QSPI_CFG_DATA_MODE_TWO_LINES | -#else - QSPI_CFG_ADDR_MODE_FOUR_LINES | - QSPI_CFG_DATA_MODE_FOUR_LINES | -#endif - QSPI_CFG_ALT_MODE_FOUR_LINES | /* Always 4 lines, note.*/ - QSPI_CFG_ALT_SIZE_8 | - QSPI_CFG_DUMMY_CYCLES(M25Q_READ_DUMMY_CYCLES - 2); - qspiReceive(devp->config->busp, &cmd, 1, buf); - - /* Enabling write operation.*/ - jesd216_cmd(devp->config->busp, M25Q_CMD_WRITE_ENABLE); - - /* Rewriting volatile configuration register.*/ - jesd216_cmd_send(devp->config->busp, M25Q_CMD_WRITE_V_CONF_REGISTER, - 1, flash_conf); -} - -void m25q_reset_memory(M25QDriver *devp) { - - /* 1x M25Q_CMD_RESET_ENABLE command.*/ - static const qspi_command_t cmd_reset_enable_1 = { - .cfg = QSPI_CFG_CMD(M25Q_CMD_RESET_ENABLE) | - QSPI_CFG_CMD_MODE_ONE_LINE, - .addr = 0, - .alt = 0 - }; - - /* 1x M25Q_CMD_RESET_MEMORY command.*/ - static const qspi_command_t cmd_reset_memory_1 = { - .cfg = QSPI_CFG_CMD(M25Q_CMD_RESET_MEMORY) | - QSPI_CFG_CMD_MODE_ONE_LINE, - .addr = 0, - .alt = 0 - }; - - /* If the device is in one bit mode then the following commands are - rejected because shorter than 8 bits. If the device is in multiple - bits mode then the commands are accepted and the device is reset to - one bit mode.*/ -#if JESD216_BUS_MODE == JESD216_BUS_MODE_QSPI4L - /* 4x M25Q_CMD_RESET_ENABLE command.*/ - static const qspi_command_t cmd_reset_enable_4 = { - .cfg = QSPI_CFG_CMD(M25Q_CMD_RESET_ENABLE) | - QSPI_CFG_CMD_MODE_FOUR_LINES, - .addr = 0, - .alt = 0 - }; - - /* 4x M25Q_CMD_RESET_MEMORY command.*/ - static const qspi_command_t cmd_reset_memory_4 = { - .cfg = QSPI_CFG_CMD(M25Q_CMD_RESET_MEMORY) | - QSPI_CFG_CMD_MODE_FOUR_LINES, - .addr = 0, - .alt = 0 - }; - - qspiCommand(devp->config->busp, &cmd_reset_enable_4); - qspiCommand(devp->config->busp, &cmd_reset_memory_4); -#else - /* 2x M25Q_CMD_RESET_ENABLE command.*/ - static const qspi_command_t cmd_reset_enable_2 = { - .cfg = QSPI_CFG_CMD(M25Q_CMD_RESET_ENABLE) | - QSPI_CFG_CMD_MODE_TWO_LINES, - .addr = 0, - .alt = 0 - }; - - /* 2x M25Q_CMD_RESET_MEMORY command.*/ - static const qspi_command_t cmd_reset_memory_2 = { - .cfg = QSPI_CFG_CMD(M25Q_CMD_RESET_MEMORY) | - QSPI_CFG_CMD_MODE_TWO_LINES, - .addr = 0, - .alt = 0 - }; - - qspiCommand(devp->config->busp, &cmd_reset_enable_2); - qspiCommand(devp->config->busp, &cmd_reset_memory_2); -#endif - - /* Now the device should be in one bit mode for sure and we perform a - device reset.*/ - qspiCommand(devp->config->busp, &cmd_reset_enable_1); - qspiCommand(devp->config->busp, &cmd_reset_memory_1); -} -#endif /* JESD216_BUS_MODE != JESD216_BUS_MODE_SPI */ - -static flash_error_t m25q_poll_status(M25QDriver *devp) { - uint8_t sts; - - do { -#if M25Q_NICE_WAITING == TRUE - osalThreadSleepMilliseconds(1); -#endif - /* Read status command.*/ - jesd216_cmd_receive(devp->config->busp, M25Q_CMD_READ_FLAG_STATUS_REGISTER, - 1, &sts); - } while ((sts & M25Q_FLAGS_PROGRAM_ERASE) == 0U); - - /* Checking for errors.*/ - if ((sts & M25Q_FLAGS_ALL_ERRORS) != 0U) { - /* Clearing status register.*/ - jesd216_cmd(devp->config->busp, M25Q_CMD_CLEAR_FLAG_STATUS_REGISTER); - - /* Program operation failed.*/ - return FLASH_ERROR_PROGRAM; - } - - return FLASH_NO_ERROR; -} - -static const flash_descriptor_t *m25q_get_descriptor(void *instance) { - M25QDriver *devp = (M25QDriver *)instance; - - osalDbgCheck(instance != NULL); - osalDbgAssert((devp->state != FLASH_UNINIT) && (devp->state != FLASH_STOP), - "invalid state"); - - return &m25q_descriptor; -} - -static flash_error_t m25q_read(void *instance, flash_offset_t offset, - size_t n, uint8_t *rp) { - M25QDriver *devp = (M25QDriver *)instance; - - osalDbgCheck((instance != NULL) && (rp != NULL) && (n > 0U)); - osalDbgCheck((size_t)offset + n <= (size_t)m25q_descriptor.sectors_count * - (size_t)m25q_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.*/ - jesd216_bus_acquire(devp->config->busp, devp->config->buscfg); - - /* FLASH_READY state while the operation is performed.*/ - devp->state = FLASH_READ; - -#if JESD216_BUS_MODE != JESD216_BUS_MODE_SPI - /* Fast read command in QSPI mode.*/ - jesd216_cmd_addr_dummy_receive(devp->config->busp, M25Q_CMD_FAST_READ, - offset, M25Q_READ_DUMMY_CYCLES, n, rp); -#else - /* Normal read command in SPI mode.*/ - jesd216_cmd_addr_receive(devp->config->busp, M25Q_CMD_READ, - offset, n, rp); -#endif - - /* Ready state again.*/ - devp->state = FLASH_READY; - - /* Bus released.*/ - jesd216_bus_release(devp->config->busp); - - return FLASH_NO_ERROR; -} - -static flash_error_t m25q_program(void *instance, flash_offset_t offset, - size_t n, const uint8_t *pp) { - M25QDriver *devp = (M25QDriver *)instance; - - osalDbgCheck((instance != NULL) && (pp != NULL) && (n > 0U)); - osalDbgCheck((size_t)offset + n <= (size_t)m25q_descriptor.sectors_count * - (size_t)m25q_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.*/ - jesd216_bus_acquire(devp->config->busp, devp->config->buscfg); - - /* FLASH_PGM state while the operation is performed.*/ - devp->state = FLASH_PGM; - - /* Data is programmed page by page.*/ - while (n > 0U) { - flash_error_t err; - - /* Data size that can be written in a single program page operation.*/ - size_t chunk = (size_t)(((offset | PAGE_MASK) + 1U) - offset); - if (chunk > n) { - chunk = n; - } - - /* Enabling write operation.*/ - jesd216_cmd(devp->config->busp, M25Q_CMD_WRITE_ENABLE); - - /* Page program command.*/ - jesd216_cmd_addr_send(devp->config->busp, M25Q_CMD_PAGE_PROGRAM, offset, - chunk, pp); - - /* Wait for status and check errors.*/ - err = m25q_poll_status(devp); - if (err != FLASH_NO_ERROR) { - - /* Bus released.*/ - jesd216_bus_release(devp->config->busp); - - return err; - } - - /* Next page.*/ - offset += chunk; - pp += chunk; - n -= chunk; - } - - /* Ready state again.*/ - devp->state = FLASH_READY; - - /* Bus released.*/ - jesd216_bus_release(devp->config->busp); - - return FLASH_NO_ERROR; -} - -static flash_error_t m25q_start_erase_all(void *instance) { - M25QDriver *devp = (M25QDriver *)instance; - - osalDbgCheck(instance != NULL); - osalDbgAssert((devp->state == FLASH_READY) || (devp->state == FLASH_ERASE), - "invalid state"); - - if (devp->state == FLASH_ERASE) { - return FLASH_BUSY_ERASING; - } - - /* Bus acquired.*/ - jesd216_bus_acquire(devp->config->busp, devp->config->buscfg); - - /* FLASH_ERASE state while the operation is performed.*/ - devp->state = FLASH_ERASE; - - /* Enabling write operation.*/ - jesd216_cmd(devp->config->busp, M25Q_CMD_WRITE_ENABLE); - - /* Bulk erase command.*/ - jesd216_cmd(devp->config->busp, M25Q_CMD_BULK_ERASE); - - /* Bus released.*/ - jesd216_bus_release(devp->config->busp); - - return FLASH_NO_ERROR; -} - -static flash_error_t m25q_start_erase_sector(void *instance, - flash_sector_t sector) { - M25QDriver *devp = (M25QDriver *)instance; - flash_offset_t offset = (flash_offset_t)(sector * SECTOR_SIZE); - - osalDbgCheck(instance != NULL); - osalDbgCheck(sector < m25q_descriptor.sectors_count); - osalDbgAssert((devp->state == FLASH_READY) || (devp->state == FLASH_ERASE), - "invalid state"); - - if (devp->state == FLASH_ERASE) { - return FLASH_BUSY_ERASING; - } - - /* Bus acquired.*/ - jesd216_bus_acquire(devp->config->busp, devp->config->buscfg); - - /* FLASH_ERASE state while the operation is performed.*/ - devp->state = FLASH_ERASE; - - /* Enabling write operation.*/ - jesd216_cmd(devp->config->busp, M25Q_CMD_WRITE_ENABLE); - - /* Sector erase command.*/ - jesd216_cmd_addr(devp->config->busp, M25Q_CMD_SECTOR_ERASE, offset); - - /* Bus released.*/ - jesd216_bus_release(devp->config->busp); - - return FLASH_NO_ERROR; -} - -static flash_error_t m25q_verify_erase(void *instance, - flash_sector_t sector) { - M25QDriver *devp = (M25QDriver *)instance; - uint8_t cmpbuf[M25Q_COMPARE_BUFFER_SIZE]; - flash_offset_t offset; - size_t n; - - osalDbgCheck(instance != NULL); - osalDbgCheck(sector < m25q_descriptor.sectors_count); - osalDbgAssert((devp->state == FLASH_READY) || (devp->state == FLASH_ERASE), - "invalid state"); - - if (devp->state == FLASH_ERASE) { - return FLASH_BUSY_ERASING; - } - - /* Bus acquired.*/ - jesd216_bus_acquire(devp->config->busp, devp->config->buscfg); - - /* FLASH_READY state while the operation is performed.*/ - devp->state = FLASH_READ; - - /* Read command.*/ - offset = (flash_offset_t)(sector * SECTOR_SIZE); - n = SECTOR_SIZE; - while (n > 0U) { - uint8_t *p; - -#if JESD216_BUS_MODE != JESD216_BUS_MODE_SPI - jesd216_cmd_addr_dummy_receive(devp->config->busp, M25Q_CMD_FAST_READ, - offset, M25Q_READ_DUMMY_CYCLES, - sizeof cmpbuf, cmpbuf); -#else - /* Normal read command in SPI mode.*/ - jesd216_cmd_addr_receive(devp->config->busp, M25Q_CMD_READ, - offset, sizeof cmpbuf, cmpbuf); -#endif - - /* Checking for erased state of current buffer.*/ - for (p = cmpbuf; p < &cmpbuf[M25Q_COMPARE_BUFFER_SIZE]; p++) { - if (*p != 0xFFU) { - /* Ready state again.*/ - devp->state = FLASH_READY; - - /* Bus released.*/ - jesd216_bus_release(devp->config->busp); - - return FLASH_ERROR_VERIFY; - } - } - - offset += sizeof cmpbuf; - n -= sizeof cmpbuf; - } - - /* Ready state again.*/ - devp->state = FLASH_READY; - - /* Bus released.*/ - jesd216_bus_release(devp->config->busp); - - return FLASH_NO_ERROR; -} - -static flash_error_t m25q_query_erase(void *instance, uint32_t *msec) { - M25QDriver *devp = (M25QDriver *)instance; - uint8_t sts; - - osalDbgCheck(instance != NULL); - osalDbgAssert((devp->state == FLASH_READY) || (devp->state == FLASH_ERASE), - "invalid state"); - - /* If there is an erase in progress then the device must be checked.*/ - if (devp->state == FLASH_ERASE) { - - /* Bus acquired.*/ - jesd216_bus_acquire(devp->config->busp, devp->config->buscfg); - - /* Read status command.*/ - jesd216_cmd_receive(devp->config->busp, M25Q_CMD_READ_FLAG_STATUS_REGISTER, - 1, &sts); - - /* If the P/E bit is zero (busy) or the flash in a suspended state then - report that the operation is still in progress.*/ - if (((sts & M25Q_FLAGS_PROGRAM_ERASE) == 0U) || - ((sts & M25Q_FLAGS_ERASE_SUSPEND) != 0U)) { - - /* Bus released.*/ - jesd216_bus_release(devp->config->busp); - - /* Recommended time before polling again, this is a simplified - implementation.*/ - if (msec != NULL) { - *msec = 1U; - } - - return FLASH_BUSY_ERASING; - } - - /* The device is ready to accept commands.*/ - devp->state = FLASH_READY; - - /* Checking for errors.*/ - if ((sts & M25Q_FLAGS_ALL_ERRORS) != 0U) { - - /* Clearing status register.*/ - jesd216_cmd(devp->config->busp, M25Q_CMD_CLEAR_FLAG_STATUS_REGISTER); - - /* Erase operation failed.*/ - return FLASH_ERROR_ERASE; - } - - /* Bus released.*/ - jesd216_bus_release(devp->config->busp); - } - - return FLASH_NO_ERROR; -} - -static flash_error_t m25q_read_sfdp(void *instance, flash_offset_t offset, - size_t n, uint8_t *rp) { - - (void)instance; - (void)rp; - (void)offset; - (void)n; - - return FLASH_NO_ERROR; -} - -/*===========================================================================*/ -/* Driver exported functions. */ -/*===========================================================================*/ - -/** - * @brief Initializes an instance. - * - * @param[out] devp pointer to the @p M25QDriver object - * - * @init - */ -void m25qObjectInit(M25QDriver *devp) { - - osalDbgCheck(devp != NULL); - - devp->vmt = &m25q_vmt; - devp->state = FLASH_STOP; - devp->config = NULL; -} - -/** - * @brief Configures and activates N25Q128 driver. - * - * @param[in] devp pointer to the @p M25QDriver object - * @param[in] config pointer to the configuration - * - * @api - */ -void m25qStart(M25QDriver *devp, const M25QConfig *config) { - - osalDbgCheck((devp != NULL) && (config != NULL)); - osalDbgAssert(devp->state != FLASH_UNINIT, "invalid state"); - - devp->config = config; - - if (devp->state == FLASH_STOP) { - - /* Bus acquisition.*/ - jesd216_bus_acquire(devp->config->busp, devp->config->buscfg); - -#if JESD216_BUS_MODE == JESD216_BUS_MODE_SPI - /* Reading device ID.*/ - jesd216_cmd_receive(devp->config->busp, M25Q_CMD_READ_ID, - sizeof devp->device_id, devp->device_id); - -#else /* JESD216_BUS_MODE != JESD216_BUS_MODE_SPI */ - /* Attempting a reset of the XIP mode, it could be in an unexpected state - because a CPU reset does not reset the memory too.*/ - m25q_reset_xip(devp); - - /* Attempting a eeset of the device, it could be in an unexpected state - because a CPU reset does not reset the memory too.*/ - m25q_reset_memory(devp); - - /* Reading device ID and unique ID.*/ - qspiReceive(devp->config->busp, &m25q_cmd_read_id, - sizeof devp->device_id, devp->device_id); -#endif /* JESD216_BUS_MODE != JESD216_BUS_MODE_SPI */ - - /* Checking if the device is white listed.*/ - osalDbgAssert(m25q_find_id(m25q_manufacturer_ids, - sizeof m25q_manufacturer_ids, - devp->device_id[0]), - "invalid manufacturer id"); - osalDbgAssert(m25q_find_id(m25q_memory_type_ids, - sizeof m25q_memory_type_ids, - devp->device_id[1]), - "invalid memory type id"); - -#if (JESD216_BUS_MODE != JESD216_BUS_MODE_SPI) && (M25Q_SWITCH_WIDTH == TRUE) - /* Setting up final bus width.*/ - qspiCommand(devp->config->busp, &m25q_cmd_write_enable); - qspiSend(devp->config->busp, &m25q_cmd_write_evconf, 1, m25q_evconf_value); - - { - uint8_t id[3]; - - /* Reading ID again for confirmation.*/ - jesd216_cmd_receive(devp->config->busp, 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 - - /* Setting up the device size.*/ - m25q_descriptor.sectors_count = (1U << (size_t)devp->device_id[2]) / - SECTOR_SIZE; - -#if (JESD216_BUS_MODE != JESD216_BUS_MODE_SPI) - { - static const uint8_t flash_conf[1] = { - (M25Q_READ_DUMMY_CYCLES << 4U) | 0x0FU - }; - - /* Setting up the dummy cycles to be used for fast read operations.*/ - jesd216_cmd(devp->config->busp, M25Q_CMD_WRITE_ENABLE); - jesd216_cmd_send(devp->config->busp, M25Q_CMD_WRITE_V_CONF_REGISTER, - 1, flash_conf); - } -#endif - - /* Driver in ready state.*/ - devp->state = FLASH_READY; - - /* Bus release.*/ - jesd216_bus_release(devp->config->busp); - } -} - -/** - * @brief Deactivates the N25Q128 driver. - * - * @param[in] devp pointer to the @p M25QDriver object - * - * @api - */ -void m25qStop(M25QDriver *devp) { - - osalDbgCheck(devp != NULL); - osalDbgAssert(devp->state != FLASH_UNINIT, "invalid state"); - - if (devp->state != FLASH_STOP) { - - /* Bus acquisition.*/ - jesd216_bus_acquire(devp->config->busp, devp->config->buscfg); - - /* Stopping bus device.*/ - jesd216_stop(devp->config->busp); - - /* Deleting current configuration.*/ - devp->config = NULL; - - /* Driver stopped.*/ - devp->state = FLASH_STOP; - - /* Bus release.*/ - jesd216_bus_release(devp->config->busp); - } -} - -#if (JESD216_BUS_MODE != JESD216_BUS_MODE_SPI) || defined(__DOXYGEN__) -#if (QSPI_SUPPORTS_MEMMAP == TRUE) || defined(__DOXYGEN__) -/** - * @brief Enters the memory Mapping mode. - * @details The memory mapping mode is only available when the QSPI mode - * is selected and the underlying QSPI controller supports the - * feature. - * - * @param[in] devp pointer to the @p M25QDriver object - * @param[out] addrp pointer to the memory start address of the mapped - * flash or @p NULL - * - * @api - */ -void m25qMemoryMap(M25QDriver *devp, uint8_t **addrp) { - static const uint8_t flash_status_xip[1] = { - (M25Q_READ_DUMMY_CYCLES << 4U) | 0x07U - }; - qspi_command_t cmd; - - /* Bus acquisition.*/ - jesd216_bus_acquire(devp->config->busp, devp->config->buscfg); - - /* Activating XIP mode in the device.*/ - jesd216_cmd(devp->config->busp, M25Q_CMD_WRITE_ENABLE); - jesd216_cmd_send(devp->config->busp, M25Q_CMD_WRITE_V_CONF_REGISTER, - 1, flash_status_xip); - - /* Putting the QSPI driver in memory mapped mode.*/ - cmd.cfg = QSPI_CFG_CMD(M25Q_CMD_FAST_READ) | - QSPI_CFG_ADDR_SIZE_24 | -#if JESD216_BUS_MODE == JESD216_BUS_MODE_QSPI1L - QSPI_CFG_CMD_MODE_ONE_LINE | - QSPI_CFG_ADDR_MODE_ONE_LINE | - QSPI_CFG_DATA_MODE_ONE_LINE | -#elif JESD216_BUS_MODE == JESD216_BUS_MODE_QSPI2L - QSPI_CFG_CMD_MODE_TWO_LINES | - QSPI_CFG_ADDR_MODE_TWO_LINES | - QSPI_CFG_DATA_MODE_TWO_LINES | -#else - QSPI_CFG_CMD_MODE_FOUR_LINES | - QSPI_CFG_ADDR_MODE_FOUR_LINES | - QSPI_CFG_DATA_MODE_FOUR_LINES | -#endif - QSPI_CFG_ALT_MODE_FOUR_LINES | /* Always 4 lines, note.*/ - QSPI_CFG_ALT_SIZE_8 | - QSPI_CFG_SIOO | - QSPI_CFG_DUMMY_CYCLES(M25Q_READ_DUMMY_CYCLES - 2); - - qspiMapFlash(devp->config->busp, &cmd, addrp); - - /* Bus release.*/ - jesd216_bus_release(devp->config->busp); -} - -/** - * @brief Leaves the memory Mapping mode. - * - * @param[in] devp pointer to the @p M25QDriver object - * - * @api - */ -void m25qMemoryUnmap(M25QDriver *devp) { - - /* Bus acquisition.*/ - jesd216_bus_acquire(devp->config->busp, devp->config->buscfg); - - qspiUnmapFlash(devp->config->busp); - - m25q_reset_xip(devp); - - /* Bus release.*/ - jesd216_bus_release(devp->config->busp); -} -#endif /* QSPI_SUPPORTS_MEMMAP == TRUE */ -#endif /* JESD216_BUS_MODE != JESD216_BUS_MODE_SPI */ - -/** @} */ +/*
+ ChibiOS - Copyright (C) 2016 Giovanni Di Sirio
+
+ This file is part of ChibiOS.
+
+ ChibiOS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ ChibiOS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/**
+ * @file m25q.c
+ * @brief Micron serial flash driver code.
+ *
+ * @addtogroup m25q
+ * @{
+ */
+
+#include <string.h>
+
+#include "hal.h"
+#include "m25q.h"
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+#define PAGE_SIZE 256U
+#define PAGE_MASK (PAGE_SIZE - 1U)
+
+#if M25Q_USE_SUB_SECTORS == TRUE
+#define SECTOR_SIZE 0x00001000U
+#define CMD_SECTOR_ERASE M25Q_CMD_SUBSECTOR_ERASE
+#else
+#define SECTOR_SIZE 0x00010000U
+#define CMD_SECTOR_ERASE M25Q_CMD_SECTOR_ERASE
+#endif
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+static const flash_descriptor_t *m25q_get_descriptor(void *instance);
+static flash_error_t m25q_read(void *instance, flash_offset_t offset,
+ size_t n, uint8_t *rp);
+static flash_error_t m25q_program(void *instance, flash_offset_t offset,
+ size_t n, const uint8_t *pp);
+static flash_error_t m25q_start_erase_all(void *instance);
+static flash_error_t m25q_start_erase_sector(void *instance,
+ flash_sector_t sector);
+static flash_error_t m25q_query_erase(void *instance, uint32_t *msec);
+static flash_error_t m25q_verify_erase(void *instance, flash_sector_t sector);
+static flash_error_t m25q_read_sfdp(void *instance, flash_offset_t offset,
+ size_t n, uint8_t *rp);
+
+/**
+ * @brief Virtual methods table.
+ */
+static const struct M25QDriverVMT m25q_vmt = {
+ m25q_get_descriptor, m25q_read, m25q_program,
+ m25q_start_erase_all, m25q_start_erase_sector,
+ m25q_query_erase, m25q_verify_erase,
+ m25q_read_sfdp
+};
+
+/**
+ * @brief N25Q128 descriptor.
+ */
+static flash_descriptor_t m25q_descriptor = {
+ .attributes = FLASH_ATTR_ERASED_IS_ONE | FLASH_ATTR_REWRITABLE |
+ FLASH_ATTR_SUSPEND_ERASE_CAPABLE,
+ .page_size = 256U,
+ .sectors_count = 0U, /* It is overwritten.*/
+ .sectors = NULL,
+ .sectors_size = SECTOR_SIZE,
+ .address = 0U
+};
+
+#if JESD216_BUS_MODE != JESD216_BUS_MODE_SPI
+/* Initial M25Q_CMD_READ_ID command.*/
+static const qspi_command_t m25q_cmd_read_id = {
+ .cfg = QSPI_CFG_CMD(M25Q_CMD_READ_ID) |
+#if M25Q_SWITCH_WIDTH == TRUE
+ QSPI_CFG_CMD_MODE_ONE_LINE |
+ QSPI_CFG_DATA_MODE_ONE_LINE,
+#else
+#if JESD216_BUS_MODE == JESD216_BUS_MODE_QSPI1L
+ QSPI_CFG_CMD_MODE_ONE_LINE |
+ QSPI_CFG_DATA_MODE_ONE_LINE,
+#elif JESD216_BUS_MODE == JESD216_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
+#endif
+ .addr = 0,
+ .alt = 0
+};
+
+/* Initial M25Q_CMD_WRITE_ENHANCED_V_CONF_REGISTER command.*/
+static const qspi_command_t m25q_cmd_write_evconf = {
+ .cfg = QSPI_CFG_CMD(M25Q_CMD_WRITE_ENHANCED_V_CONF_REGISTER) |
+#if M25Q_SWITCH_WIDTH == TRUE
+ QSPI_CFG_CMD_MODE_ONE_LINE |
+ QSPI_CFG_DATA_MODE_ONE_LINE,
+#else
+#if JESD216_BUS_MODE == JESD216_BUS_MODE_QSPI1L
+ QSPI_CFG_CMD_MODE_ONE_LINE |
+ QSPI_CFG_DATA_MODE_ONE_LINE,
+#elif JESD216_BUS_MODE == JESD216_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
+#endif
+ .addr = 0,
+ .alt = 0
+};
+
+/* Initial M25Q_CMD_WRITE_ENABLE command.*/
+static const qspi_command_t m25q_cmd_write_enable = {
+ .cfg = QSPI_CFG_CMD(M25Q_CMD_WRITE_ENABLE) |
+#if M25Q_SWITCH_WIDTH == TRUE
+ QSPI_CFG_CMD_MODE_ONE_LINE,
+#else
+#if JESD216_BUS_MODE == JESD216_BUS_MODE_QSPI1L
+ QSPI_CFG_CMD_MODE_ONE_LINE,
+#elif JESD216_BUS_MODE == JESD216_BUS_MODE_QSPI2L
+ QSPI_CFG_CMD_MODE_TWO_LINES,
+#else
+ QSPI_CFG_CMD_MODE_FOUR_LINES,
+#endif
+#endif
+ .addr = 0,
+ .alt = 0
+};
+
+/* Bus width initialization.*/
+#if JESD216_BUS_MODE == JESD216_BUS_MODE_QSPI1L
+static const uint8_t m25q_evconf_value[1] = {0xCF};
+#elif JESD216_BUS_MODE == JESD216_BUS_MODE_QSPI2L
+static const uint8_t m25q_evconf_value[1] = {0x8F};
+#else
+static const uint8_t m25q_evconf_value[1] = {0x4F};
+#endif
+#endif /* JESD216_BUS_MODE != JESD216_BUS_MODE_SPI */
+
+static const uint8_t m25q_manufacturer_ids[] = M25Q_SUPPORTED_MANUFACTURE_IDS;
+static const uint8_t m25q_memory_type_ids[] = M25Q_SUPPORTED_MEMORY_TYPE_IDS;
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+static bool m25q_find_id(const uint8_t *set, size_t size, uint8_t element) {
+ size_t i;
+
+ for (i = 0; i < size; i++) {
+ if (set[i] == element) {
+ return true;
+ }
+ }
+ return false;
+}
+
+#if (JESD216_BUS_MODE != JESD216_BUS_MODE_SPI) || defined(__DOXYGEN__)
+void m25q_reset_xip(M25QDriver *devp) {
+ static const uint8_t flash_conf[1] = {
+ (M25Q_READ_DUMMY_CYCLES << 4U) | 0x0FU
+ };
+ qspi_command_t cmd;
+ uint8_t buf[1];
+
+ /* Resetting XIP mode by reading one byte without XIP confirmation bit.*/
+ cmd.alt = 0xFF;
+ cmd.addr = 0;
+ cmd.cfg = QSPI_CFG_CMD_MODE_NONE |
+ QSPI_CFG_ADDR_SIZE_24 |
+#if JESD216_BUS_MODE == JESD216_BUS_MODE_QSPI1L
+ QSPI_CFG_ADDR_MODE_ONE_LINE |
+ QSPI_CFG_DATA_MODE_ONE_LINE |
+#elif JESD216_BUS_MODE == JESD216_BUS_MODE_QSPI2L
+ QSPI_CFG_ADDR_MODE_TWO_LINES |
+ QSPI_CFG_DATA_MODE_TWO_LINES |
+#else
+ QSPI_CFG_ADDR_MODE_FOUR_LINES |
+ QSPI_CFG_DATA_MODE_FOUR_LINES |
+#endif
+ QSPI_CFG_ALT_MODE_FOUR_LINES | /* Always 4 lines, note.*/
+ QSPI_CFG_ALT_SIZE_8 |
+ QSPI_CFG_DUMMY_CYCLES(M25Q_READ_DUMMY_CYCLES - 2);
+ qspiReceive(devp->config->busp, &cmd, 1, buf);
+
+ /* Enabling write operation.*/
+ jesd216_cmd(devp->config->busp, M25Q_CMD_WRITE_ENABLE);
+
+ /* Rewriting volatile configuration register.*/
+ jesd216_cmd_send(devp->config->busp, M25Q_CMD_WRITE_V_CONF_REGISTER,
+ 1, flash_conf);
+}
+
+void m25q_reset_memory(M25QDriver *devp) {
+
+ /* 1x M25Q_CMD_RESET_ENABLE command.*/
+ static const qspi_command_t cmd_reset_enable_1 = {
+ .cfg = QSPI_CFG_CMD(M25Q_CMD_RESET_ENABLE) |
+ QSPI_CFG_CMD_MODE_ONE_LINE,
+ .addr = 0,
+ .alt = 0
+ };
+
+ /* 1x M25Q_CMD_RESET_MEMORY command.*/
+ static const qspi_command_t cmd_reset_memory_1 = {
+ .cfg = QSPI_CFG_CMD(M25Q_CMD_RESET_MEMORY) |
+ QSPI_CFG_CMD_MODE_ONE_LINE,
+ .addr = 0,
+ .alt = 0
+ };
+
+ /* If the device is in one bit mode then the following commands are
+ rejected because shorter than 8 bits. If the device is in multiple
+ bits mode then the commands are accepted and the device is reset to
+ one bit mode.*/
+#if JESD216_BUS_MODE == JESD216_BUS_MODE_QSPI4L
+ /* 4x M25Q_CMD_RESET_ENABLE command.*/
+ static const qspi_command_t cmd_reset_enable_4 = {
+ .cfg = QSPI_CFG_CMD(M25Q_CMD_RESET_ENABLE) |
+ QSPI_CFG_CMD_MODE_FOUR_LINES,
+ .addr = 0,
+ .alt = 0
+ };
+
+ /* 4x M25Q_CMD_RESET_MEMORY command.*/
+ static const qspi_command_t cmd_reset_memory_4 = {
+ .cfg = QSPI_CFG_CMD(M25Q_CMD_RESET_MEMORY) |
+ QSPI_CFG_CMD_MODE_FOUR_LINES,
+ .addr = 0,
+ .alt = 0
+ };
+
+ qspiCommand(devp->config->busp, &cmd_reset_enable_4);
+ qspiCommand(devp->config->busp, &cmd_reset_memory_4);
+#else
+ /* 2x M25Q_CMD_RESET_ENABLE command.*/
+ static const qspi_command_t cmd_reset_enable_2 = {
+ .cfg = QSPI_CFG_CMD(M25Q_CMD_RESET_ENABLE) |
+ QSPI_CFG_CMD_MODE_TWO_LINES,
+ .addr = 0,
+ .alt = 0
+ };
+
+ /* 2x M25Q_CMD_RESET_MEMORY command.*/
+ static const qspi_command_t cmd_reset_memory_2 = {
+ .cfg = QSPI_CFG_CMD(M25Q_CMD_RESET_MEMORY) |
+ QSPI_CFG_CMD_MODE_TWO_LINES,
+ .addr = 0,
+ .alt = 0
+ };
+
+ qspiCommand(devp->config->busp, &cmd_reset_enable_2);
+ qspiCommand(devp->config->busp, &cmd_reset_memory_2);
+#endif
+
+ /* Now the device should be in one bit mode for sure and we perform a
+ device reset.*/
+ qspiCommand(devp->config->busp, &cmd_reset_enable_1);
+ qspiCommand(devp->config->busp, &cmd_reset_memory_1);
+}
+#endif /* JESD216_BUS_MODE != JESD216_BUS_MODE_SPI */
+
+static flash_error_t m25q_poll_status(M25QDriver *devp) {
+ uint8_t sts;
+
+ do {
+#if M25Q_NICE_WAITING == TRUE
+ osalThreadSleepMilliseconds(1);
+#endif
+ /* Read status command.*/
+ jesd216_cmd_receive(devp->config->busp, M25Q_CMD_READ_FLAG_STATUS_REGISTER,
+ 1, &sts);
+ } while ((sts & M25Q_FLAGS_PROGRAM_ERASE) == 0U);
+
+ /* Checking for errors.*/
+ if ((sts & M25Q_FLAGS_ALL_ERRORS) != 0U) {
+ /* Clearing status register.*/
+ jesd216_cmd(devp->config->busp, M25Q_CMD_CLEAR_FLAG_STATUS_REGISTER);
+
+ /* Program operation failed.*/
+ return FLASH_ERROR_PROGRAM;
+ }
+
+ return FLASH_NO_ERROR;
+}
+
+static const flash_descriptor_t *m25q_get_descriptor(void *instance) {
+ M25QDriver *devp = (M25QDriver *)instance;
+
+ osalDbgCheck(instance != NULL);
+ osalDbgAssert((devp->state != FLASH_UNINIT) && (devp->state != FLASH_STOP),
+ "invalid state");
+
+ return &m25q_descriptor;
+}
+
+static flash_error_t m25q_read(void *instance, flash_offset_t offset,
+ size_t n, uint8_t *rp) {
+ M25QDriver *devp = (M25QDriver *)instance;
+
+ osalDbgCheck((instance != NULL) && (rp != NULL) && (n > 0U));
+ osalDbgCheck((size_t)offset + n <= (size_t)m25q_descriptor.sectors_count *
+ (size_t)m25q_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.*/
+ jesd216_bus_acquire(devp->config->busp, devp->config->buscfg);
+
+ /* FLASH_READY state while the operation is performed.*/
+ devp->state = FLASH_READ;
+
+#if JESD216_BUS_MODE != JESD216_BUS_MODE_SPI
+ /* Fast read command in QSPI mode.*/
+ jesd216_cmd_addr_dummy_receive(devp->config->busp, M25Q_CMD_FAST_READ,
+ offset, M25Q_READ_DUMMY_CYCLES, n, rp);
+#else
+ /* Normal read command in SPI mode.*/
+ jesd216_cmd_addr_receive(devp->config->busp, M25Q_CMD_READ,
+ offset, n, rp);
+#endif
+
+ /* Ready state again.*/
+ devp->state = FLASH_READY;
+
+ /* Bus released.*/
+ jesd216_bus_release(devp->config->busp);
+
+ return FLASH_NO_ERROR;
+}
+
+static flash_error_t m25q_program(void *instance, flash_offset_t offset,
+ size_t n, const uint8_t *pp) {
+ M25QDriver *devp = (M25QDriver *)instance;
+
+ osalDbgCheck((instance != NULL) && (pp != NULL) && (n > 0U));
+ osalDbgCheck((size_t)offset + n <= (size_t)m25q_descriptor.sectors_count *
+ (size_t)m25q_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.*/
+ jesd216_bus_acquire(devp->config->busp, devp->config->buscfg);
+
+ /* FLASH_PGM state while the operation is performed.*/
+ devp->state = FLASH_PGM;
+
+ /* Data is programmed page by page.*/
+ while (n > 0U) {
+ flash_error_t err;
+
+ /* Data size that can be written in a single program page operation.*/
+ size_t chunk = (size_t)(((offset | PAGE_MASK) + 1U) - offset);
+ if (chunk > n) {
+ chunk = n;
+ }
+
+ /* Enabling write operation.*/
+ jesd216_cmd(devp->config->busp, M25Q_CMD_WRITE_ENABLE);
+
+ /* Page program command.*/
+ jesd216_cmd_addr_send(devp->config->busp, M25Q_CMD_PAGE_PROGRAM, offset,
+ chunk, pp);
+
+ /* Wait for status and check errors.*/
+ err = m25q_poll_status(devp);
+ if (err != FLASH_NO_ERROR) {
+
+ /* Bus released.*/
+ jesd216_bus_release(devp->config->busp);
+
+ return err;
+ }
+
+ /* Next page.*/
+ offset += chunk;
+ pp += chunk;
+ n -= chunk;
+ }
+
+ /* Ready state again.*/
+ devp->state = FLASH_READY;
+
+ /* Bus released.*/
+ jesd216_bus_release(devp->config->busp);
+
+ return FLASH_NO_ERROR;
+}
+
+static flash_error_t m25q_start_erase_all(void *instance) {
+ M25QDriver *devp = (M25QDriver *)instance;
+
+ osalDbgCheck(instance != NULL);
+ osalDbgAssert((devp->state == FLASH_READY) || (devp->state == FLASH_ERASE),
+ "invalid state");
+
+ if (devp->state == FLASH_ERASE) {
+ return FLASH_BUSY_ERASING;
+ }
+
+ /* Bus acquired.*/
+ jesd216_bus_acquire(devp->config->busp, devp->config->buscfg);
+
+ /* FLASH_ERASE state while the operation is performed.*/
+ devp->state = FLASH_ERASE;
+
+ /* Enabling write operation.*/
+ jesd216_cmd(devp->config->busp, M25Q_CMD_WRITE_ENABLE);
+
+ /* Bulk erase command.*/
+ jesd216_cmd(devp->config->busp, M25Q_CMD_BULK_ERASE);
+
+ /* Bus released.*/
+ jesd216_bus_release(devp->config->busp);
+
+ return FLASH_NO_ERROR;
+}
+
+static flash_error_t m25q_start_erase_sector(void *instance,
+ flash_sector_t sector) {
+ M25QDriver *devp = (M25QDriver *)instance;
+ flash_offset_t offset = (flash_offset_t)(sector * SECTOR_SIZE);
+
+ osalDbgCheck(instance != NULL);
+ osalDbgCheck(sector < m25q_descriptor.sectors_count);
+ osalDbgAssert((devp->state == FLASH_READY) || (devp->state == FLASH_ERASE),
+ "invalid state");
+
+ if (devp->state == FLASH_ERASE) {
+ return FLASH_BUSY_ERASING;
+ }
+
+ /* Bus acquired.*/
+ jesd216_bus_acquire(devp->config->busp, devp->config->buscfg);
+
+ /* FLASH_ERASE state while the operation is performed.*/
+ devp->state = FLASH_ERASE;
+
+ /* Enabling write operation.*/
+ jesd216_cmd(devp->config->busp, M25Q_CMD_WRITE_ENABLE);
+
+ /* Sector erase command.*/
+ jesd216_cmd_addr(devp->config->busp, M25Q_CMD_SECTOR_ERASE, offset);
+
+ /* Bus released.*/
+ jesd216_bus_release(devp->config->busp);
+
+ return FLASH_NO_ERROR;
+}
+
+static flash_error_t m25q_verify_erase(void *instance,
+ flash_sector_t sector) {
+ M25QDriver *devp = (M25QDriver *)instance;
+ uint8_t cmpbuf[M25Q_COMPARE_BUFFER_SIZE];
+ flash_offset_t offset;
+ size_t n;
+
+ osalDbgCheck(instance != NULL);
+ osalDbgCheck(sector < m25q_descriptor.sectors_count);
+ osalDbgAssert((devp->state == FLASH_READY) || (devp->state == FLASH_ERASE),
+ "invalid state");
+
+ if (devp->state == FLASH_ERASE) {
+ return FLASH_BUSY_ERASING;
+ }
+
+ /* Bus acquired.*/
+ jesd216_bus_acquire(devp->config->busp, devp->config->buscfg);
+
+ /* FLASH_READY state while the operation is performed.*/
+ devp->state = FLASH_READ;
+
+ /* Read command.*/
+ offset = (flash_offset_t)(sector * SECTOR_SIZE);
+ n = SECTOR_SIZE;
+ while (n > 0U) {
+ uint8_t *p;
+
+#if JESD216_BUS_MODE != JESD216_BUS_MODE_SPI
+ jesd216_cmd_addr_dummy_receive(devp->config->busp, M25Q_CMD_FAST_READ,
+ offset, M25Q_READ_DUMMY_CYCLES,
+ sizeof cmpbuf, cmpbuf);
+#else
+ /* Normal read command in SPI mode.*/
+ jesd216_cmd_addr_receive(devp->config->busp, M25Q_CMD_READ,
+ offset, sizeof cmpbuf, cmpbuf);
+#endif
+
+ /* Checking for erased state of current buffer.*/
+ for (p = cmpbuf; p < &cmpbuf[M25Q_COMPARE_BUFFER_SIZE]; p++) {
+ if (*p != 0xFFU) {
+ /* Ready state again.*/
+ devp->state = FLASH_READY;
+
+ /* Bus released.*/
+ jesd216_bus_release(devp->config->busp);
+
+ return FLASH_ERROR_VERIFY;
+ }
+ }
+
+ offset += sizeof cmpbuf;
+ n -= sizeof cmpbuf;
+ }
+
+ /* Ready state again.*/
+ devp->state = FLASH_READY;
+
+ /* Bus released.*/
+ jesd216_bus_release(devp->config->busp);
+
+ return FLASH_NO_ERROR;
+}
+
+static flash_error_t m25q_query_erase(void *instance, uint32_t *msec) {
+ M25QDriver *devp = (M25QDriver *)instance;
+ uint8_t sts;
+
+ osalDbgCheck(instance != NULL);
+ osalDbgAssert((devp->state == FLASH_READY) || (devp->state == FLASH_ERASE),
+ "invalid state");
+
+ /* If there is an erase in progress then the device must be checked.*/
+ if (devp->state == FLASH_ERASE) {
+
+ /* Bus acquired.*/
+ jesd216_bus_acquire(devp->config->busp, devp->config->buscfg);
+
+ /* Read status command.*/
+ jesd216_cmd_receive(devp->config->busp, M25Q_CMD_READ_FLAG_STATUS_REGISTER,
+ 1, &sts);
+
+ /* If the P/E bit is zero (busy) or the flash in a suspended state then
+ report that the operation is still in progress.*/
+ if (((sts & M25Q_FLAGS_PROGRAM_ERASE) == 0U) ||
+ ((sts & M25Q_FLAGS_ERASE_SUSPEND) != 0U)) {
+
+ /* Bus released.*/
+ jesd216_bus_release(devp->config->busp);
+
+ /* Recommended time before polling again, this is a simplified
+ implementation.*/
+ if (msec != NULL) {
+ *msec = 1U;
+ }
+
+ return FLASH_BUSY_ERASING;
+ }
+
+ /* The device is ready to accept commands.*/
+ devp->state = FLASH_READY;
+
+ /* Checking for errors.*/
+ if ((sts & M25Q_FLAGS_ALL_ERRORS) != 0U) {
+
+ /* Clearing status register.*/
+ jesd216_cmd(devp->config->busp, M25Q_CMD_CLEAR_FLAG_STATUS_REGISTER);
+
+ /* Erase operation failed.*/
+ return FLASH_ERROR_ERASE;
+ }
+
+ /* Bus released.*/
+ jesd216_bus_release(devp->config->busp);
+ }
+
+ return FLASH_NO_ERROR;
+}
+
+static flash_error_t m25q_read_sfdp(void *instance, flash_offset_t offset,
+ size_t n, uint8_t *rp) {
+
+ (void)instance;
+ (void)rp;
+ (void)offset;
+ (void)n;
+
+ return FLASH_NO_ERROR;
+}
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Initializes an instance.
+ *
+ * @param[out] devp pointer to the @p M25QDriver object
+ *
+ * @init
+ */
+void m25qObjectInit(M25QDriver *devp) {
+
+ osalDbgCheck(devp != NULL);
+
+ devp->vmt = &m25q_vmt;
+ devp->state = FLASH_STOP;
+ devp->config = NULL;
+}
+
+/**
+ * @brief Configures and activates N25Q128 driver.
+ *
+ * @param[in] devp pointer to the @p M25QDriver object
+ * @param[in] config pointer to the configuration
+ *
+ * @api
+ */
+void m25qStart(M25QDriver *devp, const M25QConfig *config) {
+
+ osalDbgCheck((devp != NULL) && (config != NULL));
+ osalDbgAssert(devp->state != FLASH_UNINIT, "invalid state");
+
+ devp->config = config;
+
+ if (devp->state == FLASH_STOP) {
+
+ /* Bus acquisition.*/
+ jesd216_bus_acquire(devp->config->busp, devp->config->buscfg);
+
+#if JESD216_BUS_MODE == JESD216_BUS_MODE_SPI
+ /* Reading device ID.*/
+ jesd216_cmd_receive(devp->config->busp, M25Q_CMD_READ_ID,
+ sizeof devp->device_id, devp->device_id);
+
+#else /* JESD216_BUS_MODE != JESD216_BUS_MODE_SPI */
+ /* Attempting a reset of the XIP mode, it could be in an unexpected state
+ because a CPU reset does not reset the memory too.*/
+ m25q_reset_xip(devp);
+
+ /* Attempting a eeset of the device, it could be in an unexpected state
+ because a CPU reset does not reset the memory too.*/
+ m25q_reset_memory(devp);
+
+ /* Reading device ID and unique ID.*/
+ qspiReceive(devp->config->busp, &m25q_cmd_read_id,
+ sizeof devp->device_id, devp->device_id);
+#endif /* JESD216_BUS_MODE != JESD216_BUS_MODE_SPI */
+
+ /* Checking if the device is white listed.*/
+ osalDbgAssert(m25q_find_id(m25q_manufacturer_ids,
+ sizeof m25q_manufacturer_ids,
+ devp->device_id[0]),
+ "invalid manufacturer id");
+ osalDbgAssert(m25q_find_id(m25q_memory_type_ids,
+ sizeof m25q_memory_type_ids,
+ devp->device_id[1]),
+ "invalid memory type id");
+
+#if (JESD216_BUS_MODE != JESD216_BUS_MODE_SPI) && (M25Q_SWITCH_WIDTH == TRUE)
+ /* Setting up final bus width.*/
+ qspiCommand(devp->config->busp, &m25q_cmd_write_enable);
+ qspiSend(devp->config->busp, &m25q_cmd_write_evconf, 1, m25q_evconf_value);
+
+ {
+ uint8_t id[3];
+
+ /* Reading ID again for confirmation.*/
+ jesd216_cmd_receive(devp->config->busp, 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
+
+ /* Setting up the device size.*/
+ m25q_descriptor.sectors_count = (1U << (size_t)devp->device_id[2]) /
+ SECTOR_SIZE;
+
+#if (JESD216_BUS_MODE != JESD216_BUS_MODE_SPI)
+ {
+ static const uint8_t flash_conf[1] = {
+ (M25Q_READ_DUMMY_CYCLES << 4U) | 0x0FU
+ };
+
+ /* Setting up the dummy cycles to be used for fast read operations.*/
+ jesd216_cmd(devp->config->busp, M25Q_CMD_WRITE_ENABLE);
+ jesd216_cmd_send(devp->config->busp, M25Q_CMD_WRITE_V_CONF_REGISTER,
+ 1, flash_conf);
+ }
+#endif
+
+ /* Driver in ready state.*/
+ devp->state = FLASH_READY;
+
+ /* Bus release.*/
+ jesd216_bus_release(devp->config->busp);
+ }
+}
+
+/**
+ * @brief Deactivates the N25Q128 driver.
+ *
+ * @param[in] devp pointer to the @p M25QDriver object
+ *
+ * @api
+ */
+void m25qStop(M25QDriver *devp) {
+
+ osalDbgCheck(devp != NULL);
+ osalDbgAssert(devp->state != FLASH_UNINIT, "invalid state");
+
+ if (devp->state != FLASH_STOP) {
+
+ /* Bus acquisition.*/
+ jesd216_bus_acquire(devp->config->busp, devp->config->buscfg);
+
+ /* Stopping bus device.*/
+ jesd216_stop(devp->config->busp);
+
+ /* Deleting current configuration.*/
+ devp->config = NULL;
+
+ /* Driver stopped.*/
+ devp->state = FLASH_STOP;
+
+ /* Bus release.*/
+ jesd216_bus_release(devp->config->busp);
+ }
+}
+
+#if (JESD216_BUS_MODE != JESD216_BUS_MODE_SPI) || defined(__DOXYGEN__)
+#if (QSPI_SUPPORTS_MEMMAP == TRUE) || defined(__DOXYGEN__)
+/**
+ * @brief Enters the memory Mapping mode.
+ * @details The memory mapping mode is only available when the QSPI mode
+ * is selected and the underlying QSPI controller supports the
+ * feature.
+ *
+ * @param[in] devp pointer to the @p M25QDriver object
+ * @param[out] addrp pointer to the memory start address of the mapped
+ * flash or @p NULL
+ *
+ * @api
+ */
+void m25qMemoryMap(M25QDriver *devp, uint8_t **addrp) {
+ static const uint8_t flash_status_xip[1] = {
+ (M25Q_READ_DUMMY_CYCLES << 4U) | 0x07U
+ };
+ qspi_command_t cmd;
+
+ /* Bus acquisition.*/
+ jesd216_bus_acquire(devp->config->busp, devp->config->buscfg);
+
+ /* Activating XIP mode in the device.*/
+ jesd216_cmd(devp->config->busp, M25Q_CMD_WRITE_ENABLE);
+ jesd216_cmd_send(devp->config->busp, M25Q_CMD_WRITE_V_CONF_REGISTER,
+ 1, flash_status_xip);
+
+ /* Putting the QSPI driver in memory mapped mode.*/
+ cmd.cfg = QSPI_CFG_CMD(M25Q_CMD_FAST_READ) |
+ QSPI_CFG_ADDR_SIZE_24 |
+#if JESD216_BUS_MODE == JESD216_BUS_MODE_QSPI1L
+ QSPI_CFG_CMD_MODE_ONE_LINE |
+ QSPI_CFG_ADDR_MODE_ONE_LINE |
+ QSPI_CFG_DATA_MODE_ONE_LINE |
+#elif JESD216_BUS_MODE == JESD216_BUS_MODE_QSPI2L
+ QSPI_CFG_CMD_MODE_TWO_LINES |
+ QSPI_CFG_ADDR_MODE_TWO_LINES |
+ QSPI_CFG_DATA_MODE_TWO_LINES |
+#else
+ QSPI_CFG_CMD_MODE_FOUR_LINES |
+ QSPI_CFG_ADDR_MODE_FOUR_LINES |
+ QSPI_CFG_DATA_MODE_FOUR_LINES |
+#endif
+ QSPI_CFG_ALT_MODE_FOUR_LINES | /* Always 4 lines, note.*/
+ QSPI_CFG_ALT_SIZE_8 |
+ QSPI_CFG_SIOO |
+ QSPI_CFG_DUMMY_CYCLES(M25Q_READ_DUMMY_CYCLES - 2);
+
+ qspiMapFlash(devp->config->busp, &cmd, addrp);
+
+ /* Bus release.*/
+ jesd216_bus_release(devp->config->busp);
+}
+
+/**
+ * @brief Leaves the memory Mapping mode.
+ *
+ * @param[in] devp pointer to the @p M25QDriver object
+ *
+ * @api
+ */
+void m25qMemoryUnmap(M25QDriver *devp) {
+
+ /* Bus acquisition.*/
+ jesd216_bus_acquire(devp->config->busp, devp->config->buscfg);
+
+ qspiUnmapFlash(devp->config->busp);
+
+ m25q_reset_xip(devp);
+
+ /* Bus release.*/
+ jesd216_bus_release(devp->config->busp);
+}
+#endif /* QSPI_SUPPORTS_MEMMAP == TRUE */
+#endif /* JESD216_BUS_MODE != JESD216_BUS_MODE_SPI */
+
+/** @} */
|