From 76199f21616ad86cf68f3b063c1ce23c6fc5a52f Mon Sep 17 00:00:00 2001 From: Ian McIntyre Date: Sun, 30 Nov 2025 18:52:34 -0500 Subject: First commit --- drivers/ccm-10xx/src/ahb.rs | 103 +++++ drivers/ccm-10xx/src/ccm.rs | 805 +++++++++++++++++++++++++++++++++ drivers/ccm-10xx/src/ccm_analog.rs | 244 ++++++++++ drivers/ccm-10xx/src/lib.rs | 18 + drivers/ccm-10xx/src/ral/ccm.rs | 85 ++++ drivers/ccm-10xx/src/ral/ccm_analog.rs | 129 ++++++ 6 files changed, 1384 insertions(+) create mode 100644 drivers/ccm-10xx/src/ahb.rs create mode 100644 drivers/ccm-10xx/src/ccm.rs create mode 100644 drivers/ccm-10xx/src/ccm_analog.rs create mode 100644 drivers/ccm-10xx/src/lib.rs create mode 100644 drivers/ccm-10xx/src/ral/ccm.rs create mode 100644 drivers/ccm-10xx/src/ral/ccm_analog.rs (limited to 'drivers/ccm-10xx/src') diff --git a/drivers/ccm-10xx/src/ahb.rs b/drivers/ccm-10xx/src/ahb.rs new file mode 100644 index 0000000..392be61 --- /dev/null +++ b/drivers/ccm-10xx/src/ahb.rs @@ -0,0 +1,103 @@ +//! Routines for configurating AHB clocks. + +use crate::{ccm, ccm_analog, ral}; + +/// The ARM PLL. +pub mod pll1 { + use super::{ccm, ccm_analog, ral}; + + /// A configuration for PLL1. + pub struct AbhConfiguration { + /// The PLL1 divider. + pub div_sel: u32, + /// The ARM divider, immediately after the PLL. + pub arm_divider: u32, + /// The AHB divider, downstream of the PLL and ARM divider. + pub ahb_divider: u32, + } + + impl AbhConfiguration { + /// Produces the target AHB frequency expressed by the configuration. + pub const fn ahb_frequency(&self) -> u32 { + ccm_analog::pll1::frequency(self.div_sel / self.arm_divider / self.ahb_divider) + } + } + + /// Configure the AHB root using PLL1. + /// + /// This does not touch any of the clock gates downstream of the root clock. + /// You're responsible for managing those. Note that is affects the IPG + /// clock; however, it does not affect the IPG divider. + pub fn configure_ahb( + ccm: ral::ccm::CCM, + ccm_analog: ral::ccm_analog::CCM_ANALOG, + config: &AbhConfiguration, + ) { + if ccm::ahb_clk::Selection::PeriphClk2Sel == ccm::ahb_clk::selection(ccm) { + // Switch to the pre-peripheral clock before changing + // peripheral clock 2... + ccm::ahb_clk::set_selection(ccm, ccm::ahb_clk::Selection::PrePeriphClkSel); + } + + // Temporarily switch to the crystal oscillator. + ccm::periph_clk2::set_divider(ccm, 1); + ccm::periph_clk2::set_selection(ccm, ccm::periph_clk2::Selection::Osc); + ccm::ahb_clk::set_selection(ccm, ccm::ahb_clk::Selection::PeriphClk2Sel); + + // Prepare PLL1. + ccm_analog::pll1::restart(ccm_analog, config.div_sel); + ccm::arm_divider::set_divider(ccm, config.arm_divider); + ccm::ahb_clk::set_divider(ccm, config.ahb_divider); + + // Switch back to PLL1. + ccm::pre_periph_clk_pll1::set_selection(ccm, ccm::pre_periph_clk_pll1::Selection::Pll1); + ccm::ahb_clk::set_selection(ccm, ccm::ahb_clk::Selection::PrePeriphClkSel); + } +} + +/// PLL6 that's fixed at 500MHz. +pub mod pll6_500mhz { + use super::{ccm, ccm_analog, ral}; + + /// Configuration for PLL6 using a 500MHz. + pub struct AbhConfiguration { + /// The AHB divider, just before the root clock. + pub ahb_divider: u32, + } + + impl AbhConfiguration { + /// Returns the frequency of the root clock. + pub const fn ahb_frequency(&self) -> u32 { + ccm_analog::pll6_500mhz::FREQUENCY / self.ahb_divider + } + } + + /// Configure the AHB root using PLL6 fixed at 500MHz. + /// + /// This does not touch any of the clock gates downstream of the root clock. + /// You're responsible for managing those. Note that this affects the IPG + /// clock; however, it does not touch the divider. + pub fn configure_ahb( + ccm: ral::ccm::CCM, + ccm_analog: ral::ccm_analog::CCM_ANALOG, + config: &AbhConfiguration, + ) { + if ccm::ahb_clk::Selection::PeriphClk2Sel == ccm::ahb_clk::selection(ccm) { + // Switch to the pre-peripheral clock before changing + // peripheral clock 2... + ccm::ahb_clk::set_selection(ccm, ccm::ahb_clk::Selection::PrePeriphClkSel); + } + + // Temporarily switch to the crystal oscillator. + ccm::periph_clk2::set_selection(ccm, ccm::periph_clk2::Selection::Osc); + ccm::ahb_clk::set_selection(ccm, ccm::ahb_clk::Selection::PeriphClk2Sel); + + // Prepare PLL6. + ccm_analog::pll6_500mhz::restart(ccm_analog); + ccm::ahb_clk::set_divider(ccm, config.ahb_divider); + + // Switch to PLL6. + ccm::pre_periph_clk_pll6::set_selection(ccm, ccm::pre_periph_clk_pll6::Selection::Pll6); + ccm::ahb_clk::set_selection(ccm, ccm::ahb_clk::Selection::PrePeriphClkSel); + } +} 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; + +/// 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 { + 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 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 { + 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 + }); + } +} diff --git a/drivers/ccm-10xx/src/ccm_analog.rs b/drivers/ccm-10xx/src/ccm_analog.rs new file mode 100644 index 0000000..a427827 --- /dev/null +++ b/drivers/ccm-10xx/src/ccm_analog.rs @@ -0,0 +1,244 @@ +use core::num::NonZero; + +pub use crate::ral::ccm_analog::CCM_ANALOG; +pub type Instance = ral_registers::Instance; + +/// Frequency (Hz) of the external crystal oscillator. +pub const XTAL_OSCILLATOR_HZ: u32 = 24_000_000; + +const fn pll_pfd_divider(pll_hz: u32, target_hz: u32) -> Option> { + let div = pll_hz / target_hz * 18; + // Safety: divider is between the non-zero min and max values. + unsafe { + if pll3::MIN_FRAC <= div && div <= pll3::MAX_FRAC { + Some(NonZero::new_unchecked(div)) + } else { + None + } + } +} + +/// The system PLL. +pub mod pll2 { + use core::num::NonZero; + + use crate::ral; + + /// PLL2 frequency (Hz). + /// + /// The reference manual notes that PLL2 should always run at 528MHz, + /// so this constant assumes that PLL2's DIV_SELECT field isn't + /// changed at runtime. + pub const FREQUENCY: u32 = 528_000_000; + + /// The smallest PLL2_PFD divider. + pub const MIN_FRAC: u32 = super::pll3::MIN_FRAC; + /// The largest PLL2_PFD divider. + pub const MAX_FRAC: u32 = super::pll3::MAX_FRAC; + + /// Produces a PFD divider to reach the target PFD frequency, in Hz. + pub const fn pll_pfd_divider(target_hz: u32) -> Option> { + super::pll_pfd_divider(FREQUENCY, target_hz) + } + + /// Restart the system PLL. + pub fn restart(ccm_analog: super::CCM_ANALOG) { + loop { + if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_SYS, ENABLE == 0) { + ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_SYS_SET, ENABLE: 1); + continue; + } + if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_SYS, POWERDOWN == 1) { + ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_SYS_CLR, POWERDOWN: 1); + continue; + } + if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_SYS, LOCK == 0) { + continue; + } + if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_SYS, BYPASS == 1) { + ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_SYS_CLR, BYPASS: 1); + continue; + } + break; + } + } +} + +/// The USB PLL. +/// +/// When an implementation has multiple USB peripherals, this +/// PLL is associated with USB1. +pub mod pll3 { + /// PLL3 frequency (Hz). + /// + /// The reference manual notes that PLL3 should always run at 480MHz, + /// so this constant assumes that PLL3's DIV_SELECT field isn't + /// changed at runtime. + pub const FREQUENCY: u32 = 480_000_000; + + /// The smallest PLL3_PFD divider. + pub const MIN_FRAC: u32 = 12; + /// The largest PLL3_PFD divider. + pub const MAX_FRAC: u32 = 35; + + use core::num::NonZero; + + use crate::ral; + + /// Produces a PFD divider to reach the target PFD frequency, in Hz. + pub const fn pll_pfd_divider(target_hz: u32) -> Option> { + super::pll_pfd_divider(FREQUENCY, target_hz) + } + + /// Restart the USB(1) PLL. + pub fn restart(ccm_analog: super::CCM_ANALOG) { + loop { + if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_USB1, ENABLE == 0) { + ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_USB1_SET, ENABLE: 1); + continue; + } + if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_USB1, POWER == 0) { + ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_USB1_SET, POWER: 1); + continue; + } + if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_USB1, LOCK == 0) { + continue; + } + if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_USB1, BYPASS == 1) { + ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_USB1_CLR, BYPASS: 1); + continue; + } + if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_USB1, EN_USB_CLKS == 0) { + ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_USB1_SET, EN_USB_CLKS: 1); + continue; + } + break; + } + } +} + +/// The USB2 PLL, when available. +pub mod pll7 { + /// PLL7 frequency (Hz). + /// + /// The reference manual notes that PLL7 should always run at 480MHz, + /// so this constant assumes that PLL7's DIV_SELECT field isn't + /// changed at runtime. + pub const FREQUENCY: u32 = 480_000_000; + + /// The smallest PLL7_PFD divider. + pub const MIN_FRAC: u8 = 12; + /// The largest PLL7_PFD divider. + pub const MAX_FRAC: u8 = 35; + + use crate::ral; + + /// Restart the USB2 PLL. + pub fn restart(ccm_analog: super::CCM_ANALOG) { + loop { + if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_USB2, ENABLE == 0) { + ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_USB2_SET, ENABLE: 1); + continue; + } + if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_USB2, POWER == 0) { + ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_USB2_SET, POWER: 1); + continue; + } + if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_USB2, LOCK == 0) { + continue; + } + if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_USB2, BYPASS == 1) { + ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_USB2_CLR, BYPASS: 1); + continue; + } + if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_USB2, EN_USB_CLKS == 0) { + ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_USB2_SET, EN_USB_CLKS: 1); + continue; + } + break; + } + } +} + +/// The ARM PLL. +/// +/// When available, this is always divided by the CCM `ARM_DIVIDER`. +pub mod pll1 { + use crate::ral; + + /// Restart PLL1 with a new divider selection. + /// + /// PLL1 should not be driving any components when + /// this restart happens. You're responsible for + /// switching over clocks. + /// + /// The implementation clamps `div_sel` between 54 and 108. + /// + /// When this function returns, PLL1 is running and stable. + pub fn restart(ccm_analog: super::CCM_ANALOG, div_sel: u32) { + // Restart PLL1. + ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_ARM, POWERDOWN: 1); + // Clears POWERDOWN bit from above. + ral::write_reg!( + ral::ccm_analog, + ccm_analog, + PLL_ARM, + DIV_SELECT: div_sel.clamp(54, 108) + ); + ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_ARM_SET, ENABLE: 1); + // Wait for lock... + while ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_ARM, LOCK == 0) {} + } + + /// Compute the PLL1 frequency (Hz) for a `DIV_SEL` value. + pub const fn frequency(div_sel: u32) -> u32 { + super::XTAL_OSCILLATOR_HZ * div_sel / 2 + } +} + +/// A fixed 500MHz ENET PLL. +pub mod pll6_500mhz { + use crate::ral; + + /// PLL6 frequency (Hz). + pub const FREQUENCY: u32 = 500_000_000; + + /// Restart PLL6. + /// + /// PLL6 should not be driving any components + /// when this restart happens. You're responsible + /// for switching over clocks. + /// + /// When this function returns, PLL6 is running and + /// stable. + pub fn restart(ccm_analog: super::CCM_ANALOG) { + // Clears BYPASS, if enabled. + ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_ENET, POWERDOWN: 1); + // Clears POWERDOWN from above. + ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_ENET, ENET_500M_REF_EN: 1); + while ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_ENET, LOCK == 0) {} + } +} + +/// The configurable ENET PLL. +pub mod pll6 { + use crate::ral::{self, ccm_analog}; + + /// Frequency selection. + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + #[repr(u32)] + pub enum Frequency { + Frequency25MHz = ccm_analog::PLL_ENET::DIV_SELECT::DIV_25MHZ, + Frequency50MHz = ccm_analog::PLL_ENET::DIV_SELECT::DIV_50MHZ, + Frequency100MHz = ccm_analog::PLL_ENET::DIV_SELECT::DIV_100MHZ, + Frequency125MHz = ccm_analog::PLL_ENET::DIV_SELECT::DIV_125MHZ, + } + + /// Restart the PLL with a new frequency. + pub fn restart(ccm_analog: ccm_analog::CCM_ANALOG, frequency: Frequency) { + ral::modify_reg!(ccm_analog, ccm_analog, PLL_ENET, BYPASS: 1, BYPASS_CLK_SRC: 0); + ral::modify_reg!(ccm_analog, ccm_analog, PLL_ENET, ENABLE: 1, POWERDOWN: 0, DIV_SELECT: frequency as u32); + while ral::read_reg!(ccm_analog, ccm_analog, PLL_ENET, LOCK == 0) {} + ral::modify_reg!(ccm_analog, ccm_analog, PLL_ENET, BYPASS: 0); + } +} diff --git a/drivers/ccm-10xx/src/lib.rs b/drivers/ccm-10xx/src/lib.rs new file mode 100644 index 0000000..ad286cf --- /dev/null +++ b/drivers/ccm-10xx/src/lib.rs @@ -0,0 +1,18 @@ +//! A clock configuration module (CCM) driver for i.MX RT 1000 MCUs. +//! +//! The APIs and modules exposed by this package may not always work on your +//! MCU! You're expected to pick and choose the components that are available. +//! If you're not sure what those are, use a chip-specific package that does +//! the filtering for you. + +#![no_std] + +pub mod ral { + pub(crate) use ral_registers::{modify_reg, read_reg, write_reg}; + pub mod ccm; + pub mod ccm_analog; +} + +pub mod ahb; +pub mod ccm; +pub mod ccm_analog; diff --git a/drivers/ccm-10xx/src/ral/ccm.rs b/drivers/ccm-10xx/src/ral/ccm.rs new file mode 100644 index 0000000..e8033ac --- /dev/null +++ b/drivers/ccm-10xx/src/ral/ccm.rs @@ -0,0 +1,85 @@ +use ral_registers::{Instance, register}; + +#[repr(C)] +#[allow(non_snake_case)] +pub struct RegisterBlock { + pub CCR: u32, + _reserved0: [u8; 0x04], + pub CSR: u32, + pub CCSR: u32, + pub CACRR: u32, + pub CBCDR: u32, + pub CBCMR: u32, + pub CSCMR1: u32, + pub CSCMR2: u32, + pub CSCDR1: u32, + pub CS1CDR: u32, + pub CS2CDR: u32, + pub CDCDR: u32, + _reserved1: [u8; 0x04], + pub CSCDR2: u32, + pub CSCDR3: u32, + _reserved2: [u8; 0x08], + pub CDHIPR: u32, + _reserved3: [u8; 0x08], + pub CLPCR: u32, + pub CISR: u32, + pub CIMR: u32, + pub CCOSR: u32, + pub CGPR: u32, + pub CCGR: [u32; 8], + pub CMEOR: u32, +} + +/// A CCM instance. +pub type CCM = Instance; + +register!(pub(crate) CACRR RW [ + ARM_PODF start(0) width(3) RW {} +]); + +register!(pub(crate) CSCMR1 RW [ + FLEXSPI_CLK_SRC start(31) width(1) RW {} + FLEXSPI_CLK_SEL start(29) width(2) RW {} + FLEXSPI_PODF start(23) width(3) RW {} + PERCLK_PODF start(0) width(6) RW {} + PERCLK_CLK_SEL start(6) width(1) RW {} +]); + +register!(pub(crate) CBCDR RW [ + PERIPH_CLK2_PODF start(27) width(3) RW {} + PERIPH_CLK_SEL start(25) width(1) RW {} + AHB_PODF start(10) width(3) RW {} + IPG_PODF start(8) width(2) RW {} +]); + +register!(pub(crate) CLPCR RW [ + LPM start(0) width(2) RW { + RUN = 0, + WAIT = 1, + STOP = 2, + } +]); + +register!(pub(crate) CSCDR1 RW [ + UART_CLK_PODF start(0) width(6) RW {} + /// Reduced to 1 bit, which is supported across + /// all chip variants. + UART_CLK_SEL start(6) width(1) RW {} +]); + +register!(pub(crate) CSCDR2 RW [ + LPI2C_CLK_PODF start(19) width(6) RW {} + LPI2C_CLK_SEL start(18) width(1) RW {} +]); + +register!(pub(crate) CBCMR RW [ + /// Four bits wide on the 1010. + LPSPI_PODF start(26) width(3) RW {} + PRE_PERIPH_CLK_SEL start(18) width(2) RW {} + PERIPH_CLK2_SEL start(12) width(2) RW {} + LPSPI_CLK_SEL start(4) width(2) RW {} +]); + +register!(pub(crate) CCGR RW []); +register!(pub(crate) CDHIPR RO []); diff --git a/drivers/ccm-10xx/src/ral/ccm_analog.rs b/drivers/ccm-10xx/src/ral/ccm_analog.rs new file mode 100644 index 0000000..958c7c1 --- /dev/null +++ b/drivers/ccm-10xx/src/ral/ccm_analog.rs @@ -0,0 +1,129 @@ +use ral_registers::{Instance, register}; + +#[repr(C)] +#[allow(non_snake_case)] +pub struct RegisterBlock { + pub PLL_ARM: u32, + pub PLL_ARM_SET: u32, + pub PLL_ARM_CLR: u32, + pub PLL_ARM_TOG: u32, + pub PLL_USB1: u32, + pub PLL_USB1_SET: u32, + pub PLL_USB1_CLR: u32, + pub PLL_USB1_TOG: u32, + pub PLL_USB2: u32, + pub PLL_USB2_SET: u32, + pub PLL_USB2_CLR: u32, + pub PLL_USB2_TOG: u32, + pub PLL_SYS: u32, + pub PLL_SYS_SET: u32, + pub PLL_SYS_CLR: u32, + pub PLL_SYS_TOG: u32, + pub PLL_SYS_SS: u32, + _reserved0: [u8; 12], + pub PLL_SYS_NUM: u32, + _reserved1: [u8; 12], + pub PLL_SYS_DENOM: u32, + _reserved2: [u8; 12], + pub PLL_AUDIO: u32, + pub PLL_AUDIO_SET: u32, + pub PLL_AUDIO_CLR: u32, + pub PLL_AUDIO_TOG: u32, + pub PLL_AUDIO_NUM: u32, + _reserved3: [u8; 12], + pub PLL_AUDIO_DENOM: u32, + _reserved4: [u8; 12], + pub PLL_VIDEO: u32, + pub PLL_VIDEO_SET: u32, + pub PLL_VIDEO_CLR: u32, + pub PLL_VIDEO_TOG: u32, + pub PLL_VIDEO_NUM: u32, + _reserved5: [u8; 12], + pub PLL_VIDEO_DENOM: u32, + _reserved6: [u8; 28], + pub PLL_ENET: u32, + pub PLL_ENET_SET: u32, + pub PLL_ENET_CLR: u32, + pub PLL_ENET_TOG: u32, + pub PFD_480: u32, + pub PFD_480_SET: u32, + pub PFD_480_CLR: u32, + pub PFD_480_TOG: u32, + pub PFD_528: u32, + pub PFD_528_SET: u32, + pub PFD_528_CLR: u32, + pub PFD_528_TOG: u32, + _reserved7: [u8; 64], + pub MISC0: u32, + pub MISC0_SET: u32, + pub MISC0_CLR: u32, + pub MISC0_TOG: u32, + pub MISC1: u32, + pub MISC1_SET: u32, + pub MISC1_CLR: u32, + pub MISC1_TOG: u32, + pub MISC2: u32, + pub MISC2_SET: u32, + pub MISC2_CLR: u32, + pub MISC2_TOG: u32, +} + +/// A CCM\_ANALOG instance. +#[allow(non_camel_case_types)] +pub type CCM_ANALOG = Instance; + +register!(pub PLL_USB1 RW [ + LOCK start(31) width(1) RW {} + BYPASS start(16) width(1) RW {} + ENABLE start(13) width(1) RW {} + POWER start(12) width(1) RW {} + EN_USB_CLKS start(6) width(1) RW {} +]); +pub use PLL_USB1 as PLL_USB1_SET; +pub use PLL_USB1 as PLL_USB1_CLR; + +pub use PLL_USB1 as PLL_USB2; +pub use PLL_USB2 as PLL_USB2_SET; +pub use PLL_USB2 as PLL_USB2_CLR; + +register!(pub PLL_ARM RW [ + LOCK start(31) width(1) RW {} + ENABLE start(13) width(1) RW {} + POWERDOWN start(12) width(1) RW {} + DIV_SELECT start(0) width(7) RW {} +]); +pub use PLL_ARM as PLL_ARM_SET; + +register!(pub PLL_ENET RW [ + LOCK start(31) width(1) RW {} + ENET_500M_REF_EN start(22) width(1) RW {} + POWERDOWN start(12) width(1) RW {} + BYPASS start(16) width(1) RW {} + BYPASS_CLK_SRC start(14) width(2) RW {} + ENABLE start(13) width(1) RW {} + DIV_SELECT start(0) width(2) RW { + DIV_25MHZ = 0, + DIV_50MHZ = 1, + DIV_100MHZ = 2, + DIV_125MHZ = 3, + } +]); + +register!(pub PLL_SYS RW [ + LOCK start(31) width(1) RW {} + BYPASS start(16) width(1) RW {} + ENABLE start(13) width(1) RW {} + POWERDOWN start(12) width(1) RW {} +]); + +pub use PLL_SYS as PLL_SYS_SET; +pub use PLL_SYS as PLL_SYS_CLR; + +register!(pub PFD_480 RW [ + PFD3_FRAC start(24) width(6) RW {} + PFD2_FRAC start(16) width(6) RW {} + PFD1_FRAC start(8) width(6) RW {} + PFD0_FRAC start(0) width(6) RW {} +]); + +pub use PFD_480 as PFD_528; -- cgit v1.2.3