diff options
Diffstat (limited to 'drivers/ccm-10xx/src/ccm.rs')
| -rw-r--r-- | drivers/ccm-10xx/src/ccm.rs | 805 |
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 + }); + } +} |
