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