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