diff options
Diffstat (limited to 'drivers/ccm-10xx/src/ahb.rs')
| -rw-r--r-- | drivers/ccm-10xx/src/ahb.rs | 103 |
1 files changed, 103 insertions, 0 deletions
diff --git a/drivers/ccm-10xx/src/ahb.rs b/drivers/ccm-10xx/src/ahb.rs new file mode 100644 index 0000000..392be61 --- /dev/null +++ b/drivers/ccm-10xx/src/ahb.rs @@ -0,0 +1,103 @@ +//! 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); + } +} |
