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