mirror of https://github.com/rusefi/rusefi-1.git
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:
parent
d9adba2024
commit
c23e02c582
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 */
|
||||
|
||||
/** @} */
|
|
@ -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 */
|
||||
|
||||
/** @} */
|
||||
|
|
@ -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)
|
Loading…
Reference in New Issue