#![no_std] mod ral_common { pub mod gpr_shared { #[repr(C)] #[allow(non_snake_case)] pub struct RegisterBlock { pub REG: u32, pub SET: u32, pub CLR: u32, pub TOG: u32, pub AUTHEN: u32, pub AUTHEN_SET: u32, pub AUTHEN_CLR: u32, pub AUTHEN_TOG: u32, } } pub mod gpr_private { pub use super::gpr_shared::RegisterBlock; } } pub mod ral_11xx { pub mod clock_group; pub mod clock_root; pub mod lpcg; pub mod osc_pll; pub mod pll; use core::num::{NonZeroU8, NonZeroU32}; pub use crate::ral_common::{gpr_private, gpr_shared}; pub use self::{ clock_group as CLOCK_GROUP, clock_root as CLOCK_ROOT, gpr_private as GPR_PRIVATE, gpr_shared as GPR_SHARED, lpcg as LPCG, osc_pll as OSC_PLL, }; #[repr(C)] #[allow(non_snake_case)] pub struct RegisterBlock { pub CLOCK_ROOT: [clock_root::RegisterBlock; 79], _reserved0: [u8; 6272], pub CLOCK_GROUP: [clock_group::RegisterBlock; 2], _reserved1: [u8; 1792], pub GPR_SHARED: [gpr_shared::RegisterBlock; 8], _reserved2: [u8; 800 - size_of::()], /// First register block is typically reserved. To avoid this /// reserved block, make sure your index corresponds to the /// register names. pub GPR_PRIVATE: [gpr_private::RegisterBlock; 8], _reserved3: [u8; 768], pub OSC_PLL: [osc_pll::RegisterBlock; 29], _reserved4: [u8; 3168], pub LPCG: [lpcg::RegisterBlock; 138], } pub type Instance = ral_registers::Instance; /// A clock source. /// /// These typically map to an `OSCPLL`. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u32)] #[non_exhaustive] pub enum ClockSource { /// The 16MHz RC oscillator. Rc16M = 0, /// The 48MHz RC oscillator. Rc48M = 1, /// The 48MHz RC oscillator with a fixed divide-by-2. Rc48MDiv2 = 2, /// The 400MHz RC oscillator. Rc400M = 3, /// The external 24MHz crystal oscillator VCO. /// /// This output isn't exposed by the clock tree. /// However, it's exposed via [`XtalClk`]. Xtal = 4, /// The exposed 24MHz crystal oscillator. XtalClk = 5, /// ARM PLL VCO output. /// /// Not connected to the clock tree. ArmPll = 6, /// The ARM PLL output in the clock tree. ArmPllClk = 7, /// Ths voltage controlled oscillator for PLL2. /// /// This output isn't exposed by the clock tree. /// However, the [`Pll2Clk`] is exposed. Pll2 = 8, /// The PLL2 output into the clock tree. Pll2Clk = 9, /// PFD0 of PLL2. Pll2Pfd0 = 10, /// PFD1 of PLL2. Pll2Pfd1 = 11, /// PFD2 of PLL2. Pll2Pfd2 = 12, /// PFD3 of PLL2. Pll2Pfd3 = 13, /// Ths voltage controlled oscillator for PLL3. /// /// This output isn't exposed by the clock tree. /// However, the [`Pll3Clk`] is exposed. Pll3 = 14, /// The PLL3 output into the clock tree. Pll3Clk = 15, /// PLL3 with a fixed divide-by-2. Pll3Div2 = 16, /// PFD0 of PLL3. Pll3Pfd0 = 17, /// PFD1 of PLL3. Pll3Pfd1 = 18, /// PFD2 of PLL3. Pll3Pfd2 = 19, /// PFD3 of PLL3. Pll3Pfd3 = 20, /// The voltage controlled oscillator for PLL1. /// /// This output isn't exposed by the clock tree. /// However, the [`Pll1Clk`] is exposed. Pll1 = 21, /// The PLL1 output into the clock tree. Pll1Clk = 22, /// PLL1 with a fixed divide-by-2. Pll1Div2 = 23, /// PLL1 with a fixed divide-by-5. Pll1Div5 = 24, } /// A clock root. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u32)] #[non_exhaustive] pub enum ClockRoot { /// Cortex-M7. M7 = 0, /// `BUS_CLK`. Bus = 2, /// `BUS_LPSR_CLK`. BusLpsr = 3, /// SYSTICK for the Cortex-M7. M7Systick = 8, /// FlexSPI1. Flexspi1 = 20, /// FlexSPI2. Flexspi2 = 21, /// LPSPI1. Lpspi1 = 43, /// ENET. Enet = 51, /// ENET_1G. Enet1G = 52, /// ENET QOS on supported MCUs. EnetQos = 53, /// ENET_25M clock. Enet25M = 54, /// ENET PPT. EnetTimer1 = 55, /// ENET_1G PPT. EnetTimer2 = 56, /// ENET QOS PPT on supported MCUs. EnetTimer3 = 57, } /// A clock mux value. /// /// Use [`get`](Self::get) to acquire the raw value that /// can be committed to the hardware. #[derive(Clone, Copy, PartialEq, Eq)] #[repr(transparent)] pub struct ClockMux(NonZeroU32); impl ClockMux { const fn new(raw: u32) -> Option { match NonZeroU32::new(raw.saturating_add(1)) { Some(mux) => Some(ClockMux(mux)), None => None, } } /// Returns the raw mux value that's valid for /// the hardware. pub const fn get(self) -> u32 { self.0.get() - 1 } } /// Produces the MUX selection for this clock root, as long /// as the source can be multiplexed to this root. /// /// Returns `None` if the source isn't available for this /// root. It may also return `None` if the mapping isn't implemented. /// /// The implementation may alias names for convenience. /// For example, `Xtal` and `XtalClk` alias to the same mux /// value, although `Xtal` isn't connected to the clock tree. /// /// The implementation may return roots that are marked "reserved" /// on your chip. For example, some M7 clock sources are reserved on /// the 1160 but implemented on the 1170. This call will return those /// reserved muxes for the 1170, and it's the user's job to know that /// the root is invalid on the 1160. pub const fn try_mux(root: ClockRoot, source: ClockSource) -> Option { // These sources are always available no matter the // user's selected root. match source { ClockSource::Rc48MDiv2 => return ClockMux::new(0b000), ClockSource::Xtal | ClockSource::XtalClk => return ClockMux::new(0b001), ClockSource::Rc400M => return ClockMux::new(0b010), ClockSource::Rc16M => return ClockMux::new(0b011), _ => {} } // The remaining sources are specific to the root. match root { ClockRoot::Enet | ClockRoot::Enet1G | ClockRoot::EnetQos | ClockRoot::Enet25M | ClockRoot::EnetTimer1 | ClockRoot::EnetTimer2 | ClockRoot::EnetTimer3 => match source { ClockSource::Pll1Div2 => ClockMux::new(0b100), ClockSource::Pll1Div5 => ClockMux::new(0b110), _ => None, }, ClockRoot::M7 => match source { ClockSource::ArmPll | ClockSource::ArmPllClk => ClockMux::new(0b100), _ => None, }, ClockRoot::Bus => match source { ClockSource::Pll1Div5 => ClockMux::new(0b101), _ => None, }, ClockRoot::BusLpsr => match source { ClockSource::Pll3Pfd3 => ClockMux::new(0b100), ClockSource::Pll3 | ClockSource::Pll3Clk => ClockMux::new(0b101), ClockSource::Pll2 | ClockSource::Pll2Clk => ClockMux::new(0b110), ClockSource::Pll1Div5 => ClockMux::new(0b111), _ => None, }, ClockRoot::M7Systick => match source { ClockSource::Pll1Div5 => ClockMux::new(0b110), _ => None, }, ClockRoot::Lpspi1 => match source { _ => None, }, ClockRoot::Flexspi1 | ClockRoot::Flexspi2 => match source { ClockSource::Pll3Pfd0 => ClockMux::new(0b100), ClockSource::Pll2 | ClockSource::Pll2Clk => ClockMux::new(0b101), ClockSource::Pll2Pfd2 => ClockMux::new(0b110), ClockSource::Pll3 | ClockSource::Pll3Clk => ClockMux::new(0b111), _ => None, }, } } /// Returns the MUX selection for the given clock root for the /// source. /// /// See [`try_mux`] for more information. /// /// # Panics /// /// Panics if the clock root cannot be muxed to the given source. pub const fn mux(root: ClockRoot, source: ClockSource) -> ClockMux { let Some(mux) = try_mux(root, source) else { panic!("Root clock cannot be driven by the source"); }; mux } /// Returns `true` if this clock source supports setpoints. /// /// If the source supports setpoints, it can be controlled by /// the GPC. pub fn source_has_setpoint(ccm: Instance, clock_source: ClockSource) -> bool { ral_registers::read_reg!( self, ccm, OSC_PLL[clock_source as usize].CONFIG, SETPOINT_PRESENT == 1 ) } /// Returns `true` if this clock root supports setpoints. /// /// If the root supports setpoints, it can be controlled by /// the GPC. pub fn root_has_setpoint(ccm: Instance, clock_root: ClockRoot) -> bool { ral_registers::read_reg!( self, ccm, CLOCK_ROOT[clock_root as usize].CONFIG, SETPOINT_PRESENT == 1 ) } /// The clock source is invalid for the given operation. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(transparent)] pub struct InvalidSourceError(pub ClockSource); /// The clock root is invalid for the given operation. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(transparent)] pub struct InvalidRootError(pub ClockRoot); /// Enable setpoint mode for this clock source. /// /// This implicitly disables CPULP and domain modes; the source /// cannot operate in different modes. It also disables the source /// clock in the `DIRECT` register. pub fn enable_source_setpoints( ccm: Instance, clock_source: ClockSource, ) -> Result<(), InvalidSourceError> { if !source_has_setpoint(ccm, clock_source) { return Err(InvalidSourceError(clock_source)); } set_source_direct(ccm, clock_source, false); ral_registers::modify_reg!(self, ccm, OSC_PLL[clock_source as usize].AUTHEN, CPULPM: 0, SETPOINT_MODE: 1, DOMAIN_MODE: 0, ); Ok(()) } /// Enables setpoint mode for this clock root. /// /// This implicitly disables domain mode; the root /// cannot operate in different modes. pub fn enable_root_setpoints( ccm: Instance, clock_root: ClockRoot, ) -> Result<(), InvalidRootError> { if !root_has_setpoint(ccm, clock_root) { return Err(InvalidRootError(clock_root)); } ral_registers::modify_reg!(self, ccm, CLOCK_ROOT[clock_root as usize].AUTHEN, SETPOINT_MODE: 1, DOMAIN_MODE: 0, ); Ok(()) } /// Specify which setpoints enable this clock source. /// /// `setpoints` describes the bitmask of enabled setpoints. /// `standby` describes if the source remains enabled during a standby /// transition in that setpoint. pub fn set_source_setpoints( ccm: Instance, clock_source: ClockSource, setpoints: u16, standby: u16, ) { ral_registers::write_reg!(self, ccm, OSC_PLL[clock_source as usize].SETPOINT, SETPOINT: setpoints as u32, STANDBY: standby as u32); } /// Set the clock root's selection and divider. /// /// Spins while the change is in progress. pub fn set_clock_root(ccm: Instance, clock_root: ClockRoot, mux: ClockMux, div: NonZeroU8) { ral_registers::modify_reg!(self, ccm, CLOCK_ROOT[clock_root as usize].CONTROL, MUX: mux.get(), DIV: (div.get() as u32) - 1); while ral_registers::read_reg!( self, ccm, CLOCK_ROOT[clock_root as usize].STATUS0, CHANGING == 1 ) {} } /// Directly set the clock source on or off. pub fn set_source_direct(ccm: Instance, clock_source: ClockSource, on: bool) { ral_registers::write_reg!(self, ccm, OSC_PLL[clock_source as usize].DIRECT, on as u32); } } pub mod ral_1180 { pub use crate::ral_common::{gpr_private, gpr_shared}; pub mod clock_root; pub mod lpcg; pub mod observe; pub mod osc_pll; pub use self::{ clock_root as CLOCK_ROOT, gpr_private as GPR_PRIVATE, gpr_shared as GPR_SHARED, lpcg as LPCG, observe as OBSERVE, osc_pll as OSC_PLL, }; #[repr(C)] #[allow(non_snake_case)] pub struct RegisterBlock { pub CLOCK_ROOT: [clock_root::RegisterBlock; 74], _reserved0: [u8; 7936], pub OBSERVE: [observe::RegisterBlock; 2], _reserved1: [u8; 768], pub GPR_SHARED: [gpr_shared::RegisterBlock; 16], pub GPR_SHARED_STATUS: [u32; 8], _reserved2: [u8; 480], pub GPR_PRIVATE: [gpr_private::RegisterBlock; 4], _reserved3: [u8; 896], pub OSC_PLL: [osc_pll::RegisterBlock; 25], _reserved4: [u8; 10688], pub LPCG: [lpcg::RegisterBlock; 149], } } ral_registers::register! { #[doc = "Clock root working status"] STATUS0_RO RO [ #[doc = "Current clock root POWERDOWN setting"] POWERDOWN start(27) width(1) RO {} #[doc = "Internal updating in generation logic"] SLICE_BUSY start(28) width(1) RO {} #[doc = "Internal status synchronization to clock generation logic"] UPDATE_FORWARD start(29) width(1) RO {} #[doc = "Internal status synchronization from clock generation logic"] UPDATE_REVERSE start(30) width(1) RO {} #[doc = "Internal updating in clock root"] CHANGING start(31) width(1) RO {} ] } #[cfg(test)] mod tests { use core::mem::offset_of; #[test] fn layout_1180() { use super::ral_1180::RegisterBlock; assert_eq!(offset_of!(RegisterBlock, CLOCK_ROOT), 0); assert_eq!(offset_of!(RegisterBlock, OBSERVE), 0x4400); assert_eq!(offset_of!(RegisterBlock, GPR_SHARED), 0x4800); assert_eq!(offset_of!(RegisterBlock, GPR_SHARED_STATUS), 0x4a00); assert_eq!(offset_of!(RegisterBlock, GPR_PRIVATE), 0x4C00); assert_eq!(offset_of!(RegisterBlock, OSC_PLL), 0x5000,); assert_eq!(offset_of!(RegisterBlock, LPCG), 0x8000); } #[test] fn layout_11xx() { use super::ral_11xx::RegisterBlock; assert_eq!(offset_of!(RegisterBlock, CLOCK_ROOT), 0); assert_eq!(offset_of!(RegisterBlock, CLOCK_GROUP), 0x4000); assert_eq!(offset_of!(RegisterBlock, GPR_SHARED), 0x4800); assert_eq!(offset_of!(RegisterBlock, GPR_PRIVATE), 0x4c00); assert_eq!(offset_of!(RegisterBlock, OSC_PLL), 0x5000); assert_eq!(offset_of!(RegisterBlock, LPCG), 0x6000); } }