diff options
Diffstat (limited to 'platforms/avr/suspend.c')
-rw-r--r-- | platforms/avr/suspend.c | 152 |
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 | */ | ||
23 | void 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 | */ | ||
67 | static uint8_t wdt_timeout = 0; | ||
68 | |||
69 | /** \brief Power down | ||
70 | * | ||
71 | * FIXME: needs doc | ||
72 | */ | ||
73 | static 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 | */ | ||
100 | void 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) {} | ||
120 | bool 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 | */ | ||
134 | void 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 */ | ||
143 | ISR(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 | ||