diff --git a/Cargo.toml b/Cargo.toml index 6613515bbc01..c7e8accbc2ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,8 @@ members = [ "pac/*", "boards/*", ] +# Fragments of example files, referenced by `include!()` from BSP examples +exclude = ["boards/examples"] [profile.dev] debug = true diff --git a/atsamd-hal-macros/devices.yaml b/atsamd-hal-macros/devices.yaml index 763d74a1003b..bcfb70f40688 100644 --- a/atsamd-hal-macros/devices.yaml +++ b/atsamd-hal-macros/devices.yaml @@ -21,7 +21,9 @@ families: - serial-numbers - dsu - clock - - gclk + - xosc32k + - xosc: { count: 1 } + - gclk: { count: 6 } - pm - sysctrl - wdt @@ -53,7 +55,9 @@ families: - serial-numbers - dsu - clock - - gclk + - xosc32k: { except: ["samd21el", "samd21gl"] } + - xosc: { count: 1 } + - gclk: { count: 8 } - pm - sysctrl - wdt @@ -96,7 +100,9 @@ families: - cmcc - dsu - clock - - gclk + - xosc32k + - xosc: { count: 2 } + - gclk: { count: 12 } - mclk - rstc - ramecc diff --git a/boards/atsame54_xpro/examples/blinky_rtic.rs b/boards/atsame54_xpro/examples/blinky_rtic.rs index e331bedfcfa9..32b42710b483 100644 --- a/boards/atsame54_xpro/examples/blinky_rtic.rs +++ b/boards/atsame54_xpro/examples/blinky_rtic.rs @@ -3,7 +3,7 @@ use atsame54_xpro as bsp; use bsp::hal; -use hal::clock::v2::{clock_system_at_reset, osculp32k::OscUlp1k, rtcosc::RtcOsc}; +use hal::clock::v2::{clock_system_at_reset, rtcosc::RtcOsc}; use hal::prelude::*; use hal::rtc::rtic::rtc_clock; use panic_rtt_target as _; @@ -27,26 +27,18 @@ mod app { #[init] fn init(ctx: init::Context) -> (Shared, Local) { - let mut device = ctx.device; + let device = ctx.device; let mut core: rtic::export::Peripherals = ctx.core; rtt_init_print!(); - let (_buses, clocks, tokens) = clock_system_at_reset( - device.oscctrl, - device.osc32kctrl, - device.gclk, - device.mclk, - &mut device.nvmctrl, - ); - - // Enable the 1 kHz clock from the internal 32 kHz source - let (osculp1k, _) = OscUlp1k::enable(tokens.osculp32k.osculp1k, clocks.osculp32k_base); + let (_buses, clocks, tokens) = + clock_system_at_reset(device.oscctrl, device.osc32kctrl, device.gclk, device.mclk); // Enable the RTC clock with the 1 kHz source. // Note that currently the proof of this (the `RtcOsc` instance) is not // required to start the monotonic. - let _ = RtcOsc::enable(tokens.rtcosc, osculp1k); + let _ = RtcOsc::enable(tokens.rtcosc, clocks.osculp.osculp1k); // Start the monotonic Mono::start(device.rtc); diff --git a/boards/atsame54_xpro/examples/mcan.rs b/boards/atsame54_xpro/examples/mcan.rs index 5cad635297f6..d2f9ad8fb671 100644 --- a/boards/atsame54_xpro/examples/mcan.rs +++ b/boards/atsame54_xpro/examples/mcan.rs @@ -21,7 +21,7 @@ use atsame54_xpro as bsp; use bsp::hal; -use clock::{osculp32k::OscUlp1k, rtcosc::RtcOsc}; +use clock::rtcosc::RtcOsc; use hal::clock::v2 as clock; use hal::eic::{Ch15, Eic, ExtInt, Sense}; use hal::gpio::{Interrupt as GpioInterrupt, *}; @@ -83,7 +83,7 @@ type Aux = mcan::bus::Aux< clock::types::Can1, hal::can::Dependencies< clock::types::Can1, - clock::gclk::Gclk0Id, + clock::pclk::PclkSource, bsp::Ata6561Rx, bsp::Ata6561Tx, bsp::pac::Can1, @@ -115,7 +115,7 @@ mod app { can_memory: SharedMemory = SharedMemory::new() ])] fn init(ctx: init::Context) -> (Shared, Local) { - let mut device = ctx.device; + let device = ctx.device; rtt_init_print!(); rprintln!("Application up!"); @@ -125,16 +125,12 @@ mod app { device.osc32kctrl, device.gclk, device.mclk, - &mut device.nvmctrl, ); - // Enable the 1 kHz clock from the internal 32 kHz source - let (osculp1k, _) = OscUlp1k::enable(tokens.osculp32k.osculp1k, clocks.osculp32k_base); - // Enable the RTC clock with the 1 kHz source. // Note that currently the proof of this (the `RtcOsc` instance) is not // required to start the monotonic. - let _ = RtcOsc::enable(tokens.rtcosc, osculp1k); + let _ = RtcOsc::enable(tokens.rtcosc, clocks.osculp.osculp1k); // Start the monotonic Mono::start(device.rtc); diff --git a/boards/examples/m4-adc.rs b/boards/examples/m4-adc.rs new file mode 100644 index 000000000000..0afcc86f8238 --- /dev/null +++ b/boards/examples/m4-adc.rs @@ -0,0 +1,63 @@ +// This file is included by one or more BSP examples. In normal usage, firmware +// source code needs to start with something like: +// +// ``` +// #![no_std] +// #![no_main] +// use feather_m4 as bsp; +//``` + +use atsamd_hal::adc::AdcBuilder; + +use bsp::hal; +use bsp::pac; + +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use bsp::entry; +use bsp::Pins; +use pac::{CorePeripherals, Peripherals}; + +use hal::{ + adc::{Accumulation, Prescaler}, + clock::v2::{clock_system_at_reset, pclk::Pclk}, +}; + +#[entry] +fn main() -> ! { + let peripherals = Peripherals::take().unwrap(); + let _core = CorePeripherals::take().unwrap(); + + let pins = Pins::new(peripherals.port); + + let (mut buses, clocks, tokens) = clock_system_at_reset( + peripherals.oscctrl, + peripherals.osc32kctrl, + peripherals.gclk, + peripherals.mclk, + ); + + // Enable the ADC0 ABP clock... + let apb_adc0 = buses.apb.enable(tokens.apbs.adc0); + // ...and enable the ADC0 PCLK. Both of these are required for the + // ADC to run. + let (pclk_adc0, _gclk0) = Pclk::enable(tokens.pclks.adc0, clocks.gclk0); + + let mut adc = AdcBuilder::new(Accumulation::single(atsamd_hal::adc::AdcResolution::_12)) + .with_clock_cycles_per_sample(5) + // Overruns if clock divider < 32 in debug mode + .with_clock_divider(Prescaler::Div32) + .with_vref(atsamd_hal::adc::Reference::Arefa) + .enable(peripherals.adc0, apb_adc0, pclk_adc0) + .unwrap(); + let mut adc_pin = pins.a0.into_alternate(); + + loop { + let _res = adc.read(&mut adc_pin); + #[cfg(feature = "use_semihosting")] + let _ = cortex_m_semihosting::hprintln!("ADC value: {}", _res); + } +} diff --git a/boards/examples/m4-blinky_rtic.rs b/boards/examples/m4-blinky_rtic.rs new file mode 100644 index 000000000000..2a055d3c0918 --- /dev/null +++ b/boards/examples/m4-blinky_rtic.rs @@ -0,0 +1,82 @@ +// Blink an led using an RTIC software task and the RTC-based monotonic. +// +// This file is included by one or more BSP examples. In normal usage, firmware +// source code needs to start with something like: +// +// ``` +// #![no_std] +// #![no_main] +// use feather_m4 as bsp; +//``` + +use bsp::{hal, Pins, RedLed}; +#[cfg(not(feature = "use_semihosting"))] +use panic_halt as _; +#[cfg(feature = "use_semihosting")] +use panic_semihosting as _; + +use hal::clock::v2::{clock_system_at_reset, rtcosc::RtcOsc}; +use hal::prelude::*; +use hal::rtc::rtic::rtc_clock; +use rtic::app; + +hal::rtc_monotonic!(Mono, rtc_clock::Clock1k); + +#[app(device = bsp::pac, dispatchers = [EVSYS_0])] +mod app { + use super::*; + + #[local] + struct Resources {} + + #[shared] + struct Shared { + // The LED could be a local resource, since it is only used in one task + // But we want to showcase shared resources and locking + red_led: RedLed, + } + + #[init] + fn init(cx: init::Context) -> (Shared, Resources) { + let device = cx.device; + let mut core: rtic::export::Peripherals = cx.core; + + // Use v2 of the clocks API so that we can set the RTC clock source + let (_, clocks, tokens) = + clock_system_at_reset(device.oscctrl, device.osc32kctrl, device.gclk, device.mclk); + + // Enable the RTC clock with the internal 1 kHz source. + // Note that currently the proof of this (the `RtcOsc` instance) is not + // required to start the monotonic. + let _ = RtcOsc::enable(tokens.rtcosc, clocks.osculp.osculp1k); + + // Start the monotonic + Mono::start(device.rtc); + + let pins = Pins::new(device.port); + + // We can use the RTC in standby for maximum power savings + core.SCB.set_sleepdeep(); + + blink_led::spawn().ok().unwrap(); + + ( + Shared { + red_led: pins.d13.into_push_pull_output(), + }, + Resources {}, + ) + } + + /// This function is spawned and never returns. + #[task(priority = 1, shared=[red_led])] + async fn blink_led(mut cx: blink_led::Context) { + loop { + // If the LED were a local resource, the lock would not be necessary + cx.shared.red_led.lock(|led| { + led.toggle().unwrap(); + }); + Mono::delay(400u64.millis()).await; + } + } +} diff --git a/boards/feather_m4/examples/adc.rs b/boards/feather_m4/examples/adc.rs index b6b892cf43d3..9f90fe3682a3 100644 --- a/boards/feather_m4/examples/adc.rs +++ b/boards/feather_m4/examples/adc.rs @@ -1,59 +1,5 @@ #![no_std] #![no_main] -use atsamd_hal::adc::AdcBuilder; use feather_m4 as bsp; - -use bsp::hal; -use bsp::pac; - -#[cfg(not(feature = "use_semihosting"))] -use panic_halt as _; -#[cfg(feature = "use_semihosting")] -use panic_semihosting as _; - -use bsp::entry; -use bsp::Pins; -use pac::{CorePeripherals, Peripherals}; - -use hal::{ - adc::{Accumulation, Adc, Prescaler, Resolution}, - clock::v2::{clock_system_at_reset, pclk::Pclk}, -}; - -#[entry] -fn main() -> ! { - let mut peripherals = Peripherals::take().unwrap(); - let _core = CorePeripherals::take().unwrap(); - - let pins = Pins::new(peripherals.port); - - let (mut buses, clocks, tokens) = clock_system_at_reset( - peripherals.oscctrl, - peripherals.osc32kctrl, - peripherals.gclk, - peripherals.mclk, - &mut peripherals.nvmctrl, - ); - - // Enable the ADC0 ABP clock... - let apb_adc0 = buses.apb.enable(tokens.apbs.adc0); - // ...and enable the ADC0 PCLK. Both of these are required for the - // ADC to run. - let (pclk_adc0, _gclk0) = Pclk::enable(tokens.pclks.adc0, clocks.gclk0); - - let mut adc = AdcBuilder::new(Accumulation::single(atsamd_hal::adc::AdcResolution::_12)) - .with_clock_cycles_per_sample(5) - // Overruns if clock divider < 32 in debug mode - .with_clock_divider(Prescaler::Div32) - .with_vref(atsamd_hal::adc::Reference::Arefa) - .enable(peripherals.adc0, apb_adc0, &pclk_adc0) - .unwrap(); - let mut adc_pin = pins.a0.into_alternate(); - - loop { - let res = adc.read(&mut adc_pin); - #[cfg(feature = "use_semihosting")] - cortex_m_semihosting::hprintln!("ADC value: {}", res); - } -} +include!("../../examples/m4-adc.rs"); diff --git a/boards/feather_m4/examples/blinky_rtic.rs b/boards/feather_m4/examples/blinky_rtic.rs index d853e3f52262..812fc88b53a6 100644 --- a/boards/feather_m4/examples/blinky_rtic.rs +++ b/boards/feather_m4/examples/blinky_rtic.rs @@ -1,85 +1,5 @@ -//! Blink an led using an RTIC software task and the RTC-based monotonic. - #![no_std] #![no_main] -use bsp::{hal, Pins, RedLed}; use feather_m4 as bsp; -#[cfg(not(feature = "use_semihosting"))] -use panic_halt as _; -#[cfg(feature = "use_semihosting")] -use panic_semihosting as _; - -use hal::clock::v2::{clock_system_at_reset, osculp32k::OscUlp1k, rtcosc::RtcOsc}; -use hal::prelude::*; -use hal::rtc::rtic::rtc_clock; -use rtic::app; - -hal::rtc_monotonic!(Mono, rtc_clock::Clock1k); - -#[app(device = bsp::pac, dispatchers = [EVSYS_0])] -mod app { - use super::*; - - #[local] - struct Resources {} - - #[shared] - struct Shared { - // The LED could be a local resource, since it is only used in one task - // But we want to showcase shared resources and locking - red_led: RedLed, - } - - #[init] - fn init(cx: init::Context) -> (Shared, Resources) { - let mut device = cx.device; - let mut core: rtic::export::Peripherals = cx.core; - - // Use v2 of the clocks API so that we can set the RTC clock source - let (_, clocks, tokens) = clock_system_at_reset( - device.oscctrl, - device.osc32kctrl, - device.gclk, - device.mclk, - &mut device.nvmctrl, - ); - - // Enable the 1 kHz clock from the internal 32 kHz source - let (osculp1k, _) = OscUlp1k::enable(tokens.osculp32k.osculp1k, clocks.osculp32k_base); - - // Enable the RTC clock with the 1 kHz source. - // Note that currently the proof of this (the `RtcOsc` instance) is not - // required to start the monotonic. - let _ = RtcOsc::enable(tokens.rtcosc, osculp1k); - - // Start the monotonic - Mono::start(device.rtc); - - let pins = Pins::new(device.port); - - // We can use the RTC in standby for maximum power savings - core.SCB.set_sleepdeep(); - - blink_led::spawn().ok().unwrap(); - - ( - Shared { - red_led: pins.d13.into_push_pull_output(), - }, - Resources {}, - ) - } - - /// This function is spawned and never returns. - #[task(priority = 1, shared=[red_led])] - async fn blink_led(mut cx: blink_led::Context) { - loop { - // If the LED were a local resource, the lock would not be necessary - cx.shared.red_led.lock(|led| { - led.toggle().unwrap(); - }); - Mono::delay(400u64.millis()).await; - } - } -} +include!("../../examples/m4-blinky_rtic.rs"); diff --git a/boards/feather_m4/examples/clocking_v2.rs b/boards/feather_m4/examples/clocking_v2.rs index 7a9b763bbe14..fa86e38d160f 100644 --- a/boards/feather_m4/examples/clocking_v2.rs +++ b/boards/feather_m4/examples/clocking_v2.rs @@ -8,7 +8,6 @@ use atsamd_hal::{ self as clock, dpll::Dpll, gclk::{Gclk, GclkDiv16, GclkDiv8}, - osculp32k::OscUlp32k, pclk::Pclk, rtcosc::RtcOsc, xosc32k::{ControlGainMode, Xosc1k, Xosc32k, Xosc32kBase}, @@ -43,7 +42,7 @@ mod app { #[init] fn init(cx: init::Context) -> (SharedResources, LocalResources) { - let mut device = cx.device; + let device = cx.device; // Get the clocks & tokens let (_buses, clocks, tokens) = clock::clock_system_at_reset( @@ -51,7 +50,6 @@ mod app { device.osc32kctrl, device.gclk, device.mclk, - &mut device.nvmctrl, ); // This is required because the `sercom` and `rtc` modules have not yet @@ -113,8 +111,7 @@ mod app { // 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 osculp32k = clocks.osculp.osculp32k; let (gclk5, _osculp32k) = Gclk::from_source(tokens.gclks.gclk5, osculp32k); let gclk5 = gclk5.enable(); let (_gclk5, _gclk5_out) = gclk5.enable_gclk_out(pins.pb11); diff --git a/boards/metro_m0/examples/blinky_basic.rs b/boards/metro_m0/examples/blinky_basic.rs index 0a41c9297814..5904df598d8f 100644 --- a/boards/metro_m0/examples/blinky_basic.rs +++ b/boards/metro_m0/examples/blinky_basic.rs @@ -11,24 +11,23 @@ use bsp::pac; use metro_m0 as bsp; use bsp::entry; -use hal::clock::GenericClockController; +use hal::clock::v2 as clock; use hal::delay::Delay; use hal::prelude::*; use pac::{CorePeripherals, Peripherals}; #[entry] fn main() -> ! { - let mut peripherals = Peripherals::take().unwrap(); + let peripherals = Peripherals::take().unwrap(); let core = CorePeripherals::take().unwrap(); - let mut clocks = GenericClockController::with_external_32kosc( - peripherals.gclk, - &mut peripherals.pm, - &mut peripherals.sysctrl, - &mut peripherals.nvmctrl, - ); + let (_buses, clocks, _tokens) = + clock::clock_system_at_reset(peripherals.gclk, peripherals.pm, peripherals.sysctrl); + + let gclk0 = clocks.gclk0; + let (mut delay, _gclk0) = Delay::new_with_source(core.SYST, gclk0); + let pins = bsp::Pins::new(peripherals.port); let mut red_led: bsp::RedLed = pins.d13.into(); - let mut delay = Delay::new(core.SYST, &mut clocks); loop { delay.delay_ms(200u8); red_led.set_high().unwrap(); diff --git a/boards/metro_m4/examples/adc.rs b/boards/metro_m4/examples/adc.rs index 56c46bbd97f7..79f1675e1d1b 100644 --- a/boards/metro_m4/examples/adc.rs +++ b/boards/metro_m4/examples/adc.rs @@ -1,58 +1,5 @@ #![no_std] #![no_main] -use atsamd_hal::adc::AdcBuilder; use metro_m4 as bsp; - -use bsp::hal; -use bsp::pac; - -#[cfg(not(feature = "use_semihosting"))] -use panic_halt as _; -#[cfg(feature = "use_semihosting")] -use panic_semihosting as _; - -use bsp::entry; -use bsp::Pins; -use pac::{CorePeripherals, Peripherals}; - -use hal::{ - adc::{Accumulation, Adc, Prescaler, Resolution}, - clock::v2::{clock_system_at_reset, pclk::Pclk}, -}; - -#[entry] -fn main() -> ! { - let mut peripherals = Peripherals::take().unwrap(); - let _core = CorePeripherals::take().unwrap(); - - let pins = Pins::new(peripherals.port); - - let (mut buses, clocks, tokens) = clock_system_at_reset( - peripherals.oscctrl, - peripherals.osc32kctrl, - peripherals.gclk, - peripherals.mclk, - &mut peripherals.nvmctrl, - ); - - // Enable the ADC0 ABP clock... - let apb_adc0 = buses.apb.enable(tokens.apbs.adc0); - // ...and enable the ADC0 PCLK. Both of these are required for the - // ADC to run. - let (pclk_adc0, _gclk0) = Pclk::enable(tokens.pclks.adc0, clocks.gclk0); - - let mut adc = AdcBuilder::new(Accumulation::single(atsamd_hal::adc::AdcResolution::_12)) - .with_clock_cycles_per_sample(5) - .with_clock_divider(Prescaler::Div32) - .with_vref(atsamd_hal::adc::Reference::Arefa) - .enable(peripherals.adc0, apb_adc0, &pclk_adc0) - .unwrap(); - let mut adc_pin = pins.a0.into_alternate(); - - loop { - let res = adc.read(&mut adc_pin); - #[cfg(feature = "use_semihosting")] - cortex_m_semihosting::hprintln!("ADC Result: {}", res).unwrap(); - } -} +include!("../../examples/m4-adc.rs"); diff --git a/boards/metro_m4/examples/async_adc.rs b/boards/metro_m4/examples/async_adc.rs index 818e395734d7..a4121ba277bf 100644 --- a/boards/metro_m4/examples/async_adc.rs +++ b/boards/metro_m4/examples/async_adc.rs @@ -16,7 +16,7 @@ use bsp::Pins; use pac::{CorePeripherals, Peripherals}; use hal::{ - adc::{Accumulation, Adc, Adc0, Prescaler, Resolution}, + adc::{Accumulation, Adc0, Prescaler}, clock::v2::{clock_system_at_reset, pclk::Pclk}, }; @@ -26,7 +26,7 @@ atsamd_hal::bind_multiple_interrupts!(struct Irqs { #[embassy_executor::main] async fn main(_s: embassy_executor::Spawner) -> ! { - let mut peripherals = Peripherals::take().unwrap(); + let peripherals = Peripherals::take().unwrap(); let _core = CorePeripherals::take().unwrap(); let pins = Pins::new(peripherals.port); @@ -36,28 +36,27 @@ async fn main(_s: embassy_executor::Spawner) -> ! { peripherals.osc32kctrl, peripherals.gclk, peripherals.mclk, - &mut peripherals.nvmctrl, ); // Enable the ADC0 ABP clock... let apb_adc0 = buses.apb.enable(tokens.apbs.adc0); // ...and enable the ADC0 PCLK. Both of these are required for the // ADC to run. - let (pclk_adc0, _gclk0) = Pclk::enable(tokens.pclks.adc0, clocks.gclk0); + let (pclk_adc0, _gclk0) = Pclk::enable_dyn(tokens.pclks.adc0, clocks.gclk0); let mut adc = AdcBuilder::new(Accumulation::single(atsamd_hal::adc::AdcResolution::_12)) .with_clock_cycles_per_sample(5) .with_clock_divider(Prescaler::Div32) .with_vref(atsamd_hal::adc::Reference::Arefa) - .enable(peripherals.adc0, apb_adc0, &pclk_adc0) + .enable(peripherals.adc0, apb_adc0, pclk_adc0) .unwrap() .into_future(Irqs); let mut adc_pin = pins.a0.into_alternate(); loop { - let res = adc.read(&mut adc_pin).await; + let _res = adc.read(&mut adc_pin).await; #[cfg(feature = "use_semihosting")] - cortex_m_semihosting::hprintln!("ADC Result: {}", res).unwrap(); + cortex_m_semihosting::hprintln!("ADC Result: {}", _res).unwrap(); } } diff --git a/boards/metro_m4/examples/blinky_rtic.rs b/boards/metro_m4/examples/blinky_rtic.rs index 13f39d41d5c2..34b385542b1e 100644 --- a/boards/metro_m4/examples/blinky_rtic.rs +++ b/boards/metro_m4/examples/blinky_rtic.rs @@ -1,85 +1,5 @@ -//! Blink an led using an RTIC software task and the RTC-based monotonic. - #![no_std] #![no_main] -use bsp::{hal, Pins, RedLed}; use metro_m4 as bsp; -#[cfg(not(feature = "use_semihosting"))] -use panic_halt as _; -#[cfg(feature = "use_semihosting")] -use panic_semihosting as _; - -use hal::clock::v2::{clock_system_at_reset, osculp32k::OscUlp1k, rtcosc::RtcOsc}; -use hal::prelude::*; -use hal::rtc::rtic::rtc_clock; -use rtic::app; - -hal::rtc_monotonic!(Mono, rtc_clock::Clock1k); - -#[app(device = bsp::pac, dispatchers = [EVSYS_0])] -mod app { - use super::*; - - #[local] - struct Resources {} - - #[shared] - struct Shared { - // The LED could be a local resource, since it is only used in one task - // But we want to showcase shared resources and locking - red_led: RedLed, - } - - #[init] - fn init(cx: init::Context) -> (Shared, Resources) { - let mut device = cx.device; - let mut core: rtic::export::Peripherals = cx.core; - - // Use v2 of the clocks API so that we can set the RTC clock source - let (_, clocks, tokens) = clock_system_at_reset( - device.oscctrl, - device.osc32kctrl, - device.gclk, - device.mclk, - &mut device.nvmctrl, - ); - - // Enable the 1 kHz clock from the internal 32 kHz source - let (osculp1k, _) = OscUlp1k::enable(tokens.osculp32k.osculp1k, clocks.osculp32k_base); - - // Enable the RTC clock with the 1 kHz source. - // Note that currently the proof of this (the `RtcOsc` instance) is not - // required to start the monotonic. - let _ = RtcOsc::enable(tokens.rtcosc, osculp1k); - - // Start the monotonic - Mono::start(device.rtc); - - let pins = Pins::new(device.port); - - // We can use the RTC in standby for maximum power savings - core.SCB.set_sleepdeep(); - - blink_led::spawn().ok().unwrap(); - - ( - Shared { - red_led: pins.d13.into_push_pull_output(), - }, - Resources {}, - ) - } - - /// This function is spawned and never returns. - #[task(priority = 1, shared=[red_led])] - async fn blink_led(mut cx: blink_led::Context) { - loop { - // If the LED were a local resource, the lock would not be necessary - cx.shared.red_led.lock(|led| { - led.toggle().unwrap(); - }); - Mono::delay(400u64.millis()).await; - } - } -} +include!("../../examples/m4-blinky_rtic.rs"); diff --git a/boards/pygamer/examples/blinky_rtic.rs b/boards/pygamer/examples/blinky_rtic.rs index c4549b21702c..a13bb736f57f 100644 --- a/boards/pygamer/examples/blinky_rtic.rs +++ b/boards/pygamer/examples/blinky_rtic.rs @@ -8,7 +8,7 @@ use bsp::{hal, Pins, RedLed}; use panic_halt as _; use pygamer as bsp; -use hal::clock::v2::{clock_system_at_reset, osculp32k::OscUlp1k, rtcosc::RtcOsc}; +use hal::clock::v2::{clock_system_at_reset, rtcosc::RtcOsc}; use hal::prelude::*; use hal::rtc::rtic::rtc_clock; use rtic::app; @@ -31,25 +31,17 @@ mod app { #[init] fn init(cx: init::Context) -> (Shared, Resources) { - let mut device = cx.device; + let device = cx.device; let mut core: rtic::export::Peripherals = cx.core; // Use v2 of the clocks API so that we can set the RTC clock source - let (_, clocks, tokens) = clock_system_at_reset( - device.oscctrl, - device.osc32kctrl, - device.gclk, - device.mclk, - &mut device.nvmctrl, - ); - - // Enable the 1 kHz clock from the internal 32 kHz source - let (osculp1k, _) = OscUlp1k::enable(tokens.osculp32k.osculp1k, clocks.osculp32k_base); - - // Enable the RTC clock with the 1 kHz source. + let (_, clocks, tokens) = + clock_system_at_reset(device.oscctrl, device.osc32kctrl, device.gclk, device.mclk); + + // Enable the RTC clock with the internal 1 kHz source. // Note that currently the proof of this (the `RtcOsc` instance) is not // required to start the monotonic. - let _ = RtcOsc::enable(tokens.rtcosc, osculp1k); + let _ = RtcOsc::enable(tokens.rtcosc, clocks.osculp.osculp1k); // Start the monotonic Mono::start(device.rtc); diff --git a/hal/src/clock.rs b/hal/src/clock.rs new file mode 100644 index 000000000000..df8cc9e27495 --- /dev/null +++ b/hal/src/clock.rs @@ -0,0 +1,15 @@ +//! # Clocking API +//! +//! Users are encouraged to use [`v2`] variant of an API because of the richer +//! feature set and safety. +use atsamd_hal_macros::hal_module; + +#[hal_module( + any("clock-d11", "clock-d21") => "clock/v1_thumbv6m.rs", + "clock-d5x" => "clock/v1_thumbv7em.rs", +)] +pub mod v1 {} + +pub use v1::*; + +pub mod v2; diff --git a/hal/src/peripherals/clock/d11.rs b/hal/src/clock/v1_thumbv6m.rs similarity index 99% rename from hal/src/peripherals/clock/d11.rs rename to hal/src/clock/v1_thumbv6m.rs index 0ff080c2cf17..7573da6c4d55 100644 --- a/hal/src/peripherals/clock/d11.rs +++ b/hal/src/clock/v1_thumbv6m.rs @@ -488,7 +488,7 @@ fn enable_gclk_apb(pm: &mut Pm) { /// Turn on the internal 32hkz oscillator pub fn enable_internal_32kosc(sysctrl: &mut Sysctrl) { - let calibration = super::calibration::osc32k_cal(); + let calibration = crate::calibration::osc32k_cal(); sysctrl.osc32k().write(|w| { unsafe { w.ondemand().clear_bit(); @@ -562,7 +562,7 @@ fn configure_and_enable_dfll48m(sysctrl: &mut Sysctrl, use_external_crystal: boo }); } else { // Apply calibration - let coarse = super::calibration::dfll48m_coarse_cal(); + let coarse = crate::calibration::dfll48m_coarse_cal(); let fine = 0x1ff; sysctrl.dfllval().write(|w| unsafe { diff --git a/hal/src/peripherals/clock/d5x/v1.rs b/hal/src/clock/v1_thumbv7em.rs similarity index 99% rename from hal/src/peripherals/clock/d5x/v1.rs rename to hal/src/clock/v1_thumbv7em.rs index a04166d20068..efdd9572fc9e 100644 --- a/hal/src/peripherals/clock/d5x/v1.rs +++ b/hal/src/clock/v1_thumbv7em.rs @@ -15,7 +15,7 @@ use crate::clock::v2::pclk::{Pclk, PclkSourceId, ids::*}; use crate::pac::gclk::genctrl::Srcselect::*; use crate::pac::gclk::pchctrl::Genselect::*; use crate::pac::{self, Gclk, Mclk, Nvmctrl, Osc32kctrl, Oscctrl}; -use crate::sercom::*; +// use crate::sercom::*; // TODO why does this warn now but not before? use crate::time::Hertz; pub type ClockGenId = pac::gclk::pchctrl::Genselect; @@ -439,6 +439,7 @@ clock_generator!( (tc0_tc1, Tc0Tc1Clock, TC0_TC1, Tc0Tc1), (tcc0_tcc1, Tcc0Tcc1Clock, TCC0_TCC1, Tcc0Tcc1), (tc2_tc3, Tc2Tc3Clock, TC2_TC3, Tc2Tc3), + #[hal_cfg(all("tcc2", "tcc3"))] (tcc2_tcc3, Tcc2Tcc3Clock, TCC2_TCC3, Tcc2Tcc3), #[hal_cfg(all("tc4", "tc5"))] (tc4_tc5, Tc4Tc5Clock, TC4_TC5, Tc4Tc5), diff --git a/hal/src/peripherals/clock/d5x/v2.rs b/hal/src/clock/v2.rs similarity index 93% rename from hal/src/peripherals/clock/d5x/v2.rs rename to hal/src/clock/v2.rs index c815762c2298..cbd0a036a94c 100644 --- a/hal/src/peripherals/clock/d5x/v2.rs +++ b/hal/src/clock/v2.rs @@ -65,7 +65,7 @@ //! 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`]). +//! 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`]). @@ -115,9 +115,9 @@ //! 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. +//! 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. //! @@ -175,9 +175,9 @@ //! 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. +//! 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 @@ -194,8 +194,8 @@ //! //! 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. +//! 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 @@ -219,13 +219,13 @@ //! 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. +//! Each of the PAC clocking structs (which vary between targets, including +//! `OSCCTRL`, `SYSCTRL`, `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' @@ -238,8 +238,8 @@ //! 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. +//! 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 @@ -403,24 +403,24 @@ //! //! 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. +//! this 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`] +//! `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. +//! [`DpllSourceId`] trait. Specifically, only the [`Xosc0Id`], `Xosc1Id` (only +//! some targets), [`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`]. +//! 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`. @@ -461,11 +461,11 @@ //! # ).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. +//! 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`. //! @@ -515,11 +515,11 @@ //! [`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. +//! 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::{ @@ -597,8 +597,8 @@ //! ``` //! //! 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. +//! 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 @@ -768,10 +768,7 @@ //! ``` //! //! [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 @@ -795,9 +792,7 @@ //! [`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 @@ -819,15 +814,12 @@ //! [`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 @@ -850,10 +842,13 @@ //! [`Sub1`]: typenum::Sub1 //! [`Unsigned`]: typenum::Unsigned //! -//! [interior mutability]: https://doc.rust-lang.org/reference/interior-mutability.html +//! [interior mutability]: +//! https://doc.rust-lang.org/reference/interior-mutability.html #![allow(clippy::manual_range_contains)] +use atsamd_hal_macros::hal_module; + use typenum::U0; use crate::time::Hertz; @@ -864,14 +859,32 @@ pub mod apb; pub mod dfll; pub mod dpll; pub mod gclk; +#[hal_module( + any("clock-d11", "clock-d21") => "v2/osc.rs", +)] +pub mod osc {} +#[hal_module( + "sysctrl" => "v2/osc32k.rs", +)] +pub mod osc32k {} pub mod osculp32k; pub mod pclk; -pub mod rtcosc; +#[hal_module( + "clock-d5x" => "v2/rtcosc.rs", +)] +pub mod rtcosc {} pub mod types; pub mod xosc; -pub mod xosc32k; +#[hal_module( + "xosc32k" => "v2/xosc32k.rs" +)] +pub mod xosc32k {} -mod reset; +#[hal_module( + any("clock-d11", "clock-d21") => "v2/reset_thumbv6m.rs", + "clock-d5x" => "v2/reset_thumbv7em.rs", +)] +mod reset {} pub use reset::*; // `Token` types and memory safety diff --git a/hal/src/peripherals/clock/d5x/v2/ahb.rs b/hal/src/clock/v2/ahb.rs similarity index 95% rename from hal/src/peripherals/clock/d5x/v2/ahb.rs rename to hal/src/clock/v2/ahb.rs index 75b7ee27bd14..76b7ead58f5d 100644 --- a/hal/src/peripherals/clock/d5x/v2/ahb.rs +++ b/hal/src/clock/v2/ahb.rs @@ -122,14 +122,24 @@ //! [`Clocks`]: super::Clocks //! [`Buses`]: super::Buses -use atsamd_hal_macros::hal_macro_helper; +use atsamd_hal_macros::{hal_cfg, hal_macro_helper}; use core::marker::PhantomData; use bitflags; use paste::paste; -use crate::pac::{Mclk, mclk}; +#[hal_cfg("clock-d5x")] +mod imports { + pub use crate::pac::{Mclk as Peripheral, mclk::Ahbmask}; +} + +#[hal_cfg(any("clock-d11", "clock-d21"))] +mod imports { + pub use crate::pac::{Pm as Peripheral, pm::Ahbmask}; +} + +use imports::*; use super::types::*; @@ -160,11 +170,11 @@ impl Ahb { } #[inline] - fn ahbmask(&mut self) -> &mclk::Ahbmask { + fn ahbmask(&mut self) -> &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() } + unsafe { (*Peripheral::PTR).ahbmask() } } #[inline] @@ -373,6 +383,7 @@ macro_rules! define_ahb_types { } #[hal_macro_helper] +#[hal_cfg("clock-d5x")] define_ahb_types!( Hpb0 = 0, Hpb1 = 1, @@ -383,7 +394,7 @@ define_ahb_types!( Cmcc = 8, Dmac = 9, Usb = 10, - Pac = 12, + Pac0 = 12, Qspi = 13, #[hal_cfg("gmac")] Gmac = 14, @@ -400,3 +411,14 @@ define_ahb_types!( NvmCtrlSmeeProm = 22, NvmCtrlCache = 23, ); + +#[hal_cfg(any("clock-d11", "clock-d21"))] +define_ahb_types!( + Hpb0 = 0, + Hpb1 = 1, + Hpb2 = 2, + Dsu = 3, + NvmCtrl = 4, + Dmac = 5, + Usb = 6, // TODO this should be conditional. Others? +); diff --git a/hal/src/peripherals/clock/d5x/v2/apb.rs b/hal/src/clock/v2/apb.rs similarity index 65% rename from hal/src/peripherals/clock/d5x/v2/apb.rs rename to hal/src/clock/v2/apb.rs index 57944682144e..1855be8fa7ab 100644 --- a/hal/src/peripherals/clock/d5x/v2/apb.rs +++ b/hal/src/clock/v2/apb.rs @@ -121,13 +121,25 @@ //! [`Clocks`]: super::Clocks //! [`Buses`]: super::Buses -use atsamd_hal_macros::hal_macro_helper; +use atsamd_hal_macros::{hal_cfg, hal_macro_helper}; use core::marker::PhantomData; use bitflags; use paste::paste; -use crate::pac::{self, mclk}; +#[hal_cfg("clock-d5x")] +mod imports { + pub use crate::pac::Mclk as Peripheral; + pub use crate::pac::mclk::{Apbamask, Apbbmask, Apbcmask, Apbdmask, RegisterBlock as BLOCK}; +} + +#[hal_cfg(any("clock-d11", "clock-d21"))] +mod imports { + pub use crate::pac::Pm as Peripheral; + pub use crate::pac::pm::{Apbamask, Apbbmask, Apbcmask, RegisterBlock as BLOCK}; +} + +use imports::*; use crate::typelevel::Sealed; @@ -160,35 +172,37 @@ impl Apb { } #[inline] - fn mclk(&self) -> &mclk::RegisterBlock { + fn mclk(&self) -> &BLOCK { // 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 { &*pac::Mclk::PTR } + unsafe { &*Peripheral::PTR } } #[inline] - fn apbamask(&mut self) -> &mclk::Apbamask { + fn apbamask(&mut self) -> &Apbamask { self.mclk().apbamask() } #[inline] - fn apbbmask(&mut self) -> &mclk::Apbbmask { + fn apbbmask(&mut self) -> &Apbbmask { self.mclk().apbbmask() } #[inline] - fn apbcmask(&mut self) -> &mclk::Apbcmask { + fn apbcmask(&mut self) -> &Apbcmask { self.mclk().apbcmask() } #[inline] - fn apbdmask(&mut self) -> &mclk::Apbdmask { + #[hal_cfg("clock-d5x")] + fn apbdmask(&mut self) -> &Apbdmask { self.mclk().apbdmask() } #[inline] + #[hal_macro_helper] fn enable_mask(&mut self, mask: ApbMask) { // Safety: The mask bits are derived from a `bitflags` struct, so they // are guaranteed to be valid. @@ -206,6 +220,7 @@ impl Apb { self.apbcmask() .modify(|r, w| w.bits(r.bits() | mask.bits())); } + #[hal_cfg("clock-d5x")] ApbMask::D(mask) => { self.apbdmask() .modify(|r, w| w.bits(r.bits() | mask.bits())); @@ -215,6 +230,7 @@ impl Apb { } #[inline] + #[hal_macro_helper] fn disable_mask(&mut self, mask: ApbMask) { // Safety: The mask bits are derived from a `bitflags` struct, so they // are guaranteed to be valid. @@ -232,6 +248,7 @@ impl Apb { self.apbcmask() .modify(|r, w| w.bits(r.bits() & !mask.bits())); } + #[hal_cfg("clock-d5x")] ApbMask::D(mask) => { self.apbdmask() .modify(|r, w| w.bits(r.bits() & !mask.bits())); @@ -270,20 +287,31 @@ impl Apb { /// /// Each variant is a [`bitflags`] struct with a binary representation exactly /// matching the corresponding APB `MASK` register. +#[hal_macro_helper] enum ApbMask { A(ApbAMask), B(ApbBMask), C(ApbCMask), + #[hal_cfg("clock-d5x")] D(ApbDMask), } +/// Define several APB-related types +/// +/// Define the [`DynApbId`], `ApbXMask`, [`ApbTokens`] and [`ApbClks`] types. +/// +/// This macro uses a slight hack to simplify its implementation. It uses +/// `#[cfg(all())]` and `#[cfg(any())]` to represent `#[cfg(true)]` and +/// `#[cfg(false)]`, respectively. We can use this to selectively place each +/// APB type into the [`ApbTokens`] struct or the [`ApbClks`] struct, depending +/// on whether or not the corresponding bit is enabled at power-on reset. macro_rules! define_apb_types { ( $( $Reg:ident { $( $( #[$( $cfg:tt )+] )? - $Type:ident = $BIT:literal, + $Type:ident = ($BIT:literal, $token:ident, $clk:ident) )+ } )+ @@ -348,81 +376,235 @@ macro_rules! define_apb_types { } } } + + /// Set of [`ApbToken`]s for APB clocks that are disabled at power-on reset + pub struct ApbTokens { + $( + $( + $( #[$( $cfg )+] )? + #[cfg($token())] + pub [<$Type:snake>]: ApbToken<$Type>, + )+ + )+ + } + + 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 { + $( + $( + $( #[$( $cfg )+] )? + #[cfg($token())] + [<$Type:snake>]: unsafe { ApbToken::new() }, + )+ + )+ + } + } + } + + /// Set of [`ApbClk`]s for APB clocks that are enabled at power-on reset + pub struct ApbClks { + $( + $( + $( #[$( $cfg )+] )? + #[cfg($clk())] + pub [<$Type:snake>]: ApbClk<$Type>, + )+ + )+ + } + + 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 { + Self { + $( + $( + $( #[$( $cfg )+] )? + #[cfg($clk())] + [<$Type:snake>]: ApbClk::new( unsafe { ApbToken::new() } ), + )+ + )+ + } + } + } } }; } +// (N, all, any) => include in clocks not tokens = enabled at power-on #[hal_macro_helper] +#[hal_cfg("clock-d5x")] 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, + Pac0 = (0, all, any) + Pm = (1, all, any) + Mclk = (2, all, any) + RstC = (3, all, any) + OscCtrl = (4, all, any) + Osc32kCtrl = (5, all, any) + SupC = (6, all, any) + Gclk = (7, all, any) + Wdt = (8, all, any) + Rtc = (9, all, any) + Eic = (10, all, any) + FreqM = (11, any, all) + Sercom0 = (12, any, all) + Sercom1 = (13, any, all) + Tc0 = (14, any, all) + Tc1 = (15, any, all) } 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, + Usb = (0, any, all) + Dsu = (1, all, any) + NvmCtrl = (2, all, any) + Port = (4, all, any) + EvSys = (7, any, all) + Sercom2 = (9, any, all) + Sercom3 = (10, any, all) + Tcc0 = (11, any, all) + Tcc1 = (12, any, all) + Tc2 = (13, any, all) + Tc3 = (14, any, all) + RamEcc = (16, all, any) } C { #[hal_cfg("gmac")] - Gmac = 2, - Tcc2 = 3, + Gmac = (2, all, any) + Tcc2 = (3, any, all) #[hal_cfg("tcc3")] - Tcc3 = 4, + Tcc3 = (4, any, all) #[hal_cfg("tc4")] - Tc4 = 5, + Tc4 = (5, any, all) // TODO double check this is correct #[hal_cfg("tc5")] - Tc5 = 6, - PDec = 7, - Ac = 8, - Aes = 9, - Trng = 10, - Icm = 11, - Qspi = 13, - Ccl = 14, + Tc5 = (6, any, all) + PDec = (7, any, all) + Ac = (8, any, all) + Aes = (9, any, all) + Trng = (10, any, all) + Icm = (11, any, all) + Qspi = (13, all, any) + Ccl = (14, any, all) } D { - Sercom4 = 0, - Sercom5 = 1, + Sercom4 = (0, all, any) + Sercom5 = (1, all, any) #[hal_cfg("sercom6")] - Sercom6 = 2, + Sercom6 = (2, all, any) #[hal_cfg("sercom7")] - Sercom7 = 3, + Sercom7 = (3, all, any) #[hal_cfg("tcc4")] - Tcc4 = 4, + Tcc4 = (4, all, any) #[hal_cfg("tc6")] - Tc6 = 5, + Tc6 = (5, all, any) #[hal_cfg("tc7")] - Tc7 = 6, - Adc0 = 7, - Adc1 = 8, - Dac = 9, + Tc7 = (6, all, any) + Adc0 = (7, all, any) + Adc1 = (8, all, any) + Dac = (9, all, any) + #[hal_cfg("i2s")] + I2S = (10, all, any) + Pcc = (11, all, any) + } +); + +// SAMD21/DA1 datasheet DS40001882H, Table 12-1. Peripherals Configuration +// Summary +#[hal_macro_helper] +#[hal_cfg("clock-d21")] +define_apb_types!( + A { + Pac0 = (0, all, any) + Pm = (1, all, any) + SysCtrl = (2, all, any) + Gclk = (3, all, any) + Wdt = (4, all, any) + Rtc = (5, all, any) + Eic = (6, all, any) + } + B { + Pac1 = (0, all, any) + Dsu = (1, all, any) + NvmCtrl = (2, all, any) + Port = (3, all, any) + Dmac = (4, all, any) + #[hal_cfg("usb")] + Usb = (5, all, any) + } + C { + Pac2 = (0, any, all) + EvSys = (1, any, all) + Sercom0 = (2, any, all) + Sercom1 = (3, any, all) + Sercom2 = (4, any, all) + Sercom3 = (5, any, all) + #[hal_cfg("sercom4")] + Sercom4 = (6, any, all) + #[hal_cfg("sercom5")] + Sercom5 = (7, any, all) + Tcc0 = (8, any, all) + Tcc1 = (9, any, all) + Tcc2 = (10, any, all) + Tc3 = (11, any, all) + Tc4 = (12, any, all) + Tc5 = (13, any, all) + Adc0 = (16, any, all) + Ac = (17, any, all) + Dac = (18, any, all) + Ptc = (19, any, all) #[hal_cfg("i2s")] - I2S = 10, - Pcc = 11, + I2S = (20, any, all) + Ac1 = (21, any, all) + } +); + +// Atmel-42363H-SAM-D11-Datasheet_09/2016, Table 11-1. Peripherals Configuration +// Summary +#[hal_macro_helper] +#[hal_cfg("clock-d11")] +define_apb_types!( + A { + Pac0 = (0, all, any) + Pm = (1, all, any) + SysCtrl = (2, all, any) + Gclk = (3, all, any) + Wdt = (4, all, any) + Rtc = (5, all, any) + Eic = (6, all, any) + } + B { + Pac1 = (0, all, any) + Dsu = (1, all, any) + NvmCtrl = (2, all, any) + Port = (3, all, any) + Dmac = (4, all, any) + #[hal_cfg("usb")] + Usb = (5, all, any) + } + C { + Pac2 = (0, any, all) + EvSys = (1, any, all) + Sercom0 = (2, any, all) + Sercom1 = (3, any, all) + #[hal_cfg("sercom2")] + Sercom2 = (4, any, all) + Tcc0 = (5, any, all) + Tc1 = (6, any, all) + Tc2 = (7, any, all) + Adc0 = (8, any, all) + Ac = (9, any, all) + Dac = (10, any, all) + Ptc = (11, any, all) } ); @@ -503,177 +685,3 @@ impl ApbClk { self.token } } - -//============================================================================== -// ApbTokens -//============================================================================== - -/// Set of [`ApbToken`]s for APB clocks that are disabled at power-on reset -#[hal_macro_helper] -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, - #[hal_cfg("tc4")] - pub tc4: ApbToken, - pub tcc2: ApbToken, - #[hal_cfg("tcc3")] - pub tcc3: ApbToken, - #[hal_cfg("tc5")] - 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, - #[hal_cfg("sercom6")] - pub sercom6: ApbToken, - #[hal_cfg("sercom7")] - pub sercom7: ApbToken, - #[hal_cfg("tcc4")] - pub tcc4: ApbToken, - #[hal_cfg("tc6")] - pub tc6: ApbToken, - #[hal_cfg("tc7")] - pub tc7: ApbToken, - pub adc0: ApbToken, - pub adc1: ApbToken, - pub dac: ApbToken, - #[hal_cfg("i2s")] - 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] - #[hal_macro_helper] - pub(super) unsafe fn new() -> Self { - unsafe { - 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(), - #[hal_cfg("tc4")] - tc4: ApbToken::new(), - tcc2: ApbToken::new(), - #[hal_cfg("tcc3")] - tcc3: ApbToken::new(), - #[hal_cfg("tc5")] - 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(), - #[hal_cfg("sercom6")] - sercom6: ApbToken::new(), - #[hal_cfg("sercom7")] - sercom7: ApbToken::new(), - #[hal_cfg("tcc4")] - tcc4: ApbToken::new(), - #[hal_cfg("tc6")] - tc6: ApbToken::new(), - #[hal_cfg("tc7")] - tc7: ApbToken::new(), - adc0: ApbToken::new(), - adc1: ApbToken::new(), - dac: ApbToken::new(), - #[hal_cfg("i2s")] - i2s: ApbToken::new(), - pcc: ApbToken::new(), - } - } - } -} - -//============================================================================== -// ApbClks -//============================================================================== - -/// Set of [`ApbClk`]s for APB clocks that are enabled at power-on reset -#[hal_macro_helper] -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, - #[hal_cfg("gmac")] - pub gmac: 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] - #[hal_macro_helper] - pub(super) unsafe fn new() -> Self { - unsafe { - 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()), - #[hal_cfg("gmac")] - gmac: ApbClk::new(ApbToken::new()), - qspi: ApbClk::new(ApbToken::new()), - } - } - } -} diff --git a/hal/src/peripherals/clock/d5x/v2/dfll.rs b/hal/src/clock/v2/dfll.rs similarity index 93% rename from hal/src/peripherals/clock/d5x/v2/dfll.rs rename to hal/src/clock/v2/dfll.rs index 58cbceef6af7..3dc39520b4db 100644 --- a/hal/src/peripherals/clock/d5x/v2/dfll.rs +++ b/hal/src/clock/v2/dfll.rs @@ -272,7 +272,24 @@ //! [`from_usb`]: Dfll::from_usb //! [`into_mode`]: EnabledDfll::into_mode +use atsamd_hal_macros::{hal_cfg, hal_macro_helper}; + +#[hal_cfg("oscctrl")] +mod imports { + pub use crate::pac::Oscctrl as PERIPHERAL; + pub use crate::pac::oscctrl::{ + Dfllctrla as Dfllctrl, Dfllctrlb, Dfllmul, Dfllsync, RegisterBlock, + }; +} + +#[hal_cfg("sysctrl")] +mod imports { + pub use crate::pac::Sysctrl as PERIPHERAL; + pub use crate::pac::sysctrl::{Dfllctrl, Dfllmul, RegisterBlock}; +} + use fugit::RateExtU32; +use imports::*; use typenum::U0; use crate::time::Hertz; @@ -309,51 +326,64 @@ impl DfllToken { } #[inline] - fn oscctrl(&self) -> &crate::pac::oscctrl::RegisterBlock { + fn reg_block(&self) -> &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 } + unsafe { &*PERIPHERAL::PTR } + } + + #[hal_cfg("clock-d5x")] + #[inline] + fn dfllctrl(&self) -> &Dfllctrl { + self.reg_block().dfllctrla() } + #[hal_cfg(any("clock-d11", "clock-d21"))] #[inline] - fn dfllctrla(&self) -> &crate::pac::oscctrl::Dfllctrla { - self.oscctrl().dfllctrla() + fn dfllctrl(&self) -> &Dfllctrl { + self.reg_block().dfllctrl() } + #[hal_cfg("clock-d5x")] #[inline] - fn dfllctrlb(&self) -> &crate::pac::oscctrl::Dfllctrlb { - self.oscctrl().dfllctrlb() + fn dfllctrlb(&self) -> &Dfllctrlb { + self.reg_block().dfllctrlb() } #[inline] - fn dfllmul(&self) -> &crate::pac::oscctrl::Dfllmul { - self.oscctrl().dfllmul() + fn dfllmul(&self) -> &Dfllmul { + self.reg_block().dfllmul() } + #[hal_cfg("clock-d5x")] #[inline] - fn dfllsync(&self) -> &crate::pac::oscctrl::Dfllsync { - self.oscctrl().dfllsync() + fn dfllsync(&self) -> &Dfllsync { + self.reg_block().dfllsync() } + #[hal_cfg("clock-d5x")] #[inline] fn wait_sync_enable(&self) { while self.dfllsync().read().enable().bit() {} } + #[hal_cfg("clock-d5x")] #[inline] fn wait_sync_dfllmul(&self) { while self.dfllsync().read().dfllmul().bit() {} } + #[hal_cfg("clock-d5x")] #[inline] fn wait_sync_dfllctrlb(&self) { while self.dfllsync().read().dfllctrlb().bit() {} } + #[hal_cfg("clock-d5x")] #[inline] - fn configure(&mut self, settings: settings::All) { + fn enable(&mut self, settings: settings::All) { self.dfllctrlb().modify(|_, w| { w.mode().bit(settings.closed_loop); w.usbcrm().bit(settings.usb_recovery); @@ -371,27 +401,55 @@ impl DfllToken { }); self.wait_sync_dfllmul(); } - self.dfllctrla().modify(|_, w| { + self.dfllctrl().modify(|_, w| { w.runstdby().bit(settings.run_standby); - w.ondemand().bit(settings.on_demand) + w.ondemand().bit(settings.on_demand); + w.enable().set_bit() }); + self.wait_sync_enable(); } + #[hal_cfg(any("clock-d11", "clock-d21"))] #[inline] - fn enable(&mut self) { - self.dfllctrla().modify(|_, w| w.enable().set_bit()); - self.wait_sync_enable(); + fn enable(&mut self, settings: settings::All) { + if settings.closed_loop { + self.dfllmul().write(|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.dfllctrl().write(|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); + w.runstdby().bit(settings.run_standby); + w.ondemand().bit(settings.on_demand); + w.enable().set_bit() + }); } #[inline] + #[hal_macro_helper] fn disable(&mut self) { - self.dfllctrla().modify(|_, w| w.enable().clear_bit()); + self.dfllctrl().write(|w| w.enable().clear_bit()); + #[hal_cfg("clock-d5x")] self.wait_sync_enable(); } + #[hal_cfg("clock-d5x")] + #[inline] + fn is_ready(&self) -> bool { + self.reg_block().status().read().dfllrdy().bit() + } + + #[hal_cfg(any("clock-d11", "clock-d21"))] #[inline] fn is_ready(&self) -> bool { - self.oscctrl().status().read().dfllrdy().bit() + self.reg_block().pclksr().read().dfllrdy().bit() } } @@ -401,7 +459,10 @@ impl DfllToken { type MultFactor = u16; type CoarseMaxStep = u8; +#[hal_cfg("clock-d5x")] type FineMaxStep = u8; +#[hal_cfg(any("clock-d11", "clock-d21"))] +type FineMaxStep = u16; //============================================================================== // DfllId @@ -1122,8 +1183,7 @@ impl Dfll { /// [`Source`] for other clocks. #[inline] pub fn enable(mut self) -> EnabledDfll { - self.token.configure(self.settings.all()); - self.token.enable(); + self.token.enable(self.settings.all()); Enabled::new(self) } } diff --git a/hal/src/peripherals/clock/d5x/v2/dpll.rs b/hal/src/clock/v2/dpll.rs similarity index 87% rename from hal/src/peripherals/clock/d5x/v2/dpll.rs rename to hal/src/clock/v2/dpll.rs index 7f20db8a820c..f1c35227aeb3 100644 --- a/hal/src/peripherals/clock/d5x/v2/dpll.rs +++ b/hal/src/clock/v2/dpll.rs @@ -1,9 +1,10 @@ -//! # Digital Phase-Locked Loop +//! # Fractional Digital Phase-Locked Loop //! //! ## Overview //! -//! The `dpll` module provides access to the two digital phase-locked loops -//! (DPLLs) within the `OSCCTRL` peripheral. +//! The `dpll` module provides access to the digital phase-locked loops (DPLLs) +//! within the `OSCCTRL` or `SYSCTRL` peripheral, datasheets refer to these as +//! FDPLL200M and FDPLL96M, respectively. //! //! 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 @@ -237,22 +238,41 @@ //! [`GclkDivider`]: super::gclk::GclkDivider //! [`Pclk`]: super::pclk::Pclk +use atsamd_hal_macros::{hal_cfg, hal_macro_helper}; use core::marker::PhantomData; use fugit::RateExtU32; use typenum::U0; -use crate::pac::oscctrl; -use crate::pac::oscctrl::dpll::{Dpllctrla, Dpllctrlb, Dpllratio, dpllstatus, dpllsyncbusy}; - -use crate::pac::oscctrl::dpll::dpllctrlb::Refclkselect; +#[hal_cfg("oscctrl")] +use crate::pac::{ + Oscctrl as Peripheral, + oscctrl::{ + Dpll as PacDpll, + dpll::dpllctrlb::Refclkselect, + dpll::{Dpllctrla, Dpllctrlb, Dpllratio, dpllstatus, dpllsyncbusy}, + }, +}; + +#[hal_cfg("sysctrl")] +use crate::pac::{ + Sysctrl as Peripheral, + sysctrl::{ + RegisterBlock as PacDpll, + dpllctrlb::Refclkselect, + {Dpllctrla, Dpllctrlb, Dpllratio, dpllstatus}, + }, +}; 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}; +#[hal_cfg("xosc1")] +use super::xosc::Xosc1Id; +use super::xosc::{Xosc0Id, XoscId}; +#[hal_cfg("xosc32k")] use super::xosc32k::Xosc32kId; use super::{Enabled, Source}; @@ -292,12 +312,20 @@ impl DpllToken { /// Access the corresponding PAC `DPLL` struct #[inline] - fn dpll(&self) -> &oscctrl::Dpll { + #[hal_macro_helper] + fn dpll(&self) -> &PacDpll { // 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) } + #[hal_cfg("oscctrl")] + unsafe { + (*Peripheral::PTR).dpll(D::NUM) + } + #[hal_cfg("sysctrl")] + unsafe { + &(*Peripheral::PTR) + } } /// Access the corresponding Dpllctrla register @@ -318,6 +346,7 @@ impl DpllToken { self.dpll().dpllratio() } + #[hal_cfg("oscctrl")] /// Access the corresponding DPLLSYNCBUSY register for reading only #[inline] fn syncbusy(&self) -> dpllsyncbusy::R { @@ -331,13 +360,17 @@ impl DpllToken { } #[inline] - fn configure(&mut self, id: DynDpllSourceId, settings: Settings, prediv: u16) { + #[hal_macro_helper] + fn enable(&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, + DynDpllSourceId::Xosc0 => prediv / 2 - 1, + #[hal_cfg("xosc1")] + DynDpllSourceId::Xosc1 => prediv / 2 - 1, _ => 0, }; - self.ctrlb().modify(|_, w| { + + self.ctrlb().write(|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. @@ -345,6 +378,7 @@ impl DpllToken { w.refclk().variant(id.into()); w.lbypass().bit(settings.lock_bypass); w.wuf().bit(settings.wake_up_fast); + #[hal_cfg("oscctrl")] if let Some(cap) = settings.dco_filter { w.dcoen().bit(true); unsafe { @@ -362,25 +396,40 @@ impl DpllToken { w.ldr().bits(settings.mult - 1); w.ldrfrac().bits(settings.frac) }); + #[hal_cfg("oscctrl")] while self.syncbusy().dpllratio().bit_is_set() {} self.ctrla().modify(|_, w| { w.ondemand().bit(settings.on_demand); - w.runstdby().bit(settings.run_standby) + w.runstdby().bit(settings.run_standby); + w.enable().set_bit() }); + self.wait_enabled(); + } + + /// Disable the [`Dpll`] + #[inline] + fn disable(&mut self) { + self.ctrla().modify(|_, w| w.enable().clear_bit()); + self.wait_disabled(); } - /// Enable the [`Dpll`] + /// Waits for the enable bit to synchronize in the enabled state #[inline] - fn enable(&mut self) { - self.ctrla().modify(|_, w| w.enable().set_bit()); + #[hal_macro_helper] + fn wait_enabled(&self) { + #[hal_cfg("oscctrl")] while self.syncbusy().enable().bit_is_set() {} + #[hal_cfg("sysctrl")] + while self.status().enable().bit_is_set() {} } - /// Disable the [`Dpll`] #[inline] - fn disable(&mut self) { - self.ctrla().modify(|_, w| w.enable().clear_bit()); + #[hal_macro_helper] + fn wait_disabled(&self) { + #[hal_cfg("oscctrl")] while self.syncbusy().enable().bit_is_set() {} + #[hal_cfg("sysctrl")] + while self.status().enable().bit_is_set() {} } /// Check the STATUS register to see if the clock is locked @@ -415,15 +464,14 @@ pub enum DynDpllId { // DpllId //============================================================================== -/// Type-level enum identifying one of two possible [`Dpll`]s +/// Type-level enum identifying a [`Dpll`] /// -/// 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. +/// The types implementing this trait, i.e. [`Dpll0Id`] (and `Dpll1Id`, on +/// targets with two DPLLs), identify a specific digital phase-locked loop. /// /// `DpllId` is the type-level equivalent of [`DynDpllId`]. See the -/// documentation on [type-level programming] and specifically -/// [type-level enums] for more details. +/// 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 @@ -457,10 +505,13 @@ impl DpllId for Dpll0Id { /// /// [type-level programming]: crate::typelevel /// [type-level enums]: crate::typelevel#type-level-enums +#[hal_cfg("oscctrl")] pub enum Dpll1Id {} +#[hal_cfg("oscctrl")] impl Sealed for Dpll1Id {} +#[hal_cfg("oscctrl")] impl DpllId for Dpll1Id { const DYN: DynDpllId = DynDpllId::Dpll1; const NUM: usize = 1; @@ -476,18 +527,21 @@ impl DpllId for Dpll1Id { /// a given [`Dpll`]. /// /// `DynDpllSourceId` is the value-level equivalent of [`DpllSourceId`]. +#[hal_macro_helper] #[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, + #[hal_cfg("xosc1")] /// The DPLL is driven by [`Xosc1`](super::xosc::Xosc1) Xosc1, /// The DPLL is driven by [`Xosc32k`](super::xosc32k::Xosc32k) Xosc32k, } +#[hal_cfg("oscctrl")] impl From for Refclkselect { fn from(source: DynDpllSourceId) -> Self { match source { @@ -499,6 +553,16 @@ impl From for Refclkselect { } } +#[hal_cfg("sysctrl")] +impl From for Refclkselect { + fn from(source: DynDpllSourceId) -> Self { + match source { + DynDpllSourceId::Pclk => Refclkselect::Gclk, + DynDpllSourceId::Xosc0 => Refclkselect::Ref1, + DynDpllSourceId::Xosc32k => Refclkselect::Ref0, + } + } +} //============================================================================== // DpllSourceId //============================================================================== @@ -533,10 +597,12 @@ impl DpllSourceId for Xosc0Id { const DYN: DynDpllSourceId = DynDpllSourceId::Xosc0; type Reference = settings::Xosc; } +#[hal_cfg("xosc1")] impl DpllSourceId for Xosc1Id { const DYN: DynDpllSourceId = DynDpllSourceId::Xosc1; type Reference = settings::Xosc; } +#[hal_cfg("xosc32k")] impl DpllSourceId for Xosc32kId { const DYN: DynDpllSourceId = DynDpllSourceId::Xosc32k; type Reference = settings::Xosc32k; @@ -546,6 +612,12 @@ impl DpllSourceId for Xosc32kId { // Settings //============================================================================== +/// Fractional divider for the `LDRFRAC` field +#[hal_cfg("sysctrl")] +pub const FRAC_DIV: u8 = 16; +#[hal_cfg("oscctrl")] +pub const FRAC_DIV: u8 = 32; + /// [`Dpll`] Proportional Integral Filter /// /// Filter settings affect PLL stability and jitter. The datasheet suggests a @@ -610,6 +682,7 @@ pub enum DcoFilter { /// [`Dpll`] settings relevant to all reference clocks #[derive(Copy, Clone)] +#[hal_macro_helper] struct Settings { mult: u16, frac: u8, @@ -618,13 +691,16 @@ struct Settings { on_demand: bool, run_standby: bool, filter: PiFilter, + #[hal_cfg("oscctrl")] dco_filter: Option, } /// Store and retrieve [`Dpll`] settings for different reference clocks mod settings { use super::super::pclk; + #[hal_cfg("xosc32k")] use super::RateExtU32; + use super::hal_cfg; use super::{DpllId, GclkId, Hertz}; /// [`Dpll`] settings when referenced to a [`Pclk`] @@ -648,6 +724,7 @@ mod settings { /// /// [`Dpll`]: super::Dpll /// [`Xosc32k`]: super::super::xosc32k::Xosc32k + #[hal_cfg("xosc32k")] pub struct Xosc32k; /// Generic interface for the frequency and predivider of a reference clock @@ -678,6 +755,7 @@ mod settings { } } + #[hal_cfg("xosc32k")] impl Reference for Xosc32k { #[inline] fn freq(&self) -> Hertz { @@ -699,12 +777,12 @@ mod settings { /// 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). +/// The type parameter `D` is a [`DpllId`] that determines which of the one or +/// two (depending on the target) 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 @@ -733,6 +811,7 @@ where pub type Dpll0 = Dpll; /// Type alias for the corresponding [`Dpll`] +#[hal_cfg("oscctrl")] pub type Dpll1 = Dpll; impl Dpll @@ -740,6 +819,7 @@ where D: DpllId, I: DpllSourceId, { + #[hal_macro_helper] fn new(token: DpllToken, reference: I::Reference) -> Self { let settings = Settings { mult: 1, @@ -749,6 +829,7 @@ where on_demand: true, run_standby: false, filter: PiFilter::Bw92p7kHzDf0p76, + #[hal_cfg("oscctrl")] dco_filter: None, }; Self { @@ -859,6 +940,7 @@ where } } +#[hal_cfg("xosc32k")] impl Dpll { /// Create a [`Dpll`] from an [`Xosc32k`] /// @@ -908,7 +990,7 @@ where /// parts of the division factor, i.e. the division factor is: /// /// ```text - /// int + frac / 32 + /// int + frac / FRAC_DIV /// ``` /// /// This function will confirm that the `int` and `frac` values convert to @@ -918,7 +1000,7 @@ where if int < 1 || int > 0x2000 { panic!("Invalid integer part of the DPLL loop divider") } - if frac > 31 { + if frac > FRAC_DIV - 1 { panic!("Invalid fractional part of the DPLL loop divider") } self.settings.mult = int; @@ -959,6 +1041,7 @@ where /// Enable sigma-delta DAC low pass filter #[inline] + #[hal_cfg("oscctrl")] pub fn dco_filter(mut self, capacitor: DcoFilter) -> Self { self.settings.dco_filter = Some(capacitor); self @@ -991,15 +1074,15 @@ where #[inline] fn output_freq(&self) -> Hertz { // The actual formula is: - // y = x * (mult + frac / 32) + // y = x * (mult + frac / FRAC_DIV) // To avoid integer precision loss, the formula is restructured: - // y = x * (mult + frac / 32) * 32 / 32 - // y = x * (32 * mult + 32 * frac / 32) / 32 - // y = x * (32 * mult + frac) / 32 + // y = x * (mult + frac / FRAC_DIV) * FRAC_DIV / FRAC_DIV + // y = x * (FRAC_DIV * mult + FRAC_DIV * frac / FRAC_DIV) / FRAC_DIV + // y = x * (FRAC_DIV * mult + frac) / FRAC_DIV let input = self.input_freq().to_Hz() as u64; - let multiplier_times_32 = - (32 * self.settings.mult as u32 + self.settings.frac as u32) as u64; - let output = (input * multiplier_times_32 / 32) as u32; + let multiplier_scaled = + (FRAC_DIV as u32 * self.settings.mult as u32 + self.settings.frac as u32) as u64; + let output = (input * multiplier_scaled / FRAC_DIV as u64) as u32; output.Hz() } @@ -1028,12 +1111,29 @@ where /// frequency is invalid, this call will panic. #[inline] pub fn enable(self) -> EnabledDpll { + const INPUT_MIN: u32 = 32_000; + + #[hal_cfg("sysctrl")] + const INPUT_MAX: u32 = 2_000_000; + #[hal_cfg("oscctrl")] + const INPUT_MAX: u32 = 3_200_000; + + #[hal_cfg("sysctrl")] + const OUTPUT_MIN: u32 = 48_000_000; + #[hal_cfg("oscctrl")] + const OUTPUT_MIN: u32 = 96_000_000; + + #[hal_cfg("sysctrl")] + const OUTPUT_MAX: u32 = 96_000_000; + #[hal_cfg("oscctrl")] + const OUTPUT_MAX: u32 = 200_000_000; + let input_freq = self.input_freq().to_Hz(); let output_freq = self.output_freq().to_Hz(); - if input_freq < 32_000 || input_freq > 3_200_000 { + if input_freq < INPUT_MIN || input_freq > INPUT_MAX { panic!("Invalid DPLL input frequency"); } - if output_freq < 96_000_000 || output_freq > 200_000_000 { + if output_freq < OUTPUT_MIN || output_freq > OUTPUT_MAX { panic!("Invalid DPLL output frequency"); } self.enable_unchecked() @@ -1049,8 +1149,7 @@ where 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(); + self.token.enable(I::DYN, self.settings, prediv); Enabled::new(self) } } @@ -1074,6 +1173,7 @@ pub type EnabledDpll = Enabled, N>; pub type EnabledDpll0 = EnabledDpll; /// Type alias for the corresponding [`EnabledDpll`] +#[hal_cfg("oscctrl")] pub type EnabledDpll1 = EnabledDpll; impl EnabledDpll diff --git a/hal/src/peripherals/clock/d5x/v2/gclk.rs b/hal/src/clock/v2/gclk.rs similarity index 71% rename from hal/src/peripherals/clock/d5x/v2/gclk.rs rename to hal/src/clock/v2/gclk.rs index 18a28eddf99e..af07992cd9b6 100644 --- a/hal/src/peripherals/clock/d5x/v2/gclk.rs +++ b/hal/src/clock/v2/gclk.rs @@ -83,10 +83,10 @@ //! //! 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 +//! [`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`, which is a `GclkSourceId` for +//! `Gclk1` on this target, because it implements [`GclkIo`] with //! [`GclkIo::GclkId`]` = Gclk1Id`. //! //! ```no_run @@ -335,27 +335,34 @@ //! [`Pins`]: crate::gpio::Pins //! [`Sercom0`]: crate::sercom::Sercom0 +use atsamd_hal_macros::{hal_cfg, hal_macro_helper}; 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::Nvmctrl; -use crate::pac::gclk::genctrl::Divselselect; -use crate::gpio::{self, AlternateM, AnyPin, Pin, PinId}; +use crate::gpio::{self, AlternateH, AnyPin, Pin, PinId}; use crate::pac::gclk::Genctrl; +#[hal_cfg(any("clock-d11", "clock-d21"))] +use crate::pac::gclk::Gendiv; use crate::pac::gclk::genctrl::Srcselect; use crate::time::Hertz; use crate::typelevel::{Decrement, Increment, PrivateDecrement, PrivateIncrement, Sealed}; use super::dfll::DfllId; -use super::dpll::{Dpll0Id, Dpll1Id}; +use super::dpll::Dpll0Id; +#[hal_cfg("oscctrl")] +use super::dpll::Dpll1Id; +#[hal_cfg(any("clock-d11", "clock-d21"))] +use super::osc::OscId; use super::osculp32k::OscUlp32kId; -use super::xosc::{Xosc0Id, Xosc1Id}; +use super::xosc::Xosc0Id; +#[hal_cfg("xosc1")] +use super::xosc::Xosc1Id; +#[hal_cfg("xosc32k")] use super::xosc32k::Xosc32kId; use super::{Enabled, Source}; @@ -369,12 +376,13 @@ use super::{Enabled, Source}; /// 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`]s are no different. [`Gclk`]s other than [`Gclk0`], and +/// [`Gclk2`] on SAMD21/SAMD11, 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. +/// represents one of the [`Gclk`]s. pub struct GclkToken { generator: PhantomData, } @@ -395,16 +403,31 @@ impl GclkToken { } /// SYNCBUSY register mask for the corresponding GCLK + #[hal_cfg("clock-d5x")] const MASK: u16 = 1 << G::NUM; /// Provide a reference to the corresponding [`Genctrl`] register #[inline] + #[hal_macro_helper] 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) } + #[hal_cfg("clock-d5x")] + unsafe { + (*pac::Gclk::PTR).genctrl(G::NUM) + } + #[hal_cfg(any("clock-d11", "clock-d21"))] + unsafe { + (*pac::Gclk::PTR).genctrl() + } + } + + #[hal_cfg(any("clock-d11", "clock-d21"))] + #[inline] + fn gendiv(&self) -> &Gendiv { + unsafe { (*pac::Gclk::PTR).gendiv() } } /// Block until synchronization has completed @@ -412,97 +435,21 @@ impl GclkToken { /// Reads or writes to synchronized fields must be accompanied by a check of /// the `SYNCBUSY` register. See the datasheet for more details. #[inline] + #[hal_macro_helper] 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(); + #[hal_cfg("clock-d5x")] + { + let syncbusy = unsafe { &(*pac::Gclk::PTR).syncbusy() }; + while syncbusy.read().genctrl().bits() & Self::MASK != 0 {} + } + #[hal_cfg(any("clock-d11", "clock-d21"))] + { + let status = unsafe { &(*pac::Gclk::PTR).status() }; + while status.read().syncbusy().bit() {} + } } } @@ -510,12 +457,13 @@ impl GclkToken { // DynGclkId //============================================================================== -/// Value-level enum identifying one of 12 possible [`Gclk`]s +/// Value-level enum identifying one of the possible [`Gclk`]s /// -/// The variants of this enum identify one of the 12 possible generic clock -/// generators. +/// The variants of this enum identify one generic clock generator. /// /// `DynGclkId` is the value-level equivalent of [`GclkId`]. +#[derive(Clone, Copy, PartialEq, Eq)] +#[hal_macro_helper] pub enum DynGclkId { Gclk0, Gclk1, @@ -523,27 +471,59 @@ pub enum DynGclkId { Gclk3, Gclk4, Gclk5, + #[hal_cfg("gclk6")] Gclk6, + #[hal_cfg("gclk7")] Gclk7, + #[hal_cfg("gclk8")] Gclk8, + #[hal_cfg("gclk9")] Gclk9, + #[hal_cfg("gclk10")] Gclk10, + #[hal_cfg("gclk11")] Gclk11, } +impl core::fmt::Debug for DynGclkId { + #[hal_macro_helper] + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::Gclk0 => write!(f, "Gclk0"), + Self::Gclk1 => write!(f, "Gclk1"), + Self::Gclk2 => write!(f, "Gclk2"), + Self::Gclk3 => write!(f, "Gclk3"), + Self::Gclk4 => write!(f, "Gclk4"), + Self::Gclk5 => write!(f, "Gclk5"), + #[hal_cfg("gclk6")] + Self::Gclk6 => write!(f, "Gclk6"), + #[hal_cfg("gclk7")] + Self::Gclk7 => write!(f, "Gclk7"), + #[hal_cfg("gclk8")] + Self::Gclk8 => write!(f, "Gclk8"), + #[hal_cfg("gclk9")] + Self::Gclk9 => write!(f, "Gclk9"), + #[hal_cfg("gclk10")] + Self::Gclk10 => write!(f, "Gclk10"), + #[hal_cfg("gclk11")] + Self::Gclk11 => write!(f, "Gclk11"), + } + } +} + //============================================================================== // GclkId //============================================================================== -/// Type-level enum identifying one of 12 possible [`Gclk`]s +/// Type-level enum identifying one of 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 +/// The types implementing this trait, i.e. [`Gclk0Id`] - `Gclk11Id`, are +/// type-level variants of `GclkId`, and they identify one of the 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. +/// 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 @@ -589,25 +569,43 @@ impl GclkId for Gclk1Id { 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; +macro_rules! make_gclk_id { + ($num:literal) => { + paste! { + #[doc = GCLK $num] + /// + /// 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::[]; + const NUM: usize = $num; + type Divider = GclkDiv8; + } } - } -}); + }; +} + +make_gclk_id!(2); +make_gclk_id!(3); +make_gclk_id!(4); +make_gclk_id!(5); +#[hal_cfg("gclk6")] +make_gclk_id!(6); +#[hal_cfg("gclk7")] +make_gclk_id!(7); +#[hal_cfg("gclk8")] +make_gclk_id!(8); +#[hal_cfg("gclk9")] +make_gclk_id!(9); +#[hal_cfg("gclk10")] +make_gclk_id!(10); +#[hal_cfg("gclk11")] +make_gclk_id!(11); //============================================================================== // GclkDivider @@ -631,7 +629,7 @@ 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) -> (Divselselect, u16); + fn divsel_div(&self) -> (bool, u16); } //============================================================================== @@ -678,11 +676,11 @@ impl GclkDivider for GclkDiv8 { } #[inline] - fn divsel_div(&self) -> (Divselselect, u16) { + fn divsel_div(&self) -> (bool, u16) { match self { - GclkDiv8::Div(div) => (Divselselect::Div1, (*div).into()), - GclkDiv8::Div2Pow8 => (Divselselect::Div2, 7), - GclkDiv8::Div2Pow9 => (Divselselect::Div2, 8), + GclkDiv8::Div(div) => (false, (*div).into()), + GclkDiv8::Div2Pow8 => (true, 7), + GclkDiv8::Div2Pow9 => (true, 8), } } } @@ -730,11 +728,11 @@ impl GclkDivider for GclkDiv16 { } #[inline] - fn divsel_div(&self) -> (Divselselect, u16) { + fn divsel_div(&self) -> (bool, u16) { match self { - GclkDiv16::Div(div) => (Divselselect::Div1, *div), - GclkDiv16::Div2Pow16 => (Divselselect::Div2, 15), - GclkDiv16::Div2Pow17 => (Divselselect::Div2, 16), + GclkDiv16::Div(div) => (false, *div), + GclkDiv16::Div2Pow16 => (true, 15), + GclkDiv16::Div2Pow17 => (true, 16), } } } @@ -756,6 +754,7 @@ pub trait GclkIo: PinId { // These implementations are much easier to read with `#[rustfmt::skip]` #[rustfmt::skip] +#[hal_cfg("clock-d5x")] mod gclkio_impl { use atsamd_hal_macros::hal_cfg; @@ -807,6 +806,80 @@ mod gclkio_impl { impl GclkIo for gpio::PB23 { type GclkId = Gclk1Id; } } +#[rustfmt::skip] +#[hal_cfg("clock-d21")] +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; } + #[hal_cfg("pa20")] + impl GclkIo for gpio::PA20 { type GclkId = Gclk4Id; } + #[hal_cfg("pa21")] + impl GclkIo for gpio::PA21 { type GclkId = Gclk5Id; } + impl GclkIo for gpio::PA22 { type GclkId = Gclk6Id; } + impl GclkIo for gpio::PA23 { type GclkId = Gclk7Id; } + #[hal_cfg("pa27")] + impl GclkIo for gpio::PA27 { type GclkId = Gclk0Id; } + #[hal_cfg("pa28")] + impl GclkIo for gpio::PA28 { type GclkId = Gclk0Id; } + impl GclkIo for gpio::PA30 { type GclkId = Gclk0Id; } + + #[hal_cfg("pb10")] + impl GclkIo for gpio::PB10 { type GclkId = Gclk4Id; } + #[hal_cfg("pb11")] + impl GclkIo for gpio::PB11 { type GclkId = Gclk5Id; } + #[hal_cfg("pb12")] + impl GclkIo for gpio::PB12 { type GclkId = Gclk6Id; } + #[hal_cfg("pb13")] + impl GclkIo for gpio::PB13 { type GclkId = Gclk7Id; } + #[hal_cfg("pb14")] + impl GclkIo for gpio::PB14 { type GclkId = Gclk0Id; } + #[hal_cfg("pb15")] + impl GclkIo for gpio::PB15 { type GclkId = Gclk1Id; } + #[hal_cfg("pb16")] + impl GclkIo for gpio::PB16 { type GclkId = Gclk2Id; } + #[hal_cfg("pb17")] + impl GclkIo for gpio::PB17 { type GclkId = Gclk3Id; } + #[hal_cfg("pb22")] + impl GclkIo for gpio::PB22 { type GclkId = Gclk0Id; } + #[hal_cfg("pb23")] + impl GclkIo for gpio::PB23 { type GclkId = Gclk1Id; } +} + +#[rustfmt::skip] +#[hal_cfg("clock-d11")] +mod gclkio_impl { + use super::*; + + impl GclkIo for gpio::PA08 { type GclkId = Gclk0Id; } + impl GclkIo for gpio::PA09 { type GclkId = Gclk1Id; } + #[hal_cfg("pa10")] + impl GclkIo for gpio::PA10 { type GclkId = Gclk4Id; } + #[hal_cfg("pa11")] + impl GclkIo for gpio::PA11 { type GclkId = Gclk5Id; } + impl GclkIo for gpio::PA14 { type GclkId = Gclk4Id; } + impl GclkIo for gpio::PA15 { type GclkId = Gclk5Id; } + #[hal_cfg("pa16")] + impl GclkIo for gpio::PA16 { type GclkId = Gclk2Id; } + #[hal_cfg("pa17")] + impl GclkIo for gpio::PA17 { type GclkId = Gclk3Id; } + #[hal_cfg("pa22")] + impl GclkIo for gpio::PA22 { type GclkId = Gclk1Id; } + #[hal_cfg("pa23")] + impl GclkIo for gpio::PA23 { type GclkId = Gclk2Id; } + impl GclkIo for gpio::PA24 { type GclkId = Gclk0Id; } + impl GclkIo for gpio::PA25 { type GclkId = Gclk0Id; } + #[hal_cfg("pa27")] + impl GclkIo for gpio::PA27 { type GclkId = Gclk0Id; } + impl GclkIo for gpio::PA30 { type GclkId = Gclk0Id; } + impl GclkIo for gpio::PA31 { type GclkId = Gclk0Id; } +} + //============================================================================== // Gclk0Io //============================================================================== @@ -815,13 +888,13 @@ mod gclkio_impl { /// /// 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 +/// `GclkIo`. The trait is useful to simplify some function /// signatures and to help type inference in a few cases. pub trait Gclk0Io where Self: Sized, Self: GclkIo, - Self: GclkSourceId>, + Self: GclkSourceId>, { } @@ -837,32 +910,54 @@ impl> Gclk0Io for I {} /// a given [`Gclk`]. /// /// `DynGclkSourceId` is the value-level equivalent of [`GclkSourceId`]. +#[hal_macro_helper] #[derive(Copy, Clone, PartialEq, Eq)] pub enum DynGclkSourceId { Dfll, Dpll0, + #[hal_cfg("clock-d5x")] Dpll1, Gclk1, GclkIn, + #[hal_cfg(any("clock-d11", "clock-d21"))] + Osc8M, + #[hal_cfg(any("clock-d11", "clock-d21"))] + Osc32k, OscUlp32k, Xosc0, + #[hal_cfg("clock-d5x")] Xosc1, Xosc32k, } impl From for Srcselect { + #[hal_cfg("clock-d5x")] + fn from(source: DynGclkSourceId) -> Self { + match source { + DynGclkSourceId::Dfll => Srcselect::Dfll, + DynGclkSourceId::Dpll0 => Srcselect::Dpll0, + DynGclkSourceId::Dpll1 => Srcselect::Dpll1, + DynGclkSourceId::Gclk1 => Srcselect::Gclkgen1, + DynGclkSourceId::GclkIn => Srcselect::Gclkin, + DynGclkSourceId::OscUlp32k => Srcselect::Osculp32k, + DynGclkSourceId::Xosc0 => Srcselect::Xosc0, + DynGclkSourceId::Xosc1 => Srcselect::Xosc1, + DynGclkSourceId::Xosc32k => Srcselect::Xosc32k, + } + } + + #[hal_cfg(any("clock-d11", "clock-d21"))] fn from(source: DynGclkSourceId) -> Self { - use DynGclkSourceId::*; match source { - Dfll => Self::Dfll, - Dpll0 => Self::Dpll0, - Dpll1 => Self::Dpll1, - Gclk1 => Self::Gclkgen1, - GclkIn => Self::Gclkin, - OscUlp32k => Self::Osculp32k, - Xosc0 => Self::Xosc0, - Xosc1 => Self::Xosc1, - Xosc32k => Self::Xosc32k, + DynGclkSourceId::Dfll => Srcselect::Dfll48m, + DynGclkSourceId::Dpll0 => Srcselect::Dpll96m, + DynGclkSourceId::Gclk1 => Srcselect::Gclkgen1, + DynGclkSourceId::GclkIn => Srcselect::Gclkin, + DynGclkSourceId::Osc8M => Srcselect::Osc8m, + DynGclkSourceId::Osc32k => Srcselect::Osc32k, + DynGclkSourceId::OscUlp32k => Srcselect::Osculp32k, + DynGclkSourceId::Xosc0 => Srcselect::Xosc, + DynGclkSourceId::Xosc32k => Srcselect::Xosc32k, } } } @@ -904,6 +999,7 @@ impl GclkSourceId for Dpll0Id { const DYN: DynGclkSourceId = DynGclkSourceId::Dpll0; type Resource = (); } +#[hal_cfg("oscctrl")] impl GclkSourceId for Dpll1Id { const DYN: DynGclkSourceId = DynGclkSourceId::Dpll1; type Resource = (); @@ -914,20 +1010,27 @@ impl GclkSourceId for Gclk1Id { } impl GclkSourceId for I { const DYN: DynGclkSourceId = DynGclkSourceId::GclkIn; - type Resource = Pin; + type Resource = Pin; } impl GclkSourceId for OscUlp32kId { const DYN: DynGclkSourceId = DynGclkSourceId::OscUlp32k; type Resource = (); } +#[hal_cfg(any("clock-d11", "clock-d21"))] +impl GclkSourceId for OscId { + const DYN: DynGclkSourceId = DynGclkSourceId::Osc8M; + type Resource = (); +} impl GclkSourceId for Xosc0Id { const DYN: DynGclkSourceId = DynGclkSourceId::Xosc0; type Resource = (); } +#[hal_cfg("xosc1")] impl GclkSourceId for Xosc1Id { const DYN: DynGclkSourceId = DynGclkSourceId::Xosc1; type Resource = (); } +#[hal_cfg("xosc32k")] impl GclkSourceId for Xosc32kId { const DYN: DynGclkSourceId = DynGclkSourceId::Xosc32k; type Resource = (); @@ -955,11 +1058,16 @@ impl> NotGclkIo for I {} // Settings //============================================================================== -/// Collection of [`Gclk`] settings to configure on enable +/// Collection of [`Gclk`] settings +/// +/// This structure is largely required due to the thumbv6m chips sharing one +/// GENCTRL register among all the clock generators. On these chips, all fields +/// in the register need to be updated by a 32b atomic write. struct Settings { div: G::Divider, output_off_value: bool, improve_duty_cycle: bool, + output_enable: bool, } impl Clone for Settings { @@ -976,6 +1084,7 @@ impl Default for Settings { div: G::Divider::default(), output_off_value: false, improve_duty_cycle: false, + output_enable: false, } } } @@ -990,13 +1099,13 @@ impl Default for Settings { /// 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. +/// The type parameter `G` is a [`GclkId`] that determines which of the +/// generators this [`Gclk`] represents ([`Gclk0`] - `Gclk11` on the largest +/// targets). 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 @@ -1054,15 +1163,35 @@ pub type Gclk0 = Gclk; /// 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>; +macro_rules! make_gclk { + ($num:literal) => { + paste! { + /// Type alias for the corresponding [`Gclk`] + pub type [] = Gclk<[], I>; - /// Type alias for the corresponding [`EnabledGclk`] - pub type EnabledGclk~G = EnabledGclk<[], I, N>; - } -}); + /// Type alias for the corresponding [`EnabledGclk`] + pub type [] = EnabledGclk<[], I, N>; + } + }; +} + +make_gclk!(1); +make_gclk!(2); +make_gclk!(3); +make_gclk!(4); +make_gclk!(5); +#[hal_cfg("gclk6")] +make_gclk!(6); +#[hal_cfg("gclk7")] +make_gclk!(7); +#[hal_cfg("gclk8")] +make_gclk!(8); +#[hal_cfg("gclk9")] +make_gclk!(9); +#[hal_cfg("gclk10")] +make_gclk!(10); +#[hal_cfg("gclk11")] +make_gclk!(11); impl Gclk where @@ -1098,7 +1227,7 @@ where /// /// Freeing a [`Gclk`] returns the corresponding [`GclkToken`] and GPIO /// [`Pin`]. - pub fn free_pin(self) -> (GclkToken, Pin) { + pub fn free_pin(self) -> (GclkToken, Pin) { (self.token, self.resource) } } @@ -1153,21 +1282,72 @@ where G: GclkId, I: GclkSourceId, { + /// Updates the GENCTRL register based on self.settings + /// + /// The thumbv7em chips use one GENCTRL per clock generator and are capable + /// of read-modify-write operations, but the thumbv6 chips share one GENCTRL + /// register among the clock generators so only support writes. To + /// accommodate both, this implementation maintains a [`Settings`] struct + /// containing the GENCTRL settings, and this method is called after + /// updating it to apply them. + #[hal_macro_helper] + fn update_genctrl(&self, genen: bool) { + let (divsel, div) = self.settings.div.divsel_div(); + + #[hal_cfg("clock-d5x")] + self.token.genctrl().modify(|_, w| { + w.divsel().bit(divsel); + // Safety: The `DIVSEL` and `DIV` values are derived from the + // `GclkDivider` type, so they are guaranteed to be valid. + unsafe { + w.div().bits(div); + } + w.oe().bit(self.settings.output_enable); + w.oov().bit(self.settings.output_off_value); + w.idc().bit(self.settings.improve_duty_cycle); + w.genen().bit(genen); + w.src().variant(I::DYN.into()) + }); + + #[hal_cfg(any("clock-d11", "clock-d21"))] + { + // Suppress warning for thumbv7em builds + let _ = div; + + self.token.genctrl().write(|w| { + w.divsel().bit(divsel); + w.oe().bit(self.settings.output_enable); + w.oov().bit(self.settings.output_off_value); + w.idc().bit(self.settings.improve_duty_cycle); + w.genen().bit(genen); + w.src().variant(I::DYN.into()); + unsafe { w.id().bits(G::NUM as u8) } + }); + } + + self.token.wait_syncbusy(); + } + /// Modify the source of an existing clock /// /// This is a helper function for swapping Gclk0 to different clock sources. fn change_source( - mut self, + 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, }; + + // Call update_genctrl() on object that has the correct source type + gclk.update_genctrl( + // This method is always called on Gclk0, which is always enabled + true, + ); (gclk, self.resource) } @@ -1212,33 +1392,43 @@ where /// 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 + /// the `Pin` is configured in [`AlternateH`] mode. `Pin`s are automatically + /// set to `AlternateH` 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 + /// OOV, you must *manually* set it to `AlternateH` 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.update_genctrl( + // This method is only accessible via disabled GCLKs + false, + ); + 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(); + #[hal_macro_helper] + pub fn enable(self) -> EnabledGclk { + #[hal_cfg(any("clock-d11", "clock-d21"))] + { + let (_divsel, div) = self.settings.div.divsel_div(); + self.token.gendiv().write(|w| unsafe { + w.id().bits(G::NUM as u8); + w.div().bits(div) + }); + self.token.wait_syncbusy(); + } + + self.update_genctrl(true); + Enabled::new(self) } } @@ -1253,8 +1443,8 @@ where /// 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(); + pub fn disable(self) -> Gclk { + self.0.update_genctrl(false); self.0 } } @@ -1276,6 +1466,11 @@ impl EnabledGclk0 { /// Swap [`Gclk0`] from one clock [`Source`] to another /// /// `Gclk0` will remain fully enabled during the swap. + /// + /// Note for thumbv6m chips: Before switching the Generic Clock Generator 0 + /// (GCLKGEN0) from a clock source A to another clock source B, enable the + /// "ONDEMAND" feature of the clock source A to ensure a proper transition + /// from clock source A to clock source B. #[inline] pub fn swap_sources(self, old: O, new: N) -> (EnabledGclk0, O::Dec, N::Inc) where @@ -1291,12 +1486,13 @@ impl EnabledGclk0 { /// Swap [`Gclk0`] from one [`GclkIo`] [`Pin`] to another /// /// `Gclk0` will remain fully enabled during the swap. + /// TODO there's only one input IO pad per... #[inline] pub fn swap_pins

( self, pin: P, freq: impl Into, - ) -> (EnabledGclk0, Pin) + ) -> (EnabledGclk0, Pin) where I: Gclk0Io, P: AnyPin, @@ -1337,7 +1533,7 @@ impl EnabledGclk0 { pub fn swap_pin_for_source( self, source: S, - ) -> (EnabledGclk0, Pin, S::Inc) + ) -> (EnabledGclk0, Pin, S::Inc) where I: Gclk0Io, S: Source + Increment, @@ -1352,16 +1548,31 @@ impl EnabledGclk0 { /// /// 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); + #[hal_macro_helper] + pub fn div(&mut self, divider: GclkDiv8) { + self.0.settings.div = divider; + + // D5x div is in the GENCTRL register, smaller chips keep it separate + #[hal_cfg(any("clock-d11", "clock-d21"))] + { + let (_divsel, div) = divider.divsel_div(); + // Safety: The `DIVSEL` and `DIV` values are derived from the + // `GclkDivider` type, so they are guaranteed to be valid. + self.0.token.gendiv().write(|w| unsafe { + w.id().bits(0); + w.div().bits(div) + }); + self.0.token.wait_syncbusy(); + } + + self.0.update_genctrl(true); } /// 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); + self.0.update_genctrl(true); } /// Return the [`Gclk0`] frequency @@ -1378,7 +1589,7 @@ impl EnabledGclk0 { #[inline] pub fn output_off_value(&mut self, high: bool) { self.0.settings.output_off_value = high; - self.0.token.output_off_value(high); + self.0.update_genctrl(true); } } @@ -1403,38 +1614,62 @@ where // 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<[]>, - )* - } +/// Set of [`GclkToken`]s representing the disabled [`Gclk`]s at +/// power-on reset +#[hal_macro_helper] +pub struct GclkTokens { + pub gclk0: GclkToken, + pub gclk1: GclkToken, + pub gclk2: GclkToken, + pub gclk3: GclkToken, + pub gclk4: GclkToken, + pub gclk5: GclkToken, + #[hal_cfg("gclk6")] + pub gclk6: GclkToken, + #[hal_cfg("gclk7")] + pub gclk7: GclkToken, + #[hal_cfg("gclk8")] + pub gclk8: GclkToken, + #[hal_cfg("gclk9")] + pub gclk9: GclkToken, + #[hal_cfg("gclk10")] + pub gclk10: GclkToken, + #[hal_cfg("gclk11")] + pub gclk11: 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 { - unsafe { - // Use auto wait states - nvmctrl.ctrla().modify(|_, w| w.autows().set_bit()); - GclkTokens { - #( gclk~N: GclkToken::new(), )* - } - } - } +#[hal_macro_helper] +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() -> Self { + GclkTokens { + gclk0: unsafe { GclkToken::new() }, + gclk1: unsafe { GclkToken::new() }, + gclk2: unsafe { GclkToken::new() }, + gclk3: unsafe { GclkToken::new() }, + gclk4: unsafe { GclkToken::new() }, + gclk5: unsafe { GclkToken::new() }, + #[hal_cfg("gclk6")] + gclk6: unsafe { GclkToken::new() }, + #[hal_cfg("gclk7")] + gclk7: unsafe { GclkToken::new() }, + #[hal_cfg("gclk8")] + gclk8: unsafe { GclkToken::new() }, + #[hal_cfg("gclk9")] + gclk9: unsafe { GclkToken::new() }, + #[hal_cfg("gclk10")] + gclk10: unsafe { GclkToken::new() }, + #[hal_cfg("gclk11")] + gclk11: unsafe { GclkToken::new() }, } } -}); +} //============================================================================== // GclkOut @@ -1448,7 +1683,7 @@ seq!(N in 1..=11 { /// See the [module-level documentation](self) for an example of creating a /// [`GclkOut`] from an [`EnabledGclk`]. pub struct GclkOut { - pin: Pin, + pin: Pin, freq: Hertz, } @@ -1480,7 +1715,7 @@ where /// 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 + /// [`AlternateH`] 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) @@ -1490,9 +1725,13 @@ where 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.0.settings.output_enable = true; + self.0.update_genctrl(true); + + let gclk_out = GclkOut { + pin, + freq: self.freq(), + }; (self.inc(), gclk_out) } @@ -1500,18 +1739,20 @@ where /// /// 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 + /// [`AlternateH`] 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) + ) -> (EnabledGclk, Pin) where N: Decrement, I: GclkIo, { - self.0.token.disable_gclk_out(); + self.0.settings.output_enable = false; + self.0.update_genctrl(true); + (self.dec(), gclk_out.pin) } } diff --git a/hal/src/clock/v2/osc.rs b/hal/src/clock/v2/osc.rs new file mode 100644 index 000000000000..d0ebbae9c7a8 --- /dev/null +++ b/hal/src/clock/v2/osc.rs @@ -0,0 +1,171 @@ +//! Open-loop 8MHz oscillator + +use crate::pac::sysctrl::osc8m::{Frangeselect, Prescselect}; +use crate::pac::sysctrl::Osc8m; +use crate::pac::Sysctrl; + +use fugit::RateExtU32; +use crate::time::Hertz; +use crate::typelevel::Sealed; + +use super::{Enabled, Source}; + +pub struct OscToken(()); + +impl OscToken { + pub(super) unsafe fn new() -> Self { + Self(()) + } + + fn osc8m(&self) -> &Osc8m { + unsafe { (*Sysctrl::PTR).osc8m() } + } + + fn enable(&mut self, settings: Settings) { + self.osc8m().modify(|_, w| { + if let Some(freq_range) = settings.freq_range { + w.frange().variant(freq_range.into()); + } + if let Some(calibration) = settings.calibration { + // Safety: The PAC will truncate the value to 12 bits, + // and all 12-bit values are valid + unsafe { w.calib().bits(calibration) }; + } + w.presc().variant(settings.prescaler.into()); + w.ondemand().bit(settings.on_demand); + w.runstdby().bit(settings.run_standby); + w.enable().set_bit() + }); + } + + fn disable(&mut self) { + self.osc8m().modify(|_, w| w.enable().clear_bit()) + } +} + +#[derive(Clone, Copy, PartialEq, Eq)] +struct Settings { + freq_range: Option, + calibration: Option, + prescaler: Prescaler, + on_demand: bool, + run_standby: bool, +} + +#[derive(Clone, Copy, PartialEq, Eq)] +/// Frequency ranges in Megahertz +pub enum FreqRange { + FourToSix, + SixToEight, + EightToEleven, + ElevenToFifteen, +} + +impl From for Frangeselect { + fn from(freq_range: FreqRange) -> Self { + match freq_range { + FreqRange::FourToSix => Self::_0, + FreqRange::SixToEight => Self::_1, + FreqRange::EightToEleven => Self::_2, + FreqRange::ElevenToFifteen => Self::_3, + } + } +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub enum Prescaler { + One, + Two, + Four, + Eight, +} + +impl From for Prescselect { + fn from(prescaler: Prescaler) -> Self { + match prescaler { + Prescaler::One => Self::_0, + Prescaler::Two => Self::_1, + Prescaler::Four => Self::_2, + Prescaler::Eight => Self::_3, + } + } +} + +pub enum OscId {} + +impl Sealed for OscId {} + +pub struct Osc { + token: OscToken, + settings: Settings, +} + +impl Osc { + pub fn new(token: OscToken) -> Self { + let settings = Settings { + freq_range: None, + calibration: None, + prescaler: Prescaler::Eight, + on_demand: true, + run_standby: false, + }; + Self { token, settings } + } + + pub fn freq_range(mut self, freq_range: FreqRange) -> Self { + self.settings.freq_range = Some(freq_range); + self + } + + pub fn calibration(mut self, calibration: u16) -> Self { + self.settings.calibration = Some(calibration); + self + } + + pub fn prescaler(mut self, prescaler: Prescaler) -> Self { + self.settings.prescaler = prescaler; + self + } + + pub fn on_demand(mut self, on_demand: bool) -> Self { + self.settings.on_demand = on_demand; + self + } + + pub fn run_standby(mut self, run_standby: bool) -> Self { + self.settings.run_standby = run_standby; + self + } + + pub fn freq(&self) -> Hertz { + let div = match self.settings.prescaler { + Prescaler::One => 1, + Prescaler::Two => 2, + Prescaler::Four => 4, + Prescaler::Eight => 8, + }; + (8_000_000u32 / div).Hz() + } + + pub fn enable(mut self) -> Enabled { + self.token.enable(self.settings); + Enabled::new(self) + } +} + +pub type EnabledOsc = Enabled; + +impl EnabledOsc { + pub fn disable(mut self) -> Osc { + self.0.token.disable(); + self.0 + } +} + +impl Source for EnabledOsc { + type Id = OscId; + + fn freq(&self) -> Hertz { + self.0.freq() + } +} diff --git a/hal/src/clock/v2/osc32k.rs b/hal/src/clock/v2/osc32k.rs new file mode 100644 index 000000000000..96f3bcfe4fa2 --- /dev/null +++ b/hal/src/clock/v2/osc32k.rs @@ -0,0 +1,587 @@ +//! # Tunable, low-speed and low-power clock source +//! +//! ## Overview +//! +//! The `osc32k` module provides access to the 32 kHz oscillator (OSC32K), +//! provided by the `SYSCTRL` peripheral. Depending on the target, it has one +//! or two outputs: one at 32 kHz and another divided down to 1 kHz. These +//! outputs can be disabled or enabled independently at any given time. +//! +//! We can see, that the `OSC32K` peripheral forms its own, miniature clock +//! tree. There is a 1:N producer clock; and there are one or two possible +//! consumer clocks that can be independently and optionally enabled. +//! +//! To represent this structure in the type system, we divide the `OSC32K` +//! peripheral into these three clocks. Users get access to the 1:N +//! [`EnabledOsc32kBase`] clock [`Source`], which can be consumed by both the +//! [`Osc32k`] and [`Osc1k`] clocks. Note that `Osc32k` and `Osc1k` are +//! themselves 1:N clocks as well. +//! +//! ## Write lock +//! +//! The `OSC32K` 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 [`Osc32kBase`] clock, which prevents any further access to the +//! `OSC32K` register. +//! +//! ## Example +//! +//! Creating and configuring the OSC32K 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, +//! osc32k::{Osc1k, Osc32k}, +//! }, +//! 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 [`EnabledOsc32kBase`] clock from the [`Clocks`] +//! struct and use it to enable the [`Osc1k`] and [`Osc32k`] clocks. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # osc32k::{Osc1k, Osc32k}, +//! # }, +//! # 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.osc32k_base; +//! let (osc1k, base) = Osc1k::enable(tokens.osc32k.osc1k, base); +//! let (osc32k, base) = Osc32k::enable(tokens.osc32k.osc32k, 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, +//! # osc32k::{Osc1k, Osc32k}, +//! # }, +//! # 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.osc32k_base; +//! # let (osc1k, base) = Osc1k::enable(tokens.osc32k.osc1k, base); +//! # let (osc32k, mut base) = Osc32k::enable(tokens.osc32k.osc32k, 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 `EnabledOsc32kBase` clock. +//! +//! ```no_run +//! # use atsamd_hal::{ +//! # clock::v2::{ +//! # clock_system_at_reset, +//! # osc32k::{Osc1k, Osc32k}, +//! # }, +//! # 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.osc32k_base; +//! # let (osc1k, base) = Osc1k::enable(tokens.osc32k.osc1k, base); +//! # let (osc32k, mut base) = Osc32k::enable(tokens.osc32k.osc32k, 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, +//! osc32k::{Osc1k, Osc32k}, +//! }, +//! 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.osc32k_base; +//! let (osc1k, base) = Osc1k::enable(tokens.osc32k.osc1k, base); +//! let (osc32k, mut base) = Osc32k::enable(tokens.osc32k.osc32k, 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 atsamd_hal_macros::{hal_cfg, hal_macro_helper}; + +use typenum::U0; + +use crate::pac::sysctrl::Osc32k as OSC32K; + +use crate::typelevel::{Decrement, Increment, PrivateDecrement, PrivateIncrement}; + +use fugit::RateExtU32; +use crate::time::Hertz; +use crate::typelevel::Sealed; + +use super::{Enabled, Source}; + +//============================================================================== +// Tokens +//============================================================================== + +/// Singleton token for the [`Osc32kBase`] clock +// +// There should never be more than one instance of `Osc32kBaseToken`, because +// it relies on that fact for memory safety. +// +// Users never see `Osc32kBaseToken`, because the OSC32K 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 `Osc32kBaseToken` can access the +// `OSC32K` 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. +pub struct Osc32kBaseToken(()); + +/// Singleton token that can be exchanged for [`Osc1k`] +/// +/// 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 [`Osc1k`] clock is disabled at power-on reset. To use it, you must +/// first exchange the token for an actual clock with [`Osc1k::enable`]. + +#[hal_cfg("sysctrl-d11")] +pub struct Osc1kToken(()); + +/// Singleton token that can be exchanged for [`Osc32k`] +/// +/// 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 [`Osc32k`] clock is disabled at power-on reset. To use it, you must +/// first exchange the token for an actual clock with [`Osc32k::enable`]. +pub struct Osc32kToken(()); + +/// Set of tokens representing the disabled OSC32K clocks power-on reset +#[hal_macro_helper] +pub struct Osc32kTokens { + pub base: Osc32kBaseToken, + #[hal_cfg("sysctrl-d11")] + pub osc1k: Osc1kToken, + pub osc32k: Osc32kToken, +} + +impl Osc32kTokens { + /// 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. + #[allow(unused)] + #[hal_macro_helper] + pub(super) unsafe fn new() -> Self { + Self { + base: Osc32kBaseToken(()), + #[hal_cfg("sysctrl-d11")] + osc1k: Osc1kToken(()), + osc32k: Osc32kToken(()), + } + } +} + +impl Osc32kBaseToken { + #[inline] + fn osc32k(&self) -> &OSC32K { + // Safety: The `Osc32kBaseToken` has exclusive access to the + // `OSC32K` register. See the notes on `Token` types and memory + // safety in the root of the `clock` module for more details. + unsafe { (*crate::pac::Sysctrl::PTR).osc32k() } + } + + /// Set the calibration + #[inline] + fn set_calibration(&mut self, calib: u8) { + // Safety: All bit patterns are valid for this field + self.osc32k() + .modify(|_, w| unsafe { w.calib().bits(calib) }); + } + + #[inline] + fn enable(&mut self, settings: Settings) { + self.osc32k().modify(|_, w| { + // Safety: The value comes from the `StartUpDelay` enum, + // so the value is guaranteed to be valid + unsafe { w.startup().bits(settings.start_up as u8) }; + w.ondemand().bit(settings.on_demand); + w.runstdby().bit(settings.run_standby); + w.enable().set_bit() + }); + } + + #[inline] + fn disable(&mut self) { + self.osc32k().modify(|_, w| w.enable().clear_bit()); + } + + /// Enable the 1 kHz output + #[hal_cfg("sysctrl-d11")] + #[inline] + fn enable_1k(&mut self) { + self.osc32k().modify(|_, w| w.en1k().set_bit()); + } + + /// Disable the 1 kHz output + #[hal_cfg("sysctrl-d11")] + #[inline] + fn disable_1k(&mut self) { + self.osc32k().modify(|_, w| w.en1k().clear_bit()); + } + + /// Enable the 32 kHz output + #[inline] + fn enable_32k(&mut self) { + self.osc32k().modify(|_, w| w.en32k().set_bit()); + } + + /// Disable the 32 kHz output + #[inline] + fn disable_32k(&mut self) { + self.osc32k().modify(|_, w| w.en32k().clear_bit()); + } + + /// Enable the write lock + #[inline] + fn write_lock(&mut self) { + self.osc32k().modify(|_, w| w.wrtlock().set_bit()); + } +} + +#[derive(Clone, Copy, PartialEq, Eq)] +struct Settings { + start_up: StartUpDelay, + on_demand: bool, + run_standby: bool, +} + +//============================================================================== +// StartUpDelay +//============================================================================== + +#[repr(u8)] +#[derive(Clone, Copy, Default, PartialEq, Eq)] +pub enum StartUpDelay { + #[default] + Delay92us, + Delay122us, + Delay183us, + Delay305us, + Delay549us, + Delay1ms, + Delay2ms, + Delay4ms, +} + +//============================================================================== +// OscBase +//============================================================================== + +/// OSC3ULP2K base clock, which feeds the [`Osc1k`] and [`Osc32k`] clocks +/// +/// The OSC32K 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 [`Osc32kBase`] clock +/// represents the base oscillator that feeds the optional [`Osc1k`] and +/// [`Osc32k`] output clocks. See the [module-level documentation](super) for +/// details and examples. +pub struct Osc32kBase { + token: Osc32kBaseToken, + settings: Settings, +} + +/// The [`Enabled`] [`Osc32kBase`] 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 [`Osc32kBase`] clock and restricts access to the +/// underlying type to prevent misuse. +/// +/// **NOTE:** The `Osc32kBase` 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 EnabledOsc32kBase = Enabled; + +impl Osc32kBase { + /// Create the ultra-low power base oscillator + /// + /// # Safety + /// + /// Because an `Osc32kBase` contains an `Osc32kBaseToken`, 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 fn new(token: Osc32kBaseToken) -> Self { + let settings = Settings { + start_up: StartUpDelay::Delay92us, + on_demand: true, + run_standby: false, + }; + Self { token, settings } + } + + pub fn start_up_delay(mut self, start_up: StartUpDelay) -> Self { + self.settings.start_up = start_up; + self + } + + pub fn on_demand(mut self, on_demand: bool) -> Self { + self.settings.on_demand = on_demand; + self + } + + pub fn run_standby(mut self, run_standby: bool) -> Self { + self.settings.run_standby = run_standby; + self + } + + #[inline] + pub fn enable(mut self) -> EnabledOsc32kBase { + self.token.enable(self.settings); + Enabled::new(self) + } +} + +impl EnabledOsc32kBase { + #[inline] + pub fn disable(mut self) -> Osc32kBase { + self.0.token.disable(); + self.0 + } + + /// Override the factory-default calibration value + #[inline] + pub fn set_calibration(&mut self, calib: u8) { + self.0.token.set_calibration(calib); + } + + /// Freeze the OSC32K configuration until power-on reset + /// + /// This function sets the write-lock bit, which freezes the OSC32K + /// configuration at the hardware level until power-on reset. At the API + /// level, it also consumes and drops the [`Osc32kBase`] 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 [`Osc1k`] clock +/// +/// See the discussion on [`Id` types](super#id-types) for more information. +#[hal_cfg("sysctrl-d11")] +pub enum Osc1kId {} + +#[hal_cfg("sysctrl-d11")] +impl Sealed for Osc1kId {} + +/// Type representing the identity of the [`Osc32k`] clock +/// +/// See the discussion on [`Id` types](super#id-types) for more information. +pub enum Osc32kId {} + +impl Sealed for Osc32kId {} + +//============================================================================== +// Osc1k +//============================================================================== + +/// Clock representing the 1 kHz output of the [`Osc32kBase`] clock +/// +/// The OSC32K 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 [`Osc1k`] clock is +/// derived from the [`Osc32kBase`] clock. See the +/// [module-level documentation](super) for details and examples. +#[hal_cfg("sysctrl-d11")] +pub struct Osc1k { + #[allow(unused)] + token: Osc1kToken, +} + +/// OSC1K is not available on the currently-documented target +#[cfg(doc)] +#[hal_cfg(not("sysctrl-d11"))] +pub struct Osc1k {} + +/// The [`Enabled`] [`Osc1k`] 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 [`Osc1k`] 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. +#[hal_cfg("sysctrl-d11")] +pub type EnabledOsc1k = Enabled; + +#[hal_cfg("sysctrl-d11")] +impl Osc1k { + /// Enable 1 kHz output from the [`Osc32kBase`] clock + /// + /// This will [`Increment`] the [`EnabledOsc32kBase`] counter. + #[inline] + pub fn enable( + token: Osc1kToken, + mut base: EnabledOsc32kBase, + ) -> (EnabledOsc1k, EnabledOsc32kBase) { + base.0.token.enable_1k(); + (Enabled::new(Self { token }), base.inc()) + } +} + +#[hal_cfg("sysctrl-d11")] +impl EnabledOsc1k { + /// Disable 1 kHz output from the [`Osc32kBase`] clock + /// + /// This will [`Decrement`] the [`EnabledOsc32kBase`] counter. + #[inline] + pub fn disable( + self, + mut base: EnabledOsc32kBase, + ) -> (Osc1kToken, EnabledOsc32kBase) { + base.0.token.disable_1k(); + (self.0.token, base.dec()) + } +} + +#[hal_cfg("sysctrl-d11")] +impl Source for EnabledOsc1k { + type Id = Osc1kId; + + #[inline] + fn freq(&self) -> Hertz { + 1_024u32.Hz() + } +} + +//============================================================================== +// Osc32k +//============================================================================== + +/// Clock representing the 32 kHz output of the [`Osc32kBase`] clock +/// +/// The OSC32K 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 [`Osc32k`] clock is +/// derived from the [`Osc32kBase`] clock. See the +/// [module-level documentation](super) for details and examples. +pub struct Osc32k { + #[allow(unused)] + token: Osc32kToken, +} + +/// The [`Enabled`] [`Osc32k`] 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 [`Osc32k`] 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 EnabledOsc32k = Enabled; + +impl Osc32k { + /// Enable 32 kHz output from the [`Osc32kBase`] clock + /// + /// This will [`Increment`] the [`EnabledOsc32kBase`] counter. + #[inline] + pub fn enable( + token: Osc32kToken, + mut base: EnabledOsc32kBase, + ) -> (EnabledOsc32k, EnabledOsc32kBase) { + base.0.token.enable_32k(); + (Enabled::new(Self { token }), base.inc()) + } +} + +impl EnabledOsc32k { + /// Disable 32 kHz output from the [`Osc32kBase`] clock + /// + /// This will [`Decrement`] the [`EnabledOsc32kBase`] counter. + #[inline] + pub fn disable( + self, + mut base: EnabledOsc32kBase, + ) -> (Osc32kToken, EnabledOsc32kBase) { + base.0.token.disable_32k(); + (self.0.token, base.dec()) + } +} + +impl Source for EnabledOsc32k { + type Id = Osc32kId; + + #[inline] + fn freq(&self) -> Hertz { + 32_768u32.Hz() + } +} diff --git a/hal/src/peripherals/clock/d5x/v2/osculp32k.rs b/hal/src/clock/v2/osculp32k.rs similarity index 85% rename from hal/src/peripherals/clock/d5x/v2/osculp32k.rs rename to hal/src/clock/v2/osculp32k.rs index 1b4e1c30d99b..e580bc18c561 100644 --- a/hal/src/peripherals/clock/d5x/v2/osculp32k.rs +++ b/hal/src/clock/v2/osculp32k.rs @@ -3,7 +3,8 @@ //! ## Overview //! //! The `osculp32k` module provides access to the 32 kHz ultra low power -//! internal oscillator (OSCULP32K) within the `OSC32KCTRL` peripheral. +//! internal oscillator (OSCULP32K) within the `OSC32KCTRL` or `SYSCTRL` +//! 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 @@ -13,9 +14,9 @@ //! 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`. +//! 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 @@ -161,13 +162,20 @@ //! [`clock_system_at_reset`]: super::clock_system_at_reset //! [`Clocks`]: super::Clocks +use atsamd_hal_macros::{hal_cfg, hal_macro_helper}; use fugit::RateExtU32; use typenum::U0; +#[hal_cfg("osc32kctrl")] use crate::pac::osc32kctrl::Osculp32k; +#[hal_cfg("osc32kctrl")] +use crate::typelevel::{Decrement, Increment, PrivateDecrement, PrivateIncrement}; + +#[hal_cfg("sysctrl")] +use crate::pac::sysctrl::Osculp32k; use crate::time::Hertz; -use crate::typelevel::{Decrement, Increment, PrivateDecrement, PrivateIncrement, Sealed}; +use crate::typelevel::Sealed; use super::{Enabled, Source}; @@ -197,8 +205,9 @@ struct OscUlp32kBaseToken(()); /// 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`]. +/// On some targets, the [`OscUlp1k`] clock is disabled at power-on reset, and +/// to use it the token must first be exchanged for an actual clock with +/// [`OscUlp1k::enable`]. pub struct OscUlp1kToken(()); /// Singleton token that can be exchanged for [`OscUlp32k`] @@ -207,59 +216,50 @@ pub struct OscUlp1kToken(()); /// 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`]. +/// On some targets, the [`OscUlp32k`] clock is disabled at power-on reset, and +/// to use it the token must first be exchanged 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] + #[hal_macro_helper] 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() } + #[hal_cfg("osc32kctrl")] + unsafe { + (*crate::pac::Osc32kctrl::PTR).osculp32k() + } + #[hal_cfg("sysctrl")] + unsafe { + (*crate::pac::Sysctrl::PTR).osculp32k() + } } + #[hal_cfg("clock-d5x")] /// Enable the 1 kHz output #[inline] fn enable_1k(&mut self) { self.osculp32k().modify(|_, w| w.en1k().set_bit()); } + #[hal_cfg("clock-d5x")] /// Disable the 1 kHz output #[inline] fn disable_1k(&mut self) { self.osculp32k().modify(|_, w| w.en1k().clear_bit()); } + #[hal_cfg("clock-d5x")] /// Enable the 32 kHz output #[inline] fn enable_32k(&mut self) { self.osculp32k().modify(|_, w| w.en32k().set_bit()); } + #[hal_cfg("clock-d5x")] /// Disable the 32 kHz output #[inline] fn disable_32k(&mut self) { @@ -313,9 +313,9 @@ impl OscUlp32kBase { /// 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 { + pub(super) unsafe fn new() -> Self { let token = OscUlp32kBaseToken(()); - Enabled::new(Self { token }) + Self { token } } } @@ -362,6 +362,7 @@ impl Sealed for OscUlp32kId {} /// derived from the [`OscUlp32kBase`] clock. See the /// [module-level documentation](super) for details and examples. pub struct OscUlp1k { + #[allow(unused)] token: OscUlp1kToken, } @@ -377,9 +378,24 @@ pub struct OscUlp1k { pub type EnabledOscUlp1k = Enabled; impl OscUlp1k { + /// Create the ultra-low power base oscillator + /// + /// # Safety + /// + /// Because an `OscUlp1k` contains an `OscUlp1kToken`, 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() -> Self { + let token = OscUlp1kToken(()); + Self { token } + } + /// Enable 1 kHz output from the [`OscUlp32kBase`] clock /// /// This will [`Increment`] the [`EnabledOscUlp32kBase`] counter. + #[hal_cfg("clock-d5x")] #[inline] pub fn enable( token: OscUlp1kToken, @@ -388,12 +404,18 @@ impl OscUlp1k { base.0.token.enable_1k(); (Enabled::new(Self { token }), base.inc()) } + + /// The OSCULP32K 1.024kHz output is always enabled on the documented target + #[cfg(doc)] + #[hal_cfg(not("clock-d5x"))] + pub fn enable(_token: OscUlp1kToken) {} } impl EnabledOscUlp1k { /// Disable 1 kHz output from the [`OscUlp32kBase`] clock /// /// This will [`Decrement`] the [`EnabledOscUlp32kBase`] counter. + #[hal_cfg("clock-d5x")] #[inline] pub fn disable( self, @@ -425,6 +447,7 @@ impl Source for EnabledOscUlp1k { /// derived from the [`OscUlp32kBase`] clock. See the /// [module-level documentation](super) for details and examples. pub struct OscUlp32k { + #[allow(unused)] token: OscUlp32kToken, } @@ -440,9 +463,24 @@ pub struct OscUlp32k { pub type EnabledOscUlp32k = Enabled; impl OscUlp32k { + /// Create the ultra-low power base oscillator + /// + /// # Safety + /// + /// Because an `OscUlp32k` contains an `OscUlp32kToken`, 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() -> Self { + let token = OscUlp32kToken(()); + Self { token } + } + /// Enable 32 kHz output from the [`OscUlp32kBase`] clock /// /// This will [`Increment`] the [`EnabledOscUlp32kBase`] counter. + #[hal_cfg("clock-d5x")] #[inline] pub fn enable( token: OscUlp32kToken, @@ -451,12 +489,19 @@ impl OscUlp32k { base.0.token.enable_32k(); (Enabled::new(Self { token }), base.inc()) } + + /// The OSCULP32K 32.768kHz output is always enabled on the documented + /// target + #[cfg(doc)] + #[hal_cfg(not("clock-d5x"))] + pub fn enable(_token: OscUlp32kToken) {} } impl EnabledOscUlp32k { /// Disable 32 kHz output from the [`OscUlp32kBase`] clock /// /// This will [`Decrement`] the [`EnabledOscUlp32kBase`] counter. + #[hal_cfg("clock-d5x")] #[inline] pub fn disable( self, diff --git a/hal/src/peripherals/clock/d5x/v2/pclk.rs b/hal/src/clock/v2/pclk.rs similarity index 63% rename from hal/src/peripherals/clock/d5x/v2/pclk.rs rename to hal/src/clock/v2/pclk.rs index cd58db8f6cca..02a04286dd95 100644 --- a/hal/src/peripherals/clock/d5x/v2/pclk.rs +++ b/hal/src/clock/v2/pclk.rs @@ -62,15 +62,27 @@ //! [`clock::v2::types`]: super::types //! [`Sercom`]: crate::sercom::Sercom -use atsamd_hal_macros::hal_macro_helper; +use atsamd_hal_macros::{hal_cfg, hal_macro_helper}; use core::marker::PhantomData; use paste::paste; -use seq_macro::seq; use crate::pac; -use crate::pac::gclk::pchctrl::Genselect; + +#[hal_cfg("clock-d5x")] +mod imports { + pub use crate::pac::gclk::Pchctrl as Ctrl; + pub use crate::pac::gclk::pchctrl::Genselect; +} + +#[hal_cfg(any("clock-d11", "clock-d21"))] +mod imports { + pub use crate::pac::gclk::Clkctrl as Ctrl; + pub use crate::pac::gclk::clkctrl::Genselect; +} + +use imports::*; use crate::time::Hertz; use crate::typelevel::{Decrement, Increment, Sealed}; @@ -113,31 +125,51 @@ impl PclkToken

{ /// Access the corresponding `PCHCTRL` register #[inline] - fn pchctrl(&self) -> &pac::gclk::Pchctrl { + #[hal_macro_helper] + fn ctrl(&self) -> &Ctrl { // 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) } + #[hal_cfg("clock-d5x")] + unsafe { + (*pac::Gclk::PTR).pchctrl(P::DYN as usize) + } + #[hal_cfg(any("clock-d11", "clock-d21"))] + unsafe { + (*pac::Gclk::PTR).clkctrl() + } } /// Set the [`Pclk`] source #[inline] - fn set_source(&mut self, source: DynPclkSourceId) { - self.pchctrl() - .modify(|_, w| w.r#gen().variant(source.into())); - } - - /// Enable the [`Pclk`] - #[inline] - fn enable(&mut self) { - self.pchctrl().modify(|_, w| w.chen().set_bit()); + #[hal_macro_helper] + fn enable(&mut self, source: DynPclkSourceId) { + self.ctrl().write(|w| { + w.r#gen().variant(source.into()); + #[hal_cfg(any("clock-d11", "clock-d21"))] + { + w.clken().set_bit(); + unsafe { w.id().bits(P::DYN as u8) } + } + #[hal_cfg("clock-d5x")] + w.chen().set_bit() + }); } /// Disable the [`Pclk`] #[inline] + #[hal_macro_helper] fn disable(&mut self) { - self.pchctrl().modify(|_, w| w.chen().clear_bit()); + self.ctrl().modify(|_, w| { + #[hal_cfg(any("clock-d11", "clock-d21"))] + { + w.clken().clear_bit(); + unsafe { w.id().bits(P::DYN as u8) } + } + #[hal_cfg("clock-d5x")] + w.chen().clear_bit() + }); } } @@ -157,22 +189,43 @@ impl PclkToken

{ pub mod ids { use atsamd_hal_macros::hal_cfg; - pub use crate::sercom::{Sercom0, Sercom1, Sercom2, Sercom3, Sercom4, Sercom5}; + pub use crate::sercom::{Sercom0, Sercom1}; + #[hal_cfg("sercom2")] + pub use crate::sercom::Sercom2; + #[hal_cfg("sercom3")] + pub use crate::sercom::Sercom3; + #[hal_cfg("sercom4")] + pub use crate::sercom::Sercom4; + #[hal_cfg("sercom5")] + pub use crate::sercom::Sercom5; #[hal_cfg("sercom6")] pub use crate::sercom::Sercom6; #[hal_cfg("sercom7")] pub use crate::sercom::Sercom7; pub use super::super::dfll::DfllId; - pub use super::super::dpll::{Dpll0Id, Dpll1Id}; + pub use super::super::dpll::Dpll0Id; + #[hal_cfg("clock-d5x")] + pub use super::super::dpll::Dpll1Id; + // TODO crude hack + #[hal_cfg("clock-d5x")] pub use super::super::types::{ Ac, Adc0, Adc1, CM4Trace, Ccl, Dac, Eic, EvSys0, EvSys1, EvSys2, EvSys3, EvSys4, EvSys5, EvSys6, EvSys7, EvSys8, EvSys9, EvSys10, EvSys11, FreqMMeasure, FreqMReference, PDec, - Sdhc0, SlowClk, Tc0Tc1, Tc2Tc3, Tcc0Tcc1, Tcc2Tcc3, Usb, + Sdhc0, SlowClk, Tc0Tc1, Tc2Tc3, Usb, }; + #[hal_cfg(any("clock-d11", "clock-d21"))] + pub use super::super::types::{ + Ac, AcAna, AcDig, Adc0, Dac, Eic, EvSys0, EvSys1, EvSys2, EvSys3, EvSys4, EvSys5, EvSys6, + EvSys7, EvSys8, EvSys9, EvSys10, EvSys11, Ptc, Rtc, SercomSlow, SlowClk, Usb, Wdt, + }; + + #[hal_cfg("clock-d11")] + pub use super::super::types::{Tc1Tc2, Tcc0}; + #[hal_cfg("can0")] pub use super::super::types::Can0; #[hal_cfg("can1")] @@ -183,12 +236,21 @@ pub mod ids { pub use super::super::types::Tc4Tc5; #[hal_cfg(all("tc6", "tc7"))] pub use super::super::types::Tc6Tc7; + #[hal_cfg(all("tcc0", "tcc1"))] + pub use super::super::types::Tcc0Tcc1; + #[hal_cfg(all("tcc2", "tcc3"))] + pub use super::super::types::Tcc2Tcc3; #[hal_cfg("tcc4")] pub use super::super::types::Tcc4; + + // TODO would it make sense to just pub use super::super::types::* and + // conditionally restrict what types we make there? + #[hal_cfg(all("tcc2", "tc3-d21"))] + pub use super::super::types::Tcc2Tc3; + #[hal_cfg("i2s")] pub use super::super::types::{I2S0, I2S1}; } - use ids::*; /// Append the list of all [`PclkId`] types and `snake_case` id names to the @@ -225,6 +287,7 @@ use ids::*; /// /// with_pclk_types_ids!(some_macro!(first, second)); /// ``` +#[hal_cfg("clock-d5x")] #[hal_macro_helper] macro_rules! with_pclk_types_ids { ( $some_macro:ident ! ( $( $args:tt )* ) ) => { @@ -261,6 +324,7 @@ macro_rules! with_pclk_types_ids { (Can0 = 27, can0) #[hal_cfg("can1")] (Can1 = 28, can1) + #[hal_cfg(all("tcc2", "tcc3"))] (Tcc2Tcc3 = 29, tcc2_tcc3) #[hal_cfg(all("tc4", "tc5"))] (Tc4Tc5 = 30, tc4_tc5) @@ -292,6 +356,95 @@ macro_rules! with_pclk_types_ids { }; } +#[hal_cfg("clock-d21")] +#[hal_macro_helper] +macro_rules! with_pclk_types_ids { + ( $some_macro:ident ! ( $( $args:tt )* ) ) => { + $some_macro!( + $( $args )* + (DfllId = 0, dfll) + (Dpll0Id = 1, dpll) + (SlowClk = 2, slow) + (Wdt = 3, wdt) + (Rtc = 4, rtc) + (Eic = 5, eic) + (Usb = 6, usb) + (EvSys0 = 7, ev_sys0) + (EvSys1 = 8, ev_sys1) + (EvSys2 = 9, ev_sys2) + (EvSys3 = 10, ev_sys3) + (EvSys4 = 11, ev_sys4) + (EvSys5 = 12, ev_sys5) + (EvSys6 = 13, ev_sys6) + (EvSys7 = 14, ev_sys7) + (EvSys8 = 15, ev_sys8) + (EvSys9 = 16, ev_sys9) + (EvSys10 = 17, ev_sys10) + (EvSys11 = 18, ev_sys11) + (SercomSlow = 19, sercom_slow) + (Sercom0 = 20, sercom0) + (Sercom1 = 21, sercom1) + #[hal_cfg("sercom2")] + (Sercom2 = 22, sercom2) + #[hal_cfg("sercom3")] + (Sercom3 = 23, sercom3) + #[hal_cfg("sercom4")] + (Sercom4 = 24, sercom4) + #[hal_cfg("sercom5")] + (Sercom5 = 25, sercom5) + (Tcc0Tcc1 = 26, tcc0_tcc1) + (Tcc2Tc3 = 27, tcc2_tc3) + (Tc4Tc5 = 28, tc4_tc5) + #[hal_cfg(all("tc6", "tc7"))] + (Tc6Tc7 = 29, tc6_tc7) + (Adc0 = 30, adc) + (AcDig = 31, ac_dig) + (AcAna = 32, ac_ana) + (Dac = 33, dac) + (Ptc = 34, ptc) + #[hal_cfg("i2s")] + (I2S0 = 35, i2s0) + #[hal_cfg("i2s")] + (I2S1 = 36, i2s1) + ); + }; +} + +#[hal_cfg("clock-d11")] +#[hal_macro_helper] +macro_rules! with_pclk_types_ids { + ( $some_macro:ident ! ( $( $args:tt )* ) ) => { + $some_macro!( + $( $args )* + (DfllId = 0, dfll) + (Dpll0Id = 1, dpll) + (SlowClk = 2, slow) + (Wdt = 3, wdt) + (Rtc = 4, rtc) + (Eic = 5, eic) + (Usb = 6, usb) + (EvSys0 = 7, ev_sys0) + (EvSys1 = 8, ev_sys1) + (EvSys2 = 9, ev_sys2) + (EvSys3 = 10, ev_sys3) + (EvSys4 = 11, ev_sys4) + (EvSys5 = 12, ev_sys5) + (SercomSlow = 13, sercom_slow) + (Sercom0 = 14, sercom0) + (Sercom1 = 15, sercom1) + #[hal_cfg("sercom2")] + (Sercom2 = 16, sercom2) + (Tcc0 = 17, tcc0) + (Tc1Tc2 = 18, tc1_tc2) + (Adc0 = 19, adc) + (AcDig = 20, ac_dig) + (AcAna = 21, ac_ana) + (Dac = 22, dac) + (Ptc = 23, ptc) + ); + }; +} + //============================================================================== // DynPclkId //============================================================================== @@ -368,18 +521,54 @@ pub trait PclkId: Sealed { pub type DynPclkSourceId = DynGclkId; /// Convert from [`DynPclkSourceId`] to the equivalent [PAC](crate::pac) type +#[hal_macro_helper] impl From for Genselect { fn from(source: DynPclkSourceId) -> Self { - seq!(N in 0..=11 { - match source { - #( - DynGclkId::Gclk~N => Genselect::Gclk~N, - )* - } - }) + match source { + DynPclkSourceId::Gclk0 => Genselect::Gclk0, + DynPclkSourceId::Gclk1 => Genselect::Gclk1, + DynPclkSourceId::Gclk2 => Genselect::Gclk2, + DynPclkSourceId::Gclk3 => Genselect::Gclk3, + DynPclkSourceId::Gclk4 => Genselect::Gclk4, + DynPclkSourceId::Gclk5 => Genselect::Gclk5, + #[hal_cfg("gclk6")] + DynPclkSourceId::Gclk6 => Genselect::Gclk6, + #[hal_cfg("gclk7")] + DynPclkSourceId::Gclk7 => Genselect::Gclk7, + #[hal_cfg("gclk8")] + DynPclkSourceId::Gclk8 => Genselect::Gclk8, + #[hal_cfg("gclk9")] + DynPclkSourceId::Gclk9 => Genselect::Gclk9, + #[hal_cfg("gclk10")] + DynPclkSourceId::Gclk10 => Genselect::Gclk10, + #[hal_cfg("gclk11")] + DynPclkSourceId::Gclk11 => Genselect::Gclk11, + } + } +} + +impl PclkSourceId for DynPclkSourceId { + fn source_id(&self) -> DynGclkId { + *self } } +impl Sealed for DynPclkSourceId {} + +//============================================================================== +// PclkSourceId +//============================================================================== + +pub struct PclkSource { + _src: PhantomData, +} + +impl GclkId for PclkSource { + const DYN: DynGclkId = G::DYN; + const NUM: usize = G::NUM; + type Divider = G::Divider; +} + //============================================================================== // PclkSourceId //============================================================================== @@ -399,9 +588,19 @@ impl From for Genselect { /// [`Gclk`]: super::gclk::Gclk /// [type-level programming]: crate::typelevel /// [type-level enums]: crate::typelevel#type-level-enums -pub trait PclkSourceId: GclkId {} +pub trait PclkSourceId: Sealed { + fn source_id(&self) -> DynGclkId; +} -impl PclkSourceId for G {} +// PclkSource implements PclkSourceId via its GclkId implementation +impl PclkSourceId for G { + #[inline] + fn source_id(&self) -> DynGclkId { + Self::DYN + } +} + +impl Sealed for PclkSource {} //============================================================================== // Pclk @@ -439,24 +638,41 @@ where I: PclkSourceId, { token: PclkToken

, - src: PhantomData, + src: I, freq: Hertz, } -impl Pclk +/// [`Pclk`] with a dynamic source ID ([`DynPclkSourceId`]). +pub type DynPclk

= Pclk; + +impl From>> for DynPclk

where P: PclkId, - I: PclkSourceId, + G: GclkId, +{ + fn from(value: Pclk>) -> Self { + Pclk { + token: value.token, + freq: value.freq, + src: G::DYN, + } + } +} + +impl Pclk> +where + P: PclkId, + G: GclkId, { pub(super) fn new(token: PclkToken

, freq: Hertz) -> Self { Self { token, - src: PhantomData, + src: PclkSource { _src: PhantomData }, freq, } } - /// Create and enable a [`Pclk`] + /// Create and enable a [`Pclk`] with a type-checked source ID. /// /// Creating a [`Pclk`] immediately enables the corresponding peripheral /// channel clock. It also [`Increment`]s the [`Source`]'s [`Enabled`] @@ -469,16 +685,15 @@ where #[inline] pub fn enable(mut token: PclkToken

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

, S::Dec) where - S: Source + Decrement, + S: Source + Decrement, + { + self.token.disable(); + (self.token, gclk.dec()) + } +} + +impl

DynPclk

+where + P: PclkId, +{ + pub(super) fn new(token: PclkToken

, freq: Hertz) -> Self { + Self { + token, + src: G::DYN, + freq, + } + } + + /// Create and enable a [`Pclk`] with an underlying [`DynPclkSourceId`] + /// source ID type. + /// + /// Some peripherals require a dynamic PCLK source ID type parameter; use + /// this method to create a [`Pclk`] where this type parameter is + /// type-erased. + /// + /// 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_dyn(mut token: PclkToken

, gclk: S) -> (Self, S::Inc) + where + S: Source + Increment, + { + let freq = gclk.freq(); + token.enable(G::DYN); + let pclk = Self::new::(token, freq); + (pclk, gclk.inc()) + } + + /// Disable and destroy a [`Pclk`]. + /// + /// Consume the [`Pclk`], release the [`PclkToken`], and [`Decrement`] the + /// [`EnabledGclk`]'s counter. + /// + /// # Panics + /// + /// Panics if the [`Pclk`]'s underlying GCLK source ID does not match the ID + /// of the provided [`Source`]. + /// + /// [`Enabled`]: super::Enabled + /// [`EnabledGclk`]: super::gclk::EnabledGclk + #[inline] + pub fn disable(mut self, gclk: S) -> (PclkToken

, S::Dec) + where + S: Source + Decrement, { + // Make sure that we can only decrement the source we are actually using + assert_eq!( + G::DYN, + self.src, + "Expected GCLK ID {:?}, found {:?}", + G::DYN, + self.src + ); + self.token.disable(); (self.token, gclk.dec()) } +} +impl Pclk +where + P: PclkId, + I: PclkSourceId, +{ /// Return the [`Pclk`] frequency #[inline] pub fn freq(&self) -> Hertz { diff --git a/hal/src/clock/v2/reset_thumbv6m.rs b/hal/src/clock/v2/reset_thumbv6m.rs new file mode 100644 index 000000000000..a9831beadbcb --- /dev/null +++ b/hal/src/clock/v2/reset_thumbv6m.rs @@ -0,0 +1,190 @@ +//! This module is intentionally private. Its contents are publicly exported +//! from the `v2` module, which is where the corresponding documentation will +//! appear. + +use atsamd_hal_macros::hal_macro_helper; + +use typenum::U1; + +use crate::{ + clock::v2::pclk::PclkSource, + pac::{Gclk, Pm, Sysctrl}, +}; + +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 { + gclk: Gclk, + pm: Pm, + sysctrl: Sysctrl, +} + +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. + /// + /// # Safety + /// + /// Directly configuring clocks through the PAC API can invalidate the + /// type-level guarantees of the `clock` module API. + pub unsafe fn steal(self) -> (Gclk, Pm, Sysctrl) { + (self.gclk, self.pm, self.sysctrl) + } +} + +/// 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, +} + +pub struct OscUlpClocks { + pub base: osculp32k::EnabledOscUlp32kBase, + pub osculp1k: osculp32k::EnabledOscUlp1k, + pub osculp32k: osculp32k::EnabledOscUlp32k, +} + +/// 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 1 MHz by the OSC8M divided by 8 + pub gclk0: Enabled, U1>, + /// GCLK2, driven at 32 kHz by the OSCULP + pub gclk2: Enabled, U1>, + /// 8 MHz internal oscillator, divided by 8 for a 1 MHz output + pub osc: Enabled, + /// Always-enabled OSCULP oscillators + pub osculp: OscUlpClocks, + /// [`Pclk`](pclk::Pclk) for the watchdog timer, sourced from [`Gclk2`](gclk::Gclk2) + pub wdt: pclk::Pclk>, +} + +/// 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. +#[hal_macro_helper] +pub struct Tokens { + /// Tokens to create [`apb::ApbClk`]s + pub apbs: apb::ApbTokens, + /// Token to create [`dfll::Dfll`] + pub dfll: dfll::DfllToken, + /// Token to create [`dpll::Dpll0`] + pub dpll: dpll::DpllToken, + /// Tokens to create [`gclk::Gclk`] + pub gclks: gclk::GclkTokens, + /// Tokens to create [`pclk::Pclk`]s + pub pclks: pclk::PclkTokens, + /// Tokens to create the [`osc32k`] clocks + pub osc32k: osc32k::Osc32kTokens, + /// Tokens [`xosc::Xosc0`] + pub xosc: xosc::XoscToken, + /// Tokens to create [`xosc32k`] clocks + #[hal_cfg("xosc32k")] + pub xosc32k: xosc32k::Xosc32kTokens, +} + +/// Consume the PAC clocking structs and return a HAL-level representation of +/// the clocks at power-on reset +/// +/// This function consumes the [`Gclk`], [`Pm`], and [`Sysctrl``] PAC structs +/// and returns the [`Buses`], [`Clocks`] and [`Tokens`]. +/// +/// See the [module-level documentation](super) for more details. +#[inline] +#[hal_macro_helper] +pub fn clock_system_at_reset(gclk: Gclk, pm: Pm, sysctrl: Sysctrl) -> (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 { gclk, pm, sysctrl }; + let osc = Enabled::<_, U0>::new(osc::Osc::new(osc::OscToken::new())); + let (gclk0, osc) = gclk::Gclk0::from_source(gclk::GclkToken::new(), osc); + let gclk0 = Enabled::new(gclk0); + let base = Enabled::new(osculp32k::OscUlp32kBase::new()); + let osculp1k = Enabled::new(osculp32k::OscUlp1k::new()); + let osculp32k = Enabled::<_, U0>::new(osculp32k::OscUlp32k::new()); + let (gclk2, osculp32k) = gclk::Gclk2::from_source(gclk::GclkToken::new(), osculp32k); + let gclk2 = Enabled::new(gclk2); + let wdt = pclk::Pclk::<_, PclkSource<_>>::new(pclk::PclkToken::new(), gclk2.freq()); + let osculp = OscUlpClocks { + base, + osculp1k, + osculp32k, + }; + let clocks = Clocks { + pac, + ahbs: ahb::AhbClks::new(), + apbs: apb::ApbClks::new(), + gclk0, + gclk2, + osc, + wdt, + osculp, + }; + let tokens = Tokens { + apbs: apb::ApbTokens::new(), + dfll: dfll::DfllToken::new(), + dpll: dpll::DpllToken::new(), + gclks: gclk::GclkTokens::new(), + pclks: pclk::PclkTokens::new(), + osc32k: osc32k::Osc32kTokens::new(), + xosc: xosc::XoscToken::new(), + #[hal_cfg("xosc32k")] + xosc32k: xosc32k::Xosc32kTokens::new(), + }; + (buses, clocks, tokens) + } +} diff --git a/hal/src/peripherals/clock/d5x/v2/reset.rs b/hal/src/clock/v2/reset_thumbv7em.rs similarity index 91% rename from hal/src/peripherals/clock/d5x/v2/reset.rs rename to hal/src/clock/v2/reset_thumbv7em.rs index dda29b0e6479..acac05d7d86d 100644 --- a/hal/src/peripherals/clock/d5x/v2/reset.rs +++ b/hal/src/clock/v2/reset_thumbv7em.rs @@ -4,7 +4,7 @@ use typenum::U1; -use crate::pac::{Gclk, Mclk, Nvmctrl, Osc32kctrl, Oscctrl}; +use crate::pac::{Gclk, Mclk, Osc32kctrl, Oscctrl}; use super::*; @@ -57,6 +57,12 @@ pub struct Buses { pub apb: apb::Apb, } +pub struct OscUlpClocks { + pub base: osculp32k::EnabledOscUlp32kBase, + pub osculp1k: osculp32k::EnabledOscUlp1k, + pub osculp32k: osculp32k::EnabledOscUlp32k, +} + /// Enabled clocks at power-on reset /// /// This type is constructed using the [`clock_system_at_reset`] function, which @@ -88,7 +94,7 @@ pub struct Clocks { pub dfll: Enabled, /// Always-enabled base oscillator for the [`OscUlp1k`](osculp32k::OscUlp1k) /// and [`OscUlp32k`](osculp32k::OscUlp32k) clocks. - pub osculp32k_base: Enabled, + pub osculp: OscUlpClocks, } /// Type-level tokens for unused clocks at power-on reset @@ -120,8 +126,6 @@ pub struct Tokens { /// 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 @@ -137,7 +141,6 @@ pub fn clock_system_at_reset( osc32kctrl: Osc32kctrl, gclk: Gclk, mclk: Mclk, - nvmctrl: &mut Nvmctrl, ) -> (Buses, Clocks, Tokens) { // Safety: No bus, clock or token is instantiated more than once unsafe { @@ -154,25 +157,32 @@ pub fn clock_system_at_reset( 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 base = Enabled::new(osculp32k::OscUlp32kBase::new()); + let osculp1k = Enabled::new(osculp32k::OscUlp1k::new()); + let osculp32k = Enabled::new(osculp32k::OscUlp32k::new()); + let osculp = OscUlpClocks { + base, + osculp1k, + osculp32k, + }; let clocks = Clocks { pac, ahbs: ahb::AhbClks::new(), apbs: apb::ApbClks::new(), gclk0, dfll, - osculp32k_base: osculp32k::OscUlp32kBase::new(), + osculp, }; let tokens = Tokens { apbs: apb::ApbTokens::new(), dpll0: dpll::DpllToken::new(), dpll1: dpll::DpllToken::new(), - gclks: gclk::GclkTokens::new(nvmctrl), + gclks: gclk::GclkTokens::new(), 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/peripherals/clock/d5x/v2/rtcosc.rs b/hal/src/clock/v2/rtcosc.rs similarity index 100% rename from hal/src/peripherals/clock/d5x/v2/rtcosc.rs rename to hal/src/clock/v2/rtcosc.rs index 7edd596ea319..a13b7376b678 100644 --- a/hal/src/peripherals/clock/d5x/v2/rtcosc.rs +++ b/hal/src/clock/v2/rtcosc.rs @@ -93,16 +93,16 @@ use core::marker::PhantomData; -use crate::pac::osc32kctrl::rtcctrl::Rtcselselect; -use crate::pac::osc32kctrl::Rtcctrl; use crate::pac::Osc32kctrl; +use crate::pac::osc32kctrl::Rtcctrl; +use crate::pac::osc32kctrl::rtcctrl::Rtcselselect; use crate::time::Hertz; use crate::typelevel::{Decrement, Increment}; +use super::Source; use super::osculp32k::{OscUlp1kId, OscUlp32kId}; use super::xosc32k::{Xosc1kId, Xosc32kId}; -use super::Source; //============================================================================== // RtcOscToken diff --git a/hal/src/clock/v2/types.rs b/hal/src/clock/v2/types.rs new file mode 100644 index 000000000000..93b8dc81e7eb --- /dev/null +++ b/hal/src/clock/v2/types.rs @@ -0,0 +1,213 @@ +//! 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 atsamd_hal_macros::hal_cfg; + +use crate::typelevel::Sealed; + +macro_rules! create_types { + ( + $( + $Type:ident + ),+ + ) => { + $( + /// Marker type representing the corresponding peripheral + /// + /// This type is defined by and used within the + /// [`clock`](super::super) module. See the the [`types`](super) + /// module documentation for more details. + pub enum $Type {} + impl Sealed for $Type {} + )+ + }; +} + +// AHB types +create_types!(Dmac); +create_types!(Dsu); +create_types!(Hpb0, Hpb1, Hpb2); +create_types!(NvmCtrl); +create_types!(Usb); + +// APB types +create_types!(Ac); +create_types!(Adc0); +create_types!(Dac); +create_types!(Eic); +create_types!(EvSys); +create_types!(Gclk); +#[hal_cfg("i2s")] +create_types!(I2S); +create_types!(Pac0); +create_types!(Pm); +create_types!(Port); +create_types!(Rtc); +#[hal_cfg("sercom0")] +pub use crate::sercom::Sercom0; +#[hal_cfg("sercom1")] +pub use crate::sercom::Sercom1; +#[hal_cfg("sercom2")] +pub use crate::sercom::Sercom2; +#[hal_cfg("sercom3")] +pub use crate::sercom::Sercom3; +#[hal_cfg("sercom4")] +pub use crate::sercom::Sercom4; +#[hal_cfg("sercom5")] +pub use crate::sercom::Sercom5; +#[hal_cfg("sercom6")] +pub use crate::sercom::Sercom6; +#[hal_cfg("sercom7")] +pub use crate::sercom::Sercom7; + +#[hal_cfg("tc0")] +create_types!(Tc0); +#[hal_cfg("tc1")] +create_types!(Tc1); +#[hal_cfg("tc2")] +create_types!(Tc2); +#[hal_cfg("tc3")] +create_types!(Tc3); +#[hal_cfg("tc4")] +create_types!(Tc4); +#[hal_cfg("tc5")] +create_types!(Tc5); +#[hal_cfg("tc6")] +create_types!(Tc6); +#[hal_cfg("tc7")] +create_types!(Tc7); + +#[hal_cfg("tcc0")] +create_types!(Tcc0); +#[hal_cfg("tcc1")] +create_types!(Tcc1); +#[hal_cfg("tcc2")] +create_types!(Tcc2); +#[hal_cfg("tcc3")] +create_types!(Tcc3); +#[hal_cfg("tcc4")] +create_types!(Tcc4); + +// TODO +#[hal_cfg(all("tc0", "tc1"))] +create_types!(Tc0Tc1); +#[hal_cfg(all("tc2", "tc3"))] +create_types!(Tc2Tc3); +#[hal_cfg(all("tc4", "tc5"))] +create_types!(Tc4Tc5); +#[hal_cfg(all("tc6", "tc7"))] +create_types!(Tc6Tc7); + +#[hal_cfg(all("tcc0", "tcc1"))] +create_types!(Tcc0Tcc1); +#[hal_cfg(all("tcc2", "tcc3"))] +create_types!(Tcc2Tcc3); + +#[hal_cfg(all("tc1-d11", "tc2-d11"))] +create_types!(Tc1Tc2); + +#[hal_cfg(all("tcc2", "tc3-d21"))] +create_types!(Tcc2Tc3); + +create_types!(Wdt); + +// PCLK types +// TODO set channel numbers in devices.yaml +create_types!( + EvSys0, EvSys1, EvSys2, EvSys3, EvSys4, EvSys5, EvSys6, EvSys7, EvSys8, EvSys9, EvSys10, + EvSys11 +); +#[hal_cfg("i2s")] +create_types!(I2S0, I2S1); +create_types!(SlowClk); + +#[hal_cfg("clock-d5x")] +mod variant_ahb_types { + use super::{Sealed, hal_cfg}; + + #[hal_cfg("can0")] + create_types!(Can0); + #[hal_cfg("can1")] + create_types!(Can1); + create_types!(Cmcc); + #[hal_cfg("gmac")] + create_types!(Gmac); + create_types!(Hpb3); + create_types!(Icm); + create_types!(NvmCtrlSmeeProm, NvmCtrlCache); + create_types!(Pukcc); + create_types!(Qspi, Qspi2x); + #[hal_cfg("sdhc0")] + create_types!(Sdhc0); + #[hal_cfg("sdhc1")] + create_types!(Sdhc1); +} + +#[hal_cfg("clock-d5x")] +mod variant_apb_types { + use super::Sealed; + + create_types!(Adc1); + create_types!(Aes); + create_types!(Ccl); + create_types!(FreqM); + create_types!(Mclk); + create_types!(OscCtrl); + create_types!(Osc32kCtrl); + create_types!(Pcc); + create_types!(PDec); + create_types!(RamEcc); + create_types!(RstC); + create_types!(SupC); + create_types!(Trng); +} + +#[hal_cfg("clock-d5x")] +mod variant_pclk_types { + use super::Sealed; + + create_types!(CM4Trace); + create_types!(FreqMMeasure); + create_types!(FreqMReference); +} + +#[hal_cfg(any("clock-d11", "clock-d21"))] +mod variant_apb_types { + use super::{Sealed, hal_cfg}; + + #[hal_cfg("clock-d21")] + create_types!(Ac1); + create_types!(Pac1, Pac2); + create_types!(Ptc); + create_types!(SysCtrl); +} + +#[hal_cfg(any("clock-d11", "clock-d21"))] +mod variant_pclk_types { + use super::Sealed; + + create_types!(AcDig); + create_types!(AcAna); + create_types!(SercomSlow); +} + +#[hal_cfg("clock-d5x")] +pub use variant_ahb_types::*; +pub use variant_apb_types::*; +pub use variant_pclk_types::*; diff --git a/hal/src/peripherals/clock/d5x/v2/xosc.rs b/hal/src/clock/v2/xosc.rs similarity index 81% rename from hal/src/peripherals/clock/d5x/v2/xosc.rs rename to hal/src/clock/v2/xosc.rs index 2169636ca691..49af41688882 100644 --- a/hal/src/peripherals/clock/d5x/v2/xosc.rs +++ b/hal/src/clock/v2/xosc.rs @@ -11,8 +11,8 @@ //! //! 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`]. +//! [`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 @@ -86,14 +86,15 @@ //! //! 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`. +//! then set the `CrystalCurrent` level to `Medium` (supported only on some +//! targets). 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`]. @@ -126,11 +127,11 @@ //! 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. +//! Once the clock is stable, we can also enable failure detection on targets +//! that support it. 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::{ @@ -204,17 +205,38 @@ //! [`Dfll`]: super::dfll::Dfll //! [`EnabledDfll`]: super::dfll::EnabledDfll +use atsamd_hal_macros::{hal_cfg, hal_macro_helper}; use core::marker::PhantomData; use typenum::U0; -use crate::pac::oscctrl::{self, Xoscctrl}; +#[hal_cfg("clock-d5x")] +mod imports { + pub use super::super::dfll::DfllId; + pub use crate::pac::Oscctrl as Peripheral; + pub use crate::pac::oscctrl::{Xoscctrl, status::R as STATUS_R}; + pub use crate::typelevel::{Decrement, Increment}; +} + +#[hal_cfg(any("clock-d11", "clock-d21"))] +mod imports { + pub use crate::pac::Sysctrl as Peripheral; + pub use crate::pac::sysctrl::{Xosc as Xoscctrl, pclksr::R as STATUS_R, xosc::Gainselect}; +} + +use imports::*; + +#[hal_cfg("clock-d11")] +pub use crate::gpio::{PA08 as XIn0Id, PA09 as XOut0Id}; +#[hal_cfg("clock-d21")] +pub use crate::gpio::{PA14 as XIn0Id, PA15 as XOut0Id}; +#[hal_cfg("clock-d5x")] +pub use crate::gpio::{PA14 as XIn0Id, PA15 as XOut0Id, PB22 as XIn1Id, PB23 as XOut1Id}; -use crate::gpio::{FloatingDisabled, Pin, PinId, PA14, PA15, PB22, PB23}; +use crate::gpio::{FloatingDisabled, Pin, PinId}; use crate::time::Hertz; -use crate::typelevel::{Decrement, Increment, Sealed}; +use crate::typelevel::Sealed; -use super::dfll::DfllId; use super::{Enabled, Source}; //============================================================================== @@ -252,20 +274,36 @@ impl XoscToken { /// Return a reference to the corresponding XOSCCTRL register #[inline] + #[hal_macro_helper] 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) } + #[hal_cfg("clock-d5x")] + unsafe { + (*Peripheral::PTR).xoscctrl(X::NUM) + } + #[hal_cfg(any("clock-d11", "clock-d21"))] + unsafe { + (*Peripheral::PTR).xosc() + } } /// Read the STATUS register #[inline] - fn status(&self) -> oscctrl::status::R { + #[hal_macro_helper] + 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::Oscctrl::PTR).status().read() } + #[hal_cfg("clock-d5x")] + unsafe { + (*Peripheral::PTR).status().read() + } + #[hal_cfg(any("clock-d11", "clock-d21"))] + unsafe { + (*Peripheral::PTR).pclksr().read() + } } /// Check whether the XOSC is stable and ready @@ -276,6 +314,7 @@ impl XoscToken { } /// Check whether the XOSC has triggered failure detection + #[hal_cfg("clock-d5x")] #[inline] fn has_failed(&self) -> bool { let mask = 1 << (X::NUM + 2); @@ -283,27 +322,24 @@ impl XoscToken { } /// Check whether the XOSC has been switched to the safe clock + #[hal_cfg("clock-d5x")] #[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 + #[hal_cfg("clock-d5x")] #[inline] fn switch_back(&mut self) { self.xoscctrl().modify(|_, w| w.swben().set_bit()); } /// Enable clock failure detection and set the safe clock divider + #[hal_cfg("clock-d5x")] #[inline] fn enable_failure_detection(&mut self, div: SafeClockDiv) { // Safety: The divider is guaranteed to be in the valid range 0..16. @@ -317,6 +353,7 @@ impl XoscToken { } /// Disable clock failure detection + #[hal_cfg("clock-d5x")] #[inline] fn disable_failure_detection(&mut self) { self.xoscctrl().modify(|_, w| w.cfden().clear_bit()); @@ -324,32 +361,36 @@ impl XoscToken { /// Set most of the fields in the XOSCCTRL register #[inline] - fn set_xoscctrl(&mut self, settings: Settings) { - let xtalen = settings.mode == DynMode::CrystalMode; + #[hal_macro_helper] + fn enable(&mut self, mode: DynMode, settings: Settings) { + let xtalen = 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 { + self.xoscctrl().write(|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); + #[hal_cfg(any("clock-d11", "clock-d21"))] + { + w.ampgc().bit(settings.ampgc); + w.gain().variant(settings.gain.into()) + }; + #[hal_cfg("clock-d5x")] + { + 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) + w.xtalen().bit(xtalen); + w.enable().set_bit() }); } - /// 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()); + self.xoscctrl().write(|w| w.enable().clear_bit()); } } @@ -362,6 +403,7 @@ impl XoscToken { // 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. +#[hal_cfg("clock-d5x")] #[derive(Clone, Copy)] struct Settings { start_up: StartUpDelay, @@ -370,18 +412,54 @@ struct Settings { low_buf_gain: bool, on_demand: bool, run_standby: bool, - mode: DynMode, +} + +#[hal_cfg("clock-d5x")] +impl Default for Settings { + fn default() -> Self { + Settings { + start_up: StartUpDelay::default(), + loop_control: false, + current: CrystalCurrent::default(), + low_buf_gain: false, + on_demand: true, + run_standby: false, + } + } +} + +#[hal_cfg(any("clock-d11", "clock-d21"))] +#[derive(Clone, Copy)] +struct Settings { + start_up: StartUpDelay, + ampgc: bool, + gain: Gain, + on_demand: bool, + run_standby: bool, +} + +#[hal_cfg(any("clock-d11", "clock-d21"))] +impl Default for Settings { + fn default() -> Self { + Settings { + start_up: StartUpDelay::default(), + ampgc: false, + gain: Gain::default(), + on_demand: true, + run_standby: false, + } + } } //============================================================================== // XoscId //============================================================================== -/// Type-level enum identifying one of two possible [`Xosc`]s +/// Type-level enum identifying [`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. +/// The types implementing this trait, i.e. [`Xosc0Id`] and `Xosc1Id` on +/// supporting targets, 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. @@ -410,8 +488,8 @@ impl Sealed for Xosc0Id {} impl XoscId for Xosc0Id { const NUM: usize = 0; - type XIn = PA14; - type XOut = PA15; + type XIn = XIn0Id; + type XOut = XOut0Id; } /// Type-level variant of [`XoscId`] representing the identity of XOSC1 @@ -421,14 +499,17 @@ impl XoscId for Xosc0Id { /// /// [type-level programming]: crate::typelevel /// [type-level enums]: crate::typelevel#type-level-enums +#[hal_cfg("clock-d5x")] pub enum Xosc1Id {} +#[hal_cfg("clock-d5x")] impl Sealed for Xosc1Id {} +#[hal_cfg("clock-d5x")] impl XoscId for Xosc1Id { const NUM: usize = 1; - type XIn = PB22; - type XOut = PB23; + type XIn = XIn1Id; + type XOut = XOut1Id; } //============================================================================== @@ -526,6 +607,7 @@ pub enum StartUpDelay { /// 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. +#[hal_cfg("clock-d5x")] #[derive(Clone, Copy, Default, PartialEq, Eq)] pub enum CrystalCurrent { /// Used only in [`ClockMode`] to represent the default register values @@ -541,6 +623,7 @@ pub enum CrystalCurrent { ExtraHigh, } +#[hal_cfg("clock-d5x")] impl CrystalCurrent { #[inline] fn imult(&self) -> u8 { @@ -565,6 +648,34 @@ impl CrystalCurrent { } } +//============================================================================== +// Gain +//============================================================================== + +#[hal_cfg(any("clock-d11", "clock-d21"))] +#[derive(Clone, Copy, Default, PartialEq, Eq)] +pub enum Gain { + #[default] + Zero, + One, + Two, + Three, + Four, +} + +#[hal_cfg(any("clock-d11", "clock-d21"))] +impl From for Gainselect { + fn from(gain: Gain) -> Self { + match gain { + Gain::Zero => Gainselect::_0, + Gain::One => Gainselect::_1, + Gain::Two => Gainselect::_2, + Gain::Three => Gainselect::_3, + Gain::Four => Gainselect::_4, + } + } +} + //============================================================================== // DynMode //============================================================================== @@ -660,8 +771,8 @@ impl Mode for CrystalMode { /// 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 +/// 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. @@ -692,6 +803,7 @@ where pub type Xosc0 = Xosc; /// Type alias for the corresponding [`Xosc`] +#[hal_cfg("clock-d5x")] pub type Xosc1 = Xosc; /// An [`Enabled`] [`Xosc`] @@ -709,6 +821,7 @@ pub type EnabledXosc = Enabled, N>; pub type EnabledXosc0 = EnabledXosc; /// Type alias for the corresponding [`EnabledXosc`] +#[hal_cfg("clock-d5x")] pub type EnabledXosc1 = EnabledXosc; impl Xosc { @@ -768,6 +881,7 @@ impl Xosc { } /// Set the [`CrystalCurrent`] drive strength + #[hal_cfg("clock-d5x")] #[inline] pub fn current(mut self, current: CrystalCurrent) -> Self { self.settings.current = current; @@ -778,6 +892,7 @@ impl Xosc { /// /// If enabled, the hardware will automatically adjust the oscillator /// amplitude. In most cases, this will lower power consumption. + #[hal_cfg("clock-d5x")] #[inline] pub fn loop_control(mut self, loop_control: bool) -> Self { self.settings.loop_control = loop_control; @@ -790,11 +905,26 @@ impl Xosc { /// 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. + #[hal_cfg("clock-d5x")] #[inline] pub fn low_buf_gain(mut self, low_buf_gain: bool) -> Self { self.settings.low_buf_gain = low_buf_gain; self } + + #[hal_cfg(any("clock-d11", "clock-d21"))] + #[inline] + pub fn amplitude_gain_control(mut self, enabled: bool) -> Self { + self.settings.ampgc = enabled; + self + } + + #[hal_cfg(any("clock-d11", "clock-d21"))] + #[inline] + pub fn gain(mut self, gain: Gain) -> Self { + self.settings.gain = gain; + self + } } impl Xosc @@ -802,28 +932,19 @@ where X: XoscId, M: Mode, { + #[hal_cfg("clock-d5x")] #[inline] fn new(token: XoscToken, pins: M::Pins, freq: Hertz) -> Self { - let current = match freq.to_Hz() { - 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, - }; + let mut settings = Settings::default(); + if M::DYN == DynMode::CrystalMode { + settings.current = match freq.to_Hz() { + 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"), + }; + } Self { token, pins, @@ -832,6 +953,17 @@ where } } + #[hal_cfg(any("clock-d11", "clock-d21"))] + #[inline] + fn new(token: XoscToken, pins: M::Pins, freq: Hertz) -> Self { + Self { + token, + pins, + freq, + settings: Settings::default(), + } + } + /// Return the clock or crystal frequency #[inline] pub fn freq(&self) -> Hertz { @@ -885,9 +1017,7 @@ where /// [`Source`] for other clocks. #[inline] pub fn enable(mut self) -> EnabledXosc { - self.token.reset(); - self.token.set_xoscctrl(self.settings); - self.token.enable(); + self.token.enable(M::DYN, self.settings); Enabled::new(self) } } @@ -908,6 +1038,7 @@ where } } +#[hal_macro_helper] impl EnabledXosc where X: XoscId, @@ -920,6 +1051,25 @@ where self.0.token.is_ready() } + /// XOSC failure detection is not supported by the currently-documented + /// target + #[cfg(doc)] + #[hal_cfg(not("clock-d5x"))] + pub fn has_failed(&self) {} + + /// XOSC failure detection is not supported by the currently-documented + /// target + #[cfg(doc)] + #[hal_cfg(not("clock-d5x"))] + pub fn switch_back(&self) {} +} + +#[hal_cfg("clock-d5x")] +impl EnabledXosc +where + X: XoscId, + M: Mode, +{ /// Enable continuous monitoring of the [`Xosc`] for clock failure /// /// Failure detection will continuously monitor the [`Xosc`] to verify it is diff --git a/hal/src/peripherals/clock/d5x/v2/xosc32k.rs b/hal/src/clock/v2/xosc32k.rs similarity index 89% rename from hal/src/peripherals/clock/d5x/v2/xosc32k.rs rename to hal/src/clock/v2/xosc32k.rs index 838d02b1cbf8..4b574121cd7a 100644 --- a/hal/src/peripherals/clock/d5x/v2/xosc32k.rs +++ b/hal/src/clock/v2/xosc32k.rs @@ -33,20 +33,20 @@ //! `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. +//! 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. +//! The XOSC32K peripheral on some microcontrollers supports Clock Failure +//! Detection (CFD), like the [`Xosc`] clocks. 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 +//! `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 @@ -71,7 +71,6 @@ //! osculp32k::OscUlp32k, //! xosc32k::{ //! ControlGainMode, SafeClockDiv, StartUpDelay, Xosc1k, Xosc32k, Xosc32kBase, -//! Xosc32kCfd, //! }, //! }, //! gpio::Pins, @@ -91,8 +90,8 @@ //! 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. +//! [`StartUpDelay`]. Finally, on supported targets, we select a +//! `ControlGainMode` for the crystal before enabling it. //! //! ```no_run //! # use atsamd_hal::{ @@ -101,7 +100,6 @@ //! # osculp32k::OscUlp32k, //! # xosc32k::{ //! # ControlGainMode, SafeClockDiv, StartUpDelay, Xosc1k, Xosc32k, Xosc32kBase, -//! # Xosc32kCfd, //! # }, //! # }, //! # gpio::Pins, @@ -118,6 +116,7 @@ //! # ); //! let xosc32k_base = Xosc32kBase::from_crystal(tokens.xosc32k.base, pins.pa00, pins.pa01) //! .start_up_delay(StartUpDelay::Delay1s) +//! // n.b. only some targets support this setting //! .control_gain_mode(ControlGainMode::HighSpeed) //! .enable(); //! ``` @@ -132,7 +131,6 @@ //! # osculp32k::OscUlp32k, //! # xosc32k::{ //! # ControlGainMode, SafeClockDiv, StartUpDelay, Xosc1k, Xosc32k, Xosc32kBase, -//! # Xosc32kCfd, //! # }, //! # }, //! # gpio::Pins, @@ -155,10 +153,10 @@ //! ``` //! //! 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 +//! 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 @@ -168,7 +166,6 @@ //! # osculp32k::OscUlp32k, //! # xosc32k::{ //! # ControlGainMode, SafeClockDiv, StartUpDelay, Xosc1k, Xosc32k, Xosc32kBase, -//! # Xosc32kCfd, //! # }, //! # }, //! # gpio::Pins, @@ -341,17 +338,34 @@ //! [`is_switched`]: Xosc32kCfd::is_switched //! [`switch_back`]: Xosc32kCfd::switch_back +use atsamd_hal_macros::{hal_cfg, hal_macro_helper}; use fugit::RateExtU32; use typenum::U0; -use crate::pac::osc32kctrl::xosc32k::{Cgmselect, Startupselect}; -use crate::pac::osc32kctrl::{self, status, Cfdctrl}; - -use crate::gpio::{FloatingDisabled, Pin, PA00, PA01}; +#[hal_cfg("osc32kctrl")] +use crate::{ + clock::v2::osculp32k::OscUlp32kId, + pac::{ + Osc32kctrl as PERIPHERAL, + osc32kctrl::{Cfdctrl, Xosc32k as PacXosc32k, status::R as STATUS_R, xosc32k::Cgmselect}, + }, +}; + +#[hal_cfg("sysctrl")] +use crate::pac::{ + Sysctrl as PERIPHERAL, + sysctrl::{Xosc32k as PacXosc32k, pclksr::R as STATUS_R}, +}; + +#[hal_cfg(any("port-d21", "port-d5x"))] +pub use crate::gpio::{PA00 as XIn32Id, PA01 as XOut32Id}; +#[hal_cfg("port-d11")] +pub use crate::gpio::{PA08 as XIn32Id, PA09 as XOut32Id}; + +use crate::gpio::{FloatingDisabled, Pin}; use crate::time::Hertz; use crate::typelevel::{Decrement, Increment, PrivateDecrement, PrivateIncrement, Sealed}; -use super::osculp32k::OscUlp32kId; use super::{Enabled, Source}; //============================================================================== @@ -377,6 +391,7 @@ pub struct Xosc32kBaseToken(()); /// /// 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`]. +#[hal_cfg(any("sysctrl-d11", "osc32kctrl"))] pub struct Xosc1kToken(()); /// Singleton token that can be exchanged for [`Xosc32k`] @@ -398,16 +413,21 @@ pub struct Xosc32kToken(()); /// /// Clock failure detection is disabled at power-on reset. To use it, you must /// first enable it by exchanging the token with [`Xosc32kCfd::enable`]. +#[hal_cfg("osc32kctrl")] pub struct Xosc32kCfdToken(()); +#[hal_macro_helper] /// Set of tokens representing the disabled XOSC32K clocks power-on reset pub struct Xosc32kTokens { pub base: Xosc32kBaseToken, + #[hal_cfg(any("sysctrl-d11", "osc32kctrl"))] pub xosc1k: Xosc1kToken, pub xosc32k: Xosc32kToken, + #[hal_cfg("osc32kctrl")] pub cfd: Xosc32kCfdToken, } +#[hal_macro_helper] impl Xosc32kTokens { /// Create the set of tokens /// @@ -419,8 +439,10 @@ impl Xosc32kTokens { pub(super) unsafe fn new() -> Self { Self { base: Xosc32kBaseToken(()), + #[hal_cfg(any("sysctrl-d11", "osc32kctrl"))] xosc1k: Xosc1kToken(()), xosc32k: Xosc32kToken(()), + #[hal_cfg("osc32kctrl")] cfd: Xosc32kCfdToken(()), } } @@ -428,10 +450,18 @@ impl Xosc32kTokens { impl Xosc32kBaseToken { #[inline] - fn status(&self) -> status::R { + #[hal_macro_helper] + 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() } + #[hal_cfg("osc32kctrl")] + unsafe { + (*PERIPHERAL::PTR).status().read() + } + #[hal_cfg("sysctrl")] + unsafe { + (*PERIPHERAL::PTR).pclksr().read() + } } /// Check whether the XOSC32K is stable and ready @@ -441,11 +471,11 @@ impl Xosc32kBaseToken { } #[inline] - fn xosc32k(&self) -> &osc32kctrl::Xosc32k { + fn xosc32k(&self) -> &PacXosc32k { // 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() } + unsafe { (*PERIPHERAL::PTR).xosc32k() } } /// Reset the XOSC32K register @@ -456,23 +486,22 @@ impl Xosc32kBaseToken { /// 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| { + #[hal_macro_helper] + fn enable(&mut self, mode: DynMode, settings: Settings) { + let xtalen = mode == DynMode::CrystalMode; + self.xosc32k().write(|w| { + #[hal_cfg("osc32kctrl")] w.cgm().variant(settings.cgm.into()); - w.startup().variant(settings.start_up.into()); + #[hal_cfg("sysctrl")] + w.aampen().bit(settings.aampen); + unsafe { w.startup().bits(settings.start_up as u8) }; w.ondemand().bit(settings.on_demand); w.runstdby().bit(settings.run_standby); - w.xtalen().bit(xtalen) + w.xtalen().bit(xtalen); + w.enable().set_bit() }); } - /// Disable the XOSC32K - #[inline] - fn enable(&mut self) { - self.xosc32k().modify(|_, w| w.enable().set_bit()); - } - /// Disable the XOSC32K #[inline] fn disable(&mut self) { @@ -481,12 +510,14 @@ impl Xosc32kBaseToken { /// Enable the 1 kHz output #[inline] + #[hal_cfg(any("sysctrl-d11", "osc32kctrl"))] fn enable_1k(&mut self) { self.xosc32k().modify(|_, w| w.en1k().set_bit()); } /// Disable the 1 kHz output #[inline] + #[hal_cfg(any("sysctrl-d11", "osc32kctrl"))] fn disable_1k(&mut self) { self.xosc32k().modify(|_, w| w.en1k().clear_bit()); } @@ -510,12 +541,13 @@ impl Xosc32kBaseToken { } } +#[hal_cfg("osc32kctrl")] impl Xosc32kCfdToken { #[inline] - fn status(&self) -> status::R { + 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() } + unsafe { (*PERIPHERAL::PTR).status().read() } } /// Check whether the XOSC32K has triggered failure detection @@ -535,7 +567,7 @@ impl Xosc32kCfdToken { // 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() } + unsafe { (*PERIPHERAL::PTR).cfdctrl() } } /// Enable clock failure detection and set the safe clock divider @@ -571,13 +603,46 @@ impl Xosc32kCfdToken { // 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. +#[hal_cfg("clock-d5x")] #[derive(Clone, Copy)] struct Settings { start_up: StartUpDelay, cgm: ControlGainMode, on_demand: bool, run_standby: bool, - mode: DynMode, +} + +#[hal_cfg("clock-d5x")] +impl Default for Settings { + fn default() -> Self { + Settings { + start_up: StartUpDelay::Delay63ms, + cgm: ControlGainMode::Standard, + on_demand: true, + run_standby: false, + } + } +} + +#[hal_cfg(any("clock-d11", "clock-d21"))] +#[derive(Clone, Copy)] +struct Settings { + start_up: StartUpDelay, + aampen: bool, + on_demand: bool, + run_standby: bool, +} + +#[hal_cfg(any("clock-d11", "clock-d21"))] +impl Default for Settings { + fn default() -> Self { + Settings { + start_up: StartUpDelay::Delay63ms, + aampen: false, + on_demand: true, + run_standby: false, + } + } } //============================================================================== @@ -585,10 +650,10 @@ struct Settings { //============================================================================== /// Type alias for the XOSC32K input [`Pin`] -pub type XIn32 = Pin; +pub type XIn32 = Pin; /// Type alias for the XOSC32K output [`Pin`] -pub type XOut32 = Pin; +pub type XOut32 = Pin; //============================================================================== // SafeClockDiv @@ -631,6 +696,7 @@ impl From for bool { /// The start up delay is counted using the [`OscUlp32k`] clock. /// /// [`OscUlp32k`]: super::osculp32k::OscUlp32k +#[hal_cfg("clock-d5x")] #[repr(u8)] #[derive(Clone, Copy, Default, PartialEq, Eq)] pub enum StartUpDelay { @@ -644,18 +710,19 @@ pub enum StartUpDelay { Delay8s, } -impl From for Startupselect { - fn from(delay: StartUpDelay) -> Self { - match delay { - StartUpDelay::Delay63ms => Startupselect::Cycle2048, - StartUpDelay::Delay125ms => Startupselect::Cycle4096, - StartUpDelay::Delay500ms => Startupselect::Cycle16384, - StartUpDelay::Delay1s => Startupselect::Cycle32768, - StartUpDelay::Delay2s => Startupselect::Cycle65536, - StartUpDelay::Delay4s => Startupselect::Cycle131072, - StartUpDelay::Delay8s => Startupselect::Cycle262144, - } - } +#[hal_cfg(any("clock-d11", "clock-d21"))] +#[repr(u8)] +#[derive(Clone, Copy, Default, PartialEq, Eq)] +pub enum StartUpDelay { + #[default] + Delay122us, + Delay1ms, + Delay63ms, + Delay125ms, + Delay500ms, + Delay1s, + Delay2s, + Delay4s, } //============================================================================== @@ -666,6 +733,7 @@ impl From for Startupselect { /// /// The XOSC32K crystal oscillator control loop has a configurable gain to allow /// users to trade power for speed and stability. +#[hal_cfg("clock-d5x")] #[derive(Copy, Clone, Default, PartialEq, Eq)] pub enum ControlGainMode { #[default] @@ -673,6 +741,7 @@ pub enum ControlGainMode { HighSpeed, } +#[hal_cfg("clock-d5x")] impl From for Cgmselect { fn from(cgm: ControlGainMode) -> Self { match cgm { @@ -843,6 +912,7 @@ impl Xosc32kBase { /// Set the crystal oscillator [`ControlGainMode`] #[inline] + #[hal_cfg("clock-d5x")] pub fn control_gain_mode(mut self, cgm: ControlGainMode) -> Self { self.settings.cgm = cgm; self @@ -852,13 +922,7 @@ impl Xosc32kBase { 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, - }; + let settings = Settings::default(); Self { token, pins, @@ -932,8 +996,7 @@ impl Xosc32kBase { #[inline] pub fn enable(mut self) -> EnabledXosc32kBase { self.token.reset(); - self.token.set_xosc32k(self.settings); - self.token.enable(); + self.token.enable(M::DYN, self.settings); Enabled::new(self) } } @@ -997,10 +1060,12 @@ impl EnabledXosc32kBase { /// /// [`OscUlp32k`]: super::osculp32k::OscUlp32k /// [`EnabledOscUlp32k`]: super::osculp32k::EnabledOscUlp32k +#[hal_cfg("osc32kctrl")] pub struct Xosc32kCfd { token: Xosc32kCfdToken, } +#[hal_cfg("osc32kctrl")] impl Xosc32kCfd { /// Enable continuous monitoring of the XOSC32K for clock failure /// @@ -1071,6 +1136,22 @@ impl Xosc32kCfd { } } +/// Clock failure detection is not supported by the currently-documented target +#[cfg(doc)] +#[hal_cfg(not("osc32kctrl"))] +pub struct Xosc32kCfd; + +#[cfg(doc)] +#[hal_cfg(not("osc32kctrl"))] +impl Xosc32kCfd { + /// Clock failure detection is not supported by the currently-documented target + pub fn has_failed() {} + /// Clock failure detection is not supported by the currently-documented target + pub fn is_switched() {} + /// Clock failure detection is not supported by the currently-documented target + pub fn switch_back() {} +} + //============================================================================== // Ids //============================================================================== @@ -1100,10 +1181,18 @@ impl Sealed for Xosc32kId {} /// 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. +#[hal_cfg(any("sysctrl-d11", "osc32kctrl"))] pub struct Xosc1k { token: Xosc1kToken, } + +/// XOSC1K is not available on the currently-documented target +#[cfg(doc)] +#[hal_cfg(not(any("sysctrl-d11", "osc32kctrl")))] +pub struct Xosc1k; + + /// The [`Enabled`] [`Xosc1k`] clock /// /// As described in the [`clock` module documentation](super), the [`Enabled`] @@ -1113,8 +1202,10 @@ pub struct Xosc1k { /// /// As with [`Enabled`], the default value for `N` is `U0`; if left unspecified, /// the counter is assumed to be zero. +#[hal_cfg(any("sysctrl-d11", "osc32kctrl"))] pub type EnabledXosc1k = Enabled; +#[hal_cfg(any("sysctrl-d11", "osc32kctrl"))] impl Xosc1k { /// Enable 1 kHz output from the [`Xosc32kBase`] clock /// @@ -1133,6 +1224,7 @@ impl Xosc1k { } } +#[hal_cfg(any("sysctrl-d11", "osc32kctrl"))] impl EnabledXosc1k { /// Disable 1 kHz output from the [`Xosc32kBase`] clock /// @@ -1151,6 +1243,7 @@ impl EnabledXosc1k { } } +#[hal_cfg(any("sysctrl-d11", "osc32kctrl"))] impl Source for EnabledXosc1k { type Id = Xosc1kId; diff --git a/hal/src/delay.rs b/hal/src/delay.rs index 3ea6aa60bc8a..aad0273f3447 100644 --- a/hal/src/delay.rs +++ b/hal/src/delay.rs @@ -1,6 +1,5 @@ //! Delays -use atsamd_hal_macros::hal_cfg; use cortex_m::peripheral::SYST; use cortex_m::peripheral::syst::SystClkSource; @@ -9,10 +8,8 @@ use crate::ehal::delay::DelayNs; use crate::ehal_02; use crate::time::Hertz; -#[hal_cfg("rtc-d5x")] use crate::typelevel::Increment; -#[hal_cfg("rtc-d5x")] use crate::clock::v2::{Source, gclk::Gclk0Id}; /// System timer (SysTick) as a delay provider @@ -32,7 +29,6 @@ impl Delay { } } - #[hal_cfg("rtc-d5x")] /// Configures the system timer (SysTick) as a delay provide, compatible /// with the V2 clocking API pub fn new_with_source(mut syst: SYST, gclk0: S) -> (Self, S::Inc) diff --git a/hal/src/gpio/dynpin.rs b/hal/src/gpio/dynpin.rs index 913193b81e3a..1c6d5e290a24 100644 --- a/hal/src/gpio/dynpin.rs +++ b/hal/src/gpio/dynpin.rs @@ -111,7 +111,6 @@ pub enum DynAlternate { E, F, G, - #[hal_cfg(any("port-d21", "port-d5x"))] H, #[hal_cfg("port-d5x")] I, @@ -182,9 +181,7 @@ macro_rules! dyn_alternate { }; } -dyn_alternate!(B, C, D, E, F, G); -#[hal_cfg(any("port-d21", "port-d5x"))] -dyn_alternate!(H); +dyn_alternate!(B, C, D, E, F, G, H); #[hal_cfg("port-d5x")] dyn_alternate!(I, J, K, L, M, N); diff --git a/hal/src/gpio/pin.rs b/hal/src/gpio/pin.rs index 040c8473e8bd..8979f0b63d4c 100644 --- a/hal/src/gpio/pin.rs +++ b/hal/src/gpio/pin.rs @@ -341,10 +341,7 @@ macro_rules! alternate { }; } -alternate!(B, C, D, E, F, G); - -#[hal_cfg(any("port-d21", "port-d5x"))] -alternate!(H); +alternate!(B, C, D, E, F, G, H); #[hal_cfg("port-d5x")] alternate!(I, J, K, L, M, N); @@ -752,7 +749,6 @@ impl_core_convert_from!( AlternateE, AlternateF, AlternateG, - #[hal_cfg(any("port-d21", "port-d5x"))] AlternateH, #[hal_cfg("port-d5x")] AlternateI, @@ -1088,8 +1084,7 @@ macro_rules! pins{ )+ } impl Pins { - /// Take ownership of the PAC - /// [`Port`] and split it into + /// Take ownership of the PAC [`Port`] and split it into /// discrete [`Pin`]s #[inline] pub fn new(port: Port) -> Pins { diff --git a/hal/src/gpio/reg.rs b/hal/src/gpio/reg.rs index e1229fa425ae..1eb865fe8da8 100644 --- a/hal/src/gpio/reg.rs +++ b/hal/src/gpio/reg.rs @@ -125,7 +125,6 @@ impl From for ModeFields { G => { fields.pmux = 6; } - #[hal_cfg(any("port-d21", "port-d5x"))] H => { fields.pmux = 7; } diff --git a/hal/src/lib.rs b/hal/src/lib.rs index e20f6b5025d1..4bf3008b7bba 100644 --- a/hal/src/lib.rs +++ b/hal/src/lib.rs @@ -74,6 +74,8 @@ macro_rules! dbgprint { #[cfg(feature = "async")] pub mod async_hal; +#[cfg(feature = "device")] +pub mod clock; #[cfg(feature = "device")] pub mod delay; #[cfg(feature = "device")] diff --git a/hal/src/peripherals/adc/builder.rs b/hal/src/peripherals/adc/builder.rs index baaa62e74b83..34441bf5e01a 100644 --- a/hal/src/peripherals/adc/builder.rs +++ b/hal/src/peripherals/adc/builder.rs @@ -244,17 +244,20 @@ impl AdcBuilder { Ok(adc_clk_freq / clocks_per_sample) } - /// Turn the builder into an ADC + /// Turn the builder into an ADC. + /// + /// This function will convert the provided + /// [`Pclk`](crate::clock::v2::pclk::Pclk) into a [`DynPclk`](crate::clock::v2::pclk::DynPclk). #[hal_cfg("adc-d5x")] #[inline] - pub fn enable( + pub fn enable( self, adc: I::Instance, clk: crate::clock::v2::apb::ApbClk, - pclk: &crate::clock::v2::pclk::Pclk, + pclk: impl Into>, ) -> Result, BuilderError> { let settings = self.to_settings()?; - Adc::new(adc, settings, clk, pclk).map_err(|e| e.into()) + Adc::new(adc, settings, clk, pclk.into()).map_err(|e| e.into()) } #[hal_cfg(any("adc-d11", "adc-d21"))] diff --git a/hal/src/peripherals/adc/mod.rs b/hal/src/peripherals/adc/mod.rs index c34b560ad113..f00e0f1c2bdc 100644 --- a/hal/src/peripherals/adc/mod.rs +++ b/hal/src/peripherals/adc/mod.rs @@ -48,7 +48,10 @@ pub use builder::*; #[hal_cfg(any("adc-d11", "adc-d21"))] use crate::pac::adc as adc0; #[hal_cfg("adc-d5x")] -use crate::pac::adc0; +use crate::{ + clock::v2::{apb::ApbClk, pclk::DynPclk}, + pac::adc0, +}; pub use adc0::refctrl::Refselselect as Reference; @@ -174,7 +177,8 @@ pub struct Adc { #[hal_cfg("adc-d5x")] pub struct Adc { adc: I::Instance, - _apbclk: crate::clock::v2::apb::ApbClk, + _apbclk: ApbClk, + _pclk: DynPclk, cfg: AdcSettings, discard: bool, } @@ -203,11 +207,11 @@ impl Adc { /// frequency for the ADC is restricted to 90Mhz for stable performance. #[hal_cfg("adc-d5x")] #[inline] - pub(crate) fn new( + pub(crate) fn new( adc: I::Instance, settings: AdcSettings, - clk: crate::clock::v2::apb::ApbClk, - pclk: &crate::clock::v2::pclk::Pclk, + clk: ApbClk, + pclk: DynPclk, ) -> Result { // TODO: Ideally, the ADC struct would take ownership of the Pclk type here. // However, since clock::v2 is not implemented for all chips yet, the @@ -226,6 +230,7 @@ impl Adc { let mut new_adc = Self { adc, _apbclk: clk, + _pclk: pclk, cfg: settings, discard: true, }; @@ -423,9 +428,9 @@ impl Adc { /// Return the underlying ADC PAC object and the enabled APB ADC clock. #[hal_cfg("adc-d5x")] #[inline] - pub fn free(mut self) -> (I::Instance, crate::clock::v2::apb::ApbClk) { + pub fn free(mut self) -> (I::Instance, ApbClk, DynPclk) { self.software_reset(); - (self.adc, self._apbclk) + (self.adc, self._apbclk, self._pclk) } /// Reset the peripheral. diff --git a/hal/src/peripherals/clock/d5x/mod.rs b/hal/src/peripherals/clock/d5x/mod.rs deleted file mode 100644 index ce090b2fd4b5..000000000000 --- a/hal/src/peripherals/clock/d5x/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -//! # Clocking API -//! -//! Users are encouraged to use [`v2`] variant of an API because of the richer -//! feature set and safety. - -pub mod v1; -pub use v1::*; - -pub mod v2; diff --git a/hal/src/peripherals/clock/d5x/v2/types.rs b/hal/src/peripherals/clock/d5x/v2/types.rs deleted file mode 100644 index 88bfc43ee225..000000000000 --- a/hal/src/peripherals/clock/d5x/v2/types.rs +++ /dev/null @@ -1,127 +0,0 @@ -//! 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 atsamd_hal_macros::hal_cfg; - -use crate::typelevel::Sealed; - -#[hal_cfg("sercom0")] -pub use crate::sercom::Sercom0; - -#[hal_cfg("sercom1")] -pub use crate::sercom::Sercom1; - -#[hal_cfg("sercom2")] -pub use crate::sercom::Sercom2; - -#[hal_cfg("sercom3")] -pub use crate::sercom::Sercom3; - -#[hal_cfg("sercom4")] -pub use crate::sercom::Sercom4; - -#[hal_cfg("sercom5")] -pub use crate::sercom::Sercom5; - -#[hal_cfg("sercom6")] -pub use crate::sercom::Sercom6; - -#[hal_cfg("sercom7")] -pub use crate::sercom::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); -#[hal_cfg("can0")] -create_types!(Can0); -#[hal_cfg("can1")] -create_types!(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); -#[hal_cfg("gmac")] -create_types!(Gmac); -create_types!(Hpb0, Hpb1, Hpb2, Hpb3); -create_types!(Icm); -create_types!(Mclk); -create_types!(NvmCtrl, NvmCtrlSmeeProm, NvmCtrlCache); -#[hal_cfg("i2s")] -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); -#[hal_cfg("sdhc1")] -create_types!(Sdhc1); -create_types!(SlowClk); -create_types!(SupC); -create_types!(Tc0Tc1, Tc0, Tc1); -create_types!(Tc2Tc3, Tc2, Tc3); -#[hal_cfg(all("tc4", "tc5"))] -create_types!(Tc4Tc5, Tc4, Tc5); -#[hal_cfg(all("tc6", "tc7"))] -create_types!(Tc6Tc7, Tc6, Tc7); -create_types!(Tcc0Tcc1, Tcc0, Tcc1); -create_types!(Tcc2Tcc3, Tcc2); -#[hal_cfg("tcc3")] -create_types!(Tcc3); -#[hal_cfg("tcc4")] -create_types!(Tcc4); -create_types!(Trng); -create_types!(Usb); -create_types!(Wdt); diff --git a/hal/src/peripherals/eic.rs b/hal/src/peripherals/eic.rs index e3d4f2fe829d..b959d9e74c38 100644 --- a/hal/src/peripherals/eic.rs +++ b/hal/src/peripherals/eic.rs @@ -83,7 +83,7 @@ mod impls {} pub use impls::async_api::*; #[hal_cfg("eic-d5x")] -use super::clock::v2::{self, gclk::GclkId, osculp32k::OscUlp32kId, pclk::Pclk, rtcosc::RtcOsc}; +use crate::clock::v2::{self, gclk::GclkId, osculp32k::OscUlp32kId, pclk::Pclk, rtcosc::RtcOsc}; pub type Sense = pac::eic::config::Sense0select; diff --git a/hal/src/peripherals/mod.rs b/hal/src/peripherals/mod.rs index 78eb4048a08c..45259be902ac 100644 --- a/hal/src/peripherals/mod.rs +++ b/hal/src/peripherals/mod.rs @@ -31,12 +31,6 @@ pub mod usb {} )] pub mod pwm {} -#[hal_module( - any("clock-d11", "clock-d21") => "clock/d11.rs", - "clock-d5x" => "clock/d5x/mod.rs", -)] -pub mod clock {} - #[hal_module("aes")] pub mod aes {} diff --git a/hal/src/peripherals/pwm/d5x.rs b/hal/src/peripherals/pwm/d5x.rs index a9094fac4ac6..c6b3400b3121 100644 --- a/hal/src/peripherals/pwm/d5x.rs +++ b/hal/src/peripherals/pwm/d5x.rs @@ -680,9 +680,9 @@ impl $crate::ehal_02::Pwm for $TYPE { pwm_tcc! { Tcc0Pwm: (Tcc0, TCC0Pinout, Tcc0Tcc1Clock, apbbmask, tcc0_, TccPwm0Wrapper) } #[hal_cfg("tcc1")] pwm_tcc! { Tcc1Pwm: (Tcc1, TCC1Pinout, Tcc0Tcc1Clock, apbbmask, tcc1_, TccPwm1Wrapper) } -#[hal_cfg("tcc2")] +#[hal_cfg(all("tcc2", "tcc3"))] pwm_tcc! { Tcc2Pwm: (Tcc2, TCC2Pinout, Tcc2Tcc3Clock, apbcmask, tcc2_, TccPwm2Wrapper) } -#[hal_cfg("tcc3")] +#[hal_cfg(all("tcc2", "tcc3"))] pwm_tcc! { Tcc3Pwm: (Tcc3, TCC3Pinout, Tcc2Tcc3Clock, apbcmask, tcc3_, TccPwm3Wrapper) } #[hal_cfg("tcc4")] pwm_tcc! { Tcc4Pwm: (Tcc4, TCC4Pinout, Tcc4Clock, apbdmask, tcc4_, TccPwm4Wrapper) } diff --git a/hal/src/sercom/dma.rs b/hal/src/sercom/dma.rs index 02e1c0a337d1..703b8f0b0d10 100644 --- a/hal/src/sercom/dma.rs +++ b/hal/src/sercom/dma.rs @@ -176,10 +176,9 @@ where /// the provided buffer. /// /// In order to be (safely) non-blocking, his method has to take a `'static` - /// buffer. If you'd rather use DMA with the blocking - /// [`embedded_io::Read`] trait, and avoid having - /// to use static buffers, - /// use [`Uart::with_rx_channel`](Self::with_tx_channel) instead. + /// buffer. If you'd rather use DMA with the blocking [`embedded_io::Read`] + /// trait, and avoid having to use static buffers, use + /// [`Uart::with_rx_channel`](Self::with_tx_channel) instead. #[inline] #[hal_macro_helper] pub fn receive_with_dma( @@ -218,10 +217,9 @@ where /// provided buffer. /// /// In order to be (safely) non-blocking, his method takes a `'static` - /// buffer. If you'd rather use DMA with the blocking - /// [`embedded_io::Write`] trait, and avoid - /// having to use static buffers, - /// use[`Uart::with_tx_channel`](Self::with_tx_channel) instead. + /// buffer. If you'd rather use DMA with the blocking [`embedded_io::Write`] + /// trait, and avoid having to use static buffers, use + /// [`Uart::with_tx_channel`](Self::with_tx_channel) instead. #[inline] #[hal_macro_helper] pub fn send_with_dma( diff --git a/rustfmt.sh b/rustfmt.sh index 83555c8b3b6e..12a1d195bf45 100755 --- a/rustfmt.sh +++ b/rustfmt.sh @@ -1,3 +1,5 @@ +#!/usr/bin/env bash + RET=0 # first, all boards & their examples