xref: /linux/drivers/cpufreq/rcpufreq_dt.rs (revision f688b599d711d169b22e99f2d055847d66c4e0d3)
106149d8fSViresh Kumar // SPDX-License-Identifier: GPL-2.0
206149d8fSViresh Kumar 
306149d8fSViresh Kumar //! Rust based implementation of the cpufreq-dt driver.
406149d8fSViresh Kumar 
506149d8fSViresh Kumar use kernel::{
606149d8fSViresh Kumar     c_str,
706149d8fSViresh Kumar     clk::Clk,
806149d8fSViresh Kumar     cpu, cpufreq,
906149d8fSViresh Kumar     cpumask::CpumaskVar,
1006149d8fSViresh Kumar     device::{Core, Device},
1106149d8fSViresh Kumar     error::code::*,
1206149d8fSViresh Kumar     fmt,
1306149d8fSViresh Kumar     macros::vtable,
1406149d8fSViresh Kumar     module_platform_driver, of, opp, platform,
1506149d8fSViresh Kumar     prelude::*,
1606149d8fSViresh Kumar     str::CString,
1706149d8fSViresh Kumar     sync::Arc,
1806149d8fSViresh Kumar };
1906149d8fSViresh Kumar 
2006149d8fSViresh Kumar /// Finds exact supply name from the OF node.
find_supply_name_exact(dev: &Device, name: &str) -> Option<CString>2106149d8fSViresh Kumar fn find_supply_name_exact(dev: &Device, name: &str) -> Option<CString> {
2206149d8fSViresh Kumar     let prop_name = CString::try_from_fmt(fmt!("{}-supply", name)).ok()?;
2306149d8fSViresh Kumar     dev.property_present(&prop_name)
2406149d8fSViresh Kumar         .then(|| CString::try_from_fmt(fmt!("{name}")).ok())
2506149d8fSViresh Kumar         .flatten()
2606149d8fSViresh Kumar }
2706149d8fSViresh Kumar 
2806149d8fSViresh Kumar /// Finds supply name for the CPU from DT.
find_supply_names(dev: &Device, cpu: cpu::CpuId) -> Option<KVec<CString>>29*33db8c97SViresh Kumar fn find_supply_names(dev: &Device, cpu: cpu::CpuId) -> Option<KVec<CString>> {
3006149d8fSViresh Kumar     // Try "cpu0" for older DTs, fallback to "cpu".
31*33db8c97SViresh Kumar     let name = (cpu.as_u32() == 0)
3206149d8fSViresh Kumar         .then(|| find_supply_name_exact(dev, "cpu0"))
3306149d8fSViresh Kumar         .flatten()
3406149d8fSViresh Kumar         .or_else(|| find_supply_name_exact(dev, "cpu"))?;
3506149d8fSViresh Kumar 
3606149d8fSViresh Kumar     let mut list = KVec::with_capacity(1, GFP_KERNEL).ok()?;
3706149d8fSViresh Kumar     list.push(name, GFP_KERNEL).ok()?;
3806149d8fSViresh Kumar 
3906149d8fSViresh Kumar     Some(list)
4006149d8fSViresh Kumar }
4106149d8fSViresh Kumar 
4206149d8fSViresh Kumar /// Represents the cpufreq dt device.
4306149d8fSViresh Kumar struct CPUFreqDTDevice {
4406149d8fSViresh Kumar     opp_table: opp::Table,
4506149d8fSViresh Kumar     freq_table: opp::FreqTable,
4606149d8fSViresh Kumar     _mask: CpumaskVar,
4706149d8fSViresh Kumar     _token: Option<opp::ConfigToken>,
4806149d8fSViresh Kumar     _clk: Clk,
4906149d8fSViresh Kumar }
5006149d8fSViresh Kumar 
5106149d8fSViresh Kumar #[derive(Default)]
5206149d8fSViresh Kumar struct CPUFreqDTDriver;
5306149d8fSViresh Kumar 
5406149d8fSViresh Kumar #[vtable]
5506149d8fSViresh Kumar impl opp::ConfigOps for CPUFreqDTDriver {}
5606149d8fSViresh Kumar 
5706149d8fSViresh Kumar #[vtable]
5806149d8fSViresh Kumar impl cpufreq::Driver for CPUFreqDTDriver {
5906149d8fSViresh Kumar     const NAME: &'static CStr = c_str!("cpufreq-dt");
6006149d8fSViresh Kumar     const FLAGS: u16 = cpufreq::flags::NEED_INITIAL_FREQ_CHECK | cpufreq::flags::IS_COOLING_DEV;
6106149d8fSViresh Kumar     const BOOST_ENABLED: bool = true;
6206149d8fSViresh Kumar 
6306149d8fSViresh Kumar     type PData = Arc<CPUFreqDTDevice>;
6406149d8fSViresh Kumar 
init(policy: &mut cpufreq::Policy) -> Result<Self::PData>6506149d8fSViresh Kumar     fn init(policy: &mut cpufreq::Policy) -> Result<Self::PData> {
6606149d8fSViresh Kumar         let cpu = policy.cpu();
6706149d8fSViresh Kumar         // SAFETY: The CPU device is only used during init; it won't get hot-unplugged. The cpufreq
6806149d8fSViresh Kumar         // core  registers with CPU notifiers and the cpufreq core/driver won't use the CPU device,
6906149d8fSViresh Kumar         // once the CPU is hot-unplugged.
7006149d8fSViresh Kumar         let dev = unsafe { cpu::from_cpu(cpu)? };
7106149d8fSViresh Kumar         let mut mask = CpumaskVar::new_zero(GFP_KERNEL)?;
7206149d8fSViresh Kumar 
7306149d8fSViresh Kumar         mask.set(cpu);
7406149d8fSViresh Kumar 
7506149d8fSViresh Kumar         let token = find_supply_names(dev, cpu)
7606149d8fSViresh Kumar             .map(|names| {
7706149d8fSViresh Kumar                 opp::Config::<Self>::new()
7806149d8fSViresh Kumar                     .set_regulator_names(names)?
7906149d8fSViresh Kumar                     .set(dev)
8006149d8fSViresh Kumar             })
8106149d8fSViresh Kumar             .transpose()?;
8206149d8fSViresh Kumar 
8306149d8fSViresh Kumar         // Get OPP-sharing information from "operating-points-v2" bindings.
8406149d8fSViresh Kumar         let fallback = match opp::Table::of_sharing_cpus(dev, &mut mask) {
8506149d8fSViresh Kumar             Ok(()) => false,
8606149d8fSViresh Kumar             Err(e) if e == ENOENT => {
8706149d8fSViresh Kumar                 // "operating-points-v2" not supported. If the platform hasn't
8806149d8fSViresh Kumar                 // set sharing CPUs, fallback to all CPUs share the `Policy`
8906149d8fSViresh Kumar                 // for backward compatibility.
9006149d8fSViresh Kumar                 opp::Table::sharing_cpus(dev, &mut mask).is_err()
9106149d8fSViresh Kumar             }
9206149d8fSViresh Kumar             Err(e) => return Err(e),
9306149d8fSViresh Kumar         };
9406149d8fSViresh Kumar 
9506149d8fSViresh Kumar         // Initialize OPP tables for all policy cpus.
9606149d8fSViresh Kumar         //
9706149d8fSViresh Kumar         // For platforms not using "operating-points-v2" bindings, we do this
9806149d8fSViresh Kumar         // before updating policy cpus. Otherwise, we will end up creating
9906149d8fSViresh Kumar         // duplicate OPPs for the CPUs.
10006149d8fSViresh Kumar         //
10106149d8fSViresh Kumar         // OPPs might be populated at runtime, don't fail for error here unless
10206149d8fSViresh Kumar         // it is -EPROBE_DEFER.
10306149d8fSViresh Kumar         let mut opp_table = match opp::Table::from_of_cpumask(dev, &mut mask) {
10406149d8fSViresh Kumar             Ok(table) => table,
10506149d8fSViresh Kumar             Err(e) => {
10606149d8fSViresh Kumar                 if e == EPROBE_DEFER {
10706149d8fSViresh Kumar                     return Err(e);
10806149d8fSViresh Kumar                 }
10906149d8fSViresh Kumar 
11006149d8fSViresh Kumar                 // The table is added dynamically ?
11106149d8fSViresh Kumar                 opp::Table::from_dev(dev)?
11206149d8fSViresh Kumar             }
11306149d8fSViresh Kumar         };
11406149d8fSViresh Kumar 
11506149d8fSViresh Kumar         // The OPP table must be initialized, statically or dynamically, by this point.
11606149d8fSViresh Kumar         opp_table.opp_count()?;
11706149d8fSViresh Kumar 
11806149d8fSViresh Kumar         // Set sharing cpus for fallback scenario.
11906149d8fSViresh Kumar         if fallback {
12006149d8fSViresh Kumar             mask.setall();
12106149d8fSViresh Kumar             opp_table.set_sharing_cpus(&mut mask)?;
12206149d8fSViresh Kumar         }
12306149d8fSViresh Kumar 
12406149d8fSViresh Kumar         let mut transition_latency = opp_table.max_transition_latency_ns() as u32;
12506149d8fSViresh Kumar         if transition_latency == 0 {
12606149d8fSViresh Kumar             transition_latency = cpufreq::ETERNAL_LATENCY_NS;
12706149d8fSViresh Kumar         }
12806149d8fSViresh Kumar 
12906149d8fSViresh Kumar         policy
13006149d8fSViresh Kumar             .set_dvfs_possible_from_any_cpu(true)
13106149d8fSViresh Kumar             .set_suspend_freq(opp_table.suspend_freq())
13206149d8fSViresh Kumar             .set_transition_latency_ns(transition_latency);
13306149d8fSViresh Kumar 
13406149d8fSViresh Kumar         let freq_table = opp_table.cpufreq_table()?;
13506149d8fSViresh Kumar         // SAFETY: The `freq_table` is not dropped while it is getting used by the C code.
13606149d8fSViresh Kumar         unsafe { policy.set_freq_table(&freq_table) };
13706149d8fSViresh Kumar 
13806149d8fSViresh Kumar         // SAFETY: The returned `clk` is not dropped while it is getting used by the C code.
13906149d8fSViresh Kumar         let clk = unsafe { policy.set_clk(dev, None)? };
14006149d8fSViresh Kumar 
14106149d8fSViresh Kumar         mask.copy(policy.cpus());
14206149d8fSViresh Kumar 
14306149d8fSViresh Kumar         Ok(Arc::new(
14406149d8fSViresh Kumar             CPUFreqDTDevice {
14506149d8fSViresh Kumar                 opp_table,
14606149d8fSViresh Kumar                 freq_table,
14706149d8fSViresh Kumar                 _mask: mask,
14806149d8fSViresh Kumar                 _token: token,
14906149d8fSViresh Kumar                 _clk: clk,
15006149d8fSViresh Kumar             },
15106149d8fSViresh Kumar             GFP_KERNEL,
15206149d8fSViresh Kumar         )?)
15306149d8fSViresh Kumar     }
15406149d8fSViresh Kumar 
exit(_policy: &mut cpufreq::Policy, _data: Option<Self::PData>) -> Result15506149d8fSViresh Kumar     fn exit(_policy: &mut cpufreq::Policy, _data: Option<Self::PData>) -> Result {
15606149d8fSViresh Kumar         Ok(())
15706149d8fSViresh Kumar     }
15806149d8fSViresh Kumar 
online(_policy: &mut cpufreq::Policy) -> Result15906149d8fSViresh Kumar     fn online(_policy: &mut cpufreq::Policy) -> Result {
16006149d8fSViresh Kumar         // We did light-weight tear down earlier, nothing to do here.
16106149d8fSViresh Kumar         Ok(())
16206149d8fSViresh Kumar     }
16306149d8fSViresh Kumar 
offline(_policy: &mut cpufreq::Policy) -> Result16406149d8fSViresh Kumar     fn offline(_policy: &mut cpufreq::Policy) -> Result {
16506149d8fSViresh Kumar         // Preserve policy->data and don't free resources on light-weight
16606149d8fSViresh Kumar         // tear down.
16706149d8fSViresh Kumar         Ok(())
16806149d8fSViresh Kumar     }
16906149d8fSViresh Kumar 
suspend(policy: &mut cpufreq::Policy) -> Result17006149d8fSViresh Kumar     fn suspend(policy: &mut cpufreq::Policy) -> Result {
17106149d8fSViresh Kumar         policy.generic_suspend()
17206149d8fSViresh Kumar     }
17306149d8fSViresh Kumar 
verify(data: &mut cpufreq::PolicyData) -> Result17406149d8fSViresh Kumar     fn verify(data: &mut cpufreq::PolicyData) -> Result {
17506149d8fSViresh Kumar         data.generic_verify()
17606149d8fSViresh Kumar     }
17706149d8fSViresh Kumar 
target_index(policy: &mut cpufreq::Policy, index: cpufreq::TableIndex) -> Result17806149d8fSViresh Kumar     fn target_index(policy: &mut cpufreq::Policy, index: cpufreq::TableIndex) -> Result {
17906149d8fSViresh Kumar         let Some(data) = policy.data::<Self::PData>() else {
18006149d8fSViresh Kumar             return Err(ENOENT);
18106149d8fSViresh Kumar         };
18206149d8fSViresh Kumar 
18306149d8fSViresh Kumar         let freq = data.freq_table.freq(index)?;
18406149d8fSViresh Kumar         data.opp_table.set_rate(freq)
18506149d8fSViresh Kumar     }
18606149d8fSViresh Kumar 
get(policy: &mut cpufreq::Policy) -> Result<u32>18706149d8fSViresh Kumar     fn get(policy: &mut cpufreq::Policy) -> Result<u32> {
18806149d8fSViresh Kumar         policy.generic_get()
18906149d8fSViresh Kumar     }
19006149d8fSViresh Kumar 
set_boost(_policy: &mut cpufreq::Policy, _state: i32) -> Result19106149d8fSViresh Kumar     fn set_boost(_policy: &mut cpufreq::Policy, _state: i32) -> Result {
19206149d8fSViresh Kumar         Ok(())
19306149d8fSViresh Kumar     }
19406149d8fSViresh Kumar 
register_em(policy: &mut cpufreq::Policy)19506149d8fSViresh Kumar     fn register_em(policy: &mut cpufreq::Policy) {
19606149d8fSViresh Kumar         policy.register_em_opp()
19706149d8fSViresh Kumar     }
19806149d8fSViresh Kumar }
19906149d8fSViresh Kumar 
20006149d8fSViresh Kumar kernel::of_device_table!(
20106149d8fSViresh Kumar     OF_TABLE,
20206149d8fSViresh Kumar     MODULE_OF_TABLE,
20306149d8fSViresh Kumar     <CPUFreqDTDriver as platform::Driver>::IdInfo,
20406149d8fSViresh Kumar     [(of::DeviceId::new(c_str!("operating-points-v2")), ())]
20506149d8fSViresh Kumar );
20606149d8fSViresh Kumar 
20706149d8fSViresh Kumar impl platform::Driver for CPUFreqDTDriver {
20806149d8fSViresh Kumar     type IdInfo = ();
20906149d8fSViresh Kumar     const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
21006149d8fSViresh Kumar 
probe( pdev: &platform::Device<Core>, _id_info: Option<&Self::IdInfo>, ) -> Result<Pin<KBox<Self>>>21106149d8fSViresh Kumar     fn probe(
21206149d8fSViresh Kumar         pdev: &platform::Device<Core>,
21306149d8fSViresh Kumar         _id_info: Option<&Self::IdInfo>,
21406149d8fSViresh Kumar     ) -> Result<Pin<KBox<Self>>> {
21506149d8fSViresh Kumar         cpufreq::Registration::<CPUFreqDTDriver>::new_foreign_owned(pdev.as_ref())?;
21606149d8fSViresh Kumar         Ok(KBox::new(Self {}, GFP_KERNEL)?.into())
21706149d8fSViresh Kumar     }
21806149d8fSViresh Kumar }
21906149d8fSViresh Kumar 
22006149d8fSViresh Kumar module_platform_driver! {
22106149d8fSViresh Kumar     type: CPUFreqDTDriver,
22206149d8fSViresh Kumar     name: "cpufreq-dt",
22306149d8fSViresh Kumar     author: "Viresh Kumar <viresh.kumar@linaro.org>",
22406149d8fSViresh Kumar     description: "Generic CPUFreq DT driver",
22506149d8fSViresh Kumar     license: "GPL v2",
22606149d8fSViresh Kumar }
227