diff options
Diffstat (limited to 'os/hal/lib/complex/m25q/m25q.c')
-rw-r--r-- | os/hal/lib/complex/m25q/m25q.c | 447 |
1 files changed, 447 insertions, 0 deletions
diff --git a/os/hal/lib/complex/m25q/m25q.c b/os/hal/lib/complex/m25q/m25q.c new file mode 100644 index 000000000..a2e75f3fd --- /dev/null +++ b/os/hal/lib/complex/m25q/m25q.c @@ -0,0 +1,447 @@ +/*
+ ChibiOS - Copyright (C) 2006..2018 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 M25Q serial flash driver code.
+ *
+ * @addtogroup M25Q
+ * @ingroup M25Q
+ * @{
+ */
+
+#include "hal.h"
+#include "m25q.h"
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+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_verify_erase(void *instance,
+ flash_sector_t sector);
+static flash_error_t m25q_query_erase(void *instance, uint32_t *msec);
+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 = {
+ (size_t)0,
+ m25q_get_descriptor, m25q_read, m25q_program,
+ m25q_start_erase_all, m25q_start_erase_sector,
+ m25q_query_erase, m25q_verify_erase,
+ m25q_read_sfdp
+};
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+static flash_error_t m25q_read(void *instance, flash_offset_t offset,
+ size_t n, uint8_t *rp) {
+ M25QDriver *devp = (M25QDriver *)instance;
+ flash_error_t err;
+
+ 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;
+
+ /* Actual read implementation.*/
+ err = m25q_device_read(devp, offset, n, rp);
+
+ /* Ready state again.*/
+ devp->state = FLASH_READY;
+
+ /* Bus released.*/
+ jesd216_bus_release(devp->config->busp);
+
+ return err;
+}
+
+static flash_error_t m25q_program(void *instance, flash_offset_t offset,
+ size_t n, const uint8_t *pp) {
+ M25QDriver *devp = (M25QDriver *)instance;
+ flash_error_t err;
+
+ 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;
+
+ /* Actual program implementation.*/
+ err = m25q_device_program(devp, offset, n, pp);
+
+ /* Ready state again.*/
+ devp->state = FLASH_READY;
+
+ /* Bus released.*/
+ jesd216_bus_release(devp->config->busp);
+
+ return err;
+}
+
+static flash_error_t m25q_start_erase_all(void *instance) {
+ M25QDriver *devp = (M25QDriver *)instance;
+ flash_error_t err;
+
+ 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;
+
+ /* Actual erase implementation.*/
+ err = m25q_device_start_erase_all(devp);
+
+ /* Ready state again.*/
+ devp->state = FLASH_READY;
+
+ /* Bus released.*/
+ jesd216_bus_release(devp->config->busp);
+
+ return err;
+}
+
+static flash_error_t m25q_start_erase_sector(void *instance,
+ flash_sector_t sector) {
+ M25QDriver *devp = (M25QDriver *)instance;
+ flash_error_t err;
+
+ 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;
+
+ /* Actual erase implementation.*/
+ err = m25q_device_start_erase_sector(devp, sector);
+
+ /* Bus released.*/
+ jesd216_bus_release(devp->config->busp);
+
+ return err;
+}
+
+static flash_error_t m25q_verify_erase(void *instance,
+ flash_sector_t sector) {
+ M25QDriver *devp = (M25QDriver *)instance;
+ flash_error_t err;
+
+ 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;
+
+ /* Actual verify erase implementation.*/
+ err = m25q_device_verify_erase(devp, sector);
+
+ /* Ready state again.*/
+ devp->state = FLASH_READY;
+
+ /* Bus released.*/
+ jesd216_bus_release(devp->config->busp);
+
+ return err;
+}
+
+static flash_error_t m25q_query_erase(void *instance, uint32_t *msec) {
+ M25QDriver *devp = (M25QDriver *)instance;
+ flash_error_t err;
+
+ 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);
+
+ /* Actual query erase implementation.*/
+ err = m25q_device_query_erase(devp, msec);
+
+ /* The device is ready to accept commands.*/
+ if (err == FLASH_NO_ERROR) {
+ devp->state = FLASH_READY;
+ }
+
+ /* Bus released.*/
+ jesd216_bus_release(devp->config->busp);
+ }
+ else {
+ err = FLASH_NO_ERROR;
+ }
+
+ return err;
+}
+
+static flash_error_t m25q_read_sfdp(void *instance, flash_offset_t offset,
+ size_t n, uint8_t *rp) {
+ M25QDriver *devp = (M25QDriver *)instance;
+ flash_error_t err;
+
+ osalDbgCheck((instance != NULL) && (rp != NULL) && (n > 0U));
+ 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);
+
+ /* Actual read SFDP implementation.*/
+ err = m25q_device_read_sfdp(devp, offset, n, rp);
+
+ /* The device is ready to accept commands.*/
+ if (err == FLASH_NO_ERROR) {
+ devp->state = FLASH_READY;
+ }
+
+ /* Bus released.*/
+ jesd216_bus_release(devp->config->busp);
+
+ return err;
+}
+
+/*===========================================================================*/
+/* 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);
+
+ /* Device identification and initialization.*/
+ m25q_device_init(devp);
+
+ /* 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) {
+ qspi_command_t cmd;
+
+ /* Bus acquisition.*/
+ jesd216_bus_acquire(devp->config->busp, devp->config->buscfg);
+
+ /* Activating XIP mode in the device.*/
+ m25q_activate_xip(devp);
+
+ /* 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);
+
+ /* Starting QSPI memory mapped mode.*/
+ 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);
+
+ /* Stopping QSPI memory mapped mode.*/
+ 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 */
+
+/** @} */
|