diff options
Diffstat (limited to 'platforms/avr/bootloader.c')
| -rw-r--r-- | platforms/avr/bootloader.c | 293 |
1 files changed, 293 insertions, 0 deletions
diff --git a/platforms/avr/bootloader.c b/platforms/avr/bootloader.c new file mode 100644 index 000000000..c0272903b --- /dev/null +++ b/platforms/avr/bootloader.c | |||
| @@ -0,0 +1,293 @@ | |||
| 1 | #include <stdint.h> | ||
| 2 | #include <stdbool.h> | ||
| 3 | #include <avr/io.h> | ||
| 4 | #include <avr/eeprom.h> | ||
| 5 | #include <avr/interrupt.h> | ||
| 6 | #include <avr/wdt.h> | ||
| 7 | #include <util/delay.h> | ||
| 8 | #include "bootloader.h" | ||
| 9 | #include <avr/boot.h> | ||
| 10 | |||
| 11 | #ifdef PROTOCOL_LUFA | ||
| 12 | # include <LUFA/Drivers/USB/USB.h> | ||
| 13 | #endif | ||
| 14 | |||
| 15 | /** \brief Bootloader Size in *bytes* | ||
| 16 | * | ||
| 17 | * AVR Boot section size are defined by setting BOOTSZ fuse in fact. Consult with your MCU datasheet. | ||
| 18 | * Note that 'Word'(2 bytes) size and address are used in datasheet while TMK uses 'Byte'. | ||
| 19 | * | ||
| 20 | * Size of Bootloaders in bytes: | ||
| 21 | * Atmel DFU loader(ATmega32U4) 4096 | ||
| 22 | * Atmel DFU loader(AT90USB128) 8192 | ||
| 23 | * LUFA bootloader(ATmega32U4) 4096 | ||
| 24 | * Arduino Caterina(ATmega32U4) 4096 | ||
| 25 | * USBaspLoader(ATmega***) 2048 | ||
| 26 | * Teensy halfKay(ATmega32U4) 512 | ||
| 27 | * Teensy++ halfKay(AT90USB128) 1024 | ||
| 28 | * | ||
| 29 | * AVR Boot section is located at the end of Flash memory like the followings. | ||
| 30 | * | ||
| 31 | * byte Atmel/LUFA(ATMega32u4) byte Atmel(AT90SUB128) | ||
| 32 | * 0x0000 +---------------+ 0x00000 +---------------+ | ||
| 33 | * | | | | | ||
| 34 | * | | | | | ||
| 35 | * | Application | | Application | | ||
| 36 | * | | | | | ||
| 37 | * = = = = | ||
| 38 | * | | 32KB-4KB | | 128KB-8KB | ||
| 39 | * 0x7000 +---------------+ 0x1E000 +---------------+ | ||
| 40 | * | Bootloader | 4KB | Bootloader | 8KB | ||
| 41 | * 0x7FFF +---------------+ 0x1FFFF +---------------+ | ||
| 42 | * | ||
| 43 | * | ||
| 44 | * byte Teensy(ATMega32u4) byte Teensy++(AT90SUB128) | ||
| 45 | * 0x0000 +---------------+ 0x00000 +---------------+ | ||
| 46 | * | | | | | ||
| 47 | * | | | | | ||
| 48 | * | Application | | Application | | ||
| 49 | * | | | | | ||
| 50 | * = = = = | ||
| 51 | * | | 32KB-512B | | 128KB-1KB | ||
| 52 | * 0x7E00 +---------------+ 0x1FC00 +---------------+ | ||
| 53 | * | Bootloader | 512B | Bootloader | 1KB | ||
| 54 | * 0x7FFF +---------------+ 0x1FFFF +---------------+ | ||
| 55 | */ | ||
| 56 | #define FLASH_SIZE (FLASHEND + 1L) | ||
| 57 | |||
| 58 | #if !defined(BOOTLOADER_SIZE) | ||
| 59 | uint16_t bootloader_start; | ||
| 60 | #endif | ||
| 61 | |||
| 62 | // compatibility between ATMega8 and ATMega88 | ||
| 63 | #if !defined(MCUCSR) | ||
| 64 | # if defined(MCUSR) | ||
| 65 | # define MCUCSR MCUSR | ||
| 66 | # endif | ||
| 67 | #endif | ||
| 68 | |||
| 69 | /** \brief Entering the Bootloader via Software | ||
| 70 | * | ||
| 71 | * http://www.fourwalledcubicle.com/files/LUFA/Doc/120730/html/_page__software_bootloader_start.html | ||
| 72 | */ | ||
| 73 | #define BOOTLOADER_RESET_KEY 0xB007B007 | ||
| 74 | uint32_t reset_key __attribute__((section(".noinit,\"aw\",@nobits;"))); | ||
| 75 | |||
| 76 | /** \brief initialize MCU status by watchdog reset | ||
| 77 | * | ||
| 78 | * FIXME: needs doc | ||
| 79 | */ | ||
| 80 | __attribute__((weak)) void bootloader_jump(void) { | ||
| 81 | #if !defined(BOOTLOADER_SIZE) | ||
| 82 | uint8_t high_fuse = boot_lock_fuse_bits_get(GET_HIGH_FUSE_BITS); | ||
| 83 | |||
| 84 | if (high_fuse & ~(FUSE_BOOTSZ0 & FUSE_BOOTSZ1)) { | ||
| 85 | bootloader_start = (FLASH_SIZE - 512) >> 1; | ||
| 86 | } else if (high_fuse & ~(FUSE_BOOTSZ1)) { | ||
| 87 | bootloader_start = (FLASH_SIZE - 1024) >> 1; | ||
| 88 | } else if (high_fuse & ~(FUSE_BOOTSZ0)) { | ||
| 89 | bootloader_start = (FLASH_SIZE - 2048) >> 1; | ||
| 90 | } else { | ||
| 91 | bootloader_start = (FLASH_SIZE - 4096) >> 1; | ||
| 92 | } | ||
| 93 | #endif | ||
| 94 | |||
| 95 | // Something like this might work, but it compiled larger than the block above | ||
| 96 | // bootloader_start = FLASH_SIZE - (256 << (~high_fuse & 0b110 >> 1)); | ||
| 97 | |||
| 98 | #if defined(BOOTLOADER_HALFKAY) | ||
| 99 | // http://www.pjrc.com/teensy/jump_to_bootloader.html | ||
| 100 | cli(); | ||
| 101 | // disable watchdog, if enabled (it's not) | ||
| 102 | // disable all peripherals | ||
| 103 | // a shutdown call might make sense here | ||
| 104 | UDCON = 1; | ||
| 105 | USBCON = (1 << FRZCLK); // disable USB | ||
| 106 | UCSR1B = 0; | ||
| 107 | _delay_ms(5); | ||
| 108 | # if defined(__AVR_AT90USB162__) // Teensy 1.0 | ||
| 109 | EIMSK = 0; | ||
| 110 | PCICR = 0; | ||
| 111 | SPCR = 0; | ||
| 112 | ACSR = 0; | ||
| 113 | EECR = 0; | ||
| 114 | TIMSK0 = 0; | ||
| 115 | TIMSK1 = 0; | ||
| 116 | UCSR1B = 0; | ||
| 117 | DDRB = 0; | ||
| 118 | DDRC = 0; | ||
| 119 | DDRD = 0; | ||
| 120 | PORTB = 0; | ||
| 121 | PORTC = 0; | ||
| 122 | PORTD = 0; | ||
| 123 | asm volatile("jmp 0x3E00"); | ||
| 124 | # elif defined(__AVR_ATmega32U4__) // Teensy 2.0 | ||
| 125 | EIMSK = 0; | ||
| 126 | PCICR = 0; | ||
| 127 | SPCR = 0; | ||
| 128 | ACSR = 0; | ||
| 129 | EECR = 0; | ||
| 130 | ADCSRA = 0; | ||
| 131 | TIMSK0 = 0; | ||
| 132 | TIMSK1 = 0; | ||
| 133 | TIMSK3 = 0; | ||
| 134 | TIMSK4 = 0; | ||
| 135 | UCSR1B = 0; | ||
| 136 | TWCR = 0; | ||
| 137 | DDRB = 0; | ||
| 138 | DDRC = 0; | ||
| 139 | DDRD = 0; | ||
| 140 | DDRE = 0; | ||
| 141 | DDRF = 0; | ||
| 142 | TWCR = 0; | ||
| 143 | PORTB = 0; | ||
| 144 | PORTC = 0; | ||
| 145 | PORTD = 0; | ||
| 146 | PORTE = 0; | ||
| 147 | PORTF = 0; | ||
| 148 | asm volatile("jmp 0x7E00"); | ||
| 149 | # elif defined(__AVR_AT90USB646__) // Teensy++ 1.0 | ||
| 150 | EIMSK = 0; | ||
| 151 | PCICR = 0; | ||
| 152 | SPCR = 0; | ||
| 153 | ACSR = 0; | ||
| 154 | EECR = 0; | ||
| 155 | ADCSRA = 0; | ||
| 156 | TIMSK0 = 0; | ||
| 157 | TIMSK1 = 0; | ||
| 158 | TIMSK2 = 0; | ||
| 159 | TIMSK3 = 0; | ||
| 160 | UCSR1B = 0; | ||
| 161 | TWCR = 0; | ||
| 162 | DDRA = 0; | ||
| 163 | DDRB = 0; | ||
| 164 | DDRC = 0; | ||
| 165 | DDRD = 0; | ||
| 166 | DDRE = 0; | ||
| 167 | DDRF = 0; | ||
| 168 | PORTA = 0; | ||
| 169 | PORTB = 0; | ||
| 170 | PORTC = 0; | ||
| 171 | PORTD = 0; | ||
| 172 | PORTE = 0; | ||
| 173 | PORTF = 0; | ||
| 174 | asm volatile("jmp 0xFC00"); | ||
| 175 | # elif defined(__AVR_AT90USB1286__) // Teensy++ 2.0 | ||
| 176 | EIMSK = 0; | ||
| 177 | PCICR = 0; | ||
| 178 | SPCR = 0; | ||
| 179 | ACSR = 0; | ||
| 180 | EECR = 0; | ||
| 181 | ADCSRA = 0; | ||
| 182 | TIMSK0 = 0; | ||
| 183 | TIMSK1 = 0; | ||
| 184 | TIMSK2 = 0; | ||
| 185 | TIMSK3 = 0; | ||
| 186 | UCSR1B = 0; | ||
| 187 | TWCR = 0; | ||
| 188 | DDRA = 0; | ||
| 189 | DDRB = 0; | ||
| 190 | DDRC = 0; | ||
| 191 | DDRD = 0; | ||
| 192 | DDRE = 0; | ||
| 193 | DDRF = 0; | ||
| 194 | PORTA = 0; | ||
| 195 | PORTB = 0; | ||
| 196 | PORTC = 0; | ||
| 197 | PORTD = 0; | ||
| 198 | PORTE = 0; | ||
| 199 | PORTF = 0; | ||
| 200 | asm volatile("jmp 0x1FC00"); | ||
| 201 | # endif | ||
| 202 | |||
| 203 | #elif defined(BOOTLOADER_CATERINA) | ||
| 204 | // this block may be optional | ||
| 205 | // TODO: figure it out | ||
| 206 | |||
| 207 | uint16_t *const bootKeyPtr = (uint16_t *)0x0800; | ||
| 208 | |||
| 209 | // Value used by Caterina bootloader use to determine whether to run the | ||
| 210 | // sketch or the bootloader programmer. | ||
| 211 | uint16_t bootKey = 0x7777; | ||
| 212 | |||
| 213 | *bootKeyPtr = bootKey; | ||
| 214 | |||
| 215 | // setup watchdog timeout | ||
| 216 | wdt_enable(WDTO_60MS); | ||
| 217 | |||
| 218 | while (1) { | ||
| 219 | } // wait for watchdog timer to trigger | ||
| 220 | |||
| 221 | #elif defined(BOOTLOADER_USBASP) | ||
| 222 | // Taken with permission of Stephan Baerwolf from https://github.com/tinyusbboard/API/blob/master/apipage.c | ||
| 223 | wdt_enable(WDTO_15MS); | ||
| 224 | wdt_reset(); | ||
| 225 | asm volatile("cli \n\t" | ||
| 226 | "ldi r29 , %[ramendhi] \n\t" | ||
| 227 | "ldi r28 , %[ramendlo] \n\t" | ||
| 228 | # if (FLASHEND > 131071) | ||
| 229 | "ldi r18 , %[bootaddrhi] \n\t" | ||
| 230 | "st Y+, r18 \n\t" | ||
| 231 | # endif | ||
| 232 | "ldi r18 , %[bootaddrme] \n\t" | ||
| 233 | "st Y+, r18 \n\t" | ||
| 234 | "ldi r18 , %[bootaddrlo] \n\t" | ||
| 235 | "st Y+, r18 \n\t" | ||
| 236 | "out %[mcucsrio], __zero_reg__ \n\t" | ||
| 237 | "bootloader_startup_loop%=: \n\t" | ||
| 238 | "rjmp bootloader_startup_loop%= \n\t" | ||
| 239 | : | ||
| 240 | : [mcucsrio] "I"(_SFR_IO_ADDR(MCUCSR)), | ||
| 241 | # if (FLASHEND > 131071) | ||
| 242 | [ramendhi] "M"(((RAMEND - 2) >> 8) & 0xff), [ramendlo] "M"(((RAMEND - 2) >> 0) & 0xff), [bootaddrhi] "M"((((FLASH_SIZE - BOOTLOADER_SIZE) >> 1) >> 16) & 0xff), | ||
| 243 | # else | ||
| 244 | [ramendhi] "M"(((RAMEND - 1) >> 8) & 0xff), [ramendlo] "M"(((RAMEND - 1) >> 0) & 0xff), | ||
| 245 | # endif | ||
| 246 | [bootaddrme] "M"((((FLASH_SIZE - BOOTLOADER_SIZE) >> 1) >> 8) & 0xff), [bootaddrlo] "M"((((FLASH_SIZE - BOOTLOADER_SIZE) >> 1) >> 0) & 0xff)); | ||
| 247 | |||
| 248 | #else // Assume remaining boards are DFU, even if the flag isn't set | ||
| 249 | |||
| 250 | # if !(defined(__AVR_ATmega32A__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega328__) || defined(__AVR_ATtiny85__)) // no USB - maybe BOOTLOADER_BOOTLOADHID instead though? | ||
| 251 | UDCON = 1; | ||
| 252 | USBCON = (1 << FRZCLK); // disable USB | ||
| 253 | UCSR1B = 0; | ||
| 254 | _delay_ms(5); // 5 seems to work fine | ||
| 255 | # endif | ||
| 256 | |||
| 257 | # ifdef BOOTLOADER_BOOTLOADHID | ||
| 258 | // force bootloadHID to stay in bootloader mode, so that it waits | ||
| 259 | // for a new firmware to be flashed | ||
| 260 | eeprom_write_byte((uint8_t *)1, 0x00); | ||
| 261 | # endif | ||
| 262 | |||
| 263 | // watchdog reset | ||
| 264 | reset_key = BOOTLOADER_RESET_KEY; | ||
| 265 | wdt_enable(WDTO_250MS); | ||
| 266 | for (;;) | ||
| 267 | ; | ||
| 268 | #endif | ||
| 269 | } | ||
| 270 | |||
| 271 | /* this runs before main() */ | ||
| 272 | void bootloader_jump_after_watchdog_reset(void) __attribute__((used, naked, section(".init3"))); | ||
| 273 | void bootloader_jump_after_watchdog_reset(void) { | ||
| 274 | #ifndef BOOTLOADER_HALFKAY | ||
| 275 | if ((MCUCSR & (1 << WDRF)) && reset_key == BOOTLOADER_RESET_KEY) { | ||
| 276 | reset_key = 0; | ||
| 277 | |||
| 278 | // My custom USBasploader requires this to come up. | ||
| 279 | MCUCSR = 0; | ||
| 280 | |||
| 281 | // Seems like Teensy halfkay loader requires clearing WDRF and disabling watchdog. | ||
| 282 | MCUCSR &= ~(1 << WDRF); | ||
| 283 | wdt_disable(); | ||
| 284 | |||
| 285 | // This is compled into 'icall', address should be in word unit, not byte. | ||
| 286 | # ifdef BOOTLOADER_SIZE | ||
| 287 | ((void (*)(void))((FLASH_SIZE - BOOTLOADER_SIZE) >> 1))(); | ||
| 288 | # else | ||
| 289 | asm("ijmp" ::"z"(bootloader_start)); | ||
| 290 | # endif | ||
| 291 | } | ||
| 292 | #endif | ||
| 293 | } | ||
