xref: /linux/drivers/cpufreq/rcpufreq_dt.rs (revision f688b599d711d169b22e99f2d055847d66c4e0d3)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 //! Rust based implementation of the cpufreq-dt driver.
4 
5 use kernel::{
6     c_str,
7     clk::Clk,
8     cpu, cpufreq,
9     cpumask::CpumaskVar,
10     device::{Core, Device},
11     error::code::*,
12     fmt,
13     macros::vtable,
14     module_platform_driver, of, opp, platform,
15     prelude::*,
16     str::CString,
17     sync::Arc,
18 };
19 
20 /// Finds exact supply name from the OF node.
find_supply_name_exact(dev: &Device, name: &str) -> Option<CString>21 fn find_supply_name_exact(dev: &Device, name: &str) -> Option<CString> {
22     let prop_name = CString::try_from_fmt(fmt!("{}-supply", name)).ok()?;
23     dev.property_present(&prop_name)
24         .then(|| CString::try_from_fmt(fmt!("{name}")).ok())
25         .flatten()
26 }
27 
28 /// Finds supply name for the CPU from DT.
find_supply_names(dev: &Device, cpu: cpu::CpuId) -> Option<KVec<CString>>29 fn find_supply_names(dev: &Device, cpu: cpu::CpuId) -> Option<KVec<CString>> {
30     // Try "cpu0" for older DTs, fallback to "cpu".
31     let name = (cpu.as_u32() == 0)
32         .then(|| find_supply_name_exact(dev, "cpu0"))
33         .flatten()
34         .or_else(|| find_supply_name_exact(dev, "cpu"))?;
35 
36     let mut list = KVec::with_capacity(1, GFP_KERNEL).ok()?;
37     list.push(name, GFP_KERNEL).ok()?;
38 
39     Some(list)
40 }
41 
42 /// Represents the cpufreq dt device.
43 struct CPUFreqDTDevice {
44     opp_table: opp::Table,
45     freq_table: opp::FreqTable,
46     _mask: CpumaskVar,
47     _token: Option<opp::ConfigToken>,
48     _clk: Clk,
49 }
50 
51 #[derive(Default)]
52 struct CPUFreqDTDriver;
53 
54 #[vtable]
55 impl opp::ConfigOps for CPUFreqDTDriver {}
56 
57 #[vtable]
58 impl cpufreq::Driver for CPUFreqDTDriver {
59     const NAME: &'static CStr = c_str!("cpufreq-dt");
60     const FLAGS: u16 = cpufreq::flags::NEED_INITIAL_FREQ_CHECK | cpufreq::flags::IS_COOLING_DEV;
61     const BOOST_ENABLED: bool = true;
62 
63     type PData = Arc<CPUFreqDTDevice>;
64 
init(policy: &mut cpufreq::Policy) -> Result<Self::PData>65     fn init(policy: &mut cpufreq::Policy) -> Result<Self::PData> {
66         let cpu = policy.cpu();
67         // SAFETY: The CPU device is only used during init; it won't get hot-unplugged. The cpufreq
68         // core  registers with CPU notifiers and the cpufreq core/driver won't use the CPU device,
69         // once the CPU is hot-unplugged.
70         let dev = unsafe { cpu::from_cpu(cpu)? };
71         let mut mask = CpumaskVar::new_zero(GFP_KERNEL)?;
72 
73         mask.set(cpu);
74 
75         let token = find_supply_names(dev, cpu)
76             .map(|names| {
77                 opp::Config::<Self>::new()
78                     .set_regulator_names(names)?
79                     .set(dev)
80             })
81             .transpose()?;
82 
83         // Get OPP-sharing information from "operating-points-v2" bindings.
84         let fallback = match opp::Table::of_sharing_cpus(dev, &mut mask) {
85             Ok(()) => false,
86             Err(e) if e == ENOENT => {
87                 // "operating-points-v2" not supported. If the platform hasn't
88                 // set sharing CPUs, fallback to all CPUs share the `Policy`
89                 // for backward compatibility.
90                 opp::Table::sharing_cpus(dev, &mut mask).is_err()
91             }
92             Err(e) => return Err(e),
93         };
94 
95         // Initialize OPP tables for all policy cpus.
96         //
97         // For platforms not using "operating-points-v2" bindings, we do this
98         // before updating policy cpus. Otherwise, we will end up creating
99         // duplicate OPPs for the CPUs.
100         //
101         // OPPs might be populated at runtime, don't fail for error here unless
102         // it is -EPROBE_DEFER.
103         let mut opp_table = match opp::Table::from_of_cpumask(dev, &mut mask) {
104             Ok(table) => table,
105             Err(e) => {
106                 if e == EPROBE_DEFER {
107                     return Err(e);
108                 }
109 
110                 // The table is added dynamically ?
111                 opp::Table::from_dev(dev)?
112             }
113         };
114 
115         // The OPP table must be initialized, statically or dynamically, by this point.
116         opp_table.opp_count()?;
117 
118         // Set sharing cpus for fallback scenario.
119         if fallback {
120             mask.setall();
121             opp_table.set_sharing_cpus(&mut mask)?;
122         }
123 
124         let mut transition_latency = opp_table.max_transition_latency_ns() as u32;
125         if transition_latency == 0 {
126             transition_latency = cpufreq::ETERNAL_LATENCY_NS;
127         }
128 
129         policy
130             .set_dvfs_possible_from_any_cpu(true)
131             .set_suspend_freq(opp_table.suspend_freq())
132             .set_transition_latency_ns(transition_latency);
133 
134         let freq_table = opp_table.cpufreq_table()?;
135         // SAFETY: The `freq_table` is not dropped while it is getting used by the C code.
136         unsafe { policy.set_freq_table(&freq_table) };
137 
138         // SAFETY: The returned `clk` is not dropped while it is getting used by the C code.
139         let clk = unsafe { policy.set_clk(dev, None)? };
140 
141         mask.copy(policy.cpus());
142 
143         Ok(Arc::new(
144             CPUFreqDTDevice {
145                 opp_table,
146                 freq_table,
147                 _mask: mask,
148                 _token: token,
149                 _clk: clk,
150             },
151             GFP_KERNEL,
152         )?)
153     }
154 
exit(_policy: &mut cpufreq::Policy, _data: Option<Self::PData>) -> Result155     fn exit(_policy: &mut cpufreq::Policy, _data: Option<Self::PData>) -> Result {
156         Ok(())
157     }
158 
online(_policy: &mut cpufreq::Policy) -> Result159     fn online(_policy: &mut cpufreq::Policy) -> Result {
160         // We did light-weight tear down earlier, nothing to do here.
161         Ok(())
162     }
163 
offline(_policy: &mut cpufreq::Policy) -> Result164     fn offline(_policy: &mut cpufreq::Policy) -> Result {
165         // Preserve policy->data and don't free resources on light-weight
166         // tear down.
167         Ok(())
168     }
169 
suspend(policy: &mut cpufreq::Policy) -> Result170     fn suspend(policy: &mut cpufreq::Policy) -> Result {
171         policy.generic_suspend()
172     }
173 
verify(data: &mut cpufreq::PolicyData) -> Result174     fn verify(data: &mut cpufreq::PolicyData) -> Result {
175         data.generic_verify()
176     }
177 
target_index(policy: &mut cpufreq::Policy, index: cpufreq::TableIndex) -> Result178     fn target_index(policy: &mut cpufreq::Policy, index: cpufreq::TableIndex) -> Result {
179         let Some(data) = policy.data::<Self::PData>() else {
180             return Err(ENOENT);
181         };
182 
183         let freq = data.freq_table.freq(index)?;
184         data.opp_table.set_rate(freq)
185     }
186 
get(policy: &mut cpufreq::Policy) -> Result<u32>187     fn get(policy: &mut cpufreq::Policy) -> Result<u32> {
188         policy.generic_get()
189     }
190 
set_boost(_policy: &mut cpufreq::Policy, _state: i32) -> Result191     fn set_boost(_policy: &mut cpufreq::Policy, _state: i32) -> Result {
192         Ok(())
193     }
194 
register_em(policy: &mut cpufreq::Policy)195     fn register_em(policy: &mut cpufreq::Policy) {
196         policy.register_em_opp()
197     }
198 }
199 
200 kernel::of_device_table!(
201     OF_TABLE,
202     MODULE_OF_TABLE,
203     <CPUFreqDTDriver as platform::Driver>::IdInfo,
204     [(of::DeviceId::new(c_str!("operating-points-v2")), ())]
205 );
206 
207 impl platform::Driver for CPUFreqDTDriver {
208     type IdInfo = ();
209     const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
210 
probe( pdev: &platform::Device<Core>, _id_info: Option<&Self::IdInfo>, ) -> Result<Pin<KBox<Self>>>211     fn probe(
212         pdev: &platform::Device<Core>,
213         _id_info: Option<&Self::IdInfo>,
214     ) -> Result<Pin<KBox<Self>>> {
215         cpufreq::Registration::<CPUFreqDTDriver>::new_foreign_owned(pdev.as_ref())?;
216         Ok(KBox::new(Self {}, GFP_KERNEL)?.into())
217     }
218 }
219 
220 module_platform_driver! {
221     type: CPUFreqDTDriver,
222     name: "cpufreq-dt",
223     author: "Viresh Kumar <viresh.kumar@linaro.org>",
224     description: "Generic CPUFreq DT driver",
225     license: "GPL v2",
226 }
227