1
use crate::{
2
    gpio::{AlternateFunction, Level, LpPin, lp_io::LpFunction},
3
    peripherals::{GPIO, IO_MUX, LPWR},
4
};
5

            
6
for_each_lp_function! {
7
    (($_lp:ident, LP_GPIOn, $pin:literal), $gpio:ident, $_af:ident, $_lp_in:tt $_lp_out:tt) => {
8
        #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
9
        impl LpPin for crate::peripherals::$gpio<'_> {
10
            fn lp_number(&self) -> u8 {
11
                $pin
12
            }
13
        }
14
    };
15

            
16
    // The wakeup register and the hold register have one named field for each pad, and no index, so
17
    // these functions select the field by the low-power number.
18
    (LP_GPIOn $( (($_lp:ident, LP_GPIOn, $pin:literal), $gpio:ident, $_af:ident, $_lp_in:tt $_lp_out:tt) ),*) => {
19
        paste::paste! {
20
            pub(crate) fn apply_wakeup(lp: u8, wakeup: bool, level: Level) {
21
                let trigger = crate::gpio::lp_io::wake_trigger(level);
22

            
23
                let gpio_wakeup = cfg_select! {
24
                    esp32c2 => LPWR::regs().cntl_gpio_wakeup(),
25
                    esp32c3 => LPWR::regs().gpio_wakeup(),
26
                };
27

            
28
                gpio_wakeup.modify(|_, w| unsafe {
29
                    match lp {
30
                        $(
31
                            $pin => {
32
                                w.[<gpio_pin $pin _wakeup_enable>]().bit(wakeup);
33
                                w.[<gpio_pin $pin _int_type>]().bits(trigger)
34
                            }
35
                        )*
36
                        _ => unreachable!(),
37
                    }
38
                });
39
            }
40

            
41
            pub(crate) fn pad_hold(lp: u8, enable: bool) {
42
                LPWR::regs().pad_hold().modify(|_, w| {
43
356
                    match lp {
44
                        $( $pin => w.[<gpio_pin $pin _hold>]().bit(enable), )*
45
                        _ => unreachable!(),
46
                    }
47
                });
48
            }
49

            
50
            /// Returns whether something holds the pad.
51
            pub(crate) fn is_pad_held(lp: u8) -> bool {
52
                let r = LPWR::regs().pad_hold().read();
53
1
                match lp {
54
1
                    $( $pin => r.[<gpio_pin $pin _hold>]().bit_is_set(), )*
55
                    _ => unreachable!(),
56
                }
57
            }
58

            
59
            /// Returns the pads that can wake the chip through the per-pin path, as a mask of
60
            /// low-power numbers.
61
            #[cfg(sleep_driver_supported)]
62
            pub(crate) fn wakeup_enabled_mask() -> u32 {
63
                let gpio_wakeup = cfg_select! {
64
                    esp32c2 => LPWR::regs().cntl_gpio_wakeup().read(),
65
                    esp32c3 => LPWR::regs().gpio_wakeup().read(),
66
                };
67

            
68
                let mut mask = 0;
69
                $(
70
                    if gpio_wakeup.[<gpio_pin $pin _wakeup_enable>]().bit_is_set() {
71
                        mask |= 1 << $pin;
72
                    }
73
                )*
74
                mask
75
            }
76
        }
77
    };
78
}
79

            
80
/// Takes or releases the hold of the pad of `gpio`.
81
///
82
/// The register numbers the pads of the digital supply the way the digital registers do, and the
83
/// low-power pads have their own register.
84
pub(crate) fn digital_pad_hold(gpio: u8, enable: bool) {
85
63
    let mask = 1 << gpio;
86
    LPWR::regs().dig_pad_hold().modify(|r, w| unsafe {
87
        let bits = r.dig_pad_hold().bits();
88
        w.dig_pad_hold()
89
63
            .bits(if enable { bits | mask } else { bits & !mask })
90
    });
91
}
92

            
93
/// Returns whether something holds the pad of `gpio`.
94
pub(crate) fn is_digital_pad_held(gpio: u8) -> bool {
95
1
    LPWR::regs().dig_pad_hold().read().dig_pad_hold().bits() & (1 << gpio) != 0
96
}
97

            
98
/// Configures the pad.
99
///
100
/// The low-power domain reaches the pad through the digital IO MUX. There is thus no low-power
101
/// function to select, and the low-power number is the digital pin number.
102
pub(crate) fn set_config(lp: u8, input_enable: bool, _mux: bool, _func: LpFunction) {
103
    IO_MUX::regs().gpio(lp as usize).modify(|_, w| unsafe {
104
        w.slp_sel().bit(false);
105
        w.mcu_sel().bits(AlternateFunction::GPIO as u8);
106
        w.fun_ie().bit(input_enable)
107
    });
108
331
}
109

            
110
#[expect(dead_code)]
111
pub(crate) fn init_pin(lp: u8, enable_input: bool) -> u8 {
112
    input_enable(lp, enable_input);
113
    lp
114
}
115

            
116
#[expect(dead_code)]
117
pub(crate) fn output_enable(lp: u8, enable: bool) {
118
    if enable {
119
        GPIO::regs()
120
            .enable_w1ts()
121
            .write(|w| unsafe { w.enable_w1ts().bits(1 << lp) });
122
    } else {
123
        GPIO::regs()
124
            .enable_w1tc()
125
            .write(|w| unsafe { w.enable_w1tc().bits(1 << lp) });
126
    }
127
}
128

            
129
pub(crate) fn input_enable(lp: u8, enable: bool) {
130
    IO_MUX::regs()
131
        .gpio(lp as usize)
132
        .modify(|_, w| w.fun_ie().bit(enable));
133
}
134

            
135
#[expect(dead_code)]
136
pub(crate) fn set_open_drain_output(lp: u8, enable: bool) {
137
    GPIO::regs()
138
        .pin(lp as usize)
139
        .modify(|_, w| w.pad_driver().bit(enable));
140
}