use core::num::NonZero; pub use crate::ral::ccm_analog::CCM_ANALOG; pub type Instance = ral_registers::Instance; /// Frequency (Hz) of the external crystal oscillator. pub const XTAL_OSCILLATOR_HZ: u32 = 24_000_000; const fn pll_pfd_divider(pll_hz: u32, target_hz: u32) -> Option> { let div = pll_hz / target_hz * 18; // Safety: divider is between the non-zero min and max values. unsafe { if pll3::MIN_FRAC <= div && div <= pll3::MAX_FRAC { Some(NonZero::new_unchecked(div)) } else { None } } } /// The system PLL. pub mod pll2 { use core::num::NonZero; use crate::ral; /// PLL2 frequency (Hz). /// /// The reference manual notes that PLL2 should always run at 528MHz, /// so this constant assumes that PLL2's DIV_SELECT field isn't /// changed at runtime. pub const FREQUENCY: u32 = 528_000_000; /// The smallest PLL2_PFD divider. pub const MIN_FRAC: u32 = super::pll3::MIN_FRAC; /// The largest PLL2_PFD divider. pub const MAX_FRAC: u32 = super::pll3::MAX_FRAC; /// Produces a PFD divider to reach the target PFD frequency, in Hz. pub const fn pll_pfd_divider(target_hz: u32) -> Option> { super::pll_pfd_divider(FREQUENCY, target_hz) } /// Restart the system PLL. pub fn restart(ccm_analog: super::CCM_ANALOG) { loop { if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_SYS, ENABLE == 0) { ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_SYS_SET, ENABLE: 1); continue; } if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_SYS, POWERDOWN == 1) { ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_SYS_CLR, POWERDOWN: 1); continue; } if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_SYS, LOCK == 0) { continue; } if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_SYS, BYPASS == 1) { ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_SYS_CLR, BYPASS: 1); continue; } break; } } } /// The USB PLL. /// /// When an implementation has multiple USB peripherals, this /// PLL is associated with USB1. pub mod pll3 { /// PLL3 frequency (Hz). /// /// The reference manual notes that PLL3 should always run at 480MHz, /// so this constant assumes that PLL3's DIV_SELECT field isn't /// changed at runtime. pub const FREQUENCY: u32 = 480_000_000; /// The smallest PLL3_PFD divider. pub const MIN_FRAC: u32 = 12; /// The largest PLL3_PFD divider. pub const MAX_FRAC: u32 = 35; use core::num::NonZero; use crate::ral; /// Produces a PFD divider to reach the target PFD frequency, in Hz. pub const fn pll_pfd_divider(target_hz: u32) -> Option> { super::pll_pfd_divider(FREQUENCY, target_hz) } /// Restart the USB(1) PLL. pub fn restart(ccm_analog: super::CCM_ANALOG) { loop { if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_USB1, ENABLE == 0) { ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_USB1_SET, ENABLE: 1); continue; } if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_USB1, POWER == 0) { ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_USB1_SET, POWER: 1); continue; } if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_USB1, LOCK == 0) { continue; } if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_USB1, BYPASS == 1) { ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_USB1_CLR, BYPASS: 1); continue; } if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_USB1, EN_USB_CLKS == 0) { ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_USB1_SET, EN_USB_CLKS: 1); continue; } break; } } } /// The USB2 PLL, when available. pub mod pll7 { /// PLL7 frequency (Hz). /// /// The reference manual notes that PLL7 should always run at 480MHz, /// so this constant assumes that PLL7's DIV_SELECT field isn't /// changed at runtime. pub const FREQUENCY: u32 = 480_000_000; /// The smallest PLL7_PFD divider. pub const MIN_FRAC: u8 = 12; /// The largest PLL7_PFD divider. pub const MAX_FRAC: u8 = 35; use crate::ral; /// Restart the USB2 PLL. pub fn restart(ccm_analog: super::CCM_ANALOG) { loop { if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_USB2, ENABLE == 0) { ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_USB2_SET, ENABLE: 1); continue; } if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_USB2, POWER == 0) { ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_USB2_SET, POWER: 1); continue; } if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_USB2, LOCK == 0) { continue; } if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_USB2, BYPASS == 1) { ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_USB2_CLR, BYPASS: 1); continue; } if ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_USB2, EN_USB_CLKS == 0) { ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_USB2_SET, EN_USB_CLKS: 1); continue; } break; } } } /// The ARM PLL. /// /// When available, this is always divided by the CCM `ARM_DIVIDER`. pub mod pll1 { use crate::ral; /// Restart PLL1 with a new divider selection. /// /// PLL1 should not be driving any components when /// this restart happens. You're responsible for /// switching over clocks. /// /// The implementation clamps `div_sel` between 54 and 108. /// /// When this function returns, PLL1 is running and stable. pub fn restart(ccm_analog: super::CCM_ANALOG, div_sel: u32) { // Restart PLL1. ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_ARM, POWERDOWN: 1); // Clears POWERDOWN bit from above. ral::write_reg!( ral::ccm_analog, ccm_analog, PLL_ARM, DIV_SELECT: div_sel.clamp(54, 108) ); ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_ARM_SET, ENABLE: 1); // Wait for lock... while ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_ARM, LOCK == 0) {} } /// Compute the PLL1 frequency (Hz) for a `DIV_SEL` value. pub const fn frequency(div_sel: u32) -> u32 { super::XTAL_OSCILLATOR_HZ * div_sel / 2 } } /// A fixed 500MHz ENET PLL. pub mod pll6_500mhz { use crate::ral; /// PLL6 frequency (Hz). pub const FREQUENCY: u32 = 500_000_000; /// Restart PLL6. /// /// PLL6 should not be driving any components /// when this restart happens. You're responsible /// for switching over clocks. /// /// When this function returns, PLL6 is running and /// stable. pub fn restart(ccm_analog: super::CCM_ANALOG) { // Clears BYPASS, if enabled. ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_ENET, POWERDOWN: 1); // Clears POWERDOWN from above. ral::write_reg!(ral::ccm_analog, ccm_analog, PLL_ENET, ENET_500M_REF_EN: 1); while ral::read_reg!(ral::ccm_analog, ccm_analog, PLL_ENET, LOCK == 0) {} } } /// The configurable ENET PLL. pub mod pll6 { use crate::ral::{self, ccm_analog}; /// Frequency selection. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u32)] pub enum Frequency { Frequency25MHz = ccm_analog::PLL_ENET::DIV_SELECT::DIV_25MHZ, Frequency50MHz = ccm_analog::PLL_ENET::DIV_SELECT::DIV_50MHZ, Frequency100MHz = ccm_analog::PLL_ENET::DIV_SELECT::DIV_100MHZ, Frequency125MHz = ccm_analog::PLL_ENET::DIV_SELECT::DIV_125MHZ, } /// Restart the PLL with a new frequency. pub fn restart(ccm_analog: ccm_analog::CCM_ANALOG, frequency: Frequency) { ral::modify_reg!(ccm_analog, ccm_analog, PLL_ENET, BYPASS: 1, BYPASS_CLK_SRC: 0); ral::modify_reg!(ccm_analog, ccm_analog, PLL_ENET, ENABLE: 1, POWERDOWN: 0, DIV_SELECT: frequency as u32); while ral::read_reg!(ccm_analog, ccm_analog, PLL_ENET, LOCK == 0) {} ral::modify_reg!(ccm_analog, ccm_analog, PLL_ENET, BYPASS: 0); } }