imxrt_iomuxc/
config.rs

1//! Pad configuration
2
3use crate::Iomuxc;
4use core::ptr;
5
6/// Applies the configuration `config` for the supplied pad
7///
8/// `configure` lets you specify the pad's drive strength, speed, pull-up or pull-down
9/// resistors, and other configurations. See [`Config`](struct.Config.html)
10/// for possible configurations.
11///
12/// # Example
13///
14/// ```no_run
15/// use imxrt_iomuxc::{configure, Config, OpenDrain, PullKeeper};
16/// # use imxrt_iomuxc::imxrt1060::gpio_ad_b0::GPIO_AD_B0_03;
17///
18/// const CONFIG: Config = Config::zero()
19///     .set_open_drain(OpenDrain::Enabled)
20///     .set_pull_keeper(Some(PullKeeper::Pullup100k));
21///
22/// let mut pad = unsafe { GPIO_AD_B0_03::new() };
23///
24/// configure(&mut pad, CONFIG);
25/// ```
26#[inline(always)]
27pub fn configure<I: Iomuxc>(pad: &mut I, config: Config) {
28    // Safety: same justification as set_sion.
29    unsafe {
30        let cfg = ptr::read_volatile(pad.pad());
31        let cfg = (cfg & !config.mask) | config.value;
32        ptr::write_volatile(pad.pad(), cfg);
33    }
34}
35
36const HYSTERESIS_SHIFT: u32 = 16;
37const HYSTERESIS_MASK: u32 = 1 << HYSTERESIS_SHIFT;
38
39/// The hysteresis (HYS) bit controls whether a pin acts as a Schmitt trigger,
40/// which is a comparator remembering its last input state (hysteresis).
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42#[repr(u32)]
43pub enum Hysteresis {
44    Enabled = 1 << HYSTERESIS_SHIFT,
45    Disabled = 0 << HYSTERESIS_SHIFT,
46}
47
48const PULLUPDOWN_SHIFT: u32 = 14;
49const PULLUPDOWN_MASK: u32 = 0b11 << PULLUPDOWN_SHIFT;
50
51/// Controls signals to select pull-up or pull-down internal resistance strength.
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53#[repr(u32)]
54enum PullUpDown {
55    /// 100KOhm pull Down
56    Pulldown100k = 0b00 << PULLUPDOWN_SHIFT,
57    /// 47KOhm pull up
58    Pullup47k = 0b01 << PULLUPDOWN_SHIFT,
59    /// 100KOhm pull up
60    Pullup100k = 0b10 << PULLUPDOWN_SHIFT,
61    /// 22KOhm pull up
62    Pullup22k = 0b11 << PULLUPDOWN_SHIFT,
63}
64
65const PULL_KEEP_SELECT_SHIFT: u32 = 13;
66const PULL_KEEP_SELECT_MASK: u32 = 1 << PULL_KEEP_SELECT_SHIFT;
67
68/// Control signal to enable internal pull-up/down resistors or pad keeper functionality.
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70#[repr(u32)]
71enum PullKeepSelect {
72    /// Keep the previous output value when the output driver is disabled.
73    Keeper = 0 << PULL_KEEP_SELECT_SHIFT,
74    /// Pull-up or pull-down (determined by [`PullUpDown`](struct.PullUpDown.html) flags).
75    Pull = 1 << PULL_KEEP_SELECT_SHIFT,
76}
77
78const PULLKEEP_SHIFT: u32 = 12;
79const PULLKEEP_MASK: u32 = 1 << PULLKEEP_SHIFT;
80
81/// Derives a pull/keep configuration from
82/// the field-specific enums.
83///
84/// Used to define the public API.
85const fn pull_keeper(select: PullKeepSelect, pull: Option<PullUpDown>) -> u32 {
86    PULLKEEP_MASK
87        | (select as u32)
88        | match pull {
89            None => 0u32,
90            Some(pull) => pull as u32,
91        }
92}
93
94const PULL_KEEPER_MASK: u32 = PULLKEEP_MASK | PULLUPDOWN_MASK | PULL_KEEP_SELECT_MASK;
95
96/// The pull up, pull down, or keeper configuration.
97#[derive(Clone, Copy, PartialEq, Eq, Debug)]
98#[repr(u32)]
99pub enum PullKeeper {
100    /// 100KOhm pull **down**
101    Pulldown100k = pull_keeper(PullKeepSelect::Pull, Some(PullUpDown::Pulldown100k)),
102    /// 22KOhm pull **up**
103    Pullup22k = pull_keeper(PullKeepSelect::Pull, Some(PullUpDown::Pullup22k)),
104    /// 47KOhm pull **up**
105    Pullup47k = pull_keeper(PullKeepSelect::Pull, Some(PullUpDown::Pullup47k)),
106    /// 100KOhm pull **up**
107    Pullup100k = pull_keeper(PullKeepSelect::Pull, Some(PullUpDown::Pullup100k)),
108    /// Use the keeper, instead of a pull up or pull
109    /// down resistor.
110    ///
111    /// From the reference manual,
112    ///
113    /// > A simple latch to hold the input value when OVDD is powered down, or the first inverter
114    /// > is tri-stated. Input buffer’s keeper is always enabled for all the pads.
115    Keeper = pull_keeper(PullKeepSelect::Keeper, None),
116}
117
118const OPENDRAIN_SHIFT: u32 = 11;
119const OPENDRAIN_MASK: u32 = 1 << OPENDRAIN_SHIFT;
120
121/// Open Drain Enable Field
122///
123/// If enabled, the output driver drives only logic 0. The drain of the
124/// internal transistor is open. It means that logic 1 has to be driven by
125/// an external component. This option is essential if connection between
126/// the pad and an external component is bi-directional. If disabled, then
127/// the output driver drives logic 1 and logic 0.
128#[derive(Debug, Clone, Copy, PartialEq, Eq)]
129#[repr(u32)]
130pub enum OpenDrain {
131    Enabled = 1 << OPENDRAIN_SHIFT,
132    Disabled = 0 << OPENDRAIN_SHIFT,
133}
134
135const SPEED_SHIFT: u32 = 6;
136const SPEED_MASK: u32 = 0b11 << SPEED_SHIFT;
137
138/// Sets electrical characteristics of a pin in a given frequency range
139///
140/// Speed is a selectable bit field that sets electrical characteristics of
141/// a pin in a given frequency range. This field provides additional 2-bit
142/// slew rate control. These options can either increase the output driver
143/// current in the higher frequency range, or reduce the switching noise in
144/// the lower frequency range.
145///
146/// The operational frequency on GPIO pads is dependent on slew rate (SRE),
147/// speed (SPEED), and supply voltage (OVDD).
148///
149/// See Operating Frequency table in the GPIO block guide in the reference
150/// manual for more details.
151#[derive(Debug, Clone, Copy, PartialEq, Eq)]
152#[repr(u32)]
153pub enum Speed {
154    Low = 0b00 << SPEED_SHIFT,
155    Medium = 0b01 << SPEED_SHIFT,
156    Fast = 0b10 << SPEED_SHIFT,
157    Max = 0b11 << SPEED_SHIFT,
158}
159
160const DRIVE_STRENGTH_SHIFT: u32 = 3;
161const DRIVE_STRENGTH_MASK: u32 = 0b111 << DRIVE_STRENGTH_SHIFT;
162
163/// Drive strength
164///
165/// The drive strength enable (DSE) can be explained as series resistance between an ideal driver’s
166/// output and its load. To achieve maximal transferred power, the impedance of the driver has to
167/// match the load impedance.
168#[derive(Debug, Clone, Copy, PartialEq, Eq)]
169#[repr(u32)]
170pub enum DriveStrength {
171    Disabled = 0b000 << DRIVE_STRENGTH_SHIFT,
172    /// 150 Ohm @ 3.3V, 260 Ohm@1.8V
173    R0 = 0b001 << DRIVE_STRENGTH_SHIFT,
174    /// R0 / 2
175    R0_2 = 0b010 << DRIVE_STRENGTH_SHIFT,
176    /// R0 / 3
177    R0_3 = 0b011 << DRIVE_STRENGTH_SHIFT,
178    /// R0 / 4
179    R0_4 = 0b100 << DRIVE_STRENGTH_SHIFT,
180    R0_5 = 0b101 << DRIVE_STRENGTH_SHIFT,
181    R0_6 = 0b110 << DRIVE_STRENGTH_SHIFT,
182    R0_7 = 0b111 << DRIVE_STRENGTH_SHIFT,
183}
184
185const SLEW_RATE_SHIFT: u32 = 0;
186const SLEW_RATE_MASK: u32 = 1 << SLEW_RATE_SHIFT;
187
188/// Slew Rate
189///
190/// This controls how fast the pin toggles between the two logic states.
191/// Since rapidly changing states consume more power and generate spikes,
192/// it should be enabled only when necessary.
193#[derive(Debug, Clone, Copy, PartialEq, Eq)]
194#[repr(u32)]
195pub enum SlewRate {
196    Fast = 1 << SLEW_RATE_SHIFT,
197    Slow = 0 << SLEW_RATE_SHIFT,
198}
199
200/// A configuration capable of compile-time, `const` configuration:
201///
202/// ```
203/// use imxrt_iomuxc::{Config, SlewRate, Hysteresis};
204///
205/// const UART_TX_CONFIG: Config = Config::zero()
206///     .set_slew_rate(SlewRate::Fast)
207///     .set_hysteresis(Hysteresis::Enabled);
208/// ```
209///
210/// Use [`configure()`](fn.configure.html) to set configurations to pads.
211#[derive(Clone, Copy, PartialEq, Eq, Debug)]
212pub struct Config {
213    value: u32,
214    mask: u32,
215}
216
217impl Config {
218    /// When we create the zero mask, we set all bits high. But,
219    /// the highest usable bit in the pad register is bit 16. We
220    /// can use a higher bit to represent if the config is generated
221    /// from `zero()`, or if it was generated from `modify()`.
222    const ZERO_BIT: u32 = 1 << 31;
223
224    /// Create a `Config` that will zero any unspecified field
225    ///
226    /// This may *not* represent any register's reset value. A config that's
227    /// created using `zero()` will have the effect of writing *all* fields
228    /// to the register. Those that are not set explicitly set are written as zero.
229    ///
230    /// ```no_run
231    /// # use imxrt_iomuxc::{Iomuxc, imxrt1060::gpio_ad_b0::GPIO_AD_B0_13};
232    /// # let mut gpio_ad_b0_13 = unsafe { GPIO_AD_B0_13::new() };
233    /// use imxrt_iomuxc::{
234    ///     Config, configure, SlewRate,
235    ///     Hysteresis, PullKeeper, DriveStrength
236    /// };
237    ///
238    /// // Set a configuration
239    /// configure(
240    ///     &mut gpio_ad_b0_13,
241    ///     Config::zero()
242    ///         .set_slew_rate(SlewRate::Fast)
243    ///         .set_drive_strength(DriveStrength::R0_7)
244    /// );
245    /// // DriveStrength::R0_7 as u32 | SlewRate::Fast as u32
246    ///
247    /// // Completely change that configuration
248    /// configure(
249    ///     &mut gpio_ad_b0_13,
250    ///     Config::zero()
251    ///         .set_hysteresis(Hysteresis::Enabled)
252    ///         .set_pull_keeper(Some(PullKeeper::Pullup22k))
253    /// );
254    /// // Hysteresis::Enabled as u32 | PullKeeper::Pullup22k as u32
255    /// //
256    /// // Notice that SlewRate and DriveStrength were set to zero.
257    /// ```
258    pub const fn zero() -> Self {
259        Config {
260            value: 0u32,
261            mask: 0xFFFF_FFFFu32,
262        }
263    }
264
265    /// Create a `Config` that will only modify the specified fields
266    ///
267    /// Any field that is is *not* specified in the configuration will not be touched.
268    ///
269    /// ```no_run
270    /// # use imxrt_iomuxc::{Iomuxc, imxrt1060::gpio_ad_b0::GPIO_AD_B0_13};
271    /// # let mut gpio_ad_b0_13 = unsafe { GPIO_AD_B0_13::new() };
272    /// use imxrt_iomuxc::{Config, configure, SlewRate, DriveStrength, Hysteresis};
273    ///
274    /// // Assume a starting value in the register...
275    /// configure(
276    ///     &mut gpio_ad_b0_13,
277    ///     Config::zero()
278    ///         .set_slew_rate(SlewRate::Fast)
279    ///         .set_drive_strength(DriveStrength::R0_7)
280    /// );
281    /// // DriveStrength::R0_7 as u32 | SlewRate::Fast as u32
282    ///
283    /// // Now change the slew rate, and add hysteresis
284    /// configure(
285    ///     &mut gpio_ad_b0_13,
286    ///     Config::modify()
287    ///         .set_slew_rate(SlewRate::Slow)
288    ///         .set_hysteresis(Hysteresis::Enabled)
289    /// );
290    /// // DriveStrength::R0_7 as u32 | Hysteresis::Enabled as u32
291    /// //
292    /// // Notice that the DriveStrength field is preserved.
293    /// ```
294    pub const fn modify() -> Self {
295        Config {
296            value: 0u32,
297            mask: 0u32,
298        }
299    }
300
301    /// Returns `true` if this `Config` was created using [`zero()`](struct.Config.html#method.zero), meaning that it will
302    /// zero any unspecified fields. If `false`, this config was created using [`modify()`](struct.Config.html#method.modify),
303    /// which will not touch unspecified fields.
304    ///
305    /// ```
306    /// use imxrt_iomuxc::Config;
307    ///
308    /// assert!(Config::zero().is_zero());
309    /// assert!(!Config::modify().is_zero());
310    /// ```
311    pub const fn is_zero(&self) -> bool {
312        self.mask & Self::ZERO_BIT != 0
313    }
314
315    /// Set the hysteresis bit
316    pub const fn set_hysteresis(mut self, hys: Hysteresis) -> Self {
317        self.value = (self.value & !HYSTERESIS_MASK) | (hys as u32);
318        self.mask |= HYSTERESIS_MASK;
319        self
320    }
321
322    /// Set the pull up / pull down / keeper configuration.
323    ///
324    /// A `None` value disables the pull / keeper function.
325    pub const fn set_pull_keeper(mut self, pk: Option<PullKeeper>) -> Self {
326        let pk = match pk {
327            None => 0u32,
328            Some(pk) => pk as u32,
329        };
330        self.value = (self.value & !PULL_KEEPER_MASK) | pk;
331        self.mask |= PULL_KEEPER_MASK;
332        self
333    }
334
335    /// Set the open drain value
336    pub const fn set_open_drain(mut self, od: OpenDrain) -> Self {
337        self.value = (self.value & !OPENDRAIN_MASK) | (od as u32);
338        self.mask |= OPENDRAIN_MASK;
339        self
340    }
341
342    /// Set the pin speed
343    pub const fn set_speed(mut self, speed: Speed) -> Self {
344        self.value = (self.value & !SPEED_MASK) | (speed as u32);
345        self.mask |= SPEED_MASK;
346        self
347    }
348
349    /// Set the drive strength
350    pub const fn set_drive_strength(mut self, dse: DriveStrength) -> Self {
351        self.value = (self.value & !DRIVE_STRENGTH_MASK) | (dse as u32);
352        self.mask |= DRIVE_STRENGTH_MASK;
353        self
354    }
355
356    /// Set the slew rate
357    pub const fn set_slew_rate(mut self, sre: SlewRate) -> Self {
358        self.value = (self.value & !SLEW_RATE_MASK) | (sre as u32);
359        self.mask |= SLEW_RATE_MASK;
360        self
361    }
362}
363
364/// A workaround to set a pad's open drain configuration.
365///
366/// This is a best effort until we can use the [`config`] API on
367/// the 1170. See the issue tracker for more details.
368/// https://github.com/imxrt-rs/imxrt-iomuxc/issues/28
369pub(crate) fn set_open_drain<I: Iomuxc>(_pad: &mut I) {
370    #[cfg(any(feature = "imxrt1010", feature = "imxrt1020", feature = "imxrt1060"))]
371    configure(_pad, Config::modify().set_open_drain(OpenDrain::Enabled));
372}
373
374#[cfg(test)]
375mod tests {
376    use super::*;
377
378    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
379    struct Pad(u32);
380
381    const PAD_ALL_HIGH: Pad = Pad(0x0001_FFFFu32);
382
383    /// The high bits represent the valid fields in pad registers.
384    const PAD_BITMASK: u32 = 0b1_1111_1000_1111_1001u32;
385
386    impl crate::private::Sealed for Pad {}
387
388    unsafe impl Iomuxc for Pad {
389        fn mux(&mut self) -> *mut u32 {
390            panic!("Nothing calls mux() in these tests");
391        }
392        fn pad(&mut self) -> *mut u32 {
393            &mut self.0 as *mut _
394        }
395    }
396
397    #[test]
398    fn zero() {
399        let mut pad = PAD_ALL_HIGH;
400        configure(&mut pad, Config::zero());
401        assert_eq!(pad.0, 0);
402    }
403
404    #[test]
405    fn zero_set_all() {
406        let mut pad = PAD_ALL_HIGH;
407        const CONFIG: Config = Config::zero()
408            .set_hysteresis(Hysteresis::Enabled)
409            .set_pull_keeper(Some(PullKeeper::Pullup22k))
410            .set_open_drain(OpenDrain::Enabled)
411            .set_speed(Speed::Max)
412            .set_drive_strength(DriveStrength::R0_7)
413            .set_slew_rate(SlewRate::Fast);
414
415        configure(&mut pad, CONFIG);
416
417        assert_eq!(pad.0, PAD_BITMASK);
418    }
419
420    #[test]
421    fn modify_clear_all() {
422        let mut pad = Pad(PAD_BITMASK);
423        const CONFIG: Config = Config::modify()
424            .set_hysteresis(Hysteresis::Disabled)
425            .set_pull_keeper(None)
426            .set_open_drain(OpenDrain::Disabled)
427            .set_speed(Speed::Low)
428            .set_drive_strength(DriveStrength::Disabled)
429            .set_slew_rate(SlewRate::Slow);
430
431        configure(&mut pad, CONFIG);
432
433        assert_eq!(pad.0, 0);
434    }
435
436    #[test]
437    fn pull_keeper_none() {
438        let mut pad = Pad(0);
439        configure(&mut pad, Config::zero().set_pull_keeper(None));
440        assert_eq!(pad.0, 0);
441    }
442
443    #[test]
444    fn pull_keeper_keeper() {
445        let mut pad = Pad(0);
446        configure(
447            &mut pad,
448            Config::zero().set_pull_keeper(Some(PullKeeper::Keeper)),
449        );
450        assert_eq!(pad.0, 1 << 12);
451    }
452
453    #[test]
454    fn pull_keeper_pullupdown() {
455        struct ConfigToField {
456            config: PullKeeper,
457            value: u32,
458        }
459
460        const TESTS: [ConfigToField; 4] = [
461            ConfigToField {
462                config: PullKeeper::Pulldown100k,
463                value: 0 << 14,
464            },
465            ConfigToField {
466                config: PullKeeper::Pullup100k,
467                value: 2 << 14,
468            },
469            ConfigToField {
470                config: PullKeeper::Pullup22k,
471                value: 3 << 14,
472            },
473            ConfigToField {
474                config: PullKeeper::Pullup47k,
475                value: 1 << 14,
476            },
477        ];
478
479        for test in TESTS {
480            let mut pad = Pad(0);
481            configure(&mut pad, Config::zero().set_pull_keeper(Some(test.config)));
482            assert_eq!(pad.0, 1 << 12 | 1 << 13 | test.value);
483        }
484    }
485
486    #[test]
487    fn set_open_drain() {
488        let mut pad = Pad(0);
489        super::set_open_drain(&mut pad);
490        assert_eq!(pad.0, 1 << 11);
491
492        let mut pad = Pad(0xFFFF_F7FF);
493        super::set_open_drain(&mut pad);
494        assert_eq!(pad.0, 0xFFFFFFFF);
495    }
496}
497
498/// ```rust
499/// use imxrt_iomuxc::{Config, PullKeeper};
500///
501/// const CONFIG: Config = Config::zero().set_pull_keeper(Some(PullKeeper::Keeper));
502#[cfg(doctest)]
503struct PullKeeperConstant;