//! Routines for configurating AHB clocks. use crate::{ccm, ccm_analog, ral}; /// The ARM PLL. pub mod pll1 { use super::{ccm, ccm_analog, ral}; /// A configuration for PLL1. pub struct AbhConfiguration { /// The PLL1 divider. pub div_sel: u32, /// The ARM divider, immediately after the PLL. pub arm_divider: u32, /// The AHB divider, downstream of the PLL and ARM divider. pub ahb_divider: u32, } impl AbhConfiguration { /// Produces the target AHB frequency expressed by the configuration. pub const fn ahb_frequency(&self) -> u32 { ccm_analog::pll1::frequency(self.div_sel / self.arm_divider / self.ahb_divider) } } /// Configure the AHB root using PLL1. /// /// This does not touch any of the clock gates downstream of the root clock. /// You're responsible for managing those. Note that is affects the IPG /// clock; however, it does not affect the IPG divider. pub fn configure_ahb( ccm: ral::ccm::CCM, ccm_analog: ral::ccm_analog::CCM_ANALOG, config: &AbhConfiguration, ) { if ccm::ahb_clk::Selection::PeriphClk2Sel == ccm::ahb_clk::selection(ccm) { // Switch to the pre-peripheral clock before changing // peripheral clock 2... ccm::ahb_clk::set_selection(ccm, ccm::ahb_clk::Selection::PrePeriphClkSel); } // Temporarily switch to the crystal oscillator. ccm::periph_clk2::set_divider(ccm, 1); ccm::periph_clk2::set_selection(ccm, ccm::periph_clk2::Selection::Osc); ccm::ahb_clk::set_selection(ccm, ccm::ahb_clk::Selection::PeriphClk2Sel); // Prepare PLL1. ccm_analog::pll1::restart(ccm_analog, config.div_sel); ccm::arm_divider::set_divider(ccm, config.arm_divider); ccm::ahb_clk::set_divider(ccm, config.ahb_divider); // Switch back to PLL1. ccm::pre_periph_clk_pll1::set_selection(ccm, ccm::pre_periph_clk_pll1::Selection::Pll1); ccm::ahb_clk::set_selection(ccm, ccm::ahb_clk::Selection::PrePeriphClkSel); } } /// PLL6 that's fixed at 500MHz. pub mod pll6_500mhz { use super::{ccm, ccm_analog, ral}; /// Configuration for PLL6 using a 500MHz. pub struct AbhConfiguration { /// The AHB divider, just before the root clock. pub ahb_divider: u32, } impl AbhConfiguration { /// Returns the frequency of the root clock. pub const fn ahb_frequency(&self) -> u32 { ccm_analog::pll6_500mhz::FREQUENCY / self.ahb_divider } } /// Configure the AHB root using PLL6 fixed at 500MHz. /// /// This does not touch any of the clock gates downstream of the root clock. /// You're responsible for managing those. Note that this affects the IPG /// clock; however, it does not touch the divider. pub fn configure_ahb( ccm: ral::ccm::CCM, ccm_analog: ral::ccm_analog::CCM_ANALOG, config: &AbhConfiguration, ) { if ccm::ahb_clk::Selection::PeriphClk2Sel == ccm::ahb_clk::selection(ccm) { // Switch to the pre-peripheral clock before changing // peripheral clock 2... ccm::ahb_clk::set_selection(ccm, ccm::ahb_clk::Selection::PrePeriphClkSel); } // Temporarily switch to the crystal oscillator. ccm::periph_clk2::set_selection(ccm, ccm::periph_clk2::Selection::Osc); ccm::ahb_clk::set_selection(ccm, ccm::ahb_clk::Selection::PeriphClk2Sel); // Prepare PLL6. ccm_analog::pll6_500mhz::restart(ccm_analog); ccm::ahb_clk::set_divider(ccm, config.ahb_divider); // Switch to PLL6. ccm::pre_periph_clk_pll6::set_selection(ccm, ccm::pre_periph_clk_pll6::Selection::Pll6); ccm::ahb_clk::set_selection(ccm, ccm::ahb_clk::Selection::PrePeriphClkSel); } }