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;