drivers: flash: add Winbond W25Q SPI flash driver
This commit is contained in:
parent
a7068ba506
commit
42e271ed9b
|
@ -0,0 +1,520 @@
|
|||
/*
|
||||
ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file hal_flash_device.c
|
||||
* @brief Micron W25Q serial flash driver code.
|
||||
*
|
||||
* @addtogroup WINBOND_W25Q
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "hal.h"
|
||||
#include "hal_serial_nor.h"
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local definitions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#define PAGE_SIZE 256U
|
||||
#define PAGE_MASK (PAGE_SIZE - 1U)
|
||||
|
||||
#if W25Q_USE_SUB_SECTORS == TRUE
|
||||
/* 4 KB */
|
||||
#define SECTOR_SIZE 0x00001000U
|
||||
#define CMD_SECTOR_ERASE W25Q_CMD_SECTOR_ERASE
|
||||
#else
|
||||
/* 64 KB */
|
||||
#define SECTOR_SIZE 0x00010000U
|
||||
#define CMD_SECTOR_ERASE W25Q_CMD_64K_BLOCK_ERASE
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported variables. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief W25Q128 descriptor.
|
||||
*/
|
||||
flash_descriptor_t snor_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,
|
||||
.size = 0U /* It is overwritten.*/
|
||||
|
||||
};
|
||||
|
||||
#if (SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI) || defined(__DOXYGEN__)
|
||||
#if (WSPI_SUPPORTS_MEMMAP == TRUE) || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief Fast read command for memory mapped mode.
|
||||
*/
|
||||
const wspi_command_t snor_memmap_read = {
|
||||
.cmd = W25Q_CMD_FAST_READ,
|
||||
.addr = 0,
|
||||
.dummy = W25Q_READ_DUMMY_CYCLES,
|
||||
.cfg = WSPI_CFG_ADDR_SIZE_24 |
|
||||
#if W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI1L
|
||||
WSPI_CFG_CMD_MODE_ONE_LINE |
|
||||
WSPI_CFG_ADDR_MODE_ONE_LINE |
|
||||
WSPI_CFG_DATA_MODE_ONE_LINE |
|
||||
#elif W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI2L
|
||||
WSPI_CFG_CMD_MODE_TWO_LINES |
|
||||
WSPI_CFG_ADDR_MODE_TWO_LINES |
|
||||
WSPI_CFG_DATA_MODE_TWO_LINES |
|
||||
#else
|
||||
WSPI_CFG_CMD_MODE_FOUR_LINES |
|
||||
WSPI_CFG_ADDR_MODE_FOUR_LINES |
|
||||
WSPI_CFG_DATA_MODE_FOUR_LINES |
|
||||
#endif
|
||||
WSPI_CFG_ALT_MODE_FOUR_LINES | /* Always 4 lines, note.*/
|
||||
WSPI_CFG_ALT_SIZE_8 |
|
||||
WSPI_CFG_SIOO
|
||||
};
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local variables and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI
|
||||
/* Initial W25Q_CMD_READ_ID command.*/
|
||||
static const wspi_command_t w25q_cmd_read_id = {
|
||||
.cmd = W25Q_CMD_READ_JEDEC_ID,
|
||||
.cfg = 0U |
|
||||
#if W25Q_SWITCH_WIDTH == TRUE
|
||||
WSPI_CFG_CMD_MODE_ONE_LINE |
|
||||
WSPI_CFG_DATA_MODE_ONE_LINE,
|
||||
#else
|
||||
#if W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI1L
|
||||
WSPI_CFG_CMD_MODE_ONE_LINE |
|
||||
WSPI_CFG_DATA_MODE_ONE_LINE,
|
||||
#elif W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI2L
|
||||
WSPI_CFG_CMD_MODE_TWO_LINES |
|
||||
WSPI_CFG_DATA_MODE_TWO_LINES,
|
||||
#elif W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI4L
|
||||
WSPI_CFG_CMD_MODE_FOUR_LINES |
|
||||
WSPI_CFG_DATA_MODE_FOUR_LINES,
|
||||
#else
|
||||
WSPI_CFG_CMD_MODE_EIGHT_LINES |
|
||||
WSPI_CFG_DATA_MODE_EIGHT_LINES,
|
||||
#endif
|
||||
#endif
|
||||
.addr = 0,
|
||||
.alt = 0,
|
||||
.dummy = 0
|
||||
};
|
||||
|
||||
/* Initial W25Q_CMD_WRITE_ENHANCED_V_CONF_REGISTER command.*/
|
||||
static const wspi_command_t w25q_cmd_write_evconf = {
|
||||
.cmd = W25Q_CMD_WRITE_ENHANCED_V_CONF_REGISTER,
|
||||
.cfg = 0U |
|
||||
#if W25Q_SWITCH_WIDTH == TRUE
|
||||
WSPI_CFG_CMD_MODE_ONE_LINE |
|
||||
WSPI_CFG_DATA_MODE_ONE_LINE,
|
||||
#else
|
||||
#if W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI1L
|
||||
WSPI_CFG_CMD_MODE_ONE_LINE |
|
||||
WSPI_CFG_DATA_MODE_ONE_LINE,
|
||||
#elif W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI2L
|
||||
WSPI_CFG_CMD_MODE_TWO_LINES |
|
||||
WSPI_CFG_DATA_MODE_TWO_LINES,
|
||||
#elif W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI4L
|
||||
WSPI_CFG_CMD_MODE_FOUR_LINES |
|
||||
WSPI_CFG_DATA_MODE_FOUR_LINES,
|
||||
#else
|
||||
WSPI_CFG_CMD_MODE_EIGHT_LINES |
|
||||
WSPI_CFG_DATA_MODE_EIGHT_LINES,
|
||||
#endif
|
||||
#endif
|
||||
.addr = 0,
|
||||
.alt = 0,
|
||||
.dummy = 0
|
||||
};
|
||||
|
||||
/* Initial W25Q_CMD_WRITE_ENABLE command.*/
|
||||
static const wspi_command_t w25q_cmd_write_enable = {
|
||||
.cmd = W25Q_CMD_WRITE_ENABLE,
|
||||
.cfg = 0U |
|
||||
#if W25Q_SWITCH_WIDTH == TRUE
|
||||
WSPI_CFG_CMD_MODE_ONE_LINE,
|
||||
#else
|
||||
#if W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI1L
|
||||
WSPI_CFG_CMD_MODE_ONE_LINE,
|
||||
#elif W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI2L
|
||||
WSPI_CFG_CMD_MODE_TWO_LINES,
|
||||
#elif W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI4L
|
||||
WSPI_CFG_CMD_MODE_FOUR_LINES,
|
||||
#else
|
||||
WSPI_CFG_CMD_MODE_EIGHT_LINES,
|
||||
#endif
|
||||
#endif
|
||||
.addr = 0,
|
||||
.alt = 0,
|
||||
.dummy = 0
|
||||
};
|
||||
|
||||
#endif /* SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver local functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
static bool w25q_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 w25q_poll_status(SNORDriver *devp) {
|
||||
uint8_t sts;
|
||||
|
||||
do {
|
||||
#if W25Q_NICE_WAITING == TRUE
|
||||
osalThreadSleepMilliseconds(1);
|
||||
#endif
|
||||
/* Read status command.*/
|
||||
bus_cmd_receive(devp->config->busp, W25Q_CMD_READ_STATUS_REGISTER,
|
||||
1, &sts);
|
||||
} while ((sts & W25Q_FLAGS_BUSY) != 0U);
|
||||
|
||||
/* Checking for errors.*/
|
||||
/* NOP */
|
||||
|
||||
return FLASH_NO_ERROR;
|
||||
}
|
||||
|
||||
#if (SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI) || defined(__DOXYGEN__)
|
||||
static void w25q_reset_memory(SNORDriver *devp) {
|
||||
|
||||
/* 1x W25Q_CMD_RESET_ENABLE command.*/
|
||||
static const wspi_command_t cmd_reset_enable_1 = {
|
||||
.cmd = W25Q_CMD_RESET_ENABLE,
|
||||
.cfg = WSPI_CFG_CMD_MODE_ONE_LINE,
|
||||
.addr = 0,
|
||||
.alt = 0,
|
||||
.dummy = 0
|
||||
};
|
||||
|
||||
/* 1x W25Q_CMD_RESET_MEMORY command.*/
|
||||
static const wspi_command_t cmd_reset_1 = {
|
||||
.cmd = W25Q_CMD_RESET,
|
||||
.cfg = WSPI_CFG_CMD_MODE_ONE_LINE,
|
||||
.addr = 0,
|
||||
.alt = 0,
|
||||
.dummy = 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 W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI4L
|
||||
/* 4x W25Q_CMD_RESET_ENABLE command.*/
|
||||
static const wspi_command_t cmd_reset_enable_4 = {
|
||||
.cmd = W25Q_CMD_RESET_ENABLE,
|
||||
.cfg = WSPI_CFG_CMD_MODE_FOUR_LINES,
|
||||
.addr = 0,
|
||||
.alt = 0,
|
||||
.dummy = 0
|
||||
};
|
||||
|
||||
/* 4x W25Q_CMD_RESET_MEMORY command.*/
|
||||
static const wspi_command_t cmd_reset_4 = {
|
||||
.cmd = W25Q_CMD_RESET,
|
||||
.cfg = WSPI_CFG_CMD_MODE_FOUR_LINES,
|
||||
.addr = 0,
|
||||
.alt = 0,
|
||||
.dummy = 0
|
||||
};
|
||||
|
||||
wspiCommand(devp->config->busp, &cmd_reset_enable_4);
|
||||
wspiCommand(devp->config->busp, &cmd_reset_memory_4);
|
||||
#else
|
||||
/* 2x W25Q_CMD_RESET_ENABLE command.*/
|
||||
static const wspi_command_t cmd_reset_enable_2 = {
|
||||
.cmd = W25Q_CMD_RESET_ENABLE,
|
||||
.cfg = WSPI_CFG_CMD_MODE_TWO_LINES,
|
||||
.addr = 0,
|
||||
.alt = 0,
|
||||
.dummy = 0
|
||||
};
|
||||
|
||||
/* 2x W25Q_CMD_RESET_MEMORY command.*/
|
||||
static const wspi_command_t cmd_reset_memory_2 = {
|
||||
.cmd = W25Q_CMD_RESET_MEMORY,
|
||||
.cfg = WSPI_CFG_CMD_MODE_TWO_LINES,
|
||||
.addr = 0,
|
||||
.alt = 0,
|
||||
.dummy = 0
|
||||
};
|
||||
|
||||
wspiCommand(devp->config->busp, &cmd_reset_enable_2);
|
||||
wspiCommand(devp->config->busp, &cmd_reset_2);
|
||||
#endif
|
||||
|
||||
/* Now the device should be in one bit mode for sure and we perform a
|
||||
device reset.*/
|
||||
wspiCommand(devp->config->busp, &cmd_reset_enable_1);
|
||||
wspiCommand(devp->config->busp, &cmd_reset_1);
|
||||
}
|
||||
#endif /* SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI */
|
||||
|
||||
static const uint8_t w25q_manufacturer_ids[] = W25Q_SUPPORTED_MANUFACTURE_IDS;
|
||||
static const uint8_t w25q_memory_type_ids[] = W25Q_SUPPORTED_MEMORY_TYPE_IDS;
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver exported functions. */
|
||||
/*===========================================================================*/
|
||||
|
||||
void snor_device_init(SNORDriver *devp) {
|
||||
|
||||
#if SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_SPI
|
||||
/* Reading device ID.*/
|
||||
bus_cmd_receive(devp->config->busp, W25Q_CMD_READ_JEDEC_ID,
|
||||
sizeof devp->device_id, devp->device_id);
|
||||
|
||||
#else /* SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI */
|
||||
/* Attempting a reset of the XIP mode, it could be in an unexpected state
|
||||
because a CPU reset does not reset the memory too.*/
|
||||
snor_reset_xip(devp);
|
||||
|
||||
/* Attempting a reset of the device, it could be in an unexpected state
|
||||
because a CPU reset does not reset the memory too.*/
|
||||
w25q_reset_memory(devp);
|
||||
|
||||
/* Reading device ID and unique ID.*/
|
||||
wspiReceive(devp->config->busp, &w25q_cmd_read_id,
|
||||
sizeof devp->device_id, devp->device_id);
|
||||
#endif /* SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI */
|
||||
|
||||
/* Checking if the device is white listed.*/
|
||||
osalDbgAssert(w25q_find_id(w25q_manufacturer_ids,
|
||||
sizeof w25q_manufacturer_ids,
|
||||
devp->device_id[0]),
|
||||
"invalid manufacturer id");
|
||||
osalDbgAssert(w25q_find_id(w25q_memory_type_ids,
|
||||
sizeof w25q_memory_type_ids,
|
||||
devp->device_id[1]),
|
||||
"invalid memory type id");
|
||||
|
||||
/* Setting up the device size.*/
|
||||
snor_descriptor.sectors_count = (1U << (size_t)devp->device_id[2]) /
|
||||
SECTOR_SIZE;
|
||||
snor_descriptor.size = (size_t)snor_descriptor.sectors_count * SECTOR_SIZE;
|
||||
}
|
||||
|
||||
flash_error_t snor_device_read(SNORDriver *devp, flash_offset_t offset,
|
||||
size_t n, uint8_t *rp) {
|
||||
|
||||
#if SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI
|
||||
/* Fast read command in WSPI mode.*/
|
||||
bus_cmd_addr_dummy_receive(devp->config->busp, W25Q_CMD_FAST_READ,
|
||||
offset, W25Q_READ_DUMMY_CYCLES, n, rp);
|
||||
#else
|
||||
/* Normal read command in SPI mode.*/
|
||||
bus_cmd_addr_receive(devp->config->busp, W25Q_CMD_READ,
|
||||
offset, n, rp);
|
||||
#endif
|
||||
|
||||
return FLASH_NO_ERROR;
|
||||
}
|
||||
|
||||
flash_error_t snor_device_program(SNORDriver *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.*/
|
||||
bus_cmd(devp->config->busp, W25Q_CMD_WRITE_ENABLE);
|
||||
|
||||
/* Page program command.*/
|
||||
bus_cmd_addr_send(devp->config->busp, W25Q_CMD_PAGE_PROGRAM, offset,
|
||||
chunk, pp);
|
||||
|
||||
/* Wait for status and check errors.*/
|
||||
err = w25q_poll_status(devp);
|
||||
if (err != FLASH_NO_ERROR) {
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Next page.*/
|
||||
offset += chunk;
|
||||
pp += chunk;
|
||||
n -= chunk;
|
||||
}
|
||||
|
||||
return FLASH_NO_ERROR;
|
||||
}
|
||||
|
||||
flash_error_t snor_device_start_erase_all(SNORDriver *devp) {
|
||||
|
||||
/* Enabling write operation.*/
|
||||
bus_cmd(devp->config->busp, W25Q_CMD_WRITE_ENABLE);
|
||||
|
||||
/* Bulk erase command.*/
|
||||
bus_cmd(devp->config->busp, W25Q_CMD_BULK_ERASE);
|
||||
|
||||
return FLASH_NO_ERROR;
|
||||
}
|
||||
|
||||
flash_error_t snor_device_start_erase_sector(SNORDriver *devp,
|
||||
flash_sector_t sector) {
|
||||
flash_offset_t offset = (flash_offset_t)(sector * SECTOR_SIZE);
|
||||
|
||||
/* Enabling write operation.*/
|
||||
bus_cmd(devp->config->busp, W25Q_CMD_WRITE_ENABLE);
|
||||
|
||||
/* Sector erase command.*/
|
||||
bus_cmd_addr(devp->config->busp, CMD_SECTOR_ERASE, offset);
|
||||
|
||||
return FLASH_NO_ERROR;
|
||||
}
|
||||
|
||||
flash_error_t snor_device_verify_erase(SNORDriver *devp,
|
||||
flash_sector_t sector) {
|
||||
uint8_t cmpbuf[W25Q_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 SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI
|
||||
bus_cmd_addr_dummy_receive(devp->config->busp, W25Q_CMD_FAST_READ,
|
||||
offset, W25Q_READ_DUMMY_CYCLES,
|
||||
sizeof cmpbuf, cmpbuf);
|
||||
#else
|
||||
/* Normal read command in SPI mode.*/
|
||||
bus_cmd_addr_receive(devp->config->busp, W25Q_CMD_READ,
|
||||
offset, sizeof cmpbuf, cmpbuf);
|
||||
#endif
|
||||
|
||||
/* Checking for erased state of current buffer.*/
|
||||
for (p = cmpbuf; p < &cmpbuf[W25Q_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 snor_device_query_erase(SNORDriver *devp, uint32_t *msec) {
|
||||
uint8_t sts;
|
||||
|
||||
/* Read status command.*/
|
||||
bus_cmd_receive(devp->config->busp, W25Q_CMD_READ_STATUS_REGISTER,
|
||||
1, &sts);
|
||||
|
||||
/* If the P/E bit is 1 (busy) report that the operation is still in progress.*/
|
||||
if ((sts & W25Q_FLAGS_BUSY) != 0U) {
|
||||
|
||||
/* Recommended time before polling again, this is a simplified
|
||||
implementation.*/
|
||||
if (msec != NULL) {
|
||||
*msec = 1U;
|
||||
}
|
||||
|
||||
return FLASH_BUSY_ERASING;
|
||||
}
|
||||
|
||||
/* Checking for errors.*/
|
||||
/* NOP */
|
||||
|
||||
return FLASH_NO_ERROR;
|
||||
}
|
||||
|
||||
flash_error_t snor_device_read_sfdp(SNORDriver *devp, flash_offset_t offset,
|
||||
size_t n, uint8_t *rp) {
|
||||
|
||||
(void)devp;
|
||||
(void)rp;
|
||||
(void)offset;
|
||||
(void)n;
|
||||
|
||||
return FLASH_NO_ERROR;
|
||||
}
|
||||
|
||||
#if (SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI) || defined(__DOXYGEN__)
|
||||
void snor_activate_xip(SNORDriver *devp) {
|
||||
}
|
||||
|
||||
void snor_reset_xip(SNORDriver *devp) {
|
||||
wspi_command_t cmd;
|
||||
uint8_t buf[1];
|
||||
|
||||
/* Resetting XIP mode by reading one byte without XIP confirmation bit.*/
|
||||
cmd.cmd = 0U;
|
||||
cmd.alt = 0xFFU;
|
||||
cmd.addr = 0U;
|
||||
cmd.dummy = W25Q_READ_DUMMY_CYCLES;
|
||||
cmd.cfg = WSPI_CFG_CMD_MODE_NONE |
|
||||
WSPI_CFG_ADDR_SIZE_24 |
|
||||
#if W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI1L
|
||||
WSPI_CFG_ADDR_MODE_ONE_LINE |
|
||||
WSPI_CFG_DATA_MODE_ONE_LINE |
|
||||
#elif W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI2L
|
||||
WSPI_CFG_ADDR_MODE_TWO_LINES |
|
||||
WSPI_CFG_DATA_MODE_TWO_LINES |
|
||||
#elif W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI4L
|
||||
WSPI_CFG_ADDR_MODE_FOUR_LINES |
|
||||
WSPI_CFG_DATA_MODE_FOUR_LINES |
|
||||
#else
|
||||
WSPI_CFG_ADDR_MODE_EIGHT_LINES |
|
||||
WSPI_CFG_DATA_MODE_EIGHT_LINES |
|
||||
#endif
|
||||
WSPI_CFG_ALT_MODE_FOUR_LINES | /* Always 4 lines, note.*/
|
||||
WSPI_CFG_ALT_SIZE_8;
|
||||
wspiReceive(devp->config->busp, &cmd, 1, buf);
|
||||
|
||||
/* Enabling write operation.*/
|
||||
bus_cmd(devp->config->busp, W25Q_CMD_WRITE_ENABLE);
|
||||
}
|
||||
#endif /* SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI */
|
||||
|
||||
/** @} */
|
|
@ -0,0 +1,316 @@
|
|||
/*
|
||||
ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file hal_flash_device.h
|
||||
* @brief Winbond W25Q serial flash driver header.
|
||||
*
|
||||
* @addtogroup WINBONT_N25Q
|
||||
* @{
|
||||
*/
|
||||
|
||||
#ifndef HAL_FLASH_DEVICE_H
|
||||
#define HAL_FLASH_DEVICE_H
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver constants. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @name Device capabilities
|
||||
* @{
|
||||
*/
|
||||
#define SNOR_DEVICE_SUPPORTS_XIP TRUE
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Device identification
|
||||
* @{
|
||||
*/
|
||||
#define W25Q_SUPPORTED_MANUFACTURE_IDS {0xEF}
|
||||
#define W25Q_SUPPORTED_MEMORY_TYPE_IDS {0x40, 0x60}
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Command codes
|
||||
* @{
|
||||
*/
|
||||
#define W25Q_CMD_RESET_ENABLE 0x66
|
||||
#define W25Q_CMD_RESET 0x99
|
||||
#define W25Q_CMD_READ_JEDEC_ID 0x9F
|
||||
#define W25Q_CMD_READ_SFDP_REGISTER 0x5A
|
||||
#define W25Q_CMD_READ 0x03
|
||||
#define W25Q_CMD_FAST_READ 0x0B
|
||||
#define W25Q_CMD_WRITE_ENABLE 0x06
|
||||
#define W25Q_CMD_WRITE_DISABLE 0x04
|
||||
#define W25Q_CMD_READ_STATUS_REGISTER 0x05
|
||||
#define W25Q_CMD_WRITE_STATUS_REGISTER 0x01
|
||||
#define W25Q_CMD_PAGE_PROGRAM 0x02
|
||||
#define W25Q_CMD_SECTOR_ERASE 0x20
|
||||
#define W25Q_CMD_32K_BLOCK_ERASE 0x53
|
||||
#define W25Q_CMD_64K_BLOCK_ERASE 0xD8
|
||||
#define W25Q_CMD_BULK_ERASE 0xC7
|
||||
#define W25Q_CMD_PROGRAM_ERASE_RESUME 0x7A
|
||||
#define W25Q_CMD_PROGRAM_ERASE_SUSPEND 0x75
|
||||
#define W25Q_CMD_READ_UID_ARRAY 0x4B
|
||||
#define W25Q_CMD_PROGRAM_SECURITY_REGS 0x42
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Flags status register bits
|
||||
* @{
|
||||
*/
|
||||
#define W25Q_FLAGS_BUSY 0x01U
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Bus interface modes.
|
||||
* @{
|
||||
*/
|
||||
#define W25Q_BUS_MODE_WSPI1L 1U
|
||||
#define W25Q_BUS_MODE_WSPI2L 2U
|
||||
#define W25Q_BUS_MODE_WSPI4L 4U
|
||||
/** @} */
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver pre-compile time settings. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/**
|
||||
* @brief Switch WSPI 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 WSPI bus mode.
|
||||
*/
|
||||
#if !defined(W25Q_SWITCH_WIDTH) || defined(__DOXYGEN__)
|
||||
#define W25Q_SWITCH_WIDTH TRUE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Device bus mode to be used.
|
||||
* #note if @p W25Q_SWITCH_WIDTH is @p FALSE then this is the bus mode
|
||||
* that the device is expected to be using.
|
||||
* #note if @p W25Q_SWITCH_WIDTH is @p TRUE then this is the bus mode
|
||||
* that the device will be switched in.
|
||||
* @note This option is only valid in WSPI bus mode.
|
||||
*/
|
||||
#if !defined(W25Q_BUS_MODE) || defined(__DOXYGEN__)
|
||||
#define W25Q_BUS_MODE W25Q_BUS_MODE_WSPI4L
|
||||
#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(W25Q_NICE_WAITING) || defined(__DOXYGEN__)
|
||||
#define W25Q_NICE_WAITING TRUE
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Uses 4kB sub-sectors rather than 64kB sectors.
|
||||
*/
|
||||
#if !defined(W25Q_USE_SUB_SECTORS) || defined(__DOXYGEN__)
|
||||
#define W25Q_USE_SUB_SECTORS TRUE
|
||||
#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(W25Q_COMPARE_BUFFER_SIZE) || defined(__DOXYGEN__)
|
||||
#define W25Q_COMPARE_BUFFER_SIZE 32
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Number of dummy cycles for fast read (1..15).
|
||||
* @details This is the number of dummy cycles to be used for fast read
|
||||
* operations.
|
||||
*/
|
||||
#if !defined(W25Q_READ_DUMMY_CYCLES) || defined(__DOXYGEN__)
|
||||
#define W25Q_READ_DUMMY_CYCLES 1
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Derived constants and error checks. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if (W25Q_COMPARE_BUFFER_SIZE & (W25Q_COMPARE_BUFFER_SIZE - 1)) != 0
|
||||
#error "invalid W25Q_COMPARE_BUFFER_SIZE value"
|
||||
#endif
|
||||
|
||||
#if (W25Q_READ_DUMMY_CYCLES < 1) || (W25Q_READ_DUMMY_CYCLES > 15)
|
||||
#error "invalid W25Q_READ_DUMMY_CYCLES value (1..15)"
|
||||
#endif
|
||||
|
||||
#if (W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI4L) || defined(__DOXYGEN__)
|
||||
/**
|
||||
* @brief WSPI settings for command only.
|
||||
*/
|
||||
#define SNOR_WSPI_CFG_CMD (WSPI_CFG_CMD_MODE_FOUR_LINES | \
|
||||
WSPI_CFG_ADDR_MODE_NONE | \
|
||||
WSPI_CFG_ALT_MODE_NONE | \
|
||||
WSPI_CFG_DATA_MODE_NONE | \
|
||||
WSPI_CFG_CMD_SIZE_8 | \
|
||||
WSPI_CFG_ADDR_SIZE_24)
|
||||
|
||||
/**
|
||||
* @brief WSPI settings for command and address.
|
||||
*/
|
||||
#define SNOR_WSPI_CFG_CMD_ADDR (WSPI_CFG_CMD_MODE_FOUR_LINES | \
|
||||
WSPI_CFG_ADDR_MODE_FOUR_LINES | \
|
||||
WSPI_CFG_ALT_MODE_NONE | \
|
||||
WSPI_CFG_DATA_MODE_NONE | \
|
||||
WSPI_CFG_CMD_SIZE_8 | \
|
||||
WSPI_CFG_ADDR_SIZE_24)
|
||||
|
||||
/**
|
||||
* @brief WSPI settings for command and data.
|
||||
*/
|
||||
#define SNOR_WSPI_CFG_CMD_DATA (WSPI_CFG_CMD_MODE_FOUR_LINES | \
|
||||
WSPI_CFG_ADDR_MODE_NONE | \
|
||||
WSPI_CFG_ALT_MODE_NONE | \
|
||||
WSPI_CFG_DATA_MODE_FOUR_LINES | \
|
||||
WSPI_CFG_CMD_SIZE_8 | \
|
||||
WSPI_CFG_ADDR_SIZE_24)
|
||||
|
||||
/**
|
||||
* @brief WSPI settings for command, address and data.
|
||||
*/
|
||||
#define SNOR_WSPI_CFG_CMD_ADDR_DATA (WSPI_CFG_CMD_MODE_FOUR_LINES | \
|
||||
WSPI_CFG_ADDR_MODE_FOUR_LINES | \
|
||||
WSPI_CFG_ALT_MODE_NONE | \
|
||||
WSPI_CFG_DATA_MODE_FOUR_LINES | \
|
||||
WSPI_CFG_CMD_SIZE_8 | \
|
||||
WSPI_CFG_ADDR_SIZE_24)
|
||||
|
||||
#elif W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI2L
|
||||
#define SNOR_WSPI_CFG_CMD (WSPI_CFG_CMD_MODE_TWO_LINES | \
|
||||
WSPI_CFG_ADDR_MODE_NONE | \
|
||||
WSPI_CFG_ALT_MODE_NONE | \
|
||||
WSPI_CFG_DATA_MODE_NONE | \
|
||||
WSPI_CFG_CMD_SIZE_8 | \
|
||||
WSPI_CFG_ADDR_SIZE_24)
|
||||
|
||||
#define SNOR_WSPI_CFG_CMD_ADDR (WSPI_CFG_CMD_MODE_TWO_LINES | \
|
||||
WSPI_CFG_ADDR_MODE_TWO_LINES | \
|
||||
WSPI_CFG_ALT_MODE_NONE | \
|
||||
WSPI_CFG_DATA_MODE_NONE | \
|
||||
WSPI_CFG_CMD_SIZE_8 | \
|
||||
WSPI_CFG_ADDR_SIZE_24)
|
||||
|
||||
#define SNOR_WSPI_CFG_CMD_DATA (WSPI_CFG_CMD_MODE_TWO_LINES | \
|
||||
WSPI_CFG_ADDR_MODE_NONE | \
|
||||
WSPI_CFG_ALT_MODE_NONE | \
|
||||
WSPI_CFG_DATA_MODE_TWO_LINES | \
|
||||
WSPI_CFG_CMD_SIZE_8 | \
|
||||
WSPI_CFG_ADDR_SIZE_24)
|
||||
|
||||
#define SNOR_WSPI_CFG_CMD_ADDR_DATA (WSPI_CFG_CMD_MODE_ONE_LINE | \
|
||||
WSPI_CFG_ADDR_MODE_ONE_LINE | \
|
||||
WSPI_CFG_ALT_MODE_NONE | \
|
||||
WSPI_CFG_DATA_MODE_ONE_LINE | \
|
||||
WSPI_CFG_CMD_SIZE_8 | \
|
||||
WSPI_CFG_ADDR_SIZE_24)
|
||||
|
||||
#elif W25Q_BUS_MODE == W25Q_BUS_MODE_WSPI1L
|
||||
#define SNOR_WSPI_CFG_CMD (WSPI_CFG_CMD_MODE_ONE_LINE | \
|
||||
WSPI_CFG_ADDR_MODE_NONE | \
|
||||
WSPI_CFG_ALT_MODE_NONE | \
|
||||
WSPI_CFG_DATA_MODE_NONE | \
|
||||
WSPI_CFG_CMD_SIZE_8 | \
|
||||
WSPI_CFG_ADDR_SIZE_24)
|
||||
|
||||
#define SNOR_WSPI_CFG_CMD_ADDR (WSPI_CFG_CMD_MODE_ONE_LINE | \
|
||||
WSPI_CFG_ADDR_MODE_ONE_LINE | \
|
||||
WSPI_CFG_ALT_MODE_NONE | \
|
||||
WSPI_CFG_DATA_MODE_NONE | \
|
||||
WSPI_CFG_CMD_SIZE_8 | \
|
||||
WSPI_CFG_ADDR_SIZE_24)
|
||||
|
||||
#define SNOR_WSPI_CFG_CMD_DATA (WSPI_CFG_CMD_MODE_ONE_LINE | \
|
||||
WSPI_CFG_ADDR_MODE_NONE | \
|
||||
WSPI_CFG_ALT_MODE_NONE | \
|
||||
WSPI_CFG_DATA_MODE_ONE_LINE | \
|
||||
WSPI_CFG_CMD_SIZE_8 | \
|
||||
WSPI_CFG_ADDR_SIZE_24)
|
||||
|
||||
#define SNOR_WSPI_CFG_CMD_ADDR_DATA (WSPI_CFG_CMD_MODE_ONE_LINE | \
|
||||
WSPI_CFG_ADDR_MODE_ONE_LINE | \
|
||||
WSPI_CFG_ALT_MODE_NONE | \
|
||||
WSPI_CFG_DATA_MODE_ONE_LINE | \
|
||||
WSPI_CFG_CMD_SIZE_8 | \
|
||||
WSPI_CFG_ADDR_SIZE_24)
|
||||
|
||||
#else
|
||||
#error "invalid W25Q_BUS_MODE setting"
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver data structures and types. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* Driver macros. */
|
||||
/*===========================================================================*/
|
||||
|
||||
/*===========================================================================*/
|
||||
/* External declarations. */
|
||||
/*===========================================================================*/
|
||||
|
||||
#if !defined(__DOXYGEN__)
|
||||
extern flash_descriptor_t snor_descriptor;
|
||||
#endif
|
||||
|
||||
#if (SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI) && (WSPI_SUPPORTS_MEMMAP == TRUE)
|
||||
extern const wspi_command_t snor_memmap_read;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void snor_device_init(SNORDriver *devp);
|
||||
flash_error_t snor_device_read(SNORDriver *devp, flash_offset_t offset,
|
||||
size_t n, uint8_t *rp);
|
||||
flash_error_t snor_device_program(SNORDriver *devp, flash_offset_t offset,
|
||||
size_t n, const uint8_t *pp);
|
||||
flash_error_t snor_device_start_erase_all(SNORDriver *devp);
|
||||
flash_error_t snor_device_start_erase_sector(SNORDriver *devp,
|
||||
flash_sector_t sector);
|
||||
flash_error_t snor_device_verify_erase(SNORDriver *devp,
|
||||
flash_sector_t sector);
|
||||
flash_error_t snor_device_query_erase(SNORDriver *devp, uint32_t *msec);
|
||||
flash_error_t snor_device_read_sfdp(SNORDriver *devp, flash_offset_t offset,
|
||||
size_t n, uint8_t *rp);
|
||||
#if (SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI) && \
|
||||
(SNOR_DEVICE_SUPPORTS_XIP == TRUE)
|
||||
void snor_activate_xip(SNORDriver *devp);
|
||||
void snor_reset_xip(SNORDriver *devp);
|
||||
#endif
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* HAL_FLASH_DEVICE_H */
|
||||
|
||||
/** @} */
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
FLASH_DIR=$(PROJECT_DIR)/hw_layer/drivers/flash/w25q
|
||||
|
||||
# List of all the Micron W25Q device files.
|
||||
SNORSRC := $(CHIBIOS)/os/hal/lib/complex/serial_nor/hal_serial_nor.c \
|
||||
$(FLASH_DIR)/hal_flash_device.c
|
||||
|
||||
# Required include directories
|
||||
SNORINC := $(CHIBIOS)/os/hal/lib/complex/serial_nor \
|
||||
$(FLASH_DIR)
|
||||
|
||||
# Shared variables
|
||||
ALLCSRC += $(SNORSRC)
|
||||
ALLINC += $(SNORINC)
|
Loading…
Reference in New Issue