esp_hal/gpio/wakeup/mod.rs
1//! Wake the chip through a pad.
2//!
3//! A pin declares that it can wake the chip, and its interrupt trigger sets the condition. Sleep
4//! entry then assigns all the listening pins to the hardware paths of the chip. It assigns them
5//! together, so the result does not depend on the order in which the user configured the pins.
6//!
7//! The chip has two kinds of path. The digital path reads the pad through the high-performance GPIO
8//! peripheral. It works on every pad, but it needs that peripheral powered. Every listening pin has
9//! this path, because the driver writes the interrupt-enable bit and the wakeup-enable bit of a pin
10//! together. The low-power paths are `ext0`, `ext1` and the per-pin low-power path. These paths
11//! continue to work when the peripheral is powered down. Deep sleep always powers the peripheral
12//! down, and light sleep powers it down on request, so such a sleep needs a low-power path. Only
13//! low-power pads have one, and a pin must request it with [`WakeupConfig`].
14//!
15//! The [`path`] module holds the low-power paths of the chip, and divides the pins between them.
16//! This module collects the pins and requests the power domains that they need.
17//!
18//! This module also isolates the digital pads before a deep sleep, because it holds the pad tables.
19//! That step is part of sleep entry, and it is not a wakeup source.
20//!
21//! All of this code runs with the flash accessible. Sleep entry calls the entry hook before it
22//! writes the sleep configuration to hardware, and calls the exit hook after the wake sequence
23//! restores it. The hooks still use the [`ram`][crate::ram] attribute, to keep the flash out of the
24//! sleep path.
25
26// Each generation of the low-power paths needs its own allocation.
27#[cfg_attr(sleep_ext1_version = "1", path = "ext1_v1.rs")]
28#[cfg_attr(sleep_ext1_version = "2", path = "ext1_v2.rs")]
29#[cfg_attr(sleep_ext1_version = "3", path = "ext1_v3.rs")]
30#[cfg_attr(not(sleep_ext1_version_is_set), path = "per_pin.rs")]
31mod path;
32
33use portable_atomic::Ordering;
34
35// The pad function is selected only on the chips that have a low-power IO MUX.
36#[cfg(sleep_ext1_version_is_set)]
37use crate::gpio::lp_io::LpFunction;
38use crate::{
39 gpio::{
40 AnyPin,
41 Event,
42 GpioBank,
43 Level,
44 Pin,
45 WakeConfigError,
46 low_level::PadMask,
47 lp_io::low_level,
48 },
49 peripherals::GPIO,
50 rtc_cntl::{
51 WakeupSource,
52 sleep::{SleepResource, WrappedSleepConfig},
53 },
54};
55
56/// Configures whether a pin can wake the chip from sleep.
57///
58/// The wake condition is the interrupt trigger of the pin, which
59/// [`listen`][crate::gpio::Input::listen] and the `wait_for` family set. **A pin that does not
60/// listen is not a wakeup source.** A pin that listens wakes the chip from light sleep through the
61/// digital path, and needs no configuration for that.
62///
63/// # Hold the pad at the level that does not wake the chip
64///
65/// A pad that is already at its wake level when the sleep starts ends the sleep immediately. The
66/// configuration of the pin must hold the pad at the other level. Sleep keeps the pull resistors
67/// that the pin is configured with, and adds no resistor of its own. A pin with
68/// [`Pull::None`][crate::gpio::Pull::None] and no external resistor therefore floats during the
69/// sleep, and a floating pad wakes the chip immediately and every time.
70///
71/// Give a level-triggered wake pin a pull against the level that wakes the chip, or an external
72/// resistor. For a pin that wakes the chip on a low level, use an external pull-up.
73#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, procmacros::BuilderLite)]
74#[cfg_attr(feature = "defmt", derive(defmt::Format))]
75#[non_exhaustive]
76#[instability::unstable]
77pub struct WakeupConfig {
78 /// Lets the pin wake the chip while the high-performance GPIO peripheral is powered down.
79 ///
80 /// Deep sleep needs this path, and so does a light sleep that powers the peripheral down. Only
81 /// low-power pads have such a path.
82 ///
83 /// Sleep entry configures the pad as the low-power path requires, and keeps the pull resistors
84 /// of the pin in both cases. Before a deep sleep it also holds the pad, because deep sleep
85 /// powers down the circuit that drives it. A light sleep returns the pad to the digital GPIO
86 /// peripheral when it ends. After a deep sleep, the boot releases the hold.
87 low_power_path: bool,
88}
89
90/// The pads that can wake the chip through a low-power path.
91///
92/// Hardware records the digital path, in the wakeup-enable bit of the pad. No low-power register
93/// records the request alone, because every such register also arms a level, and esp32h2 has no
94/// per-pin low-power register. This record is therefore in software. It does not have to survive a
95/// deep sleep, because that wake resets the chip, and a program starts with no wakeup sources.
96static LOW_POWER_PADS: PadMask = PadMask::new();
97
98/// The pads that sleep entry prepared, and that the exit hook must return to their driver.
99///
100/// A sleep that ends without a reset has to undo the preparation. A light sleep is such a sleep,
101/// and so is a deep sleep that the hardware rejects.
102static PREPARED_PADS: PadMask = PadMask::new();
103
104/// The pads that ended the last sleep.
105///
106/// Each path reports through a status that other code clears or overwrites later. The digital path
107/// has no status of its own, only the interrupt of the pin, which the interrupt handler of the pin
108/// clears. A low-power path keeps its status until the next sleep arms the path again.
109/// [`record_wakeup`] therefore reads all the statuses when the sleep ends, and [`caused_wakeup`]
110/// reads only this record.
111static WOKEN_PADS: PadMask = PadMask::new();
112
113for_each_gpio! {
114 (all $( ($n:literal, $gpio:ident $_ins:tt $_outs:tt $_attrs:tt) ),*) => {
115 /// The highest pin number plus one, which is the length of the pad tables.
116 const PAD_COUNT: usize = {
117 let mut highest = 0;
118 $( if $n > highest { highest = $n; } )*
119 highest + 1
120 };
121 };
122}
123
124/// Marks a pad that the low-power registers do not reach, in [`LP_NUMBERS`].
125const NO_LP_NUMBER: u8 = u8::MAX;
126
127for_each_lp_function! {
128 (LP_GPIOn $( (($_sig:ident, LP_GPIOn, $lp:literal), $gpio:ident, $_af:ident, $_in:tt $_out:tt) ),*) => {
129 /// The number that the low-power registers use for each pad that they reach.
130 ///
131 /// The two domains number the pads separately, and only some chips give a pad the same
132 /// number in both domains. A low-power register therefore takes the number from this table,
133 /// and never the pin number.
134 const LP_NUMBERS: [u8; PAD_COUNT] = {
135 let mut numbers = [NO_LP_NUMBER; PAD_COUNT];
136 $( numbers[crate::peripherals::$gpio::NUMBER as usize] = $lp; )*
137 numbers
138 };
139 };
140}
141
142/// The number of low-power pads. Every one of them can be armed at the same time.
143const MAX_ARMED: usize = {
144 let mut count = 0;
145 let mut pad = 0;
146 while pad < PAD_COUNT {
147 if LP_NUMBERS[pad] != NO_LP_NUMBER {
148 count += 1;
149 }
150 pad += 1;
151 }
152 count
153};
154
155/// A listening pin, and the level that wakes the chip.
156#[derive(Debug, Clone, Copy)]
157struct Armed {
158 /// The digital pin number.
159 gpio: u8,
160 /// The number that the low-power registers use for the pad.
161 lp: u8,
162 level: Level,
163}
164
165impl Armed {
166 const NONE: Self = Self {
167 gpio: 0,
168 lp: 0,
169 level: Level::Low,
170 };
171}
172
173/// Applies a pin's wakeup configuration.
174pub(crate) fn apply_config(pin: &AnyPin<'_>, config: &WakeupConfig) -> Result<(), WakeConfigError> {
175 if config.low_power_path {
176 if lp_number(pin.number()).is_none() {
177 return Err(WakeConfigError::NoLowPowerPath);
178 }
179
180 LOW_POWER_PADS.set(pin.number(), true);
181 enable();
182 } else {
183 LOW_POWER_PADS.set(pin.number(), false);
184 }
185
186 Ok(())
187}
188
189/// Returns whether this pad ended the last sleep.
190pub(crate) fn caused_wakeup(pin: &AnyPin<'_>) -> bool {
191 WOKEN_PADS.contains(pin.number())
192}
193
194/// Copies the pads that ended the sleep from the path statuses into [`WOKEN_PADS`].
195///
196/// A sleep ends in one of two places: at the end of a light sleep, and in the sleep initialization
197/// of a boot after a deep sleep. Both places call this function, and they call it after every
198/// sleep, so that the record does not describe an earlier sleep. The boot calls it before it
199/// releases the pads that the previous run armed, because that release also clears the `ext1`
200/// selection.
201#[crate::ram]
202pub(crate) fn record_wakeup() {
203 let cause = crate::rtc_cntl::wakeup_cause();
204
205 for bank in GpioBank::ALL {
206 // The digital path reports only through the interrupt of the pin. The interrupt status is
207 // still set here, unless an interrupt handler ran first. A handler can only run first if
208 // interrupts were enabled during the light sleep.
209 let digital = if cause.contains(WakeupSource::Gpio) {
210 bank.read_interrupt_status() & bank.listening().load(Ordering::Relaxed)
211 } else {
212 0
213 };
214
215 WOKEN_PADS.word(bank).store(digital, Ordering::Relaxed);
216 }
217
218 for (gpio, &lp) in LP_NUMBERS.iter().enumerate() {
219 if lp != NO_LP_NUMBER && path::caused_wakeup(gpio as u8, cause) {
220 WOKEN_PADS.set(gpio as u8, true);
221 }
222 }
223}
224
225/// Records that a pad can wake the chip, and registers the hooks that allocate the paths.
226///
227/// The mask bit alone does not wake the chip. A wake also needs the per-pin digital bit that
228/// `listen` writes, or a low-power path that the entry hook assigns. If no pin can wake the chip,
229/// the entry hook clears the mask bit again. A sleep with no other wakeup source is then refused,
230/// instead of never ending.
231pub(crate) fn enable() {
232 WakeupSource::Gpio.enable_with_hooks(Some(entry_hook), Some(exit_hook));
233}
234
235fn disable() {
236 WakeupSource::Gpio.disable();
237
238 // Without this, a bit that a path of this chip still holds outlives the hook that clears it,
239 // and the chip keeps a wakeup source that nobody requested.
240 path::disable();
241}
242
243/// Returns the number that the low-power registers use for `gpio`, or `None` if they do not reach
244/// the pad.
245fn lp_number(gpio: u8) -> Option<u8> {
246 match LP_NUMBERS[gpio as usize] {
247 NO_LP_NUMBER => None,
248 lp => Some(lp),
249 }
250}
251
252/// Returns every pad that the low-power registers reach, as a pin number and a low-power number.
253fn low_power_pads() -> impl Iterator<Item = (u8, u8)> {
254 LP_NUMBERS
255 .iter()
256 .copied()
257 .enumerate()
258 .filter(|&(_, lp)| lp != NO_LP_NUMBER)
259 .map(|(gpio, lp)| (gpio as u8, lp))
260}
261
262/// Returns the low-power number of every pad that the low-power registers reach.
263#[cfg(not(sleep_ext1_version = "3"))]
264fn low_power_numbers() -> impl Iterator<Item = u8> {
265 low_power_pads().map(|(_, lp)| lp)
266}
267
268/// Returns whether a wakeup source that configures pads is enabled.
269///
270/// Call this before the initialization clears the mask. The result tells the initialization whether
271/// it must release the pads that the previous run armed. ESP-IDF uses the same condition for
272/// `esp_deep_sleep_wakeup_io_reset`.
273pub(crate) fn wake_enabled() -> bool {
274 let sources = crate::rtc_cntl::sleep::enabled_sources();
275
276 #[allow(unused_mut)]
277 let mut enabled = sources.contains(WakeupSource::Gpio);
278
279 #[cfg(sleep_has_wakeup_source_ext0)]
280 {
281 enabled |= sources.contains(WakeupSource::Ext0);
282 }
283 #[cfg(sleep_has_wakeup_source_ext1)]
284 {
285 enabled |= sources.contains(WakeupSource::Ext1);
286 }
287
288 enabled
289}
290
291/// Releases the pads that the previous run armed for a deep sleep.
292///
293/// A deep sleep holds its wake pads, because it powers down the circuit that drives them. The wake
294/// resets the chip, but it does not release the hold: the hold register is in the always-on domain.
295/// A held pad would ignore its driver for the rest of the program, so the boot releases it.
296///
297/// The function releases the hold, and changes nothing else. A change of a pad function can take a
298/// pad from a low-power core, which keeps its pads while it runs.
299pub(crate) fn wake_io_reset() {
300 path::wake_io_reset();
301}
302
303/// Records that sleep entry took the hold of a pad, so that the exit hook releases it.
304#[cfg(not(sleep_ext1_version_is_set))]
305fn hold_taken(gpio: u8) {
306 PREPARED_PADS.set(gpio, true);
307}
308
309/// Assigns the listening pins to the hardware paths, and requests the power domains that they need.
310#[crate::ram]
311fn entry_hook(config: &mut WrappedSleepConfig<'_>) {
312 let mut buffer = [Armed::NONE; MAX_ARMED];
313 let (armed, mut digital) = collect(&mut buffer);
314
315 // Deep sleep powers the high-performance GPIO peripheral down. The digital path then cannot
316 // wake the chip, whatever number of pins listen.
317 digital &= !config.is_deep_sleep();
318
319 if armed.is_empty() && !digital {
320 // No pad can wake the chip, so the record must go. A mask that still claims a GPIO wakeup
321 // source lets a sleep start that no other source can end.
322 disable();
323 return;
324 }
325
326 if digital {
327 // The digital path reads the pad through the high-performance GPIO peripheral.
328 config.keep_alive(SleepResource::HpPeripherals);
329 }
330
331 path::allocate(armed, config);
332}
333
334/// Returns the pads to the digital GPIO peripheral after a sleep that did not reset the chip.
335///
336/// A light sleep is such a sleep, and so is a deep sleep that the hardware rejects. The hooks of a
337/// rejected deep sleep run in the same place, because sleep entry prepared the same pads.
338#[crate::ram]
339fn exit_hook() {
340 for bank in GpioBank::ALL {
341 let mut prepared = PREPARED_PADS.word(bank).swap(0, Ordering::Relaxed);
342
343 while prepared != 0 {
344 let pin = prepared.trailing_zeros();
345 prepared &= !(1 << pin);
346
347 let Some(lp) = lp_number(bank.offset() + pin as u8) else {
348 continue;
349 };
350
351 low_level::pad_hold(lp, false);
352
353 // Only a chip with a low-power IO MUX moved the pad, so only such a chip has to move it
354 // back. esp32c2 and esp32c3 keep the pad on the digital IO MUX.
355 #[cfg(sleep_ext1_version_is_set)]
356 low_level::set_config(lp, true, false, LpFunction::LP_GPIO);
357 }
358 }
359}
360
361/// Collects the listening pins that requested a low-power path, and returns whether any pin
362/// listens.
363///
364/// Every listening pin can end a light sleep, so the record of the listening pins gives both
365/// results. Neither result needs a read of the pin registers.
366fn collect(buffer: &mut [Armed; MAX_ARMED]) -> (&[Armed], bool) {
367 let mut count = 0;
368 let mut digital = false;
369
370 for bank in GpioBank::ALL {
371 let listening = bank.listening().load(Ordering::Relaxed);
372 digital |= listening != 0;
373
374 let mut participants = listening & LOW_POWER_PADS.word(bank).load(Ordering::Relaxed);
375 while participants != 0 {
376 let pin = participants.trailing_zeros();
377 participants &= !(1 << pin);
378
379 // Only a pad that the low-power registers reach enters the mask, so it has a number.
380 let gpio = bank.offset() + pin as u8;
381 let Some(lp) = lp_number(gpio) else { continue };
382
383 if let Some(level) = armed_level(gpio) {
384 buffer[count] = Armed { gpio, lp, level };
385 count += 1;
386 }
387 }
388 }
389
390 (&buffer[..count], digital)
391}
392
393/// Returns the level that wakes the chip through this pad, or `None` if the trigger sets no level.
394///
395/// The wake paths accept a level only, so an edge trigger becomes the level at the end of the edge.
396/// This keeps the request of the user. `AnyEdge` ends at the level that the pin is not at now,
397/// which is the meaning of "wake when the pin changes". This read of the pin can race with the pin,
398/// but the result is safe. If the pin changes before the sleep starts, the path arms a level that
399/// is already present, and the sleep is rejected or ends immediately.
400fn armed_level(gpio: u8) -> Option<Level> {
401 let trigger = GPIO::regs().pin(gpio as usize).read().int_type().bits();
402 Some(
403 if trigger == Event::HighLevel as u8 || trigger == Event::RisingEdge as u8 {
404 Level::High
405 } else if trigger == Event::LowLevel as u8 || trigger == Event::FallingEdge as u8 {
406 Level::Low
407 } else if trigger == Event::AnyEdge as u8 {
408 !Level::from(unsafe { AnyPin::steal(gpio) }.is_input_high())
409 } else {
410 return None;
411 },
412 )
413}
414
415/// Moves the pad to the low-power IO MUX, which makes it readable while the high-performance GPIO
416/// peripheral is powered down.
417///
418/// Before a deep sleep this function also holds the pad, because deep sleep powers down the circuit
419/// that drives it. A light sleep does not hold the pad. A light sleep powers down no circuit that
420/// drives the pad, and a hold would freeze an output.
421#[cfg(sleep_ext1_version_is_set)]
422fn prepare_pad(pin: &Armed, deep: bool) {
423 // The low-power IO MUX has its own pull resistors, and the digital ones stop to work as soon as
424 // the low-power IO MUX takes the pad. Copy the resistors, or an undriven pad floats away from
425 // the level that the user selected, and wakes the chip immediately.
426 #[cfg(not(esp32h2))]
427 {
428 let digital = crate::gpio::io_mux_reg(pin.gpio).read();
429 low_level::pullup_enable(pin.lp, digital.fun_wpu().bit());
430 low_level::pulldown_enable(pin.lp, digital.fun_wpd().bit());
431 }
432
433 // esp32h2 reaches the pad through the digital IO MUX, so it has no low-power MUX to select.
434 low_level::set_config(pin.lp, true, !cfg!(esp32h2), LpFunction::LP_GPIO);
435 low_level::pad_hold(pin.lp, deep);
436
437 PREPARED_PADS.set(pin.gpio, true);
438}
439
440/// Disconnects the pads that no hold keeps, so that a powered-down output driver does not increase
441/// the deep-sleep current.
442///
443/// No wakeup source controls this step, so every deep sleep does it, like
444/// `esp_sleep_isolate_digital_gpio` in ESP-IDF. The step is destructive, because a pad loses the
445/// peripheral function that drove it. It therefore runs at sleep entry, on the chips that cannot
446/// hold a single pad through a deep sleep, and after the wakeup sources take the holds that they
447/// need.
448#[cfg(sleep_deep_sleep_needs_gpio_isolation)]
449pub(crate) fn isolate_pads_for_deep_sleep() {
450 use crate::{
451 gpio::{AlternateFunction, OutputSignal, io_mux_reg},
452 peripherals::LPWR,
453 };
454
455 // If the hold is disabled, no pad keeps its level through the sleep. ESP-IDF also skips the
456 // isolation in that case.
457 let dig_iso = LPWR::regs().dig_iso().read();
458 let hold_enabled = !dig_iso.dg_pad_force_unhold().bit() && dig_iso.dg_pad_autohold_en().bit();
459 if !hold_enabled {
460 return;
461 }
462 for gpio in 0..PAD_COUNT as u8 {
463 // Only the pads that the digital supply feeds leak current, and those are the pads with no
464 // low-power number. The low-power pads have their own supply, which stays powered, and a
465 // wake pad is always a low-power pad.
466 if lp_number(gpio).is_some() {
467 continue;
468 }
469
470 // A pad that the user holds keeps its level, which is the purpose of the hold. Isolation
471 // would cancel that request.
472 if crate::gpio::lp_io::low_level::is_digital_pad_held(gpio) {
473 continue;
474 }
475
476 GPIO::regs()
477 .func_out_sel_cfg(gpio as usize)
478 .modify(|_, w| unsafe { w.bits(OutputSignal::GPIO as u32) });
479
480 io_mux_reg(gpio).modify(|_, w| unsafe {
481 w.fun_wpu().clear_bit();
482 w.fun_wpd().clear_bit();
483 w.fun_ie().clear_bit();
484 // A pad that keeps a peripheral function increases the deep-sleep current.
485 w.mcu_sel().bits(AlternateFunction::GPIO as u8)
486 });
487 }
488}