From e0b6290a542d07259afe884f16a1cd5a93bd8a20 Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Sun, 27 Jul 2025 13:37:39 +0300 Subject: [PATCH] clock: implement Hertz type --- kernel/lib/device-api/src/clock/freq.rs | 86 +++++++++++++++++++ .../device-api/src/{clock.rs => clock/mod.rs} | 12 +-- kernel/src/arch/riscv64/mod.rs | 3 +- kernel/src/device/clock/fixed.rs | 10 +-- kernel/src/device/clock/jh7110_clocks.rs | 14 +-- kernel/src/device/serial/snps_dw_apb_uart.rs | 6 +- 6 files changed, 110 insertions(+), 21 deletions(-) create mode 100644 kernel/lib/device-api/src/clock/freq.rs rename kernel/lib/device-api/src/{clock.rs => clock/mod.rs} (83%) diff --git a/kernel/lib/device-api/src/clock/freq.rs b/kernel/lib/device-api/src/clock/freq.rs new file mode 100644 index 00000000..0dfa1e00 --- /dev/null +++ b/kernel/lib/device-api/src/clock/freq.rs @@ -0,0 +1,86 @@ +use core::{ + fmt, + ops::{Div, Mul}, +}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Hertz(pub u64); + +const KILO: u64 = 1_000; +const MEGA: u64 = 1_000_000; +const GIGA: u64 = 1_000_000_000; + +pub trait IntoHertz { + fn hz(&self) -> Hertz; + #[inline] + fn khz(&self) -> Hertz { + self.hz() * KILO + } + #[inline] + fn mhz(&self) -> Hertz { + self.hz() * MEGA + } + #[inline] + fn ghz(&self) -> Hertz { + self.hz() * GIGA + } +} + +impl Mul for Hertz { + type Output = Hertz; + + fn mul(self, rhs: u32) -> Self::Output { + Self(self.0 * rhs as u64) + } +} + +impl Mul for Hertz { + type Output = Hertz; + + fn mul(self, rhs: u64) -> Self::Output { + Self(self.0 * rhs) + } +} + +impl Div for Hertz { + type Output = Hertz; + + fn div(self, rhs: u32) -> Self::Output { + Self(self.0 / rhs as u64) + } +} + +impl fmt::Display for Hertz { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let (scale, suffix) = match self.0 { + x if x >= GIGA => (GIGA, "GHz"), + x if x >= MEGA => (MEGA, "MHz"), + x if x >= KILO => (KILO, "kHz"), + x => return write!(f, "{x}Hz"), + }; + + let int = self.0 / scale; + let frac = (self.0 / (scale / 1000)) % 1000; + + if frac == 0 { + write!(f, "{int}{suffix}") + } else { + write!(f, "{int}.{frac:03}{suffix}") + } + } +} + +macro_rules! impl_into_hertz { + ($($ty:ty),+ $(,)?) => { + $( + impl IntoHertz for $ty { + #[inline(always)] + fn hz(&self) -> Hertz { + Hertz(*self as u64) + } + } + )+ + }; +} + +impl_into_hertz!(u8, u16, u32, u64); diff --git a/kernel/lib/device-api/src/clock.rs b/kernel/lib/device-api/src/clock/mod.rs similarity index 83% rename from kernel/lib/device-api/src/clock.rs rename to kernel/lib/device-api/src/clock/mod.rs index 081199e6..7235f249 100644 --- a/kernel/lib/device-api/src/clock.rs +++ b/kernel/lib/device-api/src/clock/mod.rs @@ -3,7 +3,9 @@ use yggdrasil_abi::error::Error; use crate::device::Device; -// TODO refine `clock` parameter types somehow +pub use freq::{Hertz, IntoHertz}; + +mod freq; pub struct ClockHandle { pub parent: Arc, @@ -19,11 +21,11 @@ pub trait ClockController: Device { fn enable_clock(&self, clock: Option) -> Result<(), Error>; fn disable_clock(&self, clock: Option) -> Result<(), Error>; - fn clock_rate(&self, clock: Option) -> Result { + fn clock_rate(&self, clock: Option) -> Result { let _ = clock; Err(Error::NotImplemented) } - fn set_clock_rate(&self, clock: Option, rate: u64) -> Result { + fn set_clock_rate(&self, clock: Option, rate: Hertz) -> Result { let _ = clock; let _ = rate; Err(Error::NotImplemented) @@ -44,11 +46,11 @@ impl ClockHandle { self.parent.disable_clock(self.clock) } - pub fn rate(&self) -> Result { + pub fn rate(&self) -> Result { self.parent.clock_rate(self.clock) } - pub fn set_rate(&self, rate: u64) -> Result { + pub fn set_rate(&self, rate: Hertz) -> Result { self.parent.set_clock_rate(self.clock, rate) } } diff --git a/kernel/src/arch/riscv64/mod.rs b/kernel/src/arch/riscv64/mod.rs index a97426a4..311413dc 100644 --- a/kernel/src/arch/riscv64/mod.rs +++ b/kernel/src/arch/riscv64/mod.rs @@ -2,6 +2,7 @@ use core::sync::atomic::{self, Ordering}; use abi::error::Error; +use device_api::clock::Hertz; use device_tree::{ driver::{unflatten_device_tree, InitSequence}, DeviceTree, DeviceTreeNodeExt, @@ -249,7 +250,7 @@ impl Riscv64 { .prop_cell("timebase-frequency", 1) .ok_or(Error::DoesNotExist)?; timer::FREQUENCY.store(timebase_frequency, Ordering::Release); - log::info!("System timer frequency: {timebase_frequency}"); + log::info!("System timer frequency: {}", Hertz(timebase_frequency)); Ok(()) } diff --git a/kernel/src/device/clock/fixed.rs b/kernel/src/device/clock/fixed.rs index d9203eda..d6960dd6 100644 --- a/kernel/src/device/clock/fixed.rs +++ b/kernel/src/device/clock/fixed.rs @@ -2,7 +2,7 @@ use abi::error::Error; use alloc::sync::Arc; use device_api::{ - clock::{ClockController, ClockHandle}, + clock::{ClockController, ClockHandle, Hertz}, device::{Device, DeviceInitContext}, }; use device_tree::{ @@ -12,7 +12,7 @@ use device_tree::{ struct FixedClock { name: &'static str, - frequency: u64, + frequency: Hertz, } impl Device for FixedClock { @@ -26,12 +26,12 @@ impl Device for FixedClock { } impl ClockController for FixedClock { - fn clock_rate(&self, clock: Option) -> Result { + fn clock_rate(&self, clock: Option) -> Result { debug_assert!(clock.is_none()); Ok(self.frequency) } - fn set_clock_rate(&self, _clock: Option, _rate: u64) -> Result { + fn set_clock_rate(&self, _clock: Option, _rate: Hertz) -> Result { Err(Error::InvalidOperation) } @@ -66,7 +66,7 @@ device_tree_driver! { driver: { fn probe(&self, node: &Arc, _context: &mut ProbeContext) -> Option> { let name = node.name()?; - let frequency = node.property("clock-frequency")?.read_cell(0, 1)?; + let frequency = Hertz(node.property("clock-frequency")?.read_cell(0, 1)?); log::info!("fixed-clock {name}: {frequency}"); let fixed = Arc::new(FixedClock { name, frequency }); node.make_clock_controller(fixed.clone()); diff --git a/kernel/src/device/clock/jh7110_clocks.rs b/kernel/src/device/clock/jh7110_clocks.rs index bd0e9889..d0cd09a8 100644 --- a/kernel/src/device/clock/jh7110_clocks.rs +++ b/kernel/src/device/clock/jh7110_clocks.rs @@ -5,7 +5,7 @@ use core::{marker::PhantomData, ops::Index}; use abi::error::Error; use alloc::sync::Arc; use device_api::{ - clock::{ClockController, ClockHandle, ResetController, ResetHandle}, + clock::{ClockController, ClockHandle, Hertz, ResetController, ResetHandle}, device::Device, }; use device_tree::{ @@ -285,7 +285,7 @@ impl> Crg { Ok(()) } - fn clock_rate_inner(&self, index: usize) -> Result { + fn clock_rate_inner(&self, index: usize) -> Result { if index >= C::CLOCKS.len() { return self.parents[index - C::CLOCKS.len()].rate(); } @@ -303,7 +303,7 @@ impl> Crg { let parent_rate = self.clock_rate_inner(parent)?; - Ok(parent_rate / div as u64) + Ok(parent_rate / div) } fn set_reset_asserted_inner(&self, index: usize, asserted: bool) -> Result<(), Error> { @@ -332,12 +332,12 @@ impl Device for Crg { } impl> ClockController for Crg { - fn clock_rate(&self, clock: Option) -> Result { + fn clock_rate(&self, clock: Option) -> Result { let index = clock.ok_or(Error::InvalidArgument)? as usize; self.clock_rate_inner(index) } - fn set_clock_rate(&self, _clock: Option, _rate: u64) -> Result { + fn set_clock_rate(&self, _clock: Option, _rate: Hertz) -> Result { todo!("Clock rate update not supported for jh71x0 CRGs") } @@ -400,11 +400,11 @@ impl Device for Pll { } impl ClockController for Pll { - fn set_clock_rate(&self, _clock: Option, _rate: u64) -> Result { + fn set_clock_rate(&self, _clock: Option, _rate: Hertz) -> Result { todo!("PLL rate configuration not yet implemented") } - fn clock_rate(&self, _clock: Option) -> Result { + fn clock_rate(&self, _clock: Option) -> Result { todo!("PLL rate query not yet implemented") } diff --git a/kernel/src/device/serial/snps_dw_apb_uart.rs b/kernel/src/device/serial/snps_dw_apb_uart.rs index ea889916..b1f5ba4b 100644 --- a/kernel/src/device/serial/snps_dw_apb_uart.rs +++ b/kernel/src/device/serial/snps_dw_apb_uart.rs @@ -2,7 +2,7 @@ use abi::{error::Error, io::TerminalOptions}; use alloc::sync::Arc; use device_api::{ - clock::{ClockHandle, ResetHandle}, + clock::{ClockHandle, Hertz, ResetHandle}, device::{Device, DeviceInitContext}, interrupt::{FullIrq, InterruptHandler, IrqVector}, }; @@ -132,8 +132,8 @@ impl Io { self.regs.DR.set(byte as u32); } - fn init(&mut self, baud_clock: u64, baud_rate: u64) { - let divisor = (baud_clock / (baud_rate * 16)) as u32; + fn init(&mut self, baud_clock: Hertz, baud_rate: u64) { + let divisor = (baud_clock.0 / (baud_rate * 16)) as u32; self.wait_busy(); self.regs.IER.set(0);