Skip to main content

esp_hal/gpio/lp_io/low_level/
esp32.rs

1use crate::{
2    gpio::lp_io::LpFunction,
3    peripherals::{GPIO, LPWR, RTC_IO},
4};
5
6macro_rules! lp_io_analog {
7    ($pin_peri:ident, $lp_pin:expr, $pin_reg:expr, $prefix:pat, $hold:ident) => {
8        paste::paste! {
9            #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
10            impl crate::gpio::LpPin for crate::peripherals::$pin_peri<'_> {
11                fn lp_number(&self) -> u8 {
12                    $lp_pin
13                }
14            }
15
16            impl crate::peripherals::$pin_peri<'_> {
17                #[cfg(feature = "unstable")]
18                pub(crate) fn set_analog_impl(&self) {
19                    use crate::gpio::{LpPin, Pin};
20
21                    output_enable(self.lp_number(), false);
22                    set_open_drain_output(self.number(), false);
23
24                    RTC_IO::regs().$pin_reg.modify(|_, w| {
25                        w.[<$prefix fun_ie>]().clear_bit();
26                        w.[<$prefix mux_sel>]().set_bit();
27                        unsafe { w.[<$prefix fun_sel>]().bits(LpFunction::LP_GPIO as u8) };
28
29                        // Only output pins have PU/PD resistors.
30                        for_each_gpio! {
31                            ($n:tt, $pin_peri $in_afs:tt $out_afs:tt ($input:tt [Output])) => {
32                                w.[<$prefix rue>]().bit(false);
33                                w.[<$prefix rde>]().bit(false);
34                            };
35                        }
36
37                        w
38                    });
39                }
40            }
41        }
42    };
43
44    (
45        $(($pin_peri:ident, $lp_pin:tt, $pin_reg:expr, $prefix:pat, $hold:ident))+
46    ) => {
47        $(
48            lp_io_analog!($pin_peri, $lp_pin, $pin_reg, $prefix, $hold);
49        )+
50
51        // Each pad has its own register, so this code selects the register by the low-power number.
52        pub(crate) fn set_config(lp: u8, input_enable: bool, mux: bool, func: LpFunction) {
53            paste::paste! {
54                match lp {
55                    $(
56                        $lp_pin => {
57                            RTC_IO::regs().$pin_reg.modify(|_, w| unsafe {
58                                w.[<$prefix fun_ie>]().bit(input_enable);
59                                w.[<$prefix mux_sel>]().bit(mux);
60                                w.[<$prefix fun_sel>]().bits(func as u8)
61                            });
62                        }
63                    )+
64                    _ => unreachable!(),
65                }
66            }
67        }
68
69        /// A hold takes two bits on this chip, one in the pad register and one
70        /// in the always-on register. The pad keeps the bit of the pad register
71        /// through a reset, so a release must clear both.
72        pub(crate) fn pad_hold(lp: u8, enable: bool) {
73            paste::paste! {
74                match lp {
75                    $(
76                        $lp_pin => {
77                            RTC_IO::regs()
78                                .$pin_reg
79                                .modify(|_, w| w.[<$prefix hold>]().bit(enable));
80                            LPWR::regs()
81                                .hold_force()
82                                .modify(|_, w| w.$hold().bit(enable));
83                        }
84                    )+
85                    _ => unreachable!(),
86                }
87            }
88        }
89
90        /// Returns whether something holds the pad.
91        ///
92        /// Either bit of [`pad_hold`] alone keeps the pad frozen.
93        pub(crate) fn is_pad_held(lp: u8) -> bool {
94            paste::paste! {
95                match lp {
96                    $(
97                        $lp_pin => {
98                            RTC_IO::regs().$pin_reg.read().[<$prefix hold>]().bit_is_set()
99                                || LPWR::regs().hold_force().read().$hold().bit_is_set()
100                        }
101                    )+
102                    _ => unreachable!(),
103                }
104            }
105        }
106
107        /// One bit for each low-power pad.
108        const ALL_PADS: u32 = 0 $( | 1 << $lp_pin )+;
109
110        macro_rules! set_one_pad_field {
111            $(
112                ($lp_pin, $field:ident, $enable:ident) => {{
113                    paste::paste! {
114                        RTC_IO::regs()
115                            .$pin_reg
116                            .modify(|_, w|  w.[<$prefix $field>]().bit($enable));
117                    }
118                }};
119            )+
120        }
121    };
122}
123
124lp_io_analog! {
125    (GPIO36, 0,  sensor_pads(),    sense1_, sense1    )
126    (GPIO37, 1,  sensor_pads(),    sense2_, sense2    )
127    (GPIO38, 2,  sensor_pads(),    sense3_, sense3    )
128    (GPIO39, 3,  sensor_pads(),    sense4_, sense4    )
129    (GPIO34, 4,  adc_pad(),        adc1_,   adc1      )
130    (GPIO35, 5,  adc_pad(),        adc2_,   adc2      )
131    (GPIO25, 6,  pad_dac1(),       "",      pdac1     )
132    (GPIO26, 7,  pad_dac2(),       "",      pdac2     )
133    (GPIO33, 8,  xtal_32k_pad(),   x32n_,   x32n      )
134    (GPIO32, 9,  xtal_32k_pad(),   x32p_,   x32p      )
135    (GPIO4,  10, touch_pad0(),     "",      touch_pad0)
136    (GPIO0,  11, touch_pad1(),     "",      touch_pad1)
137    (GPIO2,  12, touch_pad2(),     "",      touch_pad2)
138    (GPIO15, 13, touch_pad3(),     "",      touch_pad3)
139    (GPIO13, 14, touch_pad4(),     "",      touch_pad4)
140    (GPIO12, 15, touch_pad5(),     "",      touch_pad5)
141    (GPIO14, 16, touch_pad6(),     "",      touch_pad6)
142    (GPIO27, 17, touch_pad7(),     "",      touch_pad7)
143}
144
145macro_rules! set_pad_field {
146    ($pin:ident, $field:ident, $enable:ident) => {{
147        match $pin {
148            0 => set_one_pad_field!(0, $field, $enable),
149            1 => set_one_pad_field!(1, $field, $enable),
150            2 => set_one_pad_field!(2, $field, $enable),
151            3 => set_one_pad_field!(3, $field, $enable),
152            4 => set_one_pad_field!(4, $field, $enable),
153            5 => set_one_pad_field!(5, $field, $enable),
154            6 => set_one_pad_field!(6, $field, $enable),
155            7 => set_one_pad_field!(7, $field, $enable),
156            8 => set_one_pad_field!(8, $field, $enable),
157            9 => set_one_pad_field!(9, $field, $enable),
158            10 => set_one_pad_field!(10, $field, $enable),
159            11 => set_one_pad_field!(11, $field, $enable),
160            12 => set_one_pad_field!(12, $field, $enable),
161            13 => set_one_pad_field!(13, $field, $enable),
162            14 => set_one_pad_field!(14, $field, $enable),
163            15 => set_one_pad_field!(15, $field, $enable),
164            16 => set_one_pad_field!(16, $field, $enable),
165            17 => set_one_pad_field!(17, $field, $enable),
166            _ => unreachable!(),
167        }
168    }};
169}
170
171macro_rules! set_pull_field {
172    ($pin:ident, $field:ident, $enable:ident) => {{
173        match $pin {
174            0..=5 => warn!("Pin {} does not have PU/PD resistors", $pin),
175            6 => set_one_pad_field!(6, $field, $enable),
176            7 => set_one_pad_field!(7, $field, $enable),
177            8 => set_one_pad_field!(8, $field, $enable),
178            9 => set_one_pad_field!(9, $field, $enable),
179            10 => set_one_pad_field!(10, $field, $enable),
180            11 => set_one_pad_field!(11, $field, $enable),
181            12 => set_one_pad_field!(12, $field, $enable),
182            13 => set_one_pad_field!(13, $field, $enable),
183            14 => set_one_pad_field!(14, $field, $enable),
184            15 => set_one_pad_field!(15, $field, $enable),
185            16 => set_one_pad_field!(16, $field, $enable),
186            17 => set_one_pad_field!(17, $field, $enable),
187            _ => unreachable!(),
188        }
189    }};
190}
191
192/// Returns the bit of the pad of `gpio` in the hold register of the digital pads.
193///
194/// The register covers the pads of the digital supply, and lists them in its own order. The digital
195/// supply feeds no other pad, so no other pad reaches this function.
196fn digital_hold_bit(gpio: u8) -> Option<u8> {
197    Some(match gpio {
198        1 => 1,
199        3 => 0,
200        5 => 8,
201        6..=11 => gpio - 4,
202        16..=19 | 21..=23 => gpio - 7,
203        _ => return None,
204    })
205}
206
207/// Takes or releases the hold of the pad of `gpio`.
208pub(crate) fn digital_pad_hold(gpio: u8, enable: bool) {
209    let Some(bit) = digital_hold_bit(gpio) else {
210        return;
211    };
212
213    let mask = 1 << bit;
214    RTC_IO::regs().dig_pad_hold().modify(|r, w| unsafe {
215        let bits = r.dig_pad_hold().bits();
216        w.dig_pad_hold()
217            .bits(if enable { bits | mask } else { bits & !mask })
218    });
219}
220
221/// Returns whether something holds the pad of `gpio`.
222pub(crate) fn is_digital_pad_held(gpio: u8) -> bool {
223    let Some(bit) = digital_hold_bit(gpio) else {
224        return false;
225    };
226
227    RTC_IO::regs().dig_pad_hold().read().dig_pad_hold().bits() & (1 << bit) != 0
228}
229
230pub(crate) fn apply_wakeup(lp: u8, wakeup: bool, level: crate::gpio::Level) {
231    RTC_IO::regs().pin(lp as usize).modify(|_, w| unsafe {
232        w.wakeup_enable().bit(wakeup);
233        w.int_type().bits(crate::gpio::lp_io::wake_trigger(level))
234    });
235}
236
237/// Returns the pads whose per-pin wakeup path triggered, as a mask of low-power numbers.
238pub(crate) fn wakeup_status() -> u32 {
239    RTC_IO::regs().status().read().int().bits()
240}
241
242/// Clears [`wakeup_status`], so that it reports the next sleep and no earlier sleep.
243pub(crate) fn clear_wakeup_status() {
244    RTC_IO::regs()
245        .status_w1tc()
246        .write(|w| unsafe { w.status_int_w1tc().bits(ALL_PADS) });
247}
248
249/// Returns the pads that can wake the chip through the per-pin path, as a mask of low-power
250/// numbers.
251pub(crate) fn wakeup_enabled_mask() -> u32 {
252    let mut mask = 0;
253    let mut pads = ALL_PADS;
254    while pads != 0 {
255        let lp = pads.trailing_zeros();
256        pads &= !(1 << lp);
257
258        if RTC_IO::regs()
259            .pin(lp as usize)
260            .read()
261            .wakeup_enable()
262            .bit_is_set()
263        {
264            mask |= 1 << lp;
265        }
266    }
267    mask
268}
269
270#[expect(dead_code)]
271pub(crate) fn init_pin(lp: u8, input_enable: bool) -> u8 {
272    set_config(lp, input_enable, true, LpFunction::LP_GPIO);
273    lp
274}
275
276pub(crate) fn output_enable(lp: u8, enable: bool) {
277    if enable {
278        RTC_IO::regs()
279            .enable_w1ts()
280            .write(|w| unsafe { w.enable_w1ts().bits(1 << lp) });
281    } else {
282        RTC_IO::regs()
283            .enable_w1tc()
284            .write(|w| unsafe { w.enable_w1tc().bits(1 << lp) });
285    }
286}
287
288#[expect(dead_code)]
289pub(crate) fn input_enable(lp: u8, enable: bool) {
290    set_pad_field!(lp, fun_ie, enable);
291}
292
293pub(crate) fn pullup_enable(lp: u8, enable: bool) {
294    set_pull_field!(lp, rue, enable);
295}
296
297pub(crate) fn pulldown_enable(lp: u8, enable: bool) {
298    set_pull_field!(lp, rde, enable);
299}
300
301// The pad driver bit is part of the digital GPIO peripheral, and this chip gives a pad a different
302// low-power number, so this function takes the digital number.
303pub(crate) fn set_open_drain_output(gpio: u8, enable: bool) {
304    GPIO::regs()
305        .pin(gpio as usize)
306        .modify(|_, w| w.pad_driver().bit(enable));
307}