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!());