imxrt_hal/chip/imxrt10xx/
ccm.rs

1//! Chip-specific CCM APIs.
2//!
3//! This module and its submodules should work across all i.MX RT10xx processors
4//! (with proper family configuration).
5
6pub use crate::chip::config::ccm::*;
7
8pub mod ahb_clk;
9pub mod analog;
10pub mod clock_gate;
11pub mod output_source;
12
13use crate::ral;
14
15pub use crate::common::ccm::XTAL_OSCILLATOR_HZ;
16
17/// PERCLK clock.
18///
19/// The PERCLK clock controls GPT and PIT timers.
20///
21/// # Example
22///
23/// Use the CCM to set the PERCLK clock selection and frequency.
24/// After this snippet runs, the PERCLK clock runs at 8MHz.
25/// To safely perform this switch, disable all clock gates to the
26/// PIT and GPT peripherals.
27///
28/// ```no_run
29/// use imxrt_ral as ral;
30/// use imxrt_hal as hal;
31///
32/// use hal::ccm::{self, clock_gate};
33///
34/// let mut ccm = unsafe { ral::ccm::CCM::instance() };
35///
36/// clock_gate::PERCLK_CLOCK_GATES
37///     .iter()
38///     .for_each(|clock_gate| clock_gate.set(&mut ccm, clock_gate::OFF));
39///
40/// // 24MHz...
41/// ccm::perclk_clk::set_selection(&mut ccm, ccm::perclk_clk::Selection::Oscillator);
42/// // ...divided by 3.
43/// ccm::perclk_clk::set_divider(&mut ccm, 3);
44///
45/// clock_gate::PERCLK_CLOCK_GATES
46///     .iter()
47///     .for_each(|clock_gate| clock_gate.set(&mut ccm, clock_gate::ON));
48/// ```
49pub mod perclk_clk {
50    use crate::ral::{self, ccm::CCM};
51
52    /// PERCLK clock selection.
53    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
54    #[repr(u32)]
55    pub enum Selection {
56        /// Derive from the IPG clock root.
57        Ipg = 0,
58        /// Derive from the oscillator clock.
59        Oscillator = 1,
60    }
61
62    /// Set the PERCLK clock selection.
63    #[inline(always)]
64    pub fn set_selection(ccm: &mut CCM, selection: Selection) {
65        ral::modify_reg!(ral::ccm, ccm, CSCMR1, PERCLK_CLK_SEL: selection as u32);
66    }
67
68    /// Returns the PERCLK clock selection.
69    #[inline(always)]
70    pub fn selection(ccm: &CCM) -> Selection {
71        if ral::read_reg!(ral::ccm, ccm, CSCMR1, PERCLK_CLK_SEL == 1) {
72            Selection::Oscillator
73        } else {
74            Selection::Ipg
75        }
76    }
77
78    /// The smallest PERCLK divider.
79    pub const MIN_DIVIDER: u32 = 1;
80    /// The largest PERCLK divider.
81    pub const MAX_DIVIDER: u32 = 64;
82
83    /// Set the PERCLK clock divider.
84    ///
85    /// The implementation clamps `divider` between [`MIN_DIVIDER`] and [`MAX_DIVIDER`].
86    #[inline(always)]
87    pub fn set_divider(ccm: &mut CCM, divider: u32) {
88        let podf = divider.clamp(MIN_DIVIDER, MAX_DIVIDER) - 1;
89        ral::modify_reg!(ral::ccm, ccm, CSCMR1, PERCLK_PODF: podf);
90    }
91
92    /// Returns the PERCLK clock divider.
93    #[inline(always)]
94    pub fn divider(ccm: &CCM) -> u32 {
95        ral::read_reg!(ral::ccm, ccm, CSCMR1, PERCLK_PODF) + 1
96    }
97}
98
99/// IPG clock.
100///
101/// The IPG clock is divided from the core clock.
102pub mod ipg_clk {
103    use crate::ral::{self, ccm::CCM};
104
105    /// Returns the IPG clock divider.
106    #[inline(always)]
107    pub fn divider(ccm: &CCM) -> u32 {
108        ral::read_reg!(ral::ccm, ccm, CBCDR, IPG_PODF) + 1
109    }
110
111    /// The smallest IPG divider.
112    pub const MIN_DIVIDER: u32 = 1;
113    /// The largest IPG divider.
114    pub const MAX_DIVIDER: u32 = 4;
115
116    /// Sets the IPG clock divider.
117    ///
118    /// The implementation clamps `divider` between [`MIN_DIVIDER`] and [`MAX_DIVIDER`].
119    #[inline(always)]
120    pub fn set_divider(ccm: &mut CCM, divider: u32) {
121        let podf = divider.clamp(MIN_DIVIDER, MAX_DIVIDER) - 1;
122        ral::modify_reg!(ral::ccm, ccm, CBCDR, IPG_PODF: podf);
123    }
124}
125
126/// Wait for all handshake bits to deassert.
127pub(crate) fn wait_handshake(ccm: &crate::ral::ccm::CCM) {
128    while crate::ral::read_reg!(crate::ral::ccm, ccm, CDHIPR) != 0 {}
129}
130
131/// Low power mode.
132///
133/// From the reference manual,
134///
135/// > Setting the low power mode that system will enter on next assertion of dsm_request signal.
136///
137/// Practically, this affects the processor behavior when you use WFI, WFE, or enter another
138/// low-power state. Low-power settings that aren't "run" halt the ARM SYSTICK peripheral.
139#[derive(Debug, Clone, Copy, PartialEq, Eq)]
140#[repr(u32)]
141pub enum LowPowerMode {
142    /// Remain in run mode when entering low power.
143    RemainInRun = 0,
144    /// Move to wait mode when entering low power.
145    TransferToWait = 1,
146    /// Stop when entering low power.
147    TransferToStop = 2,
148}
149
150/// Set the CCM low power mode.
151pub fn set_low_power_mode(ccm: &mut ral::ccm::CCM, mode: LowPowerMode) {
152    ral::modify_reg!(ral::ccm, ccm, CLPCR, LPM: mode as u32);
153}
154
155/// Returns the CCM low power mode.
156pub fn low_power_mode(ccm: &ral::ccm::CCM) -> LowPowerMode {
157    match ral::read_reg!(ral::ccm, ccm, CLPCR, LPM) {
158        0 => LowPowerMode::RemainInRun,
159        1 => LowPowerMode::TransferToWait,
160        2 => LowPowerMode::TransferToStop,
161        _ => unreachable!(),
162    }
163}
164
165/// UART clock root.
166///
167/// `uart_clk` provides the clock source for all LPUART peripherals.
168/// You must disable LPUART clock gates before selecting the clock
169/// and divider.
170///
171/// # Example
172///
173/// Select a 24MHz clock for the LPUART peripherals. This would affect
174/// how baud rate is computed. Since we're only using the second LPUART
175/// peripheral, we only disable and enable its clock gates.
176///
177/// ```no_run
178/// use imxrt_hal as hal;
179/// use hal::ccm::{uart_clk, clock_gate};
180///
181/// use imxrt_ral as ral;
182///
183/// const UART_CLK_DIVIDER: u32 = 1;
184/// const UART_CLK_HZ: u32 = hal::ccm::XTAL_OSCILLATOR_HZ / UART_CLK_DIVIDER;
185///
186/// # fn opt() -> Option<()> {
187/// let mut ccm = unsafe { ral::ccm::CCM::instance() };
188/// clock_gate::lpuart::<2>().set(&mut ccm, clock_gate::OFF);
189/// uart_clk::set_selection(&mut ccm, uart_clk::Selection::Oscillator);
190/// uart_clk::set_divider(&mut ccm, UART_CLK_DIVIDER);
191///
192/// clock_gate::lpuart::<2>().set(&mut ccm, clock_gate::ON);
193/// # Some(()) }
194/// ```
195pub mod uart_clk {
196    use crate::ral::{self, ccm::CCM};
197
198    /// Returns the UART clock divider.
199    #[inline(always)]
200    pub fn divider(ccm: &CCM) -> u32 {
201        ral::read_reg!(ral::ccm, ccm, CSCDR1, UART_CLK_PODF) + 1
202    }
203
204    /// The smallest UART clock divider.
205    pub const MIN_DIVIDER: u32 = 1;
206    /// The largest UART clock divider.
207    pub const MAX_DIVIDER: u32 = 1 << 6;
208
209    /// Set the UART clock divider.
210    ///
211    /// The implementation clamps `divider` between [`MIN_DIVIDER`] and [`MAX_DIVIDER`].
212    #[inline(always)]
213    pub fn set_divider(ccm: &mut CCM, divider: u32) {
214        let podf = divider.clamp(MIN_DIVIDER, MAX_DIVIDER) - 1;
215        ral::modify_reg!(ral::ccm, ccm, CSCDR1, UART_CLK_PODF: podf);
216    }
217
218    /// UART clock selection.
219    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
220    #[repr(u32)]
221    pub enum Selection {
222        /// PLL 3 divided by 6.
223        ///
224        /// This is typically 480MHz / 6 == 80MHz.
225        Pll3Div6 = 0,
226        /// 24MHz oscillator.
227        Oscillator = 1,
228    }
229
230    /// Return the UART clock selection.
231    #[inline(always)]
232    pub fn selection(ccm: &CCM) -> Selection {
233        match ral::read_reg!(ral::ccm, ccm, CSCDR1, UART_CLK_SEL) {
234            0 => Selection::Pll3Div6,
235            1 => Selection::Oscillator,
236            _ => unreachable!(),
237        }
238    }
239
240    /// Set the UART clock selection.
241    #[inline(always)]
242    pub fn set_selection(ccm: &mut CCM, selection: Selection) {
243        ral::modify_reg!(ral::ccm, ccm, CSCDR1, UART_CLK_SEL: selection as u32);
244    }
245}
246
247/// LPI2C clock root.
248///
249/// `lpi2c_clk` provides the clock source for all LPI2C peripherals.
250/// You must disable LPI2C clock gates before selecting the clock
251/// and divider.
252///
253/// # Example
254///
255/// ```no_run
256/// use imxrt_hal as hal;
257/// use hal::ccm::{lpi2c_clk, clock_gate};
258///
259/// use imxrt_ral as ral;
260///
261/// const LPI2C_CLK_DIVIDER: u32 = 3;
262/// const LPI2C_CLK_HZ: u32 = hal::ccm::XTAL_OSCILLATOR_HZ / LPI2C_CLK_DIVIDER;
263///
264/// # fn opt() -> Option<()> {
265/// let mut ccm = unsafe { ral::ccm::CCM::instance() };
266/// clock_gate::lpi2c::<2>().set(&mut ccm, clock_gate::OFF);
267/// lpi2c_clk::set_selection(&mut ccm, lpi2c_clk::Selection::Oscillator);
268/// lpi2c_clk::set_divider(&mut ccm, LPI2C_CLK_DIVIDER);
269/// clock_gate::lpi2c::<2>().set(&mut ccm, clock_gate::ON);
270/// # Some(()) }
271/// ```
272pub mod lpi2c_clk {
273    use crate::ral::{self, ccm::CCM};
274
275    /// Returns the LPI2C clock divider.
276    #[inline(always)]
277    pub fn divider(ccm: &CCM) -> u32 {
278        ral::read_reg!(ral::ccm, ccm, CSCDR2, LPI2C_CLK_PODF) + 1
279    }
280
281    /// The smallest LPI2C clock divider.
282    pub const MIN_DIVIDER: u32 = 1;
283    /// The largest LPI2C clock divider.
284    pub const MAX_DIVIDER: u32 = 64;
285
286    /// Set the LPI2C clock divider.
287    ///
288    /// The implementation clamps `divider` between [`MIN_DIVIDER`] and [`MAX_DIVIDER`].
289    #[inline(always)]
290    pub fn set_divider(ccm: &mut CCM, divider: u32) {
291        let podf = divider.clamp(MIN_DIVIDER, MAX_DIVIDER) - 1;
292        ral::modify_reg!(ral::ccm, ccm, CSCDR2, LPI2C_CLK_PODF: podf);
293    }
294
295    /// LPI2C clock selections.
296    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
297    #[repr(u32)]
298    pub enum Selection {
299        /// Derive from PLL3 divided by 8.
300        Pll3Div8 = 0,
301        /// Derive from the crystal oscillator.
302        Oscillator = 1,
303    }
304
305    /// Returns the LPI2C clock selection.
306    #[inline(always)]
307    pub fn selection(ccm: &CCM) -> Selection {
308        match ral::read_reg!(ral::ccm, ccm, CSCDR2, LPI2C_CLK_SEL) {
309            0 => Selection::Pll3Div8,
310            1 => Selection::Oscillator,
311            _ => unreachable!(),
312        }
313    }
314
315    /// Set the LPI2C clock selection.
316    #[inline(always)]
317    pub fn set_selection(ccm: &mut CCM, selection: Selection) {
318        ral::modify_reg!(ral::ccm, ccm, CSCDR2, LPI2C_CLK_SEL: selection as u32);
319    }
320}
321
322/// LPSPI clock root.
323///
324/// `lpspi_clk` provides the clock source for all LPSPI peripherals.
325/// You must disable LPSPI clock gates before selecting the clock
326/// and divider.
327///
328/// # Example
329///
330/// ```no_run
331/// use imxrt_hal as hal;
332/// use hal::ccm::{lpspi_clk, clock_gate};
333/// use hal::ccm::analog::pll2;
334///
335/// use imxrt_ral as ral;
336///
337/// const LPSPI_CLK_DIVIDER: u32 = 8;
338/// const LPSPI_CLK_HZ: u32 = pll2::FREQUENCY / LPSPI_CLK_DIVIDER;
339///
340/// # fn opt() -> Option<()> {
341/// let mut ccm = unsafe { ral::ccm::CCM::instance() };
342/// clock_gate::lpspi::<2>().set(&mut ccm, clock_gate::OFF);
343/// lpspi_clk::set_selection(&mut ccm, lpspi_clk::Selection::Pll2);
344/// lpspi_clk::set_divider(&mut ccm, LPSPI_CLK_DIVIDER);
345///
346/// clock_gate::lpspi::<2>().set(&mut ccm, clock_gate::ON);
347/// # Some(()) }
348/// ```
349pub mod lpspi_clk {
350    use crate::ral::{self, ccm::CCM};
351
352    /// Returns the LPSPI clock divider.
353    #[inline(always)]
354    pub fn divider(ccm: &CCM) -> u32 {
355        ral::read_reg!(ral::ccm, ccm, CBCMR, LPSPI_PODF) + 1
356    }
357
358    /// The smallest LPSPI clock divider.
359    pub const MIN_DIVIDER: u32 = 1;
360    /// The largest LPSPI clock divider.
361    pub const MAX_DIVIDER: u32 = 8;
362
363    /// Set the LPSPI clock divider.
364    ///
365    /// The implementation clamps `divider` between [`MIN_DIVIDER`] and [`MAX_DIVIDER`].
366    #[inline(always)]
367    pub fn set_divider(ccm: &mut CCM, divider: u32) {
368        // 1010 MCUs support an extra bit in this field, so this
369        // could be a max of 16 for those chips.
370        let podf = divider.clamp(MIN_DIVIDER, MAX_DIVIDER) - 1;
371        ral::modify_reg!(ral::ccm, ccm, CBCMR, LPSPI_PODF: podf);
372    }
373
374    /// LPSPI clock selections.
375    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
376    #[repr(u32)]
377    pub enum Selection {
378        /// Derive from PLL3_PFD1.
379        Pll3Pfd1 = 0,
380        /// Derive from the PLL3_PFD0.
381        Pll3Pfd0 = 1,
382        /// Derive from PLL2.
383        Pll2 = 2,
384        /// Derive from PLL2_PFD2.
385        Pll2Pfd2 = 3,
386    }
387
388    /// Returns the LPSPI clock selection.
389    #[inline(always)]
390    pub fn selection(ccm: &CCM) -> Selection {
391        match ral::read_reg!(ral::ccm, ccm, CBCMR, LPSPI_CLK_SEL) {
392            0 => Selection::Pll3Pfd1,
393            1 => Selection::Pll3Pfd0,
394            2 => Selection::Pll2,
395            3 => Selection::Pll2Pfd2,
396            _ => unreachable!(),
397        }
398    }
399
400    /// Set the LPSPI clock selection.
401    #[inline(always)]
402    pub fn set_selection(ccm: &mut CCM, selection: Selection) {
403        ral::modify_reg!(ral::ccm, ccm, CBCMR, LPSPI_CLK_SEL: selection as u32);
404    }
405}
406
407macro_rules! ccm_flexio {
408    (
409        $name:ident, $desc:literal,
410        divider: ($divider_reg:ident, $divider_field:ident),
411        predivider: ($predivider_reg:ident, $predivider_field:ident),
412        selection: ($sel_reg:ident, $sel_field:ident)$(,)?
413    ) => {
414        #[doc = concat!($desc, " clock root.")]
415        pub mod $name {
416            use crate::ral::{self, ccm::CCM};
417
418            #[doc = concat!("Returns the ", $desc, " clock divider.")]
419            #[inline(always)]
420            pub fn divider(ccm: &CCM) -> u32 {
421                ral::read_reg!(ral::ccm, ccm, $divider_reg, $divider_field) + 1
422            }
423
424            #[doc = concat!("The smallest ", $desc, " clock divider.")]
425            pub const MIN_DIVIDER: u32 = 1;
426            #[doc = concat!("The largest ", $desc, " clock divider.")]
427            pub const MAX_DIVIDER: u32 = 8;
428
429            #[doc = concat!("Set the ", $desc, " clock divider.")]
430            ///
431            /// The implementation clamps `divider` between [`MIN_DIVIDER`] and [`MAX_DIVIDER`].
432            #[inline(always)]
433            pub fn set_divider(ccm: &mut CCM, divider: u32) {
434                // 1010 MCUs support an extra bit in this field, so this
435                // could be a max of 16 for those chips.
436                let podf = divider.clamp(MIN_DIVIDER, MAX_DIVIDER) - 1;
437                ral::modify_reg!(ral::ccm, ccm, $divider_reg, $divider_field: podf);
438            }
439
440            #[doc = concat!("Returns the ", $desc, " clock predivider.")]
441            #[inline(always)]
442            pub fn predivider(ccm: &CCM) -> u32 {
443                ral::read_reg!(ral::ccm, ccm, $predivider_reg, $predivider_field) + 1
444            }
445
446            #[doc = concat!("The smallest ", $desc, " clock predivider.")]
447            pub const MIN_PREDIVIDER: u32 = 1;
448            #[doc = concat!("The largest ", $desc, " clock predivider.")]
449            pub const MAX_PREDIVIDER: u32 = 8;
450
451            #[doc = concat!("Set the ", $desc, " clock predivider.")]
452            ///
453            /// The implementation clamps `predivider` between [`MIN_PREDIVIDER`] and [`MAX_PREDIVIDER`].
454            #[inline(always)]
455            pub fn set_predivider(ccm: &mut CCM, predivider: u32) {
456                let podf = predivider.clamp(MIN_PREDIVIDER, MAX_PREDIVIDER) - 1;
457                ral::modify_reg!(ral::ccm, ccm, $predivider_reg, $predivider_field: podf);
458            }
459
460            #[doc = concat!($desc, " clock selections.")]
461            #[derive(Debug, Clone, Copy, PartialEq, Eq)]
462            #[repr(u32)]
463            pub enum Selection {
464                /// Derive from PLL4.
465                Pll4 = 0,
466                /// Derive from PLL3_PFD2.
467                Pll3Pfd2 = 1,
468
469                #[cfg(any(feature = "imxrt1060", feature = "imxrt1064"))]
470                /// Derive from PLL5.
471                Pll5 = 2,
472                #[cfg(feature = "imxrt1010")]
473                /// Derive from PLL2.
474                Pll2 = 2,
475
476                //
477                // '2' reserved on 1020.
478                //
479
480                /// Derive from pll3_sw_clk.
481                Pll3SwClk = 3,
482            }
483
484            #[doc = concat!("Returns the ", $desc, " clock selections.")]
485            #[inline(always)]
486            pub fn selection(ccm: &CCM) -> Selection {
487                match ral::read_reg!(ral::ccm, ccm, $sel_reg, $sel_field) {
488                    0 => Selection::Pll4,
489                    1 => Selection::Pll3Pfd2,
490                    #[cfg(any(feature = "imxrt1060", feature = "imxrt1064"))]
491                    2 => Selection::Pll5,
492                    #[cfg(feature = "imxrt1010")]
493                    2 => Selection::Pll2,
494                    3 => Selection::Pll3SwClk,
495                    _ => unreachable!(),
496                }
497            }
498
499            #[doc = concat!("Set the ", $desc, " clock selections.")]
500            #[inline(always)]
501            pub fn set_selection(ccm: &mut CCM, selection: Selection) {
502                ral::modify_reg!(ral::ccm, ccm, $sel_reg, $sel_field: selection as u32);
503            }
504        }
505    };
506}
507
508/// SAI clock root.
509///
510/// `sai_clk` provides the clock source for each SAI peripheral.
511/// You must disable SAI clock gates before selecting the clock
512/// and divider.
513///
514/// # Example
515///
516/// ```no_run
517/// use imxrt_hal as hal;
518/// use hal::ccm::{sai_clk, clock_gate};
519/// use hal::ccm::analog::pll4;
520///
521/// use imxrt_ral as ral;
522///
523/// const SAI_CLK_DIVIDER: u32 = 8;
524///
525/// # fn opt() -> Option<()> {
526/// let mut ccm = unsafe { ral::ccm::CCM::instance() };
527/// clock_gate::sai::<1>().set(&mut ccm, clock_gate::OFF);
528/// sai_clk::set_selection::<1>(&mut ccm, sai_clk::Selection::Pll4);
529/// sai_clk::set_divider::<1>(&mut ccm, SAI_CLK_DIVIDER);
530///
531/// clock_gate::sai::<1>().set(&mut ccm, clock_gate::ON);
532/// let sai_clk_hz: u32 = pll4::frequency() / SAI_CLK_DIVIDER;
533///
534/// # Some(()) }
535/// ```
536pub mod sai_clk {
537    use crate::ral::{self, ccm::CCM};
538
539    /// Returns the `SAI<N>` clock predivider.
540    #[inline(always)]
541    pub fn predivider<const N: u8>(ccm: &CCM) -> u32
542    where
543        ral::sai::Instance<N>: ral::Valid,
544    {
545        1 + (match N {
546            1 => ral::read_reg!(ral::ccm, ccm, CS1CDR, SAI1_CLK_PRED),
547            #[cfg(not(feature = "imxrt1010"))]
548            2 => ral::read_reg!(ral::ccm, ccm, CS2CDR, SAI2_CLK_PRED),
549            3 => ral::read_reg!(ral::ccm, ccm, CS1CDR, SAI3_CLK_PRED),
550            _ => unreachable!(),
551        })
552    }
553
554    /// The smallest SAI clock predivider.
555    pub const MIN_PREDIVIDER: u32 = 1;
556    /// The largest SAI clock predivider.
557    pub const MAX_PREDIVIDER: u32 = 8;
558
559    /// Set the SAI clock divider.
560    ///
561    /// The implementation clamps `divider` between [`MIN_PREDIVIDER`] and [`MAX_PREDIVIDER`].
562    #[inline(always)]
563    pub fn set_predivider<const N: u8>(ccm: &mut CCM, predivider: u32)
564    where
565        ral::sai::Instance<N>: ral::Valid,
566    {
567        let pred = predivider.clamp(MIN_PREDIVIDER, MAX_PREDIVIDER) - 1;
568        match N {
569            1 => ral::modify_reg!(ral::ccm, ccm, CS1CDR, SAI1_CLK_PRED: pred),
570            #[cfg(not(feature = "imxrt1010"))]
571            2 => ral::modify_reg!(ral::ccm, ccm, CS2CDR, SAI2_CLK_PRED: pred),
572            3 => ral::modify_reg!(ral::ccm, ccm, CS1CDR, SAI3_CLK_PRED: pred),
573            _ => unreachable!(),
574        }
575    }
576    /// Returns the `SAI<N>` clock divider.
577    #[inline(always)]
578    pub fn divider<const N: u8>(ccm: &CCM) -> u32
579    where
580        ral::sai::Instance<N>: ral::Valid,
581    {
582        1 + (match N {
583            1 => ral::read_reg!(ral::ccm, ccm, CS1CDR, SAI1_CLK_PODF),
584            #[cfg(not(feature = "imxrt1010"))]
585            2 => ral::read_reg!(ral::ccm, ccm, CS2CDR, SAI2_CLK_PODF),
586            3 => ral::read_reg!(ral::ccm, ccm, CS1CDR, SAI3_CLK_PODF),
587            _ => unreachable!(),
588        })
589    }
590
591    /// The smallest SAI clock divider.
592    pub const MIN_DIVIDER: u32 = 1;
593    /// The largest SAI clock divider.
594    pub const MAX_DIVIDER: u32 = 64;
595
596    /// Set the SAI clock divider.
597    ///
598    /// The implementation clamps `divider` between [`MIN_DIVIDER`] and [`MAX_DIVIDER`].
599    #[inline(always)]
600    pub fn set_divider<const N: u8>(ccm: &mut CCM, divider: u32)
601    where
602        ral::sai::Instance<N>: ral::Valid,
603    {
604        let podf = divider.clamp(MIN_DIVIDER, MAX_DIVIDER) - 1;
605        match N {
606            1 => ral::modify_reg!(ral::ccm, ccm, CS1CDR, SAI1_CLK_PODF: podf),
607            #[cfg(not(feature = "imxrt1010"))]
608            2 => ral::modify_reg!(ral::ccm, ccm, CS2CDR, SAI2_CLK_PODF: podf),
609            3 => ral::modify_reg!(ral::ccm, ccm, CS1CDR, SAI3_CLK_PODF: podf),
610            _ => unreachable!(),
611        }
612    }
613
614    /// SAI clock selections.
615    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
616    #[repr(u32)]
617    pub enum Selection {
618        /// Derive from PLL3_PFD2.
619        Pll3Pfd2 = 0,
620        #[cfg(not(feature = "imxrt1010"))]
621        /// Derive from PLL5 (Video PLL).
622        Pll5 = 1,
623        #[cfg(feature = "imxrt1010")]
624        /// Derive from pll3_sw_clk
625        Pll3SwClk = 1,
626        /// Derive from PLL4 (Audio PLL).
627        Pll4 = 2,
628        /// Reserved (unused).
629        Reserved = 3,
630    }
631
632    /// Returns the SAI clock selection.
633    #[inline(always)]
634    pub fn selection<const N: u8>(ccm: &CCM) -> Selection
635    where
636        ral::sai::Instance<N>: ral::Valid,
637    {
638        let sel: u32 = match N {
639            1 => ral::read_reg!(ral::ccm, ccm, CSCMR1, SAI1_CLK_SEL),
640            #[cfg(not(feature = "imxrt1010"))]
641            2 => ral::read_reg!(ral::ccm, ccm, CSCMR1, SAI2_CLK_SEL),
642            3 => ral::read_reg!(ral::ccm, ccm, CSCMR1, SAI3_CLK_SEL),
643            _ => unreachable!(),
644        };
645        match sel {
646            0 => Selection::Pll3Pfd2,
647            #[cfg(not(feature = "imxrt1010"))]
648            1 => Selection::Pll5,
649            #[cfg(feature = "imxrt1010")]
650            1 => Selection::Pll3SwClk,
651            2 => Selection::Pll4,
652            _ => unreachable!(),
653        }
654    }
655
656    /// Set the SAI clock selection.
657    #[inline(always)]
658    pub fn set_selection<const N: u8>(ccm: &mut CCM, selection: Selection)
659    where
660        ral::sai::Instance<N>: ral::Valid,
661    {
662        match N {
663            1 => ral::modify_reg!(ral::ccm, ccm, CSCMR1, SAI1_CLK_SEL: selection as u32),
664            #[cfg(not(feature = "imxrt1010"))]
665            2 => ral::modify_reg!(ral::ccm, ccm, CSCMR1, SAI2_CLK_SEL: selection as u32),
666            3 => ral::modify_reg!(ral::ccm, ccm, CSCMR1, SAI3_CLK_SEL: selection as u32),
667            _ => unreachable!(),
668        }
669    }
670}