atsamd_hal/peripherals/clock/d5x/v2/apb.rs
1//! # Advanced peripheral bus clocks
2//!
3//! ## Overview
4//!
5//! APB clocks facilitate communication between the processor core and
6//! peripherals on the APB bus. To communicate with a peripheral, the
7//! corresponding APB clock must be enabled, which is done by setting a bit in
8//! one of the four `APBXMASK` registers.
9//!
10//! In this module, *enabled* APB clocks are represented by the [`ApbClk<A>`]
11//! struct, where the type parameter `A` is a type that implements [`ApbId`] and
12//! corresponds to one of the bits in an `APBXMASK` register.
13//!
14//! While most other clocks in the `clock` module are configured through
15//! mutually exclusive registers, the [`ApbClk`]s share the four `APBXMASK`
16//! registers. This presents a challenge for memory safety. Specifically, if we
17//! allowed unrestricted access to the corresponding `APBXMASK` register through
18//! each `ApbClk`, we could create data races.
19//!
20//! To solve this problem, we restrict access to the `APBXMASK` registers using
21//! the [`Apb`] type. `Apb` was created to act as a gateway to the `APBXMASK`
22//! registers, allowing us to use `&mut Apb` as compile-time proof of exclusive
23//! access to them.
24//!
25//! ## Example
26//!
27//! Enabling and disabling the [`ApbClk`]s proceeds according to the principles
28//! outlined in the [`clock` module documentation]. It is best shown with an
29//! example.
30//!
31//! Let's start by using [`clock_system_at_reset`] to access the HAL clocking
32//! structs.
33//!
34//! ```no_run
35//! use atsamd_hal::{
36//! clock::v2::{
37//! clock_system_at_reset,
38//! },
39//! pac::Peripherals,
40//! };
41//! let mut pac = Peripherals::take().unwrap();
42//! let (mut 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//! ```
50//!
51//! Some APB clocks are enabled at power-on reset. We can find these in the
52//! [`Clocks`] struct.
53//!
54//! ```no_run
55//! # use atsamd_hal::{
56//! # clock::v2::{
57//! # clock_system_at_reset,
58//! # },
59//! # pac::Peripherals,
60//! # };
61//! # let mut pac = Peripherals::take().unwrap();
62//! # let (mut buses, clocks, tokens) = clock_system_at_reset(
63//! # pac.oscctrl,
64//! # pac.osc32kctrl,
65//! # pac.gclk,
66//! # pac.mclk,
67//! # &mut pac.nvmctrl,
68//! # );
69//! let apb_port = clocks.apbs.port;
70//! ```
71//!
72//! Other APB clocks are disabled at power-on reset. To enable these, we must
73//! have access to the [`Apb`] bus type, which is found in the [`Buses`] struct.
74//! As described above, [`Apb`] mediates access to the shared `APBXMASK`
75//! registers. We call [`Apb::enable`] to convert an [`ApbToken`] into the
76//! corresponding [`ApbClk`]. The existence of each `ApbClk` type represents
77//! proof that the corresponding APB clock has been enabled.
78//!
79//! ```no_run
80//! # use atsamd_hal::{
81//! # clock::v2::{
82//! # clock_system_at_reset,
83//! # },
84//! # pac::Peripherals,
85//! # };
86//! # let mut pac = Peripherals::take().unwrap();
87//! # let (mut buses, clocks, tokens) = clock_system_at_reset(
88//! # pac.oscctrl,
89//! # pac.osc32kctrl,
90//! # pac.gclk,
91//! # pac.mclk,
92//! # &mut pac.nvmctrl,
93//! # );
94//! # let apb_port = clocks.apbs.port;
95//! let apb_sercom0 = buses.apb.enable(tokens.apbs.sercom0);
96//! ```
97//!
98//! The complete example is shown below.
99//!
100//! ```no_run
101//! use atsamd_hal::{
102//! clock::v2::{
103//! clock_system_at_reset,
104//! },
105//! pac::Peripherals,
106//! };
107//! let mut pac = Peripherals::take().unwrap();
108//! let (mut buses, clocks, tokens) = clock_system_at_reset(
109//! pac.oscctrl,
110//! pac.osc32kctrl,
111//! pac.gclk,
112//! pac.mclk,
113//! &mut pac.nvmctrl,
114//! );
115//! let apb_port = clocks.apbs.port;
116//! let apb_sercom0 = buses.apb.enable(tokens.apbs.sercom0);
117//! ```
118//!
119//! [`clock` module documentation]: super
120//! [`clock_system_at_reset`]: super::clock_system_at_reset
121//! [`Clocks`]: super::Clocks
122//! [`Buses`]: super::Buses
123
124use atsamd_hal_macros::hal_macro_helper;
125use core::marker::PhantomData;
126
127use bitflags;
128use paste::paste;
129
130use crate::pac::{self, mclk};
131
132use crate::typelevel::Sealed;
133
134use super::types::*;
135
136//==============================================================================
137// Registers
138//==============================================================================
139
140/// APB clock controller
141///
142/// As described in the [module-level documentation](self), this struct mediates
143/// access to the shared `APBXMASK` registers. Users can convert a disabled
144/// [`ApbToken<A>`] into an enabled [`ApbClk<A>`] using [`Apb::enable`], and
145/// vice versa with [`Apb::disable`].
146pub struct Apb(());
147
148impl Apb {
149 /// Create a new instance of [`Apb`]
150 ///
151 /// # Safety
152 ///
153 /// Because the `Apb` mediates access to the `APBMASK` registers, it must be
154 /// a singleton. There must never be two simulatenous instances of it at a
155 /// time. See the notes on `Token` types and memory safety in the root of
156 /// the `clock` module for more details.
157 #[inline]
158 pub(super) unsafe fn new() -> Self {
159 Self(())
160 }
161
162 #[inline]
163 fn mclk(&self) -> &mclk::RegisterBlock {
164 // Safety: The `Apb` type has exclusive access to the `APBXMASK`
165 // registers, and it uses a shared reference to the register block. See
166 // the notes on `Token` types and memory safety in the root of the
167 // `clock` module for more details.
168 unsafe { &*pac::Mclk::PTR }
169 }
170
171 #[inline]
172 fn apbamask(&mut self) -> &mclk::Apbamask {
173 self.mclk().apbamask()
174 }
175
176 #[inline]
177 fn apbbmask(&mut self) -> &mclk::Apbbmask {
178 self.mclk().apbbmask()
179 }
180
181 #[inline]
182 fn apbcmask(&mut self) -> &mclk::Apbcmask {
183 self.mclk().apbcmask()
184 }
185
186 #[inline]
187 fn apbdmask(&mut self) -> &mclk::Apbdmask {
188 self.mclk().apbdmask()
189 }
190
191 #[inline]
192 fn enable_mask(&mut self, mask: ApbMask) {
193 // Safety: The mask bits are derived from a `bitflags` struct, so they
194 // are guaranteed to be valid.
195 unsafe {
196 match mask {
197 ApbMask::A(mask) => {
198 self.apbamask()
199 .modify(|r, w| w.bits(r.bits() | mask.bits()));
200 }
201 ApbMask::B(mask) => {
202 self.apbbmask()
203 .modify(|r, w| w.bits(r.bits() | mask.bits()));
204 }
205 ApbMask::C(mask) => {
206 self.apbcmask()
207 .modify(|r, w| w.bits(r.bits() | mask.bits()));
208 }
209 ApbMask::D(mask) => {
210 self.apbdmask()
211 .modify(|r, w| w.bits(r.bits() | mask.bits()));
212 }
213 }
214 }
215 }
216
217 #[inline]
218 fn disable_mask(&mut self, mask: ApbMask) {
219 // Safety: The mask bits are derived from a `bitflags` struct, so they
220 // are guaranteed to be valid.
221 unsafe {
222 match mask {
223 ApbMask::A(mask) => {
224 self.apbamask()
225 .modify(|r, w| w.bits(r.bits() & !mask.bits()));
226 }
227 ApbMask::B(mask) => {
228 self.apbbmask()
229 .modify(|r, w| w.bits(r.bits() & !mask.bits()));
230 }
231 ApbMask::C(mask) => {
232 self.apbcmask()
233 .modify(|r, w| w.bits(r.bits() & !mask.bits()));
234 }
235 ApbMask::D(mask) => {
236 self.apbdmask()
237 .modify(|r, w| w.bits(r.bits() & !mask.bits()));
238 }
239 }
240 }
241 }
242
243 /// Enable the corresponding APB clock
244 ///
245 /// Consume an [`ApbToken`], enable the corresponding APB clock and return
246 /// an [`ApbClk`]. The `ApbClk` represents proof that the corresponding APB
247 /// clock has been enabled.
248 #[inline]
249 pub fn enable<A: ApbId>(&mut self, token: ApbToken<A>) -> ApbClk<A> {
250 self.enable_mask(A::DYN.into());
251 ApbClk::new(token)
252 }
253
254 /// Disable the corresponding APB clock
255 ///
256 /// Consume the [`ApbClk`], disable the corresponding APB clock and return
257 /// the [`ApbToken`].
258 #[inline]
259 pub fn disable<A: ApbId>(&mut self, clock: ApbClk<A>) -> ApbToken<A> {
260 self.disable_mask(A::DYN.into());
261 clock.free()
262 }
263}
264
265//==============================================================================
266// DynApbId & ApbMask
267//==============================================================================
268
269/// A mask corresponding to one of the APB bridge registers
270///
271/// Each variant is a [`bitflags`] struct with a binary representation exactly
272/// matching the corresponding APB `MASK` register.
273enum ApbMask {
274 A(ApbAMask),
275 B(ApbBMask),
276 C(ApbCMask),
277 D(ApbDMask),
278}
279
280macro_rules! define_apb_types {
281 (
282 $(
283 $Reg:ident {
284 $(
285 $( #[$( $cfg:tt )+] )?
286 $Type:ident = $BIT:literal,
287 )+
288 }
289 )+
290 ) => {
291 /// Value-level enum identifying a single APB clock
292 ///
293 /// Each variant of this enum corresponds to a specific bit in one of
294 /// the four `APBXMASK` registers and identifies one of many possible
295 /// APB clocks, which can vary by chip.
296 ///
297 /// `DynApbId` is the value-level equivalent of [`ApbId`].
298 #[repr(u8)]
299 pub enum DynApbId {
300 $(
301 $(
302 $( #[$( $cfg )+] )?
303 $Type,
304 )+
305 )+
306 }
307
308 $(
309 $(
310 $( #[$( $cfg )+] )?
311 impl ApbId for $Type {
312 const DYN: DynApbId = DynApbId::$Type;
313 }
314 )+
315 )+
316
317 paste! {
318 $(
319 bitflags::bitflags! {
320 #[
321 doc =
322 "APB bridge `" $Reg "` register mask\n"
323 "\n"
324 "This is a [`bitflags`] struct with a binary representation "
325 "exactly matching the `APB" $Reg "MASK` register."
326 ]
327 struct [<Apb $Reg Mask>]: u32 {
328 $(
329 $( #[$( $cfg )+] )?
330 const [<$Type:upper>] = 1 << $BIT;
331 )+
332 }
333 }
334
335 )+
336
337 impl From<DynApbId> for ApbMask {
338 #[inline]
339 fn from(id: DynApbId) -> Self {
340 use DynApbId::*;
341 match id {
342 $(
343 $(
344 $( #[$( $cfg )+] )?
345 $Type => ApbMask::$Reg([<Apb $Reg Mask>]::[<$Type:upper>]),
346 )+
347 )+
348 }
349 }
350 }
351 }
352 };
353}
354
355#[hal_macro_helper]
356define_apb_types!(
357 A {
358 Pac = 0,
359 Pm = 1,
360 Mclk = 2,
361 RstC = 3,
362 OscCtrl = 4,
363 Osc32kCtrl = 5,
364 SupC = 6,
365 Gclk = 7,
366 Wdt = 8,
367 Rtc = 9,
368 Eic = 10,
369 FreqM = 11,
370 Sercom0 = 12,
371 Sercom1 = 13,
372 Tc0 = 14,
373 Tc1 = 15,
374 }
375 B {
376 Usb = 0,
377 Dsu = 1,
378 NvmCtrl = 2,
379 Port = 4,
380 EvSys = 7,
381 Sercom2 = 9,
382 Sercom3 = 10,
383 Tcc0 = 11,
384 Tcc1 = 12,
385 Tc2 = 13,
386 Tc3 = 14,
387 RamEcc = 16,
388 }
389 C {
390 #[hal_cfg("gmac")]
391 Gmac = 2,
392 Tcc2 = 3,
393 #[hal_cfg("tcc3")]
394 Tcc3 = 4,
395 #[hal_cfg("tc4")]
396 Tc4 = 5,
397 #[hal_cfg("tc5")]
398 Tc5 = 6,
399 PDec = 7,
400 Ac = 8,
401 Aes = 9,
402 Trng = 10,
403 Icm = 11,
404 Qspi = 13,
405 Ccl = 14,
406 }
407 D {
408 Sercom4 = 0,
409 Sercom5 = 1,
410 #[hal_cfg("sercom6")]
411 Sercom6 = 2,
412 #[hal_cfg("sercom7")]
413 Sercom7 = 3,
414 #[hal_cfg("tcc4")]
415 Tcc4 = 4,
416 #[hal_cfg("tc6")]
417 Tc6 = 5,
418 #[hal_cfg("tc7")]
419 Tc7 = 6,
420 Adc0 = 7,
421 Adc1 = 8,
422 Dac = 9,
423 #[hal_cfg("i2s")]
424 I2S = 10,
425 Pcc = 11,
426 }
427);
428
429//==============================================================================
430// ApbId
431//==============================================================================
432
433/// Type-level enum identifying one of the possible APB clocks
434///
435/// The types implementing this trait are type-level variants of `ApbId`, and
436/// they identify one of the many possible APB clocks, which can vary by chip.
437/// Each type corresponds to a specific bit in one of the four `APBXMASK`
438/// registers.
439///
440/// `ApbId` is the type-level equivalent of [`DynApbId`]. See the documentation
441/// on [type-level programming] and specifically [type-level enums] for more
442/// details.
443///
444/// [type-level programming]: crate::typelevel
445/// [type-level enums]: crate::typelevel#type-level-enums
446pub trait ApbId: Sealed {
447 /// Corresponding variant of [`DynApbId`]
448 const DYN: DynApbId;
449}
450
451//==============================================================================
452// ApbToken
453//==============================================================================
454
455/// Singleton token that can be exchanged for an [`ApbClk`]
456///
457/// As explained in the [`clock` module documentation](super), instances of
458/// various `Token` types can be exchanged for actual clock types. They
459/// represent clocks that are disabled.
460///
461/// The type parameter `A` is an [`ApbId`] indicating which APB clock is
462/// represented by this token. To enable the corresponding APB clock, use the
463/// [`Apb::enable`] method.
464pub struct ApbToken<A: ApbId> {
465 id: PhantomData<A>,
466}
467
468impl<A: ApbId> ApbToken<A> {
469 /// Create a new instance of [`ApbToken`]
470 ///
471 /// # Safety
472 ///
473 /// Each `ApbToken` is a singleton. There must never be two simulatenous
474 /// instances with the same [`ApbId`]. See the notes on `Token` types and
475 /// memory safety in the root of the `clock` module for more details.
476 #[inline]
477 unsafe fn new() -> Self {
478 ApbToken { id: PhantomData }
479 }
480}
481
482//==============================================================================
483// ApbClk
484//==============================================================================
485
486/// An enabled APB clock
487///
488/// An [`ApbClk`] represents an enabled APB clock. The type parameter `A` is an
489/// [`ApbId`], which corresponds to a particular bit in the `APBXMASK`
490/// registers. An `ApbClk` can be disabled with the [`Apb::disable`] method.
491pub struct ApbClk<A: ApbId> {
492 token: ApbToken<A>,
493}
494
495impl<A: ApbId> ApbClk<A> {
496 #[inline]
497 fn new(token: ApbToken<A>) -> Self {
498 ApbClk { token }
499 }
500
501 #[inline]
502 fn free(self) -> ApbToken<A> {
503 self.token
504 }
505}
506
507//==============================================================================
508// ApbTokens
509//==============================================================================
510
511/// Set of [`ApbToken`]s for APB clocks that are disabled at power-on reset
512#[hal_macro_helper]
513pub struct ApbTokens {
514 pub freq_m: ApbToken<FreqM>,
515 pub sercom0: ApbToken<Sercom0>,
516 pub sercom1: ApbToken<Sercom1>,
517 pub tc0: ApbToken<Tc0>,
518 pub tc1: ApbToken<Tc1>,
519 pub usb: ApbToken<Usb>,
520 pub ev_sys: ApbToken<EvSys>,
521 pub sercom2: ApbToken<Sercom2>,
522 pub sercom3: ApbToken<Sercom3>,
523 pub tcc0: ApbToken<Tcc0>,
524 pub tcc1: ApbToken<Tcc1>,
525 pub tc2: ApbToken<Tc2>,
526 pub tc3: ApbToken<Tc3>,
527 #[hal_cfg("tc4")]
528 pub tc4: ApbToken<Tc4>,
529 pub tcc2: ApbToken<Tcc2>,
530 #[hal_cfg("tcc3")]
531 pub tcc3: ApbToken<Tcc3>,
532 #[hal_cfg("tc5")]
533 pub tc5: ApbToken<Tc5>,
534 pub p_dec: ApbToken<PDec>,
535 pub ac: ApbToken<Ac>,
536 pub aes: ApbToken<Aes>,
537 pub trng: ApbToken<Trng>,
538 pub icm: ApbToken<Icm>,
539 pub ccl: ApbToken<Ccl>,
540 pub sercom4: ApbToken<Sercom4>,
541 pub sercom5: ApbToken<Sercom5>,
542 #[hal_cfg("sercom6")]
543 pub sercom6: ApbToken<Sercom6>,
544 #[hal_cfg("sercom7")]
545 pub sercom7: ApbToken<Sercom7>,
546 #[hal_cfg("tcc4")]
547 pub tcc4: ApbToken<Tcc4>,
548 #[hal_cfg("tc6")]
549 pub tc6: ApbToken<Tc6>,
550 #[hal_cfg("tc7")]
551 pub tc7: ApbToken<Tc7>,
552 pub adc0: ApbToken<Adc0>,
553 pub adc1: ApbToken<Adc1>,
554 pub dac: ApbToken<Dac>,
555 #[hal_cfg("i2s")]
556 pub i2s: ApbToken<I2S>,
557 pub pcc: ApbToken<Pcc>,
558}
559
560impl ApbTokens {
561 /// Create the set of [`ApbToken`]s
562 ///
563 /// # Safety
564 ///
565 /// All invariants required by `ApbToken::new` must be upheld here as well.
566 #[inline]
567 #[hal_macro_helper]
568 pub(super) unsafe fn new() -> Self {
569 unsafe {
570 Self {
571 freq_m: ApbToken::new(),
572 sercom0: ApbToken::new(),
573 sercom1: ApbToken::new(),
574 tc0: ApbToken::new(),
575 tc1: ApbToken::new(),
576 usb: ApbToken::new(),
577 ev_sys: ApbToken::new(),
578 sercom2: ApbToken::new(),
579 sercom3: ApbToken::new(),
580 tcc0: ApbToken::new(),
581 tcc1: ApbToken::new(),
582 tc2: ApbToken::new(),
583 tc3: ApbToken::new(),
584 #[hal_cfg("tc4")]
585 tc4: ApbToken::new(),
586 tcc2: ApbToken::new(),
587 #[hal_cfg("tcc3")]
588 tcc3: ApbToken::new(),
589 #[hal_cfg("tc5")]
590 tc5: ApbToken::new(),
591 p_dec: ApbToken::new(),
592 ac: ApbToken::new(),
593 aes: ApbToken::new(),
594 trng: ApbToken::new(),
595 icm: ApbToken::new(),
596 ccl: ApbToken::new(),
597 sercom4: ApbToken::new(),
598 sercom5: ApbToken::new(),
599 #[hal_cfg("sercom6")]
600 sercom6: ApbToken::new(),
601 #[hal_cfg("sercom7")]
602 sercom7: ApbToken::new(),
603 #[hal_cfg("tcc4")]
604 tcc4: ApbToken::new(),
605 #[hal_cfg("tc6")]
606 tc6: ApbToken::new(),
607 #[hal_cfg("tc7")]
608 tc7: ApbToken::new(),
609 adc0: ApbToken::new(),
610 adc1: ApbToken::new(),
611 dac: ApbToken::new(),
612 #[hal_cfg("i2s")]
613 i2s: ApbToken::new(),
614 pcc: ApbToken::new(),
615 }
616 }
617 }
618}
619
620//==============================================================================
621// ApbClks
622//==============================================================================
623
624/// Set of [`ApbClk`]s for APB clocks that are enabled at power-on reset
625#[hal_macro_helper]
626pub struct ApbClks {
627 pub pac: ApbClk<Pac>,
628 pub pm: ApbClk<Pm>,
629 pub mclk: ApbClk<Mclk>,
630 pub rst_c: ApbClk<RstC>,
631 pub osc_ctrl: ApbClk<OscCtrl>,
632 pub osc32k_ctrl: ApbClk<Osc32kCtrl>,
633 pub sup_c: ApbClk<SupC>,
634 pub gclk: ApbClk<Gclk>,
635 pub wdt: ApbClk<Wdt>,
636 pub rtc: ApbClk<Rtc>,
637 pub eic: ApbClk<Eic>,
638 pub dsu: ApbClk<Dsu>,
639 pub nvm_ctrl: ApbClk<NvmCtrl>,
640 pub port: ApbClk<Port>,
641 pub ram_ecc: ApbClk<RamEcc>,
642 #[hal_cfg("gmac")]
643 pub gmac: ApbClk<Gmac>,
644 pub qspi: ApbClk<Qspi>,
645}
646
647impl ApbClks {
648 /// Create the set of [`ApbClk`]s
649 ///
650 /// # Safety
651 ///
652 /// All invariants required by `ApbToken::new` must be upheld here as well.
653 #[inline]
654 #[hal_macro_helper]
655 pub(super) unsafe fn new() -> Self {
656 unsafe {
657 ApbClks {
658 pac: ApbClk::new(ApbToken::new()),
659 pm: ApbClk::new(ApbToken::new()),
660 mclk: ApbClk::new(ApbToken::new()),
661 rst_c: ApbClk::new(ApbToken::new()),
662 osc_ctrl: ApbClk::new(ApbToken::new()),
663 osc32k_ctrl: ApbClk::new(ApbToken::new()),
664 sup_c: ApbClk::new(ApbToken::new()),
665 gclk: ApbClk::new(ApbToken::new()),
666 wdt: ApbClk::new(ApbToken::new()),
667 rtc: ApbClk::new(ApbToken::new()),
668 eic: ApbClk::new(ApbToken::new()),
669 dsu: ApbClk::new(ApbToken::new()),
670 nvm_ctrl: ApbClk::new(ApbToken::new()),
671 port: ApbClk::new(ApbToken::new()),
672 ram_ecc: ApbClk::new(ApbToken::new()),
673 #[hal_cfg("gmac")]
674 gmac: ApbClk::new(ApbToken::new()),
675 qspi: ApbClk::new(ApbToken::new()),
676 }
677 }
678 }
679}