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