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);
}
}
|