1 /*
2 * Copyright (c) 2019 - 2025, Nordic Semiconductor ASA
3 * All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice, this
11 * list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the copyright holder nor the names of its
18 * contributors may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 #include <nrfx.h>
35
36 #if NRFX_CHECK(NRFX_NVMC_ENABLED)
37
38 #include <nrfx_nvmc.h>
39
40 /**
41 * Value representing the number of bytes in a word.
42 *
43 * It is used in loops iterating over bytes contained in a word
44 * or in word-alignment checks.
45 */
46 #define NVMC_BYTES_IN_WORD 4
47
48 /** Value representing non-volatile memory (NVM) base address. */
49 #if defined(NRF5340_XXAA_NETWORK)
50 #define NVMC_FLASH_BASE_ADDRESS 0x01000000uL
51 #else
52 #define NVMC_FLASH_BASE_ADDRESS 0
53 #endif
54
55 /**
56 * Value representing non-volatile memory (NVM) page count.
57 *
58 * This symbol is needed to determine NVM page count for chips that cannot
59 * always access FICR for this information.
60 */
61 #if defined(NRF5340_XXAA_APPLICATION) || defined(NRF9120_XXAA) || defined(NRF9160_XXAA)
62 #define NVMC_FLASH_PAGE_COUNT 256
63 #elif defined(NRF5340_XXAA_NETWORK)
64 #define NVMC_FLASH_PAGE_COUNT 128
65 #endif
66
67 /**
68 * Value representing non-volatile memory (NVM) page size in bytes.
69 *
70 * This symbol is needed to determine NVM page size for chips that cannot
71 * always access FICR for this information.
72 */
73 #if defined(NRF5340_XXAA_APPLICATION) || defined(NRF9120_XXAA) || defined(NRF9160_XXAA)
74 #define NVMC_FLASH_PAGE_SIZE 0x1000 ///< 4 kB
75 #elif defined(NRF5340_XXAA_NETWORK)
76 #define NVMC_FLASH_PAGE_SIZE 0x800 ///< 2 kB
77 #endif
78
79 #if NRF_NVMC_HAS_PARTIAL_ERASE
80 /**
81 * Value representing the page erase time.
82 *
83 * This value is used to determine whether the partial erase is still in progress.
84 */
85 #if defined(NRF52805_XXAA) || defined(NRF52810_XXAA) || \
86 defined(NRF52811_XXAA) || defined(NRF52840_XXAA)
87 #define NVMC_PAGE_ERASE_DURATION_MS 85
88 #elif defined(NRF52820_XXAA) || defined(NRF52833_XXAA) || \
89 defined(NRF5340_XXAA_APPLICATION) || defined(NRF5340_XXAA_NETWORK) || \
90 defined(NRF9120_XXAA) || defined(NRF9160_XXAA)
91 #define NVMC_PAGE_ERASE_DURATION_MS 87
92 #else
93 #error "Page partial erase present but could not determine its total duration for given SoC"
94 #endif
95
96 /**
97 * Value representing the invalid page partial erase address.
98 *
99 * This value is used for representing a NULL pointer for
100 * partial erase, as that address 0 can be a valid
101 * memory address in flash.
102 */
103 #define NVMC_PARTIAL_ERASE_INVALID_ADDR 0xFFFFFFFF
104
105 /** Internal counter for page partial erase. */
106 static uint32_t m_partial_erase_time_elapsed;
107
108 /** Partial erase page address. */
109 static uint32_t m_partial_erase_page_addr = NVMC_PARTIAL_ERASE_INVALID_ADDR;
110
111 #endif // NRF_NVMC_HAS_PARTIAL_ERASE
112
flash_page_size_get(void)113 static uint32_t flash_page_size_get(void)
114 {
115 uint32_t flash_page_size = 0;
116
117 #if defined(NRF51) || defined(NRF52_SERIES)
118 flash_page_size = nrf_ficr_codepagesize_get(NRF_FICR);
119 #elif defined(NVMC_FLASH_PAGE_SIZE)
120 flash_page_size = NVMC_FLASH_PAGE_SIZE;
121 #else
122 #error "Cannot determine flash page size for a given SoC."
123 #endif
124
125 return flash_page_size;
126 }
127
flash_page_count_get(void)128 static uint32_t flash_page_count_get(void)
129 {
130 uint32_t page_count = 0;
131
132 #if defined(NRF51) || defined(NRF52_SERIES)
133 page_count = nrf_ficr_codesize_get(NRF_FICR);
134 #elif defined(NVMC_FLASH_PAGE_COUNT)
135 page_count = NVMC_FLASH_PAGE_COUNT;
136 #else
137 #error "Cannot determine flash page count for a given SoC."
138 #endif
139
140 return page_count;
141 }
142
flash_total_size_get(void)143 static uint32_t flash_total_size_get(void)
144 {
145 return flash_page_size_get() * flash_page_count_get();
146 }
147
is_page_aligned_check(uint32_t addr)148 static bool is_page_aligned_check(uint32_t addr)
149 {
150 /* If the modulo operation returns '0', then the address is aligned. */
151 return !(addr % flash_page_size_get());
152 }
153
is_halfword_aligned(uint32_t addr)154 __STATIC_INLINE bool is_halfword_aligned(uint32_t addr)
155 {
156 return ((addr & 0x1u) == 0u);
157 }
158
is_valid_address(uint32_t addr,bool uicr_allowed)159 __STATIC_INLINE bool is_valid_address(uint32_t addr, bool uicr_allowed)
160 {
161 if ((addr - NVMC_FLASH_BASE_ADDRESS) < flash_total_size_get())
162 {
163 return true;
164 }
165 #if !defined(NRF_TRUSTZONE_NONSECURE)
166 if (uicr_allowed &&
167 (addr - (uint32_t)NRF_UICR) < sizeof(NRF_UICR_Type))
168 {
169 return true;
170 }
171 #else
172 (void)uicr_allowed;
173 #endif
174
175 return false;
176 }
177
partial_word_create(uint32_t addr,uint8_t const * bytes,uint32_t bytes_count)178 static uint32_t partial_word_create(uint32_t addr, uint8_t const * bytes, uint32_t bytes_count)
179 {
180 uint32_t value32;
181 uint32_t byte_shift;
182
183 byte_shift = addr % NVMC_BYTES_IN_WORD;
184
185 NRFX_ASSERT(bytes_count <= (NVMC_BYTES_IN_WORD - byte_shift));
186
187 value32 = 0xFFFFFFFF;
188 for (uint32_t i = 0; i < bytes_count; i++)
189 {
190 ((uint8_t *)&value32)[byte_shift] = bytes[i];
191 byte_shift++;
192 }
193
194 return value32;
195 }
196
nvmc_readonly_mode_set(void)197 static void nvmc_readonly_mode_set(void)
198 {
199 /*
200 * For secure code, the access mode needs to be set for both secure and
201 * non-secure regions.
202 */
203 #if NRF_NVMC_HAS_NON_SECURE_OPERATIONS
204 nrf_nvmc_nonsecure_mode_set(NRF_NVMC, NRF_NVMC_NS_MODE_READONLY);
205 #endif
206 #if !defined(NRF_TRUSTZONE_NONSECURE)
207 nrf_nvmc_mode_set(NRF_NVMC, NRF_NVMC_MODE_READONLY);
208 #endif
209 }
210
nvmc_write_mode_set(void)211 static void nvmc_write_mode_set(void)
212 {
213 /*
214 * For secure code, the access mode needs to be set for both secure and
215 * non-secure regions.
216 */
217 #if NRF_NVMC_HAS_NON_SECURE_OPERATIONS
218 nrf_nvmc_nonsecure_mode_set(NRF_NVMC, NRF_NVMC_NS_MODE_WRITE);
219 #endif
220 #if !defined(NRF_TRUSTZONE_NONSECURE)
221 nrf_nvmc_mode_set(NRF_NVMC, NRF_NVMC_MODE_WRITE);
222 #endif
223 }
224
nvmc_erase_mode_set(void)225 static void nvmc_erase_mode_set(void)
226 {
227 /*
228 * For secure code, the access mode needs to be set for both secure and
229 * non-secure regions.
230 */
231 #if NRF_NVMC_HAS_NON_SECURE_OPERATIONS
232 nrf_nvmc_nonsecure_mode_set(NRF_NVMC, NRF_NVMC_NS_MODE_ERASE);
233 #endif
234 #if !defined(NRF_TRUSTZONE_NONSECURE)
235 nrf_nvmc_mode_set(NRF_NVMC, NRF_NVMC_MODE_ERASE);
236 #endif
237 }
238
nvmc_word_write(uint32_t addr,uint32_t value)239 static void nvmc_word_write(uint32_t addr, uint32_t value)
240 {
241 #if defined(NRF91_SERIES)
242 while (!nrf_nvmc_write_ready_check(NRF_NVMC))
243 {}
244 #else
245 while (!nrf_nvmc_ready_check(NRF_NVMC))
246 {}
247 #endif
248
249 nrf_nvmc_word_write(addr, value);
250 __DMB();
251 }
252
nvmc_words_write(uint32_t addr,void const * src,uint32_t num_words)253 static void nvmc_words_write(uint32_t addr, void const * src, uint32_t num_words)
254 {
255 for (uint32_t i = 0; i < num_words; i++)
256 {
257 nvmc_word_write(addr + (NVMC_BYTES_IN_WORD * i), ((uint32_t const *)src)[i]);
258 }
259 }
260
nrfx_nvmc_page_erase(uint32_t addr)261 nrfx_err_t nrfx_nvmc_page_erase(uint32_t addr)
262 {
263 NRFX_ASSERT(is_valid_address(addr, false));
264
265 if (!is_page_aligned_check(addr))
266 {
267 return NRFX_ERROR_INVALID_ADDR;
268 }
269
270 nvmc_erase_mode_set();
271 nrf_nvmc_page_erase_start(NRF_NVMC, addr);
272 while (!nrf_nvmc_ready_check(NRF_NVMC))
273 {}
274 nvmc_readonly_mode_set();
275
276 return NRFX_SUCCESS;
277 }
278
nrfx_nvmc_uicr_erase(void)279 nrfx_err_t nrfx_nvmc_uicr_erase(void)
280 {
281 #if NRF_NVMC_HAS_UICR_ERASE
282 nvmc_erase_mode_set();
283 nrf_nvmc_uicr_erase_start(NRF_NVMC);
284 while (!nrf_nvmc_ready_check(NRF_NVMC))
285 {}
286 nvmc_readonly_mode_set();
287 return NRFX_SUCCESS;
288 #else
289 return NRFX_ERROR_NOT_SUPPORTED;
290 #endif
291 }
292
nrfx_nvmc_all_erase(void)293 void nrfx_nvmc_all_erase(void)
294 {
295 nvmc_erase_mode_set();
296 nrf_nvmc_erase_all_start(NRF_NVMC);
297 while (!nrf_nvmc_ready_check(NRF_NVMC))
298 {}
299 nvmc_readonly_mode_set();
300 }
301
302 #if NRF_NVMC_HAS_PARTIAL_ERASE
nrfx_nvmc_page_partial_erase_init(uint32_t addr,uint32_t duration_ms)303 nrfx_err_t nrfx_nvmc_page_partial_erase_init(uint32_t addr, uint32_t duration_ms)
304 {
305 NRFX_ASSERT(is_valid_address(addr, false));
306
307 if (!is_page_aligned_check(addr))
308 {
309 return NRFX_ERROR_INVALID_ADDR;
310 }
311
312 m_partial_erase_time_elapsed = 0;
313 m_partial_erase_page_addr = addr;
314 nrf_nvmc_partial_erase_duration_set(NRF_NVMC, duration_ms);
315
316 return NRFX_SUCCESS;
317 }
318
nrfx_nvmc_page_partial_erase_continue(void)319 bool nrfx_nvmc_page_partial_erase_continue(void)
320 {
321 NRFX_ASSERT(m_partial_erase_page_addr != NVMC_PARTIAL_ERASE_INVALID_ADDR);
322
323 uint32_t duration_ms = nrf_nvmc_partial_erase_duration_get(NRF_NVMC);
324
325 #if defined(NVMC_CONFIG_WEN_PEen)
326 nrf_nvmc_mode_set(NRF_NVMC, NRF_NVMC_MODE_PARTIAL_ERASE);
327 #else
328 nrf_nvmc_mode_set(NRF_NVMC, NRF_NVMC_MODE_ERASE);
329 #endif
330
331 nrf_nvmc_page_partial_erase_start(NRF_NVMC, m_partial_erase_page_addr);
332 while (!nrf_nvmc_ready_check(NRF_NVMC))
333 {}
334 nvmc_readonly_mode_set();
335
336 m_partial_erase_time_elapsed += duration_ms;
337 if (m_partial_erase_time_elapsed < NVMC_PAGE_ERASE_DURATION_MS)
338 {
339 return false;
340 }
341 else
342 {
343 m_partial_erase_page_addr = NVMC_PARTIAL_ERASE_INVALID_ADDR;
344 return true;
345 }
346 }
347 #endif // NRF_NVMC_HAS_PARTIAL_ERASE
348
nrfx_nvmc_byte_writable_check(uint32_t addr,uint8_t val_to_check)349 bool nrfx_nvmc_byte_writable_check(uint32_t addr, uint8_t val_to_check)
350 {
351 NRFX_ASSERT(is_valid_address(addr, true));
352
353 uint8_t val_on_addr = nrf_nvmc_byte_read(addr);
354 return (val_to_check & val_on_addr) == val_to_check;
355 }
356
nrfx_nvmc_halfword_writable_check(uint32_t addr,uint16_t val_to_check)357 bool nrfx_nvmc_halfword_writable_check(uint32_t addr, uint16_t val_to_check)
358 {
359 NRFX_ASSERT(is_valid_address(addr, true));
360 NRFX_ASSERT(is_halfword_aligned(addr));
361
362 uint16_t val_on_addr;
363
364 if ((addr - NVMC_FLASH_BASE_ADDRESS) < flash_total_size_get())
365 {
366 val_on_addr = nrf_nvmc_halfword_read(addr);
367 }
368 else
369 {
370 val_on_addr = nrfx_nvmc_otp_halfword_read(addr);
371 }
372 return (val_to_check & val_on_addr) == val_to_check;
373 }
374
nrfx_nvmc_word_writable_check(uint32_t addr,uint32_t val_to_check)375 bool nrfx_nvmc_word_writable_check(uint32_t addr, uint32_t val_to_check)
376 {
377 NRFX_ASSERT(is_valid_address(addr, true));
378 NRFX_ASSERT(nrfx_is_word_aligned((void const *)addr));
379
380 uint32_t val_on_addr = nrf_nvmc_word_read(addr);
381 return (val_to_check & val_on_addr) == val_to_check;
382 }
383
nrfx_nvmc_byte_write(uint32_t addr,uint8_t value)384 void nrfx_nvmc_byte_write(uint32_t addr, uint8_t value)
385 {
386 NRFX_ASSERT(is_valid_address(addr, true));
387
388 uint32_t aligned_addr = addr & ~(0x03UL);
389
390 nrfx_nvmc_word_write(aligned_addr, partial_word_create(addr, &value, 1));
391 }
392
nrfx_nvmc_halfword_write(uint32_t addr,uint16_t value)393 void nrfx_nvmc_halfword_write(uint32_t addr, uint16_t value)
394 {
395 NRFX_ASSERT(is_valid_address(addr, true));
396 NRFX_ASSERT(is_halfword_aligned(addr));
397
398 uint32_t aligned_addr = addr & ~(0x03UL);
399
400 nrfx_nvmc_word_write(aligned_addr, partial_word_create(addr, (const uint8_t *)&value, 2));
401 }
402
nrfx_nvmc_word_write(uint32_t addr,uint32_t value)403 void nrfx_nvmc_word_write(uint32_t addr, uint32_t value)
404 {
405 NRFX_ASSERT(is_valid_address(addr, true));
406 NRFX_ASSERT(nrfx_is_word_aligned((void const *)addr));
407
408 nvmc_write_mode_set();
409
410 nvmc_word_write(addr, value);
411
412 nvmc_readonly_mode_set();
413 }
414
nrfx_nvmc_bytes_write(uint32_t addr,void const * src,uint32_t num_bytes)415 void nrfx_nvmc_bytes_write(uint32_t addr, void const * src, uint32_t num_bytes)
416 {
417 NRFX_ASSERT(is_valid_address(addr, true));
418
419 nvmc_write_mode_set();
420
421 uint8_t const * bytes_src = (uint8_t const *)src;
422
423 uint32_t unaligned_bytes = addr % NVMC_BYTES_IN_WORD;
424 if (unaligned_bytes != 0)
425 {
426 uint32_t leading_bytes = NVMC_BYTES_IN_WORD - unaligned_bytes;
427 if (leading_bytes > num_bytes)
428 {
429 leading_bytes = num_bytes;
430 }
431
432 nvmc_word_write(addr - unaligned_bytes,
433 partial_word_create(addr, bytes_src, leading_bytes));
434 num_bytes -= leading_bytes;
435 addr += leading_bytes;
436 bytes_src += leading_bytes;
437 }
438
439 #if defined(__CORTEX_M) && (__CORTEX_M == 0U)
440 if (!nrfx_is_word_aligned((void const *)bytes_src))
441 {
442 /* Cortex-M0 allows only word-aligned RAM access.
443 If source address is not word-aligned, bytes are combined
444 into words explicitly. */
445 for (uint32_t i = 0; i < num_bytes / NVMC_BYTES_IN_WORD; i++)
446 {
447 uint32_t word = (uint32_t)bytes_src[0]
448 | ((uint32_t)bytes_src[1]) << 8
449 | ((uint32_t)bytes_src[2]) << 16
450 | ((uint32_t)bytes_src[3]) << 24;
451
452 nvmc_word_write(addr, word);
453 bytes_src += NVMC_BYTES_IN_WORD;
454 addr += NVMC_BYTES_IN_WORD;
455 }
456 }
457 else
458 #endif
459 {
460 uint32_t word_count = num_bytes / NVMC_BYTES_IN_WORD;
461
462 nvmc_words_write(addr, (uint32_t const *)bytes_src, word_count);
463
464 addr += word_count * NVMC_BYTES_IN_WORD;
465 bytes_src += word_count * NVMC_BYTES_IN_WORD;
466 }
467
468 uint32_t trailing_bytes = num_bytes % NVMC_BYTES_IN_WORD;
469 if (trailing_bytes != 0)
470 {
471 nvmc_word_write(addr, partial_word_create(addr, bytes_src, trailing_bytes));
472 }
473
474 nvmc_readonly_mode_set();
475 }
476
nrfx_nvmc_words_write(uint32_t addr,void const * src,uint32_t num_words)477 void nrfx_nvmc_words_write(uint32_t addr, void const * src, uint32_t num_words)
478 {
479 NRFX_ASSERT(is_valid_address(addr, true));
480 NRFX_ASSERT(nrfx_is_word_aligned((void const *)addr));
481 NRFX_ASSERT(nrfx_is_word_aligned(src));
482
483 nvmc_write_mode_set();
484
485 nvmc_words_write(addr, src, num_words);
486
487 nvmc_readonly_mode_set();
488 }
489
nrfx_nvmc_otp_halfword_read(uint32_t addr)490 uint16_t nrfx_nvmc_otp_halfword_read(uint32_t addr)
491 {
492 NRFX_ASSERT(is_halfword_aligned(addr));
493
494 uint32_t aligned_addr = addr & ~(0x03UL);
495 uint32_t val32 = nrf_nvmc_word_read(aligned_addr);
496
497 return (nrfx_is_word_aligned((void const *)addr) ? (uint16_t)(val32)
498 : (uint16_t)(val32 >> 16));
499 }
500
nrfx_nvmc_flash_size_get(void)501 uint32_t nrfx_nvmc_flash_size_get(void)
502 {
503 return flash_total_size_get();
504 }
505
nrfx_nvmc_flash_page_size_get(void)506 uint32_t nrfx_nvmc_flash_page_size_get(void)
507 {
508 return flash_page_size_get();
509 }
510
nrfx_nvmc_flash_page_count_get(void)511 uint32_t nrfx_nvmc_flash_page_count_get(void)
512 {
513 return flash_page_count_get();
514 }
515
516 #endif // NRFX_CHECK(NRFX_NVMC_ENABLED)
517