aboutsummaryrefslogtreecommitdiff
path: root/drivers/ccm-11xx
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/ccm-11xx')
-rw-r--r--drivers/ccm-11xx/Cargo.toml7
-rw-r--r--drivers/ccm-11xx/src/lib.rs465
-rw-r--r--drivers/ccm-11xx/src/ral_1180/clock_root.rs61
-rw-r--r--drivers/ccm-11xx/src/ral_1180/lpcg.rs15
-rw-r--r--drivers/ccm-11xx/src/ral_1180/observe.rs31
-rw-r--r--drivers/ccm-11xx/src/ral_1180/osc_pll.rs15
-rw-r--r--drivers/ccm-11xx/src/ral_11xx/clock_group.rs33
-rw-r--r--drivers/ccm-11xx/src/ral_11xx/clock_root.rs106
-rw-r--r--drivers/ccm-11xx/src/ral_11xx/lpcg.rs28
-rw-r--r--drivers/ccm-11xx/src/ral_11xx/osc_pll.rs173
-rw-r--r--drivers/ccm-11xx/src/ral_11xx/pll.rs598
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,
+ );
+}