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}