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