drivers/flash/soc_flash_nrf: introduce synchronization api usage

Rework ticker synchronization using newly introduced
radio synchronization API.
In kconfig synchronization using ll ticker become choice
option.

If CONFIG_SOC_FLASH_NRF_PARTIAL_ERASE is enabled the erase
timing is changed so intervals become similar to slots duration.
Previously interval was always ~90 ms, which looks like it was kept
so disproportional by oversight while the partial erase was
introduced.

Signed-off-by: Andrzej Puzdrowski <andrzej.puzdrowski@nordicsemi.no>
This commit is contained in:
Andrzej Puzdrowski 2020-07-18 18:08:33 +02:00 committed by Carles Cufí
parent 4c2432fdd4
commit affbafac26
4 changed files with 61 additions and 262 deletions

View file

@ -7,6 +7,7 @@ zephyr_library_sources_ifdef(CONFIG_NORDIC_QSPI_NOR nrf_qspi_nor.c)
zephyr_library_sources_ifdef(CONFIG_FLASH_SIMULATOR flash_simulator.c)
zephyr_library_sources_ifdef(CONFIG_SPI_FLASH_AT45 spi_flash_at45.c)
zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_NRF soc_flash_nrf.c)
zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_NRF_RADIO_SYNC soc_flash_nrf_ticker.c)
zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_MCUX soc_flash_mcux.c)
zephyr_library_sources_ifdef(CONFIG_FLASH_PAGE_LAYOUT flash_page_layout.c)
zephyr_library_sources_ifdef(CONFIG_USERSPACE flash_handlers.c)

View file

@ -20,12 +20,25 @@ menuconfig SOC_FLASH_NRF
if SOC_FLASH_NRF
choice
prompt "Nordic nRFx flash driver synchronization"
default SOC_FLASH_NRF_RADIO_SYNC if BT_CTLR
default SOC_FLASH_NRF_RADIO_SYNC_NONE
help
synchronization between flash memory driver and radio.
config SOC_FLASH_NRF_RADIO_SYNC
bool "Nordic nRFx flash driver synchronized with radio"
default y
depends on BT_CTLR
help
Enable synchronization between flash memory driver and radio.
Enable synchronization between flash memory driver and radio using
BLE LL controller ticker API.
config SOC_FLASH_NRF_RADIO_SYNC_NONE
bool "none"
help
disable synchronization between flash memory driver and radio.
endchoice
config SOC_FLASH_NRF_PARTIAL_ERASE
bool "Nordic nRFx flash driver partial erase"

View file

@ -16,6 +16,8 @@
#include <string.h>
#include <nrfx_nvmc.h>
#include "soc_flash_nrf.h"
#if DT_NODE_HAS_STATUS(DT_INST(0, nordic_nrf51_flash_controller), okay)
#define DT_DRV_COMPAT nordic_nrf51_flash_controller
#elif DT_NODE_HAS_STATUS(DT_INST(0, nordic_nrf52_flash_controller), okay)
@ -30,80 +32,21 @@
#define SOC_NV_FLASH_NODE DT_INST(0, soc_nv_flash)
#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
#include <sys/__assert.h>
#include <bluetooth/hci.h>
#include "controller/hal/ticker.h"
#include "controller/ticker/ticker.h"
#include "controller/include/ll.h"
#define FLASH_RADIO_ABORT_DELAY_US 1500
#define FLASH_RADIO_WORK_DELAY_US 200
#define FLASH_INTERVAL_ERASE (FLASH_RADIO_ABORT_DELAY_US + \
FLASH_RADIO_WORK_DELAY_US + \
FLASH_SLOT_ERASE)
#define FLASH_SLOT_WRITE (FLASH_INTERVAL_WRITE - \
FLASH_RADIO_ABORT_DELAY_US - \
FLASH_RADIO_WORK_DELAY_US)
#define FLASH_INTERVAL_WRITE 7500
#ifndef CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE
#define FLASH_SLOT_WRITE 7500
#if defined(CONFIG_SOC_FLASH_NRF_PARTIAL_ERASE)
/* The timeout is multiplied by 1.5 because switching tasks may take
* significant portion of time.
*/
#define FLASH_TIMEOUT_MS ((FLASH_PAGE_ERASE_MAX_TIME_US) * \
(FLASH_PAGE_MAX_CNT) / 1000 * 15 / 10)
#define FLASH_SLOT_ERASE (MAX(CONFIG_SOC_FLASH_NRF_PARTIAL_ERASE_MS * 1000, \
7500))
#else
#define FLASH_TIMEOUT_MS ((FLASH_PAGE_ERASE_MAX_TIME_US) * \
(FLASH_PAGE_MAX_CNT) / 1000)
#define FLASH_SLOT_ERASE FLASH_PAGE_ERASE_MAX_TIME_US
#endif /* CONFIG_SOC_FLASH_NRF_PARTIAL_ERASE */
#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */
#define FLASH_OP_DONE (0) /* 0 for compliance with the driver API. */
#define FLASH_OP_ONGOING (-1)
struct flash_context {
uint32_t data_addr; /* Address of data to write. */
uint32_t flash_addr; /* Address of flash to write or erase. */
uint32_t len; /* Size off data to write or erase [B]. */
#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
uint8_t enable_time_limit; /* execution limited to timeslot. */
uint32_t interval; /* timeslot interval. */
uint32_t slot; /* timeslot length. */
#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */
#if defined(CONFIG_SOC_FLASH_NRF_PARTIAL_ERASE)
uint32_t flash_addr_next;
#endif /* CONFIG_SOC_FLASH_NRF_PARTIAL_ERASE */
}; /*< Context type for f. @ref write_op @ref erase_op */
#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
typedef int (*flash_op_handler_t) (void *context);
struct flash_op_desc {
flash_op_handler_t handler;
struct flash_context *context; /* [in,out] */
int result;
};
/* semaphore for synchronization of flash operations */
static struct k_sem sem_sync;
static int write_op(void *context); /* instance of flash_op_handler_t */
static int write_in_timeslice(off_t addr, const void *data, size_t len);
static int erase_op(void *context); /* instance of flash_op_handler_t */
static int erase_in_timeslice(uint32_t addr, uint32_t size);
#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */
#endif /* !CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE */
static const struct flash_parameters flash_nrf_parameters = {
#if IS_ENABLED(CONFIG_SOC_FLASH_NRF_EMULATE_ONE_BYTE_WRITE_ACCESS)
@ -213,11 +156,11 @@ static int flash_nrf_write(const struct device *dev, off_t addr,
SYNC_LOCK();
#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
if (ticker_is_initialized(0)) {
#ifndef CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE
if (nrf_flash_sync_is_required()) {
ret = write_in_timeslice(addr, data, len);
} else
#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */
#endif /* !CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE */
{
ret = write(addr, data, len);
}
@ -256,11 +199,11 @@ static int flash_nrf_erase(const struct device *dev, off_t addr, size_t size)
SYNC_LOCK();
#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
if (ticker_is_initialized(0)) {
#ifndef CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE
if (nrf_flash_sync_is_required()) {
ret = erase_in_timeslice(addr, size);
} else
#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */
#endif /* !CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE */
{
ret = erase(addr, size);
}
@ -310,9 +253,9 @@ static int nrf_flash_init(const struct device *dev)
{
SYNC_INIT();
#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
k_sem_init(&sem_sync, 0, 1);
#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */
#ifndef CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE
nrf_flash_sync_init();
#endif /* !CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE */
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
dev_layout.pages_count = nrfx_nvmc_flash_page_count_get();
@ -326,147 +269,7 @@ DEVICE_AND_API_INIT(nrf_flash, DT_INST_LABEL(0), nrf_flash_init,
NULL, NULL, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
&flash_nrf_api);
#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
static inline int _ticker_stop(uint8_t inst_idx, uint8_t u_id, uint8_t tic_id)
{
int ret = ticker_stop(inst_idx, u_id, tic_id, NULL, NULL);
if (ret != TICKER_STATUS_SUCCESS &&
ret != TICKER_STATUS_BUSY) {
__ASSERT(0, "Failed to stop ticker.\n");
}
return ret;
}
static void time_slot_callback_work(uint32_t ticks_at_expire, uint32_t remainder,
uint16_t lazy, void *context)
{
struct flash_op_desc *op_desc;
uint8_t instance_index;
uint8_t ticker_id;
__ASSERT(ll_radio_state_is_idle(),
"Radio is on during flash operation.\n");
op_desc = context;
if (op_desc->handler(op_desc->context) == FLASH_OP_DONE) {
ll_timeslice_ticker_id_get(&instance_index, &ticker_id);
/* Stop the time slot ticker */
_ticker_stop(instance_index, 0, ticker_id);
((struct flash_op_desc *)context)->result = 0;
/* notify thread that data is available */
k_sem_give(&sem_sync);
}
}
static void time_slot_delay(uint32_t ticks_at_expire, uint32_t ticks_delay,
ticker_timeout_func callback, void *context)
{
uint8_t instance_index;
uint8_t ticker_id;
int err;
ll_timeslice_ticker_id_get(&instance_index, &ticker_id);
/* start a secondary one-shot ticker after ticks_delay,
* this will let any radio role to gracefully abort and release the
* Radio h/w.
*/
err = ticker_start(instance_index, /* Radio instance ticker */
0, /* user_id */
(ticker_id + 1), /* ticker_id */
ticks_at_expire, /* current tick */
ticks_delay, /* one-shot delayed timeout */
0, /* periodic timeout */
0, /* periodic remainder */
0, /* lazy, voluntary skips */
0,
callback, /* handler for executing radio abort or */
/* flash work */
context, /* the context for the flash operation */
NULL, /* no op callback */
NULL);
if (err != TICKER_STATUS_SUCCESS && err != TICKER_STATUS_BUSY) {
((struct flash_op_desc *)context)->result = -ECANCELED;
/* abort flash timeslots */
_ticker_stop(instance_index, 0, ticker_id);
/* notify thread that data is available */
k_sem_give(&sem_sync);
}
}
static void time_slot_callback_abort(uint32_t ticks_at_expire, uint32_t remainder,
uint16_t lazy, void *context)
{
ll_radio_state_abort();
time_slot_delay(ticks_at_expire,
HAL_TICKER_US_TO_TICKS(FLASH_RADIO_WORK_DELAY_US),
time_slot_callback_work,
context);
}
static void time_slot_callback_prepare(uint32_t ticks_at_expire, uint32_t remainder,
uint16_t lazy, void *context)
{
#if defined(CONFIG_BT_CTLR_LOW_LAT)
time_slot_callback_abort(ticks_at_expire, remainder, lazy, context);
#else /* !CONFIG_BT_CTLR_LOW_LAT */
time_slot_delay(ticks_at_expire,
HAL_TICKER_US_TO_TICKS(FLASH_RADIO_ABORT_DELAY_US),
time_slot_callback_abort,
context);
#endif /* CONFIG_BT_CTLR_LOW_LAT */
}
static int work_in_time_slice(struct flash_op_desc *p_flash_op_desc)
{
uint8_t instance_index;
uint8_t ticker_id;
int result;
uint32_t err;
struct flash_context *context = p_flash_op_desc->context;
ll_timeslice_ticker_id_get(&instance_index, &ticker_id);
err = ticker_start(instance_index,
3, /* user id for thread mode */
/* (MAYFLY_CALL_ID_PROGRAM) */
ticker_id, /* flash ticker id */
ticker_ticks_now_get(), /* current tick */
0, /* first int. immediately */
/* period */
HAL_TICKER_US_TO_TICKS(context->interval),
/* period remainder */
HAL_TICKER_REMAINDER(context->interval),
0, /* lazy, voluntary skips */
HAL_TICKER_US_TO_TICKS(context->slot),
time_slot_callback_prepare,
p_flash_op_desc,
NULL, /* no op callback */
NULL);
if (err != TICKER_STATUS_SUCCESS && err != TICKER_STATUS_BUSY) {
result = -ECANCELED;
} else if (k_sem_take(&sem_sync, K_MSEC(FLASH_TIMEOUT_MS)) != 0) {
/* Stop any scheduled jobs */
_ticker_stop(instance_index, 3, ticker_id);
/* wait for operation's complete overrun*/
result = -ETIMEDOUT;
} else {
result = p_flash_op_desc->result;
}
return result;
}
#ifndef CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE
static int erase_in_timeslice(uint32_t addr, uint32_t size)
{
@ -474,8 +277,6 @@ static int erase_in_timeslice(uint32_t addr, uint32_t size)
.flash_addr = addr,
.len = size,
.enable_time_limit = 1, /* enable time limit */
.interval = FLASH_INTERVAL_ERASE,
.slot = FLASH_SLOT_ERASE,
#if defined(CONFIG_SOC_FLASH_NRF_PARTIAL_ERASE)
.flash_addr_next = addr
#endif
@ -486,7 +287,8 @@ static int erase_in_timeslice(uint32_t addr, uint32_t size)
.context = &context
};
return work_in_time_slice(&flash_op_desc);
nrf_flash_sync_set_context(FLASH_SLOT_ERASE);
return nrf_flash_sync_exe(&flash_op_desc);
}
static int write_in_timeslice(off_t addr, const void *data, size_t len)
@ -495,9 +297,7 @@ static int write_in_timeslice(off_t addr, const void *data, size_t len)
.data_addr = (uint32_t) data,
.flash_addr = addr,
.len = len,
.enable_time_limit = 1, /* enable time limit */
.interval = FLASH_INTERVAL_WRITE,
.slot = FLASH_SLOT_WRITE
.enable_time_limit = 1 /* enable time limit */
};
struct flash_op_desc flash_op_desc = {
@ -505,25 +305,24 @@ static int write_in_timeslice(off_t addr, const void *data, size_t len)
.context = &context
};
return work_in_time_slice(&flash_op_desc);
nrf_flash_sync_set_context(FLASH_SLOT_WRITE);
return nrf_flash_sync_exe(&flash_op_desc);
}
#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */
#endif /* !CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE */
static int erase_op(void *context)
{
uint32_t pg_size = nrfx_nvmc_flash_page_size_get();
struct flash_context *e_ctx = context;
#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
uint32_t ticks_begin = 0U;
uint32_t ticks_diff;
#ifndef CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE
uint32_t i = 0U;
if (e_ctx->enable_time_limit) {
ticks_begin = ticker_ticks_now_get();
nrf_flash_sync_get_timestamp_begin();
}
#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */
#endif /* !CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE */
#ifdef CONFIG_SOC_FLASH_NRF_UICR
if (e_ctx->flash_addr == (off_t)NRF_UICR) {
@ -551,19 +350,16 @@ static int erase_op(void *context)
e_ctx->flash_addr += pg_size;
#endif /* CONFIG_SOC_FLASH_NRF_PARTIAL_ERASE */
#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
#ifndef CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE
i++;
if (e_ctx->enable_time_limit) {
ticks_diff =
ticker_ticks_diff_get(ticker_ticks_now_get(),
ticks_begin);
if (ticks_diff + ticks_diff/i >
HAL_TICKER_US_TO_TICKS(e_ctx->slot)) {
if (nrf_flash_sync_check_time_limit(i)) {
break;
}
}
#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */
#endif /* !CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE */
} while (e_ctx->len > 0);
@ -581,15 +377,13 @@ static int write_op(void *context)
{
struct flash_context *w_ctx = context;
#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
uint32_t ticks_begin = 0U;
uint32_t ticks_diff;
#ifndef CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE
uint32_t i = 1U;
if (w_ctx->enable_time_limit) {
ticks_begin = ticker_ticks_now_get();
nrf_flash_sync_get_timestamp_begin();
}
#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */
#endif /* !CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE */
#if IS_ENABLED(CONFIG_SOC_FLASH_NRF_EMULATE_ONE_BYTE_WRITE_ACCESS)
/* If not aligned, write unaligned beginning */
if (!is_aligned_32(w_ctx->flash_addr)) {
@ -605,18 +399,14 @@ static int write_op(void *context)
shift_write_context(count, w_ctx);
#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
#ifndef CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE
if (w_ctx->enable_time_limit) {
ticks_diff =
ticker_ticks_diff_get(ticker_ticks_now_get(),
ticks_begin);
if (ticks_diff * 2U >
HAL_TICKER_US_TO_TICKS(w_ctx->slot)) {
if (nrf_flash_sync_check_time_limit(1)) {
nvmc_wait_ready();
return FLASH_OP_ONGOING;
}
}
#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */
#endif /* !CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE */
}
#endif /* CONFIG_SOC_FLASH_NRF_EMULATE_ONE_BYTE_WRITE_ACCESS */
/* Write all the 4-byte aligned data */
@ -626,20 +416,16 @@ static int write_op(void *context)
shift_write_context(sizeof(uint32_t), w_ctx);
#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
#ifndef CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE
i++;
if (w_ctx->enable_time_limit) {
ticks_diff =
ticker_ticks_diff_get(ticker_ticks_now_get(),
ticks_begin);
if (ticks_diff + ticks_diff/i >
HAL_TICKER_US_TO_TICKS(w_ctx->slot)) {
if (nrf_flash_sync_check_time_limit(i)) {
nvmc_wait_ready();
return FLASH_OP_ONGOING;
}
}
#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */
#endif /* !CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE */
}
#if IS_ENABLED(CONFIG_SOC_FLASH_NRF_EMULATE_ONE_BYTE_WRITE_ACCESS)
/* Write remaining unaligned data */
@ -661,9 +447,9 @@ static int erase(uint32_t addr, uint32_t size)
struct flash_context context = {
.flash_addr = addr,
.len = size,
#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
#ifndef CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE
.enable_time_limit = 0, /* disable time limit */
#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */
#endif /* !CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE */
#if defined(CONFIG_SOC_FLASH_NRF_PARTIAL_ERASE)
.flash_addr_next = addr
#endif
@ -678,9 +464,9 @@ static int write(off_t addr, const void *data, size_t len)
.data_addr = (uint32_t) data,
.flash_addr = addr,
.len = len,
#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
#ifndef CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE
.enable_time_limit = 0 /* disable time limit */
#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */
#endif /* !CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE */
};
return write_op(&context);

View file

@ -16,18 +16,17 @@ struct flash_context {
uint32_t data_addr; /* Address of data to write. */
uint32_t flash_addr; /* Address of flash to write or erase. */
uint32_t len; /* Size of data to write or erase [B]. */
#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
#ifndef CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE
uint8_t enable_time_limit; /* set execution limited to the execution
* window.
*/
#endif /* CONFIG_SOC_FLASH_NRF_RADIO_SYNC */
#endif /* !CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE */
#if defined(CONFIG_SOC_FLASH_NRF_PARTIAL_ERASE)
uint32_t flash_addr_next;
#endif /* CONFIG_SOC_FLASH_NRF_PARTIAL_ERASE */
}; /*< Context type for f. @ref write_op @ref erase_op */
#if defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC)
#ifndef CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE
#if defined(CONFIG_SOC_FLASH_NRF_PARTIAL_ERASE)
/* The timeout is multiplied by 1.5 because switching tasks may take
@ -192,5 +191,5 @@ bool nrf_flash_sync_check_time_limit(uint32_t iteration);
* @}
*/
#endif /* defined(CONFIG_SOC_FLASH_NRF_RADIO_SYNC) */
#endif /* !CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE */
#endif /* !__SOC_FLASH_NRF_H__ */