Tidy up atomicity in timer.c and ring_buffer.h
[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 #ifndef NO_SUSPEND_POWER_DOWN
51 /* Power down MCU with watchdog timer
52 * wdto: watchdog timer timeout defined in <avr/wdt.h>
53 * WDTO_15MS
54 * WDTO_30MS
55 * WDTO_60MS
56 * WDTO_120MS
57 * WDTO_250MS
58 * WDTO_500MS
59 * WDTO_1S
60 * WDTO_2S
61 * WDTO_4S
62 * WDTO_8S
63 */
64 static uint8_t wdt_timeout = 0;
65
66 static void power_down(uint8_t wdto)
67 {
68 #ifdef PROTOCOL_LUFA
69 if (USB_DeviceState == DEVICE_STATE_Configured) return;
70 #endif
71 wdt_timeout = wdto;
72
73 // Watchdog Interrupt Mode
74 wdt_intr_enable(wdto);
75
76 #ifdef BACKLIGHT_ENABLE
77 backlight_set(0);
78 #endif
79
80 // Turn off LED indicators
81 led_set(0);
82
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
88 // TODO: more power saving
89 // See PicoPower application note
90 // - I/O port input with pullup
91 // - prescale clock
92 // - BOD disable
93 // - Power Reduction Register PRR
94 set_sleep_mode(SLEEP_MODE_PWR_DOWN);
95 sleep_enable();
96 sei();
97 sleep_cpu();
98 sleep_disable();
99
100 // Disable watchdog after sleep
101 wdt_disable();
102 }
103 #endif
104
105 void suspend_power_down(void)
106 {
107 #ifndef NO_SUSPEND_POWER_DOWN
108 power_down(WDTO_15MS);
109 #endif
110 }
111
112 __attribute__ ((weak)) void matrix_power_up(void) {}
113 __attribute__ ((weak)) void matrix_power_down(void) {}
114 bool suspend_wakeup_condition(void)
115 {
116 matrix_power_up();
117 matrix_scan();
118 matrix_power_down();
119 for (uint8_t r = 0; r < MATRIX_ROWS; r++) {
120 if (matrix_get_row(r)) return true;
121 }
122 return false;
123 }
124
125 // run immediately after wakeup
126 void suspend_wakeup_init(void)
127 {
128 // clear keyboard state
129 clear_keyboard();
130 #ifdef BACKLIGHT_ENABLE
131 backlight_init();
132 #endif
133 led_set(host_keyboard_leds());
134 }
135
136 #ifndef NO_SUSPEND_POWER_DOWN
137 /* watchdog timeout */
138 ISR(WDT_vect)
139 {
140 // compensate timer for sleep
141 switch (wdt_timeout) {
142 case WDTO_15MS:
143 timer_count += 15 + 2; // WDTO_15MS + 2(from observation)
144 break;
145 default:
146 ;
147 }
148 }
149 #endif