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 | } | ||