suspend.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. #include <stdbool.h>
  2. #include <avr/sleep.h>
  3. #include <avr/wdt.h>
  4. #include <avr/interrupt.h>
  5. #include "matrix.h"
  6. #include "action.h"
  7. #include "backlight.h"
  8. #include "suspend_avr.h"
  9. #include "suspend.h"
  10. #include "timer.h"
  11. #include "led.h"
  12. #include "host.h"
  13. #include "rgblight_reconfig.h"
  14. #ifdef PROTOCOL_LUFA
  15. #include "lufa.h"
  16. #endif
  17. #ifdef AUDIO_ENABLE
  18. #include "audio.h"
  19. #endif /* AUDIO_ENABLE */
  20. #if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE)
  21. #include "rgblight.h"
  22. #endif
  23. #define wdt_intr_enable(value) \
  24. __asm__ __volatile__ ( \
  25. "in __tmp_reg__,__SREG__" "\n\t" \
  26. "cli" "\n\t" \
  27. "wdr" "\n\t" \
  28. "sts %0,%1" "\n\t" \
  29. "out __SREG__,__tmp_reg__" "\n\t" \
  30. "sts %0,%2" "\n\t" \
  31. : /* no outputs */ \
  32. : "M" (_SFR_MEM_ADDR(_WD_CONTROL_REG)), \
  33. "r" (_BV(_WD_CHANGE_BIT) | _BV(WDE)), \
  34. "r" ((uint8_t) ((value & 0x08 ? _WD_PS3_MASK : 0x00) | \
  35. _BV(WDIE) | (value & 0x07)) ) \
  36. : "r0" \
  37. )
  38. /** \brief Suspend idle
  39. *
  40. * FIXME: needs doc
  41. */
  42. void suspend_idle(uint8_t time)
  43. {
  44. cli();
  45. set_sleep_mode(SLEEP_MODE_IDLE);
  46. sleep_enable();
  47. sei();
  48. sleep_cpu();
  49. sleep_disable();
  50. }
  51. // TODO: This needs some cleanup
  52. /** \brief Run keyboard level Power down
  53. *
  54. * FIXME: needs doc
  55. */
  56. __attribute__ ((weak))
  57. void suspend_power_down_user (void) { }
  58. /** \brief Run keyboard level Power down
  59. *
  60. * FIXME: needs doc
  61. */
  62. __attribute__ ((weak))
  63. void suspend_power_down_kb(void) {
  64. suspend_power_down_user();
  65. }
  66. #ifndef NO_SUSPEND_POWER_DOWN
  67. /** \brief Power down MCU with watchdog timer
  68. *
  69. * wdto: watchdog timer timeout defined in <avr/wdt.h>
  70. * WDTO_15MS
  71. * WDTO_30MS
  72. * WDTO_60MS
  73. * WDTO_120MS
  74. * WDTO_250MS
  75. * WDTO_500MS
  76. * WDTO_1S
  77. * WDTO_2S
  78. * WDTO_4S
  79. * WDTO_8S
  80. */
  81. static uint8_t wdt_timeout = 0;
  82. /** \brief Power down
  83. *
  84. * FIXME: needs doc
  85. */
  86. static void power_down(uint8_t wdto)
  87. {
  88. #ifdef PROTOCOL_LUFA
  89. if (USB_DeviceState == DEVICE_STATE_Configured) return;
  90. #endif
  91. wdt_timeout = wdto;
  92. // Watchdog Interrupt Mode
  93. wdt_intr_enable(wdto);
  94. #ifdef BACKLIGHT_ENABLE
  95. backlight_set(0);
  96. #endif
  97. // Turn off LED indicators
  98. led_set(0);
  99. #ifdef AUDIO_ENABLE
  100. // This sometimes disables the start-up noise, so it's been disabled
  101. // stop_all_notes();
  102. #endif /* AUDIO_ENABLE */
  103. #if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE)
  104. #ifdef RGBLIGHT_ANIMATIONS
  105. rgblight_timer_disable();
  106. #endif
  107. rgblight_disable_noeeprom();
  108. #endif
  109. suspend_power_down_kb();
  110. // TODO: more power saving
  111. // See PicoPower application note
  112. // - I/O port input with pullup
  113. // - prescale clock
  114. // - BOD disable
  115. // - Power Reduction Register PRR
  116. set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  117. sleep_enable();
  118. sei();
  119. sleep_cpu();
  120. sleep_disable();
  121. // Disable watchdog after sleep
  122. wdt_disable();
  123. }
  124. #endif
  125. /** \brief Suspend power down
  126. *
  127. * FIXME: needs doc
  128. */
  129. void suspend_power_down(void)
  130. {
  131. suspend_power_down_kb();
  132. #ifndef NO_SUSPEND_POWER_DOWN
  133. power_down(WDTO_15MS);
  134. #endif
  135. }
  136. __attribute__ ((weak)) void matrix_power_up(void) {}
  137. __attribute__ ((weak)) void matrix_power_down(void) {}
  138. bool suspend_wakeup_condition(void)
  139. {
  140. matrix_power_up();
  141. matrix_scan();
  142. matrix_power_down();
  143. for (uint8_t r = 0; r < MATRIX_ROWS; r++) {
  144. if (matrix_get_row(r)) return true;
  145. }
  146. return false;
  147. }
  148. /** \brief run user level code immediately after wakeup
  149. *
  150. * FIXME: needs doc
  151. */
  152. __attribute__ ((weak))
  153. void suspend_wakeup_init_user(void) { }
  154. /** \brief run keyboard level code immediately after wakeup
  155. *
  156. * FIXME: needs doc
  157. */
  158. __attribute__ ((weak))
  159. void suspend_wakeup_init_kb(void) {
  160. suspend_wakeup_init_user();
  161. }
  162. /** \brief run immediately after wakeup
  163. *
  164. * FIXME: needs doc
  165. */
  166. void suspend_wakeup_init(void)
  167. {
  168. // clear keyboard state
  169. clear_keyboard();
  170. #ifdef BACKLIGHT_ENABLE
  171. backlight_init();
  172. #endif
  173. led_set(host_keyboard_leds());
  174. #if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE)
  175. #ifdef BOOTLOADER_TEENSY
  176. wait_ms(10);
  177. #endif
  178. rgblight_enable_noeeprom();
  179. #ifdef RGBLIGHT_ANIMATIONS
  180. rgblight_timer_enable();
  181. #endif
  182. #endif
  183. suspend_wakeup_init_kb();
  184. }
  185. #ifndef NO_SUSPEND_POWER_DOWN
  186. /* watchdog timeout */
  187. ISR(WDT_vect)
  188. {
  189. // compensate timer for sleep
  190. switch (wdt_timeout) {
  191. case WDTO_15MS:
  192. timer_count += 15 + 2; // WDTO_15MS + 2(from observation)
  193. break;
  194. default:
  195. ;
  196. }
  197. }
  198. #endif