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