Settings ext storage (#3155)

* flash_main.cpp: reorder code for easy integration of ext storage

* Add EFI_STORAGE_INT_FLASH option

Default set to TRUE

* Add ChibiOS's Managed Flash Storage to build

* Add support for QSPI flash (SST26F064A tested)

* board: subaru: enable WSPI and NOR flash drivers

* Add option to save settings on ext flash MFS partition

* board: subaru: store settings on QSPI NOR flash
This commit is contained in:
Andrey G 2021-08-14 16:36:08 +03:00 committed by GitHub
parent d9adba2024
commit c23e02c582
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 1013 additions and 16 deletions

View File

@ -166,6 +166,7 @@ include $(CHIBIOS)/os/ex/devices/ST/lis302dl.mk
endif
include $(CHIBIOS)/os/hal/lib/streams/streams.mk
include $(CHIBIOS)/os/various/cpp_wrappers/chcpp.mk
include $(CHIBIOS)/os/hal/lib/complex/mfs/hal_mfs.mk
ifeq ($(USE_FATFS),yes)
include $(CHIBIOS)/os/various/fatfs_bindings/fatfs.mk

View File

@ -36,3 +36,6 @@ DDEFS += -DUART_USE_WAIT=FALSE
ALLCSRC += $(BOARDSRC)
ALLCPPSRC += $(BOARDCPPSRC)
ALLINC += $(BOARDINC)
#Serial flash support
include $(PROJECT_DIR)/hw_layer/drivers/flash/sst26f_jedec.mk

View File

@ -139,4 +139,11 @@
#define EFI_NARROW_EGO_AVERAGING TRUE
/* this board has external QSPI NOR flash */
#undef EFI_STORAGE_EXT_SNOR
#define EFI_STORAGE_EXT_SNOR TRUE
#undef EFI_STORAGE_INT_FLASH
#define EFI_STORAGE_INT_FLASH FALSE
#endif /* EFIFEATURES_SUBARUEG33_H_ */

View File

@ -13,6 +13,8 @@
/* this file is exist just to include mcuconf.h from THIS directory */
#include "mcuconf.h"
#define HAL_USE_WSPI TRUE
#include "../../../hw_layer/ports/stm32/stm32f7/cfg/halconf.h"
#endif /* _HALCONF_SUBARUEG33_H_ */

View File

@ -76,4 +76,12 @@
//#undef STM32_CAN_CAN1_IRQ_PRIORITY
//#define STM32_CAN_CAN1_IRQ_PRIORITY 4
/*
* WSPI driver system settings.
*/
#define STM32_WSPI_USE_QUADSPI1 TRUE
#define STM32_WSPI_QUADSPI1_DMA_STREAM STM32_DMA_STREAM_ID(2, 7)
#define JEDEC_BUS_MODE JEDEC_BUS_MODE_WSPI4L
#define STM32_WSPI_QUADSPI1_PRESCALER_VALUE 40
#endif /* _MCUCONF_SUBARUEG33_H_ */

View File

@ -403,4 +403,12 @@
#define CONFIG_RESET_SWITCH_PIN 6
#endif
#ifndef EFI_STORAGE_INT_FLASH
#define EFI_STORAGE_INT_FLASH TRUE
#endif
#ifndef EFI_STORAGE_EXT_SNOR
#define EFI_STORAGE_EXT_SNOR FALSE
#endif
#define EFI_JOYSTICK TRUE

View File

@ -20,6 +20,11 @@
#include "tunerstudio.h"
#endif
#if EFI_STORAGE_EXT_SNOR == TRUE
#include "hal_serial_nor.h"
#include "hal_mfs.h"
#endif
#include "runtime_state.h"
static bool needToWriteConfiguration = false;
@ -28,6 +33,41 @@ extern persistent_config_container_s persistentState;
extern engine_configuration_s *engineConfiguration;
/* if we store settings externally */
#if EFI_STORAGE_EXT_SNOR == TRUE
/* Some fields in following struct is used for DMA transfers, so do no cache */
NO_CACHE SNORDriver snor1;
const WSPIConfig WSPIcfg1 = {
.end_cb = NULL,
.error_cb = NULL,
.dcr = STM32_DCR_FSIZE(23U) | /* 8MB device. */
STM32_DCR_CSHT(1U) /* NCS 2 cycles delay. */
};
const SNORConfig snorcfg1 = {
.busp = &WSPID1,
.buscfg = &WSPIcfg1
};
/* Managed Flash Storage stuff */
MFSDriver mfsd;
const MFSConfig mfsd_nor_config = {
.flashp = (BaseFlash *)&snor1,
.erased = 0xFFFFFFFFU,
.bank_size = 64 * 1024U,
.bank0_start = 0U,
.bank0_sectors = 128U, /* 128 * 4 K = 0.5 Mb */
.bank1_start = 128U,
.bank1_sectors = 128U
};
#define EFI_MSF_SETTINGS_RECORD_ID 1
#endif
/**
* https://sourceforge.net/p/rusefi/tickets/335/
*
@ -108,6 +148,8 @@ int eraseAndFlashCopy(flashaddr_t storageAddress, const TStorage& data) {
bool burnWithoutFlash = false;
void writeToFlashNow(void) {
bool isSuccess = false;
if (burnWithoutFlash) {
needToWriteConfiguration = false;
return;
@ -119,12 +161,27 @@ void writeToFlashNow(void) {
persistentState.version = FLASH_DATA_VERSION;
persistentState.value = flashStateCrc(&persistentState);
#if EFI_STORAGE_EXT_SNOR == TRUE
mfs_error_t err;
/* In case of MFS:
* do we need to have two copies?
* do we need to protect it with CRC? */
err = mfsWriteRecord(&mfsd, EFI_MSF_SETTINGS_RECORD_ID,
sizeof(persistentState), (uint8_t *)&persistentState);
if (err == MFS_NO_ERROR)
isSuccess = true;
#endif
#if EFI_STORAGE_INT_FLASH == TRUE
// Flash two copies
int result1 = eraseAndFlashCopy(getFlashAddrFirstCopy(), persistentState);
int result2 = eraseAndFlashCopy(getFlashAddrSecondCopy(), persistentState);
// handle success/failure
bool isSuccess = (result1 == FLASH_RETURN_SUCCESS) && (result2 == FLASH_RETURN_SUCCESS);
isSuccess = (result1 == FLASH_RETURN_SUCCESS) && (result2 == FLASH_RETURN_SUCCESS);
#endif
if (isSuccess) {
efiPrintf("FLASH_SUCCESS");
@ -181,7 +238,39 @@ static persisted_configuration_state_e doReadConfiguration(flashaddr_t address)
* connectivity so no console output here
*/
static persisted_configuration_state_e readConfiguration() {
persisted_configuration_state_e result = CRC_FAILED;
efiAssert(CUSTOM_ERR_ASSERT, getCurrentRemainingStack() > EXPECTED_REMAINING_STACK, "read f", PC_ERROR);
#if EFI_STORAGE_EXT_SNOR == TRUE
mfs_error_t err;
size_t settings_size = sizeof(persistentState);
err = mfsReadRecord(&mfsd, EFI_MSF_SETTINGS_RECORD_ID,
&settings_size, (uint8_t *)&persistentState);
if ((err == MFS_NO_ERROR) && (sizeof(persistentState) == settings_size))
result = PC_OK;
#endif
#if EFI_STORAGE_INT_FLASH == TRUE
auto firstCopyAddr = getFlashAddrFirstCopy();
auto secondyCopyAddr = getFlashAddrSecondCopy();
result = doReadConfiguration(firstCopyAddr);
if (result != PC_OK) {
efiPrintf("Reading second configuration copy");
result = doReadConfiguration(secondyCopyAddr);
}
#endif
return result;
}
void readFromFlash() {
persisted_configuration_state_e result = PC_OK;
#if HW_CHECK_MODE
/*
* getFlashAddr does device validation, we want validation to be invoked even while we are
* HW_CHECK_MODE mode where we would not need actual address
@ -190,16 +279,10 @@ static persisted_configuration_state_e readConfiguration() {
auto firstCopyAddr = getFlashAddrFirstCopy();
auto secondyCopyAddr = getFlashAddrSecondCopy();
#if HW_CHECK_MODE
persisted_configuration_state_e result = PC_OK;
resetConfigurationExt(DEFAULT_ENGINE_TYPE PASS_ENGINE_PARAMETER_SUFFIX);
#else // HW_CHECK_MODE
persisted_configuration_state_e result = doReadConfiguration(firstCopyAddr);
if (result != PC_OK) {
efiPrintf("Reading second configuration copy");
result = doReadConfiguration(secondyCopyAddr);
}
#else
result = readConfiguration();
#endif
if (result == CRC_FAILED) {
// we are here on first boot on brand new chip
@ -213,16 +296,11 @@ static persisted_configuration_state_e readConfiguration() {
*/
applyNonPersistentConfiguration(PASS_ENGINE_PARAMETER_SIGNATURE);
}
#endif // HW_CHECK_MODE
// we can only change the state after the CRC check
engineConfiguration->byFirmwareVersion = getRusEfiVersion();
memset(persistentState.persistentConfiguration.warning_message , 0, ERROR_BUFFER_SIZE);
validateConfiguration(PASS_ENGINE_PARAMETER_SIGNATURE);
return result;
}
void readFromFlash() {
persisted_configuration_state_e result = readConfiguration();
if (result == CRC_FAILED) {
efiPrintf("Need to reset flash to default due to CRC");
@ -249,6 +327,21 @@ static void writeConfigCommand() {
}
void initFlash() {
#if EFI_STORAGE_EXT_SNOR == TRUE
mfs_error_t err;
/* Initializing and starting snor1 driver.*/
snorObjectInit(&snor1);
snorStart(&snor1, &snorcfg1);
/* MFS */
mfsObjectInit(&mfsd);
err = mfsStart(&mfsd, &mfsd_nor_config);
if (err != MFS_NO_ERROR) {
/* hm...? */
}
#endif
addConsoleAction("readconfig", readFromFlash);
/**
* This would write NOW (you should not be doing this while connected to real engine)

View File

@ -0,0 +1,637 @@
/*
* hal_flash_device.h
*
* QSPI NOR flash driver with JEDEC SFDP for ChibiOS
* Tested and developed with Microchip SST26F064A
*
* @date Aug 14, 2021
* @author Andrey Gusakov, (c) 2021
*
* Based on ChibiOS drivers: Macronix MX25 and Micron N25Q
* ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
*
*/
/**
* @file hal_flash_device.c
* @brief Jedec JESD216 SFDP code.
*
* @addtogroup JEDEC_SFDP
* @{
*/
#include <string.h>
#include "hal.h"
#include "hal_serial_nor.h"
/*===========================================================================*/
/* Driver local definitions. */
/*===========================================================================*/
/*===========================================================================*/
/* Driver exported variables. */
/*===========================================================================*/
/**
* @brief Flash 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 = 0U, /* It is overwritten.*/
.address = 0U,
.size = 0U /* It is overwritten.*/
};
/* NOT TESTED YET */
#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 = JEDEC_CMD_READ,
.addr = 0,
.dummy = 0, //JEDEC_READ_DUMMY_CYCLES,
.cfg = WSPI_CFG_ADDR_SIZE_24 |
#if JEDEC_BUS_MODE == JEDEC_BUS_MODE_WSPI1L
WSPI_CFG_CMD_MODE_ONE_LINE |
WSPI_CFG_ADDR_MODE_ONE_LINE |
WSPI_CFG_DATA_MODE_ONE_LINE |
#elif JEDEC_BUS_MODE == JEDEC_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_NONE
};
#endif
#endif
/*===========================================================================*/
/* Driver local variables and types. */
/*===========================================================================*/
/* Hack to keep ChibiOS sources untouched and use on MCU with data cache:
* read/write through temp buffer in non-cached ram. */
#define NO_CACHE __attribute__((section(".ram2")))
static NO_CACHE uint8_t tmpbuf[256] __attribute__((aligned (32)));
/* Buffer for SFDP parsing */
static uint32_t sfdpbuf[64 / 4];
/* JEDEC read command.*/
static wspi_command_t jedec_cmd_read;
/* JEDEC erase command.*/
static wspi_command_t jedec_cmd_erase;
/* JEDEC page program command.*/
static wspi_command_t jedec_cmd_program;
/*===========================================================================*/
/* Driver local functions. */
/*===========================================================================*/
static flash_error_t jedec_poll_status(SNORDriver *devp) {
uint8_t sts;
do {
#if JEDEC_NICE_WAITING == TRUE
osalThreadSleepMilliseconds(1);
#endif
/* Read status command.*/
bus_cmd_receive(devp->config->busp, JEDEC_CMD_READ_STATUS_REGISTER,
1, tmpbuf);
sts = tmpbuf[0];
} while ((sts & JEDEC_FLAGS_STS_BUSY) != 0U);
return FLASH_NO_ERROR;
}
#if (SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI) || defined(__DOXYGEN__)
static void jedec_reset_memory(SNORDriver *devp) {
/* 1x JEDEC_CMD_RESET_ENABLE command.*/
static const wspi_command_t cmd_reset_enable_1 = {
.cmd = JEDEC_CMD_RESET_ENABLE,
.cfg = WSPI_CFG_CMD_MODE_ONE_LINE,
.addr = 0,
.alt = 0,
.dummy = 0
};
/* 1x JEDEC_CMD_RESET_MEMORY command.*/
static const wspi_command_t cmd_reset_memory_1 = {
.cmd = JEDEC_CMD_RESET_MEMORY,
.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 JEDEC_BUS_MODE == JEDEC_BUS_MODE_WSPI4L
/* 4x JEDEC_CMD_RESET_ENABLE command.*/
static const wspi_command_t cmd_reset_enable_4 = {
.cmd = JEDEC_CMD_RESET_ENABLE,
.cfg = WSPI_CFG_CMD_MODE_FOUR_LINES,
.addr = 0,
.alt = 0,
.dummy = 0
};
/* 4x JEDEC_CMD_RESET_MEMORY command.*/
static const wspi_command_t cmd_reset_memory_4 = {
.cmd = JEDEC_CMD_RESET_MEMORY,
.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);
#elif JEDEC_BUS_MODE == JEDEC_BUS_MODE_WSPI4L
/* 2x JEDEC_CMD_RESET_ENABLE command.*/
static const wspi_command_t cmd_reset_enable_2 = {
.cmd = JEDEC_CMD_RESET_ENABLE,
.cfg = WSPI_CFG_CMD_MODE_TWO_LINES,
.addr = 0,
.alt = 0,
.dummy = 0
};
/* 2x JEDEC_CMD_RESET_MEMORY command.*/
static const wspi_command_t cmd_reset_memory_2 = {
.cmd = JEDEC_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_memory_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_memory_1);
}
#endif /* SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI */
static void jedec_set_config(SNORDriver *devp, uint8_t val)
{
tmpbuf[0] = 0;
tmpbuf[1] = val;
bus_cmd_send(devp->config->busp, JEDEC_CMD_WRITE_STATUS_REGISTER, 2, tmpbuf);
}
static uint8_t jedec_get_config(SNORDriver *devp) {
/* Read status command.*/
bus_cmd_receive(devp->config->busp, JEDEC_CMD_READ_CONFIGURATION_REGISTER,
1, tmpbuf);
return tmpbuf[0];
}
static void jedec_write_enable(SNORDriver *devp, int enable) {
/* Enabling write operation.*/
bus_cmd(devp->config->busp, enable ? JEDEC_CMD_WRITE_ENABLE : JEDEC_CMD_WRITE_DISABLE);
}
static void snor_device_fill_cmd(wspi_command_t *cmd,
uint32_t cfg, uint8_t opcode,
uint8_t mode_clocks, uint8_t dummy_clocks)
{
cmd->cmd = opcode;
cmd->cfg = cfg;
cmd->dummy = 0;
cmd->alt = 0; /* ? */
if (mode_clocks) {
uint8_t mode_bytes = 0;
/* Alt bytes mode - same as address */
if ((cmd->cfg & WSPI_CFG_ADDR_MODE_MASK) == WSPI_CFG_ADDR_MODE_ONE_LINE) {
cmd->cfg |= WSPI_CFG_ALT_MODE_ONE_LINE;
mode_bytes = mode_clocks / 8;
} else if ((cmd->cfg & WSPI_CFG_ADDR_MODE_MASK) == WSPI_CFG_ADDR_MODE_TWO_LINES) {
cmd->cfg |= WSPI_CFG_ALT_MODE_TWO_LINES;
mode_bytes = mode_clocks / 4;
} else if ((cmd->cfg & WSPI_CFG_ADDR_MODE_MASK) == WSPI_CFG_ADDR_MODE_FOUR_LINES) {
cmd->cfg |= WSPI_CFG_ALT_MODE_FOUR_LINES;
mode_bytes = mode_clocks / 2;
} /* else if ((cmd->cfg & WSPI_CFG_ADDR_MODE_MASK) == WSPI_CFG_ADDR_MODE_EIGHT_LINES){
cmd->cfg |= WSPI_CFG_ALT_MODE_EIGHT_LINES;
mode_bytes = mode_clocks / 1;
} */
if (mode_bytes == 1)
cmd->cfg |= WSPI_CFG_ALT_SIZE_8;
else if (mode_bytes == 2)
cmd->cfg |= WSPI_CFG_ALT_SIZE_16;
else if (mode_bytes == 3)
cmd->cfg |= WSPI_CFG_ALT_SIZE_24;
else if (mode_bytes == 4)
cmd->cfg |= WSPI_CFG_ALT_SIZE_32;
else
osalDbgAssert(0, "Failed to calculate alternative bytes size");
} else {
cmd->cfg |= WSPI_CFG_ALT_MODE_NONE;
}
cmd->dummy = dummy_clocks;
}
/*===========================================================================*/
/* Driver exported functions. */
/*===========================================================================*/
void snor_device_init(SNORDriver *devp) {
int i;
uint8_t cfg;
uint8_t parameter_headers_n;
/* use as temp buffer, should be at least 64 bytes */
uint8_t *buf = (uint8_t *)sfdpbuf;
uint32_t *sfdp = sfdpbuf;
/* offset in sfdp area */
flash_offset_t offset = 0;
const uint8_t sfdp_sign[4] = {0x53, 0x46, 0x44, 0x50}; /* "SFDP" */
#if 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.*/
jedec_reset_memory(devp);
#endif /* SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI */
/* SST26VF specific: adjust configuration register */
cfg = jedec_get_config(devp);
/* disable WP and HOLD */
cfg |= (1 << 1);
/* WP# disable */
cfg &= ~(1 << 7);
jedec_set_config(devp, cfg);
/* Global Block Protection Unlock */
jedec_write_enable(devp, 1);
bus_cmd(devp->config->busp, JEDEC_CMD_GLOBAL_BLOCK_PROTECTION_UNLOCK);
/* Reading SFDP Header. */
snor_device_read_sfdp(devp, offset, 8, buf);
/* Checking if the device supports SFDP. */
osalDbgAssert(memcmp(sfdp_sign, buf, 4) == 0,
"chip does not support SFDP");
/* Find JEDEC Flash Parameter Header */
parameter_headers_n = buf[6];
for (i = 0; i < parameter_headers_n; i++) {
int length;
/* each header is 8 bytes lont + 8 bytes of SFDP header */
offset = 8 + (i * 8);
snor_device_read_sfdp(devp, offset, 8, buf);
if (buf[0] != 0x00) {
/* vendor-specific header - skip. */
continue;
}
/* get Parameter Table Pointer */
offset = buf[4] | (buf[5] << 8) | (buf[6] << 16);
/* and length */
length = buf[3] * 4; /* in DWORDs */
if (length != 0x40)
continue;
snor_device_read_sfdp(devp, offset, length, buf);
break;
}
osalDbgAssert(i != parameter_headers_n,
"JEDEC SFDP parameters block not found");
/* Setting up the device sizes.*/
/* Chip density defined in bits */
if (sfdp[1] & 0x80000000)
/* more than 4 gigabits */
snor_descriptor.size = (size_t)(1 << ((sfdp[1] & 0x7fffffff) - 3));
else
snor_descriptor.size = (size_t)((sfdp[1] + 1) >> 3);
/* Use sector size 1, assume smalest */
snor_descriptor.sectors_size = (size_t)1 << (sfdp[7] & 0xff);
snor_descriptor.sectors_count = snor_descriptor.size /
snor_descriptor.sectors_size;
/* Fastest read command */
/* TODO: add 4-4-4 and 2-2-2 support */
if (sfdp[0] & (1 << 21)) {
/* 1-4-4 */
snor_device_fill_cmd(&jedec_cmd_read,
WSPI_CFG_CMD_MODE_ONE_LINE | WSPI_CFG_CMD_SIZE_8 |
WSPI_CFG_ADDR_MODE_FOUR_LINES |
WSPI_CFG_DATA_MODE_FOUR_LINES,
(sfdp[2] >> 8) & 0xff, (sfdp[2] >> 5) & 0x07, (sfdp[2] >> 0) & 0x1f);
} else if (sfdp[0] & (1 << 22)) {
/* 1-1-4 */
snor_device_fill_cmd(&jedec_cmd_read,
WSPI_CFG_CMD_MODE_ONE_LINE | WSPI_CFG_CMD_SIZE_8 |
WSPI_CFG_ADDR_MODE_ONE_LINE |
WSPI_CFG_DATA_MODE_FOUR_LINES,
(sfdp[2] >> 24) & 0xff, (sfdp[2] >> 21) & 0x07, (sfdp[2] >> 16) & 0x1f);
} else if (sfdp[0] & (1 << 20)) {
/* 1-2-2 */
snor_device_fill_cmd(&jedec_cmd_read,
WSPI_CFG_CMD_MODE_ONE_LINE | WSPI_CFG_CMD_SIZE_8 |
WSPI_CFG_ADDR_MODE_TWO_LINES |
WSPI_CFG_DATA_MODE_TWO_LINES,
(sfdp[3] >> 24) & 0xff, (sfdp[3] >> 21) & 0x07, (sfdp[3] >> 16) & 0x1f);
} else if (sfdp[0] & (1 << 16)) {
/* 1-1-2 */
snor_device_fill_cmd(&jedec_cmd_read,
WSPI_CFG_CMD_MODE_ONE_LINE | WSPI_CFG_CMD_SIZE_8 |
WSPI_CFG_ADDR_MODE_ONE_LINE |
WSPI_CFG_DATA_MODE_TWO_LINES,
(sfdp[3] >> 8) & 0xff, (sfdp[3] >> 5) & 0x07, (sfdp[3] >> 0) & 0x1f);
}
if (1) {
/* Fallback to 1-1-1 */
snor_device_fill_cmd(&jedec_cmd_read,
WSPI_CFG_CMD_MODE_ONE_LINE | WSPI_CFG_CMD_SIZE_8 |
WSPI_CFG_ADDR_MODE_ONE_LINE |
WSPI_CFG_DATA_MODE_ONE_LINE,
JEDEC_CMD_READ, 0, 0);
}
/* TODO: get from SFDP */
snor_device_fill_cmd(&jedec_cmd_erase,
WSPI_CFG_CMD_MODE_ONE_LINE | WSPI_CFG_CMD_SIZE_8 |
WSPI_CFG_ADDR_MODE_ONE_LINE,
JEDEC_CMD_SUBSECTOR_ERASE, 0, 0);
snor_device_fill_cmd(&jedec_cmd_program,
WSPI_CFG_CMD_MODE_ONE_LINE | WSPI_CFG_CMD_SIZE_8 |
WSPI_CFG_ADDR_MODE_ONE_LINE |
WSPI_CFG_DATA_MODE_ONE_LINE,
JEDEC_CMD_PAGE_PROGRAM, 0, 0);
/* TODO: how to check addressing in SFDP? */
if (1) {
jedec_cmd_read.cfg |= WSPI_CFG_ADDR_SIZE_24;
jedec_cmd_erase.cfg |= WSPI_CFG_ADDR_SIZE_24;
jedec_cmd_program.cfg |= WSPI_CFG_ADDR_SIZE_24;
} else {
jedec_cmd_read.cfg |= WSPI_CFG_ADDR_SIZE_32;
jedec_cmd_erase.cfg |= WSPI_CFG_ADDR_SIZE_32;
jedec_cmd_program.cfg |= WSPI_CFG_ADDR_SIZE_32;
}
}
flash_error_t snor_device_read(SNORDriver *devp, flash_offset_t offset,
size_t n, uint8_t *rp) {
while (n) {
size_t chunk = n < sizeof(tmpbuf) ? n : sizeof(tmpbuf);
jedec_cmd_read.addr = offset;
/* read through non-cached buffer */
if (wspiReceive(devp->config->busp, &jedec_cmd_read, chunk, tmpbuf))
return FLASH_ERROR_READ;
memcpy(rp, tmpbuf, chunk);
offset += chunk;
rp += chunk;
n -= chunk;
}
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 | (snor_descriptor.page_size - 1)) + 1U) - offset);
if (chunk > n)
chunk = n;
/* send through non-cached buffer */
memcpy(tmpbuf, pp, chunk);
/* Enabling write operation.*/
jedec_write_enable(devp, 1);
/* Page program command.*/
jedec_cmd_program.addr = offset;
wspiSend(devp->config->busp, &jedec_cmd_program, chunk, tmpbuf);
/* Wait for status and check errors.*/
err = jedec_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.*/
jedec_write_enable(devp, 1);
/* Bulk erase command.*/
bus_cmd(devp->config->busp, JEDEC_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 * snor_descriptor.sectors_size);
/* Enabling write operation.*/
jedec_write_enable(devp, 1);
/* Sector erase command.*/
jedec_cmd_erase.addr = offset;
wspiCommand(devp->config->busp, &jedec_cmd_erase);
return FLASH_NO_ERROR;
}
flash_error_t snor_device_verify_erase(SNORDriver *devp,
flash_sector_t sector) {
flash_offset_t offset;
size_t n;
/* Read command.*/
offset = (flash_offset_t)(sector * snor_descriptor.sectors_size);
n = snor_descriptor.sectors_size;
while (n > 0U) {
size_t i;
size_t chunk = n < sizeof(tmpbuf) ? n : sizeof(tmpbuf);
jedec_cmd_read.addr = offset;
/* read through non-cached buffer */
if (wspiReceive(devp->config->busp, &jedec_cmd_read, chunk, tmpbuf)) {
return FLASH_ERROR_READ;
}
/* Checking for erased state of current buffer.*/
for (i = 0; i < chunk; i++) {
if (tmpbuf[i] != 0xFFU) {
/* Ready state again.*/
devp->state = FLASH_READY;
return FLASH_ERROR_VERIFY;
}
}
offset += chunk;
n -= chunk;
}
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, JEDEC_CMD_READ_STATUS_REGISTER,
1, tmpbuf);
sts = tmpbuf[0];
/* Busy?.*/
if ((sts & JEDEC_FLAGS_STS_BUSY) != 0U) {
/* Recommended time before polling again, this is a simplified
implementation.*/
if (msec != NULL) {
*msec = 1U;
}
return FLASH_BUSY_ERASING;
}
return FLASH_NO_ERROR;
}
flash_error_t snor_device_read_sfdp(SNORDriver *devp, flash_offset_t offset,
size_t n, uint8_t *rp) {
/* JEDEC SFDP read command.*/
wspi_command_t jedec_cmd_read_sfdp = {
.cmd = JEDEC_CMD_READ_DISCOVERY_PARAMETER,
.cfg = WSPI_CFG_CMD_MODE_ONE_LINE |
WSPI_CFG_CMD_SIZE_8 |
WSPI_CFG_ADDR_MODE_ONE_LINE |
WSPI_CFG_ADDR_SIZE_24 |
WSPI_CFG_ALT_MODE_NONE |
WSPI_CFG_DATA_MODE_ONE_LINE,
.addr = 0,
.alt = 0,
.dummy = 8 /* cycles, not bytes! */
};
while (n) {
size_t chunk = n < sizeof(tmpbuf) ? n : sizeof(tmpbuf);
jedec_cmd_read_sfdp.addr = offset;
/* read through non-cached buffer */
if (wspiReceive(devp->config->busp, &jedec_cmd_read_sfdp, chunk, tmpbuf))
return FLASH_ERROR_HW_FAILURE;
memcpy(rp, tmpbuf, chunk);
offset += chunk;
rp += chunk;
n -= chunk;
}
return FLASH_NO_ERROR;
}
#if (SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI) || defined(__DOXYGEN__)
void snor_activate_xip(SNORDriver *devp) {
(void)devp;
#if 0
static const uint8_t flash_status_xip[1] = {
(JEDEC_READ_DUMMY_CYCLES << 4U) | 0x07U
};
/* Activating XIP mode in the device.*/
jedec_write_enable(devp, 1);
bus_cmd_send(devp->config->busp, JEDEC_CMD_WRITE_V_CONF_REGISTER,
1, flash_status_xip);
#endif
}
void snor_reset_xip(SNORDriver *devp) {
(void)devp;
#if 0
static const uint8_t flash_conf[1] = {
(JEDEC_READ_DUMMY_CYCLES << 4U) | 0x0FU
};
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 = JEDEC_READ_DUMMY_CYCLES - 2U;
cmd.cfg = WSPI_CFG_CMD_MODE_NONE |
WSPI_CFG_ADDR_SIZE_24 |
#if JEDEC_BUS_MODE == JEDEC_BUS_MODE_WSPI1L
WSPI_CFG_ADDR_MODE_ONE_LINE |
WSPI_CFG_DATA_MODE_ONE_LINE |
#elif JEDEC_BUS_MODE == JEDEC_BUS_MODE_WSPI2L
WSPI_CFG_ADDR_MODE_TWO_LINES |
WSPI_CFG_DATA_MODE_TWO_LINES |
#elif JEDEC_BUS_MODE == JEDEC_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.*/
jedec_write_enable(devp, 1);
/* Rewriting volatile configuration register.*/
bus_cmd_send(devp->config->busp, JEDEC_CMD_WRITE_V_CONF_REGISTER,
1, flash_conf);
#endif
}
#endif /* SNOR_BUS_DRIVER == SNOR_BUS_DRIVER_WSPI */
/** @} */

View File

@ -0,0 +1,225 @@
/*
* hal_flash_device.h
*
* QSPI NOR flash driver with JEDEC SFDP for ChibiOS
* Tested and developed with Microchip SST26F064A
*
* @date Aug 14, 2021
* @author Andrey Gusakov, (c) 2021
*/
/**
* @file hal_flash_device.h
* @brief Jedec JESD216 SFDP
*
* @addtogroup JEDEC_SFDP
* @{
*/
#ifndef HAL_FLASH_DEVICE_H
#define HAL_FLASH_DEVICE_H
/*===========================================================================*/
/* Driver constants. */
/*===========================================================================*/
/**
* @name Device capabilities
* @{
*/
#define SNOR_DEVICE_SUPPORTS_XIP TRUE
/** @} */
/**
* @name Device identification
* @{
*/
/** @} */
/**
* @name Command codes
* @{
*/
#define JEDEC_CMD_READ_DISCOVERY_PARAMETER 0x5A
/* default/fallback commands */
#define JEDEC_CMD_WRITE_STATUS_REGISTER 0x01
#define JEDEC_CMD_PAGE_PROGRAM 0x02
#define JEDEC_CMD_READ 0x03
#define JEDEC_CMD_WRITE_DISABLE 0x04
#define JEDEC_CMD_READ_STATUS_REGISTER 0x05
#define JEDEC_CMD_WRITE_ENABLE 0x06
#define JEDEC_CMD_SUBSECTOR_ERASE 0x20
#define JEDEC_CMD_READ_CONFIGURATION_REGISTER 0x35
#define JEDEC_CMD_BULK_ERASE 0xC7
#define JEDEC_CMD_RESET_ENABLE 0x66
#define JEDEC_CMD_GLOBAL_BLOCK_PROTECTION_UNLOCK 0x98
#define JEDEC_CMD_RESET_MEMORY 0x99
/** @} */
/**
* @name Flags status register bits
* @{
*/
#define JEDEC_FLAGS_STS_BUSY 0x80U
/** @} */
/**
* @name Bus interface modes.
* @{
*/
#define JEDEC_BUS_MODE_WSPI1L 1U
#define JEDEC_BUS_MODE_WSPI2L 2U
#define JEDEC_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(JEDEC_SWITCH_WIDTH) || defined(__DOXYGEN__)
#define JEDEC_SWITCH_WIDTH TRUE
#endif
/**
* @brief Device bus mode to be used.
* #note if @p JEDEC_SWITCH_WIDTH is @p FALSE then this is the bus mode
* that the device is expected to be using.
* #note if @p JEDEC_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(JEDEC_BUS_MODE) || defined(__DOXYGEN__)
#define JEDEC_BUS_MODE JEDEC_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(JEDEC_NICE_WAITING) || defined(__DOXYGEN__)
#define JEDEC_NICE_WAITING TRUE
#endif
/**
* @brief Uses 4kB sub-sectors rather than 64kB sectors.
*/
#if !defined(JEDEC_USE_SUB_SECTORS) || defined(__DOXYGEN__)
#define JEDEC_USE_SUB_SECTORS FALSE
#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(JEDEC_READ_DUMMY_CYCLES) || defined(__DOXYGEN__)
#define JEDEC_READ_DUMMY_CYCLES 8
#endif
/*===========================================================================*/
/* Derived constants and error checks. */
/*===========================================================================*/
#if (JEDEC_READ_DUMMY_CYCLES < 1) || (JEDEC_READ_DUMMY_CYCLES > 15)
#error "invalid JEDEC_READ_DUMMY_CYCLES value (1..15)"
#endif
/**
* @brief WSPI settings for command only.
*/
#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)
/**
* @brief WSPI settings for command and address.
*/
#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)
/**
* @brief WSPI settings for command and data.
*/
#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)
/**
* @brief WSPI settings for command, address and data.
*/
#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)
/*===========================================================================*/
/* 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 */
/** @} */

View File

@ -0,0 +1,13 @@
FLASH_DIR=$(PROJECT_DIR)/hw_layer/drivers/flash
# List of all the Micron N25Q 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)