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