1
#![cfg_attr(docsrs, procmacros::doc_replace(
2
    "lp_io" => gpio_for_signal!(LP_GPIO1),
3
    "lp_num" => "1"
4
))]
5
//! Low Power IO (LP_IO)
6
//!
7
//! # Overview
8
//!
9
//! The hardware provides GPIO pins with low-power capabilities and analog
10
//! functions. These pins are controlled by the LP IO peripheral, which some
11
//! devices call RTC IO.
12
//!
13
//! ## Configuration
14
//!
15
//! Low-power pins bypass the regular IO MUX and GPIO matrix so they can be used
16
//! by the ULP/LP core and low-power peripherals. They can remain operational
17
//! during deep sleep and can be used as wake-up sources.
18
#![cfg_attr(
19
    ulp_riscv_driver_supported,
20
    doc = "
21

            
22
## Handing a pin to the low-power core
23

            
24
[`LowPowerInput`], [`LowPowerOutput`], and [`LowPowerOutputOpenDrain`] are opaque tokens that
25
represent a pad owned by the low-power domain. Obtain them by consuming a
26
[`Input`](crate::gpio::Input) or [`Output`](crate::gpio::Output) driver with
27
[`Input::into_lp`](crate::gpio::Input::into_lp), [`Output::into_lp`](crate::gpio::Output::into_lp),
28
or [`Output::into_open_drain_lp`](crate::gpio::Output::into_open_drain_lp). The conversion muxes the
29
pad into the low-power domain; afterwards the HP core can no longer drive it.
30

            
31
The `PIN` parameter of the tokens is the low-power pin number, which is how low-power core firmware
32
refers to the pin. The conversions verify at runtime that the pad they are given is that pin, and
33
hand the driver back when it is not.
34

            
35
The tokens can also be created straight from a pin with `LowPowerInput::new`,
36
`LowPowerOutput::new`, and `LowPowerOutputOpenDrain::new`, which check the pin number at compile
37
time but leave the pad's configuration up to the token.
38

            
39
## Examples
40

            
41
### Hand a pin to the LP core
42

            
43
```rust, no_run
44
# {before_snippet}
45
use esp_hal::gpio::{Level, Output, OutputConfig};
46
let pin = Output::new(peripherals.__lp_io__, Level::Low, OutputConfig::default());
47
let lp_pin = pin.into_lp::<__lp_num__>().unwrap();
48
# {after_snippet}
49
```
50
"
51
)]
52

            
53
#[cfg(ulp_riscv_driver_supported)]
54
use core::marker::PhantomData;
55

            
56
#[cfg(any(ulp_riscv_driver_supported, lp_io_has_gpio_matrix))]
57
use super::{InputPin, LpPin, OutputPin};
58

            
59
define_lp_io_signals!();
60
define_lp_functions!();
61

            
62
#[cfg_attr(lp_io_version = "esp32", path = "low_level/esp32.rs")]
63
#[cfg_attr(lp_io_version = "v2", path = "low_level/v2.rs")]
64
#[cfg_attr(lp_io_version = "v3", path = "low_level/v3.rs")]
65
#[cfg_attr(lp_io_version = "esp32h2", path = "low_level/esp32h2.rs")]
66
#[cfg_attr(lp_io_version = "esp32p4", path = "low_level/esp32p4.rs")]
67
#[cfg_attr(lp_io_version = "esp32s31", path = "low_level/esp32s31.rs")]
68
#[cfg_attr(lp_io_version = "v4", path = "low_level/v4.rs")]
69
pub(crate) mod low_level;
70

            
71
/// Returns the trigger type that the low-power wakeup register of a pad needs to wake the chip at
72
/// `level`.
73
///
74
/// The field takes the same values as the digital interrupt type.
75
// Only the chips that arm the pads one by one have such a register. The other chips reach the pads
76
// through `ext1`, which takes a mask.
77
#[cfg(all(
78
    sleep_pin_wakeup_version_is_set,
79
    any(sleep_ext1_version = "1", not(sleep_ext1_version_is_set))
80
))]
81
pub(crate) const fn wake_trigger(level: super::Level) -> u8 {
82
    match level {
83
        super::Level::Low => super::Event::LowLevel as u8,
84
        super::Level::High => super::Event::HighLevel as u8,
85
    }
86
}
87

            
88
/// Trait implemented by pins with a known low-power pin number.
89
#[cfg(ulp_riscv_driver_supported)]
90
#[doc(hidden)]
91
pub trait LowPowerPin<const PIN: u8>: LpPin {}
92

            
93
#[cfg(ulp_riscv_driver_supported)]
94
for_each_lp_function! {
95
    (($_signal:ident, LP_GPIOn, $pin:literal), $gpio:ident, $_af:ident, $_lp_in:tt $_lp_out:tt) => {
96
        impl LowPowerPin<$pin> for crate::peripherals::$gpio<'_> {}
97
    };
98
}
99

            
100
/// Configures an LP pin as an open-drain output with its pull-up enabled, and routes an LP
101
/// peripheral's signal pair to it through the LP GPIO matrix.
102
#[cfg(lp_io_has_gpio_matrix)]
103
#[cfg_attr(not(soc_has_lp_i2c0), expect(dead_code))]
104
pub(crate) fn connect_open_drain_signals(
105
    pin: &(impl LpPin + InputPin + OutputPin),
106
    input: LpInputSignal,
107
    output: LpOutputSignal,
108
) {
109
    let lp_pin = low_level::init_pin(pin.lp_number(), true);
110
    low_level::set_open_drain_output(pin.number(), true);
111
    low_level::input_enable(lp_pin, true);
112
    low_level::pullup_enable(lp_pin, true);
113
    low_level::pulldown_enable(lp_pin, false);
114
    low_level::output_enable(lp_pin, true);
115

            
116
    route_input(lp_pin, input, true);
117
    route_output(lp_pin, output);
118
}
119

            
120
/// Returns an LP pin to its reset state, handing the pad back to the digital IO MUX.
121
///
122
/// A pin in this state no longer drives, senses or pulls anything, so any LP peripheral signal
123
/// still routed to it through the LP GPIO matrix cannot reach the pad.
124
#[cfg(lp_i2c_master_driver_supported)]
125
pub(crate) use low_level::reset_pin;
126

            
127
/// Configures an LP pin as an input and routes an LP peripheral's input signal to it.
128
#[cfg(all(lp_io_has_gpio_matrix, lp_uart_driver_supported))]
129
pub(crate) fn connect_input_signal(pin: &(impl LpPin + InputPin), input: LpInputSignal) {
130
    let lp_pin = pin.lp_number();
131

            
132
    let mux_af = low_level::input_signals(lp_pin)
133
        .iter()
134
        .find(|(_, signal)| *signal == input)
135
        .map(|(af, _)| *af);
136

            
137
    match mux_af {
138
        Some(af) => low_level::set_config(lp_pin, true, true, af),
139
        None => {
140
            low_level::init_pin(lp_pin, true);
141
        }
142
    }
143

            
144
    low_level::input_enable(lp_pin, true);
145
    route_input(lp_pin, input, mux_af.is_none());
146
}
147

            
148
/// Configures an LP pin as an output and routes an LP peripheral's output signal to it.
149
#[cfg(all(lp_io_has_gpio_matrix, lp_uart_driver_supported))]
150
pub(crate) fn connect_output_signal(pin: &(impl LpPin + OutputPin), output: LpOutputSignal) {
151
    let lp_pin = pin.lp_number();
152

            
153
    let mux_af = low_level::output_signals(lp_pin)
154
        .iter()
155
        .find(|(_, signal)| *signal == output)
156
        .map(|(af, _)| *af);
157

            
158
    match mux_af {
159
        Some(af) => low_level::set_config(lp_pin, false, true, af),
160
        None => {
161
            low_level::init_pin(lp_pin, false);
162
            route_output(lp_pin, output);
163
        }
164
    }
165
}
166

            
167
/// Points an LP peripheral's input signal at `lp_pin`, either through the LP GPIO matrix or, when
168
/// the pad's own LP IO MUX function feeds the peripheral, straight from the pad.
169
#[cfg(lp_io_has_gpio_matrix)]
170
fn route_input(lp_pin: u8, input: LpInputSignal, use_gpio_matrix: bool) {
171
    crate::peripherals::LP_GPIO::regs()
172
        .func_in_sel_cfg(input as usize)
173
        .write(|w| unsafe {
174
            w.sig_in_sel().bit(use_gpio_matrix);
175
            cfg_select! {
176
                esp32s31 => {
177
                    w.func_in_inv_sel().clear_bit();
178
                    w.func_in_sel().bits(lp_pin)
179
                }
180
                _ => {
181
                    w.in_inv_sel().clear_bit();
182
                    w.in_sel().bits(lp_pin)
183
                }
184
            }
185
        });
186
}
187

            
188
#[cfg(lp_io_has_gpio_matrix)]
189
fn route_output(lp_pin: u8, output: LpOutputSignal) {
190
    crate::peripherals::LP_GPIO::regs()
191
        .func_out_sel_cfg(lp_pin as usize)
192
        .write(|w| unsafe {
193
            cfg_select! {
194
                esp32s31 => {
195
                    w.func_out_sel().bits(output as _);
196
                    w.func_out_inv_sel().clear_bit();
197
                    w.func_oe_sel().clear_bit();
198
                    w.func_oe_inv_sel().clear_bit()
199
                }
200
                _ => {
201
                    w.out_sel().bits(output as _);
202
                    w.out_inv_sel().clear_bit();
203
                    // Let the peripheral drive the output enable signal.
204
                    w.oe_sel().clear_bit();
205
                    w.oe_inv_sel().clear_bit()
206
                }
207
            }
208
        });
209
}
210

            
211
/// Returns the number that the low-power registers use for `gpio`, if they reach the pad.
212
// Needed because AnyPin::lp_number is infallible
213
pub(crate) fn lp_number(gpio: u8) -> Option<u8> {
214
419
    for_each_lp_function! {
215
        (($_signal:ident, LP_GPIOn, $pin:literal), $gpio:ident, $_af:ident, $_lp_in:tt $_lp_out:tt) => {
216
            if gpio == crate::peripherals::$gpio::NUMBER {
217
                return Some($pin);
218
            }
219
        };
220
    }
221

            
222
    None
223
}
224

            
225
/// Tokens to hand out a pin to a low-power CPU.
226
// FIXME: tokens should be 'static to be handed out.
227
#[cfg(ulp_riscv_driver_supported)]
228
mod ulp_tokens {
229
    use super::*;
230
    use crate::gpio::Pin;
231

            
232
    impl<'d> crate::gpio::Input<'d> {
233
        /// Hands the pin over to the low-power core.
234
        ///
235
        /// `PIN` is the low-power pin number, which is how low-power core firmware refers to the
236
        /// pin.
237
        ///
238
        /// # Errors
239
        ///
240
        /// The original driver when the pad is not the low-power pin numbered `PIN`.
241
        #[instability::unstable]
242
        pub fn into_lp<const PIN: u8>(self) -> Result<LowPowerInput<'d, PIN>, Self> {
243
            if lp_number(self.pin.pin.number()) != Some(PIN) {
244
                return Err(self);
245
            }
246

            
247
            Ok(LowPowerInput::new_untyped(self.pin.pin))
248
        }
249
    }
250

            
251
    impl<'d> crate::gpio::Output<'d> {
252
        /// Hands the pin over to the low-power core.
253
        ///
254
        /// `PIN` is the low-power pin number, which is how low-power core firmware refers to the
255
        /// pin.
256
        ///
257
        /// # Errors
258
        ///
259
        /// The original driver when the pad is not the low-power pin numbered `PIN`.
260
        #[instability::unstable]
261
        pub fn into_lp<const PIN: u8>(self) -> Result<LowPowerOutput<'d, PIN>, Self> {
262
            if lp_number(self.pin.pin.number()) != Some(PIN) {
263
                return Err(self);
264
            }
265

            
266
            Ok(LowPowerOutput::new_untyped(self.pin.pin))
267
        }
268

            
269
        /// Hands the pin over to the low-power core as an open-drain output.
270
        ///
271
        /// `PIN` is the low-power pin number, which is how low-power core firmware refers to the
272
        /// pin. The pad's pull-up is enabled, regardless of the driver's configuration.
273
        ///
274
        /// # Errors
275
        ///
276
        /// The original driver when the pad is not the low-power pin numbered `PIN`.
277
        #[instability::unstable]
278
        pub fn into_open_drain_lp<const PIN: u8>(
279
            self,
280
        ) -> Result<LowPowerOutputOpenDrain<'d, PIN>, Self> {
281
            if lp_number(self.pin.pin.number()) != Some(PIN) {
282
                return Err(self);
283
            }
284

            
285
            Ok(LowPowerOutputOpenDrain::new_untyped(self.pin.pin))
286
        }
287
    }
288

            
289
    /// A GPIO output pin configured for low-power operation.
290
    #[instability::unstable]
291
    pub struct LowPowerOutput<'d, const PIN: u8> {
292
        pub(super) phantom: PhantomData<&'d mut ()>,
293
    }
294

            
295
    impl<'d, const PIN: u8> LowPowerOutput<'d, PIN> {
296
        /// Creates a new output pin for use by the low-power core.
297
        #[instability::unstable]
298
        pub fn new<P>(pin: P) -> Self
299
        where
300
            P: LowPowerPin<PIN> + OutputPin + 'd,
301
        {
302
            Self::new_untyped(pin)
303
        }
304

            
305
        /// Takes a pad that the caller confirmed to be the low-power pin with the number `PIN`.
306
        pub(super) fn new_untyped<P>(_pin: P) -> Self
307
        where
308
            P: Pin + 'd,
309
        {
310
            low_level::init_pin(PIN, false);
311
            low_level::output_enable(PIN, true);
312

            
313
            Self {
314
                phantom: PhantomData,
315
            }
316
        }
317
    }
318

            
319
    /// A GPIO input pin configured for low-power operation.
320
    #[instability::unstable]
321
    pub struct LowPowerInput<'d, const PIN: u8> {
322
        pub(super) phantom: PhantomData<&'d mut ()>,
323
    }
324

            
325
    impl<'d, const PIN: u8> LowPowerInput<'d, PIN> {
326
        /// Creates a new input pin for use by the low-power core.
327
        #[instability::unstable]
328
        pub fn new<P>(pin: P) -> Self
329
        where
330
            P: LowPowerPin<PIN> + InputPin + 'd,
331
        {
332
            Self::new_untyped(pin)
333
        }
334

            
335
        /// Takes a pad that the caller confirmed to be the low-power pin with the number `PIN`.
336
        pub(super) fn new_untyped<P>(_pin: P) -> Self
337
        where
338
            P: Pin + 'd,
339
        {
340
            low_level::init_pin(PIN, true);
341
            low_level::input_enable(PIN, true);
342
            low_level::pullup_enable(PIN, false);
343
            low_level::pulldown_enable(PIN, false);
344

            
345
            Self {
346
                phantom: PhantomData,
347
            }
348
        }
349

            
350
        /// Enables or disables the internal pull-up resistor.
351
        pub fn pullup_enable(&self, enable: bool) {
352
            low_level::pullup_enable(PIN, enable);
353
        }
354

            
355
        /// Enables or disables the internal pull-down resistor.
356
        pub fn pulldown_enable(&self, enable: bool) {
357
            low_level::pulldown_enable(PIN, enable);
358
        }
359
    }
360

            
361
    /// A GPIO open-drain output pin configured for low-power operation.
362
    #[instability::unstable]
363
    pub struct LowPowerOutputOpenDrain<'d, const PIN: u8> {
364
        pub(super) phantom: PhantomData<&'d mut ()>,
365
    }
366

            
367
    impl<'d, const PIN: u8> LowPowerOutputOpenDrain<'d, PIN> {
368
        /// Creates a new open-drain output pin for use by the low-power core.
369
        #[instability::unstable]
370
        pub fn new<P>(pin: P) -> Self
371
        where
372
            P: LowPowerPin<PIN> + InputPin + OutputPin + 'd,
373
        {
374
            Self::new_untyped(pin)
375
        }
376

            
377
        /// Takes a pad that the caller confirmed to be the low-power pin with the number `PIN`.
378
        pub(super) fn new_untyped<P>(pin: P) -> Self
379
        where
380
            P: Pin + 'd,
381
        {
382
            let gpio = pin.number();
383
            low_level::init_pin(PIN, true);
384
            low_level::set_open_drain_output(gpio, true);
385
            low_level::input_enable(PIN, true);
386
            low_level::pullup_enable(PIN, true);
387
            low_level::pulldown_enable(PIN, false);
388
            low_level::output_enable(PIN, true);
389

            
390
            Self {
391
                phantom: PhantomData,
392
            }
393
        }
394

            
395
        /// Enables or disables the internal pull-up resistor.
396
        pub fn pullup_enable(&self, enable: bool) {
397
            low_level::pullup_enable(PIN, enable);
398
        }
399

            
400
        /// Enables or disables the internal pull-down resistor.
401
        pub fn pulldown_enable(&self, enable: bool) {
402
            low_level::pulldown_enable(PIN, enable);
403
        }
404
    }
405
}
406

            
407
#[cfg(ulp_riscv_driver_supported)]
408
pub use ulp_tokens::*;