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

1//! RTC oscillator
2//!
3//! This module provides a representation of the RTC oscillator, which
4//! can be selected from one of four possible options. The [`RtcOsc`] type
5//! represents proof that the RTC oscillator has been properly configured and
6//! that its clock source has been locked and cannot be modified.
7//!
8//! For the moment, the [`RtcOsc`] is not used anywhere else in the HAL. A
9//! future change to the [`rtc`](crate::rtc) module should either take ownership
10//! of the [`RtcOsc`] at creation of the [`Rtc`] or replace the [`RtcOsc`]
11//! entirely with an integrated [`Rtc`] API.
12//!
13//! # Example
14//!
15//! To create the [`RtcOsc`] let's start by using [`clock_system_at_reset`] to
16//! access the HAL clocking structs.
17//!
18//! ```no_run
19//! use atsamd_hal::{
20//!     pac::Peripherals,
21//!     thumbv7em::clock::v2::{clock_system_at_reset, osculp32k::OscUlp32k, rtcosc::RtcOsc},
22//! };
23//! let mut pac = Peripherals::take().unwrap();
24//! let (buses, clocks, tokens) = clock_system_at_reset(
25//!     pac.oscctrl,
26//!     pac.osc32kctrl,
27//!     pac.gclk,
28//!     pac.mclk,
29//!     &mut pac.nvmctrl,
30//! );
31//! ```
32//!
33//! Next, let's enable the 32 kHz output of the internal, ultra-low power
34//! oscillator.
35//!
36//! ```no_run
37//! # use atsamd_hal::{
38//! #     pac::Peripherals,
39//! #     thumbv7em::clock::v2::{clock_system_at_reset, osculp32k::OscUlp32k, rtcosc::RtcOsc},
40//! # };
41//! # let mut pac = Peripherals::take().unwrap();
42//! # let (buses, clocks, tokens) = clock_system_at_reset(
43//! #     pac.oscctrl,
44//! #     pac.osc32kctrl,
45//! #     pac.gclk,
46//! #     pac.mclk,
47//! #     &mut pac.nvmctrl,
48//! # );
49//! let (osculp32k, base) = OscUlp32k::enable(tokens.osculp32k.osculp32k, clocks.osculp32k_base);
50//! ```
51//!
52//! Finally, we can use the [`EnabledOscUlp32k`] to enabled the [`RtcOsc`].
53//!
54//! ```no_run
55//! # use atsamd_hal::{
56//! #     pac::Peripherals,
57//! #     thumbv7em::clock::v2::{clock_system_at_reset, osculp32k::OscUlp32k, rtcosc::RtcOsc},
58//! # };
59//! # let mut pac = Peripherals::take().unwrap();
60//! # let (buses, clocks, tokens) = clock_system_at_reset(
61//! #     pac.oscctrl,
62//! #     pac.osc32kctrl,
63//! #     pac.gclk,
64//! #     pac.mclk,
65//! #     &mut pac.nvmctrl,
66//! # );
67//! # let (osculp32k, base) = OscUlp32k::enable(tokens.osculp32k.osculp32k, clocks.osculp32k_base);
68//! let (rtc_osc, osculp32k) = RtcOsc::enable(tokens.rtcosc, osculp32k);
69//! ```
70//!
71//! The entire example is provided below.
72//!
73//! ```no_run
74//! use atsamd_hal::{
75//!     pac::Peripherals,
76//!     thumbv7em::clock::v2::{clock_system_at_reset, osculp32k::OscUlp32k, rtcosc::RtcOsc},
77//! };
78//! let mut pac = Peripherals::take().unwrap();
79//! let (buses, clocks, tokens) = clock_system_at_reset(
80//!     pac.oscctrl,
81//!     pac.osc32kctrl,
82//!     pac.gclk,
83//!     pac.mclk,
84//!     &mut pac.nvmctrl,
85//! );
86//! let (osculp32k, base) = OscUlp32k::enable(tokens.osculp32k.osculp32k, clocks.osculp32k_base);
87//! let (rtc_osc, osculp32k) = RtcOsc::enable(tokens.rtcosc, osculp32k);
88//! ```
89//!
90//! [`clock_system_at_reset`]: super::clock_system_at_reset
91//! [`Rtc`]: crate::rtc::Rtc
92//! [`EnabledOscUlp32k`]: super::osculp32k::EnabledOscUlp32k
93
94use core::marker::PhantomData;
95
96use crate::pac::osc32kctrl::rtcctrl::Rtcselselect;
97use crate::pac::osc32kctrl::Rtcctrl;
98use crate::pac::Osc32kctrl;
99
100use crate::time::Hertz;
101use crate::typelevel::{Decrement, Increment};
102
103use super::osculp32k::{OscUlp1kId, OscUlp32kId};
104use super::xosc32k::{Xosc1kId, Xosc32kId};
105use super::Source;
106
107//==============================================================================
108// RtcOscToken
109//==============================================================================
110
111/// Token used to retrieve the RTC oscillator
112/// Singleton token that can be exchanged for the [`RtcOsc`]
113///
114/// As explained in the [`clock` module documentation](super), instances of
115/// various `Token` types can be exchanged for actual clock types. They
116/// typically represent clocks that are disabled at power-on reset.
117///
118/// To use the [`RtcOsc`], you must first exchange this token with the
119/// [`RtcOsc::enable`] function.
120pub struct RtcOscToken(());
121
122impl RtcOscToken {
123    /// Create a new instance of [`RtcOscToken`]
124    ///
125    /// # Safety
126    ///
127    /// The `RtcOscToken` is a singleton. There must never be two simulatenous
128    /// instances of it. See the notes on `Token` types and memory safety in the
129    /// root of the `clock` module for more details.
130    #[inline]
131    pub(super) unsafe fn new() -> Self {
132        Self(())
133    }
134
135    #[inline]
136    fn rtcctrl(&self) -> &Rtcctrl {
137        // Safety: The `RtcOsc` only has exclusive access to the RTCCTRL
138        // register. See the notes on `Token` types and memory safety in the
139        // root of the `clock` module for more details.
140        unsafe { (*Osc32kctrl::PTR).rtcctrl() }
141    }
142
143    #[inline]
144    fn set_source(&mut self, source: DynRtcSourceId) {
145        self.rtcctrl().write(|w| w.rtcsel().variant(source.into()));
146    }
147}
148
149//==============================================================================
150// DynRtcSourceId
151//==============================================================================
152
153/// Value-level enum of possible clock sources for the [`RtcOsc`]
154///
155/// The variants of this enum identify one of four possible clock sources for
156/// the [`RtcOsc`].
157///
158/// `DynRtcSourceId` is the value-level equivalent of [`RtcSourceId`].
159#[derive(Copy, Clone, PartialEq, Eq)]
160pub enum DynRtcSourceId {
161    /// The RTC is sourced from the [`OscUlp1k`](super::osculp32k::OscUlp1k)
162    OscUlp1k,
163    /// The RTC is sourced from the [`OscUlp32k`](super::osculp32k::OscUlp32k)
164    OscUlp32k,
165    /// The RTC is sourced from the [`Xosc1k`](super::xosc32k::Xosc1k)
166    Xosc1k,
167    /// The RTC is sourced from the [`Xosc32k`](super::xosc32k::Xosc32k)
168    Xosc32k,
169}
170
171impl From<DynRtcSourceId> for Rtcselselect {
172    #[inline]
173    fn from(source: DynRtcSourceId) -> Self {
174        use DynRtcSourceId::*;
175        match source {
176            OscUlp1k => Self::Ulp1k,
177            OscUlp32k => Self::Ulp32k,
178            Xosc1k => Self::Xosc1k,
179            Xosc32k => Self::Xosc32k,
180        }
181    }
182}
183
184//==============================================================================
185// RtcSourceId
186//==============================================================================
187
188/// Type-level `enum` for the RTC oscillator clock source
189///
190/// The RTC can be sourced from any of [`OscUlp1k`](super::osculp32k::OscUlp1k),
191/// [`OscUlp32k`](super::osculp32k::OscUlp32k),
192/// [`Xosc1k`](super::xosc32k::Xosc1k) or [`Xosc32k`](super::xosc32k::Xosc32k).
193///
194/// See the [type-level enum] documentation for more details on the pattern.
195///
196/// [type-level enum]: crate::typelevel#type-level-enum
197/// Type-level enum of possible clock [`Source`]s for the [`RtcOsc`]
198///
199/// The types implementing this trait are type-level variants of `RtcSourceId`,
200/// and they identify one of four possible clock [`Source`]s for the [`RtcOsc`].
201/// All implementers of this trait are [`Id` types](super#id-types), which are
202/// described in more detail in the [`clock` module documentation](super).
203///
204/// `RtcSourceId` is the type-level equivalent of [`DynRtcSourceId`]. See the
205/// documentation on [type-level programming] and specifically
206/// [type-level enums] for more details.
207///
208/// [type-level programming]: crate::typelevel
209/// [type-level enums]: crate::typelevel#type-level-enums
210pub trait RtcSourceId {
211    /// Corresponding [`DynRtcSourceId`]
212    const DYN: DynRtcSourceId;
213    /// Clock source frequency, either 1 kHz or 32 kHz
214    const FREQ: Hertz;
215}
216
217impl RtcSourceId for OscUlp1kId {
218    const DYN: DynRtcSourceId = DynRtcSourceId::OscUlp1k;
219    const FREQ: Hertz = Hertz::Hz(1024);
220}
221impl RtcSourceId for OscUlp32kId {
222    const DYN: DynRtcSourceId = DynRtcSourceId::OscUlp32k;
223    const FREQ: Hertz = Hertz::Hz(32_768);
224}
225impl RtcSourceId for Xosc1kId {
226    const DYN: DynRtcSourceId = DynRtcSourceId::Xosc1k;
227    const FREQ: Hertz = Hertz::Hz(1024);
228}
229impl RtcSourceId for Xosc32kId {
230    const DYN: DynRtcSourceId = DynRtcSourceId::Xosc32k;
231    const FREQ: Hertz = Hertz::Hz(32_768);
232}
233
234//==============================================================================
235// RtcOsc
236//==============================================================================
237
238/// Oscillator for the [`Rtc`]
239///
240/// The `RtcOsc` represents proof that a clock source for the [`Rtc`] has been
241/// selected and configured. It also guarantees that the clock source for the
242/// RTC will not be modified or disabled while it is in use.
243///
244/// See the [module-level documentation](self) for an example of creating the
245/// `RtcOsc`.
246///
247/// [`Rtc`]: crate::rtc::Rtc
248pub struct RtcOsc<I: RtcSourceId> {
249    token: RtcOscToken,
250    source: PhantomData<I>,
251}
252
253impl<I: RtcSourceId> RtcOsc<I> {
254    /// Consume the [`RtcOscToken`] and return the [`RtcOsc`]
255    ///
256    /// Enabling the [`RtcOsc`] will [`Increment`] the consumer count of its
257    /// [`Enabled`](super::Enabled) clock [`Source`].
258    #[inline]
259    pub fn enable<S>(mut token: RtcOscToken, source: S) -> (Self, S::Inc)
260    where
261        S: Source<Id = I> + Increment,
262    {
263        token.set_source(I::DYN);
264        let rtc_osc = Self {
265            token,
266            source: PhantomData,
267        };
268        (rtc_osc, source.inc())
269    }
270
271    /// Consume the [`RtcOsc`] and return the [`RtcOscToken`]
272    ///
273    /// Disabling the `RtcOsc` will [`Decrement`] the consumer count of its
274    /// [`Enabled`](super::Enabled) clock [`Source`].
275    #[inline]
276    pub fn disable<S>(self, source: S) -> (RtcOscToken, S::Dec)
277    where
278        S: Source<Id = I> + Decrement,
279    {
280        (self.token, source.dec())
281    }
282
283    /// Return the [`RtcOsc`] frequency, which can either be 1 kHz or 32 kHz
284    #[inline]
285    pub fn freq(&self) -> Hertz {
286        I::FREQ
287    }
288}