imxrt_iomuxc/
lib.rs

1//! `imxrt-iomuxc` is a library for configuring i.MX RT processor pads. Using its API, you can
2//!
3//! - configure a pad for a peripheral, and specify its electrical properties.
4//! - manage pad objects as ownable resources.
5//! - statically constrain pads to work with peripherals.
6//!
7//! As an end user, you're expected to use `imxrt-iomuxc` through a hardware abstraction layer
8//! (HAL) or board support package (BSP). Specifically, you should have access to pad structs and
9//! objects, and you should be able to configure pads with the [`configure`] APIs.
10//!
11//! As a library developer who writes HALs or hardware drivers, you may use the `imxrt-iomuxc`
12//! pin traits in your APIs to statically ensure pad-peripheral compatibility. See the design
13//! guidance for examples on how to achieve this.
14//!
15//! # Definitions
16//!
17//! A 'pad' is the physical input / output on an i.MX RT processor.
18//! Pads may be configured for various functions. A pad may act as a UART pin, an I2C
19//! pin, or other types of pins. A 'pin' is a pad that's configured for a functional
20//! purpose. The traits let us say which pad can be used for which peripheral pin.
21//!
22//! # Features
23//!
24//! Processor pads, and their pin implementations, are enabled with optional feature flags. For
25//! example, the `imxrt1060` feature flag exposes an `imxrt1060` module that defines all i.MX
26//! RT 1060 processor pads. Users and integrators are responsible for making sure an enabled
27//! feature makes sense for their system.
28//!
29//! # Design Guidance
30//!
31//! For recommendations on how you can use these traits, see the module-level documentation. The
32//! rest of this section describes general guidance for designing APIs with these traits.
33//!
34//! ## Matching pads and peripherals
35//!
36//! The pin traits allow you to restrict the pads and peripherals that comprise a peripheral. This
37//! lets you catch invalid peripheral configurations at compile time.
38//!
39//! In the example below, we implement a hypothetical `lpuart_new` function, which is responsible
40//! for preparing a LPUART peripheral. To properly configure the peripheral, we need the two
41//! pads that represent a peripheral's TX and RX pins. The implementation will use the
42//! `imxrt_iomuxc::lpuart::prepare()` function to prepare the pins.
43//!
44//! Note the trait bounds on `lpuart_new`. The usage requires that
45//!
46//! - the user provides one TX and one RX pin
47//! - the modules for each pin match
48//!
49//! ```no_run
50//! use imxrt_iomuxc as iomuxc;
51//! use iomuxc::lpuart::{Pin, Tx, Rx};
52//!
53//! # struct Lpuart<const N: u8>;
54//! /// Creates a UART peripheral from the TX and RX pads
55//! fn lpuart_new<T, R, const N: u8>(mut tx: T, mut rx: R) -> Lpuart<N>
56//! where
57//!     T: Pin<Direction = Tx, Module = iomuxc::consts::Const<N>>,
58//!     R: Pin<Direction = Rx, Module = <T as Pin>::Module>,
59//! {
60//!     iomuxc::lpuart::prepare(&mut tx);
61//!     iomuxc::lpuart::prepare(&mut rx);
62//!     // Prepare the rest of the peripheral, and return it...
63//!     # Lpuart
64//! }
65//!
66//! # let gpio_ad_b0_13 = unsafe { imxrt_iomuxc::imxrt1060::gpio_ad_b0::GPIO_AD_B0_13::new() };
67//! # let gpio_ad_b0_12 = unsafe { imxrt_iomuxc::imxrt1060::gpio_ad_b0::GPIO_AD_B0_12::new() };
68//! // GPIO_AD_B0_13 and GPIO_AD_B0_12 are a suitable pair of UART pins
69//! lpuart_new(gpio_ad_b0_12, gpio_ad_b0_13);
70//! ```
71//!
72//! Specifically, the trait bounds guard against non-UART pins:
73//!
74//! ```compile_fail
75//! # use imxrt_iomuxc as iomuxc;
76//! # use iomuxc::lpuart::{Pin, Tx, Rx};
77//! # struct Lpuart<const N: u8>;
78//! # fn lpuart_new<T, R, const N: u8>(mut tx: T, mut rx: R) -> Lpuart<N>
79//! # where
80//! #     T: Pin<Direction = Tx, Module = iomuxc::consts::Const<N>>,
81//! #     R: Pin<Direction = Rx, Module = <T as Pin>::Module>,
82//! # {
83//! #     iomuxc::lpuart::prepare(&mut tx);
84//! #     iomuxc::lpuart::prepare(&mut rx);
85//! #     Lpuart
86//! # }
87//! # let gpio_ad_b0_10 = unsafe { imxrt_iomuxc::imxrt1060::gpio_ad_b0::GPIO_AD_B0_10::new() };
88//! # let gpio_ad_b0_11 = unsafe { imxrt_iomuxc::imxrt1060::gpio_ad_b0::GPIO_AD_B0_11::new() };
89//! // Neither pad is a valid UART pin
90//! lpuart_new(gpio_ad_b0_10, gpio_ad_b0_11);
91//! ```
92//!
93//! It also guards against mismatched UART pins:
94//!
95//! ```compile_fail
96//! # use imxrt_iomuxc as iomuxc;
97//! # use iomuxc::lpuart::{Pin, Tx, Rx};
98//! # struct Lpuart<const N: u8>;
99//! # fn lpuart_new<T, R, const N: u8>(mut tx: T, mut rx: R) -> Lpuart<N>
100//! # where
101//! #     T: Pin<Direction = Tx, Module = iomuxc::consts::Const<N>>,
102//! #     R: Pin<Direction = Rx, Module = <T as Pin>::Module>,
103//! # {
104//! #     iomuxc::lpuart::prepare(&mut tx);
105//! #     iomuxc::lpuart::prepare(&mut rx);
106//! #     Lpuart
107//! # }
108//! # let gpio_ad_b0_13 = unsafe { imxrt_iomuxc::imxrt1060::gpio_ad_b0::GPIO_AD_B0_13::new() };
109//! # let gpio_ad_b1_02 = unsafe { imxrt_iomuxc::imxrt1060::gpio_ad_b1::GPIO_AD_B1_02::new() };
110//! // GPIO_AD_B1_02 is a UART2 TX pin, but GPIO_AD_B0_13 is a UART1 RX pin
111//! lpuart_new(gpio_ad_b1_02, gpio_ad_b0_13);
112//! ```
113//!
114//! ## Type-Erased Pads
115//!
116//! At the expense of requiring `unsafe`, users may favor type-erased pads over strongly-typed pads.
117//! When creating APIs that consume strongly-typed pads, or pads that conform to peripheral pin interfaces,
118//! consider supporting an `unsafe` API to create the peripheral without requiring the strongly-typed pads.
119//! The API will expect that the user is responsible for manually configuring the type-erased pad.
120//!
121//! ```no_run
122//! use imxrt_iomuxc::{self as iomuxc, ErasedPad, lpuart::{Pin, Tx, Rx}};
123//! # use imxrt_iomuxc::imxrt1060::gpio_ad_b0::{GPIO_AD_B0_13, GPIO_AD_B0_12};
124//! # pub struct Lpuart<const N: u8>;
125//!
126//! impl<const N: u8> Lpuart<N> {
127//!     pub fn new<T, R>(mut tx: T, mut rx: R, /* ... */) -> Self
128//!     where
129//!         T: Pin<Direction = Tx, Module = iomuxc::consts::Const<N>>,
130//!         R: Pin<Direction = Rx, Module = <T as Pin>::Module>,
131//!     {
132//!         imxrt_iomuxc::lpuart::prepare(&mut tx);
133//!         imxrt_iomuxc::lpuart::prepare(&mut rx);
134//!         // ...
135//!         # Lpuart
136//!     }
137//!
138//!     pub fn with_erased_pads(tx: ErasedPad, rx: ErasedPad, /* ... */) -> Self {
139//!         // ...
140//!         # Lpuart
141//!     }
142//! }
143//!
144//! // Preferred: create a LPUART peripheral with strongly-typed pads...
145//! let gpio_ad_b0_13 = unsafe { GPIO_AD_B0_13::new() };
146//! let gpio_ad_b0_12 = unsafe { GPIO_AD_B0_12::new() };
147//! let uart1 = Lpuart::<1>::new(gpio_ad_b0_12, gpio_ad_b0_13);
148//!
149//! // Optional: create a LPUART peripheral from type-erased pads...
150//! let gpio_ad_b0_13 = unsafe { GPIO_AD_B0_13::new() };
151//! let gpio_ad_b0_12 = unsafe { GPIO_AD_B0_12::new() };
152//!
153//! let mut rx_pad = gpio_ad_b0_13.erase();
154//! let mut tx_pad = gpio_ad_b0_12.erase();
155//!
156//! // User is responsible for configuring the pad,
157//! // since we can't call `prepare()` on the pad...
158//! unsafe {
159//!     // Daisy registers and values aren't attached
160//!     // to erased pads, so we have to reference this
161//!     // manually.
162//!     <GPIO_AD_B0_13 as imxrt_iomuxc::lpuart::Pin>::DAISY.map(|daisy| daisy.write());
163//!     <GPIO_AD_B0_12 as imxrt_iomuxc::lpuart::Pin>::DAISY.map(|daisy| daisy.write());
164//! }
165//! imxrt_iomuxc::alternate(&mut tx_pad, 2);
166//! imxrt_iomuxc::alternate(&mut rx_pad, 2);
167//! imxrt_iomuxc::clear_sion(&mut tx_pad);
168//! imxrt_iomuxc::clear_sion(&mut rx_pad);
169//! // Pads are configured for LPUART settings
170//! let uart1 = Lpuart::<1>::with_erased_pads(tx_pad, rx_pad);
171//! ```
172
173#![no_std]
174#![cfg_attr(docsrs, feature(doc_cfg))]
175
176#[macro_use]
177pub mod adc;
178mod config;
179#[macro_use]
180pub mod flexcan;
181#[macro_use]
182pub mod flexio;
183#[macro_use]
184pub mod flexpwm;
185#[macro_use]
186pub mod lpi2c;
187#[macro_use]
188pub mod lpspi;
189#[macro_use]
190pub mod lpuart;
191#[macro_use]
192pub mod sai;
193#[macro_use]
194pub mod usdhc;
195
196use core::ptr;
197
198pub use config::{
199    configure, Config, DriveStrength, Hysteresis, OpenDrain, PullKeeper, SlewRate, Speed,
200};
201
202#[allow(deprecated)]
203pub use config::{PullKeep, PullKeepSelect, PullUpDown};
204
205/// Re-export of top-level components, without the chip-specific modules.
206///
207/// `prelude` is to help HAL implementors re-export the `imxrt-iomuxc` APIs
208/// as a single module.
209///
210/// ```
211/// // Your crate's module:
212/// pub mod iomuxc {
213///     // Re-export common modules and types
214///     pub use imxrt_iomuxc::prelude::*;
215///     // Conditionally re-export chip-specific pads
216///     #[cfg(feature = "imxrt1060")]
217///     pub use imxrt_iomuxc::imxrt1060::*;
218/// }
219/// ```
220pub mod prelude {
221    pub use crate::config::{
222        configure, Config, DriveStrength, Hysteresis, OpenDrain, PullKeeper, SlewRate, Speed,
223    };
224
225    #[allow(deprecated)]
226    pub use crate::config::{PullKeep, PullKeepSelect, PullUpDown};
227
228    pub use crate::{
229        adc, alternate, ccm, clear_sion, consts, flexcan, flexio, flexpwm, gpio, lpi2c, lpspi,
230        lpuart, sai, set_sion, usdhc, Daisy, ErasedPad, Pad, WrongPadError,
231    };
232}
233
234/// Type-level constants and traits.
235pub mod consts {
236    /// A type-level constant.
237    ///
238    /// You can pattern match these in trait constraints. See the package documentation for
239    /// examples.
240    #[derive(Debug)]
241    pub enum Const<const N: u8> {}
242    #[doc(hidden)]
243    pub trait Unsigned {
244        const USIZE: usize;
245        fn to_usize() -> usize {
246            Self::USIZE
247        }
248    }
249    impl<const N: u8> Unsigned for Const<N> {
250        const USIZE: usize = N as usize;
251    }
252    macro_rules! ux {
253        ($($Ux:ident => $N:literal,)+) => {
254            $(pub type $Ux = Const<$N>;)+
255        };
256    }
257    ux! {
258        U0 => 0, U1 => 1, U2 => 2, U3 => 3, U4 => 4,
259        U5 => 5, U6 => 6, U7 => 7, U8 => 8, U9 => 9,
260        U10 => 10, U11 => 11, U12 => 12, U13 => 13, U14 => 14,
261        U15 => 15, U16 => 16, U17 => 17, U18 => 18, U19 => 19,
262        U20 => 20, U21 => 21, U22 => 22, U23 => 23, U24 => 24,
263        U25 => 25, U26 => 26, U27 => 27, U28 => 28, U29 => 29,
264        U30 => 30, U31 => 31, U32 => 32, U33 => 33, U34 => 34,
265        U35 => 35, U36 => 36, U37 => 37, U38 => 38, U39 => 39,
266        U40 => 40, U41 => 41,
267    }
268}
269
270#[cfg(feature = "imxrt1010")]
271#[cfg_attr(docsrs, doc(cfg(feature = "imxrt1010")))]
272pub mod imxrt1010;
273
274#[cfg(feature = "imxrt1020")]
275#[cfg_attr(docsrs, doc(cfg(feature = "imxrt1020")))]
276pub mod imxrt1020;
277
278#[cfg(feature = "imxrt1060")]
279#[cfg_attr(docsrs, doc(cfg(feature = "imxrt1060")))]
280pub mod imxrt1060;
281
282#[cfg(feature = "imxrt1170")]
283#[cfg_attr(docsrs, doc(cfg(feature = "imxrt1170")))]
284pub mod imxrt1170;
285
286/// An IOMUXC-capable pad which can support I/O multiplexing
287///
288/// # Safety
289///
290/// This should only be implemented on types that return pointers to static
291/// memory.
292pub unsafe trait Iomuxc: private::Sealed {
293    /// Returns the absolute address of the multiplex register.
294    #[doc(hidden)]
295    fn mux(&mut self) -> *mut u32;
296    /// Returns the absolute address of the pad configuration register.
297    #[doc(hidden)]
298    fn pad(&mut self) -> *mut u32;
299}
300
301mod private {
302    pub trait Sealed {}
303}
304
305const SION_BIT: u32 = 1 << 4;
306
307/// Set the SION bit in a pad's MUX register
308///
309/// Users who are using strongly-typed pads should not call `set_sion()` directly.
310/// Instead, `set_sion()` will be used in a peripheral's `prepare()` function as needed,
311/// so that you don't have to call it.
312///
313/// However, you should use `set_sion()` if you're using any type-erased pads, since those
314/// pads cannot be used with a peripheral's `prepare()` function.
315#[inline(always)]
316pub fn set_sion<I: Iomuxc>(pad: &mut I) {
317    // Safety:
318    //
319    // Pointer reads and writes are unsafe. But, because we control
320    // all IOMUXC implementations, we know that the returned pointers
321    // are vaild, aligned, and initialized (MMIO memory).
322    //
323    // The interface design ensures that all pads, type I, are unique
324    // owners of MMIO memory. Users would have to use unsafe code to violate
325    // that guarantee.
326    //
327    // By taking a mutable reference, the caller has to ensure atomicity of this
328    // read-modify-write operation (or, violate the requirement with more unsafe
329    // code).
330    unsafe {
331        let mut mux = ptr::read_volatile(pad.mux());
332        mux |= SION_BIT;
333        ptr::write_volatile(pad.mux(), mux);
334    }
335}
336
337/// Clear the SION bit in a pad's MUX register
338///
339/// Users who are using strongly-typed pads should not call `clear_sion()` directly.
340/// Instead, `clear_sion()` will be used in a peripheral's `prepare()` function as needed,
341/// so that you don't have to call it.
342///
343/// However, you should use `clear_sion()` if you're using any type-erased pads, since those
344/// pads cannot be used with a peripheral's `prepare()` function.
345#[inline(always)]
346pub fn clear_sion<I: Iomuxc>(pad: &mut I) {
347    // Safety: same justification as set_sion
348    unsafe {
349        let mut mux = ptr::read_volatile(pad.mux());
350        mux &= !SION_BIT;
351        ptr::write_volatile(pad.mux(), mux);
352    }
353}
354
355/// Set an alternate value for the pad
356///
357/// Users who are using strongly-typed pads should not call `alternate()` directly.
358/// Instead, `alternate()` will be used in a peripheral's `prepare()` function as needed,
359/// so that you don't have to call it.
360///
361/// However, you should use `alternate()` if you're using any type-erased pads, since those
362/// pads cannot be used with a peripheral's `prepare()` function.
363#[inline(always)]
364pub fn alternate<I: Iomuxc>(pad: &mut I, alt: u32) {
365    const ALT_MASK: u32 = 0b1111;
366    // Safety: same justification as set_sion. Argument extends to
367    // pad values and alternate values.
368    unsafe {
369        let mut mux = ptr::read_volatile(pad.mux());
370        mux = (mux & !ALT_MASK) | (alt & ALT_MASK);
371        ptr::write_volatile(pad.mux(), mux);
372    }
373}
374
375/// An i.MXT RT pad
376///
377/// The `Base` is the pad tag, like `GPIO_AD_B0`. The `Offset` is the
378/// constant (type) that describes the pad number.
379///
380/// `Pad`s have no size.
381#[derive(Debug)]
382pub struct Pad<const MUX: u32, const PAD: u32> {
383    // Block auto-implement of Send / Sync. We'll manually implement
384    // the traits.
385    _not_send_sync: ::core::marker::PhantomData<*const ()>,
386}
387
388impl<const MUX: u32, const PAD: u32> Pad<MUX, PAD> {
389    /// Creates a handle to the pad
390    ///
391    /// # Safety
392    ///
393    /// `new()` may be called anywhere, by anyone. This could lead to multiple objects that
394    /// mutate the same memory. Consider calling `new()` once, near startup, then passing objects
395    /// and references throughout your program.
396    #[inline(always)]
397    pub const unsafe fn new() -> Self {
398        Self {
399            _not_send_sync: ::core::marker::PhantomData,
400        }
401    }
402    /// Cast the MUX address.
403    const fn mux() -> *mut u32 {
404        MUX as *mut u32
405    }
406    /// Cast the PAD address.
407    const fn pad() -> *mut u32 {
408        PAD as *mut u32
409    }
410}
411
412unsafe impl<const MUX: u32, const PAD: u32> Send for Pad<MUX, PAD> {}
413
414impl<const MUX: u32, const PAD: u32> Pad<MUX, PAD> {
415    /// Erase the pad's type, returning an `ErasedPad`
416    #[inline(always)]
417    pub const fn erase(self) -> ErasedPad {
418        ErasedPad {
419            mux: Self::mux(),
420            pad: Self::pad(),
421        }
422    }
423
424    /// Set the alternate value for this pad.
425    ///
426    /// Performs a read-modify-write on the pad's mux register to set the
427    /// alternate value to `alt`.
428    ///
429    /// # Safety
430    ///
431    /// This function performs a read-modify-write operation on peripheral
432    /// memory. It could race with other calls that modify this pad's mux register.
433    /// For a safer interface, see [`alternate()`](crate::alternate()).
434    #[inline(always)]
435    pub unsafe fn set_alternate(alt: u32) {
436        let mut pad = Self::new();
437        alternate(&mut pad, alt);
438    }
439
440    /// Set the pad's SION bit.
441    ///
442    /// Performs a read-modify-write on the pad's mux register to set the SION
443    /// bit.
444    ///
445    /// # Safety
446    ///
447    /// This function performs a read-modify-write operation on peripheral
448    /// memory. It could race with other calls that modify this pad's mux register.
449    /// For a safer interface, see [`set_sion()`](crate::set_sion()).
450    #[inline(always)]
451    pub unsafe fn set_sion() {
452        let mut pad = Self::new();
453        set_sion(&mut pad);
454    }
455
456    /// Clear the pad's SION bit.
457    ///
458    /// Performs a read-modify-write on the pad's mux register to Clear the SION
459    /// bit.
460    ///
461    /// # Safety
462    ///
463    /// This function performs a read-modify-write operation on peripheral
464    /// memory. It could race with other calls that modify this pad's mux register.
465    /// For a safer interface, see [`clear_sion()`](crate::clear_sion()).
466    #[inline(always)]
467    pub unsafe fn clear_sion() {
468        let mut pad = Self::new();
469        clear_sion(&mut pad);
470    }
471
472    /// Set the pad's configuration.
473    ///
474    /// # Safety
475    ///
476    /// This function performs a read-modify-write operation on peripheral memory.
477    /// It could race with any other function that modifies this pad's registers.
478    /// For a safer interface, see [`configure()`](crate::configure()).
479    #[inline(always)]
480    pub unsafe fn configure(config: Config) {
481        let mut pad = Self::new();
482        configure(&mut pad, config);
483    }
484}
485
486impl<const MUX: u32, const PAD: u32> private::Sealed for Pad<MUX, PAD> {}
487
488unsafe impl<const MUX: u32, const PAD: u32> crate::Iomuxc for Pad<MUX, PAD> {
489    #[inline(always)]
490    fn mux(&mut self) -> *mut u32 {
491        Self::mux()
492    }
493
494    #[inline(always)]
495    fn pad(&mut self) -> *mut u32 {
496        Self::pad()
497    }
498}
499
500/// A pad that has its type erased
501///
502/// `ErasedPad` moves the pad state to run time, rather than compile time.
503/// The type may provide more flexibility for some APIs. Each `ErasedPad` is
504/// two pointers large.
505///
506/// `ErasedPad` may be converted back into their strongly-typed analogs using
507/// `TryFrom` and `TryInto` conversions.
508///
509/// ```no_run
510/// use imxrt_iomuxc as iomuxc;
511/// # type GPIO_AD_B0_03 = iomuxc::Pad<0xDEAD, 0xBEEF>;
512/// let gpio_ad_b0_03 = unsafe { GPIO_AD_B0_03::new() };
513/// let mut erased = gpio_ad_b0_03.erase();
514///
515/// // Erased pads may be manually manipulated
516/// iomuxc::alternate(&mut erased, 7);
517/// iomuxc::set_sion(&mut erased);
518///
519/// // Try to convert the erased pad back to its strongly-typed counterpart
520/// use core::convert::TryFrom;
521/// let gpio_ad_b0_03 = GPIO_AD_B0_03::try_from(erased).unwrap();
522/// ```
523#[derive(Debug)]
524pub struct ErasedPad {
525    mux: *mut u32,
526    pad: *mut u32,
527}
528
529impl private::Sealed for ErasedPad {}
530
531unsafe impl crate::Iomuxc for ErasedPad {
532    #[inline(always)]
533    fn mux(&mut self) -> *mut u32 {
534        self.mux
535    }
536
537    #[inline(always)]
538    fn pad(&mut self) -> *mut u32 {
539        self.pad
540    }
541}
542
543unsafe impl Send for ErasedPad {}
544
545/// An error that indicates the conversion from an `ErasedPad` to a
546/// strongly-typed pad failed.
547///
548/// Failure happens when trying to convert an `ErasedPad` into the incorrect
549/// pad. The error indicator wraps the pad that failed to convert.
550#[derive(Debug)]
551pub struct WrongPadError(pub ErasedPad);
552
553impl<const MUX: u32, const PAD: u32> ::core::convert::TryFrom<ErasedPad> for Pad<MUX, PAD> {
554    type Error = WrongPadError;
555    fn try_from(erased_pad: ErasedPad) -> Result<Self, Self::Error> {
556        if erased_pad.mux == Self::mux() && erased_pad.pad == Self::pad() {
557            Ok(unsafe { Self::new() })
558        } else {
559            Err(WrongPadError(erased_pad))
560        }
561    }
562}
563
564/// A daisy selection
565///
566/// A daisy chain specifies which pad will be used for a peripheral's
567/// input. Call `write()` to commit the settings described by a `Daisy`
568/// value.
569#[derive(Clone, Copy, PartialEq, Eq, Debug)]
570pub struct Daisy {
571    reg: *mut u32,
572    value: u32,
573}
574
575impl Daisy {
576    /// Create a new select input that, when utilized, will write
577    /// `value` into `reg`
578    #[allow(unused)] // Used behind feature flags
579    const fn new(reg: *mut u32, value: u32) -> Self {
580        Daisy { reg, value }
581    }
582
583    /// Commit the settings defined by this `Daisy` value to the hardware
584    ///
585    /// # Safety
586    ///
587    /// This modifies a global, processor register, so the typical
588    /// rules around mutable static memory apply.
589    #[inline(always)]
590    pub unsafe fn write(self) {
591        ptr::write_volatile(self.reg, self.value);
592    }
593}
594
595/// GPIO pad configuration
596pub mod gpio {
597    /// A GPIO pin
598    ///
599    /// The constant `N` is the associated GPIO module
600    /// (a `3` for `GPIO3`).
601    pub trait Pin<const N: u8>: super::Iomuxc {
602        /// The alternate value for this pad
603        const ALT: u32;
604        /// The offset; `U13` for `GPIO5_IO13`
605        const OFFSET: u32;
606    }
607
608    /// Prepare a pad to be used as a GPIO pin
609    pub fn prepare<P: Pin<N>, const N: u8>(pin: &mut P) {
610        super::alternate(pin, P::ALT);
611    }
612}
613
614/// CCM pad configuration.
615pub mod ccm {
616    /// A CCM pin.
617    ///
618    /// These can be used for observing clock outputs, or for generating
619    /// outputs for your PMIC.
620    pub trait Pin: super::Iomuxc {
621        /// The alternate value for this pad.
622        const ALT: u32;
623        /// The pin function.
624        type Function: Function;
625    }
626
627    /// Prepare a pad to be used as a CCM pin.
628    pub fn prepare<P: Pin>(pin: &mut P) {
629        super::alternate(pin, P::ALT);
630    }
631
632    mod private {
633        pub trait Sealed {}
634    }
635    /// A CCM pin function.
636    pub trait Function: private::Sealed {}
637
638    /// Observability output.
639    pub enum Observable<const N: u8> {}
640    impl private::Sealed for Observable<1> {}
641    impl private::Sealed for Observable<2> {}
642    impl Function for Observable<1> {}
643    impl Function for Observable<2> {}
644}
645
646#[cfg(test)]
647mod tests {
648    use super::*;
649
650    #[derive(Debug)]
651    struct TestBase;
652
653    type TestPad = Pad<0, 0>;
654
655    #[test]
656    fn erased_pad_convert_success() {
657        let pad = unsafe { TestPad::new() };
658        let erased = pad.erase();
659
660        use core::convert::TryFrom;
661        TestPad::try_from(erased).expect("This is the test pad");
662    }
663
664    #[test]
665    fn erased_pad_convert_fail() {
666        let pad = unsafe { TestPad::new() };
667        let erased = pad.erase();
668
669        use core::convert::TryFrom;
670        type OtherPad = Pad<1, 1>;
671        OtherPad::try_from(erased).expect_err("This is a different pad");
672    }
673}
674
675/// ```
676/// fn is_send<S: Send>(s: S) {}
677/// type GPIO_AD_B0_03 = imxrt_iomuxc::Pad<0xDEAD, 0xBEEF>;
678/// is_send(unsafe { GPIO_AD_B0_03::new() }.erase());
679/// ```
680#[cfg(doctest)]
681struct ErasedPadsAreSend;
682
683/// ```
684/// fn is_send<S: Send>(s: S) {}
685/// type GPIO_AD_B0_03 = imxrt_iomuxc::Pad<0xDEAD, 0xBEEF>;
686/// is_send(unsafe { GPIO_AD_B0_03::new() });
687/// is_send(unsafe { GPIO_AD_B0_03::new() }.erase());
688/// ```
689#[cfg(doctest)]
690struct PadsAreSend;
691
692/// ```compile_fail
693/// fn is_sync<S: Sync>(s: S) {}
694/// type GPIO_AD_B0_03 = imxrt_iomuxc::Pad<0xDEAD, 0xBEEF>;
695/// is_sync(unsafe { GPIO_AD_B0_03::new() }.erase())
696/// ```
697#[cfg(doctest)]
698struct ErasedPadsAreNotSync;
699
700/// ```compile_fail
701/// fn is_sync<S: Sync>(s: S) {}
702/// type GPIO_AD_B0_03 = imxrt_iomuxc::Pad<0xDEAD, 0xBEEF>;
703/// is_sync(unsafe { GPIO_AD_B0_03::new() })
704/// ```
705#[cfg(doctest)]
706struct PadsAreNotSync;