suspend.c 4.9 KB

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