|
| 1 | +/* |
| 2 | + This file is part of the SAMD Bootloader Updater library. |
| 3 | + Copyright (c) 2018 Arduino SA. All rights reserved. |
| 4 | +
|
| 5 | + This library is free software; you can redistribute it and/or |
| 6 | + modify it under the terms of the GNU Lesser General Public |
| 7 | + License as published by the Free Software Foundation; either |
| 8 | + version 2.1 of the License, or (at your option) any later version. |
| 9 | +
|
| 10 | + This library is distributed in the hope that it will be useful, |
| 11 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 13 | + Lesser General Public License for more details. |
| 14 | +
|
| 15 | + You should have received a copy of the GNU Lesser General Public |
| 16 | + License along with this library; if not, write to the Free Software |
| 17 | + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| 18 | +*/ |
| 19 | + |
| 20 | +#include "SAMD_BootloaderUpdater.h" |
| 21 | + |
| 22 | +#define PAGE_SIZE (64) |
| 23 | +#define PAGES (4096) |
| 24 | +#define MAX_FLASH (PAGE_SIZE * PAGES) |
| 25 | +#define ROW_SIZE (PAGE_SIZE * 4) |
| 26 | + |
| 27 | +__attribute__((aligned (4))) |
| 28 | +uint8_t booloaderData[8192] = { |
| 29 | +#if defined(ARDUINO_SAMD_MKRVIDOR4000) |
| 30 | + #include "bootloaders/mkrvidor4000.h" |
| 31 | +#else |
| 32 | + #error "Unsupported board!" |
| 33 | +#endif |
| 34 | +}; |
| 35 | + |
| 36 | +#define BOOTLOADER_START 0x00000000 |
| 37 | +#define USER_ROW_START 0x00804000 |
| 38 | + |
| 39 | +extern "C" { |
| 40 | + // these functions must be in RAM (.data) and NOT inlined |
| 41 | + // as they erase and copy the sketch data in flash |
| 42 | + |
| 43 | + __attribute__ ((long_call, noinline, section (".data#"))) |
| 44 | + static void eraseFlash(uint32_t address, int length) |
| 45 | + { |
| 46 | + for (int i = 0; i < length; i += ROW_SIZE) { |
| 47 | + NVMCTRL->ADDR.reg = ((uint32_t)((uint32_t)address + i)) / 2; |
| 48 | + NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_ER; |
| 49 | + |
| 50 | + while (!NVMCTRL->INTFLAG.bit.READY); |
| 51 | + } |
| 52 | + } |
| 53 | + |
| 54 | + __attribute__ ((long_call, noinline, section (".data#"))) |
| 55 | + static void readFlash(uint32_t src, void* dest, int length) |
| 56 | + { |
| 57 | + memcpy(dest, (void*)src, length); |
| 58 | + } |
| 59 | + |
| 60 | + __attribute__ ((long_call, noinline, section (".data#"))) |
| 61 | + static void writeFlash(uint32_t dest, const void* src, int length) |
| 62 | + { |
| 63 | + volatile uint32_t* d = (uint32_t*)dest; |
| 64 | + const volatile uint32_t* s = (const uint32_t*)src; |
| 65 | + |
| 66 | + for (int i = 0; i < length; i += 4) { |
| 67 | + *d++ = *s++; |
| 68 | + |
| 69 | + while (!NVMCTRL->INTFLAG.bit.READY); |
| 70 | + } |
| 71 | + } |
| 72 | +} |
| 73 | + |
| 74 | +bool SAMD_BootloaderUpdaterClass::needsUpdate() |
| 75 | +{ |
| 76 | + return (memcmp(BOOTLOADER_START, booloaderData, sizeof(booloaderData)) == 0); |
| 77 | +} |
| 78 | + |
| 79 | +int SAMD_BootloaderUpdaterClass::update(void(*progressCallback)(float)) |
| 80 | +{ |
| 81 | + // enable auto page writes |
| 82 | + NVMCTRL->CTRLB.bit.MANW = 0; |
| 83 | + |
| 84 | + // copy the user row, and set the BOOTPROT size to 0 |
| 85 | + uint32_t userRow[PAGE_SIZE / sizeof(uint32_t)]; |
| 86 | + readFlash(USER_ROW_START, userRow, sizeof(userRow)); |
| 87 | + userRow[0] |= 0x00000007; |
| 88 | + |
| 89 | + // erase the user row and flash the update value |
| 90 | + eraseFlash(USER_ROW_START, sizeof(userRow)); |
| 91 | + writeFlash(USER_ROW_START, userRow, sizeof(userRow)); |
| 92 | + |
| 93 | + if (progressCallback) { |
| 94 | + progressCallback(0.0); |
| 95 | + } |
| 96 | + |
| 97 | + // erase and copy the flash row by row |
| 98 | + for (size_t i = 0; i < sizeof(booloaderData); i += ROW_SIZE) { |
| 99 | + eraseFlash(BOOTLOADER_START + i, ROW_SIZE); |
| 100 | + writeFlash(BOOTLOADER_START + i, &booloaderData[i], ROW_SIZE); |
| 101 | + |
| 102 | + if (progressCallback) { |
| 103 | + progressCallback((i + ROW_SIZE) * 100.0 / sizeof(booloaderData)); |
| 104 | + } |
| 105 | + } |
| 106 | + |
| 107 | + return needsUpdate() ? 1 : 0; |
| 108 | +} |
| 109 | + |
| 110 | +SAMD_BootloaderUpdaterClass SAMD_BootloaderUpdater; |
0 commit comments