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}