You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

180 lines
3.5 KiB

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