/*
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 .
*/
/**
* @file m25q.c
* @brief Micron serial flash driver code.
*
* @addtogroup 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. */
/*===========================================================================*/
/*===========================================================================*/
/* 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 */
/** @} */