From d2ba3da08daa807ee5adc0e17546551bbfe7c1f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20G=C3=B3rski?= Date: Mon, 26 Dec 2022 17:26:06 +0100 Subject: [PATCH] Clocking API V2 (for thumbv7em) (#450) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new API for the `clock` module on `thumbv7em` targets Add a new `clock` module API that enforces correctness by construction. It is impossible to create an invalid clock tree without using `unsafe`. Specifically, use typed tokens and clocks to guarantee memory safety and act as proof that a clock is correctly configured, and use type-level numbers to count consumer clocks at compile-time and restrict a given clock's API while it is in use. This commit comes after two years of work, starting with #272, then with #429 and culminating with PR #450. Co-authored-by: Bradley Harden Co-authored-by: Gabriel Górski Co-authored-by: Henrik Tjäder --- boards/feather_m4/Cargo.toml | 1 + boards/feather_m4/examples/clocking_v2.rs | 171 +++ hal/Cargo.toml | 2 +- hal/src/dmac/channel/reg.rs | 2 +- hal/src/thumbv7em/clock.rs | 550 +------- hal/src/thumbv7em/clock/v1.rs | 571 ++++++++ hal/src/thumbv7em/clock/v2.rs | 1014 ++++++++++++++ hal/src/thumbv7em/clock/v2/ahb.rs | 397 ++++++ hal/src/thumbv7em/clock/v2/apb.rs | 669 +++++++++ hal/src/thumbv7em/clock/v2/dfll.rs | 1183 ++++++++++++++++ hal/src/thumbv7em/clock/v2/dpll.rs | 1023 ++++++++++++++ hal/src/thumbv7em/clock/v2/gclk.rs | 1499 +++++++++++++++++++++ hal/src/thumbv7em/clock/v2/osculp32k.rs | 490 +++++++ hal/src/thumbv7em/clock/v2/pclk.rs | 537 ++++++++ hal/src/thumbv7em/clock/v2/reset.rs | 179 +++ hal/src/thumbv7em/clock/v2/rtcosc.rs | 289 ++++ hal/src/thumbv7em/clock/v2/types.rs | 102 ++ hal/src/thumbv7em/clock/v2/xosc.rs | 1024 ++++++++++++++ hal/src/thumbv7em/clock/v2/xosc32k.rs | 1231 +++++++++++++++++ hal/src/typelevel.rs | 78 +- 20 files changed, 10465 insertions(+), 547 deletions(-) create mode 100644 boards/feather_m4/examples/clocking_v2.rs create mode 100644 hal/src/thumbv7em/clock/v1.rs create mode 100644 hal/src/thumbv7em/clock/v2.rs create mode 100644 hal/src/thumbv7em/clock/v2/ahb.rs create mode 100644 hal/src/thumbv7em/clock/v2/apb.rs create mode 100644 hal/src/thumbv7em/clock/v2/dfll.rs create mode 100644 hal/src/thumbv7em/clock/v2/dpll.rs create mode 100644 hal/src/thumbv7em/clock/v2/gclk.rs create mode 100644 hal/src/thumbv7em/clock/v2/osculp32k.rs create mode 100644 hal/src/thumbv7em/clock/v2/pclk.rs create mode 100644 hal/src/thumbv7em/clock/v2/reset.rs create mode 100644 hal/src/thumbv7em/clock/v2/rtcosc.rs create mode 100644 hal/src/thumbv7em/clock/v2/types.rs create mode 100644 hal/src/thumbv7em/clock/v2/xosc.rs create mode 100644 hal/src/thumbv7em/clock/v2/xosc32k.rs diff --git a/boards/feather_m4/Cargo.toml b/boards/feather_m4/Cargo.toml index 7a6e669f67c..c7fe6bc5a6d 100644 --- a/boards/feather_m4/Cargo.toml +++ b/boards/feather_m4/Cargo.toml @@ -31,6 +31,7 @@ optional = true [dev-dependencies] cortex-m = "0.7" usbd-serial = "0.1" +cortex-m-rtic = "0.6.0-rc.2" panic-halt = "0.2" panic-semihosting = "0.5" smart-leds = "0.3" diff --git a/boards/feather_m4/examples/clocking_v2.rs b/boards/feather_m4/examples/clocking_v2.rs new file mode 100644 index 00000000000..821e471b3a0 --- /dev/null +++ b/boards/feather_m4/examples/clocking_v2.rs @@ -0,0 +1,171 @@ +#![no_main] +#![no_std] + +use panic_halt as _; + +use core::fmt::Write as _; + +use atsamd_hal::{ + clock::v2::{ + self as clock, + dpll::Dpll, + gclk::{Gclk, GclkDiv16, GclkDiv8}, + osculp32k::OscUlp32k, + pclk::Pclk, + rtcosc::RtcOsc, + xosc32k::{ControlGainMode, Xosc1k, Xosc32k, Xosc32kBase}, + }, + ehal::serial::Read as _, + ehal::serial::Write, + gpio::{Pins, PA04, PA05}, + rtc::{ClockMode, Rtc}, + sercom::{ + uart::{self, BaudMode, Flags, Oversampling}, + IoSet3, Sercom0, + }, + time::U32Ext, +}; + +use rtic::app; + +type Pads = uart::PadsFromIds; +type Uart = uart::Uart, uart::Duplex>; + +#[app(device = atsamd_hal::pac, peripherals = true)] +mod app { + use super::*; + + #[shared] + struct SharedResources { + uart: Uart, + rtc: Rtc, + } + + #[local] + struct LocalResources {} + + #[init] + fn init(cx: init::Context) -> (SharedResources, LocalResources, init::Monotonics()) { + let mut device = cx.device; + + // Get the clocks & tokens + let (_buses, clocks, tokens) = clock::clock_system_at_reset( + device.OSCCTRL, + device.OSC32KCTRL, + device.GCLK, + device.MCLK, + &mut device.NVMCTRL, + ); + + // This is required because the `sercom` and `rtc` modules have not yet + // been update to use `clock::v2` + let (_, _, _, mut mclk) = unsafe { clocks.pac.steal() }; + + // Get the pins + let pins = Pins::new(device.PORT); + + // Take `Dfll` 48 MHz, divide down to `2 MHz` through `Gclk1` + let (gclk1, dfll) = Gclk::from_source(tokens.gclks.gclk1, clocks.dfll); + let gclk1 = gclk1.div(GclkDiv16::Div(24)).enable(); + + // Output `Gclk1` on PB15 pin + let (gclk1, _gclk1_out) = gclk1.enable_gclk_out(pins.pb15); + + // Setup a peripheral channel to power up `Dpll0` from `Gclk1` + let (pclk_dpll0, gclk1) = Pclk::enable(tokens.pclks.dpll0, gclk1); + + // Configure `Dpll0` with `2 * 60 + 0/32 = 120 MHz` frequency + let dpll0 = Dpll::from_pclk(tokens.dpll0, pclk_dpll0) + .loop_div(60, 0) + .enable(); + + // Swap source of `Gclk0` from Dfll to Dpll0, `48 Mhz -> 120 MHz` + let (gclk0, _dfll, _dpll0) = clocks.gclk0.swap_sources(dfll, dpll0); + + // Output `Gclk0` on pin PB14 + let (gclk0, _gclk0_out) = gclk0.enable_gclk_out(pins.pb14); + + // Setup a peripheral channel to power up `Dpll1` from `Gclk1` + let (pclk_dpll1, _gclk1) = Pclk::enable(tokens.pclks.dpll1, gclk1); + + // Configure `Dpll1` with `2 * 50 + 0/32 = 100 MHz` frequency + let dpll1 = Dpll::from_pclk(tokens.dpll1, pclk_dpll1) + .loop_div(50, 0) + .enable(); + + // Output `Dpll1` on PB20 pin via `Gclk6`, divided by 200 resulting in 0.5 MHz + // output frequency + let (gclk6, _dpll1) = Gclk::from_source(tokens.gclks.gclk6, dpll1); + let gclk6 = gclk6.div(GclkDiv8::Div(200)).enable(); + let (_gclk6, _gclk6_out) = gclk6.enable_gclk_out(pins.pb12); + + // Configure `Xosc32k` with both outputs (1kHz, 32kHz) activated + let xosc32k_base = Xosc32kBase::from_crystal(tokens.xosc32k.base, pins.pa00, pins.pa01) + .control_gain_mode(ControlGainMode::HighSpeed) + .on_demand(false) + .run_standby(true) + .enable(); + let (xosc1k, xosc32k_base) = Xosc1k::enable(tokens.xosc32k.xosc1k, xosc32k_base); + let (xosc32k, _xosc32k_base) = Xosc32k::enable(tokens.xosc32k.xosc32k, xosc32k_base); + + // Output `Xosc32k` on PB16 pin via `Gclk2`, divided by 2 resulting in 16 kHz + // output frequency + let (gclk2, _xosc32k) = Gclk::from_source(tokens.gclks.gclk2, xosc32k); + let gclk2 = gclk2.div(GclkDiv8::Div(2)).enable(); + let (_gclk2, _gclk2_out) = gclk2.enable_gclk_out(pins.pb16); + + // Output `OscUlp32k` on PB11 pin via `Gclk5`, without any division resulting in + // 32 kHz output frequency + let (osculp32k, _osculp_base) = + OscUlp32k::enable(tokens.osculp32k.osculp32k, clocks.osculp32k_base); + let (gclk5, _osculp32k) = Gclk::from_source(tokens.gclks.gclk5, osculp32k); + let gclk5 = gclk5.enable(); + let (_gclk5, _gclk5_out) = gclk5.enable_gclk_out(pins.pb11); + + // Setup a peripheral channel to power up `Uart` from `Gclk0` + let (pclk_sercom0, _gclk0) = Pclk::enable(tokens.pclks.sercom0, gclk0); + + use atsamd_hal::sercom::uart; + + let pads = uart::Pads::default().rx(pins.pa05).tx(pins.pa04); + // In the future, the `Uart` will take ownership of the `Pclk` and will + // take an `ApbClk` instead of `&MCLK` + let mut uart = uart::Config::new(&mclk, device.SERCOM0, pads, pclk_sercom0.freq()) + .baud(115_200.hz(), BaudMode::Arithmetic(Oversampling::Bits16)) + .enable(); + uart.enable_interrupts(Flags::RXC); + + // Initialize the RTC oscillator from the 1 kHz output of XOSC32K + let (rtc_osc, _xosc1k) = RtcOsc::enable(tokens.rtcosc, xosc1k); + + // Setup an `Rtc` in `ClockMode` + // In the future, the `Rtc` will take ownership of the `RtcOsc` + let rtc = Rtc::clock_mode(device.RTC, rtc_osc.freq(), &mut mclk); + + writeln!(&mut uart as &mut dyn Write<_, Error = _>, "RTIC booted!").unwrap(); + + ( + SharedResources { uart, rtc }, + LocalResources {}, + init::Monotonics(), + ) + } + + #[task(binds = SERCOM0_2, shared = [uart, rtc])] + fn uart(cx: uart::Context) { + let mut uart = cx.shared.uart; + let mut rtc = cx.shared.rtc; + // Read from `Uart` to clean interrupt flag + let _ = uart.lock(|u| u.read().unwrap()); + + // Print out `DateTime` coming from `Rtc` + uart.lock(|u| { + writeln!( + u as &mut dyn Write<_, Error = _>, + "{:#?}", + rtc.lock(|r| r.current_time()) + ) + .unwrap() + }); + } +} diff --git a/hal/Cargo.toml b/hal/Cargo.toml index c575e959494..24c0af673f8 100644 --- a/hal/Cargo.toml +++ b/hal/Cargo.toml @@ -15,7 +15,7 @@ repository = "https://github.com/atsamd-rs/atsamd" readme = "README.md" documentation = "https://docs.rs/crate/atsamd-hal/" edition = "2021" -rust-version = "1.56" +rust-version = "1.65" [package.metadata.docs.rs] features = ["samd21g", "samd21g-rt", "unproven", "usb"] diff --git a/hal/src/dmac/channel/reg.rs b/hal/src/dmac/channel/reg.rs index 4cc53ddf0a8..5fb9ac34250 100644 --- a/hal/src/dmac/channel/reg.rs +++ b/hal/src/dmac/channel/reg.rs @@ -112,7 +112,7 @@ macro_rules! reg_proxy { paste! { /// Register proxy tied to a specific channel pub(super) struct [< $reg:camel Proxy >] { - #[allow(ununsed)] + #[allow(unused)] dmac: DMAC, _id: PhantomData, _reg: PhantomData, diff --git a/hal/src/thumbv7em/clock.rs b/hal/src/thumbv7em/clock.rs index b7cc875637a..ce090b2fd4b 100644 --- a/hal/src/thumbv7em/clock.rs +++ b/hal/src/thumbv7em/clock.rs @@ -1,545 +1,9 @@ -//! Configuring the system clock sources. -//! You will typically need to create an instance of `GenericClockController` -//! before you can set up most of the peripherals on the atsamd51 device. -//! The other types in this module are used to enforce at compile time -//! that the peripherals have been correctly configured. -#![allow(clippy::from_over_into)] +//! # Clocking API +//! +//! Users are encouraged to use [`v2`] variant of an API because of the richer +//! feature set and safety. -use crate::pac::gclk::genctrl::SRC_A::*; -use crate::pac::gclk::pchctrl::GEN_A::*; -use crate::pac::{self, GCLK, MCLK, NVMCTRL, OSC32KCTRL, OSCCTRL}; -use crate::time::{Hertz, MegaHertz}; +pub mod v1; +pub use v1::*; -pub type ClockGenId = pac::gclk::pchctrl::GEN_A; -pub type ClockSource = pac::gclk::genctrl::SRC_A; - -#[allow(non_camel_case_types)] -pub enum ClockId { - DFLL48 = 0, - FDPLL0, - FDPLL1, - SLOW_32K, - EIC, - FREQM_MSR, - FREQM_REF, - SERCOM0_CORE, - SERCOM1_CORE, - TC0_TC1, - USB, - EVSYS0, - EVSYS1, - EVSYS2, - EVSYS3, - EVSYS4, - EVSYS5, - EVSYS6, - EVSYS7, - EVSYS8, - EVSYS9, - EVSYS10, - EVSYS11, - SERCOM2_CORE, - SERCOM3_CORE, - TCC0_TCC1, - TC2_TC3, - CAN0, - CAN1, - TCC2_TCC3, - TC4_TC5, - PDEC, - AC, - CCL, - SERCOM4_CORE, - SERCOM5_CORE, - SERCOM6_CORE, - SERCOM7_CORE, - TCC4, - TC6_TC7, - ADC0, - ADC1, - DAC, - I2S0, - I2S1, - SDHC0, - SDHC1, - CM4_TRACE, -} - -impl From for u8 { - fn from(clock: ClockId) -> u8 { - clock as u8 - } -} - -/// Represents a configured clock generator. -/// Can be converted into the effective clock frequency. -/// Its primary purpose is to be passed in to methods -/// such as `GenericClockController::tcc2_tc3` to configure -/// the clock for a peripheral. -//#[derive(Clone, Copy)] -pub struct GClock { - gclk: ClockGenId, - freq: Hertz, -} - -impl Into for GClock { - fn into(self) -> Hertz { - self.freq - } -} - -struct State { - gclk: GCLK, -} - -impl State { - fn reset_gclk(&mut self) { - self.gclk.ctrla.write(|w| w.swrst().set_bit()); - while self.gclk.ctrla.read().swrst().bit_is_set() || self.gclk.syncbusy.read().bits() != 0 { - } - } - - fn wait_for_sync(&mut self) { - while self.gclk.syncbusy.read().bits() != 0 {} - } - - fn set_gclk_divider_and_source( - &mut self, - gclk: ClockGenId, - divider: u16, - src: ClockSource, - improve_duty_cycle: bool, - ) { - // validate the divisor factor based on gclk ID (see 14.8.3) - let mut divisor_invalid = false; - if gclk == GCLK1 { - if divider as u32 >= 2_u32.pow(16) { - divisor_invalid = true; - } - } else if divider >= 2_u16.pow(8) { - divisor_invalid = true; - } - if divisor_invalid { - panic!("invalid divisor {} for GCLK {}", divider, gclk as u8); - } - - self.gclk.genctrl[u8::from(gclk) as usize].write(|w| unsafe { - w.src().variant(src); - w.div().bits(divider); - // divide directly by divider, rather than 2^(n+1) - w.divsel().clear_bit(); - w.idc().bit(improve_duty_cycle); - w.genen().set_bit(); - w.oe().set_bit() - }); - - self.wait_for_sync(); - } - - fn enable_clock_generator(&mut self, clock: ClockId, generator: ClockGenId) { - self.gclk.pchctrl[u8::from(clock) as usize].write(|w| unsafe { - w.gen().bits(generator.into()); - w.chen().set_bit() - }); - self.wait_for_sync(); - } - - fn configure_standby(&mut self, gclk: ClockGenId, enable: bool) { - self.gclk.genctrl[u8::from(gclk) as usize].modify(|_, w| w.runstdby().bit(enable)); - self.wait_for_sync(); - } -} - -/// `GenericClockController` encapsulates the GCLK hardware. -/// It provides a type safe way to configure the system clocks. -/// Initializing the `GenericClockController` instance configures -/// the system to run at 120MHz by taking the DFLL48 -/// and feeding it into the DPLL0 hardware which multiplies the -/// signal by 2.5x. -pub struct GenericClockController { - state: State, - gclks: [Hertz; 12], - used_clocks: u64, -} - -impl GenericClockController { - /// Reset the clock controller, configure the system to run - /// at 120Mhz and reset various clock dividers. - pub fn with_internal_32kosc( - gclk: GCLK, - mclk: &mut MCLK, - osc32kctrl: &mut OSC32KCTRL, - oscctrl: &mut OSCCTRL, - nvmctrl: &mut NVMCTRL, - ) -> Self { - Self::new(gclk, mclk, osc32kctrl, oscctrl, nvmctrl, false) - } - - /// Reset the clock controller, configure the system to run - /// at 120Mhz and reset various clock dividers. - pub fn with_external_32kosc( - gclk: GCLK, - mclk: &mut MCLK, - osc32kctrl: &mut OSC32KCTRL, - oscctrl: &mut OSCCTRL, - nvmctrl: &mut NVMCTRL, - ) -> Self { - Self::new(gclk, mclk, osc32kctrl, oscctrl, nvmctrl, true) - } - - fn new( - gclk: GCLK, - mclk: &mut MCLK, - osc32kctrl: &mut OSC32KCTRL, - oscctrl: &mut OSCCTRL, - nvmctrl: &mut NVMCTRL, - use_external_crystal: bool, - ) -> Self { - let mut state = State { gclk }; - - set_flash_to_half_auto_wait_state(nvmctrl); - enable_gclk_apb(mclk); - - if use_external_crystal { - enable_external_32kosc(osc32kctrl); - state.reset_gclk(); - state.set_gclk_divider_and_source(GCLK1, 1, XOSC32K, false); - } else { - enable_internal_32kosc(osc32kctrl); - state.reset_gclk(); - state.set_gclk_divider_and_source(GCLK1, 1, OSCULP32K, false); - } - - while state.gclk.syncbusy.read().genctrl().is_gclk0() {} - - #[cfg(feature = "usb")] - configure_usb_correction(oscctrl); - - // GCLK5 set to 2MHz - unsafe { - state.gclk.genctrl[5].write(|w| { - w.src().dfll(); - w.genen().set_bit(); - w.div().bits(24) - }); - } - - while state.gclk.syncbusy.read().genctrl().is_gclk5() {} - - configure_and_enable_dpll0(oscctrl, &mut state.gclk); - wait_for_dpllrdy(oscctrl); - - unsafe { - // GCLK0 set to DPLL0 (120MHz) - state.gclk.genctrl[0].write(|w| { - w.src().dpll0(); - w.div().bits(1); - w.oe().set_bit(); - w.genen().set_bit() - }); - } - - while state.gclk.syncbusy.read().genctrl().is_gclk0() {} - - mclk.cpudiv.write(|w| w.div().div1()); - - Self { - state, - gclks: [ - OSC120M_FREQ, - OSC32K_FREQ, - Hertz(0), - Hertz(0), - Hertz(0), - MegaHertz(2).into(), - Hertz(0), - Hertz(0), - Hertz(0), - Hertz(0), - Hertz(0), - Hertz(0), - ], - used_clocks: 1u64 << u8::from(ClockId::FDPLL0), - } - } - - /// Returns a `GClock` for gclk0, the 120MHz oscillator. - pub fn gclk0(&mut self) -> GClock { - GClock { - gclk: GCLK0, - freq: self.gclks[0], - } - } - - /// Returns a `GClock` for gclk1, the 32KHz oscillator. - pub fn gclk1(&mut self) -> GClock { - GClock { - gclk: GCLK1, - freq: self.gclks[1], - } - } - - /// Returns the `GClock` for the specified clock generator. - /// If that clock generator has not yet been configured, - /// returns None. - pub fn get_gclk(&mut self, gclk: ClockGenId) -> Option { - let idx = u8::from(gclk) as usize; - if self.gclks[idx].0 == 0 { - None - } else { - Some(GClock { - gclk, - freq: self.gclks[idx], - }) - } - } - - /// Configures a clock generator with the specified divider and - /// source. - /// `divider` is a linear divider to be applied to the clock - /// source. While the hardware also supports an exponential divider, - /// this function doesn't expose that functionality at this time. - /// `improve_duty_cycle` is a boolean that, when set to true, enables - /// a 50/50 duty cycle for odd divider values. - /// Returns a `GClock` for the configured clock generator. - /// Returns `None` if the clock generator has already been configured. - pub fn configure_gclk_divider_and_source( - &mut self, - gclk: ClockGenId, - divider: u16, - src: ClockSource, - improve_duty_cycle: bool, - ) -> Option { - let idx = u8::from(gclk) as usize; - if self.gclks[idx].0 != 0 { - return None; - } - self.state - .set_gclk_divider_and_source(gclk, divider, src, improve_duty_cycle); - let freq: Hertz = match src { - XOSC32K | OSCULP32K => OSC32K_FREQ, - GCLKGEN1 => self.gclks[1], - DFLL => OSC48M_FREQ, - DPLL0 => OSC120M_FREQ, - XOSC0 | XOSC1 | GCLKIN | DPLL1 => unimplemented!(), - }; - self.gclks[idx] = Hertz(freq.0 / divider as u32); - Some(GClock { gclk, freq }) - } - - /// Enables or disables the given GClk from operation in standby. - pub fn configure_standby(&mut self, gclk: ClockGenId, enable: bool) { - self.state.configure_standby(gclk, enable) - } -} - -macro_rules! clock_generator { - ( - $( - $(#[$attr:meta])* - ($id:ident, $Type:ident, $clock:ident), - )+ - ) => { - -$( - -/// A typed token that indicates that the clock for the peripheral(s) -/// with the matching name has been configured. -/// The effective clock frequency is available via the `freq` method, -/// or by converting the object into a `Hertz` instance. -/// The peripheral initialization code will typically require passing -/// in this object to prove at compile time that the clock has been -/// correctly initialized. -$(#[$attr])* -#[derive(Debug)] -pub struct $Type { - freq: Hertz, -} - -$(#[$attr])* -impl $Type { - /// Returns the frequency of the configured clock - pub fn freq(&self) -> Hertz { - self.freq - } -} -$(#[$attr])* -impl Into for $Type { - fn into(self) -> Hertz { - self.freq - } -} -)+ - -impl GenericClockController { - $( - /// Configure the clock for peripheral(s) that match the name - /// of this function to use the specific clock generator. - /// The `GClock` parameter may be one of default clocks - /// return from `gclk0()`, `gclk1()` or a clock configured - /// by the host application using the `configure_gclk_divider_and_source` - /// method. - /// Returns a typed token that proves that the clock has been configured; - /// the peripheral initialization code will typically require that this - /// clock token be passed in to ensure that the clock has been initialized - /// appropriately. - /// Returns `None` is the specified generic clock has already been - /// configured. - $(#[$attr])* - pub fn $id(&mut self, generator: &GClock) -> Option<$Type> { - let bits: u64 = 1 << u8::from(ClockId::$clock) as u64; - if (self.used_clocks & bits) != 0 { - return None; - } - self.used_clocks |= bits; - - self.state.enable_clock_generator(ClockId::$clock, generator.gclk); - let freq = self.gclks[u8::from(generator.gclk) as usize]; - Some($Type{freq}) - } - )+ -} - } -} - -clock_generator!( - (tc0_tc1, Tc0Tc1Clock, TC0_TC1), - (tcc0_tcc1, Tcc0Tcc1Clock, TCC0_TCC1), - (tc2_tc3, Tc2Tc3Clock, TC2_TC3), - (tcc2_tcc3, Tcc2Tcc3Clock, TCC2_TCC3), - (tc4_tc5, Tc4Tc5Clock, TC4_TC5), - (tcc4, Tcc4Clock, TCC4), - (tc6_tc7, Tc6Tc7Clock, TC6_TC7), - (sercom0_core, Sercom0CoreClock, SERCOM0_CORE), - (sercom1_core, Sercom1CoreClock, SERCOM1_CORE), - (sercom2_core, Sercom2CoreClock, SERCOM2_CORE), - (sercom3_core, Sercom3CoreClock, SERCOM3_CORE), - (sercom4_core, Sercom4CoreClock, SERCOM4_CORE), - (sercom5_core, Sercom5CoreClock, SERCOM5_CORE), - #[cfg(feature = "min-samd51n")] - (sercom6_core, Sercom6CoreClock, SERCOM6_CORE), - #[cfg(feature = "min-samd51n")] - (sercom7_core, Sercom7CoreClock, SERCOM7_CORE), - (usb, UsbClock, USB), - (adc0, Adc0Clock, ADC0), - (adc1, Adc1Clock, ADC1), - (eic, EicClock, EIC), - (freq_m_msr, FreqmMsrClock, FREQM_MSR), - (freq_m_ref, FreqmRefClock, FREQM_REF), - (evsys0, Evsys0Clock, EVSYS0), - (evsys1, Evsys1Clock, EVSYS1), - (evsys2, Evsys2Clock, EVSYS2), - (evsys3, Evsys3Clock, EVSYS3), - (evsys4, Evsys4Clock, EVSYS4), - (evsys5, Evsys5Clock, EVSYS5), - (evsys6, Evsys6Clock, EVSYS6), - (evsys7, Evsys7Clock, EVSYS7), - (evsys8, Evsys8Clock, EVSYS8), - (evsys9, Evsys9Clock, EVSYS9), - (evsys10, Evsys10Clock, EVSYS10), - (evsys11, Evsys11Clock, EVSYS11), - (can0, Can0Clock, CAN0), - (can1, Can1Clock, CAN1), - (pdec, PdecClock, PDEC), - (ac, AcClock, AC), - (ccl, CclClock, CCL), - (dac, DacClock, DAC), - (i2s0, I2S0Clock, I2S0), - (i2s1, I2S1Clock, I2S1), - (sdhc0, Sdhc0Clock, SDHC0), - (sdhc1, Sdhc1Clock, SDHC1), - (cm4_trace, Cm4TraceClock, CM4_TRACE), -); - -/// The frequency of the 48Mhz source. -pub const OSC48M_FREQ: Hertz = Hertz(48_000_000); -/// The frequency of the 32Khz source. -pub const OSC32K_FREQ: Hertz = Hertz(32_768); -/// The frequency of the 120Mhz source. -pub const OSC120M_FREQ: Hertz = Hertz(120_000_000); - -fn set_flash_to_half_auto_wait_state(nvmctrl: &mut NVMCTRL) { - // Zero indicates zero wait states, one indicates one wait state, etc., - // up to 15 wait states. - nvmctrl.ctrla.modify(|_, w| unsafe { w.rws().bits(0b0111) }); -} - -fn enable_gclk_apb(mclk: &mut MCLK) { - mclk.apbamask.modify(|_, w| w.gclk_().set_bit()); -} - -/// Turn on the internal 32hkz oscillator -fn enable_internal_32kosc(osc32kctrl: &mut OSC32KCTRL) { - osc32kctrl.osculp32k.modify(|_, w| { - w.en32k().set_bit(); - w.en1k().set_bit() - }); - osc32kctrl.rtcctrl.write(|w| w.rtcsel().ulp1k()); -} - -/// Turn on the external 32hkz oscillator -fn enable_external_32kosc(osc32kctrl: &mut OSC32KCTRL) { - osc32kctrl.xosc32k.modify(|_, w| { - w.ondemand().clear_bit(); - // Enable 32khz output - w.en32k().set_bit(); - w.en1k().set_bit(); - // Crystal connected to xin32/xout32 - w.xtalen().set_bit(); - w.enable().set_bit(); - w.cgm().xt(); - w.runstdby().set_bit() - }); - - osc32kctrl.rtcctrl.write(|w| w.rtcsel().xosc1k()); - - // Wait for the oscillator to stabilize - while osc32kctrl.status.read().xosc32krdy().bit_is_clear() {} -} - -fn wait_for_dpllrdy(oscctrl: &mut OSCCTRL) { - while oscctrl.dpll[0].dpllstatus.read().lock().bit_is_clear() - || oscctrl.dpll[0].dpllstatus.read().clkrdy().bit_is_clear() - {} -} - -/// Configure the dpll0 to run at 120MHz -fn configure_and_enable_dpll0(oscctrl: &mut OSCCTRL, gclk: &mut GCLK) { - gclk.pchctrl[ClockId::FDPLL0 as usize].write(|w| { - w.chen().set_bit(); - w.gen().gclk5() - }); - unsafe { - oscctrl.dpll[0].dpllratio.write(|w| { - w.ldr().bits(59); - w.ldrfrac().bits(0) - }); - } - oscctrl.dpll[0].dpllctrlb.write(|w| w.refclk().gclk()); - oscctrl.dpll[0].dpllctrla.write(|w| { - w.enable().set_bit(); - w.ondemand().clear_bit() - }); -} - -#[cfg(feature = "usb")] -/// Configure the dfll48m to calibrate against the 1Khz USB SOF reference. -fn configure_usb_correction(oscctrl: &mut OSCCTRL) { - oscctrl.dfllmul.write(|w| unsafe { - w.cstep().bits(0x1) - .fstep().bits(0x1) - // scaling factor for 1Khz SOF signal. - .mul().bits((48_000_000u32 / 1000) as u16) - }); - while oscctrl.dfllsync.read().dfllmul().bit_is_set() {} - - oscctrl.dfllctrlb.write(|w| { - // closed loop mode - w.mode().set_bit() - // chill cycle disable - .ccdis().set_bit() - // usb correction - .usbcrm().set_bit() - }); - while oscctrl.dfllsync.read().dfllctrlb().bit_is_set() {} -} +pub mod v2; diff --git a/hal/src/thumbv7em/clock/v1.rs b/hal/src/thumbv7em/clock/v1.rs new file mode 100644 index 00000000000..0be7a81d9fd --- /dev/null +++ b/hal/src/thumbv7em/clock/v1.rs @@ -0,0 +1,571 @@ +//! # Clocking API v1 +//! +//! Configuring the system clock sources. You will typically need to create an +//! instance of `GenericClockController` before you can set up most of the +//! peripherals on the atsamd51 device. The other types in this module are used +//! to enforce at compile time that the peripherals have been correctly +//! configured. +#![allow(clippy::from_over_into)] + +use crate::clock::v2::pclk::{ids::*, Pclk, PclkSourceId}; +use crate::pac::gclk::genctrl::SRC_A::*; +use crate::pac::gclk::pchctrl::GEN_A::*; +use crate::pac::{self, GCLK, MCLK, NVMCTRL, OSC32KCTRL, OSCCTRL}; +use crate::sercom::*; +use crate::time::{Hertz, MegaHertz}; + +pub type ClockGenId = pac::gclk::pchctrl::GEN_A; +pub type ClockSource = pac::gclk::genctrl::SRC_A; + +#[allow(non_camel_case_types)] +pub enum ClockId { + DFLL48 = 0, + FDPLL0, + FDPLL1, + SLOW_32K, + EIC, + FREQM_MSR, + FREQM_REF, + SERCOM0_CORE, + SERCOM1_CORE, + TC0_TC1, + USB, + EVSYS0, + EVSYS1, + EVSYS2, + EVSYS3, + EVSYS4, + EVSYS5, + EVSYS6, + EVSYS7, + EVSYS8, + EVSYS9, + EVSYS10, + EVSYS11, + SERCOM2_CORE, + SERCOM3_CORE, + TCC0_TCC1, + TC2_TC3, + CAN0, + CAN1, + TCC2_TCC3, + TC4_TC5, + PDEC, + AC, + CCL, + SERCOM4_CORE, + SERCOM5_CORE, + SERCOM6_CORE, + SERCOM7_CORE, + TCC4, + TC6_TC7, + ADC0, + ADC1, + DAC, + I2S0, + I2S1, + SDHC0, + SDHC1, + CM4_TRACE, +} + +impl From for u8 { + fn from(clock: ClockId) -> u8 { + clock as u8 + } +} + +/// Represents a configured clock generator. +/// Can be converted into the effective clock frequency. +/// Its primary purpose is to be passed in to methods +/// such as `GenericClockController::tcc2_tc3` to configure +/// the clock for a peripheral. +//#[derive(Clone, Copy)] +pub struct GClock { + gclk: ClockGenId, + freq: Hertz, +} + +impl Into for GClock { + fn into(self) -> Hertz { + self.freq + } +} + +struct State { + gclk: GCLK, +} + +impl State { + fn reset_gclk(&mut self) { + self.gclk.ctrla.write(|w| w.swrst().set_bit()); + while self.gclk.ctrla.read().swrst().bit_is_set() || self.gclk.syncbusy.read().bits() != 0 { + } + } + + fn wait_for_sync(&mut self) { + while self.gclk.syncbusy.read().bits() != 0 {} + } + + fn set_gclk_divider_and_source( + &mut self, + gclk: ClockGenId, + divider: u16, + src: ClockSource, + improve_duty_cycle: bool, + ) { + // validate the divisor factor based on gclk ID (see 14.8.3) + let mut divisor_invalid = false; + if gclk == GCLK1 { + if divider as u32 >= 2_u32.pow(16) { + divisor_invalid = true; + } + } else if divider >= 2_u16.pow(8) { + divisor_invalid = true; + } + if divisor_invalid { + panic!("invalid divisor {} for GCLK {}", divider, gclk as u8); + } + + self.gclk.genctrl[u8::from(gclk) as usize].write(|w| unsafe { + w.src().variant(src); + w.div().bits(divider); + // divide directly by divider, rather than 2^(n+1) + w.divsel().clear_bit(); + w.idc().bit(improve_duty_cycle); + w.genen().set_bit(); + w.oe().set_bit() + }); + + self.wait_for_sync(); + } + + fn enable_clock_generator(&mut self, clock: ClockId, generator: ClockGenId) { + self.gclk.pchctrl[u8::from(clock) as usize].write(|w| unsafe { + w.gen().bits(generator.into()); + w.chen().set_bit() + }); + self.wait_for_sync(); + } + + fn configure_standby(&mut self, gclk: ClockGenId, enable: bool) { + self.gclk.genctrl[u8::from(gclk) as usize].modify(|_, w| w.runstdby().bit(enable)); + self.wait_for_sync(); + } +} + +/// `GenericClockController` encapsulates the GCLK hardware. +/// It provides a type safe way to configure the system clocks. +/// Initializing the `GenericClockController` instance configures +/// the system to run at 120MHz by taking the DFLL48 +/// and feeding it into the DPLL0 hardware which multiplies the +/// signal by 2.5x. +pub struct GenericClockController { + state: State, + gclks: [Hertz; 12], + used_clocks: u64, +} + +impl GenericClockController { + /// Reset the clock controller, configure the system to run + /// at 120Mhz and reset various clock dividers. + pub fn with_internal_32kosc( + gclk: GCLK, + mclk: &mut MCLK, + osc32kctrl: &mut OSC32KCTRL, + oscctrl: &mut OSCCTRL, + nvmctrl: &mut NVMCTRL, + ) -> Self { + Self::new(gclk, mclk, osc32kctrl, oscctrl, nvmctrl, false) + } + + /// Reset the clock controller, configure the system to run + /// at 120Mhz and reset various clock dividers. + pub fn with_external_32kosc( + gclk: GCLK, + mclk: &mut MCLK, + osc32kctrl: &mut OSC32KCTRL, + oscctrl: &mut OSCCTRL, + nvmctrl: &mut NVMCTRL, + ) -> Self { + Self::new(gclk, mclk, osc32kctrl, oscctrl, nvmctrl, true) + } + + fn new( + gclk: GCLK, + mclk: &mut MCLK, + osc32kctrl: &mut OSC32KCTRL, + oscctrl: &mut OSCCTRL, + nvmctrl: &mut NVMCTRL, + use_external_crystal: bool, + ) -> Self { + let mut state = State { gclk }; + + set_flash_to_half_auto_wait_state(nvmctrl); + enable_gclk_apb(mclk); + + if use_external_crystal { + enable_external_32kosc(osc32kctrl); + state.reset_gclk(); + state.set_gclk_divider_and_source(GCLK1, 1, XOSC32K, false); + } else { + enable_internal_32kosc(osc32kctrl); + state.reset_gclk(); + state.set_gclk_divider_and_source(GCLK1, 1, OSCULP32K, false); + } + + while state.gclk.syncbusy.read().genctrl().is_gclk0() {} + + #[cfg(feature = "usb")] + configure_usb_correction(oscctrl); + + // GCLK5 set to 2MHz + unsafe { + state.gclk.genctrl[5].write(|w| { + w.src().dfll(); + w.genen().set_bit(); + w.div().bits(24) + }); + } + + while state.gclk.syncbusy.read().genctrl().is_gclk5() {} + + configure_and_enable_dpll0(oscctrl, &mut state.gclk); + wait_for_dpllrdy(oscctrl); + + unsafe { + // GCLK0 set to DPLL0 (120MHz) + state.gclk.genctrl[0].write(|w| { + w.src().dpll0(); + w.div().bits(1); + w.oe().set_bit(); + w.genen().set_bit() + }); + } + + while state.gclk.syncbusy.read().genctrl().is_gclk0() {} + + mclk.cpudiv.write(|w| w.div().div1()); + + Self { + state, + gclks: [ + OSC120M_FREQ, + OSC32K_FREQ, + Hertz(0), + Hertz(0), + Hertz(0), + MegaHertz(2).into(), + Hertz(0), + Hertz(0), + Hertz(0), + Hertz(0), + Hertz(0), + Hertz(0), + ], + used_clocks: 1u64 << u8::from(ClockId::FDPLL0), + } + } + + /// Returns a `GClock` for gclk0, the 120MHz oscillator. + pub fn gclk0(&mut self) -> GClock { + GClock { + gclk: GCLK0, + freq: self.gclks[0], + } + } + + /// Returns a `GClock` for gclk1, the 32KHz oscillator. + pub fn gclk1(&mut self) -> GClock { + GClock { + gclk: GCLK1, + freq: self.gclks[1], + } + } + + /// Returns the `GClock` for the specified clock generator. + /// If that clock generator has not yet been configured, + /// returns None. + pub fn get_gclk(&mut self, gclk: ClockGenId) -> Option { + let idx = u8::from(gclk) as usize; + if self.gclks[idx].0 == 0 { + None + } else { + Some(GClock { + gclk, + freq: self.gclks[idx], + }) + } + } + + /// Configures a clock generator with the specified divider and + /// source. + /// `divider` is a linear divider to be applied to the clock + /// source. While the hardware also supports an exponential divider, + /// this function doesn't expose that functionality at this time. + /// `improve_duty_cycle` is a boolean that, when set to true, enables + /// a 50/50 duty cycle for odd divider values. + /// Returns a `GClock` for the configured clock generator. + /// Returns `None` if the clock generator has already been configured. + pub fn configure_gclk_divider_and_source( + &mut self, + gclk: ClockGenId, + divider: u16, + src: ClockSource, + improve_duty_cycle: bool, + ) -> Option { + let idx = u8::from(gclk) as usize; + if self.gclks[idx].0 != 0 { + return None; + } + self.state + .set_gclk_divider_and_source(gclk, divider, src, improve_duty_cycle); + let freq: Hertz = match src { + XOSC32K | OSCULP32K => OSC32K_FREQ, + GCLKGEN1 => self.gclks[1], + DFLL => OSC48M_FREQ, + DPLL0 => OSC120M_FREQ, + XOSC0 | XOSC1 | GCLKIN | DPLL1 => unimplemented!(), + }; + self.gclks[idx] = Hertz(freq.0 / divider as u32); + Some(GClock { gclk, freq }) + } + + /// Enables or disables the given GClk from operation in standby. + pub fn configure_standby(&mut self, gclk: ClockGenId, enable: bool) { + self.state.configure_standby(gclk, enable) + } +} + +macro_rules! clock_generator { + ( + $( + $(#[$attr:meta])* + ($id:ident, $Type:ident, $clock:ident, $PclkId:ident), + )+ + ) => { + +$( + +/// A typed token that indicates that the clock for the peripheral(s) +/// with the matching name has been configured. +/// The effective clock frequency is available via the `freq` method, +/// or by converting the object into a `Hertz` instance. +/// The peripheral initialization code will typically require passing +/// in this object to prove at compile time that the clock has been +/// correctly initialized. +$(#[$attr])* +#[derive(Debug)] +pub struct $Type { + freq: Hertz, +} + +$(#[$attr])* +impl $Type { + /// Returns the frequency of the configured clock + pub fn freq(&self) -> Hertz { + self.freq + } +} +$(#[$attr])* +impl Into for $Type { + fn into(self) -> Hertz { + self.freq + } +} + +/// V2 to V1 compatibility layer that allows to convert V2 [`Pclk`] constructs +/// into corresponding V1 `*Clock` types. Thus, user can manage V1 clocking +/// compatible peripherals while using V2 clocking API +$(#[$attr])* +impl core::convert::From> for $Type { + fn from(pclk: Pclk<$PclkId, I>) -> Self { + $Type { + freq: pclk.freq() + } + } +} + + +)+ + +impl GenericClockController { + $( + /// Configure the clock for peripheral(s) that match the name + /// of this function to use the specific clock generator. + /// The `GClock` parameter may be one of default clocks + /// return from `gclk0()`, `gclk1()` or a clock configured + /// by the host application using the `configure_gclk_divider_and_source` + /// method. + /// Returns a typed token that proves that the clock has been configured; + /// the peripheral initialization code will typically require that this + /// clock token be passed in to ensure that the clock has been initialized + /// appropriately. + /// Returns `None` is the specified generic clock has already been + /// configured. + $(#[$attr])* + pub fn $id(&mut self, generator: &GClock) -> Option<$Type> { + let bits: u64 = 1 << u8::from(ClockId::$clock) as u64; + if (self.used_clocks & bits) != 0 { + return None; + } + self.used_clocks |= bits; + + self.state.enable_clock_generator(ClockId::$clock, generator.gclk); + let freq = self.gclks[u8::from(generator.gclk) as usize]; + Some($Type{freq}) + } + )+ +} + } +} + +clock_generator!( + (tc0_tc1, Tc0Tc1Clock, TC0_TC1, Tc0Tc1), + (tcc0_tcc1, Tcc0Tcc1Clock, TCC0_TCC1, Tcc0Tcc1), + (tc2_tc3, Tc2Tc3Clock, TC2_TC3, Tc2Tc3), + (tcc2_tcc3, Tcc2Tcc3Clock, TCC2_TCC3, Tcc2Tcc3), + #[cfg(feature = "min-samd51j")] + (tc4_tc5, Tc4Tc5Clock, TC4_TC5, Tc4Tc5), + #[cfg(feature = "min-samd51j")] + (tcc4, Tcc4Clock, TCC4, Tcc4), + #[cfg(feature = "min-samd51n")] + (tc6_tc7, Tc6Tc7Clock, TC6_TC7, Tc6Tc7), + (sercom0_core, Sercom0CoreClock, SERCOM0_CORE, Sercom0), + (sercom1_core, Sercom1CoreClock, SERCOM1_CORE, Sercom1), + (sercom2_core, Sercom2CoreClock, SERCOM2_CORE, Sercom2), + (sercom3_core, Sercom3CoreClock, SERCOM3_CORE, Sercom3), + (sercom4_core, Sercom4CoreClock, SERCOM4_CORE, Sercom4), + (sercom5_core, Sercom5CoreClock, SERCOM5_CORE, Sercom5), + #[cfg(feature = "min-samd51n")] + (sercom6_core, Sercom6CoreClock, SERCOM6_CORE, Sercom6), + #[cfg(feature = "min-samd51n")] + (sercom7_core, Sercom7CoreClock, SERCOM7_CORE, Sercom7), + (usb, UsbClock, USB, Usb), + (adc0, Adc0Clock, ADC0, Adc0), + (adc1, Adc1Clock, ADC1, Adc1), + (eic, EicClock, EIC, Eic), + (freq_m_msr, FreqmMsrClock, FREQM_MSR, FreqMMeasure), + (freq_m_ref, FreqmRefClock, FREQM_REF, FreqMReference), + (evsys0, Evsys0Clock, EVSYS0, EvSys0), + (evsys1, Evsys1Clock, EVSYS1, EvSys1), + (evsys2, Evsys2Clock, EVSYS2, EvSys2), + (evsys3, Evsys3Clock, EVSYS3, EvSys3), + (evsys4, Evsys4Clock, EVSYS4, EvSys4), + (evsys5, Evsys5Clock, EVSYS5, EvSys5), + (evsys6, Evsys6Clock, EVSYS6, EvSys6), + (evsys7, Evsys7Clock, EVSYS7, EvSys7), + (evsys8, Evsys8Clock, EVSYS8, EvSys8), + (evsys9, Evsys9Clock, EVSYS9, EvSys9), + (evsys10, Evsys10Clock, EVSYS10, EvSys10), + (evsys11, Evsys11Clock, EVSYS11, EvSys11), + #[cfg(any(feature = "same51", feature = "same53", feature = "same54"))] + (can0, Can0Clock, CAN0, Can0), + #[cfg(any(feature = "same51", feature = "same53", feature = "same54"))] + (can1, Can1Clock, CAN1, Can1), + (pdec, PdecClock, PDEC, PDec), + (ac, AcClock, AC, Ac), + (ccl, CclClock, CCL, Ccl), + (dac, DacClock, DAC, Dac), + #[cfg(feature = "min-samd51j")] + (i2s0, I2S0Clock, I2S0, I2S0), + #[cfg(feature = "min-samd51j")] + (i2s1, I2S1Clock, I2S1, I2S1), + (sdhc0, Sdhc0Clock, SDHC0, Sdhc0), + #[cfg(feature = "min-samd51n")] + (sdhc1, Sdhc1Clock, SDHC1, Sdhc1), + (cm4_trace, Cm4TraceClock, CM4_TRACE, CM4Trace), +); + +/// The frequency of the 48Mhz source. +pub const OSC48M_FREQ: Hertz = Hertz(48_000_000); +/// The frequency of the 32Khz source. +pub const OSC32K_FREQ: Hertz = Hertz(32_768); +/// The frequency of the 120Mhz source. +pub const OSC120M_FREQ: Hertz = Hertz(120_000_000); + +fn set_flash_to_half_auto_wait_state(nvmctrl: &mut NVMCTRL) { + // Zero indicates zero wait states, one indicates one wait state, etc., + // up to 15 wait states. + nvmctrl.ctrla.modify(|_, w| unsafe { w.rws().bits(0b0111) }); +} + +fn enable_gclk_apb(mclk: &mut MCLK) { + mclk.apbamask.modify(|_, w| w.gclk_().set_bit()); +} + +/// Turn on the internal 32hkz oscillator +fn enable_internal_32kosc(osc32kctrl: &mut OSC32KCTRL) { + osc32kctrl.osculp32k.modify(|_, w| { + w.en32k().set_bit(); + w.en1k().set_bit() + }); + osc32kctrl.rtcctrl.write(|w| w.rtcsel().ulp1k()); +} + +/// Turn on the external 32hkz oscillator +fn enable_external_32kosc(osc32kctrl: &mut OSC32KCTRL) { + osc32kctrl.xosc32k.modify(|_, w| { + w.ondemand().clear_bit(); + // Enable 32khz output + w.en32k().set_bit(); + w.en1k().set_bit(); + // Crystal connected to xin32/xout32 + w.xtalen().set_bit(); + w.enable().set_bit(); + w.cgm().xt(); + w.runstdby().set_bit() + }); + + osc32kctrl.rtcctrl.write(|w| w.rtcsel().xosc1k()); + + // Wait for the oscillator to stabilize + while osc32kctrl.status.read().xosc32krdy().bit_is_clear() {} +} + +fn wait_for_dpllrdy(oscctrl: &mut OSCCTRL) { + while oscctrl.dpll[0].dpllstatus.read().lock().bit_is_clear() + || oscctrl.dpll[0].dpllstatus.read().clkrdy().bit_is_clear() + {} +} + +/// Configure the dpll0 to run at 120MHz +fn configure_and_enable_dpll0(oscctrl: &mut OSCCTRL, gclk: &mut GCLK) { + gclk.pchctrl[ClockId::FDPLL0 as usize].write(|w| { + w.chen().set_bit(); + w.gen().gclk5() + }); + unsafe { + oscctrl.dpll[0].dpllratio.write(|w| { + w.ldr().bits(59); + w.ldrfrac().bits(0) + }); + } + oscctrl.dpll[0].dpllctrlb.write(|w| w.refclk().gclk()); + oscctrl.dpll[0].dpllctrla.write(|w| { + w.enable().set_bit(); + w.ondemand().clear_bit() + }); +} + +#[cfg(feature = "usb")] +/// Configure the dfll48m to calibrate against the 1Khz USB SOF reference. +fn configure_usb_correction(oscctrl: &mut OSCCTRL) { + oscctrl.dfllmul.write(|w| unsafe { + w.cstep().bits(0x1) + .fstep().bits(0x1) + // scaling factor for 1Khz SOF signal. + .mul().bits((48_000_000u32 / 1000) as u16) + }); + while oscctrl.dfllsync.read().dfllmul().bit_is_set() {} + + oscctrl.dfllctrlb.write(|w| { + // closed loop mode + w.mode().set_bit() + // chill cycle disable + .ccdis().set_bit() + // usb correction + .usbcrm().set_bit() + }); + while oscctrl.dfllsync.read().dfllctrlb().bit_is_set() {} +} diff --git a/hal/src/thumbv7em/clock/v2.rs b/hal/src/thumbv7em/clock/v2.rs new file mode 100644 index 00000000000..751e0ee9b44 --- /dev/null +++ b/hal/src/thumbv7em/clock/v2.rs @@ -0,0 +1,1014 @@ +//! # Version 2 of the `clock` module +//! +//! ## Overview +//! +//! This module provides a simple, ergonomic, and most of all **safe** API to +//! create and manage the clock tree in ATSAMD5x and E5x devices. It uses +//! [type-level programming techniques](crate::typelevel) to prevent users from +//! creating invalid or unsound clocking configurations. +//! +//!

+//! Note: Using a type-level API does place some limitations +//! on how the clocks can be configured. The types must be checked at +//! compile-time, which means the state of a given clock must also be known at +//! compile-time. This is exceedingly reasonable for most clocking +//! configurations, because most users set up their clocks once and never change +//! them again. However, if you need to dynamically change the clocking +//! configuration at run-time, you may find this API less ergonomic. A future, +//! fully dynamic API has been discussed, but nothing has been developed so far. +//!

+//! +//! The sections that follow provide an explanation of key concepts in the +//! module. We highly recommend users read through them to better understand the +//! `clock` module API. A [complete example](self#getting-started) is also +//! provided. +//! +//! ## Clock safety +//! +//! A clock tree represents dependencies among clocks, where producer clocks +//! feed consumer clocks. Root clocks are the original producers, as they are +//! derived from oscillators or external clocks. Branch clocks are both +//! producers and consumers, since they modify and distribute clocks. And leaf +//! clocks are consumers only; they drive peripherals or external clock outputs +//! but do not feed other clocks. +//! +//! To safely create and use a clock tree, it is critical that producer clocks +//! not be modified or disabled while their consumer clocks are still in active +//! use. Stated differently, if clock `B` consumes clock `A`, then clock `A` +//! **must not** be modified or disabled while clock `B` is still in use. +//! +//! Notice that this requirement mimics the principle of "aliased XOR mutable" +//! underlying the Rust borrow checker. A producer clock can only be modified if +//! it is not "borrowed" (consumed) by any other clocks. +//! +//! The following sections will review the various type-level programming +//! techniques used to enforce this principle in the `clock` module. +//! +//! ## Clock state machines +//! +//! Each available clock is represented in Rust as a unique, singleton object. +//! Users cannot create two instances of the same clock without using `unsafe`. +//! +//! However, a given clock is not always represented with the same **type**. +//! Specifically, each clock has at least two representations, one for the +//! configured and enabled clock, and another for the unconfigured and disabled +//! clock. +//! +//! These states are represented in Rust using distinct types, forming a +//! type-level state machine. Moreover, the disabled state is always represented +//! by a `Token` type. As the name implies, `Token`s have no functionality on +//! their own; they can only be exchanged for a different type representing +//! another state. +//! +//! ## Clock relationships +//! +//! In general, there are two classes of clock in ATSAMD chips. Some clocks map +//! one-to-one (1:1) to a specific bus or peripheral. This is true for the AHB +//! clocks ([`AhbClk`]s), APB clocks ([`ApbClk`]s), GCLK outputs ([`GclkOut`]s), +//! peripheral channel clocks ([`Pclk`]s), and RTC oscillator ([`RtcOsc`]). +//! Other clocks form one-to-many (1:N) relationships, like the external crystal +//! oscillator ([`Xosc`]), the 48 MHz DFLL ([`Dfll`]) or the two DPLLs +//! ([`Dpll`]). +//! +//! The `clock` module uses a distinct approach for each class. +//! +//! ### 1:1 clocks +//! +//! One-to-one relationships are easily modelled in Rust using move semantics. +//! For example, an enabled peripheral channel clock is represented as a +//! [`Pclk`] object. The respective peripheral API can move the `Pclk` and take +//! ownership of it. In that case, the `Pclk` acts as proof that the peripheral +//! clock is enabled, and the transfer of ownership prevents users from +//! modifying or disabling the `Pclk` while it is in use by the peripheral. +//! +//! One-to-one clocks generally have little to no configuration. They are +//! typically converted directly from disabled `Token` types to fully enabled +//! clock types. For example, the `Pclk` type has only two methods, +//! [`Pclk::enable`] and [`Pclk::disable`], which convert [`PclkToken`]s to +//! `Pclk`s and vice versa. +//! +//! ### 1:N clocks +//! +//! One-to-many relationships are more difficult to model in Rust. +//! +//! As discussed above, we are trying to create something akin to "aliased XOR +//! mutable", where producer clocks cannot be modified while used by consumer +//! clocks. A natural approach would be to use the Rust borrow checker directly. +//! In that case, consumer clocks would hold `&Producer` references to the +//! `Producer` clock object. The existence of outstanding shared borrows would +//! naturally prevent users from calling `Producer` methods taking `&mut self`. +//! +//! Unfortunately, while this approach could work, there is a critical problem +//! with disastrous consequences for ergonomics. To satisfy the Rust borrow +//! checker, `Producer` clock objects *could not be moved* while `&Producer` +//! references were still held by consumer clocks. +//! +//! However, this restriction is unnecessary. A `Producer` clock object is +//! merely a semantic object representing the "idea" of a producer clock. And +//! "borrowing" the producer is not meant to protect memory from corruption. +//! Rather, our goal is only to restrict the `Producer` API, to prevent it from +//! being modified or disabled once it has been connected to a consumer. We +//! don't need to permanently hold the `Producer` object in place to do that. +//! +//! It is possible to build a `clock` API based on the borrow checker, but it +//! would be extremely frustrating to use in practice, because of restrictions +//! on the movement of `Producer` objects. +//! +//! Instead, the `clock` module takes a different approach. It uses type-level +//! programming to track, at compile-time, the number of consumer clocks, N, +//! fed by a particular producer clock. With this approach, we can move +//! `Producer` objects while still making them impossible to modify if N > 0. +//! +//! The following sections will describe the implementation of this strategy. +//! +//! ## Tracking N at compile-time for 1:N clocks +//! +//! We have two specific goals. We need to both track the number of consumer +//! clocks, N, that are actively using a given producer clock. And we need to +//! restrict the producer clock API when N > 0. +//! +//! ### A compile-time counter +//! +//! First, we need to develop some way to track the number of consumer clocks, +//! N, within the type system. To accomplish this, we need both a way to +//! represent N in the type system and a way to increase or decrease N when +//! making or breaking connections in the clock tree. +//! +//! To represent N, we can use type-level, [`Unsigned`] integers from the +//! [`typenum`] crate (i.e. [`U0`], [`U1`], etc). And we can use a type +//! *parameter*, `N`, to represent some unknown, type-level number. +//! +//! Next, we need a way to increase or decrease the type parameter `N`. The +//! [`typenum`] crate provides type aliases [`Add1`] and [`Sub1`] that map from +//! each [`Unsigned`] integer to its successor and predecessor types, +//! respectively. We can leverage these to create our own type with a counter +//! that we [`Increment`] or [`Decrement`] at compile-time. These two traits +//! form the foundation for our strategy for handling 1:N clocks in this module. +//! +//! ### The `Enabled` wrapper +//! +//! Our representation of a 1:N producer clock is [`Enabled`], which is a +//! wrapper struct that pairs some *enabled* clock type `T` with a type `N` +//! representing a consumer count. The wrapper restricts access to the +//! underlying clock type, `T`, allowing us to selectively define methods when +//! `N = U0`, that is, when there are no consumers of a given producer clock. +//! +//! The [`Enabled`] type itself implements [`Increment`] and [`Decrement`] as +//! well, which allows type-level transformations to increment or decrement the +//! counter, e.g. `Enabled` to `Enabled`. Such transformations can +//! only be performed within the HAL; so users cannot change the consumer count +//! arbitrarily. +//! +//! ### Acting as a clock `Source` +//! +//! Finally, we need to define some generic interface for interacting with 1:N +//! producer clocks. However, when designing this interface, we need to be +//! careful not to lose information during type-level transformations. +//! +//! In particular, the `Enabled` counter type alone is not enough for proper +//! clock safety. If we used consumer `A` to `Increment` producer `P` from +//! `Enabled` to `Enabled`, but then used consumer `B` to +//! `Decrement` the producer back to `Enabled`, we would leave consumer +//! `A` dangling. +//! +//! To solve this problem, we need some way to guarantee that a given consumer +//! can only `Decrement` the same producer it `Increment`ed. Stated differently, +//! we need a way to track the identity of each consumer's clock source. +//! +//! The [`Source`] trait is designed for this purpose. It marks +//! [`Enabled`] producer clocks, and it's associated type, [`Id`], is the +//! identity type that should be stored by consumers. +//! +//! Given that all implementers of `Source` are instances of `Enabled`, +//! the naïve choice for [`Source::Id`] would be `T`. However, in a moment, we +//! will see why this choice is not ideal. +//! +//! ### `Id` types +//! +//! Many of the clock types in this module have additional type parameters that +//! track the clock's configuration. For instance, [`Xosc0`] represents one +//! of the external crystal oscillators. Here, the type parameter `M` represents +//! the XOSC's [`Mode`](xosc::Mode), which can either be [`CrystalMode`] or +//! [`ClockMode`]. Accordingly, methods to adjust the crystal current, etc. are +//! only available on `Xosc0`. +//! +//! While these type parameters are important and necessary for configuration of +//! a given producer clock, they are not relevant to consumer clocks. A consumer +//! clock does not need to know or care which `Mode` the XOSC is using, but +//! it *does* need to track that its clock [`Source`] is XOSC0. +//! +//! From this, we can see that `Enabled, N>` should not implement +//! `Source` with `Source::Id = Xosc0`, because that would require consumers +//! to needlessly track the XOSC `Mode`. +//! +//! Instead, this module defines a series of `Id` types representing the +//! *identity* of a given clock, rather than the clock itself. This is like the +//! distinction between a passport and a person. A passport identifies a person, +//! regardless of changes to their clothes or hair. The `Id` types serve to +//! erase configuration information, representing only the clock's identity. +//! +//! For `Xosc0`, the corresponding `Id` type is [`Xosc0Id`]. Thus, +//! `Enabled, N>` implements `Source` with `Source::Id = Xosc0Id`. +//! +//! ## Notes on memory safety +//! +//! ### Register interfaces +//! +//! Although HAL users see `Token` types as merely opaque objects, internally +//! they serve a dual purpose as the primary register interface to control the +//! corresponding clock. Moreover, they also fundamentally restructure the way +//! registers are accessed relative to the [PAC]. +//! +//! Each of the four PAC clocking structs ([`OSCCTRL`], [`OSC32KCTRL`], [`GCLK`] +//! and [`MCLK`]) is a singleton object that controls a set of MMIO registers. +//! It is impossible to create two instances of any PAC object without `unsafe`. +//! However, each object controls a large set of registers that can be further +//! sub-divided into smaller sets for individual clocks. For example, the +//! [`GCLK`] object controls registers for 12 different clock generators and 48 +//! peripheral channel clocks. +//! +//! `Token` types serve to break up the large PAC objects into smaller, +//! more-targetted pieces. And in the process, they also remove the PAC objects' +//! [interior mutability]. But this is only possible because each `Token` is +//! *also* a singleton, and because individual clocks are configured through +//! *mutually exclusive* sets of registers. +//! +//! ### Bus clocks +//! +//! Bus clocks are fundamentally different from the other clock types in this +//! module, because they do not use mutually exclusive registers for +//! configuration. For instance, the registers that control [`Dpll0`] are +//! mutually exclusive to those that control [`Dpll1`], but `ApbClk` +//! and `ApbClk` share a single register. +//! +//! This presents a challenge for memory safety, because we need some way to +//! guarantee that there are no data races. For example, if both +//! `ApbClk` and `ApbClk` tried to modify the `APBAMASK` +//! register from two different execution contexts, a read/modify/write +//! operation could be preempted, leading to memory corruption. +//! +//! To prevent data races when controlling bus clocks, we introduce two new +//! types to mediate access to the shared registers. For [`AhbClk`]s, this is +//! the [`Ahb`] type; and for [`ApbClk`]s, this is the [`Apb`] type. In a sense, +//! the `Ahb` and `Apb` types represent the actual corresponding buses. Thus, +//! enabling an APB clock by converting an [`ApbToken`] into an `ApbClk` +//! requires exclusive access to the `Apb` in the form of `&mut Apb`. +//! +//! ## Getting started +//! +//! To set up a clock tree, start by trading the [PAC](crate::pac)-level +//! clocking structs for their HAL equivalents. Right now, the only way to do so +//! safely is using the [`clock_system_at_reset`] function, which assumes all +//! clocks are in their default state at power-on reset. If this is not the +//! case, because, for example, a bootloader has modified the clocks, then you +//! may need to manually create the matching configuration using `unsafe` code. +//! +//! ```no_run +//! use atsamd_hal::clock::v2::clock_system_at_reset; +//! use atsamd_hal::pac::Peripherals; +//! let mut pac = Peripherals::take().unwrap(); +//! let (buses, clocks, tokens) = clock_system_at_reset( +//! pac.OSCCTRL, +//! pac.OSC32KCTRL, +//! pac.GCLK, +//! pac.MCLK, +//! &mut pac.NVMCTRL, +//! ); +//! ``` +//! +//! At this point, you may notice that the function returned three different +//! objects, the [`Buses`], [`Clocks`] and [`Tokens`]. +//! +//! The [`Buses`] struct contains the [`Ahb`] and [`Apb`] objects, which +//! represent the corresponding AHB and APB buses. See the [notes on memory +//! safety](self#notes-on-memory-safety) for more details on these types. +//! +//! The [`Clocks`] struct contains all of the clocks that are enabled and +//! running at power-on reset, specifically: +//! - All of the [`AhbClks`] +//! - Some of the [`ApbClks`] +//! - The 48 MHz [`Dfll`], running in open-loop mode, represented as as +//! `Enabled`. `N = U1` here because [`Gclk0`] consumes it. See +//! [above](self#tracking-n-at-compile-time-for-1n-clocks) for details on +//! [`Enabled`]. +//! - [`Gclk0`], sourced by the `Dfll` and represented as +//! `Enabled, U1>`. Note the use of [`DfllId`] as an [`Id` +//! type](self#id-types) here. Although `Gclk0` is not consumed by any clock +//! represented in this module, it *is* consumed by the processor's main +//! clock. We represent this by setting `N = U1`, which we use to restrict the +//! available API. Specifically, [`EnabledGclk0`] has special methods not +//! available to other [`Gclk`]s. +//! - The [`OscUlp32kBase`] clock, which can act as a [`Source`] for the +//! [`OscUlp1k`] and [`OscUlp32k`] clocks. It has no consumers at power-on +//! reset, so it is represented as `Enabled`. However, it +//! can never be disabled, so we provide no `.disable()` method. +//! +//! The [`Tokens`] struct contains all of the available `Token`s, which +//! [represent clocks that are disabled](self#clock-state-machines) at power-on +//! reset. Each `Token` can be exchanged for a corresponding clock object. +//! +//! ## Example clock tree +//! +//! Finally, we will walk through the creation of a simple clock tree to +//! illustrate some of the remaining concepts inherent to this module. +//! +//! Starting from the previous snippet, we have the [`Buses`], [`Clocks`] and +//! [`Tokens`] to work with, and our clock tree at power-on reset looks like +//! this. +//! +//! ```text +//! DFLL (48 MHz) +//! └── GCLK0 (48 MHz) +//! └── Main clock (48 MHz) +//! ``` +//! +//! Our goal will be a clock tree that looks like this: +//! +//! ```text +//! XOSC0 (8 MHz) +//! └── DPLL0 (100 MHz) +//! └── GCLK0 (100 MHz) +//! ├── Main clock (100 MHz) +//! ├── SERCOM0 peripheral clock +//! └── Output to GPIO pin +//! ``` +//! +//! We will use an external crystal oscillator running at 8 MHz to feed a DPLL, +//! which will increase the clock frequency to 100 MHz. Then, we will +//! reconfigure GCLK0 to use the 100 MHz DPLL clock instead of the 48 MHz DFLL +//! clock. +//! +//! First, let's import some of the necessary types. We will see what each type +//! represents in turn. +//! +//! ```no_run +//! use atsamd_hal::{ +//! clock::v2::{ +//! clock_system_at_reset, +//! dpll::Dpll, +//! pclk::Pclk, +//! xosc::Xosc, +//! }, +//! gpio::Pins, +//! pac::Peripherals, +//! time::U32Ext, +//! }; +//! ``` +//! +//! To create an instance of [`Xosc`], we will first need to identify which of +//! the two XOSC clocks we will use. Suppose an external crystal is attached to +//! pins `PA14` and `PA15`. These pins feed the XOSC0 clock, so we will want to +//! create an instance of [`Xosc0`]. Note that `Xosc0` is merely an alias for +//! `Xosc`. Here, [`Xosc0Id`] represents the +//! [*identity*](self#id-types) of the XOSC0 clock, rather than the clock +//! itself, and `M` represents the XOSC [`Mode`](xosc::Mode). +//! +//! Next, we access the [`Tokens`] struct to extract the corresponding +//! [`XoscToken`] for XOSC0, and we trade the PAC `PORT` struct for the +//! [`gpio::Pins`] struct to access the GPIO pins. We can then call +//! [`Xosc::from_crystal`] to trade the token and [`Pin`]s to yield an instance +//! of [`Xosc0`]. In doing so, we also provide the oscillator frequency. +//! +//! Finally, we can chain a call to the [`Xosc::enable`] method to enable the +//! XOSC and return an instance of [`EnabledXosc0`], which is simply an +//! alias for `Enabled, N>`. In this case, we get +//! `EnabledXosc0`. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # xosc::Xosc, +//! # }, +//! # gpio::Pins, +//! # pac::Peripherals, +//! # time::U32Ext, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let (buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! let pins = Pins::new(pac.PORT); +//! let xosc0 = Xosc::from_crystal( +//! tokens.xosc0, +//! pins.pa14, +//! pins.pa15, +//! 8.mhz(), +//! ).enable(); +//! ``` +//! +//! Next, we want to use a DPLL to multiply the 8 MHz crystal clock up to 100 +//! MHz. Once again, we need to decide between two instances of a clock, because +//! each chip has two [`Dpll`]s. This time, however, our decision between +//! [`Dpll0`] and [`Dpll1`] is arbitrary. +//! +//! Also note that, like before, `Dpll0` and `Dpll1` are aliases for +//! `Dpll` and `Dpll`. [`Dpll0Id`] and [`Dpll1Id`] +//! represent the *identity* of the respective DPLL, while `I` represents the +//! [`Id` type](self#id-types) for the [`Source`] driving the DPLL. In this +//! particular case, we aim to create an instance of `Dpll0`. +//! +//! Only certain clocks can drive the DPLL, so `I` is constrained by the +//! [`DpllSourceId`] trait. Specifically, only the [`Xosc0Id`], [`Xosc1Id`], +//! [`Xosc32kId`] and [`GclkId`] types implement this trait. +//! +//! As before, we access the [`Tokens`] struct and use the corresponding +//! [`DpllToken`] when creating an instance of `Dpll`. However, unlike before, +//! we are creating a new clock-tree relationship that must be tracked by the +//! type system. Because DPLL0 will now consume XOSC0, we must [`Increment`] +//! the [`Enabled`] counter for [`EnabledXosc0`]. +//! +//! Thus, to create an instance of `Dpll0`, we must provide the +//! `EnabledXosc0`, so that its `U0` type parameter can be incremented to `U1`. +//! The `Dpll::from_xosc` method takes ownership of the `EnabledXosc0` and +//! returns it with this modified type parameter. +//! +//! This is the essence of clock safety in this module. Once the counter type +//! has been incremeneted to `U1`, the `EnabledXosc0` can no longer be modified +//! or disabled. All further code can guarantee this invariant is upheld. To +//! modify the `EnabledXosc0`, we would first have to use `Dpll::free_source` to +//! disable the DPLL and [`Decrement`] the counter back to `U0`. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # dpll::Dpll, +//! # xosc::Xosc, +//! # }, +//! # gpio::Pins, +//! # pac::Peripherals, +//! # time::U32Ext, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let (buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! # let pins = Pins::new(pac.PORT); +//! # let xosc0 = Xosc::from_crystal( +//! # tokens.xosc0, +//! # pins.pa14, +//! # pins.pa15, +//! # 8.mhz(), +//! # ).enable(); +//! let (dpll0, xosc0) = Dpll::from_xosc(tokens.dpll0, xosc0); +//! ``` +//! Next, we set the DPLL pre-divider and loop divider. We must pre-divide +//! the XOSC clock down from 8 MHz to 2 MHz, so that it is within the valid +//! input frequency range for the DPLL. Then, we set the DPLL loop divider, so +//! that it will multiply the 2 MHz clock by 50 for a 100 MHz output. We do not +//! need fractional mutiplication here, so the fractional loop divider is zero. +//! Finally, we can enable the `Dpll`, yielding an instance of +//! `EnabledDpll0`. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # dpll::Dpll, +//! # xosc::Xosc, +//! # }, +//! # gpio::Pins, +//! # pac::Peripherals, +//! # time::U32Ext, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let (buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! # let pins = Pins::new(pac.PORT); +//! # let xosc0 = Xosc::from_crystal( +//! # tokens.xosc0, +//! # pins.pa14, +//! # pins.pa15, +//! # 8.mhz(), +//! # ).enable(); +//! # let (dpll0, xosc0) = Dpll::from_xosc(tokens.dpll0, xosc0); +//! let dpll0 = dpll0.prediv(4).loop_div(50, 0).enable(); +//! ``` +//! +//! So far, our clock tree looks like this +//! +//! ```text +//! DFLL (48 MHz) +//! └── GCLK0 (48 MHz) +//! └── Main clock (48 MHz) +//! +//! XOSC0 (8 MHz) +//! └── DPLL0 (100 MHz) +//! ``` +//! +//! Our next task will be to swap GCLK0 from the 48 MHz DFLL to the 100 MHz +//! DPLL. To do that, we will use the special [`swap_sources`] method on +//! [`EnabledGclk0`] to change the base clock without disabling GCLK0 or the +//! main clock. +//! +//! This time we will be modifying two [`Enabled`] counters simultaneously. +//! We will [`Decrement`] the [`EnabledDfll`] count from `U1` to `U0`, and +//! we will [`Increment`] the [`EnabledDpll0`] count from `U0` to `U1`. +//! Again, we need to provide both the DFLL and DPLL clocks, so that their +//! type parameters can be changed. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # dpll::Dpll, +//! # xosc::Xosc, +//! # }, +//! # gpio::Pins, +//! # pac::Peripherals, +//! # time::U32Ext, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let (buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! # let pins = Pins::new(pac.PORT); +//! # let xosc0 = Xosc::from_crystal( +//! # tokens.xosc0, +//! # pins.pa14, +//! # pins.pa15, +//! # 8.mhz(), +//! # ).enable(); +//! # let (dpll0, xosc0) = Dpll::from_xosc(tokens.dpll0, xosc0); +//! # let dpll0 = dpll0.prediv(4).loop_div(50, 0).enable(); +//! let (gclk0, dfll, dpll0) = clocks.gclk0.swap_sources(clocks.dfll, dpll0); +//! ``` +//! +//! At this point, the DFLL is completely unused, so it can be disbled and +//! deconstructed, leaving only the [`DfllToken`]. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # dpll::Dpll, +//! # xosc::Xosc, +//! # }, +//! # gpio::Pins, +//! # pac::Peripherals, +//! # time::U32Ext, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let (buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! # let pins = Pins::new(pac.PORT); +//! # let xosc0 = Xosc::from_crystal( +//! # tokens.xosc0, +//! # pins.pa14, +//! # pins.pa15, +//! # 8.mhz(), +//! # ).enable(); +//! # let (dpll0, xosc0) = Dpll::from_xosc(tokens.dpll0, xosc0); +//! # let dpll0 = dpll0.prediv(4).loop_div(50, 0).enable(); +//! # let (gclk0, dfll, dpll0) = clocks.gclk0.swap_sources(clocks.dfll, dpll0); +//! let dfll_token = dfll.disable().free(); +//! ``` +//! +//! Our clock tree now looks like this: +//! +//! ```text +//! XOSC0 (8 MHz) +//! └── DPLL0 (100 MHz) +//! └── GCLK0 (100 MHz) +//! └── Main clock (100 MHz) +//! ``` +//! +//! We have the clocks set up, but we're not using them for anything other than +//! the main clock. Our final steps will create SERCOM APB and peripheral +//! clocks and will output the raw GCLK0 to a GPIO pin. +//! +//! To enable the APB clock for SERCOM0, we must access the [`Apb`] bus struct. +//! We provide an [`ApbToken`] to the [`Apb::enable`] method and receive an +//! [`ApbClk`] in return. APB clocks are [1:1 clocks](self#clock-relationships), +//! so the `ApbClk` is not wrapped with [`Enabled`]. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # dpll::Dpll, +//! # xosc::Xosc, +//! # }, +//! # gpio::Pins, +//! # pac::Peripherals, +//! # time::U32Ext, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let (mut buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! # let pins = Pins::new(pac.PORT); +//! # let xosc0 = Xosc::from_crystal( +//! # tokens.xosc0, +//! # pins.pa14, +//! # pins.pa15, +//! # 8.mhz(), +//! # ).enable(); +//! # let (dpll0, xosc0) = Dpll::from_xosc(tokens.dpll0, xosc0); +//! # let dpll0 = dpll0.prediv(4).loop_div(50, 0).enable(); +//! # let (gclk0, dfll, dpll0) = clocks.gclk0.swap_sources(clocks.dfll, dpll0); +//! # let dfll_token = dfll.disable().free(); +//! let apb_sercom0 = buses.apb.enable(tokens.apbs.sercom0); +//! ``` +//! +//! To enable a peripheral channel clock for SERCOM0, we must provide the +//! corresponding [`PclkToken`], as well as the instance of [`EnabledGclk0`], so +//! that its counter can be incremented. The resulting clock has the type +//! `Pclk`. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # dpll::Dpll, +//! # pclk::Pclk, +//! # xosc::Xosc, +//! # }, +//! # gpio::Pins, +//! # pac::Peripherals, +//! # time::U32Ext, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let (mut buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! # let pins = Pins::new(pac.PORT); +//! # let xosc0 = Xosc::from_crystal( +//! # tokens.xosc0, +//! # pins.pa14, +//! # pins.pa15, +//! # 8.mhz(), +//! # ).enable(); +//! # let (dpll0, xosc0) = Dpll::from_xosc(tokens.dpll0, xosc0); +//! # let dpll0 = dpll0.prediv(4).loop_div(50, 0).enable(); +//! # let (gclk0, dfll, dpll0) = clocks.gclk0.swap_sources(clocks.dfll, dpll0); +//! # let dfll_token = dfll.disable().free(); +//! # let apb_sercom0 = buses.apb.enable(tokens.apbs.sercom0); +//! let (pclk_sercom0, gclk0) = Pclk::enable(tokens.pclks.sercom0, gclk0); +//! ``` +//! +//! Like [`Dpll`], [`Pclk`] also takes two type parameters. The +//! first represents the corresponding peripheral, while the second is again an +//! [`Id` type](self#id-types) representing the [`Source`] driving the [`Pclk`], +//! which is restricted by the [`PclkSourceId`] trait. Because peripheral +//! channel clocks can only be driven by GCLKs, [`PclkSourceId`] is effectively +//! synonymous with the [`GclkId`] trait. +//! +//! Finally, we would like to output GCLK0 to a GPIO pin. Doing so takes a +//! slightly different approach. This time, we provide a GPIO [`Pin`] to the +//! [`Gclk`], which creates a [`GclkOut`] and [`Increment`]s the consumer count +//! for [`EnabledGclk0`]. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # dpll::Dpll, +//! # pclk::Pclk, +//! # xosc::Xosc, +//! # }, +//! # gpio::Pins, +//! # pac::Peripherals, +//! # time::U32Ext, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let (mut buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! # let pins = Pins::new(pac.PORT); +//! # let xosc0 = Xosc::from_crystal( +//! # tokens.xosc0, +//! # pins.pa14, +//! # pins.pa15, +//! # 8.mhz(), +//! # ).enable(); +//! # let (dpll0, xosc0) = Dpll::from_xosc(tokens.dpll0, xosc0); +//! # let dpll0 = dpll0.prediv(4).loop_div(50, 0).enable(); +//! # let (gclk0, dfll, dpll0) = clocks.gclk0.swap_sources(clocks.dfll, dpll0); +//! # let dfll_token = dfll.disable().free(); +//! # let apb_sercom0 = buses.apb.enable(tokens.apbs.sercom0); +//! # let (pclk_sercom0, gclk0) = Pclk::enable(tokens.pclks.sercom0, gclk0); +//! let (gclk0, gclk0_out) = gclk0.enable_gclk_out(pins.pb14); +//! ``` +//! +//! We have arrived at our final, desired clock tree. Putting the whole example +//! together, we get +//! +//! ```no_run +//! use atsamd_hal::{ +//! clock::v2::{ +//! clock_system_at_reset, +//! dpll::Dpll, +//! pclk::Pclk, +//! xosc::Xosc, +//! }, +//! gpio::Pins, +//! pac::Peripherals, +//! time::U32Ext, +//! }; +//! +//! let mut pac = Peripherals::take().unwrap(); +//! let (mut buses, clocks, tokens) = clock_system_at_reset( +//! pac.OSCCTRL, +//! pac.OSC32KCTRL, +//! pac.GCLK, +//! pac.MCLK, +//! &mut pac.NVMCTRL, +//! ); +//! let pins = Pins::new(pac.PORT); +//! let xosc0 = Xosc::from_crystal( +//! tokens.xosc0, +//! pins.pa14, +//! pins.pa15, +//! 8.mhz(), +//! ) +//! .enable(); +//! let (dpll0, xosc0) = Dpll::from_xosc(tokens.dpll0, xosc0); +//! let dpll0 = dpll0.prediv(4).loop_div(50, 0).enable(); +//! let (gclk0, dfll, dpll0) = clocks.gclk0.swap_sources(clocks.dfll, dpll0); +//! let dfll_token = dfll.disable().free(); +//! let apb_sercom0 = buses.apb.enable(tokens.apbs.sercom0); +//! let (pclk_sercom0, gclk0) = Pclk::enable(tokens.pclks.sercom0, gclk0); +//! let (gclk0, gclk0_out) = gclk0.enable_gclk_out(pins.pb14); +//! ``` +//! +//! [PAC]: crate::pac +//! [`OSCCTRL`]: crate::pac::OSCCTRL +//! [`OSC32KCTRL`]: crate::pac::OSC32KCTRL +//! [`GCLK`]: crate::pac::GCLK +//! [`MCLK`]: crate::pac::MCLK +//! [`Peripherals::steal`]: crate::pac::Peripherals::steal +//! +//! [`Ahb`]: ahb::Ahb +//! [`AhbClk`]: ahb::AhbClk +//! [`AhbClk`]: ahb::AhbClk +//! [`AhbClks`]: ahb::AhbClks +//! +//! [`Apb`]: apb::Apb +//! [`Apb::enable`]: apb::Apb::enable +//! [`ApbClk`]: apb::ApbClk +//! [`ApbClk`]: apb::ApbClk +//! [`ApbClk::enable`]: apb::ApbClk::enable +//! [`ApbClks`]: apb::ApbClks +//! [`ApbToken`]: apb::ApbToken +//! +//! [`Dfll`]: dfll::Dfll +//! [`Dfll`]: dfll::Dfll +//! [`DfllId`]: dfll::DfllId +//! [`DfllToken`]: dfll::DfllToken +//! [`EnabledDfll`]: dfll::EnabledDfll +//! +//! [`Dpll`]: dpll::Dpll +//! [`Dpll`]: dpll::Dpll +//! [`Dpll0`]: dpll::Dpll0 +//! [`Dpll1`]: dpll::Dpll1 +//! [`Dpll0Id`]: dpll::Dpll0Id +//! [`Dpll1Id`]: dpll::Dpll1Id +//! [`DpllSourceId`]: dpll::DpllSourceId +//! [`DpllToken`]: dpll::DpllToken +//! [`EnabledDpll0`]: dpll::EnabledDpll0 +//! +//! [`Gclk0`]: gclk::Gclk0 +//! [`GclkId`]: gclk::GclkId +//! [`EnabledGclk0`]: gclk::EnabledGclk0 +//! [`swap_sources`]: gclk::EnabledGclk0::swap_sources +//! [`GclkOut`]: gclk::GclkOut +//! +//! [`OscUlp32kBase`]: osculp32k::OscUlp32kBase +//! [`OscUlp1k`]: osculp32k::OscUlp1k +//! [`OscUlp32k`]: osculp32k::OscUlp32k +//! +//! [`Pclk`]: pclk::Pclk +//! [`Pclk`]: pclk::Pclk +//! [`Pclk::enable`]: pclk::Pclk::enable +//! [`Pclk::disable`]: pclk::Pclk::disable +//! [`PclkSourceId`]: pclk::PclkSourceId +//! [`PclkToken`]: pclk::PclkToken +//! +//! [`RtcOsc`]: rtcosc::RtcOsc +//! +//! [`Xosc`]: xosc::Xosc +//! [`Xosc::from_crystal`]: xosc::Xosc::from_crystal +//! [`Xosc::enable`]: xosc::Xosc::enable +//! [`Xosc0`]: xosc::Xosc0 +//! [`Xosc0`]: xosc::Xosc0 +//! [`Xosc0Id`]: xosc::Xosc0Id +//! [`Xosc1Id`]: xosc::Xosc1Id +//! [`XoscToken`]: xosc::XoscToken +//! [`EnabledXosc0`]: xosc::EnabledXosc0 +//! [`EnabledXosc0`]: xosc::EnabledXosc0 +//! [`CrystalMode`]: xosc::CrystalMode +//! [`ClockMode`]: xosc::ClockMode +//! +//! [`Xosc32kId`]: xosc32k::Xosc32kId +//! +//! [type-level]: crate::typelevel +//! [`Increment`]: crate::typelevel::Increment +//! [`Decrement`]: crate::typelevel::Decrement +//! +//! [`Id`]: Source::Id +//! +//! [`gpio::Pins`]: crate::gpio::Pins +//! [`Pin`]: crate::gpio::Pin +//! +//! [`U1`]: typenum::U1 +//! [`Add1`]: typenum::Add1 +//! [`Sub1`]: typenum::Sub1 +//! [`Unsigned`]: typenum::Unsigned +//! +//! [interior mutability]: https://doc.rust-lang.org/reference/interior-mutability.html + +use typenum::U0; + +use crate::time::Hertz; +use crate::typelevel::{PrivateDecrement, PrivateIncrement, Sealed}; + +pub mod ahb; +pub mod apb; +pub mod dfll; +pub mod dpll; +pub mod gclk; +pub mod osculp32k; +pub mod pclk; +pub mod rtcosc; +pub mod types; +pub mod xosc; +pub mod xosc32k; + +mod reset; +pub use reset::*; + +// `Token` types and memory safety +// +// Each of the PAC [`Peripherals`] is a zero-sized, singleton struct that +// mediates access to the MMIO hardware registers. It is not possible to create +// two instances of any peripheral without causing a run-time panic. These +// structs implement [`Deref`] by conjuring a pointer to the corresponding +// register block, and each register within the block is represented by a +// `vcell::VolatileCell`. Because each register is wrapped in a `VolatileCell`, +// it is safe to both read and write them through shared references. However, +// because a read/modify/write operation is not atomic, the [`Peripherals`] +// structs do not implement [`Sync`]. +// +// This is a reasonable approach for the PAC, since it is generated from an +// SVD file. However, it is not the ideal structure for our HAL API. In +// particular, each [`Peripherals`] struct represents an entire peripheral, +// rather than a particular functional unit. In the HAL, we want our API to +// focus on functional units, so we need to define our own abstraction for +// registers, which will involve `unsafe` code. +// +// In the `clock` module, we represent each functional unit with a +// corresponding `Token` type. Just like the [`Peripherals`], each `Token` type +// is meant to be a singleton. However, unlike the PAC, we do not have to +// allow users to create `Token`s directly. Instead, we can have users exchange +// [`Peripherals`] for the `Token`s. Because each PAC struct is a singleton, we +// can guarantee each `Token` will be a singleton as well. With this approach, +// we don't need to implement our own run-time panicking; we simply extend the +// existing guarantees of the PAC. +// +// To implement a memory safe API, we must ensure that all `Token` types access +// mutually exclusive sets of registers. In that way, we guarantee no two +// `Token` types can access the same register. Moreover, in contrast to the PAC +// [`Peripherals`], we can make our `Token`s [`Sync`] if we remove all interior +// mutability and guarantee that writing or modifying a register requires +// ownership or an `&mut` reference. +// +// Thus, our `Token`-based API should be memory safe if we always obey the +// following requirements: +// - It should be `unsafe` to create a `Token` type unless it is created in +// exchange for the corresponding PAC peripheral struct. +// - Each `Token` type should have access to a mutually exclusive set of +// registers relative to the other `Token`s. +// - Writing or modifying a register should always require ownership of, or an +// `&mut` reference to, the corresponding `Token`. +// - When conjuring references to PAC registers or register blocks, we should +// *only* use shared, `&` references. There is no need to use exclusive, +// `&mut` references, because each register is wrapped in a `VolatileCell`. +// Moreover, using `&mut` references could cause UB, if we accidentally +// create two simultaneous references to the same register block from +// different `Tokens`. +// +// [`Peripherals`]: crate::pac::Peripherals +// [`Deref`]: core::ops::Deref + +/// Marks [`Enabled`] 1:N producer clocks that can act as a clock source +/// +/// Implementers of this type act as producer clocks and feed consumer clocks in +/// the clock tree. All implementors are [`Enabled`], 1:N clocks. The `Id` +/// associated type maps to the corresponding [`Id` type](self#id-types) of the +/// implementer. +/// +/// See the documentation on [`Source` clocks](self#acting-as-a-clock-source) +/// for more details. +pub trait Source: Sealed { + /// Corresponding `Id` type for the implementer + /// + /// A given implementer of [`Source`] might have type parameters + /// representing its configuration. For instance, [`EnabledXosc0`] has a + /// type parameter to track its [`Mode`]. However, a consumer clock + /// typically does not care about such configuration. It only needs to + /// know *which* upstream clock is its [`Source`]. + /// + /// `Id` types exist to fill this role. They represent the *identity* of a + /// given clock, regardless of any configuration. This is like the + /// distinction between a passport and a person. A passport identifies a + /// person, regardless of changes to their clothes or hair. + /// + /// Thus, [`EnabledXosc0`] implements [`Source`] with `Id = `[`Xosc0Id`], + /// regardless of `M`. + /// + /// See the documentation on [`Id` types](self#id-types) for more details. + /// + /// [`EnabledXosc0`]: xosc::EnabledXosc0 + /// [`Mode`]: xosc::Mode + /// [`Xosc0Id`]: xosc::Xosc0Id + type Id; + + /// Return the frequency of the clock source + fn freq(&self) -> Hertz; +} + +/// An enabled, 1:N clock with a compile-time counter for N +/// +/// This struct is a wrapper around other clock types from this module. It +/// represents a clock, `T`, that has been enabled, and it maintains a +/// compile-time counter, `N`, of its consumer clocks in the clock tree. +/// +/// Compile-time counting allows the API to restrict when clocks may be modified +/// or disabled. For example, `Enabled` clocks can only be disabled when their +/// counter is [`U0`]. +/// +/// The type-level counter is implemented using [`Unsigned`] integers from +/// the [`typenum`] crate, and it is modified using the [`Increment`] and +/// [`Decrement`] traits. +/// +/// See the [`Enabled` wrapper documentation](self#the-enabled-wrapper) for more +/// details. +/// +/// [`EnabledGclk0`]: gclk::EnabledGclk0 +/// [`Increment`]: crate::typelevel::Increment +/// [`Decrement`]: crate::typelevel::Decrement +/// [`Unsigned`]: typenum::Unsigned +pub struct Enabled(pub(crate) T, N); + +impl Sealed for Enabled {} + +impl Enabled { + #[inline] + pub(crate) fn new(t: T) -> Self { + Enabled(t, N::default()) + } +} + +impl PrivateIncrement for Enabled { + type Inc = Enabled; + + #[inline] + fn inc(self) -> Self::Inc { + Enabled(self.0, self.1.inc()) + } +} + +impl PrivateDecrement for Enabled { + type Dec = Enabled; + + #[inline] + fn dec(self) -> Self::Dec { + Enabled(self.0, self.1.dec()) + } +} diff --git a/hal/src/thumbv7em/clock/v2/ahb.rs b/hal/src/thumbv7em/clock/v2/ahb.rs new file mode 100644 index 00000000000..e7c5767835c --- /dev/null +++ b/hal/src/thumbv7em/clock/v2/ahb.rs @@ -0,0 +1,397 @@ +//! # Advanced high performance bus clocks +//! +//! ## Overview +//! +//! AHB clocks facilitate communication between the processor core and +//! peripherals on the AHB bus. To communicate with a peripheral, the +//! corresponding AHB clock must be enabled, which is done by setting a bit in +//! the `AHBMASK` register. +//! +//! In this module, *enabled* AHB clocks are represented by the [`AhbClk`] +//! struct, where the type parameter `A` is a type that implements [`AhbId`] and +//! corresponds to one of the bits in the `AHBMASK` register. +//! +//! While most other clocks in the `clock` module are configured through +//! mutually exclusive registers, the [`AhbClk`]s share a single `AHBMASK` +//! register. This presents a challenge for memory safety. Specifically, if we +//! allowed unrestricted access to the `AHBMASK` register through each `AhbClk`, +//! we could create data races. +//! +//! To solve this problem, we restrict access to the `AHBMASK` register using +//! the [`Ahb`] type. `Ahb` was created to act as a gateway to the `AHBMASK` +//! register, allowing us to use `&mut Ahb` as compile-time proof of exclusive +//! access to it. +//! +//! ## Example +//! +//! Enabling and disabling the [`AhbClk`]s proceeds according to the principles +//! outlined in the [`clock` module documentation]. It is best shown with an +//! example. +//! +//! Let's start by using [`clock_system_at_reset`] to access the HAL clocking +//! structs. +//! +//! ```no_run +//! use atsamd_hal::{ +//! clock::v2::{ +//! clock_system_at_reset, +//! }, +//! pac::Peripherals, +//! }; +//! let mut pac = Peripherals::take().unwrap(); +//! let (mut buses, clocks, tokens) = clock_system_at_reset( +//! pac.OSCCTRL, +//! pac.OSC32KCTRL, +//! pac.GCLK, +//! pac.MCLK, +//! &mut pac.NVMCTRL, +//! ); +//! ``` +//! +//! All AHB clocks are enabled at power-on reset. We can find them in the +//! [`Clocks`] struct. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # }, +//! # pac::Peripherals, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let (mut buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! let ahb_qspi = clocks.ahbs.qspi; +//! ``` +//! +//! To disable an `AhbClk`, we must have access to the [`Ahb`] bus type, which +//! is found in the [`Buses`] struct. As described above, [`Ahb`] mediates +//! access to the shared `AHBMASK` register. We call [`Ahb::disable`] to convert +//! an [`AhbClk`] into the corresponding [`AhbToken`]. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # }, +//! # pac::Peripherals, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let (mut buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! # let ahb_qspi = clocks.ahbs.qspi; +//! let ahb_qspi = buses.ahb.disable(ahb_qspi); +//! ``` +//! +//! To reenable an `AhbClk`, users must save the `AhbToken` and use it when +//! calling [`Ahb::enable`]. +//! +//! The complete example is shown below. +//! +//! ```no_run +//! use atsamd_hal::{ +//! clock::v2::{ +//! clock_system_at_reset, +//! }, +//! pac::Peripherals, +//! }; +//! let mut pac = Peripherals::take().unwrap(); +//! let (mut buses, clocks, tokens) = clock_system_at_reset( +//! pac.OSCCTRL, +//! pac.OSC32KCTRL, +//! pac.GCLK, +//! pac.MCLK, +//! &mut pac.NVMCTRL, +//! ); +//! let ahb_qspi = clocks.ahbs.qspi; +//! let ahb_qspi = buses.ahb.disable(ahb_qspi); +//! ``` +//! +//! [`clock` module documentation]: super +//! [`clock_system_at_reset`]: super::clock_system_at_reset +//! [`Clocks`]: super::Clocks +//! [`Buses`]: super::Buses + +use core::marker::PhantomData; + +use bitflags; +use paste::paste; + +use crate::pac::{mclk, MCLK}; + +use super::types::*; + +//============================================================================== +// Ahb +//============================================================================== + +/// AHB clock controller +/// +/// As described in the [module-level documentation](self), this struct mediates +/// access to the shared `AHBMASK` register. Users can convert a disabled +/// [`AhbToken`] into an enabled [`AhbClk`] using [`Ahb::enable`], and +/// vice versa with [`Ahb::disable`]. +pub struct Ahb(()); + +impl Ahb { + /// Create a new instance of [`Ahb`] + /// + /// # Safety + /// + /// Because the `Ahb` mediates access to the `AHBMASK` register, it must be + /// a singleton. There must never be two simulatenous instances of it at a + /// time. See the notes on `Token` types and memory safety in the root of + /// the `clock` module for more details. + #[inline] + pub(super) unsafe fn new() -> Self { + Self(()) + } + + #[inline] + fn ahbmask(&mut self) -> &mclk::AHBMASK { + // Safety: The `Ahb` type has exclusive access to the `AHBMASK` + // register. See the notes on `Token` types and memory safety in the + // root of the `clock` module for more details. + unsafe { &(*MCLK::PTR).ahbmask } + } + + #[inline] + fn enable_mask(&mut self, mask: AhbMask) { + // Safety: The mask bits are derived from a `bitflags` struct, so they + // are guaranteed to be valid. + self.ahbmask() + .modify(|r, w| unsafe { w.bits(r.bits() | mask.bits()) }); + } + + #[inline] + fn disable_mask(&mut self, mask: AhbMask) { + // Safety: The mask bits are derived from a `bitflags` struct, so they + // are guaranteed to be valid. + self.ahbmask() + .modify(|r, w| unsafe { w.bits(r.bits() & !mask.bits()) }); + } + + /// Enable the corresponding AHB clock + /// + /// Consume an [`AhbToken`], enable the corresponding AHB clock and return + /// an [`AhbClk`]. The `AhbClk` represents proof that the corresponding AHB + /// clock has been enabled. + #[inline] + pub fn enable(&mut self, token: AhbToken) -> AhbClk { + self.enable_mask(A::DYN.into()); + AhbClk::new(token) + } + + /// Disable the corresponding AHB clock + /// + /// Consume the [`AhbClk`], disable the corresponding AHB clock and return + /// the [`AhbToken`]. + #[inline] + pub fn disable(&mut self, clock: AhbClk) -> AhbToken { + self.disable_mask(A::DYN.into()); + clock.free() + } +} + +//============================================================================== +// AhbId +//============================================================================== + +/// Type-level enum identifying one of the possible AHB clocks +/// +/// The types implementing this trait are type-level variants of `AhbId`, and +/// they identify one of the possible AHB clocks, which can vary by chip. Each +/// type corresponds to a specific bit in the `AHBMASK` register. +/// +/// `AhbId` is the type-level equivalent of [`DynAhbId`]. See the documentation +/// on [type-level programming] and specifically [type-level enums] for more +/// details. +/// +/// [type-level programming]: crate::typelevel +/// [type-level enums]: crate::typelevel#type-level-enums +pub trait AhbId: crate::typelevel::Sealed { + /// Corresponding [`DynAhbId`] + const DYN: DynAhbId; +} + +//============================================================================== +// AhbToken +//============================================================================== + +/// Singleton token that can be exchanged for an [`AhbClk`] +/// +/// As explained in the [`clock` module documentation](super), instances of +/// various `Token` types can be exchanged for actual clock types. They +/// represent clocks that are disabled. +/// +/// The type parameter `A` is an [`AhbId`] indicating which AHB clock is +/// represented by this token. To enable the corresponding AHB clock, use the +/// [`Ahb::enable`] method. +pub struct AhbToken { + id: PhantomData, +} + +impl AhbToken { + /// Create a new instance of [`AhbToken`] + /// + /// # Safety + /// + /// Each `AhbToken` is a singleton. There must never be two simulatenous + /// instances with the same [`AhbId`]. See the notes on `Token` types and + /// memory safety in the root of the `clock` module for more details. + #[inline] + unsafe fn new() -> Self { + AhbToken { id: PhantomData } + } +} + +//============================================================================== +// AhbClk +//============================================================================== + +/// An enabled AHB clock +/// +/// An [`AhbClk`] represents an enabled AHB clock. The type parameter `A` is an +/// [`AhbId`], which corresponds to a particular bit in the `AHBMASK` +/// register. An `AhbClk` can be disabled with the [`Ahb::disable`] method. +pub struct AhbClk { + token: AhbToken, +} + +impl AhbClk { + #[inline] + fn new(token: AhbToken) -> Self { + AhbClk { token } + } + + #[inline] + fn free(self) -> AhbToken { + self.token + } +} + +//============================================================================== +// DynAhbId & AhbClks +//============================================================================== + +macro_rules! define_ahb_types { + ( + $( + $( #[$( $cfg:tt )+] )? + $Type:ident = $BIT:literal, + )+ + ) => { + paste! { + bitflags::bitflags! { + /// AHB clock register mask + /// + /// This is a [`bitflags`] struct with a binary representation + /// exactly matching the `AHBMASK` register. + struct AhbMask: u32 { + $( + $( #[$( $cfg )+] )? + const [<$Type:upper>] = 1 << $BIT; + )+ + } + } + + /// Value-level enum identifying a single AHB clock + /// + /// Each variant of this enum corresponds to a specific bit in the + /// `AHBMASK` register and identifies one of the possible AHB + /// clocks, which can vary by chip. + /// + /// `DynAhbId` is the value-level equivalent of [`AhbId`]. + #[repr(u8)] + pub enum DynAhbId { + $( + $( #[$( $cfg )+] )? + $Type = $BIT, + )+ + } + + impl From for AhbMask { + #[inline] + fn from(id: DynAhbId) -> AhbMask { + match id { + $( + $( #[$( $cfg )+] )? + DynAhbId::$Type => AhbMask::[<$Type:upper>], + )+ + } + } + } + + $( + $( #[$( $cfg )+] )? + impl AhbId for $Type { + const DYN: DynAhbId = DynAhbId::$Type; + } + )+ + + /// Set of all [`AhbClk`]s + /// + /// All [`AhbClk`]s are enabled at power-on reset. + pub struct AhbClks { + $( + $( #[$( $cfg )+] )? + pub [<$Type:snake>]: AhbClk<$Type>, + )+ + } + impl AhbClks { + /// Create the set of [`AhbClk`]s + /// + /// # Safety + /// + /// All invariants of `AhbToken::new` must be upheld here. + #[inline] + pub(super) unsafe fn new() -> Self { + AhbClks { + $( + $( #[$( $cfg )+] )? + [<$Type:snake>]: AhbClk::new(AhbToken::new()), + )+ + } + } + } + } + }; +} + +define_ahb_types!( + Hpb0 = 0, + Hpb1 = 1, + Hpb2 = 2, + Hpb3 = 3, + Dsu = 4, + NvmCtrl = 6, + Cmcc = 8, + Dmac = 9, + Usb = 10, + Pac = 12, + Qspi = 13, + #[cfg(any(feature = "same53", feature = "same54"))] + Gmac = 14, + Sdhc0 = 15, + #[cfg(feature = "min-samd51n")] + Sdhc1 = 16, + #[cfg(any(feature = "same51", feature = "same53", feature = "same54"))] + Can0 = 17, + #[cfg(any(feature = "same51", feature = "same53", feature = "same54"))] + Can1 = 18, + Icm = 19, + Pukcc = 20, + Qspi2x = 21, + NvmCtrlSmeeProm = 22, + NvmCtrlCache = 23, +); diff --git a/hal/src/thumbv7em/clock/v2/apb.rs b/hal/src/thumbv7em/clock/v2/apb.rs new file mode 100644 index 00000000000..084c819a21f --- /dev/null +++ b/hal/src/thumbv7em/clock/v2/apb.rs @@ -0,0 +1,669 @@ +//! # Advanced peripheral bus clocks +//! +//! ## Overview +//! +//! APB clocks facilitate communication between the processor core and +//! peripherals on the APB bus. To communicate with a peripheral, the +//! corresponding APB clock must be enabled, which is done by setting a bit in +//! one of the four `APBXMASK` registers. +//! +//! In this module, *enabled* APB clocks are represented by the [`ApbClk`] +//! struct, where the type parameter `A` is a type that implements [`ApbId`] and +//! corresponds to one of the bits in an `APBXMASK` register. +//! +//! While most other clocks in the `clock` module are configured through +//! mutually exclusive registers, the [`ApbClk`]s share the four `APBXMASK` +//! registers. This presents a challenge for memory safety. Specifically, if we +//! allowed unrestricted access to the corresponding `APBXMASK` register through +//! each `ApbClk`, we could create data races. +//! +//! To solve this problem, we restrict access to the `APBXMASK` registers using +//! the [`Apb`] type. `Apb` was created to act as a gateway to the `APBXMASK` +//! registers, allowing us to use `&mut Apb` as compile-time proof of exclusive +//! access to them. +//! +//! ## Example +//! +//! Enabling and disabling the [`ApbClk`]s proceeds according to the principles +//! outlined in the [`clock` module documentation]. It is best shown with an +//! example. +//! +//! Let's start by using [`clock_system_at_reset`] to access the HAL clocking +//! structs. +//! +//! ```no_run +//! use atsamd_hal::{ +//! clock::v2::{ +//! clock_system_at_reset, +//! }, +//! pac::Peripherals, +//! }; +//! let mut pac = Peripherals::take().unwrap(); +//! let (mut buses, clocks, tokens) = clock_system_at_reset( +//! pac.OSCCTRL, +//! pac.OSC32KCTRL, +//! pac.GCLK, +//! pac.MCLK, +//! &mut pac.NVMCTRL, +//! ); +//! ``` +//! +//! Some APB clocks are enabled at power-on reset. We can find these in the +//! [`Clocks`] struct. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # }, +//! # pac::Peripherals, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let (mut buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! let apb_port = clocks.apbs.port; +//! ``` +//! +//! Other APB clocks are disabled at power-on reset. To enable these, we must +//! have access to the [`Apb`] bus type, which is found in the [`Buses`] struct. +//! As described above, [`Apb`] mediates access to the shared `APBXMASK` +//! registers. We call [`Apb::enable`] to convert an [`ApbToken`] into the +//! corresponding [`ApbClk`]. The existence of each `ApbClk` type represents +//! proof that the corresponding APB clock has been enabled. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # }, +//! # pac::Peripherals, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let (mut buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! # let apb_port = clocks.apbs.port; +//! let apb_sercom0 = buses.apb.enable(tokens.apbs.sercom0); +//! ``` +//! +//! The complete example is shown below. +//! +//! ```no_run +//! use atsamd_hal::{ +//! clock::v2::{ +//! clock_system_at_reset, +//! }, +//! pac::Peripherals, +//! }; +//! let mut pac = Peripherals::take().unwrap(); +//! let (mut buses, clocks, tokens) = clock_system_at_reset( +//! pac.OSCCTRL, +//! pac.OSC32KCTRL, +//! pac.GCLK, +//! pac.MCLK, +//! &mut pac.NVMCTRL, +//! ); +//! let apb_port = clocks.apbs.port; +//! let apb_sercom0 = buses.apb.enable(tokens.apbs.sercom0); +//! ``` +//! +//! [`clock` module documentation]: super +//! [`clock_system_at_reset`]: super::clock_system_at_reset +//! [`Clocks`]: super::Clocks +//! [`Buses`]: super::Buses + +use core::marker::PhantomData; + +use bitflags; +use paste::paste; + +use crate::pac::{mclk, MCLK}; + +use crate::typelevel::Sealed; + +use super::types::*; + +//============================================================================== +// Registers +//============================================================================== + +/// APB clock controller +/// +/// As described in the [module-level documentation](self), this struct mediates +/// access to the shared `APBXMASK` registers. Users can convert a disabled +/// [`ApbToken`] into an enabled [`ApbClk`] using [`Apb::enable`], and +/// vice versa with [`Apb::disable`]. +pub struct Apb(()); + +impl Apb { + /// Create a new instance of [`Apb`] + /// + /// # Safety + /// + /// Because the `Apb` mediates access to the `APBMASK` registers, it must be + /// a singleton. There must never be two simulatenous instances of it at a + /// time. See the notes on `Token` types and memory safety in the root of + /// the `clock` module for more details. + #[inline] + pub(super) unsafe fn new() -> Self { + Self(()) + } + + #[inline] + fn mclk(&self) -> &mclk::RegisterBlock { + // Safety: The `Apb` type has exclusive access to the `APBXMASK` + // registers, and it uses a shared reference to the register block. See + // the notes on `Token` types and memory safety in the root of the + // `clock` module for more details. + unsafe { &*MCLK::PTR } + } + + #[inline] + fn apbamask(&mut self) -> &mclk::APBAMASK { + &self.mclk().apbamask + } + + #[inline] + fn apbbmask(&mut self) -> &mclk::APBBMASK { + &self.mclk().apbbmask + } + + #[inline] + fn apbcmask(&mut self) -> &mclk::APBCMASK { + &self.mclk().apbcmask + } + + #[inline] + fn apbdmask(&mut self) -> &mclk::APBDMASK { + &self.mclk().apbdmask + } + + #[inline] + fn enable_mask(&mut self, mask: ApbMask) { + // Safety: The mask bits are derived from a `bitflags` struct, so they + // are guaranteed to be valid. + unsafe { + match mask { + ApbMask::A(mask) => { + self.apbamask() + .modify(|r, w| w.bits(r.bits() | mask.bits())); + } + ApbMask::B(mask) => { + self.apbbmask() + .modify(|r, w| w.bits(r.bits() | mask.bits())); + } + ApbMask::C(mask) => { + self.apbcmask() + .modify(|r, w| w.bits(r.bits() | mask.bits())); + } + ApbMask::D(mask) => { + self.apbdmask() + .modify(|r, w| w.bits(r.bits() | mask.bits())); + } + } + } + } + + #[inline] + fn disable_mask(&mut self, mask: ApbMask) { + // Safety: The mask bits are derived from a `bitflags` struct, so they + // are guaranteed to be valid. + unsafe { + match mask { + ApbMask::A(mask) => { + self.apbamask() + .modify(|r, w| w.bits(r.bits() & !mask.bits())); + } + ApbMask::B(mask) => { + self.apbbmask() + .modify(|r, w| w.bits(r.bits() & !mask.bits())); + } + ApbMask::C(mask) => { + self.apbcmask() + .modify(|r, w| w.bits(r.bits() & !mask.bits())); + } + ApbMask::D(mask) => { + self.apbdmask() + .modify(|r, w| w.bits(r.bits() & !mask.bits())); + } + } + } + } + + /// Enable the corresponding APB clock + /// + /// Consume an [`ApbToken`], enable the corresponding APB clock and return + /// an [`ApbClk`]. The `ApbClk` represents proof that the corresponding APB + /// clock has been enabled. + #[inline] + pub fn enable(&mut self, token: ApbToken) -> ApbClk { + self.enable_mask(A::DYN.into()); + ApbClk::new(token) + } + + /// Disable the corresponding APB clock + /// + /// Consume the [`ApbClk`], disable the corresponding APB clock and return + /// the [`ApbToken`]. + #[inline] + pub fn disable(&mut self, clock: ApbClk) -> ApbToken { + self.disable_mask(A::DYN.into()); + clock.free() + } +} + +//============================================================================== +// DynApbId & ApbMask +//============================================================================== + +/// A mask corresponding to one of the APB bridge registers +/// +/// Each variant is a [`bitflags`] struct with a binary representation exactly +/// matching the corresponding APB `MASK` register. +enum ApbMask { + A(ApbAMask), + B(ApbBMask), + C(ApbCMask), + D(ApbDMask), +} + +macro_rules! define_apb_types { + ( + $( + $Reg:ident { + $( + $( #[$( $cfg:tt )+] )? + $Type:ident = $BIT:literal, + )+ + } + )+ + ) => { + /// Value-level enum identifying a single APB clock + /// + /// Each variant of this enum corresponds to a specific bit in one of + /// the four `APBXMASK` registers and identifies one of many possible + /// APB clocks, which can vary by chip. + /// + /// `DynApbId` is the value-level equivalent of [`ApbId`]. + #[repr(u8)] + pub enum DynApbId { + $( + $( + $( #[$( $cfg )+] )? + $Type, + )+ + )+ + } + + $( + $( + $( #[$( $cfg )+] )? + impl ApbId for $Type { + const DYN: DynApbId = DynApbId::$Type; + } + )+ + )+ + + paste! { + $( + bitflags::bitflags! { + #[ + doc = + "APB bridge `" $Reg "` register mask\n" + "\n" + "This is a [`bitflags`] struct with a binary representation " + "exactly matching the `APB" $Reg "MASK` register." + ] + struct []: u32 { + $( + $( #[$( $cfg )+] )? + const [<$Type:upper>] = 1 << $BIT; + )+ + } + } + + )+ + + impl From for ApbMask { + #[inline] + fn from(id: DynApbId) -> Self { + use DynApbId::*; + match id { + $( + $( + $( #[$( $cfg )+] )? + $Type => ApbMask::$Reg([]::[<$Type:upper>]), + )+ + )+ + } + } + } + } + }; +} + +define_apb_types!( + A { + Pac = 0, + Pm = 1, + Mclk = 2, + RstC = 3, + OscCtrl = 4, + Osc32kCtrl = 5, + SupC = 6, + Gclk = 7, + Wdt = 8, + Rtc = 9, + Eic = 10, + FreqM = 11, + Sercom0 = 12, + Sercom1 = 13, + Tc0 = 14, + Tc1 = 15, + } + B { + Usb = 0, + Dsu = 1, + NvmCtrl = 2, + Port = 4, + EvSys = 7, + Sercom2 = 9, + Sercom3 = 10, + Tcc0 = 11, + Tcc1 = 12, + Tc2 = 13, + Tc3 = 14, + RamEcc = 16, + } + C { + #[cfg(any(feature = "same53", feature = "same54"))] + Gmac = 2, + Tcc2 = 3, + #[cfg(feature = "min-samd51j")] + Tcc3 = 4, + #[cfg(feature = "min-samd51j")] + Tc4 = 5, + #[cfg(feature = "min-samd51j")] + Tc5 = 6, + PDec = 7, + Ac = 8, + Aes = 9, + Trng = 10, + Icm = 11, + Qspi = 13, + Ccl = 14, + } + D { + Sercom4 = 0, + Sercom5 = 1, + #[cfg(feature = "min-samd51n")] + Sercom6 = 2, + #[cfg(feature = "min-samd51n")] + Sercom7 = 3, + #[cfg(feature = "min-samd51j")] + Tcc4 = 4, + #[cfg(feature = "min-samd51n")] + Tc6 = 5, + #[cfg(feature = "min-samd51n")] + Tc7 = 6, + Adc0 = 7, + Adc1 = 8, + Dac = 9, + #[cfg(feature = "min-samd51j")] + I2S = 10, + Pcc = 11, + } +); + +//============================================================================== +// ApbId +//============================================================================== + +/// Type-level enum identifying one of the possible APB clocks +/// +/// The types implementing this trait are type-level variants of `ApbId`, and +/// they identify one of the many possible APB clocks, which can vary by chip. +/// Each type corresponds to a specific bit in one of the four `APBXMASK` +/// registers. +/// +/// `ApbId` is the type-level equivalent of [`DynApbId`]. See the documentation +/// on [type-level programming] and specifically [type-level enums] for more +/// details. +/// +/// [type-level programming]: crate::typelevel +/// [type-level enums]: crate::typelevel#type-level-enums +pub trait ApbId: Sealed { + /// Corresponding variant of [`DynApbId`] + const DYN: DynApbId; +} + +//============================================================================== +// ApbToken +//============================================================================== + +/// Singleton token that can be exchanged for an [`ApbClk`] +/// +/// As explained in the [`clock` module documentation](super), instances of +/// various `Token` types can be exchanged for actual clock types. They +/// represent clocks that are disabled. +/// +/// The type parameter `A` is an [`ApbId`] indicating which APB clock is +/// represented by this token. To enable the corresponding APB clock, use the +/// [`Apb::enable`] method. +pub struct ApbToken { + id: PhantomData, +} + +impl ApbToken { + /// Create a new instance of [`ApbToken`] + /// + /// # Safety + /// + /// Each `ApbToken` is a singleton. There must never be two simulatenous + /// instances with the same [`ApbId`]. See the notes on `Token` types and + /// memory safety in the root of the `clock` module for more details. + #[inline] + unsafe fn new() -> Self { + ApbToken { id: PhantomData } + } +} + +//============================================================================== +// ApbClk +//============================================================================== + +/// An enabled APB clock +/// +/// An [`ApbClk`] represents an enabled APB clock. The type parameter `A` is an +/// [`ApbId`], which corresponds to a particular bit in the `APBXMASK` +/// registers. An `ApbClk` can be disabled with the [`Apb::disable`] method. +pub struct ApbClk { + token: ApbToken, +} + +impl ApbClk { + #[inline] + fn new(token: ApbToken) -> Self { + ApbClk { token } + } + + #[inline] + fn free(self) -> ApbToken { + self.token + } +} + +//============================================================================== +// ApbTokens +//============================================================================== + +/// Set of [`ApbToken`]s for APB clocks that are disabled at power-on reset +pub struct ApbTokens { + pub freq_m: ApbToken, + pub sercom0: ApbToken, + pub sercom1: ApbToken, + pub tc0: ApbToken, + pub tc1: ApbToken, + pub usb: ApbToken, + pub ev_sys: ApbToken, + pub sercom2: ApbToken, + pub sercom3: ApbToken, + pub tcc0: ApbToken, + pub tcc1: ApbToken, + pub tc2: ApbToken, + pub tc3: ApbToken, + pub tcc2: ApbToken, + #[cfg(feature = "min-samd51j")] + pub tcc3: ApbToken, + #[cfg(feature = "min-samd51j")] + pub tc5: ApbToken, + pub p_dec: ApbToken, + pub ac: ApbToken, + pub aes: ApbToken, + pub trng: ApbToken, + pub icm: ApbToken, + pub ccl: ApbToken, + pub sercom4: ApbToken, + pub sercom5: ApbToken, + #[cfg(feature = "min-samd51n")] + pub sercom6: ApbToken, + #[cfg(feature = "min-samd51n")] + pub sercom7: ApbToken, + #[cfg(feature = "min-samd51j")] + pub tcc4: ApbToken, + #[cfg(feature = "min-samd51n")] + pub tc6: ApbToken, + #[cfg(feature = "min-samd51n")] + pub tc7: ApbToken, + pub adc0: ApbToken, + pub adc1: ApbToken, + pub dac: ApbToken, + #[cfg(feature = "min-samd51j")] + pub i2s: ApbToken, + pub pcc: ApbToken, +} + +impl ApbTokens { + /// Create the set of [`ApbToken`]s + /// + /// # Safety + /// + /// All invariants required by `ApbToken::new` must be upheld here as well. + #[inline] + pub(super) unsafe fn new() -> Self { + Self { + freq_m: ApbToken::new(), + sercom0: ApbToken::new(), + sercom1: ApbToken::new(), + tc0: ApbToken::new(), + tc1: ApbToken::new(), + usb: ApbToken::new(), + ev_sys: ApbToken::new(), + sercom2: ApbToken::new(), + sercom3: ApbToken::new(), + tcc0: ApbToken::new(), + tcc1: ApbToken::new(), + tc2: ApbToken::new(), + tc3: ApbToken::new(), + tcc2: ApbToken::new(), + #[cfg(feature = "min-samd51j")] + tcc3: ApbToken::new(), + #[cfg(feature = "min-samd51j")] + tc5: ApbToken::new(), + p_dec: ApbToken::new(), + ac: ApbToken::new(), + aes: ApbToken::new(), + trng: ApbToken::new(), + icm: ApbToken::new(), + ccl: ApbToken::new(), + sercom4: ApbToken::new(), + sercom5: ApbToken::new(), + #[cfg(feature = "min-samd51n")] + sercom6: ApbToken::new(), + #[cfg(feature = "min-samd51n")] + sercom7: ApbToken::new(), + #[cfg(feature = "min-samd51j")] + tcc4: ApbToken::new(), + #[cfg(feature = "min-samd51n")] + tc6: ApbToken::new(), + #[cfg(feature = "min-samd51n")] + tc7: ApbToken::new(), + adc0: ApbToken::new(), + adc1: ApbToken::new(), + dac: ApbToken::new(), + #[cfg(feature = "min-samd51j")] + i2s: ApbToken::new(), + pcc: ApbToken::new(), + } + } +} + +//============================================================================== +// ApbClks +//============================================================================== + +/// Set of [`ApbClk`]s for APB clocks that are enabled at power-on reset +pub struct ApbClks { + pub pac: ApbClk, + pub pm: ApbClk, + pub mclk: ApbClk, + pub rst_c: ApbClk, + pub osc_ctrl: ApbClk, + pub osc32k_ctrl: ApbClk, + pub sup_c: ApbClk, + pub gclk: ApbClk, + pub wdt: ApbClk, + pub rtc: ApbClk, + pub eic: ApbClk, + pub dsu: ApbClk, + pub nvm_ctrl: ApbClk, + pub port: ApbClk, + pub ram_ecc: ApbClk, + #[cfg(any(feature = "same53", feature = "same54"))] + pub gmac: ApbClk, + #[cfg(feature = "min-samd51j")] + pub tc4: ApbClk, + pub qspi: ApbClk, +} + +impl ApbClks { + /// Create the set of [`ApbClk`]s + /// + /// # Safety + /// + /// All invariants required by `ApbToken::new` must be upheld here as well. + #[inline] + pub(super) unsafe fn new() -> Self { + ApbClks { + pac: ApbClk::new(ApbToken::new()), + pm: ApbClk::new(ApbToken::new()), + mclk: ApbClk::new(ApbToken::new()), + rst_c: ApbClk::new(ApbToken::new()), + osc_ctrl: ApbClk::new(ApbToken::new()), + osc32k_ctrl: ApbClk::new(ApbToken::new()), + sup_c: ApbClk::new(ApbToken::new()), + gclk: ApbClk::new(ApbToken::new()), + wdt: ApbClk::new(ApbToken::new()), + rtc: ApbClk::new(ApbToken::new()), + eic: ApbClk::new(ApbToken::new()), + dsu: ApbClk::new(ApbToken::new()), + nvm_ctrl: ApbClk::new(ApbToken::new()), + port: ApbClk::new(ApbToken::new()), + ram_ecc: ApbClk::new(ApbToken::new()), + #[cfg(any(feature = "same53", feature = "same54"))] + gmac: ApbClk::new(ApbToken::new()), + #[cfg(feature = "min-samd51j")] + tc4: ApbClk::new(ApbToken::new()), + qspi: ApbClk::new(ApbToken::new()), + } + } +} diff --git a/hal/src/thumbv7em/clock/v2/dfll.rs b/hal/src/thumbv7em/clock/v2/dfll.rs new file mode 100644 index 00000000000..0c23bd916a5 --- /dev/null +++ b/hal/src/thumbv7em/clock/v2/dfll.rs @@ -0,0 +1,1183 @@ +//! # Digital Frequency Locked Loop +//! +//! The `dfll` module provides access to the 48 MHz digital frequency locked +//! loop (DFLL) within the `OSCCTRL` peripheral. +//! +//! ## Operation modes +//! +//! The DFLL can operate in both open-loop and closed-loop modes. In open-loop +//! mode, it uses an internal oscillator to produce an unreferenced, 48 MHz +//! output clock. While in closed-loop mode, the DFLL multiplies a low-frequency +//! input clock to yield a 48 MHz output clock. The reference clock can be +//! provided by a GCLK, through the DFLL peripheral channel clock, or it can be +//! provided by the USB start-of-frame signal. +//! +//! The DFLL is represented by the type [`Dfll`], where `M` is one of three +//! [`Mode`] types. The default type is [`OpenLoop`], while the other two types, +//! [`FromPclk`] and [`FromUsb`], represent closed-loop `Mode`s with the +//! corresponding [`Reference`] clock. +//! +//! ## The DFLL at power-on reset +//! +//! Because the DFLL can produce a 48 MHz clock from an internal oscillator, it +//! is used as the system's default master clock at power-on reset. While most +//! clocks are disabled at reset and represented by items in the [`Tokens`] +//! struct, the [`Dfll`] is [`Enabled`] at reset, so it is found in the +//! [`Clocks`] struct. +//! +//! At reset, the [`EnabledDfll`] is in [`OpenLoop`] [`Mode`] and has one +//! consumer clock, so its complete type is `EnabledDfll`. The corresponding +//! consumer is [`Gclk0`], which is represented as `EnabledGclk0`. +//! Here, the [`EnabledGclk0`] has its own consumer as well, which is the +//! system's master clock. +//! +//! ## Example +//! +//! Configuring the [`Dfll`] proceeds according to the principles outlined in +//! the [`clock` module documentation]. Suppose we start with the default clock +//! tree after power-on reset. +//! +//! ```text +//! DFLL (48 MHz; open-loop mode) +//! └── GCLK0 (48 MHz) +//! └── Master clock (48 MHz) +//! ``` +//! +//! We would like to transform it to a clock tree like this: +//! +//! ```text +//! XOSC0 (24 MHz; external clock) +//! └── GCLK0 (24 MHz) +//! ├── Master clock (24 MHz) +//! └── DFLL (48 MHz; closed-loop mode) +//! ``` +//! +//! Let's start by using [`clock_system_at_reset`] to access the HAL clocking +//! structs. We'll also need access to the GPIO [`Pins`]. +//! +//! ```no_run +//! use atsamd_hal::{ +//! clock::v2::{clock_system_at_reset, dfll::Dfll, xosc::Xosc}, +//! gpio::Pins, +//! pac::Peripherals, +//! time::U32Ext, +//! }; +//! let mut pac = Peripherals::take().unwrap(); +//! let pins = Pins::new(pac.PORT); +//! let (buses, clocks, tokens) = clock_system_at_reset( +//! pac.OSCCTRL, +//! pac.OSC32KCTRL, +//! pac.GCLK, +//! pac.MCLK, +//! &mut pac.NVMCTRL, +//! ); +//! ``` +//! +//! Next, we create a 24 MHz [`Xosc`] clock from one of the [`Pins`] and enable +//! it. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{clock_system_at_reset, dfll::Dfll, xosc::Xosc}, +//! # gpio::Pins, +//! # pac::Peripherals, +//! # time::U32Ext, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let pins = Pins::new(pac.PORT); +//! # let (buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! let xosc0 = Xosc::from_clock(tokens.xosc0, pins.pa14, 24.mhz()).enable(); +//! ``` +//! +//! We can then swap [`Gclk0`] from the [`EnabledDfll`] to the [`EnabledXosc`]. +//! This releases the [`EnabledDfll`] and [`Decrement`]s its consumer count, +//! which allows us to disable it and retrieve the underlying [`DfllToken`]. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{clock_system_at_reset, dfll::Dfll, xosc::Xosc}, +//! # gpio::Pins, +//! # pac::Peripherals, +//! # time::U32Ext, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let pins = Pins::new(pac.PORT); +//! # let (buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! # let xosc0 = Xosc::from_clock(tokens.xosc0, pins.pa14, 24.mhz()).enable(); +//! let (gclk0, dfll, xosc0) = clocks.gclk0.swap_sources(clocks.dfll, xosc0); +//! let token_dfll = dfll.disable().free(); +//! ``` +//! +//! Next, we can enable the peripheral channel clock, or [`Pclk`], for the +//! [`Dfll`], sourcing it from [`Gclk0`]. With the `Pclk`, we can then recreate +//! the `Dfll` in closed-loop mode. Finally, we can adjust some of the +//! closed-loop parameters before we enable it. The returned [`EnabledDfll`] can +//! be used as a clock [`Source`] elsewhere in the clock tree. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{clock_system_at_reset, dfll::Dfll, pclk::Pclk, xosc::Xosc}, +//! # gpio::Pins, +//! # pac::Peripherals, +//! # time::U32Ext, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let pins = Pins::new(pac.PORT); +//! # let (buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! # let xosc0 = Xosc::from_clock(tokens.xosc0, pins.pa14, 24.mhz()).enable(); +//! # let (gclk0, dfll, xosc0) = clocks.gclk0.swap_sources(clocks.dfll, xosc0); +//! # let token_dfll = dfll.disable().free(); +//! let (pclk_dfll, gclk0) = Pclk::enable(tokens.pclks.dfll, gclk0); +//! let dfll = Dfll::from_pclk(token_dfll, pclk_dfll) +//! .coarse_max_step(1) +//! .fine_max_step(10) +//! .quick_lock(false) +//! .enable(); +//! ``` +//! +//! The entire example is provided below. +//! +//! ```no_run +//! use atsamd_hal::{ +//! clock::v2::{clock_system_at_reset, dfll::Dfll, pclk::Pclk, xosc::Xosc}, +//! gpio::Pins, +//! pac::Peripherals, +//! time::U32Ext, +//! }; +//! let mut pac = Peripherals::take().unwrap(); +//! let pins = Pins::new(pac.PORT); +//! let (buses, clocks, tokens) = clock_system_at_reset( +//! pac.OSCCTRL, +//! pac.OSC32KCTRL, +//! pac.GCLK, +//! pac.MCLK, +//! &mut pac.NVMCTRL, +//! ); +//! let xosc0 = Xosc::from_clock(tokens.xosc0, pins.pa14, 24.mhz()).enable(); +//! let (gclk0, dfll, xosc0) = clocks.gclk0.swap_sources(clocks.dfll, xosc0); +//! let token_dfll = dfll.disable().free(); +//! let (pclk_dfll, gclk0) = Pclk::enable(tokens.pclks.dfll, gclk0); +//! let dfll = Dfll::from_pclk(token_dfll, pclk_dfll) +//! .coarse_max_step(1) +//! .fine_max_step(10) +//! .quick_lock(false) +//! .enable(); +//! ``` +//! +//! # Reconfiguring an [`EnabledDfll`] +//! +//! In some cases, users may want to reconfigure the DFLL while it remains +//! enabled. For instance, a user may want to place the DFLL in its closed-loop, +//! USB recovery mode while in use by the system's master clock. It would +//! normally be impossible to do so with other clocks in the `clock` module, +//! because changing the clock source would break an invariant of the clock +//! tree. However, the DFLL is special, because its output frequency is always +//! 48 MHz. Moreover, by design, consumers of the DFLL aren't affected by its +//! configuration (see the discussion on [`Id` types]). +//! +//! For this reason, we define a special [`into_mode`] function on +//! [`EnabledDfll`]. It will consume the `EnabledDfll` and transform it to use +//! a different [`Mode`]. +//! +//! While the [`Dfll`] constructors (i.e. [`open_loop`], [`from_pclk`], and +//! [`from_usb`]) handle the [`Mode`] type for you, [`into_mode`] is generic +//! over the initial and final `Mode`, so it takes and returns the corresponding +//! `Mode` types directly. Furthermore, `into_mode` also accepts a closure, +//! allowing you to modify the [`Dfll`] before the new `Mode` is applied. +//! +//! Consider the following example. As above, we start with the clocks in their +//! default configuration at power-on reset. Remember that the [`Dfll`] is used +//! by the system's master clock. At this point, we would like to reconfigure it +//! to use an external 32 kHz clock on pin PA10. First, we construct a [`Gclk`] +//! from the corresponding [`gpio`] [`Pin`]. Then we enable the [`Pclk`] for the +//! DFLL and construct an instance of [`FromPclk`]. Finally, we call +//! `into_mode`, which takes an instance of [`FromPclk`] and returns an instance +//! of [`OpenLoop`]. Neither [`OpenLoop`] nor [`FromUsb`] need to store a +//! corresponding resource, so they are both effectively equivalent to the `()` +//! type. We can also change some of the DFLL control loop settings prior to the +//! [`Mode`] change using the closure argument to `into_mode`. +//! +//! ```no_run +//! use atsamd_hal::{ +//! clock::v2::{ +//! clock_system_at_reset, +//! dfll::{Dfll, FromPclk}, +//! gclk::Gclk, +//! pclk::Pclk, +//! }, +//! gpio::Pins, +//! pac::Peripherals, +//! time::U32Ext, +//! }; +//! let mut pac = Peripherals::take().unwrap(); +//! let pins = Pins::new(pac.PORT); +//! let (buses, mut clocks, tokens) = clock_system_at_reset( +//! pac.OSCCTRL, +//! pac.OSC32KCTRL, +//! pac.GCLK, +//! pac.MCLK, +//! &mut pac.NVMCTRL, +//! ); +//! let gclk4 = Gclk::from_pin(tokens.gclks.gclk4, pins.pa10, 32_768.hz()).enable(); +//! let (pclk, gclk4) = Pclk::enable(tokens.pclks.dfll, gclk4); +//! let from_pclk = FromPclk { pclk }; +//! let (dfll, open_loop) = clocks.dfll.into_mode(from_pclk, |dfll| { +//! dfll.set_coarse_max_step(1); +//! dfll.set_fine_max_step(8); +//! dfll.set_chill_cycle(false); +//! dfll.set_run_standby(true); +//! }); +//! ``` +//! +//! [`clock_system_at_reset`]: super::clock_system_at_reset +//! [`clock` module documentation]: super +//! [`Id` types]: super#id-types +//! [`Clocks`]: super::Clocks +//! [`Tokens`]: super::Tokens +//! [`gpio`]: crate::gpio +//! [`Pin`]: crate::gpio::Pin +//! [`Pins`]: crate::gpio::Pins +//! [`Xosc`]: super::xosc::Xosc +//! [`Gclk`]: super::gclk::Gclk +//! [`EnabledXosc`]: super::xosc::EnabledXosc +//! [`Gclk0`]: super::gclk::Gclk0 +//! [`EnabledGclk0`]: super::gclk::EnabledGclk0 +//! [`Decrement`]: crate::typelevel::Decrement +//! [`open_loop`]: Dfll::open_loop +//! [`from_pclk`]: Dfll::from_pclk +//! [`from_usb`]: Dfll::from_usb +//! [`into_mode`]: EnabledDfll::into_mode + +use crate::time::Hertz; +use crate::typelevel::{NoneT, Sealed}; +use typenum::U0; + +use super::gclk::GclkId; +use super::pclk::Pclk; +use super::{Enabled, Source}; + +//============================================================================== +// DfllToken +//============================================================================== + +/// Singleton token that can be exchanged for the [`Dfll`] +/// +/// As explained in the [`clock` module documentation](super), instances of +/// various `Token` types represent disabled clocks and can be exchanged for +/// actual clock types. However, unlike most other clocks in the module, the +/// [`Dfll`] is [`Enabled`] at power-on reset. Thus, users will never deal with +/// the `DfllToken` unless they first disable the [`EnabledDfll`]. +pub struct DfllToken(()); + +impl DfllToken { + /// Create a new [`DfllToken`] + /// + /// # Safety + /// + /// The `DfllToken`s is a singleton. There must never be two simulatenous + /// instances of it. See the notes on `Token` types and memory safety in the + /// root of the `clock` module for more details. + #[inline] + pub(crate) unsafe fn new() -> Self { + Self(()) + } + + #[inline] + fn oscctrl(&self) -> &crate::pac::oscctrl::RegisterBlock { + // Safety: The `DfllToken` only has access to a mutually exclusive set + // of registers for the DFLL, and we use a shared reference to the + // register block. See the notes on `Token` types and memory safety in + // the root of the `clock` module for more details. + unsafe { &*crate::pac::OSCCTRL::PTR } + } + + #[inline] + fn dfllctrla(&self) -> &crate::pac::oscctrl::DFLLCTRLA { + &self.oscctrl().dfllctrla + } + + #[inline] + fn dfllctrlb(&self) -> &crate::pac::oscctrl::DFLLCTRLB { + &self.oscctrl().dfllctrlb + } + + #[inline] + fn dfllmul(&self) -> &crate::pac::oscctrl::DFLLMUL { + &self.oscctrl().dfllmul + } + + #[inline] + fn dfllsync(&self) -> &crate::pac::oscctrl::DFLLSYNC { + &self.oscctrl().dfllsync + } + + #[inline] + fn wait_sync_enable(&self) { + while self.dfllsync().read().enable().bit() {} + } + + #[inline] + fn wait_sync_dfllmul(&self) { + while self.dfllsync().read().dfllmul().bit() {} + } + + #[inline] + fn wait_sync_dfllctrlb(&self) { + while self.dfllsync().read().dfllctrlb().bit() {} + } + + #[inline] + fn configure(&mut self, settings: settings::All) { + self.dfllctrlb().modify(|_, w| { + w.mode().bit(settings.closed_loop); + w.usbcrm().bit(settings.usb_recovery); + w.ccdis().bit(!settings.chill_cycle); + w.qldis().bit(!settings.quick_lock) + }); + self.wait_sync_dfllctrlb(); + if settings.closed_loop { + self.dfllmul().modify(|_, w| + // Safety: All bit patterns are valid for these fields + unsafe { + w.mul().bits(settings.mult_factor); + w.cstep().bits(settings.coarse_max_step); + w.fstep().bits(settings.fine_max_step) + }); + self.wait_sync_dfllmul(); + } + self.dfllctrla().modify(|_, w| { + w.runstdby().bit(settings.run_standby); + w.ondemand().bit(settings.on_demand) + }); + } + + #[inline] + fn enable(&mut self) { + self.dfllctrla().modify(|_, w| w.enable().set_bit()); + self.wait_sync_enable(); + } + + #[inline] + fn disable(&mut self) { + self.dfllctrla().modify(|_, w| w.enable().clear_bit()); + self.wait_sync_enable(); + } +} + +//============================================================================== +// Aliases +//============================================================================== + +type MultFactor = u16; +type CoarseMaxStep = u8; +type FineMaxStep = u8; + +//============================================================================== +// DfllId +//============================================================================== + +/// [`Id` type](super#id-types) representing the identity of the DFLL clock +pub enum DfllId {} + +impl Sealed for DfllId {} + +//============================================================================== +// Mode types +//============================================================================== + +pub struct OpenLoop; + +pub struct FromUsb; + +pub struct FromPclk { + pub pclk: Pclk, +} + +//============================================================================== +// DynReference +//============================================================================== + +/// Value-level enum identifying one of two possible reference clocks for the +/// [`Dfll`] +/// +/// When the [`Dfll`] is in closed-loop mode, it requires a reference clock +/// input. The variants of this enum represent the two possible reference +/// clocks. +/// +/// `DynReference` is the value-level equivalent of [`Reference`]. +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum DynReference { + /// The DFLL is driven by a [`Pclk`] + Pclk, + /// The DFLL is driven by the USB start-of-frame signal + Usb, +} + +//============================================================================== +// Reference +//============================================================================== + +/// Type-level enum identifying one of two possible [`Dfll`] reference clocks +/// +/// When the [`Dfll`] is in closed-loop mode, it requires a reference clock +/// input. The types implementing this trait, i.e. [`FromPclk`] and [`FromUsb`], +/// are type-level variants of `Reference`, and they identify one of the two +/// possible reference clocks. +/// +/// `Reference` is the type-level equivalent of [`DynReference`]. See the +/// documentation on [type-level programming] and specifically +/// [type-level enums] for more details. +/// +/// [type-level programming]: crate::typelevel +/// [type-level enums]: crate::typelevel#type-level-enums +pub trait Reference { + /// Corresponding variant of [`DynReference`] + const DYN: DynReference; + #[doc(hidden)] + type Settings: Settings; + #[doc(hidden)] + fn from_settings(reference: Self::Settings) -> Self; + #[doc(hidden)] + fn into_settings(self) -> Self::Settings; +} + +impl Reference for FromUsb { + const DYN: DynReference = DynReference::Usb; + type Settings = settings::Usb; + fn from_settings(_: Self::Settings) -> Self { + FromUsb + } + fn into_settings(self) -> Self::Settings { + settings::Usb + } +} + +impl Reference for FromPclk { + const DYN: DynReference = DynReference::Pclk; + type Settings = settings::Pclk; + fn from_settings(reference: Self::Settings) -> Self { + Self { + pclk: reference.pclk, + } + } + fn into_settings(self) -> Self::Settings { + settings::Pclk::new(self.pclk) + } +} + +//============================================================================== +// DynMode +//============================================================================== + +/// Value-level enum identifying the [`Dfll`] control loop mode +/// +/// The [`Dfll`] can operate in both open-loop and closed-loop modes. +/// Furthermore, when the DFLL is in closed-loop mode, it requires a +/// corresponding reference clock. +/// +/// `DynMode` is the value-level equivalent of [`Mode`]. +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum DynMode { + OpenLoop, + ClosedLoop(DynReference), +} + +//============================================================================== +// Mode +//============================================================================== + +/// Type-level enum identifying the [`Dfll`] control loop mode +/// +/// The types implementing this trait, i.e. [`OpenLoop`], [`FromPclk`] and +/// [`FromUsb`], are type-level variants of `Mode`, and they determine whether +/// the DFLL operates in closed-loop mode, and if so, which [`Reference`] clock +/// to use. +/// +/// `Mode` is the type-level equivalent of [`DynMode`]. See the documentation on +/// [type-level programming] and specifically [type-level enums] for more +/// details. +/// +/// [type-level programming]: crate::typelevel +/// [type-level enums]: crate::typelevel#type-level-enums +pub trait Mode { + /// Corresponding variant of [`DynMode`] + const DYN: DynMode; + #[doc(hidden)] + type Settings: Settings; + #[doc(hidden)] + fn from_settings(mode: Self::Settings) -> Self; + #[doc(hidden)] + fn into_settings(self) -> Self::Settings; +} + +impl Mode for OpenLoop { + const DYN: DynMode = DynMode::OpenLoop; + type Settings = settings::OpenLoop; + fn from_settings(_: Self::Settings) -> Self { + OpenLoop + } + fn into_settings(self) -> Self::Settings { + settings::OpenLoop + } +} + +impl Mode for R { + const DYN: DynMode = DynMode::ClosedLoop(R::DYN); + type Settings = settings::ClosedLoop; + fn from_settings(mode: Self::Settings) -> Self { + R::from_settings(mode.reference) + } + fn into_settings(self) -> Self::Settings { + let reference = R::into_settings(self); + settings::ClosedLoop::new(reference) + } +} + +//============================================================================== +// Settings +//============================================================================== + +mod settings { + //! Store and retrieve [`Dfll`] settings in different modes + //! + //! Many of the [`Dfll`] settings are not valid or required in every + //! operating mode. This module provides a framework to store only the + //! minimum required settings for each mode in a generic way. Specifically, + //! the [`Minimum`] struct stores the few settings relevant in all modes, + //! along with a generic, mode-specific type. The [`Settings`] trait unifies + //! all concrete instances of [`Minimum`] by providing a function to return + //! a collection of [`All`] settings. Each sub-struct within [`Minimum`] + //! implements [`Settings`] and is responsible for filling the relevent + //! fields of [`All`]. + //! + //! [`Dfll`]: super::Dfll + + use super::super::pclk; + use super::{CoarseMaxStep, DfllId, FineMaxStep, GclkId, Hertz, MultFactor}; + + /// Collection of all possible [`Dfll`] settings + /// + /// This struct is returned by the [`Settings`] trait. + /// + /// [`Dfll`]: super::Dfll + pub struct All { + pub src_freq: Hertz, + pub closed_loop: bool, + pub usb_recovery: bool, + pub mult_factor: MultFactor, + pub chill_cycle: bool, + pub quick_lock: bool, + pub coarse_max_step: CoarseMaxStep, + pub fine_max_step: FineMaxStep, + pub run_standby: bool, + pub on_demand: bool, + } + + impl Default for All { + #[inline] + fn default() -> Self { + All { + src_freq: Hertz(48_000_000), + closed_loop: false, + usb_recovery: false, + mult_factor: 1, + chill_cycle: true, + quick_lock: true, + coarse_max_step: 1, + fine_max_step: 1, + run_standby: false, + on_demand: true, + } + } + } + + /// Collection of [`Dfll`] settings containing only the minimum required + /// for the specific mode + /// + /// Many [`Dfll`] settings are not valid or required in every operating + /// mode. This struct provides a framework to store and retrieve only the + /// minimum settings for each mode in a generic way. + /// + /// Specifically, it stores flags for the `RUNSTDBY` and `ONDEMAND` fields, + /// which are relevant in every mode, and it stores a mode-specific type, + /// `T`. This can be either [`OpenLoop`] or [`ClosedLoop`], which both + /// implement the [`Settings`] trait. + /// + /// [`Dfll`]: super::Dfll + pub struct Minimum { + pub mode: T, + pub run_standby: bool, + pub on_demand: bool, + } + + impl Minimum { + pub fn new(mode: T) -> Self { + Self { + mode, + run_standby: false, + on_demand: true, + } + } + } + + /// Collection of settings specific to open-loop [`Dfll`] operation + /// + /// Right now, this struct is empty, as none of the settings are relevant to + /// open-loop operation. + /// + /// [`Dfll`]: super::Dfll + pub struct OpenLoop; + + /// Collection of settings specific to closed-loop [`Dfll`] operation + /// + /// This struct stores the maximum step size for the coarse and fine + /// adjustments in closed-loop mode. It also stores an additional type, `T`, + /// containing settings specific to the reference clock, which can be either + /// [`Pclk`] or [`Usb`]. Both implement the [`Settings`] trait. + /// + /// [`Dfll`]: super::Dfll + pub struct ClosedLoop { + pub reference: T, + pub coarse_max_step: CoarseMaxStep, + pub fine_max_step: FineMaxStep, + } + + impl ClosedLoop { + pub fn new(reference: T) -> Self { + Self { + reference, + coarse_max_step: 1, + fine_max_step: 1, + } + } + } + + /// Collection of settings specific to [`Dfll`] USB recovery mode + /// + /// Right now, this struct is empty, but its implementation of [`Settings`] + /// fills several fields of [`All`] with known, constant values for USB + /// recovery mode. + /// + /// [`Dfll`]: super::Dfll + pub struct Usb; + + /// Collection of [`Dfll`] settings when used in closed-loop mode with a + /// [`Pclk`] reference + /// + /// This struct stores the [`Pclk`] and multiplication factor, which + /// determine the precise [`Dfll`] frequency, as well as flags to control + /// the chill-cycle and quick-lock features. Note that these flags indicate + /// whether the feature is *enabled*, while the corresponding register bits + /// indicate whether the feature is *disabled*. + /// + /// [`Dfll`]: super::Dfll + /// [`Pclk`]: pclk::Pclk + pub struct Pclk { + pub pclk: pclk::Pclk, + pub mult_factor: MultFactor, + pub chill_cycle: bool, + pub quick_lock: bool, + } + + impl Pclk { + pub fn new(pclk: pclk::Pclk) -> Self { + // Cast is fine because division result cannot be greater than u16::MAX + let mult_factor = (48_000_000 / pclk.freq().0) as u16; + Self { + pclk, + mult_factor, + chill_cycle: true, + quick_lock: true, + } + } + } + + /// Generic interface to convert the [`Minimum`] settings into a collection + /// of [`All`] settings + /// + /// Because many of the [`Dfll`] settings are not valid or relevant in every + /// operating mode, we only want to store the [`Minimum`] required settings + /// for each. To do so, we must have a generic interface to retrieve + /// settings in every mode. + /// + /// This trait provides a recursive interface to yield a collection of + /// [`All`] [`Dfll`] settings. Each implementer of [`Settings`] is required + /// to fill its respective fields of [`All`] and recursively defer other + /// fields to any sub-structs. At the bottom of the stack, structs can defer + /// to the [`Default`] settings for [`All`]. + /// + /// [`Dfll`]: super::Dfll + pub trait Settings { + /// Fill the respective fields of [`All`] and recursively defer any + /// remaining fields to sub-structs or the [`Default`] settings + fn all(&self) -> All; + } + + impl Settings for Minimum { + #[inline] + fn all(&self) -> All { + All { + run_standby: self.run_standby, + on_demand: self.on_demand, + ..self.mode.all() + } + } + } + + impl Settings for OpenLoop { + #[inline] + fn all(&self) -> All { + All::default() + } + } + + impl Settings for ClosedLoop { + #[inline] + fn all(&self) -> All { + All { + closed_loop: true, + coarse_max_step: self.coarse_max_step, + fine_max_step: self.fine_max_step, + ..self.reference.all() + } + } + } + + impl Settings for Usb { + #[inline] + fn all(&self) -> All { + All { + usb_recovery: true, + src_freq: Hertz(1_000), + mult_factor: 48_000, + ..All::default() + } + } + } + + impl Settings for Pclk { + #[inline] + fn all(&self) -> All { + All { + src_freq: self.pclk.freq(), + mult_factor: self.mult_factor, + chill_cycle: self.chill_cycle, + quick_lock: self.quick_lock, + ..All::default() + } + } + } +} + +use settings::Settings; + +//============================================================================== +// Dfll +//============================================================================== + +/// Digital frequency-locked loop used to generate a 48 MHz clock +/// +/// The DFLL generates a 48 MHz clock in two different possible [`Mode`]s. In +/// [`OpenLoop`] `Mode`, it generates the output clock from an internal +/// oscillator, while in the two closed-loop `Mode`s, it multiplies a +/// low-frequency [`Reference`] clock. +/// +/// On its own, the `Dfll` type does not represent the enabled DFLL. Instead, it +/// must first be wrapped with [`Enabled`], which implements compile-time safety +/// of the clock tree. +/// +/// Because the terminal call to [`enable`] consumes the `Dfll` and returns an +/// [`EnabledDfll`], the remaining API uses the builder pattern, where each +/// method takes and returns `self` by value, allowing them to be easily +/// chained. +/// +/// See the [module-level documentation](self) for an example of creating, +/// configuring and using the `Dfll`. +/// +/// [`enable`]: Dfll::enable +pub struct Dfll { + token: DfllToken, + settings: settings::Minimum, +} + +impl Dfll { + #[inline] + fn from_mode(token: DfllToken, mode: M) -> Self { + let mode = M::into_settings(mode); + let settings = settings::Minimum::new(mode); + Self { token, settings } + } +} + +impl Dfll { + /// Create the [`Dfll`] in open-loop mode + /// + /// Creating a [`Dfll`] does not modify any of the hardware registers. It + /// only creates a struct to track the `Dfll` configuration. + /// + /// The configuration data is stored until the user calls [`enable`]. + /// At that point, all of the registers are written according to the + /// initialization procedures specified in the datasheet, and an + /// [`EnabledDfll`] is returned. The `Dfll` is not active or useful until + /// that point. + /// + /// [`enable`]: Dfll::enable + #[inline] + pub fn open_loop(token: DfllToken) -> Self { + Self::from_mode(token, OpenLoop) + } + + /// Consume the [`Dfll`] and release the [`DfllToken`] + #[inline] + pub fn free(self) -> DfllToken { + self.token + } +} + +impl Dfll { + /// Create the [`Dfll`] in USB recovery mode + /// + /// This creates the `Dfll` in closed-loop mode referenced to the USB + /// start-of-frame signal. For now, this function does not require any proof + /// of a functioning USB interface. Future versions of this function may + /// take ownership of some resource both to prove USB has been setup + /// correctly and to prevent modification while in use. + /// + /// Creating a [`Dfll`] does not modify any of the hardware registers. It + /// only creates a struct to track the `Dfll` configuration. + /// + /// The configuration data is stored until the user calls [`enable`]. + /// At that point, all of the registers are written according to the + /// initialization procedures specified in the datasheet, and an + /// [`EnabledDfll`] is returned. The `Dfll` is not active or useful until + /// that point. + /// + /// [`enable`]: Dfll::enable + #[inline] + pub fn from_usb(token: DfllToken) -> Self { + Self::from_mode(token, FromUsb) + } + + /// Consume the [`Dfll`] and release the [`DfllToken`] + #[inline] + pub fn free(self) -> DfllToken { + self.token + } +} + +impl Dfll> { + /// Create the [`Dfll`] in closed-loop mode + /// + /// This creates the `Dfll` in closed-loop mode referenced to a [`Gclk`] + /// through a [`Pclk`]. It will also auto-calculate the correct + /// multiplication factor to best yield 48 MHz at the output. + /// + /// Creating a [`Dfll`] does not modify any of the hardware registers. It + /// only creates a struct to track the `Dfll` configuration. + /// + /// The configuration data is stored until the user calls [`enable`]. + /// At that point, all of the registers are written according to the + /// initialization procedures specified in the datasheet, and an + /// [`EnabledDfll`] is returned. The `Dfll` is not active or useful until + /// that point. + /// + /// # Panics + /// + /// According to the datasheet, the [`Pclk`] frequency must be between + /// 732 Hz and 33 kHz. This function will perform a run-time check of the + /// input frequency and panic if it is out of range. To use a `Pclk` + /// frequency outside this range or to force a particular multiplication + /// factor, use [`Dfll::from_pclk_unchecked`]. + /// + /// [`Gclk`]: super::gclk::Gclk + /// [`enable`]: Dfll::enable + #[inline] + pub fn from_pclk(token: DfllToken, pclk: Pclk) -> Self { + const MIN: u32 = 48_000_000 / MultFactor::MAX as u32; + const MAX: u32 = 33_000; + let freq = pclk.freq().0; + if freq < MIN || freq > MAX { + panic!("Invalid Pclk input frequency"); + } + Self::from_mode(token, FromPclk { pclk }) + } + + /// Create the [`Dfll`] in closed-loop mode + /// + /// This constructor behaves identically to [`Dfll::from_pclk`], but it + /// skips the run-time check of the [`Pclk`] frequency and does not + /// auto-calculate the multiplication factor. + #[inline] + pub fn from_pclk_unchecked( + token: DfllToken, + pclk: Pclk, + mult_factor: MultFactor, + ) -> Self { + let mut dfll = Self::from_mode(token, FromPclk { pclk }); + dfll.settings.mode.reference.mult_factor = mult_factor; + dfll + } + + /// Consume the [`Dfll`], release the [`DfllToken`], and return the [`Pclk`] + #[inline] + pub fn free(self) -> (DfllToken, Pclk) { + (self.token, self.settings.mode.reference.pclk) + } + + /// Enable or disable the [`Dfll`] chill cycle + /// + /// See the documentation of [`chill_cycle`] for more details. + /// + /// [`chill_cycle`]: Dfll::chill_cycle + #[inline] + pub fn set_chill_cycle(&mut self, value: bool) { + self.settings.mode.reference.chill_cycle = value; + } + + /// Enable or disable the [`Dfll`] chill cycle + /// + /// When operating in closed-loop mode with small multiplication factors, + /// the DFLL can sometimes have trouble locking. To avoid this, the hardware + /// normally implements a chill cycle, during which the output frequency is + /// not measured. The chill cycle is enabled by default, but it can be + /// disabled to reduce the duration before lock. See the datasheet for more + /// details. + #[inline] + pub fn chill_cycle(mut self, value: bool) -> Self { + self.set_chill_cycle(value); + self + } + + /// Enable or disable the [`Dfll`] quick lock + /// + /// See the documentation of [`quick_lock`] for more details. + /// + /// [`quick_lock`]: Dfll::quick_lock + #[inline] + pub fn set_quick_lock(&mut self, value: bool) { + self.settings.mode.reference.quick_lock = value; + } + + /// Enable or disable the [`Dfll`] quick lock + /// + /// By default, the DFLL locking requirements are somewhat loose. Users can + /// tighten these requirements by disabling the quick lock feature, which is + /// enabled by default. See the datasheet for more details. + #[inline] + pub fn quick_lock(mut self, value: bool) -> Self { + self.set_quick_lock(value); + self + } +} + +impl Dfll { + /// Set the maximum coarse step size during closed-loop frequency tuning + /// + /// See the documentation of [`coarse_max_step`] for more details. + /// + /// [`coarse_max_step`]: Dfll::coarse_max_step + #[inline] + pub fn set_coarse_max_step(&mut self, coarse_max_step: CoarseMaxStep) { + self.settings.mode.coarse_max_step = coarse_max_step; + } + + /// Set the maximum coarse step size during closed-loop frequency tuning + /// + /// In closed-loop operation, the DFLL output frequency is continuously + /// regulated against the reference clock by adjusting the coarse and fine + /// tuning parameters. This function sets a maximum step size for the coarse + /// tuning parameter. + /// + /// In general, a small step size will ensure low overshoot in the output + /// frequency, but it will lengthen the time to lock. A larger step size + /// will produce more overshoot but will be quicker to lock. See the + /// datasheet for more details. + #[inline] + pub fn coarse_max_step(mut self, coarse_max_step: CoarseMaxStep) -> Self { + self.set_coarse_max_step(coarse_max_step); + self + } + + /// Set the maximum fine step size during closed-loop frequency tuning + /// + /// See the documentation of [`fine_max_step`] for more details. + /// + /// [`fine_max_step`]: Dfll::fine_max_step + #[inline] + pub fn set_fine_max_step(&mut self, fine_max_step: FineMaxStep) { + self.settings.mode.fine_max_step = fine_max_step; + } + + /// Set the maximum fine step size during closed-loop frequency tuning + /// + /// In closed-loop operation, the DFLL output frequency is continuously + /// regulated against the reference clock by adjusting the coarse and fine + /// tuning parameters. This function sets a maximum step size for the fine + /// tuning parameter. + /// + /// In general, a small step size will ensure low overshoot in the output + /// frequency, but it will lengthen the time to lock. A larger step size + /// will produce more overshoot but will be quicker to lock. See the + /// datasheet for more details. + #[inline] + pub fn fine_max_step(mut self, fine_max_step: FineMaxStep) -> Self { + self.set_fine_max_step(fine_max_step); + self + } +} + +impl Dfll { + /// Return the [`Dfll`] output frequency + /// + /// The output frequency will always be close to, if not exactly, 48 MHz. + #[inline] + pub fn freq(&self) -> Hertz { + // Valid for all modes based on default values + let settings = self.settings.all(); + Hertz(settings.src_freq.0 * settings.mult_factor as u32) + } + + /// Control the [`Dfll`] behavior during idle or standby sleep modes + /// + /// See the documentation of [`run_standby`] for more details. + /// + /// [`run_standby`]: Dfll::run_standby + #[inline] + pub fn set_run_standby(&mut self, value: bool) { + self.settings.run_standby = value; + } + + /// Control the [`Dfll`] behavior during idle or standby sleep modes + /// + /// When `true`, the `Dfll` will run in standby sleep mode, but its behavior + /// can still be modified by the on-demand setting. See the datasheet for + /// more details. + #[inline] + pub fn run_standby(mut self, value: bool) -> Self { + self.set_run_standby(value); + self + } + + /// Control the [`Dfll`] on-demand functionality + /// + /// See the documentation of [`on_demand`] for more details. + /// + /// [`on_demand`]: Dfll::on_demand + #[inline] + pub fn set_on_demand(&mut self, value: bool) { + self.settings.on_demand = value; + } + + /// Control the [`Dfll`] on-demand functionality + /// + /// When `true`, only run the clock when requested by peripheral. If `false` + /// the clock will be always active. This setting will also modify the + /// behavior in standby sleep modes. See the datasheet for more details. + #[inline] + pub fn on_demand(mut self, value: bool) -> Self { + self.set_on_demand(value); + self + } + + /// Enable the [`Dfll`], so that it can be used as a clock [`Source`] + /// + /// As mentioned when creating a new `Dfll`, no hardware registers are + /// actually modified until this call. Rather, the desired configuration is + /// stored internally, and the `Dfll` is initialized and configured here + /// according to the datasheet. + /// + /// The returned value is an [`EnabledDfll`] that can be used as a clock + /// [`Source`] for other clocks. + #[inline] + pub fn enable(mut self) -> EnabledDfll { + self.token.configure(self.settings.all()); + self.token.enable(); + Enabled::new(self) + } +} + +//============================================================================== +// EnabledDfll +//============================================================================== + +/// An [`Enabled`] [`Dfll`] +/// +/// As described in the [`clock` module documentation](super), the [`Enabled`] +/// wrapper implements compile-time clock tree safety by tracking the number of +/// consumer clocks and restricting access to the underlying [`Dfll`] to prevent +/// modification while in use. +/// +/// As with [`Enabled`], the default value for `N` is `U0`; if left unspecified, +/// the counter is assumed to be zero. +pub type EnabledDfll = Enabled, N>; + +impl EnabledDfll { + /// Disable the [`Dfll`] + #[inline] + pub fn disable(mut self) -> Dfll { + self.0.token.disable(); + self.0 + } +} + +impl EnabledDfll +where + M: Mode, + N: Default, +{ + /// Change the [`Dfll`] [`Mode`] while it remains enabled + /// + /// Take ownership of an [`EnabledDfll`] and convert it to use a new + /// [`Mode`]. This requires an instance of the new `Mode` type and returns + /// an instance of the old `Mode` type. Users can also supply a closure to + /// alter the [`Dfll`] settings before they are applied. The closure takes + /// `&mut Dfll` as its input, so it can only modify those settings with a + /// `set_` method. + /// + /// See the [`dfll` module documentation] for more details on why and how + /// this function would be used. + /// + /// [`dfll` module documentation]: super::dfll#reconfiguring-an-enableddfll + pub fn into_mode(self, mode: T, f: F) -> (EnabledDfll, M) + where + T: Mode, + F: FnOnce(&mut Dfll), + { + let old = M::from_settings(self.0.settings.mode); + let mut dfll = Dfll::from_mode(self.0.token, mode); + f(&mut dfll); + let dfll = dfll.enable().0; + (Enabled::new(dfll), old) + } +} + +//============================================================================== +// Source +//============================================================================== + +impl Source for EnabledDfll { + type Id = DfllId; + + #[inline] + fn freq(&self) -> Hertz { + self.0.freq() + } +} diff --git a/hal/src/thumbv7em/clock/v2/dpll.rs b/hal/src/thumbv7em/clock/v2/dpll.rs new file mode 100644 index 00000000000..129324ec001 --- /dev/null +++ b/hal/src/thumbv7em/clock/v2/dpll.rs @@ -0,0 +1,1023 @@ +//! # Digital Phase-Locked Loop +//! +//! ## Overview +//! +//! The `dpll` module provides access to the two digital phase-locked loops +//! (DPLLs) within the `OSCCTRL` peripheral. +//! +//! A DPLL is used to multiply clock frequencies. It takes a lower-frequency +//! input clock and produces a higher-frequency output clock. It works by taking +//! the output clock, dividing it down to the same frequency as the input clock, +//! comparing phase between the two signals, and locking that phase difference +//! to zero. Consequently, the clock divider within the feedback loop sets the +//! frequency multiplication factor. +//! +//! The DPLLs operate over a large range of frequencies, but their operating +//! region is not infinite. Specifically, they can only accept input frequencies +//! between 32 kHz and 3.2 MHz, and they can only output frequencies in the +//! range of 96 MHz to 200 MHz. +//! +//! Creating and configuring a [`Dpll`] proceeds according to the principles +//! outlined in the [`clock` module documentation]. It is best shown with an +//! example. +//! +//! ## Example +//! +//! Suppose we start with the default clock tree after power-on reset. +//! +//! ```text +//! DFLL (48 MHz) +//! └── GCLK0 (48 MHz) +//! └── Master clock (48 MHz) +//! ``` +//! +//! We would like to transform it to a clock tree like this: +//! +//! ```text +//! DFLL (48 MHz) +//! └── GCLK1 (2 MHz) +//! └── DPLL (200 MHz) +//! └── GCLK0 (200 MHz) +//! └── Master clock (200 MHz) +//! ``` +//! +//! Let's start by using [`clock_system_at_reset`] to access the HAL clocking +//! structs. +//! +//! ```no_run +//! use atsamd_hal::{ +//! clock::v2::{ +//! clock_system_at_reset, +//! dpll::Dpll, +//! gclk::{Gclk, GclkDiv16}, +//! pclk::Pclk, +//! }, +//! pac::Peripherals, +//! }; +//! let mut pac = Peripherals::take().unwrap(); +//! let (buses, clocks, tokens) = clock_system_at_reset( +//! pac.OSCCTRL, +//! pac.OSC32KCTRL, +//! pac.GCLK, +//! pac.MCLK, +//! &mut pac.NVMCTRL, +//! ); +//! ``` +//! +//! First, we would like to divide down the 48 MHz output of the [`Dfll`] to +//! produce a valid input frequency for the [`Dpll`]. We start by feeding the +//! already-[`Enabled`] [`Dfll`] to [`Gclk1`] with a [`GclkDivider`] of 24, +//! producing a 2 MHz output frequency. This has the side effect of +//! [`Increment`]ing the counter for [`EnabledDfll`]. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # dpll::Dpll, +//! # gclk::{Gclk, GclkDiv16}, +//! # pclk::Pclk, +//! # }, +//! # pac::Peripherals, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let (buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! let (gclk1, dfll) = Gclk::from_source(tokens.gclks.gclk1, clocks.dfll); +//! let gclk1 = gclk1.div(GclkDiv16::Div(24)).enable(); +//! ``` +//! +//! Next, we use the output of [`Gclk1`] to enable the peripheral channel clock +//! ([`Pclk`]) for [`Dpll0`]. This [`Increment`]s the counter for +//! [`EnabledGclk1`]. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # dpll::Dpll, +//! # gclk::{Gclk, GclkDiv16}, +//! # pclk::Pclk, +//! # }, +//! # pac::Peripherals, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let (buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! # let (gclk1, dfll) = Gclk::from_source(tokens.gclks.gclk1, clocks.dfll); +//! # let gclk1 = gclk1.div(GclkDiv16::Div(24)).enable(); +//! let (pclk_dpll0, gclk1) = Pclk::enable(tokens.pclks.dpll0, gclk1); +//! ``` +//! +//! Now we use [`Dpll::from_pclk`], which consumes the [`Pclk`] and returns an +//! instance of [`Dpll0`]. We use builder API functions to set the loop divider +//! to 100 and enable the [`Dpll`]. This will multiply the 2 MHz input clock to +//! produce a 200 MHz output clock. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # dpll::Dpll, +//! # gclk::{Gclk, GclkDiv16}, +//! # pclk::Pclk, +//! # }, +//! # pac::Peripherals, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let (buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! # let (gclk1, dfll) = Gclk::from_source(tokens.gclks.gclk1, clocks.dfll); +//! # let gclk1 = gclk1.div(GclkDiv16::Div(24)).enable(); +//! # let (pclk_dpll0, gclk1) = Pclk::enable(tokens.pclks.dpll0, gclk1); +//! let dpll0 = Dpll::from_pclk(tokens.dpll0, pclk_dpll0) +//! .loop_div(100, 0) +//! .enable(); +//! ``` +//! +//! There are two things to note at this point. +//! +//! First, the loop divider has both an integer part and a fractional part. +//! However, users should generally avoid using fractional division, if +//! possible, because it increases the jitter of the output clock. See the +//! [`Dpll::loop_div`] documentation for more details. +//! +//! Second, because the input clock frequency and loop division factors are +//! run-time values, the [`Dpll`] cannot verify at compile time that the input +//! and output frequencies satisfy the requirements specified in the +//! [overview](self#overview). Instead, these values are checked at run-time. If +//! either frequency violates its requirement, the call to [`Dpll::enable`] will +//! panic. +//! +//! Finally, we wait until the [`EnabledDpll0`] output is ready, and then we +//! swap the [`EnabledGclk0`], which feeds the processor master clock, from the +//! 48 MHz [`EnabledDfll`] to the 200 MHz [`EnabledDpll0`]. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # dpll::Dpll, +//! # gclk::{Gclk, GclkDiv16}, +//! # pclk::Pclk, +//! # }, +//! # pac::Peripherals, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let (buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! # let (gclk1, dfll) = Gclk::from_source(tokens.gclks.gclk1, clocks.dfll); +//! # let gclk1 = gclk1.div(GclkDiv16::Div(24)).enable(); +//! # let (pclk_dpll0, gclk1) = Pclk::enable(tokens.pclks.dpll0, gclk1); +//! # let dpll0 = Dpll::from_pclk(tokens.dpll0, pclk_dpll0) +//! # .loop_div(100, 0) +//! # .enable(); +//! while !dpll0.is_ready() {} +//! let (gclk0, dfll, dpll0) = clocks.gclk0.swap_sources(dfll, dpll0); +//! ``` +//! +//! We have now achieved the disired clock tree. The complete example is +//! provided below. +//! +//! ```no_run +//! use atsamd_hal::{ +//! clock::v2::{ +//! clock_system_at_reset, +//! dpll::Dpll, +//! gclk::{Gclk, GclkDiv16}, +//! pclk::Pclk, +//! }, +//! pac::Peripherals, +//! }; +//! let mut pac = Peripherals::take().unwrap(); +//! let (buses, clocks, tokens) = clock_system_at_reset( +//! pac.OSCCTRL, +//! pac.OSC32KCTRL, +//! pac.GCLK, +//! pac.MCLK, +//! &mut pac.NVMCTRL, +//! ); +//! let (gclk1, dfll) = Gclk::from_source(tokens.gclks.gclk1, clocks.dfll); +//! let gclk1 = gclk1.div(GclkDiv16::Div(24)).enable(); +//! let (pclk_dpll0, gclk1) = Pclk::enable(tokens.pclks.dpll0, gclk1); +//! let dpll0 = Dpll::from_pclk(tokens.dpll0, pclk_dpll0) +//! .loop_div(100, 0) +//! .enable(); +//! while !dpll0.is_ready() {} +//! let (gclk0, dfll, dpll0) = clocks.gclk0.swap_sources(dfll, dpll0); +//! ``` +//! +//! [`clock` module documentation]: super +//! [`clock_system_at_reset`]: super::clock_system_at_reset +//! [`Dfll`]: super::dfll::Dfll +//! [`EnabledDfll`]: super::dfll::EnabledDfll +//! [`EnabledGclk0`]: super::gclk::EnabledGclk0 +//! [`Gclk1`]: super::gclk::Gclk1 +//! [`EnabledGclk1`]: super::gclk::EnabledGclk1 +//! [`GclkDivider`]: super::gclk::GclkDivider +//! [`Pclk`]: super::pclk::Pclk + +use core::marker::PhantomData; + +use typenum::U0; + +use crate::pac::oscctrl::dpll::{dpllstatus, dpllsyncbusy, DPLLCTRLA, DPLLCTRLB, DPLLRATIO}; +use crate::pac::oscctrl::DPLL; + +use crate::pac::oscctrl::dpll::dpllctrlb::REFCLK_A; + +use crate::time::Hertz; +use crate::typelevel::{Decrement, Increment, Sealed}; + +use super::gclk::GclkId; +use super::pclk::{Pclk, PclkId}; +use super::xosc::{Xosc0Id, Xosc1Id, XoscId}; +use super::xosc32k::Xosc32kId; +use super::{Enabled, Source}; + +//============================================================================== +// DpllToken +//============================================================================== + +/// Singleton token that can be exchanged for a [`Dpll`] +/// +/// As explained in the [`clock` module documentation](super), instances of +/// various `Token` types can be exchanged for actual clock types. They +/// typically represent clocks that are disabled at power-on reset. +/// +/// [`DpllToken`]s are no different. Both [`Dpll`]s are disabled at power-on +/// reset. To use a [`Dpll`], you must first exchange the token for an actual +/// clock with [`Dpll::from_pclk`], [`Dpll::from_xosc`] or +/// [`Dpll::from_xosc32k`]. +/// +/// [`DpllToken`] is generic over the [`DpllId`], where each corresponding token +/// represents one of the two respective [`Dpll`]s. +pub struct DpllToken { + dpll: PhantomData, +} + +impl DpllToken { + /// Create a new instance of [`DpllToken`] + /// + /// # Safety + /// + /// Each `DpllToken`s is a singleton. There must never be two simulatenous + /// instances with the same [`DpllId`]. See the notes on `Token` types and + /// memory safety in the root of the `clock` module for more details. + #[inline] + pub(super) unsafe fn new() -> Self { + Self { dpll: PhantomData } + } + + /// Access the corresponding PAC `DPLL` struct + #[inline] + fn dpll(&self) -> &DPLL { + // Safety: Each `DpllToken` only has access to a mutually exclusive set + // of registers for the corresponding `DpllId`, and we use a shared + // reference to the register block. See the notes on `Token` types and + // memory safety in the root of the `clock` module for more details. + unsafe { &(*crate::pac::OSCCTRL::PTR).dpll[D::NUM] } + } + + /// Access the corresponding DPLLCTRLA register + #[inline] + fn ctrla(&self) -> &DPLLCTRLA { + &self.dpll().dpllctrla + } + + /// Access the corresponding DPLLCTRLB register + #[inline] + fn ctrlb(&self) -> &DPLLCTRLB { + &self.dpll().dpllctrlb + } + + /// Access the corresponding DPLLRATIO register + #[inline] + fn ratio(&self) -> &DPLLRATIO { + &self.dpll().dpllratio + } + + /// Access the corresponding DPLLSYNCBUSY register for reading only + #[inline] + fn syncbusy(&self) -> dpllsyncbusy::R { + self.dpll().dpllsyncbusy.read() + } + + /// Access the corresponding DPLLSTATUS register for reading only + #[inline] + fn status(&self) -> dpllstatus::R { + self.dpll().dpllstatus.read() + } + + #[inline] + fn configure(&mut self, id: DynDpllSourceId, settings: Settings, prediv: u16) { + // Convert the actual predivider to the `div` register field value + let div = match id { + DynDpllSourceId::Xosc0 | DynDpllSourceId::Xosc1 => prediv / 2 - 1, + _ => 0, + }; + self.ctrlb().modify(|_, w| { + // Safety: The value is masked to the correct bit width by the PAC. + // An invalid value could produce an invalid clock frequency, but + // that does not break memory safety. + unsafe { w.div().bits(div) }; + w.refclk().variant(id.into()); + w.lbypass().bit(settings.lock_bypass); + w.wuf().bit(settings.wake_up_fast) + }); + // Safety: The values are masked to the correct bit width by the PAC. + // Invalid values here could produce invalid clock frequencies, but that + // does not break memory safety. + self.ratio().write(|w| unsafe { + w.ldr().bits(settings.mult - 1); + w.ldrfrac().bits(settings.frac) + }); + while self.syncbusy().dpllratio().bit_is_set() {} + self.ctrla().modify(|_, w| { + w.ondemand().bit(settings.on_demand); + w.runstdby().bit(settings.run_standby) + }); + } + + /// Enable the [`Dpll`] + #[inline] + fn enable(&mut self) { + self.ctrla().modify(|_, w| w.enable().set_bit()); + while self.syncbusy().enable().bit_is_set() {} + } + + /// Disable the [`Dpll`] + #[inline] + fn disable(&mut self) { + self.ctrla().modify(|_, w| w.enable().clear_bit()); + while self.syncbusy().enable().bit_is_set() {} + } + + /// Check the STATUS register to see if the clock is locked + #[inline] + fn is_locked(&self) -> bool { + self.status().lock().bit() + } + + /// Check the STATUS register to see if the clock is ready + #[inline] + fn is_ready(&self) -> bool { + self.status().clkrdy().bit() + } +} + +//============================================================================== +// DynDpllId +//============================================================================== + +/// Value-level enum identifying one of two possible [`Dpll`]s +/// +/// The variants of this enum identify one of the two possible digital +/// phase-locked loops. +/// +/// `DynDpllId` is the value-level equivalent of [`DpllId`]. +pub enum DynDpllId { + Dpll0, + Dpll1, +} + +//============================================================================== +// DpllId +//============================================================================== + +/// Type-level enum identifying one of two possible [`Dpll`]s +/// +/// The types implementing this trait, i.e. [`Dpll0Id`] and [`Dpll1Id`], are +/// type-level variants of `DpllId`, and they identify one of the two possible +/// digital phase-locked loops. +/// +/// `DpllId` is the type-level equivalent of [`DynDpllId`]. See the +/// documentation on [type-level programming] and specifically +/// [type-level enums] for more details. +/// +/// [type-level programming]: crate::typelevel +/// [type-level enums]: crate::typelevel#type-level-enums +pub trait DpllId: Sealed + PclkId { + /// Corresponding variant of [`DynDpllId`] + const DYN: DynDpllId; + /// Corresponding numeric index + const NUM: usize; +} + +/// Type-level variant of [`DpllId`] representing the identity of DPLL0 +/// +/// See the documentation on [type-level programming] and specifically +/// [type-level enums] for more details. +/// +/// [type-level programming]: crate::typelevel +/// [type-level enums]: crate::typelevel#type-level-enums +pub enum Dpll0Id {} + +impl Sealed for Dpll0Id {} + +impl DpllId for Dpll0Id { + const DYN: DynDpllId = DynDpllId::Dpll0; + const NUM: usize = 0; +} + +/// Type-level variant of [`DpllId`] representing the identity of DPLL1 +/// +/// See the documentation on [type-level programming] and specifically +/// [type-level enums] for more details. +/// +/// [type-level programming]: crate::typelevel +/// [type-level enums]: crate::typelevel#type-level-enums +pub enum Dpll1Id {} + +impl Sealed for Dpll1Id {} + +impl DpllId for Dpll1Id { + const DYN: DynDpllId = DynDpllId::Dpll1; + const NUM: usize = 1; +} + +//============================================================================== +// DynDpllSourceId +//============================================================================== + +/// Value-level enum of possible clock sources for a [`Dpll`] +/// +/// The variants of this enum identify one of four possible clock sources for +/// a given [`Dpll`]. +/// +/// `DynDpllSourceId` is the value-level equivalent of [`DpllSourceId`]. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub enum DynDpllSourceId { + /// The DPLL is driven by a [`Pclk`] + Pclk, + /// The DPLL is driven by [`Xosc0`](super::xosc::Xosc0) + Xosc0, + /// The DPLL is driven by [`Xosc1`](super::xosc::Xosc1) + Xosc1, + /// The DPLL is driven by [`Xosc32k`](super::xosc32k::Xosc32k) + Xosc32k, +} + +impl From for REFCLK_A { + fn from(source: DynDpllSourceId) -> Self { + match source { + DynDpllSourceId::Pclk => REFCLK_A::GCLK, + DynDpllSourceId::Xosc0 => REFCLK_A::XOSC0, + DynDpllSourceId::Xosc1 => REFCLK_A::XOSC1, + DynDpllSourceId::Xosc32k => REFCLK_A::XOSC32, + } + } +} + +//============================================================================== +// DpllSourceId +//============================================================================== + +/// Type-level enum of possible clock [`Source`]s for a [`Dpll`] +/// +/// The types implementing this trait are type-level variants of `DpllSourceId`, +/// and they identify one of four possible clock [`Source`]s for a given +/// [`Dpll`]. All implementers of this trait are `Id` types, which are described +/// in more detail in the [`clock` module documentation](super). +/// +/// `DpllSourceId` is the type-level equivalent of [`DynDpllSourceId`]. See the +/// documentation on [type-level programming] and specifically +/// [type-level enums] for more details. +/// +/// [type-level programming]: crate::typelevel +/// [type-level enums]: crate::typelevel#type-level-enums +pub trait DpllSourceId { + /// Corresponding variant of [`DynDpllSourceId`] + const DYN: DynDpllSourceId; + + /// Reference-specific settings type + #[doc(hidden)] + type Reference: settings::Reference; +} + +impl DpllSourceId for G { + const DYN: DynDpllSourceId = DynDpllSourceId::Pclk; + type Reference = settings::Pclk; +} +impl DpllSourceId for Xosc0Id { + const DYN: DynDpllSourceId = DynDpllSourceId::Xosc0; + type Reference = settings::Xosc; +} +impl DpllSourceId for Xosc1Id { + const DYN: DynDpllSourceId = DynDpllSourceId::Xosc1; + type Reference = settings::Xosc; +} +impl DpllSourceId for Xosc32kId { + const DYN: DynDpllSourceId = DynDpllSourceId::Xosc32k; + type Reference = settings::Xosc32k; +} + +//============================================================================== +// Settings +//============================================================================== + +/// [`Dpll`] settings relevant to all reference clocks +#[derive(Copy, Clone)] +struct Settings { + mult: u16, + frac: u8, + lock_bypass: bool, + wake_up_fast: bool, + on_demand: bool, + run_standby: bool, +} + +/// Store and retrieve [`Dpll`] settings for different reference clocks +mod settings { + use super::super::pclk; + use super::{DpllId, GclkId, Hertz}; + + /// [`Dpll`] settings when referenced to a [`Pclk`] + /// + /// [`Dpll`]: super::Dpll + /// [`Pclk`]: pclk::Pclk + pub struct Pclk { + pub pclk: pclk::Pclk, + } + + /// [`Dpll`] settings when referenced to an [`Xosc`] + /// + /// [`Dpll`]: super::Dpll + /// [`Xosc`]: super::super::xosc::Xosc + pub struct Xosc { + pub freq: Hertz, + pub prediv: u16, + } + + /// [`Dpll`] settings when referenced to an [`Xosc32k`] + /// + /// [`Dpll`]: super::Dpll + /// [`Xosc32k`]: super::super::xosc32k::Xosc32k + pub struct Xosc32k; + + /// Generic interface for the frequency and predivider of a reference clock + pub trait Reference { + fn freq(&self) -> Hertz; + fn prediv(&self) -> u16; + } + + impl Reference for Pclk { + #[inline] + fn freq(&self) -> Hertz { + self.pclk.freq() + } + #[inline] + fn prediv(&self) -> u16 { + 1 + } + } + + impl Reference for Xosc { + #[inline] + fn freq(&self) -> Hertz { + self.freq + } + #[inline] + fn prediv(&self) -> u16 { + self.prediv + } + } + + impl Reference for Xosc32k { + #[inline] + fn freq(&self) -> Hertz { + Hertz(32_768) + } + #[inline] + fn prediv(&self) -> u16 { + 1 + } + } +} + +//============================================================================== +// Dpll +//============================================================================== + +/// Digital phase-locked loop used to multiply clock frequencies +/// +/// A DPLL is used to multiply clock frequencies, taking a lower-frequency input +/// clock and producing a higher-frequency output clock. +/// +/// The type parameter `D` is a [`DpllId`] that determines which of the two +/// instances this `Dpll` represents ([`Dpll0`] or [`Dpll1`]). The type +/// parameter `I` represents the `Id` type for the clock [`Source`] driving this +/// `Dpll`. It must be one of the valid [`DpllSourceId`]s. See the +/// [`clock` module documentation](super) for more detail on +/// [`Id` types](super#id-types). +/// +/// On its own, an instance of `Dpll` does not represent an enabled DPLL. +/// Instead, it must first be wrapped with [`Enabled`], which implements +/// compile-time safety of the clock tree. +/// +/// Because the terminal call to [`enable`] consumes the `Dpll` and returns an +/// [`EnabledDpll`], the remaining API uses the builder pattern, where each +/// method takes and returns `self` by value, allowing them to be easily +/// chained. +/// +/// See the [module-level documentation](self) for an example of creating, +/// configuring and using a `Dpll`. +/// +/// [`enable`]: Dpll::enable +pub struct Dpll +where + D: DpllId, + I: DpllSourceId, +{ + token: DpllToken, + reference: I::Reference, + settings: Settings, +} + +/// Type alias for the corresponding [`Dpll`] +pub type Dpll0 = Dpll; + +/// Type alias for the corresponding [`Dpll`] +pub type Dpll1 = Dpll; + +impl Dpll +where + D: DpllId, + I: DpllSourceId, +{ + fn new(token: DpllToken, reference: I::Reference) -> Self { + let settings = Settings { + mult: 1, + frac: 0, + lock_bypass: false, + wake_up_fast: false, + on_demand: true, + run_standby: false, + }; + Self { + token, + reference, + settings, + } + } +} + +impl Dpll +where + D: DpllId, + G: GclkId, +{ + /// Create a [`Dpll`] from a [`Pclk`] + /// + /// Creating a [`Dpll`] does not modify any of the hardware registers. It + /// only creates a struct to track the DPLL configuration. + /// + /// The configuration data is stored until the user calls [`enable`]. At + /// that point, all of the registers are written according to the + /// initialization procedures specified in the datasheet, and an + /// [`EnabledDpll`] is returned. The `Dpll` is not active or useful until + /// that point. + /// + /// [`enable`]: Dpll::enable + #[inline] + pub fn from_pclk(token: DpllToken, pclk: Pclk) -> Self { + let reference = settings::Pclk { pclk }; + Dpll::new(token, reference) + } + + /// Consume the [`Dpll`], release the [`DpllToken`], and return the [`Pclk`] + #[inline] + pub fn free_pclk(self) -> (DpllToken, Pclk) { + (self.token, self.reference.pclk) + } +} + +impl Dpll +where + D: DpllId, + X: XoscId + DpllSourceId = settings::Xosc>, +{ + /// Create a [`Dpll`] from an [`Xosc`] + /// + /// Note that, when the [`Dpll`] is driven by an [`Xosc`], there is an extra + /// clock divider between the `Xosc` output and the input to the actual + /// phase-locked loop. This allows the [`Xosc`] frequency to be above the + /// maximum DPLL input frequency of 3.2 MHz. + /// + /// The `Xosc` pre-divider can be set to any *even* value in the range + /// `[2, 4096]`. It defaults to the minimum value of 2, but it can be + /// changed with the [`Dpll::prediv`] method. + /// + /// Creating a [`Dpll`] does not modify any of the hardware registers. It + /// only creates a struct to track the DPLL configuration and [`Increment`]s + /// the [`Source`] [`Enabled`] counter. + /// + /// The configuration data is stored until the user calls [`enable`]. At + /// that point, all of the registers are written according to the + /// initialization procedures specified in the datasheet, and an + /// [`EnabledDpll`] is returned. The `Dpll` is not active or useful until + /// that point. + /// + /// [`Xosc`]: super::xosc::Xosc + /// [`enable`]: Dpll::enable + #[inline] + pub fn from_xosc(token: DpllToken, source: S) -> (Self, S::Inc) + where + S: Source + Increment, + { + let reference = settings::Xosc { + freq: source.freq(), + prediv: 2, + }; + let dpll = Dpll::new(token, reference); + (dpll, source.inc()) + } + + /// Consume the [`Dpll`], release the [`DpllToken`], and [`Decrement`] the + /// [`EnabledXosc`] consumer count + /// + /// [`EnabledXosc`]: super::xosc::EnabledXosc + #[inline] + pub fn free_xosc(self, source: S) -> (DpllToken, S::Dec) + where + S: Source + Decrement, + { + (self.token, source.dec()) + } + + /// Set the [`Xosc`] pre-division factor + /// + /// The [`Xosc`] output frequency is divided down before it enters the + /// actual phase-locked loop. This function will panic if the pre-division + /// factor is not an *even* number in the range `[2, 4096]`. + /// + /// [`Xosc`]: super::xosc::Xosc + #[inline] + pub fn prediv(mut self, prediv: u16) -> Self { + if prediv % 2 != 0 || prediv < 2 || prediv > 4096 { + panic!("DPLL prediv must be an even integer in the range [2, 4096]") + } + self.reference.prediv = prediv; + self + } +} + +impl Dpll { + /// Create a [`Dpll`] from an [`Xosc32k`] + /// + /// Creating a [`Dpll`] does not modify any of the hardware registers. It + /// only creates a struct to track the DPLL configuration and [`Increment`]s + /// the [`Source`] [`Enabled`] counter. + /// + /// The configuration data is stored until the user calls [`enable`]. At + /// that point, all of the registers are written according to the + /// initialization procedures specified in the datasheet, and an + /// [`EnabledDpll`] is returned. The `Dpll` is not active or useful until + /// that point. + /// + /// [`Xosc32k`]: super::xosc32k::Xosc32k + /// [`enable`]: Dpll::enable + #[inline] + pub fn from_xosc32k(token: DpllToken, source: S) -> (Self, S::Inc) + where + S: Source + Increment, + { + let dpll = Dpll::new(token, settings::Xosc32k); + (dpll, source.inc()) + } + + /// Consume the [`Dpll`], release the [`DpllToken`], and [`Decrement`] the + /// [`EnabledXosc32k`] consumer count + /// + /// [`EnabledXosc32k`]: super::xosc32k::EnabledXosc32k + /// d`] consumer count + pub fn free_xosc32k(self, source: S) -> (DpllToken, S::Dec) + where + S: Source + Decrement, + { + (self.token, source.dec()) + } +} + +impl Dpll +where + D: DpllId, + I: DpllSourceId, +{ + /// Set the [`Dpll`] loop divider, which is also the frequency + /// multiplication factor + /// + /// The inputs to this function are the natural integer and fractional + /// parts of the division factor, i.e. the division factor is: + /// + /// ```text + /// int + frac / 32 + /// ``` + /// + /// This function will confirm that the `int` and `frac` values convert to + /// valid `LDR` and `LDRFRAC` register fields, panicking otherwise. + #[inline] + pub fn loop_div(mut self, int: u16, frac: u8) -> Self { + if int < 1 || int > 0x2000 { + panic!("Invalid integer part of the DPLL loop divider") + } + if frac > 31 { + panic!("Invalid fractional part of the DPLL loop divider") + } + self.settings.mult = int; + self.settings.frac = frac; + self + } + + /// Bypass the [`Dpll`] lock + /// + /// If `true`, the [`Dpll`] will output its clock regardless of whether it + /// is locked. + #[inline] + pub fn lock_bypass(mut self, bypass: bool) -> Self { + self.settings.lock_bypass = bypass; + self + } + + /// Output the [`Dpll`] clock immediately, without waiting for various + /// conditions + /// + /// See the datasheet for complete details. + #[inline] + pub fn wake_up_fast(mut self, wuf: bool) -> Self { + self.settings.wake_up_fast = wuf; + self + } + + /// Set on-demand mode + /// + /// See the datasheet for complete details. + #[inline] + pub fn on_demand(mut self, on_demand: bool) -> Self { + self.settings.on_demand = on_demand; + self + } + + /// Set run-in-standby mode + /// + /// See the datasheet for complete details. + #[inline] + pub fn run_standby(mut self, run_standby: bool) -> Self { + self.settings.run_standby = run_standby; + self + } + + #[inline] + fn input_freq(&self) -> u32 { + use settings::Reference; + self.reference.freq().0 / self.reference.prediv() as u32 + } + + #[inline] + fn output_freq(&self) -> u32 { + self.input_freq() * (self.settings.mult as u32 + self.settings.frac as u32 / 32) + } + + /// Return the output frequency of the [`Dpll`] + #[inline] + pub fn freq(&self) -> Hertz { + Hertz(self.output_freq()) + } + + /// Enable the [`Dpll`], so that it can be used as a clock [`Source`] + /// + /// As mentioned when creating a new `Dpll`, no hardware registers are + /// actually modified until this call. Rather, the desired configuration is + /// stored internally, and the [`Dpll`] is initialized and configured here + /// according to the datasheet. + /// + /// The returned value is an [`EnabledDpll`] that can be used as a clock + /// [`Source`] for other clocks. + /// + /// # Panics + /// + /// This function will also check that the input and output clock + /// frequencies fall within the valid ranges specified in the datasheet. + /// Specifically, the input frequency must be between 32 kHz and 3.2 MHz, + /// while the output frequency must be between 96 MHz and 200 MHz. If either + /// frequency is invalid, this call will panic. + #[inline] + pub fn enable(self) -> EnabledDpll { + let input_freq = self.input_freq(); + let output_freq = self.output_freq(); + if input_freq < 32_000 || input_freq > 3_200_000 { + panic!("Invalid DPLL input frequency"); + } + if output_freq < 96_000_000 || output_freq > 200_000_000 { + panic!("Invalid DPLL output frequency"); + } + self.enable_unchecked() + } + + /// Enable the [`Dpll`] without validating the input & output frequencies + /// + /// This is equivalent to calling [`Dpll::enable`] but without the checks on + /// input and output frequencies. Using frequencies outside the ranges + /// specified in the datasheet may not work and could cause clocking + /// problems. + #[inline] + pub fn enable_unchecked(mut self) -> EnabledDpll { + use settings::Reference; + let prediv = self.reference.prediv(); + self.token.configure(I::DYN, self.settings, prediv); + self.token.enable(); + Enabled::new(self) + } +} + +//============================================================================== +// EnabledDpll +//============================================================================== + +/// An [`Enabled`] [`Dpll`] +/// +/// As described in the [`clock` module documentation](super), the [`Enabled`] +/// wrapper implements compile-time clock tree safety by tracking the number of +/// consumer clocks and restricting access to the underlying [`Dpll`] to prevent +/// modification while in use. +/// +/// As with [`Enabled`], the default value for `N` is `U0`; if left unspecified, +/// the counter is assumed to be zero. +pub type EnabledDpll = Enabled, N>; + +/// Type alias for the corresponding [`EnabledDpll`] +pub type EnabledDpll0 = EnabledDpll; + +/// Type alias for the corresponding [`EnabledDpll`] +pub type EnabledDpll1 = EnabledDpll; + +impl EnabledDpll +where + D: DpllId, + I: DpllSourceId, +{ + /// Disable the [`Dpll`] + /// + /// This method is only implemented for `N = U0`, which means the clock can + /// only be disabled when no other clocks consume this [`Dpll`]. + #[inline] + pub fn disable(mut self) -> Dpll { + self.0.token.disable(); + self.0 + } +} + +impl EnabledDpll +where + D: DpllId, + I: DpllSourceId, +{ + /// Test whether the [`Dpll`] is locked + #[inline] + pub fn is_locked(&self) -> bool { + self.0.token.is_locked() + } + + /// Test whether the [`Dpll`] is ready + #[inline] + pub fn is_ready(&self) -> bool { + self.0.token.is_ready() + } +} + +//============================================================================== +// Source +//============================================================================== + +impl Source for EnabledDpll +where + D: DpllId, + I: DpllSourceId, +{ + type Id = D; + + #[inline] + fn freq(&self) -> Hertz { + self.0.freq() + } +} diff --git a/hal/src/thumbv7em/clock/v2/gclk.rs b/hal/src/thumbv7em/clock/v2/gclk.rs new file mode 100644 index 00000000000..07bbdb3a4e5 --- /dev/null +++ b/hal/src/thumbv7em/clock/v2/gclk.rs @@ -0,0 +1,1499 @@ +//! # Generic Clock Controllers +//! +//! ## Overview +//! +//! The generic clock controller is central to the clocking system in ATSAMD +//! chips. It provides 12 generic clock generators to modify and distribute +//! clocks to other peripherals. Within the clock tree, these clock generators +//! act as the branch clocks, connecting internal or external root or branch +//! clocks to other branch or leaf clocks. +//! +//! Each clock generator takes an input clock, optionally divides it, and +//! produces an output clock. The input clock can be: +//! +//! - A GPIO input ([`Pin`]) +//! - An external crystal oscillator ([`Xosc`]) +//! - An external 32 kHz oscillator ([`Xosc32k`]) +//! - The ultra-low power 32 kHz oscillator ([`OscUlp32k`]) +//! - The 48 MHz DFLL ([`Dfll`]) +//! - A DPLL ([`Dpll`]) +//! - Generic clock generator #1 ([`Gclk1`]) +//! +//! The output clock can be: +//! - A peripheral channel clock ([`Pclk`]) +//! - A GPIO pin ([`GclkOut`]) +//! +//! ## Example +//! +//! The configuration of a [`Gclk`] is best shown with an example. However, the +//! example assumes you are already familiar with the basics of the `clock` +//! module. See the [`clock` module documentation](super) for an overview. +//! +//! Suppose we start with the default clock tree after power-on reset. +//! +//! ```text +//! DFLL (48 MHz) +//! └── GCLK0 (48 MHz) +//! └── Main clock (48 MHz) +//! ``` +//! +//! We would like to transform it to a clock tree like this: +//! +//! ```text +//! DFLL (48 MHz) +//! └── GCLK0 (48 MHz) +//! └── Main clock (48 MHz) +//! +//! GCLK_IN1 (PB14, 24 MHz) +//! └── GCLK1 (12 MHz) +//! ├── SERCOM0 +//! └── GCLK2 (3 MHz) +//! ├── SERCOM1 +//! └── GCLK_OUT2 (PA16, 3 MHz) +//! ``` +//! +//! Let's start by using [`clock_system_at_reset`] to access the HAL clocking +//! structs. We will also need access to the [`gpio`] [`Pins`]. +//! +//! ```no_run +//! use atsamd_hal::{ +//! clock::v2::{ +//! clock_system_at_reset, +//! gclk::{Gclk, GclkDiv8, GclkDiv16}, +//! pclk::Pclk, +//! }, +//! gpio::Pins, +//! pac::Peripherals, +//! time::U32Ext, +//! }; +//! let mut pac = Peripherals::take().unwrap(); +//! let (mut buses, clocks, tokens) = clock_system_at_reset( +//! pac.OSCCTRL, +//! pac.OSC32KCTRL, +//! pac.GCLK, +//! pac.MCLK, +//! &mut pac.NVMCTRL, +//! ); +//! let pins = Pins::new(pac.PORT); +//! ``` +//! +//! Next, we use [`Gclk::from_pin`] to create a [`Gclk`] from a [`GclkToken`], +//! GPIO [`Pin`] and frequency, in [`Hertz`]. In this case, we create an +//! instance of [`Gclk1`]. +//! +//! At this point, notice that [`Gclk`] takes two type parameters. `G` is +//! a [`GclkId`] identifying which of the 12 generators this `Gclk` represents. +//! [`Gclk1`] is simply an alias for `Gclk`. `I` is an +//! [`Id` type](super#id-types) identifying the input clock, which must be a +//! valid [`GclkSourceId`]. In this case, `I` is [`PB14`](gpio::PB14), which is +//! a `GclkSourceId` for `Gclk1`, because it implements [`GclkIo`] with +//! [`GclkIo::GclkId`]` = Gclk1Id`. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # gclk::{Gclk, GclkDiv8, GclkDiv16}, +//! # pclk::Pclk, +//! # }, +//! # gpio::Pins, +//! # pac::Peripherals, +//! # time::U32Ext, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let (mut buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! # let pins = Pins::new(pac.PORT); +//! let gclk1 = Gclk::from_pin(tokens.gclks.gclk1, pins.pb15, 24.mhz()); +//! ``` +//! +//! While we have created a [`Gclk`], we have not yet enabled it. But before +//! doing so, we would like to set the divider to reduce the input frequency of +//! 24 MHz to a 12 MHz output. We call `Gclk::div`, which uses a builder API, so +//! that it can be chained with the call to `Gclk::enable`. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # gclk::{Gclk, GclkDiv8, GclkDiv16}, +//! # pclk::Pclk, +//! # }, +//! # gpio::Pins, +//! # pac::Peripherals, +//! # time::U32Ext, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let (mut buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! # let pins = Pins::new(pac.PORT); +//! # let gclk1 = Gclk::from_pin(tokens.gclks.gclk1, pins.pb15, 24.mhz()); +//! let gclk1 = gclk1.div(GclkDiv16::Div(2)).enable(); +//! ``` +//! +//! Note that the divider value supplied to `Gclk::div` must be wrapped by the +//! [`GclkDiv16`] enum. This is for a few different reasons. First, [`Gclk1`] +//! accepts a wider range of divider values than the other [`Gclk`]s, which use +//! [`GclkDiv8`] instead. Second, the actual divider value is controlled by two +//! register fields, and the set of valid values is best expressed as a Rust +//! enum. The `GclkDiv8` and `GclkDiv16` enums are connected by the +//! [`GclkDivider`] trait. +//! +//! Once [`Gclk1`] is enabled, we can use it to enable the [`Pclk`] for +//! [`Sercom0`]. This follows the usual pattern. We provide a [`PclkToken`] and +//! the [`EnabledGclk1`]. In return, we get an enabled [`Pclk`] and the +//! [`EnabledGclk1`] counter is [`Increment`]ed. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # gclk::{Gclk, GclkDiv8, GclkDiv16}, +//! # pclk::Pclk, +//! # }, +//! # gpio::Pins, +//! # pac::Peripherals, +//! # time::U32Ext, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let (mut buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! # let pins = Pins::new(pac.PORT); +//! # let gclk1 = Gclk::from_pin(tokens.gclks.gclk1, pins.pb15, 24.mhz()); +//! # let gclk1 = gclk1.div(GclkDiv16::Div(2)).enable(); +//! let (pclk_sercom0, gclk1) = Pclk::enable(tokens.pclks.sercom0, gclk1); +//! ``` +//! +//! Next, we use [`Gclk1`] as a clock [`Source`] to create an instance of +//! [`Gclk2`] with [`Gclk::from_source`]. However, keep in mind that this is +//! only true for [`Gclk1`]. No other [`Gclk`] can act as a [`Source`] for +//! another [`Gclk`]. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # gclk::{Gclk, GclkDiv8, GclkDiv16}, +//! # pclk::Pclk, +//! # }, +//! # gpio::Pins, +//! # pac::Peripherals, +//! # time::U32Ext, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let (mut buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! # let pins = Pins::new(pac.PORT); +//! # let gclk1 = Gclk::from_pin(tokens.gclks.gclk1, pins.pb15, 24.mhz()); +//! # let gclk1 = gclk1.div(GclkDiv16::Div(2)).enable(); +//! # let (pclk_sercom0, gclk1) = Pclk::enable(tokens.pclks.sercom0, gclk1); +//! let (gclk2, gclk1) = Gclk::from_source(tokens.gclks.gclk2, gclk1); +//! ``` +//! +//! The pattern repeats now. We divide [`Gclk1`] by 4 to produce the [`Gclk2`] +//! output. Then we enable it to produce an [`EnabledGclk2`] and use it to yield +//! another [`Pclk`]. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # gclk::{Gclk, GclkDiv8, GclkDiv16}, +//! # pclk::Pclk, +//! # }, +//! # gpio::Pins, +//! # pac::Peripherals, +//! # time::U32Ext, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let (mut buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! # let pins = Pins::new(pac.PORT); +//! # let gclk1 = Gclk::from_pin(tokens.gclks.gclk1, pins.pb15, 24.mhz()); +//! # let gclk1 = gclk1.div(GclkDiv16::Div(2)).enable(); +//! # let (pclk_sercom0, gclk1) = Pclk::enable(tokens.pclks.sercom0, gclk1); +//! # let (gclk2, gclk1) = Gclk::from_source(tokens.gclks.gclk2, gclk1); +//! let gclk2 = gclk2.div(GclkDiv8::Div(4)).enable(); +//! let (pclk_sercom1, gclk2) = Pclk::enable(tokens.pclks.sercom1, gclk2); +//! ``` +//! +//! Finally, we output [`Gclk2`] directly to a GPIO pin. We supply the GPIO +//! [`Pin`] to the [`EnabledGclk2`] to yield a [`GclkOut`]. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # gclk::{Gclk, GclkDiv8, GclkDiv16}, +//! # pclk::Pclk, +//! # }, +//! # gpio::Pins, +//! # pac::Peripherals, +//! # time::U32Ext, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let (mut buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! # let pins = Pins::new(pac.PORT); +//! # let gclk1 = Gclk::from_pin(tokens.gclks.gclk1, pins.pb15, 24.mhz()); +//! # let gclk1 = gclk1.div(GclkDiv16::Div(2)).enable(); +//! # let (pclk_sercom0, gclk1) = Pclk::enable(tokens.pclks.sercom0, gclk1); +//! # let (gclk2, gclk1) = Gclk::from_source(tokens.gclks.gclk2, gclk1); +//! # let gclk2 = gclk2.div(GclkDiv8::Div(4)).enable(); +//! # let (pclk_sercom1, gclk2) = Pclk::enable(tokens.pclks.sercom1, gclk2); +//! let (gclk2, gclk2_out) = gclk2.enable_gclk_out(pins.pa16); +//! ``` +//! +//! The full example is provided below. +//! +//! ```no_run +//! use atsamd_hal::{ +//! clock::v2::{ +//! clock_system_at_reset, +//! gclk::{Gclk, GclkDiv8, GclkDiv16}, +//! pclk::Pclk, +//! }, +//! gpio::Pins, +//! pac::Peripherals, +//! time::U32Ext, +//! }; +//! let mut pac = Peripherals::take().unwrap(); +//! let (mut buses, clocks, tokens) = clock_system_at_reset( +//! pac.OSCCTRL, +//! pac.OSC32KCTRL, +//! pac.GCLK, +//! pac.MCLK, +//! &mut pac.NVMCTRL, +//! ); +//! let pins = Pins::new(pac.PORT); +//! let gclk1 = Gclk::from_pin(tokens.gclks.gclk1, pins.pb15, 24.mhz()); +//! let gclk1 = gclk1.div(GclkDiv16::Div(2)).enable(); +//! let (pclk_sercom0, gclk1) = Pclk::enable(tokens.pclks.sercom0, gclk1); +//! let (gclk2, gclk1) = Gclk::from_source(tokens.gclks.gclk2, gclk1); +//! let gclk2 = gclk2.div(GclkDiv8::Div(4)).enable(); +//! let (pclk_sercom1, gclk2) = Pclk::enable(tokens.pclks.sercom1, gclk2); +//! let (gclk2, gclk2_out) = gclk2.enable_gclk_out(pins.pa16); +//! ``` +//! +//! ## `Gclk0` +//! +//! [`Gclk0`] is significant and special relative to the other [`Gclk`]s. It is +//! the clock generator for the processor's main clock, so it can never be +//! disabled. Consequently, it has a special API not available to the other +//! `Gclk`s. While normal `Gclk`s can only change their clock [`Source`] or +//! divider while disabled, `Gclk0` can never be disabled, so we provide this +//! functionality on [`EnabledGclk0`] instead. +//! +//! We model the main clock's consumption of `Gclk0` by setting its [`Enabled`] +//! counter to [`U1`] in [`clock_system_at_reset`]. This prevents users from +//! ever disabling `EnabledGclk0`, because there is no way to [`Decrement`] its +//! `Counter` to [`U0`]. +//! +//! Additionally, we provide functions to change the clock `Source`, divider, +//! etc. on `EnabledGclk0`, but we restrict them to the case where `N = U1`. +//! This prevents users from changing its `Source` or divider if any *other, +//! additional* clock consumes it (besides the main clock). +//! +//! [`clock_system_at_reset`]: super::clock_system_at_reset +//! [`Xosc`]: super::xosc::Xosc +//! [`Xosc32k`]: super::xosc32k::Xosc32k +//! [`OscUlp32k`]: super::osculp32k::OscUlp32k +//! [`Dfll`]: super::dfll::Dfll +//! [`Dpll`]: super::dpll::Dpll +//! [`PclkToken`]: super::pclk::PclkToken +//! [`Pclk`]: super::pclk::Pclk +//! [`Pins`]: crate::gpio::Pins +//! [`Sercom0`]: crate::sercom::Sercom0 + +use core::cmp::max; +use core::marker::PhantomData; + +use paste::paste; +use seq_macro::seq; +use typenum::{U0, U1}; + +use crate::pac; +use crate::pac::gclk::genctrl::DIVSEL_A; +use crate::pac::NVMCTRL; + +use crate::gpio::{self, AlternateM, AnyPin, Pin, PinId}; +use crate::pac::gclk::genctrl::SRC_A; +use crate::pac::gclk::GENCTRL; +use crate::time::Hertz; +use crate::typelevel::{Decrement, Increment, PrivateDecrement, PrivateIncrement, Sealed}; + +use super::dfll::DfllId; +use super::dpll::{Dpll0Id, Dpll1Id}; +use super::osculp32k::OscUlp32kId; +use super::xosc::{Xosc0Id, Xosc1Id}; +use super::xosc32k::Xosc32kId; +use super::{Enabled, Source}; + +//============================================================================== +// GclkToken +//============================================================================== + +/// Singleton token that can be exchanged for a [`Gclk`] +/// +/// As explained in the [`clock` module documentation](super), instances of +/// various `Token` types can be exchanged for actual clock types. They +/// typically represent clocks that are disabled at power-on reset. +/// +/// [`GclkToken`]s are no different. All [`Gclk`]s other than [`Gclk0`] are +/// disabled at power-on reset. To use a [`Gclk`], you must first exchange the +/// token for an actual clock with [`Gclk::from_source`] or [`Gclk::from_pin`]. +/// +/// [`GclkToken`] is generic over the [`GclkId`], where each corresponding token +/// represents one of the 12 respective [`Gclk`]s. +pub struct GclkToken { + gen: PhantomData, +} + +impl GclkToken { + /// Create a new instance of [`GclkToken`] + /// + /// # Safety + /// + /// Each `GclkToken`s is a singleton. There must never be two simulatenous + /// instances with the same [`GclkId`]. See the notes on `Token` types and + /// memory safety in the root of the `clock` module for more details. + #[inline] + pub(super) unsafe fn new() -> Self { + GclkToken { gen: PhantomData } + } + + /// SYNCBUSY register mask for the corresponding GCLK + const MASK: u16 = 1 << G::NUM; + + /// Provide a reference to the corresponding [`GENCTRL`] register + #[inline] + fn genctrl(&self) -> &GENCTRL { + // Safety: Each `GclkToken` only has access to a mutually exclusive set + // of registers for the corresponding `GclkId`, and we use a shared + // reference to the register block. See the notes on `Token` types and + // memory safety in the root of the `clock` module for more details. + unsafe { &(*pac::GCLK::PTR).genctrl[G::NUM] } + } + + /// Block until synchronization has completed + /// + /// Reads or writes to synchronized fields must be accompanied by a check of + /// the `SYNCBUSY` register. See the datasheet for more details. + #[inline] + fn wait_syncbusy(&self) { + // Safety: We are only reading from the `SYNCBUSY` register, and we are + // only observing the bit corresponding to this particular `GclkId`, so + // there is no risk of memory corruption. + let syncbusy = unsafe { &(*pac::GCLK::PTR).syncbusy }; + while syncbusy.read().genctrl().bits() & Self::MASK != 0 {} + } + + /// Set the clock source for this [`Gclk`] + #[inline] + fn set_source(&mut self, source: DynGclkSourceId) { + self.genctrl().modify(|_, w| w.src().variant(source.into())); + self.wait_syncbusy(); + } + + /// Set the [`GclkDivider`] value + /// + /// Use the internal interface of [`GclkDivider`] to set the `DIV` and + /// `DIVSEL` fields of the `GENCTRL` register. + #[inline] + fn set_div(&mut self, div: G::Divider) { + let (divsel, div) = div.divsel_div(); + // Safety: The `DIVSEL` and `DIV` values are derived from the + // `GclkDivider` type, so they are guaranteed to be valid. + self.genctrl().modify(|_, w| unsafe { + w.divsel().variant(divsel); + w.div().bits(div) + }); + self.wait_syncbusy(); + } + + /// Output a 50-50 duty-cycle clock when using an odd division factor + #[inline] + fn improve_duty_cycle(&mut self, flag: bool) { + self.genctrl().modify(|_, w| w.idc().bit(flag)); + self.wait_syncbusy(); + } + + /// Set the state of [`GclkOut`] pins when the GCLK_IO output is disabled + #[inline] + fn output_off_value(&mut self, high: bool) { + self.genctrl().modify(|_, w| w.oov().bit(high)); + self.wait_syncbusy(); + } + + /// Enable [`Gclk`] output to a GPIO [`Pin`] + #[inline] + fn enable_gclk_out(&mut self) { + self.genctrl().modify(|_, w| w.oe().set_bit()); + self.wait_syncbusy(); + } + + /// Disable [`Gclk`] output on a GPIO [`Pin`] + /// + /// If a corresponding [`Pin`] is in the [`AlternateM`] mode, it's logic + /// level will depend on the [`output_off_value`]. + #[inline] + fn disable_gclk_out(&mut self) { + self.genctrl().modify(|_, w| w.oe().clear_bit()); + self.wait_syncbusy(); + } + + #[inline] + fn configure(&mut self, id: DynGclkSourceId, settings: Settings) { + let (divsel, div) = settings.div.divsel_div(); + self.genctrl().modify(|_, w| { + // Safety: The `DIVSEL` and `DIV` values are derived from the + // `GclkDivider` type, so they are guaranteed to be valid. + unsafe { + w.divsel().variant(divsel); + w.div().bits(div); + }; + w.src().variant(id.into()); + w.idc().bit(settings.improve_duty_cycle); + w.oov().bit(settings.output_off_value) + }); + self.wait_syncbusy(); + } + + /// Enable the [`Gclk`] + #[inline] + fn enable(&mut self) { + self.genctrl().modify(|_, w| w.genen().set_bit()); + self.wait_syncbusy(); + } + + /// Disable the [`Gclk`] + #[inline] + fn disable(&mut self) { + self.genctrl().modify(|_, w| w.genen().clear_bit()); + self.wait_syncbusy(); + } +} + +//============================================================================== +// DynGclkId +//============================================================================== + +/// Value-level enum identifying one of 12 possible [`Gclk`]s +/// +/// The variants of this enum identify one of the 12 possible generic clock +/// generators. +/// +/// `DynGclkId` is the value-level equivalent of [`GclkId`]. +pub enum DynGclkId { + Gclk0, + Gclk1, + Gclk2, + Gclk3, + Gclk4, + Gclk5, + Gclk6, + Gclk7, + Gclk8, + Gclk9, + Gclk10, + Gclk11, +} + +//============================================================================== +// GclkId +//============================================================================== + +/// Type-level enum identifying one of 12 possible [`Gclk`]s +/// +/// The types implementing this trait, i.e. [`Gclk0Id`] - [`Gclk11Id`], are +/// type-level variants of `GclkId`, and they identify one of the 12 possible +/// generic clock generators. +/// +/// `GclkId` is the type-level equivalent of [`DynGclkId`]. See the +/// documentation on [type-level programming] and specifically +/// [type-level enums] for more details. +/// +/// [type-level programming]: crate::typelevel +/// [type-level enums]: crate::typelevel#type-level-enums +pub trait GclkId: Sealed { + /// Corresponding variant of [`DynGclkId`] + const DYN: DynGclkId; + /// Corresponding numeric index (0..12) + const NUM: usize; + /// Corresponding [`GclkDivider`] type + /// + /// [`Gclk1`] uses [`GclkDiv16`], while all other [`Gclk`]s use + /// [`GclkDiv8`]. + type Divider: GclkDivider; +} + +/// Type-level variant of [`GclkId`] representing the identity of GCLK0 +/// +/// See the documentation on [type-level programming] and specifically +/// [type-level enums] for more details. +/// +/// [type-level programming]: crate::typelevel +/// [type-level enums]: crate::typelevel#type-level-enums +pub enum Gclk0Id {} +impl Sealed for Gclk0Id {} +impl GclkId for Gclk0Id { + const DYN: DynGclkId = DynGclkId::Gclk0; + const NUM: usize = 0; + type Divider = GclkDiv8; +} + +/// Type-level variant of [`GclkId`] representing the identity of GCLK1 +/// +/// See the documentation on [type-level programming] and specifically +/// [type-level enums] for more details. +/// +/// [type-level programming]: crate::typelevel +/// [type-level enums]: crate::typelevel#type-level-enums +pub enum Gclk1Id {} +impl Sealed for Gclk1Id {} +impl GclkId for Gclk1Id { + const DYN: DynGclkId = DynGclkId::Gclk1; + const NUM: usize = 1; + type Divider = GclkDiv16; +} + +seq!(N in 2..=11 { + paste! { + /// Type-level variant of [`GclkId`] representing the identity of + #[doc = "GCLK" N] + /// + /// See the documentation on [type-level programming] and specifically + /// [type-level enums] for more details. + /// + /// [type-level programming]: crate::typelevel + /// [type-level enums]: crate::typelevel#type-level-enums + pub enum [] {} + impl Sealed for [] {} + impl GclkId for [] { + const DYN: DynGclkId = DynGclkId::Gclk~N; + const NUM: usize = N; + type Divider = GclkDiv8; + } + } +}); + +//============================================================================== +// GclkDivider +//============================================================================== + +/// Trait unifying the two [`Gclk`] divider types, [`GclkDiv8`] and +/// [`GclkDiv16`] +/// +/// Choosing a [`Gclk`] division factor can be complicated. [`Gclk1`] can accept +/// a 16-bit divider value, while all other [`Gclk`]s only take an 8-bit value. +/// Moreover, the set of valid clock dividers does not form a contiguous range. +/// For example, the valid set of dividers for most [`Gclk`]s is 1-256 and 512. +/// +/// The [`GclkDiv8`] and [`GclkDiv16`] enums provide simple and intuitive +/// user-facing interfaces to choose the *actual* clock divider value. This +/// trait, on the other hand, provides an internal-facing interface used by HAL +/// authors to extract the clock divider and convert it to the corresponding +/// `DIVSEL` and `DIV` register fields. Users should have no reason to interact +/// with this trait directly. +pub trait GclkDivider: Sealed + Default + Copy { + /// Returns the actual clock divider value as a `u32` + fn divider(&self) -> u32; + /// Return the corresponding `DIVSEL` and and `DIV` register fields + fn divsel_div(&self) -> (DIVSEL_A, u16); +} + +//============================================================================== +// GclkDiv8 +//============================================================================== + +/// Enum for the clock division factor of all [`Gclk`]s other than [`Gclk1`] +/// +/// Choosing a [`Gclk`] division factor can be complicated, because the set of +/// valid values is not contiguous. For clocks other than [`Gclk1`], the +/// division factor can be 1-256 or 512. `GclkDiv8` provides an enum interface +/// to enforce validity of the division factor. See the datasheet for more +/// details. +#[derive(Clone, Copy)] +pub enum GclkDiv8 { + /// Use a literal division factor + /// + /// All values in the range `[1-255]` are valid. Zero is also valid, but it + /// is interpreted as `1`. + Div(u8), + /// Use a division factor of `2^8 = 256` + Div2Pow8, + /// Use a division factor of `2^9 = 512` + Div2Pow9, +} + +impl Sealed for GclkDiv8 {} + +impl Default for GclkDiv8 { + #[inline] + fn default() -> Self { + Self::Div(0) + } +} + +impl GclkDivider for GclkDiv8 { + #[inline] + fn divider(&self) -> u32 { + match self { + GclkDiv8::Div(div) => (*div).into(), + GclkDiv8::Div2Pow8 => 256, + GclkDiv8::Div2Pow9 => 512, + } + } + + #[inline] + fn divsel_div(&self) -> (DIVSEL_A, u16) { + match self { + GclkDiv8::Div(div) => (DIVSEL_A::DIV1, (*div).into()), + GclkDiv8::Div2Pow8 => (DIVSEL_A::DIV2, 7), + GclkDiv8::Div2Pow9 => (DIVSEL_A::DIV2, 8), + } + } +} + +//============================================================================== +// GclkDiv16 +//============================================================================== + +/// Enum for the clock division factor of [`Gclk1`] only +/// +/// Choosing the [`Gclk1`] division factor can be complicated, because the set +/// of valid values is not contiguous. For [`Gclk1`], the division factor can be +/// 1-65536 or 131072. `GclkDiv16` provides an enum interface to enforce +/// validity of the division factor. See the datasheet for more details. +#[derive(Clone, Copy)] +pub enum GclkDiv16 { + /// Use a literal division factor + /// + /// All values in the range `[1-65535]` are valid. Zero is also valid, but + /// it is interpreted as `1`. + Div(u16), + /// Use a division factor of `2^16 = 65536` + Div2Pow16, + /// Use a division factor of `2^17 = 131072` + Div2Pow17, +} + +impl Sealed for GclkDiv16 {} + +impl Default for GclkDiv16 { + #[inline] + fn default() -> Self { + Self::Div(0) + } +} + +impl GclkDivider for GclkDiv16 { + #[inline] + fn divider(&self) -> u32 { + match self { + GclkDiv16::Div(div) => (*div).into(), + GclkDiv16::Div2Pow16 => 65536, + GclkDiv16::Div2Pow17 => 131072, + } + } + + #[inline] + fn divsel_div(&self) -> (DIVSEL_A, u16) { + match self { + GclkDiv16::Div(div) => (DIVSEL_A::DIV1, *div), + GclkDiv16::Div2Pow16 => (DIVSEL_A::DIV2, 15), + GclkDiv16::Div2Pow17 => (DIVSEL_A::DIV2, 16), + } + } +} + +//============================================================================== +// GclkIo +//============================================================================== + +/// Trait mapping each [`PinId`] to its corresponding [`GclkId`] when used as a +/// [`Gclk`] input or output +/// +/// If a given [`PinId`] can be used as a [`Gclk`] input or output, it can only +/// be used with one specific [`GclkId`]. This trait provides a mapping from +/// such a `PinId` to its corresponding `GclkId`. +pub trait GclkIo: PinId { + /// Corresponding [`GclkId`] for this [`PinId`] + type GclkId: GclkId; +} + +// These implementations are much easier to read with `#[rustfmt::skip]` +#[rustfmt::skip] +mod gclkio_impl { + + use super::*; + + impl GclkIo for gpio::PA10 { type GclkId = Gclk4Id; } + impl GclkIo for gpio::PA11 { type GclkId = Gclk5Id; } + impl GclkIo for gpio::PA14 { type GclkId = Gclk0Id; } + impl GclkIo for gpio::PA15 { type GclkId = Gclk1Id; } + impl GclkIo for gpio::PA16 { type GclkId = Gclk2Id; } + impl GclkIo for gpio::PA17 { type GclkId = Gclk3Id; } + impl GclkIo for gpio::PA27 { type GclkId = Gclk1Id; } + impl GclkIo for gpio::PA30 { type GclkId = Gclk0Id; } + impl GclkIo for gpio::PB10 { type GclkId = Gclk4Id; } + impl GclkIo for gpio::PB11 { type GclkId = Gclk5Id; } + #[cfg(feature = "min-samd51j")] + impl GclkIo for gpio::PB12 { type GclkId = Gclk6Id; } + #[cfg(feature = "min-samd51j")] + impl GclkIo for gpio::PB13 { type GclkId = Gclk7Id; } + #[cfg(feature = "min-samd51j")] + impl GclkIo for gpio::PB14 { type GclkId = Gclk0Id; } + #[cfg(feature = "min-samd51j")] + impl GclkIo for gpio::PB15 { type GclkId = Gclk1Id; } + #[cfg(feature = "min-samd51j")] + impl GclkIo for gpio::PB16 { type GclkId = Gclk2Id; } + #[cfg(feature = "min-samd51j")] + impl GclkIo for gpio::PB17 { type GclkId = Gclk3Id; } + #[cfg(feature = "min-samd51n")] + impl GclkIo for gpio::PB18 { type GclkId = Gclk4Id; } + #[cfg(feature = "min-samd51n")] + impl GclkIo for gpio::PB19 { type GclkId = Gclk5Id; } + #[cfg(feature = "min-samd51n")] + impl GclkIo for gpio::PB20 { type GclkId = Gclk6Id; } + #[cfg(feature = "min-samd51n")] + impl GclkIo for gpio::PB21 { type GclkId = Gclk7Id; } + impl GclkIo for gpio::PB22 { type GclkId = Gclk0Id; } + impl GclkIo for gpio::PB23 { type GclkId = Gclk1Id; } +} + +//============================================================================== +// Gclk0Io +//============================================================================== + +/// Set of [`PinId`]s whose implementations of [`GclkIo`] map to [`Gclk0Id`] +/// +/// This is effectively a trait alias for [`PinId`]s that implement [`GclkIo`] +/// with a `GclkId` associated type of [`Gclk0Id`], i.e. +/// `GclkIo`. The trait is useful to simply some function +/// signatures and to help type inference in a few cases. +pub trait Gclk0Io +where + Self: Sized, + Self: GclkIo, + Self: GclkSourceId>, +{ +} + +impl> Gclk0Io for I {} + +//============================================================================== +// DynGclkSourceId +//============================================================================== + +/// Value-level enum of possible clock sources for a [`Gclk`] +/// +/// The variants of this enum identify one of nine possible clock sources for +/// a given [`Gclk`]. +/// +/// `DynGclkSourceId` is the value-level equivalent of [`GclkSourceId`]. +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum DynGclkSourceId { + Dfll, + Dpll0, + Dpll1, + Gclk1, + GclkIn, + OscUlp32k, + Xosc0, + Xosc1, + Xosc32k, +} + +impl From for SRC_A { + fn from(source: DynGclkSourceId) -> Self { + use DynGclkSourceId::*; + use SRC_A::*; + match source { + Dfll => DFLL, + Dpll0 => DPLL0, + Dpll1 => DPLL1, + Gclk1 => GCLKGEN1, + GclkIn => GCLKIN, + OscUlp32k => OSCULP32K, + Xosc0 => XOSC0, + Xosc1 => XOSC1, + Xosc32k => XOSC32K, + } + } +} + +//============================================================================== +// GclkSourceId +//============================================================================== + +/// Type-level enum of possible clock [`Source`]s for a [`Gclk`] +/// +/// The types implementing this trait are type-level variants of `GclkSourceId`, +/// and they identify one of nine possible clock [`Source`]s for a given +/// [`Gclk`]. All implementers of this trait are `Id` types, which are described +/// in more detail in the [`clock` module documentation](super). +/// +/// `GclkSourceId` is the type-level equivalent of [`DynGclkSourceId`]. See the +/// documentation on [type-level programming] and specifically +/// [type-level enums] for more details. +/// +/// [type-level programming]: crate::typelevel +/// [type-level enums]: crate::typelevel#type-level-enums +pub trait GclkSourceId { + /// Corresponding variant of [`DynGclkSourceId`] + const DYN: DynGclkSourceId; + + /// Resource to store in the [`Gclk`] + /// + /// Maps to the corresponding [`Pin`] for [`GclkIo`] types. In all other + /// cases, there is nothing to store, so it is `()`. + #[doc(hidden)] + type Resource; +} + +impl GclkSourceId for DfllId { + const DYN: DynGclkSourceId = DynGclkSourceId::Dfll; + type Resource = (); +} +impl GclkSourceId for Dpll0Id { + const DYN: DynGclkSourceId = DynGclkSourceId::Dpll0; + type Resource = (); +} +impl GclkSourceId for Dpll1Id { + const DYN: DynGclkSourceId = DynGclkSourceId::Dpll1; + type Resource = (); +} +impl GclkSourceId for Gclk1Id { + const DYN: DynGclkSourceId = DynGclkSourceId::Gclk1; + type Resource = (); +} +impl GclkSourceId for I { + const DYN: DynGclkSourceId = DynGclkSourceId::GclkIn; + type Resource = Pin; +} +impl GclkSourceId for OscUlp32kId { + const DYN: DynGclkSourceId = DynGclkSourceId::OscUlp32k; + type Resource = (); +} +impl GclkSourceId for Xosc0Id { + const DYN: DynGclkSourceId = DynGclkSourceId::Xosc0; + type Resource = (); +} +impl GclkSourceId for Xosc1Id { + const DYN: DynGclkSourceId = DynGclkSourceId::Xosc1; + type Resource = (); +} +impl GclkSourceId for Xosc32kId { + const DYN: DynGclkSourceId = DynGclkSourceId::Xosc32k; + type Resource = (); +} + +//============================================================================== +// NotGclkIo +//============================================================================== + +/// Type-level enum of [`GclkSourceId`] types that are not a [`GclkIo`] +/// +/// The datasheet notes that a [`Gclk`] can use a GPIO [`Pin`] as either input +/// or output, but not both. Stated differently, you cannot create a [`GclkOut`] +/// from a `Gclk` where the [`GclkSourceId`] is a [`PinId`]. +/// +/// This trait acts as a [type-level enum] narrowing [`GclkSourceId`] to exclude +/// any types which implement [`GclkIo`]. +/// +/// [type-level enum]: crate::typelevel#type-level-enums +pub trait NotGclkIo: GclkSourceId {} + +impl> NotGclkIo for I {} + +//============================================================================== +// Settings +//============================================================================== + +/// Collection of [`Gclk`] settings to configure on enable +struct Settings { + div: G::Divider, + output_off_value: bool, + improve_duty_cycle: bool, +} + +impl Clone for Settings { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for Settings {} + +impl Default for Settings { + fn default() -> Self { + Settings { + div: G::Divider::default(), + output_off_value: false, + improve_duty_cycle: false, + } + } +} + +//============================================================================== +// Gclk +//============================================================================== + +/// Generic clock generator used to distribute clocks to various peripherals +/// +/// A generic clock generator acts as a branch in the clock tree. It can connect +/// a root or branch clock to other branch or leaf clocks. In particular, all +/// peripheral [`Pclk`]s must be derived from a `Gclk`. +/// +/// The type parameter `G` is a [`GclkId`] that determines which of the 12 +/// generators this [`Gclk`] represents ([`Gclk0`] - [`Gclk11`]). The type +/// parameter `I` represents the `Id` type for the clock [`Source`] driving this +/// `Gclk`. It must be one of the valid [`GclkSourceId`]s. Alternatively, if the +/// `Gclk` is driven by a [GPIO](gpio) [`Pin`], then `I` is a [`PinId`] +/// implementing [`GclkIo`]. See the [`clock` module documentation](super) for +/// more detail on `Id` types. +/// +/// On its own, an instance of `Gclk` does not represent an enabled clock +/// generator. Instead, it must first be wrapped with [`Enabled`], which +/// implements compile-time safety of the clock tree. +/// +/// Because the terminal call to [`enable`] consumes the `Gclk` and returns an +/// [`EnabledGclk`], the remaining API uses the builder pattern, where each +/// method takes and returns `self` by value, allowing them to be easily +/// chained. +/// +/// See the [module-level documentation](self) for an example of creating, +/// configuring and using a `Gclk`. +/// +/// [`Pclk`]: super::pclk::Pclk +/// [`enable`]: Gclk::enable +pub struct Gclk +where + G: GclkId, + I: GclkSourceId, +{ + token: GclkToken, + resource: I::Resource, + src_freq: Hertz, + settings: Settings, +} + +/// An [`Enabled`] [`Gclk`] +/// +/// As described in the [`clock` module documentation](super), the [`Enabled`] +/// wrapper implements compile-time clock tree safety by tracking the number of +/// clocks consuming this [`Gclk`] and restricts access to the underlying +/// [`Gclk`] to prevent misuse. +/// +/// As with [`Enabled`], the default value for `N` is `U0`; if left unspecified, +/// the counter is assumed to be zero. +pub type EnabledGclk = Enabled, N>; + +/// Type alias for the corresponding [`Gclk`] +/// +/// As mentioned in the [module-level documentation](self), `Gclk0` is special, +/// because it provides the processor main clock. We represent this by +/// permanently [`Increment`]ing the counter for [`EnabledGclk0`], which +/// prevents it from ever being disabled. Accordingly, we also provide a few +/// special methods on [`EnabledGclk0`] to configure the `Gclk` while it is +/// actively running. +pub type Gclk0 = Gclk; + +/// Type alias for the corresponding [`EnabledGclk`] +/// +/// As mentioned in the [module-level documentation](self), `Gclk0` is special, +/// because it provides the processor main clock. We represent this by +/// permanently [`Increment`]ing the counter for [`EnabledGclk0`], which +/// prevents it from ever being disabled. Thus, the default value for `N` is +/// [`U1`] instead of [`U0`]. Accordingly, we also provide a few special methods +/// on [`EnabledGclk0`] to configure the `Gclk` while it is actively running. +pub type EnabledGclk0 = EnabledGclk; + +seq!(G in 1..=11 { + paste! { + /// Type alias for the corresponding [`Gclk`] + pub type Gclk~G = Gclk<[], I>; + + /// Type alias for the corresponding [`EnabledGclk`] + pub type EnabledGclk~G = EnabledGclk<[], I, N>; + } +}); + +impl Gclk +where + G: GclkId, + I: GclkIo, +{ + /// Create a new [`Gclk`] from a GPIO [`Pin`] + /// + /// Creating a [`Gclk`] does not modify any of the hardware registers. It + /// only serves to consume the [`Pin`] and create a struct to track the GCLK + /// configuration. + /// + /// The configuration data is stored until the user calls [`enable`]. At + /// that point, all of the registers are written according to the + /// initialization procedures specified in the datasheet, and an + /// [`EnabledGclk`] is returned. The `Gclk` is not active or useful until + /// that point. + /// + /// [`enable`]: Gclk::enable + pub fn from_pin

(token: GclkToken, pin: P, freq: impl Into) -> Self + where + P: AnyPin, + { + Gclk { + token, + resource: pin.into().into_mode(), + src_freq: freq.into(), + settings: Settings::default(), + } + } + + /// Consume the [`Gclk`] and free its corresponding resources + /// + /// Freeing a [`Gclk`] returns the corresponding [`GclkToken`] and GPIO + /// [`Pin`]. + pub fn free_pin(self) -> (GclkToken, Pin) { + (self.token, self.resource) + } +} + +impl Gclk +where + G: GclkId, + I: NotGclkIo, +{ + /// Create a new [`Gclk`] from a clock [`Source`] + /// + /// Creating a [`Gclk`] does not modify any of the hardware registers. It + /// only serves to [`Increment`] the [`Source`]'s [`Enabled`] counter + /// and create a struct to track the GCLK configuration. + /// + /// The configuration data is stored until the user calls [`enable`]. At + /// that point, all of the registers are written according to the + /// initialization procedures specified in the datasheet, and an + /// [`EnabledGclk`] is returned. The `Gclk` is not active or useful until + /// that point. + /// + /// [`enable`]: Gclk::enable + #[inline] + pub fn from_source(token: GclkToken, source: S) -> (Gclk, S::Inc) + where + S: Source + Increment, + { + let config = Gclk { + token, + resource: (), + src_freq: source.freq(), + settings: Settings::default(), + }; + (config, source.inc()) + } + + /// Consume the [`Gclk`] and free its corresponding resources + /// + /// Freeing a [`Gclk`] returns the corresponding [`GclkToken`] and + /// [`Decrement`]s the [`Source`]'s [`Enabled`] counter. + #[inline] + pub fn free_source(self, source: S) -> (GclkToken, S::Dec) + where + S: Source + Decrement, + { + (self.token, source.dec()) + } +} + +impl Gclk +where + G: GclkId, + I: GclkSourceId, +{ + /// Modify the source of an existing clock + /// + /// This is a helper function for swapping Gclk0 to different clock sources. + fn change_source( + mut self, + resource: N::Resource, + freq: Hertz, + ) -> (Gclk, I::Resource) { + self.token.set_source(N::DYN); + let gclk = Gclk { + token: self.token, + resource, + src_freq: freq, + settings: self.settings, + }; + (gclk, self.resource) + } + + /// Set the [`GclkDivider`] value + /// + /// Set the clock division factor from input to output. This takes either a + /// [`GclkDiv8`] or [`GclkDiv16`] enum, restricting the possible division + /// factors to only the valid ones for the given [`Gclk`]. See the + /// [`GclkDivider`] trait for more details. + #[inline] + pub fn div(mut self, div: G::Divider) -> Self { + self.settings.div = div; + self + } + + /// Output a 50-50 duty cycle clock when using an odd [`GclkDivider`] + #[inline] + pub fn improve_duty_cycle(mut self, flag: bool) -> Self { + self.settings.improve_duty_cycle = flag; + self + } + + /// Return the [`Gclk`] ouput frequency + /// + /// This is the input frequency divided by the [`GclkDivider`]. + #[inline] + pub fn freq(&self) -> Hertz { + let div = max(1, self.settings.div.divider()); + Hertz(self.src_freq.0 / div) + } + + /// Set the state of [`GclkOut`] pins when [`GclkIo`] output is disabled + /// + /// The output off value (OOV) determines the logic level of a [GPIO](gpio) + /// [`Pin`] (configured as a [`GclkIo`] output) when the [`Gclk`] is + /// disabled **OR** the [`GclkOut`] is disabled. + /// + /// As mentioned in the [`Gclk`] documentation, configuration options are + /// not usually applied until the call to [`Gclk::enable`]. However, because + /// the OOV is relevant when the `Gclk` is *disabled*, we make an exception. + /// When calling this function, the new OOV will take effect immediately. + /// + /// However, remember that the `Pin` is not controlled by the `Gclk` unless + /// the `Pin` is configured in [`AlternateM`] mode. `Pin`s are automatically + /// set to `AlternateM` mode when calling [`enable_gclk_out`], but by that + /// point, the OOV is irrelevant. If you need the `Pin` to be set to its + /// OOV, you must *manually* set it to `AlternateM` mode before constructing + /// the `GclkOut`. + /// + /// [`enable_gclk_out`]: EnabledGclk::enable_gclk_out + #[inline] + pub fn output_off_value(mut self, high: bool) -> Self { + self.settings.output_off_value = high; + self.token.output_off_value(high); + self + } + + /// Enable the [`Gclk`], so that it can be used as a clock [`Source`] + /// + /// As mentioned in the [`Gclk`] documentation, no hardware registers are + /// actually modified until this call. Rather, the desired configuration is + /// stored internally, and the [`Gclk`] is initialized and configured here + /// according to the datasheet. + /// + /// The returned value is an [`EnabledGclk`] that can be used as a clock + /// [`Source`] for other clocks. + #[inline] + pub fn enable(mut self) -> EnabledGclk { + self.token.configure(I::DYN, self.settings); + self.token.enable(); + Enabled::new(self) + } +} + +impl EnabledGclk +where + G: GclkId, + I: GclkSourceId, +{ + /// Disable the [`Gclk`] + /// + /// This method is only implemented for `N = U0`, which means the clock can + /// only be disabled when no other clocks consume this [`Gclk`]. + #[inline] + pub fn disable(mut self) -> Gclk { + self.0.token.disable(); + self.0 + } +} + +/// Special methods for an [`Enabled`] [`Gclk0`] +/// +/// [`Gclk0`] is special, because it drives the processor's main clock, which +/// can never be disabled. As discussed in the [module-level documentation], +/// this fact is represented by permanently [`Increment`]ing the counter for +/// [`EnabledGclk0`]. Thus, the minimum value for `N` is `U1` and +/// [`EnabledGclk0`] can never be disabled. +/// +/// These methods represent actions that can be taken when `N = U1`, i.e. the +/// [`Enabled`] counter is at its minimum value. This is the only time it's +/// safe to change the [`Gclk0`] [`Source`] or change its [`GclkDivider`] value. +/// +/// [module-level documentation]: self +impl EnabledGclk0 { + /// Swap [`Gclk0`] from one clock [`Source`] to another + /// + /// `Gclk0` will remain fully enabled during the swap. + #[inline] + pub fn swap_sources(self, old: O, new: N) -> (EnabledGclk0, O::Dec, N::Inc) + where + O: Source + Decrement, + N: Source + Increment, + N::Id: NotGclkIo, + { + let (gclk, _) = self.0.change_source((), new.freq()); + let enabled = Enabled::new(gclk); + (enabled, old.dec(), new.inc()) + } + + /// Swap [`Gclk0`] from one [`GclkIo`] [`Pin`] to another + /// + /// `Gclk0` will remain fully enabled during the swap. + #[inline] + pub fn swap_pins

( + self, + pin: P, + freq: impl Into, + ) -> (EnabledGclk0, Pin) + where + I: Gclk0Io, + P: AnyPin, + P::Id: Gclk0Io, + { + let pin = pin.into().into_mode(); + let (gclk, pin) = self.0.change_source(pin, freq.into()); + let enabled = Enabled::new(gclk); + (enabled, pin) + } + + /// Swap [`Gclk0`] from a clock [`Source`] to a [`GclkIo`] [`Pin`] + /// + /// `Gclk0` will remain fully enabled during the swap. + #[inline] + pub fn swap_source_for_pin( + self, + source: S, + pin: P, + freq: impl Into, + ) -> (EnabledGclk0, S::Dec) + where + S: Source + Decrement, + P: AnyPin, + P::Id: Gclk0Io, + { + let pin = pin.into().into_mode(); + let (gclk, _) = self.0.change_source(pin, freq.into()); + let enabled = Enabled::new(gclk); + (enabled, source.dec()) + } + + /// Swap [`Gclk0`] from a [`GclkIo`] [`Pin`] to a clock [`Source`] + /// + /// `Gclk0` will remain fully enabled during the swap. + #[inline] + pub fn swap_pin_for_source( + self, + source: S, + ) -> (EnabledGclk0, Pin, S::Inc) + where + I: Gclk0Io, + S: Source + Increment, + S::Id: NotGclkIo, + { + let (gclk, pin) = self.0.change_source((), source.freq()); + let enabled = Enabled::new(gclk); + (enabled, pin, source.inc()) + } + + /// Set the [`GclkDivider`] value for [`Gclk0`] + /// + /// See [`Gclk::div`] documentation for more details. + #[inline] + pub fn div(&mut self, div: GclkDiv8) { + self.0.settings.div = div; + self.0.token.set_div(div); + } + + /// Output a 50-50 duty cycle clock when using an odd [`GclkDivider`] + #[inline] + pub fn improve_duty_cycle(&mut self, flag: bool) { + self.0.settings.improve_duty_cycle = flag; + self.0.token.improve_duty_cycle(flag); + } + + /// Return the [`Gclk0`] frequency + /// + /// See [`Gclk::freq`] documentation for more details. + #[inline] + pub fn freq(&self) -> Hertz { + self.0.freq() + } + + /// Set the state of [`GclkOut`] pins when [`GclkIo`] output is disabled + /// + /// See [`Gclk::output_off_value`] documentation for more details. + #[inline] + pub fn output_off_value(&mut self, high: bool) { + self.0.settings.output_off_value = high; + self.0.token.output_off_value(high); + } +} + +//============================================================================== +// Source +//============================================================================== + +impl Source for EnabledGclk +where + G: GclkId, + I: GclkSourceId, +{ + type Id = G; + + #[inline] + fn freq(&self) -> Hertz { + self.0.freq() + } +} + +//============================================================================== +// Tokens +//============================================================================== + +seq!(N in 1..=11 { + paste! { + /// Set of [`GclkToken`]s representing the disabled [`Gclk`]s at + /// power-on reset + pub struct GclkTokens { + #( + /// [`GclkToken`] for + #[doc = "[`Gclk" N "`]"] + pub gclk~N: GclkToken<[]>, + )* + } + + impl GclkTokens { + /// Create the set of [`GclkToken`]s + /// + /// # Safety + /// + /// All of the invariants required by `GclkToken::new` must be + /// upheld here as well. + #[inline] + pub(super) unsafe fn new(nvmctrl: &mut NVMCTRL) -> Self { + // Use auto wait states + nvmctrl.ctrla.modify(|_, w| w.autows().set_bit()); + GclkTokens { + #( gclk~N: GclkToken::new(), )* + } + } + } + } +}); + +//============================================================================== +// GclkOut +//============================================================================== + +/// A GPIO [`Pin`] configured as a [`Gclk`] output +/// +/// The existence of this struct serves as proof that the corresponding [`Gclk`] +/// is [`Enabled`] and that it has been output to [`PinId`] `I`. +/// +/// See the [module-level documentation](self) for an example of creating a +/// [`GclkOut`] from an [`EnabledGclk`]. +pub struct GclkOut { + pin: Pin, + freq: Hertz, +} + +impl GclkOut +where + G: GclkId, + I: GclkIo, +{ + /// Return the frequency of the corresponding [`Gclk`] + #[inline] + pub fn freq(&self) -> Hertz { + self.freq + } +} + +impl EnabledGclk +where + G: GclkId, + S: NotGclkIo, +{ + /// Create and enable a [`GclkOut`] + /// + /// Enabling [`GclkIo`] output will [`Increment`] the `EnabledGclk` + /// counter, which will prevent it from being disabled while the + /// `GclkOut` exists. + /// + /// Note that a given [`Gclk`] can only use [`GclkIo`] for input **or** + /// output, but not both simultaneously. The [`NotGclkIo`] trait exists to + /// enforce this requirement. + /// + /// Finally, when a [`GclkOut`] is disabled, but the [`Pin`] is still in + /// [`AlternateM`] mode, it takes the "output off value" of the `Gclk`. See + /// the [`Gclk::output_off_value`] documentation for more details. + #[inline] + pub fn enable_gclk_out

(mut self, pin: P) -> (EnabledGclk, GclkOut) + where + N: Increment, + P: AnyPin, + P::Id: GclkIo, + { + let pin = pin.into().into_mode(); + let freq = self.freq(); + self.0.token.enable_gclk_out(); + let gclk_out = GclkOut { pin, freq }; + (self.inc(), gclk_out) + } + + /// Disable a [`GclkOut`] and free its [`Pin`] + /// + /// Disabling [`GclkIo`] output will [`Decrement`] the [`EnabledGclk`] + /// counter. When a [`GclkOut`] is disabled, but the [`Pin`] is still in + /// [`AlternateM`] mode, it takes the "output off value" of the `Gclk`. See + /// the [`Gclk::output_off_value`] documentation for more details. + #[inline] + pub fn disable_gclk_out( + mut self, + gclk_out: GclkOut, + ) -> (EnabledGclk, Pin) + where + N: Decrement, + I: GclkIo, + { + self.0.token.disable_gclk_out(); + (self.dec(), gclk_out.pin) + } +} diff --git a/hal/src/thumbv7em/clock/v2/osculp32k.rs b/hal/src/thumbv7em/clock/v2/osculp32k.rs new file mode 100644 index 00000000000..c37d39aade4 --- /dev/null +++ b/hal/src/thumbv7em/clock/v2/osculp32k.rs @@ -0,0 +1,490 @@ +//! # Internal, ultra low power, 32 kHz oscillator +//! +//! ## Overview +//! +//! The `osculp32k` module provides access to the 32 kHz ultra low power +//! internal oscillator (OSCULP32K) within the `OSC32KCTRL` peripheral. +//! +//! The `OSCULP32K` clock is unlike most other clocks. First, it is an internal +//! clock that is always enabled and can't be disabled. And second, it has two +//! separate outputs, one at 32 kHz and another divided down to 1 kHz. Moreover, +//! none, either or both of these outputs can be enabled at any given time. +//! +//! We can see, then, that the `OSCULP32K` peripheral forms its own, miniature +//! clock tree. There is a 1:N producer clock that is always enabled; and there +//! are two possible consumer clocks that can be independently and optionally +//! enabled. In fact, this structure is illustrated by the `OSCULP32K` +//! register, which has no regular `ENABLE` bit and two different enable bits +//! for clock output, `EN32K` and `EN1K`. +//! +//! To represent this structure in the type system, we divide the `OSCULP32K` +//! peripheral into these three clocks. Users get access to the 1:N +//! [`EnabledOscUlp32kBase`] clock [`Source`] at power-on reset, which can be +//! consumed by both the [`OscUlp32k`] and [`OscUlp1k`] clocks. Note that +//! `OscUlp32k` and `OscUlp1k` are themselves 1:N clocks as well. +//! +//! ## Write lock +//! +//! Rhe `OSCULP32K` register has a dedicated write lock bit that will freeze its +//! configuration until the next power-on reset. We implement this by simply +//! dropping the [`OscUlp32kBase`] clock, which prevents any further access to +//! the `OSCULP32K` register. +//! +//! ## Example +//! +//! Creating and configuring the OSCULP32K clocks proceeds according to the +//! principles outlined in the [`clock` module documentation]. It is best shown +//! with an example. +//! +//! Let's start by using [`clock_system_at_reset`] to access the HAL clocking +//! structs. +//! +//! ```no_run +//! use atsamd_hal::{ +//! clock::v2::{ +//! clock_system_at_reset, +//! osculp32k::{OscUlp1k, OscUlp32k}, +//! }, +//! pac::Peripherals, +//! }; +//! let mut pac = Peripherals::take().unwrap(); +//! let (buses, clocks, tokens) = clock_system_at_reset( +//! pac.OSCCTRL, +//! pac.OSC32KCTRL, +//! pac.GCLK, +//! pac.MCLK, +//! &mut pac.NVMCTRL, +//! ); +//! ``` +//! +//! Next, we can extract the [`EnabledOscUlp32kBase`] clock from the [`Clocks`] +//! struct and use it to enable the [`OscUlp1k`] and [`OscUlp32k`] clocks. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # osculp32k::{OscUlp1k, OscUlp32k}, +//! # }, +//! # pac::Peripherals, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let (buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! let base = clocks.osculp32k_base; +//! let (osculp1k, base) = OscUlp1k::enable(tokens.osculp32k.osculp1k, base); +//! let (osculp32k, base) = OscUlp32k::enable(tokens.osculp32k.osculp32k, base); +//! ``` +//! +//! We can then override the calibration value read from flash at start up. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # osculp32k::{OscUlp1k, OscUlp32k}, +//! # }, +//! # pac::Peripherals, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let (buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! # let base = clocks.osculp32k_base; +//! # let (osculp1k, base) = OscUlp1k::enable(tokens.osculp32k.osculp1k, base); +//! # let (osculp32k, mut base) = OscUlp32k::enable(tokens.osculp32k.osculp32k, base); +//! base.set_calibration(128); +//! ``` +//! +//! And finally, we can set the write lock bit to freeze the configuation until +//! the next power-on reset. Doing so also drops the `EnabledOscUlp32kBase` +//! clock. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # osculp32k::{OscUlp1k, OscUlp32k}, +//! # }, +//! # pac::Peripherals, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let (buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! # let base = clocks.osculp32k_base; +//! # let (osculp1k, base) = OscUlp1k::enable(tokens.osculp32k.osculp1k, base); +//! # let (osculp32k, mut base) = OscUlp32k::enable(tokens.osculp32k.osculp32k, base); +//! # base.set_calibration(128); +//! base.write_lock(); +//! ``` +//! +//! The complete example is shown below. +//! +//! ```no_run +//! use atsamd_hal::{ +//! clock::v2::{ +//! clock_system_at_reset, +//! osculp32k::{OscUlp1k, OscUlp32k}, +//! }, +//! pac::Peripherals, +//! }; +//! let mut pac = Peripherals::take().unwrap(); +//! let (buses, clocks, tokens) = clock_system_at_reset( +//! pac.OSCCTRL, +//! pac.OSC32KCTRL, +//! pac.GCLK, +//! pac.MCLK, +//! &mut pac.NVMCTRL, +//! ); +//! let base = clocks.osculp32k_base; +//! let (osculp1k, base) = OscUlp1k::enable(tokens.osculp32k.osculp1k, base); +//! let (osculp32k, mut base) = OscUlp32k::enable(tokens.osculp32k.osculp32k, base); +//! base.set_calibration(128); +//! base.write_lock(); +//! ``` +//! +//! [`clock` module documentation]: super +//! [`clock_system_at_reset`]: super::clock_system_at_reset +//! [`Clocks`]: super::Clocks + +use typenum::U0; + +use crate::pac::osc32kctrl::OSCULP32K; + +use crate::time::Hertz; +use crate::typelevel::{Decrement, Increment, PrivateDecrement, PrivateIncrement, Sealed}; + +use super::{Enabled, Source}; + +//============================================================================== +// Tokens +//============================================================================== + +/// Singleton token for the [`OscUlp32kBase`] clock +// +// There should never be more than one instance of `OscUlp32kBaseToken`, because +// it relies on that fact for memory safety. +// +// Users never see `OscUlp32kBaseToken`, because the OSCULP32K base oscillator +// is always enabled. Internally, however, it is used as a register interface. +// The token is zero-sized, so it can be carried by clock types without +// introducing any memory bloat. +// +// As part of that register interface, the `OscUlp32kBaseToken` can access the +// `OSCULP32K` register. That the token is a singleton guarantees the register +// is written from only one location. This allows the token to be `Sync`, even +// though the PAC `OSC32KCTRL` struct is not. +struct OscUlp32kBaseToken(()); + +/// Singleton token that can be exchanged for [`OscUlp1k`] +/// +/// As explained in the [`clock` module documentation](super), instances of +/// various `Token` types can be exchanged for actual clock types. They +/// typically represent clocks that are disabled at power-on reset. +/// +/// The [`OscUlp1k`] clock is disabled at power-on reset. To use it, you must +/// first exchange the token for an actual clock with [`OscUlp1k::enable`]. +pub struct OscUlp1kToken(()); + +/// Singleton token that can be exchanged for [`OscUlp32k`] +/// +/// As explained in the [`clock` module documentation](super), instances of +/// various `Token` types can be exchanged for actual clock types. They +/// typically represent clocks that are disabled at power-on reset. +/// +/// The [`OscUlp32k`] clock is disabled at power-on reset. To use it, you must +/// first exchange the token for an actual clock with [`OscUlp32k::enable`]. +pub struct OscUlp32kToken(()); + +/// Set of tokens representing the disabled OSCULP32K clocks power-on reset +pub struct OscUlp32kTokens { + pub osculp1k: OscUlp1kToken, + pub osculp32k: OscUlp32kToken, +} + +impl OscUlp32kTokens { + /// Create the set of tokens + /// + /// # Safety + /// + /// There must never be more than one instance of each token at any given + /// time. See the notes on `Token` types and memory safety in the root of + /// the `clock` module for more details. + pub(super) unsafe fn new() -> Self { + Self { + osculp1k: OscUlp1kToken(()), + osculp32k: OscUlp32kToken(()), + } + } +} + +impl OscUlp32kBaseToken { + #[inline] + fn osculp32k(&self) -> &OSCULP32K { + // Safety: The `OscUlp32kBaseToken` has exclusive access to the + // `OSCULP32K` register. See the notes on `Token` types and memory + // safety in the root of the `clock` module for more details. + unsafe { &(*crate::pac::OSC32KCTRL::PTR).osculp32k } + } + + /// Set the calibration + #[inline] + fn set_calibration(&mut self, calib: u8) { + // Safety: All bit patterns are valid for this field + self.osculp32k() + .modify(|_, w| unsafe { w.calib().bits(calib) }); + } + + /// Enable the 1 kHz output + #[inline] + fn enable_1k(&mut self) { + self.osculp32k().modify(|_, w| w.en1k().set_bit()); + } + + /// Disable the 1 kHz output + #[inline] + fn disable_1k(&mut self) { + self.osculp32k().modify(|_, w| w.en1k().clear_bit()); + } + + /// Enable the 32 kHz output + #[inline] + fn enable_32k(&mut self) { + self.osculp32k().modify(|_, w| w.en32k().set_bit()); + } + + /// Disable the 32 kHz output + #[inline] + fn disable_32k(&mut self) { + self.osculp32k().modify(|_, w| w.en32k().clear_bit()); + } + + /// Enable the write lock + #[inline] + fn write_lock(&mut self) { + self.osculp32k().modify(|_, w| w.wrtlock().set_bit()); + } +} + +//============================================================================== +// OscUlpBase +//============================================================================== + +/// OSC3ULP2K base clock, which feeds the [`OscUlp1k`] and [`OscUlp32k`] clocks +/// +/// The OSCULP32K peripheral has two possible clock outputs, one at 32 kHz and +/// another at 1 kHz. This structure is represented in the type system as a set +/// of three clocks forming a small clock tree. The [`OscUlp32kBase`] clock +/// represents the base oscillator that feeds the optional [`OscUlp1k`] and +/// [`OscUlp32k`] output clocks. See the [module-level documentation](super) for +/// details and examples. +pub struct OscUlp32kBase { + token: OscUlp32kBaseToken, +} + +/// The [`Enabled`] [`OscUlp32kBase`] clock +/// +/// As described in the [`clock` module documentation](super), the [`Enabled`] +/// wrapper implements compile-time clock tree safety by tracking the number of +/// clocks consuming the [`OscUlp32kBase`] clock and restricts access to the +/// underlying type to prevent misuse. +/// +/// **NOTE:** The `OscUlp32kBase` clock is internal and can never be disabled, +/// so we do not provide a `disable` method. +/// +/// As with [`Enabled`], the default value for `N` is `U0`; if left unspecified, +/// the counter is assumed to be zero. +pub type EnabledOscUlp32kBase = Enabled; + +impl OscUlp32kBase { + /// Create the ultra-low power base oscillator + /// + /// # Safety + /// + /// Because an `OscUlp32kBase` contains an `OscUlp32kBaseToken`, there must + /// never be more than one instance of this struct at any given time. See + /// the notes on `Token` types and memory safety in the root of the `clock` + /// module for more details. + #[inline] + pub(super) unsafe fn new() -> EnabledOscUlp32kBase { + let token = OscUlp32kBaseToken(()); + Enabled::new(Self { token }) + } +} + +impl EnabledOscUlp32kBase { + /// Override the factory-default calibration value + #[inline] + pub fn set_calibration(&mut self, calib: u8) { + self.0.token.set_calibration(calib); + } + + /// Freeze the OSCULP32K configuration until power-on reset + /// + /// This function sets the write-lock bit, which freezes the OSCULP32K + /// configuration at the hardware level until power-on reset. At the API + /// level, it also consumes and drops the [`OscUlp32kBase`] clock, which + /// prevents any further modifications. + #[inline] + pub fn write_lock(mut self) { + self.0.token.write_lock(); + } +} + +//============================================================================== +// Ids +//============================================================================== + +/// Type representing the identity of the [`OscUlp1k`] clock +/// +/// See the discussion on [`Id` types](super#id-types) for more information. +pub enum OscUlp1kId {} + +impl Sealed for OscUlp1kId {} + +/// Type representing the identity of the [`OscUlp32k`] clock +/// +/// See the discussion on [`Id` types](super#id-types) for more information. +pub enum OscUlp32kId {} + +impl Sealed for OscUlp32kId {} + +//============================================================================== +// OscUlp1k +//============================================================================== + +/// Clock representing the 1 kHz output of the [`OscUlp32kBase`] clock +/// +/// The OSCULP32K peripheral has two possible clock outputs, one at 32 kHz and +/// another at 1 kHz. This structure is represented in the type system as a set +/// of three clocks forming a small clock tree. The [`OscUlp1k`] clock is +/// derived from the [`OscUlp32kBase`] clock. See the +/// [module-level documentation](super) for details and examples. +pub struct OscUlp1k { + token: OscUlp1kToken, +} + +/// The [`Enabled`] [`OscUlp1k`] clock +/// +/// As described in the [`clock` module documentation](super), the [`Enabled`] +/// wrapper implements compile-time clock tree safety by tracking the number of +/// clocks consuming the [`OscUlp1k`] clock and restricts access to the +/// underlying type to prevent misuse. +/// +/// As with [`Enabled`], the default value for `N` is `U0`; if left unspecified, +/// the counter is assumed to be zero. +pub type EnabledOscUlp1k = Enabled; + +impl OscUlp1k { + /// Enable 1 kHz output from the [`OscUlp32kBase`] clock + /// + /// This will [`Increment`] the [`EnabledOscUlp32kBase`] counter. + #[inline] + pub fn enable( + token: OscUlp1kToken, + mut base: EnabledOscUlp32kBase, + ) -> (EnabledOscUlp1k, EnabledOscUlp32kBase) { + base.0.token.enable_1k(); + (Enabled::new(Self { token }), base.inc()) + } +} + +impl EnabledOscUlp1k { + /// Disable 1 kHz output from the [`OscUlp32kBase`] clock + /// + /// This will [`Decrement`] the [`EnabledOscUlp32kBase`] counter. + #[inline] + pub fn disable( + self, + mut base: EnabledOscUlp32kBase, + ) -> (OscUlp1kToken, EnabledOscUlp32kBase) { + base.0.token.disable_1k(); + (self.0.token, base.dec()) + } +} + +impl Source for EnabledOscUlp1k { + type Id = OscUlp1kId; + + #[inline] + fn freq(&self) -> Hertz { + Hertz(1024) + } +} + +//============================================================================== +// OscUlp32k +//============================================================================== + +/// Clock representing the 32 kHz output of the [`OscUlp32kBase`] clock +/// +/// The OSCULP32K peripheral has two possible clock outputs, one at 32 kHz and +/// another at 1 kHz. This structure is represented in the type system as a set +/// of three clocks forming a small clock tree. The [`OscUlp32k`] clock is +/// derived from the [`OscUlp32kBase`] clock. See the +/// [module-level documentation](super) for details and examples. +pub struct OscUlp32k { + token: OscUlp32kToken, +} + +/// The [`Enabled`] [`OscUlp32k`] clock +/// +/// As described in the [`clock` module documentation](super), the [`Enabled`] +/// wrapper implements compile-time clock tree safety by tracking the number of +/// clocks consuming the [`OscUlp32k`] clock and restricts access to the +/// underlying type to prevent misuse. +/// +/// As with [`Enabled`], the default value for `N` is `U0`; if left unspecified, +/// the counter is assumed to be zero. +pub type EnabledOscUlp32k = Enabled; + +impl OscUlp32k { + /// Enable 32 kHz output from the [`OscUlp32kBase`] clock + /// + /// This will [`Increment`] the [`EnabledOscUlp32kBase`] counter. + #[inline] + pub fn enable( + token: OscUlp32kToken, + mut base: EnabledOscUlp32kBase, + ) -> (EnabledOscUlp32k, EnabledOscUlp32kBase) { + base.0.token.enable_32k(); + (Enabled::new(Self { token }), base.inc()) + } +} + +impl EnabledOscUlp32k { + /// Disable 32 kHz output from the [`OscUlp32kBase`] clock + /// + /// This will [`Decrement`] the [`EnabledOscUlp32kBase`] counter. + #[inline] + pub fn disable( + self, + mut base: EnabledOscUlp32kBase, + ) -> (OscUlp32kToken, EnabledOscUlp32kBase) { + base.0.token.disable_32k(); + (self.0.token, base.dec()) + } +} + +impl Source for EnabledOscUlp32k { + type Id = OscUlp32kId; + + #[inline] + fn freq(&self) -> Hertz { + Hertz(32_768) + } +} diff --git a/hal/src/thumbv7em/clock/v2/pclk.rs b/hal/src/thumbv7em/clock/v2/pclk.rs new file mode 100644 index 00000000000..a620b5fb476 --- /dev/null +++ b/hal/src/thumbv7em/clock/v2/pclk.rs @@ -0,0 +1,537 @@ +//! # Peripheral Channel Clocks +//! +//! ## Overview +//! +//! Peripheral channel clocks, or [`Pclk`]s, connect generic clock controllers +//! ([`Gclk`]s) to various peripherals within the chip. Each [`Pclk`] maps 1:1 +//! with a corresponding peripheral. +//! +//! The 48 possible [`Pclk`]s are distinguished by their corresponding +//! [`PclkId`] types. Ideally, each [`PclkId`] type would be a relevant type +//! from a corresponding HAL module. For example, each of the eight different +//! [`Sercom`] types implements [`PclkId`]. However, the HAL does not yet +//! support all peripherals, nor have all existing HAL peripherals been +//! integrated with `clock::v2`. In those cases, a dummy type is defined in the +//! [`clock::v2::types`] module. +//! +//! [`Pclk`]s are typically leaves in the clock tree. The only exceptions are +//! [`Pclk`]s used for the [`DFLL`] or [`DPLL`] peripherals. In those cases, the +//! [`Pclk`] acts as a branch clock. +//! +//! Each [`Pclk`] powers only a single peripheral; they do not act as general +//! purpose clock [`Source`]s for other clocks in the tree. As a result, they do +//! not need to be wrapped with [`Enabled`]. +//! +//! [`Pclk`]s also do not have any meaningful configuration beyond identifying +//! which [`EnabledGclk`] is its [`Source`]. Consequently, [`PclkToken`]s can be +//! directly converted into enabled [`Pclk`]s with [`Pclk::enable`]. +//! +//! See the [`clock` module documentation] for a more thorough explanation of +//! the various concepts discussed above. +//! +//! ## Example +//! +//! The following example shows how to enable the [`Pclk`] for [`Sercom0`]. It +//! derives the [`Sercom0`] clock from [`EnabledGclk0`], which is already +//! running at power-on reset. In doing so, the [`EnabledGclk0`] counter is +//! [`Increment`]ed. +//! +//! ```no_run +//! use atsamd_hal::{ +//! clock::v2::{clock_system_at_reset, pclk::Pclk}, +//! pac::Peripherals, +//! }; +//! let mut pac = Peripherals::take().unwrap(); +//! let (buses, clocks, tokens) = clock_system_at_reset( +//! pac.OSCCTRL, +//! pac.OSC32KCTRL, +//! pac.GCLK, +//! pac.MCLK, +//! &mut pac.NVMCTRL, +//! ); +//! let (pclk_sercom0, gclk0) = Pclk::enable(tokens.pclks.sercom0, clocks.gclk0); +//! ``` +//! +//! [`Gclk`]: super::gclk::Gclk +//! [`DFLL`]: super::dfll +//! [`DPLL`]: super::dpll +//! [`Enabled`]: super::Enabled +//! [`EnabledGclk`]: super::gclk::EnabledGclk +//! [`EnabledGclk0`]: super::gclk::EnabledGclk0 +//! [`clock` module documentation]: super +//! [`clock::v2::types`]: super::types +//! [`Sercom`]: crate::sercom::Sercom + +use core::marker::PhantomData; + +use paste::paste; +use seq_macro::seq; + +use crate::pac; +use crate::pac::gclk::pchctrl::GEN_A; + +use crate::time::Hertz; +use crate::typelevel::{Decrement, Increment, Sealed}; + +use super::gclk::{DynGclkId, GclkId}; +use super::Source; + +//============================================================================== +// PclkToken +//============================================================================== + +/// Singleton token that can be exchanged for a [`Pclk`] +/// +/// As explained in the [`clock` module documentation](super), instances of +/// various `Token` types can be exchanged for actual clock types. They +/// typically represent clocks that are disabled at power-on reset. +/// +/// [`PclkToken`]s are no different. All [`Pclk`]s are disabled at power-on +/// reset. To use a [`Pclk`], you must first exchange the token for an actual +/// clock with the [`Pclk::enable`] function. +/// +/// [`PclkToken`] is generic over the [`PclkId`], where each token represents a +/// corresponding peripheral clock channel. +pub struct PclkToken { + pclk: PhantomData

, +} + +impl PclkToken

{ + /// Create a new instance of [`PclkToken`] + /// + /// # Safety + /// + /// Each `PclkToken`s is a singleton. There must never be two simulatenous + /// instances with the same [`PclkId`]. See the notes on `Token` types and + /// memory safety in the root of the `clock` module for more details. + #[inline] + pub(super) unsafe fn new() -> Self { + PclkToken { pclk: PhantomData } + } + + /// Access the corresponding `PCHCTRL` register + #[inline] + fn pchctrl(&self) -> &pac::gclk::PCHCTRL { + // Safety: Each `PclkToken` only has access to a mutually exclusive set + // of registers for the corresponding `PclkId`, and we use a shared + // reference to the register block. See the notes on `Token` types and + // memory safety in the root of the `clock` module for more details. + unsafe { &(*pac::GCLK::PTR).pchctrl[P::DYN as usize] } + } + + /// Set the [`Pclk`] source + #[inline] + fn set_source(&mut self, source: DynPclkSourceId) { + self.pchctrl().modify(|_, w| w.gen().variant(source.into())); + } + + /// Enable the [`Pclk`] + #[inline] + fn enable(&mut self) { + self.pchctrl().modify(|_, w| w.chen().set_bit()); + } + + /// Disable the [`Pclk`] + #[inline] + fn disable(&mut self) { + self.pchctrl().modify(|_, w| w.chen().clear_bit()); + } +} + +//============================================================================== +// PclkId types +//============================================================================== + +/// Module containing only the types implementing [`PclkId`] +/// +/// Because there are so many types that implement [`PclkId`], it is helpful to +/// have them defined in a separate module, so that you can import all of them +/// using a wildcard (`*`) without importing anything else, i.e. +/// +/// ``` +/// use atsamd_hal::clock::v2::pclk::ids::*; +/// ``` +pub mod ids { + + pub use crate::sercom::{Sercom0, Sercom1, Sercom2, Sercom3, Sercom4, Sercom5}; + #[cfg(feature = "min-samd51n")] + pub use crate::sercom::{Sercom6, Sercom7}; + + pub use super::super::dfll::DfllId; + pub use super::super::dpll::{Dpll0Id, Dpll1Id}; + pub use super::super::types::{ + Ac, Adc0, Adc1, CM4Trace, Ccl, Dac, Eic, EvSys0, EvSys1, EvSys10, EvSys11, EvSys2, EvSys3, + EvSys4, EvSys5, EvSys6, EvSys7, EvSys8, EvSys9, FreqMMeasure, FreqMReference, PDec, Sdhc0, + SlowClk, Tc0Tc1, Tc2Tc3, Tcc0Tcc1, Tcc2Tcc3, Usb, + }; + #[cfg(any(feature = "same51", feature = "same53", feature = "same54"))] + pub use super::super::types::{Can0, Can1}; + #[cfg(feature = "min-samd51n")] + pub use super::super::types::{Sdhc1, Tc6Tc7}; + #[cfg(feature = "min-samd51j")] + pub use super::super::types::{Tc4Tc5, Tcc4, I2S0, I2S1}; +} + +use ids::*; + +/// Append the list of all [`PclkId`] types and `snake_case` id names to the +/// arguments of a macro call +/// +/// This macro will perform the embedded macro call with a list of tuples +/// appended to the arguments. Each tuple contains a type implementing +/// [`PclkId`], its corresponding `PCHCTRL` register index, and the `snake_case` +/// name of the corresponding token in the [`PclkTokens`] struct. +/// +/// **Note:** The entries within [`DynPclkId`] do not match the type names. +/// Rather, they match the `snake_case` names converted to `CamelCase`. +/// +/// An optional attribute is added just before each tuple. These are mainly used +/// to declare the conditions under which the corresponding peripheral exists. +/// For example, `Sercom6` and `Sercom7` are tagged with +/// `#[cfg(feature = "min-samd51n")]`. +/// +/// The example below shows the pattern that should be used to match against the +/// appended tokens. +/// +/// ```ignore +/// macro_rules! some_macro { +/// ( +/// $first_arg:tt, +/// $second_arg:tt +/// $( +/// $( #[$cfg:meta] )? +/// ($Type:ident = $N:literal, $Id:ident) +/// )+ +/// ) => +/// { +/// // implementation here ... +/// } +/// } +/// +/// with_pclk_types_ids!(some_macro!(first, second)); +/// ``` +macro_rules! with_pclk_types_ids { + ( $some_macro:ident ! ( $( $args:tt )* ) ) => { + $some_macro!( + $( $args )* + (DfllId = 0, dfll) + (Dpll0Id = 1, dpll0) + (Dpll1Id = 2, dpll1) + (SlowClk = 3, slow) + (Eic = 4, eic) + (FreqMMeasure = 5, freq_m_measure) + (FreqMReference = 6, freq_m_reference) + (Sercom0 = 7, sercom0) + (Sercom1 = 8, sercom1) + (Tc0Tc1 = 9, tc0_tc1) + (Usb = 10, usb) + (EvSys0 = 11, ev_sys0) + (EvSys1 = 12, ev_sys1) + (EvSys2 = 13, ev_sys2) + (EvSys3 = 14, ev_sys3) + (EvSys4 = 15, ev_sys4) + (EvSys5 = 16, ev_sys5) + (EvSys6 = 17, ev_sys6) + (EvSys7 = 18, ev_sys7) + (EvSys8 = 19, ev_sys8) + (EvSys9 = 20, ev_sys9) + (EvSys10 = 21, ev_sys10) + (EvSys11 = 22, ev_sys11) + (Sercom2 = 23, sercom2) + (Sercom3 = 24, sercom3) + (Tcc0Tcc1 = 25, tcc0_tcc1) + (Tc2Tc3 = 26, tc2_tc3) + #[cfg(any(feature = "same51", feature = "same53", feature = "same54"))] + (Can0 = 27, can0) + #[cfg(any(feature = "same51", feature = "same53", feature = "same54"))] + (Can1 = 28, can1) + (Tcc2Tcc3 = 29, tcc2_tcc3) + #[cfg(feature = "min-samd51j")] + (Tc4Tc5 = 30, tc4_tc5) + (PDec = 31, pdec) + (Ac = 32, ac) + (Ccl = 33, ccl) + (Sercom4 = 34, sercom4) + (Sercom5 = 35, sercom5) + #[cfg(feature = "min-samd51n")] + (Sercom6 = 36, sercom6) + #[cfg(feature = "min-samd51n")] + (Sercom7 = 37, sercom7) + #[cfg(feature = "min-samd51j")] + (Tcc4 = 38, tcc4) + #[cfg(feature = "min-samd51n")] + (Tc6Tc7 = 39, tc6_tc7) + (Adc0 = 40, adc0) + (Adc1 = 41, adc1) + (Dac = 42, dac) + #[cfg(feature = "min-samd51j")] + (I2S0 = 43, i2s0) + #[cfg(feature = "min-samd51j")] + (I2S1 = 44, i2s1) + (Sdhc0 = 45, sdhc0) + #[cfg(feature = "min-samd51n")] + (Sdhc1 = 46, sdhc1) + (CM4Trace = 47, cm4_trace) + ); + }; +} + +//============================================================================== +// DynPclkId +//============================================================================== + +macro_rules! dyn_pclk_id { + ( + $( + $( #[$cfg:meta] )? + ($Type:ident = $N:literal, $id:ident) + )+ + ) => { + paste! { + /// Value-level enum identifying one of the 48 possible [`Pclk`]s + /// + /// The variants of this enum identify one of the 48 possible + /// peripheral channel clocks. When cast to a `u8`, each variant + /// maps to its corresponding `PCHCTRL` index. + /// + /// `DynPclkId` is the value-level equivalent of [`PclkId`]. + #[repr(u8)] + pub enum DynPclkId { + $( + $( #[$cfg] )? + [<$id:camel>] = $N, + )+ + } + + $( + $( #[$cfg] )? + impl PclkId for $Type { + const DYN: DynPclkId = DynPclkId::[<$id:camel>]; + } + )+ + } + }; +} + +with_pclk_types_ids!(dyn_pclk_id!()); + +//============================================================================== +// PclkId +//============================================================================== + +/// Type-level enum identifying one of the 48 possible [`Pclk`]s +/// +/// The types implementing this trait, e.g. [`Sercom0`] or [`DfllId`], are +/// type-level variants of `PclkId`, and they identify one of the 48 possible +/// peripheral channel clocks. +/// +/// `PclkId` is the type-level equivalent of [`DynPclkId`]. See the +/// documentation on [type-level programming] and specifically +/// [type-level enums] for more details. +/// +/// [type-level programming]: crate::typelevel +/// [type-level enums]: crate::typelevel#type-level-enums +pub trait PclkId: Sealed { + /// Corresponding variant of [`DynPclkId`] + const DYN: DynPclkId; +} + +//============================================================================== +// DynPclkSourceId +//============================================================================== + +/// Value-level enum of possible clock sources for a [`Pclk`] +/// +/// The variants of this enum identify the [`Gclk`] used as a clock source for +/// a given [`Pclk`]. Because the variants are identical to [`DynGclkId`], we +/// simply define it as a type alias. +/// +/// `DynPclkSourceId` is the value-level equivalent of [`PclkSourceId`]. +/// +/// [`Gclk`]: super::gclk::Gclk +pub type DynPclkSourceId = DynGclkId; + +/// Convert from [`DynPclkSourceId`] to the equivalent [PAC](crate::pac) type +impl From for GEN_A { + fn from(source: DynPclkSourceId) -> Self { + seq!(N in 0..=11 { + match source { + #( + DynGclkId::Gclk~N => GEN_A::GCLK~N, + )* + } + }) + } +} + +//============================================================================== +// PclkSourceId +//============================================================================== + +/// Type-level enum of possible clock [`Source`]s for a [`Pclk`] +/// +/// The types implementing this trait are type-level variants of `PclkSourceId`, +/// and they identify the [`Gclk`] acting as a clock [`Source`] for a given +/// [`Pclk`]. Accordingly, all implementers of this trait are [`GclkId`] types, +/// and this trait is simply a trait alias for [`GclkId`]. `Id` types in general +/// are described in more detail in the [`clock` module documentation](super). +/// +/// `PclkSourceId` is the type-level equivalent of [`DynPclkSourceId`]. See the +/// documentation on [type-level programming] and specifically +/// [type-level enums] for more details. +/// +/// [`Gclk`]: super::gclk::Gclk +/// [type-level programming]: crate::typelevel +/// [type-level enums]: crate::typelevel#type-level-enums +pub trait PclkSourceId: GclkId {} + +impl PclkSourceId for G {} + +//============================================================================== +// Pclk +//============================================================================== + +/// Peripheral channel clock for a given peripheral +/// +/// Peripheral channel clocks connect generic clock generators ([`Gclk`]s) to +/// various peripherals. `Pclk`s usually act as leaves in the clock tree, except +/// when they feed the [`DFLL`] and [`DPLL`] peripherals. +/// +/// The type parameter `P` is a [`PclkId`] that determines which of the 48 +/// peripherals this [`Pclk`] feeds. The type parameter `I` represents the `Id` +/// type for the [`EnabledGclk`] acting as the `Pclk`'s [`Source`]. It must be +/// one of the valid [`PclkSourceId`]s, which is simply a trait alias for +/// [`GclkId`]. See the [`clock` module documentation](super) for more detail on +/// `Id` types. +/// +/// `Pclk`s cannot act as general purpose clock [`Source`]s; rather, they map +/// 1:1 with corresponding peripherals. Thus, enabled `Pclk`s do not need a +/// compile-time counter of consumer clocks, so they are not wrapped with +/// [`Enabled`]. Enabled `Pclk`s are created directly from [`PclkToken`]s with +/// [`Pclk::enable`]. +/// +/// See the [module-level documentation](self) for an example. +/// +/// [`Enabled`]: super::Enabled +/// [`Gclk`]: super::gclk::Gclk +/// [`EnabledGclk`]: super::gclk::EnabledGclk +/// [`DFLL`]: super::dfll +/// [`DPLL`]: super::dpll +pub struct Pclk +where + P: PclkId, + I: PclkSourceId, +{ + token: PclkToken

, + src: PhantomData, + freq: Hertz, +} + +impl Pclk +where + P: PclkId, + I: PclkSourceId, +{ + pub(super) fn new(token: PclkToken

, freq: Hertz) -> Self { + Self { + token, + src: PhantomData, + freq, + } + } + + /// Create and enable a [`Pclk`] + /// + /// Creating a [`Pclk`] immediately enables the corresponding peripheral + /// channel clock. It also [`Increment`]s the [`Source`]'s [`Enabled`] + /// counter. + /// + /// Note that the [`Source`] will always be an [`EnabledGclk`]. + /// + /// [`Enabled`]: super::Enabled + /// [`EnabledGclk`]: super::gclk::EnabledGclk + #[inline] + pub fn enable(mut token: PclkToken

, gclk: S) -> (Self, S::Inc) + where + S: Source + Increment, + { + let freq = gclk.freq(); + token.set_source(I::DYN); + token.enable(); + let pclk = Pclk::new(token, freq); + (pclk, gclk.inc()) + } + + /// Disable and destroy a [`Pclk`] + /// + /// Consume the [`Pclk`], release the [`PclkToken`], and [`Decrement`] the + /// [`EnabledGclk`]'s counter + /// + /// [`Enabled`]: super::Enabled + /// [`EnabledGclk`]: super::gclk::EnabledGclk + #[inline] + pub fn disable(mut self, gclk: S) -> (PclkToken

, S::Dec) + where + S: Source + Decrement, + { + self.token.disable(); + (self.token, gclk.dec()) + } + + /// Return the [`Pclk`] frequency + #[inline] + pub fn freq(&self) -> Hertz { + self.freq + } +} + +impl Sealed for Pclk +where + P: PclkId, + I: PclkSourceId, +{ +} + +//============================================================================== +// PclkTokens +//============================================================================== + +macro_rules! define_pclk_tokens_struct { + ( + $( + $( #[$cfg:meta] )? + ($Type:ident = $_:literal, $id:ident) + )+ + ) => + { + /// Set of [`PclkToken`]s representing the disabled [`Pclk`]s at + /// power-on reset + pub struct PclkTokens { + $( + $( #[$cfg] )? + pub $id: PclkToken<$Type>, + )+ + } + + impl PclkTokens { + /// Create the set of [`PclkToken`]s + /// + /// # Safety + /// + /// All invariants required by `PclkToken::new` must be upheld here + #[inline] + pub(super) fn new() -> Self { + unsafe { + Self { + $( + $( #[$cfg] )? + $id: PclkToken::new(), + )+ + } + } + } + } + }; +} + +with_pclk_types_ids!(define_pclk_tokens_struct!()); diff --git a/hal/src/thumbv7em/clock/v2/reset.rs b/hal/src/thumbv7em/clock/v2/reset.rs new file mode 100644 index 00000000000..20cd9998081 --- /dev/null +++ b/hal/src/thumbv7em/clock/v2/reset.rs @@ -0,0 +1,179 @@ +//! This module is intentionally private. Its contents are publicly exported +//! from the `v2` module, which is where the corresponding documentation will +//! appear. + +use typenum::U1; + +use crate::pac::{GCLK, MCLK, NVMCTRL, OSC32KCTRL, OSCCTRL}; + +use super::*; + +/// Collection of low-level PAC structs +/// +/// This struct serves to guard access to the low-level PAC structs. It places +/// them behind an `unsafe` barrier. +/// +/// Normally, users trade the low-level PAC structs for the higher-level +/// `clock::v2` API. However, in some cases, the `clock::v2` API may not be +/// sufficient. In these cases, users can access the registers directly by +/// calling [`Pac::steal`] to recover the PAC structs. +pub struct Pac { + oscctrl: OSCCTRL, + osc32kctrl: OSC32KCTRL, + gclk: GCLK, + mclk: MCLK, +} + +impl Pac { + /// Escape hatch allowing to access low-level PAC structs + /// + /// Consume the [`Pac`] and return the low-level PAC structs. This is + /// useful when the `clock::v2` API does not provide a necessary feature, or + /// when dealing with the legacy `clock::v1` API. For example, many of the + /// `clock::v1` functions require access to the [`MCLK`] peripheral. + /// + /// # Safety + /// + /// Directly configuring clocks through the PAC API can invalidate the + /// type-level guarantees of the `clock` module API. + pub unsafe fn steal(self) -> (OSCCTRL, OSC32KCTRL, GCLK, MCLK) { + (self.oscctrl, self.osc32kctrl, self.gclk, self.mclk) + } +} + +/// Bus clock objects +/// +/// This type is constructed using the [`clock_system_at_reset`] function, which +/// consumes the PAC-level clocking structs and returns the HAL-level clocking +/// structs in their reset state. +/// +/// This type contains the [bus clocks](super#bus-clocks), which are a necessary +/// to implement memory safety for the [`AhbClk`]s and [`ApbClk`]s. +/// +/// [`AhbClk`]: super::ahb::AhbClk +/// [`ApbClk`]: super::apb::ApbClk +pub struct Buses { + pub ahb: ahb::Ahb, + pub apb: apb::Apb, +} + +/// Enabled clocks at power-on reset +/// +/// This type is constructed using the [`clock_system_at_reset`] function, which +/// consumes the PAC-level clocking structs and returns the HAL-level clocking +/// structs in their reset state. +/// +/// This type represents the clocks as they are configured at power-on reset. +/// The main clock, [`Gclk0`](gclk::Gclk0), runs at 48 MHz using the +/// [`Dfll`](dfll::Dfll) in open-loop mode. The ultra-low power +/// [base oscillator](osculp32k::OscUlp32kBase) is also enabled and running, as +/// it can never be disabled. +/// +/// As described in the [top-level](super::super) documentation for the `clock` +/// module, only [`Enabled`] clocks can be used as a [`Source`] for downstream +/// clocks. This struct contains all of the `Enabled` clocks at reset. +/// +/// This struct also contains the [`Pac`] wrapper struct, which provides +/// `unsafe` access to the low-level PAC structs. +pub struct Clocks { + /// Wrapper providing `unsafe` access to low-level PAC structs + pub pac: Pac, + /// Enabled AHB clocks + pub ahbs: ahb::AhbClks, + /// Enabled APB clocks + pub apbs: apb::ApbClks, + /// Main system clock, driven at 48 MHz by the DFLL in open loop mode + pub gclk0: Enabled, U1>, + /// DFLL48 in open loop mode + pub dfll: Enabled, + /// Always-enabled base oscillator for the [`OscUlp1k`](osculp32k::OscUlp1k) + /// and [`OscUlp32k`](osculp32k::OscUlp32k) clocks. + pub osculp32k_base: Enabled, +} + +/// Type-level tokens for unused clocks at power-on reset +/// +/// This type is constructed using the [`clock_system_at_reset`] function, which +/// consumes the PAC-level clocking structs and returns the HAL-level clocking +/// structs in their reset state. +/// +/// As described in the [top-level](super::super) documentation for the `clock` +/// module, token types are used to guanrantee the uniqueness of each clock. To +/// configure or enable a clock, you must provide the corresponding token. +pub struct Tokens { + /// Tokens to create [`apb::ApbClk`]s + pub apbs: apb::ApbTokens, + /// Token to create [`dpll::Dpll0`] + pub dpll0: dpll::DpllToken, + /// Token to create [`dpll::Dpll1`] + pub dpll1: dpll::DpllToken, + /// Tokens to create [`gclk::Gclk`] + pub gclks: gclk::GclkTokens, + /// Tokens to create [`pclk::Pclk`]s + pub pclks: pclk::PclkTokens, + /// Tokens to create [`rtcosc::RtcOsc`] + pub rtcosc: rtcosc::RtcOscToken, + /// Tokens [`xosc::Xosc0`] + pub xosc0: xosc::XoscToken, + /// Token to create [`xosc::Xosc1`] + pub xosc1: xosc::XoscToken, + /// Tokens to create [`xosc32k::Xosc32kBase`], [`xosc32k::Xosc1k`] and + /// [`xosc32k::Xosc32k`] + pub xosc32k: xosc32k::Xosc32kTokens, + /// Tokens to create [`osculp32k::OscUlp1k`] and [`osculp32k::OscUlp32k`] + pub osculp32k: osculp32k::OscUlp32kTokens, +} + +/// Consume the PAC clocking structs and return a HAL-level +/// representation of the clocks at power-on reset +/// +/// This function consumes the [`OSCCTRL`], [`OSC32KCTRL`], [`GCLK`] and +/// [`MCLK`] PAC structs and returns the [`Buses`], [`Clocks`] and [`Tokens`]. +/// +/// See the [module-level documentation](super) for more details. +#[inline] +pub fn clock_system_at_reset( + oscctrl: OSCCTRL, + osc32kctrl: OSC32KCTRL, + gclk: GCLK, + mclk: MCLK, + nvmctrl: &mut NVMCTRL, +) -> (Buses, Clocks, Tokens) { + // Safety: No bus, clock or token is instantiated more than once + unsafe { + let buses = Buses { + ahb: ahb::Ahb::new(), + apb: apb::Apb::new(), + }; + let pac = Pac { + oscctrl, + osc32kctrl, + gclk, + mclk, + }; + let dfll = Enabled::<_>::new(dfll::Dfll::open_loop(dfll::DfllToken::new())); + let (gclk0, dfll) = gclk::Gclk0::from_source(gclk::GclkToken::new(), dfll); + let gclk0 = Enabled::new(gclk0); + let clocks = Clocks { + pac, + ahbs: ahb::AhbClks::new(), + apbs: apb::ApbClks::new(), + gclk0, + dfll, + osculp32k_base: osculp32k::OscUlp32kBase::new(), + }; + let tokens = Tokens { + apbs: apb::ApbTokens::new(), + dpll0: dpll::DpllToken::new(), + dpll1: dpll::DpllToken::new(), + gclks: gclk::GclkTokens::new(nvmctrl), + pclks: pclk::PclkTokens::new(), + rtcosc: rtcosc::RtcOscToken::new(), + xosc0: xosc::XoscToken::new(), + xosc1: xosc::XoscToken::new(), + xosc32k: xosc32k::Xosc32kTokens::new(), + osculp32k: osculp32k::OscUlp32kTokens::new(), + }; + (buses, clocks, tokens) + } +} diff --git a/hal/src/thumbv7em/clock/v2/rtcosc.rs b/hal/src/thumbv7em/clock/v2/rtcosc.rs new file mode 100644 index 00000000000..e894817d5a8 --- /dev/null +++ b/hal/src/thumbv7em/clock/v2/rtcosc.rs @@ -0,0 +1,289 @@ +//! RTC oscillator +//! +//! This module provides a representation of the RTC oscillator, which +//! can be selected from one of four possible options. The [`RtcOsc`] type +//! represents proof that the RTC oscillator has been properly configured and +//! that its clock source has been locked and cannot be modified. +//! +//! For the moment, the [`RtcOsc`] is not used anywhere else in the HAL. A +//! future change to the [`rtc`](crate::rtc) module should either take ownership +//! of the [`RtcOsc`] at creation of the [`Rtc`] or replace the [`RtcOsc`] +//! entirely with an integrated [`Rtc`] API. +//! +//! # Example +//! +//! To create the [`RtcOsc`] let's start by using [`clock_system_at_reset`] to +//! access the HAL clocking structs. +//! +//! ```no_run +//! use atsamd_hal::{ +//! pac::Peripherals, +//! thumbv7em::clock::v2::{clock_system_at_reset, osculp32k::OscUlp32k, rtcosc::RtcOsc}, +//! }; +//! let mut pac = Peripherals::take().unwrap(); +//! let (buses, clocks, tokens) = clock_system_at_reset( +//! pac.OSCCTRL, +//! pac.OSC32KCTRL, +//! pac.GCLK, +//! pac.MCLK, +//! &mut pac.NVMCTRL, +//! ); +//! ``` +//! +//! Next, let's enable the 32 kHz output of the internal, ultra-low power +//! oscillator. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # pac::Peripherals, +//! # thumbv7em::clock::v2::{clock_system_at_reset, osculp32k::OscUlp32k, rtcosc::RtcOsc}, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let (buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! let (osculp32k, base) = OscUlp32k::enable(tokens.osculp32k.osculp32k, clocks.osculp32k_base); +//! ``` +//! +//! Finally, we can use the [`EnabledOscUlp32k`] to enabled the [`RtcOsc`]. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # pac::Peripherals, +//! # thumbv7em::clock::v2::{clock_system_at_reset, osculp32k::OscUlp32k, rtcosc::RtcOsc}, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let (buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! # let (osculp32k, base) = OscUlp32k::enable(tokens.osculp32k.osculp32k, clocks.osculp32k_base); +//! let (rtc_osc, osculp32k) = RtcOsc::enable(tokens.rtcosc, osculp32k); +//! ``` +//! +//! The entire example is provided below. +//! +//! ```no_run +//! use atsamd_hal::{ +//! pac::Peripherals, +//! thumbv7em::clock::v2::{clock_system_at_reset, osculp32k::OscUlp32k, rtcosc::RtcOsc}, +//! }; +//! let mut pac = Peripherals::take().unwrap(); +//! let (buses, clocks, tokens) = clock_system_at_reset( +//! pac.OSCCTRL, +//! pac.OSC32KCTRL, +//! pac.GCLK, +//! pac.MCLK, +//! &mut pac.NVMCTRL, +//! ); +//! let (osculp32k, base) = OscUlp32k::enable(tokens.osculp32k.osculp32k, clocks.osculp32k_base); +//! let (rtc_osc, osculp32k) = RtcOsc::enable(tokens.rtcosc, osculp32k); +//! ``` +//! +//! [`clock_system_at_reset`]: super::clock_system_at_reset +//! [`Rtc`]: crate::rtc::Rtc +//! [`EnabledOscUlp32k`]: super::osculp32k::EnabledOscUlp32k + +use core::marker::PhantomData; + +use crate::pac::osc32kctrl::rtcctrl::RTCSEL_A; +use crate::pac::osc32kctrl::RTCCTRL; +use crate::pac::OSC32KCTRL; + +use crate::time::Hertz; +use crate::typelevel::{Decrement, Increment}; + +use super::osculp32k::{OscUlp1kId, OscUlp32kId}; +use super::xosc32k::{Xosc1kId, Xosc32kId}; +use super::Source; + +//============================================================================== +// RtcOscToken +//============================================================================== + +/// Token used to retrieve the RTC oscillator +/// Singleton token that can be exchanged for the [`RtcOsc`] +/// +/// As explained in the [`clock` module documentation](super), instances of +/// various `Token` types can be exchanged for actual clock types. They +/// typically represent clocks that are disabled at power-on reset. +/// +/// To use the [`RtcOsc`], you must first exchange this token with the +/// [`RtcOsc::enable`] function. +pub struct RtcOscToken(()); + +impl RtcOscToken { + /// Create a new instance of [`RtcOscToken`] + /// + /// # Safety + /// + /// The `RtcOscToken` is a singleton. There must never be two simulatenous + /// instances of it. See the notes on `Token` types and memory safety in the + /// root of the `clock` module for more details. + #[inline] + pub(super) unsafe fn new() -> Self { + Self(()) + } + + #[inline] + fn rtcctrl(&self) -> &RTCCTRL { + // Safety: The `RtcOsc` only has exclusive access to the RTCCTRL + // register. See the notes on `Token` types and memory safety in the + // root of the `clock` module for more details. + unsafe { &(*OSC32KCTRL::PTR).rtcctrl } + } + + #[inline] + fn set_source(&mut self, source: DynRtcSourceId) { + self.rtcctrl().write(|w| w.rtcsel().variant(source.into())); + } +} + +//============================================================================== +// DynRtcSourceId +//============================================================================== + +/// Value-level enum of possible clock sources for the [`RtcOsc`] +/// +/// The variants of this enum identify one of four possible clock sources for +/// the [`RtcOsc`]. +/// +/// `DynRtcSourceId` is the value-level equivalent of [`RtcSourceId`]. +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum DynRtcSourceId { + /// The RTC is sourced from the [`OscUlp1k`](super::osculp32k::OscUlp1k) + OscUlp1k, + /// The RTC is sourced from the [`OscUlp32k`](super::osculp32k::OscUlp32k) + OscUlp32k, + /// The RTC is sourced from the [`Xosc1k`](super::xosc32k::Xosc1k) + Xosc1k, + /// The RTC is sourced from the [`Xosc32k`](super::xosc32k::Xosc32k) + Xosc32k, +} + +impl From for RTCSEL_A { + #[inline] + fn from(source: DynRtcSourceId) -> Self { + use DynRtcSourceId::*; + use RTCSEL_A::*; + match source { + OscUlp1k => ULP1K, + OscUlp32k => ULP32K, + Xosc1k => XOSC1K, + Xosc32k => XOSC32K, + } + } +} + +//============================================================================== +// RtcSourceId +//============================================================================== + +/// Type-level `enum` for the RTC oscillator clock source +/// +/// The RTC can be sourced from any of [`OscUlp1k`](super::osculp32k::OscUlp1k), +/// [`OscUlp32k`](super::osculp32k::OscUlp32k), +/// [`Xosc1k`](super::xosc32k::Xosc1k) or [`Xosc32k`](super::xosc32k::Xosc32k). +/// +/// See the [type-level enum] documentation for more details on the pattern. +/// +/// [type-level enum]: crate::typelevel#type-level-enum +/// Type-level enum of possible clock [`Source`]s for the [`RtcOsc`] +/// +/// The types implementing this trait are type-level variants of `RtcSourceId`, +/// and they identify one of four possible clock [`Source`]s for the [`RtcOsc`]. +/// All implementers of this trait are [`Id` types](super#id-types), which are +/// described in more detail in the [`clock` module documentation](super). +/// +/// `RtcSourceId` is the type-level equivalent of [`DynRtcSourceId`]. See the +/// documentation on [type-level programming] and specifically +/// [type-level enums] for more details. +/// +/// [type-level programming]: crate::typelevel +/// [type-level enums]: crate::typelevel#type-level-enums +pub trait RtcSourceId { + /// Corresponding [`DynRtcSourceId`] + const DYN: DynRtcSourceId; + /// Clock source frequency, either 1 kHz or 32 kHz + const FREQ: Hertz; +} + +impl RtcSourceId for OscUlp1kId { + const DYN: DynRtcSourceId = DynRtcSourceId::OscUlp1k; + const FREQ: Hertz = Hertz(1024); +} +impl RtcSourceId for OscUlp32kId { + const DYN: DynRtcSourceId = DynRtcSourceId::OscUlp32k; + const FREQ: Hertz = Hertz(32_768); +} +impl RtcSourceId for Xosc1kId { + const DYN: DynRtcSourceId = DynRtcSourceId::Xosc1k; + const FREQ: Hertz = Hertz(1024); +} +impl RtcSourceId for Xosc32kId { + const DYN: DynRtcSourceId = DynRtcSourceId::Xosc32k; + const FREQ: Hertz = Hertz(32_768); +} + +//============================================================================== +// RtcOsc +//============================================================================== + +/// Oscillator for the [`Rtc`] +/// +/// The `RtcOsc` represents proof that a clock source for the [`Rtc`] has been +/// selected and configured. It also guarantees that the clock source for the +/// RTC will not be modified or disabled while it is in use. +/// +/// See the [module-level documentation](self) for an example of creating the +/// `RtcOsc`. +/// +/// [`Rtc`]: crate::rtc::Rtc +pub struct RtcOsc { + token: RtcOscToken, + source: PhantomData, +} + +impl RtcOsc { + /// Consume the [`RtcOscToken`] and return the [`RtcOsc`] + /// + /// Enabling the [`RtcOsc`] will [`Increment`] the consumer count of its + /// [`Enabled`](super::Enabled) clock [`Source`]. + #[inline] + pub fn enable(mut token: RtcOscToken, source: S) -> (Self, S::Inc) + where + S: Source + Increment, + { + token.set_source(I::DYN); + let rtc_osc = Self { + token, + source: PhantomData, + }; + (rtc_osc, source.inc()) + } + + /// Consume the [`RtcOsc`] and return the [`RtcOscToken`] + /// + /// Disabling the `RtcOsc` will [`Decrement`] the consumer count of its + /// [`Enabled`](super::Enabled) clock [`Source`]. + #[inline] + pub fn disable(self, source: S) -> (RtcOscToken, S::Dec) + where + S: Source + Decrement, + { + (self.token, source.dec()) + } + + /// Return the [`RtcOsc`] frequency, which can either be 1 kHz or 32 kHz + #[inline] + pub fn freq(&self) -> Hertz { + I::FREQ + } +} diff --git a/hal/src/thumbv7em/clock/v2/types.rs b/hal/src/thumbv7em/clock/v2/types.rs new file mode 100644 index 00000000000..a3b10fa6c69 --- /dev/null +++ b/hal/src/thumbv7em/clock/v2/types.rs @@ -0,0 +1,102 @@ +//! Module defining or exporting peripheral types for the ['ahb'], ['apb'] and +//! ['pclk'] modules +//! +//! The `ahb`, `apb` and `pclk` modules each define structs that are +//! generic over a type parameter representing a peripheral. Some peripheral +//! modules already define suitable types for this purpose. For example, +//! [`sercom`] defines the [`Sercom0`], [`Sercom1`], etc. types. But other +//! peripherals are either not yet implemented in the HAL or do not define a +//! suitable type. This module defines a type for such peripherals. If/when a +//! suitable type is added for a given peripheral, the type defined here should +//! be deprecated or removed. +//! +//! [`ahb`]: super::ahb +//! [`apb`]: super::apb +//! [`pclk`]: super::pclk +//! [`sercom`]: crate::sercom +//! [`Sercom0`]: crate::sercom::Sercom0 +//! [`Sercom1`]: crate::sercom::Sercom1 + +use crate::typelevel::Sealed; + +#[cfg(feature = "min-samd51g")] +pub use crate::sercom::{Sercom0, Sercom1, Sercom2, Sercom3, Sercom4, Sercom5}; +#[cfg(feature = "min-samd51n")] +pub use crate::sercom::{Sercom6, Sercom7}; + +macro_rules! create_types { + ( + $( + $Type:ident + ),+ + ) => { + $( + /// Marker type representing the corresponding peripheral + /// + /// This type is defined by and used within the [`clock`](super) + /// module. See the the [`types`](self) module documentation for + /// more details. + pub enum $Type {} + impl Sealed for $Type {} + )+ + }; +} + +create_types!(Ac); +create_types!(Adc0, Adc1); +create_types!(Aes); +#[cfg(any(feature = "same51", feature = "same53", feature = "same54"))] +create_types!(Can0, Can1); +create_types!(Ccl); +create_types!(Cmcc); +create_types!(CM4Trace); +create_types!(Dac); +create_types!(Dmac); +create_types!(Dsu); +create_types!(Eic); +create_types!( + EvSys, EvSys0, EvSys1, EvSys2, EvSys3, EvSys4, EvSys5, EvSys6, EvSys7, EvSys8, EvSys9, EvSys10, + EvSys11 +); +create_types!(FreqM); +create_types!(FreqMMeasure); +create_types!(FreqMReference); +create_types!(Gclk); +#[cfg(any(feature = "same53", feature = "same54"))] +create_types!(Gmac); +create_types!(Hpb0, Hpb1, Hpb2, Hpb3); +create_types!(Icm); +create_types!(Mclk); +create_types!(NvmCtrl, NvmCtrlSmeeProm, NvmCtrlCache); +#[cfg(feature = "min-samd51j")] +create_types!(I2S, I2S0, I2S1); +create_types!(OscCtrl); +create_types!(Osc32kCtrl); +create_types!(Pac); +create_types!(Pcc); +create_types!(PDec); +create_types!(Pm); +create_types!(Port); +create_types!(Pukcc); +create_types!(Qspi, Qspi2x); +create_types!(RamEcc); +create_types!(RstC); +create_types!(Rtc); +create_types!(Sdhc0); +#[cfg(feature = "min-samd51n")] +create_types!(Sdhc1); +create_types!(SlowClk); +create_types!(SupC); +create_types!(Tc0Tc1, Tc0, Tc1); +create_types!(Tc2Tc3, Tc2, Tc3); +#[cfg(feature = "min-samd51j")] +create_types!(Tc4Tc5, Tc4, Tc5); +#[cfg(feature = "min-samd51n")] +create_types!(Tc6Tc7, Tc6, Tc7); +create_types!(Tcc0Tcc1, Tcc0, Tcc1); +create_types!(Tcc2Tcc3, Tcc2); +#[cfg(feature = "min-samd51j")] +create_types!(Tcc3, Tcc4); +create_types!(Trng); +create_types!(Usb); +create_types!(Wdt); diff --git a/hal/src/thumbv7em/clock/v2/xosc.rs b/hal/src/thumbv7em/clock/v2/xosc.rs new file mode 100644 index 00000000000..1660db6956a --- /dev/null +++ b/hal/src/thumbv7em/clock/v2/xosc.rs @@ -0,0 +1,1024 @@ +//! # External multipurpose crystal oscillator controller +//! +//! ## Overview +//! +//! The `xosc` module provides access to the two external crystal oscillator +//! controllers (XOSCs) within the `OSCCTRL` peripheral. +//! +//! Each XOSC peripheral can operate in two [`Mode`]s. It can accept an external +//! clock or can interface with an crystal oscillator. In both cases, the clock +//! must be in the 8-48 MHz range. +//! +//! When used with an external clock, only one GPIO [`Pin`] is required, but +//! when used with a crystal oscillator, two GPIO `Pin`s are required. The +//! [`XIn`] `Pin` is used in both `Mode`s, while the [`XOut`] `Pin` is only +//! used in [`CrystalMode`]. +//! +//! When operating in [`CrystalMode`], the XOSC peripheral provides several +//! configuration options to increase stability or reduce power consumption of +//! the crystal. +//! +//! The XOSC peripheral can also detect failure of the clock or crystal; and if +//! failure occurs, it can automatically switch to a safe, backup clock derived +//! from the [DFLL]. +//! +//! Creating and configuring an [`Xosc`] proceeds according to the principles +//! outlined in the [`clock` module documentation]. It is best shown with an +//! example. +//! +//! ## Example +//! +//! Let's start by using [`clock_system_at_reset`] to access the HAL clocking +//! structs. We'll also need access to the GPIO [`Pins`]. +//! +//! ```no_run +//! use atsamd_hal::{ +//! clock::v2::{ +//! clock_system_at_reset, +//! xosc::{CrystalCurrent, SafeClockDiv, StartUpDelay, Xosc}, +//! }, +//! gpio::Pins, +//! pac::Peripherals, +//! time::U32Ext, +//! }; +//! let mut pac = Peripherals::take().unwrap(); +//! let pins = Pins::new(pac.PORT); +//! let (buses, clocks, tokens) = clock_system_at_reset( +//! pac.OSCCTRL, +//! pac.OSC32KCTRL, +//! pac.GCLK, +//! pac.MCLK, +//! &mut pac.NVMCTRL, +//! ); +//! ``` +//! +//! Next, we can create and configure the [`Xosc`] in one long chain of methods, +//! using the provided builder API. The final call to [`Xosc::enable`] yields an +//! [`EnabledXosc`] that can act as a clock [`Source`] for other clocks in the +//! tree. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # xosc::{CrystalCurrent, SafeClockDiv, StartUpDelay, Xosc}, +//! # }, +//! # gpio::Pins, +//! # pac::Peripherals, +//! # time::U32Ext, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let pins = Pins::new(pac.PORT); +//! # let (buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! let mut xosc = Xosc::from_crystal(tokens.xosc0, pins.pa14, pins.pa15, 20.mhz()) +//! .current(CrystalCurrent::Medium) +//! .loop_control(true) +//! .low_buf_gain(true) +//! .start_up_delay(StartUpDelay::Delay488us) +//! .enable(); +//! ``` +//! +//! We start by calling [`Xosc::from_crystal`], and we provide the corresponding +//! [`XIn`] and [`XOut`] [`Pin`]s, as well as the nominal crystal frequency. We +//! then set the [`CrystalCurrent`] level to `Medium`. The default current level +//! for a 20 MHz signal is actually `High`, but we opt for a lower current under +//! the assumption that our crystal's capacitive load is small. Next, we turn on +//! automatic loop control, which should save power, but we also set +//! `LOWBUFGAIN` to `1`. Counterintuitively, this actually _increases_ the +//! crystal amplitude, which increases power consumption, but it also improves +//! stability. We then apply a 488 μs start up delay, to allow the clock to +//! stabilize before it is applied to any logic. Finally, we enable the `Xosc`. +//! +//! Next, we wait until the `Xosc` is stable and ready to be used as a clock +//! [`Source`]. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # xosc::{CrystalCurrent, SafeClockDiv, StartUpDelay, Xosc}, +//! # }, +//! # gpio::Pins, +//! # pac::Peripherals, +//! # time::U32Ext, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let pins = Pins::new(pac.PORT); +//! # let (buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! # let mut xosc = Xosc::from_crystal(tokens.xosc0, pins.pa14, pins.pa15, 20.mhz()) +//! # .current(CrystalCurrent::Medium) +//! # .loop_control(true) +//! # .low_buf_gain(true) +//! # .start_up_delay(StartUpDelay::Delay488us) +//! # .enable(); +//! while !xosc.is_ready() {} +//! ``` +//! +//! Once the clock is stable, we can also enable failure detection. To do so, we +//! must provide the [`EnabledDfll`] to act as the backup safe clock. We can +//! also select a divider for the safe clock, so that it loosely matches the +//! `Xosc` frequency. In thise case, we divide the 48 MHz [`Dfll`] down to +//! 24 MHz, which is the closest option to 20 MHz. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # xosc::{CrystalCurrent, SafeClockDiv, StartUpDelay, Xosc}, +//! # }, +//! # gpio::Pins, +//! # pac::Peripherals, +//! # time::U32Ext, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let pins = Pins::new(pac.PORT); +//! # let (buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! # let mut xosc = Xosc::from_crystal(tokens.xosc0, pins.pa14, pins.pa15, 20.mhz()) +//! # .current(CrystalCurrent::Medium) +//! # .loop_control(true) +//! # .low_buf_gain(true) +//! # .start_up_delay(StartUpDelay::Delay488us) +//! # .enable(); +//! # while !xosc.is_ready() {} +//! xosc.enable_failure_detection(clocks.dfll, SafeClockDiv::Div2); +//! ``` +//! +//! In the event of a clock failure, the [`Xosc`] would be automatically +//! switched to the safe clock, and [`EnabledXosc::has_failed`] would return +//! true. If the problem were later resolved, the `Xosc` could be switched back +//! to the crystal with [`EnabledXosc::switch_back`]. +//! +//! The complete example is provided below. +//! +//! ```no_run +//! use atsamd_hal::{ +//! clock::v2::{ +//! clock_system_at_reset, +//! xosc::{CrystalCurrent, SafeClockDiv, StartUpDelay, Xosc}, +//! }, +//! gpio::Pins, +//! pac::Peripherals, +//! time::U32Ext, +//! }; +//! let mut pac = Peripherals::take().unwrap(); +//! let pins = Pins::new(pac.PORT); +//! let (buses, clocks, tokens) = clock_system_at_reset( +//! pac.OSCCTRL, +//! pac.OSC32KCTRL, +//! pac.GCLK, +//! pac.MCLK, +//! &mut pac.NVMCTRL, +//! ); +//! let mut xosc = Xosc::from_crystal(tokens.xosc0, pins.pa14, pins.pa15, 20.mhz()) +//! .current(CrystalCurrent::Medium) +//! .loop_control(true) +//! .low_buf_gain(true) +//! .start_up_delay(StartUpDelay::Delay488us) +//! .enable(); +//! while !xosc.is_ready() {} +//! xosc.enable_failure_detection(clocks.dfll, SafeClockDiv::Div2); +//! ``` +//! +//! [`Pins`]: crate::gpio::Pins +//! [`clock` module documentation]: super +//! [`clock_system_at_reset`]: super::clock_system_at_reset +//! [DFLL]: super::dfll +//! [`Dfll`]: super::dfll::Dfll +//! [`EnabledDfll`]: super::dfll::EnabledDfll + +use core::marker::PhantomData; + +use typenum::U0; + +use crate::pac::oscctrl::{self, XOSCCTRL}; + +use crate::gpio::{FloatingDisabled, Pin, PinId, PA14, PA15, PB22, PB23}; +use crate::time::Hertz; +use crate::typelevel::{Decrement, Increment, Sealed}; + +use super::dfll::DfllId; +use super::{Enabled, Source}; + +//============================================================================== +// XoscToken +//============================================================================== + +/// Singleton token that can be exchanged for an [`Xosc`] +/// +/// As explained in the [`clock` module documentation](super), instances of +/// various `Token` types can be exchanged for actual clock types. They +/// typically represent clocks that are disabled at power-on reset. +/// +/// [`XoscToken`]s are no different. Both [`Xosc`]s are disabled at power-on +/// reset. To use an [`Xosc`], you must first exchange the token for an actual +/// clock with [`Xosc::from_clock`] or [`Xosc::from_crystal`]. +/// +/// [`XoscToken`] is generic over the [`XoscId`], where each corresponding token +/// represents one of the two respective [`Xosc`]s. +pub struct XoscToken { + id: PhantomData, +} + +impl XoscToken { + /// Create a new instance of [`XoscToken`] + /// + /// # Safety + /// + /// Each `XoscToken`s is a singleton. There must never be two simulatenous + /// instances with the same [`XoscId`]. See the notes on `Token` types and + /// memory safety in the root of the `clock` module for more details. + #[inline] + pub(super) unsafe fn new() -> Self { + Self { id: PhantomData } + } + + /// Return a reference to the corresponding XOSCCTRL register + #[inline] + fn xoscctrl(&self) -> &XOSCCTRL { + // Safety: Each `XoscToken` only has access to a mutually exclusive set + // of registers for the corresponding `XoscId`, and we use a shared + // reference to the register block. See the notes on `Token` types and + // memory safety in the root of the `clock` module for more details. + unsafe { &(*crate::pac::OSCCTRL::PTR).xoscctrl[X::NUM] } + } + + /// Read the STATUS register + #[inline] + fn status(&self) -> oscctrl::status::R { + // Safety: We are only reading from the `STATUS` register, so there is + // no risk of memory corruption. + unsafe { (*crate::pac::OSCCTRL::PTR).status.read() } + } + + /// Check whether the XOSC is stable and ready + #[inline] + fn is_ready(&self) -> bool { + let mask = 1 << X::NUM; + self.status().bits() & mask != 0 + } + + /// Check whether the XOSC has triggered failure detection + #[inline] + fn has_failed(&self) -> bool { + let mask = 1 << (X::NUM + 2); + self.status().bits() & mask != 0 + } + + /// Check whether the XOSC has been switched to the safe clock + #[inline] + fn is_switched(&self) -> bool { + let mask = 1 << (X::NUM + 4); + self.status().bits() & mask != 0 + } + + /// Reset the XOSCCTRL register + #[inline] + fn reset(&self) { + self.xoscctrl().reset(); + } + + /// Switch from the safe clock back to the XOSC clock/oscillator + /// + /// This bit is cleared by the hardware after successfully switching back + #[inline] + fn switch_back(&mut self) { + self.xoscctrl().modify(|_, w| w.swben().set_bit()); + } + + /// Enable clock failure detection and set the safe clock divider + #[inline] + fn enable_failure_detection(&mut self, div: SafeClockDiv) { + // Safety: The divider is guaranteed to be in the valid range 0..16. + // The PAC is wrong here. It seems to think the field is 4 bits wide and + // the set of valid values is only 0..8. The `bits` method should really + // be safe here, just like it is for the `STARTUP` field. + self.xoscctrl().modify(|_, w| unsafe { + w.cfdpresc().bits(div as u8); + w.cfden().set_bit() + }); + } + + /// Disable clock failure detection + #[inline] + fn disable_failure_detection(&mut self) { + self.xoscctrl().modify(|_, w| w.cfden().clear_bit()); + } + + /// Set most of the fields in the XOSCCTRL register + #[inline] + fn set_xoscctrl(&mut self, settings: Settings) { + let xtalen = settings.mode == DynMode::CrystalMode; + // Safety: The `IMULT` and `IPTAT` values come from the + // `CrystalCurrent`, so they are guaranteed to be valid. + self.xoscctrl().modify(|_, w| unsafe { + w.startup().bits(settings.start_up as u8); + w.enalc().bit(settings.loop_control); + w.imult().bits(settings.current.imult()); + w.iptat().bits(settings.current.iptat()); + w.lowbufgain().bit(settings.low_buf_gain); + w.ondemand().bit(settings.on_demand); + w.runstdby().bit(settings.run_standby); + w.xtalen().bit(xtalen) + }); + } + + /// Enable the XOSC + #[inline] + fn enable(&mut self) { + self.xoscctrl().modify(|_, w| w.enable().set_bit()); + } + + /// Disable the XOSC + #[inline] + fn disable(&mut self) { + self.xoscctrl().modify(|_, w| w.enable().clear_bit()); + } +} + +//============================================================================== +// Settings +//============================================================================== + +// Collection of XOSCCTRL register fields +// +// All of these fields are set in a single write to XOSCCTRL during the call to +// [`Xosc::enable`]. The remaining fields are only modified after it has been +// enabled. +#[derive(Clone, Copy)] +struct Settings { + start_up: StartUpDelay, + loop_control: bool, + current: CrystalCurrent, + low_buf_gain: bool, + on_demand: bool, + run_standby: bool, + mode: DynMode, +} + +//============================================================================== +// XoscId +//============================================================================== + +/// Type-level enum identifying one of two possible [`Xosc`]s +/// +/// The types implementing this trait, i.e. [`Xosc0Id`] and [`Xosc1Id`], are +/// type-level variants of `XoscId`, and they identify one of two possible +/// external crystal oscillators. +/// +/// See the documentation on [type-level programming] and specifically +/// [type-level enums] for more details. +/// +/// [type-level programming]: crate::typelevel +/// [type-level enums]: crate::typelevel#type-level-enums +pub trait XoscId: Sealed { + /// Corresponding numeric index + const NUM: usize; + /// Corresponding XIN [`PinId`] + type XIn: PinId; + /// Corresponding XOUT [`PinId`] + type XOut: PinId; +} + +/// Type-level variant of [`XoscId`] representing the identity of XOSC0 +/// +/// See the documentation on [type-level programming] and specifically +/// [type-level enums] for more details. +/// +/// [type-level programming]: crate::typelevel +/// [type-level enums]: crate::typelevel#type-level-enums +pub enum Xosc0Id {} + +impl Sealed for Xosc0Id {} + +impl XoscId for Xosc0Id { + const NUM: usize = 0; + type XIn = PA14; + type XOut = PA15; +} + +/// Type-level variant of [`XoscId`] representing the identity of XOSC1 +/// +/// See the documentation on [type-level programming] and specifically +/// [type-level enums] for more details. +/// +/// [type-level programming]: crate::typelevel +/// [type-level enums]: crate::typelevel#type-level-enums +pub enum Xosc1Id {} + +impl Sealed for Xosc1Id {} + +impl XoscId for Xosc1Id { + const NUM: usize = 1; + type XIn = PB22; + type XOut = PB23; +} + +//============================================================================== +// XIn & XOut +//============================================================================== + +/// Type alias for the [`Xosc`] input [`Pin`] +pub type XIn = Pin<::XIn, FloatingDisabled>; + +/// Type alias for the [`Xosc`] output [`Pin`] +pub type XOut = Pin<::XOut, FloatingDisabled>; + +//============================================================================== +// SafeClockDiv +//============================================================================== + +/// Division factor for the safe clock prescaler +/// +/// If an [`Xosc`] clock failure is detected, the hardware will switch to a safe +/// clock derived from the [`Dfll`]. This enum sets the divider between the +/// 48 MHz DFLL and the safe clock frequency. The divider can be any value of +/// 2^N, with N in the range `0..16`. +/// +///[`Dfll`]: super::dfll::Dfll +#[repr(u8)] +#[derive(Clone, Copy, Default, PartialEq, Eq)] +pub enum SafeClockDiv { + #[default] + Div1, + Div2, + Div4, + Div8, + Div16, + Div32, + Div64, + Div128, + Div256, + Div512, + Div1024, + Div2048, + Div4096, + Div8192, + Div16384, + Div32768, +} + +//============================================================================== +// StartUpDelay +//============================================================================== + +/// Start up delay before continuous [`Xosc`] monitoring takes effect +/// +/// After a hard reset or waking from sleep, the [`Xosc`] output will remained +/// masked for the start up period, to ensure an unstable clock is not +/// propagated into the digital logic. +/// +/// The start up delay is counted using the [`OscUlp32k`] clock, and the delay +/// is equal to 2^N clock cycles, where N is selectable in the range `0..16`. +/// +/// [`OscUlp32k`]: super::osculp32k::OscUlp32k +#[repr(u8)] +#[derive(Clone, Copy, Default, PartialEq, Eq)] +pub enum StartUpDelay { + #[default] + Delay31us, + Delay62us, + Delay122us, + Delay244us, + Delay488us, + Delay977us, + Delay2ms, + Delay4ms, + Delay8ms, + Delay16ms, + Delay31ms, + Delay63ms, + Delay125ms, + Delay250ms, + Delay500ms, + Delay1s, +} + +//============================================================================== +// CrystalCurrent +//============================================================================== + +/// Crystal current level +/// +/// This struct represents an abstraction over the datasheet table for the +/// `IMULT` and `IPTAT` register fields, which control the current used when an +/// [`Xosc`] is in [`CrystalMode`] +/// +/// The variants of this enum are not named according to the explicit frequency +/// range provided in the datasheet. While the datasheet recommends settings for +/// each frequency range, it also acknowledges some flexibility in that choice. +/// Specifically, it notes that users can save power by selecting the next-lower +/// frequency range if the capacitive load is small. +#[derive(Clone, Copy, Default, PartialEq, Eq)] +pub enum CrystalCurrent { + /// Used only in [`ClockMode`] to represent the default register values + #[default] + Zero, + /// Typically used for 8 MHz oscillators + Low, + /// Typically used for 8-16 MHz oscillators + Medium, + /// Typically used for 16-24 MHz oscillators + High, + /// Typically used for 24-48 MHz oscillators + ExtraHigh, +} + +impl CrystalCurrent { + #[inline] + fn imult(&self) -> u8 { + match self { + Self::Zero => 0, + Self::Low => 3, + Self::Medium => 4, + Self::High => 5, + Self::ExtraHigh => 6, + } + } + + #[inline] + fn iptat(&self) -> u8 { + match self { + Self::Zero => 0, + Self::Low => 2, + Self::Medium => 3, + Self::High => 3, + Self::ExtraHigh => 3, + } + } +} + +//============================================================================== +// DynMode +//============================================================================== + +/// Value-level enum identifying one of two possible [`Xosc`] operating modes +/// +/// An [`Xosc`] can be sourced from either an external clock or crystal +/// oscillator. The variants of this enum identify one of these two possible +/// operating modes. +/// +/// `DynMode` is the value-level equivalent of [`Mode`]. +#[derive(Clone, Copy, Default, PartialEq, Eq)] +pub enum DynMode { + #[default] + ClockMode, + CrystalMode, +} + +//============================================================================== +// Mode +//============================================================================== + +/// Type-level `enum` for the [`Xosc`] operating mode +/// +/// An [`Xosc`] can be sourced from either an external clock or a cyrstal +/// oscillator. This type-level `enum` provides two type-level variants, +/// [`ClockMode`] and [`CrystalMode`], representing these operating modes. +/// +/// `Mode` is the type-level equivalent of [`DynMode`]. See the documentation on +/// [type-level programming] and specifically [type-level enums] for more +/// details. +/// +/// [type-level programming]: crate::typelevel +/// [type-level enums]: crate::typelevel#type-level-enums +pub trait Mode: Sealed { + /// Corresponding variant of [`DynMode`] + const DYN: DynMode; + #[doc(hidden)] + type Pins; +} + +//============================================================================== +// ClockMode +//============================================================================== + +/// Type-level variant of the [`Xosc`] operating [`Mode`] +/// +/// Represents the [`Xosc`] configured to use an externally provided clock. +/// +/// See the documentation on [type-level programming] and specifically +/// [type-level enums] for more details. +/// +/// [type-level programming]: crate::typelevel +/// [type-level enums]: crate::typelevel#type-level-enums +pub enum ClockMode {} + +impl Sealed for ClockMode {} + +impl Mode for ClockMode { + const DYN: DynMode = DynMode::ClockMode; + type Pins = XIn; +} + +//============================================================================== +// CrystalMode +//============================================================================== + +/// Type-level variant of the [`Xosc`] operating [`Mode`] +/// +/// Represents the [`Xosc`] configured to use an external crystal oscillator. +/// +/// See the documentation on [type-level programming] and specifically +/// [type-level enums] for more details. +/// +/// [type-level programming]: crate::typelevel +/// [type-level enums]: crate::typelevel#type-level-enums +pub enum CrystalMode {} + +impl Sealed for CrystalMode {} + +impl Mode for CrystalMode { + const DYN: DynMode = DynMode::CrystalMode; + type Pins = (XIn, XOut); +} + +//============================================================================== +// Xosc +//============================================================================== + +/// An external multipurpose crystal oscillator controller +/// +/// An `Xosc` interfaces with either an external clock or external crystal +/// oscillator and delivers the resulting clock to the rest of the clock system. +/// +/// The type parameter `X` is a [`XoscId`] that determines which of the two +/// instances this `Xosc` represents ([`Xosc0`] or [`Xosc1`]). The type +/// parameter `M` represents the operating [`Mode`], either [`ClockMode`] or +/// [`CrystalMode`]. +/// +/// On its own, an instance of `Xosc` does not represent an enabled XOSC. +/// Instead, it must first be wrapped with [`Enabled`], which implements +/// compile-time safety of the clock tree. +/// +/// Because the terminal call to [`enable`] consumes the `Xosc` and returns an +/// [`EnabledXosc`], the remaining API uses the builder pattern, where each +/// method takes and returns `self` by value, allowing them to be easily +/// chained. +/// +/// See the [module-level documentation](self) for an example of creating, +/// configuring and using an `Xosc`. +/// +/// [`enable`]: Xosc::enable +pub struct Xosc +where + X: XoscId, + M: Mode, +{ + token: XoscToken, + pins: M::Pins, + freq: Hertz, + settings: Settings, +} + +/// Type alias for the corresponding [`Xosc`] +pub type Xosc0 = Xosc; + +/// Type alias for the corresponding [`Xosc`] +pub type Xosc1 = Xosc; + +/// An [`Enabled`] [`Xosc`] +/// +/// As described in the [`clock` module documentation](super), the [`Enabled`] +/// wrapper implements compile-time clock tree safety by tracking the number of +/// consumer clocks and restricting access to the underlying [`Xosc`] to prevent +/// modification while in use. +/// +/// As with [`Enabled`], the default value for `N` is `U0`; if left unspecified, +/// the counter is assumed to be zero. +pub type EnabledXosc = Enabled, N>; + +/// Type alias for the corresponding [`EnabledXosc`] +pub type EnabledXosc0 = EnabledXosc; + +/// Type alias for the corresponding [`EnabledXosc`] +pub type EnabledXosc1 = EnabledXosc; + +impl Xosc { + /// Create an [`Xosc`] from an external clock, taking ownership of the + /// [`XIn`] [`Pin`] + /// + /// Creating a [`Xosc`] does not modify any of the hardware registers. It + /// only creates a struct to track the configuration. The configuration data + /// is stored until the user calls [`enable`]. At that point, all of the + /// registers are written according to the initialization procedures + /// specified in the datasheet, and an [`EnabledXosc`] is returned. The + /// `Xosc` is not active or useful until that point. + /// + /// [`enable`]: Xosc::enable + #[inline] + pub fn from_clock(token: XoscToken, xin: impl Into>, freq: impl Into) -> Self { + let pins = xin.into(); + Xosc::new(token, pins, freq.into()) + } + + /// Consume the [`Xosc`] and release the [`XoscToken`] and [`XIn`] [`Pin`] + #[inline] + pub fn free(self) -> (XoscToken, XIn) { + (self.token, self.pins) + } +} + +impl Xosc { + /// Create an [`Xosc`] from an external crystal oscillator, taking ownership + /// of the [`XIn`] and [`XOut`] [`Pin`]s. + /// + /// Creating a [`Xosc`] does not modify any of the hardware registers. It + /// only creates a struct to track the configuration. The configuration data + /// is stored until the user calls [`enable`]. At that point, all of the + /// registers are written according to the initialization procedures + /// specified in the datasheet, and an [`EnabledXosc`] is returned. The + /// `Xosc` is not active or useful until that point. + /// + /// [`enable`]: Xosc::enable + #[inline] + pub fn from_crystal( + token: XoscToken, + xin: impl Into>, + xout: impl Into>, + freq: impl Into, + ) -> Self { + let pins = (xin.into(), xout.into()); + Xosc::new(token, pins, freq.into()) + } + + /// Consume the [`Xosc`] and release the [`XoscToken`], [`XIn`] and [`XOut`] + /// [`Pin`]s + #[inline] + pub fn free(self) -> (XoscToken, XIn, XOut) { + let (xin, xout) = self.pins; + (self.token, xin, xout) + } + + /// Set the [`CrystalCurrent`] drive strength + #[inline] + pub fn current(mut self, current: CrystalCurrent) -> Self { + self.settings.current = current; + self + } + + /// Toggle automatic loop control + /// + /// If enabled, the hardware will automatically adjust the oscillator + /// amplitude. In most cases, this will lower power consumption. + #[inline] + pub fn loop_control(mut self, loop_control: bool) -> Self { + self.settings.loop_control = loop_control; + self + } + + /// Modify the oscillator amplitude when automatic loop control is enabled + /// + /// The datasheet name for this setting is very misleading. When automatic + /// loop control is enabled, setting the `LOWBUFGAIN` field to `1` will + /// _increase_ the oscillator amplitude by a factor of appoximately 2. This + /// can help solve stability issues. + #[inline] + pub fn low_buf_gain(mut self, low_buf_gain: bool) -> Self { + self.settings.low_buf_gain = low_buf_gain; + self + } +} + +impl Xosc +where + X: XoscId, + M: Mode, +{ + #[inline] + fn new(token: XoscToken, pins: M::Pins, freq: Hertz) -> Self { + let current = match freq.0 { + 8_000_000 => CrystalCurrent::Low, + 8_000_001..=16_000_000 => CrystalCurrent::Medium, + 16_000_001..=24_000_000 => CrystalCurrent::High, + 24_000_001..=48_000_000 => CrystalCurrent::ExtraHigh, + _ => panic!("The XOSC input frequency must be 8-48 MHz"), + }; + let current = match M::DYN { + DynMode::ClockMode => CrystalCurrent::Zero, + DynMode::CrystalMode => current, + }; + let settings = Settings { + start_up: StartUpDelay::Delay31us, + loop_control: false, + current, + low_buf_gain: false, + on_demand: true, + run_standby: false, + mode: M::DYN, + }; + Self { + token, + pins, + freq, + settings, + } + } + + /// Return the clock or crystal frequency + #[inline] + pub fn freq(&self) -> Hertz { + self.freq + } + + /// Set the start up delay before the [`Xosc`] is unmasked and continuously + /// monitored + /// + /// During the start up period, the [`Xosc`] is masked to prevent clock + /// instability from propagating to the digital logic. During this time, + /// clock failure detection is disabled. + #[inline] + pub fn start_up_delay(mut self, delay: StartUpDelay) -> Self { + self.settings.start_up = delay; + self + } + + /// Control the [`Xosc`] on-demand behavior + /// + /// When the on-demand is enabled, the [`Xosc`] will only run in Idle or + /// Standby sleep modes if it is requested by a peripheral. Otherwise, its + /// behavior is dependent on the run-standby setting. + #[inline] + pub fn on_demand(mut self, on_demand: bool) -> Self { + self.settings.on_demand = on_demand; + self + } + + /// Control the [`Xosc`] behavior in Standby sleep mode + /// + /// When `RUNSTDBY` is disabled, the [`Xosc`] will never run in Standby + /// sleep mode unless `ONDEMAND` is enabled and the `Xosc` is requested by a + /// peripheral. When `RUNSTDBY` is enabled, the `Xosc` will run in Standby + /// sleep mode, but it can still be disabled if `ONDEMAND` is enabled and + /// the `Xosc` is not requested. + #[inline] + pub fn run_standby(mut self, run_standby: bool) -> Self { + self.settings.run_standby = run_standby; + self + } + + /// Enable the [`Xosc`], so that it can be used as a clock [`Source`] + /// + /// As mentioned when creating a new `Xosc`, no hardware registers are + /// actually modified until this call. Rather, the desired configuration is + /// stored internally, and the `Xosc` is initialized and configured here + /// according to the datasheet. + /// + /// The returned value is an [`EnabledXosc`] that can be used as a clock + /// [`Source`] for other clocks. + #[inline] + pub fn enable(mut self) -> EnabledXosc { + self.token.reset(); + self.token.set_xoscctrl(self.settings); + self.token.enable(); + Enabled::new(self) + } +} + +impl EnabledXosc +where + X: XoscId, + M: Mode, +{ + /// Disable the [`Xosc`] + /// + /// This method is only implemented for `N = U0`, which means the clock can + /// only be disabled when no other clocks consume this [`Xosc`]. + #[inline] + pub fn disable(mut self) -> Xosc { + self.0.token.disable(); + self.0 + } +} + +impl EnabledXosc +where + X: XoscId, + M: Mode, +{ + /// Check whether the [`Xosc`] is stable and ready to be used as a clock + /// [`Source`] + #[inline] + pub fn is_ready(&self) -> bool { + self.0.token.is_ready() + } + + /// Enable continuous monitoring of the [`Xosc`] for clock failure + /// + /// Failure detection will continuously monitor the [`Xosc`] to verify it is + /// still running. In the event of a failure, the `Xosc` output will be + /// switched to the "safe clock". + /// + /// The safe clock is derived from the DFLL, which runs at 48 MHz. The XOSC + /// peripheral provides a prescaler to divide down the 48 MHz DFLL to better + /// match the clock it replaces. The prescaler division factor can be any + /// power of two, `2^N`, with `N` in the range `0..16`. + /// + /// For example, if the [`Xosc`] input frequency is 16 MHz, a reasonable + /// divider would be 4, becuase the safe clock frequency would be 12 MHz, + /// which is closest to 16 MHz. + /// + /// Note that clock failure is triggered when four safe clock periods pass + /// without seeing a rising & falling edge pair on the XOSC clock. Once + /// failure is detected, the corresponding bit in the `STATUS` register will + /// go high and an interrupt will be triggered. + /// + /// If the external clock can be fixed, the `Xosc` can be switched back to + /// it using [`EnabledXosc::switch_back`]. + /// + /// Because the safe clock makes use of the DFLL, the `Xosc` must register + /// as a consumer of the [`EnabledDfll`] and [`Increment`] its counter. + /// + /// [`EnabledDfll`]: super::dfll::EnabledDfll + #[inline] + pub fn enable_failure_detection(&mut self, dfll: S, div: SafeClockDiv) -> S::Inc + where + S: Source + Increment, + { + self.0.token.enable_failure_detection(div); + dfll.inc() + } + + /// Check whether the [`Xosc`] has triggered clock failure detection + /// + /// Failure detection must be enabled for this to return `true`. Failure is + /// triggered when four safe clock periods pass without seeing a rising & + /// falling edge pair on the XOSC clock. + /// + /// See [`EnabledXosc::enable_failure_detection`] for more details. + #[inline] + pub fn has_failed(&self) -> bool { + self.0.token.has_failed() + } + + /// Check whether the [`Xosc`] has been switched to the safe clock + /// + /// Returns `true` if the [`Xosc`] has been switched to the safe clock. This + /// will only occur if clock failure detection is enabled. + #[inline] + pub fn is_switched(&self) -> bool { + self.0.token.is_switched() + } + + /// Attempt to switch from the safe clock back to the external clock + /// + /// This function will set the switch back bit (`SWBEN`) in the `XOSCCTRL` + /// register. Once the hardware has successfully switched back, this bit + /// will be automatically cleared. + /// + /// Users can check whether switching back was successful by checking the + /// `STATUS` register with [`EnabledXosc::is_switched`]. + #[inline] + pub fn switch_back(&mut self) { + self.0.token.switch_back() + } + + /// Disable continuous monitoring of the [`Xosc`] for clock failure + /// + /// Once failure monitoring is disabled, the DFLL is no longer used as the + /// safe clock, so the [`EnabledDfll`] counter can be [`Decrement`]ed. + /// + /// [`EnabledDfll`]: super::dfll::EnabledDfll + #[inline] + pub fn disable_failure_detection(&mut self, dfll: S) -> S::Dec + where + S: Source + Decrement, + { + self.0.token.disable_failure_detection(); + dfll.dec() + } +} + +//============================================================================== +// Source +//============================================================================== + +impl Source for EnabledXosc +where + X: XoscId, + M: Mode, +{ + type Id = X; + + #[inline] + fn freq(&self) -> Hertz { + self.0.freq() + } +} diff --git a/hal/src/thumbv7em/clock/v2/xosc32k.rs b/hal/src/thumbv7em/clock/v2/xosc32k.rs new file mode 100644 index 00000000000..dccd9e1af74 --- /dev/null +++ b/hal/src/thumbv7em/clock/v2/xosc32k.rs @@ -0,0 +1,1231 @@ +//! # External, 32 kHz crystal oscillator controller +//! +//! ## Overview +//! +//! The `xosc32k` module provides access to the 32 kHz external crystal +//! oscillator controller (XOSC32K) within the `OSC32KCTRL` peripheral. +//! +//! The peripheral can operate in two [`Mode`]s. It can accept an external +//! clock, or it can interface with an crystal oscillator. In both cases, the +//! clock must be 32,768 Hz. +//! +//! When used with an external clock, only one GPIO [`Pin`] is required, but +//! when used with a crystal oscillator, two GPIO `Pin`s are required. The +//! [`XIn32`] `Pin` is used in both `Mode`s, while the [`XOut32`] `Pin` is only +//! used in [`CrystalMode`]. +//! +//! ## Clock tree structure +//! +//! The `XOSC32K` clock is unlike most other clocks, because it has two separate +//! outputs, one at 32 kHz and another divided down to 1 kHz. Moreover, none, +//! either or both of these outputs can be enabled at any given time. +//! +//! We can see, then, that the `XOSC32K` peripheral forms its own, miniature +//! clock tree. There is a 1:N producer clock that must be enabled first; and +//! there are two possible consumer clocks that can be independently and +//! optionally enabled. In fact, this structure is illustrated by the `XOSC32K` +//! register, which has three different enable bits: `ENABLE`, `EN32K` and +//! `EN1K`. +//! +//! To represent this structure in the type system, we divide the `XOSC32K` +//! peripheral into these three clocks. Users start by enabling the +//! [`Xosc32kBase`] clock, which corresponds to setting the `XOSC32K` register +//! `ENABLE` bit. The call to [`Xosc32kBase::enable`] returns a 1:N [`Enabled`] +//! clock [`Source`], which can be consumed by both the [`Xosc32k`] and +//! [`Xosc1k`] clocks. Enabling either of these two clocks will [`Increment`] +//! the [`EnabledXosc32kBase`] counter, preventing it from being disabled. +//! Note that `Xosc32k` and `Xosc1k` are themselves 1:N clocks as well. +//! +//! ## Clock failure detection and write lock +//! +//! Like the [`Xosc`] clocks, the XOSC32K peripheral also has clock failure +//! detection. However, unlike the `XOSCCTRL` registers, the `XOSC32K` register +//! has a dedicated write lock bit that will freeze its configuration until the +//! next power-on reset. +//! +//! While `Xosc` clock failure detection is configured directly in the +//! `XOSCCTRL` register, the XOSC32K peripheral has a separate, dedicated +//! clock failure detection register (`CFDCTRL`). This difference likely exists +//! to provide control of clock failure detection *after* write lock has been +//! enabled. +//! +//! In this module, write lock is implemented by simply dropping the +//! [`Xosc32kBase`] clock, which prevents any further access to the `XOSC32K` +//! register. Thus, to allow control of clock failure detection in the presence +//! of write lock, we provide a dedicated [`Xosc32kCfd`] interface, which has +//! exclusive control over the `CFDCTRL` register. +//! +//! ## Example +//! +//! Creating and configuring the XOSC32K clocks proceeds according to the +//! principles outlined in the [`clock` module documentation]. It is best shown +//! with an example. +//! +//! Let's start by using [`clock_system_at_reset`] to access the HAL clocking +//! structs. We'll also need access to the GPIO [`Pins`]. +//! +//! ```no_run +//! use atsamd_hal::{ +//! clock::v2::{ +//! clock_system_at_reset, +//! osculp32k::OscUlp32k, +//! xosc32k::{ +//! ControlGainMode, SafeClockDiv, StartUpDelay, Xosc1k, Xosc32k, Xosc32kBase, +//! Xosc32kCfd, +//! }, +//! }, +//! gpio::Pins, +//! pac::Peripherals, +//! }; +//! let mut pac = Peripherals::take().unwrap(); +//! let pins = Pins::new(pac.PORT); +//! let (buses, clocks, tokens) = clock_system_at_reset( +//! pac.OSCCTRL, +//! pac.OSC32KCTRL, +//! pac.GCLK, +//! pac.MCLK, +//! &mut pac.NVMCTRL, +//! ); +//! ``` +//! +//! Next, we create the [`Xosc32kBase`] clock from a 32 kHz oscillator using its +//! corresponding [`Xosc32kBaseToken`] and the [`XIn32`] and [`XOut32`] `Pin`s. +//! We then set the delay before the clock is unmasked by providing a desired +//! [`StartUpDelay`]. Finally, we select a [`ControlGainMode`] for the crystal +//! before enabling it. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # osculp32k::OscUlp32k, +//! # xosc32k::{ +//! # ControlGainMode, SafeClockDiv, StartUpDelay, Xosc1k, Xosc32k, Xosc32kBase, +//! # Xosc32kCfd, +//! # }, +//! # }, +//! # gpio::Pins, +//! # pac::Peripherals, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let pins = Pins::new(pac.PORT); +//! # let (buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! let xosc32k_base = Xosc32kBase::from_crystal(tokens.xosc32k.base, pins.pa00, pins.pa01) +//! .start_up_delay(StartUpDelay::Delay1s) +//! .control_gain_mode(ControlGainMode::HighSpeed) +//! .enable(); +//! ``` +//! +//! At this point, we opt to wait until the `Xosc32kBase` oscillator `is_ready` +//! and stable. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # osculp32k::OscUlp32k, +//! # xosc32k::{ +//! # ControlGainMode, SafeClockDiv, StartUpDelay, Xosc1k, Xosc32k, Xosc32kBase, +//! # Xosc32kCfd, +//! # }, +//! # }, +//! # gpio::Pins, +//! # pac::Peripherals, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let pins = Pins::new(pac.PORT); +//! # let (buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! # let xosc32k_base = Xosc32kBase::from_crystal(tokens.xosc32k.base, pins.pa00, pins.pa01) +//! # .start_up_delay(StartUpDelay::Delay1s) +//! # .control_gain_mode(ControlGainMode::HighSpeed) +//! # .enable(); +//! while !xosc32k_base.is_ready() {} +//! ``` +//! +//! With the [`EnabledXosc32kBase`] clock in hand, we can enable the [`Xosc1k`] +//! and [`Xosc32k`], each of which [`Increment`]s the [`Enabled`] counter. +//! Once we are satisfied with the configuration, we can call `write_lock` to +//! lock the XOSC32K configuration at the hardware level. Doing so also consumes +//! the `EnabledXosc32kBase` clock, which eliminates any ability to change the +//! configuration at the API level. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # osculp32k::OscUlp32k, +//! # xosc32k::{ +//! # ControlGainMode, SafeClockDiv, StartUpDelay, Xosc1k, Xosc32k, Xosc32kBase, +//! # Xosc32kCfd, +//! # }, +//! # }, +//! # gpio::Pins, +//! # pac::Peripherals, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let pins = Pins::new(pac.PORT); +//! # let (buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! # let xosc32k_base = Xosc32kBase::from_crystal(tokens.xosc32k.base, pins.pa00, pins.pa01) +//! # .start_up_delay(StartUpDelay::Delay1s) +//! # .control_gain_mode(ControlGainMode::HighSpeed) +//! # .enable(); +//! # while !xosc32k_base.is_ready() {} +//! let (xosc1k, xosc32k_base) = Xosc1k::enable(tokens.xosc32k.xosc1k, xosc32k_base); +//! let (xosc32k, xosc32k_base) = Xosc32k::enable(tokens.xosc32k.xosc32k, xosc32k_base); +//! xosc32k_base.write_lock(); +//! ``` +//! +//! However, while we have locked the XOSC32K configuration, we still want to +//! enable clock failure detection, which will continuously monitor the clock +//! and switch to a safe, backup clock if necessary. +//! +//! To do so, we must first enable the backup clock, which, for the XOSC32K, is +//! the [`OscUlp32k`]. The OSCULP32K peripheral has a nearly identical structure +//! to the XOSC32K; we create an [`EnabledOscUlp32k`] from the +//! [`EnabledOscUlp32kBase`] clock and the corresponding [`OscUlp32kToken`]. +//! +//! Upon creation of the [`Xosc32kCfd`] struct, we register it as a consumer of +//! the `EnabledOscUlp32k`, which will `Increment` its `Counter` as well. When +//! creating the safe clock, the `OscUlp32k` can be optionally divided by two, +//! which is selected with [`SafeClockDiv`]. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # osculp32k::OscUlp32k, +//! # xosc32k::{ +//! # ControlGainMode, SafeClockDiv, StartUpDelay, Xosc1k, Xosc32k, Xosc32kBase, +//! # Xosc32kCfd, +//! # }, +//! # }, +//! # gpio::Pins, +//! # pac::Peripherals, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let pins = Pins::new(pac.PORT); +//! # let (buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! # let xosc32k_base = Xosc32kBase::from_crystal(tokens.xosc32k.base, pins.pa00, pins.pa01) +//! # .start_up_delay(StartUpDelay::Delay1s) +//! # .control_gain_mode(ControlGainMode::HighSpeed) +//! # .enable(); +//! # while !xosc32k_base.is_ready() {} +//! # let (xosc1k, xosc32k_base) = Xosc1k::enable(tokens.xosc32k.xosc1k, xosc32k_base); +//! # let (xosc32k, xosc32k_base) = Xosc32k::enable(tokens.xosc32k.xosc32k, xosc32k_base); +//! # xosc32k_base.write_lock(); +//! let (osculp32k, osculp32k_base) = +//! OscUlp32k::enable(tokens.osculp32k.osculp32k, clocks.osculp32k_base); +//! let (mut cfd, osculp32k) = +//! Xosc32kCfd::enable(tokens.xosc32k.cfd, osculp32k, SafeClockDiv::Div1); +//! ``` +//! +//! Finally, with the clock failure detection interface in hand, we can do +//! things like check if the XOSC32K [`has_failed`] or if it [`is_switched`] to +//! the safe clock. If we are able to recover from a clock failure, we can even +//! [`switch_back`] to the crystal oscillator. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # osculp32k::OscUlp32k, +//! # xosc32k::{ +//! # ControlGainMode, SafeClockDiv, StartUpDelay, Xosc1k, Xosc32k, Xosc32kBase, +//! # Xosc32kCfd, +//! # }, +//! # }, +//! # gpio::Pins, +//! # pac::Peripherals, +//! # }; +//! # let mut pac = Peripherals::take().unwrap(); +//! # let pins = Pins::new(pac.PORT); +//! # let (buses, clocks, tokens) = clock_system_at_reset( +//! # pac.OSCCTRL, +//! # pac.OSC32KCTRL, +//! # pac.GCLK, +//! # pac.MCLK, +//! # &mut pac.NVMCTRL, +//! # ); +//! # let xosc32k_base = Xosc32kBase::from_crystal(tokens.xosc32k.base, pins.pa00, pins.pa01) +//! # .start_up_delay(StartUpDelay::Delay1s) +//! # .control_gain_mode(ControlGainMode::HighSpeed) +//! # .enable(); +//! # while !xosc32k_base.is_ready() {} +//! # let (xosc1k, xosc32k_base) = Xosc1k::enable(tokens.xosc32k.xosc1k, xosc32k_base); +//! # let (xosc32k, xosc32k_base) = Xosc32k::enable(tokens.xosc32k.xosc32k, xosc32k_base); +//! # xosc32k_base.write_lock(); +//! # let (osculp32k, osculp32k_base) = +//! # OscUlp32k::enable(tokens.osculp32k.osculp32k, clocks.osculp32k_base); +//! # let (mut cfd, osculp32k) = +//! # Xosc32kCfd::enable(tokens.xosc32k.cfd, osculp32k, SafeClockDiv::Div1); +//! if cfd.has_failed() && cfd.is_switched() { +//! cfd.switch_back(); +//! } +//! ``` +//! +//! The complete example is provided below. +//! +//! ```no_run +//! use atsamd_hal::{ +//! clock::v2::{ +//! clock_system_at_reset, +//! osculp32k::OscUlp32k, +//! xosc32k::{ +//! ControlGainMode, SafeClockDiv, StartUpDelay, Xosc1k, Xosc32k, Xosc32kBase, +//! Xosc32kCfd, +//! }, +//! }, +//! gpio::Pins, +//! pac::Peripherals, +//! }; +//! let mut pac = Peripherals::take().unwrap(); +//! let pins = Pins::new(pac.PORT); +//! let (buses, clocks, tokens) = clock_system_at_reset( +//! pac.OSCCTRL, +//! pac.OSC32KCTRL, +//! pac.GCLK, +//! pac.MCLK, +//! &mut pac.NVMCTRL, +//! ); +//! let xosc32k_base = Xosc32kBase::from_crystal(tokens.xosc32k.base, pins.pa00, pins.pa01) +//! .start_up_delay(StartUpDelay::Delay1s) +//! .control_gain_mode(ControlGainMode::HighSpeed) +//! .enable(); +//! while !xosc32k_base.is_ready() {} +//! let (xosc1k, xosc32k_base) = Xosc1k::enable(tokens.xosc32k.xosc1k, xosc32k_base); +//! let (xosc32k, xosc32k_base) = Xosc32k::enable(tokens.xosc32k.xosc32k, xosc32k_base); +//! xosc32k_base.write_lock(); +//! let (osculp32k, osculp32k_base) = +//! OscUlp32k::enable(tokens.osculp32k.osculp32k, clocks.osculp32k_base); +//! let (mut cfd, osculp32k) = +//! Xosc32kCfd::enable(tokens.xosc32k.cfd, osculp32k, SafeClockDiv::Div1); +//! if cfd.has_failed() && cfd.is_switched() { +//! cfd.switch_back(); +//! } +//! ``` +//! +//! [`clock` module documentation]: super +//! [`Pins`]: crate::gpio::Pins +//! [`clock_system_at_reset`]: super::clock_system_at_reset +//! [`Xosc`]: super::xosc::Xosc +//! [`OscUlp32k`]: super::osculp32k::OscUlp32k +//! [`EnabledOscUlp32k`]: super::osculp32k::EnabledOscUlp32k +//! [`OscUlp32kToken`]: super::osculp32k::OscUlp32kToken +//! [`EnabledOscUlp32kBase`]: super::osculp32k::EnabledOscUlp32kBase +//! [`OscUlp32k`]: super::osculp32k::OscUlp32k +//! [`has_failed`]: Xosc32kCfd::has_failed +//! [`is_switched`]: Xosc32kCfd::is_switched +//! [`switch_back`]: Xosc32kCfd::switch_back + +use typenum::U0; + +use crate::pac::osc32kctrl::xosc32k::{CGM_A, STARTUP_A}; +use crate::pac::osc32kctrl::{status, CFDCTRL, XOSC32K}; + +use crate::gpio::{FloatingDisabled, Pin, PA00, PA01}; +use crate::time::Hertz; +use crate::typelevel::{Decrement, Increment, PrivateDecrement, PrivateIncrement, Sealed}; + +use super::osculp32k::OscUlp32kId; +use super::{Enabled, Source}; + +//============================================================================== +// Tokens +//============================================================================== + +/// Singleton token that can be exchanged for [`Xosc32kBase`] +/// +/// As explained in the [`clock` module documentation](super), instances of +/// various `Token` types can be exchanged for actual clock types. They +/// typically represent clocks that are disabled at power-on reset. +/// +/// The [`Xosc32kBase`] clock is disabled at power-on reset. To use it, you must +/// first exchange the token for an actual clock with +/// [`Xosc32kBase::from_clock`] or [`Xosc32kBase::from_crystal`]. +pub struct Xosc32kBaseToken(()); + +/// Singleton token that can be exchanged for [`Xosc1k`] +/// +/// As explained in the [`clock` module documentation](super), instances of +/// various `Token` types can be exchanged for actual clock types. They +/// typically represent clocks that are disabled at power-on reset. +/// +/// The [`Xosc1k`] clock is disabled at power-on reset. To use it, you must +/// first exchange the token for an actual clock with [`Xosc1k::enable`]. +pub struct Xosc1kToken(()); + +/// Singleton token that can be exchanged for [`Xosc32k`] +/// +/// As explained in the [`clock` module documentation](super), instances of +/// various `Token` types can be exchanged for actual clock types. They +/// typically represent clocks that are disabled at power-on reset. +/// +/// The [`Xosc32k`] clock is disabled at power-on reset. To use it, you must +/// first exchange the token for an actual clock with [`Xosc32k::enable`]. +pub struct Xosc32kToken(()); + +/// Singleton token that can be exchanged for [`Xosc32kCfd`] +/// +/// As explained in the [module-level documentation](self), clock failure +/// detection can be used even after the `XOSC32K` register has been write +/// locked. For that reason, users control clock failure detection through the +/// dedicated [`Xosc32kCfd`] type. +/// +/// Clock failure detection is disabled at power-on reset. To use it, you must +/// first enable it by exchanging the token with [`Xosc32kCfd::enable`]. +pub struct Xosc32kCfdToken(()); + +/// Set of tokens representing the disabled XOSC32K clocks power-on reset +pub struct Xosc32kTokens { + pub base: Xosc32kBaseToken, + pub xosc1k: Xosc1kToken, + pub xosc32k: Xosc32kToken, + pub cfd: Xosc32kCfdToken, +} + +impl Xosc32kTokens { + /// Create the set of tokens + /// + /// # Safety + /// + /// There must never be more than one instance of these tokens at any given + /// time. See the notes on `Token` types and memory safety in the root of + /// the `clock` module for more details. + pub(super) unsafe fn new() -> Self { + Self { + base: Xosc32kBaseToken(()), + xosc1k: Xosc1kToken(()), + xosc32k: Xosc32kToken(()), + cfd: Xosc32kCfdToken(()), + } + } +} + +impl Xosc32kBaseToken { + #[inline] + fn status(&self) -> status::R { + // Safety: We are only reading from the `STATUS` register, so there is + // no risk of memory corruption. + unsafe { (*crate::pac::OSC32KCTRL::PTR).status.read() } + } + + /// Check whether the XOSC32K is stable and ready + #[inline] + fn is_ready(&self) -> bool { + self.status().xosc32krdy().bit() + } + + #[inline] + fn xosc32k(&self) -> &XOSC32K { + // Safety: The `Xosc32kBaseToken` has exclusive access to the `XOSC32K` + // register. See the notes on `Token` types and memory safety in the + // root of the `clock` module for more details. + unsafe { &(*crate::pac::OSC32KCTRL::PTR).xosc32k } + } + + /// Reset the XOSC32K register + #[inline] + fn reset(&mut self) { + self.xosc32k().reset(); + } + + /// Set most of the fields in the XOSC32K register + #[inline] + fn set_xosc32k(&mut self, settings: Settings) { + let xtalen = settings.mode == DynMode::CrystalMode; + self.xosc32k().modify(|_, w| { + w.cgm().variant(settings.cgm.into()); + w.startup().variant(settings.start_up.into()); + w.ondemand().bit(settings.on_demand); + w.runstdby().bit(settings.run_standby); + w.xtalen().bit(xtalen) + }); + } + + /// Disable the XOSC32K + #[inline] + fn enable(&mut self) { + self.xosc32k().modify(|_, w| w.enable().set_bit()); + } + + /// Disable the XOSC32K + #[inline] + fn disable(&mut self) { + self.xosc32k().modify(|_, w| w.enable().clear_bit()); + } + + /// Enable the 1 kHz output + #[inline] + fn enable_1k(&mut self) { + self.xosc32k().modify(|_, w| w.en1k().set_bit()); + } + + /// Disable the 1 kHz output + #[inline] + fn disable_1k(&mut self) { + self.xosc32k().modify(|_, w| w.en1k().clear_bit()); + } + + /// Enable the 32 kHz output + #[inline] + fn enable_32k(&mut self) { + self.xosc32k().modify(|_, w| w.en32k().set_bit()); + } + + /// Disable the 32 kHz output + #[inline] + fn disable_32k(&mut self) { + self.xosc32k().modify(|_, w| w.en32k().clear_bit()); + } + + /// Enable the write lock + #[inline] + fn write_lock(&mut self) { + self.xosc32k().modify(|_, w| w.wrtlock().set_bit()); + } +} + +impl Xosc32kCfdToken { + #[inline] + fn status(&self) -> status::R { + // Safety: We are only reading from the `STATUS` register, so there is + // no risk of memory corruption. + unsafe { (*crate::pac::OSC32KCTRL::PTR).status.read() } + } + + /// Check whether the XOSC32K has triggered failure detection + #[inline] + fn has_failed(&self) -> bool { + self.status().xosc32kfail().bit() + } + + /// Check whether the XOSC32K has been switched to the safe clock + #[inline] + fn is_switched(&self) -> bool { + self.status().xosc32ksw().bit() + } + + #[inline] + fn cfdctrl(&self) -> &CFDCTRL { + // Safety: The `Xosc32kCfdToken` has exclusive access to the `CFDCTRL` + // register. See the notes on `Token` types and memory safety in the + // root of the `clock` module for more details. + unsafe { &(*crate::pac::OSC32KCTRL::PTR).cfdctrl } + } + + /// Enable clock failure detection and set the safe clock divider + #[inline] + fn enable(&mut self, div: SafeClockDiv) { + self.cfdctrl().modify(|_, w| { + w.cfdpresc().bit(div.into()); + w.cfden().set_bit() + }); + } + + /// Disable clock failure detection + #[inline] + fn disable(&mut self) { + self.cfdctrl().modify(|_, w| w.cfden().clear_bit()); + } + + /// Switch from the safe clock back to the XOSC32K clock/oscillator + /// + /// This bit is cleared by the hardware after successfully switching back + #[inline] + fn switch_back(&mut self) { + self.cfdctrl().modify(|_, w| w.swback().set_bit()); + } +} + +//============================================================================== +// Settings +//============================================================================== + +// Collection of XOSC32K register fields +// +// All of these fields are set in a single write to XOSC32K during the call to +// [`Xosc32kBase::enable`]. The remaining fields are only modified after it has +// been enabled. +#[derive(Clone, Copy)] +struct Settings { + start_up: StartUpDelay, + cgm: ControlGainMode, + on_demand: bool, + run_standby: bool, + mode: DynMode, +} + +//============================================================================== +// XIn32 & XOut32 +//============================================================================== + +/// Type alias for the XOSC32K input [`Pin`] +pub type XIn32 = Pin; + +/// Type alias for the XOSC32K output [`Pin`] +pub type XOut32 = Pin; + +//============================================================================== +// SafeClockDiv +//============================================================================== + +/// Division factor for the safe clock prescaler +/// +/// If an XOSC32K clock failure is detected, the hardware will switch to a safe +/// clock derived from the [`OscUlp32k`]. This enum sets the divider between it +/// and the safe clock frequency. The divider can be 1 or 2. +/// +///[`OscUlp32k`]: super::osculp32k::OscUlp32k +#[repr(u8)] +#[derive(Clone, Copy, Default, PartialEq, Eq)] +pub enum SafeClockDiv { + #[default] + Div1, + Div2, +} + +impl From for bool { + fn from(div: SafeClockDiv) -> Self { + match div { + SafeClockDiv::Div1 => false, + SafeClockDiv::Div2 => true, + } + } +} + +//============================================================================== +// StartUpDelay +//============================================================================== + +/// Start up delay before continuous monitoring takes effect +/// +/// After a hard reset or waking from sleep, the XOSC32K output will remained +/// masked for the start up period, to ensure an unstable clock is not +/// propagated into the digital logic. +/// +/// The start up delay is counted using the [`OscUlp32k`] clock. +/// +/// [`OscUlp32k`]: super::osculp32k::OscUlp32k +#[repr(u8)] +#[derive(Clone, Copy, Default, PartialEq, Eq)] +pub enum StartUpDelay { + #[default] + Delay63ms, + Delay125ms, + Delay500ms, + Delay1s, + Delay2s, + Delay4s, + Delay8s, +} + +impl From for STARTUP_A { + fn from(delay: StartUpDelay) -> Self { + match delay { + StartUpDelay::Delay63ms => STARTUP_A::CYCLE2048, + StartUpDelay::Delay125ms => STARTUP_A::CYCLE4096, + StartUpDelay::Delay500ms => STARTUP_A::CYCLE16384, + StartUpDelay::Delay1s => STARTUP_A::CYCLE32768, + StartUpDelay::Delay2s => STARTUP_A::CYCLE65536, + StartUpDelay::Delay4s => STARTUP_A::CYCLE131072, + StartUpDelay::Delay8s => STARTUP_A::CYCLE262144, + } + } +} + +//============================================================================== +// ControlGainMode +//============================================================================== + +/// Gain mode for the XOSC32K control loop +/// +/// The XOSC32K crystal oscillator control loop has a configurable gain to allow +/// users to trade power for speed and stability. +#[derive(Copy, Clone, Default, PartialEq, Eq)] +pub enum ControlGainMode { + #[default] + Standard, + HighSpeed, +} + +impl From for CGM_A { + fn from(cgm: ControlGainMode) -> Self { + match cgm { + ControlGainMode::Standard => CGM_A::XT, + ControlGainMode::HighSpeed => CGM_A::HS, + } + } +} + +//============================================================================== +// DynMode +//============================================================================== + +/// Value-level enum identifying one of two possible XOSC32K operating modes +/// +/// The XOSC32K clock can be sourced from either an external clock or crystal +/// oscillator. The variants of this enum identify one of these two possible +/// operating modes. +/// +/// `DynMode` is the value-level equivalent of [`Mode`]. +#[derive(Clone, Copy, Default, PartialEq, Eq)] +pub enum DynMode { + #[default] + ClockMode, + CrystalMode, +} + +//============================================================================== +// Mode +//============================================================================== + +/// Type-level enum for the XOSC32K operation mode +/// +/// The XOSC32K clock can be sourced from either an external clock or a crystal +/// oscillator. This type-level `enum` provides two type-level variants, +/// [`ClockMode`] and [`CrystalMode`], representing these operating modes. +/// +/// `Mode` is the type-level equivalent of [`DynMode`]. See the documentation on +/// [type-level programming] and specifically [type-level enums] for more +/// details. +/// +/// [type-level programming]: crate::typelevel +/// [type-level enums]: crate::typelevel#type-level-enums +pub trait Mode: Sealed { + const DYN: DynMode; + #[doc(hidden)] + type Pins; +} + +/// Type-level variant of the XOSC32K operating [`Mode`] +/// +/// In this `Mode`, the XOSC32K clock will be sourced from an external clock. +/// +/// See the documentation on [type-level programming] and specifically +/// [type-level enums] for more details. +/// +/// [type-level programming]: crate::typelevel +/// [type-level enums]: crate::typelevel#type-level-enums +pub enum ClockMode {} +impl Sealed for ClockMode {} +impl Mode for ClockMode { + const DYN: DynMode = DynMode::ClockMode; + type Pins = XIn32; +} + +/// Type-level variant of the XOSC32K operating [`Mode`] +/// +/// In this `Mode`, the XOSC32K clock will be sourced from a crystal oscillator. +/// +/// See the documentation on [type-level programming] and specifically +/// [type-level enums] for more details. +/// +/// [type-level programming]: crate::typelevel +/// [type-level enums]: crate::typelevel#type-level-enums +pub enum CrystalMode {} +impl Sealed for CrystalMode {} +impl Mode for CrystalMode { + const DYN: DynMode = DynMode::CrystalMode; + type Pins = (XIn32, XOut32); +} + +//============================================================================== +// Xosc32kBase +//============================================================================== + +/// XOSC32K base clock, which feeds the [`Xosc1k`] and [`Xosc32k`] clocks +/// +/// The XOSC32K peripheral has two possible clock outputs, one at 32 kHz and +/// another at 1 kHz. This structure is represented in the type system as a set +/// of three clocks forming a small clock tree. The [`Xosc32kBase`] clock +/// represents the configurable base oscillator that feeds the optional +/// [`Xosc1k`] and [`Xosc32k`] output clocks. See the +/// [module-level documentation](super) for details and examples. +pub struct Xosc32kBase { + token: Xosc32kBaseToken, + pins: M::Pins, + settings: Settings, +} + +/// The [`Enabled`] [`Xosc32kBase`] clock +/// +/// As described in the [`clock` module documentation](super), the [`Enabled`] +/// wrapper implements compile-time clock tree safety by tracking the number of +/// clocks consuming the [`Xosc32kBase`] clock and restricts access to the +/// underlying type to prevent misuse. +/// +/// As with [`Enabled`], the default value for `N` is `U0`; if left unspecified, +/// the counter is assumed to be zero. +pub type EnabledXosc32kBase = Enabled, N>; + +impl Xosc32kBase { + /// Create the [`Xosc32kBase`] clock from an external clock, taking + /// ownership of the [`XIn32`] [`Pin`] + /// + /// Creating an [`Xosc32kBase`] clock does not modify any of the hardware + /// registers. It only creates a struct to track the configuration. The + /// configuration data is stored until the user calls [`enable`]. At that + /// point, all of the registers are written according to the initialization + /// procedures specified in the datasheet, and an [`EnabledXosc32kBase`] + /// clock is returned. The `Xosc32kBase` clock is not active or useful until + /// that point. + /// + /// [`enable`]: Xosc32kBase::enable + #[inline] + pub fn from_clock(token: Xosc32kBaseToken, xin32: impl Into) -> Self { + let pins = xin32.into(); + Self::new(token, pins) + } + + /// Consume the [`Xosc32kBase`] and release the [`Xosc32kBaseToken`] and + /// [`XIn32`] [`Pin`] + #[inline] + pub fn free(self) -> (Xosc32kBaseToken, XIn32) { + (self.token, self.pins) + } +} + +impl Xosc32kBase { + /// Create the [`Xosc32kBase`] clock from an external crystal oscillator, + /// taking ownership of the [`XIn32`] and [`XOut32`] [`Pin`]s. + /// + /// Creating an [`Xosc32kBase`] clock does not modify any of the hardware + /// registers. It only creates a struct to track the configuration. The + /// configuration data is stored until the user calls [`enable`]. At that + /// point, all of the registers are written according to the initialization + /// procedures specified in the datasheet, and an [`EnabledXosc32kBase`] + /// clock is returned. The `Xosc32kBase` is not active or useful until that + /// point. + /// + /// [`enable`]: Xosc32kBase::enable + #[inline] + pub fn from_crystal( + token: Xosc32kBaseToken, + xin32: impl Into, + xout32: impl Into, + ) -> Self { + let pins = (xin32.into(), xout32.into()); + Self::new(token, pins) + } + + /// Consume the [`Xosc32kBase`] and release the [`Xosc32kBaseToken`], + /// [`XIn32`] and [`XOut32`] [`Pin`]s + #[inline] + pub fn free(self) -> (Xosc32kBaseToken, XIn32, XOut32) { + let (xin32, xout32) = self.pins; + (self.token, xin32, xout32) + } + + /// Set the crystal oscillator [`ControlGainMode`] + #[inline] + pub fn control_gain_mode(mut self, cgm: ControlGainMode) -> Self { + self.settings.cgm = cgm; + self + } +} + +impl Xosc32kBase { + #[inline] + fn new(token: Xosc32kBaseToken, pins: M::Pins) -> Self { + let settings = Settings { + start_up: StartUpDelay::Delay63ms, + cgm: ControlGainMode::Standard, + on_demand: true, + run_standby: false, + mode: M::DYN, + }; + Self { + token, + pins, + settings, + } + } + + /// Set the start up delay before the [`Xosc32kBase`] clock is unmasked and + /// continuously monitored + /// + /// During the start up period, the [`Xosc32kBase`] clock is masked to + /// prevent clock instability from propagating to the digital logic. During + /// this time, clock failure detection is disabled. + #[inline] + pub fn start_up_delay(mut self, delay: StartUpDelay) -> Self { + self.settings.start_up = delay; + self + } + + /// Control the XOSC32K on-demand behavior + /// + /// When the on-demand is enabled, the XOSC32K clocks will only run in Idle + /// or Standby sleep modes if it is requested by a peripheral. Otherwise, + /// its behavior is dependent on the run-standby setting. + #[inline] + pub fn on_demand(mut self, on_demand: bool) -> Self { + self.settings.on_demand = on_demand; + self + } + + /// Control the XOSC32K behavior in Standby sleep mode + /// + /// When `RUNSTDBY` is disabled, the XOSC32K clocks will never run in + /// Standby sleep mode unless `ONDEMAND` is enabled and a clock is requested + /// by a peripheral. + /// + /// When `RUNSTDBY` is enabled, the `Xosc` will run in Standby sleep mode, + /// but it can still be disabled if `ONDEMAND` is enabled and a clock is not + /// requested. + #[inline] + pub fn run_standby(mut self, run_standby: bool) -> Self { + self.settings.run_standby = run_standby; + self + } + + /// Freeze the XOSC32K configuration until power-on reset + /// + /// This function sets the write-lock bit, which freezes the XOSC32K + /// configuration at the hardware level until power-on reset. At the API + /// level, it also consumes and drops the [`Xosc32kBase`], which prevents + /// any further modifications. + /// + /// **NOTE:** Because the `Xosc32kBase` is not yet enabled, calling this + /// method will lock both the [`Xosc1k`] and [`Xosc32k`] in their disabled + /// state. + #[inline] + pub fn write_lock(mut self) { + self.token.write_lock(); + } + + /// Enable the [`Xosc32kBase`] clock, so that it can be used as a clock + /// [`Source`] for the [`Xosc1k`] and [`Xosc32k`] clocks + /// + /// As mentioned when creating a new `Xosc32kBase`, no hardware registers + /// are actually modified until this call. Rather, the desired configuration + /// is stored internally, and the `Xosc32kBase` is initialized and + /// configured here according to the datasheet. + /// + /// The returned value is an [`EnabledXosc32kBase`] that can be used as a + /// clock [`Source`] for the [`Xosc1k`] and [`Xosc32k`] clocks. + #[inline] + pub fn enable(mut self) -> EnabledXosc32kBase { + self.token.reset(); + self.token.set_xosc32k(self.settings); + self.token.enable(); + Enabled::new(self) + } +} + +impl EnabledXosc32kBase { + /// Disable the [`Xosc32kBase`] clock + /// + /// This method is only implemented for `N = U0`, which means the clock can + /// only be disabled when no other clocks consume this [`Xosc32kBase`] + /// clock. + #[inline] + pub fn disable(mut self) -> Xosc32kBase { + self.0.token.disable(); + self.0 + } +} + +impl EnabledXosc32kBase { + /// Check whether the XOSC32K is stable and ready to be used as a clock + /// [`Source`] + #[inline] + pub fn is_ready(&self) -> bool { + self.0.token.is_ready() + } + + /// Freeze the XOSC32K configuration until power-on reset + /// + /// This function sets the write-lock bit, which freezes the XOSC32K + /// configuration at the hardware level until power-on reset. At the API + /// level, it also consumes and drops the [`Xosc32kBase`] clock, which + /// prevents any further modifications. + #[inline] + pub fn write_lock(mut self) { + self.0.token.write_lock(); + } +} + +//============================================================================== +// Xosc32kCfd +//============================================================================== + +/// Clock failure detection interface for the XOSC32K peripheral +/// +/// The XOSC32K peripheral provides a hardware method to continuously monitor +/// the clock to verify it is still running. In the event of a failure, the +/// output will be switched to a "safe clock" derived from the [`OscUlp32k`]. +/// The XOSC32K peripheral provides a prescaler to optionally divide the +/// `OscUlp32k` by two. +/// +/// Note that clock failure is triggered when four safe clock periods pass +/// without seeing a rising & falling edge pair on the XOSC32K clock. Once +/// failure is detected, the corresponding bit in the `STATUS` register will +/// go high and an interrupt will be triggered. +/// +/// If the external clock can be fixed, the XOSC32K clock can be switched back +/// using [`Xosc32kCfd::switch_back`]. +/// +/// Because the safe clock makes use of the `OscUlp32k`, the `Xosc32kCfd` must +/// register as a consumer of the [`EnabledOscUlp32k`] and [`Increment`] its +/// counter. +/// +/// [`OscUlp32k`]: super::osculp32k::OscUlp32k +/// [`EnabledOscUlp32k`]: super::osculp32k::EnabledOscUlp32k +pub struct Xosc32kCfd { + token: Xosc32kCfdToken, +} + +impl Xosc32kCfd { + /// Enable continuous monitoring of the XOSC32K for clock failure + /// + /// Because the safe clock makes use of the [`OscUlp32k`], the `Xosc32kCfd` + /// must register as a consumer of the [`EnabledOscUlp32k`] and + /// [`Increment`] its counter. + /// + /// [`OscUlp32k`]: super::osculp32k::OscUlp32k + /// [`EnabledOscUlp32k`]: super::osculp32k::EnabledOscUlp32k + #[inline] + pub fn enable( + mut token: Xosc32kCfdToken, + osc_ulp_32k: S, + div: SafeClockDiv, + ) -> (Xosc32kCfd, S::Inc) + where + S: Source + Increment, + { + token.enable(div); + (Self { token }, osc_ulp_32k.inc()) + } + + /// Check whether the XOSC32K has triggered clock failure detection + /// + /// Failure is triggered when four safe clock periods pass without seeing a + /// rising & falling edge pair on the XOSC32K clock. + #[inline] + pub fn has_failed(&self) -> bool { + self.token.has_failed() + } + + /// Check whether the XOSC32K has been switched to the safe clock + /// + /// Returns `true` if the XOSC32K has been switched to the safe clock. + #[inline] + pub fn is_switched(&self) -> bool { + self.token.is_switched() + } + + /// Attempt to switch from the safe clock back to the external clock + /// + /// This function will set the switch back bit (`SWBACK`) in the `CFDCTRL` + /// register. Once the hardware has successfully switched back, this bit + /// will be automatically cleared. + /// + /// Users can check whether switching back was successful by checking the + /// `STATUS` register with [`Xosc32kCfd::is_switched`]. + #[inline] + pub fn switch_back(&mut self) { + self.token.switch_back() + } + + /// Disable continuous monitoring of the XOSC32K for clock failure + /// + /// Once failure monitoring is disabled, the [`OscUlp32k`] is no longer used + /// as the safe clock, so the [`EnabledOscUlp32k`] counter can be + /// [`Decrement`]ed. + /// + /// [`OscUlp32k`]: super::osculp32k::OscUlp32k + /// [`EnabledOscUlp32k`]: super::osculp32k::EnabledOscUlp32k + #[inline] + pub fn disable(mut self, osc_ulp_32k: S) -> (Xosc32kCfdToken, S::Dec) + where + S: Source + Decrement, + { + self.token.disable(); + (self.token, osc_ulp_32k.dec()) + } +} + +//============================================================================== +// Ids +//============================================================================== + +/// Type representing the identity of the [`Xosc1k`] clock +/// +/// See the discussion on [`Id` types](super#id-types) for more information. +pub enum Xosc1kId {} + +impl Sealed for Xosc1kId {} + +/// Type representing the identity of the [`Xosc32k`] clock +/// +/// See the discussion on [`Id` types](super#id-types) for more information. +pub enum Xosc32kId {} + +impl Sealed for Xosc32kId {} + +//============================================================================== +// Xosc1k +//============================================================================== + +/// Clock representing the 1 kHz output of the [`Xosc32kBase`] clock +/// +/// The XOSC32K peripheral has two possible clock outputs, one at 32 kHz and +/// another at 1 kHz. This structure is represented in the type system as a set +/// of three clocks forming a small clock tree. The [`Xosc1k`] clock is derived +/// from the [`Xosc32kBase`] clock. See the [module-level documentation](super) +/// for details and examples. +pub struct Xosc1k { + token: Xosc1kToken, +} + +/// The [`Enabled`] [`Xosc1k`] clock +/// +/// As described in the [`clock` module documentation](super), the [`Enabled`] +/// wrapper implements compile-time clock tree safety by tracking the number of +/// clocks consuming the [`Xosc1k`] clock and restricts access to the underlying +/// type to prevent misuse. +/// +/// As with [`Enabled`], the default value for `N` is `U0`; if left unspecified, +/// the counter is assumed to be zero. +pub type EnabledXosc1k = Enabled; + +impl Xosc1k { + /// Enable 1 kHz output from the [`Xosc32kBase`] clock + /// + /// This will [`Increment`] the [`EnabledXosc32kBase`] counter. + #[inline] + pub fn enable( + token: Xosc1kToken, + mut base: EnabledXosc32kBase, + ) -> (EnabledXosc1k, EnabledXosc32kBase) + where + M: Mode, + N: Increment, + { + base.0.token.enable_1k(); + (Enabled::new(Self { token }), base.inc()) + } +} + +impl EnabledXosc1k { + /// Disable 1 kHz output from the [`Xosc32kBase`] clock + /// + /// This will [`Decrement`] the [`EnabledXosc32kBase`] counter. + #[inline] + pub fn disable( + self, + mut base: EnabledXosc32kBase, + ) -> (Xosc1kToken, EnabledXosc32kBase) + where + M: Mode, + N: Decrement, + { + base.0.token.disable_1k(); + (self.0.token, base.dec()) + } +} + +impl Source for EnabledXosc1k { + type Id = Xosc1kId; + + #[inline] + fn freq(&self) -> Hertz { + Hertz(1024) + } +} + +//============================================================================== +// Xosc32k +//============================================================================== + +/// Clock representing the 32 kHz output of the [`Xosc32kBase`] clock +/// +/// The XOSC32K peripheral has two possible clock outputs, one at 32 kHz and +/// another at 1 kHz. This structure is represented in the type system as a set +/// of three clocks forming a small clock tree. The [`Xosc32k`] clock is derived +/// from the [`Xosc32kBase`] clock. See the [module-level documentation](super) +/// for details and examples. +pub struct Xosc32k { + token: Xosc32kToken, +} + +/// The [`Enabled`] [`Xosc32k`] clock +/// +/// As described in the [`clock` module documentation](super), the [`Enabled`] +/// wrapper implements compile-time clock tree safety by tracking the number of +/// clocks consuming the [`Xosc32k`] clock and restricts access to the +/// underlying type to prevent misuse. +/// +/// As with [`Enabled`], the default value for `N` is `U0`; if left unspecified, +/// the counter is assumed to be zero. +pub type EnabledXosc32k = Enabled; + +impl Xosc32k { + /// Enable 32 kHz output from the [`Xosc32kBase`] clock + /// + /// This will [`Increment`] the [`EnabledXosc32kBase`] counter. + #[inline] + pub fn enable( + token: Xosc32kToken, + mut base: EnabledXosc32kBase, + ) -> (EnabledXosc32k, EnabledXosc32kBase) + where + M: Mode, + N: Increment, + { + base.0.token.enable_32k(); + (Enabled::new(Self { token }), base.inc()) + } +} + +impl EnabledXosc32k { + /// Disable 1 kHz output from the [`Xosc32kBase`] clock + /// + /// This will [`Decrement`] the [`EnabledXosc32kBase`] counter. + #[inline] + pub fn disable( + self, + mut base: EnabledXosc32kBase, + ) -> (Xosc32kToken, EnabledXosc32kBase) + where + M: Mode, + N: Decrement, + { + base.0.token.disable_32k(); + (self.0.token, base.dec()) + } +} + +impl Source for EnabledXosc32k { + type Id = Xosc32kId; + + #[inline] + fn freq(&self) -> Hertz { + Hertz(32_768) + } +} diff --git a/hal/src/typelevel.rs b/hal/src/typelevel.rs index 718a0076518..b94073fecde 100644 --- a/hal/src/typelevel.rs +++ b/hal/src/typelevel.rs @@ -631,7 +631,10 @@ //! you put back an instance of `P` exactly. The final use of [`Into`] is key //! here. It transforms the `SpecificPin` back into `P` itself. -use typenum::{Bit, UInt, Unsigned, U0}; +use core::ops::{Add, Sub}; + +use typenum::{Add1, Bit, Sub1, UInt, Unsigned, B1, U0}; + mod private { /// Super trait used to mark traits with an exhaustive set of /// implementations @@ -644,15 +647,38 @@ mod private { impl Sealed for u32 {} impl Sealed for i32 {} impl Sealed for f32 {} + + /// Mapping from an instance of a countable type to its successor + pub trait Increment { + /// Successor type of `Self` + type Inc; + /// Consume an instance of `Self` and return its successor + fn inc(self) -> Self::Inc; + } + + /// Mapping from an instance of a countable type to its predecessor + pub trait Decrement { + /// Predecessor type of `Self` + type Dec; + /// Consume an instance of `Self` and return its predecessor + fn dec(self) -> Self::Dec; + } } +pub(crate) use private::Decrement as PrivateDecrement; +pub(crate) use private::Increment as PrivateIncrement; pub(crate) use private::Sealed; -/// Type-level version of the [None] variant +/// Type-level version of the [`None`] variant #[derive(Default)] pub struct NoneT; + impl Sealed for NoneT {} +//============================================================================== +// Is +//============================================================================== + /// Marker trait for type identity /// /// This trait is used as part of the [`AnyKind`] trait pattern. It represents @@ -702,8 +728,56 @@ where type Type = T; } +//============================================================================== +// Counting +//============================================================================== + /// Implement `Sealed` for [`U0`] impl Sealed for U0 {} /// Implement `Sealed` for all type-level, [`Unsigned`] integers *except* [`U0`] impl Sealed for UInt {} + +/// Trait mapping each countable type to its successor +/// +/// This trait maps each countable type to its corresponding successor type. The +/// actual implementation of this trait is contained within `PrivateIncrement`. +/// Access to `PrivateIncrement` is restricted, so that safe HAL APIs can be +/// built with it. +pub trait Increment: PrivateIncrement {} + +impl Increment for T {} + +/// Trait mapping each countable type to its predecessor +/// +/// This trait maps each countable type to its corresponding predecessor type. +/// The actual implementation of this trait is contained within +/// `PrivateDecrement`. Access to `PrivateDecrement` is restricted, so that safe +/// HAL APIs can be built with it. +pub trait Decrement: PrivateDecrement {} + +impl Decrement for T {} + +impl PrivateIncrement for N +where + N: Unsigned + Add, + Add1: Unsigned, +{ + type Inc = Add1; + #[inline] + fn inc(self) -> Self::Inc { + Self::Inc::default() + } +} + +impl PrivateDecrement for N +where + N: Unsigned + Sub, + Sub1: Unsigned, +{ + type Dec = Sub1; + #[inline] + fn dec(self) -> Self::Dec { + Self::Dec::default() + } +}