1 /**
2  * \file
3  *
4  * \brief SAM Non Volatile Memory driver
5  *
6  * Copyright (C) 2012-2016 Atmel Corporation. All rights reserved.
7  *
8  * \asf_license_start
9  *
10  * \page License
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions are met:
14  *
15  * 1. Redistributions of source code must retain the above copyright notice,
16  *    this list of conditions and the following disclaimer.
17  *
18  * 2. Redistributions in binary form must reproduce the above copyright notice,
19  *    this list of conditions and the following disclaimer in the documentation
20  *    and/or other materials provided with the distribution.
21  *
22  * 3. The name of Atmel may not be used to endorse or promote products derived
23  *    from this software without specific prior written permission.
24  *
25  * 4. This software may only be redistributed and used in connection with an
26  *    Atmel microcontroller product.
27  *
28  * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
29  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
30  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
31  * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
32  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
36  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
37  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38  * POSSIBILITY OF SUCH DAMAGE.
39  *
40  * \asf_license_stop
41  *
42  */
43 /*
44  * Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
45  */
46 #include "nvm.h"
47 #include <system.h>
48 #include <system_interrupt.h>
49 #include <string.h>
50 
51 /**
52  * \internal Internal device instance struct
53  *
54  * This struct contains information about the NVM module which is
55  * often used by the different functions. The information is loaded
56  * into the struct in the nvm_init() function.
57  */
58 struct _nvm_module {
59 	/** Number of bytes contained per page. */
60 	uint16_t page_size;
61 	/** Total number of pages in the NVM memory. */
62 	uint16_t number_of_pages;
63 	/** If \c false, a page write command will be issued automatically when the
64 	 *  page buffer is full. */
65 	bool manual_page_write;
66 };
67 
68 /**
69  * \internal Instance of the internal device struct
70  */
71 static struct _nvm_module _nvm_dev;
72 
73 /**
74  * \internal Pointer to the NVM MEMORY region start address
75  */
76 #define NVM_MEMORY        ((volatile uint16_t *)FLASH_ADDR)
77 
78 /**
79  * \internal Pointer to the NVM USER MEMORY region start address
80  */
81 #define NVM_USER_MEMORY   ((volatile uint16_t *)NVMCTRL_USER)
82 
83 
84 /**
85  * \brief Sets the up the NVM hardware module based on the configuration.
86  *
87  * Writes a given configuration of an NVM controller configuration to the
88  * hardware module, and initializes the internal device struct.
89  *
90  * \param[in] config    Configuration settings for the NVM controller
91  *
92  * \note The security bit must be cleared in order successfully use this
93  *       function. This can only be done by a chip erase.
94  *
95  * \return Status of the configuration procedure.
96  *
97  * \retval STATUS_OK      If the initialization was a success
98  * \retval STATUS_BUSY    If the module was busy when the operation was attempted
99  * \retval STATUS_ERR_IO  If the security bit has been set, preventing the
100  *                        EEPROM and/or auxiliary space configuration from being
101  *                        altered
102  */
nvm_set_config(const struct nvm_config * const config)103 enum status_code nvm_set_config(
104 		const struct nvm_config *const config)
105 {
106 	/* Sanity check argument */
107 	Assert(config);
108 
109 	/* Get a pointer to the module hardware instance */
110 	Nvmctrl *const nvm_module = NVMCTRL;
111 
112 #if (SAML21) || (SAML22) || (SAMC20) || (SAMC21) || (SAMR30)
113 	/* Turn on the digital interface clock */
114 	system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBB, MCLK_APBBMASK_NVMCTRL);
115 #else
116 	/* Turn on the digital interface clock */
117 	system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBB, PM_APBBMASK_NVMCTRL);
118 #endif
119 
120 	/* Clear error flags */
121 	nvm_module->STATUS.reg = NVMCTRL_STATUS_MASK;
122 
123 	/* Check if the module is busy */
124 	if (!nvm_is_ready()) {
125 		return STATUS_BUSY;
126 	}
127 
128 #if (!SAMC20) && (!SAMC21)
129 	/* Writing configuration to the CTRLB register */
130 	nvm_module->CTRLB.reg =
131 			NVMCTRL_CTRLB_SLEEPPRM(config->sleep_power_mode) |
132 			((config->manual_page_write & 0x01) << NVMCTRL_CTRLB_MANW_Pos) |
133 			NVMCTRL_CTRLB_RWS(config->wait_states) |
134 			((config->disable_cache & 0x01) << NVMCTRL_CTRLB_CACHEDIS_Pos) |
135 			NVMCTRL_CTRLB_READMODE(config->cache_readmode);
136 #else
137 	uint8_t cache_disable_value =  0;
138 	if (config->disable_rww_cache == false) {
139 		cache_disable_value = 0x02;
140 	} else {
141 		cache_disable_value = (config->disable_cache & 0x01);
142 	}
143 	/* Writing configuration to the CTRLB register */
144 	nvm_module->CTRLB.reg =
145 			NVMCTRL_CTRLB_SLEEPPRM(config->sleep_power_mode) |
146 			((config->manual_page_write & 0x01) << NVMCTRL_CTRLB_MANW_Pos) |
147 			NVMCTRL_CTRLB_RWS(config->wait_states) |
148 			(cache_disable_value << NVMCTRL_CTRLB_CACHEDIS_Pos) |
149 			NVMCTRL_CTRLB_READMODE(config->cache_readmode);
150 #endif
151 
152 	/* Initialize the internal device struct */
153 	_nvm_dev.page_size         = (8 << nvm_module->PARAM.bit.PSZ);
154 	_nvm_dev.number_of_pages   = nvm_module->PARAM.bit.NVMP;
155 	_nvm_dev.manual_page_write = config->manual_page_write;
156 
157 	/* If the security bit is set, the auxiliary space cannot be written */
158 	if (nvm_module->STATUS.reg & NVMCTRL_STATUS_SB) {
159 		return STATUS_ERR_IO;
160 	}
161 
162 	return STATUS_OK;
163 }
164 
165 /**
166  * \brief Executes a command on the NVM controller.
167  *
168  * Executes an asynchronous command on the NVM controller, to perform a requested
169  * action such as an NVM page read or write operation.
170  *
171  * \note The function will return before the execution of the given command is
172  *       completed.
173  *
174  * \param[in] command    Command to issue to the NVM controller
175  * \param[in] address    Address to pass to the NVM controller in NVM memory
176  *                       space
177  * \param[in] parameter  Parameter to pass to the NVM controller, not used
178  *                       for this driver
179  *
180  * \return Status of the attempt to execute a command.
181  *
182  * \retval STATUS_OK               If the command was accepted and execution
183  *                                 is now in progress
184  * \retval STATUS_BUSY             If the NVM controller was already busy
185  *                                 executing a command when the new command
186  *                                 was issued
187  * \retval STATUS_ERR_IO           If the command was invalid due to memory or
188  *                                 security locking
189  * \retval STATUS_ERR_INVALID_ARG  If the given command was invalid or
190  *                                 unsupported
191  * \retval STATUS_ERR_BAD_ADDRESS  If the given address was invalid
192  */
nvm_execute_command(const enum nvm_command command,const uint32_t address,const uint32_t parameter)193 enum status_code nvm_execute_command(
194 		const enum nvm_command command,
195 		const uint32_t address,
196 		const uint32_t parameter)
197 {
198 	uint32_t ctrlb_bak;
199 
200 	/* Check that the address given is valid  */
201 	if (address > ((uint32_t)_nvm_dev.page_size * _nvm_dev.number_of_pages)
202 		&& !(address >= NVMCTRL_AUX0_ADDRESS && address <= NVMCTRL_AUX1_ADDRESS )){
203 #ifdef FEATURE_NVM_RWWEE
204 		if (address >= ((uint32_t)NVMCTRL_RWW_EEPROM_SIZE + NVMCTRL_RWW_EEPROM_ADDR)
205 			|| address < NVMCTRL_RWW_EEPROM_ADDR){
206 			return STATUS_ERR_BAD_ADDRESS;
207 		}
208 #else
209 		return STATUS_ERR_BAD_ADDRESS;
210 #endif
211 	}
212 
213 	/* Get a pointer to the module hardware instance */
214 	Nvmctrl *const nvm_module = NVMCTRL;
215 
216 	/* Turn off cache before issuing flash commands */
217 	ctrlb_bak = nvm_module->CTRLB.reg;
218 #if (SAMC20) || (SAMC21)
219 	nvm_module->CTRLB.reg = ((ctrlb_bak &(~(NVMCTRL_CTRLB_CACHEDIS(0x2))))
220 							| NVMCTRL_CTRLB_CACHEDIS(0x1));
221 #else
222 	nvm_module->CTRLB.reg = ctrlb_bak | NVMCTRL_CTRLB_CACHEDIS;
223 #endif
224 
225 	/* Clear error flags */
226 	nvm_module->STATUS.reg = NVMCTRL_STATUS_MASK;
227 
228 	/* Check if the module is busy */
229 	if (!nvm_is_ready()) {
230 		/* Restore the setting */
231 		nvm_module->CTRLB.reg = ctrlb_bak;
232 		return STATUS_BUSY;
233 	}
234 
235 	switch (command) {
236 
237 		/* Commands requiring address (protected) */
238 		case NVM_COMMAND_ERASE_AUX_ROW:
239 		case NVM_COMMAND_WRITE_AUX_ROW:
240 
241 			/* Auxiliary space cannot be accessed if the security bit is set */
242 			if (nvm_module->STATUS.reg & NVMCTRL_STATUS_SB) {
243 				/* Restore the setting */
244 				nvm_module->CTRLB.reg = ctrlb_bak;
245 				return STATUS_ERR_IO;
246 			}
247 
248 			/* Set address, command will be issued elsewhere */
249 			nvm_module->ADDR.reg = (uintptr_t)&NVM_MEMORY[address / 4];
250 			break;
251 
252 		/* Commands requiring address (unprotected) */
253 		case NVM_COMMAND_ERASE_ROW:
254 		case NVM_COMMAND_WRITE_PAGE:
255 		case NVM_COMMAND_LOCK_REGION:
256 		case NVM_COMMAND_UNLOCK_REGION:
257 #ifdef FEATURE_NVM_RWWEE
258 		case NVM_COMMAND_RWWEE_ERASE_ROW:
259 		case NVM_COMMAND_RWWEE_WRITE_PAGE:
260 #endif
261 
262 			/* Set address, command will be issued elsewhere */
263 			nvm_module->ADDR.reg = (uintptr_t)&NVM_MEMORY[address / 4];
264 			break;
265 
266 		/* Commands not requiring address */
267 		case NVM_COMMAND_PAGE_BUFFER_CLEAR:
268 		case NVM_COMMAND_SET_SECURITY_BIT:
269 		case NVM_COMMAND_ENTER_LOW_POWER_MODE:
270 		case NVM_COMMAND_EXIT_LOW_POWER_MODE:
271 			break;
272 
273 		default:
274 			/* Restore the setting */
275 			nvm_module->CTRLB.reg = ctrlb_bak;
276 			return STATUS_ERR_INVALID_ARG;
277 	}
278 
279 	/* Set command */
280 	nvm_module->CTRLA.reg = command | NVMCTRL_CTRLA_CMDEX_KEY;
281 
282 	/* Wait for the NVM controller to become ready */
283 	while (!nvm_is_ready()) {
284 	}
285 
286 	/* Restore the setting */
287 	nvm_module->CTRLB.reg = ctrlb_bak;
288 
289 	return STATUS_OK;
290 }
291 
292 /**
293  * \brief Updates an arbitrary section of a page with new data.
294  *
295  * Writes from a buffer to a given page in the NVM memory, retaining any
296  * unmodified data already stored in the page.
297  *
298  * \note If manual write mode is enable, the write command must be executed after
299  * this function, otherwise the data will not write to NVM from page buffer.
300  *
301  * \warning This routine is unsafe if data integrity is critical; a system reset
302  *          during the update process will result in up to one row of data being
303  *          lost. If corruption must be avoided in all circumstances (including
304  *          power loss or system reset) this function should not be used.
305  *
306  * \param[in]  destination_address  Destination page address to write to
307  * \param[in]  buffer               Pointer to buffer where the data to write is
308  *                                  stored
309  * \param[in]  offset               Number of bytes to offset the data write in
310  *                                  the page
311  * \param[in]  length               Number of bytes in the page to update
312  *
313  * \return Status of the attempt to update a page.
314  *
315  * \retval STATUS_OK               Requested NVM memory page was successfully
316  *                                 read
317  * \retval STATUS_BUSY             NVM controller was busy when the operation
318  *                                 was attempted
319  * \retval STATUS_ERR_BAD_ADDRESS  The requested address was outside the
320  *                                 acceptable range of the NVM memory region
321  * \retval STATUS_ERR_INVALID_ARG  The supplied length and offset was invalid
322  */
nvm_update_buffer(const uint32_t destination_address,uint8_t * const buffer,uint16_t offset,uint16_t length)323 enum status_code nvm_update_buffer(
324 		const uint32_t destination_address,
325 		uint8_t *const buffer,
326 		uint16_t offset,
327 		uint16_t length)
328 {
329 	enum status_code error_code = STATUS_OK;
330 	uint8_t row_buffer[NVMCTRL_ROW_PAGES][NVMCTRL_PAGE_SIZE];
331 
332 	/* Ensure the read does not overflow the page size */
333 	if ((offset + length) > _nvm_dev.page_size) {
334 		return STATUS_ERR_INVALID_ARG;
335 	}
336 
337 	/* Calculate the starting row address of the page to update */
338 	uint32_t row_start_address =
339 			destination_address & ~((_nvm_dev.page_size * NVMCTRL_ROW_PAGES) - 1);
340 
341 	/* Read in the current row contents */
342 	for (uint32_t i = 0; i < NVMCTRL_ROW_PAGES; i++) {
343 		do
344 		{
345 			error_code = nvm_read_buffer(
346 					row_start_address + (i * _nvm_dev.page_size),
347 					row_buffer[i], _nvm_dev.page_size);
348 		} while (error_code == STATUS_BUSY);
349 
350 		if (error_code != STATUS_OK) {
351 			return error_code;
352 		}
353 	}
354 
355 	/* Calculate the starting page in the row that is to be updated */
356 	uint8_t page_in_row =
357 			(destination_address % (_nvm_dev.page_size * NVMCTRL_ROW_PAGES)) /
358 			_nvm_dev.page_size;
359 
360 	/* Update the specified bytes in the page buffer */
361 	for (uint32_t i = 0; i < length; i++) {
362 		row_buffer[page_in_row][offset + i] = buffer[i];
363 	}
364 
365 	system_interrupt_enter_critical_section();
366 
367 	/* Erase the row */
368 	do
369 	{
370 		error_code = nvm_erase_row(row_start_address);
371 	} while (error_code == STATUS_BUSY);
372 
373 	if (error_code != STATUS_OK) {
374 		system_interrupt_leave_critical_section();
375 		return error_code;
376 	}
377 
378 	/* Write the updated row contents to the erased row */
379 	for (uint32_t i = 0; i < NVMCTRL_ROW_PAGES; i++) {
380 		do
381 		{
382 			error_code = nvm_write_buffer(
383 					row_start_address + (i * _nvm_dev.page_size),
384 					row_buffer[i], _nvm_dev.page_size);
385 		} while (error_code == STATUS_BUSY);
386 
387 		if (error_code != STATUS_OK) {
388 			system_interrupt_leave_critical_section();
389 			return error_code;
390 		}
391 	}
392 
393 	system_interrupt_leave_critical_section();
394 
395 	return error_code;
396 }
397 
398 /**
399  * \brief Writes a number of bytes to a page in the NVM memory region.
400  *
401  * Writes from a buffer to a given page address in the NVM memory.
402  *
403  * \param[in]  destination_address  Destination page address to write to
404  * \param[in]  buffer               Pointer to buffer where the data to write is
405  *                                  stored
406  * \param[in]  length               Number of bytes in the page to write
407  *
408  * \note If writing to a page that has previously been written to, the page's
409  *       row should be erased (via \ref nvm_erase_row()) before attempting to
410  *       write new data to the page.
411  *
412  * \note For SAM D21 RWW devices, see \c SAMD21_64K, command \c NVM_COMMAND_RWWEE_WRITE_PAGE
413  * must be executed before any other commands after writing a page,
414  * refer to errata 13588.
415  *
416  * \note If manual write mode is enabled, the write command must be executed after
417  * this function, otherwise the data will not write to NVM from page buffer.
418  *
419  * \return Status of the attempt to write a page.
420  *
421  * \retval STATUS_OK               Requested NVM memory page was successfully
422  *                                 read
423  * \retval STATUS_BUSY             NVM controller was busy when the operation
424  *                                 was attempted
425  * \retval STATUS_ERR_BAD_ADDRESS  The requested address was outside the
426  *                                 acceptable range of the NVM memory region or
427  *                                 not aligned to the start of a page
428  * \retval STATUS_ERR_INVALID_ARG  The supplied write length was invalid
429  */
nvm_write_buffer(const uint32_t destination_address,const uint8_t * buffer,uint16_t length)430 enum status_code nvm_write_buffer(
431 		const uint32_t destination_address,
432 		const uint8_t *buffer,
433 		uint16_t length)
434 {
435 #ifdef FEATURE_NVM_RWWEE
436 	bool is_rww_eeprom = false;
437 #endif
438 
439 	/* Check if the destination address is valid */
440 	if (destination_address >
441 			((uint32_t)_nvm_dev.page_size * _nvm_dev.number_of_pages)) {
442 #ifdef FEATURE_NVM_RWWEE
443 		if (destination_address >= ((uint32_t)NVMCTRL_RWW_EEPROM_SIZE + NVMCTRL_RWW_EEPROM_ADDR)
444 			|| destination_address < NVMCTRL_RWW_EEPROM_ADDR){
445 			return STATUS_ERR_BAD_ADDRESS;
446 		}
447 		is_rww_eeprom = true;
448 #else
449 		return STATUS_ERR_BAD_ADDRESS;
450 #endif
451 	}
452 
453 	/* Check if the write address not aligned to the start of a page */
454 	if (destination_address & (_nvm_dev.page_size - 1)) {
455 		return STATUS_ERR_BAD_ADDRESS;
456 	}
457 
458 	/* Check if the write length is longer than an NVM page */
459 	if (length > _nvm_dev.page_size) {
460 		return STATUS_ERR_INVALID_ARG;
461 	}
462 
463 	/* Get a pointer to the module hardware instance */
464 	Nvmctrl *const nvm_module = NVMCTRL;
465 
466 	/* Check if the module is busy */
467 	if (!nvm_is_ready()) {
468 		return STATUS_BUSY;
469 	}
470 
471 	/* Erase the page buffer before buffering new data */
472 	nvm_module->CTRLA.reg = NVM_COMMAND_PAGE_BUFFER_CLEAR | NVMCTRL_CTRLA_CMDEX_KEY;
473 
474 	/* Check if the module is busy */
475 	while (!nvm_is_ready()) {
476 		/* Force-wait for the buffer clear to complete */
477 	}
478 
479 	/* Clear error flags */
480 	nvm_module->STATUS.reg = NVMCTRL_STATUS_MASK;
481 
482 	uint32_t nvm_address = destination_address / 2;
483 
484 	/* NVM _must_ be accessed as a series of 16-bit words, perform manual copy
485 	 * to ensure alignment */
486 	for (uint16_t i = 0; i < length; i += 2) {
487 		uint16_t data;
488 
489 		/* Copy first byte of the 16-bit chunk to the temporary buffer */
490 		data = buffer[i];
491 
492 		/* If we are not at the end of a write request with an odd byte count,
493 		 * store the next byte of data as well */
494 		if (i < (length - 1)) {
495 			data |= (buffer[i + 1] << 8);
496 		}
497 
498 		/* Store next 16-bit chunk to the NVM memory space */
499 		NVM_MEMORY[nvm_address++] = data;
500 	}
501 
502 	/* If automatic page write mode is enable, then perform a manual NVM
503 	 * write when the length of data to be programmed is less than page size
504 	 */
505 	if ((_nvm_dev.manual_page_write == false) && (length < NVMCTRL_PAGE_SIZE)) {
506 #ifdef FEATURE_NVM_RWWEE
507 	 return ((is_rww_eeprom) ?
508 				(nvm_execute_command(NVM_COMMAND_RWWEE_WRITE_PAGE,destination_address, 0)):
509 	 			(nvm_execute_command(NVM_COMMAND_WRITE_PAGE,destination_address, 0)));
510 #else
511 		return nvm_execute_command(NVM_COMMAND_WRITE_PAGE,
512 				destination_address, 0);
513 #endif
514 	}
515 
516 	return STATUS_OK;
517 }
518 
519 /**
520  * \brief Reads a number of bytes from a page in the NVM memory region.
521  *
522  * Reads a given number of bytes from a given page address in the NVM memory
523  * space into a buffer.
524  *
525  * \param[in]  source_address  Source page address to read from
526  * \param[out] buffer          Pointer to a buffer where the content of the read
527  *                             page will be stored
528  * \param[in]  length          Number of bytes in the page to read
529  *
530  * \return Status of the page read attempt.
531  *
532  * \retval STATUS_OK               Requested NVM memory page was successfully
533  *                                 read
534  * \retval STATUS_BUSY             NVM controller was busy when the operation
535  *                                 was attempted
536  * \retval STATUS_ERR_BAD_ADDRESS  The requested address was outside the
537  *                                 acceptable range of the NVM memory region or
538  *                                 not aligned to the start of a page
539  * \retval STATUS_ERR_INVALID_ARG  The supplied read length was invalid
540  */
nvm_read_buffer(const uint32_t source_address,uint8_t * const buffer,uint16_t length)541 enum status_code nvm_read_buffer(
542 		const uint32_t source_address,
543 		uint8_t *const buffer,
544 		uint16_t length)
545 {
546 	/* Check if the source address is valid */
547 	if (source_address >
548 			((uint32_t)_nvm_dev.page_size * _nvm_dev.number_of_pages)) {
549 #ifdef FEATURE_NVM_RWWEE
550 		if (source_address >= ((uint32_t)NVMCTRL_RWW_EEPROM_SIZE + NVMCTRL_RWW_EEPROM_ADDR)
551 			|| source_address < NVMCTRL_RWW_EEPROM_ADDR){
552 			return STATUS_ERR_BAD_ADDRESS;
553 		}
554 #else
555 		return STATUS_ERR_BAD_ADDRESS;
556 #endif
557 	}
558 
559 	/* Check if the read address is not aligned to the start of a page */
560 	if (source_address & (_nvm_dev.page_size - 1)) {
561 		return STATUS_ERR_BAD_ADDRESS;
562 	}
563 
564 	/* Check if the write length is longer than an NVM page */
565 	if (length > _nvm_dev.page_size) {
566 		return STATUS_ERR_INVALID_ARG;
567 	}
568 
569 	/* Get a pointer to the module hardware instance */
570 	Nvmctrl *const nvm_module = NVMCTRL;
571 
572 	/* Check if the module is busy */
573 	if (!nvm_is_ready()) {
574 		return STATUS_BUSY;
575 	}
576 
577 	/* Clear error flags */
578 	nvm_module->STATUS.reg = NVMCTRL_STATUS_MASK;
579 
580 	uint32_t page_address = source_address / 2;
581 
582 	/* NVM _must_ be accessed as a series of 16-bit words, perform manual copy
583 	 * to ensure alignment */
584 	for (uint16_t i = 0; i < length; i += 2) {
585 		/* Fetch next 16-bit chunk from the NVM memory space */
586 		uint16_t data = NVM_MEMORY[page_address++];
587 
588 		/* Copy first byte of the 16-bit chunk to the destination buffer */
589 		buffer[i] = (data & 0xFF);
590 
591 		/* If we are not at the end of a read request with an odd byte count,
592 		 * store the next byte of data as well */
593 		if (i < (length - 1)) {
594 			buffer[i + 1] = (data >> 8);
595 		}
596 	}
597 
598 	return STATUS_OK;
599 }
600 
601 /**
602  * \brief Erases a row in the NVM memory space.
603  *
604  * Erases a given row in the NVM memory region.
605  *
606  * \param[in] row_address  Address of the row to erase
607  *
608  * \return Status of the NVM row erase attempt.
609  *
610  * \retval STATUS_OK               Requested NVM memory row was successfully
611  *                                 erased
612  * \retval STATUS_BUSY             NVM controller was busy when the operation
613  *                                 was attempted
614  * \retval STATUS_ERR_BAD_ADDRESS  The requested row address was outside the
615  *                                 acceptable range of the NVM memory region or
616  *                                 not aligned to the start of a row
617  * \retval STATUS_ABORTED          NVM erased error
618  */
nvm_erase_row(const uint32_t row_address)619 enum status_code nvm_erase_row(
620 		const uint32_t row_address)
621 {
622 #ifdef FEATURE_NVM_RWWEE
623 		bool is_rww_eeprom = false;
624 #endif
625 
626 	/* Check if the row address is valid */
627 	if (row_address >
628 			((uint32_t)_nvm_dev.page_size * _nvm_dev.number_of_pages)) {
629 #ifdef FEATURE_NVM_RWWEE
630 		if (row_address >= ((uint32_t)NVMCTRL_RWW_EEPROM_SIZE + NVMCTRL_RWW_EEPROM_ADDR)
631 			|| row_address < NVMCTRL_RWW_EEPROM_ADDR){
632 			return STATUS_ERR_BAD_ADDRESS;
633 		}
634 		is_rww_eeprom = true;
635 #else
636 		return STATUS_ERR_BAD_ADDRESS;
637 #endif
638 	}
639 
640 	/* Check if the address to erase is not aligned to the start of a row */
641 	if (row_address & ((_nvm_dev.page_size * NVMCTRL_ROW_PAGES) - 1)) {
642 		return STATUS_ERR_BAD_ADDRESS;
643 	}
644 
645 	/* Get a pointer to the module hardware instance */
646 	Nvmctrl *const nvm_module = NVMCTRL;
647 
648 	/* Check if the module is busy */
649 	if (!nvm_is_ready()) {
650 		return STATUS_BUSY;
651 	}
652 
653 	/* Clear error flags */
654 	nvm_module->STATUS.reg = NVMCTRL_STATUS_MASK;
655 
656 	/* Set address and command */
657 	nvm_module->ADDR.reg  = (uintptr_t)&NVM_MEMORY[row_address / 4];
658 
659 #ifdef SAMD21_64K
660 	if (is_rww_eeprom) {
661 		NVM_MEMORY[row_address / 2] = 0x0;
662 	}
663 #endif
664 
665 #ifdef FEATURE_NVM_RWWEE
666 	nvm_module->CTRLA.reg = ((is_rww_eeprom) ?
667 								(NVM_COMMAND_RWWEE_ERASE_ROW | NVMCTRL_CTRLA_CMDEX_KEY):
668 								(NVM_COMMAND_ERASE_ROW | NVMCTRL_CTRLA_CMDEX_KEY));
669 #else
670 	nvm_module->CTRLA.reg = NVM_COMMAND_ERASE_ROW | NVMCTRL_CTRLA_CMDEX_KEY;
671 #endif
672 
673 	while (!nvm_is_ready()) {
674 	}
675 
676 	/* There existed error in NVM erase operation */
677 	if ((enum nvm_error)(nvm_module->STATUS.reg & NVM_ERRORS_MASK) != NVM_ERROR_NONE) {
678 		return STATUS_ABORTED;
679 	}
680 
681 	return STATUS_OK;
682 }
683 
684 /**
685  * \brief Reads the parameters of the NVM controller.
686  *
687  * Retrieves the page size, number of pages, and other configuration settings
688  * of the NVM region.
689  *
690  * \param[out] parameters    Parameter structure, which holds page size and
691  *                           number of pages in the NVM memory
692  */
nvm_get_parameters(struct nvm_parameters * const parameters)693 void nvm_get_parameters(
694 		struct nvm_parameters *const parameters)
695 {
696 	/* Sanity check parameters */
697 	Assert(parameters);
698 
699 	/* Get a pointer to the module hardware instance */
700 	Nvmctrl *const nvm_module = NVMCTRL;
701 
702 	/* Clear error flags */
703 	nvm_module->STATUS.reg = NVMCTRL_STATUS_MASK;
704 
705 	/* Read out from the PARAM register */
706 	uint32_t param_reg = nvm_module->PARAM.reg;
707 
708 	/* Mask out page size exponent and convert to a number of bytes */
709 	parameters->page_size =
710 			8 << ((param_reg & NVMCTRL_PARAM_PSZ_Msk) >> NVMCTRL_PARAM_PSZ_Pos);
711 
712 	/* Mask out number of pages count */
713 	parameters->nvm_number_of_pages =
714 			(param_reg & NVMCTRL_PARAM_NVMP_Msk) >> NVMCTRL_PARAM_NVMP_Pos;
715 
716 #ifdef FEATURE_NVM_RWWEE
717 	/* Mask out rwwee number of pages count */
718 	parameters->rww_eeprom_number_of_pages =
719 			(param_reg & NVMCTRL_PARAM_RWWEEP_Msk) >> NVMCTRL_PARAM_RWWEEP_Pos;
720 #endif
721 
722 	/* Read the current EEPROM fuse value from the USER row */
723 	uint16_t eeprom_fuse_value =
724 			(NVM_USER_MEMORY[NVMCTRL_FUSES_EEPROM_SIZE_Pos / 16] &
725 			NVMCTRL_FUSES_EEPROM_SIZE_Msk) >> NVMCTRL_FUSES_EEPROM_SIZE_Pos;
726 
727 	/* Translate the EEPROM fuse byte value to a number of NVM pages */
728 	if (eeprom_fuse_value == 7) {
729 		parameters->eeprom_number_of_pages = 0;
730 	}
731 	else {
732 		parameters->eeprom_number_of_pages =
733 				NVMCTRL_ROW_PAGES << (6 - eeprom_fuse_value);
734 	}
735 
736 	/* Read the current BOOTSZ fuse value from the USER row */
737 	uint16_t boot_fuse_value =
738 			(NVM_USER_MEMORY[NVMCTRL_FUSES_BOOTPROT_Pos / 16] &
739 			NVMCTRL_FUSES_BOOTPROT_Msk) >> NVMCTRL_FUSES_BOOTPROT_Pos;
740 
741 	/* Translate the BOOTSZ fuse byte value to a number of NVM pages */
742 	if (boot_fuse_value == 7) {
743 		parameters->bootloader_number_of_pages = 0;
744 	}
745 	else {
746 		parameters->bootloader_number_of_pages =
747 				NVMCTRL_ROW_PAGES << (7 - boot_fuse_value);
748 	}
749 }
750 
751 /**
752  * \brief Checks whether the page region is locked.
753  *
754  * Extracts the region to which the given page belongs and checks whether
755  * that region is locked.
756  *
757  * \param[in] page_number    Page number to be checked
758  *
759  * \return Page lock status.
760  *
761  * \retval true              Page is locked
762  * \retval false             Page is not locked
763  *
764  */
nvm_is_page_locked(uint16_t page_number)765 bool nvm_is_page_locked(uint16_t page_number)
766 {
767 	uint16_t pages_in_region;
768 	uint16_t region_number;
769 
770 #ifdef FEATURE_NVM_RWWEE
771 	Assert(page_number < _nvm_dev.number_of_pages);
772 #endif
773 
774 	/* Get a pointer to the module hardware instance */
775 	Nvmctrl *const nvm_module = NVMCTRL;
776 
777 	/* Get number of pages in a region */
778 	pages_in_region = _nvm_dev.number_of_pages / 16;
779 
780 	/* Get region for given page */
781 	region_number = page_number / pages_in_region;
782 
783 	return !(nvm_module->LOCK.reg & (1 << region_number));
784 }
785 
786 ///@cond INTERNAL
787 
788 /**
789  * \internal
790  *
791  * \brief Translate fusebit words into struct content.
792  *
793  */
_nvm_translate_raw_fusebits_to_struct(uint32_t * raw_user_row,struct nvm_fusebits * fusebits)794 static void _nvm_translate_raw_fusebits_to_struct (
795 		uint32_t *raw_user_row,
796 		struct nvm_fusebits *fusebits)
797 {
798 
799 	fusebits->bootloader_size = (enum nvm_bootloader_size)
800 			((raw_user_row[0] & NVMCTRL_FUSES_BOOTPROT_Msk)
801 			>> NVMCTRL_FUSES_BOOTPROT_Pos);
802 
803 	fusebits->eeprom_size = (enum nvm_eeprom_emulator_size)
804 			((raw_user_row[0] & NVMCTRL_FUSES_EEPROM_SIZE_Msk)
805 			>> NVMCTRL_FUSES_EEPROM_SIZE_Pos);
806 
807 #if (SAML21) || (SAML22) || (SAMR30)
808 	fusebits->bod33_level = (uint8_t)
809 			((raw_user_row[0] & FUSES_BOD33USERLEVEL_Msk)
810 			>> FUSES_BOD33USERLEVEL_Pos);
811 
812 	fusebits->bod33_enable = (bool)
813 			(!((raw_user_row[0] & FUSES_BOD33_DIS_Msk)
814 			>> FUSES_BOD33_DIS_Pos));
815 
816 	fusebits->bod33_action = (enum nvm_bod33_action)
817 			((raw_user_row[0] & FUSES_BOD33_ACTION_Msk)
818 			>> FUSES_BOD33_ACTION_Pos);
819 
820 	fusebits->bod33_hysteresis = (bool)
821 			((raw_user_row[1] & FUSES_BOD33_HYST_Msk)
822 			>> FUSES_BOD33_HYST_Pos);
823 
824 #elif (SAMD20) || (SAMD21) || (SAMR21)|| (SAMDA1) || (SAMD09) || (SAMD10) || (SAMD11) || (SAMHA1)
825 	fusebits->bod33_level = (uint8_t)
826 			((raw_user_row[0] & FUSES_BOD33USERLEVEL_Msk)
827 			>> FUSES_BOD33USERLEVEL_Pos);
828 
829 	fusebits->bod33_enable = (bool)
830 			((raw_user_row[0] & FUSES_BOD33_EN_Msk)
831 			>> FUSES_BOD33_EN_Pos);
832 
833 	fusebits->bod33_action = (enum nvm_bod33_action)
834 			((raw_user_row[0] & FUSES_BOD33_ACTION_Msk)
835 			>> FUSES_BOD33_ACTION_Pos);
836 	fusebits->bod33_hysteresis = (bool)
837 			((raw_user_row[1] & FUSES_BOD33_HYST_Msk)
838 			>> FUSES_BOD33_HYST_Pos);
839 #elif (SAMC20) || (SAMC21)
840 	fusebits->bodvdd_level = (uint8_t)
841 			((raw_user_row[0] & FUSES_BODVDDUSERLEVEL_Msk)
842 			>> FUSES_BODVDDUSERLEVEL_Pos);
843 
844 	fusebits->bodvdd_enable = (bool)
845 			(!((raw_user_row[0] & FUSES_BODVDD_DIS_Msk)
846 			>> FUSES_BODVDD_DIS_Pos));
847 
848 	fusebits->bodvdd_action = (enum nvm_bod33_action)
849 			((raw_user_row[0] & FUSES_BODVDD_ACTION_Msk)
850 			>> FUSES_BODVDD_ACTION_Pos);
851 
852 	fusebits->bodvdd_hysteresis = (raw_user_row[1] & FUSES_BODVDD_HYST_Msk)
853 									>> FUSES_BODVDD_HYST_Pos;
854 #endif
855 
856 #ifdef FEATURE_BOD12
857 
858 #ifndef FUSES_BOD12USERLEVEL_Pos
859 #define FUSES_BOD12USERLEVEL_Pos 17
860 #define FUSES_BOD12USERLEVEL_Msk (0x3Ful << FUSES_BOD12USERLEVEL_Pos)
861 #endif
862 #ifndef FUSES_BOD12_DIS_Pos
863 #define FUSES_BOD12_DIS_Pos 23
864 #define FUSES_BOD12_DIS_Msk (0x1ul << FUSES_BOD12_DIS_Pos)
865 #endif
866 #ifndef FUSES_BOD12_ACTION_Pos
867 #define FUSES_BOD12_ACTION_Pos 24
868 #define FUSES_BOD12_ACTION_Msk (0x3ul << FUSES_BOD12_ACTION_Pos)
869 #endif
870 
871 	fusebits->bod12_level = (uint8_t)
872 			((raw_user_row[0] & FUSES_BOD12USERLEVEL_Msk)
873 			>> FUSES_BOD12USERLEVEL_Pos);
874 
875 	fusebits->bod12_enable = (bool)
876 			(!((raw_user_row[0] & FUSES_BOD12_DIS_Msk)
877 			>> FUSES_BOD12_DIS_Pos));
878 
879 	fusebits->bod12_action = (enum nvm_bod12_action)
880 			((raw_user_row[0] & FUSES_BOD12_ACTION_Msk)
881 			>> FUSES_BOD33_ACTION_Pos);
882 
883 	fusebits->bod12_hysteresis = (bool)
884 			((raw_user_row[1] & FUSES_BOD12_HYST_Msk)
885 			>> FUSES_BOD12_HYST_Pos);
886 #endif
887 
888 	fusebits->wdt_enable = (bool)
889 			((raw_user_row[0] & WDT_FUSES_ENABLE_Msk) >> WDT_FUSES_ENABLE_Pos);
890 
891 	fusebits->wdt_always_on = (bool)
892 			((raw_user_row[0] & WDT_FUSES_ALWAYSON_Msk) >> WDT_FUSES_ALWAYSON_Pos);
893 
894 	fusebits->wdt_timeout_period = (uint8_t)
895 			((raw_user_row[0] & WDT_FUSES_PER_Msk) >> WDT_FUSES_PER_Pos);
896 
897 #if (SAML21) || (SAML22) || (SAMC20) || (SAMC21) || (SAMR30)
898 	fusebits->wdt_window_timeout = (enum nvm_wdt_window_timeout)
899 			((raw_user_row[1] & WDT_FUSES_WINDOW_Msk) >> WDT_FUSES_WINDOW_Pos);
900 #else
901 	/* WDT Windows timout lay between two 32-bit words in the user row. Because only one bit lays in word[0],
902 	   bits in word[1] must be left sifted by one to make the correct number */
903 	fusebits->wdt_window_timeout = (enum nvm_wdt_window_timeout)
904 			(((raw_user_row[0] & WDT_FUSES_WINDOW_0_Msk) >> WDT_FUSES_WINDOW_0_Pos) |
905 			((raw_user_row[1] & WDT_FUSES_WINDOW_1_Msk) << 1));
906 #endif
907 	fusebits->wdt_early_warning_offset = (enum nvm_wdt_early_warning_offset)
908 			((raw_user_row[1] & WDT_FUSES_EWOFFSET_Msk) >> WDT_FUSES_EWOFFSET_Pos);
909 
910 	fusebits->wdt_window_mode_enable_at_poweron = (bool)
911 			((raw_user_row[1] & WDT_FUSES_WEN_Msk) >> WDT_FUSES_WEN_Pos);
912 
913 	fusebits->lockbits = (uint16_t)
914 			((raw_user_row[1] & NVMCTRL_FUSES_REGION_LOCKS_Msk)
915 			>> NVMCTRL_FUSES_REGION_LOCKS_Pos);
916 
917 }
918 
919 ///@endcond
920 
921 /**
922  * \brief Get fuses from user row.
923  *
924  * Read out the fuse settings from the user row.
925  *
926  * \param[in] fusebits Pointer to a 64-bit wide memory buffer of type struct nvm_fusebits
927  *
928  * \return             Status of read fuses attempt.
929  *
930  * \retval STATUS_OK   This function will always return STATUS_OK
931  */
nvm_get_fuses(struct nvm_fusebits * fusebits)932 enum status_code nvm_get_fuses (
933 		struct nvm_fusebits *fusebits)
934 {
935 	enum status_code error_code = STATUS_OK;
936 	uint32_t raw_fusebits[2];
937 
938 	/* Make sure the module is ready */
939 	while (!nvm_is_ready()) {
940 	}
941 
942 	/* Read the fuse settings in the user row, 64 bit */
943 	((uint16_t*)&raw_fusebits)[0] = (uint16_t)NVM_MEMORY[NVMCTRL_USER / 2];
944 	((uint16_t*)&raw_fusebits)[1] = (uint16_t)NVM_MEMORY[(NVMCTRL_USER / 2) + 1];
945 	((uint16_t*)&raw_fusebits)[2] = (uint16_t)NVM_MEMORY[(NVMCTRL_USER / 2) + 2];
946 	((uint16_t*)&raw_fusebits)[3] = (uint16_t)NVM_MEMORY[(NVMCTRL_USER / 2) + 3];
947 
948 	_nvm_translate_raw_fusebits_to_struct(raw_fusebits, fusebits);
949 
950 	return error_code;
951 }
952 
953 /**
954  * \brief Set fuses from user row.
955  *
956  * Set fuse settings from the user row.
957  *
958  * \note When writing to the user row, the values do not get loaded by the
959  * other modules on the device until a device reset occurs.
960  *
961  * \param[in] fusebits Pointer to a 64-bit wide memory buffer of type struct nvm_fusebits
962  *
963  * \return             Status of read fuses attempt.
964  *
965  * \retval STATUS_OK   This function will always return STATUS_OK
966  *
967  * \retval STATUS_BUSY             If the NVM controller was already busy
968  *                                 executing a command when the new command
969  *                                 was issued
970  * \retval STATUS_ERR_IO           If the command was invalid due to memory or
971  *                                 security locking
972  * \retval STATUS_ERR_INVALID_ARG  If the given command was invalid or
973  *                                 unsupported
974  * \retval STATUS_ERR_BAD_ADDRESS  If the given address was invalid
975  */
976 
nvm_set_fuses(struct nvm_fusebits * fb)977 enum status_code nvm_set_fuses(struct nvm_fusebits *fb)
978 {
979     uint32_t fusebits[2];
980 	enum status_code error_code = STATUS_OK;
981 
982 	if (fb == NULL) {
983 		return STATUS_ERR_INVALID_ARG;
984 	}
985     /* Read the fuse settings in the user row, 64 bit */
986     fusebits[0] = *((uint32_t *)NVMCTRL_AUX0_ADDRESS);
987     fusebits[1] = *(((uint32_t *)NVMCTRL_AUX0_ADDRESS) + 1);
988 
989 	/* Set user fuses bit */
990 	fusebits[0] &= (~NVMCTRL_FUSES_BOOTPROT_Msk);
991 	fusebits[0] |= NVMCTRL_FUSES_BOOTPROT(fb->bootloader_size);
992 
993 	fusebits[0] &= (~NVMCTRL_FUSES_EEPROM_SIZE_Msk);
994 	fusebits[0] |= NVMCTRL_FUSES_EEPROM_SIZE(fb->eeprom_size);
995 
996 #if (SAML21) || (SAML22) || (SAMR30)
997 	fusebits[0] &= (~FUSES_BOD33USERLEVEL_Msk);
998 	fusebits[0] |= FUSES_BOD33USERLEVEL(fb->bod33_level);
999 
1000 	fusebits[0] &= (~FUSES_BOD33_DIS_Msk);
1001 	fusebits[0] |= (!fb->bod33_enable) << FUSES_BOD33_DIS_Pos;
1002 
1003 	fusebits[0] &= (~FUSES_BOD33_ACTION_Msk);
1004 	fusebits[0] |= fb->bod33_action << FUSES_BOD33_ACTION_Pos;
1005 
1006 	fusebits[1] &= (~FUSES_BOD33_HYST_Msk);
1007 	fusebits[1] |= fb->bod33_hysteresis << FUSES_BOD33_HYST_Pos;
1008 
1009 #elif (SAMD20) || (SAMD21) || (SAMR21) || (SAMDA1) || (SAMD09) || (SAMD10) || (SAMD11) || (SAMHA1)
1010 	fusebits[0] &= (~FUSES_BOD33USERLEVEL_Msk);
1011 	fusebits[0] |= FUSES_BOD33USERLEVEL(fb->bod33_level);
1012 
1013 	fusebits[0] &= (~FUSES_BOD33_EN_Msk);
1014 	fusebits[0] |= (fb->bod33_enable) << FUSES_BOD33_EN_Pos;
1015 
1016 	fusebits[0] &= (~FUSES_BOD33_ACTION_Msk);
1017 	fusebits[0] |= fb->bod33_action << FUSES_BOD33_ACTION_Pos;
1018 
1019 	fusebits[1] &= (~FUSES_BOD33_HYST_Msk);
1020 	fusebits[1] |= fb->bod33_hysteresis << FUSES_BOD33_HYST_Pos;
1021 
1022 #elif (SAMC20) || (SAMC21)
1023 	fusebits[0] &= (~FUSES_BODVDDUSERLEVEL_Msk);
1024 	fusebits[0] |= FUSES_BODVDDUSERLEVEL(fb->bodvdd_level);
1025 
1026 	fusebits[0] &= (~FUSES_BODVDD_DIS_Msk);
1027 	fusebits[0] |= (!fb->bodvdd_enable) << FUSES_BODVDD_DIS_Pos;
1028 
1029 	fusebits[0] &= (~FUSES_BODVDD_ACTION_Msk);
1030 	fusebits[0] |= fb->bodvdd_action << FUSES_BODVDD_ACTION_Pos;
1031 
1032 	fusebits[1] &= (~FUSES_BODVDD_HYST_Msk);
1033 	fusebits[1] |= fb->bodvdd_hysteresis << FUSES_BODVDD_HYST_Pos;
1034 
1035 #endif
1036 
1037 	fusebits[0] &= (~WDT_FUSES_ENABLE_Msk);
1038 	fusebits[0] |= fb->wdt_enable << WDT_FUSES_ENABLE_Pos;
1039 
1040 	fusebits[0] &= (~WDT_FUSES_ALWAYSON_Msk);
1041 	fusebits[0] |= (fb->wdt_always_on) << WDT_FUSES_ALWAYSON_Pos;
1042 
1043 	fusebits[0] &= (~WDT_FUSES_PER_Msk);
1044 	fusebits[0] |= fb->wdt_timeout_period << WDT_FUSES_PER_Pos;
1045 
1046 #if (SAML21) || (SAML22) || (SAMC20) || (SAMC21) || (SAMR30)
1047 	fusebits[1] &= (~WDT_FUSES_WINDOW_Msk);
1048 	fusebits[1] |= fb->wdt_window_timeout << WDT_FUSES_WINDOW_Pos;
1049 #else
1050    /* WDT Windows timout lay between two 32-bit words in the user row. the last one bit lays in word[0],
1051 	   and the other bits in word[1] */
1052 	fusebits[0] &= (~WDT_FUSES_WINDOW_0_Msk);
1053 	fusebits[0] |= (fb->wdt_window_timeout & 0x1) << WDT_FUSES_WINDOW_0_Pos;
1054 
1055 	fusebits[1] &= (~WDT_FUSES_WINDOW_1_Msk);
1056 	fusebits[1] |= (fb->wdt_window_timeout >> 1) << WDT_FUSES_WINDOW_1_Pos;
1057 
1058 #endif
1059 	fusebits[1] &= (~WDT_FUSES_EWOFFSET_Msk);
1060 	fusebits[1] |= fb->wdt_early_warning_offset << WDT_FUSES_EWOFFSET_Pos;
1061 
1062 	fusebits[1] &= (~WDT_FUSES_WEN_Msk);
1063 	fusebits[1] |= fb->wdt_window_mode_enable_at_poweron << WDT_FUSES_WEN_Pos;
1064 
1065 	fusebits[1] &= (~NVMCTRL_FUSES_REGION_LOCKS_Msk);
1066 	fusebits[1] |= fb->lockbits << NVMCTRL_FUSES_REGION_LOCKS_Pos;
1067 
1068 #ifdef FEATURE_BOD12
1069 
1070 #ifndef FUSES_BOD12USERLEVEL_Pos
1071 #define FUSES_BOD12USERLEVEL_Pos 17
1072 #define FUSES_BOD12USERLEVEL_Msk (0x3Ful << FUSES_BOD12USERLEVEL_Pos)
1073 #endif
1074 #ifndef FUSES_BOD12_DIS_Pos
1075 #define FUSES_BOD12_DIS_Pos 23
1076 #define FUSES_BOD12_DIS_Msk (0x1ul << FUSES_BOD12_DIS_Pos)
1077 #endif
1078 #ifndef FUSES_BOD12_ACTION_Pos
1079 #define FUSES_BOD12_ACTION_Pos 24
1080 #define FUSES_BOD12_ACTION_Msk (0x3ul << FUSES_BOD12_ACTION_Pos)
1081 #endif
1082 
1083 	fusebits[0] &= (~FUSES_BOD12USERLEVEL_Msk);
1084 	fusebits[0] |= ((FUSES_BOD12USERLEVEL_Msk & ((fb->bod12_level) <<
1085 						FUSES_BOD12USERLEVEL_Pos)));
1086 
1087 	fusebits[0] &= (~FUSES_BOD12_DIS_Msk);
1088 	fusebits[0] |= (!fb->bod12_enable) << FUSES_BOD12_DIS_Pos;
1089 
1090 	fusebits[0] &= (~FUSES_BOD12_ACTION_Msk);
1091 	fusebits[0] |= fb->bod12_action << FUSES_BOD12_ACTION_Pos;
1092 
1093 	fusebits[1] &= (~FUSES_BOD12_HYST_Msk);
1094 	fusebits[1] |= fb->bod12_hysteresis << FUSES_BOD12_HYST_Pos;
1095 #endif
1096 
1097 	error_code = nvm_execute_command(NVM_COMMAND_ERASE_AUX_ROW,NVMCTRL_AUX0_ADDRESS,0);
1098 	if (error_code != STATUS_OK) {
1099 		return error_code;
1100 	}
1101 
1102 	error_code = nvm_execute_command(NVM_COMMAND_PAGE_BUFFER_CLEAR,NVMCTRL_AUX0_ADDRESS,0);
1103 	if (error_code != STATUS_OK) {
1104 		return error_code;
1105 	}
1106 
1107 	*((uint32_t *)NVMCTRL_AUX0_ADDRESS) = fusebits[0];
1108     *(((uint32_t *)NVMCTRL_AUX0_ADDRESS) + 1) = fusebits[1];
1109 
1110 	error_code = nvm_execute_command(NVM_COMMAND_WRITE_AUX_ROW,NVMCTRL_AUX0_ADDRESS,0);
1111 	if (error_code != STATUS_OK) {
1112 		return error_code;
1113 	}
1114 
1115 	return error_code;
1116 }
1117