Skip to main content

esp_hal/gpio/lp_io/low_level/
esp32p4.rs

1use crate::{
2    gpio::{
3        LpPin,
4        lp_io::{LpFunction, LpInputSignal, LpOutputSignal},
5    },
6    peripherals::{HP_SYS, LP_GPIO, LP_IO_MUX},
7};
8
9for_each_lp_function! {
10    (($_lp_pin_name:ident, LP_GPIOn, $lp_pin:literal), $gpio:ident, $_af:ident, $_lp_in:tt $_lp_out:tt) => {
11        impl LpPin for crate::peripherals::$gpio<'_> {
12            fn lp_number(&self) -> u8 {
13                $lp_pin
14            }
15        }
16    };
17
18    // Each pad has its own signal table, so this code selects the table by the low-power number.
19    (LP_GPIOn $((
20        ($_lp_pin_name:ident, LP_GPIOn, $lp_pin:literal),
21        $gpio:ident,
22        $_af:ident,
23        ($( $lp_in_af:ident => $lp_in_signal:ident )*)
24        ($( $lp_out_af:ident => $lp_out_signal:ident )*)
25    )),*) => {
26        pub(crate) fn input_signals(lp: u8) -> &'static [(LpFunction, LpInputSignal)] {
27            match lp {
28                $( $lp_pin => &[$( (LpFunction::$lp_in_af, LpInputSignal::$lp_in_signal) ),*], )*
29                _ => unreachable!(),
30            }
31        }
32
33        pub(crate) fn output_signals(lp: u8) -> &'static [(LpFunction, LpOutputSignal)] {
34            match lp {
35                $( $lp_pin => &[$( (LpFunction::$lp_out_af, LpOutputSignal::$lp_out_signal) ),*], )*
36                _ => unreachable!(),
37            }
38        }
39    };
40}
41
42pub(crate) fn set_config(lp: u8, input_enable: bool, mux: bool, func: LpFunction) {
43    if mux {
44        LP_GPIO::regs()
45            .clk_en()
46            .modify(|_, w| w.reg_clk_en().set_bit());
47        while LP_GPIO::regs().clk_en().read().reg_clk_en().bit_is_clear() {}
48    }
49
50    LP_IO_MUX::regs().pad(lp as usize).modify(|_, w| unsafe {
51        w.mux_sel().bit(mux);
52        w.fun_ie().bit(input_enable);
53        w.fun_sel().bits(func as u8)
54    });
55}
56
57pub(crate) fn init_pin(lp: u8, input_enable: bool) -> u8 {
58    set_config(lp, input_enable, true, LpFunction::LP_GPIO);
59    lp
60}
61
62pub(crate) fn output_enable(lp: u8, enable: bool) {
63    if enable {
64        LP_GPIO::regs()
65            .enable_w1ts()
66            .write(|w| unsafe { w.bits(1 << lp) });
67    } else {
68        LP_GPIO::regs()
69            .enable_w1tc()
70            .write(|w| unsafe { w.bits(1 << lp) });
71    }
72}
73
74pub(crate) fn input_enable(lp: u8, enable: bool) {
75    LP_IO_MUX::regs()
76        .pad(lp as usize)
77        .modify(|_, w| w.slp_ie().bit(enable));
78}
79
80pub(crate) fn pullup_enable(lp: u8, enable: bool) {
81    LP_IO_MUX::regs()
82        .pad(lp as usize)
83        .modify(|_, w| w.rue().bit(enable));
84}
85
86pub(crate) fn pulldown_enable(lp: u8, enable: bool) {
87    LP_IO_MUX::regs()
88        .pad(lp as usize)
89        .modify(|_, w| w.rde().bit(enable));
90}
91
92pub(crate) fn pad_hold(lp: u8, enable: bool) {
93    let mask = 1 << lp;
94    LP_IO_MUX::regs().lp_pad_hold().modify(|r, w| unsafe {
95        let bits = r.reg_lp_gpio_hold().bits();
96        w.reg_lp_gpio_hold()
97            .bits(if enable { bits | mask } else { bits & !mask })
98    });
99}
100
101/// Returns whether something holds the pad.
102pub(crate) fn is_pad_held(lp: u8) -> bool {
103    LP_IO_MUX::regs()
104        .lp_pad_hold()
105        .read()
106        .reg_lp_gpio_hold()
107        .bits()
108        & (1 << lp)
109        != 0
110}
111
112/// Takes or releases the hold of the pad of `gpio`.
113///
114/// The registers start at the first pad of the digital supply, which follows the 16 low-power pads.
115pub(crate) fn digital_pad_hold(gpio: u8, enable: bool) {
116    let Some(bit) = gpio.checked_sub(16) else {
117        return;
118    };
119
120    if bit < 32 {
121        let mask = 1 << bit;
122        HP_SYS::regs().gpio_o_hold_ctrl0().modify(|r, w| unsafe {
123            let bits = r.reg_gpio_0_hold_low().bits();
124            w.reg_gpio_0_hold_low()
125                .bits(if enable { bits | mask } else { bits & !mask })
126        });
127    } else {
128        let mask = 1 << (bit - 32);
129        HP_SYS::regs().gpio_o_hold_ctrl1().modify(|r, w| unsafe {
130            let bits = r.reg_gpio_0_hold_high().bits();
131            w.reg_gpio_0_hold_high()
132                .bits(if enable { bits | mask } else { bits & !mask })
133        });
134    }
135}
136
137/// Returns whether something holds the pad of `gpio`.
138pub(crate) fn is_digital_pad_held(gpio: u8) -> bool {
139    let Some(bit) = gpio.checked_sub(16) else {
140        return false;
141    };
142
143    if bit < 32 {
144        HP_SYS::regs()
145            .gpio_o_hold_ctrl0()
146            .read()
147            .reg_gpio_0_hold_low()
148            .bits()
149            & (1 << bit)
150            != 0
151    } else {
152        HP_SYS::regs()
153            .gpio_o_hold_ctrl1()
154            .read()
155            .reg_gpio_0_hold_high()
156            .bits()
157            & (1 << (bit - 32))
158            != 0
159    }
160}
161
162// The pad driver bit is part of the digital GPIO peripheral, so this function takes the digital
163// number.
164pub(crate) fn set_open_drain_output(gpio: u8, enable: bool) {
165    crate::peripherals::GPIO::regs()
166        .pin(gpio as usize)
167        .modify(|_, w| w.pad_driver().bit(enable));
168}
169
170#[cfg(lp_i2c_master_driver_supported)]
171pub(crate) fn reset_pin(lp: u8) {
172    LP_GPIO::regs()
173        .clk_en()
174        .modify(|_, w| w.reg_clk_en().set_bit());
175    while LP_GPIO::regs().clk_en().read().reg_clk_en().bit_is_clear() {}
176
177    output_enable(lp, false);
178    // On this chip, each low-power pad has the same digital pin number.
179    set_open_drain_output(lp, false);
180
181    // Resistors, input enable, the pad's LP function and whether it is muxed to the LP IO at all
182    // are all held in this register. Resetting it hands the pad back to the digital IO MUX.
183    LP_IO_MUX::regs().pad(lp as usize).reset();
184}