Tidy up atomicity in timer.c and ring_buffer.h
[jackhill/qmk/firmware.git] / tmk_core / common / avr / suspend.c
CommitLineData
a074364c 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"
b2badef7 11#include "led.h"
4b3358ac 12
a074364c 13#ifdef PROTOCOL_LUFA
4b3358ac 14 #include "lufa.h"
a074364c 15#endif
16
4b3358ac
I
17#ifdef AUDIO_ENABLE
18 #include "audio.h"
19#endif /* AUDIO_ENABLE */
20
21
a074364c 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
40void 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
5893f0fa 50#ifndef NO_SUSPEND_POWER_DOWN
a074364c 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 */
64static uint8_t wdt_timeout = 0;
5893f0fa 65
a074364c 66static 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
b2badef7 76#ifdef BACKLIGHT_ENABLE
a70f4396 77 backlight_set(0);
b2badef7
PE
78#endif
79
a70f4396
I
80 // Turn off LED indicators
81 led_set(0);
82
4b3358ac 83 #ifdef AUDIO_ENABLE
157ddccc
JH
84 // This sometimes disables the start-up noise, so it's been disabled
85 // stop_all_notes();
4b3358ac
I
86 #endif /* AUDIO_ENABLE */
87
a074364c 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}
5893f0fa 103#endif
a074364c 104
105void suspend_power_down(void)
106{
5893f0fa 107#ifndef NO_SUSPEND_POWER_DOWN
a074364c 108 power_down(WDTO_15MS);
5893f0fa 109#endif
a074364c 110}
111
6b588eb7 112__attribute__ ((weak)) void matrix_power_up(void) {}
113__attribute__ ((weak)) void matrix_power_down(void) {}
a074364c 114bool suspend_wakeup_condition(void)
115{
116 matrix_power_up();
117 matrix_scan();
118 matrix_power_down();
8e88d55b
JH
119 for (uint8_t r = 0; r < MATRIX_ROWS; r++) {
120 if (matrix_get_row(r)) return true;
121 }
122 return false;
a074364c 123}
124
125// run immediately after wakeup
126void suspend_wakeup_init(void)
127{
128 // clear keyboard state
129 clear_keyboard();
130#ifdef BACKLIGHT_ENABLE
131 backlight_init();
132#endif
cf3926a8 133 led_set(host_keyboard_leds());
a074364c 134}
135
136#ifndef NO_SUSPEND_POWER_DOWN
137/* watchdog timeout */
138ISR(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