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