File Index
Files and directories
Sign in
FILE

rcpufreq_dt.rs

File preview with syntax highlighting.

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