nvm.c 35 KB

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