teensy4_bsp/
clock_power.rs

1//! Implements the board's clock and power policy.
2
3#![allow(clippy::assertions_on_constants)]
4
5use crate::{
6    hal::{
7        self,
8        ccm::{self, clock_gate, XTAL_OSCILLATOR_HZ},
9        dcdc,
10    },
11    ral,
12};
13
14pub use hal::lpi2c::ClockSpeed as Lpi2cClockSpeed;
15
16/// Frequency (Hz) of the crystal oscillator.
17///
18/// This is 24MHz.
19pub const XTAL_OSCILLATOR_FREQUENCY: u32 = XTAL_OSCILLATOR_HZ;
20
21const PLL1_DIV_SEL: u32 = 100;
22const ARM_DIVIDER: u32 = 2;
23const AHB_DIVIDER: u32 = 1;
24const IPG_DIVIDER: u32 = 4;
25
26/// Frequency (Hz) of the ARM core.
27///
28/// `board` guarantees that the ARM core runs at 600MHz. It's derived from
29/// PLL1.
30pub const ARM_FREQUENCY: u32 =
31    ccm::analog::pll1::frequency(PLL1_DIV_SEL) / ARM_DIVIDER / AHB_DIVIDER;
32const _: () = assert!(ARM_FREQUENCY == 600_000_000);
33
34/// Frequency (Hz) of the IPG bus.
35pub const IPG_FREQUENCY: u32 = ARM_FREQUENCY / IPG_DIVIDER;
36const _: () = assert!(IPG_FREQUENCY == 150_000_000);
37
38/// Prepares the AHB and IPG clock root.
39fn setup_ahb_ipg_clk(ccm: &mut ral::ccm::CCM, ccm_analog: &mut ral::ccm_analog::CCM_ANALOG) {
40    clock_gate::IPG_CLOCK_GATES
41        .iter()
42        .for_each(|locator| locator.set(ccm, clock_gate::OFF));
43
44    if ccm::ahb_clk::Selection::PeriphClk2Sel == ccm::ahb_clk::selection(ccm) {
45        // Switch to the pre-peripheral clock before changing
46        // peripheral clock 2...
47        ccm::ahb_clk::set_selection(ccm, ccm::ahb_clk::Selection::PrePeriphClkSel);
48    }
49
50    // Temporarily switch to the crystal oscillator.
51    ccm::periph_clk2::set_divider(ccm, 1);
52    ccm::periph_clk2::set_selection(ccm, ccm::periph_clk2::Selection::Osc);
53    ccm::ahb_clk::set_selection(ccm, ccm::ahb_clk::Selection::PeriphClk2Sel);
54
55    // Prepare PLL1.
56    ccm::analog::pll1::restart(ccm_analog, PLL1_DIV_SEL);
57    ccm::arm_divider::set_divider(ccm, ARM_DIVIDER);
58    ccm::ahb_clk::set_divider(ccm, AHB_DIVIDER);
59
60    // Switch back to PLL1.
61    ccm::pre_periph_clk::set_selection(ccm, ccm::pre_periph_clk::Selection::Pll1);
62    ccm::ahb_clk::set_selection(ccm, ccm::ahb_clk::Selection::PrePeriphClkSel);
63
64    ccm::ipg_clk::set_divider(ccm, IPG_DIVIDER);
65}
66
67const PERCLK_DIVIDER: u32 = 24;
68/// PERCLK clock frequency (Hz).
69///
70/// This is the PIT timer frequency. It can also be used as the GPT frequency,
71/// provided you use the peripheral clock or high-frequency reference clock as
72/// the GPT clock selection.
73///
74/// PERCLK runs at 1MHz. It derives from the crystal oscillator.
75pub const PERCLK_FREQUENCY: u32 = XTAL_OSCILLATOR_FREQUENCY / PERCLK_DIVIDER;
76const _: () = assert!(PERCLK_FREQUENCY == 1_000_000);
77
78/// Prepare PERCLK for PIT and GPT timers.
79fn setup_perclk_clk(ccm: &mut ral::ccm::CCM) {
80    clock_gate::PERCLK_CLOCK_GATES
81        .iter()
82        .for_each(|locator| locator.set(ccm, clock_gate::OFF));
83    ccm::perclk_clk::set_divider(ccm, PERCLK_DIVIDER);
84    ccm::perclk_clk::set_selection(ccm, ccm::perclk_clk::Selection::Oscillator);
85}
86
87const UART_DIVIDER: u32 = 3;
88/// Frequency (Hz) of all UARTs.
89///
90/// Use this to compute any alternate baud rates that aren't supported
91/// by [`lpuart_baud()`].
92pub const UART_FREQUENCY: u32 = XTAL_OSCILLATOR_FREQUENCY / UART_DIVIDER;
93
94/// Prepare UART root clock for all LPUARTs.
95fn setup_uart_clk(ccm: &mut ral::ccm::CCM) {
96    clock_gate::UART_CLOCK_GATES
97        .iter()
98        .for_each(|locator| locator.set(ccm, clock_gate::OFF));
99
100    ccm::uart_clk::set_selection(ccm, ccm::uart_clk::Selection::Oscillator);
101    ccm::uart_clk::set_divider(ccm, UART_DIVIDER);
102}
103
104/// Type for LPUART baud rates.
105///
106/// Use [`lpuart_baud()`] to easily compute a baud rate.
107pub type LpuartBaud = hal::lpuart::Baud;
108
109/// Computes a UART baud rate.
110///
111/// Note that this can evaluate at compile time. This function assumes that
112/// the UART clock matches [`UART_FREQUENCY`].
113///
114/// ```
115/// use teensy4_bsp as bsp;
116/// use teensy4_bsp::board;
117///
118/// const MY_BAUD: board::LpuartBaud = board::lpuart_baud(9600);
119/// ```
120pub const fn lpuart_baud(bps: u32) -> hal::lpuart::Baud {
121    hal::lpuart::Baud::compute(UART_FREQUENCY, bps)
122}
123
124const LPI2C_DIVIDER: u32 = 3;
125const LPI2C_CLOCK_SOURCE: ccm::lpi2c_clk::Selection = ccm::lpi2c_clk::Selection::Oscillator;
126
127/// Frequency (Hz) of all LPI2C peripherals.
128pub const LPI2C_FREQUENCY: u32 = XTAL_OSCILLATOR_FREQUENCY / LPI2C_DIVIDER;
129const _: () = assert!(LPI2C_FREQUENCY == 8_000_000);
130
131/// Prepare the LPI2C clock root.
132fn setup_lpi2c_clk(ccm: &mut ral::ccm::CCM) {
133    clock_gate::LPI2C_CLOCK_GATES
134        .iter()
135        .for_each(|locator| locator.set(ccm, clock_gate::OFF));
136    ccm::lpi2c_clk::set_divider(ccm, LPI2C_DIVIDER);
137    ccm::lpi2c_clk::set_selection(ccm, LPI2C_CLOCK_SOURCE);
138}
139
140/// LPI2C timing parameters.
141pub type Lpi2cBaud = hal::lpi2c::Timing;
142
143/// Computes a LPI2C baud rate, assuming ideal bus behavior.
144///
145/// If this produces timing configurations doesn't closely approximate your
146/// expected baud rate, you may have more success by defining the LPI2C timing
147/// configurations yourself.
148///
149/// This function assumes that the LPI2C clock frequency matches [`LPI2C_FREQUENCY`].
150/// Note that this can evaluate at compile time.
151///
152/// ```no_run
153/// use teensy4_bsp as bsp;
154/// use bsp::board;
155///
156/// const MY_BAUD: board::Lpi2cBaud = board::lpi2c_baud(board::Lpi2cClockSpeed::KHz400);
157/// ```
158pub const fn lpi2c_baud(clock_speed: Lpi2cClockSpeed) -> Lpi2cBaud {
159    Lpi2cBaud::ideal(LPI2C_FREQUENCY, clock_speed)
160}
161
162const LPSPI_DIVIDER: u32 = 4;
163/// Frequency (Hz) of all LPSPI clocks.
164pub const LPSPI_FREQUENCY: u32 = ccm::analog::pll2::FREQUENCY / LPSPI_DIVIDER;
165const _: () = assert!(LPSPI_FREQUENCY == 132_000_000);
166
167/// Prepare the LPSPI clock root.
168fn setup_lpspi_clk(ccm: &mut ral::ccm::CCM) {
169    clock_gate::LPSPI_CLOCK_GATES
170        .iter()
171        .for_each(|locator| locator.set(ccm, clock_gate::OFF));
172    ccm::lpspi_clk::set_selection(ccm, ccm::lpspi_clk::Selection::Pll2);
173    ccm::lpspi_clk::set_divider(ccm, LPSPI_DIVIDER);
174}
175
176const CLOCK_GATES: &[clock_gate::Locator] = &[
177    clock_gate::pit(),
178    clock_gate::gpt_bus::<1>(),
179    clock_gate::gpt_bus::<2>(),
180    clock_gate::gpt_serial::<1>(),
181    clock_gate::gpt_serial::<2>(),
182    clock_gate::gpio::<1>(),
183    clock_gate::gpio::<2>(),
184    clock_gate::gpio::<3>(),
185    clock_gate::gpio::<4>(),
186    clock_gate::usb(),
187    clock_gate::dma(),
188    clock_gate::snvs_lp(),
189    clock_gate::snvs_hp(),
190    clock_gate::lpi2c::<1>(),
191    clock_gate::lpi2c::<3>(),
192    clock_gate::lpspi::<1>(),
193    clock_gate::lpspi::<2>(),
194    clock_gate::lpspi::<3>(),
195    clock_gate::lpspi::<4>(),
196    clock_gate::lpuart::<6>(),
197    clock_gate::lpuart::<4>(),
198    clock_gate::lpuart::<2>(),
199    clock_gate::lpuart::<3>(),
200    clock_gate::lpuart::<8>(),
201    clock_gate::lpuart::<1>(),
202    clock_gate::lpuart::<7>(),
203    clock_gate::lpuart::<5>(),
204    clock_gate::flexpwm::<1>(),
205    clock_gate::flexpwm::<2>(),
206    clock_gate::flexpwm::<3>(),
207    clock_gate::flexpwm::<4>(),
208    clock_gate::flexio::<1>(),
209    clock_gate::flexio::<2>(),
210    clock_gate::flexio::<3>(),
211    clock_gate::adc::<1>(),
212    clock_gate::adc::<2>(),
213    clock_gate::trng(),
214];
215
216/// Prepare clocks and power for the MCU.
217///
218/// This implements the [`board`](crate::board#clock-policy)'s clock policy. This
219/// function is automatically called when acquiring board resources.
220pub fn prepare_clocks_and_power(
221    ccm: &mut ral::ccm::CCM,
222    ccm_analog: &mut ral::ccm_analog::CCM_ANALOG,
223    dcdc: &mut ral::dcdc::DCDC,
224) {
225    ccm::set_low_power_mode(ccm, ccm::LowPowerMode::RemainInRun);
226    dcdc::set_target_vdd_soc(dcdc, 1250);
227    ccm::analog::pll3::restart(ccm_analog);
228
229    setup_ahb_ipg_clk(ccm, ccm_analog);
230    setup_lpi2c_clk(ccm);
231    setup_lpspi_clk(ccm);
232    setup_perclk_clk(ccm);
233    setup_uart_clk(ccm);
234
235    CLOCK_GATES
236        .iter()
237        .for_each(|locator| locator.set(ccm, clock_gate::ON));
238}