-/* Copyright (c) 2002, 2003, 2004 Eric B. Weddington\r
- All rights reserved.\r
-\r
- Redistribution and use in source and binary forms, with or without\r
- modification, are permitted provided that the following conditions are met:\r
-\r
- * Redistributions of source code must retain the above copyright\r
- notice, this list of conditions and the following disclaimer.\r
- * Redistributions in binary form must reproduce the above copyright\r
- notice, this list of conditions and the following disclaimer in\r
- the documentation and/or other materials provided with the\r
- distribution.\r
- * Neither the name of the copyright holders nor the names of\r
- contributors may be used to endorse or promote products derived\r
- from this software without specific prior written permission.\r
-\r
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r
- ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE\r
- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r
- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r
- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r
- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\r
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r
- POSSIBILITY OF SUCH DAMAGE. */\r
-\r
-#ifndef _AVR_BOOT_H_\r
-#define _AVR_BOOT_H_ 1\r
-\r
-/** \defgroup avr_boot Bootloader Support Utilities\r
- \code\r
- #include <avr/io.h>\r
- #include <avr/boot.h>\r
- \endcode\r
-\r
- The macros in this module provide a C language interface to the\r
- bootloader support functionality of certain AVR processors. These\r
- macros are designed to work with all sizes of flash memory.\r
-\r
- \note Not all AVR processors provide bootloader support. See your\r
- processor datasheet to see if it provides bootloader support.\r
-\r
- \todo From email with Marek: On smaller devices (all except ATmega64/128),\r
- __SPM_REG is in the I/O space, accessible with the shorter "in" and "out"\r
- instructions - since the boot loader has a limited size, this could be an\r
- important optimization.\r
-\r
- \par API Usage Example\r
- The following code shows typical usage of the boot API.\r
-\r
- \code\r
- #include <avr/interrupt.h>\r
- #include <avr/pgmspace.h>\r
- \r
- #define ADDRESS 0x1C000UL\r
- \r
- void boot_test(void)\r
- {\r
- unsigned char buffer[8];\r
- \r
- cli();\r
- \r
- // Erase page.\r
- boot_page_erase((unsigned long)ADDRESS);\r
- while(boot_rww_busy())\r
- {\r
- boot_rww_enable();\r
- }\r
- \r
- // Write data to buffer a word at a time. Note incrementing address\r
- // by 2. SPM_PAGESIZE is defined in the microprocessor IO header file.\r
- for(unsigned long i = ADDRESS; i < ADDRESS + SPM_PAGESIZE; i += 2)\r
- {\r
- boot_page_fill(i, (i-ADDRESS) + ((i-ADDRESS+1) << 8));\r
- }\r
- \r
- // Write page.\r
- boot_page_write((unsigned long)ADDRESS);\r
- while(boot_rww_busy())\r
- {\r
- boot_rww_enable();\r
- }\r
- \r
- sei();\r
- \r
- // Read back the values and display.\r
- // (The show() function is undefined and is used here as an example\r
- // only.)\r
- for(unsigned long i = ADDRESS; i < ADDRESS + 256; i++)\r
- {\r
- show(utoa(pgm_read_byte(i), buffer, 16));\r
- }\r
- \r
- return;\r
- }\endcode */\r
-\r
-#include <avr/eeprom.h>\r
-#include <avr/io.h>\r
-#include <inttypes.h>\r
-#include <limits.h>\r
-\r
-/* Check for SPM Control Register in processor. */\r
-#if defined (SPMCSR)\r
-# define __SPM_REG SPMCSR\r
-#elif defined (SPMCR)\r
-# define __SPM_REG SPMCR\r
-#else\r
-# error AVR processor does not provide bootloader support!\r
-#endif\r
-\r
-/** \ingroup avr_boot\r
- \def BOOTLOADER_SECTION\r
-\r
- Used to declare a function or variable to be placed into a\r
- new section called .bootloader. This section and its contents\r
- can then be relocated to any address (such as the bootloader\r
- NRWW area) at link-time. */\r
-\r
-#define BOOTLOADER_SECTION __attribute__ ((section (".bootloader")))\r
-\r
-/* Create common bit definitions. */\r
-#ifdef ASB\r
-#define __COMMON_ASB ASB\r
-#else\r
-#define __COMMON_ASB RWWSB\r
-#endif\r
-\r
-#ifdef ASRE\r
-#define __COMMON_ASRE ASRE\r
-#else\r
-#define __COMMON_ASRE RWWSRE\r
-#endif\r
-\r
-/* Define the bit positions of the Boot Lock Bits. */\r
-\r
-#define BLB12 5\r
-#define BLB11 4\r
-#define BLB02 3\r
-#define BLB01 2\r
-\r
-/** \ingroup avr_boot\r
- \def boot_spm_interrupt_enable()\r
- Enable the SPM interrupt. */\r
-\r
-#define boot_spm_interrupt_enable() (__SPM_REG |= (uint8_t)_BV(SPMIE))\r
-\r
-/** \ingroup avr_boot\r
- \def boot_spm_interrupt_disable()\r
- Disable the SPM interrupt. */\r
-\r
-#define boot_spm_interrupt_disable() (__SPM_REG &= (uint8_t)~_BV(SPMIE))\r
-\r
-/** \ingroup avr_boot\r
- \def boot_is_spm_interrupt()\r
- Check if the SPM interrupt is enabled. */\r
-\r
-#define boot_is_spm_interrupt() (__SPM_REG & (uint8_t)_BV(SPMIE))\r
-\r
-/** \ingroup avr_boot\r
- \def boot_rww_busy()\r
- Check if the RWW section is busy. */\r
-\r
-#define boot_rww_busy() (__SPM_REG & (uint8_t)_BV(__COMMON_ASB))\r
-\r
-/** \ingroup avr_boot\r
- \def boot_spm_busy()\r
- Check if the SPM instruction is busy. */\r
-\r
-#define boot_spm_busy() (__SPM_REG & (uint8_t)_BV(SPMEN))\r
-\r
-/** \ingroup avr_boot\r
- \def boot_spm_busy_wait()\r
- Wait while the SPM instruction is busy. */\r
-\r
-#define boot_spm_busy_wait() do{}while(boot_spm_busy())\r
-\r
-#define __BOOT_PAGE_ERASE (_BV(SPMEN) | _BV(PGERS))\r
-#define __BOOT_PAGE_WRITE (_BV(SPMEN) | _BV(PGWRT))\r
-#define __BOOT_PAGE_FILL _BV(SPMEN)\r
-#define __BOOT_RWW_ENABLE (_BV(SPMEN) | _BV(__COMMON_ASRE))\r
-#define __BOOT_LOCK_BITS_SET (_BV(SPMEN) | _BV(BLBSET))\r
-\r
-#define __BOOT_LOCK_BITS_MASK (_BV(BLB01) | _BV(BLB02) \\r
- | _BV(BLB11) | _BV(BLB12))\r
-\r
-#define eeprom_busy_wait() do {} while (!eeprom_is_ready())\r
-\r
-#define __boot_page_fill_normal(address, data) \\r
-({ \\r
- boot_spm_busy_wait(); \\r
- eeprom_busy_wait(); \\r
- __asm__ __volatile__ \\r
- ( \\r
- "movw r0, %3\n\t" \\r
- "movw r30, %2\n\t" \\r
- "sts %0, %1\n\t" \\r
- "spm\n\t" \\r
- "clr r1\n\t" \\r
- : "=m" (__SPM_REG) \\r
- : "r" ((uint8_t)__BOOT_PAGE_FILL), \\r
- "r" ((uint16_t)address), \\r
- "r" ((uint16_t)data) \\r
- : "r0", "r30", "r31" \\r
- ); \\r
-})\r
-\r
-#define __boot_page_fill_alternate(address, data)\\r
-({ \\r
- boot_spm_busy_wait(); \\r
- eeprom_busy_wait(); \\r
- __asm__ __volatile__ \\r
- ( \\r
- "movw r0, %3\n\t" \\r
- "movw r30, %2\n\t" \\r
- "sts %0, %1\n\t" \\r
- "spm\n\t" \\r
- ".word 0xffff\n\t" \\r
- "nop\n\t" \\r
- "clr r1\n\t" \\r
- : "=m" (__SPM_REG) \\r
- : "r" ((uint8_t)__BOOT_PAGE_FILL), \\r
- "r" ((uint16_t)address), \\r
- "r" ((uint16_t)data) \\r
- : "r0", "r30", "r31" \\r
- ); \\r
-})\r
-\r
-#define __boot_page_fill_extended(address, data) \\r
-({ \\r
- boot_spm_busy_wait(); \\r
- eeprom_busy_wait(); \\r
- __asm__ __volatile__ \\r
- ( \\r
- "movw r0, %4\n\t" \\r
- "movw r30, %A3\n\t" \\r
- "sts %1, %C3\n\t" \\r
- "sts %0, %2\n\t" \\r
- "spm\n\t" \\r
- "clr r1\n\t" \\r
- : "=m" (__SPM_REG), \\r
- "=m" (RAMPZ) \\r
- : "r" ((uint8_t)__BOOT_PAGE_FILL), \\r
- "r" ((uint32_t)address), \\r
- "r" ((uint16_t)data) \\r
- : "r0", "r30", "r31" \\r
- ); \\r
-})\r
-\r
-#define __boot_page_erase_normal(address) \\r
-({ \\r
- boot_spm_busy_wait(); \\r
- eeprom_busy_wait(); \\r
- __asm__ __volatile__ \\r
- ( \\r
- "movw r30, %2\n\t" \\r
- "sts %0, %1\n\t" \\r
- "spm\n\t" \\r
- : "=m" (__SPM_REG) \\r
- : "r" ((uint8_t)__BOOT_PAGE_ERASE), \\r
- "r" ((uint16_t)address) \\r
- : "r30", "r31" \\r
- ); \\r
-})\r
-\r
-#define __boot_page_erase_alternate(address) \\r
-({ \\r
- boot_spm_busy_wait(); \\r
- eeprom_busy_wait(); \\r
- __asm__ __volatile__ \\r
- ( \\r
- "movw r30, %2\n\t" \\r
- "sts %0, %1\n\t" \\r
- "spm\n\t" \\r
- ".word 0xffff\n\t" \\r
- "nop\n\t" \\r
- : "=m" (__SPM_REG) \\r
- : "r" ((uint8_t)__BOOT_PAGE_ERASE), \\r
- "r" ((uint16_t)address) \\r
- : "r30", "r31" \\r
- ); \\r
-})\r
-\r
-#define __boot_page_erase_extended(address) \\r
-({ \\r
- boot_spm_busy_wait(); \\r
- eeprom_busy_wait(); \\r
- __asm__ __volatile__ \\r
- ( \\r
- "movw r30, %A3\n\t" \\r
- "sts %1, %C3\n\t" \\r
- "sts %0, %2\n\t" \\r
- "spm\n\t" \\r
- : "=m" (__SPM_REG), \\r
- "=m" (RAMPZ) \\r
- : "r" ((uint8_t)__BOOT_PAGE_ERASE), \\r
- "r" ((uint32_t)address) \\r
- : "r30", "r31" \\r
- ); \\r
-})\r
-\r
-#define __boot_page_write_normal(address) \\r
-({ \\r
- boot_spm_busy_wait(); \\r
- eeprom_busy_wait(); \\r
- __asm__ __volatile__ \\r
- ( \\r
- "movw r30, %2\n\t" \\r
- "sts %0, %1\n\t" \\r
- "spm\n\t" \\r
- : "=m" (__SPM_REG) \\r
- : "r" ((uint8_t)__BOOT_PAGE_WRITE), \\r
- "r" ((uint16_t)address) \\r
- : "r30", "r31" \\r
- ); \\r
-})\r
-\r
-#define __boot_page_write_alternate(address) \\r
-({ \\r
- boot_spm_busy_wait(); \\r
- eeprom_busy_wait(); \\r
- __asm__ __volatile__ \\r
- ( \\r
- "movw r30, %2\n\t" \\r
- "sts %0, %1\n\t" \\r
- "spm\n\t" \\r
- ".word 0xffff\n\t" \\r
- "nop\n\t" \\r
- : "=m" (__SPM_REG) \\r
- : "r" ((uint8_t)__BOOT_PAGE_WRITE), \\r
- "r" ((uint16_t)address) \\r
- : "r30", "r31" \\r
- ); \\r
-})\r
-\r
-#define __boot_page_write_extended(address) \\r
-({ \\r
- boot_spm_busy_wait(); \\r
- eeprom_busy_wait(); \\r
- __asm__ __volatile__ \\r
- ( \\r
- "movw r30, %A3\n\t" \\r
- "sts %1, %C3\n\t" \\r
- "sts %0, %2\n\t" \\r
- "spm\n\t" \\r
- : "=m" (__SPM_REG), \\r
- "=m" (RAMPZ) \\r
- : "r" ((uint8_t)__BOOT_PAGE_WRITE), \\r
- "r" ((uint32_t)address) \\r
- : "r30", "r31" \\r
- ); \\r
-})\r
-\r
-#define __boot_rww_enable() \\r
-({ \\r
- boot_spm_busy_wait(); \\r
- eeprom_busy_wait(); \\r
- __asm__ __volatile__ \\r
- ( \\r
- "sts %0, %1\n\t" \\r
- "spm\n\t" \\r
- : "=m" (__SPM_REG) \\r
- : "r" ((uint8_t)__BOOT_RWW_ENABLE) \\r
- ); \\r
-})\r
-\r
-#define __boot_rww_enable_alternate() \\r
-({ \\r
- boot_spm_busy_wait(); \\r
- eeprom_busy_wait(); \\r
- __asm__ __volatile__ \\r
- ( \\r
- "sts %0, %1\n\t" \\r
- "spm\n\t" \\r
- ".word 0xffff\n\t" \\r
- "nop\n\t" \\r
- : "=m" (__SPM_REG) \\r
- : "r" ((uint8_t)__BOOT_RWW_ENABLE) \\r
- ); \\r
-})\r
-\r
-#define __boot_lock_bits_set(lock_bits) \\r
-({ \\r
- uint8_t value = (uint8_t)(lock_bits | __BOOT_LOCK_BITS_MASK); \\r
- boot_spm_busy_wait(); \\r
- eeprom_busy_wait(); \\r
- __asm__ __volatile__ \\r
- ( \\r
- "ldi r30, 1\n\t" \\r
- "ldi r31, 0\n\t" \\r
- "mov r0, %2\n\t" \\r
- "sts %0, %1\n\t" \\r
- "spm\n\t" \\r
- : "=m" (__SPM_REG) \\r
- : "r" ((uint8_t)__BOOT_LOCK_BITS_SET), \\r
- "r" (value) \\r
- : "r0", "r30", "r31" \\r
- ); \\r
-})\r
-\r
-#define __boot_lock_bits_set_alternate(lock_bits) \\r
-({ \\r
- uint8_t value = (uint8_t)(lock_bits | __BOOT_LOCK_BITS_MASK); \\r
- boot_spm_busy_wait(); \\r
- eeprom_busy_wait(); \\r
- __asm__ __volatile__ \\r
- ( \\r
- "ldi r30, 1\n\t" \\r
- "ldi r31, 0\n\t" \\r
- "mov r0, %2\n\t" \\r
- "sts %0, %1\n\t" \\r
- "spm\n\t" \\r
- ".word 0xffff\n\t" \\r
- "nop\n\t" \\r
- : "=m" (__SPM_REG) \\r
- : "r" ((uint8_t)__BOOT_LOCK_BITS_SET), \\r
- "r" (value) \\r
- : "r0", "r30", "r31" \\r
- ); \\r
-})\r
-\r
-/** \ingroup avr_boot\r
- \def boot_page_fill(address, data)\r
-\r
- Fill the bootloader temporary page buffer for flash \r
- address with data word. \r
-\r
- \note The address is a byte address. The data is a word. The AVR \r
- writes data to the buffer a word at a time, but addresses the buffer\r
- per byte! So, increment your address by 2 between calls, and send 2\r
- data bytes in a word format! The LSB of the data is written to the lower \r
- address; the MSB of the data is written to the higher address.*/\r
-\r
-/** \ingroup avr_boot\r
- \def boot_page_erase(address)\r
-\r
- Erase the flash page that contains address.\r
-\r
- \note address is a byte address in flash, not a word address. */\r
-\r
-/** \ingroup avr_boot\r
- \def boot_page_write(address)\r
-\r
- Write the bootloader temporary page buffer \r
- to flash page that contains address.\r
- \r
- \note address is a byte address in flash, not a word address. */\r
-\r
-/** \ingroup avr_boot\r
- \def boot_rww_enable()\r
-\r
- Enable the Read-While-Write memory section. */\r
-\r
-/** \ingroup avr_boot\r
- \def boot_lock_bits_set(lock_bits)\r
-\r
- Set the bootloader lock bits. */\r
-\r
-/* Normal versions of the macros use 16-bit addresses.\r
- Extended versions of the macros use 32-bit addresses.\r
- Alternate versions of the macros use 16-bit addresses and require special\r
- instruction sequences after LPM.\r
-\r
- FLASHEND is defined in the ioXXXX.h file.\r
- USHRT_MAX is defined in <limits.h>. */ \r
-\r
-#if defined(__AVR_ATmega161__) || defined(__AVR_ATmega163__) \\r
- || defined(__AVR_ATmega323__)\r
-\r
-/* Alternate: ATmega161/163/323 and 16 bit address */\r
-#define boot_page_fill(address, data) __boot_page_fill_alternate(address, data)\r
-#define boot_page_erase(address) __boot_page_erase_alternate(address)\r
-#define boot_page_write(address) __boot_page_write_alternate(address)\r
-#define boot_rww_enable() __boot_rww_enable_alternate()\r
-#define boot_lock_bits_set(lock_bits) __boot_lock_bits_set_alternate(lock_bits)\r
-\r
-#elif (FLASHEND > USHRT_MAX) && !defined(__USING_MINT8)\r
-\r
-/* Extended: >16 bit address */\r
-#define boot_page_fill(address, data) __boot_page_fill_extended(address, data)\r
-#define boot_page_erase(address) __boot_page_erase_extended(address)\r
-#define boot_page_write(address) __boot_page_write_extended(address)\r
-#define boot_rww_enable() __boot_rww_enable()\r
-#define boot_lock_bits_set(lock_bits) __boot_lock_bits_set(lock_bits)\r
-\r
-#else\r
-\r
-/* Normal: 16 bit address */\r
-#define boot_page_fill(address, data) __boot_page_fill_normal(address, data)\r
-#define boot_page_erase(address) __boot_page_erase_normal(address)\r
-#define boot_page_write(address) __boot_page_write_normal(address)\r
-#define boot_rww_enable() __boot_rww_enable()\r
-#define boot_lock_bits_set(lock_bits) __boot_lock_bits_set(lock_bits)\r
-\r
-#endif\r
-\r
-#endif /* _AVR_BOOT_H_ */\r