2015-04-09 18:32:04 +02:00
# include <stdint.h>
# include <stdbool.h>
# include <avr/io.h>
2017-01-21 18:30:06 +01:00
# include <avr/eeprom.h>
2015-04-09 18:32:04 +02:00
# include <avr/interrupt.h>
# include <avr/wdt.h>
# include <util/delay.h>
# include "bootloader.h"
2017-11-28 05:08:21 +01:00
# include <avr/boot.h>
2015-04-09 18:32:04 +02:00
# ifdef PROTOCOL_LUFA
2019-08-30 20:19:03 +02:00
# include <LUFA / Drivers / USB / USB.h>
2015-04-09 18:32:04 +02:00
# endif
2018-03-22 07:50:38 +01:00
/** \brief Bootloader Size in *bytes*
2015-05-13 08:01:49 +02:00
*
* AVR Boot section size are defined by setting BOOTSZ fuse in fact . Consult with your MCU datasheet .
* Note that ' Word ' ( 2 bytes ) size and address are used in datasheet while TMK uses ' Byte ' .
*
* Size of Bootloaders in bytes :
* Atmel DFU loader ( ATmega32U4 ) 4096
* Atmel DFU loader ( AT90USB128 ) 8192
* LUFA bootloader ( ATmega32U4 ) 4096
* Arduino Caterina ( ATmega32U4 ) 4096
* USBaspLoader ( ATmega * * * ) 2048
* Teensy halfKay ( ATmega32U4 ) 512
* Teensy + + halfKay ( AT90USB128 ) 1024
*
* AVR Boot section is located at the end of Flash memory like the followings .
*
* byte Atmel / LUFA ( ATMega32u4 ) byte Atmel ( AT90SUB128 )
* 0x0000 + - - - - - - - - - - - - - - - + 0x00000 + - - - - - - - - - - - - - - - +
* | | | |
* | | | |
* | Application | | Application |
* | | | |
* = = = =
* | | 32 KB - 4 KB | | 128 KB - 8 KB
2016-09-29 03:26:41 +02:00
* 0x7000 + - - - - - - - - - - - - - - - + 0x1E000 + - - - - - - - - - - - - - - - +
2015-05-13 08:01:49 +02:00
* | Bootloader | 4 KB | Bootloader | 8 KB
* 0x7FFF + - - - - - - - - - - - - - - - + 0x1FFFF + - - - - - - - - - - - - - - - +
*
*
* byte Teensy ( ATMega32u4 ) byte Teensy + + ( AT90SUB128 )
* 0x0000 + - - - - - - - - - - - - - - - + 0x00000 + - - - - - - - - - - - - - - - +
* | | | |
* | | | |
* | Application | | Application |
* | | | |
* = = = =
* | | 32 KB - 512 B | | 128 KB - 1 KB
* 0x7E00 + - - - - - - - - - - - - - - - + 0x1FC00 + - - - - - - - - - - - - - - - +
* | Bootloader | 512 B | Bootloader | 1 KB
* 0x7FFF + - - - - - - - - - - - - - - - + 0x1FFFF + - - - - - - - - - - - - - - - +
2015-04-09 18:32:04 +02:00
*/
2017-11-28 05:08:21 +01:00
# define FLASH_SIZE (FLASHEND + 1L)
# if !defined(BOOTLOADER_SIZE)
2019-08-30 20:19:03 +02:00
uint16_t bootloader_start ;
2017-11-28 05:08:21 +01:00
# endif
2015-04-09 18:32:04 +02:00
2019-08-30 20:19:03 +02:00
// compatibility between ATMega8 and ATMega88
# if !defined(MCUCSR)
# if defined(MCUSR)
# define MCUCSR MCUSR
# endif
2019-07-16 04:11:59 +02:00
# endif
2018-03-22 07:50:38 +01:00
/** \brief Entering the Bootloader via Software
*
2015-04-09 18:32:04 +02:00
* http : //www.fourwalledcubicle.com/files/LUFA/Doc/120730/html/_page__software_bootloader_start.html
*/
# define BOOTLOADER_RESET_KEY 0xB007B007
2019-08-30 20:19:03 +02:00
uint32_t reset_key __attribute__ ( ( section ( " .noinit, \" aw \" ,@nobits; " ) ) ) ;
2015-04-09 18:32:04 +02:00
2018-05-16 22:51:24 +02:00
/** \brief initialize MCU status by watchdog reset
2018-03-22 07:50:38 +01:00
*
* FIXME : needs doc
*/
2015-04-09 18:32:04 +02:00
void bootloader_jump ( void ) {
2019-08-30 20:19:03 +02:00
# if !defined(BOOTLOADER_SIZE)
uint8_t high_fuse = boot_lock_fuse_bits_get ( GET_HIGH_FUSE_BITS ) ;
2017-01-21 18:30:06 +01:00
2019-09-07 17:12:46 +02:00
if ( high_fuse & ~ ( FUSE_BOOTSZ0 & FUSE_BOOTSZ1 ) ) {
2019-08-30 20:19:03 +02:00
bootloader_start = ( FLASH_SIZE - 512 ) > > 1 ;
2019-09-07 17:12:46 +02:00
} else if ( high_fuse & ~ ( FUSE_BOOTSZ1 ) ) {
2019-08-30 20:19:03 +02:00
bootloader_start = ( FLASH_SIZE - 1024 ) > > 1 ;
2019-09-07 17:12:46 +02:00
} else if ( high_fuse & ~ ( FUSE_BOOTSZ0 ) ) {
2019-08-30 20:19:03 +02:00
bootloader_start = ( FLASH_SIZE - 2048 ) > > 1 ;
} else {
bootloader_start = ( FLASH_SIZE - 4096 ) > > 1 ;
}
# endif
2016-07-07 04:48:19 +02:00
2017-11-28 05:08:21 +01:00
// Something like this might work, but it compiled larger than the block above
// bootloader_start = FLASH_SIZE - (256 << (~high_fuse & 0b110 >> 1));
2019-08-30 20:19:03 +02:00
# if defined(BOOTLOADER_HALFKAY)
// http://www.pjrc.com/teensy/jump_to_bootloader.html
cli ( ) ;
// disable watchdog, if enabled (it's not)
// disable all peripherals
// a shutdown call might make sense here
UDCON = 1 ;
USBCON = ( 1 < < FRZCLK ) ; // disable USB
UCSR1B = 0 ;
_delay_ms ( 5 ) ;
# if defined(__AVR_AT90USB162__) // Teensy 1.0
EIMSK = 0 ;
PCICR = 0 ;
SPCR = 0 ;
ACSR = 0 ;
EECR = 0 ;
TIMSK0 = 0 ;
TIMSK1 = 0 ;
UCSR1B = 0 ;
DDRB = 0 ;
DDRC = 0 ;
DDRD = 0 ;
PORTB = 0 ;
PORTC = 0 ;
PORTD = 0 ;
asm volatile ( " jmp 0x3E00 " ) ;
# elif defined(__AVR_ATmega32U4__) // Teensy 2.0
EIMSK = 0 ;
PCICR = 0 ;
SPCR = 0 ;
ACSR = 0 ;
EECR = 0 ;
ADCSRA = 0 ;
TIMSK0 = 0 ;
TIMSK1 = 0 ;
TIMSK3 = 0 ;
TIMSK4 = 0 ;
UCSR1B = 0 ;
TWCR = 0 ;
DDRB = 0 ;
DDRC = 0 ;
DDRD = 0 ;
DDRE = 0 ;
DDRF = 0 ;
TWCR = 0 ;
PORTB = 0 ;
PORTC = 0 ;
PORTD = 0 ;
PORTE = 0 ;
PORTF = 0 ;
asm volatile ( " jmp 0x7E00 " ) ;
# elif defined(__AVR_AT90USB646__) // Teensy++ 1.0
EIMSK = 0 ;
PCICR = 0 ;
SPCR = 0 ;
ACSR = 0 ;
EECR = 0 ;
ADCSRA = 0 ;
TIMSK0 = 0 ;
TIMSK1 = 0 ;
TIMSK2 = 0 ;
TIMSK3 = 0 ;
UCSR1B = 0 ;
TWCR = 0 ;
DDRA = 0 ;
DDRB = 0 ;
DDRC = 0 ;
DDRD = 0 ;
DDRE = 0 ;
DDRF = 0 ;
PORTA = 0 ;
PORTB = 0 ;
PORTC = 0 ;
PORTD = 0 ;
PORTE = 0 ;
PORTF = 0 ;
asm volatile ( " jmp 0xFC00 " ) ;
# elif defined(__AVR_AT90USB1286__) // Teensy++ 2.0
EIMSK = 0 ;
PCICR = 0 ;
SPCR = 0 ;
ACSR = 0 ;
EECR = 0 ;
ADCSRA = 0 ;
TIMSK0 = 0 ;
TIMSK1 = 0 ;
TIMSK2 = 0 ;
TIMSK3 = 0 ;
UCSR1B = 0 ;
TWCR = 0 ;
DDRA = 0 ;
DDRB = 0 ;
DDRC = 0 ;
DDRD = 0 ;
DDRE = 0 ;
DDRF = 0 ;
PORTA = 0 ;
PORTB = 0 ;
PORTC = 0 ;
PORTD = 0 ;
PORTE = 0 ;
PORTF = 0 ;
asm volatile ( " jmp 0x1FC00 " ) ;
# endif
# elif defined(BOOTLOADER_CATERINA)
// this block may be optional
// TODO: figure it out
uint16_t * const bootKeyPtr = ( uint16_t * ) 0x0800 ;
// Value used by Caterina bootloader use to determine whether to run the
// sketch or the bootloader programmer.
uint16_t bootKey = 0x7777 ;
* bootKeyPtr = bootKey ;
// setup watchdog timeout
wdt_enable ( WDTO_60MS ) ;
2017-11-28 05:08:21 +01:00
2019-08-30 20:19:03 +02:00
while ( 1 ) {
} // wait for watchdog timer to trigger
2017-11-28 05:08:21 +01:00
2019-08-30 20:19:03 +02:00
# elif defined(BOOTLOADER_USBASP)
// Taken with permission of Stephan Baerwolf from https://github.com/tinyusbboard/API/blob/master/apipage.c
wdt_enable ( WDTO_15MS ) ;
wdt_reset ( ) ;
asm volatile ( " cli \n \t "
" ldi r29 , %[ramendhi] \n \t "
" ldi r28 , %[ramendlo] \n \t "
# if (FLASHEND > 131071)
" ldi r18 , %[bootaddrhi] \n \t "
" st Y+, r18 \n \t "
# endif
" ldi r18 , %[bootaddrme] \n \t "
" st Y+, r18 \n \t "
" ldi r18 , %[bootaddrlo] \n \t "
" st Y+, r18 \n \t "
" out %[mcucsrio], __zero_reg__ \n \t "
" bootloader_startup_loop%=: \n \t "
" rjmp bootloader_startup_loop%= \n \t "
:
2020-04-02 23:59:37 +02:00
: [ mcucsrio ] " I " ( _SFR_IO_ADDR ( MCUCSR ) ) ,
2019-08-30 20:19:03 +02:00
# if (FLASHEND > 131071)
2020-04-02 23:59:37 +02:00
[ ramendhi ] " M " ( ( ( RAMEND - 2 ) > > 8 ) & 0xff ) , [ ramendlo ] " M " ( ( ( RAMEND - 2 ) > > 0 ) & 0xff ) , [ bootaddrhi ] " M " ( ( ( ( FLASH_SIZE - BOOTLOADER_SIZE ) > > 1 ) > > 16 ) & 0xff ) ,
2019-08-30 20:19:03 +02:00
# else
2020-04-02 23:59:37 +02:00
[ ramendhi ] " M " ( ( ( RAMEND - 1 ) > > 8 ) & 0xff ) , [ ramendlo ] " M " ( ( ( RAMEND - 1 ) > > 0 ) & 0xff ) ,
2019-08-30 20:19:03 +02:00
# endif
2020-04-02 23:59:37 +02:00
[ bootaddrme ] " M " ( ( ( ( FLASH_SIZE - BOOTLOADER_SIZE ) > > 1 ) > > 8 ) & 0xff ) , [ bootaddrlo ] " M " ( ( ( ( FLASH_SIZE - BOOTLOADER_SIZE ) > > 1 ) > > 0 ) & 0xff ) ) ;
2019-08-30 20:19:03 +02:00
# else // Assume remaining boards are DFU, even if the flag isn't set
2020-04-02 23:23:57 +02:00
# if !(defined(__AVR_ATmega32A__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATtiny85__)) // no USB - maybe BOOTLOADER_BOOTLOADHID instead though?
2019-08-30 20:19:03 +02:00
UDCON = 1 ;
USBCON = ( 1 < < FRZCLK ) ; // disable USB
UCSR1B = 0 ;
_delay_ms ( 5 ) ; // 5 seems to work fine
# endif
# ifdef BOOTLOADER_BOOTLOADHID
// force bootloadHID to stay in bootloader mode, so that it waits
// for a new firmware to be flashed
eeprom_write_byte ( ( uint8_t * ) 1 , 0x00 ) ;
# endif
// watchdog reset
reset_key = BOOTLOADER_RESET_KEY ;
wdt_enable ( WDTO_250MS ) ;
for ( ; ; )
;
# endif
2016-07-07 04:48:19 +02:00
}
2015-04-09 18:32:04 +02:00
/* this runs before main() */
2019-08-30 20:19:03 +02:00
void bootloader_jump_after_watchdog_reset ( void ) __attribute__ ( ( used , naked , section ( " .init3 " ) ) ) ;
void bootloader_jump_after_watchdog_reset ( void ) {
# ifndef BOOTLOADER_HALFKAY
if ( ( MCUCSR & ( 1 < < WDRF ) ) & & reset_key = = BOOTLOADER_RESET_KEY ) {
reset_key = 0 ;
// My custom USBasploader requires this to come up.
MCUCSR = 0 ;
// Seems like Teensy halfkay loader requires clearing WDRF and disabling watchdog.
MCUCSR & = ~ ( 1 < < WDRF ) ;
wdt_disable ( ) ;
// This is compled into 'icall', address should be in word unit, not byte.
# ifdef BOOTLOADER_SIZE
( ( void ( * ) ( void ) ) ( ( FLASH_SIZE - BOOTLOADER_SIZE ) > > 1 ) ) ( ) ;
# else
asm ( " ijmp " : : " z " ( bootloader_start ) ) ;
# endif
}
# endif
2015-04-09 18:32:04 +02:00
}