Skip to content

[nrf fromtree] drivers: flash: nordic: Introduce nrf_mramc driver #3072

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

Merged
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 @@ -53,6 +53,7 @@ zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_MAX32 flash_max32.c)
zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_MCUX soc_flash_mcux.c)
zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_NRF soc_flash_nrf.c)
zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_NRF_MRAM soc_flash_nrf_mram.c)
zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_NRF_MRAMC soc_flash_nrf_mramc.c)
zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_NRF_RRAM soc_flash_nrf_rram.c)
zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_NUMAKER soc_flash_numaker.c)
zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_NUMAKER_RMC soc_flash_numaker_rmc.c)
Expand Down
1 change: 1 addition & 0 deletions drivers/flash/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ source "drivers/flash/Kconfig.nordic_qspi_nor"
source "drivers/flash/Kconfig.npcx_fiu"
source "drivers/flash/Kconfig.nrf"
source "drivers/flash/Kconfig.nrf_mram"
source "drivers/flash/Kconfig.nrf_mramc"
source "drivers/flash/Kconfig.nrf_rram"
source "drivers/flash/Kconfig.numaker"
source "drivers/flash/Kconfig.numaker_rmc"
Expand Down
26 changes: 26 additions & 0 deletions drivers/flash/Kconfig.nrf_mramc
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#
# Copyright (c) 2025 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
#

config SOC_FLASH_NRF_MRAMC
bool "Nordic Semiconductor flash driver for MRAM using MRAM Controller"
default y
depends on DT_HAS_NORDIC_NRF_MRAMC_ENABLED
select NRFX_MRAMC
select FLASH_HAS_DRIVER_ENABLED
select FLASH_HAS_PAGE_LAYOUT
select FLASH_HAS_NO_EXPLICIT_ERASE
select SOC_FLASH_NRF_MRAMC_FLUSH_CACHE
imply MPU_ALLOW_FLASH_WRITE if ARM_MPU
help
Enables Nordic Semiconductor flash driver for MRAMC in direct write mode.

config SOC_FLASH_NRF_MRAMC_FLUSH_CACHE
bool "Invalidate MRAM cache after erase operations"
default y
depends on SOC_FLASH_NRF_MRAMC
help
Enables invalidation of the MRAM cache after write and erase operations to
ensure data read consistency.
221 changes: 221 additions & 0 deletions drivers/flash/soc_flash_nrf_mramc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
/*
* Copyright (c) 2025 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <string.h>

#include <zephyr/drivers/flash.h>
#include <zephyr/logging/log.h>
#include <nrfx_mramc.h>
#if defined(CONFIG_SOC_FLASH_NRF_MRAMC_FLUSH_CACHE)
#include <hal/nrf_cache.h>
#endif

LOG_MODULE_REGISTER(flash_nrf_mramc, CONFIG_FLASH_LOG_LEVEL);

#define DT_DRV_COMPAT nordic_nrf_mramc

#define _ADD_SIZE(node_id) + DT_REG_SIZE(node_id)
#define _WBS(node_id) DT_PROP(node_id, write_block_size),
#define _EBS(node_id) DT_PROP(node_id, erase_block_size),

Check notice on line 23 in drivers/flash/soc_flash_nrf_mramc.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/flash/soc_flash_nrf_mramc.c:23 - #include <string.h> - - #include <zephyr/drivers/flash.h> - #include <zephyr/logging/log.h> - #include <nrfx_mramc.h> - #if defined(CONFIG_SOC_FLASH_NRF_MRAMC_FLUSH_CACHE) - #include <hal/nrf_cache.h> - #endif +#include <string.h> + +#include <zephyr/drivers/flash.h> +#include <zephyr/logging/log.h> +#include <nrfx_mramc.h> +#if defined(CONFIG_SOC_FLASH_NRF_MRAMC_FLUSH_CACHE) +#include <hal/nrf_cache.h> +#endif LOG_MODULE_REGISTER(flash_nrf_mramc, CONFIG_FLASH_LOG_LEVEL); #define DT_DRV_COMPAT nordic_nrf_mramc -#define _ADD_SIZE(node_id) + DT_REG_SIZE(node_id) -#define _WBS(node_id) DT_PROP(node_id, write_block_size), -#define _EBS(node_id) DT_PROP(node_id, erase_block_size), +#define _ADD_SIZE(node_id) +DT_REG_SIZE(node_id) +#define _WBS(node_id) DT_PROP(node_id, write_block_size), +#define _EBS(node_id) DT_PROP(node_id, erase_block_size),

Check notice on line 23 in drivers/flash/soc_flash_nrf_mramc.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/flash/soc_flash_nrf_mramc.c:23 - #include <string.h> - - #include <zephyr/drivers/flash.h> - #include <zephyr/logging/log.h> - #include <nrfx_mramc.h> - #if defined(CONFIG_SOC_FLASH_NRF_MRAMC_FLUSH_CACHE) - #include <hal/nrf_cache.h> - #endif +#include <string.h> + +#include <zephyr/drivers/flash.h> +#include <zephyr/logging/log.h> +#include <nrfx_mramc.h> +#if defined(CONFIG_SOC_FLASH_NRF_MRAMC_FLUSH_CACHE) +#include <hal/nrf_cache.h> +#endif LOG_MODULE_REGISTER(flash_nrf_mramc, CONFIG_FLASH_LOG_LEVEL); #define DT_DRV_COMPAT nordic_nrf_mramc -#define _ADD_SIZE(node_id) + DT_REG_SIZE(node_id) -#define _WBS(node_id) DT_PROP(node_id, write_block_size), -#define _EBS(node_id) DT_PROP(node_id, erase_block_size), +#define _ADD_SIZE(node_id) +DT_REG_SIZE(node_id) +#define _WBS(node_id) DT_PROP(node_id, write_block_size), +#define _EBS(node_id) DT_PROP(node_id, erase_block_size),
#define MRAM_SIZE (0 DT_INST_FOREACH_CHILD_STATUS_OKAY(0, _ADD_SIZE))
#define ERASE_VALUE (uint8_t)NRFY_MRAMC_WORD_AFTER_ERASED

/* Use DT_FOREACH_CHILD_STATUS_OKAY of mramc with _FIRST() helper
* macro to get the write block size and erase block size from
* the first child node.
*/
#define WBS_LIST DT_INST_FOREACH_CHILD_STATUS_OKAY(0, _WBS)
#define EBS_LIST DT_INST_FOREACH_CHILD_STATUS_OKAY(0, _EBS)

#define _FIRST_HELPER(first, ...) first
#define _FIRST(...) _FIRST_HELPER(__VA_ARGS__)

#define WRITE_BLOCK_SIZE _FIRST(WBS_LIST)
#define ERASE_BLOCK_SIZE _FIRST(EBS_LIST)

BUILD_ASSERT((ERASE_BLOCK_SIZE % WRITE_BLOCK_SIZE) == 0,
"erase-block-size expected to be a multiple of write-block-size");

Check notice on line 42 in drivers/flash/soc_flash_nrf_mramc.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/flash/soc_flash_nrf_mramc.c:42 -#define WBS_LIST DT_INST_FOREACH_CHILD_STATUS_OKAY(0, _WBS) -#define EBS_LIST DT_INST_FOREACH_CHILD_STATUS_OKAY(0, _EBS) - -#define _FIRST_HELPER(first, ...) first -#define _FIRST(...) _FIRST_HELPER(__VA_ARGS__) - -#define WRITE_BLOCK_SIZE _FIRST(WBS_LIST) -#define ERASE_BLOCK_SIZE _FIRST(EBS_LIST) +#define WBS_LIST DT_INST_FOREACH_CHILD_STATUS_OKAY(0, _WBS) +#define EBS_LIST DT_INST_FOREACH_CHILD_STATUS_OKAY(0, _EBS) + +#define _FIRST_HELPER(first, ...) first +#define _FIRST(...) _FIRST_HELPER(__VA_ARGS__) + +#define WRITE_BLOCK_SIZE _FIRST(WBS_LIST) +#define ERASE_BLOCK_SIZE _FIRST(EBS_LIST) BUILD_ASSERT((ERASE_BLOCK_SIZE % WRITE_BLOCK_SIZE) == 0, - "erase-block-size expected to be a multiple of write-block-size"); + "erase-block-size expected to be a multiple of write-block-size");

Check notice on line 42 in drivers/flash/soc_flash_nrf_mramc.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/flash/soc_flash_nrf_mramc.c:42 -#define WBS_LIST DT_INST_FOREACH_CHILD_STATUS_OKAY(0, _WBS) -#define EBS_LIST DT_INST_FOREACH_CHILD_STATUS_OKAY(0, _EBS) - -#define _FIRST_HELPER(first, ...) first -#define _FIRST(...) _FIRST_HELPER(__VA_ARGS__) - -#define WRITE_BLOCK_SIZE _FIRST(WBS_LIST) -#define ERASE_BLOCK_SIZE _FIRST(EBS_LIST) +#define WBS_LIST DT_INST_FOREACH_CHILD_STATUS_OKAY(0, _WBS) +#define EBS_LIST DT_INST_FOREACH_CHILD_STATUS_OKAY(0, _EBS) + +#define _FIRST_HELPER(first, ...) first +#define _FIRST(...) _FIRST_HELPER(__VA_ARGS__) + +#define WRITE_BLOCK_SIZE _FIRST(WBS_LIST) +#define ERASE_BLOCK_SIZE _FIRST(EBS_LIST) BUILD_ASSERT((ERASE_BLOCK_SIZE % WRITE_BLOCK_SIZE) == 0, - "erase-block-size expected to be a multiple of write-block-size"); + "erase-block-size expected to be a multiple of write-block-size");
/**
* @param[in] addr Address of mram memory.
* @param[in] len Number of bytes for the intended operation.
* @param[in] must_align Require MRAM word alignment, if applicable.
*
* @return true if the address and length are valid, false otherwise.
*/
static bool validate_action(uint32_t addr, size_t len, bool must_align)
{
if (!nrfx_mramc_valid_address_check(addr, true)) {
LOG_ERR("Invalid address: %x", addr);
return false;
}

if (!nrfx_mramc_fits_memory_check(addr, true, len)) {
LOG_ERR("Address %x with length %zu exceeds MRAM size", addr, len);
return false;
}

if (must_align && !(nrfx_is_word_aligned((void const *)addr))) {
LOG_ERR("Address %x is not word aligned", addr);
return false;
}

return true;
}

static int nrf_mramc_read(const struct device *dev, off_t offset, void *data, size_t len)
{
ARG_UNUSED(dev);
uint32_t addr = NRFX_MRAMC_MAP_TO_ADDR(offset);

if (data == NULL) {
LOG_ERR("Data pointer is NULL");
return -EINVAL;
}

/* Validate addr and length in the range */
if (!validate_action(addr, len, false)) {
return -EINVAL;
}

LOG_DBG("read: %x:%zu", addr, len);

/* Buffer read number of bytes and store in data pointer.
*/
nrfx_mramc_buffer_read(data, addr, len);
return 0;
}

static int nrf_mramc_write(const struct device *dev, off_t offset,
const void *data, size_t len)
{

Check notice on line 95 in drivers/flash/soc_flash_nrf_mramc.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/flash/soc_flash_nrf_mramc.c:95 -static int nrf_mramc_write(const struct device *dev, off_t offset, - const void *data, size_t len) +static int nrf_mramc_write(const struct device *dev, off_t offset, const void *data, size_t len)

Check notice on line 95 in drivers/flash/soc_flash_nrf_mramc.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/flash/soc_flash_nrf_mramc.c:95 -static int nrf_mramc_write(const struct device *dev, off_t offset, - const void *data, size_t len) +static int nrf_mramc_write(const struct device *dev, off_t offset, const void *data, size_t len)
ARG_UNUSED(dev);
uint32_t addr = NRFX_MRAMC_MAP_TO_ADDR(offset);

if (data == NULL) {
LOG_ERR("Data pointer is NULL");
return -EINVAL;
}

/* Validate addr and length in the range */
if (!validate_action(addr, len, true)) {
return -EINVAL;
}

LOG_DBG("write: %x:%zu", addr, len);

/* Words write function takes second argument as number of write blocks
* and not number of bytes
*/
nrfx_mramc_words_write(addr, data, len / WRITE_BLOCK_SIZE);
#if defined(CONFIG_SOC_FLASH_NRF_MRAMC_FLUSH_CACHE)
if (nrf_cache_enable_check(NRF_ICACHE)) {
while (nrf_cache_busy_check(NRF_ICACHE)) {
}
nrf_cache_invalidate(NRF_ICACHE);
}
#endif
return 0;
}

static int nrf_mramc_erase(const struct device *dev, off_t offset, size_t size)
{
ARG_UNUSED(dev);
uint32_t addr = NRFX_MRAMC_MAP_TO_ADDR(offset);

/* Validate addr and length in the range */
if (size == 0) {
LOG_DBG("No data to erase");
return 0;
}

if (!validate_action(addr, size, true)) {
return -EINVAL;
}

LOG_DBG("erase: %p:%zu", (void *)addr, size);

/* Erase function takes second argument as number of write blocks
* and not number of bytes
*/
nrfx_mramc_area_erase(addr, size / WRITE_BLOCK_SIZE);
#if defined(CONFIG_SOC_FLASH_NRF_MRAMC_FLUSH_CACHE)
if (nrf_cache_enable_check(NRF_ICACHE)) {
while (nrf_cache_busy_check(NRF_ICACHE)) {
}
nrf_cache_invalidate(NRF_ICACHE);
}
#endif
return 0;
}

static int nrf_mramc_get_size(const struct device *dev, uint64_t *size)
{
ARG_UNUSED(dev);
*size = nrfx_mramc_memory_size_get();
return 0;
}

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

static const struct flash_parameters parameters = {
.write_block_size = WRITE_BLOCK_SIZE,
.erase_value = ERASE_VALUE,
.caps = {
.no_explicit_erase = true,
},
};

Check notice on line 173 in drivers/flash/soc_flash_nrf_mramc.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/flash/soc_flash_nrf_mramc.c:173 - .caps = { - .no_explicit_erase = true, - }, + .caps = + { + .no_explicit_erase = true, + },

Check notice on line 173 in drivers/flash/soc_flash_nrf_mramc.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/flash/soc_flash_nrf_mramc.c:173 - .caps = { - .no_explicit_erase = true, - }, + .caps = + { + .no_explicit_erase = true, + },

return &parameters;
}

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

Check notice on line 181 in drivers/flash/soc_flash_nrf_mramc.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/flash/soc_flash_nrf_mramc.c:181 - const struct flash_pages_layout **layout, size_t *layout_size) + const struct flash_pages_layout **layout, size_t *layout_size)

Check notice on line 181 in drivers/flash/soc_flash_nrf_mramc.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/flash/soc_flash_nrf_mramc.c:181 - const struct flash_pages_layout **layout, size_t *layout_size) + const struct flash_pages_layout **layout, size_t *layout_size)
ARG_UNUSED(dev);

static const struct flash_pages_layout pages_layout = {
.pages_count = MRAM_SIZE / (ERASE_BLOCK_SIZE),
.pages_size = ERASE_BLOCK_SIZE,
};

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

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

nrfx_mramc_config_t config = NRFX_MRAMC_DEFAULT_CONFIG();
nrfx_err_t err = nrfx_mramc_init(&config, NULL);

if (err != NRFX_SUCCESS) {
LOG_ERR("Failed to initialize MRAMC: %d", err);
return -EIO;
}
LOG_DBG("MRAMC initialized successfully");
return 0;
}

static DEVICE_API(flash, nrf_mram_api) = {
.read = nrf_mramc_read,
.write = nrf_mramc_write,
.erase = nrf_mramc_erase,
.get_size = nrf_mramc_get_size,
.get_parameters = nrf_mramc_get_parameters,
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
.page_layout = nrf_mramc_page_layout,
#endif
};

DEVICE_DT_INST_DEFINE(0, mramc_sys_init, NULL, NULL, NULL, POST_KERNEL, CONFIG_FLASH_INIT_PRIORITY,
&nrf_mram_api);
16 changes: 16 additions & 0 deletions dts/bindings/flash_controller/nordic,nrf-mramc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#
# Copyright (c) 2025 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
#

description: |
Nordic MRAMC (Magnetoresistive Random Access Memory Controller)

The Magnetoresistive random access memory controller (MRAMC) is used for writing
the internal MRAM memory, the secure information configuration registers (SICR),
and the user information configuration registers (UICR).

compatible: "nordic,nrf-mramc"

include: base.yaml
1 change: 1 addition & 0 deletions modules/hal_nordic/nrfx/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ zephyr_library_sources_ifdef(CONFIG_NRFX_GRTC ${SRC_DIR}/nrfx_grtc.c)
zephyr_library_sources_ifdef(CONFIG_NRFX_I2S ${SRC_DIR}/nrfx_i2s.c)
zephyr_library_sources_ifdef(CONFIG_NRFX_IPC ${SRC_DIR}/nrfx_ipc.c)
zephyr_library_sources_ifdef(CONFIG_NRFX_LPCOMP ${SRC_DIR}/nrfx_lpcomp.c)
zephyr_library_sources_ifdef(CONFIG_NRFX_MRAMC ${SRC_DIR}/nrfx_mramc.c)
zephyr_library_sources_ifdef(CONFIG_NRFX_NFCT ${SRC_DIR}/nrfx_nfct.c)
zephyr_library_sources_ifdef(CONFIG_NRFX_NVMC ${SRC_DIR}/nrfx_nvmc.c)
zephyr_library_sources_ifdef(CONFIG_NRFX_PDM ${SRC_DIR}/nrfx_pdm.c)
Expand Down
4 changes: 4 additions & 0 deletions modules/hal_nordic/nrfx/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,10 @@ config NRFX_LPCOMP
bool "LPCOMP driver"
depends on $(dt_nodelabel_exists,comp) && !SOC_NRF52810 && !SOC_NRF52811 && !SOC_NRF52820

config NRFX_MRAMC
bool "MRAMC driver"
depends on $(dt_has_compat,$(DT_COMPAT_NORDIC_NRF_MRAMC))

config NRFX_NFCT
bool "NFCT driver"
depends on $(dt_nodelabel_exists,nfct)
Expand Down
4 changes: 4 additions & 0 deletions modules/hal_nordic/nrfx/Kconfig.logging
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ config NRFX_LPCOMP_LOG
bool "LPCOMP driver logging"
depends on NRFX_LPCOMP

config NRFX_MRAMC_LOG
bool "MRAMC driver logging"
depends on NRFX_MRAMC

config NRFX_NFCT_LOG
bool "NFCT driver logging"
depends on NRFX_NFCT
Expand Down
7 changes: 7 additions & 0 deletions modules/hal_nordic/nrfx/nrfx_kconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,13 @@
#define NRFX_LPCOMP_CONFIG_LOG_ENABLED 1
#endif

#ifdef CONFIG_NRFX_MRAMC
#define NRFX_MRAMC_ENABLED 1
#endif
#ifdef CONFIG_NRFX_MRAMC_LOG
#define NRFX_MRAMC_CONFIG_LOG_ENABLED 1
#endif

#ifdef CONFIG_NRFX_NFCT
#define NRFX_NFCT_ENABLED 1
#endif
Expand Down
Loading