aboutsummaryrefslogtreecommitdiff
path: root/platforms/avr/bootloader.c
diff options
context:
space:
mode:
Diffstat (limited to 'platforms/avr/bootloader.c')
-rw-r--r--platforms/avr/bootloader.c293
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)
59uint16_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
74uint32_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() */
272void bootloader_jump_after_watchdog_reset(void) __attribute__((used, naked, section(".init3")));
273void 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}