lpc55_hal/drivers/
clocks.rs

1//!* API to configure the clocks.
2//!
3//! This is very incomplete (e.g., no support for PLL clocks).
4//! It is also likely buggy, and more complex than needed
5//!
6//! It is currently used to prepare for using the USBFSD and
7//! Flexcomm peripherals.
8use core::{cmp::min, convert::TryFrom};
9use embedded_time::rate::Extensions;
10
11use crate::typestates::{
12    main_clock::MainClock,
13    ClocksSupport1MhzFroToken,
14    ClocksSupport32KhzFroToken,
15    // clock_state,
16    ClocksSupportFlexcommToken,
17    ClocksSupportTouchToken,
18    ClocksSupportUsbfsToken,
19    ClocksSupportUsbhsToken,
20    ClocksSupportUtickToken,
21};
22use crate::{
23    peripherals::{anactrl::Anactrl, pmc::Pmc, syscon::Syscon},
24    time::{Hertz, Megahertz},
25};
26
27// #[allow(unused_imports)]
28// use cortex_m_semihosting::{hprintln, dbg};
29
30// UM 41.4.7 says: need >= 12Mhz for USBFS
31// Empirically, this does not enumerate though
32// TODO: It seems even Fro12Mhz works, but e.g.
33// PLL at 13.mhz does not - might also be a bug
34// in PLL code ofc
35const MIN_USBFS_FREQ: Megahertz = Megahertz(24);
36const MIN_USBHS_FREQ: Megahertz = Megahertz(96);
37const DEFAULT_FREQ: Megahertz = Megahertz(12);
38
39#[derive(Debug, Default)]
40pub struct ClockRequirements {
41    pub system_frequency: Option<Megahertz>,
42    pub custom_pll: Option<Pll>,
43}
44
45#[derive(Debug, Copy, Clone)]
46pub struct Clocks {
47    pub(crate) main_clock: MainClock,
48    pub(crate) system_frequency: Hertz,
49}
50
51impl Clocks {
52    pub fn support_flexcomm_token(&self) -> Option<ClocksSupportFlexcommToken> {
53        Some(ClocksSupportFlexcommToken { __: () })
54    }
55
56    pub fn support_usbfs_token(&self) -> Option<ClocksSupportUsbfsToken> {
57        let fast_enough = self.system_frequency >= Hertz::from(MIN_USBFS_FREQ);
58        let can_latch_sof = self.main_clock == MainClock::Fro96Mhz;
59
60        if fast_enough && can_latch_sof {
61            Some(ClocksSupportUsbfsToken { __: () })
62        } else {
63            None
64        }
65    }
66
67    pub fn support_usbhs_token(&self) -> Option<ClocksSupportUsbhsToken> {
68        let fast_enough = self.system_frequency >= Hertz::from(MIN_USBHS_FREQ);
69        if fast_enough {
70            Some(ClocksSupportUsbhsToken { __: () })
71        } else {
72            None
73        }
74    }
75
76    pub fn support_utick_token(&self) -> Option<ClocksSupportUtickToken> {
77        Some(ClocksSupportUtickToken { __: () })
78    }
79
80    pub fn support_1mhz_fro_token(&self) -> Option<ClocksSupport1MhzFroToken> {
81        Some(ClocksSupport1MhzFroToken { __: () })
82    }
83
84    pub fn support_touch_token(&self) -> Option<ClocksSupportTouchToken> {
85        if self.system_frequency.0 >= 96 {
86            Some(ClocksSupportTouchToken { __: () })
87        } else {
88            None
89        }
90    }
91
92    pub fn enable_32k_fro(&self, pmc: &mut Pmc) -> ClocksSupport32KhzFroToken {
93        let mut token = ClocksSupport32KhzFroToken { __: () };
94        pmc.power_on(&mut token);
95        token
96    }
97}
98
99/// Output of Pll is: M/(2NP) times input
100///
101/// "There may be several ways to obtain the same PLL output frequency.
102/// PLL power depends on Fcco (a lower frequency uses less power) and the divider used.
103/// Bypassing the input and/or output divider saves power."
104
105// #[allow(dead_code)]
106#[derive(Debug)]
107pub struct Pll {
108    n: u8,
109    m: u16,
110    p: u8,
111    selp: u8,
112    seli: u8,
113}
114
115impl Pll {
116    // allow user to override if they know better...
117    /// # Safety
118    ///
119    /// Input values must be valid for PLL
120    pub unsafe fn new(n: u8, m: u16, p: u8) -> Pll {
121        // UM 4.6.6.3.2
122        let selp = min((m >> 2) + 1, 31) as u8;
123        let seli = min(
124            63,
125            match m {
126                m if m >= 8000 => 1,
127                m if m >= 122 => 8000 / m,
128                _ => 2 * (m >> 2) + 3,
129            },
130        ) as u8;
131        // let seli = min(2*(m >> 2) + 3, 63);
132        Pll {
133            n,
134            m,
135            p,
136            selp,
137            seli,
138        }
139    }
140}
141
142static mut CONFIGURED: bool = false;
143
144#[derive(Debug)]
145pub enum ClocksError {
146    // TODO: Add "cause"
147    AlreadyConfigured,
148    NotFeasible,
149}
150
151pub type Result<T> = core::result::Result<T, ClocksError>;
152
153// TODO:
154// - make sure Fro12Mhz is running for FLEXCOMM0
155// - make sure Fro12 and Fro96 are even powered
156
157impl ClockRequirements {
158    pub fn system_frequency<Freq>(mut self, freq: Freq) -> Self
159    where
160        Freq: Into<Megahertz>,
161    {
162        self.system_frequency = Some(freq.into());
163        self
164    }
165
166    // generated via `scripts/generate-pll-settings.py`,
167    // massaged a bit by hand
168    fn get_pll(freq: u32) -> Pll {
169        debug_assert!(freq >= 5);
170        debug_assert!(freq <= 150);
171        // let ns: [u32; 9] = [1, 2, 3, 4, 6, 8, 12, 16, 24];
172        // let ns: [u32; 2] = [1, 2];
173        // let ps: [u32; 11] = [2, 3, 4, 6, 8, 9, 12, 16, 18, 24, 30];
174        // let ps: [u32; 5] = [6, 8, 12, 16, 24];
175
176        // for n in ns.iter() { for p in ps.iter() { for m in 3..=97 {
177        for n in 1..=6 {
178            for p in 1..=30 {
179                for m in 1..=255 {
180                    // if 2 * freq * (*n) * (*p) == 12 * m {
181                    if 2 * freq * n * p == 12 * m {
182                        // UM 4.6.6.3.2
183                        let selp = (m >> 2) + 1; // <= 31
184                        let seli = 2 * (m >> 2) + 3; // <= 63
185                        return Pll {
186                            // n: *n as u8,
187                            n: n as u8,
188                            m: m as u16,
189                            // p: *p as u8,
190                            p: p as u8,
191                            selp: selp as u8,
192                            seli: seli as u8,
193                        };
194                    }
195                }
196            }
197        }
198
199        unreachable!();
200    }
201
202    fn configure_pll0(pll: Pll, pmc: &mut Pmc, syscon: &mut Syscon) {
203        pmc.raw
204            .pdruncfg0
205            .modify(|_, w| w.pden_pll0().poweredoff().pden_pll0_sscg().poweredoff());
206
207        syscon.raw.pll0ctrl.write(|w| unsafe {
208            w.clken()
209                .enable()
210                .seli()
211                .bits(pll.seli)
212                .selp()
213                .bits(pll.selp)
214        });
215
216        syscon
217            .raw
218            .pll0ndec
219            .write(|w| unsafe { w.ndiv().bits(pll.n) });
220        syscon.raw.pll0ndec.write(|w| unsafe {
221            w.ndiv().bits(pll.n).nreq().set_bit() // latch
222        });
223
224        syscon
225            .raw
226            .pll0pdec
227            .write(|w| unsafe { w.pdiv().bits(pll.p) });
228        syscon.raw.pll0pdec.write(|w| unsafe {
229            w.pdiv().bits(pll.p).preq().set_bit() // latch
230        });
231
232        syscon
233            .raw
234            .pll0sscg0
235            .write(|w| unsafe { w.md_lbs().bits(0) });
236
237        syscon
238            .raw
239            .pll0sscg1
240            .write(|w| unsafe { w.mdiv_ext().bits(pll.m).sel_ext().set_bit() });
241        syscon.raw.pll0sscg1.write(|w| unsafe {
242            w.mdiv_ext()
243                .bits(pll.m)
244                .sel_ext()
245                .set_bit()
246                .mreq()
247                .set_bit() // latch
248                .md_req()
249                .set_bit() // latch
250        });
251
252        pmc.raw
253            .pdruncfg0
254            .modify(|_, w| w.pden_pll0().poweredon().pden_pll0_sscg().poweredon());
255
256        // wait at least 6 ms for PLL to stabilize
257        crate::wait_at_least(6_000);
258    }
259
260    fn get_clock_source_and_div_for_freq(
261        freq: Megahertz,
262        pmc: &mut Pmc,
263        syscon: &mut Syscon,
264    ) -> (MainClock, u8) {
265        let (main_clock, sys_divider) = match freq {
266            freq if freq <= 12_u32.MHz() && 12 % freq.0 == 0 => (MainClock::Fro12Mhz, 12 / freq.0),
267            freq if freq <= 96_u32.MHz() && 96 % freq.0 == 0 => (MainClock::Fro96Mhz, 96 / freq.0),
268            // For reference: how to get 150 MHz using 16Mhz external crystal
269            // freq if freq == 150.mhz() && 150 % freq.0 == 0 => {
270            //     // Use crystal as input to PLL0
271            //     // Power on 32M crystal for stable pll operation
272            //     pmc.raw.pdruncfg0.modify(|_,w| w.pden_xtal32m().poweredon());
273            //     pmc.raw.pdruncfg0.modify(|_,w| w.pden_ldoxo32m().poweredon());
274
275            //     // Connect external 32M as clk input
276            //     syscon.raw.clock_ctrl.modify(|_,w| w.clkin_ena().set_bit());
277            //     anactrl.raw.xo32m_ctrl.modify(|_,w| w.enable_system_clk_out().set_bit());
278
279            //     // select clkin for pll0
280            //     syscon.raw.pll0clksel.write(|w| unsafe{ w.bits(1) });
281
282            //     // 150 MHz settings
283            //     let pll = Pll {
284            //         n: 8,
285            //         m: 150,
286            //         p: 1,
287            //         selp: 31,
288            //         seli: 53,
289            //     };
290
291            //     Self::configure_pll0(pll, pmc, syscon);
292
293            //     (MainClock::Pll0, 1)
294            // }
295            // Get 150 MHz using internal FRO12
296            freq if freq == 150_u32.MHz() => {
297                syscon.raw.pll0clksel.write(|w| {
298                    w.sel().enum_0x0() /* FRO 12 MHz input */
299                });
300                Self::configure_pll0(
301                    Pll {
302                        n: 8,
303                        m: 200,
304                        p: 1,
305                        selp: 31,
306                        seli: 53,
307                    },
308                    pmc,
309                    syscon,
310                );
311                (MainClock::Pll0, 1)
312            }
313
314            _ => {
315                let pll = Self::get_pll(freq.0);
316                syscon.raw.pll0clksel.write(|w| {
317                    w.sel().enum_0x0() /* FRO 12 MHz input */
318                });
319                Self::configure_pll0(pll, pmc, syscon);
320                (MainClock::Pll0, 1)
321            }
322        };
323        debug_assert!(sys_divider < 256);
324        (main_clock, sys_divider as u8)
325    }
326
327    fn set_new_clock_source(
328        freq: Megahertz,
329        main_clock: MainClock,
330        sys_divider: u8,
331        syscon: &mut Syscon,
332    ) {
333        // set highest flash wait cycles
334        syscon
335            .raw
336            .fmccr
337            .modify(|_, w| unsafe { w.flashtim().bits(11) });
338
339        match main_clock {
340            MainClock::Fro12Mhz => {
341                // Fro12
342                syscon.raw.mainclksela.modify(|_, w| w.sel().enum_0x0());
343                // Main A
344                syscon.raw.mainclkselb.modify(|_, w| w.sel().enum_0x0());
345                unsafe {
346                    syscon
347                        .raw
348                        .ahbclkdiv
349                        .modify(|_, w| w.div().bits(sys_divider - 1))
350                };
351            }
352            MainClock::Fro96Mhz => {
353                // Fro96
354                syscon.raw.mainclksela.modify(|_, w| w.sel().enum_0x3());
355                // Main B
356                syscon.raw.mainclkselb.modify(|_, w| w.sel().enum_0x0());
357                unsafe {
358                    syscon
359                        .raw
360                        .ahbclkdiv
361                        .modify(|_, w| w.div().bits(sys_divider - 1))
362                };
363            }
364            MainClock::Pll0 => {
365                // Fro12
366                syscon.raw.mainclksela.modify(|_, w| w.sel().enum_0x0());
367                // Pll0
368                syscon.raw.mainclkselb.modify(|_, w| w.sel().enum_0x1());
369                unsafe {
370                    syscon
371                        .raw
372                        .ahbclkdiv
373                        .modify(|_, w| w.div().bits(sys_divider - 1))
374                };
375            }
376        }
377
378        // fix wait cycles
379        match freq.0 {
380            0..=99 => {
381                unsafe {
382                    syscon
383                        .raw
384                        .fmccr
385                        .modify(|_, w| w.flashtim().bits((freq.0 / 11) as u8 - 1))
386                };
387            }
388            100..=115 => {
389                unsafe { syscon.raw.fmccr.modify(|_, w| w.flashtim().bits(9)) };
390            }
391            116..=130 => {
392                unsafe { syscon.raw.fmccr.modify(|_, w| w.flashtim().bits(10)) };
393            }
394            _ => {
395                unsafe { syscon.raw.fmccr.modify(|_, w| w.flashtim().bits(11)) };
396            }
397        }
398    }
399
400    /// Requirements solver - tries to generate and configure a clock configuration
401    /// from (partial) requirements.
402    ///
403    /// Can be called only once, to not invalidate previous configurations
404    pub fn configure(
405        self,
406        anactrl: &mut Anactrl,
407        pmc: &mut Pmc,
408        syscon: &mut Syscon,
409    ) -> Result<Clocks> {
410        if unsafe { CONFIGURED } {
411            return Err(ClocksError::AlreadyConfigured);
412        }
413
414        let freq: Megahertz = self.system_frequency.unwrap_or(DEFAULT_FREQ);
415
416        // turn on FRO192M: clear bit 5, according to `fsl_power.h` from the SDK
417        // unsafe { pmc.raw.pdruncfgclr0.write(|w| w.bits(1u32 << 5)) };
418        // but it's hidden in UM, so let's assume this is always cleared
419
420        // turn on 1mhz, 12mhz and 96mhz clocks
421        anactrl
422            .raw
423            .fro192m_ctrl
424            .modify(|_, w| w.ena_96mhzclk().enable());
425        anactrl
426            .raw
427            .fro192m_ctrl
428            .modify(|_, w| w.ena_12mhzclk().enable());
429
430        syscon
431            .raw
432            .clock_ctrl
433            .modify(|_, w| w.fro1mhz_clk_ena().enable().fro1mhz_utick_ena().enable());
434
435        let (main_clock, sys_divider) = Self::get_clock_source_and_div_for_freq(freq, pmc, syscon);
436        Self::set_new_clock_source(freq, main_clock, sys_divider, syscon);
437
438        unsafe { CONFIGURED = true };
439
440        Ok(Clocks {
441            main_clock,
442            system_frequency: Hertz::try_from(freq).unwrap(),
443        })
444    }
445
446    /// Same as above, but allows clock to be changed after an initial configuration.
447    ///
448    /// # Safety
449    ///
450    /// This is unsafe because it's up to the developer to ensure the new configuration is okay for
451    /// the device peripherals being used.
452    pub unsafe fn reconfigure(self, _clocks: Clocks, pmc: &mut Pmc, syscon: &mut Syscon) -> Clocks {
453        let freq: Megahertz = self.system_frequency.unwrap_or(DEFAULT_FREQ);
454
455        let (main_clock, sys_divider) = Self::get_clock_source_and_div_for_freq(freq, pmc, syscon);
456
457        Self::set_new_clock_source(freq, main_clock, sys_divider, syscon);
458
459        Clocks {
460            main_clock,
461            system_frequency: Hertz::try_from(freq).unwrap(),
462        }
463    }
464}