2 * Copyright © 2019 Keith Packard <keithp@keithp.com>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
23 /* Erase rows are four pages */
25 samd21_nvmctrl_row_size(void)
27 return samd21_nvmctrl_page_size() * 4;
30 /* size of a lock region. That's just total flash size / 16 */
32 samd21_nvmctrl_lock_region(void)
34 return samd21_flash_size() >> 4;
37 /* Find the bit index of an address within the lock word */
39 ao_flash_lock_region_bit(void *addr)
41 return (uint8_t) (((uintptr_t) addr) / samd21_nvmctrl_lock_region());
45 ao_flash_is_locked(void *addr)
47 return (samd21_nvmctrl.lock >> ao_flash_lock_region_bit(addr)) & 1;
50 /* Execute a single flash operation, waiting for it to complete. This
51 * bit of code must be in ram
53 static void __attribute__ ((section(".sdata2.flash"), noinline))
54 _ao_flash_execute(uint16_t cmd)
56 while ((samd21_nvmctrl.intflag & (1 << SAMD21_NVMCTRL_INTFLAG_READY)) == 0)
58 samd21_nvmctrl.ctrla = ((cmd << SAMD21_NVMCTRL_CTRLA_CMD) |
59 (SAMD21_NVMCTRL_CTRLA_CMDEX_KEY << SAMD21_NVMCTRL_CTRLA_CMDEX));
60 while ((samd21_nvmctrl.intflag & (1 << SAMD21_NVMCTRL_INTFLAG_READY)) == 0)
62 samd21_nvmctrl.intflag = ((1 << SAMD21_NVMCTRL_INTFLAG_READY) |
63 (1 << SAMD21_NVMCTRL_INTFLAG_ERROR));
66 /* Set the address of the next flash operation */
68 _ao_flash_set_addr(void *addr)
70 while ((samd21_nvmctrl.intflag & (1 << SAMD21_NVMCTRL_INTFLAG_READY)) == 0)
72 samd21_nvmctrl.addr = ((uint32_t) addr) >> 1;
73 while ((samd21_nvmctrl.intflag & (1 << SAMD21_NVMCTRL_INTFLAG_READY)) == 0)
77 /* Unlock a region of flash */
79 _ao_flash_unlock(void *addr)
81 if (!ao_flash_is_locked(addr))
84 _ao_flash_set_addr(addr);
85 _ao_flash_execute(SAMD21_NVMCTRL_CTRLA_CMD_UR);
88 /* Erase a row of flash */
90 _ao_flash_erase_row(void *row)
92 _ao_flash_unlock(row);
93 _ao_flash_set_addr(row);
94 _ao_flash_execute(SAMD21_NVMCTRL_CTRLA_CMD_ER);
98 ao_flash_erase_page(uint32_t *page)
100 uint8_t *row = (uint8_t *) page;
101 uint32_t row_size = samd21_nvmctrl_row_size();
102 uint32_t rows = (row_size + 255) / 256;
104 ao_arch_block_interrupts();
106 if (((uintptr_t) row & (row_size - 1)) == 0) {
108 _ao_flash_erase_row(row);
113 ao_arch_release_interrupts();
117 ao_flash_page(uint32_t *page, uint32_t *src)
119 uint32_t page_size = samd21_nvmctrl_page_size();
120 uint32_t pages = 256 / page_size;
122 uint32_t per_page = page_size / sizeof(uint32_t);
124 ao_arch_block_interrupts();
127 /* Clear write buffer */
128 _ao_flash_execute(SAMD21_NVMCTRL_CTRLA_CMD_PBC);
129 _ao_flash_set_addr(page);
130 for (i = 0; i < per_page; i++)
132 _ao_flash_execute(SAMD21_NVMCTRL_CTRLA_CMD_WP);
135 ao_arch_release_interrupts();