Skip to main content

esp_hal/gpio/lp_io/low_level/
v2.rs

1use crate::{
2    gpio::{Level, LpPin, lp_io::LpFunction},
3    peripherals::{GPIO, LPWR, RTC_IO, SENS},
4};
5
6#[rustfmt::skip]
7macro_rules! pin_reg {
8    (GPIO0)  => { RTC_IO::regs().touch_pad(0) };
9    (GPIO1)  => { RTC_IO::regs().touch_pad(1) };
10    (GPIO2)  => { RTC_IO::regs().touch_pad(2) };
11    (GPIO3)  => { RTC_IO::regs().touch_pad(3) };
12    (GPIO4)  => { RTC_IO::regs().touch_pad(4) };
13    (GPIO5)  => { RTC_IO::regs().touch_pad(5) };
14    (GPIO6)  => { RTC_IO::regs().touch_pad(6) };
15    (GPIO7)  => { RTC_IO::regs().touch_pad(7) };
16    (GPIO8)  => { RTC_IO::regs().touch_pad(8) };
17    (GPIO9)  => { RTC_IO::regs().touch_pad(9) };
18    (GPIO10) => { RTC_IO::regs().touch_pad(10) };
19    (GPIO11) => { RTC_IO::regs().touch_pad(11) };
20    (GPIO12) => { RTC_IO::regs().touch_pad(12) };
21    (GPIO13) => { RTC_IO::regs().touch_pad(13) };
22    (GPIO14) => { RTC_IO::regs().touch_pad(14) };
23    (GPIO15) => { RTC_IO::regs().xtal_32p_pad() };
24    (GPIO16) => { RTC_IO::regs().xtal_32n_pad() };
25    (GPIO17) => { RTC_IO::regs().pad_dac1() };
26    (GPIO18) => { RTC_IO::regs().pad_dac2() };
27    (GPIO19) => { RTC_IO::regs().rtc_pad19() };
28    (GPIO20) => { RTC_IO::regs().rtc_pad20() };
29    (GPIO21) => { RTC_IO::regs().rtc_pad21() };
30}
31
32#[rustfmt::skip]
33macro_rules! hold_field {
34    ($reg:ident, GPIO0)  => { $reg.touch_pad0() };
35    ($reg:ident, GPIO1)  => { $reg.touch_pad1() };
36    ($reg:ident, GPIO2)  => { $reg.touch_pad2() };
37    ($reg:ident, GPIO3)  => { $reg.touch_pad3() };
38    ($reg:ident, GPIO4)  => { $reg.touch_pad4() };
39    ($reg:ident, GPIO5)  => { $reg.touch_pad5() };
40    ($reg:ident, GPIO6)  => { $reg.touch_pad6() };
41    ($reg:ident, GPIO7)  => { $reg.touch_pad7() };
42    ($reg:ident, GPIO8)  => { $reg.touch_pad8() };
43    ($reg:ident, GPIO9)  => { $reg.touch_pad9() };
44    ($reg:ident, GPIO10) => { $reg.touch_pad10() };
45    ($reg:ident, GPIO11) => { $reg.touch_pad11() };
46    ($reg:ident, GPIO12) => { $reg.touch_pad12() };
47    ($reg:ident, GPIO13) => { $reg.touch_pad13() };
48    ($reg:ident, GPIO14) => { $reg.touch_pad14() };
49    ($reg:ident, GPIO15) => { $reg.x32p() };
50    ($reg:ident, GPIO16) => { $reg.x32n() };
51    ($reg:ident, GPIO17) => { $reg.pdac1() };
52    ($reg:ident, GPIO18) => { $reg.pdac2() };
53    ($reg:ident, GPIO19) => { $reg.pad19() };
54    ($reg:ident, GPIO20) => { $reg.pad20() };
55    ($reg:ident, GPIO21) => { $reg.pad21() };
56}
57
58// Generates one big match statement because the pin registers have different types.
59for_each_lp_function!(
60    (LP_GPIOn $(
61        (($_lp:ident, LP_GPIOn, $n:literal), $gpio:ident, $_af:ident, $_lp_in:tt $_lp_out:tt)
62    ),*) => {
63        macro_rules! with_pin_reg {
64            ($pin:expr, |$reg:ident| $code:expr) => {{
65                match $pin {
66                    $(
67                        $n => {
68                            let $reg = pin_reg!($gpio);
69                            $code
70                        }
71                    )*
72                    _ => unreachable!(),
73                }
74            }};
75        }
76    };
77);
78
79for_each_lp_function! {
80    (($_lp:ident, LP_GPIOn, $n:literal), $gpio:ident, $_af:ident, $_lp_in:tt $_lp_out:tt) => {
81        #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
82        impl LpPin for crate::peripherals::$gpio<'_> {
83            fn lp_number(&self) -> u8 {
84                $n
85            }
86        }
87    };
88
89    // The hold register has one named field for each pad, and no index, so this code selects the
90    // field by the low-power number.
91    (LP_GPIOn $( (($_lp:ident, LP_GPIOn, $n:literal), $gpio:ident, $_af:ident, $_lp_in:tt $_lp_out:tt) ),*) => {
92        pub(crate) fn pad_hold(lp: u8, enable: bool) {
93            LPWR::regs().pad_hold().modify(|_, w| {
94                match lp {
95                    $( $n => hold_field!(w, $gpio).bit(enable), )*
96                    _ => unreachable!(),
97                }
98            });
99        }
100
101        /// Returns whether something holds the pad.
102        pub(crate) fn is_pad_held(lp: u8) -> bool {
103            let r = LPWR::regs().pad_hold().read();
104            match lp {
105                $( $n => hold_field!(r, $gpio).bit_is_set(), )*
106                _ => unreachable!(),
107            }
108        }
109
110        /// One bit for each low-power pad.
111        #[cfg(sleep_driver_supported)]
112        const ALL_PADS: u32 = 0 $( | 1 << $n )*;
113    };
114}
115
116/// Returns the bit of the pad of `gpio` in the hold register of the digital pads.
117///
118/// The register starts at the first pad of the digital supply, and the low-power pads take the
119/// numbers below it.
120fn digital_hold_bit(gpio: u8) -> Option<u8> {
121    gpio.checked_sub(21)
122}
123
124/// Takes or releases the hold of the pad of `gpio`.
125pub(crate) fn digital_pad_hold(gpio: u8, enable: bool) {
126    let Some(bit) = digital_hold_bit(gpio) else {
127        return;
128    };
129
130    let mask = 1 << bit;
131    LPWR::regs().dig_pad_hold().modify(|r, w| unsafe {
132        let bits = r.dig_pad_hold().bits();
133        w.dig_pad_hold()
134            .bits(if enable { bits | mask } else { bits & !mask })
135    });
136}
137
138/// Returns whether something holds the pad of `gpio`.
139pub(crate) fn is_digital_pad_held(gpio: u8) -> bool {
140    let Some(bit) = digital_hold_bit(gpio) else {
141        return false;
142    };
143
144    LPWR::regs().dig_pad_hold().read().dig_pad_hold().bits() & (1 << bit) != 0
145}
146
147pub(crate) fn set_config(lp: u8, input_enable: bool, mux: bool, func: LpFunction) {
148    enable_iomux_clk_gate();
149    with_pin_reg!(lp, |reg| reg.modify(|_, w| unsafe {
150        w.fun_ie().bit(input_enable);
151        w.mux_sel().bit(mux);
152        w.fun_sel().bits(func as u8)
153    }));
154}
155
156pub(crate) fn apply_wakeup(lp: u8, wakeup: bool, level: Level) {
157    RTC_IO::regs().pin(lp as usize).modify(|_, w| unsafe {
158        w.wakeup_enable().bit(wakeup);
159        w.int_type().bits(crate::gpio::lp_io::wake_trigger(level))
160    });
161}
162
163/// Returns the pads whose per-pin wakeup path triggered, as a mask of low-power numbers.
164#[cfg(sleep_driver_supported)]
165pub(crate) fn wakeup_status() -> u32 {
166    let status = RTC_IO::regs().rtc_gpio_status().read();
167    cfg_select! {
168        esp32s2 => status.gpio_status_int().bits(),
169        esp32s3 => status.int().bits(),
170    }
171}
172
173/// Returns the pads that can wake the chip through the per-pin path, as a mask of low-power
174/// numbers.
175#[cfg(sleep_driver_supported)]
176pub(crate) fn wakeup_enabled_mask() -> u32 {
177    let mut mask = 0;
178    let mut pads = ALL_PADS;
179    while pads != 0 {
180        let lp = pads.trailing_zeros();
181        pads &= !(1 << lp);
182
183        if RTC_IO::regs()
184            .pin(lp as usize)
185            .read()
186            .wakeup_enable()
187            .bit_is_set()
188        {
189            mask |= 1 << lp;
190        }
191    }
192    mask
193}
194
195/// Clears [`wakeup_status`], so that it reports the next sleep and no earlier sleep.
196#[cfg(sleep_driver_supported)]
197pub(crate) fn clear_wakeup_status() {
198    RTC_IO::regs().rtc_gpio_status_w1tc().write(|w| unsafe {
199        cfg_select! {
200            esp32s2 => w.gpio_status_int_w1tc().bits(ALL_PADS),
201            esp32s3 => w.rtc_gpio_status_int_w1tc().bits(ALL_PADS),
202        }
203    });
204}
205
206for_each_analog_function! {
207    (($_ch:ident, ADCn_CHm, $_n:literal, $_m:literal), $gpio:ident) => {
208        impl crate::peripherals::$gpio<'_> {
209            #[cfg(feature = "unstable")]
210            pub(crate) fn set_analog_impl(&self) {
211                use crate::gpio::{LpPin, Pin};
212
213                enable_iomux_clk_gate();
214
215                output_enable(self.lp_number(), false);
216                set_open_drain_output(self.number(), false);
217
218                pin_reg!($gpio).modify(|_, w| {
219                    w.fun_ie().clear_bit();
220                    w.mux_sel().set_bit();
221                    unsafe { w.fun_sel().bits(LpFunction::LP_GPIO as u8) };
222                    w.rue().bit(false);
223                    w.rde().bit(false)
224                });
225            }
226        }
227    };
228}
229
230pub(crate) fn init_pin(lp: u8, input_enable: bool) -> u8 {
231    set_config(lp, input_enable, true, LpFunction::LP_GPIO);
232    lp
233}
234
235pub(crate) fn output_enable(lp: u8, enable: bool) {
236    if enable {
237        RTC_IO::regs()
238            .rtc_gpio_enable_w1ts()
239            .write(|w| unsafe { w.rtc_gpio_enable_w1ts().bits(1 << lp) });
240    } else {
241        RTC_IO::regs()
242            .enable_w1tc()
243            .write(|w| unsafe { w.enable_w1tc().bits(1 << lp) });
244    }
245}
246
247pub(crate) fn input_enable(lp: u8, enable: bool) {
248    with_pin_reg!(lp, |reg| reg.modify(|_, w| w.fun_ie().bit(enable)));
249}
250
251pub(crate) fn pullup_enable(lp: u8, enable: bool) {
252    with_pin_reg!(lp, |reg| reg.modify(|_, w| w.rue().bit(enable)));
253}
254
255pub(crate) fn pulldown_enable(lp: u8, enable: bool) {
256    with_pin_reg!(lp, |reg| reg.modify(|_, w| w.rde().bit(enable)));
257}
258
259// The pad driver bit is part of the digital GPIO peripheral, so this function takes the digital
260// number.
261pub(crate) fn set_open_drain_output(gpio: u8, enable: bool) {
262    GPIO::regs()
263        .pin(gpio as usize)
264        .modify(|_, w| w.pad_driver().bit(enable));
265}
266
267#[cfg(lp_i2c_master_driver_supported)]
268pub(crate) fn reset_pin(lp: u8) {
269    output_enable(lp, false);
270    // On these chips, each low-power pad has the same digital pin number.
271    set_open_drain_output(lp, false);
272
273    // Resistors, input enable, the pad's LP function and whether it is muxed to the LP IO at all
274    // are all held in this register.
275    enable_iomux_clk_gate();
276    with_pin_reg!(lp, |reg| reg.reset());
277}
278
279fn enable_iomux_clk_gate() {
280    cfg_select! {
281        esp32s2 => {
282            SENS::regs()
283                .sar_io_mux_conf()
284                .modify(|_, w| w.iomux_clk_gate_en().set_bit());
285        }
286        esp32s3 => {
287            SENS::regs()
288                .sar_peri_clk_gate_conf()
289                .modify(|_, w| w.iomux_clk_en().set_bit());
290        }
291    }
292}