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