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