Skip to main content

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}