diff options
Diffstat (limited to 'drivers/ccm-11xx')
| -rw-r--r-- | drivers/ccm-11xx/Cargo.toml | 7 | ||||
| -rw-r--r-- | drivers/ccm-11xx/src/lib.rs | 465 | ||||
| -rw-r--r-- | drivers/ccm-11xx/src/ral_1180/clock_root.rs | 61 | ||||
| -rw-r--r-- | drivers/ccm-11xx/src/ral_1180/lpcg.rs | 15 | ||||
| -rw-r--r-- | drivers/ccm-11xx/src/ral_1180/observe.rs | 31 | ||||
| -rw-r--r-- | drivers/ccm-11xx/src/ral_1180/osc_pll.rs | 15 | ||||
| -rw-r--r-- | drivers/ccm-11xx/src/ral_11xx/clock_group.rs | 33 | ||||
| -rw-r--r-- | drivers/ccm-11xx/src/ral_11xx/clock_root.rs | 106 | ||||
| -rw-r--r-- | drivers/ccm-11xx/src/ral_11xx/lpcg.rs | 28 | ||||
| -rw-r--r-- | drivers/ccm-11xx/src/ral_11xx/osc_pll.rs | 173 | ||||
| -rw-r--r-- | drivers/ccm-11xx/src/ral_11xx/pll.rs | 598 |
11 files changed, 1532 insertions, 0 deletions
diff --git a/drivers/ccm-11xx/Cargo.toml b/drivers/ccm-11xx/Cargo.toml new file mode 100644 index 0000000..ed0ffa7 --- /dev/null +++ b/drivers/ccm-11xx/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "imxrt-drivers-ccm-11xx" +version = "0.1.0" +edition = "2024" + +[dependencies] +ral-registers = { workspace = true } diff --git a/drivers/ccm-11xx/src/lib.rs b/drivers/ccm-11xx/src/lib.rs new file mode 100644 index 0000000..09982d8 --- /dev/null +++ b/drivers/ccm-11xx/src/lib.rs @@ -0,0 +1,465 @@ +#![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::<gpr_private::RegisterBlock>()], + /// 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<RegisterBlock>; + + /// 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<Self> { + 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<ClockMux> { + // 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<u32> 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); + } +} diff --git a/drivers/ccm-11xx/src/ral_1180/clock_root.rs b/drivers/ccm-11xx/src/ral_1180/clock_root.rs new file mode 100644 index 0000000..ebf8db9 --- /dev/null +++ b/drivers/ccm-11xx/src/ral_1180/clock_root.rs @@ -0,0 +1,61 @@ +#[repr(C)] +#[allow(non_snake_case)] +pub struct RegisterBlock { + pub CONTROL: u32, + pub CONTROL_SET: u32, + pub CONTROL_CLR: u32, + pub CONTROL_TOG: u32, + _reserved0: [u8; 16], + pub STATUS0: u32, + _reserved1: [u8; 12], + pub AUTHEN: u32, + _reserved2: [u8; 76], +} + +ral_registers::register! { + #[doc = "Clock Root Control Register"] + pub CONTROL<u32> RW [ + #[doc = "Clock division fraction."] + DIV start(0) width(8) RW {} + #[doc = "Clock multiplexer."] + MUX start(8) width(2) RW {} + #[doc = "Shutdown clock root."] + OFF start(24) width(1) RW {} + ] +} + +pub use CONTROL as CONTROL_SET; +pub use CONTROL as CONTROL_CLR; +pub use CONTROL as CONTROL_TOG; + +ral_registers::register! { + #[doc = "Clock root working status"] + pub STATUS0<u32> RO [ + #[doc = "Current clock root DIV setting"] + DIV start(0) width(8) RO {} + #[doc = "Current clock root MUX setting"] + MUX start(8) width(2) RO {} + #[doc = "Current clock root OFF setting"] + OFF start(24) width(1) RO {} + #[doc = "Internal updating in generation logic Indication for clock generation logic is applying new setting."] + SLICE_BUSY start(28) width(1) RO {} + #[doc = "Indication for clock root internal logic is updating. This status is a combination of UPDATE_FORWARD and SLICE_BUSY."] + CHANGING start(31) width(1) RO {} + ] +} + +ral_registers::register! { + #[doc = "Clock root access control"] + pub AUTHEN<u32> RW [ + #[doc = "User access permission"] + TZ_USER start(8) width(1) RW {} + #[doc = "Non-secure access permission"] + TZ_NS start(9) width(1) RW {} + #[doc = "Lock TrustZone settings"] + LOCK_TZ start(11) width(1) RW {} + #[doc = "Lock white list"] + LOCK_LIST start(15) width(1) RW {} + #[doc = "Whitelist settings"] + WHITE_LIST start(16) width(16) RW {} + ] +} diff --git a/drivers/ccm-11xx/src/ral_1180/lpcg.rs b/drivers/ccm-11xx/src/ral_1180/lpcg.rs new file mode 100644 index 0000000..21d099f --- /dev/null +++ b/drivers/ccm-11xx/src/ral_1180/lpcg.rs @@ -0,0 +1,15 @@ +#[repr(C)] +#[allow(non_snake_case)] +pub struct RegisterBlock { + pub DIRECT: u32, + _reserved0: [u8; 12], + pub LPM0: u32, + pub LPM1: u32, + _reserved1: [u8; 4], + pub LPM_CUR: u32, + pub STATUS0: u32, + pub STATUS1: u32, + _reserved2: [u8; 8], + pub AUTHEN: u32, + _reserved3: [u8; 12], +} diff --git a/drivers/ccm-11xx/src/ral_1180/observe.rs b/drivers/ccm-11xx/src/ral_1180/observe.rs new file mode 100644 index 0000000..7371acd --- /dev/null +++ b/drivers/ccm-11xx/src/ral_1180/observe.rs @@ -0,0 +1,31 @@ +#[repr(C)] +#[allow(non_snake_case)] +pub struct RegisterBlock { + pub CONTROL: u32, + pub CONTROL_SET: u32, + pub CONTROL_CLR: u32, + pub CONTROL_TOG: u32, + _reserved0: [u8; 16], + pub STATUS: u32, + _reserved1: [u8; 12], + pub AUTHEN: u32, + pub AUTHEN_SET: u32, + pub AUTHEN_CLR: u32, + pub AUTHEN_TOG: u32, + pub FREQUENCY_CURRENT: u32, + pub FREQUENCY_MIN: u32, + pub FREQUENCY_MAX: u32, + _reserved2: [u8; 4], + pub PERIOD_CURRENT: u32, + pub PERIOD_MIN: u32, + pub PERIOD_MAX: u32, + _reserved3: [u8; 4], + pub HIGH_CURRENT: u32, + pub HIGH_MIN: u32, + pub HIGH_MAX: u32, + _reserved4: [u8; 4], + pub LOW_CURRENT: u32, + pub LOW_MIN: u32, + pub LOW_MAX: u32, + _reserved5: [u8; 4], +} diff --git a/drivers/ccm-11xx/src/ral_1180/osc_pll.rs b/drivers/ccm-11xx/src/ral_1180/osc_pll.rs new file mode 100644 index 0000000..21d099f --- /dev/null +++ b/drivers/ccm-11xx/src/ral_1180/osc_pll.rs @@ -0,0 +1,15 @@ +#[repr(C)] +#[allow(non_snake_case)] +pub struct RegisterBlock { + pub DIRECT: u32, + _reserved0: [u8; 12], + pub LPM0: u32, + pub LPM1: u32, + _reserved1: [u8; 4], + pub LPM_CUR: u32, + pub STATUS0: u32, + pub STATUS1: u32, + _reserved2: [u8; 8], + pub AUTHEN: u32, + _reserved3: [u8; 12], +} diff --git a/drivers/ccm-11xx/src/ral_11xx/clock_group.rs b/drivers/ccm-11xx/src/ral_11xx/clock_group.rs new file mode 100644 index 0000000..cdef7da --- /dev/null +++ b/drivers/ccm-11xx/src/ral_11xx/clock_group.rs @@ -0,0 +1,33 @@ +pub use super::clock_root::RegisterBlock; + +ral_registers::register! { + #[doc = "Clock group control"] + pub CONTROL<u32> RW [ + #[doc = "Clock divider0"] + DIV0 start(0) width(4) RW {} + #[doc = "Clock group global restart count"] + RSTDIV start(16) width(8) RW {} + #[doc = "OFF"] + OFF start(24) width(1) RW {} + ] +} + +pub use CONTROL as CONTROL_SET; +pub use CONTROL as CONTROL_CLR; +pub use CONTROL as CONTROL_TOG; + +pub mod status0 { + pub use super::CONTROL::{DIV0, OFF, RSTDIV}; + pub use crate::STATUS0_RO::*; +} + +pub use super::clock_root::STATUS1; + +pub use super::clock_root::CONFIG; + +pub use super::clock_root::{AUTHEN, AUTHEN_CLR, AUTHEN_SET, AUTHEN_TOG}; + +#[allow(non_snake_case)] +pub mod SETPOINT { + pub use super::CONFIG::*; +} diff --git a/drivers/ccm-11xx/src/ral_11xx/clock_root.rs b/drivers/ccm-11xx/src/ral_11xx/clock_root.rs new file mode 100644 index 0000000..68c2f93 --- /dev/null +++ b/drivers/ccm-11xx/src/ral_11xx/clock_root.rs @@ -0,0 +1,106 @@ +#[repr(C)] +#[allow(non_snake_case)] +pub struct RegisterBlock { + pub CONTROL: u32, + pub CONTROL_SET: u32, + pub CONTROL_CLR: u32, + pub CONTROL_TOG: u32, + _reserved0: [u8; 16], + pub STATUS0: u32, + pub STATUS1: u32, + _reserved1: [u8; 4], + pub CONFIG: u32, + pub AUTHEN: u32, + pub AUTHEN_SET: u32, + pub AUTHEN_CLR: u32, + pub AUTHEN_TOG: u32, + pub SETPOINT: [u32; 16], +} + +ral_registers::register! { + #[doc = "Clock root control"] + pub CONTROL<u32> RW [ + #[doc = "Clock divider"] + DIV start(0) width(8) RW {} + #[doc = "Clock multiplexer"] + MUX start(8) width(3) RW {} + #[doc = "OFF"] + OFF start(24) width(1) RW {} + ] +} + +pub use CONTROL as CONTROL_SET; +pub use CONTROL as CONTROL_CLR; +pub use CONTROL as CONTROL_TOG; + +#[allow(non_snake_case)] +pub mod STATUS0 { + pub use super::CONTROL::{DIV, MUX, OFF}; + pub use crate::STATUS0_RO::*; +} + +ral_registers::register! { + #[doc = "Clock root low power status"] + pub STATUS1<u32> RO [ + #[doc = "Target Setpoint"] + TARGET_SETPOINT start(16) width(4) RO {} + #[doc = "Current Setpoint"] + CURRENT_SETPOINT start(20) width(4) RO {} + #[doc = "Clock frequency decrease request"] + DOWN_REQUEST start(24) width(1) RO {} + #[doc = "Clock frequency decrease finish"] + DOWN_DONE start(25) width(1) RO {} + #[doc = "Clock frequency increase request"] + UP_REQUEST start(26) width(1) RO {} + #[doc = "Clock frequency increase finish"] + UP_DONE start(27) width(1) RO {} + ] +} + +ral_registers::register! { + #[doc = "Clock root configuration"] + pub CONFIG<u32> RO [ + #[doc = "Setpoint present"] + SETPOINT_PRESENT start(4) width(1) RO {} + ] +} + +ral_registers::register! { + #[doc = "Clock root access control"] + pub AUTHEN<u32> RW [ + #[doc = "User access"] + TZ_USER start(0) width(1) RW {} + #[doc = "Non-secure access"] + TZ_NS start(1) width(1) RW {} + #[doc = "Lock truszone setting"] + LOCK_TZ start(4) width(1) RW {} + #[doc = "Whitelist"] + WHITE_LIST start(8) width(4) RW {} + #[doc = "Lock Whitelist"] + LOCK_LIST start(12) width(1) RW {} + #[doc = "Low power and access control by domain"] + DOMAIN_MODE start(16) width(1) RW {} + #[doc = "Low power and access control by Setpoint"] + SETPOINT_MODE start(17) width(1) RW {} + #[doc = "Lock low power and access mode"] + LOCK_MODE start(20) width(1) RW {} + ] +} + +pub use AUTHEN as AUTHEN_SET; +pub use AUTHEN as AUTHEN_CLR; +pub use AUTHEN as AUTHEN_TOG; + +ral_registers::register! { + #[doc = "Setpoint setting"] + pub SETPOINT<u32> RW [ + #[doc = "Clock divider"] + DIV start(0) width(8) RW {} + #[doc = "Clock multiplexer"] + MUX start(8) width(3) RW {} + #[doc = "OFF"] + OFF start(24) width(1) RW {} + #[doc = "Grade"] + GRADE start(28) width(4) RW {} + ] +} diff --git a/drivers/ccm-11xx/src/ral_11xx/lpcg.rs b/drivers/ccm-11xx/src/ral_11xx/lpcg.rs new file mode 100644 index 0000000..9d22646 --- /dev/null +++ b/drivers/ccm-11xx/src/ral_11xx/lpcg.rs @@ -0,0 +1,28 @@ +pub use super::osc_pll::RegisterBlock; + +pub use super::osc_pll::DIRECT; + +pub use super::osc_pll::DOMAIN; + +#[allow(non_snake_case)] +pub mod STATUS0 { + pub use super::super::osc_pll::STATUS0::{ACTIVE_DOMAIN, DOMAIN_ENABLE, ON, access}; +} + +#[allow(non_snake_case)] +pub mod STATUS1 { + pub use super::super::osc_pll::STATUS1::{ + CPU0_MODE, CPU0_MODE_DONE, CPU0_MODE_REQUEST, CPU1_MODE, CPU1_MODE_DONE, CPU1_MODE_REQUEST, + CPU2_MODE, CPU2_MODE_DONE, CPU2_MODE_REQUEST, CPU3_MODE, CPU3_MODE_DONE, CPU3_MODE_REQUEST, + CURRENT_SETPOINT, SETPOINT_OFF_DONE, SETPOINT_OFF_REQUEST, SETPOINT_ON_DONE, + SETPOINT_ON_REQUEST, TARGET_SETPOINT, access, + }; +} + +pub mod config { + pub use super::super::clock_root::CONFIG::SETPOINT_PRESENT; +} + +pub use super::osc_pll::SETPOINT; + +pub use super::osc_pll::AUTHEN; diff --git a/drivers/ccm-11xx/src/ral_11xx/osc_pll.rs b/drivers/ccm-11xx/src/ral_11xx/osc_pll.rs new file mode 100644 index 0000000..c4f10ba --- /dev/null +++ b/drivers/ccm-11xx/src/ral_11xx/osc_pll.rs @@ -0,0 +1,173 @@ +#[repr(C)] +#[allow(non_snake_case)] +pub struct RegisterBlock { + pub DIRECT: u32, + pub DOMAIN: u32, + pub SETPOINT: u32, + _reserved: [u8; 4], + pub STATUS0: u32, + pub STATUS1: u32, + pub CONFIG: u32, + pub AUTHEN: u32, +} + +ral_registers::register! { + #[doc = "Clock source direct control"] + pub DIRECT<u32> RW [ + #[doc = "turn on clock source"] + ON start(0) width(1) RW {} + ] +} + +ral_registers::register! { + #[doc = "Clock source domain control"] + pub DOMAIN<u32> RW [ + #[doc = "Current dependence level"] + LEVEL start(0) width(3) RW {} + #[doc = "Dependence level"] + LEVEL0 start(16) width(3) RW {} + #[doc = "Depend level"] + LEVEL1 start(20) width(3) RW {} + #[doc = "Depend level"] + LEVEL2 start(24) width(3) RW {} + #[doc = "Depend level"] + LEVEL3 start(28) width(3) RW {} + ] +} + +ral_registers::register! { + #[doc = "Clock source Setpoint setting"] + pub SETPOINT<u32> RW [ + #[doc = "Setpoint"] + SETPOINT start(0) width(16) RW {} + #[doc = "Standby"] + STANDBY start(16) width(16) RW {} + ] +} + +ral_registers::register! { + #[doc = "Clock source working status"] + pub STATUS0<u32> RO [ + #[doc = "Clock source current state"] + ON start(0) width(1) RO {} + #[doc = "Clock source active"] + STATUS_EARLY start(4) width(1) RO {} + #[doc = "Clock source ready"] + STATUS_LATE start(5) width(1) RO {} + #[doc = "Domains that own this clock source"] + ACTIVE_DOMAIN start(8) width(4) RO {} + #[doc = "Enable status from each domain"] + DOMAIN_ENABLE start(12) width(4) RO {} + #[doc = "In use"] + IN_USE start(28) width(1) RO {} + ] +} + +ral_registers::register! { + #[doc = "Clock source low power status"] + pub STATUS1<u32> RO [ + #[doc = "Domain0 Low Power Mode"] + CPU0_MODE start(0) width(2) RO { + #[doc = "Run"] + RUN = 0, + #[doc = "Wait"] + WAIT = 0x1, + #[doc = "Stop"] + STOP = 0x2, + #[doc = "Suspend"] + SUSPEND = 0x3, + } + #[doc = "Domain0 request enter Low Power Mode"] + CPU0_MODE_REQUEST start(2) width(1) RO {} + #[doc = "Domain0 Low Power Mode task done"] + CPU0_MODE_DONE start(3) width(1) RO {} + #[doc = "Domain1 Low Power Mode"] + CPU1_MODE start(4) width(2) RO { + #[doc = "Run"] + RUN = 0, + #[doc = "Wait"] + WAIT = 0x1, + #[doc = "Stop"] + STOP = 0x2, + #[doc = "Suspend"] + SUSPEND = 0x3, + } + #[doc = "Domain1 request enter Low Power Mode"] + CPU1_MODE_REQUEST start(6) width(1) RO {} + #[doc = "Domain1 Low Power Mode task done"] + CPU1_MODE_DONE start(7) width(1) RO {} + #[doc = "Domain2 Low Power Mode"] + CPU2_MODE start(8) width(2) RO { + #[doc = "Run"] + RUN = 0, + #[doc = "Wait"] + WAIT = 0x1, + #[doc = "Stop"] + STOP = 0x2, + #[doc = "Suspend"] + SUSPEND = 0x3, + } + #[doc = "Domain2 request enter Low Power Mode"] + CPU2_MODE_REQUEST start(10) width(1) RO {} + #[doc = "Domain2 Low Power Mode task done"] + CPU2_MODE_DONE start(11) width(1) RO {} + #[doc = "Domain3 Low Power Mode"] + CPU3_MODE start(12) width(2) RO { + #[doc = "Run"] + RUN = 0, + #[doc = "Wait"] + WAIT = 0x1, + #[doc = "Stop"] + STOP = 0x2, + #[doc = "Suspend"] + SUSPEND = 0x3, + } + #[doc = "Domain3 request enter Low Power Mode"] + CPU3_MODE_REQUEST start(14) width(1) RO {} + #[doc = "Domain3 Low Power Mode task done"] + CPU3_MODE_DONE start(15) width(1) RO {} + #[doc = "Next Setpoint to change to"] + TARGET_SETPOINT start(16) width(4) RO {} + #[doc = "Current Setpoint"] + CURRENT_SETPOINT start(20) width(4) RO {} + #[doc = "Clock gate turn off request from GPC Setpoint"] + SETPOINT_OFF_REQUEST start(24) width(1) RO {} + #[doc = "Clock source turn off finish from GPC Setpoint"] + SETPOINT_OFF_DONE start(25) width(1) RO {} + #[doc = "Clock gate turn on request from GPC Setpoint"] + SETPOINT_ON_REQUEST start(26) width(1) RO {} + #[doc = "Clock gate turn on finish from GPC Setpoint"] + SETPOINT_ON_DONE start(27) width(1) RO {} + #[doc = "Clock gate turn off request from GPC standby"] + STANDBY_IN_REQUEST start(28) width(1) RO {} + #[doc = "Clock source turn off finish from GPC standby"] + STANDBY_IN_DONE start(29) width(1) RO {} + #[doc = "Clock gate turn on finish from GPC standby"] + STANDBY_OUT_DONE start(30) width(1) RO {} + #[doc = "Clock gate turn on request from GPC standby"] + STANDBY_OUT_REQUEST start(31) width(1) RO {} + ] +} + +ral_registers::register! { + #[doc = "Clock source configuration"] + pub CONFIG<u32> RO [ + #[doc = "Automode Present"] + AUTOMODE_PRESENT start(1) width(1) RO {} + #[doc = "Setpoint present"] + SETPOINT_PRESENT start(4) width(1) RO {} + ] +} + +#[allow(non_snake_case)] +pub mod AUTHEN { + pub use super::super::clock_root::AUTHEN::*; + #[allow(non_upper_case_globals)] + pub mod CPULPM { + pub const access: ral_registers::Access = ral_registers::Access::RW; + pub const offset: u32 = 18; + pub const mask: u32 = 1 << 18; + #[doc(hidden)] + pub mod vals {} + } +} diff --git a/drivers/ccm-11xx/src/ral_11xx/pll.rs b/drivers/ccm-11xx/src/ral_11xx/pll.rs new file mode 100644 index 0000000..d5e04d4 --- /dev/null +++ b/drivers/ccm-11xx/src/ral_11xx/pll.rs @@ -0,0 +1,598 @@ +use core::num::NonZero; + +pub type Instance = ral_registers::Instance<RegisterBlock>; + +#[repr(C)] +#[allow(non_snake_case)] +pub struct RegisterBlock { + reserved_0: [u8; 512], + pub ARM_PLL_CTRL: u32, + reserved_1: [u8; 12], + pub SYS_PLL3_CTRL: u32, + reserved_2: [u8; 12], + pub SYS_PLL3_UPDATE: u32, + reserved_3: [u8; 12], + pub SYS_PLL3_PFD: u32, + reserved_4: [u8; 12], + pub SYS_PLL2_CTRL: u32, + reserved_5: [u8; 12], + pub SYS_PLL2_UPDATE: u32, + reserved_6: [u8; 12], + pub SYS_PLL2_SS: u32, + reserved_7: [u8; 12], + pub SYS_PLL2_PFD: u32, + reserved_8: [u8; 44], + pub SYS_PLL2_MFD: u32, + reserved_9: [u8; 12], + pub SYS_PLL1_SS: u32, + reserved_10: [u8; 12], + pub SYS_PLL1_CTRL: u32, + reserved_11: [u8; 12], + pub SYS_PLL1_DENOMINATOR: u32, + reserved_12: [u8; 12], + pub SYS_PLL1_NUMERATOR: u32, + reserved_13: [u8; 12], + pub SYS_PLL1_DIV_SELECT: u32, + reserved_14: [u8; 12], + pub PLL_AUDIO_CTRL: u32, + reserved_15: [u8; 12], + pub PLL_AUDIO_SS: u32, + reserved_16: [u8; 12], + pub PLL_AUDIO_DENOMINATOR: u32, + reserved_17: [u8; 12], + pub PLL_AUDIO_NUMERATOR: u32, + reserved_18: [u8; 12], + pub PLL_AUDIO_DIV_SELECT: u32, + reserved_19: [u8; 12], + pub PLL_VIDEO_CTRL: u32, + reserved_20: [u8; 12], + pub PLL_VIDEO_SS: u32, + reserved_21: [u8; 12], + pub PLL_VIDEO_DENOMINATOR: u32, + reserved_22: [u8; 12], + pub PLL_VIDEO_NUMERATOR: u32, + reserved_23: [u8; 12], + pub PLL_VIDEO_DIV_SELECT: u32, +} + +ral_registers::register! { + #[doc = "SYS_PLL1_CTRL_REGISTER"] + pub SYS_PLL1_CTRL<u32> RW [ + #[doc = "ENABLE_CLK"] + ENABLE_CLK start(13) width(1) RW {} + #[doc = "SYS_PLL1_GATE"] + SYS_PLL1_GATE start(14) width(1) RW {} + #[doc = "SYS_PLL1_DIV2"] + SYS_PLL1_DIV2 start(25) width(1) RW {} + #[doc = "SYS_PLL1_DIV5"] + SYS_PLL1_DIV5 start(26) width(1) RW {} + #[doc = "SYS_PLL1_DIV5_CONTROL_MODE"] + SYS_PLL1_DIV5_CONTROL_MODE start(27) width(1) RW { + #[doc = "Software Mode (Default)"] + SW = 0, + #[doc = "GPC Mode"] + GPC = 0x1, + } + #[doc = "SYS_PLL1_DIV2_CONTROL_MODE"] + SYS_PLL1_DIV2_CONTROL_MODE start(28) width(1) RW { + #[doc = "Software Mode (Default)"] + SW = 0, + #[doc = "GPC Mode"] + GPC = 0x1, + } + #[doc = "SYS_PLL1_STABLE"] + SYS_PLL1_STABLE start(29) width(1) RO {} + #[doc = "SYS_PLL1_AI_BUSY"] + SYS_PLL1_AI_BUSY start(30) width(1) RO {} + #[doc = "SYS_PLL1_CONTROL_MODE"] + SYS_PLL1_CONTROL_MODE start(31) width(1) RW { + #[doc = "Software Mode (Default)"] + SW = 0, + #[doc = "GPC Mode"] + GPC = 0x1, + } + ] +} + +/// Manage all SYS_PLL1 clocks using setpoints. +pub fn enable_sys_pll1_setpoints(pll: ral_registers::Instance<RegisterBlock>) { + ral_registers::modify_reg!(self, pll, SYS_PLL1_CTRL, + SYS_PLL1_CONTROL_MODE: GPC, + SYS_PLL1_DIV2_CONTROL_MODE: GPC, + SYS_PLL1_DIV5_CONTROL_MODE: GPC, + ); +} + +ral_registers::register! { + #[doc = "ARM_PLL_CTRL_REGISTER"] + pub ARM_PLL_CTRL<u32> RW [ + #[doc = "DIV_SELECT"] + DIV_SELECT start(0) width(8) RW {} + #[doc = "PLL Start up initialization"] + HOLD_RING_OFF start(12) width(1) RW {} + #[doc = "Powers up the PLL."] + POWERUP start(13) width(1) RW {} + #[doc = "Enable the clock output."] + ENABLE_CLK start(14) width(1) RW {} + #[doc = "POST_DIV_SEL"] + POST_DIV_SEL start(15) width(2) RW { + #[doc = "Divide by 2"] + DIV2 = 0, + #[doc = "Divide by 4"] + DIV4 = 0x1, + #[doc = "Divide by 8"] + DIV8 = 0x2, + #[doc = "Divide by 1"] + DIV1 = 0x3, + } + #[doc = "Bypass the pll."] + BYPASS start(17) width(1) RW {} + #[doc = "ARM_PLL_STABLE"] + ARM_PLL_STABLE start(29) width(1) RO {} + #[doc = "ARM_PLL_GATE"] + ARM_PLL_GATE start(30) width(1) RW {} + #[doc = "pll_arm_control_mode"] + ARM_PLL_CONTROL_MODE start(31) width(1) RW { + #[doc = "Software Mode (Default)"] + SW = 0, + #[doc = "GPC Mode"] + GPC = 0x1, + } + ] +} + +/// Post divider for the ARM PLL. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum ArmPllPostDiv { + /// Divide by 2. + Div2 = 0, + /// Divide by 4. + Div4 = 1, + /// Divide by 8. + Div8 = 2, + /// Divide by 1. + Div1 = 3, +} + +impl ArmPllPostDiv { + /// Return the divisor for this post divider. + #[must_use] + pub const fn divisor(self) -> u32 { + match self { + Self::Div2 => 2, + Self::Div4 => 4, + Self::Div8 => 8, + Self::Div1 => 1, + } + } +} + +/// The ARM PLL loop divider. +/// +/// This value is always clamped between 104 and 208. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(transparent)] +pub struct ArmPllDivSelect(NonZero<u32>); + +impl ArmPllDivSelect { + /// Construct a loop divider. + /// + /// The implementation clamps the value between 104 and 208. + #[must_use] + pub const fn new(div_select: u32) -> Option<Self> { + if 104 <= div_select && div_select <= 208 { + Some(Self(NonZero::new(div_select).unwrap())) + } else { + None + } + } + /// Returns the divider as a raw value. + #[must_use] + #[inline] + pub const fn get(self) -> u32 { + self.0.get() + } +} + +/// Manage the ARM PLL with setpoints. +/// +/// `post_div` is the post divider selection applied when the +/// PLL is enabled. Similarly `div_select` is the loop divider. +/// Use [`arm_pll_frequency`] to understand the expected frequency +/// affected by this call. +pub fn enable_arm_pll_setpoints( + pll: ral_registers::Instance<RegisterBlock>, + post_div: ArmPllPostDiv, + div_select: ArmPllDivSelect, +) { + ral_registers::modify_reg!(self, pll, ARM_PLL_CTRL, + DIV_SELECT: div_select.get(), + POST_DIV_SEL: post_div as u32, + ARM_PLL_GATE: 1, + ARM_PLL_CONTROL_MODE: GPC, + ENABLE_CLK: 0, + ); +} + +/// Computes the expected ARM PLL frequency. +pub const fn arm_pll_frequency(post_div: ArmPllPostDiv, div_select: ArmPllDivSelect) -> u32 { + 24_000_000 / 2 / post_div.divisor() * div_select.get() +} + +ral_registers::register! { + #[doc = "SYS_PLL2_CTRL_REGISTER"] + pub SYS_PLL2_CTRL<u32> RW [ + #[doc = "Enable Internal PLL Regulator"] + PLL_REG_EN start(3) width(1) RW {} + #[doc = "PLL Start up initialization"] + HOLD_RING_OFF start(11) width(1) RW {} + #[doc = "Enable the clock output."] + ENABLE_CLK start(13) width(1) RW {} + #[doc = "Bypass the pll."] + BYPASS start(16) width(1) RW {} + #[doc = "DITHER_ENABLE"] + DITHER_ENABLE start(17) width(1) RW {} + #[doc = "PFD_OFFSET_EN"] + PFD_OFFSET_EN start(18) width(1) RW {} + #[doc = "PLL_DDR_OVERRIDE"] + PLL_DDR_OVERRIDE start(19) width(1) RW {} + #[doc = "Powers up the PLL."] + POWERUP start(23) width(1) RW {} + #[doc = "SYS_PLL2_STABLE"] + SYS_PLL2_STABLE start(29) width(1) RO {} + #[doc = "SYS_PLL2_GATE"] + SYS_PLL2_GATE start(30) width(1) RW {} + #[doc = "SYS_PLL2_control_mode"] + SYS_PLL2_CONTROL_MODE start(31) width(1) RW { + #[doc = "Software Mode (Default)"] + SW = 0, + #[doc = "GPC Mode"] + GPC = 0x1, + } + ] +} + +ral_registers::register! { + #[doc = "SYS_PLL2_UPDATE_REGISTER"] + pub SYS_PLL2_UPDATE<u32> RW [ + #[doc = "PFD0_UPDATE"] + PFD0_UPDATE start(1) width(1) RW {} + #[doc = "PFD1_UPDATE"] + PFD1_UPDATE start(2) width(1) RW {} + #[doc = "PFD2_UPDATE"] + PFD2_UPDATE start(3) width(1) RW {} + #[doc = "PFD3_UPDATE"] + PFD3_UPDATE start(4) width(1) RW {} + #[doc = "pfd0_control_mode"] + PFD0_CONTROL_MODE start(5) width(1) RW { + #[doc = "Software Mode (Default)"] + SW = 0, + #[doc = "GPC Mode"] + GPC = 0x1, + } + #[doc = "pfd1_control_mode"] + PFD1_CONTROL_MODE start(6) width(1) RW { + #[doc = "Software Mode (Default)"] + SW = 0, + #[doc = "GPC Mode"] + GPC = 0x1, + } + #[doc = "pfd2_control_mode"] + PFD2_CONTROL_MODE start(7) width(1) RW { + #[doc = "Software Mode (Default)"] + SW = 0, + #[doc = "GPC Mode"] + GPC = 0x1, + } + #[doc = "pfd3_control_mode"] + PFD3_CONTROL_MODE start(8) width(1) RW { + #[doc = "Software Mode (Default)"] + SW = 0, + #[doc = "GPC Mode"] + GPC = 0x1, + } + ] +} + +/// Manage SYS_PLL2 clocks and PFDs using setpoints. +pub fn enable_sys_pll2_setpoints(pll: ral_registers::Instance<RegisterBlock>) { + ral_registers::modify_reg!(self, pll, SYS_PLL2_UPDATE, + PFD3_CONTROL_MODE: GPC, + PFD2_CONTROL_MODE: GPC, + PFD1_CONTROL_MODE: GPC, + PFD0_CONTROL_MODE: GPC, + ); + ral_registers::modify_reg!(self, pll, SYS_PLL2_CTRL, + SYS_PLL2_CONTROL_MODE: GPC, + SYS_PLL2_GATE: 1, + ENABLE_CLK: 0, + ); +} + +ral_registers::register! { + #[doc = "SYS_PLL3_CTRL_REGISTER"] + pub SYS_PLL3_CTRL<u32> RW [ + #[doc = "SYS PLL3 DIV2 gate"] + SYS_PLL3_DIV2 start(3) width(1) RW {} + #[doc = "Enable Internal PLL Regulator"] + PLL_REG_EN start(4) width(1) RW {} + #[doc = "PLL Start up initialization"] + HOLD_RING_OFF start(11) width(1) RW {} + #[doc = "Enable the clock output."] + ENABLE_CLK start(13) width(1) RW {} + #[doc = "BYPASS"] + BYPASS start(16) width(1) RW {} + #[doc = "Powers up the PLL."] + POWERUP start(21) width(1) RW {} + #[doc = "SYS_PLL3_DIV2_CONTROL_MODE"] + SYS_PLL3_DIV2_CONTROL_MODE start(28) width(1) RW { + #[doc = "Software Mode (Default)"] + SW = 0, + #[doc = "GPC Mode"] + GPC = 0x1, + } + #[doc = "SYS_PLL3_STABLE"] + SYS_PLL3_STABLE start(29) width(1) RO {} + #[doc = "SYS_PLL3_GATE"] + SYS_PLL3_GATE start(30) width(1) RW {} + #[doc = "SYS_PLL3_control_mode"] + SYS_PLL3_CONTROL_MODE start(31) width(1) RW { + #[doc = "Software Mode (Default)"] + SW = 0, + #[doc = "GPC Mode"] + GPC = 0x1, + } + ] +} + +ral_registers::register! { + #[doc = "SYS_PLL3_UPDATE_REGISTER"] + pub SYS_PLL3_UPDATE<u32> RW [ + #[doc = "PFD0_OVERRIDE"] + PFD0_UPDATE start(1) width(1) RW {} + #[doc = "PFD1_OVERRIDE"] + PFD1_UPDATE start(2) width(1) RW {} + #[doc = "PFD2_OVERRIDE"] + PFD2_UPDATE start(3) width(1) RW {} + #[doc = "PFD3_UPDATE"] + PFD3_UPDATE start(4) width(1) RW {} + #[doc = "pfd0_control_mode"] + PFD0_CONTROL_MODE start(5) width(1) RW { + #[doc = "Software Mode (Default)"] + SW = 0, + #[doc = "GPC Mode"] + GPC = 0x1, + } + #[doc = "pfd1_control_mode"] + PFD1_CONTROL_MODE start(6) width(1) RW { + #[doc = "Software Mode (Default)"] + SW = 0, + #[doc = "GPC Mode"] + GPC = 0x1, + } + #[doc = "pdf2_control_mode"] + PFD2_CONTROL_MODE start(7) width(1) RW { + #[doc = "Software Mode (Default)"] + SW = 0, + #[doc = "GPC Mode"] + GPC = 0x1, + } + #[doc = "pfd3_control_mode"] + PFD3_CONTROL_MODE start(8) width(1) RW { + #[doc = "Software Mode (Default)"] + SW = 0, + #[doc = "GPC Mode"] + GPC = 0x1, + } + ] +} + +/// Manage SYS_PLL3 clocks and PFDs using setpoints. +pub fn enable_sys_pll3_setpoints(pll: ral_registers::Instance<RegisterBlock>) { + ral_registers::modify_reg!(self, pll, SYS_PLL3_UPDATE, + PFD3_CONTROL_MODE: GPC, + PFD2_CONTROL_MODE: GPC, + PFD1_CONTROL_MODE: GPC, + PFD0_CONTROL_MODE: GPC, + ); + ral_registers::modify_reg!(self, pll, SYS_PLL3_CTRL, + SYS_PLL3_CONTROL_MODE: GPC, + SYS_PLL3_DIV2_CONTROL_MODE: GPC, + SYS_PLL3_GATE: 1, + ENABLE_CLK: 0, + ); +} + +ral_registers::register! { + #[doc = "SYS_PLL3_PFD_REGISTER"] + pub SYS_PLL3_PFD<u32> RW [ + #[doc = "PFD0_FRAC"] + PFD0_FRAC start(0) width(6) RW {} + #[doc = "PFD0_STABLE"] + PFD0_STABLE start(6) width(1) RO {} + #[doc = "PFD0_DIV1_CLKGATE"] + PFD0_DIV1_CLKGATE start(7) width(1) RW {} + #[doc = "PFD1_FRAC"] + PFD1_FRAC start(8) width(6) RW {} + #[doc = "PFD1_STABLE"] + PFD1_STABLE start(14) width(1) RO {} + #[doc = "PFD1_DIV1_CLKGATE"] + PFD1_DIV1_CLKGATE start(15) width(1) RW {} + #[doc = "PFD2_FRAC"] + PFD2_FRAC start(16) width(6) RW {} + #[doc = "PFD2_STABLE"] + PFD2_STABLE start(22) width(1) RO {} + #[doc = "PFD2_DIV1_CLKGATE"] + PFD2_DIV1_CLKGATE start(23) width(1) RW {} + #[doc = "PFD3_FRAC"] + PFD3_FRAC start(24) width(6) RW {} + #[doc = "PFD3_STABLE"] + PFD3_STABLE start(30) width(1) RO {} + #[doc = "PFD3_DIV1_CLKGATE"] + PFD3_DIV1_CLKGATE start(31) width(1) RW {} + ] +} + +ral_registers::register! { + #[doc = "SYS_PLL2_PFD_REGISTER"] + pub SYS_PLL2_PFD<u32> RW [ + #[doc = "PFD0_FRAC"] + PFD0_FRAC start(0) width(6) RW {} + #[doc = "PFD0_STABLE"] + PFD0_STABLE start(6) width(1) RO {} + #[doc = "PFD0_DIV1_CLKGATE"] + PFD0_DIV1_CLKGATE start(7) width(1) RW {} + #[doc = "PFD1_FRAC"] + PFD1_FRAC start(8) width(6) RW {} + #[doc = "PFD1_STABLE"] + PFD1_STABLE start(14) width(1) RO {} + #[doc = "PFD1_DIV1_CLKGATE"] + PFD1_DIV1_CLKGATE start(15) width(1) RW {} + #[doc = "PFD2_FRAC"] + PFD2_FRAC start(16) width(6) RW {} + #[doc = "PFD2_STABLE"] + PFD2_STABLE start(22) width(1) RO {} + #[doc = "PFD2_DIV1_CLKGATE"] + PFD2_DIV1_CLKGATE start(23) width(1) RW {} + #[doc = "PFD3_FRAC"] + PFD3_FRAC start(24) width(6) RW {} + #[doc = "PFD3_STABLE"] + PFD3_STABLE start(30) width(1) RO {} + #[doc = "PFD3_DIV1_CLKGATE"] + PFD3_DIV1_CLKGATE start(31) width(1) RW {} + ] +} + +/// A fractional divider for a PFD. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(transparent)] +pub struct PfdFrac(NonZero<u8>); + +impl PfdFrac { + /// Create a new PDF fractional divider. + /// + /// Returns `None` if `div` is not between 12 and 35. + pub const fn new(div: u8) -> Option<Self> { + // Safety: div is between 12 and 35. + unsafe { + if 12 <= div && div <= 35 { + Some(Self(NonZero::new_unchecked(div))) + } else { + None + } + } + } + + /// Similar to `new`, but this will automatically clamp + /// the value between the 12 and 35. + pub const fn clamp(div: u8) -> Self { + // Safety: div is clamped between 12 and 35. + unsafe { + Self(NonZero::new_unchecked(if div < 12 { + 12 + } else if div > 35 { + 35 + } else { + div + })) + } + } + + /// Return the inner value. + #[inline] + pub const fn get(self) -> u8 { + self.0.get() + } +} + +/// Returns the PLL3 PFD fractional dividers. +/// +/// If a divider is invalid, the implementation returns `None` +/// for that divider. +pub fn pll3_pfd_fracs(pll: ral_registers::Instance<RegisterBlock>) -> [Option<PfdFrac>; 4] { + let (pfd0, pfd1, pfd2, pfd3) = ral_registers::read_reg!( + self, + pll, + SYS_PLL3_PFD, + PFD0_FRAC, + PFD1_FRAC, + PFD2_FRAC, + PFD3_FRAC + ); + + [ + PfdFrac::new(pfd0 as _), + PfdFrac::new(pfd1 as _), + PfdFrac::new(pfd2 as _), + PfdFrac::new(pfd3 as _), + ] +} + +/// Set the PLL3 PFD fractional dividers. +/// +/// The call updates all four dividers without affecting the gating. +/// However, it does not update the values. +pub fn set_pll3_pfd_fracs(pll: ral_registers::Instance<RegisterBlock>, pfds: [PfdFrac; 4]) { + ral_registers::modify_reg!(self, pll, SYS_PLL3_PFD, + PFD3_FRAC: pfds[3].get() as u32, + PFD2_FRAC: pfds[2].get() as u32, + PFD1_FRAC: pfds[1].get() as u32, + PFD0_FRAC: pfds[0].get() as u32, + ); +} + +/// Update the PFD fractional divider values that were previously +/// set. +/// +/// Only those that have a `true` update are updated. +pub fn update_pll3_pfd_fracs(pll: ral_registers::Instance<RegisterBlock>, update: [bool; 4]) { + ral_registers::modify_reg!(self, pll, SYS_PLL3_UPDATE, + PFD3_UPDATE: update[3] as u32, + PFD2_UPDATE: update[2] as u32, + PFD1_UPDATE: update[1] as u32, + PFD0_UPDATE: update[0] as u32, + ); + ral_registers::modify_reg!(self, pll, SYS_PLL3_UPDATE, + PFD3_UPDATE: false as u32, + PFD2_UPDATE: false as u32, + PFD1_UPDATE: false as u32, + PFD0_UPDATE: false as u32, + ); +} + +/// Returns the PLL2 PFD fractional dividers. +/// +/// If a divider is invalid, the implementation returns `None` +/// for that divider. +pub fn pll2_pfd_fracs(pll: ral_registers::Instance<RegisterBlock>) -> [Option<PfdFrac>; 4] { + let (pfd0, pfd1, pfd2, pfd3) = ral_registers::read_reg!( + self, + pll, + SYS_PLL2_PFD, + PFD0_FRAC, + PFD1_FRAC, + PFD2_FRAC, + PFD3_FRAC + ); + + [ + PfdFrac::new(pfd0 as _), + PfdFrac::new(pfd1 as _), + PfdFrac::new(pfd2 as _), + PfdFrac::new(pfd3 as _), + ] +} + +/// Set the PLL2 PFD fractional dividers. +/// +/// The call updates all four dividers without affecting the gating. +/// However, it does not update the values. +pub fn set_pll2_pfd_fracs(pll: ral_registers::Instance<RegisterBlock>, pfds: [PfdFrac; 4]) { + ral_registers::modify_reg!(self, pll, SYS_PLL2_PFD, + PFD3_FRAC: pfds[3].get() as u32, + PFD2_FRAC: pfds[2].get() as u32, + PFD1_FRAC: pfds[1].get() as u32, + PFD0_FRAC: pfds[0].get() as u32, + ); +} |
