aboutsummaryrefslogtreecommitdiff
path: root/platforms/avr/suspend.c
diff options
context:
space:
mode:
Diffstat (limited to 'platforms/avr/suspend.c')
-rw-r--r--platforms/avr/suspend.c152
1 files changed, 152 insertions, 0 deletions
diff --git a/platforms/avr/suspend.c b/platforms/avr/suspend.c
new file mode 100644
index 000000000..b614746e6
--- /dev/null
+++ b/platforms/avr/suspend.c
@@ -0,0 +1,152 @@
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 "suspend.h"
8#include "timer.h"
9#include "led.h"
10#include "host.h"
11
12#ifdef PROTOCOL_LUFA
13# include "lufa.h"
14#endif
15#ifdef PROTOCOL_VUSB
16# include "vusb.h"
17#endif
18
19/** \brief Suspend idle
20 *
21 * FIXME: needs doc
22 */
23void suspend_idle(uint8_t time) {
24 cli();
25 set_sleep_mode(SLEEP_MODE_IDLE);
26 sleep_enable();
27 sei();
28 sleep_cpu();
29 sleep_disable();
30}
31
32// TODO: This needs some cleanup
33
34#if !defined(NO_SUSPEND_POWER_DOWN) && defined(WDT_vect)
35
36// clang-format off
37#define wdt_intr_enable(value) \
38__asm__ __volatile__ ( \
39 "in __tmp_reg__,__SREG__" "\n\t" \
40 "cli" "\n\t" \
41 "wdr" "\n\t" \
42 "sts %0,%1" "\n\t" \
43 "out __SREG__,__tmp_reg__" "\n\t" \
44 "sts %0,%2" "\n\t" \
45 : /* no outputs */ \
46 : "M" (_SFR_MEM_ADDR(_WD_CONTROL_REG)), \
47 "r" (_BV(_WD_CHANGE_BIT) | _BV(WDE)), \
48 "r" ((uint8_t) ((value & 0x08 ? _WD_PS3_MASK : 0x00) | _BV(WDIE) | (value & 0x07))) \
49 : "r0" \
50)
51// clang-format on
52
53/** \brief Power down MCU with watchdog timer
54 *
55 * wdto: watchdog timer timeout defined in <avr/wdt.h>
56 * WDTO_15MS
57 * WDTO_30MS
58 * WDTO_60MS
59 * WDTO_120MS
60 * WDTO_250MS
61 * WDTO_500MS
62 * WDTO_1S
63 * WDTO_2S
64 * WDTO_4S
65 * WDTO_8S
66 */
67static uint8_t wdt_timeout = 0;
68
69/** \brief Power down
70 *
71 * FIXME: needs doc
72 */
73static void power_down(uint8_t wdto) {
74 wdt_timeout = wdto;
75
76 // Watchdog Interrupt Mode
77 wdt_intr_enable(wdto);
78
79 // TODO: more power saving
80 // See PicoPower application note
81 // - I/O port input with pullup
82 // - prescale clock
83 // - BOD disable
84 // - Power Reduction Register PRR
85 set_sleep_mode(SLEEP_MODE_PWR_DOWN);
86 sleep_enable();
87 sei();
88 sleep_cpu();
89 sleep_disable();
90
91 // Disable watchdog after sleep
92 wdt_disable();
93}
94#endif
95
96/** \brief Suspend power down
97 *
98 * FIXME: needs doc
99 */
100void suspend_power_down(void) {
101#ifdef PROTOCOL_LUFA
102 if (USB_DeviceState == DEVICE_STATE_Configured) return;
103#endif
104#ifdef PROTOCOL_VUSB
105 if (!vusb_suspended) return;
106#endif
107
108 suspend_power_down_quantum();
109
110#ifndef NO_SUSPEND_POWER_DOWN
111 // Enter sleep state if possible (ie, the MCU has a watchdog timeout interrupt)
112# if defined(WDT_vect)
113 power_down(WDTO_15MS);
114# endif
115#endif
116}
117
118__attribute__((weak)) void matrix_power_up(void) {}
119__attribute__((weak)) void matrix_power_down(void) {}
120bool suspend_wakeup_condition(void) {
121 matrix_power_up();
122 matrix_scan();
123 matrix_power_down();
124 for (uint8_t r = 0; r < MATRIX_ROWS; r++) {
125 if (matrix_get_row(r)) return true;
126 }
127 return false;
128}
129
130/** \brief run immediately after wakeup
131 *
132 * FIXME: needs doc
133 */
134void suspend_wakeup_init(void) {
135 // clear keyboard state
136 clear_keyboard();
137
138 suspend_wakeup_init_quantum();
139}
140
141#if !defined(NO_SUSPEND_POWER_DOWN) && defined(WDT_vect)
142/* watchdog timeout */
143ISR(WDT_vect) {
144 // compensate timer for sleep
145 switch (wdt_timeout) {
146 case WDTO_15MS:
147 timer_count += 15 + 2; // WDTO_15MS + 2(from observation)
148 break;
149 default:;
150 }
151}
152#endif