Skip to main content

ra_hal/
clock.rs

1//! Clock information and configuration.
2
3#[cfg_attr(ra2a1, path = "clock/ra2a1.rs")]
4#[cfg_attr(ra4m1, path = "clock/ra4m1.rs")]
5#[cfg_attr(ra4l1, path = "clock/ra4l1.rs")]
6#[cfg_attr(ra6m5, path = "clock/ra6m5.rs")]
7#[cfg_attr(ra8m1, path = "clock/ra8m1.rs")]
8mod _clock;
9
10pub(crate) use _clock::init;
11use embassy_sync::once_lock::OnceLock;
12use fugit::{HertzU32, KilohertzU32, MegahertzU32};
13
14use crate::pac;
15
16#[cfg(sciclk)]
17use _clock::SciClockSource;
18#[cfg(slcdc)]
19use _clock::SlcdcClockSource;
20#[cfg(spiclk)]
21use _clock::SpiClockSource;
22#[cfg(usbclk)]
23use _clock::UsbClockSource;
24
25static CLOCK_STATUS: OnceLock<ClockStatus> = OnceLock::new();
26const _1MHZ: HertzU32 = MegahertzU32::from_raw(1).convert();
27
28/// Current state of the clocks.
29pub struct ClockStatus {
30    /// Input frequency for `ICLK`.
31    pub master: HertzU32,
32
33    /// Main Oscillator frequency
34    pub mosc: Option<HertzU32>,
35
36    /// Is the 32,768 Hz sub-clock oscillator installed?
37    pub sosc: bool,
38
39    /// High-speed On-Chip Oscillator frequency (`HOCO`).
40    pub hoco: HertzU32,
41
42    /// PLL frequency (if enabled).
43    #[cfg(all(pll, not(ra8m1)))]
44    pub pll: Option<HertzU32>,
45    /// PLL frequencies (if enabled).
46    #[cfg(all(pll, ra8m1))]
47    pub pll: Option<(HertzU32, HertzU32, HertzU32)>,
48
49    /// PLL2 frequency (if enabled).
50    #[cfg(all(pll2, not(ra8m1)))]
51    pub pll2: Option<HertzU32>,
52    /// PLL2 frequencies (if enabled).
53    #[cfg(all(pll2, ra8m1))]
54    pub pll2: Option<(HertzU32, HertzU32, HertzU32)>,
55
56    #[cfg(bclk)]
57    pub bus_clock: HertzU32,
58
59    /// System clock frequency (`ICLK`).
60    pub system: HertzU32,
61
62    /// Flash interface clock (`FCLK`).
63    pub flash: HertzU32,
64
65    /// Peripheral Clock "A" (`PCLKA`).
66    #[cfg(pclka)]
67    pub peripheral_a: HertzU32,
68
69    /// Peripheral Clock "B" (`PCLKB`).
70    #[cfg(pclkb)]
71    pub peripheral_b: HertzU32,
72
73    /// Peripheral Clock "C" (`PCLKC`).
74    #[cfg(pclkc)]
75    pub peripheral_c: HertzU32,
76
77    /// Peripheral Clock "D" (`PCLKD`).
78    #[cfg(pclkd)]
79    pub peripheral_d: HertzU32,
80
81    /// Peripheral Clock "E" (`PCLKE`).
82    #[cfg(pclke)]
83    pub peripheral_e: HertzU32,
84
85    #[cfg(usbclk)]
86    /// USB Clock (`UCLK`)
87    pub usb: Option<HertzU32>,
88
89    #[cfg(sciclk)]
90    /// SCI Clock (`SCICLK`)
91    pub sci: Option<HertzU32>,
92
93    #[cfg(spiclk)]
94    /// SPI Clock (`SPICLK`)
95    pub spi: Option<HertzU32>,
96
97    #[cfg(slcdc)]
98    pub slcdc: Option<HertzU32>,
99}
100
101/// Indicates what clock source the system clock (`ICLK`) should derive from.
102///
103/// # TODO
104/// Cortex-M85 chip support.
105#[cfg_attr(feature = "defmt", derive(defmt::Format))]
106#[derive(Debug, Copy, Clone, PartialEq)]
107pub enum SystemClockSource {
108    /// System clock source is Main Oscillator (`MOSC`).
109    Mosc,
110
111    /// System clock source is the 32.768 kHz Sub-clock Oscillator (`SOSC`).
112    Sosc,
113
114    /// System clock source is the High-speed On Chip Oscillator (`HOCO`).
115    Hoco,
116
117    /// System clock source is the 8 MHz Medium-speed On Chip Oscillator (`MOCO`).
118    Moco,
119
120    /// System clock source is the 32.768 kHz Low-speed On Chip Oscillator (`LOCO`).
121    #[cfg(not(ra8m1))]
122    Loco,
123
124    #[cfg(all(not(ra8m1), pll))]
125    /// System clock source is `PLL`.
126    Pll,
127
128    #[cfg(ra8m1)]
129    Pll1P,
130}
131
132/// Intended clock configuration.
133#[cfg_attr(feature = "defmt", derive(defmt::Format))]
134#[derive(Debug, Clone)]
135pub struct ClockConfig {
136    system: SystemClockSource,
137
138    /// High speed On Chip Oscillator frequency.
139    hoco: HocoFrequency,
140
141    /// Main Clock Oscillator frequency (external, 1–20 MHz)
142    mosc: Option<HertzU32>,
143
144    /// Enable Sub-clock Oscillator? (external, 32.768 kHz)
145    sosc: bool,
146
147    /// Enable Phase Locked Loop?
148    #[cfg(pll)]
149    pll: Option<PllConfig>,
150
151    /// Enable Phase Locked Loop №2?
152    #[cfg(pll2)]
153    pll2: Option<PllConfig>,
154
155    #[cfg(usbclk)]
156    usb: Option<UsbClockSource>,
157
158    #[cfg(sciclk)]
159    sci: Option<(SciClockSource, HertzU32)>,
160
161    #[cfg(spiclk)]
162    spi: Option<(SpiClockSource, HertzU32)>,
163
164    #[cfg(slcdc)]
165    slcdc: Option<SlcdcClockSource>,
166}
167
168cfg_select! {
169    not(ra8m1) => {
170        #[cfg(all(pll, not(ra4m1)))]
171        pub use _clock::PllInDiv;
172        #[cfg(ra4m1)]
173        pub use _clock::PllOutDiv;
174        #[cfg(any(pll, pll2))]
175        pub use _clock::{PllInput, PllOutMul};
176
177        /// Configuration for a Phase Locked Loop.
178        #[cfg_attr(feature = "defmt", derive(defmt::Format))]
179        #[cfg(any(pll, pll2))]
180        #[derive(Debug, Clone)]
181        pub struct PllConfig {
182            input: PllInput,
183            #[cfg(not(ra4m1))]
184            div: PllInDiv,
185            #[cfg(ra4m1)]
186            div: PllOutDiv,
187            mul: PllOutMul,
188        }
189    },
190    ra8m1 => {
191        pub use _clock::PllInDiv;
192        pub use _clock::{PllInput, PllPDiv, PllQDiv, PllRDiv, PllOutMul};
193
194        /// Configuration for a Phase Locked Loop.
195        #[cfg_attr(feature = "defmt", derive(defmt::Format))]
196        #[derive(Debug, Clone)]
197        pub struct PllConfig {
198            /// Reference clock
199            input: PllInput,
200            /// Input clock divider
201            div: PllInDiv,
202            /// Multiplication ratio
203            mul: PllOutMul,
204            /// Output clock divider
205            div_p: PllPDiv,
206            /// Output clock divider
207            div_q: PllQDiv,
208            /// Output clock divider
209            div_r: PllRDiv,
210        }
211    }
212}
213
214/// Returns the active state of the clock tree.
215pub fn clock_status() -> &'static ClockStatus {
216    CLOCK_STATUS
217        .try_get()
218        .expect("CLOCK_STATUS not initialized")
219}
220
221#[cfg(feature = "defmt")]
222fn print_frequency(fmt: defmt::Formatter, label: &str, frequency: HertzU32) {
223    if frequency < _1MHZ {
224        let pll: KilohertzU32 = frequency.convert();
225        defmt::write!(fmt, ", {}: {}", label, pll);
226    } else {
227        let pll: MegahertzU32 = frequency.convert();
228        defmt::write!(fmt, ", {}: {}", label, pll);
229    }
230}
231
232#[cfg(feature = "defmt")]
233impl defmt::Format for ClockStatus {
234    fn format(&self, fmt: defmt::Formatter) {
235        let system = pac::SYSTEM;
236        let cksel = system.sckscr().read().cksel();
237
238        let &ClockStatus {
239            master: _,
240            system,
241            flash,
242            #[cfg(pclka)]
243            peripheral_a,
244            #[cfg(pclkb)]
245            peripheral_b,
246            #[cfg(pclkc)]
247            peripheral_c,
248            #[cfg(pclkd)]
249            peripheral_d,
250            #[cfg(pclke)]
251            peripheral_e,
252            hoco,
253            #[cfg(pll)]
254            pll,
255            #[cfg(pll2)]
256            pll2,
257            #[cfg(bclk)]
258            bus_clock,
259            sosc,
260            mosc,
261            #[cfg(usbclk)]
262            usb,
263            #[cfg(sciclk)]
264            sci,
265            #[cfg(spiclk)]
266            spi,
267            #[cfg(slcdc)]
268            slcdc,
269        } = clock_status();
270
271        defmt::write!(fmt, "SYSTEM: ");
272
273        if hoco < _1MHZ {
274            let hoco: KilohertzU32 = hoco.convert();
275            defmt::write!(fmt, "HOCO: {}", hoco);
276        } else {
277            let hoco: MegahertzU32 = hoco.convert();
278            defmt::write!(fmt, "HOCO: {}", hoco);
279        }
280        defmt::write!(fmt, ", SOSC: {}", sosc);
281
282        match mosc {
283            Some(mosc) => {
284                print_frequency(fmt, "MOSC", mosc.convert());
285            }
286            None => {
287                defmt::write!(fmt, ", MOSC: None");
288            }
289        }
290
291        #[cfg(pll)]
292        cfg_select! {
293            not(ra8m1) => {
294                match pll {
295                    Some(pll) => print_frequency(fmt, "PLL", pll),
296                    None => defmt::write!(fmt, ", PLL: OFF"),
297                }
298            }
299            ra8m1 => {
300                match pll {
301                    Some((pll_p, pll_q, pll_r)) => {
302                        print_frequency(fmt, "PLL.P", pll_p);
303                        print_frequency(fmt, "PLL.Q", pll_q);
304                        print_frequency(fmt, "PLL.R", pll_r);
305                    }
306                    None => defmt::write!(fmt, ", PLL: OFF"),
307                }
308            }
309        }
310
311        #[cfg(pll2)]
312        cfg_select! {
313            not(ra8m1) => {
314                match pll2 {
315                    Some(pll) => print_frequency(fmt, "PLL2", pll),
316                    None => defmt::write!(fmt, ", PLL2: OFF"),
317                }
318            }
319            ra8m1 => {
320                match pll2 {
321                    Some((pll_p, pll_q, pll_r)) => {
322                        print_frequency(fmt, "PLL2.P", pll_p);
323                        print_frequency(fmt, "PLL2.Q", pll_q);
324                        print_frequency(fmt, "PLL2.R", pll_r);
325                    }
326                    None => defmt::write!(fmt, ", PLL2: OFF"),
327                }
328            }
329        }
330
331        defmt::write!(fmt, ", ROOT: {}", cksel);
332
333        print_frequency(fmt, "ICLK", system.convert());
334        print_frequency(fmt, "FCLK", flash.convert());
335
336        #[cfg(bclk)]
337        print_frequency(fmt, "BCLK", bus_clock.convert());
338
339        #[cfg(pclka)]
340        print_frequency(fmt, "PCLKA", peripheral_a.convert());
341
342        #[cfg(pclkb)]
343        print_frequency(fmt, "PCLKB", peripheral_b.convert());
344
345        #[cfg(pclkc)]
346        print_frequency(fmt, "PCLKC", peripheral_c.convert());
347
348        #[cfg(pclkd)]
349        print_frequency(fmt, "PCLKD", peripheral_d.convert());
350
351        #[cfg(pclke)]
352        print_frequency(fmt, "PCLKE", peripheral_e.convert());
353
354        #[cfg(usbclk)]
355        if let Some(usb) = usb {
356            print_frequency(fmt, "USB", usb.convert());
357        } else {
358            defmt::write!(fmt, ", USB: OFF");
359        }
360
361        #[cfg(sciclk)]
362        if let Some(sci) = sci {
363            print_frequency(fmt, "SCI", sci.convert());
364        } else {
365            defmt::write!(fmt, ", SCI: OFF");
366        }
367
368        #[cfg(spiclk)]
369        if let Some(spi) = spi {
370            print_frequency(fmt, "SPI", spi.convert());
371        } else {
372            defmt::write!(fmt, ", SPI: OFF");
373        }
374    }
375}
376
377include!(concat!(env!("OUT_DIR"), "/hoco.rs"));