Skip to main content

esp_hal/gpio/lp_io/low_level/
v4.rs

1use crate::{
2    gpio::{LpPin, lp_io::LpFunction},
3    peripherals::LP_AON,
4};
5
6cfg_select! {
7    esp32c6 => {
8        use crate::peripherals::{LP_IO as LP_GPIO, LP_IO as LP_IO_MUX};
9    }
10    any(esp32c5, esp32c61, esp32h4) => {
11        // Only a low-power peripheral or a low-power core reads and drives the pads.
12        #[cfg(any(
13            ulp_riscv_driver_supported,
14            lp_io_has_gpio_matrix,
15            lp_i2c_master_driver_supported,
16        ))]
17        use crate::peripherals::LP_GPIO;
18        use crate::peripherals::LP_IO_MUX;
19    }
20}
21
22for_each_lp_function! {
23    (($_lp:ident, LP_GPIOn, $pin:literal), $gpio:ident, $_af:ident, $_lp_in:tt $_lp_out:tt) => {
24        #[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
25        impl LpPin for crate::peripherals::$gpio<'_> {
26            fn lp_number(&self) -> u8 {
27                $pin
28            }
29        }
30    };
31}
32
33// The low-power number of a pad is its digital number on these chips, and one register holds every
34// pad, so both paths write the same bit.
35pub(crate) fn pad_hold(lp: u8, enable: bool) {
36    digital_pad_hold(lp, enable);
37}
38
39/// Returns whether something holds the pad.
40pub(crate) fn is_pad_held(lp: u8) -> bool {
41    is_digital_pad_held(lp)
42}
43
44/// Takes or releases the hold of the pad of `gpio`.
45pub(crate) fn digital_pad_hold(gpio: u8, enable: bool) {
46    let update = |bits: u32, mask: u32| if enable { bits | mask } else { bits & !mask };
47    if gpio < 32 {
48        LP_AON::regs()
49            .gpio_hold0()
50            .modify(|r, w| unsafe { w.bits(update(r.bits(), 1 << gpio)) });
51    } else {
52        LP_AON::regs()
53            .gpio_hold1()
54            .modify(|r, w| unsafe { w.bits(update(r.bits(), 1 << (gpio - 32))) });
55    }
56}
57
58/// Returns whether something holds the pad of `gpio`.
59pub(crate) fn is_digital_pad_held(gpio: u8) -> bool {
60    if gpio < 32 {
61        LP_AON::regs().gpio_hold0().read().bits() & (1 << gpio) != 0
62    } else {
63        LP_AON::regs().gpio_hold1().read().bits() & (1 << (gpio - 32)) != 0
64    }
65}
66
67pub(crate) fn set_config(lp: u8, input_enable: bool, mux: bool, func: LpFunction) {
68    let mask = 1 << lp;
69    LP_AON::regs().gpio_mux().modify(|r, w| unsafe {
70        let bits = r.sel().bits();
71        w.sel().bits(if mux { bits | mask } else { bits & !mask })
72    });
73
74    LP_IO_MUX::regs().gpio(lp as usize).modify(|_, w| unsafe {
75        w.slp_sel().bit(false);
76        w.fun_ie().bit(input_enable);
77        w.mcu_ie().bit(input_enable);
78        w.mcu_sel().bits(func as u8)
79    });
80}
81
82#[cfg(any(ulp_riscv_driver_supported, lp_io_has_gpio_matrix))]
83pub(crate) fn init_pin(lp: u8, input_enable: bool) -> u8 {
84    set_config(lp, input_enable, true, LpFunction::LP_GPIO);
85    lp
86}
87
88#[cfg(any(
89    ulp_riscv_driver_supported,
90    lp_io_has_gpio_matrix,
91    lp_i2c_master_driver_supported,
92))]
93pub(crate) fn output_enable(lp: u8, enable: bool) {
94    if enable {
95        LP_GPIO::regs()
96            .out_enable_w1ts()
97            .write(|w| unsafe { w.enable_w1ts().bits(1 << lp) });
98    } else {
99        LP_GPIO::regs()
100            .out_enable_w1tc()
101            .write(|w| unsafe { w.enable_w1tc().bits(1 << lp) });
102    }
103}
104
105#[cfg(any(ulp_riscv_driver_supported, lp_io_has_gpio_matrix))]
106pub(crate) fn input_enable(lp: u8, enable: bool) {
107    LP_IO_MUX::regs().gpio(lp as usize).modify(|_, w| {
108        w.fun_ie().bit(enable);
109        w.mcu_ie().bit(enable)
110    });
111}
112
113pub(crate) fn pullup_enable(lp: u8, enable: bool) {
114    LP_IO_MUX::regs()
115        .gpio(lp as usize)
116        .modify(|_, w| w.fun_wpu().bit(enable));
117}
118
119pub(crate) fn pulldown_enable(lp: u8, enable: bool) {
120    LP_IO_MUX::regs()
121        .gpio(lp as usize)
122        .modify(|_, w| w.fun_wpd().bit(enable));
123}
124
125// On these chips, the pad driver bit is part of the low-power GPIO peripheral, so this function
126// takes the low-power number, like the functions beside it.
127#[cfg(any(
128    ulp_riscv_driver_supported,
129    lp_io_has_gpio_matrix,
130    lp_i2c_master_driver_supported,
131))]
132pub(crate) fn set_open_drain_output(lp: u8, enable: bool) {
133    LP_GPIO::regs()
134        .pin(lp as usize)
135        .modify(|_, w| w.pad_driver().bit(enable));
136}
137
138#[cfg(lp_i2c_master_driver_supported)]
139pub(crate) fn reset_pin(lp: u8) {
140    output_enable(lp, false);
141    set_open_drain_output(lp, false);
142
143    // Resistors, input enable and the pad's LP function all live in this register.
144    LP_IO_MUX::regs().gpio(lp as usize).reset();
145
146    // Hand the pad back to the digital IO MUX.
147    LP_AON::regs()
148        .gpio_mux()
149        .modify(|r, w| unsafe { w.sel().bits(r.sel().bits() & !(1 << lp)) });
150}