aboutsummaryrefslogtreecommitdiff
path: root/drivers/ccm-10xx/src/ahb.rs
diff options
context:
space:
mode:
authorIan McIntyre <me@mciantyre.dev>2025-11-30 18:52:34 -0500
committerIan McIntyre <me@mciantyre.dev>2025-11-30 19:10:51 -0500
commit76199f21616ad86cf68f3b063c1ce23c6fc5a52f (patch)
tree4c076d0afd649803a2bd9a5ed5cbb1f1c74fb459 /drivers/ccm-10xx/src/ahb.rs
First commit
Diffstat (limited to 'drivers/ccm-10xx/src/ahb.rs')
-rw-r--r--drivers/ccm-10xx/src/ahb.rs103
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);
+ }
+}