suspend.c 4.9 KB

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