From ec46dd27613de0e068af78f607435a145d17e29b Mon Sep 17 00:00:00 2001 From: CordlessCoder Date: Tue, 28 Jul 2026 11:14:25 +0100 Subject: [PATCH 1/7] MSPM0 Low-power sleep and executor-thread --- embassy-mspm0/Cargo.toml | 7 + embassy-mspm0/src/executor.rs | 101 +++++++++++++ embassy-mspm0/src/lib.rs | 5 + embassy-mspm0/src/low_power/c110x.rs | 70 +++++++++ embassy-mspm0/src/low_power/full.rs | 80 +++++++++++ embassy-mspm0/src/low_power/h321x.rs | 66 +++++++++ embassy-mspm0/src/low_power/mod.rs | 204 +++++++++++++++++++++++++++ embassy-mspm0/src/sysctl/mod.rs | 138 ++++++++++++++++++ embassy-mspm0/src/time_driver/mod.rs | 2 +- embassy-mspm0/src/time_driver/tim.rs | 54 +++---- embassy-mspm0/src/trng.rs | 17 +-- 11 files changed, 692 insertions(+), 52 deletions(-) create mode 100644 embassy-mspm0/src/executor.rs create mode 100644 embassy-mspm0/src/low_power/c110x.rs create mode 100644 embassy-mspm0/src/low_power/full.rs create mode 100644 embassy-mspm0/src/low_power/h321x.rs create mode 100644 embassy-mspm0/src/low_power/mod.rs diff --git a/embassy-mspm0/Cargo.toml b/embassy-mspm0/Cargo.toml index d591f6514d..7b0553da0e 100644 --- a/embassy-mspm0/Cargo.toml +++ b/embassy-mspm0/Cargo.toml @@ -47,6 +47,7 @@ rustdoc-args = ["--cfg", "docsrs"] [dependencies] embassy-sync = { version = "0.8.0", path = "../embassy-sync" } +embassy-executor = { version = "0.10.0", path = "../embassy-executor", optional = true } # TODO: Support other tick rates embassy-time-driver = { version = "0.2.2", path = "../embassy-time-driver", optional = true, features = ["tick-hz-32_768"] } embassy-time-queue-utils = { version = "0.3.2", path = "../embassy-time-queue-utils", optional = true } @@ -116,6 +117,12 @@ nrst-pin-as-gpio = [] ## Allow using the SWD pins as regular GPIO pins. swd-pins-as-gpio = [] +low-power = [] + +executor-thread = ["_executor"] + +_executor = ["dep:embassy-executor", "low-power"] + #! ## Time # Features starting with `_` are for internal use only. They're not intended diff --git a/embassy-mspm0/src/executor.rs b/embassy-mspm0/src/executor.rs new file mode 100644 index 0000000000..187f5f7756 --- /dev/null +++ b/embassy-mspm0/src/executor.rs @@ -0,0 +1,101 @@ +//! MSPM0-specific `embassy-executor` platform. +//! +//! This module provides an `embassy-executor` platform specific for MSPM0 chips that integrates [`low_power::sleep()`](crate::low_power::sleep) in the main loop. +//! Read the `embassy-executor` README for information about what "executor platforms" are and how they work. +//! +//! To use it: +//! - Enable the `executor-thread` feature on this crate. +//! - **Do not** enable features `platform-cortex-m`, `executor-thread` or `executor-interrupt` in the `embassy-executor` crate. +//! - Tell the `main` macro to use this executor like this: +//! +//! ```rust,no_run +//! #[embassy_executor::main(executor = "embassy_mspm0::executor::Executor", entry = "cortex_m_rt::entry")] +//! async fn main(spawner: Spawner) { +//! let p = embassy_mspm0::init(Config::default()); +//! // ... +//! } +//! ``` + +#[unsafe(export_name = "__pender")] +#[cfg(feature = "executor-thread")] +fn __pender(_context: *mut ()) { + thread::SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst); +} + +#[cfg(feature = "executor-thread")] +pub use thread::*; +#[cfg(feature = "executor-thread")] +mod thread { + use core::marker::PhantomData; + use core::sync::atomic::{AtomicBool, Ordering}; + + use embassy_executor::{Spawner, raw}; + + const THREAD_PENDER: usize = usize::MAX; + + /// Set by the pender to signal pending work; checked before sleeping since `WFI` ignores `SEV`. + pub(crate) static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); + + /// Thread-mode executor that deep-sleeps on idle via [`low_power::sleep`](crate::low_power::sleep). + /// + /// This is the simplest and most common kind of executor. It runs on + /// thread mode (at the lowest priority level), and uses the `WFE` ARM instruction + /// to sleep when it has no more work to do. When a task is woken, a `SEV` instruction + /// is executed, to make the `WFE` exit from sleep and poll the task. + pub struct Executor { + inner: raw::Executor, + not_send: PhantomData<*mut ()>, + } + + impl Executor { + /// Create a new Executor. + pub fn new() -> Self { + Self { + inner: raw::Executor::new(THREAD_PENDER as *mut ()), + not_send: PhantomData, + } + } + + /// Run the executor. + /// + /// The `init` closure is called with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the executor starts running the tasks. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + /// + /// This function never returns. + pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + init(self.inner.spawner()); + + loop { + unsafe { + self.inner.poll(); + + critical_section::with(|cs| { + if SIGNAL_WORK_THREAD_MODE.load(Ordering::SeqCst) { + SIGNAL_WORK_THREAD_MODE.store(false, Ordering::SeqCst); + } else { + crate::low_power::sleep(cs); + } + }); + } + } + } + } + + impl Default for Executor { + fn default() -> Self { + Self::new() + } + } +} diff --git a/embassy-mspm0/src/lib.rs b/embassy-mspm0/src/lib.rs index 76bcee6fa1..85b62f6b29 100644 --- a/embassy-mspm0/src/lib.rs +++ b/embassy-mspm0/src/lib.rs @@ -14,12 +14,16 @@ mod macros; pub mod adc; pub mod dma; +#[cfg(feature = "_executor")] +pub mod executor; pub mod gpio; // TODO: I2C unicomm #[cfg(not(unicomm))] pub mod i2c; #[cfg(not(unicomm))] pub mod i2c_target; +#[cfg(feature = "low-power")] +pub mod low_power; #[cfg(any(mspm0g150x, mspm0g151x, mspm0g350x, mspm0g351x))] pub mod mathacl; pub mod sysctl; @@ -194,6 +198,7 @@ pub fn init(config: Config) -> Peripherals { w.set_mfpclken(true); }); + // TODO: Errata PCMU_ERR_03 states that BOR thresholds othre than 0 don't work in STANDBY. pac::SYSCTL.borthreshold().modify(|w| { w.set_level(0); }); diff --git a/embassy-mspm0/src/low_power/c110x.rs b/embassy-mspm0/src/low_power/c110x.rs new file mode 100644 index 0000000000..af1988c879 --- /dev/null +++ b/embassy-mspm0/src/low_power/c110x.rs @@ -0,0 +1,70 @@ +//! C-series deep sleep: STOP0/2 + STANDBY0/1 (no STOP1). +//! +//! Covers mspm0c110x and mspm0c1105/c1106. These families lack the STOP1 (4 MHz SYSOSC) sub-mode, +//! and their STOP0 additionally clears `USELFCLK`. +//! +//! The entry sequence from the TRM is: +//! `PMODECFG.DSLEEP` selects STOP vs STANDBY, +//! `SYSOSCCFG.DISABLESTOP` selects STOP0 vs STOP2 (this family has no 4 MHz STOP1) with STOP0 also clearing `MCLKCFG.USELFCLK`, +//! `MCLKCFG.STOPCLKSTBY` selects STANDBY0 vs STANDBY1. + +use critical_section::CriticalSection; +use pac::sysctl::vals::Dsleep; + +use crate::pac; + +/// Deep-sleep idle modes, ordered by increasing power saving. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum SleepMode { + /// SYSOSC available. Fastest wake, highest STOP current. + Stop0, + /// SYSOSC disabled; ULPCLK runs from LFCLK. Lowest STOP current. + Stop2, + /// low-speed peripherals retained. + Standby0, + /// only TIMG0/TIMG1 remain clocked. Lowest wake-capable current. + Standby1, +} + +/// Enter a deep-sleep `mode` and block until an interrupt wakes the core. +/// +/// This runs with interrupts masked, but `WFI` still wakes on enabled interrupts with PRIMASK set. +/// They will run once the `CriticalSection` exits. +/// +/// # Safety +/// The caller is responsible for ensuring deep sleep is safe right now: no transaction that must survive is in +/// flight (PD1 powers down and its peripherals lose state unless retained by the mode), and a wake source is armed. +pub unsafe fn enter_sleep(_cs: CriticalSection, mode: SleepMode) { + let sysctl = pac::SYSCTL; + + let dsleep = match mode { + SleepMode::Stop0 | SleepMode::Stop2 => Dsleep::STOP, + SleepMode::Standby0 | SleepMode::Standby1 => Dsleep::STANDBY, + }; + sysctl.pmodecfg().modify(|w| w.set_dsleep(dsleep)); + + match mode { + SleepMode::Stop0 => { + sysctl.sysosccfg().modify(|w| w.set_disablestop(false)); + sysctl.mclkcfg().modify(|w| w.set_uselfclk(false)); + } + SleepMode::Stop2 => sysctl.sysosccfg().modify(|w| w.set_disablestop(true)), + SleepMode::Standby0 => sysctl.mclkcfg().modify(|w| w.set_stopclkstby(false)), + SleepMode::Standby1 => sysctl.mclkcfg().modify(|w| w.set_stopclkstby(true)), + } + + super::arm_and_wait(); +} + +/// Map the family-independent [`SleepLevel`](super::SleepLevel) to this family's [`SleepMode`]. +pub(super) fn level_to_mode(level: super::SleepLevel) -> SleepMode { + use super::SleepLevel; + + match level { + SleepLevel::Stop0 | SleepLevel::Stop1 => SleepMode::Stop0, + SleepLevel::Stop2 => SleepMode::Stop2, + SleepLevel::Standby0 => SleepMode::Standby0, + SleepLevel::Standby1 => SleepMode::Standby1, + } +} diff --git a/embassy-mspm0/src/low_power/full.rs b/embassy-mspm0/src/low_power/full.rs new file mode 100644 index 0000000000..37deeaf810 --- /dev/null +++ b/embassy-mspm0/src/low_power/full.rs @@ -0,0 +1,80 @@ +//! Full-capability deep sleep: STOP0/1/2 + STANDBY0/1. +//! +//! Covers every family whose SYSCTL exposes the full STOP policy: all G families and the supported +//! L families. +//! +//! The entry sequence from the TRM is: +//! `PMODECFG.DSLEEP` selects STOP vs STANDBY, +//! `SYSOSCCFG.{USE4MHZSTOP, DISABLESTOP}` combination selects the STOP sub-mode, +//! `MCLKCFG.STOPCLKSTBY` selects the STANDBY sub-mode. + +use critical_section::CriticalSection; +use pac::sysctl::vals::Dsleep; + +use crate::pac; + +/// Deep-sleep idle modes, ordered by increasing power saving. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum SleepMode { + /// SYSOSC stays at full speed. Fastest wake, highest STOP current. + Stop0, + /// SYSOSC limited to 4 MHz. + Stop1, + /// SYSOSC disabled; ULPCLK runs from LFCLK. Lowest STOP current. + Stop2, + /// low-speed peripherals retained. + Standby0, + /// only TIMG0/TIMG1 remain clocked. Lowest wake-capable current. + Standby1, +} + +/// Enter a deep-sleep `mode` and block until an interrupt wakes the core. +/// +/// This runs with interrupts masked, but `WFI` still wakes on enabled interrupts with PRIMASK set. +/// They will run once the `CriticalSection` exits. +/// +/// # Safety +/// The caller is responsible for ensuring deep sleep is safe right now: no transaction that must survive is in +/// flight (PD1 powers down and its peripherals lose state unless retained by the mode), and a wake source is armed. +pub unsafe fn enter_sleep(_cs: CriticalSection, mode: SleepMode) { + let sysctl = pac::SYSCTL; + + let dsleep = match mode { + SleepMode::Stop0 | SleepMode::Stop1 | SleepMode::Stop2 => Dsleep::STOP, + SleepMode::Standby0 | SleepMode::Standby1 => Dsleep::STANDBY, + }; + sysctl.pmodecfg().modify(|w| w.set_dsleep(dsleep)); + + match mode { + SleepMode::Stop0 => sysctl.sysosccfg().modify(|w| { + w.set_use4mhzstop(false); + w.set_disablestop(false); + }), + SleepMode::Stop1 => sysctl.sysosccfg().modify(|w| { + w.set_use4mhzstop(true); + w.set_disablestop(false); + }), + SleepMode::Stop2 => sysctl.sysosccfg().modify(|w| { + w.set_use4mhzstop(false); + w.set_disablestop(true); + }), + SleepMode::Standby0 => sysctl.mclkcfg().modify(|w| w.set_stopclkstby(false)), + SleepMode::Standby1 => sysctl.mclkcfg().modify(|w| w.set_stopclkstby(true)), + } + + super::arm_and_wait(); +} + +/// Map the family-independent [`SleepLevel`](super::SleepLevel) to this family's [`SleepMode`]. +pub(super) fn level_to_mode(level: super::SleepLevel) -> SleepMode { + use super::SleepLevel; + + match level { + SleepLevel::Stop0 => SleepMode::Stop0, + SleepLevel::Stop1 => SleepMode::Stop1, + SleepLevel::Stop2 => SleepMode::Stop2, + SleepLevel::Standby0 => SleepMode::Standby0, + SleepLevel::Standby1 => SleepMode::Standby1, + } +} diff --git a/embassy-mspm0/src/low_power/h321x.rs b/embassy-mspm0/src/low_power/h321x.rs new file mode 100644 index 0000000000..a7b9fb9c49 --- /dev/null +++ b/embassy-mspm0/src/low_power/h321x.rs @@ -0,0 +1,66 @@ +//! H321x deep sleep: STOP0/2 + STANDBY0/1 (no STOP1). +//! +//! Covers mspm0h321x. It lacks the STOP1 (4 MHz SYSOSC) sub-mode, and its STOP0 clears `DISABLESTOP` only. +//! +//! The entry sequence from the TRM is: +//! `PMODECFG.DSLEEP` selects STOP vs STANDBY, +//! `SYSOSCCFG.DISABLESTOP` selects STOP0 vs STOP2 (no 4 MHz STOP1, and STOP0 clears DISABLESTOP only), +//! `MCLKCFG.STOPCLKSTBY` selects STANDBY0 vs STANDBY1. + +use critical_section::CriticalSection; +use pac::sysctl::vals::Dsleep; + +use crate::pac; + +/// Deep-sleep idle modes, ordered by increasing power saving. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum SleepMode { + /// SYSOSC available. Fastest wake, highest STOP current. + Stop0, + /// SYSOSC disabled; ULPCLK runs from LFCLK. Lowest STOP current. + Stop2, + /// Low-speed peripherals retained. + Standby0, + /// Only TIMG0/TIMG1 remain clocked. Lowest wake-capable current. + Standby1, +} + +/// Enter a deep-sleep `mode` and block until an interrupt wakes the core. +/// +/// This runs with interrupts masked, but `WFI` still wakes on enabled interrupts with PRIMASK set. +/// They will run once the `CriticalSection` exits. +/// +/// # Safety +/// The caller is responsible for ensuring deep sleep is safe right now: no transaction that must survive is in +/// flight (PD1 powers down and its peripherals lose state unless retained by the mode), and a wake source is armed. +pub unsafe fn enter_sleep(_cs: CriticalSection, mode: SleepMode) { + let sysctl = pac::SYSCTL; + + let dsleep = match mode { + SleepMode::Stop0 | SleepMode::Stop2 => Dsleep::STOP, + SleepMode::Standby0 | SleepMode::Standby1 => Dsleep::STANDBY, + }; + sysctl.pmodecfg().modify(|w| w.set_dsleep(dsleep)); + + match mode { + SleepMode::Stop0 => sysctl.sysosccfg().modify(|w| w.set_disablestop(false)), + SleepMode::Stop2 => sysctl.sysosccfg().modify(|w| w.set_disablestop(true)), + SleepMode::Standby0 => sysctl.mclkcfg().modify(|w| w.set_stopclkstby(false)), + SleepMode::Standby1 => sysctl.mclkcfg().modify(|w| w.set_stopclkstby(true)), + } + + super::arm_and_wait(); +} + +/// Map the family-independent [`SleepLevel`](super::SleepLevel) to this family's [`SleepMode`]. +pub(super) fn level_to_mode(level: super::SleepLevel) -> SleepMode { + use super::SleepLevel; + + match level { + SleepLevel::Stop0 | SleepLevel::Stop1 => SleepMode::Stop0, + SleepLevel::Stop2 => SleepMode::Stop2, + SleepLevel::Standby0 => SleepMode::Standby0, + SleepLevel::Standby1 => SleepMode::Standby1, + } +} diff --git a/embassy-mspm0/src/low_power/mod.rs b/embassy-mspm0/src/low_power/mod.rs new file mode 100644 index 0000000000..acf92ff49a --- /dev/null +++ b/embassy-mspm0/src/low_power/mod.rs @@ -0,0 +1,204 @@ +//! Low-power (deep-sleep) support. +//! +//! Deep-sleep depth is gated by [`WakeGuard`](crate::sysctl::WakeGuard), which drivers hold to keep +//! the chip shallower than a given [`SleepLevel`]; the low-power executor then idles into the deepest +//! mode no guard blocks. +//! +//! # Wake source caveats +//! - `GPIO_ERR_01` (L, G) — a wake edge can be missed. Only the STANDBY1 half is handled: if the pin +//! is still asserted when the chip goes back to sleep no further edge is detected +//! - `GPIO_ERR_08` (H) — in low-power mode a GPIO can trigger a fast wake regardless of the `FASTWAKE` +//! register and the pin configuration. +//! - `UART_ERR_01` (L, G, H) — a start bit arriving while the chip is on its way back into STANDBY1 +//! is not received. +use core::sync::atomic::Ordering; + +use critical_section::CriticalSection; +use pac::cpuss::vals::Prefetch; +use pac::sysctl::vals::Dsleep; +use portable_atomic::AtomicU8; + +use crate::pac; + +#[cfg(any( + mspm0l110x, mspm0l130x, mspm0l134x, mspm0l122x, mspm0l222x, mspm0g110x, mspm0g150x, mspm0g310x, mspm0g350x, + mspm0g151x, mspm0g351x, mspm0g518x +))] +#[path = "full.rs"] +mod inner; + +#[cfg(any(mspm0c110x, mspm0c1105_c1106))] +#[path = "c110x.rs"] +mod inner; + +#[cfg(mspm0h321x)] +#[path = "h321x.rs"] +mod inner; + +#[cfg(any( + mspm0l110x, + mspm0l130x, + mspm0l134x, + mspm0l122x, + mspm0l222x, + mspm0g110x, + mspm0g150x, + mspm0g310x, + mspm0g350x, + mspm0g151x, + mspm0g351x, + mspm0g518x, + mspm0c110x, + mspm0c1105_c1106, + mspm0h321x +))] +pub use inner::{SleepMode, enter_sleep}; + +#[cfg(not(any( + mspm0l110x, + mspm0l130x, + mspm0l134x, + mspm0l122x, + mspm0l222x, + mspm0g110x, + mspm0g150x, + mspm0g310x, + mspm0g350x, + mspm0g151x, + mspm0g351x, + mspm0g518x, + mspm0c110x, + mspm0c1105_c1106, + mspm0h321x +)))] +compile_error!("the `low-power` feature is not implemented for this chip family"); + +pub use crate::sysctl::SleepLevel; + +static SLEEP_BLOCKS: [AtomicU8; 5] = [const { AtomicU8::new(0) }; 5]; + +/// Block sleep at `level` and every deeper mode. Paired with [`unblock`] by +/// [`WakeGuard`](crate::sysctl::WakeGuard). +pub(crate) fn block(level: SleepLevel) { + trace!("Blocking sleep at level {:?}", level); + if SLEEP_BLOCKS[level as usize].fetch_add(1, Ordering::Relaxed) == (u8::MAX - 1) { + panic!("Blocking at SleepLevel {:?} would overflow", level) + }; +} + +/// Remove a block previously added at `level`. +pub(crate) fn unblock(level: SleepLevel) { + trace!("Unblocking sleep at level {:?}", level); + SLEEP_BLOCKS[level as usize].fetch_sub(1, Ordering::Relaxed); +} + +/// Deepest mode currently permitted, or `None` if all deep sleep is blocked. +fn deepest_allowed() -> Option { + for (i, blocks) in SLEEP_BLOCKS.iter().enumerate() { + if blocks.load(Ordering::Relaxed) > 0 { + return i.checked_sub(1).map(|j| SleepLevel::LEVELS[j]); + } + } + Some(SleepLevel::Standby1) +} + +/// Enter the deepest sleep permitted by the active [`WakeGuard`](crate::sysctl::WakeGuard)s, waiting +/// for an interrupt. +/// +/// Called by the low-power executor on idle. With no guards held it enters the deepest mode the +/// chip supports; a held guard caps the depth, and a guard on [`SleepLevel::Stop0`] keeps it a +/// plain `WFI`. +/// +/// # Safety +/// Deep sleep powers down PD1 (and, in STANDBY, most of PD0). Any peripheral transaction that must +/// survive has to be protected by a [`WakeGuard`](crate::sysctl::WakeGuard) shallow enough to keep it +/// clocked. Until the drivers hold their own guards, the caller is responsible for this. +pub unsafe fn sleep(cs: CriticalSection) { + trace!("Attempting to enter low-power sleep"); + + // Some of the prefetcher errata applies even for a plain WFI + // FIXME: This could be a problem for embassy-executor's default executor. + let _prefetch = PrefetchSuspend::new(); + + match deepest_allowed() { + None => { + trace!("Low-power sleep blocked"); + cortex_m::asm::dsb(); + cortex_m::asm::wfi(); + cortex_m::asm::isb(); + } + Some(level) => { + trace!("Low-power sleep allowed, mode: {:?}", level); + enter_sleep(cs, inner::level_to_mode(level)); + } + } +} + +/// Enter SHUTDOWN, the lowest-power state. Does not return. +/// +/// SHUTDOWN powers down VCORE: all SRAM is lost except the `SHUTDNSTORE` bytes, and the only wake +/// sources are a wake-capable IO event, NRST, or SWD activity. Does not return as the wake results +/// in a reset. +/// You can respond to the reset on boot using [`ResetCause::BorWakeFromShutdown`](crate::ResetCause). +// +// From the TRM: SYSCTL "Operating Modes": set `PMODECFG.DSLEEP = SHUTDOWN`, arm `SLEEPDEEP`, +// then `WFI`. This is identical across every MSPM0 family. +pub fn shutdown(_cs: CriticalSection) -> ! { + let sysctl = pac::SYSCTL; + sysctl.pmodecfg().modify(|w| w.set_dsleep(Dsleep::SHUTDOWN)); + + let _prefetch = PrefetchSuspend::new(); + + let mut scb = unsafe { cortex_m::Peripherals::steal() }.SCB; + scb.set_sleepdeep(); + cortex_m::asm::dsb(); + + cortex_m::asm::wfi(); + + unsafe { core::hint::unreachable_unchecked() } +} + +/// Workaround for CPU_ERR_02, CPU_ERR_03, PMCU_ERR_13 - the prefetcher has at least one errata in +/// sleep for every currently supported MCU. +struct PrefetchSuspend(pac::cpuss::regs::Ctl); + +impl PrefetchSuspend { + fn new() -> Self { + let saved = pac::CPUSS.ctl().read(); + let mut disabled = saved; + disabled.set_prefetch(Prefetch::DISABLE); + pac::CPUSS.ctl().write_value(disabled); + + // CPU_ERR_02 means the prefetcher will not be disabled until pending flash access is finished. + // Reading any SYSCTL register after disabling prefetch will complete the pending flash access. + #[cfg(not(mspm0h321x))] + let _ = pac::SYSCTL.shutdnstore(0).read(); + #[cfg(mspm0h321x)] + let _ = pac::SYSCTL.clkstatus().read(); + + cortex_m::asm::dsb(); + cortex_m::asm::isb(); + + Self(saved) + } +} + +impl Drop for PrefetchSuspend { + fn drop(&mut self) { + pac::CPUSS.ctl().write_value(self.0); + } +} + +/// Arm ARM deep-sleep (`SLEEPDEEP`), wait for an interrupt, then clear it. +/// +/// The mode-specific SYSCTL programming must already be done by the caller, and the prefetcher must +/// already be suspended by [`PrefetchSuspend`]. `WFI` wakes on a pending enabled interrupt even with +/// PRIMASK set; `SLEEPDEEP` is cleared on wake so a later plain executor idle does not deep-sleep. +pub(crate) unsafe fn arm_and_wait() { + let mut scb = unsafe { cortex_m::Peripherals::steal() }.SCB; + scb.set_sleepdeep(); + cortex_m::asm::dsb(); + cortex_m::asm::wfi(); + cortex_m::asm::isb(); + scb.clear_sleepdeep(); +} diff --git a/embassy-mspm0/src/sysctl/mod.rs b/embassy-mspm0/src/sysctl/mod.rs index c8d4588ba0..d0a7085fdb 100644 --- a/embassy-mspm0/src/sysctl/mod.rs +++ b/embassy-mspm0/src/sysctl/mod.rs @@ -23,6 +23,144 @@ mod inner; pub use inner::ClkOutSource; +/// Deep-sleep idle modes, ordered by increasing power saving. +/// +/// Has no effect when the `low-power` feature is disabled. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum SleepLevel { + Stop0, + Stop1, + Stop2, + Standby0, + Standby1, +} + +impl SleepLevel { + #[allow(unused)] + pub(crate) const LEVELS: [SleepLevel; 5] = [ + SleepLevel::Stop0, + SleepLevel::Stop1, + SleepLevel::Stop2, + SleepLevel::Standby0, + SleepLevel::Standby1, + ]; + + /// Shallowest level to block so a PD0 peripheral clocked at `clock_hz` keeps running, or `None` + /// to block nothing (any sleep depth is fine). + /// + /// `clock_hz` is the frequency of the clock that the peripheral depends on. + /// ULPCLK for bus-clocked peripherals, or the LFCLK/MFCLK source rate for those clocked directly. + /// The per-mode ceiling is the same across every MSPM0 family: STOP0/STOP1 cap at 4 MHz, STOP2 + /// and STANDBY0 at 32 kHz (LFCLK), and only STANDBY1 unclocks PD0 (there just TIMG0/1 stay clocked). + /// + /// NOTE: Assumes the RUN0 run mode, the only one the HAL configures today + /// (STOP0 reaches 4 MHz only when entered from RUN0). + pub const fn floor_for_clock_hz(clock_hz: u32) -> Option { + // Per-mode clock ceilings, from the family TRMs' "DMA Operating Mode Support" and "Operating + // Modes" sections. + // STANDBY0 clocks all PD0 peripherals from LFCLK; STANDBY1 does not. + const STOP_HZ: u32 = 4_000_000; + const LFCLK_HZ: u32 = 32_768; + + if clock_hz > STOP_HZ { + // Needs MCLK + Some(Self::Stop0) + } else if clock_hz > LFCLK_HZ { + // Reqires MFCLK + Some(Self::Stop2) + } else if clock_hz > 0 { + // LFCLK is enough, keep PD0 alive + Some(Self::Standby1) + } else { + // No clock + None + } + } +} + +// Boundary checks for `floor_for_clock_hz`. `crate::fmt` cannot be used in const. +const _: () = { + core::assert!(matches!( + SleepLevel::floor_for_clock_hz(4_000_001), + Some(SleepLevel::Stop0) + )); + core::assert!(matches!( + SleepLevel::floor_for_clock_hz(4_000_000), + Some(SleepLevel::Stop2) + )); + core::assert!(matches!( + SleepLevel::floor_for_clock_hz(32_769), + Some(SleepLevel::Stop2) + )); + core::assert!(matches!( + SleepLevel::floor_for_clock_hz(32_768), + Some(SleepLevel::Standby1) + )); + core::assert!(matches!(SleepLevel::floor_for_clock_hz(1), Some(SleepLevel::Standby1))); + core::assert!(matches!(SleepLevel::floor_for_clock_hz(0), None)); +}; + +/// A token forbidding a deep-sleep mode (and anything deeper) while held. +/// +/// A guard at `level` blocks that [`SleepLevel`] and every deeper mode; the low-power executor then +/// idles into the deepest mode still permitted, or a plain `WFI` if even [`SleepLevel::Stop0`] is +/// blocked. Guards are refcounted per level. +/// +/// Always available so drivers can hold one unconditionally. +/// Without the `low-power` feature it is a no-op. +#[must_use] +pub struct WakeGuard { + #[cfg(feature = "low-power")] + level: SleepLevel, + _unit: (), +} + +impl WakeGuard { + /// Forbid entering `level` or any deeper mode until dropped. + /// + /// [`SleepLevel::Stop0`] blocks all deep sleep, leaving only `WFI`. Without the `low-power` + /// feature `level` is ignored and this does nothing. + #[inline] + pub fn new(level: SleepLevel) -> Self { + #[cfg(not(feature = "low-power"))] + let _ = level; + #[cfg(feature = "low-power")] + crate::low_power::block(level); + + Self { + #[cfg(feature = "low-power")] + level, + _unit: (), + } + } +} + +impl Drop for WakeGuard { + #[inline] + fn drop(&mut self) { + #[cfg(feature = "low-power")] + crate::low_power::unblock(self.level); + } +} + +/// Frequency of MCLK, which is also the rate of the ULPCLK ("bus clock") driving PD0 peripherals. +// TODO: Compute this once the MCLK rate can be adjusted. +#[cfg(any(mspm0c110x, mspm0c1105_c1106))] +#[allow(dead_code)] +pub(crate) fn mclk_frequency() -> u32 { + 24_000_000 +} + +#[cfg(any( + mspm0g110x, mspm0g150x, mspm0g151x, mspm0g310x, mspm0g350x, mspm0g351x, mspm0h321x, mspm0l110x, mspm0l122x, + mspm0l130x, mspm0l134x, mspm0l222x +))] +#[allow(dead_code)] +pub(crate) fn mclk_frequency() -> u32 { + 32_000_000 +} + /// Divider applied to the clock source of the CLK_OUT pin. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] diff --git a/embassy-mspm0/src/time_driver/mod.rs b/embassy-mspm0/src/time_driver/mod.rs index 4ea02a3b21..022b58b837 100644 --- a/embassy-mspm0/src/time_driver/mod.rs +++ b/embassy-mspm0/src/time_driver/mod.rs @@ -1,4 +1,4 @@ // TODO: Alternative TIMB implementation #[path = "tim.rs"] mod driver; -pub use driver::*; +pub(crate) use driver::*; diff --git a/embassy-mspm0/src/time_driver/tim.rs b/embassy-mspm0/src/time_driver/tim.rs index 6bddb98f6b..b006366f4a 100644 --- a/embassy-mspm0/src/time_driver/tim.rs +++ b/embassy-mspm0/src/time_driver/tim.rs @@ -17,6 +17,18 @@ use crate::{peripherals, tim}; #[cfg(any(time_driver_timg12, time_driver_timg13))] compile_error!("TIMG12 and TIMG13 are not supported by the time driver yet"); +// Only TIMG0 and TIMG1 remain clocked in STANDBY, so they are the only timers that can wake the +// core from deep sleep via the time driver. Reject a `low-power` build on any other timer. +// TODO: Or maybe allow using them, but disable STANDBY? STOP0 will still work. +// Another option is to leak a wake guard when one of those timers is used. + +#[cfg(all(feature = "low-power", not(any(time_driver_timg0, time_driver_timg1))))] +compile_error!( + "the `low-power` feature requires the time driver to run on TIMG0 or TIMG1, as they are the \ + only timers clocked in STANDBY. Enable `time-driver-timg0`, `time-driver-timg1`, or \ + `time-driver-any` (which selects TIMG0 when available)." +); + // Currently TIMG12 and TIMG13 are excluded because those are 32-bit timers. #[cfg(time_driver_timg0)] type T = peripherals::TIMG0; @@ -61,23 +73,9 @@ fn regs() -> Tim { // - `period` is incremented on overflow (at counter value 0) // - `period` is incremented "midway" between overflows (at counter value 0x8000) // -// Therefore, when `period` is even, counter is in 0..0x7FFF. When odd, counter is in 0x8000..0xFFFF +// When `period` is even, counter is in 0..0x7FFF. When odd, counter is in 0x8000..0xFFFF // This allows for now() to return the correct value even if it races an overflow. // -// The overflow half must be counted on the *zero* event, not the load event, or that invariant does -// not hold. SLAU847F Figure 28-10 shows that in up counting mode the load event is asserted during -// the `CTR == LOAD` TIMCLK cycle, i.e. one full tick *before* the counter wraps, while the zero -// event is asserted during the `CTR == 0` cycle. Counting on the load event advances `period` while -// `counter` still reads 0xFFFF, which is the one pairing the parity repair below cannot fix: it -// leaves a stale counter against a fresh period for a whole tick on every wrap, worth 2^16 ticks of -// error. The zero event lands exactly on the parity boundary, same as CCU0 lands exactly on -// `CTR == 0x8000`. (LOAD is 2^16 - 1, not 2^16: up counting mode has a period of `LOAD + 1`.) -// -// To get `now()`, `period` is read first, then `counter` is read. If the counter value matches -// the expected range for the `period` parity, we're done. If it doesn't, this means that -// a new period start has raced us between reading `period` and `counter`, so we assume the `counter` value -// corresponds to the next period. -// // `period` is a 32bit integer, so It overflows on 2^32 * 2^15 / 32768 seconds of uptime, which is 136 years. fn calc_now(period: u32, counter: u16) -> u64 { ((period as u64) << 15) + ((counter as u32 ^ ((period & 1) << 15)) as u64) @@ -116,11 +114,13 @@ impl TimxDriver { // 1. Select TIMCLK source regs.clksel().modify(|w| { + // Use LFCLK at 32.768 kHz, as it's available all the way down to STANDBY w.set_lfclk_sel(true); }); - // 2. Divide by TIMCLK, we don't need to divide further for the 32kHz tick rate + // 2. Divide by TIMCLK regs.clkdiv().modify(|w| { + // 32.768 kHz on the LFCLK requires no division. w.set_ratio(0); // + 1 }); @@ -140,7 +140,7 @@ impl TimxDriver { regs.counterregs(0).ctrctl().modify(|w| { w.set_repeat(Repeat::REPEAT_1); - w.set_cvae(Cvae::ZEROVAL); + w.set_cvae(Cvae::NOCHANGE); w.set_cm(Cm::UP); // Must explicitly set CZC, CAC and CLC to 0 in order for all the timers to count. @@ -158,6 +158,8 @@ impl TimxDriver { // Middle regs.counterregs(0).cc(0).write_value(0x8000 as u32); regs.counterregs(0).load().write_value(u16::MAX as u32); + // Start with the counter at 1 to avoid immediately incrementing period. + regs.counterregs(0).ctr().write_value(1); // Enable the period interrupts // @@ -176,15 +178,6 @@ impl TimxDriver { w.set_en(true); }); - // Enabling the counter with CVAE = ZEROVAL zeroes it, which can latch a zero event. Discard - // anything latched up to here before arming the interrupt, or that event would be counted as - // a wrap that never happened and put the whole timebase a one period off. - regs.cpu_int(0).iclr().write(|w| { - w.set_z(true); - w.set_ccu0(true); - w.set_ccu1(true); - }); - ::Interrupt::IRQ.unpend(); unsafe { ::Interrupt::IRQ.enable() }; } @@ -295,15 +288,6 @@ impl Driver for TimxDriver { // On MSPM0 this sequence reread and comparison must be done or else time may // appear to go backwards. - // - // The timer counter and the software period counter are not updated as one - // atomic operation. It is possible for the timer interrupt to increment the - // period counter while reading the counter. - // - // For example, `period` may be read as X while the timer is near the end of - // that period. The timer then wraps and the interrupt increments `period` to - // X + 1 before the counter is read. If the counter read returns 0x0000, using - // the stale period X would produce a timestamp 32768 ticks in the past. loop { let period = self.period.load(Ordering::Relaxed); // Ensure the compiler does not read the counter before the period. diff --git a/embassy-mspm0/src/trng.rs b/embassy-mspm0/src/trng.rs index 61dc1437b7..3b520976ba 100644 --- a/embassy-mspm0/src/trng.rs +++ b/embassy-mspm0/src/trng.rs @@ -280,21 +280,6 @@ impl TryRngCore for Trng<'_, D> { impl TryCryptoRng for Trng<'_, Crypto> {} -// TODO: Replace this when the MCLK rate can be adjusted. -#[cfg(any(mspm0c110x, mspm0c1105_c1106))] -fn get_mclk_frequency() -> u32 { - 24_000_000 -} - -// TODO: Replace this when the MCLK rate can be adjusted. -#[cfg(any( - mspm0g110x, mspm0g150x, mspm0g151x, mspm0g310x, mspm0g350x, mspm0g351x, mspm0h321x, mspm0l110x, mspm0l122x, - mspm0l130x, mspm0l134x, mspm0l222x -))] -fn get_mclk_frequency() -> u32 { - 32_000_000 -} - // Inner TRNG driver implementation. Used to reduce monomorphization bloat. struct TrngInner<'d> { decim_rate: vals::DecimRate, @@ -356,7 +341,7 @@ impl TrngInner<'_> { fn set_div(&mut self) { // L-series TRM 13.2.2: The TRNG is derived from MCLK. Datasheets specify 9.5-20 MHz range. - let freq = get_mclk_frequency(); + let freq = crate::sysctl::mclk_frequency(); let ratio = if freq > 160_000_000 { panic!("MCLK frequency {} > 160 MHz is not compatible with the TRNG", freq) } else if freq >= 80_000_000 { From 490dadf8652e9340b13ead0b6c8fe499e2e086b6 Mon Sep 17 00:00:00 2001 From: CordlessCoder Date: Tue, 28 Jul 2026 11:39:12 +0100 Subject: [PATCH 2/7] MSPM0 Implement executor-interrupt --- embassy-mspm0/Cargo.toml | 5 + embassy-mspm0/src/executor.rs | 162 ++++++++++++++++++++++++++++- embassy-mspm0/src/low_power/mod.rs | 2 + 3 files changed, 164 insertions(+), 5 deletions(-) diff --git a/embassy-mspm0/Cargo.toml b/embassy-mspm0/Cargo.toml index 7b0553da0e..e23582c7f9 100644 --- a/embassy-mspm0/Cargo.toml +++ b/embassy-mspm0/Cargo.toml @@ -27,6 +27,9 @@ build = [ {target = "thumbv6m-none-eabi", features = ["defmt", "mspm0l1345dgs28", "time-driver-any"]}, {target = "thumbv6m-none-eabi", features = ["defmt", "mspm0l1106dgs28", "time-driver-any"]}, {target = "thumbv6m-none-eabi", features = ["defmt", "mspm0l1228pt", "time-driver-any"]}, + {target = "thumbv6m-none-eabi", features = ["defmt", "mspm0l1306rhb", "time-driver-any", "executor-thread"]}, + {target = "thumbv6m-none-eabi", features = ["defmt", "mspm0l1306rhb", "time-driver-any", "executor-interrupt"]}, + {target = "thumbv6m-none-eabi", features = ["defmt", "mspm0l1306rhb", "time-driver-any", "executor-thread", "executor-interrupt"]}, ] [package.metadata.embassy_docs] @@ -121,6 +124,8 @@ low-power = [] executor-thread = ["_executor"] +executor-interrupt = ["_executor"] + _executor = ["dep:embassy-executor", "low-power"] #! ## Time diff --git a/embassy-mspm0/src/executor.rs b/embassy-mspm0/src/executor.rs index 187f5f7756..d269ebc638 100644 --- a/embassy-mspm0/src/executor.rs +++ b/embassy-mspm0/src/executor.rs @@ -4,7 +4,7 @@ //! Read the `embassy-executor` README for information about what "executor platforms" are and how they work. //! //! To use it: -//! - Enable the `executor-thread` feature on this crate. +//! - Enable the `executor-thread` and/or `executor-interrupt` feature on this crate. //! - **Do not** enable features `platform-cortex-m`, `executor-thread` or `executor-interrupt` in the `embassy-executor` crate. //! - Tell the `main` macro to use this executor like this: //! @@ -17,9 +17,36 @@ //! ``` #[unsafe(export_name = "__pender")] -#[cfg(feature = "executor-thread")] -fn __pender(_context: *mut ()) { - thread::SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst); +#[cfg(any(feature = "executor-thread", feature = "executor-interrupt"))] +fn __pender(context: *mut ()) { + // `context` is either `THREAD_PENDER`, or an interrupt number passed to `InterruptExecutor::start`. + let context = context as usize; + + #[cfg(feature = "executor-thread")] + // Try to optimize away the branch when only thread mode is enabled. + if !cfg!(feature = "executor-interrupt") || context == thread::THREAD_PENDER { + thread::SIGNAL_WORK_THREAD_MODE.store(true, core::sync::atomic::Ordering::SeqCst); + return; + } + + #[cfg(feature = "executor-interrupt")] + { + use cortex_m::interrupt::InterruptNumber; + use cortex_m::peripheral::NVIC; + + #[derive(Clone, Copy)] + struct Irq(u16); + + // SAFETY: `context` was an `InterruptNumber` when passed to `InterruptExecutor::start`. + unsafe impl InterruptNumber for Irq { + fn number(self) -> u16 { + self.0 + } + } + + // MSPM0 is Cortex-M0+, which has no STIR. + NVIC::pend(Irq(context as u16)); + } } #[cfg(feature = "executor-thread")] @@ -31,7 +58,7 @@ mod thread { use embassy_executor::{Spawner, raw}; - const THREAD_PENDER: usize = usize::MAX; + pub(super) const THREAD_PENDER: usize = usize::MAX; /// Set by the pender to signal pending work; checked before sleeping since `WFI` ignores `SEV`. pub(crate) static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); @@ -99,3 +126,128 @@ mod thread { } } } + +#[cfg(feature = "executor-interrupt")] +pub use interrupt::*; +#[cfg(feature = "executor-interrupt")] +mod interrupt { + use core::cell::{Cell, UnsafeCell}; + use core::mem::MaybeUninit; + + use cortex_m::interrupt::InterruptNumber; + use cortex_m::peripheral::NVIC; + use critical_section::Mutex; + use embassy_executor::raw; + + /// Interrupt-mode executor. + /// + /// This executor runs tasks in interrupt mode. The interrupt handler is set up + /// to poll tasks, and when a task is woken the interrupt is pended from software. + /// + /// This allows running async tasks at a priority higher than thread mode. One + /// use case is to leave thread mode free for non-async tasks. Another use case is + /// to run multiple executors: one in thread mode for low priority tasks and another in + /// interrupt mode for higher priority tasks. Higher priority tasks will preempt lower + /// priority ones. + /// + /// It is even possible to run multiple interrupt mode executors at different priorities, + /// by assigning different priorities to the interrupts. + /// + /// To use it, you have to pick an interrupt that won't be used by the hardware. + /// MSPM0 has no dedicated software interrupt, so use the interrupt of a peripheral the + /// application leaves unused. + /// + /// It is somewhat more complex to use, it's recommended to use the thread-mode + /// `Executor` instead, if it works for your use case. + pub struct InterruptExecutor { + started: Mutex>, + executor: UnsafeCell>, + } + + unsafe impl Send for InterruptExecutor {} + unsafe impl Sync for InterruptExecutor {} + + impl InterruptExecutor { + /// Create a new, not started `InterruptExecutor`. + #[inline] + pub const fn new() -> Self { + Self { + started: Mutex::new(Cell::new(false)), + executor: UnsafeCell::new(MaybeUninit::uninit()), + } + } + + /// Executor interrupt callback. + /// + /// # Safety + /// + /// - You MUST call this from the interrupt handler, and from nowhere else. + /// - You must not call this before calling `start()`. + pub unsafe fn on_interrupt(&'static self) { + let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; + executor.poll(); + } + + /// Start the executor. + /// + /// This initializes the executor, enables the interrupt, and returns. + /// The executor keeps running in the background through the interrupt. + /// + /// This returns a [`SendSpawner`] you can use to spawn tasks on it. A [`SendSpawner`] + /// is returned instead of a [`Spawner`](embassy_executor::Spawner) because the executor effectively runs in a + /// different "thread" (the interrupt), so spawning tasks on it is effectively + /// sending them. + /// + /// To obtain a [`Spawner`](embassy_executor::Spawner) for this executor, use [`Spawner::for_current_executor()`](embassy_executor::Spawner::for_current_executor()) from + /// a task running in it. + /// + /// # Interrupt requirements + /// + /// You must write the interrupt handler yourself, and make it call [`on_interrupt()`](Self::on_interrupt). + /// + /// This method already enables (unmasks) the interrupt, you must NOT do it yourself. + /// + /// You must set the interrupt priority before calling this method. You MUST NOT + /// do it after. + /// + /// [`SendSpawner`]: embassy_executor::SendSpawner + pub fn start(&'static self, irq: impl InterruptNumber) -> embassy_executor::SendSpawner { + if critical_section::with(|cs| self.started.borrow(cs).replace(true)) { + panic!("InterruptExecutor::start() called multiple times on the same executor."); + } + + unsafe { + (&mut *self.executor.get()) + .as_mut_ptr() + .write(raw::Executor::new(irq.number() as *mut ())) + } + + let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; + + unsafe { NVIC::unmask(irq) } + + executor.spawner().make_send() + } + + /// Get a SendSpawner for this executor + /// + /// This returns a [`SendSpawner`](embassy_executor::SendSpawner) you can use to spawn tasks on this + /// executor. + /// + /// This MUST only be called on an executor that has already been started. + /// The function will panic otherwise. + pub fn spawner(&'static self) -> embassy_executor::SendSpawner { + if !critical_section::with(|cs| self.started.borrow(cs).get()) { + panic!("InterruptExecutor::spawner() called on uninitialized executor."); + } + let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; + executor.spawner().make_send() + } + } + + impl Default for InterruptExecutor { + fn default() -> Self { + Self::new() + } + } +} diff --git a/embassy-mspm0/src/low_power/mod.rs b/embassy-mspm0/src/low_power/mod.rs index acf92ff49a..41ea8801f5 100644 --- a/embassy-mspm0/src/low_power/mod.rs +++ b/embassy-mspm0/src/low_power/mod.rs @@ -110,6 +110,8 @@ fn deepest_allowed() -> Option { /// plain `WFI`. /// /// # Safety +/// Must be called from thread mode. `WFI` in a handler is only woken by an interrupt of *higher* +/// priority than the one running, so sleeping inside the lowest-priority handler never returns. /// Deep sleep powers down PD1 (and, in STANDBY, most of PD0). Any peripheral transaction that must /// survive has to be protected by a [`WakeGuard`](crate::sysctl::WakeGuard) shallow enough to keep it /// clocked. Until the drivers hold their own guards, the caller is responsible for this. From 5bbf29fcab1244f0acfc4e19f86d59af3bbd4b93 Mon Sep 17 00:00:00 2001 From: CordlessCoder Date: Tue, 28 Jul 2026 11:49:11 +0100 Subject: [PATCH 3/7] Fix typo --- embassy-mspm0/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-mspm0/src/lib.rs b/embassy-mspm0/src/lib.rs index 85b62f6b29..1a8574d7f1 100644 --- a/embassy-mspm0/src/lib.rs +++ b/embassy-mspm0/src/lib.rs @@ -198,7 +198,7 @@ pub fn init(config: Config) -> Peripherals { w.set_mfpclken(true); }); - // TODO: Errata PCMU_ERR_03 states that BOR thresholds othre than 0 don't work in STANDBY. + // TODO: Errata PCMU_ERR_03 states that BOR thresholds other than 0 don't work in STANDBY. pac::SYSCTL.borthreshold().modify(|w| { w.set_level(0); }); From a6055ffa632a57abdb841ab803335c5d7442ec34 Mon Sep 17 00:00:00 2001 From: CordlessCoder Date: Wed, 29 Jul 2026 00:41:25 +0100 Subject: [PATCH 4/7] Add shutdown safety comment --- embassy-mspm0/src/low_power/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-mspm0/src/low_power/mod.rs b/embassy-mspm0/src/low_power/mod.rs index 41ea8801f5..f3d2199443 100644 --- a/embassy-mspm0/src/low_power/mod.rs +++ b/embassy-mspm0/src/low_power/mod.rs @@ -157,6 +157,7 @@ pub fn shutdown(_cs: CriticalSection) -> ! { cortex_m::asm::wfi(); + // SAFETY: Setting DSLEEP to SHUTDOWN means WFI will never return. unsafe { core::hint::unreachable_unchecked() } } From 0e3d76dc05967d9163a5319a2527ca07bb62ded9 Mon Sep 17 00:00:00 2001 From: CordlessCoder Date: Wed, 29 Jul 2026 09:13:00 +0100 Subject: [PATCH 5/7] Update mspm0-metapac to mspm0-data-f78bc71 chiptool now generates register values in PascalCase rather than SCREAMING_SNAKE, and emits the key/index fieldsets as newtypes carrying associated constants instead of enums. No functional change. --- embassy-mspm0/Cargo.toml | 4 +- embassy-mspm0/build.rs | 4 +- embassy-mspm0/src/adc.rs | 50 +++++++++---------- embassy-mspm0/src/dma.rs | 36 ++++++------- embassy-mspm0/src/gpio.rs | 12 ++--- embassy-mspm0/src/i2c.rs | 50 +++++++++---------- embassy-mspm0/src/i2c_target.rs | 18 +++---- embassy-mspm0/src/lib.rs | 40 +++++++-------- embassy-mspm0/src/low_power/c110x.rs | 4 +- embassy-mspm0/src/low_power/full.rs | 4 +- embassy-mspm0/src/low_power/h321x.rs | 4 +- embassy-mspm0/src/low_power/mod.rs | 4 +- embassy-mspm0/src/mathacl.rs | 24 ++++----- embassy-mspm0/src/sysctl/c1103_1104.rs | 10 ++-- embassy-mspm0/src/sysctl/c1105_1106.rs | 10 ++-- .../src/sysctl/g110x_150x_310x_350x.rs | 12 ++--- embassy-mspm0/src/sysctl/g151x_351x.rs | 12 ++--- embassy-mspm0/src/sysctl/g511x_518x.rs | 12 ++--- embassy-mspm0/src/sysctl/h321x.rs | 10 ++-- embassy-mspm0/src/sysctl/l_typea.rs | 8 +-- embassy-mspm0/src/sysctl/l_typeb.rs | 10 ++-- embassy-mspm0/src/sysctl/mod.rs | 18 +++---- embassy-mspm0/src/time_driver/tim.rs | 18 +++---- embassy-mspm0/src/trng.rs | 44 ++++++++-------- embassy-mspm0/src/uart/mod.rs | 44 ++++++++-------- embassy-mspm0/src/wwdt.rs | 50 +++++++++---------- 26 files changed, 255 insertions(+), 257 deletions(-) diff --git a/embassy-mspm0/Cargo.toml b/embassy-mspm0/Cargo.toml index e23582c7f9..1c73fde859 100644 --- a/embassy-mspm0/Cargo.toml +++ b/embassy-mspm0/Cargo.toml @@ -74,7 +74,7 @@ critical-section = "1.2.0" micromath = "2.0.0" # mspm0-metapac = { version = "" } -mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-1da5eda11bf1ae69a604ba62df6242884df15d68" } +mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-f78bc71fcc6d74b25c2cae58220d8bf443fb12df" } rand_core = "0.9" # Force no cache padding or the types are too large. maitake-sync = { version = "0.3.0", default-features = false, features = ["critical-section", "no-cache-pad"]} @@ -88,7 +88,7 @@ quote = "1.0.40" cfg_aliases = "0.2.1" # mspm0-metapac = { version = "", default-features = false, features = ["metadata"] } -mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-1da5eda11bf1ae69a604ba62df6242884df15d68", default-features = false, features = ["metadata"] } +mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-f78bc71fcc6d74b25c2cae58220d8bf443fb12df", default-features = false, features = ["metadata"] } [features] default = ["rt"] diff --git a/embassy-mspm0/build.rs b/embassy-mspm0/build.rs index 856a2e9633..edea53541b 100644 --- a/embassy-mspm0/build.rs +++ b/embassy-mspm0/build.rs @@ -202,11 +202,11 @@ fn generate_groups() -> TokenStream { let stat = group.iidx().read().stat(); // check for spurious interrupts - if stat == crate::pac::cpuss::vals::Iidx::NO_INTR { + if stat == crate::pac::cpuss::vals::Iidx::NoIntr { return; } - // MUST subtract by 1 because NO_INTR offsets IIDX values. + // MUST subtract by 1 because NoIntr offsets IIDX values. let iidx = stat.to_bits() - 1; let Ok(group) = #group_enum::try_from(iidx as u8) else { diff --git a/embassy-mspm0/src/adc.rs b/embassy-mspm0/src/adc.rs index 0c29ecc970..5f5743045f 100644 --- a/embassy-mspm0/src/adc.rs +++ b/embassy-mspm0/src/adc.rs @@ -211,7 +211,7 @@ impl<'d, T: Instance, M: Mode> Adc<'d, T, M> { }); r.ctl1().modify(|w| { - w.set_sc(vals::Sc::START); + w.set_sc(vals::Sc::Start); }); // Wait for conversion @@ -297,7 +297,7 @@ impl<'d, T: Instance> Adc<'d, T, Async> { }); r.ctl1().modify(|w| { - w.set_sc(vals::Sc::START); + w.set_sc(vals::Sc::Start); }); Self::wait_for_conversion().await; @@ -344,7 +344,7 @@ impl<'d, T: Instance> Adc<'d, T, Async> { }); r.ctl1().modify(|w| { - w.set_sc(vals::Sc::START); + w.set_sc(vals::Sc::Start); }); Self::wait_for_conversion().await; @@ -432,20 +432,20 @@ impl<'d, T: Instance, M: Mode> Adc<'d, T, M> { r.gprcm(0).rstctl().write(|w| { w.set_resetstkyclr(true); w.set_resetassert(true); - w.set_key(vals::ResetKey::KEY); + w.set_key(vals::ResetKey::Key); }); r.gprcm(0).pwren().modify(|reg| { reg.set_enable(true); - reg.set_key(vals::PwrenKey::KEY); + reg.set_key(vals::PwrenKey::Key); }); // Wait for power up cortex_m::asm::delay(16); r.gprcm(0).clkcfg().write(|w| { - w.set_key(vals::ClkcfgKey::KEY); - w.set_sampclk(vals::Sampclk::SYSOSC); + w.set_key(vals::ClkcfgKey::Key); + w.set_sampclk(vals::Sampclk::Sysosc); }); // FIXME: Consider clock config @@ -453,20 +453,20 @@ impl<'d, T: Instance, M: Mode> Adc<'d, T, M> { r.ctl0().write(|w| { w.set_enc(false); // TODO: power down config - w.set_pwrdn(vals::Pwrdn::MANUAL); - w.set_sclkdiv(vals::Sclkdiv::DIV_BY_4); + w.set_pwrdn(vals::Pwrdn::Manual); + w.set_sclkdiv(vals::Sclkdiv::DivBy4); }); r.clkfreq().write(|w| { - w.set_frange(vals::Frange::RANGE24TO32); + w.set_frange(vals::Frange::Range24to32); }); r.ctl1().write(|w| { - w.set_trigsrc(vals::Trigsrc::SOFTWARE); - w.set_sc(vals::Sc::STOP); - w.set_conseq(vals::Conseq::SEQUENCE); - w.set_sampmode(vals::Sampmode::AUTO); - w.set_avgn(vals::Avgn::DISABLE); + w.set_trigsrc(vals::Trigsrc::Software); + w.set_sc(vals::Sc::Stop); + w.set_conseq(vals::Conseq::Sequence); + w.set_sampmode(vals::Sampmode::Auto); + w.set_avgn(vals::Avgn::Disable); w.set_avgd(0); }); @@ -477,7 +477,7 @@ impl<'d, T: Instance, M: Mode> Adc<'d, T, M> { w.set_rstsampcapen(false); w.set_dmaen(false); w.set_fifoen(false); - w.set_sampcnt(vals::Sampcnt::MIN); + w.set_sampcnt(vals::Sampcnt::Min); w.set_startadd(0); w.set_endadd(0); }); @@ -510,7 +510,7 @@ impl<'d, T: Instance, M: Mode> Adc<'d, T, M> { // TODO: More parameters w.set_avgen(false); w.set_bcsen(false); - w.set_trig(vals::Trig::AUTO_NEXT); + w.set_trig(vals::Trig::AutoNext); w.set_wincomp(false); }); } @@ -576,17 +576,17 @@ trait SealedBorrowedChannel<'a, T> { const fn to_res(resolution: Resolution) -> vals::Res { match resolution { - Resolution::Bits12 => vals::Res::BIT_12, - Resolution::Bits10 => vals::Res::BIT_10, - Resolution::Bits8 => vals::Res::BIT_8, + Resolution::Bits12 => vals::Res::Bit12, + Resolution::Bits10 => vals::Res::Bit10, + Resolution::Bits8 => vals::Res::Bit8, } } const fn from_res(res: vals::Res) -> Resolution { match res { - vals::Res::BIT_12 => Resolution::Bits12, - vals::Res::BIT_10 => Resolution::Bits10, - vals::Res::BIT_8 => Resolution::Bits8, + vals::Res::Bit12 => Resolution::Bits12, + vals::Res::Bit10 => Resolution::Bits10, + vals::Res::Bit8 => Resolution::Bits8, // SAFETY: The HAL will never program an invalid valid. vals::Res::_RESERVED_3 => unsafe { unreachable_unchecked() }, } @@ -594,8 +594,8 @@ const fn from_res(res: vals::Res) -> Resolution { const fn convert_stime(stime: SampleTimeComparator) -> vals::Stime { match stime { - SampleTimeComparator::Scomp0 => vals::Stime::SEL_SCOMP0, - SampleTimeComparator::Scomp1 => vals::Stime::SEL_SCOMP1, + SampleTimeComparator::Scomp0 => vals::Stime::SelScomp0, + SampleTimeComparator::Scomp1 => vals::Stime::SelScomp1, } } diff --git a/embassy-mspm0/src/dma.rs b/embassy-mspm0/src/dma.rs index 1a30094c46..652c74bde9 100644 --- a/embassy-mspm0/src/dma.rs +++ b/embassy-mspm0/src/dma.rs @@ -202,7 +202,7 @@ pub trait Word: SealedWord + 'static { impl SealedWord for u8 { fn width() -> vals::Wdth { - vals::Wdth::BYTE + vals::Wdth::Byte } } impl Word for u8 { @@ -213,7 +213,7 @@ impl Word for u8 { impl SealedWord for u16 { fn width() -> vals::Wdth { - vals::Wdth::HALF + vals::Wdth::Half } } impl Word for u16 { @@ -224,7 +224,7 @@ impl Word for u16 { impl SealedWord for u32 { fn width() -> vals::Wdth { - vals::Wdth::WORD + vals::Wdth::Word } } impl Word for u32 { @@ -235,7 +235,7 @@ impl Word for u32 { impl SealedWord for u64 { fn width() -> vals::Wdth { - vals::Wdth::LONG + vals::Wdth::Long } } impl Word for u64 { @@ -399,17 +399,17 @@ fn verify_transfer(ptr: *const [W]) -> Result<(), Error> { fn convert_burst_size(value: BurstSize) -> vals::Burstsz { match value { - BurstSize::Complete => vals::Burstsz::INFINITI, - BurstSize::_8 => vals::Burstsz::BURST_8, - BurstSize::_16 => vals::Burstsz::BURST_16, - BurstSize::_32 => vals::Burstsz::BURST_32, + BurstSize::Complete => vals::Burstsz::Infiniti, + BurstSize::_8 => vals::Burstsz::Burst8, + BurstSize::_16 => vals::Burstsz::Burst16, + BurstSize::_32 => vals::Burstsz::Burst32, } } fn convert_mode(mode: TransferMode) -> vals::Tm { match mode { - TransferMode::Single => vals::Tm::SINGLE, - TransferMode::Block => vals::Tm::BLOCK, + TransferMode::Single => vals::Tm::Single, + TransferMode::Block => vals::Tm::Block, } } @@ -517,22 +517,22 @@ impl<'d> Channel<'d> { w.set_req(false); // Not every part supports auto enable, so force its value to 0. - w.set_autoen(Autoen::NONE); - w.set_preirq(Preirq::PREIRQ_DISABLE); + w.set_autoen(Autoen::None); + w.set_preirq(Preirq::PreirqDisable); w.set_srcwdth(src_wdth); w.set_dstwdth(dst_wdth); w.set_srcincr(if increment_src { - Incr::INCREMENT + Incr::Increment } else { - Incr::UNCHANGED + Incr::Unchanged }); w.set_dstincr(if increment_dst { - Incr::INCREMENT + Incr::Increment } else { - Incr::UNCHANGED + Incr::Unchanged }); - w.set_em(Em::NORMAL); + w.set_em(Em::Normal); // Single and block will clear the enable bit when the transfers finish. w.set_tm(convert_mode(options.mode)); }); @@ -540,7 +540,7 @@ impl<'d> Channel<'d> { self.tctl().write(|w| { w.set_tsel(trigger_sel); // Basic channels do not implement cross triggering. - w.set_tint(vals::Tint::EXTERNAL); + w.set_tint(vals::Tint::External); }); self.sz().write(|w| { diff --git a/embassy-mspm0/src/gpio.rs b/embassy-mspm0/src/gpio.rs index ebae411945..795b0ddb43 100644 --- a/embassy-mspm0/src/gpio.rs +++ b/embassy-mspm0/src/gpio.rs @@ -318,19 +318,19 @@ impl<'d> Flex<'d> { // Per https://tweedegolf.nl/en/blog/235/debloat-your-async-rust // // We match the async pass-through suggestion to reduce async bloat. - self.wait_inner(Polarity::RISE) + self.wait_inner(Polarity::Rise) } /// Wait for the pin to undergo a transition from high to low. #[inline] pub fn wait_for_falling_edge(&mut self) -> impl Future { - self.wait_inner(Polarity::FALL) + self.wait_inner(Polarity::Fall) } /// Wait for the pin to undergo any transition, i.e low to high OR high to low. #[inline] pub fn wait_for_any_edge(&mut self) -> impl Future { - self.wait_inner(Polarity::RISE_FALL) + self.wait_inner(Polarity::RiseFall) } async fn wait_inner(&mut self, polarity: Polarity) { @@ -1029,17 +1029,17 @@ pub(crate) fn init(gpio: gpio::Gpio) { gpio.gprcm().rstctl().write(|w| { w.set_resetstkyclr(true); w.set_resetassert(true); - w.set_key(ResetKey::KEY); + w.set_key(ResetKey::Key); }); gpio.gprcm().pwren().write(|w| { w.set_enable(true); - w.set_key(PwrenKey::KEY); + w.set_key(PwrenKey::Key); }); gpio.evt_mode().modify(|w| { // The CPU will clear it's own interrupts - w.set_cpu_cfg(EvtCfg::SOFTWARE); + w.set_cpu_cfg(EvtCfg::Software); }); } diff --git a/embassy-mspm0/src/i2c.rs b/embassy-mspm0/src/i2c.rs index a073e4706f..173c4bfa7a 100644 --- a/embassy-mspm0/src/i2c.rs +++ b/embassy-mspm0/src/i2c.rs @@ -58,14 +58,14 @@ pub enum ClockDiv { impl ClockDiv { pub(crate) fn into(self) -> vals::Ratio { match self { - Self::DivBy1 => vals::Ratio::DIV_BY_1, - Self::DivBy2 => vals::Ratio::DIV_BY_2, - Self::DivBy3 => vals::Ratio::DIV_BY_3, - Self::DivBy4 => vals::Ratio::DIV_BY_4, - Self::DivBy5 => vals::Ratio::DIV_BY_5, - Self::DivBy6 => vals::Ratio::DIV_BY_6, - Self::DivBy7 => vals::Ratio::DIV_BY_7, - Self::DivBy8 => vals::Ratio::DIV_BY_8, + Self::DivBy1 => vals::Ratio::DivBy1, + Self::DivBy2 => vals::Ratio::DivBy2, + Self::DivBy3 => vals::Ratio::DivBy3, + Self::DivBy4 => vals::Ratio::DivBy4, + Self::DivBy5 => vals::Ratio::DivBy5, + Self::DivBy6 => vals::Ratio::DivBy6, + Self::DivBy7 => vals::Ratio::DivBy7, + Self::DivBy8 => vals::Ratio::DivBy8, } } @@ -406,7 +406,7 @@ impl<'d, M: Mode> I2c<'d, M> { // set up glitch filter self.info.regs.gfctl().modify(|w| { w.set_agfen(false); - w.set_agfsel(vals::Agfsel::AGLIT_50); + w.set_agfsel(vals::Agfsel::Aglit50); w.set_chain(true); }); @@ -436,13 +436,13 @@ impl<'d, M: Mode> I2c<'d, M> { .regs .controller(0) .cfifoctl() - .write(|w| w.set_txtrig(vals::CfifoctlTxtrig::EMPTY)); + .write(|w| w.set_txtrig(vals::CfifoctlTxtrig::Empty)); // Set Rx Fifo threshold, follow TI example self.info .regs .controller(0) .cfifoctl() - .write(|w| w.set_rxtrig(vals::CfifoctlRxtrig::LEVEL_1)); + .write(|w| w.set_rxtrig(vals::CfifoctlRxtrig::Level1)); // Enable controller clock stretching, follow TI example self.info.regs.controller(0).ccr().modify(|w| { @@ -498,8 +498,8 @@ impl<'d, M: Mode> I2c<'d, M> { // is BUSY or I2C is in slave mode. self.info.regs.controller(0).csa().modify(|w| { w.set_taddr(address as u16); - w.set_cmode(vals::Mode::MODE7); - w.set_dir(vals::Dir::RECEIVE); + w.set_cmode(vals::Mode::Mode7); + w.set_dir(vals::Dir::Receive); }); self.info.regs.controller(0).cctr().modify(|w| { @@ -517,8 +517,8 @@ impl<'d, M: Mode> I2c<'d, M> { // Start transfer of length amount of bytes self.info.regs.controller(0).csa().modify(|w| { w.set_taddr(address as u16); - w.set_cmode(vals::Mode::MODE7); - w.set_dir(vals::Dir::TRANSMIT); + w.set_cmode(vals::Mode::Mode7); + w.set_dir(vals::Dir::Transmit); }); self.info.regs.controller(0).cctr().modify(|w| { w.set_cblen(length as u16); @@ -729,10 +729,10 @@ impl<'d> I2c<'d, Async> { self.state.waker.register(cx.waker()); let result = match self.info.regs.cpu_int(0).iidx().read().stat() { - CpuIntIidxStat::NO_INTR => Poll::Pending, - CpuIntIidxStat::CNACKFG => Poll::Ready(Err(Error::Nack)), - CpuIntIidxStat::CARBLOSTFG => Poll::Ready(Err(Error::Arbitration)), - CpuIntIidxStat::CTXDONEFG => Poll::Ready(Ok(())), + CpuIntIidxStat::NoIntr => Poll::Pending, + CpuIntIidxStat::Cnackfg => Poll::Ready(Err(Error::Nack)), + CpuIntIidxStat::Carblostfg => Poll::Ready(Err(Error::Arbitration)), + CpuIntIidxStat::Ctxdonefg => Poll::Ready(Ok(())), _ => Poll::Pending, }; @@ -790,10 +790,10 @@ impl<'d> I2c<'d, Async> { self.state.waker.register(cx.waker()); let result = match self.info.regs.cpu_int(0).iidx().read().stat() { - CpuIntIidxStat::NO_INTR => Poll::Pending, - CpuIntIidxStat::CNACKFG => Poll::Ready(Err(Error::Nack)), - CpuIntIidxStat::CARBLOSTFG => Poll::Ready(Err(Error::Arbitration)), - CpuIntIidxStat::CRXDONEFG => Poll::Ready(Ok(())), + CpuIntIidxStat::NoIntr => Poll::Pending, + CpuIntIidxStat::Cnackfg => Poll::Ready(Err(Error::Nack)), + CpuIntIidxStat::Carblostfg => Poll::Ready(Err(Error::Arbitration)), + CpuIntIidxStat::Crxdonefg => Poll::Ready(Ok(())), _ => Poll::Pending, }; @@ -1032,12 +1032,12 @@ impl<'d, M: Mode> I2c<'d, M> { T::info().regs.gprcm(0).rstctl().write(|w| { w.set_resetstkyclr(true); w.set_resetassert(true); - w.set_key(vals::ResetKey::KEY); + w.set_key(vals::ResetKey::Key); }); T::info().regs.gprcm(0).pwren().write(|w| { w.set_enable(true); - w.set_key(vals::PwrenKey::KEY); + w.set_key(vals::PwrenKey::Key); }); // init delay, 16 cycles diff --git a/embassy-mspm0/src/i2c_target.rs b/embassy-mspm0/src/i2c_target.rs index e371fa9033..02498064de 100644 --- a/embassy-mspm0/src/i2c_target.rs +++ b/embassy-mspm0/src/i2c_target.rs @@ -258,12 +258,12 @@ impl<'d, M: Mode> I2cTarget<'d, M> { regs.gprcm(0).rstctl().write(|w| { w.set_resetstkyclr(true); w.set_resetassert(true); - w.set_key(vals::ResetKey::KEY); + w.set_key(vals::ResetKey::Key); }); regs.gprcm(0).pwren().write(|w| { w.set_enable(true); - w.set_key(vals::PwrenKey::KEY); + w.set_key(vals::PwrenKey::Key); }); self.info.interrupt.disable(); @@ -349,7 +349,7 @@ impl<'d> I2cTarget<'d, Async> { // Set the rx fifo interrupt to avoid a fifo overflow regs.target(0).tfifoctl().modify(|r| { - r.set_rxtrig(vals::TfifoctlRxtrig::LEVEL_6); + r.set_rxtrig(vals::TfifoctlRxtrig::Level6); }); self.wait_on( @@ -370,13 +370,13 @@ impl<'d> I2cTarget<'d, Async> { } let iidx = regs.cpu_int(0).iidx().read().stat(); - trace!("ls:{} len:{}", iidx as u8, len); + trace!("ls:{} len:{}", iidx.to_bits(), len); let result = match iidx { - CpuIntIidxStat::TTXEMPTY => match len { + CpuIntIidxStat::Ttxempty => match len { 0 => Poll::Ready(Ok(Command::Read)), w => Poll::Ready(Ok(Command::WriteRead(w))), }, - CpuIntIidxStat::TSTOPFG => match (is_gencall, len) { + CpuIntIidxStat::Tstopfg => match (is_gencall, len) { (_, 0) => Poll::Pending, (true, w) => Poll::Ready(Ok(Command::GeneralCall(w))), (false, w) => Poll::Ready(Ok(Command::Write(w))), @@ -423,11 +423,11 @@ impl<'d> I2cTarget<'d, Async> { let iidx = regs.cpu_int(0).iidx().read().stat(); let fifo_bytes = fifo_size - regs.target(0).tfifosr().read().txfifocnt() as usize; - trace!("rs:{}, fifo:{}", iidx as u8, fifo_bytes); + trace!("rs:{}, fifo:{}", iidx.to_bits(), fifo_bytes); let result = match iidx { - CpuIntIidxStat::TTXEMPTY => Poll::Ready(Ok(ReadStatus::NeedMoreBytes)), - CpuIntIidxStat::TSTOPFG => match fifo_bytes { + CpuIntIidxStat::Ttxempty => Poll::Ready(Ok(ReadStatus::NeedMoreBytes)), + CpuIntIidxStat::Tstopfg => match fifo_bytes { 0 => Poll::Ready(Ok(ReadStatus::Done)), w => Poll::Ready(Ok(ReadStatus::LeftoverBytes(w as u16))), }, diff --git a/embassy-mspm0/src/lib.rs b/embassy-mspm0/src/lib.rs index 1a8574d7f1..3d4ed4c17e 100644 --- a/embassy-mspm0/src/lib.rs +++ b/embassy-mspm0/src/lib.rs @@ -323,12 +323,12 @@ pub fn read_reset_cause() -> Result { use pac::sysctl::vals::Id; match cause_raw { - Id::NORST => Ok(NoReset), - Id::PORHWFAIL => Ok(PorHwFailure), - Id::POREXNRST => Ok(PorExternalNrst), - Id::PORSW => Ok(PorSwTriggered), - Id::BORSUPPLY => Ok(BorSupplyFailure), - Id::BORWAKESHUTDN => Ok(BorWakeFromShutdown), + Id::Norst => Ok(NoReset), + Id::Porhwfail => Ok(PorHwFailure), + Id::Porexnrst => Ok(PorExternalNrst), + Id::Porsw => Ok(PorSwTriggered), + Id::Borsupply => Ok(BorSupplyFailure), + Id::Borwakeshutdn => Ok(BorWakeFromShutdown), #[cfg(not(any( mspm0c110x, mspm0c1105_c1106, @@ -340,22 +340,22 @@ pub fn read_reset_cause() -> Result { mspm0g351x, mspm0g518x, )))] - Id::BOOTNONPMUPARITY => Ok(BootrstNonPmuParityFault), - Id::BOOTCLKFAIL => Ok(BootrstClockFault), - Id::BOOTSW => Ok(BootrstSwTriggered), - Id::BOOTEXNRST => Ok(BootrstExternalNrst), - Id::BOOTWWDT0 => Ok(BootrstWwdt0Violation), - Id::SYSBSLEXIT => Ok(SysrstBslExit), - Id::SYSBSLENTRY => Ok(SysrstBslEntry), + Id::Bootnonpmuparity => Ok(BootrstNonPmuParityFault), + Id::Bootclkfail => Ok(BootrstClockFault), + Id::Bootsw => Ok(BootrstSwTriggered), + Id::Bootexnrst => Ok(BootrstExternalNrst), + Id::Bootwwdt0 => Ok(BootrstWwdt0Violation), + Id::Sysbslexit => Ok(SysrstBslExit), + Id::Sysbslentry => Ok(SysrstBslEntry), #[cfg(any(mspm0g110x, mspm0g150x, mspm0g151x, mspm0g310x, mspm0g350x, mspm0g351x, mspm0g518x))] - Id::SYSWWDT1 => Ok(SysrstWwdt1Violation), + Id::Syswwdt1 => Ok(SysrstWwdt1Violation), #[cfg(not(any(mspm0c110x, mspm0c1105_c1106, mspm0g351x, mspm0g151x)))] - Id::SYSFLASHECC => Ok(SysrstFlashEccError), - Id::SYSCPULOCK => Ok(SysrstCpuLockupViolation), - Id::SYSDBG => Ok(SysrstDebugTriggered), - Id::SYSSW => Ok(SysrstSwTriggered), - Id::CPUDBG => Ok(CpurstDebugTriggered), - Id::CPUSW => Ok(CpurstSwTriggered), + Id::Sysflashecc => Ok(SysrstFlashEccError), + Id::Syscpulock => Ok(SysrstCpuLockupViolation), + Id::Sysdbg => Ok(SysrstDebugTriggered), + Id::Syssw => Ok(SysrstSwTriggered), + Id::Cpudbg => Ok(CpurstDebugTriggered), + Id::Cpusw => Ok(CpurstSwTriggered), other => Err(other as u8), } } diff --git a/embassy-mspm0/src/low_power/c110x.rs b/embassy-mspm0/src/low_power/c110x.rs index af1988c879..9be6f29f49 100644 --- a/embassy-mspm0/src/low_power/c110x.rs +++ b/embassy-mspm0/src/low_power/c110x.rs @@ -39,8 +39,8 @@ pub unsafe fn enter_sleep(_cs: CriticalSection, mode: SleepMode) { let sysctl = pac::SYSCTL; let dsleep = match mode { - SleepMode::Stop0 | SleepMode::Stop2 => Dsleep::STOP, - SleepMode::Standby0 | SleepMode::Standby1 => Dsleep::STANDBY, + SleepMode::Stop0 | SleepMode::Stop2 => Dsleep::Stop, + SleepMode::Standby0 | SleepMode::Standby1 => Dsleep::Standby, }; sysctl.pmodecfg().modify(|w| w.set_dsleep(dsleep)); diff --git a/embassy-mspm0/src/low_power/full.rs b/embassy-mspm0/src/low_power/full.rs index 37deeaf810..301eff683d 100644 --- a/embassy-mspm0/src/low_power/full.rs +++ b/embassy-mspm0/src/low_power/full.rs @@ -41,8 +41,8 @@ pub unsafe fn enter_sleep(_cs: CriticalSection, mode: SleepMode) { let sysctl = pac::SYSCTL; let dsleep = match mode { - SleepMode::Stop0 | SleepMode::Stop1 | SleepMode::Stop2 => Dsleep::STOP, - SleepMode::Standby0 | SleepMode::Standby1 => Dsleep::STANDBY, + SleepMode::Stop0 | SleepMode::Stop1 | SleepMode::Stop2 => Dsleep::Stop, + SleepMode::Standby0 | SleepMode::Standby1 => Dsleep::Standby, }; sysctl.pmodecfg().modify(|w| w.set_dsleep(dsleep)); diff --git a/embassy-mspm0/src/low_power/h321x.rs b/embassy-mspm0/src/low_power/h321x.rs index a7b9fb9c49..6887b19ba8 100644 --- a/embassy-mspm0/src/low_power/h321x.rs +++ b/embassy-mspm0/src/low_power/h321x.rs @@ -38,8 +38,8 @@ pub unsafe fn enter_sleep(_cs: CriticalSection, mode: SleepMode) { let sysctl = pac::SYSCTL; let dsleep = match mode { - SleepMode::Stop0 | SleepMode::Stop2 => Dsleep::STOP, - SleepMode::Standby0 | SleepMode::Standby1 => Dsleep::STANDBY, + SleepMode::Stop0 | SleepMode::Stop2 => Dsleep::Stop, + SleepMode::Standby0 | SleepMode::Standby1 => Dsleep::Standby, }; sysctl.pmodecfg().modify(|w| w.set_dsleep(dsleep)); diff --git a/embassy-mspm0/src/low_power/mod.rs b/embassy-mspm0/src/low_power/mod.rs index f3d2199443..b7d220069f 100644 --- a/embassy-mspm0/src/low_power/mod.rs +++ b/embassy-mspm0/src/low_power/mod.rs @@ -147,7 +147,7 @@ pub unsafe fn sleep(cs: CriticalSection) { // then `WFI`. This is identical across every MSPM0 family. pub fn shutdown(_cs: CriticalSection) -> ! { let sysctl = pac::SYSCTL; - sysctl.pmodecfg().modify(|w| w.set_dsleep(Dsleep::SHUTDOWN)); + sysctl.pmodecfg().modify(|w| w.set_dsleep(Dsleep::Shutdown)); let _prefetch = PrefetchSuspend::new(); @@ -169,7 +169,7 @@ impl PrefetchSuspend { fn new() -> Self { let saved = pac::CPUSS.ctl().read(); let mut disabled = saved; - disabled.set_prefetch(Prefetch::DISABLE); + disabled.set_prefetch(Prefetch::Disable); pac::CPUSS.ctl().write_value(disabled); // CPU_ERR_02 means the prefetcher will not be disabled until pending flash access is finished. diff --git a/embassy-mspm0/src/mathacl.rs b/embassy-mspm0/src/mathacl.rs index e79e099be1..636e933c57 100644 --- a/embassy-mspm0/src/mathacl.rs +++ b/embassy-mspm0/src/mathacl.rs @@ -42,15 +42,15 @@ impl<'d> Mathacl<'d> { pub fn new(_instance: Peri<'d, T>) -> Self { // Init power T::regs().gprcm(0).rstctl().write(|w| { - w.set_resetstkyclr(vals::Resetstkyclr::CLR); - w.set_resetassert(vals::Resetassert::ASSERT); - w.set_key(vals::ResetKey::KEY); + w.set_resetstkyclr(vals::Resetstkyclr::Clr); + w.set_resetassert(vals::Resetassert::Assert); + w.set_key(vals::ResetKey::Key); }); // Enable power T::regs().gprcm(0).pwren().write(|w| { w.set_enable(true); - w.set_key(vals::PwrenKey::KEY); + w.set_key(vals::PwrenKey::Key); }); // init delay, 16 cycles @@ -72,7 +72,7 @@ impl<'d> Mathacl<'d> { let native = self.div_iq(IQType::from_f32(rad, 15, true)?, IQType::from_f32(PI, 15, true)?)?; self.regs.ctl().write(|w| { - w.set_func(vals::Func::SINCOS); + w.set_func(vals::Func::Sincos); w.set_numiter(precision as u8); }); @@ -82,7 +82,7 @@ impl<'d> Mathacl<'d> { }); // check if done - while self.regs.status().read().busy() == vals::Busy::NOTDONE {} + while self.regs.status().read().busy() == vals::Busy::Notdone {} match sin { true => Ok(IQType::from_reg(self.regs.res2().read().data(), 0, true) @@ -113,7 +113,7 @@ impl<'d> Mathacl<'d> { let signed = true; self.regs.ctl().write(|w| { - w.set_func(vals::Func::DIV); + w.set_func(vals::Func::Div); w.set_optype(signed); }); @@ -126,7 +126,7 @@ impl<'d> Mathacl<'d> { }); // check if done - while self.regs.status().read().busy() == vals::Busy::NOTDONE {} + while self.regs.status().read().busy() == vals::Busy::Notdone {} // read quotient Ok(self.regs.res1().read().data() as i32) @@ -141,7 +141,7 @@ impl<'d> Mathacl<'d> { let signed = false; self.regs.ctl().write(|w| { - w.set_func(vals::Func::DIV); + w.set_func(vals::Func::Div); w.set_optype(signed); }); @@ -154,7 +154,7 @@ impl<'d> Mathacl<'d> { }); // check if done - while self.regs.status().read().busy() == vals::Busy::NOTDONE {} + while self.regs.status().read().busy() == vals::Busy::Notdone {} // read quotient Ok(self.regs.res1().read().data()) @@ -178,7 +178,7 @@ impl<'d> Mathacl<'d> { } self.regs.ctl().write(|w| { - w.set_func(vals::Func::DIV); + w.set_func(vals::Func::Div); w.set_optype(dividend.signed); w.set_qval(dividend.f_bits.into()); }); @@ -192,7 +192,7 @@ impl<'d> Mathacl<'d> { }); // check if done - while self.regs.status().read().busy() == vals::Busy::NOTDONE {} + while self.regs.status().read().busy() == vals::Busy::Notdone {} // read quotient return Ok( diff --git a/embassy-mspm0/src/sysctl/c1103_1104.rs b/embassy-mspm0/src/sysctl/c1103_1104.rs index a841bf4875..dbcca47556 100644 --- a/embassy-mspm0/src/sysctl/c1103_1104.rs +++ b/embassy-mspm0/src/sysctl/c1103_1104.rs @@ -47,12 +47,12 @@ impl ClkOutSource { pub(super) fn convert_src(self) -> vals::Exclksrc { match self { - ClkOutSource::Sysosc(_) => vals::Exclksrc::SYSOSC, - ClkOutSource::UlpClk(_) => vals::Exclksrc::ULPCLK, - ClkOutSource::LfClk(_) => vals::Exclksrc::LFCLK, + ClkOutSource::Sysosc(_) => vals::Exclksrc::Sysosc, + ClkOutSource::UlpClk(_) => vals::Exclksrc::Ulpclk, + ClkOutSource::LfClk(_) => vals::Exclksrc::Lfclk, // FIXME: Wrong name from SVD - ClkOutSource::MfpClk(_) => vals::Exclksrc::MFCLK, - ClkOutSource::Hfclk(_) => vals::Exclksrc::HFCLK, + ClkOutSource::MfpClk(_) => vals::Exclksrc::Mfclk, + ClkOutSource::Hfclk(_) => vals::Exclksrc::Hfclk, } } } diff --git a/embassy-mspm0/src/sysctl/c1105_1106.rs b/embassy-mspm0/src/sysctl/c1105_1106.rs index 47e71c405a..262fc2147a 100644 --- a/embassy-mspm0/src/sysctl/c1105_1106.rs +++ b/embassy-mspm0/src/sysctl/c1105_1106.rs @@ -47,12 +47,12 @@ impl ClkOutSource { pub(super) fn convert_src(self) -> vals::Exclksrc { match self { - ClkOutSource::Sysosc(_) => vals::Exclksrc::SYSOSC, - ClkOutSource::UlpClk(_) => vals::Exclksrc::ULPCLK, - ClkOutSource::LfClk(_) => vals::Exclksrc::LFCLK, + ClkOutSource::Sysosc(_) => vals::Exclksrc::Sysosc, + ClkOutSource::UlpClk(_) => vals::Exclksrc::Ulpclk, + ClkOutSource::LfClk(_) => vals::Exclksrc::Lfclk, // FIXME: Wrong name from SVD - ClkOutSource::MfpClk(_) => vals::Exclksrc::MFCLK, - ClkOutSource::Hfclk(_) => vals::Exclksrc::HFCLK, + ClkOutSource::MfpClk(_) => vals::Exclksrc::Mfclk, + ClkOutSource::Hfclk(_) => vals::Exclksrc::Hfclk, } } } diff --git a/embassy-mspm0/src/sysctl/g110x_150x_310x_350x.rs b/embassy-mspm0/src/sysctl/g110x_150x_310x_350x.rs index cf3bf55735..ab60c57ee4 100644 --- a/embassy-mspm0/src/sysctl/g110x_150x_310x_350x.rs +++ b/embassy-mspm0/src/sysctl/g110x_150x_310x_350x.rs @@ -53,12 +53,12 @@ impl ClkOutSource { pub(super) fn convert_src(self) -> vals::Exclksrc { match self { - ClkOutSource::Sysosc(_) => vals::Exclksrc::SYSOSC, - ClkOutSource::UlpClk(_) => vals::Exclksrc::ULPCLK, - ClkOutSource::LfClk(_) => vals::Exclksrc::LFCLK, - ClkOutSource::MfpClk(_) => vals::Exclksrc::MFPCLK, - ClkOutSource::Hfclk(_) => vals::Exclksrc::HFCLK, - ClkOutSource::SysPllClk1(_) => vals::Exclksrc::SYSPLLOUT1, + ClkOutSource::Sysosc(_) => vals::Exclksrc::Sysosc, + ClkOutSource::UlpClk(_) => vals::Exclksrc::Ulpclk, + ClkOutSource::LfClk(_) => vals::Exclksrc::Lfclk, + ClkOutSource::MfpClk(_) => vals::Exclksrc::Mfpclk, + ClkOutSource::Hfclk(_) => vals::Exclksrc::Hfclk, + ClkOutSource::SysPllClk1(_) => vals::Exclksrc::Syspllout1, } } } diff --git a/embassy-mspm0/src/sysctl/g151x_351x.rs b/embassy-mspm0/src/sysctl/g151x_351x.rs index 5f078af89a..6a3ccbfef0 100644 --- a/embassy-mspm0/src/sysctl/g151x_351x.rs +++ b/embassy-mspm0/src/sysctl/g151x_351x.rs @@ -53,12 +53,12 @@ impl ClkOutSource { pub(super) fn convert_src(self) -> vals::Exclksrc { match self { - ClkOutSource::Sysosc(_) => vals::Exclksrc::SYSOSC, - ClkOutSource::UlpClk(_) => vals::Exclksrc::ULPCLK, - ClkOutSource::LfClk(_) => vals::Exclksrc::LFCLK, - ClkOutSource::MfpClk(_) => vals::Exclksrc::MFPCLK, - ClkOutSource::Hfclk(_) => vals::Exclksrc::HFCLK, - ClkOutSource::SysPllClk1(_) => vals::Exclksrc::SYSPLLOUT1, + ClkOutSource::Sysosc(_) => vals::Exclksrc::Sysosc, + ClkOutSource::UlpClk(_) => vals::Exclksrc::Ulpclk, + ClkOutSource::LfClk(_) => vals::Exclksrc::Lfclk, + ClkOutSource::MfpClk(_) => vals::Exclksrc::Mfpclk, + ClkOutSource::Hfclk(_) => vals::Exclksrc::Hfclk, + ClkOutSource::SysPllClk1(_) => vals::Exclksrc::Syspllout1, } } } diff --git a/embassy-mspm0/src/sysctl/g511x_518x.rs b/embassy-mspm0/src/sysctl/g511x_518x.rs index eeb2716641..4de49a73f1 100644 --- a/embassy-mspm0/src/sysctl/g511x_518x.rs +++ b/embassy-mspm0/src/sysctl/g511x_518x.rs @@ -59,12 +59,12 @@ impl ClkOutSource { pub(super) fn convert_src(self) -> vals::Exclksrc { match self { - ClkOutSource::Sysosc(_) => vals::Exclksrc::SYSOSC, - ClkOutSource::UlpClk(_) => vals::Exclksrc::ULPCLK, - ClkOutSource::LfClk(_) => vals::Exclksrc::LFCLK, - ClkOutSource::MfpClk(_) => vals::Exclksrc::MFPCLK, - ClkOutSource::Hfclk(_) => vals::Exclksrc::HFCLK, - ClkOutSource::SysPllClk1(_) => vals::Exclksrc::SYSPLLOUT1, + ClkOutSource::Sysosc(_) => vals::Exclksrc::Sysosc, + ClkOutSource::UlpClk(_) => vals::Exclksrc::Ulpclk, + ClkOutSource::LfClk(_) => vals::Exclksrc::Lfclk, + ClkOutSource::MfpClk(_) => vals::Exclksrc::Mfpclk, + ClkOutSource::Hfclk(_) => vals::Exclksrc::Hfclk, + ClkOutSource::SysPllClk1(_) => vals::Exclksrc::Syspllout1, // FIXME: Update SVD to define _RESERVED_6 as USBFLL ClkOutSource::UsbFll(_) => vals::Exclksrc::_RESERVED_6, } diff --git a/embassy-mspm0/src/sysctl/h321x.rs b/embassy-mspm0/src/sysctl/h321x.rs index 4a4ca72ae7..fffe53c988 100644 --- a/embassy-mspm0/src/sysctl/h321x.rs +++ b/embassy-mspm0/src/sysctl/h321x.rs @@ -47,12 +47,12 @@ impl ClkOutSource { pub(super) fn convert_src(self) -> vals::Exclksrc { match self { - ClkOutSource::Sysosc(_) => vals::Exclksrc::SYSOSC, - ClkOutSource::UlpClk(_) => vals::Exclksrc::ULPCLK, - ClkOutSource::LfClk(_) => vals::Exclksrc::LFCLK, + ClkOutSource::Sysosc(_) => vals::Exclksrc::Sysosc, + ClkOutSource::UlpClk(_) => vals::Exclksrc::Ulpclk, + ClkOutSource::LfClk(_) => vals::Exclksrc::Lfclk, // FIXME: Wrong name from SVD - ClkOutSource::MfpClk(_) => vals::Exclksrc::MFPCLK, - ClkOutSource::Hfclk(_) => vals::Exclksrc::HFCLK, + ClkOutSource::MfpClk(_) => vals::Exclksrc::Mfpclk, + ClkOutSource::Hfclk(_) => vals::Exclksrc::Hfclk, } } } diff --git a/embassy-mspm0/src/sysctl/l_typea.rs b/embassy-mspm0/src/sysctl/l_typea.rs index e52b21550e..aed84ab219 100644 --- a/embassy-mspm0/src/sysctl/l_typea.rs +++ b/embassy-mspm0/src/sysctl/l_typea.rs @@ -41,10 +41,10 @@ impl ClkOutSource { pub(super) fn convert_src(self) -> vals::Exclksrc { match self { - ClkOutSource::Sysosc(_) => vals::Exclksrc::SYSOSC, - ClkOutSource::UlpClk(_) => vals::Exclksrc::ULPCLK, - ClkOutSource::LfClk(_) => vals::Exclksrc::LFCLK, - ClkOutSource::MfpClk(_) => vals::Exclksrc::MFPCLK, + ClkOutSource::Sysosc(_) => vals::Exclksrc::Sysosc, + ClkOutSource::UlpClk(_) => vals::Exclksrc::Ulpclk, + ClkOutSource::LfClk(_) => vals::Exclksrc::Lfclk, + ClkOutSource::MfpClk(_) => vals::Exclksrc::Mfpclk, } } } diff --git a/embassy-mspm0/src/sysctl/l_typeb.rs b/embassy-mspm0/src/sysctl/l_typeb.rs index caf35d483d..474f05b773 100644 --- a/embassy-mspm0/src/sysctl/l_typeb.rs +++ b/embassy-mspm0/src/sysctl/l_typeb.rs @@ -47,11 +47,11 @@ impl ClkOutSource { pub(super) fn convert_src(self) -> vals::Exclksrc { match self { - ClkOutSource::Sysosc(_) => vals::Exclksrc::SYSOSC, - ClkOutSource::UlpClk(_) => vals::Exclksrc::ULPCLK, - ClkOutSource::LfClk(_) => vals::Exclksrc::LFCLK, - ClkOutSource::MfpClk(_) => vals::Exclksrc::MFPCLK, - ClkOutSource::Hfclk(_) => vals::Exclksrc::HFCLK, + ClkOutSource::Sysosc(_) => vals::Exclksrc::Sysosc, + ClkOutSource::UlpClk(_) => vals::Exclksrc::Ulpclk, + ClkOutSource::LfClk(_) => vals::Exclksrc::Lfclk, + ClkOutSource::MfpClk(_) => vals::Exclksrc::Mfpclk, + ClkOutSource::Hfclk(_) => vals::Exclksrc::Hfclk, } } } diff --git a/embassy-mspm0/src/sysctl/mod.rs b/embassy-mspm0/src/sysctl/mod.rs index d0a7085fdb..41bc48a7e8 100644 --- a/embassy-mspm0/src/sysctl/mod.rs +++ b/embassy-mspm0/src/sysctl/mod.rs @@ -248,15 +248,15 @@ macro_rules! impl_clk_out_pin { /// (DIVEN, DIVVAL) fn div_to_pac(div: Option) -> (bool, vals::Exclkdivval) { match div { - Some(ClkOutDiv::Div2) => (true, vals::Exclkdivval::DIV2), - Some(ClkOutDiv::Div4) => (true, vals::Exclkdivval::DIV4), - Some(ClkOutDiv::Div6) => (true, vals::Exclkdivval::DIV6), - Some(ClkOutDiv::Div8) => (true, vals::Exclkdivval::DIV8), - Some(ClkOutDiv::Div10) => (true, vals::Exclkdivval::DIV10), - Some(ClkOutDiv::Div12) => (true, vals::Exclkdivval::DIV12), - Some(ClkOutDiv::Div14) => (true, vals::Exclkdivval::DIV14), - Some(ClkOutDiv::Div16) => (true, vals::Exclkdivval::DIV16), + Some(ClkOutDiv::Div2) => (true, vals::Exclkdivval::Div2), + Some(ClkOutDiv::Div4) => (true, vals::Exclkdivval::Div4), + Some(ClkOutDiv::Div6) => (true, vals::Exclkdivval::Div6), + Some(ClkOutDiv::Div8) => (true, vals::Exclkdivval::Div8), + Some(ClkOutDiv::Div10) => (true, vals::Exclkdivval::Div10), + Some(ClkOutDiv::Div12) => (true, vals::Exclkdivval::Div12), + Some(ClkOutDiv::Div14) => (true, vals::Exclkdivval::Div14), + Some(ClkOutDiv::Div16) => (true, vals::Exclkdivval::Div16), // divider is ignored. set to default value - None => (false, vals::Exclkdivval::DIV2), + None => (false, vals::Exclkdivval::Div2), } } diff --git a/embassy-mspm0/src/time_driver/tim.rs b/embassy-mspm0/src/time_driver/tim.rs index b006366f4a..0783c8fdcc 100644 --- a/embassy-mspm0/src/time_driver/tim.rs +++ b/embassy-mspm0/src/time_driver/tim.rs @@ -100,14 +100,14 @@ impl TimxDriver { // Reset timer regs.gprcm(0).rstctl().write(|w| { w.set_resetassert(true); - w.set_key(ResetKey::KEY); + w.set_key(ResetKey::Key); w.set_resetstkyclr(true); }); // Power up timer regs.gprcm(0).pwren().write(|w| { w.set_enable(true); - w.set_key(PwrenKey::KEY); + w.set_key(PwrenKey::Key); }); // Following the instructions according to SLAU847D 23.2.1: TIMCLK Configuration @@ -139,9 +139,9 @@ impl TimxDriver { }); regs.counterregs(0).ctrctl().modify(|w| { - w.set_repeat(Repeat::REPEAT_1); - w.set_cvae(Cvae::NOCHANGE); - w.set_cm(Cm::UP); + w.set_repeat(Repeat::Repeat1); + w.set_cvae(Cvae::Nochange); + w.set_cm(Cm::Up); // Must explicitly set CZC, CAC and CLC to 0 in order for all the timers to count. // @@ -150,9 +150,9 @@ impl TimxDriver { // Looking at a bit representation of the reset value, this appears to be an AND // of 2-input QEI mode and CCCTL_3 ACOND. Given that TIMG14 and TIMA0 have no QEI // and 4 capture and compare channels, this works by accident for those timer units. - w.set_czc(CxC::CCTL0); - w.set_cac(CxC::CCTL0); - w.set_clc(CxC::CCTL0); + w.set_czc(CxC::Cctl0); + w.set_cac(CxC::Cctl0); + w.set_clc(CxC::Cctl0); }); // Middle @@ -165,7 +165,7 @@ impl TimxDriver { // // This does not appear to ever be set for CPU_INT in the TI SDK and is not technically needed. regs.evt_mode().modify(|w| { - w.set_evt_cfg(0, EvtCfg::SOFTWARE); + w.set_evt_cfg(0, EvtCfg::Software); }); regs.cpu_int(0).imask().modify(|w| { diff --git a/embassy-mspm0/src/trng.rs b/embassy-mspm0/src/trng.rs index 3b520976ba..6fde0529cb 100644 --- a/embassy-mspm0/src/trng.rs +++ b/embassy-mspm0/src/trng.rs @@ -60,9 +60,9 @@ impl sealed::Sealed for FastDecimRate {} impl Into for FastDecimRate { fn into(self) -> vals::DecimRate { match self { - Self::Decim1 => vals::DecimRate::DECIM_1, - Self::Decim2 => vals::DecimRate::DECIM_2, - Self::Decim3 => vals::DecimRate::DECIM_3, + Self::Decim1 => vals::DecimRate::Decim1, + Self::Decim2 => vals::DecimRate::Decim2, + Self::Decim3 => vals::DecimRate::Decim3, } } } @@ -84,11 +84,11 @@ impl sealed::Sealed for CryptoDecimRate {} impl Into for CryptoDecimRate { fn into(self) -> vals::DecimRate { match self { - Self::Decim4 => vals::DecimRate::DECIM_4, - Self::Decim5 => vals::DecimRate::DECIM_5, - Self::Decim6 => vals::DecimRate::DECIM_6, - Self::Decim7 => vals::DecimRate::DECIM_7, - Self::Decim8 => vals::DecimRate::DECIM_8, + Self::Decim4 => vals::DecimRate::Decim4, + Self::Decim5 => vals::DecimRate::Decim5, + Self::Decim6 => vals::DecimRate::Decim6, + Self::Decim7 => vals::DecimRate::Decim7, + Self::Decim8 => vals::DecimRate::Decim8, } } } @@ -303,7 +303,7 @@ impl TrngInner<'_> { fn fail_reset(&mut self) -> Result<(), Error> { regs().iclr().write(|w| w.set_irq_health_fail(true)); - self.set_cmd(PWR_OFF); + self.set_cmd(PwrOff); self.init() } @@ -311,32 +311,32 @@ impl TrngInner<'_> { // L-series TRM 13.2.5.2 self.set_div(); // 2. Set the clock divider. regs().imask().write_value(Int::default()); // 3. Disable all interrupts. - self.set_cmd(NORM_FUNC); // 4. Set to normal function mode. + self.set_cmd(NormFunc); // 4. Set to normal function mode. self.dig_test()?; // 5. Perform digital block start-up self-tests. self.ana_test()?; // 6. Perform analog block start-up self-test. self.clr_rdy(); // 7.a Clear IRQ_CAPTURED_RDY_IRQ. self.set_decim_rate(); // 7.b Set decimation rate. - self.set_cmd(NORM_FUNC); // 7.b Set to normal function mode again after changing decimation rate. + self.set_cmd(NormFunc); // 7.b Set to normal function mode again after changing decimation rate. _ = self.read(); // 8. By 13.2.4.1, must discard first value. Ok(()) } fn reset(&mut self) { regs().gprcm().rstctl().write(|w| { - w.set_key(RstctlKey::KEY); + w.set_key(RstctlKey::Key); w.set_resetassert(true); }); } fn power_on(&mut self) { regs().gprcm().pwren().write(|w| { - w.set_key(PwrenKey::KEY); + w.set_key(PwrenKey::Key); w.set_enable(true); }); } fn power_off(&mut self) { - regs().gprcm().pwren().write(|w| w.set_key(PwrenKey::KEY)); + regs().gprcm().pwren().write(|w| w.set_key(PwrenKey::Key)); } fn set_div(&mut self) { @@ -345,15 +345,15 @@ impl TrngInner<'_> { let ratio = if freq > 160_000_000 { panic!("MCLK frequency {} > 160 MHz is not compatible with the TRNG", freq) } else if freq >= 80_000_000 { - Ratio::DIV_BY_8 + Ratio::DivBy8 } else if freq >= 60_000_000 { - Ratio::DIV_BY_6 + Ratio::DivBy6 } else if freq >= 40_000_000 { - Ratio::DIV_BY_4 + Ratio::DivBy4 } else if freq >= 20_000_000 { - Ratio::DIV_BY_2 + Ratio::DivBy2 } else if freq >= 9_500_000 { - Ratio::DIV_BY_1 + Ratio::DivBy1 } else { panic!("MCLK frequency {} < 9.5 MHz is not compatible with the TRNG", freq) }; @@ -375,7 +375,7 @@ impl TrngInner<'_> { } fn dig_test(&mut self) -> Result<(), Error> { - self.set_cmd(PWRUP_DIG); + self.set_cmd(PwrupDig); let results = regs().test_results().read(); for n in 0..8u8 { // 13.2.4.1: Digital tests must pass. @@ -389,10 +389,10 @@ impl TrngInner<'_> { fn ana_test(&mut self) -> Result<(), Error> { // 13.2.4.2: Analog tests have a small chance to fail, so try up to 3 times. for _ in 0..3 { - self.set_cmd(PWRUP_ANA); + self.set_cmd(PwrupAna); let results = regs().test_results().read(); if !results.ana_test() { - self.set_cmd(PWR_OFF); + self.set_cmd(PwrOff); } else { return Ok(()); } diff --git a/embassy-mspm0/src/uart/mod.rs b/embassy-mspm0/src/uart/mod.rs index 03e68d297e..d05b326e35 100644 --- a/embassy-mspm0/src/uart/mod.rs +++ b/embassy-mspm0/src/uart/mod.rs @@ -732,12 +732,12 @@ fn enable(regs: Regs) { gprcm.rstctl().write(|w| { w.set_resetstkyclr(true); w.set_resetassert(true); - w.set_key(vals::ResetKey::KEY); + w.set_key(vals::ResetKey::Key); }); gprcm.pwren().write(|w| { w.set_enable(true); - w.set_key(vals::PwrenKey::KEY); + w.set_key(vals::PwrenKey::Key); }); } @@ -784,7 +784,7 @@ fn configure( w.set_txe(enable_tx); // RXD_OUT_EN and TXD_OUT_EN? w.set_menc(false); - w.set_mode(vals::Mode::UART); + w.set_mode(vals::Mode::Uart); w.set_rtsen(enable_rts); w.set_ctsen(enable_cts); // oversampling is set later @@ -796,22 +796,22 @@ fn configure( info.regs.ifls().modify(|w| { // TODO: Need power domain info for other options. - w.set_txiflsel(vals::Iflssel::AT_LEAST_ONE); - w.set_rxiflsel(vals::Iflssel::AT_LEAST_ONE); + w.set_txiflsel(vals::Iflssel::AtLeastOne); + w.set_rxiflsel(vals::Iflssel::AtLeastOne); }); info.regs.lcrh().modify(|w| { let eps = if matches!(config.parity, Parity::ParityEven) { - vals::Eps::EVEN + vals::Eps::Even } else { - vals::Eps::ODD + vals::Eps::Odd }; let wlen = match config.data_bits { - DataBits::DataBits5 => vals::Wlen::DATABIT5, - DataBits::DataBits6 => vals::Wlen::DATABIT6, - DataBits::DataBits7 => vals::Wlen::DATABIT7, - DataBits::DataBits8 => vals::Wlen::DATABIT8, + DataBits::DataBits5 => vals::Wlen::Databit5, + DataBits::DataBits6 => vals::Wlen::Databit6, + DataBits::DataBits7 => vals::Wlen::Databit7, + DataBits::DataBits8 => vals::Wlen::Databit8, }; // Used in LIN mode only @@ -890,14 +890,14 @@ fn set_baudrate_inner(regs: Regs, clock: u32, baudrate: u32) -> Result<(), Confi const MAX_FBRD: u8 = 2_u8.pow(6); const DIVS: [(u8, vals::Clkdiv); 8] = [ - (1, vals::Clkdiv::DIV_BY_1), - (2, vals::Clkdiv::DIV_BY_2), - (3, vals::Clkdiv::DIV_BY_3), - (4, vals::Clkdiv::DIV_BY_4), - (5, vals::Clkdiv::DIV_BY_5), - (6, vals::Clkdiv::DIV_BY_6), - (7, vals::Clkdiv::DIV_BY_7), - (8, vals::Clkdiv::DIV_BY_8), + (1, vals::Clkdiv::DivBy1), + (2, vals::Clkdiv::DivBy2), + (3, vals::Clkdiv::DivBy3), + (4, vals::Clkdiv::DivBy4), + (5, vals::Clkdiv::DivBy5), + (6, vals::Clkdiv::DivBy6), + (7, vals::Clkdiv::DivBy7), + (8, vals::Clkdiv::DivBy8), ]; // Quoting from SLAU 846 section 18.2.3.4: @@ -910,19 +910,19 @@ fn set_baudrate_inner(regs: Regs, clock: u32, baudrate: u32) -> Result<(), Confi // Based on these requirements, prioritize higher oversampling first to increase tolerance to clock // deviation. If no valid BRD value can be found satisifying the highest sample rate, then reduce // sample rate until valid parameters are found. - const OVS: [(u8, vals::Hse); 3] = [(16, vals::Hse::OVS16), (8, vals::Hse::OVS8), (3, vals::Hse::OVS3)]; + const OVS: [(u8, vals::Hse); 3] = [(16, vals::Hse::Ovs16), (8, vals::Hse::Ovs8), (3, vals::Hse::Ovs3)]; // 3x oversampling is not supported with manchester coding, DALI or IrDA. let x3_invalid = { let ctl0 = regs.ctl0().read(); let irctl = regs.irctl().read(); - ctl0.menc() || matches!(ctl0.mode(), vals::Mode::DALI) || irctl.iren() + ctl0.menc() || matches!(ctl0.mode(), vals::Mode::Dali) || irctl.iren() }; let mut found = None; 'outer: for &(oversampling, hse_value) in &OVS { - if matches!(hse_value, vals::Hse::OVS3) && x3_invalid { + if matches!(hse_value, vals::Hse::Ovs3) && x3_invalid { continue; } diff --git a/embassy-mspm0/src/wwdt.rs b/embassy-mspm0/src/wwdt.rs index 92aeb8b408..850a3b97d2 100644 --- a/embassy-mspm0/src/wwdt.rs +++ b/embassy-mspm0/src/wwdt.rs @@ -84,7 +84,7 @@ impl Timeout { | Self::Sec5120 | Self::Sec6144 | Self::Sec7168 - | Self::Sec8192 => vals::Per::EN_25, + | Self::Sec8192 => vals::Per::En25, // period count is 2**21 Self::Sec64 | Self::Sec128 @@ -93,15 +93,13 @@ impl Timeout { | Self::Sec320 | Self::Sec384 | Self::Sec448 - | Self::Sec512 => vals::Per::EN_21, + | Self::Sec512 => vals::Per::En21, // period count is 2**18 Self::Sec8 | Self::Sec16 | Self::Sec24 | Self::Sec32 | Self::Sec40 | Self::Sec48 | Self::Sec56 => { - vals::Per::EN_18 + vals::Per::En18 } // period count is 2**15 - Self::Sec1 | Self::Sec2 | Self::Sec3 | Self::Sec4 | Self::Sec5 | Self::Sec6 | Self::Sec7 => { - vals::Per::EN_15 - } + Self::Sec1 | Self::Sec2 | Self::Sec3 | Self::Sec4 | Self::Sec5 | Self::Sec6 | Self::Sec7 => vals::Per::En15, // period count is 2**12 Self::MSec130 | Self::MSec250 @@ -109,7 +107,7 @@ impl Timeout { | Self::MSec500 | Self::MSec630 | Self::MSec750 - | Self::MSec880 => vals::Per::EN_12, + | Self::MSec880 => vals::Per::En12, // period count is 2**10 Self::USec31250 | Self::USec62500 @@ -117,7 +115,7 @@ impl Timeout { | Self::USec125000 | Self::USec156250 | Self::USec187500 - | Self::USec218750 => vals::Per::EN_10, + | Self::USec218750 => vals::Per::En10, // period count is 2**8 Self::USec7810 | Self::USec15630 @@ -125,10 +123,10 @@ impl Timeout { | Self::USec32250 | Self::USec39060 | Self::USec46880 - | Self::USec54690 => vals::Per::EN_8, + | Self::USec54690 => vals::Per::En8, // period count is 2**6 Self::USec1950 | Self::USec3910 | Self::USec5860 | Self::USec9770 | Self::USec11720 | Self::USec13670 => { - vals::Per::EN_6 + vals::Per::En6 } } } @@ -228,14 +226,14 @@ pub enum ClosedWindowPercentage { impl ClosedWindowPercentage { fn get_native_size(self) -> vals::Window { match self { - Self::Zero => vals::Window::SIZE_0, - Self::Twelve => vals::Window::SIZE_12, - Self::Eighteen => vals::Window::SIZE_18, - Self::TwentyFive => vals::Window::SIZE_25, - Self::Fifty => vals::Window::SIZE_50, - Self::SeventyFive => vals::Window::SIZE_75, - Self::EightyOne => vals::Window::SIZE_81, - Self::EightySeven => vals::Window::SIZE_87, + Self::Zero => vals::Window::Size0, + Self::Twelve => vals::Window::Size12, + Self::Eighteen => vals::Window::Size18, + Self::TwentyFive => vals::Window::Size25, + Self::Fifty => vals::Window::Size50, + Self::SeventyFive => vals::Window::Size75, + Self::EightyOne => vals::Window::Size81, + Self::EightySeven => vals::Window::Size87, } } } @@ -271,13 +269,13 @@ impl Watchdog { T::regs().gprcm(0).rstctl().write(|w| { w.set_resetstkyclr(true); w.set_resetassert(true); - w.set_key(vals::ResetKey::KEY); + w.set_key(vals::ResetKey::Key); }); // Enable power for watchdog T::regs().gprcm(0).pwren().write(|w| { w.set_enable(true); - w.set_key(vals::PwrenKey::KEY); + w.set_key(vals::PwrenKey::Key); }); // init delay, 16 cycles @@ -301,16 +299,16 @@ impl Watchdog { T::regs().wwdtctl0().write(|w| { w.set_clkdiv(config.timeout.get_clkdiv()); w.set_per(config.timeout.get_period()); - w.set_mode(vals::Mode::WINDOW); + w.set_mode(vals::Mode::Window); w.set_window0(config.closed_window.get_native_size()); - w.set_window1(vals::Window::SIZE_0); - w.set_key(vals::Wwdtctl0Key::KEY); + w.set_window1(vals::Window::Size0); + w.set_key(vals::Wwdtctl0Key::Key); }); // Set Window0 as active window T::regs().wwdtctl1().write(|w| { - w.set_winsel(vals::Winsel::WIN0); - w.set_key(vals::Wwdtctl1Key::KEY); + w.set_winsel(vals::Winsel::Win0); + w.set_key(vals::Wwdtctl1Key::Key); }); Self { regs: T::regs() } @@ -319,7 +317,7 @@ impl Watchdog { /// Pet (reload, refresh) the watchdog. pub fn pet(&mut self) { self.regs.wwdtcntrst().write(|w| { - w.set_restart(vals::WwdtcntrstRestart::RESTART); + w.set_restart(vals::WwdtcntrstRestart::Restart); }); } } From 087b1c78806c6b780277bac1d8037d2e31635df7 Mon Sep 17 00:00:00 2001 From: CordlessCoder Date: Tue, 28 Jul 2026 12:32:00 +0100 Subject: [PATCH 6/7] Bunch of driver changes to support MSPM0 sleep mode --- embassy-mspm0/src/adc.rs | 10 ++- embassy-mspm0/src/gpio.rs | 105 +++++++++++++++++++++++++---- embassy-mspm0/src/i2c.rs | 17 +++++ embassy-mspm0/src/i2c_target.rs | 5 ++ embassy-mspm0/src/trng.rs | 4 ++ embassy-mspm0/src/uart/buffered.rs | 24 ++++++- embassy-mspm0/src/uart/mod.rs | 17 ++++- 7 files changed, 159 insertions(+), 23 deletions(-) diff --git a/embassy-mspm0/src/adc.rs b/embassy-mspm0/src/adc.rs index 5f5743045f..e8179e5a73 100644 --- a/embassy-mspm0/src/adc.rs +++ b/embassy-mspm0/src/adc.rs @@ -525,15 +525,13 @@ impl<'d, T: Instance, M: Mode> Adc<'d, T, M> { #[inline] fn wait_for_conversion() -> impl Future { let r = T::info().regs; - let state = T::state(); poll_fn(move |cx| { - state.waker.register(cx.waker()); - - if !r.ctl0().read().enc() { - Poll::Ready(()) - } else { + if r.ctl0().read().enc() { + cx.waker().wake_by_ref(); Poll::Pending + } else { + Poll::Ready(()) } }) } diff --git a/embassy-mspm0/src/gpio.rs b/embassy-mspm0/src/gpio.rs index 795b0ddb43..a92db29b17 100644 --- a/embassy-mspm0/src/gpio.rs +++ b/embassy-mspm0/src/gpio.rs @@ -2,9 +2,11 @@ use core::convert::Infallible; use core::future::Future; +use core::sync::atomic::Ordering; use embassy_hal_internal::{Peri, PeripheralType, impl_peripheral}; use maitake_sync::WaitMap; +use portable_atomic::AtomicU32; use crate::pac::gpio::vals::*; use crate::pac::gpio::{self}; @@ -336,36 +338,40 @@ impl<'d> Flex<'d> { async fn wait_inner(&mut self, polarity: Polarity) { let pin = &self.pin; let block = pin.block(); + let bit = pin.bit_index(); + let key = pin.pin_port(); // Selecting the event to trigger. A RMW operation. critical_section::with(|_cs| { - if pin.bit_index() >= 16 { + if bit >= 16 { block.polarity31_16().modify(|w| { - w.set_dio(pin.bit_index() - 16, polarity); + w.set_dio(bit - 16, Polarity::RiseFall); }); } else { block.polarity15_0().modify(|w| { - w.set_dio(pin.bit_index(), polarity); + w.set_dio(bit, Polarity::RiseFall); }); }; }); + let _arm = EdgeArm::new(block, key, polarity); + // Clear previous edge events. This is done after setting the event to listen for to avoid a redundant write. block.cpu_int().iclr().write(|w| { - w.set_dio(pin.bit_index(), true); + w.set_dio(bit, true); }); - let key = pin.pin_port(); + let (rise, fall, mask) = want_masks(key); let result = GPIO_WAIT_MAP .wait_for(key, || { - if pin.block().cpu_int().ris().read().dio(pin.bit_index()) { + if (rise.load(Ordering::Relaxed) | fall.load(Ordering::Relaxed)) & mask == 0 { return true; } // Because pin singletons are Send, unmasking interrupts must be guarded by critical section. critical_section::with(|_cs| { - self.pin.block().cpu_int().imask().modify(|w| { - w.set_dio(self.pin.bit_index(), true); + block.cpu_int().imask().modify(|w| { + w.set_dio(bit, true); }); }); @@ -382,6 +388,46 @@ impl<'d> Flex<'d> { } } +struct EdgeArm { + block: gpio::Gpio, + pin_port: u8, +} + +impl EdgeArm { + fn new(block: gpio::Gpio, pin_port: u8, polarity: Polarity) -> Self { + let (rise, fall, mask) = want_masks(pin_port); + + if matches!(polarity, Polarity::Rise | Polarity::RiseFall) { + rise.fetch_or(mask, Ordering::Relaxed); + } + if matches!(polarity, Polarity::Fall | Polarity::RiseFall) { + fall.fetch_or(mask, Ordering::Relaxed); + } + + critical_section::with(|_cs| { + block.fastwake().modify(|w| w.set_din(usize::from(pin_port % 32), true)); + }); + + Self { block, pin_port } + } +} + +impl Drop for EdgeArm { + fn drop(&mut self) { + let (rise, fall, mask) = want_masks(self.pin_port); + let bit = usize::from(self.pin_port % 32); + + critical_section::with(|_cs| { + self.block.fastwake().modify(|w| w.set_din(bit, false)); + self.block.cpu_int().imask().modify(|w| w.set_dio(bit, false)); + }); + self.block.cpu_int().iclr().write(|w| w.set_dio(bit, true)); + + rise.fetch_and(!mask, Ordering::Relaxed); + fall.fetch_and(!mask, Ordering::Relaxed); + } +} + impl<'d> Drop for Flex<'d> { #[inline] fn drop(&mut self) { @@ -951,6 +997,24 @@ macro_rules! impl_pin { /// This map must **never** be closed because gpio wakers may be used forever. static GPIO_WAIT_MAP: WaitMap = WaitMap::new(); +const PORT_COUNT: usize = if cfg!(gpio_pc) { + 3 +} else if cfg!(gpio_pb) { + 2 +} else { + 1 +}; + +static WANT_RISE: [AtomicU32; PORT_COUNT] = [const { AtomicU32::new(0) }; PORT_COUNT]; + +static WANT_FALL: [AtomicU32; PORT_COUNT] = [const { AtomicU32::new(0) }; PORT_COUNT]; + +fn want_masks(pin_port: u8) -> (&'static AtomicU32, &'static AtomicU32, u32) { + let port = usize::from(pin_port / 32); + + (&WANT_RISE[port], &WANT_FALL[port], 1 << (pin_port % 32)) +} + pub(crate) trait SealedPin { fn pin_port(&self) -> u8; @@ -1050,14 +1114,29 @@ fn irq_handler(gpio: gpio::Gpio, port: Port) { let bits = gpio.cpu_int().mis().read().0; + let level = gpio.din31_0().read(); + for i in BitIter(bits) { let id = ((port as u8) * 32) + i as u8; - let _ = GPIO_WAIT_MAP.wake(&id, ()); + let (rise, fall, mask) = want_masks(id); - // Notify the future that an edge event has occurred by masking the interrupt for this pin. - gpio.cpu_int().imask().modify(|w| { - w.set_dio(i as usize, false); - }); + let fired = if level.dio(i as usize) { rise } else { fall }; + + if fired.load(Ordering::Relaxed) & mask != 0 { + rise.fetch_and(!mask, Ordering::Relaxed); + fall.fetch_and(!mask, Ordering::Relaxed); + + let _ = GPIO_WAIT_MAP.wake(&id, ()); + + // Notify the future that an edge event has occurred by masking the interrupt for this pin. + gpio.cpu_int().imask().modify(|w| { + w.set_dio(i as usize, false); + }); + } else { + gpio.cpu_int().iclr().write(|w| { + w.set_dio(i as usize, true); + }); + } } } diff --git a/embassy-mspm0/src/i2c.rs b/embassy-mspm0/src/i2c.rs index 173c4bfa7a..5f14a3478d 100644 --- a/embassy-mspm0/src/i2c.rs +++ b/embassy-mspm0/src/i2c.rs @@ -17,6 +17,7 @@ use crate::interrupt::{Interrupt, InterruptExt}; use crate::mode::{Async, Blocking, Mode}; use crate::pac::i2c::{I2c as Regs, vals}; use crate::pac::{self}; +use crate::sysctl::{SleepLevel, WakeGuard}; /// The clock source for the I2C. #[derive(Clone, Copy, PartialEq, Eq, Debug)] @@ -223,6 +224,14 @@ impl Config { } } + pub(crate) fn wake_floor(&self) -> Option { + let source_hz = match self.clock_source { + ClockSel::MfClk => 4_000_000, + ClockSel::BusClk => 32_000_000, + }; + SleepLevel::floor_for_clock_hz(source_hz) + } + fn check_clock_i2c(&self) -> bool { // make sure source clock is ~20 faster than i2c clock let clk_ratio = 20; @@ -321,6 +330,7 @@ pub struct I2c<'d, M: Mode> { state: &'static State, scl: Option>, sda: Option>, + wake_floor: Option, _phantom: PhantomData, } @@ -425,6 +435,8 @@ impl<'d, M: Mode> I2c<'d, M> { .clock .store(config.calculate_clock_source(), Ordering::Relaxed); + self.wake_floor = config.wake_floor(); + self.info .regs .controller(0) @@ -699,6 +711,8 @@ impl<'d> I2c<'d, Blocking> { impl<'d> I2c<'d, Async> { async fn write_async_internal(&mut self, addr: u8, write: &[u8], end_w_stop: bool) -> Result<(), Error> { + let _guard = self.wake_floor.map(WakeGuard::new); + let ctrl = self.info.regs.controller(0); let mut bytes_to_send = write.len(); @@ -762,6 +776,8 @@ impl<'d> I2c<'d, Async> { restart: bool, end_w_stop: bool, ) -> Result<(), Error> { + let _guard = self.wake_floor.map(WakeGuard::new); + let read_len = read.len(); let mut bytes_to_read = read_len; @@ -1066,6 +1082,7 @@ impl<'d, M: Mode> I2c<'d, M> { state: T::state(), scl: scl_inner, sda: sda_inner, + wake_floor: None, _phantom: PhantomData, }; this.init(&config)?; diff --git a/embassy-mspm0/src/i2c_target.rs b/embassy-mspm0/src/i2c_target.rs index 02498064de..864b4756eb 100644 --- a/embassy-mspm0/src/i2c_target.rs +++ b/embassy-mspm0/src/i2c_target.rs @@ -18,6 +18,7 @@ use crate::interrupt::InterruptExt; use crate::mode::{Async, Blocking, Mode}; use crate::pac::i2c::vals; use crate::pac::{self}; +use crate::sysctl::WakeGuard; use crate::{Peri, i2c, i2c_target, interrupt}; #[non_exhaustive] @@ -98,6 +99,7 @@ pub struct I2cTarget<'d, M: Mode> { sda: Option>, config: i2c::Config, target_config: i2c_target::Config, + wake_guard: Option, _phantom: PhantomData, } @@ -172,6 +174,8 @@ impl<'d> I2cTarget<'d, Async> { pub fn reset(&mut self) -> Result<(), ConfigError> { self.init()?; unsafe { self.info.interrupt.enable() }; + + self.wake_guard = self.config.wake_floor().map(WakeGuard::new); Ok(()) } } @@ -235,6 +239,7 @@ impl<'d, M: Mode> I2cTarget<'d, M> { sda, config, target_config, + wake_guard: None, _phantom: PhantomData, } } diff --git a/embassy-mspm0/src/trng.rs b/embassy-mspm0/src/trng.rs index 6fde0529cb..d5509de5b3 100644 --- a/embassy-mspm0/src/trng.rs +++ b/embassy-mspm0/src/trng.rs @@ -14,6 +14,8 @@ use rand_core::{TryCryptoRng, TryRngCore}; use crate::peripherals::TRNG; use crate::sealed; +#[cfg(feature = "rt")] +use crate::sysctl::{SleepLevel, WakeGuard}; static WAKER: AtomicWaker = AtomicWaker::new(); @@ -432,6 +434,8 @@ impl TrngInner<'_> { #[cfg(feature = "rt")] async fn async_read_u32(&mut self) -> Result { + let _guard = SleepLevel::floor_for_clock_hz(crate::sysctl::mclk_frequency()).map(WakeGuard::new); + poll_fn(|cx| { WAKER.register(cx.waker()); let result = self.poll(); diff --git a/embassy-mspm0/src/uart/buffered.rs b/embassy-mspm0/src/uart/buffered.rs index 885d7ab030..9bcbf03785 100644 --- a/embassy-mspm0/src/uart/buffered.rs +++ b/embassy-mspm0/src/uart/buffered.rs @@ -13,6 +13,7 @@ use embedded_hal_nb::nb; use crate::gpio::{AnyPin, SealedPin}; use crate::interrupt::typelevel::Binding; use crate::pac::uart::Uart as Regs; +use crate::sysctl::{SleepLevel, WakeGuard}; use crate::uart::{Config, ConfigError, CtsPin, Error, Info, Instance, RtsPin, RxPin, State, TxPin}; use crate::{Peri, interrupt}; @@ -146,6 +147,7 @@ impl<'d> BufferedUart<'d> { rx: self.rx.rx.as_mut().map(Peri::reborrow), rts: self.rx.rts.as_mut().map(Peri::reborrow), reborrowed: true, + wake_guard: None, }, ) } @@ -161,6 +163,7 @@ pub struct BufferedUartRx<'d> { rx: Option>, rts: Option>, reborrowed: bool, + wake_guard: Option, } impl SetConfig for BufferedUartRx<'_> { @@ -214,7 +217,12 @@ impl<'d> BufferedUartRx<'d> { rts.update_pf(config.rts_pf()); } - super::reconfigure(&self.info, &self.state.state, config) + super::reconfigure(&self.info, &self.state.state, config)?; + + if !self.reborrowed { + self.wake_guard = self.rx_wake_guard(config.low_power_rx_wake); + } + Ok(()) } /// Set baudrate @@ -222,6 +230,14 @@ impl<'d> BufferedUartRx<'d> { super::set_baudrate(&self.info, self.state.state.clock.load(Ordering::Relaxed), baudrate) } + fn rx_wake_guard(&self, low_power_rx_wake: bool) -> Option { + if low_power_rx_wake { + Some(WakeGuard::new(SleepLevel::Standby1)) + } else { + SleepLevel::floor_for_clock_hz(self.state.state.clock.load(Ordering::Relaxed)).map(WakeGuard::new) + } + } + /// Read from UART RX buffer, blocking execution until done. pub fn blocking_read(&mut self, buffer: &mut [u8]) -> Result { self.blocking_read_inner(buffer) @@ -606,9 +622,11 @@ impl<'d> BufferedUart<'d> { rx, rts, reborrowed: false, + wake_guard: None, }, }; this.enable_and_configure(tx_buffer, rx_buffer, &config)?; + this.rx.wake_guard = this.rx.rx_wake_guard(config.low_power_rx_wake); Ok(this) } @@ -662,8 +680,10 @@ impl<'d> BufferedUartRx<'d> { rx, rts, reborrowed: false, + wake_guard: None, }; this.enable_and_configure(rx_buffer, &config)?; + this.wake_guard = this.rx_wake_guard(config.low_power_rx_wake); Ok(this) } @@ -881,6 +901,8 @@ impl<'d> BufferedUartTx<'d> { } async fn flush_inner(&self) -> Result<(), Error> { + let _guard = SleepLevel::floor_for_clock_hz(self.state.state.clock.load(Ordering::Relaxed)).map(WakeGuard::new); + poll_fn(move |cx| { let state = self.state; diff --git a/embassy-mspm0/src/uart/mod.rs b/embassy-mspm0/src/uart/mod.rs index d05b326e35..97b8405afd 100644 --- a/embassy-mspm0/src/uart/mod.rs +++ b/embassy-mspm0/src/uart/mod.rs @@ -28,9 +28,9 @@ pub enum ClockSel { /// /// The MCLK runs at 4 MHz. MfClk, - // BusClk, - // BusClk depends on the timer's power domain. - // This will be implemented later. + + /// Use the bus clock (ULPCLK), which runs at the MCLK rate. + BusClk, } #[non_exhaustive] @@ -158,6 +158,10 @@ pub struct Config { /// Set the pull configuration for the CTS pin. pub cts_pull: Pull, + + /// Let the chip deep-sleep (down to STANDBY0) while an async [`BufferedUart`] receiver is + /// listening, waking on an incoming RX start bit. + pub low_power_rx_wake: bool, } impl Default for Config { @@ -181,6 +185,7 @@ impl Default for Config { rx_pull: Pull::None, rts_pull: Pull::None, cts_pull: Pull::None, + low_power_rx_wake: false, } } } @@ -768,11 +773,17 @@ fn configure( w.set_lfclk_sel(false); w.set_busclk_sel(false); } + ClockSel::BusClk => { + w.set_busclk_sel(true); + w.set_lfclk_sel(false); + w.set_mfclk_sel(false); + } }); let clock = match config.clock_source { ClockSel::LfClk => 32768, ClockSel::MfClk => 4_000_000, + ClockSel::BusClk => crate::sysctl::mclk_frequency(), }; state.clock.store(clock, Ordering::Relaxed); From 3aec6bbe1e4a0a52589e8f62f0cb10bfdbb21bb4 Mon Sep 17 00:00:00 2001 From: CordlessCoder Date: Wed, 29 Jul 2026 09:41:58 +0100 Subject: [PATCH 7/7] MSPM0 Expose peripheral power domains Which power domain a peripheral instance sits in decides whether it can work through deep sleep at all: PD0 is powered in RUN/SLEEP/STOP/STANDBY, PD1 only in RUN and SLEEP, and SYSCTL forces PD1 peripherals to a disabled state on STOP/STANDBY entry. Arming a wake source on a PD1 instance is therefore silently dead. The domain is a per-instance and per-chip property, not a property of the peripheral kind: UART3 is PD1 on G-series but PD0 on L122x/L222x, UART7 is PD0 while UART3-6 are PD1, and TIMA0 differs between families. It has to come from the chip metadata. --- embassy-mspm0/build.rs | 56 ++++++++++- embassy-mspm0/src/i2c.rs | 12 ++- embassy-mspm0/src/i2c_target.rs | 2 +- embassy-mspm0/src/sysctl/mod.rs | 136 ++++++++++++++++++++------- embassy-mspm0/src/time_driver/tim.rs | 8 ++ embassy-mspm0/src/trng.rs | 6 +- embassy-mspm0/src/uart/buffered.rs | 11 ++- embassy-mspm0/src/uart/mod.rs | 17 +++- 8 files changed, 198 insertions(+), 50 deletions(-) diff --git a/embassy-mspm0/build.rs b/embassy-mspm0/build.rs index edea53541b..50bbc2c9bf 100644 --- a/embassy-mspm0/build.rs +++ b/embassy-mspm0/build.rs @@ -8,7 +8,7 @@ use std::sync::LazyLock; use std::{env, fs}; use common::CfgSet; -use mspm0_metapac::metadata::{ALL_CHIPS, METADATA}; +use mspm0_metapac::metadata::{ALL_CHIPS, METADATA, PowerDomain}; use proc_macro2::{Ident, Literal, Span, TokenStream}; use quote::{format_ident, quote}; @@ -67,6 +67,7 @@ fn generate_code(cfgs: &mut CfgSet) { g.extend(generate_timers()); g.extend(generate_interrupts()); g.extend(generate_peripheral_instances()); + g.extend(generate_power_domains(&singletons)); g.extend(generate_pin_trait_impls()); g.extend(generate_groups()); g.extend(generate_dma_channel_count()); @@ -672,16 +673,65 @@ fn generate_interrupts() -> TokenStream { } } +fn power_domain_ident(domain: &PowerDomain) -> Ident { + format_ident!( + "{}", + match domain { + PowerDomain::Pd0 => "Pd0", + PowerDomain::Pd1 => "Pd1", + PowerDomain::Backup => "Backup", + } + ) +} + +/// Implement `PowerDomainInstance` for every peripheral singleton. +/// +/// Driven off the singleton list rather than the metadata so it cannot emit an impl for a type +/// `get_singletons` decided not to create. +fn generate_power_domains(singletons: &[Singleton]) -> TokenStream { + let impls = singletons.iter().filter_map(|singleton| { + let name = singleton.name.as_str(); + + // A pin's domain says nothing useful: GPIO logic is in PD0 on every chip. + if METADATA.pins.iter().any(|pin| pin.pin == name) { + return None; + } + + // Singletons without a metadata entry of their own inherit from the peripheral they belong to. + let owner = match name { + "CLK_OUT" => "SYSCTL", + _ if name.starts_with("DMA_CH") => "DMA", + _ => name, + }; + + let domain = METADATA + .peripherals + .iter() + .find(|peripheral| peripheral.name == owner) + .map(|peripheral| power_domain_ident(&peripheral.power_domain)) + .unwrap_or_else(|| panic!("no power domain for singleton {name} (looked for peripheral {owner})")); + + let peri = format_ident!("{}", name); + + Some(quote! { impl_power_domain!(#peri, #domain); }) + }); + + quote! { + #(#impls)* + } +} + fn generate_peripheral_instances() -> TokenStream { let mut impls = Vec::::new(); for peripheral in METADATA.peripherals { let peri = format_ident!("{}", peripheral.name); let fifo_size = peripheral.sys_fentries; + let power_domain = power_domain_ident(&peripheral.power_domain); let tokens = match peripheral.kind { - "uart" => Some(quote! { impl_uart_instance!(#peri); }), - "i2c" => Some(quote! { impl_i2c_instance!(#peri, #fifo_size); }), + "uart" => Some(quote! { impl_uart_instance!(#peri, #power_domain); }), + "i2c" => Some(quote! { impl_i2c_instance!(#peri, #fifo_size, #power_domain); }), "wwdt" => Some(quote! { impl_wwdt_instance!(#peri); }), "adc" => Some(quote! { impl_adc_instance!(#peri); }), "mathacl" => Some(quote! { impl_mathacl_instance!(#peri); }), diff --git a/embassy-mspm0/src/i2c.rs b/embassy-mspm0/src/i2c.rs index 5f14a3478d..88acb2e917 100644 --- a/embassy-mspm0/src/i2c.rs +++ b/embassy-mspm0/src/i2c.rs @@ -17,7 +17,7 @@ use crate::interrupt::{Interrupt, InterruptExt}; use crate::mode::{Async, Blocking, Mode}; use crate::pac::i2c::{I2c as Regs, vals}; use crate::pac::{self}; -use crate::sysctl::{SleepLevel, WakeGuard}; +use crate::sysctl::{PowerDomain, SleepLevel, WakeGuard}; /// The clock source for the I2C. #[derive(Clone, Copy, PartialEq, Eq, Debug)] @@ -224,12 +224,12 @@ impl Config { } } - pub(crate) fn wake_floor(&self) -> Option { + pub(crate) fn wake_floor(&self, power_domain: PowerDomain) -> Option { let source_hz = match self.clock_source { ClockSel::MfClk => 4_000_000, ClockSel::BusClk => 32_000_000, }; - SleepLevel::floor_for_clock_hz(source_hz) + power_domain.floor_to_keep_running(source_hz) } fn check_clock_i2c(&self) -> bool { @@ -435,7 +435,7 @@ impl<'d, M: Mode> I2c<'d, M> { .clock .store(config.calculate_clock_source(), Ordering::Relaxed); - self.wake_floor = config.wake_floor(); + self.wake_floor = config.wake_floor(self.info.power_domain); self.info .regs @@ -1029,6 +1029,7 @@ pub(crate) struct Info { pub(crate) regs: Regs, pub(crate) interrupt: Interrupt, pub fifo_size: usize, + pub(crate) power_domain: PowerDomain, } pub(crate) struct State { @@ -1097,7 +1098,7 @@ pub(crate) trait SealedInstance { } macro_rules! impl_i2c_instance { - ($instance: ident, $fifo_size: expr) => { + ($instance: ident, $fifo_size: expr, $power_domain: ident) => { impl crate::i2c::SealedInstance for crate::peripherals::$instance { fn info() -> &'static crate::i2c::Info { use crate::i2c::Info; @@ -1107,6 +1108,7 @@ macro_rules! impl_i2c_instance { regs: crate::pac::$instance, interrupt: crate::interrupt::typelevel::$instance::IRQ, fifo_size: $fifo_size, + power_domain: crate::sysctl::PowerDomain::$power_domain, }; &INFO } diff --git a/embassy-mspm0/src/i2c_target.rs b/embassy-mspm0/src/i2c_target.rs index 864b4756eb..9c50484946 100644 --- a/embassy-mspm0/src/i2c_target.rs +++ b/embassy-mspm0/src/i2c_target.rs @@ -175,7 +175,7 @@ impl<'d> I2cTarget<'d, Async> { self.init()?; unsafe { self.info.interrupt.enable() }; - self.wake_guard = self.config.wake_floor().map(WakeGuard::new); + self.wake_guard = self.config.wake_floor(self.info.power_domain).map(WakeGuard::new); Ok(()) } } diff --git a/embassy-mspm0/src/sysctl/mod.rs b/embassy-mspm0/src/sysctl/mod.rs index 41bc48a7e8..3d45e8390c 100644 --- a/embassy-mspm0/src/sysctl/mod.rs +++ b/embassy-mspm0/src/sysctl/mod.rs @@ -2,6 +2,8 @@ #![macro_use] +use embassy_hal_internal::PeripheralType; + use crate::gpio::{AnyPin, PfType, Pin, Pull, SealedPin}; use crate::pac::sysctl::vals; use crate::peripherals::CLK_OUT; @@ -45,62 +47,124 @@ impl SleepLevel { SleepLevel::Standby0, SleepLevel::Standby1, ]; +} + +/// The power domain a peripheral instance belongs to. +/// +/// Which domain an instance is in is a property of the chip, not of the peripheral kind: the same IP +/// appears in both domains on one die, and the same instance name differs between chips. +/// See [`PowerDomainInstance`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum PowerDomain { + /// Low-speed domain, clocked by ULPCLK. Powered in every mode but SHUTDOWN. + Pd0, + + /// High-performance domain, clocked by MCLK. Powered only in RUN and SLEEP; SYSCTL forces its + /// peripherals to a disabled state on deep-sleep entry. + Pd1, - /// Shallowest level to block so a PD0 peripheral clocked at `clock_hz` keeps running, or `None` - /// to block nothing (any sleep depth is fine). + /// Backup domain, powered from `VBAT` and clocked by LFCLK. Survives even SHUTDOWN. /// - /// `clock_hz` is the frequency of the clock that the peripheral depends on. - /// ULPCLK for bus-clocked peripherals, or the LFCLK/MFCLK source rate for those clocked directly. + /// Only present on chips with an independent `VBAT` supply. + Backup, +} + +impl PowerDomain { + /// Whether the domain stays powered through deep sleep. + /// + /// Takes no [`SleepLevel`]: every level is a STOP or STANDBY mode, and PD1 is disabled in all of + /// them. Being powered is not the same as being clocked — see [`Self::floor_to_keep_running`]. + pub const fn is_powered_in_deep_sleep(self) -> bool { + !matches!(self, Self::Pd1) + } + + /// Shallowest level to block so an instance in this domain, clocked at `clock_hz`, keeps + /// running, or `None` to block nothing (any sleep depth is fine). + /// + /// `clock_hz` is the frequency of the clock that the peripheral depends on: ULPCLK for + /// bus-clocked peripherals, or the LFCLK/MFCLK source rate for those clocked directly. It is + /// ignored for the domains deep sleep does not clock down. /// The per-mode ceiling is the same across every MSPM0 family: STOP0/STOP1 cap at 4 MHz, STOP2 /// and STANDBY0 at 32 kHz (LFCLK), and only STANDBY1 unclocks PD0 (there just TIMG0/1 stay clocked). /// + /// Answers only for peripherals that must run *continuously*. Work merely triggered while asleep + /// is a different question: a DMA transfer or an ADC conversion raises an asynchronous request + /// that powers PD1 back up on demand, so those must not block sleep on this. + /// /// NOTE: Assumes the RUN0 run mode, the only one the HAL configures today /// (STOP0 reaches 4 MHz only when entered from RUN0). - pub const fn floor_for_clock_hz(clock_hz: u32) -> Option { + pub const fn floor_to_keep_running(self, clock_hz: u32) -> Option { // Per-mode clock ceilings, from the family TRMs' "DMA Operating Mode Support" and "Operating // Modes" sections. // STANDBY0 clocks all PD0 peripherals from LFCLK; STANDBY1 does not. const STOP_HZ: u32 = 4_000_000; const LFCLK_HZ: u32 = 32_768; - if clock_hz > STOP_HZ { - // Needs MCLK - Some(Self::Stop0) - } else if clock_hz > LFCLK_HZ { - // Reqires MFCLK - Some(Self::Stop2) - } else if clock_hz > 0 { - // LFCLK is enough, keep PD0 alive - Some(Self::Standby1) - } else { - // No clock - None + match self { + // Disabled by SYSCTL on entry to any deep-sleep mode, whatever it is clocked at. + Self::Pd1 => Some(SleepLevel::Stop0), + + // Powered from VBAT, so no mode reaches it. + Self::Backup => None, + + Self::Pd0 => { + if clock_hz > STOP_HZ { + // Needs MCLK + Some(SleepLevel::Stop0) + } else if clock_hz > LFCLK_HZ { + // Requires MFCLK + Some(SleepLevel::Stop2) + } else if clock_hz > 0 { + // LFCLK is enough, keep PD0 alive + Some(SleepLevel::Standby1) + } else { + // No clock + None + } + } } } } -// Boundary checks for `floor_for_clock_hz`. `crate::fmt` cannot be used in const. +// Boundary checks for `floor_to_keep_running`. `crate::fmt` cannot be used in const. const _: () = { - core::assert!(matches!( - SleepLevel::floor_for_clock_hz(4_000_001), - Some(SleepLevel::Stop0) - )); - core::assert!(matches!( - SleepLevel::floor_for_clock_hz(4_000_000), - Some(SleepLevel::Stop2) - )); - core::assert!(matches!( - SleepLevel::floor_for_clock_hz(32_769), - Some(SleepLevel::Stop2) - )); - core::assert!(matches!( - SleepLevel::floor_for_clock_hz(32_768), - Some(SleepLevel::Standby1) - )); - core::assert!(matches!(SleepLevel::floor_for_clock_hz(1), Some(SleepLevel::Standby1))); - core::assert!(matches!(SleepLevel::floor_for_clock_hz(0), None)); + use PowerDomain::{Backup, Pd0, Pd1}; + + core::assert!(matches!(Pd0.floor_to_keep_running(4_000_001), Some(SleepLevel::Stop0))); + core::assert!(matches!(Pd0.floor_to_keep_running(4_000_000), Some(SleepLevel::Stop2))); + core::assert!(matches!(Pd0.floor_to_keep_running(32_769), Some(SleepLevel::Stop2))); + core::assert!(matches!(Pd0.floor_to_keep_running(32_768), Some(SleepLevel::Standby1))); + core::assert!(matches!(Pd0.floor_to_keep_running(1), Some(SleepLevel::Standby1))); + core::assert!(matches!(Pd0.floor_to_keep_running(0), None)); + + // No clock rate makes PD1 survive, or stops the backup domain from surviving. + core::assert!(matches!(Pd1.floor_to_keep_running(0), Some(SleepLevel::Stop0))); + core::assert!(matches!(Pd1.floor_to_keep_running(32_000_000), Some(SleepLevel::Stop0))); + core::assert!(matches!(Backup.floor_to_keep_running(32_000_000), None)); + + core::assert!(Pd0.is_powered_in_deep_sleep()); + core::assert!(!Pd1.is_powered_in_deep_sleep()); + core::assert!(Backup.is_powered_in_deep_sleep()); }; +/// The [`PowerDomain`] a peripheral instance is in. +/// +/// Implemented for every peripheral singleton from the chip metadata. GPIO pins are excluded: the +/// GPIO logic is in PD0 on every chip, and its PD1 register interface is only ever reachable in RUN. +pub trait PowerDomainInstance: PeripheralType { + /// The domain this instance is in. + const POWER_DOMAIN: PowerDomain; +} + +macro_rules! impl_power_domain { + ($instance:ident, $domain:ident) => { + impl crate::sysctl::PowerDomainInstance for crate::peripherals::$instance { + const POWER_DOMAIN: crate::sysctl::PowerDomain = crate::sysctl::PowerDomain::$domain; + } + }; +} + /// A token forbidding a deep-sleep mode (and anything deeper) while held. /// /// A guard at `level` blocks that [`SleepLevel`] and every deeper mode; the low-power executor then diff --git a/embassy-mspm0/src/time_driver/tim.rs b/embassy-mspm0/src/time_driver/tim.rs index 0783c8fdcc..13dcfcc24b 100644 --- a/embassy-mspm0/src/time_driver/tim.rs +++ b/embassy-mspm0/src/time_driver/tim.rs @@ -61,6 +61,14 @@ type T = peripherals::TIMA0; #[cfg(time_driver_tima1)] type T = peripherals::TIMA1; +// The timer must also be in PD0 to survive deep sleep. Checked against the chip metadata rather than +// trusted from the name above, since the same timer name is PD0 on some chips and PD1 on others. +#[cfg(feature = "low-power")] +const _: () = core::assert!( + ::POWER_DOMAIN.is_powered_in_deep_sleep(), + "the time driver's timer is in PD1, which deep sleep powers down" +); + fn regs() -> Tim { T::info().regs } diff --git a/embassy-mspm0/src/trng.rs b/embassy-mspm0/src/trng.rs index d5509de5b3..e64e2339a9 100644 --- a/embassy-mspm0/src/trng.rs +++ b/embassy-mspm0/src/trng.rs @@ -15,7 +15,7 @@ use rand_core::{TryCryptoRng, TryRngCore}; use crate::peripherals::TRNG; use crate::sealed; #[cfg(feature = "rt")] -use crate::sysctl::{SleepLevel, WakeGuard}; +use crate::sysctl::{PowerDomainInstance, WakeGuard}; static WAKER: AtomicWaker = AtomicWaker::new(); @@ -434,7 +434,9 @@ impl TrngInner<'_> { #[cfg(feature = "rt")] async fn async_read_u32(&mut self) -> Result { - let _guard = SleepLevel::floor_for_clock_hz(crate::sysctl::mclk_frequency()).map(WakeGuard::new); + let _guard = ::POWER_DOMAIN + .floor_to_keep_running(crate::sysctl::mclk_frequency()) + .map(WakeGuard::new); poll_fn(|cx| { WAKER.register(cx.waker()); diff --git a/embassy-mspm0/src/uart/buffered.rs b/embassy-mspm0/src/uart/buffered.rs index 9bcbf03785..8494c72395 100644 --- a/embassy-mspm0/src/uart/buffered.rs +++ b/embassy-mspm0/src/uart/buffered.rs @@ -234,7 +234,10 @@ impl<'d> BufferedUartRx<'d> { if low_power_rx_wake { Some(WakeGuard::new(SleepLevel::Standby1)) } else { - SleepLevel::floor_for_clock_hz(self.state.state.clock.load(Ordering::Relaxed)).map(WakeGuard::new) + self.info + .power_domain + .floor_to_keep_running(self.state.state.clock.load(Ordering::Relaxed)) + .map(WakeGuard::new) } } @@ -901,7 +904,11 @@ impl<'d> BufferedUartTx<'d> { } async fn flush_inner(&self) -> Result<(), Error> { - let _guard = SleepLevel::floor_for_clock_hz(self.state.state.clock.load(Ordering::Relaxed)).map(WakeGuard::new); + let _guard = self + .info + .power_domain + .floor_to_keep_running(self.state.state.clock.load(Ordering::Relaxed)) + .map(WakeGuard::new); poll_fn(move |cx| { let state = self.state; diff --git a/embassy-mspm0/src/uart/mod.rs b/embassy-mspm0/src/uart/mod.rs index 97b8405afd..bc304c772a 100644 --- a/embassy-mspm0/src/uart/mod.rs +++ b/embassy-mspm0/src/uart/mod.rs @@ -14,6 +14,7 @@ use crate::gpio::{AnyPin, PfType, Pull, SealedPin}; use crate::interrupt::{Interrupt, InterruptExt}; use crate::mode::{Blocking, Mode}; use crate::pac::uart::{Uart as Regs, vals}; +use crate::sysctl::PowerDomain; /// The clock source for the UART. #[derive(Clone, Copy, PartialEq, Eq, Debug)] @@ -97,6 +98,12 @@ pub enum ConfigError { /// The baud rate could not be configured with the given clocks. InvalidBaudRate, + + /// [`Config::low_power_rx_wake`] was set on an instance that deep sleep powers down. + /// + /// SYSCTL disables PD1 peripherals on entry to STOP and STANDBY, so a PD1 UART cannot detect the + /// start bit that would wake the chip. Use a PD0 instance. + NoDeepSleepWake, } #[non_exhaustive] @@ -161,6 +168,8 @@ pub struct Config { /// Let the chip deep-sleep (down to STANDBY0) while an async [`BufferedUart`] receiver is /// listening, waking on an incoming RX start bit. + /// + /// Only PD0 instances can do this; anything else is a [`ConfigError::NoDeepSleepWake`]. pub low_power_rx_wake: bool, } @@ -584,6 +593,7 @@ pub trait RtsPin: crate::gpio::Pin { pub(crate) struct Info { pub(crate) regs: Regs, pub(crate) interrupt: Interrupt, + pub(crate) power_domain: PowerDomain, } pub(crate) struct State { @@ -761,6 +771,10 @@ fn configure( return Err(ConfigError::RxOrTxNotEnabled); } + if config.low_power_rx_wake && !info.power_domain.is_powered_in_deep_sleep() { + return Err(ConfigError::NoDeepSleepWake); + } + // SLAU846B says that clocks should be enabled before disabling the uart. r.clksel().write(|w| match config.clock_source { ClockSel::LfClk => { @@ -1094,7 +1108,7 @@ pub(crate) trait SealedInstance { } macro_rules! impl_uart_instance { - ($instance: ident) => { + ($instance: ident, $power_domain: ident) => { impl crate::uart::SealedInstance for crate::peripherals::$instance { fn info() -> &'static crate::uart::Info { use crate::interrupt::typelevel::Interrupt; @@ -1103,6 +1117,7 @@ macro_rules! impl_uart_instance { const INFO: Info = Info { regs: crate::pac::$instance, interrupt: crate::interrupt::typelevel::$instance::IRQ, + power_domain: crate::sysctl::PowerDomain::$power_domain, }; &INFO }