summaryrefslogtreecommitdiff
path: root/rust/kernel/cpufreq.rs
blob: 233ae8a7f2c34dfa7b638502d46d805b4c854b9b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2025 Lennard Kittner <lennard@kittner.dev>

//! 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<AccessType: 'static> {
    inner: *mut bindings::cpufreq_policy,
    _access_type: PhantomData<AccessType>
}

impl CpufreqPolicy<ReadOnly> {
    /// Returns the frequency policy of a specific CPU and mark it as busy.
    pub fn get_policy_from_cpu(cpu: u32) -> Option<CpufreqPolicy<ReadOnly
>> {
        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<ReadWrite> {
   /// 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<CpufreqPolicy<ReadWrite>> {
        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<AccessType> CpufreqPolicy<AccessType> {
    /// 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<AccessType: 'static> Drop for CpufreqPolicy<AccessType> {
    fn drop(&mut self) {
        if core::any::TypeId::of::<AccessType>() == core::any::TypeId::of::<ReadOnly>() {
            unsafe { bindings::cpufreq_cpu_put(self.as_ptr()) }
        }
        if core::any::TypeId::of::<AccessType>() == core::any::TypeId::of::<ReadWrite>() {
            unsafe { bindings::cpufreq_cpu_release(self.as_ptr()) };
        }
    }
}