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;