--- /dev/null
+/* 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