bootloader.c 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. /*
  2. * CNC-remote-control
  3. * Button processor - Bootloader
  4. *
  5. * Copyright (C) 2011-2016 Michael Buesch <m@bues.ch>
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * version 2 as published by the Free Software Foundation.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. */
  16. #include "util.h"
  17. #include "spi_interface.h"
  18. #include <avr/io.h>
  19. #include <avr/interrupt.h>
  20. #include <avr/wdt.h>
  21. #include <avr/pgmspace.h>
  22. #include <avr/eeprom.h>
  23. #include <avr/boot.h>
  24. #include <util/crc16.h>
  25. #include <string.h>
  26. #include <stdint.h>
  27. static uint8_t page_buffer[SPM_PAGESIZE];
  28. static void disable_all_irq_sources(void)
  29. {
  30. GICR = 0;
  31. TIMSK = 0;
  32. SPCR = 0;
  33. UCSRB = 0;
  34. ADCSRA = 0;
  35. EECR = 0;
  36. ACSR = 0;
  37. TWCR = 0;
  38. SPMCR = 0;
  39. }
  40. static void route_irqs_to_bootloader(void)
  41. {
  42. uint8_t tmp;
  43. __asm__ __volatile__(
  44. " ldi %[_tmp], %[_IVCE] \n"
  45. " out %[_GICR], %[_tmp] \n"
  46. " ldi %[_tmp], %[_IVSEL] \n"
  47. " out %[_GICR], %[_tmp] \n"
  48. : [_tmp] "=a" (tmp)
  49. : [_GICR] "I" (_SFR_IO_ADDR(GICR))
  50. , [_IVSEL] "M" (1 << IVSEL)
  51. , [_IVCE] "M" (1 << IVCE)
  52. );
  53. }
  54. static void route_irqs_to_application(void)
  55. {
  56. uint8_t tmp;
  57. __asm__ __volatile__(
  58. " ldi %[_tmp], %[_IVCE] \n"
  59. " out %[_GICR], %[_tmp] \n"
  60. " clr %[_tmp] \n"
  61. " out %[_GICR], %[_tmp] \n"
  62. : [_tmp] "=a" (tmp)
  63. : [_GICR] "I" (_SFR_IO_ADDR(GICR))
  64. , [_IVCE] "M" (1 << IVCE)
  65. );
  66. }
  67. static inline void spi_busy(bool busy)
  68. {
  69. if (busy) {
  70. /* Busy */
  71. SPI_SLAVE_TRANSIRQ_PORT = (uint8_t)(SPI_SLAVE_TRANSIRQ_PORT |
  72. (1u << SPI_SLAVE_TRANSIRQ_BIT));
  73. } else {
  74. /* Ready */
  75. SPI_SLAVE_TRANSIRQ_PORT = (uint8_t)(SPI_SLAVE_TRANSIRQ_PORT &
  76. ~(1u << SPI_SLAVE_TRANSIRQ_BIT));
  77. }
  78. }
  79. static void spi_init(void)
  80. {
  81. DDRB = (uint8_t)(DDRB | (1u << 4/*MISO*/));
  82. DDRB = (uint8_t)(DDRB & ~((1u << 5/*SCK*/) | (1u << 3/*MOSI*/) |
  83. (1u << 2/*SS*/)));
  84. spi_busy(1);
  85. SPI_SLAVE_TRANSIRQ_DDR = (uint8_t)(SPI_SLAVE_TRANSIRQ_DDR |
  86. (1u << SPI_SLAVE_TRANSIRQ_BIT));
  87. SPCR = (1u << SPE) | (0u << SPIE) | (0u << CPOL) | (0u << CPHA);
  88. SPSR = 0u;
  89. (void)SPSR; /* clear state */
  90. (void)SPDR; /* clear state */
  91. }
  92. static void spi_disable(void)
  93. {
  94. SPCR = 0;
  95. SPSR = 0;
  96. SPDR = 0;
  97. (void)SPSR; /* clear state */
  98. (void)SPDR; /* clear state */
  99. DDRB = 0;
  100. }
  101. static inline void spi_transwait(void)
  102. {
  103. while (!(SPSR & (1 << SPIF)));
  104. }
  105. static noinline uint8_t spi_xfer_sync(uint8_t tx)
  106. {
  107. uint8_t data;
  108. SPDR = tx;
  109. spi_busy(0);
  110. spi_transwait();
  111. spi_busy(1);
  112. data = SPDR;
  113. return data;
  114. }
  115. static noreturn noinline void exit_bootloader(void)
  116. {
  117. irq_disable();
  118. spi_disable();
  119. disable_all_irq_sources();
  120. wdt_enable(WDTO_2S);
  121. route_irqs_to_application();
  122. /* Jump to application code */
  123. __asm__ __volatile__(
  124. "ijmp\n"
  125. : /* None */
  126. : [_Z] "z" (0x0000)
  127. );
  128. unreachable();
  129. }
  130. static bool verify_page(uint16_t page_address)
  131. {
  132. uint8_t i, data0, data1;
  133. for (i = 0; i < ARRAY_SIZE(page_buffer); i++) {
  134. wdt_reset();
  135. data0 = page_buffer[i];
  136. data1 = pgm_read_byte((void PROGPTR *)(void *)(page_address + i));
  137. if (data0 != data1)
  138. return 0;
  139. }
  140. return 1;
  141. }
  142. static void write_page(uint16_t page_address)
  143. {
  144. uint8_t i, sreg;
  145. uint16_t data;
  146. eeprom_busy_wait();
  147. boot_spm_busy_wait();
  148. sreg = irq_disable_save();
  149. boot_page_erase(page_address);
  150. boot_spm_busy_wait();
  151. for (i = 0; i < SPM_PAGESIZE; i = (uint8_t)(i + 2u)) {
  152. wdt_reset();
  153. data = (uint16_t)(page_buffer[i]);
  154. data |= ((uint16_t)(page_buffer[i + 1]) << 8);
  155. boot_page_fill(page_address + i, data);
  156. }
  157. boot_page_write(page_address);
  158. boot_spm_busy_wait();
  159. boot_rww_enable();
  160. irq_restore(sreg);
  161. }
  162. static noinline uint8_t calc_crc8(uint8_t crc, uint8_t data)
  163. {
  164. return spi_crc8(crc, data);
  165. }
  166. static void do_flash(void)
  167. {
  168. uint8_t data, addr_lo, addr_hi;
  169. uint8_t crc = 0, expected_crc;
  170. uint16_t page_address, i;
  171. bool ok;
  172. addr_lo = spi_xfer_sync(0);
  173. crc = calc_crc8(crc, addr_lo);
  174. addr_hi = spi_xfer_sync(0);
  175. crc = calc_crc8(crc, addr_hi);
  176. page_address = (uint16_t)addr_lo | ((uint16_t)addr_hi << 8);
  177. for (i = 0; i < ARRAY_SIZE(page_buffer); i++) {
  178. data = spi_xfer_sync(0);
  179. page_buffer[i] = data;
  180. crc = calc_crc8(crc, data);
  181. }
  182. crc ^= 0xFF;
  183. expected_crc = spi_xfer_sync(0);
  184. if (expected_crc != crc) {
  185. spi_xfer_sync(SPI_RESULT_FAIL);
  186. return;
  187. }
  188. spi_xfer_sync(SPI_RESULT_OK);
  189. write_page(page_address);
  190. ok = verify_page(page_address);
  191. memset(page_buffer, 0xFF, sizeof(page_buffer));
  192. if (ok)
  193. spi_xfer_sync(SPI_RESULT_OK);
  194. else
  195. spi_xfer_sync(SPI_RESULT_FAIL);
  196. }
  197. static void handle_spi(void)
  198. {
  199. uint8_t data, txdata = 0;
  200. while (1) {
  201. data = spi_xfer_sync(txdata);
  202. txdata = 0;
  203. switch (data) {
  204. case SPI_CONTROL_ENTERBOOT:
  205. case SPI_CONTROL_ENTERBOOT2:
  206. /* We're already here. */
  207. txdata = SPI_RESULT_OK;
  208. break;
  209. case SPI_CONTROL_TESTAPP:
  210. txdata = SPI_RESULT_FAIL;
  211. break;
  212. case SPI_CONTROL_ENTERAPP:
  213. exit_bootloader();
  214. break;
  215. case SPI_CONTROL_STARTFLASH:
  216. do_flash();
  217. break;
  218. default:
  219. /* Ignore unknown commands */
  220. break;
  221. }
  222. }
  223. }
  224. static uint8_t saved_mcucsr __attribute__((section(".noinit")));
  225. void early_init(void) __attribute__((naked, section(".init3"), used));
  226. void early_init(void)
  227. {
  228. irq_disable();
  229. saved_mcucsr = MCUCSR;
  230. MCUCSR = 0;
  231. wdt_enable(WDTO_2S);
  232. }
  233. int main(void) _mainfunc;
  234. int main(void)
  235. {
  236. uint8_t mcucsr;
  237. irq_disable();
  238. wdt_enable(WDTO_2S);
  239. mcucsr = saved_mcucsr;
  240. saved_mcucsr = 0;
  241. if (!(mcucsr & (1 << PORF))) {
  242. if ((mcucsr & (1 << WDRF)) ||
  243. (mcucsr & (1 << BORF)))
  244. exit_bootloader();
  245. }
  246. disable_all_irq_sources();
  247. route_irqs_to_bootloader();
  248. spi_init();
  249. while (1) {
  250. wdt_reset();
  251. handle_spi();
  252. }
  253. }