aboutsummaryrefslogtreecommitdiff
path: root/drivers/ccm-10xx/src/ahb.rs
blob: 392be61e4890269ad0dc23e9fca2afaa11094113 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
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);
    }
}