suspend.c 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  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. /** \brief Suspend idle
  18. *
  19. * FIXME: needs doc
  20. */
  21. void suspend_idle(uint8_t time) {
  22. cli();
  23. set_sleep_mode(SLEEP_MODE_IDLE);
  24. sleep_enable();
  25. sei();
  26. sleep_cpu();
  27. sleep_disable();
  28. }
  29. // TODO: This needs some cleanup
  30. #if !defined(NO_SUSPEND_POWER_DOWN) && defined(WDT_vect)
  31. // clang-format off
  32. #define wdt_intr_enable(value) \
  33. __asm__ __volatile__ ( \
  34. "in __tmp_reg__,__SREG__" "\n\t" \
  35. "cli" "\n\t" \
  36. "wdr" "\n\t" \
  37. "sts %0,%1" "\n\t" \
  38. "out __SREG__,__tmp_reg__" "\n\t" \
  39. "sts %0,%2" "\n\t" \
  40. : /* no outputs */ \
  41. : "M" (_SFR_MEM_ADDR(_WD_CONTROL_REG)), \
  42. "r" (_BV(_WD_CHANGE_BIT) | _BV(WDE)), \
  43. "r" ((uint8_t) ((value & 0x08 ? _WD_PS3_MASK : 0x00) | _BV(WDIE) | (value & 0x07))) \
  44. : "r0" \
  45. )
  46. // clang-format on
  47. /** \brief Power down MCU with watchdog timer
  48. *
  49. * wdto: watchdog timer timeout defined in <avr/wdt.h>
  50. * WDTO_15MS
  51. * WDTO_30MS
  52. * WDTO_60MS
  53. * WDTO_120MS
  54. * WDTO_250MS
  55. * WDTO_500MS
  56. * WDTO_1S
  57. * WDTO_2S
  58. * WDTO_4S
  59. * WDTO_8S
  60. */
  61. static uint8_t wdt_timeout = 0;
  62. /** \brief Power down
  63. *
  64. * FIXME: needs doc
  65. */
  66. static void power_down(uint8_t wdto) {
  67. wdt_timeout = wdto;
  68. // Watchdog Interrupt Mode
  69. wdt_intr_enable(wdto);
  70. // TODO: more power saving
  71. // See PicoPower application note
  72. // - I/O port input with pullup
  73. // - prescale clock
  74. // - BOD disable
  75. // - Power Reduction Register PRR
  76. set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  77. sleep_enable();
  78. sei();
  79. sleep_cpu();
  80. sleep_disable();
  81. // Disable watchdog after sleep
  82. wdt_disable();
  83. }
  84. #endif
  85. /** \brief Suspend power down
  86. *
  87. * FIXME: needs doc
  88. */
  89. void suspend_power_down(void) {
  90. #ifdef PROTOCOL_LUFA
  91. if (USB_DeviceState == DEVICE_STATE_Configured) return;
  92. #endif
  93. #ifdef PROTOCOL_VUSB
  94. if (!vusb_suspended) return;
  95. #endif
  96. suspend_power_down_quantum();
  97. #ifndef NO_SUSPEND_POWER_DOWN
  98. // Enter sleep state if possible (ie, the MCU has a watchdog timeout interrupt)
  99. # if defined(WDT_vect)
  100. power_down(WDTO_15MS);
  101. # endif
  102. #endif
  103. }
  104. __attribute__((weak)) void matrix_power_up(void) {}
  105. __attribute__((weak)) void matrix_power_down(void) {}
  106. bool suspend_wakeup_condition(void) {
  107. matrix_power_up();
  108. matrix_scan();
  109. matrix_power_down();
  110. for (uint8_t r = 0; r < MATRIX_ROWS; r++) {
  111. if (matrix_get_row(r)) return true;
  112. }
  113. return false;
  114. }
  115. /** \brief run immediately after wakeup
  116. *
  117. * FIXME: needs doc
  118. */
  119. void suspend_wakeup_init(void) {
  120. // clear keyboard state
  121. clear_keyboard();
  122. suspend_wakeup_init_quantum();
  123. }
  124. #if !defined(NO_SUSPEND_POWER_DOWN) && defined(WDT_vect)
  125. /* watchdog timeout */
  126. ISR(WDT_vect) {
  127. // compensate timer for sleep
  128. switch (wdt_timeout) {
  129. case WDTO_15MS:
  130. timer_count += 15 + 2; // WDTO_15MS + 2(from observation)
  131. break;
  132. default:;
  133. }
  134. }
  135. #endif