// SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2025 Lennard Kittner //! CPU Frequency scaling. //! //! C header: [`include/linux/cpufreq.h`](srctree/include/linux/cpufreq.h) use core::marker::PhantomData; use crate::bindings; //TODO: policy also contains a cpufreq_governor // we probably have to register our own governor or find a governor that will do what we tell it to do. // We could also try to unregister the governor and see what happens without a governor /// Returns the current CPU frequency (in kHz) /// cpufreq_get wrapper pub fn get_current_frequency(cpu: u32) -> u32 { unsafe { bindings::cpufreq_get(cpu) } } /// Gets the CPU frequency (in kHz) from policy->cur /// This is the last known freq, without actually getting it from the driver. /// Return value will be same as what is shown in scaling_cur_freq in sysfs /// cpufreq_quick_get wrapper pub fn get_current_frequency_from_policy(cpu: u32) -> u32 { unsafe { bindings::cpufreq_quick_get(cpu) } } /// Relations between frequencies #[derive(Debug, Clone, Copy)] #[repr(u32)] pub enum FrequencyRelation { /// Lowest frequency at or above target Lowest = 0, /// Highest frequency below or at target Highest = 1, /// Closest frequency to target Closest = 2, /// Get if possible an efficient frequency /// Efficient = 1 << 2 /// Lowest + Efficient LowestEfficient = 0 | 1 << 2, /// Highest + Efficient HighestEfficient = 1 | 1 << 2, /// Closest + Efficient ClosestEfficient = 2 | 1 << 2, } /// cpufreq_cpuinfo wrapper #[repr(transparent)] pub struct CpufreqCpuinfo { inner: bindings::cpufreq_cpuinfo, } impl CpufreqCpuinfo { /// Creates CpufreqCpuinfo from raw cpufreq_cpuinfo pub fn new(info: bindings::cpufreq_cpuinfo) -> CpufreqCpuinfo { CpufreqCpuinfo { inner: info, } } /// The raw cpufreq_cpuinfo pub fn get_raw(&self) -> bindings::cpufreq_cpuinfo { self.inner } /// The maximum frequency in kHz pub fn get_max_frequency(&self) -> u32 { self.inner.max_freq } /// The minimum frequency in kHz pub fn get_min_frequency(&self) -> u32 { self.inner.min_freq } /// The transition latency in nanoseconds /// TODO: can I return Duration pub fn get_transition_latency(&self) -> u32 { self.inner.transition_latency } } /// Marker type for read only access pub struct ReadOnly; /// Marker type for read and write access pub struct ReadWrite; /// cpufreq_policy wrapper #[repr(transparent)] pub struct CpufreqPolicy { inner: *mut bindings::cpufreq_policy, _access_type: PhantomData } impl CpufreqPolicy { /// Returns the frequency policy of a specific CPU and mark it as busy. pub fn get_policy_from_cpu(cpu: u32) -> Option> { let ptr = unsafe { bindings::cpufreq_cpu_get(cpu) }; if ptr == core::ptr::null_mut() { None } else { Some(CpufreqPolicy { inner: ptr, _access_type: PhantomData, }) } } /// Puts this policy on the cpu it belongs to pub fn put_on_cpu(self) { // drop will call cpufreq_cpu_put } } impl CpufreqPolicy { /// Returns the frequency policy of a specific CPU and mark it as busy and lock it. pub fn acquire_policy_from_cpu(cpu: u32) -> Option> { let ptr = unsafe { bindings::cpufreq_cpu_acquire(cpu) }; if ptr == core::ptr::null_mut() { None } else { Some(CpufreqPolicy { inner: ptr, _access_type: PhantomData, }) } } /// Releases the lock on this policy and puts it on the cpu it belongs to pub fn release(self) { // drop will call cpufreq_cpu_release } /// Reevaluates the policy on the CPU pub fn update(&self) { let cpu = unsafe { *self.inner }.cpu; unsafe { bindings::cpufreq_update_policy(cpu) }; } /// Enable fast frequency switching for the policy. /// The attempt will fail if there is at least one transition notifier registered /// at this point, as fast frequency switching is quite fundamentally at odds /// with transition notifiers. Thus if successful, it will make registration of /// transition notifiers fail going forward. pub fn enable_fast_switching(&mut self) { unsafe { bindings::cpufreq_enable_fast_switch(self.as_ptr()) }; } /// Disables fast frequency switching for the policy. pub fn disable_fast_switch(&mut self) { unsafe { bindings::cpufreq_disable_fast_switch(self.as_ptr()) }; } /// Sets the maximum frequency of the policy pub fn set_max_frequency(&mut self, max: u32) { unsafe { *self.inner }.max = max; } /// Sets the minimum frequency of the policy pub fn set_min_frequency(&mut self, min: u32) { unsafe { *self.inner }.min = min; } /// Pass a target frequency to the cpufreq driver pub fn set_driver_target(&mut self, target_freq: u32, relation: FrequencyRelation) -> Result<(), i32> { let err = unsafe { bindings::cpufreq_driver_target(self.as_ptr(), target_freq, relation as u32) }; if err == 0 { Ok(()) } else { Err(err) } } } impl CpufreqPolicy { /// Returns a raw pointer to the inner C struct. #[inline] pub fn as_ptr(&self) -> *mut bindings::cpufreq_policy { self.inner } /// Whether the policy is inactive /// TODO: doc and inline static pub fn is_inactive(&self) -> bool { //bindings::policy_is_inactive(self.as_ptr()) todo!() } /// Whether the policy is shared /// TODO: doc and inline static pub fn is_shared(&self) -> bool { //bindings::policy_is_shared(self.as_ptr()) todo!() } /// Returns the cpu frequency info pub fn get_cpu_frequency_info(&self) -> CpufreqCpuinfo { CpufreqCpuinfo::new(unsafe {*self.inner}.cpuinfo) } /// Returns the minimum frequency of the policy in kHz pub fn get_min_frequency(&self) -> u32 { unsafe { *self.inner }.min } /// Returns the maximum frequency of the policy in kHz pub fn get_max_frequency(&self) -> u32 { unsafe { *self.inner }.max } /// Returns the current frequency targeted by the policy in kHz /// only needed if cpufreq governors are used pub fn get_current_frequency(&self) -> u32 { unsafe { *self.inner }.cur } } impl Drop for CpufreqPolicy { fn drop(&mut self) { if core::any::TypeId::of::() == core::any::TypeId::of::() { unsafe { bindings::cpufreq_cpu_put(self.as_ptr()) } } if core::any::TypeId::of::() == core::any::TypeId::of::() { unsafe { bindings::cpufreq_cpu_release(self.as_ptr()) }; } } }