Skip to content

Commit b0feb15

Browse files
committed
drivers: nrf_mram: handle non aligned write-block-size
When write-block-size is not aligned to the mram-word-size source addresses could be non aligned to the start of an mram word. To write correctly the data, a read-modify-write operation needs to be performed for data that is partially written in an mram word. Signed-off-by: Riadh Ghaddab <riadh.ghaddab@nordicsemi.no>
1 parent 17d6ce8 commit b0feb15

1 file changed

Lines changed: 127 additions & 43 deletions

File tree

drivers/flash/soc_flash_nrf_mram.c

Lines changed: 127 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ uint32_t nrf_mram_safe_read_word(uint32_t addr)
8484
barrier_isync_fence_full();
8585

8686
/* Read operation that may fault */
87-
memcpy(rbuf, (void *)(addr & ~MRAM_WORD_MASK), MRAM_WORD_SIZE);
87+
memcpy(rbuf, (void *)addr, MRAM_WORD_SIZE);
8888

8989
/* DSB ensures the load and any resulting fault status write complete */
9090
barrier_dsync_fence_full();
@@ -106,22 +106,9 @@ uint32_t nrf_mram_safe_read_word(uint32_t addr)
106106
return faulted; /* 0 = read succeeded, non-zero = bus fault occurred */
107107
}
108108

109-
static int nrf_mram_write_and_verify(uint32_t addr, const void *data, size_t len)
109+
static int nrf_mram_write_and_verify_word(uint32_t addr, const void *data, size_t len)
110110
{
111111
uint8_t retries = CONFIG_NRF_MRAM_MAX_RETRIES;
112-
#if (WRITE_BLOCK_SIZE & MRAM_WORD_MASK)
113-
uint8_t rbuf[MRAM_WORD_SIZE];
114-
115-
if (len % MRAM_WORD_SIZE) {
116-
/* For partial word writes, we need to read the existing data first to avoid
117-
* overwriting the unwritten bytes in the same word.
118-
*/
119-
memcpy(rbuf + len, (void *)(addr + len), MRAM_WORD_SIZE - len);
120-
memcpy(rbuf, data, len);
121-
data = rbuf;
122-
len = MRAM_WORD_SIZE;
123-
}
124-
#endif
125112

126113
while (retries--) {
127114
memcpy((void *)addr, data, len);
@@ -136,27 +123,27 @@ static int nrf_mram_write_and_verify(uint32_t addr, const void *data, size_t len
136123
return -EIO;
137124
}
138125

139-
static int nrf_mram_erase_and_verify(uint32_t addr, size_t len)
140-
{
141-
uint8_t retries = CONFIG_NRF_MRAM_MAX_RETRIES;
142126
#if (WRITE_BLOCK_SIZE & MRAM_WORD_MASK)
127+
static int nrf_mram_write_and_verify_partial(uint32_t addr, const void *data, size_t len)
128+
{
143129
uint8_t rbuf[MRAM_WORD_SIZE];
144130

145-
if (len % MRAM_WORD_SIZE) {
146-
/* For partial word writes, we need to read the existing data first to avoid
147-
* overwriting the unwritten bytes in the same word.
148-
*/
149-
memcpy(rbuf, (void *)(addr + len), MRAM_WORD_SIZE - len);
150-
}
131+
/* For partial word writes, we need to read the existing data first to avoid
132+
* overwriting the unwritten bytes in the same word.
133+
*/
134+
memcpy(rbuf, (void *)(addr & ~MRAM_WORD_MASK), MRAM_WORD_SIZE);
135+
memcpy(rbuf + (addr & MRAM_WORD_MASK), data, len);
136+
137+
return nrf_mram_write_and_verify_word(addr & ~MRAM_WORD_MASK, rbuf, MRAM_WORD_SIZE);
138+
}
151139
#endif
152140

141+
static int nrf_mram_erase_and_verify_word(uint32_t addr, size_t len)
142+
{
143+
uint8_t retries = CONFIG_NRF_MRAM_MAX_RETRIES;
144+
153145
while (retries--) {
154146
memset((void *)addr, ERASE_VALUE, len);
155-
#if (WRITE_BLOCK_SIZE & MRAM_WORD_MASK)
156-
if (len % MRAM_WORD_SIZE) {
157-
memcpy((void *)addr, rbuf, MRAM_WORD_SIZE - len);
158-
}
159-
#endif
160147
if (!nrf_mram_safe_read_word(addr)) {
161148
return 0;
162149
}
@@ -168,8 +155,23 @@ static int nrf_mram_erase_and_verify(uint32_t addr, size_t len)
168155
return -EIO;
169156
}
170157

158+
#if (WRITE_BLOCK_SIZE & MRAM_WORD_MASK)
159+
static int nrf_mram_erase_and_verify_partial(uint32_t addr, size_t len)
160+
{
161+
uint8_t rbuf[MRAM_WORD_SIZE];
162+
163+
/* For partial word erases, we need to read the existing data first to avoid
164+
* overwriting the unwritten bytes in the same word.
165+
*/
166+
memcpy(rbuf, (void *)(addr & ~MRAM_WORD_MASK), MRAM_WORD_SIZE);
167+
memset(rbuf + (addr & MRAM_WORD_MASK), ERASE_VALUE, len);
168+
169+
return nrf_mram_write_and_verify_word(addr & ~MRAM_WORD_MASK, rbuf, MRAM_WORD_SIZE);
170+
}
171+
#endif
172+
171173
#ifdef CONFIG_MRAM_LATENCY
172-
static inline bool nrf_mram_ready(uint32_t addr, uint8_t ironside_se_ver)
174+
static inline uint32_t nrf_mram_ready(uint32_t addr, uint8_t ironside_se_ver)
173175
{
174176
if (ironside_se_ver < IRONSIDE_SE_SUPPORT_READY_VER) {
175177
return true;
@@ -233,6 +235,50 @@ static int nrf_mram_read(const struct device *dev, off_t offset, void *data, siz
233235
return 0;
234236
}
235237

238+
#if (WRITE_BLOCK_SIZE & MRAM_WORD_MASK)
239+
/**
240+
* @brief Structure to hold divided byte lengths for MRAM operations.
241+
*/
242+
struct nrf_mram_length_breakdown {
243+
size_t first_word_bytes; /**< Number of bytes in the first partial MRAM word */
244+
size_t aligned_bytes; /**< Number of bytes in fully aligned MRAM words */
245+
size_t last_word_bytes; /**< Number of bytes in the last partial MRAM word */
246+
};
247+
248+
/**
249+
* @brief Divide operation length into first word, aligned words, and last word components.
250+
*
251+
* Calculates how many bytes belong to:
252+
* - The first partial MRAM word (if offset is not word-aligned)
253+
* - Fully aligned MRAM words in the middle
254+
* - The last partial MRAM word (if total length doesn't end on word boundary)
255+
*
256+
* @param offset Relative offset into memory.
257+
* @param len Number of bytes for the intended operation.
258+
*
259+
* @return Structure containing the divided byte lengths.
260+
*/
261+
static inline struct nrf_mram_length_breakdown nrf_mram_divide_length(off_t offset, size_t len)
262+
{
263+
struct nrf_mram_length_breakdown len_break = {0};
264+
size_t first_word_bytes = MRAM_WORD_SIZE - (offset & MRAM_WORD_MASK);
265+
size_t remaining = 0;
266+
267+
if (len > first_word_bytes) {
268+
len_break.first_word_bytes = first_word_bytes;
269+
remaining = len - first_word_bytes;
270+
len_break.aligned_bytes = remaining & ~MRAM_WORD_MASK;
271+
len_break.last_word_bytes = remaining & MRAM_WORD_MASK;
272+
} else {
273+
len_break.first_word_bytes = len;
274+
len_break.aligned_bytes = 0;
275+
len_break.last_word_bytes = 0;
276+
}
277+
278+
return len_break;
279+
}
280+
#endif
281+
236282
static int nrf_mram_write(const struct device *dev, off_t offset, const void *data, size_t len)
237283
{
238284
struct nrf_mram_data_t *nrf_mram_data = dev->data;
@@ -255,25 +301,44 @@ static int nrf_mram_write(const struct device *dev, off_t offset, const void *da
255301
#endif
256302
}
257303

304+
#if (WRITE_BLOCK_SIZE & MRAM_WORD_MASK)
305+
struct nrf_mram_length_breakdown len_break = nrf_mram_divide_length(offset, len);
306+
307+
/* First, write the partial bytes from the first word */
308+
if (len_break.first_word_bytes) {
309+
while (!nrf_mram_ready(addr, ironside_se_ver)) {
310+
/* Wait until MRAM controller is ready */
311+
}
312+
ret = nrf_mram_write_and_verify_partial(addr, data, len_break.first_word_bytes);
313+
if (ret) {
314+
goto unlock;
315+
}
316+
/* align to the next word boundary */
317+
len = len_break.aligned_bytes;
318+
data = (const void *)((uintptr_t)data + len_break.first_word_bytes);
319+
addr += len_break.first_word_bytes;
320+
}
321+
#endif
258322
for (uint32_t i = 0; i < (len / MRAM_WORD_SIZE); i++) {
259323
while (!nrf_mram_ready(addr + (i * MRAM_WORD_SIZE), ironside_se_ver)) {
260324
/* Wait until MRAM controller is ready */
261325
}
262-
ret = nrf_mram_write_and_verify(addr + (i * MRAM_WORD_SIZE),
263-
(void *)((uintptr_t)data + (i * MRAM_WORD_SIZE)),
264-
MRAM_WORD_SIZE);
326+
ret = nrf_mram_write_and_verify_word(
327+
addr + (i * MRAM_WORD_SIZE),
328+
(void *)((uintptr_t)data + (i * MRAM_WORD_SIZE)), MRAM_WORD_SIZE);
265329
if (ret) {
266330
goto unlock;
267331
}
268332
}
269333
#if (WRITE_BLOCK_SIZE & MRAM_WORD_MASK)
270-
if (len % MRAM_WORD_SIZE) {
271-
while (!nrf_mram_ready(addr + (len & ~MRAM_WORD_MASK), ironside_se_ver)) {
334+
if (len_break.last_word_bytes) {
335+
while (!nrf_mram_ready(addr + len_break.aligned_bytes, ironside_se_ver)) {
272336
/* Wait until MRAM controller is ready */
273337
}
274-
ret = nrf_mram_write_and_verify(addr + (len & ~MRAM_WORD_MASK),
275-
(void *)((uintptr_t)data + (len & ~MRAM_WORD_MASK)),
276-
len & MRAM_WORD_MASK);
338+
ret = nrf_mram_write_and_verify_partial(
339+
addr + len_break.aligned_bytes,
340+
(void *)((uintptr_t)data + len_break.aligned_bytes),
341+
len_break.last_word_bytes);
277342
if (ret) {
278343
goto unlock;
279344
}
@@ -312,23 +377,42 @@ static int nrf_mram_erase(const struct device *dev, off_t offset, size_t size)
312377
mram_no_latency_sync_request();
313378
#endif
314379
}
380+
381+
#if (WRITE_BLOCK_SIZE & MRAM_WORD_MASK)
382+
struct nrf_mram_length_breakdown len_break = nrf_mram_divide_length(offset, size);
383+
384+
/* First, erase the partial bytes from the first word */
385+
if (len_break.first_word_bytes) {
386+
while (!nrf_mram_ready(addr, ironside_se_ver)) {
387+
/* Wait until MRAM controller is ready */
388+
}
389+
ret = nrf_mram_erase_and_verify_partial(addr, len_break.first_word_bytes);
390+
if (ret) {
391+
goto unlock;
392+
}
393+
/* align to the next word boundary */
394+
size = len_break.aligned_bytes;
395+
addr += len_break.first_word_bytes;
396+
}
397+
#endif
398+
315399
for (uint32_t i = 0; i < (size / MRAM_WORD_SIZE); i++) {
316400
while (!nrf_mram_ready(addr + (i * MRAM_WORD_SIZE), ironside_se_ver)) {
317401
/* Wait until MRAM controller is ready */
318402
}
319-
ret = nrf_mram_erase_and_verify(addr + (i * MRAM_WORD_SIZE), MRAM_WORD_SIZE);
403+
ret = nrf_mram_erase_and_verify_word(addr + (i * MRAM_WORD_SIZE), MRAM_WORD_SIZE);
320404
if (ret) {
321405
goto unlock;
322406
}
323407
}
324408

325409
#if (WRITE_BLOCK_SIZE & MRAM_WORD_MASK)
326-
if (size % MRAM_WORD_SIZE) {
327-
while (!nrf_mram_ready(addr + (size & ~MRAM_WORD_MASK), ironside_se_ver)) {
410+
if (len_break.last_word_bytes) {
411+
while (!nrf_mram_ready(addr + len_break.aligned_bytes, ironside_se_ver)) {
328412
/* Wait until MRAM controller is ready */
329413
}
330-
ret = nrf_mram_erase_and_verify(addr + (size & ~MRAM_WORD_MASK),
331-
size & MRAM_WORD_MASK);
414+
ret = nrf_mram_erase_and_verify_partial(addr + len_break.aligned_bytes,
415+
len_break.last_word_bytes);
332416
if (ret) {
333417
goto unlock;
334418
}

0 commit comments

Comments
 (0)