diff --git a/drivers/flash/CMakeLists.txt b/drivers/flash/CMakeLists.txt index b72aa37a360..9c82932b524 100644 --- a/drivers/flash/CMakeLists.txt +++ b/drivers/flash/CMakeLists.txt @@ -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) diff --git a/drivers/flash/Kconfig b/drivers/flash/Kconfig index cfa34d3e1c4..8e18b2deef6 100644 --- a/drivers/flash/Kconfig +++ b/drivers/flash/Kconfig @@ -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" diff --git a/drivers/flash/Kconfig.nrf_mramc b/drivers/flash/Kconfig.nrf_mramc new file mode 100644 index 00000000000..1637baec896 --- /dev/null +++ b/drivers/flash/Kconfig.nrf_mramc @@ -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. diff --git a/drivers/flash/soc_flash_nrf_mramc.c b/drivers/flash/soc_flash_nrf_mramc.c new file mode 100644 index 00000000000..c08f8f32851 --- /dev/null +++ b/drivers/flash/soc_flash_nrf_mramc.c @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + + #include + + #include + #include + #include + #if defined(CONFIG_SOC_FLASH_NRF_MRAMC_FLUSH_CACHE) + #include + #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 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"); + +/** + * @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) +{ + 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, + }, + }; + + return ¶meters; +} + +#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) +{ + 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); diff --git a/dts/bindings/flash_controller/nordic,nrf-mramc.yaml b/dts/bindings/flash_controller/nordic,nrf-mramc.yaml new file mode 100644 index 00000000000..0afef7563b3 --- /dev/null +++ b/dts/bindings/flash_controller/nordic,nrf-mramc.yaml @@ -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 diff --git a/modules/hal_nordic/nrfx/CMakeLists.txt b/modules/hal_nordic/nrfx/CMakeLists.txt index 1d18d708d74..35c46e235f2 100644 --- a/modules/hal_nordic/nrfx/CMakeLists.txt +++ b/modules/hal_nordic/nrfx/CMakeLists.txt @@ -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) diff --git a/modules/hal_nordic/nrfx/Kconfig b/modules/hal_nordic/nrfx/Kconfig index 2b2b288cc34..d1456f4b069 100644 --- a/modules/hal_nordic/nrfx/Kconfig +++ b/modules/hal_nordic/nrfx/Kconfig @@ -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) diff --git a/modules/hal_nordic/nrfx/Kconfig.logging b/modules/hal_nordic/nrfx/Kconfig.logging index 2bf24721880..322660f40d1 100644 --- a/modules/hal_nordic/nrfx/Kconfig.logging +++ b/modules/hal_nordic/nrfx/Kconfig.logging @@ -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 diff --git a/modules/hal_nordic/nrfx/nrfx_kconfig.h b/modules/hal_nordic/nrfx/nrfx_kconfig.h index 191855b3ff0..5aa856f2299 100644 --- a/modules/hal_nordic/nrfx/nrfx_kconfig.h +++ b/modules/hal_nordic/nrfx/nrfx_kconfig.h @@ -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