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