diff options
| author | James Munns <[email protected]> | 2025-11-18 14:19:09 +0100 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-11-18 14:19:09 +0100 |
| commit | 5b1149a52dbec9e3bdd10dc341dc0751ab4798a6 (patch) | |
| tree | 3ff7098471cf660a54a707464a0e2feb2080b09e /src/ostimer.rs | |
| parent | 62e297c130ac26afe4d7d5752bb79709bd370e39 (diff) | |
| parent | c8942aec2478ff077b55da0e86801f8a6a88a7de (diff) | |
Merge pull request #11 from jamesmunns/james/impl-clocks
Implement initial `clock` driver.
This PR introduces an initial two-phase clock driver system:
1. The first stage is responsible for initializing the core/system clocks at the time of `embassy_mcxa::init()`
2. The second stage is done on creation of peripherals
This work is limited to currently used clocks and peripherals, but has room for expansion for later peripherals. This model is based on the preliminary refactoring performed for the `embassy-imxrt` crate.
Diffstat (limited to 'src/ostimer.rs')
| -rw-r--r-- | src/ostimer.rs | 73 |
1 files changed, 47 insertions, 26 deletions
diff --git a/src/ostimer.rs b/src/ostimer.rs index 8bc68389a..cd5451b53 100644 --- a/src/ostimer.rs +++ b/src/ostimer.rs | |||
| @@ -29,8 +29,13 @@ | |||
| 29 | 29 | ||
| 30 | use core::sync::atomic::{AtomicBool, Ordering}; | 30 | use core::sync::atomic::{AtomicBool, Ordering}; |
| 31 | 31 | ||
| 32 | use embassy_hal_internal::{Peri, PeripheralType}; | ||
| 33 | |||
| 34 | use crate::clocks::periph_helpers::{OsTimerConfig, OstimerClockSel}; | ||
| 35 | use crate::clocks::{assert_reset, enable_and_reset, is_reset_released, release_reset, Gate, PoweredClock}; | ||
| 32 | use crate::interrupt::InterruptExt; | 36 | use crate::interrupt::InterruptExt; |
| 33 | use crate::pac; | 37 | use crate::pac; |
| 38 | use crate::peripherals::OSTIMER0; | ||
| 34 | 39 | ||
| 35 | // PAC defines the shared RegisterBlock under `ostimer0`. | 40 | // PAC defines the shared RegisterBlock under `ostimer0`. |
| 36 | type Regs = pac::ostimer0::RegisterBlock; | 41 | type Regs = pac::ostimer0::RegisterBlock; |
| @@ -197,16 +202,16 @@ impl<'d> Alarm<'d> { | |||
| 197 | pub struct Config { | 202 | pub struct Config { |
| 198 | /// Initialize MATCH registers to their max values and mask/clear the interrupt flag. | 203 | /// Initialize MATCH registers to their max values and mask/clear the interrupt flag. |
| 199 | pub init_match_max: bool, | 204 | pub init_match_max: bool, |
| 200 | /// OSTIMER clock frequency in Hz (must match the actual hardware clock) | 205 | pub power: PoweredClock, |
| 201 | pub clock_frequency_hz: u64, | 206 | pub source: OstimerClockSel, |
| 202 | } | 207 | } |
| 203 | 208 | ||
| 204 | impl Default for Config { | 209 | impl Default for Config { |
| 205 | fn default() -> Self { | 210 | fn default() -> Self { |
| 206 | Self { | 211 | Self { |
| 207 | init_match_max: true, | 212 | init_match_max: true, |
| 208 | // Default to 1MHz - user should override this with actual frequency | 213 | power: PoweredClock::NormalEnabledDeepSleepDisabled, |
| 209 | clock_frequency_hz: 1_000_000, | 214 | source: OstimerClockSel::Clk1M, |
| 210 | } | 215 | } |
| 211 | } | 216 | } |
| 212 | } | 217 | } |
| @@ -222,8 +227,16 @@ impl<'d, I: Instance> Ostimer<'d, I> { | |||
| 222 | /// Construct OSTIMER handle. | 227 | /// Construct OSTIMER handle. |
| 223 | /// Requires clocks for the instance to be enabled by the board before calling. | 228 | /// Requires clocks for the instance to be enabled by the board before calling. |
| 224 | /// Does not enable NVIC or INTENA; use time_driver::init() for async operation. | 229 | /// Does not enable NVIC or INTENA; use time_driver::init() for async operation. |
| 225 | pub fn new(_inst: impl Instance, cfg: Config, _p: &'d crate::pac::Peripherals) -> Self { | 230 | pub fn new(_inst: Peri<'d, I>, cfg: Config) -> Self { |
| 226 | assert!(cfg.clock_frequency_hz > 0, "OSTIMER frequency must be greater than 0"); | 231 | let clock_freq = unsafe { |
| 232 | enable_and_reset::<I>(&OsTimerConfig { | ||
| 233 | power: cfg.power, | ||
| 234 | source: cfg.source, | ||
| 235 | }) | ||
| 236 | .expect("Enabling OsTimer clock should not fail") | ||
| 237 | }; | ||
| 238 | |||
| 239 | assert!(clock_freq > 0, "OSTIMER frequency must be greater than 0"); | ||
| 227 | 240 | ||
| 228 | if cfg.init_match_max { | 241 | if cfg.init_match_max { |
| 229 | let r: &Regs = unsafe { &*I::ptr() }; | 242 | let r: &Regs = unsafe { &*I::ptr() }; |
| @@ -233,7 +246,7 @@ impl<'d, I: Instance> Ostimer<'d, I> { | |||
| 233 | 246 | ||
| 234 | Self { | 247 | Self { |
| 235 | _inst: core::marker::PhantomData, | 248 | _inst: core::marker::PhantomData, |
| 236 | clock_frequency_hz: cfg.clock_frequency_hz, | 249 | clock_frequency_hz: clock_freq as u64, |
| 237 | _phantom: core::marker::PhantomData, | 250 | _phantom: core::marker::PhantomData, |
| 238 | } | 251 | } |
| 239 | } | 252 | } |
| @@ -260,7 +273,7 @@ impl<'d, I: Instance> Ostimer<'d, I> { | |||
| 260 | /// # Safety | 273 | /// # Safety |
| 261 | /// This operation will reset the entire OSTIMER peripheral. Any active alarms | 274 | /// This operation will reset the entire OSTIMER peripheral. Any active alarms |
| 262 | /// or time_driver operations will be disrupted. Use with caution. | 275 | /// or time_driver operations will be disrupted. Use with caution. |
| 263 | pub fn reset(&self, peripherals: &crate::pac::Peripherals) { | 276 | pub fn reset(&self, _peripherals: &crate::pac::Peripherals) { |
| 264 | critical_section::with(|_| { | 277 | critical_section::with(|_| { |
| 265 | let r: &Regs = unsafe { &*I::ptr() }; | 278 | let r: &Regs = unsafe { &*I::ptr() }; |
| 266 | 279 | ||
| @@ -270,19 +283,17 @@ impl<'d, I: Instance> Ostimer<'d, I> { | |||
| 270 | .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit()); | 283 | .write(|w| w.ostimer_intrflag().clear_bit_by_one().ostimer_intena().clear_bit()); |
| 271 | 284 | ||
| 272 | unsafe { | 285 | unsafe { |
| 273 | crate::reset::assert::<crate::reset::line::Ostimer0>(peripherals); | 286 | assert_reset::<OSTIMER0>(); |
| 274 | } | ||
| 275 | 287 | ||
| 276 | for _ in 0..RESET_STABILIZE_SPINS { | 288 | for _ in 0..RESET_STABILIZE_SPINS { |
| 277 | cortex_m::asm::nop(); | 289 | cortex_m::asm::nop(); |
| 278 | } | 290 | } |
| 279 | 291 | ||
| 280 | unsafe { | 292 | release_reset::<OSTIMER0>(); |
| 281 | crate::reset::release::<crate::reset::line::Ostimer0>(peripherals); | ||
| 282 | } | ||
| 283 | 293 | ||
| 284 | while !<crate::reset::line::Ostimer0 as crate::reset::ResetLine>::is_released(&peripherals.mrcc0) { | 294 | while !is_reset_released::<OSTIMER0>() { |
| 285 | cortex_m::asm::nop(); | 295 | cortex_m::asm::nop(); |
| 296 | } | ||
| 286 | } | 297 | } |
| 287 | 298 | ||
| 288 | for _ in 0..RESET_STABILIZE_SPINS { | 299 | for _ in 0..RESET_STABILIZE_SPINS { |
| @@ -469,14 +480,13 @@ fn now_ticks_read() -> u64 { | |||
| 469 | // Read high then low to minimize incoherent snapshots | 480 | // Read high then low to minimize incoherent snapshots |
| 470 | let hi = (r.evtimerh().read().evtimer_count_value().bits() as u64) & (EVTIMER_HI_MASK as u64); | 481 | let hi = (r.evtimerh().read().evtimer_count_value().bits() as u64) & (EVTIMER_HI_MASK as u64); |
| 471 | let lo = r.evtimerl().read().evtimer_count_value().bits() as u64; | 482 | let lo = r.evtimerl().read().evtimer_count_value().bits() as u64; |
| 472 | |||
| 473 | // Combine and convert from Gray code to binary | 483 | // Combine and convert from Gray code to binary |
| 474 | let gray = lo | (hi << EVTIMER_HI_SHIFT); | 484 | let gray = lo | (hi << EVTIMER_HI_SHIFT); |
| 475 | gray_to_bin(gray) | 485 | gray_to_bin(gray) |
| 476 | } | 486 | } |
| 477 | 487 | ||
| 478 | // Instance trait like other drivers, providing a PAC pointer for this OSTIMER instance | 488 | // Instance trait like other drivers, providing a PAC pointer for this OSTIMER instance |
| 479 | pub trait Instance { | 489 | pub trait Instance: Gate<MrccPeriphConfig = OsTimerConfig> + PeripheralType { |
| 480 | fn ptr() -> *const Regs; | 490 | fn ptr() -> *const Regs; |
| 481 | } | 491 | } |
| 482 | 492 | ||
| @@ -491,12 +501,12 @@ impl Instance for crate::peripherals::OSTIMER0 { | |||
| 491 | } | 501 | } |
| 492 | 502 | ||
| 493 | // Also implement Instance for the Peri wrapper type | 503 | // Also implement Instance for the Peri wrapper type |
| 494 | impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::OSTIMER0> { | 504 | // impl Instance for embassy_hal_internal::Peri<'_, crate::peripherals::OSTIMER0> { |
| 495 | #[inline(always)] | 505 | // #[inline(always)] |
| 496 | fn ptr() -> *const Regs { | 506 | // fn ptr() -> *const Regs { |
| 497 | pac::Ostimer0::ptr() | 507 | // pac::Ostimer0::ptr() |
| 498 | } | 508 | // } |
| 499 | } | 509 | // } |
| 500 | 510 | ||
| 501 | #[inline(always)] | 511 | #[inline(always)] |
| 502 | fn bin_to_gray(x: u64) -> u64 { | 512 | fn bin_to_gray(x: u64) -> u64 { |
| @@ -524,7 +534,10 @@ pub mod time_driver { | |||
| 524 | bin_to_gray, now_ticks_read, Regs, ALARM_ACTIVE, ALARM_CALLBACK, ALARM_FLAG, ALARM_TARGET_TIME, | 534 | bin_to_gray, now_ticks_read, Regs, ALARM_ACTIVE, ALARM_CALLBACK, ALARM_FLAG, ALARM_TARGET_TIME, |
| 525 | EVTIMER_HI_MASK, EVTIMER_HI_SHIFT, LOW_32_BIT_MASK, | 535 | EVTIMER_HI_MASK, EVTIMER_HI_SHIFT, LOW_32_BIT_MASK, |
| 526 | }; | 536 | }; |
| 537 | use crate::clocks::periph_helpers::{OsTimerConfig, OstimerClockSel}; | ||
| 538 | use crate::clocks::{enable_and_reset, PoweredClock}; | ||
| 527 | use crate::pac; | 539 | use crate::pac; |
| 540 | use crate::peripherals::OSTIMER0; | ||
| 528 | pub struct Driver; | 541 | pub struct Driver; |
| 529 | static TIMER_WAKER: AtomicWaker = AtomicWaker::new(); | 542 | static TIMER_WAKER: AtomicWaker = AtomicWaker::new(); |
| 530 | 543 | ||
| @@ -611,6 +624,14 @@ pub mod time_driver { | |||
| 611 | /// Note: The frequency parameter is currently accepted for API compatibility. | 624 | /// Note: The frequency parameter is currently accepted for API compatibility. |
| 612 | /// The embassy_time_driver macro handles driver registration automatically. | 625 | /// The embassy_time_driver macro handles driver registration automatically. |
| 613 | pub fn init(priority: crate::interrupt::Priority, frequency_hz: u64) { | 626 | pub fn init(priority: crate::interrupt::Priority, frequency_hz: u64) { |
| 627 | let _clock_freq = unsafe { | ||
| 628 | enable_and_reset::<OSTIMER0>(&OsTimerConfig { | ||
| 629 | power: PoweredClock::AlwaysEnabled, | ||
| 630 | source: OstimerClockSel::Clk1M, | ||
| 631 | }) | ||
| 632 | .expect("Enabling OsTimer clock should not fail") | ||
| 633 | }; | ||
| 634 | |||
| 614 | // Mask/clear at peripheral and set default MATCH | 635 | // Mask/clear at peripheral and set default MATCH |
| 615 | let r: &Regs = unsafe { &*pac::Ostimer0::ptr() }; | 636 | let r: &Regs = unsafe { &*pac::Ostimer0::ptr() }; |
| 616 | super::prime_match_registers(r); | 637 | super::prime_match_registers(r); |
