Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

drivers: Add flash driver for RRAM #1501

Merged
merged 7 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions drivers/flash/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,4 @@ zephyr_library_sources_ifdef(CONFIG_FLASH_SHELL flash_shell.c)
zephyr_library_sources_ifdef(CONFIG_FLASH_JESD216 jesd216.c)
zephyr_library_sources_ifdef(CONFIG_FLASH_INFINEON_CAT1 flash_ifx_cat1.c)
zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_NUMAKER soc_flash_numaker.c)
zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_NRF_RRAM soc_flash_nrf_rram.c)
2 changes: 2 additions & 0 deletions drivers/flash/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -160,4 +160,6 @@ source "drivers/flash/Kconfig.andes"

source "drivers/flash/Kconfig.ambiq"

source "drivers/flash/Kconfig.nrf_rram"

endif # FLASH
69 changes: 69 additions & 0 deletions drivers/flash/Kconfig.nrf_rram
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#
# Copyright (c) 2024 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
#

menuconfig SOC_FLASH_NRF_RRAM
bool "Nordic Semiconductor flash driver for nRF RRAM"
default y
depends on DT_HAS_NORDIC_RRAM_CONTROLLER_ENABLED
select FLASH_HAS_DRIVER_ENABLED
select FLASH_HAS_PAGE_LAYOUT
select FLASH_NRF_FORCE_ALT
select MPU_ALLOW_FLASH_WRITE if ARM_MPU
help
Enables Nordic Semiconductor flash driver for nRF RRAM controllers.
The entire contiguous block of RRAM gets logically divided into pages,
but partial erase is always supported.

if SOC_FLASH_NRF_RRAM

config NRF_RRAM_WRITE_BUFFER_SIZE
int "Internal write-buffer size"
default 32 if !SOC_FLASH_NRF_RADIO_SYNC_NONE
default 0
range 0 32
help
Number of 128-bit words.
Maximum buffer size can be configured to the value of 32 (128-bit words).
Set 0 to disable write-buffer.

config NRF_RRAM_READYNEXT_TIMEOUT_VALUE
int "Preload timeout value for waiting for a next write in RRAMC clock cycles"
default 128
range 0 4095
help
Set 0 to disable ready next timeout counter.

choice SOC_FLASH_NRF_RADIO_SYNC_CHOICE
prompt "Nordic nRFx flash driver synchronization"
default SOC_FLASH_NRF_RADIO_SYNC_TICKER if BT_LL_SW_SPLIT
default SOC_FLASH_NRF_RADIO_SYNC_NONE
help
synchronization between flash memory driver and radio.

config SOC_FLASH_NRF_RADIO_SYNC_TICKER
bool "Nordic nRFx flash driver synchronized with radio"
depends on BT_LL_SW_SPLIT
help
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_TIMEOUT_MULTIPLIER
int "Multiplier for flash operation timeouts [x0.1]"
depends on !SOC_FLASH_NRF_RADIO_SYNC_NONE
default 1
help
This is a multiplier that will be divided by 10 that is applied
to the flash erase and write operations timeout. The base for
the multiplication would allow erasing all nRF flash pages in
blocking mode.

endif # SOC_FLASH_NRF_RRAM
313 changes: 313 additions & 0 deletions drivers/flash/soc_flash_nrf_rram.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,313 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

#define DT_DRV_COMPAT nordic_rram_controller

#include <string.h>
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/flash.h>
#include <zephyr/logging/log.h>
#include <zephyr/irq.h>
#include <zephyr/sys/barrier.h>
#include <hal/nrf_rramc.h>

#include <zephyr/../../drivers/flash/soc_flash_nrf.h>

LOG_MODULE_REGISTER(flash_nrf_rram, CONFIG_FLASH_LOG_LEVEL);

#define RRAM DT_INST(0, soc_nv_flash)

#define RRAM_START DT_REG_ADDR(RRAM)
#define RRAM_SIZE DT_REG_SIZE(RRAM)

#define PAGE_SIZE DT_PROP(RRAM, erase_block_size)
#define PAGE_COUNT ((RRAM_SIZE) / (PAGE_SIZE))

#define WRITE_BLOCK_SIZE_FROM_DT DT_PROP(RRAM, write_block_size)
#define ERASE_VALUE 0xFF

#ifdef CONFIG_MULTITHREADING
static struct k_sem sem_lock;
#define SYNC_INIT() k_sem_init(&sem_lock, 1, 1)
#define SYNC_LOCK() k_sem_take(&sem_lock, K_FOREVER)
#define SYNC_UNLOCK() k_sem_give(&sem_lock)
#else
#define SYNC_INIT()
#define SYNC_LOCK()
#define SYNC_UNLOCK()
#endif /* CONFIG_MULTITHREADING */

#if CONFIG_NRF_RRAM_WRITE_BUFFER_SIZE > 0
#define WRITE_BUFFER_ENABLE 1
#define WRITE_BUFFER_SIZE CONFIG_NRF_RRAM_WRITE_BUFFER_SIZE
#define WRITE_LINE_SIZE 16 /* In bytes, one line is 128 bits. */
#define WRITE_BUFFER_MAX_SIZE (WRITE_BUFFER_SIZE * WRITE_LINE_SIZE)
BUILD_ASSERT((PAGE_SIZE % (WRITE_LINE_SIZE) == 0), "erase-block-size must be a multiple of 16");
BUILD_ASSERT((WRITE_BLOCK_SIZE_FROM_DT % (WRITE_LINE_SIZE) == 0),
"if NRF_RRAM_WRITE_BUFFER_SIZE > 0, then write-block-size must be a multiple of 16");
#else
#define WRITE_BUFFER_ENABLE 0
#define WRITE_BUFFER_SIZE 0
#define WRITE_LINE_SIZE WRITE_BLOCK_SIZE_FROM_DT
#define WRITE_BUFFER_MAX_SIZE 16 /* In bytes, one line is 128 bits. */
BUILD_ASSERT((PAGE_SIZE % (WRITE_LINE_SIZE) == 0),
"erase-block-size must be a multiple of write-block-size");
#endif

#ifndef CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE

#if (WRITE_BUFFER_SIZE < 2)
#define FLASH_SLOT_WRITE 500
#elif (WRITE_BUFFER_SIZE < 4)
#define FLASH_SLOT_WRITE 1000
#elif (WRITE_BUFFER_SIZE < 9)
#define FLASH_SLOT_WRITE 2000
#elif (WRITE_BUFFER_SIZE < 17)
#define FLASH_SLOT_WRITE 4000
#else
#define FLASH_SLOT_WRITE 8000 /* longest write takes 7107 us */
#endif

static int write_op(void *context); /* instance of flash_op_handler_t */
static int write_synchronously(off_t addr, const void *data, size_t len);

#endif /* !CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE */

static inline bool is_within_bounds(off_t addr, size_t len, off_t boundary_start,
size_t boundary_size)
{
return (addr >= boundary_start && (addr < (boundary_start + boundary_size)) &&
(len <= (boundary_start + boundary_size - addr)));
}

#if WRITE_BUFFER_ENABLE
static void commit_changes(size_t len)
{
if (nrf_rramc_empty_buffer_check(NRF_RRAMC)) {
/* The internal write-buffer has been committed to RRAM and is now empty. */
return;
}

if ((len % (WRITE_BUFFER_MAX_SIZE)) == 0) {
/* Our last operation was buffer size-aligned, so we're done. */
return;
}

nrf_rramc_task_trigger(NRF_RRAMC, NRF_RRAMC_TASK_COMMIT_WRITEBUF);

barrier_dmem_fence_full();
}
#endif

static void rram_write(off_t addr, const void *data, size_t len)
{
nrf_rramc_config_t config = {.mode_write = true, .write_buff_size = WRITE_BUFFER_SIZE};

nrf_rramc_config_set(NRF_RRAMC, &config);

if (data) {
memcpy((void *)addr, data, len);
} else {
memset((void *)addr, ERASE_VALUE, len);
}

barrier_dmem_fence_full(); /* Barrier following our last write. */

#if WRITE_BUFFER_ENABLE
commit_changes(len);
#endif

config.mode_write = false;
nrf_rramc_config_set(NRF_RRAMC, &config);
}

#ifndef CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE
static void shift_write_context(uint32_t shift, struct flash_context *w_ctx)
{
w_ctx->flash_addr += shift;

/* NULL data_addr => erase emulation request*/
if (w_ctx->data_addr) {
w_ctx->data_addr += shift;
}

w_ctx->len -= shift;
}

static int write_op(void *context)
{
struct flash_context *w_ctx = context;
size_t len;

uint32_t i = 0U;

if (w_ctx->enable_time_limit) {
nrf_flash_sync_get_timestamp_begin();
}

while (w_ctx->len > 0) {
len = (w_ctx->len > WRITE_BUFFER_MAX_SIZE) ? WRITE_BUFFER_MAX_SIZE : w_ctx->len;

rram_write(w_ctx->flash_addr, (const void *)w_ctx->data_addr, len);

shift_write_context(len, w_ctx);

if (w_ctx->len > 0) {
i++;

if (w_ctx->enable_time_limit) {
if (nrf_flash_sync_check_time_limit(i)) {
return FLASH_OP_ONGOING;
}
}
}
}

return FLASH_OP_DONE;
}

static int write_synchronously(off_t addr, const void *data, size_t len)
{
struct flash_context context = {
.data_addr = (uint32_t)data,
.flash_addr = addr,
.len = len,
.enable_time_limit = 1 /* enable time limit */
};

struct flash_op_desc flash_op_desc = {.handler = write_op, .context = &context};

nrf_flash_sync_set_context(FLASH_SLOT_WRITE);
return nrf_flash_sync_exe(&flash_op_desc);
}

#endif /* !CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE */

static int nrf_write(off_t addr, const void *data, size_t len)
{
int ret = 0;

if (!is_within_bounds(addr, len, 0, RRAM_SIZE)) {
return -EINVAL;
}
addr += RRAM_START;

if (!len) {
return 0;
}

LOG_DBG("Write: %p:%zu", (void *)addr, len);

SYNC_LOCK();

#ifndef CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE
if (nrf_flash_sync_is_required()) {
ret = write_synchronously(addr, data, len);
} else
#endif /* !CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE */
{
rram_write(addr, data, len);
}

SYNC_UNLOCK();

return ret;
}

static int nrf_rram_read(const struct device *dev, off_t addr, void *data, size_t len)
{
ARG_UNUSED(dev);

if (!is_within_bounds(addr, len, 0, RRAM_SIZE)) {
return -EINVAL;
}
addr += RRAM_START;

memcpy(data, (void *)addr, len);

return 0;
}

static int nrf_rram_write(const struct device *dev, off_t addr, const void *data, size_t len)
{
ARG_UNUSED(dev);

if (data == NULL) {
return -EINVAL;
}

return nrf_write(addr, data, len);
}

static int nrf_rram_erase(const struct device *dev, off_t addr, size_t len)
{
ARG_UNUSED(dev);

return nrf_write(addr, NULL, len);
}

static const struct flash_parameters *nrf_rram_get_parameters(const struct device *dev)
{
ARG_UNUSED(dev);

static const struct flash_parameters parameters = {
.write_block_size = WRITE_LINE_SIZE,
.erase_value = ERASE_VALUE,
};

return &parameters;
}

#if defined(CONFIG_FLASH_PAGE_LAYOUT)
static void nrf_rram_page_layout(const struct device *dev, const struct flash_pages_layout **layout,
size_t *layout_size)
{
ARG_UNUSED(dev);

static const struct flash_pages_layout pages_layout = {
.pages_count = PAGE_COUNT,
.pages_size = PAGE_SIZE,
};

*layout = &pages_layout;
*layout_size = 1;
}
#endif

static const struct flash_driver_api nrf_rram_api = {
.read = nrf_rram_read,
.write = nrf_rram_write,
.erase = nrf_rram_erase,
.get_parameters = nrf_rram_get_parameters,
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
.page_layout = nrf_rram_page_layout,
#endif
};

static int nrf_rram_init(const struct device *dev)
{
ARG_UNUSED(dev);

SYNC_INIT();

#ifndef CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE
nrf_flash_sync_init();
#endif /* !CONFIG_SOC_FLASH_NRF_RADIO_SYNC_NONE */

#if CONFIG_NRF_RRAM_READYNEXT_TIMEOUT_VALUE > 0
nrf_rramc_ready_next_timeout_t params = {
.value = CONFIG_NRF_RRAM_READYNEXT_TIMEOUT_VALUE,
.enable = true,
};
nrf_rramc_ready_next_timeout_set(NRF_RRAMC, &params);
#endif

return 0;
}

DEVICE_DT_INST_DEFINE(0, nrf_rram_init, NULL, NULL, NULL, POST_KERNEL, CONFIG_FLASH_INIT_PRIORITY,
&nrf_rram_api);
Loading
Loading