libs
This commit is contained in:
30
lib_qspi_flash/lib_qspi_flash/CMakeLists.txt
Normal file
30
lib_qspi_flash/lib_qspi_flash/CMakeLists.txt
Normal 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()
|
||||
394
lib_qspi_flash/lib_qspi_flash/api/rtos_qspi_flash.h
Normal file
394
lib_qspi_flash/lib_qspi_flash/api/rtos_qspi_flash.h
Normal 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_ */
|
||||
10
lib_qspi_flash/lib_qspi_flash/lib_build_info.cmake
Normal file
10
lib_qspi_flash/lib_qspi_flash/lib_build_info.cmake
Normal 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()
|
||||
399
lib_qspi_flash/lib_qspi_flash/src/rtos_qspi_flash.c
Normal file
399
lib_qspi_flash/lib_qspi_flash/src/rtos_qspi_flash.c
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user