Skip to content

Commit ad03431

Browse files
57300jfischer-no
authored andcommitted
[nrf fromtree] drivers: flash: Add flash driver for MRAM
Basic driver utilizing the flash API for NVM operations on the nRF54H20. Signed-off-by: Grzegorz Swiderski <[email protected]> (cherry picked from commit f91323e)
1 parent c92db91 commit ad03431

File tree

4 files changed

+199
-0
lines changed

4 files changed

+199
-0
lines changed

drivers/flash/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,4 @@ zephyr_library_sources_ifdef(CONFIG_FLASH_JESD216 jesd216.c)
119119
zephyr_library_sources_ifdef(CONFIG_FLASH_INFINEON_CAT1 flash_ifx_cat1.c)
120120
zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_NUMAKER soc_flash_numaker.c)
121121
zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_NRF_RRAM soc_flash_nrf_rram.c)
122+
zephyr_library_sources_ifdef(CONFIG_SOC_FLASH_NRF_MRAM soc_flash_nrf_mram.c)

drivers/flash/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,4 +162,6 @@ source "drivers/flash/Kconfig.ambiq"
162162

163163
source "drivers/flash/Kconfig.nrf_rram"
164164

165+
source "drivers/flash/Kconfig.nrf_mram"
166+
165167
endif # FLASH

drivers/flash/Kconfig.nrf_mram

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#
2+
# Copyright (c) 2024 Nordic Semiconductor ASA
3+
#
4+
# SPDX-License-Identifier: Apache-2.0
5+
#
6+
7+
config SOC_FLASH_NRF_MRAM
8+
bool "Nordic Semiconductor flash driver for MRAM"
9+
default y
10+
depends on DT_HAS_NORDIC_MRAM_ENABLED
11+
select FLASH_HAS_DRIVER_ENABLED
12+
select FLASH_HAS_PAGE_LAYOUT
13+
imply MPU_ALLOW_FLASH_WRITE if ARM_MPU
14+
help
15+
Enables Nordic Semiconductor flash driver for MRAM in direct write mode.
16+
17+
Note that MRAM words are auto-erased when written to, but writing to a
18+
pre-erased area is faster. Hence, the erase API is not required, but
19+
it can be used to amortize write performance for some use cases.

drivers/flash/soc_flash_nrf_mram.c

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
/*
2+
* Copyright (c) 2024 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <string.h>
8+
9+
#include <zephyr/drivers/flash.h>
10+
#include <zephyr/logging/log.h>
11+
#include <zephyr/sys/barrier.h>
12+
13+
LOG_MODULE_REGISTER(flash_nrf_mram, CONFIG_FLASH_LOG_LEVEL);
14+
15+
#define DT_DRV_COMPAT nordic_mram
16+
17+
#define MRAM_START DT_INST_REG_ADDR(0)
18+
#define MRAM_SIZE DT_INST_REG_SIZE(0)
19+
20+
#define MRAM_WORD_SIZE 16
21+
#define MRAM_WORD_MASK 0xf
22+
23+
#define WRITE_BLOCK_SIZE DT_INST_PROP_OR(0, write_block_size, MRAM_WORD_SIZE)
24+
#define ERASE_BLOCK_SIZE DT_INST_PROP_OR(0, erase_block_size, WRITE_BLOCK_SIZE)
25+
26+
#define ERASE_VALUE 0xff
27+
28+
BUILD_ASSERT(MRAM_START > 0, "nordic,mram: start address expected to be non-zero");
29+
BUILD_ASSERT((ERASE_BLOCK_SIZE % WRITE_BLOCK_SIZE) == 0,
30+
"erase-block-size expected to be a multiple of write-block-size");
31+
32+
/**
33+
* @param[in,out] offset Relative offset into memory, from the driver API.
34+
* @param[in] len Number of bytes for the intended operation.
35+
* @param[in] must_align Require MRAM word alignment, if applicable.
36+
*
37+
* @return Absolute address in MRAM, or NULL if @p offset or @p len are not
38+
* within bounds or appropriately aligned.
39+
*/
40+
static uintptr_t validate_and_map_addr(off_t offset, size_t len, bool must_align)
41+
{
42+
if (unlikely(offset < 0 || offset >= MRAM_SIZE || len > MRAM_SIZE - offset)) {
43+
LOG_ERR("invalid offset: %ld:%zu", offset, len);
44+
return 0;
45+
}
46+
47+
const uintptr_t addr = MRAM_START + offset;
48+
49+
if (WRITE_BLOCK_SIZE > 1 && must_align &&
50+
unlikely((addr % WRITE_BLOCK_SIZE) != 0 || (len % WRITE_BLOCK_SIZE) != 0)) {
51+
LOG_ERR("invalid alignment: %p:%zu", (void *)addr, len);
52+
return 0;
53+
}
54+
55+
return addr;
56+
}
57+
58+
/**
59+
* @param[in] addr_end Last modified MRAM address (not inclusive).
60+
*/
61+
static void commit_changes(uintptr_t addr_end)
62+
{
63+
/* Barrier following our last write. */
64+
barrier_dmem_fence_full();
65+
66+
if ((WRITE_BLOCK_SIZE & MRAM_WORD_MASK) == 0 || (addr_end & MRAM_WORD_MASK) == 0) {
67+
/* Our last operation was MRAM word-aligned, so we're done.
68+
* Note: if WRITE_BLOCK_SIZE is a multiple of MRAM_WORD_SIZE,
69+
* then this was already checked in validate_and_map_addr().
70+
*/
71+
return;
72+
}
73+
74+
/* Get the most significant byte (MSB) of the last MRAM word we were modifying.
75+
* Writing to this byte makes the MRAM controller commit other pending writes to that word.
76+
*/
77+
addr_end |= MRAM_WORD_MASK;
78+
79+
/* Issue a dummy write, since we didn't have anything to write here.
80+
* Doing this lets us finalize our changes before we exit the driver API.
81+
*/
82+
sys_write8(sys_read8(addr_end), addr_end);
83+
}
84+
85+
static int nrf_mram_read(const struct device *dev, off_t offset, void *data, size_t len)
86+
{
87+
ARG_UNUSED(dev);
88+
89+
const uintptr_t addr = validate_and_map_addr(offset, len, false);
90+
91+
if (!addr) {
92+
return -EINVAL;
93+
}
94+
95+
LOG_DBG("read: %p:%zu", (void *)addr, len);
96+
97+
memcpy(data, (void *)addr, len);
98+
99+
return 0;
100+
}
101+
102+
static int nrf_mram_write(const struct device *dev, off_t offset, const void *data, size_t len)
103+
{
104+
ARG_UNUSED(dev);
105+
106+
const uintptr_t addr = validate_and_map_addr(offset, len, true);
107+
108+
if (!addr) {
109+
return -EINVAL;
110+
}
111+
112+
LOG_DBG("write: %p:%zu", (void *)addr, len);
113+
114+
memcpy((void *)addr, data, len);
115+
commit_changes(addr + len);
116+
117+
return 0;
118+
}
119+
120+
static int nrf_mram_erase(const struct device *dev, off_t offset, size_t size)
121+
{
122+
ARG_UNUSED(dev);
123+
124+
const uintptr_t addr = validate_and_map_addr(offset, size, true);
125+
126+
if (!addr) {
127+
return -EINVAL;
128+
}
129+
130+
LOG_DBG("erase: %p:%zu", (void *)addr, size);
131+
132+
memset((void *)addr, ERASE_VALUE, size);
133+
commit_changes(addr + size);
134+
135+
return 0;
136+
}
137+
138+
static const struct flash_parameters *nrf_mram_get_parameters(const struct device *dev)
139+
{
140+
ARG_UNUSED(dev);
141+
142+
static const struct flash_parameters parameters = {
143+
.write_block_size = WRITE_BLOCK_SIZE,
144+
.erase_value = ERASE_VALUE,
145+
};
146+
147+
return &parameters;
148+
}
149+
150+
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
151+
static void nrf_mram_page_layout(const struct device *dev, const struct flash_pages_layout **layout,
152+
size_t *layout_size)
153+
{
154+
ARG_UNUSED(dev);
155+
156+
static const struct flash_pages_layout pages_layout = {
157+
.pages_count = (MRAM_SIZE) / (ERASE_BLOCK_SIZE),
158+
.pages_size = ERASE_BLOCK_SIZE,
159+
};
160+
161+
*layout = &pages_layout;
162+
*layout_size = 1;
163+
}
164+
#endif
165+
166+
static const struct flash_driver_api nrf_mram_api = {
167+
.read = nrf_mram_read,
168+
.write = nrf_mram_write,
169+
.erase = nrf_mram_erase,
170+
.get_parameters = nrf_mram_get_parameters,
171+
#if defined(CONFIG_FLASH_PAGE_LAYOUT)
172+
.page_layout = nrf_mram_page_layout,
173+
#endif
174+
};
175+
176+
DEVICE_DT_INST_DEFINE(0, NULL, NULL, NULL, NULL, POST_KERNEL, CONFIG_FLASH_INIT_PRIORITY,
177+
&nrf_mram_api);

0 commit comments

Comments
 (0)