Skip to main content

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