suspend.c 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  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 "suspend.h"
  8. #include "timer.h"
  9. #include "led.h"
  10. #include "host.h"
  11. #ifdef PROTOCOL_LUFA
  12. # include "lufa.h"
  13. #endif
  14. #ifdef PROTOCOL_VUSB
  15. # include "vusb.h"
  16. #endif
  17. #ifdef BACKLIGHT_ENABLE
  18. # include "backlight.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. #endif
  26. #ifdef LED_MATRIX_ENABLE
  27. # include "led_matrix.h"
  28. #endif
  29. #ifdef RGB_MATRIX_ENABLE
  30. # include "rgb_matrix.h"
  31. #endif
  32. /** \brief Suspend idle
  33. *
  34. * FIXME: needs doc
  35. */
  36. void suspend_idle(uint8_t time) {
  37. cli();
  38. set_sleep_mode(SLEEP_MODE_IDLE);
  39. sleep_enable();
  40. sei();
  41. sleep_cpu();
  42. sleep_disable();
  43. }
  44. // TODO: This needs some cleanup
  45. /** \brief Run keyboard level Power down
  46. *
  47. * FIXME: needs doc
  48. */
  49. __attribute__((weak)) void suspend_power_down_user(void) {}
  50. /** \brief Run keyboard level Power down
  51. *
  52. * FIXME: needs doc
  53. */
  54. __attribute__((weak)) void suspend_power_down_kb(void) { suspend_power_down_user(); }
  55. #if !defined(NO_SUSPEND_POWER_DOWN) && defined(WDT_vect)
  56. // clang-format off
  57. #define wdt_intr_enable(value) \
  58. __asm__ __volatile__ ( \
  59. "in __tmp_reg__,__SREG__" "\n\t" \
  60. "cli" "\n\t" \
  61. "wdr" "\n\t" \
  62. "sts %0,%1" "\n\t" \
  63. "out __SREG__,__tmp_reg__" "\n\t" \
  64. "sts %0,%2" "\n\t" \
  65. : /* no outputs */ \
  66. : "M" (_SFR_MEM_ADDR(_WD_CONTROL_REG)), \
  67. "r" (_BV(_WD_CHANGE_BIT) | _BV(WDE)), \
  68. "r" ((uint8_t) ((value & 0x08 ? _WD_PS3_MASK : 0x00) | _BV(WDIE) | (value & 0x07))) \
  69. : "r0" \
  70. )
  71. // clang-format on
  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. wdt_timeout = wdto;
  93. // Watchdog Interrupt Mode
  94. wdt_intr_enable(wdto);
  95. // TODO: more power saving
  96. // See PicoPower application note
  97. // - I/O port input with pullup
  98. // - prescale clock
  99. // - BOD disable
  100. // - Power Reduction Register PRR
  101. set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  102. sleep_enable();
  103. sei();
  104. sleep_cpu();
  105. sleep_disable();
  106. // Disable watchdog after sleep
  107. wdt_disable();
  108. }
  109. #endif
  110. /** \brief Suspend power down
  111. *
  112. * FIXME: needs doc
  113. */
  114. void suspend_power_down(void) {
  115. #ifdef PROTOCOL_LUFA
  116. if (USB_DeviceState == DEVICE_STATE_Configured) return;
  117. #endif
  118. #ifdef PROTOCOL_VUSB
  119. if (!vusb_suspended) return;
  120. #endif
  121. suspend_power_down_kb();
  122. #ifndef NO_SUSPEND_POWER_DOWN
  123. // Turn off backlight
  124. # ifdef BACKLIGHT_ENABLE
  125. backlight_set(0);
  126. # endif
  127. // Turn off LED indicators
  128. uint8_t leds_off = 0;
  129. # if defined(BACKLIGHT_CAPS_LOCK) && defined(BACKLIGHT_ENABLE)
  130. if (is_backlight_enabled()) {
  131. // Don't try to turn off Caps Lock indicator as it is backlight and backlight is already off
  132. leds_off |= (1 << USB_LED_CAPS_LOCK);
  133. }
  134. # endif
  135. led_set(leds_off);
  136. // Turn off audio
  137. # ifdef AUDIO_ENABLE
  138. stop_all_notes();
  139. # endif
  140. // Turn off underglow
  141. # if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE)
  142. rgblight_suspend();
  143. # endif
  144. # if defined(LED_MATRIX_ENABLE)
  145. led_matrix_set_suspend_state(true);
  146. # endif
  147. # if defined(RGB_MATRIX_ENABLE)
  148. rgb_matrix_set_suspend_state(true);
  149. # endif
  150. // Enter sleep state if possible (ie, the MCU has a watchdog timeout interrupt)
  151. # if defined(WDT_vect)
  152. power_down(WDTO_15MS);
  153. # endif
  154. #endif
  155. }
  156. __attribute__((weak)) void matrix_power_up(void) {}
  157. __attribute__((weak)) void matrix_power_down(void) {}
  158. bool suspend_wakeup_condition(void) {
  159. matrix_power_up();
  160. matrix_scan();
  161. matrix_power_down();
  162. for (uint8_t r = 0; r < MATRIX_ROWS; r++) {
  163. if (matrix_get_row(r)) return true;
  164. }
  165. return false;
  166. }
  167. /** \brief run user level code immediately after wakeup
  168. *
  169. * FIXME: needs doc
  170. */
  171. __attribute__((weak)) void suspend_wakeup_init_user(void) {}
  172. /** \brief run keyboard level code immediately after wakeup
  173. *
  174. * FIXME: needs doc
  175. */
  176. __attribute__((weak)) void suspend_wakeup_init_kb(void) { suspend_wakeup_init_user(); }
  177. /** \brief run immediately after wakeup
  178. *
  179. * FIXME: needs doc
  180. */
  181. void suspend_wakeup_init(void) {
  182. // clear keyboard state
  183. clear_keyboard();
  184. // Turn on backlight
  185. #ifdef BACKLIGHT_ENABLE
  186. backlight_init();
  187. #endif
  188. // Restore LED indicators
  189. led_set(host_keyboard_leds());
  190. // Wake up underglow
  191. #if defined(RGBLIGHT_SLEEP) && defined(RGBLIGHT_ENABLE)
  192. rgblight_wakeup();
  193. #endif
  194. #if defined(LED_MATRIX_ENABLE)
  195. led_matrix_set_suspend_state(false);
  196. #endif
  197. #if defined(RGB_MATRIX_ENABLE)
  198. rgb_matrix_set_suspend_state(false);
  199. #endif
  200. suspend_wakeup_init_kb();
  201. }
  202. #if !defined(NO_SUSPEND_POWER_DOWN) && defined(WDT_vect)
  203. /* watchdog timeout */
  204. ISR(WDT_vect) {
  205. // compensate timer for sleep
  206. switch (wdt_timeout) {
  207. case WDTO_15MS:
  208. timer_count += 15 + 2; // WDTO_15MS + 2(from observation)
  209. break;
  210. default:;
  211. }
  212. }
  213. #endif