1
//! The low-power path of esp32c2 and esp32c3.
2
//!
3
//! These chips have no `ext1`. Their per-pin path gives each low-power pad its own level, keeps no
4
//! power domain powered, and reaches the pad through the digital IO MUX. There is thus nothing to
5
//! allocate, and no pad to move to another IO MUX.
6

            
7
use super::{Armed, LP_NUMBERS, NO_LP_NUMBER};
8
use crate::{
9
    gpio::{Level, lp_io::low_level},
10
    peripherals::LPWR,
11
    rtc_cntl::{WakeupReason, WakeupSource, sleep::WrappedSleepConfig},
12
};
13

            
14
/// These chips have no separate path to clear, because all their pads use the GPIO wakeup source.
15
pub(super) fn disable() {}
16

            
17
pub(super) fn allocate(armed: &[Armed], config: &mut WrappedSleepConfig<'_>) {
18
    // A pad outside this sleep must not wake the chip.
19
    for &lp in LP_NUMBERS.iter().filter(|&&lp| lp != NO_LP_NUMBER) {
20
        low_level::apply_wakeup(lp, false, Level::Low);
21
    }
22

            
23
    for pin in armed {
24
        // Deep sleep powers the digital IO MUX down, and the pull resistors are part of it. The pad
25
        // must therefore keep its own state through the sleep, or it floats to an unknown level.
26
        // The hold outlives the sleep: neither the wake of a light sleep nor the reset of a
27
        // deep-sleep wake releases it, so it is released in software.
28
        if config.is_deep_sleep() {
29
            low_level::pad_hold(pin.lp, true);
30
            super::hold_taken(pin.gpio);
31
        }
32

            
33
        low_level::apply_wakeup(pin.lp, true, pin.level);
34
    }
35

            
36
    prepare_gpio_wakeup();
37
}
38

            
39
/// Returns whether this pad ended the last sleep through the per-pin path.
40
pub(super) fn caused_wakeup(gpio: u8, cause: WakeupReason) -> bool {
41
    // Only a low-power pad can take this path.
42
    let Some(lp) = super::lp_number(gpio) else {
43
        return false;
44
    };
45

            
46
    let status = cfg_select! {
47
        esp32c2 => LPWR::regs()
48
            .cntl_gpio_wakeup()
49
            .read()
50
            .gpio_wakeup_status()
51
            .bits(),
52
        esp32c3 => LPWR::regs()
53
            .gpio_wakeup()
54
            .read()
55
            .gpio_wakeup_status()
56
            .bits(),
57
    };
58

            
59
    cause.contains(WakeupSource::Gpio) && status & (1 << lp) != 0
60
}
61

            
62
/// Releases the pads that the previous run armed for a deep sleep.
63
///
64
/// The per-pin registers keep their wakeup enables through the wake, so they still name the pads
65
/// that the previous run armed.
66
pub(super) fn wake_io_reset() {
67
    let armed = low_level::wakeup_enabled_mask();
68

            
69
    for lp in super::low_power_numbers() {
70
        if armed & (1 << lp) != 0 {
71
            low_level::pad_hold(lp, false);
72
        }
73
    }
74
}
75

            
76
/// Enables the clock of the pad-scanning logic that this path needs, and clears the status of the
77
/// previous wake.
78
fn prepare_gpio_wakeup() {
79
    let gpio_wakeup = cfg_select! {
80
        esp32c2 => LPWR::regs().cntl_gpio_wakeup(),
81
        esp32c3 => LPWR::regs().gpio_wakeup(),
82
    };
83

            
84
    gpio_wakeup.modify(|_, w| w.gpio_pin_clk_gate().set_bit());
85
    LPWR::regs()
86
        .ext_wakeup_conf()
87
        .modify(|_, w| w.gpio_wakeup_filter().set_bit());
88

            
89
    gpio_wakeup.modify(|_, w| w.gpio_wakeup_status_clr().set_bit());
90
    gpio_wakeup.modify(|_, w| w.gpio_wakeup_status_clr().clear_bit());
91
}