From e854f8bdbfe4b870c38744d35aa6d63f5cca6841 Mon Sep 17 00:00:00 2001 From: Giovanni Di Sirio Date: Thu, 8 Mar 2018 12:55:21 +0000 Subject: Experimental M25Q driver rework. git-svn-id: https://svn.code.sf.net/p/chibios/svn2/trunk@11633 110e8d01-0319-4d1e-a829-52ad28d1bb01 --- .../complex/m25q/devices/micron_n25q/m25q_flash.c | 547 +++++++++++++++++++++ .../complex/m25q/devices/micron_n25q/m25q_flash.h | 152 ++++++ .../m25q/devices/micron_n25q/micron_n25q.mk | 14 + os/hal/lib/complex/m25q/m25q.c | 447 +++++++++++++++++ os/hal/lib/complex/m25q/m25q.h | 185 +++++++ 5 files changed, 1345 insertions(+) create mode 100644 os/hal/lib/complex/m25q/devices/micron_n25q/m25q_flash.c create mode 100644 os/hal/lib/complex/m25q/devices/micron_n25q/m25q_flash.h create mode 100644 os/hal/lib/complex/m25q/devices/micron_n25q/micron_n25q.mk create mode 100644 os/hal/lib/complex/m25q/m25q.c create mode 100644 os/hal/lib/complex/m25q/m25q.h (limited to 'os') diff --git a/os/hal/lib/complex/m25q/devices/micron_n25q/m25q_flash.c b/os/hal/lib/complex/m25q/devices/micron_n25q/m25q_flash.c new file mode 100644 index 000000000..7aeae9065 --- /dev/null +++ b/os/hal/lib/complex/m25q/devices/micron_n25q/m25q_flash.c @@ -0,0 +1,547 @@ +/* + 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 . +*/ + +/** + * @file m25q_flash.c + * @brief Micron N25Q serial flash driver code. + * + * @addtogroup MICRON_N25Q + * @ingroup M25Q + * @{ + */ + +#include + +#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. */ +/*===========================================================================*/ + +/** + * @brief N25Q128 descriptor. + */ +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 +}; + +/*===========================================================================*/ +/* Driver local variables and types. */ +/*===========================================================================*/ + +#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 */ + +/*===========================================================================*/ +/* 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; +} + +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; +} + +#if (JESD216_BUS_MODE != JESD216_BUS_MODE_SPI) || defined(__DOXYGEN__) +static 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 /* #if 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 exported functions. */ +/*===========================================================================*/ + +void m25q_device_init(M25QDriver *devp) { + +#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 +} + +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; +} + +flash_error_t m25q_device_read(M25QDriver *devp, flash_offset_t offset, + size_t n, uint8_t *rp) { + +#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 + + return FLASH_NO_ERROR; +} + +flash_error_t m25q_device_program(M25QDriver *devp, flash_offset_t offset, + size_t n, const uint8_t *pp) { + + /* 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; + } + + return FLASH_NO_ERROR; +} + +flash_error_t m25q_device_start_erase_all(M25QDriver *devp) { + + /* Enabling write operation.*/ + jesd216_cmd(devp->config->busp, M25Q_CMD_WRITE_ENABLE); + + /* Bulk erase command.*/ + jesd216_cmd(devp->config->busp, M25Q_CMD_BULK_ERASE); + + return FLASH_NO_ERROR; +} + +flash_error_t m25q_device_start_erase_sector(M25QDriver *devp, + flash_sector_t sector) { + flash_offset_t offset = (flash_offset_t)(sector * SECTOR_SIZE); + + /* 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); + + return FLASH_NO_ERROR; +} + +flash_error_t m25q_device_verify_erase(M25QDriver *devp, + flash_sector_t sector) { + uint8_t cmpbuf[M25Q_COMPARE_BUFFER_SIZE]; + flash_offset_t offset; + size_t n; + + /* 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; + + return FLASH_ERROR_VERIFY; + } + } + + offset += sizeof cmpbuf; + n -= sizeof cmpbuf; + } + + return FLASH_NO_ERROR; +} + +flash_error_t m25q_device_query_erase(M25QDriver *devp, uint32_t *msec) { + uint8_t sts; + + /* 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)) { + + /* Recommended time before polling again, this is a simplified + implementation.*/ + if (msec != NULL) { + *msec = 1U; + } + + return FLASH_BUSY_ERASING; + } + + /* 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; + } + + return FLASH_NO_ERROR; +} + +flash_error_t m25q_device_read_sfdp(M25QDriver *devp, flash_offset_t offset, + size_t n, uint8_t *rp) { + + (void)devp; + (void)rp; + (void)offset; + (void)n; + + return FLASH_NO_ERROR; +} + +#if (JESD216_BUS_MODE != JESD216_BUS_MODE_SPI) || defined(__DOXYGEN__) +void m25q_activate_xip(M25QDriver *devp) { + static const uint8_t flash_status_xip[1] = { + (M25Q_READ_DUMMY_CYCLES << 4U) | 0x07U + }; + + /* 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); +} + +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); +} +#endif /* #if JESD216_BUS_MODE != JESD216_BUS_MODE_SPI */ + +/** @} */ diff --git a/os/hal/lib/complex/m25q/devices/micron_n25q/m25q_flash.h b/os/hal/lib/complex/m25q/devices/micron_n25q/m25q_flash.h new file mode 100644 index 000000000..5948f068b --- /dev/null +++ b/os/hal/lib/complex/m25q/devices/micron_n25q/m25q_flash.h @@ -0,0 +1,152 @@ +/* + 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 . +*/ + +/** + * @file n25q_flash.h + * @brief Micron N25Q serial flash driver header. + * + * @addtogroup MICRON_N25Q + * @ingroup M25Q + * @{ + */ + +#ifndef MICRON_N25Q_H +#define MICRON_N25Q_H + +#include "hal_jesd216_flash.h" +#include "m25q_flash.h" + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/** + * @name Device identification + * @{ + */ +#define M25Q_SUPPORTED_MANUFACTURE_IDS {0x20} +#define M25Q_SUPPORTED_MEMORY_TYPE_IDS {0xBA, 0xBB} +/** @} */ + +/** + * @name Command codes + * @{ + */ +#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 0x0B +#define M25Q_CMD_WRITE_ENABLE 0x06 +#define M25Q_CMD_WRITE_DISABLE 0x04 +#define M25Q_CMD_READ_STATUS_REGISTER 0x05 +#define M25Q_CMD_WRITE_STATUS_REGISTER 0x01 +#define M25Q_CMD_READ_LOCK_REGISTER 0xE8 +#define M25Q_CMD_WRITE_LOCK_REGISTER 0xE5 +#define M25Q_CMD_READ_FLAG_STATUS_REGISTER 0x70 +#define M25Q_CMD_CLEAR_FLAG_STATUS_REGISTER 0x50 +#define M25Q_CMD_READ_NV_CONFIGURATION_REGISTER 0xB5 +#define M25Q_CMD_WRITE_NV_CONFIGURATION_REGISTER 0xB1 +#define M25Q_CMD_READ_V_CONF_REGISTER 0x85 +#define M25Q_CMD_WRITE_V_CONF_REGISTER 0x81 +#define M25Q_CMD_READ_ENHANCED_V_CONF_REGISTER 0x65 +#define M25Q_CMD_WRITE_ENHANCED_V_CONF_REGISTER 0x61 +#define M25Q_CMD_PAGE_PROGRAM 0x02 +#define M25Q_CMD_SUBSECTOR_ERASE 0x20 +#define M25Q_CMD_SECTOR_ERASE 0xD8 +#define M25Q_CMD_BULK_ERASE 0xC7 +#define M25Q_CMD_PROGRAM_ERASE_RESUME 0x7A +#define M25Q_CMD_PROGRAM_ERASE_SUSPEND 0x75 +#define M25Q_CMD_READ_OTP_ARRAY 0x4B +#define M25Q_CMD_PROGRAM_OTP_ARRAY 0x42 +/** @} */ + +/** + * @name Flags status register bits + * @{ + */ +#define M25Q_FLAGS_PROGRAM_ERASE 0x80U +#define M25Q_FLAGS_ERASE_SUSPEND 0x40U +#define M25Q_FLAGS_ERASE_ERROR 0x20U +#define M25Q_FLAGS_PROGRAM_ERROR 0x10U +#define M25Q_FLAGS_VPP_ERROR 0x08U +#define M25Q_FLAGS_PROGRAM_SUSPEND 0x04U +#define M25Q_FLAGS_PROTECTION_ERROR 0x02U +#define M25Q_FLAGS_RESERVED 0x01U +#define M25Q_FLAGS_ALL_ERRORS (M25Q_FLAGS_ERASE_ERROR | \ + M25Q_FLAGS_PROGRAM_ERROR | \ + M25Q_FLAGS_VPP_ERROR | \ + M25Q_FLAGS_PROTECTION_ERROR) +/** @} */ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#if !defined(__DOXYGEN__) +extern flash_descriptor_t m25q_descriptor; +#endif + +#ifdef __cplusplus +extern "C" { +#endif +void m25q_device_init(M25QDriver *devp); +const flash_descriptor_t *m25q_get_descriptor(void *instance); +flash_error_t m25q_device_read(M25QDriver *devp, flash_offset_t offset, + size_t n, uint8_t *rp); +flash_error_t m25q_device_program(M25QDriver *devp, flash_offset_t offset, + size_t n, const uint8_t *pp); +flash_error_t m25q_device_start_erase_all(M25QDriver *devp); +flash_error_t m25q_device_start_erase_sector(M25QDriver *devp, + flash_sector_t sector); +flash_error_t m25q_device_verify_erase(M25QDriver *devp, + flash_sector_t sector); +flash_error_t m25q_device_query_erase(M25QDriver *devp, uint32_t *msec); +flash_error_t m25q_device_read_sfdp(M25QDriver *devp, flash_offset_t offset, + size_t n, uint8_t *rp); +#if JESD216_BUS_MODE != JESD216_BUS_MODE_SPI + void m25q_activate_xip(M25QDriver *devp); + void m25q_reset_xip(M25QDriver *devp); +#endif +#ifdef __cplusplus +} +#endif + +#endif /* MICRON_N25Q_H */ + +/** @} */ + diff --git a/os/hal/lib/complex/m25q/devices/micron_n25q/micron_n25q.mk b/os/hal/lib/complex/m25q/devices/micron_n25q/micron_n25q.mk new file mode 100644 index 000000000..5a5e0ea49 --- /dev/null +++ b/os/hal/lib/complex/m25q/devices/micron_n25q/micron_n25q.mk @@ -0,0 +1,14 @@ +# List of all the Micron N25Q device files. +M25QSRC := $(CHIBIOS)/os/hal/lib/peripherals/flash/hal_flash.c \ + $(CHIBIOS)/os/hal/lib/peripherals/flash/hal_jesd216_flash.c \ + $(CHIBIOS)/os/hal/lib/complex/m25q/m25q.c \ + $(CHIBIOS)/os/hal/lib/complex/m25q/devices/micron_n25q/m25q_flash.c + +# Required include directories +M25QINC := $(CHIBIOS)/os/hal/lib/peripherals/flash \ + $(CHIBIOS)/os/hal/lib/complex/m25q \ + $(CHIBIOS)/os/hal/lib/complex/m25q/devices/micron_n25q + +# Shared variables +ALLCSRC += $(M25QSRC) +ALLINC += $(M25QINC) 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 . +*/ + +/** + * @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 */ + +/** @} */ diff --git a/os/hal/lib/complex/m25q/m25q.h b/os/hal/lib/complex/m25q/m25q.h new file mode 100644 index 000000000..5ddf9d532 --- /dev/null +++ b/os/hal/lib/complex/m25q/m25q.h @@ -0,0 +1,185 @@ +/* + 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 . +*/ + +/** + * @file m25q.h + * @brief M25Q serial flash driver header. + * + * @addtogroup M25Q + * @ingroup M25Q + * @{ + */ + +#ifndef M25Q_H +#define M25Q_H + +#include "hal_jesd216_flash.h" + +/*===========================================================================*/ +/* Driver constants. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* Driver pre-compile time settings. */ +/*===========================================================================*/ + +/** + * @name Configuration options + * @{ + */ +/** + * @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 + * Enhanced Volatile Configuration Register. If the flash + * device is configured using the Non Volatile Configuration + * Register then this option is not required. + * @note This option is only valid in QSPI bus modes. + */ +#if !defined(M25Q_SWITCH_WIDTH) || defined(__DOXYGEN__) +#define M25Q_SWITCH_WIDTH TRUE +#endif + +/** + * @brief Delays insertions. + * @details If enabled this options inserts delays into the flash waiting + * routines releasing some extra CPU time for threads with lower + * priority, this may slow down the driver a bit however. + */ +#if !defined(M25Q_NICE_WAITING) || defined(__DOXYGEN__) +#define M25Q_NICE_WAITING TRUE +#endif + +/** + * @brief Uses 4kB sub-sectors rather than 64kB sectors. + */ +#if !defined(M25Q_USE_SUB_SECTORS) || defined(__DOXYGEN__) +#define M25Q_USE_SUB_SECTORS FALSE +#endif + +/** + * @brief Size of the compare buffer. + * @details This buffer is allocated in the stack frame of the function + * @p flashVerifyErase() and its size must be a power of two. + * Larger buffers lead to better verify performance but increase + * stack usage for that function. + */ +#if !defined(M25Q_COMPARE_BUFFER_SIZE) || defined(__DOXYGEN__) +#define M25Q_COMPARE_BUFFER_SIZE 32 +#endif +/** @} */ + +/*===========================================================================*/ +/* Derived constants and error checks. */ +/*===========================================================================*/ + +#if (M25Q_READ_DUMMY_CYCLES < 1) || (M25Q_READ_DUMMY_CYCLES > 15) +#error "invalid M25Q_READ_DUMMY_CYCLES value (1..15)" +#endif + +#if (M25Q_COMPARE_BUFFER_SIZE & (M25Q_COMPARE_BUFFER_SIZE - 1)) != 0 +#error "invalid M25Q_COMPARE_BUFFER_SIZE value" +#endif + +/*===========================================================================*/ +/* Driver data structures and types. */ +/*===========================================================================*/ + +/** + * @brief Type of a M25Q configuration structure. + */ +typedef struct { + _jesd216_config +} M25QConfig; + +/** + * @brief @p M25Q specific methods. + */ +#define _m25q_methods \ + _jesd216_flash_methods + +/** + * @extends JESD216FlashVMT + * + * @brief @p M25Q virtual methods table. + */ +struct M25QDriverVMT { + _m25q_methods +}; + +/** + * @extends JESD216Flash + * + * @brief Type of M25Q flash class. + */ +typedef struct { + /** + * @brief M25QDriver Virtual Methods Table. + */ + const struct M25QDriverVMT *vmt; + _jesd216_flash_data + /** + * @brief Current configuration data. + */ + const M25QConfig *config; + /** + * @brief Device ID and unique ID. + */ + uint8_t device_id[20]; +} M25QDriver; + +/*===========================================================================*/ +/* Driver macros. */ +/*===========================================================================*/ + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + void m25qObjectInit(M25QDriver *devp); + void m25qStart(M25QDriver *devp, const M25QConfig *config); + void m25qStop(M25QDriver *devp); +#if (JESD216_BUS_MODE != JESD216_BUS_MODE_SPI) || defined(__DOXYGEN__) +#if (QSPI_SUPPORTS_MEMMAP == TRUE) || defined(__DOXYGEN__) + void m25qMemoryMap(M25QDriver *devp, uint8_t ** addrp); + void m25qMemoryUnmap(M25QDriver *devp); +#endif /* QSPI_SUPPORTS_MEMMAP == TRUE */ +#endif /* JESD216_BUS_MODE != JESD216_BUS_MODE_SPI */ +#ifdef __cplusplus +} +#endif + +/* Device-specific implementations.*/ +#include "m25q_flash.h" + +#endif /* M25Q_H */ + +/** @} */ + -- cgit v1.2.3