atsamd_hal/peripherals/clock/d5x/v2/pclk.rs
1//! # Peripheral Channel Clocks
2//!
3//! ## Overview
4//!
5//! Peripheral channel clocks, or [`Pclk`]s, connect generic clock controllers
6//! ([`Gclk`]s) to various peripherals within the chip. Each [`Pclk`] maps 1:1
7//! with a corresponding peripheral.
8//!
9//! The 48 possible [`Pclk`]s are distinguished by their corresponding
10//! [`PclkId`] types. Ideally, each [`PclkId`] type would be a relevant type
11//! from a corresponding HAL module. For example, each of the eight different
12//! [`Sercom`] types implements [`PclkId`]. However, the HAL does not yet
13//! support all peripherals, nor have all existing HAL peripherals been
14//! integrated with `clock::v2`. In those cases, a dummy type is defined in the
15//! [`clock::v2::types`] module.
16//!
17//! [`Pclk`]s are typically leaves in the clock tree. The only exceptions are
18//! [`Pclk`]s used for the [`DFLL`] or [`DPLL`] peripherals. In those cases, the
19//! [`Pclk`] acts as a branch clock.
20//!
21//! Each [`Pclk`] powers only a single peripheral; they do not act as general
22//! purpose clock [`Source`]s for other clocks in the tree. As a result, they do
23//! not need to be wrapped with [`Enabled`].
24//!
25//! [`Pclk`]s also do not have any meaningful configuration beyond identifying
26//! which [`EnabledGclk`] is its [`Source`]. Consequently, [`PclkToken`]s can be
27//! directly converted into enabled [`Pclk`]s with [`Pclk::enable`].
28//!
29//! See the [`clock` module documentation] for a more thorough explanation of
30//! the various concepts discussed above.
31//!
32//! ## Example
33//!
34//! The following example shows how to enable the [`Pclk`] for [`Sercom0`]. It
35//! derives the [`Sercom0`] clock from [`EnabledGclk0`], which is already
36//! running at power-on reset. In doing so, the [`EnabledGclk0`] counter is
37//! [`Increment`]ed.
38//!
39//! ```no_run
40//! use atsamd_hal::{
41//! clock::v2::{clock_system_at_reset, pclk::Pclk},
42//! pac::Peripherals,
43//! };
44//! let mut pac = Peripherals::take().unwrap();
45//! let (buses, clocks, tokens) = clock_system_at_reset(
46//! pac.oscctrl,
47//! pac.osc32kctrl,
48//! pac.gclk,
49//! pac.mclk,
50//! &mut pac.nvmctrl,
51//! );
52//! let (pclk_sercom0, gclk0) = Pclk::enable(tokens.pclks.sercom0, clocks.gclk0);
53//! ```
54//!
55//! [`Gclk`]: super::gclk::Gclk
56//! [`DFLL`]: super::dfll
57//! [`DPLL`]: super::dpll
58//! [`Enabled`]: super::Enabled
59//! [`EnabledGclk`]: super::gclk::EnabledGclk
60//! [`EnabledGclk0`]: super::gclk::EnabledGclk0
61//! [`clock` module documentation]: super
62//! [`clock::v2::types`]: super::types
63//! [`Sercom`]: crate::sercom::Sercom
64
65use atsamd_hal_macros::hal_macro_helper;
66
67use core::marker::PhantomData;
68
69use paste::paste;
70use seq_macro::seq;
71
72use crate::pac;
73use crate::pac::gclk::pchctrl::Genselect;
74
75use crate::time::Hertz;
76use crate::typelevel::{Decrement, Increment, Sealed};
77
78use super::Source;
79use super::gclk::{DynGclkId, GclkId};
80
81//==============================================================================
82// PclkToken
83//==============================================================================
84
85/// Singleton token that can be exchanged for a [`Pclk`]
86///
87/// As explained in the [`clock` module documentation](super), instances of
88/// various `Token` types can be exchanged for actual clock types. They
89/// typically represent clocks that are disabled at power-on reset.
90///
91/// [`PclkToken`]s are no different. All [`Pclk`]s are disabled at power-on
92/// reset. To use a [`Pclk`], you must first exchange the token for an actual
93/// clock with the [`Pclk::enable`] function.
94///
95/// [`PclkToken`] is generic over the [`PclkId`], where each token represents a
96/// corresponding peripheral clock channel.
97pub struct PclkToken<P: PclkId> {
98 pclk: PhantomData<P>,
99}
100
101impl<P: PclkId> PclkToken<P> {
102 /// Create a new instance of [`PclkToken`]
103 ///
104 /// # Safety
105 ///
106 /// Each `PclkToken`s is a singleton. There must never be two simulatenous
107 /// instances with the same [`PclkId`]. See the notes on `Token` types and
108 /// memory safety in the root of the `clock` module for more details.
109 #[inline]
110 pub(super) unsafe fn new() -> Self {
111 PclkToken { pclk: PhantomData }
112 }
113
114 /// Access the corresponding `PCHCTRL` register
115 #[inline]
116 fn pchctrl(&self) -> &pac::gclk::Pchctrl {
117 // Safety: Each `PclkToken` only has access to a mutually exclusive set
118 // of registers for the corresponding `PclkId`, and we use a shared
119 // reference to the register block. See the notes on `Token` types and
120 // memory safety in the root of the `clock` module for more details.
121 unsafe { (*pac::Gclk::PTR).pchctrl(P::DYN as usize) }
122 }
123
124 /// Set the [`Pclk`] source
125 #[inline]
126 fn set_source(&mut self, source: DynPclkSourceId) {
127 self.pchctrl()
128 .modify(|_, w| w.r#gen().variant(source.into()));
129 }
130
131 /// Enable the [`Pclk`]
132 #[inline]
133 fn enable(&mut self) {
134 self.pchctrl().modify(|_, w| w.chen().set_bit());
135 }
136
137 /// Disable the [`Pclk`]
138 #[inline]
139 fn disable(&mut self) {
140 self.pchctrl().modify(|_, w| w.chen().clear_bit());
141 }
142}
143
144//==============================================================================
145// PclkId types
146//==============================================================================
147
148/// Module containing only the types implementing [`PclkId`]
149///
150/// Because there are so many types that implement [`PclkId`], it is helpful to
151/// have them defined in a separate module, so that you can import all of them
152/// using a wildcard (`*`) without importing anything else, i.e.
153///
154/// ```
155/// use atsamd_hal::clock::v2::pclk::ids::*;
156/// ```
157pub mod ids {
158 use atsamd_hal_macros::hal_cfg;
159
160 pub use crate::sercom::{Sercom0, Sercom1, Sercom2, Sercom3, Sercom4, Sercom5};
161
162 #[hal_cfg("sercom6")]
163 pub use crate::sercom::Sercom6;
164 #[hal_cfg("sercom7")]
165 pub use crate::sercom::Sercom7;
166
167 pub use super::super::dfll::DfllId;
168 pub use super::super::dpll::{Dpll0Id, Dpll1Id};
169
170 pub use super::super::types::{
171 Ac, Adc0, Adc1, CM4Trace, Ccl, Dac, Eic, EvSys0, EvSys1, EvSys2, EvSys3, EvSys4, EvSys5,
172 EvSys6, EvSys7, EvSys8, EvSys9, EvSys10, EvSys11, FreqMMeasure, FreqMReference, PDec,
173 Sdhc0, SlowClk, Tc0Tc1, Tc2Tc3, Tcc0Tcc1, Tcc2Tcc3, Usb,
174 };
175
176 #[hal_cfg("can0")]
177 pub use super::super::types::Can0;
178 #[hal_cfg("can1")]
179 pub use super::super::types::Can1;
180 #[hal_cfg("sdhc1")]
181 pub use super::super::types::Sdhc1;
182 #[hal_cfg(all("tc4", "tc5"))]
183 pub use super::super::types::Tc4Tc5;
184 #[hal_cfg(all("tc6", "tc7"))]
185 pub use super::super::types::Tc6Tc7;
186 #[hal_cfg("tcc4")]
187 pub use super::super::types::Tcc4;
188 #[hal_cfg("i2s")]
189 pub use super::super::types::{I2S0, I2S1};
190}
191
192use ids::*;
193
194/// Append the list of all [`PclkId`] types and `snake_case` id names to the
195/// arguments of a macro call
196///
197/// This macro will perform the embedded macro call with a list of tuples
198/// appended to the arguments. Each tuple contains a type implementing
199/// [`PclkId`], its corresponding `PCHCTRL` register index, and the `snake_case`
200/// name of the corresponding token in the [`PclkTokens`] struct.
201///
202/// **Note:** The entries within [`DynPclkId`] do not match the type names.
203/// Rather, they match the `snake_case` names converted to `CamelCase`.
204///
205/// An optional attribute is added just before each tuple. These are mainly used
206/// to declare the conditions under which the corresponding peripheral exists.
207///
208/// The example below shows the pattern that should be used to match against the
209/// appended tokens.
210///
211/// ```ignore
212/// macro_rules! some_macro {
213/// (
214/// $first_arg:tt,
215/// $second_arg:tt
216/// $(
217/// $( #[$cfg:meta] )?
218/// ($Type:ident = $N:literal, $Id:ident)
219/// )+
220/// ) =>
221/// {
222/// // implementation here ...
223/// }
224/// }
225///
226/// with_pclk_types_ids!(some_macro!(first, second));
227/// ```
228#[hal_macro_helper]
229macro_rules! with_pclk_types_ids {
230 ( $some_macro:ident ! ( $( $args:tt )* ) ) => {
231 $some_macro!(
232 $( $args )*
233 (DfllId = 0, dfll)
234 (Dpll0Id = 1, dpll0)
235 (Dpll1Id = 2, dpll1)
236 (SlowClk = 3, slow)
237 (Eic = 4, eic)
238 (FreqMMeasure = 5, freq_m_measure)
239 (FreqMReference = 6, freq_m_reference)
240 (Sercom0 = 7, sercom0)
241 (Sercom1 = 8, sercom1)
242 (Tc0Tc1 = 9, tc0_tc1)
243 (Usb = 10, usb)
244 (EvSys0 = 11, ev_sys0)
245 (EvSys1 = 12, ev_sys1)
246 (EvSys2 = 13, ev_sys2)
247 (EvSys3 = 14, ev_sys3)
248 (EvSys4 = 15, ev_sys4)
249 (EvSys5 = 16, ev_sys5)
250 (EvSys6 = 17, ev_sys6)
251 (EvSys7 = 18, ev_sys7)
252 (EvSys8 = 19, ev_sys8)
253 (EvSys9 = 20, ev_sys9)
254 (EvSys10 = 21, ev_sys10)
255 (EvSys11 = 22, ev_sys11)
256 (Sercom2 = 23, sercom2)
257 (Sercom3 = 24, sercom3)
258 (Tcc0Tcc1 = 25, tcc0_tcc1)
259 (Tc2Tc3 = 26, tc2_tc3)
260 #[hal_cfg("can0")]
261 (Can0 = 27, can0)
262 #[hal_cfg("can1")]
263 (Can1 = 28, can1)
264 (Tcc2Tcc3 = 29, tcc2_tcc3)
265 #[hal_cfg(all("tc4", "tc5"))]
266 (Tc4Tc5 = 30, tc4_tc5)
267 (PDec = 31, pdec)
268 (Ac = 32, ac)
269 (Ccl = 33, ccl)
270 (Sercom4 = 34, sercom4)
271 (Sercom5 = 35, sercom5)
272 #[hal_cfg("sercom6")]
273 (Sercom6 = 36, sercom6)
274 #[hal_cfg("sercom7")]
275 (Sercom7 = 37, sercom7)
276 #[hal_cfg("tcc4")]
277 (Tcc4 = 38, tcc4)
278 #[hal_cfg(all("tc6", "tc7"))]
279 (Tc6Tc7 = 39, tc6_tc7)
280 (Adc0 = 40, adc0)
281 (Adc1 = 41, adc1)
282 (Dac = 42, dac)
283 #[hal_cfg("i2s")]
284 (I2S0 = 43, i2s0)
285 #[hal_cfg("i2s")]
286 (I2S1 = 44, i2s1)
287 (Sdhc0 = 45, sdhc0)
288 #[hal_cfg("sdhc1")]
289 (Sdhc1 = 46, sdhc1)
290 (CM4Trace = 47, cm4_trace)
291 );
292 };
293}
294
295//==============================================================================
296// DynPclkId
297//==============================================================================
298
299macro_rules! dyn_pclk_id {
300 (
301 $(
302 $( #[$cfg:meta] )?
303 ($Type:ident = $N:literal, $id:ident)
304 )+
305 ) => {
306 paste! {
307 /// Value-level enum identifying one of the 48 possible [`Pclk`]s
308 ///
309 /// The variants of this enum identify one of the 48 possible
310 /// peripheral channel clocks. When cast to a `u8`, each variant
311 /// maps to its corresponding `PCHCTRL` index.
312 ///
313 /// `DynPclkId` is the value-level equivalent of [`PclkId`].
314 #[repr(u8)]
315 pub enum DynPclkId {
316 $(
317 $( #[$cfg] )?
318 [<$id:camel>] = $N,
319 )+
320 }
321
322 $(
323 $( #[$cfg] )?
324 impl PclkId for $Type {
325 const DYN: DynPclkId = DynPclkId::[<$id:camel>];
326 }
327 )+
328 }
329 };
330}
331
332with_pclk_types_ids!(dyn_pclk_id!());
333
334//==============================================================================
335// PclkId
336//==============================================================================
337
338/// Type-level enum identifying one of the 48 possible [`Pclk`]s
339///
340/// The types implementing this trait, e.g. [`Sercom0`] or [`DfllId`], are
341/// type-level variants of `PclkId`, and they identify one of the 48 possible
342/// peripheral channel clocks.
343///
344/// `PclkId` is the type-level equivalent of [`DynPclkId`]. See the
345/// documentation on [type-level programming] and specifically
346/// [type-level enums] for more details.
347///
348/// [type-level programming]: crate::typelevel
349/// [type-level enums]: crate::typelevel#type-level-enums
350pub trait PclkId: Sealed {
351 /// Corresponding variant of [`DynPclkId`]
352 const DYN: DynPclkId;
353}
354
355//==============================================================================
356// DynPclkSourceId
357//==============================================================================
358
359/// Value-level enum of possible clock sources for a [`Pclk`]
360///
361/// The variants of this enum identify the [`Gclk`] used as a clock source for
362/// a given [`Pclk`]. Because the variants are identical to [`DynGclkId`], we
363/// simply define it as a type alias.
364///
365/// `DynPclkSourceId` is the value-level equivalent of [`PclkSourceId`].
366///
367/// [`Gclk`]: super::gclk::Gclk
368pub type DynPclkSourceId = DynGclkId;
369
370/// Convert from [`DynPclkSourceId`] to the equivalent [PAC](crate::pac) type
371impl From<DynPclkSourceId> for Genselect {
372 fn from(source: DynPclkSourceId) -> Self {
373 seq!(N in 0..=11 {
374 match source {
375 #(
376 DynGclkId::Gclk~N => Genselect::Gclk~N,
377 )*
378 }
379 })
380 }
381}
382
383//==============================================================================
384// PclkSourceId
385//==============================================================================
386
387/// Type-level enum of possible clock [`Source`]s for a [`Pclk`]
388///
389/// The types implementing this trait are type-level variants of `PclkSourceId`,
390/// and they identify the [`Gclk`] acting as a clock [`Source`] for a given
391/// [`Pclk`]. Accordingly, all implementers of this trait are [`GclkId`] types,
392/// and this trait is simply a trait alias for [`GclkId`]. `Id` types in general
393/// are described in more detail in the [`clock` module documentation](super).
394///
395/// `PclkSourceId` is the type-level equivalent of [`DynPclkSourceId`]. See the
396/// documentation on [type-level programming] and specifically
397/// [type-level enums] for more details.
398///
399/// [`Gclk`]: super::gclk::Gclk
400/// [type-level programming]: crate::typelevel
401/// [type-level enums]: crate::typelevel#type-level-enums
402pub trait PclkSourceId: GclkId {}
403
404impl<G: GclkId> PclkSourceId for G {}
405
406//==============================================================================
407// Pclk
408//==============================================================================
409
410/// Peripheral channel clock for a given peripheral
411///
412/// Peripheral channel clocks connect generic clock generators ([`Gclk`]s) to
413/// various peripherals. `Pclk`s usually act as leaves in the clock tree, except
414/// when they feed the [`DFLL`] and [`DPLL`] peripherals.
415///
416/// The type parameter `P` is a [`PclkId`] that determines which of the 48
417/// peripherals this [`Pclk`] feeds. The type parameter `I` represents the `Id`
418/// type for the [`EnabledGclk`] acting as the `Pclk`'s [`Source`]. It must be
419/// one of the valid [`PclkSourceId`]s, which is simply a trait alias for
420/// [`GclkId`]. See the [`clock` module documentation](super) for more detail on
421/// `Id` types.
422///
423/// `Pclk`s cannot act as general purpose clock [`Source`]s; rather, they map
424/// 1:1 with corresponding peripherals. Thus, enabled `Pclk`s do not need a
425/// compile-time counter of consumer clocks, so they are not wrapped with
426/// [`Enabled`]. Enabled `Pclk`s are created directly from [`PclkToken`]s with
427/// [`Pclk::enable`].
428///
429/// See the [module-level documentation](self) for an example.
430///
431/// [`Enabled`]: super::Enabled
432/// [`Gclk`]: super::gclk::Gclk
433/// [`EnabledGclk`]: super::gclk::EnabledGclk
434/// [`DFLL`]: super::dfll
435/// [`DPLL`]: super::dpll
436pub struct Pclk<P, I>
437where
438 P: PclkId,
439 I: PclkSourceId,
440{
441 token: PclkToken<P>,
442 src: PhantomData<I>,
443 freq: Hertz,
444}
445
446impl<P, I> Pclk<P, I>
447where
448 P: PclkId,
449 I: PclkSourceId,
450{
451 pub(super) fn new(token: PclkToken<P>, freq: Hertz) -> Self {
452 Self {
453 token,
454 src: PhantomData,
455 freq,
456 }
457 }
458
459 /// Create and enable a [`Pclk`]
460 ///
461 /// Creating a [`Pclk`] immediately enables the corresponding peripheral
462 /// channel clock. It also [`Increment`]s the [`Source`]'s [`Enabled`]
463 /// counter.
464 ///
465 /// Note that the [`Source`] will always be an [`EnabledGclk`].
466 ///
467 /// [`Enabled`]: super::Enabled
468 /// [`EnabledGclk`]: super::gclk::EnabledGclk
469 #[inline]
470 pub fn enable<S>(mut token: PclkToken<P>, gclk: S) -> (Self, S::Inc)
471 where
472 S: Source<Id = I> + Increment,
473 {
474 let freq = gclk.freq();
475 token.set_source(I::DYN);
476 token.enable();
477 let pclk = Pclk::new(token, freq);
478 (pclk, gclk.inc())
479 }
480
481 /// Disable and destroy a [`Pclk`]
482 ///
483 /// Consume the [`Pclk`], release the [`PclkToken`], and [`Decrement`] the
484 /// [`EnabledGclk`]'s counter
485 ///
486 /// [`Enabled`]: super::Enabled
487 /// [`EnabledGclk`]: super::gclk::EnabledGclk
488 #[inline]
489 pub fn disable<S>(mut self, gclk: S) -> (PclkToken<P>, S::Dec)
490 where
491 S: Source<Id = I> + Decrement,
492 {
493 self.token.disable();
494 (self.token, gclk.dec())
495 }
496
497 /// Return the [`Pclk`] frequency
498 #[inline]
499 pub fn freq(&self) -> Hertz {
500 self.freq
501 }
502}
503
504impl<P, I> Sealed for Pclk<P, I>
505where
506 P: PclkId,
507 I: PclkSourceId,
508{
509}
510
511//==============================================================================
512// PclkTokens
513//==============================================================================
514
515macro_rules! define_pclk_tokens_struct {
516 (
517 $(
518 $( #[$cfg:meta] )?
519 ($Type:ident = $_:literal, $id:ident)
520 )+
521 ) =>
522 {
523 /// Set of [`PclkToken`]s representing the disabled [`Pclk`]s at
524 /// power-on reset
525 pub struct PclkTokens {
526 $(
527 $( #[$cfg] )?
528 pub $id: PclkToken<$Type>,
529 )+
530 }
531
532 impl PclkTokens {
533 /// Create the set of [`PclkToken`]s
534 ///
535 /// # Safety
536 ///
537 /// All invariants required by `PclkToken::new` must be upheld here
538 #[inline]
539 pub(super) fn new() -> Self {
540 unsafe {
541 Self {
542 $(
543 $( #[$cfg] )?
544 $id: PclkToken::new(),
545 )+
546 }
547 }
548 }
549 }
550 };
551}
552
553with_pclk_types_ids!(define_pclk_tokens_struct!());