This commit is contained in:
Dan-guanghua
2025-12-15 12:05:06 +08:00
parent 749e9926e5
commit 90c69ea943
18 changed files with 9373 additions and 866 deletions

View File

@@ -0,0 +1,30 @@
if((${CMAKE_SYSTEM_NAME} STREQUAL XCORE_XS3A) OR (${CMAKE_SYSTEM_NAME} STREQUAL XCORE_XS2A))
## Create library target
add_library(framework_rtos_drivers_qspi_io INTERFACE)
target_sources(framework_rtos_drivers_qspi_io
INTERFACE
src/rtos_qspi_flash.c
src/rtos_qspi_flash_rpc.c
)
target_include_directories(framework_rtos_drivers_qspi_io
INTERFACE
api
)
target_link_libraries(framework_rtos_drivers_qspi_io
INTERFACE
lib_qspi_fast_read
rtos::osal
)
target_compile_options(framework_rtos_drivers_qspi_io
INTERFACE
-lquadflash
)
target_link_options(framework_rtos_drivers_qspi_io
INTERFACE
-lquadflash
)
## Create an alias
add_library(rtos::drivers::qspi_io ALIAS framework_rtos_drivers_qspi_io)
endif()

View File

@@ -0,0 +1,394 @@
// Copyright 2020-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#ifndef RTOS_QSPI_FLASH_H_
#define RTOS_QSPI_FLASH_H_
/**
* \addtogroup rtos_qspi_flash_driver rtos_qspi_flash_driver
*
* The public API for using the RTOS QSPI flash driver.
* @{
*/
#include <quadflash.h>
#include <quadflashlib.h>
#include "xs1.h"
#include "xcore/clock.h"
#include "xcore/port.h"
#define RTOS_QSPI_FLASH_READ_CHUNK_SIZE (24*1024)
/**
* Typedef to the RTOS QSPI flash driver instance struct.
*/
typedef struct rtos_qspi_flash_struct rtos_qspi_flash_t;
/**
* Struct representing an RTOS QSPI flash driver instance.
*
* The members in this struct should not be accessed directly.
*/
struct rtos_qspi_flash_struct {
__attribute__((fptrgroup("rtos_qspi_flash_read_fptr_grp")))
void (*read)(rtos_qspi_flash_t *, uint8_t *, unsigned, size_t);
__attribute__((fptrgroup("rtos_qspi_flash_read_mode_fptr_grp")))
void (*read_mode)(rtos_qspi_flash_t *, uint8_t *, unsigned, size_t, qspi_fast_flash_read_transfer_mode_t);
__attribute__((fptrgroup("rtos_qspi_flash_write_fptr_grp")))
void (*write)(rtos_qspi_flash_t *, const uint8_t *, unsigned, size_t);
__attribute__((fptrgroup("rtos_qspi_flash_erase_fptr_grp")))
void (*erase)(rtos_qspi_flash_t *, unsigned, size_t);
__attribute__((fptrgroup("rtos_qspi_flash_lock_fptr_grp")))
void (*lock)(rtos_qspi_flash_t *);
__attribute__((fptrgroup("rtos_qspi_flash_unlock_fptr_grp")))
void (*unlock)(rtos_qspi_flash_t *);
fl_QSPIPorts qspi_ports;
fl_QuadDeviceSpec qspi_spec;
size_t flash_size;
unsigned calibration_valid;
unsigned last_op;
volatile int spinlock;
volatile int ll_req_flag;
};
/**
* \addtogroup rtos_qspi_flash_driver_core rtos_qspi_flash_driver_core
*
* The core functions for using an RTOS QSPI flash driver instance after
* it has been initialized and started. These functions may be used
* by both the host and any client tiles that RPC has been enabled for.
* @{
*/
/**
* Obtains a lock for exclusive access to the QSPI flash. This allows
* a thread to perform a sequence of operations (such as read, modify, erase,
* write) without the risk of another thread issuing a command in the middle of
* the sequence and corrupting the data in the flash.
*
* If only a single atomic operation needs to be performed, such as a read, it
* is not necessary to call this to obtain the lock first. Each individual operation
* obtains and releases the lock automatically so that they cannot run while another
* thread has the lock.
*
* The lock MUST be released when it is no longer needed by calling
* rtos_qspi_flash_unlock().
*
* \param ctx A pointer to the QSPI flash driver instance to lock.
*/
inline void rtos_qspi_flash_lock(
rtos_qspi_flash_t *ctx)
{
ctx->lock(ctx);
}
/**
* Releases a lock for exclusive access to the QSPI flash. The lock
* must have already been obtained by calling rtos_qspi_flash_lock().
*
* \param ctx A pointer to the QSPI flash driver instance to unlock.
*/
inline void rtos_qspi_flash_unlock(
rtos_qspi_flash_t *ctx)
{
ctx->unlock(ctx);
}
/**
* This reads data from the flash in quad I/O mode. All four lines are
* used to send the address and to read the data.
*
* \param ctx A pointer to the QSPI flash driver instance to use.
* \param data Pointer to the buffer to save the read data to.
* \param address The byte address in the flash to begin reading at.
* Only bits 23:0 contain the address. Bits 31:24 are
* ignored.
* \param len The number of bytes to read and save to \p data.
*/
inline void rtos_qspi_flash_read(
rtos_qspi_flash_t *ctx,
uint8_t *data,
unsigned address,
size_t len)
{
ctx->read(ctx, data, address, len);
}
/**
* This is a lower level version of rtos_qspi_flash_read() that is safe
* to call from within ISRs. If a task currently own the flash lock, or
* if another core is actively doing a read with this function, then the
* read will not be performed and an error returned. It is up to the
* application to determine what it should do in this situation and to
* avoid a potential deadlock.
*
* This function may only be called on the same tile as the underlying
* peripheral.
*
* This function uses the lib_quadflash API to perform the read. It is up
* to the application to ensure that XCORE resources are properly configured.
*
* \note It is not possible to call this from a task that currently owns
* the flash lock taken with rtos_qspi_flash_lock(). In general it is not
* advisable to call this from an RTOS task unless the small amount of
* overhead time that is introduced by rtos_qspi_flash_read() is unacceptable.
*
* \param ctx A pointer to the QSPI flash driver instance to use.
* \param data Pointer to the buffer to save the read data to.
* \param address The byte address in the flash to begin reading at.
* Only bits 23:0 contain the address. Bits 31:24 are
* ignored.
* \param len The number of bytes to read and save to \p data.
*
* \retval 0 if the flash was available and the read operation was performed.
* \retval -1 if the flash was unavailable and the read could not be performed.
*/
int rtos_qspi_flash_read_ll(
rtos_qspi_flash_t *ctx,
uint8_t *data,
unsigned address,
size_t len);
/**
* This writes data to the QSPI flash. The standard page program command
* is sent and only SIO0 (MOSI) is used to send the address and data.
*
* The driver handles sending the write enable command, as well as waiting for
* the write to complete.
*
* This function may return before the write operation is complete, as the actual
* write operation is queued and executed by a thread created by the driver.
*
* \note this function does NOT erase the flash first. Erase operations must be
* explicitly requested by the application.
*
* \param ctx A pointer to the QSPI flash driver instance to use.
* \param data Pointer to the data to write to the flash.
* \param address The byte address in the flash to begin writing at.
* Only bits 23:0 contain the address. The byte in bits 31:24 is
* not sent.
* \param len The number of bytes to write to the flash.
*/
inline void rtos_qspi_flash_write(
rtos_qspi_flash_t *ctx,
const uint8_t *data,
unsigned address,
size_t len)
{
ctx->write(ctx, data, address, len);
}
/**
* This erases data from the QSPI flash. If the address range to erase
* spans multiple sectors, then all of these sectors will be erased by issuing
* multiple erase commands.
*
* The driver handles sending the write enable command, as well as waiting for
* the write to complete.
*
* This function may return before the write operation is complete, as the actual
* erase operation is queued and executed by a thread created by the driver.
*
* \note The smallest amount of data that can be erased is a 4k sector.
* This means that data outside the address range specified by \p address
* and \p len will be erased if the address range does not both begin and
* end at 4k sector boundaries.
*
* \param ctx A pointer to the QSPI flash driver instance to use.
* \param address The byte address to begin erasing. This does not need to begin
* at a sector boundary, but if it does not, note that the entire
* sector that contains this address will still be erased.
* \param len The minimum number of bytes to erase. If \p address + \p len - 1
* does not correspond to the last address within a sector, note that
* the entire sector that contains this address will still be erased.
*/
inline void rtos_qspi_flash_erase(
rtos_qspi_flash_t *ctx,
unsigned address,
size_t len)
{
ctx->erase(ctx, address, len);
}
/**
* This gets the size in bytes of the flash chip.
*
* \param A pointer to the QSPI flash driver instance to query.
*
* \returns the size in bytes of the flash chip.
*/
inline size_t rtos_qspi_flash_size_get(
rtos_qspi_flash_t *qspi_flash_ctx)
{
return qspi_flash_ctx->flash_size;
}
/**
* This gets the size in bytes of each page in the flash chip.
*
* \param A pointer to the QSPI flash driver instance to query.
*
* \returns the size in bytes of the flash page.
*/
inline size_t rtos_qspi_flash_page_size_get(
rtos_qspi_flash_t *qspi_flash_ctx)
{
return qspi_flash_ctx->qspi_spec.pageSize;
}
/**
* This gets the number of pages in the flash chip.
*
* \param A pointer to the QSPI flash driver instance to query.
*
* \returns the number of pages in the flash chip.
*/
inline size_t rtos_qspi_flash_page_count_get(
rtos_qspi_flash_t *qspi_flash_ctx)
{
return qspi_flash_ctx->qspi_spec.numPages;
}
/**
* This gets the sector size of the flash chip
*
* \param A pointer to the QSPI flash driver instance to query.
*
* \returns the size in bytes of the smallest sector
*/
inline size_t rtos_qspi_flash_sector_size_get(
rtos_qspi_flash_t *qspi_flash_ctx)
{
return qspi_flash_ctx->qspi_spec.sectorEraseSize;
}
/**
* Gets the value of the calibration valid.
*
* \param A pointer to the QSPI flash driver instance to query.
*
* \returns 1 if calibration was successful
* 0 otherwise
*/
inline unsigned rtos_qspi_flash_calibration_valid_get(
rtos_qspi_flash_t *qspi_flash_ctx)
{
return qspi_flash_ctx->calibration_valid;
}
/**@}*/
/**
* Starts an RTOS QSPI flash driver instance. This must only be called by the tile that
* owns the driver instance. It may be called either before or after starting
* the RTOS, but must be called before any of the core QSPI flash driver functions are
* called with this instance.
*
* rtos_qspi_flash_init() must be called on this QSPI flash driver instance prior to calling this.
*
* \param ctx A pointer to the QSPI flash driver instance to start.
* \param priority The priority of the task that gets created by the driver to
* handle the QSPI flash interface.
*/
void rtos_qspi_flash_start(
rtos_qspi_flash_t *ctx,
unsigned priority);
/**
* Sets the core affinity for a RTOS QSPI flash driver instance.
* This must only be called by the tile that owns the driver instance.
* It may be called either before or after starting the RTOS, and should
* be called before any of the core QSPI flash driver functions are
* called with this instance.
*
* Since interrupts are disabled during the QSPI transaction on the op thread, a
* core mask is provided to allow users to avoid collisions with application ISRs.
*
* rtos_qspi_flash_start() must be called on this QSPI flash driver instance prior to calling this.
*
* \param ctx A pointer to the QSPI flash driver instance to start.
* \param op_core_mask A bitmask representing the cores on which the QSPI I/O thread
* created by the driver is allowed to run. Bit 0 is core 0, bit 1 is core 1,
* etc.
*/
void rtos_qspi_flash_op_core_affinity_set(
rtos_qspi_flash_t *ctx,
uint32_t op_core_mask);
/**
* Initializes an RTOS QSPI flash driver instance.
* This must only be called by the tile that owns the driver instance. It may be
* called either before or after starting the RTOS, but must be called before calling
* rtos_qspi_flash_start() or any of the core QSPI flash driver functions with this instance.
*
* This function will initialize a flash driver using lib_quadflash for
* all operations.
*
* \param ctx A pointer to the QSPI flash driver instance to initialize.
* \param clock_block The clock block to use for the qspi_io interface.
* \param cs_port The chip select port. MUST be a 1-bit port.
* \param sclk_port The SCLK port. MUST be a 1-bit port.
* \param sio_port The SIO port. MUST be a 4-bit port.
* \param spec A pointer to the flash part specification.
* This may be set to NULL to use the XTC default
*/
void rtos_qspi_flash_init(
rtos_qspi_flash_t *ctx,
xclock_t clock_block,
port_t cs_port,
port_t sclk_port,
port_t sio_port,
fl_QuadDeviceSpec *spec);
/**
* Initializes an RTOS QSPI flash driver instance.
* This must only be called by the tile that owns the driver instance. It may be
* called either before or after starting the RTOS, but must be called before calling
* rtos_qspi_flash_start() or any of the core QSPI flash driver functions with this instance.
*
* This function will initialize a flash driver using lib_quadflash for
* erase and writes, and lib_qspi_fast_read for reads. If calibration
* fails the driver will enable lib_quadflash for reads and allow the
* application to decide what to do about the failed calibration. The
* status of the calibration can be checked at runtime by calling
* rtos_qspi_flash_calibration_valid_get().
*
* \param ctx A pointer to the QSPI flash driver instance to initialize.
* \param clock_block The clock block to use for the qspi_io interface.
* \param cs_port The chip select port. MUST be a 1-bit port.
* \param sclk_port The SCLK port. MUST be a 1-bit port.
* \param sio_port The SIO port. MUST be a 4-bit port.
* \param spec A pointer to the flash part specification.
* This may be set to NULL to use the XTC default
* \param read_mode The transfer mode to use for port reads.
* Invalid values will default to qspi_fast_flash_read_transfer_raw
* \param read_divide The divisor to use for QSPI SCLK.
* \param calibration_pattern_addr The address of the default calibration pattern.
* This driver requires the default calibration pattern
* supplied with lib_qspi_fast_read and does not support
* custom patterns.
*/
void rtos_qspi_flash_fast_read_init(
rtos_qspi_flash_t *ctx,
xclock_t clock_block,
port_t cs_port,
port_t sclk_port,
port_t sio_port,
fl_QuadDeviceSpec *spec,
uint8_t read_divide,
uint32_t calibration_pattern_addr);
/**@}*/
#endif /* RTOS_QSPI_FLASH_H_ */

View File

@@ -0,0 +1,10 @@
set(LIB_NAME lib_qspi_flash)
set(LIB_VERSION 0.0.1)
set(LIB_INCLUDES api)
set(LIB_DEPENDENT_MODULES "lib_logging(3.2.0)")
set(LIB_COMPILER_FLAGS -O3 -DREF_CLK_FREQ=100 -fasm-linenum -fcomment-asm)
list(APPEND LIB_COMPILER_FLAGS -DXASSERT_ENABLE_ASSERTIONS=0
-DXASSERT_ENABLE_DEBUG=0
-DXASSERT_ENABLE_LINE_NUMBERS=0)
XMOS_REGISTER_MODULE()

View File

@@ -0,0 +1,399 @@
// Copyright 2020-2023 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#define DEBUG_UNIT RTOS_QSPI_FLASH
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#include <xcore/assert.h>
#include <xcore/lock.h>
#include "rtos_qspi_flash.h"
#ifndef QSPI_FL_RETRY_DELAY_TICKS
#define QSPI_FL_RETRY_DELAY_TICKS 1000
#endif
#ifndef QSPI_FL_RETRY_ATTEMPT_CNT
#define QSPI_FL_RETRY_ATTEMPT_CNT 5
#endif
/* TODO, these will be removed once moved to the public API */
#define ERASE_CHIP 0xC7
extern void fl_int_read(
unsigned char cmd,
unsigned int address,
unsigned char * destination,
unsigned int num_bytes);
extern void fl_int_write(
unsigned char cmd,
unsigned int pageAddress,
const unsigned char data[],
unsigned int num_bytes);
extern void fl_int_sendSingleByteCommand(unsigned char cmd);
extern void fl_int_eraseSector(
unsigned char cmd,
unsigned int sectorAddress);
/* end TODO */
/* Library only supports 4096 sector size*/
#define QSPI_ERASE_TYPE_SIZE_LOG2 12
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#define FLASH_OP_NONE 0
#define FLASH_OP_READ 1
#define FLASH_OP_WRITE 2
#define FLASH_OP_ERASE 3
#define FLASH_OP_READ_FAST_RAW 4
#define FLASH_OP_READ_FAST_NIBBLE_SWAP 5
#define FLASH_OP_LL_SETUP 6
extern unsigned __libc_hwlock;
typedef struct {
int op;
uint8_t *data;
unsigned address;
size_t len;
unsigned priority;
} qspi_flash_op_req_t;
/*
* Returns true if the spinlock is
* acquired, false if not available.
* NOT recursive.
*/
static bool spinlock_get(volatile int *lock)
{
bool ret;
lock_acquire(__libc_hwlock);
{
if (*lock == 0) {
*lock = 1;
ret = true;
} else {
ret = false;
}
}
lock_release(__libc_hwlock);
return ret;
}
/*
* Releases the lock. It MUST be owned
* by the caller.
*/
static void spinlock_release(volatile int *lock)
{
*lock = 0;
}
static void rtos_qspi_flash_fl_connect_with_retry(
rtos_qspi_flash_t *ctx)
{
if (fl_connectToDevice(&ctx->qspi_ports, &ctx->qspi_spec, 1) == 0) {
return;
} else {
int cnt = 0;
while (fl_connectToDevice(&ctx->qspi_ports, &ctx->qspi_spec, 1) != 0) {
delay_ticks(QSPI_FL_RETRY_DELAY_TICKS);
if (cnt++ >= QSPI_FL_RETRY_ATTEMPT_CNT) {
xassert(0); /* fl_connectToDevice failed too many times */
}
}
}
}
int rtos_qspi_flash_read_ll(
rtos_qspi_flash_t *ctx,
uint8_t *data,
unsigned address,
size_t len)
{
uint32_t irq_mask;
bool lock_acquired;
// printf("Asked to ll read %d bytes at address 0x%08x\n", len, address);
lock_acquired = spinlock_get(&ctx->spinlock);
while (lock_acquired && len > 0) {
size_t read_len = MIN(len, RTOS_QSPI_FLASH_READ_CHUNK_SIZE);
/*
* Cap the address at the size of the flash.
* This ensures the correction below will work if
* address is outside the flash's address space.
*/
if (address >= ctx->flash_size) {
address = ctx->flash_size;
}
if (address + read_len > ctx->flash_size) {
int original_len = read_len;
/* Don't read past the end of the flash */
read_len = ctx->flash_size - address;
/* Return all 0xFF bytes for addresses beyond the end of the flash */
memset(&data[read_len], 0xFF, original_len - read_len);
}
// printf("Read %d bytes from flash at address 0x%x\n", read_len, address);
fl_int_read(ctx->qspi_spec.readCommand, address, data, read_len);
len -= read_len;
data += read_len;
address += read_len;
}
if (lock_acquired) {
spinlock_release(&ctx->spinlock);
}
return lock_acquired ? 0 : -1;
}
static void read_op(
rtos_qspi_flash_t *ctx,
uint8_t *data,
unsigned address,
size_t len)
{
// printf("Asked to read %d bytes at address 0x%08x\n", len, address);
while (len > 0) {
size_t read_len = MIN(len, RTOS_QSPI_FLASH_READ_CHUNK_SIZE);
/*
* Cap the address at the size of the flash.
* This ensures the correction below will work if
* address is outside the flash's address space.
*/
if (address >= ctx->flash_size) {
address = ctx->flash_size;
}
if (address + read_len > ctx->flash_size) {
int original_len = read_len;
/* Don't read past the end of the flash */
read_len = ctx->flash_size - address;
/* Return all 0xFF bytes for addresses beyond the end of the flash */
memset(&data[read_len], 0xFF, original_len - read_len);
}
// printf("Read %d bytes from flash at address 0x%x\n", read_len, address);
fl_int_read(ctx->qspi_spec.readCommand, address, data, read_len);
len -= read_len;
data += read_len;
address += read_len;
}
}
static void while_busy(void)
{
bool busy;
do {
busy = fl_getBusyStatus();
} while (busy);
}
static void write_op(
rtos_qspi_flash_t *ctx,
const uint8_t *data,
unsigned address,
size_t len)
{
size_t bytes_left_to_write = len;
unsigned address_to_write = address;
const uint8_t *write_buf = data;
// printf("Asked to write %d bytes at address 0x%08x\n", bytes_left_to_write, address_to_write);
while (bytes_left_to_write > 0) {
/* compute the maximum number of bytes that can be written to the current page. */
size_t max_bytes_to_write = fl_getPageSize() - (address_to_write & (fl_getPageSize() - 1));
size_t bytes_to_write = bytes_left_to_write <= max_bytes_to_write ? bytes_left_to_write : max_bytes_to_write;
if (address_to_write >= ctx->flash_size) {
break; /* do not write past the end of the flash */
}
// printf("Write %d bytes from flash at address 0x%x\n", bytes_to_write, address_to_write);
fl_int_sendSingleByteCommand(ctx->qspi_spec.writeEnableCommand);
fl_int_write(ctx->qspi_spec.programPageCommand, address_to_write, write_buf, bytes_to_write);
while_busy();
fl_int_sendSingleByteCommand(ctx->qspi_spec.writeDisableCommand);
bytes_left_to_write -= bytes_to_write;
write_buf += bytes_to_write;
address_to_write += bytes_to_write;
}
}
#define SECTORS_TO_BYTES(s, ss_log2) ((s) << (ss_log2))
#define BYTES_TO_SECTORS(b, ss_log2) (((b) + (1 << ss_log2) - 1) >> (ss_log2))
#define SECTOR_TO_BYTE_ADDRESS(s, ss_log2) SECTORS_TO_BYTES(s, ss_log2)
#define BYTE_TO_SECTOR_ADDRESS(b, ss_log2) ((b) >> (ss_log2))
static void erase_op(
rtos_qspi_flash_t *ctx,
unsigned address,
size_t len)
{
size_t bytes_left_to_erase = len;
unsigned address_to_erase = address;
// printf("Asked to erase %d bytes at address 0x%08x\n", bytes_left_to_erase, address_to_erase);
if (address_to_erase == 0 && bytes_left_to_erase >= ctx->flash_size) {
/* Use chip erase when being asked to erase the entire address range */
// printf("Erasing entire chip\n");
fl_int_sendSingleByteCommand(ctx->qspi_spec.writeEnableCommand);
fl_int_sendSingleByteCommand(ERASE_CHIP);
while_busy();
fl_int_sendSingleByteCommand(ctx->qspi_spec.writeDisableCommand);
} else {
if (SECTOR_TO_BYTE_ADDRESS(BYTE_TO_SECTOR_ADDRESS(address_to_erase, QSPI_ERASE_TYPE_SIZE_LOG2), QSPI_ERASE_TYPE_SIZE_LOG2) != address_to_erase) {
/*
* If the provided starting erase address does not begin on the smallest
* sector boundary, then update the starting address and number of bytes
* to erase so that it does.
*/
unsigned sector_address;
sector_address = BYTE_TO_SECTOR_ADDRESS(address_to_erase, QSPI_ERASE_TYPE_SIZE_LOG2);
bytes_left_to_erase += address_to_erase - SECTOR_TO_BYTE_ADDRESS(sector_address, QSPI_ERASE_TYPE_SIZE_LOG2);
address_to_erase = SECTOR_TO_BYTE_ADDRESS(sector_address, QSPI_ERASE_TYPE_SIZE_LOG2);
// printf("adjusted starting erase address to %d\n", address_to_erase);
}
while (bytes_left_to_erase > 0) {
int erase_length;
int erase_length_log2 = QSPI_ERASE_TYPE_SIZE_LOG2;
if (address_to_erase >= ctx->flash_size) {
break; /* do not erase past the end of the flash */
}
erase_length = 1 << erase_length_log2;
xassert(address_to_erase == SECTOR_TO_BYTE_ADDRESS(BYTE_TO_SECTOR_ADDRESS(address_to_erase, erase_length_log2), erase_length_log2));
// printf("Erasing %d bytes (%d) at byte address %d, sector %d\n", erase_length, bytes_left_to_erase, address_to_erase, BYTE_TO_SECTOR_ADDRESS(address_to_erase, erase_length_log2));
fl_int_sendSingleByteCommand(ctx->qspi_spec.writeEnableCommand);
fl_int_eraseSector(ctx->qspi_spec.sectorEraseCommand, address_to_erase);
while_busy();
fl_int_sendSingleByteCommand(ctx->qspi_spec.writeDisableCommand);
address_to_erase += erase_length;
bytes_left_to_erase -= erase_length < bytes_left_to_erase ? erase_length : bytes_left_to_erase;
}
}
// printf("Erasing complete\n");
}
__attribute__((fptrgroup("rtos_qspi_flash_read_fptr_grp")))
static void qspi_flash_local_read(
rtos_qspi_flash_t *ctx,
uint8_t *data,
unsigned address,
size_t len)
{
qspi_flash_op_req_t op = {
.op = FLASH_OP_READ,
.data = data,
.address = address,
.len = len
};
read_op(ctx, op.data, op.address, op.len);
}
__attribute__((fptrgroup("rtos_qspi_flash_write_fptr_grp")))
static void qspi_flash_local_write(
rtos_qspi_flash_t *ctx,
const uint8_t *data,
unsigned address,
size_t len)
{
qspi_flash_op_req_t op = {
.op = FLASH_OP_WRITE,
.address = address,
.len = len
};
write_op(ctx, data, op.address, op.len);
}
__attribute__((fptrgroup("rtos_qspi_flash_erase_fptr_grp")))
static void qspi_flash_local_erase(
rtos_qspi_flash_t *ctx,
unsigned address,
size_t len)
{
qspi_flash_op_req_t op = {
.op = FLASH_OP_ERASE,
.address = address,
.len = len
};
erase_op(ctx, op.address, op.len);
}
void rtos_qspi_flash_init(
rtos_qspi_flash_t *ctx,
xclock_t clock_block,
port_t cs_port,
port_t sclk_port,
port_t sio_port,
fl_QuadDeviceSpec *spec)
{
ctx->qspi_ports.qspiCS = cs_port;
ctx->qspi_ports.qspiSCLK = sclk_port;
ctx->qspi_ports.qspiSIO = sio_port;
ctx->qspi_ports.qspiClkblk = clock_block;
fl_QuadDeviceSpec default_spec = FL_QUADDEVICE_DEFAULT;
if (spec == NULL) {
memcpy(&ctx->qspi_spec, &default_spec, sizeof(fl_QuadDeviceSpec));
} else {
memcpy(&ctx->qspi_spec, spec, sizeof(fl_QuadDeviceSpec));
}
xassert(fl_connectToDevice(&ctx->qspi_ports, &ctx->qspi_spec, 1) == 0);
/* Copy the spec back in case one was provided which has params populated by sfdp */
xassert(fl_copySpec(&ctx->qspi_spec) == 0);
ctx->flash_size = fl_getFlashSize();
/* Driver currently only supports 4096 sector size */
xassert(rtos_qspi_flash_sector_size_get(ctx) == (1 << QSPI_ERASE_TYPE_SIZE_LOG2));
/* Enable quad flash */
xassert(fl_quadEnable() == 0);
ctx->calibration_valid = 0;
ctx->last_op = FLASH_OP_NONE;
ctx->read = qspi_flash_local_read;
ctx->write = qspi_flash_local_write;
ctx->erase = qspi_flash_local_erase;
}