atsamd_hal/peripherals/clock/d5x/v2/
xosc.rs

1//! # External multipurpose crystal oscillator controller
2//!
3//! ## Overview
4//!
5//! The `xosc` module provides access to the two external crystal oscillator
6//! controllers (XOSCs) within the `OSCCTRL` peripheral.
7//!
8//! Each XOSC peripheral can operate in two [`Mode`]s. It can accept an external
9//! clock or can interface with an crystal oscillator. In both cases, the clock
10//! must be in the 8-48 MHz range.
11//!
12//! When used with an external clock, only one GPIO [`Pin`] is required, but
13//! when used with a crystal oscillator, two GPIO `Pin`s are required. The
14//! [`XIn`] `Pin` is used in both `Mode`s, while the [`XOut`] `Pin` is only
15//! used in [`CrystalMode`].
16//!
17//! When operating in [`CrystalMode`], the XOSC peripheral provides several
18//! configuration options to increase stability or reduce power consumption of
19//! the crystal.
20//!
21//! The XOSC peripheral can also detect failure of the clock or crystal; and if
22//! failure occurs, it can automatically switch to a safe, backup clock derived
23//! from the [DFLL].
24//!
25//! Creating and configuring an [`Xosc`] proceeds according to the principles
26//! outlined in the [`clock` module documentation]. It is best shown with an
27//! example.
28//!
29//! ## Example
30//!
31//! Let's start by using [`clock_system_at_reset`] to access the HAL clocking
32//! structs. We'll also need access to the GPIO [`Pins`].
33//!
34//! ```no_run
35//! use atsamd_hal::{
36//!     clock::v2::{
37//!         clock_system_at_reset,
38//!         xosc::{CrystalCurrent, SafeClockDiv, StartUpDelay, Xosc},
39//!     },
40//!     gpio::Pins,
41//!     pac::Peripherals,
42//!     fugit::RateExtU32,
43//! };
44//! let mut pac = Peripherals::take().unwrap();
45//! let pins = Pins::new(pac.port);
46//! let (buses, clocks, tokens) = clock_system_at_reset(
47//!     pac.oscctrl,
48//!     pac.osc32kctrl,
49//!     pac.gclk,
50//!     pac.mclk,
51//!     &mut pac.nvmctrl,
52//! );
53//! ```
54//!
55//! Next, we can create and configure the [`Xosc`] in one long chain of methods,
56//! using the provided builder API. The final call to [`Xosc::enable`] yields an
57//! [`EnabledXosc`] that can act as a clock [`Source`] for other clocks in the
58//! tree.
59//!
60//! ```no_run
61//! # use atsamd_hal::{
62//! #     clock::v2::{
63//! #         clock_system_at_reset,
64//! #         xosc::{CrystalCurrent, SafeClockDiv, StartUpDelay, Xosc},
65//! #     },
66//! #     gpio::Pins,
67//! #     pac::Peripherals,
68//! #     fugit::RateExtU32,
69//! # };
70//! # let mut pac = Peripherals::take().unwrap();
71//! # let pins = Pins::new(pac.port);
72//! # let (buses, clocks, tokens) = clock_system_at_reset(
73//! #     pac.oscctrl,
74//! #     pac.osc32kctrl,
75//! #     pac.gclk,
76//! #     pac.mclk,
77//! #     &mut pac.nvmctrl,
78//! # );
79//! let mut xosc = Xosc::from_crystal(tokens.xosc0, pins.pa14, pins.pa15, 20.MHz())
80//!     .current(CrystalCurrent::Medium)
81//!     .loop_control(true)
82//!     .low_buf_gain(true)
83//!     .start_up_delay(StartUpDelay::Delay488us)
84//!     .enable();
85//! ```
86//!
87//! We start by calling [`Xosc::from_crystal`], and we provide the corresponding
88//! [`XIn`] and [`XOut`] [`Pin`]s, as well as the nominal crystal frequency. We
89//! then set the [`CrystalCurrent`] level to `Medium`. The default current level
90//! for a 20 MHz signal is actually `High`, but we opt for a lower current under
91//! the assumption that our crystal's capacitive load is small. Next, we turn on
92//! automatic loop control, which should save power, but we also set
93//! `LOWBUFGAIN` to `1`. Counterintuitively, this actually _increases_ the
94//! crystal amplitude, which increases power consumption, but it also improves
95//! stability. We then apply a 488 μs start up delay, to allow the clock to
96//! stabilize before it is applied to any logic. Finally, we enable the `Xosc`.
97//!
98//! Next, we wait until the `Xosc` is stable and ready to be used as a clock
99//! [`Source`].
100//!
101//! ```no_run
102//! # use atsamd_hal::{
103//! #     clock::v2::{
104//! #         clock_system_at_reset,
105//! #         xosc::{CrystalCurrent, SafeClockDiv, StartUpDelay, Xosc},
106//! #     },
107//! #     gpio::Pins,
108//! #     pac::Peripherals,
109//! #     fugit::RateExtU32,
110//! # };
111//! # let mut pac = Peripherals::take().unwrap();
112//! # let pins = Pins::new(pac.port);
113//! # let (buses, clocks, tokens) = clock_system_at_reset(
114//! #     pac.oscctrl,
115//! #     pac.osc32kctrl,
116//! #     pac.gclk,
117//! #     pac.mclk,
118//! #     &mut pac.nvmctrl,
119//! # );
120//! # let mut xosc = Xosc::from_crystal(tokens.xosc0, pins.pa14, pins.pa15, 20.MHz())
121//! #     .current(CrystalCurrent::Medium)
122//! #     .loop_control(true)
123//! #     .low_buf_gain(true)
124//! #     .start_up_delay(StartUpDelay::Delay488us)
125//! #     .enable();
126//! while !xosc.is_ready() {}
127//! ```
128//!
129//! Once the clock is stable, we can also enable failure detection. To do so, we
130//! must provide the [`EnabledDfll`] to act as the backup safe clock. We can
131//! also select a divider for the safe clock, so that it loosely matches the
132//! `Xosc` frequency. In thise case, we divide the 48 MHz [`Dfll`] down to
133//! 24 MHz, which is the closest option to 20 MHz.
134//!
135//! ```no_run
136//! # use atsamd_hal::{
137//! #     clock::v2::{
138//! #         clock_system_at_reset,
139//! #         xosc::{CrystalCurrent, SafeClockDiv, StartUpDelay, Xosc},
140//! #     },
141//! #     gpio::Pins,
142//! #     pac::Peripherals,
143//! #     fugit::RateExtU32,
144//! # };
145//! # let mut pac = Peripherals::take().unwrap();
146//! # let pins = Pins::new(pac.port);
147//! # let (buses, clocks, tokens) = clock_system_at_reset(
148//! #     pac.oscctrl,
149//! #     pac.osc32kctrl,
150//! #     pac.gclk,
151//! #     pac.mclk,
152//! #     &mut pac.nvmctrl,
153//! # );
154//! # let mut xosc = Xosc::from_crystal(tokens.xosc0, pins.pa14, pins.pa15, 20.MHz())
155//! #     .current(CrystalCurrent::Medium)
156//! #     .loop_control(true)
157//! #     .low_buf_gain(true)
158//! #     .start_up_delay(StartUpDelay::Delay488us)
159//! #     .enable();
160//! # while !xosc.is_ready() {}
161//! xosc.enable_failure_detection(clocks.dfll, SafeClockDiv::Div2);
162//! ```
163//!
164//! In the event of a clock failure, the [`Xosc`] would be automatically
165//! switched to the safe clock, and [`EnabledXosc::has_failed`] would return
166//! true. If the problem were later resolved, the `Xosc` could be switched back
167//! to the crystal with [`EnabledXosc::switch_back`].
168//!
169//! The complete example is provided below.
170//!
171//! ```no_run
172//! use atsamd_hal::{
173//!     clock::v2::{
174//!         clock_system_at_reset,
175//!         xosc::{CrystalCurrent, SafeClockDiv, StartUpDelay, Xosc},
176//!     },
177//!     gpio::Pins,
178//!     pac::Peripherals,
179//!     fugit::RateExtU32,
180//! };
181//! let mut pac = Peripherals::take().unwrap();
182//! let pins = Pins::new(pac.port);
183//! let (buses, clocks, tokens) = clock_system_at_reset(
184//!     pac.oscctrl,
185//!     pac.osc32kctrl,
186//!     pac.gclk,
187//!     pac.mclk,
188//!     &mut pac.nvmctrl,
189//! );
190//! let mut xosc = Xosc::from_crystal(tokens.xosc0, pins.pa14, pins.pa15, 20.MHz())
191//!     .current(CrystalCurrent::Medium)
192//!     .loop_control(true)
193//!     .low_buf_gain(true)
194//!     .start_up_delay(StartUpDelay::Delay488us)
195//!     .enable();
196//! while !xosc.is_ready() {}
197//! xosc.enable_failure_detection(clocks.dfll, SafeClockDiv::Div2);
198//! ```
199//!
200//! [`Pins`]: crate::gpio::Pins
201//! [`clock` module documentation]: super
202//! [`clock_system_at_reset`]: super::clock_system_at_reset
203//! [DFLL]: super::dfll
204//! [`Dfll`]: super::dfll::Dfll
205//! [`EnabledDfll`]: super::dfll::EnabledDfll
206
207use core::marker::PhantomData;
208
209use typenum::U0;
210
211use crate::pac::oscctrl::{self, Xoscctrl};
212
213use crate::gpio::{FloatingDisabled, Pin, PinId, PA14, PA15, PB22, PB23};
214use crate::time::Hertz;
215use crate::typelevel::{Decrement, Increment, Sealed};
216
217use super::dfll::DfllId;
218use super::{Enabled, Source};
219
220//==============================================================================
221// XoscToken
222//==============================================================================
223
224/// Singleton token that can be exchanged for an [`Xosc`]
225///
226/// As explained in the [`clock` module documentation](super), instances of
227/// various `Token` types can be exchanged for actual clock types. They
228/// typically represent clocks that are disabled at power-on reset.
229///
230/// [`XoscToken`]s are no different. Both [`Xosc`]s are disabled at power-on
231/// reset. To use an [`Xosc`], you must first exchange the token for an actual
232/// clock with [`Xosc::from_clock`] or [`Xosc::from_crystal`].
233///
234/// [`XoscToken`] is generic over the [`XoscId`], where each corresponding token
235/// represents one of the two respective [`Xosc`]s.
236pub struct XoscToken<X: XoscId> {
237    id: PhantomData<X>,
238}
239
240impl<X: XoscId> XoscToken<X> {
241    /// Create a new instance of [`XoscToken`]
242    ///
243    /// # Safety
244    ///
245    /// Each `XoscToken`s is a singleton. There must never be two simulatenous
246    /// instances with the same [`XoscId`]. See the notes on `Token` types and
247    /// memory safety in the root of the `clock` module for more details.
248    #[inline]
249    pub(super) unsafe fn new() -> Self {
250        Self { id: PhantomData }
251    }
252
253    /// Return a reference to the corresponding XOSCCTRL register
254    #[inline]
255    fn xoscctrl(&self) -> &Xoscctrl {
256        // Safety: Each `XoscToken` only has access to a mutually exclusive set
257        // of registers for the corresponding `XoscId`, and we use a shared
258        // reference to the register block. See the notes on `Token` types and
259        // memory safety in the root of the `clock` module for more details.
260        unsafe { (*crate::pac::Oscctrl::PTR).xoscctrl(X::NUM) }
261    }
262
263    /// Read the STATUS register
264    #[inline]
265    fn status(&self) -> oscctrl::status::R {
266        // Safety: We are only reading from the `STATUS` register, so there is
267        // no risk of memory corruption.
268        unsafe { (*crate::pac::Oscctrl::PTR).status().read() }
269    }
270
271    /// Check whether the XOSC is stable and ready
272    #[inline]
273    fn is_ready(&self) -> bool {
274        let mask = 1 << X::NUM;
275        self.status().bits() & mask != 0
276    }
277
278    /// Check whether the XOSC has triggered failure detection
279    #[inline]
280    fn has_failed(&self) -> bool {
281        let mask = 1 << (X::NUM + 2);
282        self.status().bits() & mask != 0
283    }
284
285    /// Check whether the XOSC has been switched to the safe clock
286    #[inline]
287    fn is_switched(&self) -> bool {
288        let mask = 1 << (X::NUM + 4);
289        self.status().bits() & mask != 0
290    }
291
292    /// Reset the XOSCCTRL register
293    #[inline]
294    fn reset(&self) {
295        self.xoscctrl().reset();
296    }
297
298    /// Switch from the safe clock back to the XOSC clock/oscillator
299    ///
300    /// This bit is cleared by the hardware after successfully switching back
301    #[inline]
302    fn switch_back(&mut self) {
303        self.xoscctrl().modify(|_, w| w.swben().set_bit());
304    }
305
306    /// Enable clock failure detection and set the safe clock divider
307    #[inline]
308    fn enable_failure_detection(&mut self, div: SafeClockDiv) {
309        // Safety: The divider is guaranteed to be in the valid range 0..16.
310        // The PAC is wrong here. It seems to think the field is 4 bits wide and
311        // the set of valid values is only 0..8. The `bits` method should really
312        // be safe here, just like it is for the `STARTUP` field.
313        self.xoscctrl().modify(|_, w| unsafe {
314            w.cfdpresc().bits(div as u8);
315            w.cfden().set_bit()
316        });
317    }
318
319    /// Disable clock failure detection
320    #[inline]
321    fn disable_failure_detection(&mut self) {
322        self.xoscctrl().modify(|_, w| w.cfden().clear_bit());
323    }
324
325    /// Set most of the fields in the XOSCCTRL register
326    #[inline]
327    fn set_xoscctrl(&mut self, settings: Settings) {
328        let xtalen = settings.mode == DynMode::CrystalMode;
329        // Safety: The `IMULT` and `IPTAT` values come from the
330        // `CrystalCurrent`, so they are guaranteed to be valid.
331        self.xoscctrl().modify(|_, w| unsafe {
332            w.startup().bits(settings.start_up as u8);
333            w.enalc().bit(settings.loop_control);
334            w.imult().bits(settings.current.imult());
335            w.iptat().bits(settings.current.iptat());
336            w.lowbufgain().bit(settings.low_buf_gain);
337            w.ondemand().bit(settings.on_demand);
338            w.runstdby().bit(settings.run_standby);
339            w.xtalen().bit(xtalen)
340        });
341    }
342
343    /// Enable the XOSC
344    #[inline]
345    fn enable(&mut self) {
346        self.xoscctrl().modify(|_, w| w.enable().set_bit());
347    }
348
349    /// Disable the XOSC
350    #[inline]
351    fn disable(&mut self) {
352        self.xoscctrl().modify(|_, w| w.enable().clear_bit());
353    }
354}
355
356//==============================================================================
357// Settings
358//==============================================================================
359
360// Collection of XOSCCTRL register fields
361//
362// All of these fields are set in a single write to XOSCCTRL during the call to
363// [`Xosc::enable`]. The remaining fields are only modified after it has been
364// enabled.
365#[derive(Clone, Copy)]
366struct Settings {
367    start_up: StartUpDelay,
368    loop_control: bool,
369    current: CrystalCurrent,
370    low_buf_gain: bool,
371    on_demand: bool,
372    run_standby: bool,
373    mode: DynMode,
374}
375
376//==============================================================================
377// XoscId
378//==============================================================================
379
380/// Type-level enum identifying one of two possible [`Xosc`]s
381///
382/// The types implementing this trait, i.e. [`Xosc0Id`] and [`Xosc1Id`], are
383/// type-level variants of `XoscId`, and they identify one of two possible
384/// external crystal oscillators.
385///
386/// See the documentation on [type-level programming] and specifically
387/// [type-level enums] for more details.
388///
389/// [type-level programming]: crate::typelevel
390/// [type-level enums]: crate::typelevel#type-level-enums
391pub trait XoscId: Sealed {
392    /// Corresponding numeric index
393    const NUM: usize;
394    /// Corresponding XIN [`PinId`]
395    type XIn: PinId;
396    /// Corresponding XOUT [`PinId`]
397    type XOut: PinId;
398}
399
400/// Type-level variant of [`XoscId`] representing the identity of XOSC0
401///
402/// See the documentation on [type-level programming] and specifically
403/// [type-level enums] for more details.
404///
405/// [type-level programming]: crate::typelevel
406/// [type-level enums]: crate::typelevel#type-level-enums
407pub enum Xosc0Id {}
408
409impl Sealed for Xosc0Id {}
410
411impl XoscId for Xosc0Id {
412    const NUM: usize = 0;
413    type XIn = PA14;
414    type XOut = PA15;
415}
416
417/// Type-level variant of [`XoscId`] representing the identity of XOSC1
418///
419/// See the documentation on [type-level programming] and specifically
420/// [type-level enums] for more details.
421///
422/// [type-level programming]: crate::typelevel
423/// [type-level enums]: crate::typelevel#type-level-enums
424pub enum Xosc1Id {}
425
426impl Sealed for Xosc1Id {}
427
428impl XoscId for Xosc1Id {
429    const NUM: usize = 1;
430    type XIn = PB22;
431    type XOut = PB23;
432}
433
434//==============================================================================
435// XIn & XOut
436//==============================================================================
437
438/// Type alias for the [`Xosc`] input [`Pin`]
439pub type XIn<X> = Pin<<X as XoscId>::XIn, FloatingDisabled>;
440
441/// Type alias for the [`Xosc`] output [`Pin`]
442pub type XOut<X> = Pin<<X as XoscId>::XOut, FloatingDisabled>;
443
444//==============================================================================
445// SafeClockDiv
446//==============================================================================
447
448/// Division factor for the safe clock prescaler
449///
450/// If an [`Xosc`] clock failure is detected, the hardware will switch to a safe
451/// clock derived from the [`Dfll`]. This enum sets the divider between the
452/// 48 MHz DFLL and the safe clock frequency. The divider can be any value of
453/// 2^N, with N in the range `0..16`.
454///
455///[`Dfll`]: super::dfll::Dfll
456#[repr(u8)]
457#[derive(Clone, Copy, Default, PartialEq, Eq)]
458pub enum SafeClockDiv {
459    #[default]
460    Div1,
461    Div2,
462    Div4,
463    Div8,
464    Div16,
465    Div32,
466    Div64,
467    Div128,
468    Div256,
469    Div512,
470    Div1024,
471    Div2048,
472    Div4096,
473    Div8192,
474    Div16384,
475    Div32768,
476}
477
478//==============================================================================
479// StartUpDelay
480//==============================================================================
481
482/// Start up delay before continuous [`Xosc`] monitoring takes effect
483///
484/// After a hard reset or waking from sleep, the [`Xosc`] output will remained
485/// masked for the start up period, to ensure an unstable clock is not
486/// propagated into the digital logic.
487///
488/// The start up delay is counted using the [`OscUlp32k`] clock, and the delay
489/// is equal to 2^N clock cycles, where N is selectable in the range `0..16`.
490///
491/// [`OscUlp32k`]: super::osculp32k::OscUlp32k
492#[repr(u8)]
493#[derive(Clone, Copy, Default, PartialEq, Eq)]
494pub enum StartUpDelay {
495    #[default]
496    Delay31us,
497    Delay62us,
498    Delay122us,
499    Delay244us,
500    Delay488us,
501    Delay977us,
502    Delay2ms,
503    Delay4ms,
504    Delay8ms,
505    Delay16ms,
506    Delay31ms,
507    Delay63ms,
508    Delay125ms,
509    Delay250ms,
510    Delay500ms,
511    Delay1s,
512}
513
514//==============================================================================
515// CrystalCurrent
516//==============================================================================
517
518/// Crystal current level
519///
520/// This struct represents an abstraction over the datasheet table for the
521/// `IMULT` and `IPTAT` register fields, which control the current used when an
522/// [`Xosc`] is in [`CrystalMode`]
523///
524/// The variants of this enum are not named according to the explicit frequency
525/// range provided in the datasheet. While the datasheet recommends settings for
526/// each frequency range, it also acknowledges some flexibility in that choice.
527/// Specifically, it notes that users can save power by selecting the next-lower
528/// frequency range if the capacitive load is small.
529#[derive(Clone, Copy, Default, PartialEq, Eq)]
530pub enum CrystalCurrent {
531    /// Used only in [`ClockMode`] to represent the default register values
532    #[default]
533    Zero,
534    /// Typically used for 8 MHz oscillators
535    Low,
536    /// Typically used for 8-16 MHz oscillators
537    Medium,
538    /// Typically used for 16-24 MHz oscillators
539    High,
540    /// Typically used for 24-48 MHz oscillators
541    ExtraHigh,
542}
543
544impl CrystalCurrent {
545    #[inline]
546    fn imult(&self) -> u8 {
547        match self {
548            Self::Zero => 0,
549            Self::Low => 3,
550            Self::Medium => 4,
551            Self::High => 5,
552            Self::ExtraHigh => 6,
553        }
554    }
555
556    #[inline]
557    fn iptat(&self) -> u8 {
558        match self {
559            Self::Zero => 0,
560            Self::Low => 2,
561            Self::Medium => 3,
562            Self::High => 3,
563            Self::ExtraHigh => 3,
564        }
565    }
566}
567
568//==============================================================================
569// DynMode
570//==============================================================================
571
572/// Value-level enum identifying one of two possible [`Xosc`] operating modes
573///
574/// An [`Xosc`] can be sourced from either an external clock or crystal
575/// oscillator. The variants of this enum identify one of these two possible
576/// operating modes.
577///
578/// `DynMode` is the value-level equivalent of [`Mode`].
579#[derive(Clone, Copy, Default, PartialEq, Eq)]
580pub enum DynMode {
581    #[default]
582    ClockMode,
583    CrystalMode,
584}
585
586//==============================================================================
587// Mode
588//==============================================================================
589
590/// Type-level `enum` for the [`Xosc`] operating mode
591///
592/// An [`Xosc`] can be sourced from either an external clock or a cyrstal
593/// oscillator. This type-level `enum` provides two type-level variants,
594/// [`ClockMode`] and [`CrystalMode`], representing these operating modes.
595///
596/// `Mode` is the type-level equivalent of [`DynMode`]. See the documentation on
597/// [type-level programming] and specifically [type-level enums] for more
598/// details.
599///
600/// [type-level programming]: crate::typelevel
601/// [type-level enums]: crate::typelevel#type-level-enums
602pub trait Mode: Sealed {
603    /// Corresponding variant of [`DynMode`]
604    const DYN: DynMode;
605    #[doc(hidden)]
606    type Pins<X: XoscId>;
607}
608
609//==============================================================================
610// ClockMode
611//==============================================================================
612
613/// Type-level variant of the [`Xosc`] operating [`Mode`]
614///
615/// Represents the [`Xosc`] configured to use an externally provided clock.
616///
617/// See the documentation on [type-level programming] and specifically
618/// [type-level enums] for more details.
619///
620/// [type-level programming]: crate::typelevel
621/// [type-level enums]: crate::typelevel#type-level-enums
622pub enum ClockMode {}
623
624impl Sealed for ClockMode {}
625
626impl Mode for ClockMode {
627    const DYN: DynMode = DynMode::ClockMode;
628    type Pins<X: XoscId> = XIn<X>;
629}
630
631//==============================================================================
632// CrystalMode
633//==============================================================================
634
635/// Type-level variant of the [`Xosc`] operating [`Mode`]
636///
637/// Represents the [`Xosc`] configured to use an external crystal oscillator.
638///
639/// See the documentation on [type-level programming] and specifically
640/// [type-level enums] for more details.
641///
642/// [type-level programming]: crate::typelevel
643/// [type-level enums]: crate::typelevel#type-level-enums
644pub enum CrystalMode {}
645
646impl Sealed for CrystalMode {}
647
648impl Mode for CrystalMode {
649    const DYN: DynMode = DynMode::CrystalMode;
650    type Pins<X: XoscId> = (XIn<X>, XOut<X>);
651}
652
653//==============================================================================
654// Xosc
655//==============================================================================
656
657/// An external multipurpose crystal oscillator controller
658///
659/// An `Xosc` interfaces with either an external clock or external crystal
660/// oscillator and delivers the resulting clock to the rest of the clock system.
661///
662/// The type parameter `X` is a [`XoscId`] that determines which of the two
663/// instances this `Xosc` represents ([`Xosc0`] or [`Xosc1`]). The type
664/// parameter `M` represents the operating [`Mode`], either [`ClockMode`] or
665/// [`CrystalMode`].
666///
667/// On its own, an instance of `Xosc` does not represent an enabled XOSC.
668/// Instead, it must first be wrapped with [`Enabled`], which implements
669/// compile-time safety of the clock tree.
670///
671/// Because the terminal call to [`enable`] consumes the `Xosc` and returns an
672/// [`EnabledXosc`], the remaining API uses the builder pattern, where each
673/// method takes and returns `self` by value, allowing them to be easily
674/// chained.
675///
676/// See the [module-level documentation](self) for an example of creating,
677/// configuring and using an `Xosc`.
678///
679/// [`enable`]: Xosc::enable
680pub struct Xosc<X, M>
681where
682    X: XoscId,
683    M: Mode,
684{
685    token: XoscToken<X>,
686    pins: M::Pins<X>,
687    freq: Hertz,
688    settings: Settings,
689}
690
691/// Type alias for the corresponding [`Xosc`]
692pub type Xosc0<M> = Xosc<Xosc0Id, M>;
693
694/// Type alias for the corresponding [`Xosc`]
695pub type Xosc1<M> = Xosc<Xosc1Id, M>;
696
697/// An [`Enabled`] [`Xosc`]
698///
699/// As described in the [`clock` module documentation](super), the [`Enabled`]
700/// wrapper implements compile-time clock tree safety by tracking the number of
701/// consumer clocks and restricting access to the underlying [`Xosc`] to prevent
702/// modification while in use.
703///
704/// As with [`Enabled`], the default value for `N` is `U0`; if left unspecified,
705/// the counter is assumed to be zero.
706pub type EnabledXosc<X, M, N = U0> = Enabled<Xosc<X, M>, N>;
707
708/// Type alias for the corresponding [`EnabledXosc`]
709pub type EnabledXosc0<M, N = U0> = EnabledXosc<Xosc0Id, M, N>;
710
711/// Type alias for the corresponding [`EnabledXosc`]
712pub type EnabledXosc1<M, N = U0> = EnabledXosc<Xosc1Id, M, N>;
713
714impl<X: XoscId> Xosc<X, ClockMode> {
715    /// Create an [`Xosc`] from an external clock, taking ownership of the
716    /// [`XIn`] [`Pin`]
717    ///
718    /// Creating a [`Xosc`] does not modify any of the hardware registers. It
719    /// only creates a struct to track the configuration. The configuration data
720    /// is stored until the user calls [`enable`]. At that point, all of the
721    /// registers are written according to the initialization procedures
722    /// specified in the datasheet, and an [`EnabledXosc`] is returned. The
723    /// `Xosc` is not active or useful until that point.
724    ///
725    /// [`enable`]: Xosc::enable
726    #[inline]
727    pub fn from_clock(token: XoscToken<X>, xin: impl Into<XIn<X>>, freq: impl Into<Hertz>) -> Self {
728        let pins = xin.into();
729        Xosc::new(token, pins, freq.into())
730    }
731
732    /// Consume the [`Xosc`] and release the [`XoscToken`] and [`XIn`] [`Pin`]
733    #[inline]
734    pub fn free(self) -> (XoscToken<X>, XIn<X>) {
735        (self.token, self.pins)
736    }
737}
738
739impl<X: XoscId> Xosc<X, CrystalMode> {
740    /// Create an [`Xosc`] from an external crystal oscillator, taking ownership
741    /// of the [`XIn`] and [`XOut`] [`Pin`]s.
742    ///
743    /// Creating a [`Xosc`] does not modify any of the hardware registers. It
744    /// only creates a struct to track the configuration. The configuration data
745    /// is stored until the user calls [`enable`]. At that point, all of the
746    /// registers are written according to the initialization procedures
747    /// specified in the datasheet, and an [`EnabledXosc`] is returned. The
748    /// `Xosc` is not active or useful until that point.
749    ///
750    /// [`enable`]: Xosc::enable
751    #[inline]
752    pub fn from_crystal(
753        token: XoscToken<X>,
754        xin: impl Into<XIn<X>>,
755        xout: impl Into<XOut<X>>,
756        freq: impl Into<Hertz>,
757    ) -> Self {
758        let pins = (xin.into(), xout.into());
759        Xosc::new(token, pins, freq.into())
760    }
761
762    /// Consume the [`Xosc`] and release the [`XoscToken`], [`XIn`] and [`XOut`]
763    /// [`Pin`]s
764    #[inline]
765    pub fn free(self) -> (XoscToken<X>, XIn<X>, XOut<X>) {
766        let (xin, xout) = self.pins;
767        (self.token, xin, xout)
768    }
769
770    /// Set the [`CrystalCurrent`] drive strength
771    #[inline]
772    pub fn current(mut self, current: CrystalCurrent) -> Self {
773        self.settings.current = current;
774        self
775    }
776
777    /// Toggle automatic loop control
778    ///
779    /// If enabled, the hardware will automatically adjust the oscillator
780    /// amplitude. In most cases, this will lower power consumption.
781    #[inline]
782    pub fn loop_control(mut self, loop_control: bool) -> Self {
783        self.settings.loop_control = loop_control;
784        self
785    }
786
787    /// Modify the oscillator amplitude when automatic loop control is enabled
788    ///
789    /// The datasheet name for this setting is very misleading. When automatic
790    /// loop control is enabled, setting the `LOWBUFGAIN` field to `1` will
791    /// _increase_ the oscillator amplitude by a factor of appoximately 2. This
792    /// can help solve stability issues.
793    #[inline]
794    pub fn low_buf_gain(mut self, low_buf_gain: bool) -> Self {
795        self.settings.low_buf_gain = low_buf_gain;
796        self
797    }
798}
799
800impl<X, M> Xosc<X, M>
801where
802    X: XoscId,
803    M: Mode,
804{
805    #[inline]
806    fn new(token: XoscToken<X>, pins: M::Pins<X>, freq: Hertz) -> Self {
807        let current = match freq.to_Hz() {
808            8_000_000 => CrystalCurrent::Low,
809            8_000_001..=16_000_000 => CrystalCurrent::Medium,
810            16_000_001..=24_000_000 => CrystalCurrent::High,
811            24_000_001..=48_000_000 => CrystalCurrent::ExtraHigh,
812            _ => panic!("The XOSC input frequency must be 8-48 MHz"),
813        };
814        let current = match M::DYN {
815            DynMode::ClockMode => CrystalCurrent::Zero,
816            DynMode::CrystalMode => current,
817        };
818        let settings = Settings {
819            start_up: StartUpDelay::Delay31us,
820            loop_control: false,
821            current,
822            low_buf_gain: false,
823            on_demand: true,
824            run_standby: false,
825            mode: M::DYN,
826        };
827        Self {
828            token,
829            pins,
830            freq,
831            settings,
832        }
833    }
834
835    /// Return the clock or crystal frequency
836    #[inline]
837    pub fn freq(&self) -> Hertz {
838        self.freq
839    }
840
841    /// Set the start up delay before the [`Xosc`] is unmasked and continuously
842    /// monitored
843    ///
844    /// During the start up period, the [`Xosc`] is masked to prevent clock
845    /// instability from propagating to the digital logic. During this time,
846    /// clock failure detection is disabled.
847    #[inline]
848    pub fn start_up_delay(mut self, delay: StartUpDelay) -> Self {
849        self.settings.start_up = delay;
850        self
851    }
852
853    /// Control the [`Xosc`] on-demand behavior
854    ///
855    /// When the on-demand is enabled, the [`Xosc`] will only run in Idle or
856    /// Standby sleep modes if it is requested by a peripheral. Otherwise, its
857    /// behavior is dependent on the run-standby setting.
858    #[inline]
859    pub fn on_demand(mut self, on_demand: bool) -> Self {
860        self.settings.on_demand = on_demand;
861        self
862    }
863
864    /// Control the [`Xosc`] behavior in Standby sleep mode
865    ///
866    /// When `RUNSTDBY` is disabled, the [`Xosc`] will never run in Standby
867    /// sleep mode unless `ONDEMAND` is enabled and the `Xosc` is requested by a
868    /// peripheral. When `RUNSTDBY` is enabled, the `Xosc` will run in Standby
869    /// sleep mode, but it can still be disabled if `ONDEMAND` is enabled and
870    /// the `Xosc` is not requested.
871    #[inline]
872    pub fn run_standby(mut self, run_standby: bool) -> Self {
873        self.settings.run_standby = run_standby;
874        self
875    }
876
877    /// Enable the [`Xosc`], so that it can be used as a clock [`Source`]
878    ///
879    /// As mentioned when creating a new `Xosc`, no hardware registers are
880    /// actually modified until this call. Rather, the desired configuration is
881    /// stored internally, and the `Xosc` is initialized and configured here
882    /// according to the datasheet.
883    ///
884    /// The returned value is an [`EnabledXosc`] that can be used as a clock
885    /// [`Source`] for other clocks.
886    #[inline]
887    pub fn enable(mut self) -> EnabledXosc<X, M> {
888        self.token.reset();
889        self.token.set_xoscctrl(self.settings);
890        self.token.enable();
891        Enabled::new(self)
892    }
893}
894
895impl<X, M> EnabledXosc<X, M>
896where
897    X: XoscId,
898    M: Mode,
899{
900    /// Disable the [`Xosc`]
901    ///
902    /// This method is only implemented for `N = U0`, which means the clock can
903    /// only be disabled when no other clocks consume this [`Xosc`].
904    #[inline]
905    pub fn disable(mut self) -> Xosc<X, M> {
906        self.0.token.disable();
907        self.0
908    }
909}
910
911impl<X, M, N> EnabledXosc<X, M, N>
912where
913    X: XoscId,
914    M: Mode,
915{
916    /// Check whether the [`Xosc`] is stable and ready to be used as a clock
917    /// [`Source`]
918    #[inline]
919    pub fn is_ready(&self) -> bool {
920        self.0.token.is_ready()
921    }
922
923    /// Enable continuous monitoring of the [`Xosc`] for clock failure
924    ///
925    /// Failure detection will continuously monitor the [`Xosc`] to verify it is
926    /// still running. In the event of a failure, the `Xosc` output will be
927    /// switched to the "safe clock".
928    ///
929    /// The safe clock is derived from the DFLL, which runs at 48 MHz. The XOSC
930    /// peripheral provides a prescaler to divide down the 48 MHz DFLL to better
931    /// match the clock it replaces. The prescaler division factor can be any
932    /// power of two, `2^N`, with `N` in the range `0..16`.
933    ///
934    /// For example, if the [`Xosc`] input frequency is 16 MHz, a reasonable
935    /// divider would be 4, becuase the safe clock frequency would be 12 MHz,
936    /// which is closest to 16 MHz.
937    ///
938    /// Note that clock failure is triggered when four safe clock periods pass
939    /// without seeing a rising & falling edge pair on the XOSC clock. Once
940    /// failure is detected, the corresponding bit in the `STATUS` register will
941    /// go high and an interrupt will be triggered.
942    ///
943    /// If the external clock can be fixed, the `Xosc` can be switched back to
944    /// it using [`EnabledXosc::switch_back`].
945    ///
946    /// Because the safe clock makes use of the DFLL, the `Xosc` must register
947    /// as a consumer of the [`EnabledDfll`] and [`Increment`] its counter.
948    ///
949    /// [`EnabledDfll`]: super::dfll::EnabledDfll
950    #[inline]
951    pub fn enable_failure_detection<S>(&mut self, dfll: S, div: SafeClockDiv) -> S::Inc
952    where
953        S: Source<Id = DfllId> + Increment,
954    {
955        self.0.token.enable_failure_detection(div);
956        dfll.inc()
957    }
958
959    /// Check whether the [`Xosc`] has triggered clock failure detection
960    ///
961    /// Failure detection must be enabled for this to return `true`. Failure is
962    /// triggered when four safe clock periods pass without seeing a rising &
963    /// falling edge pair on the XOSC clock.
964    ///
965    /// See [`EnabledXosc::enable_failure_detection`] for more details.
966    #[inline]
967    pub fn has_failed(&self) -> bool {
968        self.0.token.has_failed()
969    }
970
971    /// Check whether the [`Xosc`] has been switched to the safe clock
972    ///
973    /// Returns `true` if the [`Xosc`] has been switched to the safe clock. This
974    /// will only occur if clock failure detection is enabled.
975    #[inline]
976    pub fn is_switched(&self) -> bool {
977        self.0.token.is_switched()
978    }
979
980    /// Attempt to switch from the safe clock back to the external clock
981    ///
982    /// This function will set the switch back bit (`SWBEN`) in the `XOSCCTRL`
983    /// register. Once the hardware has successfully switched back, this bit
984    /// will be automatically cleared.
985    ///
986    /// Users can check whether switching back was successful by checking the
987    /// `STATUS` register with [`EnabledXosc::is_switched`].
988    #[inline]
989    pub fn switch_back(&mut self) {
990        self.0.token.switch_back()
991    }
992
993    /// Disable continuous monitoring of the [`Xosc`] for clock failure
994    ///
995    /// Once failure monitoring is disabled, the DFLL is no longer used as the
996    /// safe clock, so the [`EnabledDfll`] counter can be [`Decrement`]ed.
997    ///
998    /// [`EnabledDfll`]: super::dfll::EnabledDfll
999    #[inline]
1000    pub fn disable_failure_detection<S>(&mut self, dfll: S) -> S::Dec
1001    where
1002        S: Source<Id = DfllId> + Decrement,
1003    {
1004        self.0.token.disable_failure_detection();
1005        dfll.dec()
1006    }
1007}
1008
1009//==============================================================================
1010// Source
1011//==============================================================================
1012
1013impl<X, M, N> Source for EnabledXosc<X, M, N>
1014where
1015    X: XoscId,
1016    M: Mode,
1017{
1018    type Id = X;
1019
1020    #[inline]
1021    fn freq(&self) -> Hertz {
1022        self.0.freq()
1023    }
1024}