aboutsummaryrefslogtreecommitdiff
path: root/drivers/ccm-10xx/src/ccm.rs
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ccm-10xx/src/ccm.rs')
-rw-r--r--drivers/ccm-10xx/src/ccm.rs805
1 files changed, 805 insertions, 0 deletions
diff --git a/drivers/ccm-10xx/src/ccm.rs b/drivers/ccm-10xx/src/ccm.rs
new file mode 100644
index 0000000..6e2e051
--- /dev/null
+++ b/drivers/ccm-10xx/src/ccm.rs
@@ -0,0 +1,805 @@
+//! Clock controller module.
+
+use crate::ral;
+
+pub use ral::ccm::CCM;
+pub type Instance = ral_registers::Instance<ral::ccm::RegisterBlock>;
+
+/// Wait for all handshake bits to deassert.
+fn wait_handshake(ccm: ral::ccm::CCM) {
+ while ral::read_reg!(ral::ccm, ccm, CDHIPR) != 0 {}
+}
+
+/// PERCLK clock.
+///
+/// The PERCLK clock controls GPT and PIT timers.
+pub mod perclk_clk {
+ use crate::ral::{self, ccm::CCM};
+
+ /// PERCLK clock selection.
+ #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[repr(u32)]
+ pub enum Selection {
+ /// Derive from the IPG clock root.
+ Ipg = 0,
+ /// Derive from the oscillator clock.
+ Oscillator = 1,
+ }
+
+ /// Set the PERCLK clock selection.
+ pub fn set_selection(ccm: CCM, selection: Selection) {
+ ral::modify_reg!(ral::ccm, ccm, CSCMR1, PERCLK_CLK_SEL: selection as u32);
+ }
+
+ /// Returns the PERCLK clock selection.
+ pub fn selection(ccm: CCM) -> Selection {
+ if ral::read_reg!(ral::ccm, ccm, CSCMR1, PERCLK_CLK_SEL == 1) {
+ Selection::Oscillator
+ } else {
+ Selection::Ipg
+ }
+ }
+
+ /// The smallest PERCLK divider.
+ pub const MIN_DIVIDER: u32 = 1;
+ /// The largest PERCLK divider.
+ pub const MAX_DIVIDER: u32 = 64;
+
+ /// Set the PERCLK clock divider.
+ ///
+ /// The implementation clamps `divider` between [`MIN_DIVIDER`] and [`MAX_DIVIDER`].
+ pub fn set_divider(ccm: CCM, divider: u32) {
+ let podf = divider.clamp(MIN_DIVIDER, MAX_DIVIDER) - 1;
+ ral::modify_reg!(ral::ccm, ccm, CSCMR1, PERCLK_PODF: podf);
+ }
+
+ /// Returns the PERCLK clock divider.
+ pub fn divider(ccm: CCM) -> u32 {
+ ral::read_reg!(ral::ccm, ccm, CSCMR1, PERCLK_PODF) + 1
+ }
+}
+
+/// IPG clock.
+///
+/// The IPG clock is divided from the core clock.
+pub mod ipg_clk {
+ use crate::ral::{self, ccm::CCM};
+
+ /// Returns the IPG clock divider.
+ pub fn divider(ccm: CCM) -> u32 {
+ ral::read_reg!(ral::ccm, ccm, CBCDR, IPG_PODF) + 1
+ }
+
+ /// The smallest IPG divider.
+ pub const MIN_DIVIDER: u32 = 1;
+ /// The largest IPG divider.
+ pub const MAX_DIVIDER: u32 = 4;
+
+ /// Sets the IPG clock divider.
+ ///
+ /// The implementation clamps `divider` between [`MIN_DIVIDER`] and [`MAX_DIVIDER`].
+ pub fn set_divider(ccm: CCM, divider: u32) {
+ let podf = divider.clamp(MIN_DIVIDER, MAX_DIVIDER) - 1;
+ ral::modify_reg!(ral::ccm, ccm, CBCDR, IPG_PODF: podf);
+ }
+}
+
+/// Low power mode.
+///
+/// From the reference manual,
+///
+/// > Setting the low power mode that system will enter on next assertion of dsm_request signal.
+///
+/// Practically, this affects the processor behavior when you use WFI, WFE, or enter another
+/// low-power state. Low-power settings that aren't "run" halt the ARM SYSTICK peripheral.
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[repr(u32)]
+pub enum LowPowerMode {
+ /// Remain in run mode when entering low power.
+ RemainInRun = ral::ccm::CLPCR::LPM::RUN,
+ /// Move to wait mode when entering low power.
+ TransferToWait = ral::ccm::CLPCR::LPM::WAIT,
+ /// Stop when entering low power.
+ TransferToStop = ral::ccm::CLPCR::LPM::STOP,
+}
+
+/// Set the CCM low power mode.
+pub fn set_low_power_mode(ccm: ral::ccm::CCM, mode: LowPowerMode) {
+ ral::modify_reg!(ral::ccm, ccm, CLPCR, LPM: mode as u32);
+}
+
+/// Returns the CCM low power mode.
+///
+/// Returns `None` if the low power mode is the reserved field value.
+pub fn low_power_mode(ccm: ral::ccm::CCM) -> Option<LowPowerMode> {
+ use ral::ccm::CLPCR::LPM::{RUN, STOP, WAIT};
+ Some(match ral::read_reg!(ral::ccm, ccm, CLPCR, LPM) {
+ RUN => LowPowerMode::RemainInRun,
+ WAIT => LowPowerMode::TransferToWait,
+ STOP => LowPowerMode::TransferToStop,
+ _ => return None,
+ })
+}
+
+/// UART clock root.
+///
+/// `uart_clk` provides the clock source for all LPUART peripherals.
+/// You must disable LPUART clock gates before selecting the clock
+/// and divider.
+pub mod uart_clk {
+ use crate::ral::{self, ccm::CCM};
+
+ /// Returns the UART clock divider.
+ pub fn divider(ccm: CCM) -> u32 {
+ ral::read_reg!(ral::ccm, ccm, CSCDR1, UART_CLK_PODF) + 1
+ }
+
+ /// The smallest UART clock divider.
+ pub const MIN_DIVIDER: u32 = 1;
+ /// The largest UART clock divider.
+ pub const MAX_DIVIDER: u32 = 1 << 6;
+
+ /// Set the UART clock divider.
+ ///
+ /// The implementation clamps `divider` between [`MIN_DIVIDER`] and [`MAX_DIVIDER`].
+ pub fn set_divider(ccm: CCM, divider: u32) {
+ let podf = divider.clamp(MIN_DIVIDER, MAX_DIVIDER) - 1;
+ ral::modify_reg!(ral::ccm, ccm, CSCDR1, UART_CLK_PODF: podf);
+ }
+
+ /// UART clock selection.
+ #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[repr(u32)]
+ pub enum Selection {
+ /// PLL 3 divided by 6.
+ ///
+ /// This is typically 480MHz / 6 == 80MHz.
+ Pll3Div6 = 0,
+ /// 24MHz oscillator.
+ Oscillator = 1,
+ }
+
+ /// Return the UART clock selection.
+ pub fn selection(ccm: CCM) -> Selection {
+ match ral::read_reg!(ral::ccm, ccm, CSCDR1, UART_CLK_SEL) {
+ 0 => Selection::Pll3Div6,
+ 1 => Selection::Oscillator,
+ _ => unreachable!(),
+ }
+ }
+
+ /// Set the UART clock selection.
+ pub fn set_selection(ccm: CCM, selection: Selection) {
+ ral::modify_reg!(ral::ccm, ccm, CSCDR1, UART_CLK_SEL: selection as u32);
+ }
+}
+
+/// LPI2C clock root.
+///
+/// `lpi2c_clk` provides the clock source for all LPI2C peripherals.
+/// You must disable LPI2C clock gates before selecting the clock
+/// and divider.
+pub mod lpi2c_clk {
+ use crate::ral::{self, ccm::CCM};
+
+ /// Returns the LPI2C clock divider.
+ pub fn divider(ccm: CCM) -> u32 {
+ ral::read_reg!(ral::ccm, ccm, CSCDR2, LPI2C_CLK_PODF) + 1
+ }
+
+ /// The smallest LPI2C clock divider.
+ pub const MIN_DIVIDER: u32 = 1;
+ /// The largest LPI2C clock divider.
+ pub const MAX_DIVIDER: u32 = 64;
+
+ /// Set the LPI2C clock divider.
+ ///
+ /// The implementation clamps `divider` between [`MIN_DIVIDER`] and [`MAX_DIVIDER`].
+ pub fn set_divider(ccm: CCM, divider: u32) {
+ let podf = divider.clamp(MIN_DIVIDER, MAX_DIVIDER) - 1;
+ ral::modify_reg!(ral::ccm, ccm, CSCDR2, LPI2C_CLK_PODF: podf);
+ }
+
+ /// LPI2C clock selections.
+ #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[repr(u32)]
+ pub enum Selection {
+ /// Derive from PLL3 divided by 8.
+ Pll3Div8 = 0,
+ /// Derive from the crystal oscillator.
+ Oscillator = 1,
+ }
+
+ /// Returns the LPI2C clock selection.
+ pub fn selection(ccm: CCM) -> Selection {
+ match ral::read_reg!(ral::ccm, ccm, CSCDR2, LPI2C_CLK_SEL) {
+ 0 => Selection::Pll3Div8,
+ 1 => Selection::Oscillator,
+ _ => unreachable!(),
+ }
+ }
+
+ /// Set the LPI2C clock selection.
+ pub fn set_selection(ccm: CCM, selection: Selection) {
+ ral::modify_reg!(ral::ccm, ccm, CSCDR2, LPI2C_CLK_SEL: selection as u32);
+ }
+}
+
+/// LPSPI clock root.
+///
+/// `lpspi_clk` provides the clock source for all LPSPI peripherals.
+/// You must disable LPSPI clock gates before selecting the clock
+/// and divider.
+pub mod lpspi_clk {
+ use crate::ral::{self, ccm::CCM};
+
+ /// Returns the LPSPI clock divider.
+ pub fn divider(ccm: CCM) -> u32 {
+ ral::read_reg!(ral::ccm, ccm, CBCMR, LPSPI_PODF) + 1
+ }
+
+ /// The smallest LPSPI clock divider.
+ pub const MIN_DIVIDER: u32 = 1;
+ /// The largest LPSPI clock divider.
+ pub const MAX_DIVIDER: u32 = 8;
+
+ /// Set the LPSPI clock divider.
+ ///
+ /// The implementation clamps `divider` between [`MIN_DIVIDER`] and [`MAX_DIVIDER`].
+ pub fn set_divider(ccm: CCM, divider: u32) {
+ // 1010 MCUs support an extra bit in this field, so this
+ // could be a max of 16 for those chips.
+ let podf = divider.clamp(MIN_DIVIDER, MAX_DIVIDER) - 1;
+ ral::modify_reg!(ral::ccm, ccm, CBCMR, LPSPI_PODF: podf);
+ }
+
+ /// LPSPI clock selections.
+ #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[repr(u32)]
+ pub enum Selection {
+ /// Derive from PLL3_PFD1.
+ Pll3Pfd1 = 0,
+ /// Derive from the PLL3_PFD0.
+ Pll3Pfd0 = 1,
+ /// Derive from PLL2.
+ Pll2 = 2,
+ /// Derive from PLL2_PFD2.
+ Pll2Pfd2 = 3,
+ }
+
+ /// Returns the LPSPI clock selection.
+ pub fn selection(ccm: CCM) -> Selection {
+ match ral::read_reg!(ral::ccm, ccm, CBCMR, LPSPI_CLK_SEL) {
+ 0 => Selection::Pll3Pfd1,
+ 1 => Selection::Pll3Pfd0,
+ 2 => Selection::Pll2,
+ 3 => Selection::Pll2Pfd2,
+ _ => unreachable!(),
+ }
+ }
+
+ /// Set the LPSPI clock selection.
+ pub fn set_selection(ccm: CCM, selection: Selection) {
+ ral::modify_reg!(ral::ccm, ccm, CBCMR, LPSPI_CLK_SEL: selection as u32);
+ }
+}
+
+/// AHB (ARM) clock root.
+///
+/// The AHB module exposes all selections and dividers that affect
+/// the AHB / ARM clock. This includes all clock trees behind the main
+/// selector, and the driving PLL.
+///
+/// Chip-specific features affect what's exposed from this module.
+///
+/// Note: the i.MX RT 1010 processors refer to the AHB clock root as the
+/// "core clock root." Nevertheless, the core clock root has an "AHB divider."
+/// For consistency, we refer to the 1010's core clock root as the AHB clock
+/// root.
+pub mod ahb_clk {
+ use crate::ral::{self, ccm::CCM};
+
+ /// Set the AHB divider.
+ ///
+ /// The implementation clamps `divider` between 1 and 8.
+ pub fn set_divider(ccm: CCM, divider: u32) {
+ let podf = divider.clamp(1, 8) - 1;
+ ral::modify_reg!(ral::ccm, ccm, CBCDR, AHB_PODF: podf);
+ super::wait_handshake(ccm);
+ }
+
+ /// Returns the AHB divider.
+ pub fn divider(ccm: CCM) -> u32 {
+ ral::read_reg!(ral::ccm, ccm, CBCDR, AHB_PODF) + 1
+ }
+
+ /// Peripheral clock selection.
+ #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[repr(u32)]
+ pub enum Selection {
+ /// Derive from PRE_PERIPH_CLK.
+ PrePeriphClkSel = 0,
+ /// Derive from PERIPH_CLK2.
+ PeriphClk2Sel = 1,
+ }
+
+ /// Set the peripheral clock selection.
+ pub fn set_selection(ccm: CCM, selection: Selection) {
+ ral::modify_reg!(ral::ccm, ccm, CBCDR, PERIPH_CLK_SEL: selection as u32);
+ super::wait_handshake(ccm);
+ }
+
+ /// Returns the peripheral clock selection.
+ pub fn selection(ccm: CCM) -> Selection {
+ match ral::read_reg!(ral::ccm, ccm, CBCDR, PERIPH_CLK_SEL) {
+ 0 => Selection::PrePeriphClkSel,
+ 1 => Selection::PeriphClk2Sel,
+ _ => unreachable!(),
+ }
+ }
+}
+
+/// ARM divider.
+///
+/// This divides the output from the AHB PLL (either PLL1 or PLL6, depending
+/// on the system).
+pub mod arm_divider {
+ use crate::ral::{self, ccm::CCM};
+
+ /// Set the ARM divider.
+ ///
+ /// The implementation clamps `divider` between 1 and 8.
+ pub fn set_divider(ccm: CCM, divider: u32) {
+ let podf = divider.clamp(1, 8) - 1;
+ ral::modify_reg!(ral::ccm, ccm, CACRR, ARM_PODF: podf);
+ crate::ccm::wait_handshake(ccm);
+ }
+
+ /// Returns the ARM divider.
+ pub fn divider(ccm: CCM) -> u32 {
+ ral::read_reg!(ral::ccm, ccm, CACRR, ARM_PODF) + 1
+ }
+}
+
+/// One of the accessory muxes upstream of the AHB / ARM clock.
+pub mod periph_clk2 {
+ use crate::ral::{self, ccm::CCM};
+
+ /// Set the peripheral clock 2 divider.
+ ///
+ /// The implementation clamps `divider` between 1 and 8. You should first switch
+ /// away the core clock selection before changing this divider.
+ pub fn set_divider(ccm: CCM, divider: u32) {
+ let podf = divider.clamp(1, 8) - 1;
+ ral::modify_reg!(ral::ccm, ccm, CBCDR, PERIPH_CLK2_PODF: podf);
+ }
+
+ /// Returns the peripheral clock 2 divider.
+ pub fn divider(ccm: CCM) -> u32 {
+ ral::read_reg!(ral::ccm, ccm, CBCDR, PERIPH_CLK2_PODF) + 1
+ }
+
+ /// Peripheral CLK2 selection.
+ #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[repr(u32)]
+ pub enum Selection {
+ /// Use PLL3 (possibly bypassed by an upstream mux).
+ Pll3Sw = 0,
+ /// Crystall oscillator.
+ Osc = 1,
+ /// The PLL2 bypass.
+ Pll2Bypass = 2,
+ }
+
+ /// Set the peripheral clock2 selection.
+ pub fn set_selection(ccm: CCM, selection: Selection) {
+ ral::modify_reg!(ral::ccm, ccm, CBCMR, PERIPH_CLK2_SEL: selection as u32);
+ crate::ccm::wait_handshake(ccm);
+ }
+
+ /// Returns the peripheral clock2 selection.
+ pub fn selection(ccm: CCM) -> Selection {
+ let raw = ral::read_reg!(ral::ccm, ccm, CBCMR, PERIPH_CLK2_SEL);
+ match raw {
+ 0 => Selection::Pll3Sw,
+ 1 => Selection::Osc,
+ 2 => Selection::Pll2Bypass,
+ _ => unreachable!(),
+ }
+ }
+}
+
+/// FlexSPI1 with AXI/SEMC selectors.
+pub mod flexspi1_clk_axi_semc {
+ use crate::{ccm::CCM, ral};
+
+ /// FlexSPI1 selections
+ #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[repr(u32)]
+ pub enum Selection {
+ /// The SEMC clock root.
+ AxiSemc = 0,
+ /// PLL3.
+ Pll3 = 1,
+ /// PFD2 of PLL2.
+ Pll2Pfd2 = 2,
+ /// PFD0 of PLL3.
+ Pll3Pfd0 = 3,
+ }
+
+ /// Set the FlexSPI1 clock selection.
+ pub fn set_selection(ccm: CCM, selection: Selection) {
+ ral::modify_reg!(ral::ccm, ccm, CSCMR1, FLEXSPI_CLK_SEL: selection as u32);
+ }
+
+ /// Return the FlexSPI1 clock selection.
+ pub fn selection(ccm: CCM) -> Selection {
+ match ral::read_reg!(ral::ccm, ccm, CSCMR1, FLEXSPI_CLK_SEL) {
+ 0 => Selection::AxiSemc,
+ 1 => Selection::Pll3,
+ 2 => Selection::Pll2Pfd2,
+ 3 => Selection::Pll3Pfd0,
+ _ => unreachable!(),
+ }
+ }
+
+ /// The smallest divider clamped by the implementation.
+ pub const MIN_DIVIDER: u32 = 1;
+
+ /// The largest divider clamped by the implementation.
+ pub const MAX_DIVIDER: u32 = 8;
+
+ /// Return the divider.
+ pub fn divider(ccm: CCM) -> u32 {
+ 1 + ral::read_reg!(ral::ccm, ccm, CSCMR1, FLEXSPI_PODF)
+ }
+
+ /// Set the FlexSPI1 root clock divider.
+ ///
+ /// The implementation clamps the input between [`MIN_DIVIDER`]
+ /// and [`MAX_DIVIDER`].
+ pub fn set_divider(ccm: CCM, divider: u32) {
+ let divider = divider.clamp(MIN_DIVIDER, MAX_DIVIDER) - 1;
+ ral::modify_reg!(ral::ccm, ccm, CSCMR1, FLEXSPI_PODF: divider);
+ }
+}
+
+/// FlexSPI1 with a PLL2 root clock.
+///
+/// This root clock can also switch to the `periph_clk2` source.
+/// However, this isn't yet implemented.
+pub mod flexspi1_clk_root_pll2 {
+ #[doc(inline)]
+ pub use super::flexspi1_clk_axi_semc::{MAX_DIVIDER, MIN_DIVIDER, divider, set_divider};
+ use crate::{ccm::CCM, ral};
+
+ /// FlexSPI1 selections
+ #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[repr(u32)]
+ pub enum Selection {
+ /// PLL2.
+ Pll2 = 0,
+ /// PLL3.
+ Pll3 = 1,
+ /// PFD2 of PLL2.
+ Pll2Pfd2 = 2,
+ /// PFD0 of PLL3.
+ Pll3Pfd0 = 3,
+ }
+
+ /// Set the FlexSPI1 clock selection.
+ pub fn set_selection(ccm: CCM, selection: Selection) {
+ ral::modify_reg!(ral::ccm, ccm, CSCMR1, FLEXSPI_CLK_SEL: selection as u32);
+ }
+
+ /// Return the FlexSPI1 clock selection.
+ pub fn selection(ccm: CCM) -> Selection {
+ match ral::read_reg!(ral::ccm, ccm, CSCMR1, FLEXSPI_CLK_SEL) {
+ 0 => Selection::Pll2,
+ 1 => Selection::Pll3,
+ 2 => Selection::Pll2Pfd2,
+ 3 => Selection::Pll3Pfd0,
+ _ => unreachable!(),
+ }
+ }
+}
+
+/// Pre-peripheral clock.
+pub mod pre_periph_clk_pll1 {
+ use crate::ral::{self, ccm::CCM};
+
+ /// Pre-peripheral clock selection.
+ #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[repr(u32)]
+ pub enum Selection {
+ /// PLL2.
+ Pll2 = 0,
+ /// PFD2 of PLL2
+ Pll2Pfd2 = 1,
+ /// PFD0 of PLL2.
+ Pll2Pfd0 = 2,
+ /// PLL1.
+ Pll1 = 3,
+ }
+
+ /// Set the pre-peripheral clock selection.
+ pub fn set_selection(ccm: CCM, selection: Selection) {
+ ral::modify_reg!(ral::ccm, ccm, CBCMR, PRE_PERIPH_CLK_SEL: selection as u32);
+ }
+
+ /// Returns the pre-peripheral clock selection.
+ pub fn selection(ccm: CCM) -> Selection {
+ use Selection::*;
+ match ral::read_reg!(ral::ccm, ccm, CBCMR, PRE_PERIPH_CLK_SEL) {
+ 0 => Pll2,
+ 1 => Pll2Pfd2,
+ 2 => Pll2Pfd0,
+ 3 => Pll1,
+ _ => unreachable!(),
+ }
+ }
+}
+
+/// Pre-peripheral clock.
+pub mod pre_periph_clk_pll6 {
+ use crate::ral::{self, ccm::CCM};
+
+ /// Pre-peripheral clock selection.
+ #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[repr(u32)]
+ pub enum Selection {
+ /// PLL2.
+ Pll2 = 0,
+ /// PFD3 of PLL3.
+ Pll3Pfd3 = 1,
+ /// PFD3 of PLL2.
+ Pll2Pfd3 = 2,
+ /// PLL6.
+ Pll6 = 3,
+ }
+
+ /// Set the pre-peripheral clock selection.
+ pub fn set_selection(ccm: CCM, selection: Selection) {
+ ral::modify_reg!(ral::ccm, ccm, CBCMR, PRE_PERIPH_CLK_SEL: selection as u32);
+ }
+
+ /// Returns the pre-peripheral clock selection.
+ pub fn selection(ccm: CCM) -> Selection {
+ use Selection::*;
+ match ral::read_reg!(ral::ccm, ccm, CBCMR, PRE_PERIPH_CLK_SEL) {
+ 0 => Pll2,
+ 1 => Pll3Pfd3,
+ 2 => Pll2Pfd3,
+ 3 => Pll6,
+ _ => unreachable!(),
+ }
+ }
+}
+
+/// Clock gate control.
+pub mod clock_gate {
+ use crate::ral::{self, ccm::CCM};
+
+ /// A clock gate locator.
+ ///
+ /// This enum encodes the register and field for a clock gate.
+ /// Note that not all clock gates are valid for all MCUs.
+ #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[repr(u16)]
+ pub enum Locator {
+ Ccgr0Cg00 = clock_gate(0, 00),
+ Ccgr0Cg01 = clock_gate(0, 01),
+ Ccgr0Cg02 = clock_gate(0, 02),
+ Ccgr0Cg03 = clock_gate(0, 03),
+ Ccgr0Cg04 = clock_gate(0, 04),
+ Ccgr0Cg05 = clock_gate(0, 05),
+ Ccgr0Cg06 = clock_gate(0, 06),
+ Ccgr0Cg07 = clock_gate(0, 07),
+ Ccgr0Cg08 = clock_gate(0, 08),
+ Ccgr0Cg09 = clock_gate(0, 09),
+ Ccgr0Cg10 = clock_gate(0, 10),
+ Ccgr0Cg11 = clock_gate(0, 11),
+ Ccgr0Cg12 = clock_gate(0, 12),
+ Ccgr0Cg13 = clock_gate(0, 13),
+ Ccgr0Cg14 = clock_gate(0, 14),
+ Ccgr0Cg15 = clock_gate(0, 15),
+
+ Ccgr1Cg00 = clock_gate(1, 00),
+ Ccgr1Cg01 = clock_gate(1, 01),
+ Ccgr1Cg02 = clock_gate(1, 02),
+ Ccgr1Cg03 = clock_gate(1, 03),
+ Ccgr1Cg04 = clock_gate(1, 04),
+ Ccgr1Cg05 = clock_gate(1, 05),
+ Ccgr1Cg06 = clock_gate(1, 06),
+ Ccgr1Cg07 = clock_gate(1, 07),
+ Ccgr1Cg08 = clock_gate(1, 08),
+ Ccgr1Cg09 = clock_gate(1, 09),
+ Ccgr1Cg10 = clock_gate(1, 10),
+ Ccgr1Cg11 = clock_gate(1, 11),
+ Ccgr1Cg12 = clock_gate(1, 12),
+ Ccgr1Cg13 = clock_gate(1, 13),
+ Ccgr1Cg14 = clock_gate(1, 14),
+ Ccgr1Cg15 = clock_gate(1, 15),
+
+ Ccgr2Cg00 = clock_gate(2, 00),
+ Ccgr2Cg01 = clock_gate(2, 01),
+ Ccgr2Cg02 = clock_gate(2, 02),
+ Ccgr2Cg03 = clock_gate(2, 03),
+ Ccgr2Cg04 = clock_gate(2, 04),
+ Ccgr2Cg05 = clock_gate(2, 05),
+ Ccgr2Cg06 = clock_gate(2, 06),
+ Ccgr2Cg07 = clock_gate(2, 07),
+ Ccgr2Cg08 = clock_gate(2, 08),
+ Ccgr2Cg09 = clock_gate(2, 09),
+ Ccgr2Cg10 = clock_gate(2, 10),
+ Ccgr2Cg11 = clock_gate(2, 11),
+ Ccgr2Cg12 = clock_gate(2, 12),
+ Ccgr2Cg13 = clock_gate(2, 13),
+ Ccgr2Cg14 = clock_gate(2, 14),
+ Ccgr2Cg15 = clock_gate(2, 15),
+
+ Ccgr3Cg00 = clock_gate(3, 00),
+ Ccgr3Cg01 = clock_gate(3, 01),
+ Ccgr3Cg02 = clock_gate(3, 02),
+ Ccgr3Cg03 = clock_gate(3, 03),
+ Ccgr3Cg04 = clock_gate(3, 04),
+ Ccgr3Cg05 = clock_gate(3, 05),
+ Ccgr3Cg06 = clock_gate(3, 06),
+ Ccgr3Cg07 = clock_gate(3, 07),
+ Ccgr3Cg08 = clock_gate(3, 08),
+ Ccgr3Cg09 = clock_gate(3, 09),
+ Ccgr3Cg10 = clock_gate(3, 10),
+ Ccgr3Cg11 = clock_gate(3, 11),
+ Ccgr3Cg12 = clock_gate(3, 12),
+ Ccgr3Cg13 = clock_gate(3, 13),
+ Ccgr3Cg14 = clock_gate(3, 14),
+ Ccgr3Cg15 = clock_gate(3, 15),
+
+ Ccgr4Cg00 = clock_gate(4, 00),
+ Ccgr4Cg01 = clock_gate(4, 01),
+ Ccgr4Cg02 = clock_gate(4, 02),
+ Ccgr4Cg03 = clock_gate(4, 03),
+ Ccgr4Cg04 = clock_gate(4, 04),
+ Ccgr4Cg05 = clock_gate(4, 05),
+ Ccgr4Cg06 = clock_gate(4, 06),
+ Ccgr4Cg07 = clock_gate(4, 07),
+ Ccgr4Cg08 = clock_gate(4, 08),
+ Ccgr4Cg09 = clock_gate(4, 09),
+ Ccgr4Cg10 = clock_gate(4, 10),
+ Ccgr4Cg11 = clock_gate(4, 11),
+ Ccgr4Cg12 = clock_gate(4, 12),
+ Ccgr4Cg13 = clock_gate(4, 13),
+ Ccgr4Cg14 = clock_gate(4, 14),
+ Ccgr4Cg15 = clock_gate(4, 15),
+
+ Ccgr5Cg00 = clock_gate(5, 00),
+ Ccgr5Cg01 = clock_gate(5, 01),
+ Ccgr5Cg02 = clock_gate(5, 02),
+ Ccgr5Cg03 = clock_gate(5, 03),
+ Ccgr5Cg04 = clock_gate(5, 04),
+ Ccgr5Cg05 = clock_gate(5, 05),
+ Ccgr5Cg06 = clock_gate(5, 06),
+ Ccgr5Cg07 = clock_gate(5, 07),
+ Ccgr5Cg08 = clock_gate(5, 08),
+ Ccgr5Cg09 = clock_gate(5, 09),
+ Ccgr5Cg10 = clock_gate(5, 10),
+ Ccgr5Cg11 = clock_gate(5, 11),
+ Ccgr5Cg12 = clock_gate(5, 12),
+ Ccgr5Cg13 = clock_gate(5, 13),
+ Ccgr5Cg14 = clock_gate(5, 14),
+ Ccgr5Cg15 = clock_gate(5, 15),
+
+ Ccgr6Cg00 = clock_gate(6, 00),
+ Ccgr6Cg01 = clock_gate(6, 01),
+ Ccgr6Cg02 = clock_gate(6, 02),
+ Ccgr6Cg03 = clock_gate(6, 03),
+ Ccgr6Cg04 = clock_gate(6, 04),
+ Ccgr6Cg05 = clock_gate(6, 05),
+ Ccgr6Cg06 = clock_gate(6, 06),
+ Ccgr6Cg07 = clock_gate(6, 07),
+ Ccgr6Cg08 = clock_gate(6, 08),
+ Ccgr6Cg09 = clock_gate(6, 09),
+ Ccgr6Cg10 = clock_gate(6, 10),
+ Ccgr6Cg11 = clock_gate(6, 11),
+ Ccgr6Cg12 = clock_gate(6, 12),
+ Ccgr6Cg13 = clock_gate(6, 13),
+ Ccgr6Cg14 = clock_gate(6, 14),
+ Ccgr6Cg15 = clock_gate(6, 15),
+
+ Ccgr7Cg00 = clock_gate(7, 00),
+ Ccgr7Cg01 = clock_gate(7, 01),
+ Ccgr7Cg02 = clock_gate(7, 02),
+ Ccgr7Cg03 = clock_gate(7, 03),
+ Ccgr7Cg04 = clock_gate(7, 04),
+ Ccgr7Cg05 = clock_gate(7, 05),
+ Ccgr7Cg06 = clock_gate(7, 06),
+ Ccgr7Cg07 = clock_gate(7, 07),
+ Ccgr7Cg08 = clock_gate(7, 08),
+ Ccgr7Cg09 = clock_gate(7, 09),
+ Ccgr7Cg10 = clock_gate(7, 10),
+ Ccgr7Cg11 = clock_gate(7, 11),
+ Ccgr7Cg12 = clock_gate(7, 12),
+ Ccgr7Cg13 = clock_gate(7, 13),
+ Ccgr7Cg14 = clock_gate(7, 14),
+ Ccgr7Cg15 = clock_gate(7, 15),
+ }
+
+ /// Encode a clock gate.
+ const fn clock_gate(register: u8, field: u8) -> u16 {
+ ((register as u16) << 8) | ((field * 2) as u16)
+ }
+
+ impl Locator {
+ /// Access the register index.
+ const fn register(self) -> usize {
+ (((self as u16) >> 8) & 0xFF) as usize
+ }
+ /// Produce the bit shift for the field.
+ const fn shift(self) -> u32 {
+ ((self as u16) & 0xFF) as u32
+ }
+ /// Produce the mask for the field.
+ const fn mask(self) -> u32 {
+ 0b11 << self.shift()
+ }
+ }
+
+ /// The activity for a [`Locator`].
+ #[cfg_attr(feature = "defmt", derive(defmt::Format))]
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ #[repr(u32)]
+ pub enum Activity {
+ /// Clock is off in all modes.
+ Off = 0,
+ /// Clock is on in run mode, but off in WAIT and STOP modes.
+ OnlyRun = 1,
+ /// Clock is on in all modes, except stop mode.
+ On = 3,
+ }
+
+ impl From<bool> for Activity {
+ fn from(value: bool) -> Self {
+ if value { Self::On } else { Self::Off }
+ }
+ }
+
+ /// Helper constant to turn off a clock gate.
+ pub const OFF: Activity = Activity::Off;
+ /// Helper constant to turn on a clock gate.
+ pub const ON: Activity = Activity::On;
+
+ /// Returns a clock gate's activity.
+ ///
+ /// Returns `None` if the field represents the reserved value.
+ pub fn get(ccm: CCM, locator: Locator) -> Option<Activity> {
+ let ccgr = ral::read_reg!(ral::ccm, ccm, CCGR[locator.register()]);
+ let field = (ccgr & locator.mask()) >> locator.shift();
+ Some(match field {
+ 0 => Activity::Off,
+ 1 => Activity::OnlyRun,
+ 3 => Activity::On,
+ _ => return None,
+ })
+ }
+
+ /// Set the clock gate's activity.
+ pub fn set(ccm: CCM, locator: Locator, activity: Activity) {
+ ral::modify_reg!(ral::ccm, ccm, CCGR[locator.register()], |mut ccgr| {
+ ccgr &= !locator.mask();
+ ccgr |= (activity as u32) << locator.shift();
+ ccgr
+ });
+ }
+}