From nobody Sun Dec 14 21:51:19 2025 Received: from mail-pl1-f181.google.com (mail-pl1-f181.google.com [209.85.214.181]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A472A22A4FF for ; Mon, 13 Jan 2025 11:24:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.181 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1736767473; cv=none; b=ACcClzXFggl9jlRgojxNy5fIoCjSaDT9PPmvS29JBjyvZf/2d1jqynPhatVwGxDT18J2D4dqJjXre+OOCHCfPtUK1fSJ+DdstBwSGJSITjcIgnDGOXJ7Uw8eS6+U279j8jp+0MOCURKyDNyAxBDjnejitaY11iys7Obh6VVEZ7k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1736767473; c=relaxed/simple; bh=4fg2ZGQhlYA8r3JLieZC4cnAZfBC3Xm4Z/gakVH89vk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=NBpqMraiDuIgMYeEEOg6foCmsSgjJsD63/kjYawvMyJmP3qGt3039Wov6WmJAlBCV8GLd1UaGr5KLwTOty35E/fjXxoZ+hJ1DKY8VIU+SEMX7ppdrl9+69Ta0IQgbwViKw9lIBG/swizeNOgTurSYYTg2SnTd2RCPBJ2zD+RArk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org; spf=pass smtp.mailfrom=linaro.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b=CE3lnLRq; arc=none smtp.client-ip=209.85.214.181 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linaro.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=linaro.org header.i=@linaro.org header.b="CE3lnLRq" Received: by mail-pl1-f181.google.com with SMTP id d9443c01a7336-216426b0865so70469125ad.0 for ; Mon, 13 Jan 2025 03:24:31 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1736767471; x=1737372271; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=ze1PBk/48GQ6DBzWf8nXWZfuqwWl+q48VfNbS3eSzOI=; b=CE3lnLRqHs9Eb9X7xJp74egoqoGopK8sPcTTd7rFl2wDNZ4L3In1wNsVcFmCq0mg+c /SBbpkRbcwJe6mvdIFU5CbmcSRlCO+gXM0WqQTh3dvMR9Ac7YTfG5xkUhv+A7h/YAGpX 3imJ3IdQq02ByE0CRScv6Y2+WisaGXQ1zWnHaVVQi6dSmu2VZhll3a8MNxN0SpRtQ0jc 4UC0yab+n9sFdK9zFAAx2ZnceFsDtun+Xcbb7lwmV8oGvcyITYjqKfmcjk6Z2XaMILYN Q5otramEYCNTpELIfiI+RlnS5iWCuZ/pVVnxH2bS8VvYHE5i2juE9HLZrJG4bbx/BFnO N9WA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1736767471; x=1737372271; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=ze1PBk/48GQ6DBzWf8nXWZfuqwWl+q48VfNbS3eSzOI=; b=kO1E2io3Si9uN/Cgt8SGGbF/wpPgR1GR3wns+U3mQa+u41nENGlSaVIlGev4vFBIe0 j1PCxjFKfNWhzJjxX9uwSzST3aLNEHp3VyiC4C3fAks1kitKrFwFpyI3fq3flOQTJPpa 1tUgeI2z4dU1/MG+1stjE51ZWF0wcsVLEZwI4wXAGd3rJMwnm3/3WJRFMPXREJa1zUcu 0iKtn2kkEefSOskhOnIAD63hHrhihWR4MBwGhzjssi8b+TpUx1rsu8GU79DTTJh5+Mmu Jt2Vlx38RKRx6QzIOFj8VYItQ6NtN5W1SbEFueTPV7SutcjK9+/dNuV5IA/R0OuaBV38 KWxQ== X-Forwarded-Encrypted: i=1; AJvYcCWmHr/t1MnS+gi/qkluIMrxoLECQVeaCroQNsWwKNCb+D6ayI9ncRGJmo39k4CaA8AWw6yEhW66ckqNPOk=@vger.kernel.org X-Gm-Message-State: AOJu0YyWJ2dowGwUSg3PC6S7XIyXZR3yW0gVz05W+Xyv/dV1qoenj+dE HytBc9n3xIn1QL8geRGapOtv2sV2jRiph6Nv23Jdi5S4azgWzCyU0E8Nesywil0= X-Gm-Gg: ASbGnctgpl3Qw7xv2fjSgw/7HDALRtgh1RDsaeSPeA5VfJ/JjJrFEQdfrwL47fSNqPI K1iXOFIIbNALCpnAs9NZPPj2+s6cwdPAkcR/VRStxaDk+qHXbQ1A94WL+42SP+QAPvxlwVsZ+02 BnLtH3HgK6bK2kNA0trH/5hHnEBeXD8COqX6eJjxceErctVPWayhQT4bmUbOB6g7huz6gClbvvy Nc0COMAIPo2smusAKS3R9iSxa5HJEQO6e1Bw+tgu6vEz1AX93sPTBl4BY4= X-Google-Smtp-Source: AGHT+IGGqOnEWmNDriR/HLmBnXwApY2rUgwBNSNC9XvMqqfb9u5C7/4ouaI8c61RrPI+L3Fo3BBqXQ== X-Received: by 2002:a17:902:cf0b:b0:215:431f:268a with SMTP id d9443c01a7336-21a83f647e1mr336644375ad.31.1736767471041; Mon, 13 Jan 2025 03:24:31 -0800 (PST) Received: from localhost ([122.172.84.139]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-21a9f155e00sm51657985ad.103.2025.01.13.03.24.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 13 Jan 2025 03:24:30 -0800 (PST) From: Viresh Kumar To: "Rafael J. Wysocki" , Viresh Kumar , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross Cc: linux-pm@vger.kernel.org, Vincent Guittot , linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org Subject: [PATCH V7 15/16] cpufreq: Add Rust based cpufreq-dt driver Date: Mon, 13 Jan 2025 16:53:10 +0530 Message-Id: <556e67bb5c5954d7f513716fb1bae32d55918742.1736766672.git.viresh.kumar@linaro.org> X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" This commit adds a Rust based cpufreq-dt driver, which covers most of the functionality of the existing C based driver. Only a handful of things are left, like fetching platform data from cpufreq-dt-platdev.c. This is tested with the help of QEMU for now and switching of frequencies work as expected. Signed-off-by: Viresh Kumar --- drivers/cpufreq/Kconfig | 12 ++ drivers/cpufreq/Makefile | 1 + drivers/cpufreq/rcpufreq_dt.rs | 230 +++++++++++++++++++++++++++++++++ 3 files changed, 243 insertions(+) create mode 100644 drivers/cpufreq/rcpufreq_dt.rs diff --git a/drivers/cpufreq/Kconfig b/drivers/cpufreq/Kconfig index d64b07ec48e5..78702a08364f 100644 --- a/drivers/cpufreq/Kconfig +++ b/drivers/cpufreq/Kconfig @@ -217,6 +217,18 @@ config CPUFREQ_DT =20 If in doubt, say N. =20 +config CPUFREQ_DT_RUST + tristate "Rust based Generic DT based cpufreq driver" + depends on HAVE_CLK && OF && RUST + select CPUFREQ_DT_PLATDEV + select PM_OPP + help + This adds a Rust based generic DT based cpufreq driver for frequency + management. It supports both uniprocessor (UP) and symmetric + multiprocessor (SMP) systems. + + If in doubt, say N. + config CPUFREQ_VIRT tristate "Virtual cpufreq driver" depends on GENERIC_ARCH_TOPOLOGY diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index d35a28dd9463..db38d1d5562d 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_COMMON) +=3D cpufreq_governor.o obj-$(CONFIG_CPU_FREQ_GOV_ATTR_SET) +=3D cpufreq_governor_attr_set.o =20 obj-$(CONFIG_CPUFREQ_DT) +=3D cpufreq-dt.o +obj-$(CONFIG_CPUFREQ_DT_RUST) +=3D rcpufreq_dt.o obj-$(CONFIG_CPUFREQ_DT_PLATDEV) +=3D cpufreq-dt-platdev.o obj-$(CONFIG_CPUFREQ_VIRT) +=3D virtual-cpufreq.o =20 diff --git a/drivers/cpufreq/rcpufreq_dt.rs b/drivers/cpufreq/rcpufreq_dt.rs new file mode 100644 index 000000000000..9f761082fa14 --- /dev/null +++ b/drivers/cpufreq/rcpufreq_dt.rs @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Rust based implementation of the cpufreq-dt driver. + +use core::format_args; + +use kernel::{ + c_str, clk, cpu, cpufreq, cpumask::Cpumask, device::Device, + error::code::*, fmt, macros::vtable, module_platform_driver, of, opp, = platform, prelude::*, + str::CString, sync::Arc, +}; + +// Finds exact supply name from the OF node. +fn find_supply_name_exact(dev: &Device, name: &str) -> Option { + let name_cstr =3D CString::try_from_fmt(fmt!("{}-supply", name)).ok()?; + + if dev.property_present(&name_cstr) { + CString::try_from_fmt(fmt!("{}", name)).ok() + } else { + None + } +} + +// Finds supply name for the CPU from DT. +fn find_supply_names(dev: &Device, cpu: u32) -> Option> { + // Try "cpu0" for older DTs. + let name =3D match cpu { + 0 =3D> find_supply_name_exact(dev, "cpu0"), + _ =3D> None, + } + .or(find_supply_name_exact(dev, "cpu"))?; + + let mut list =3D KVec::with_capacity(1, GFP_KERNEL).ok()?; + list.push(name, GFP_KERNEL).ok()?; + + Some(list) +} + +// Represents the cpufreq dt device. +struct CPUFreqDTDevice { + opp_table: opp::Table, + freq_table: opp::FreqTable, + #[allow(dead_code)] + mask: Cpumask, + #[allow(dead_code)] + token: Option, + #[allow(dead_code)] + clk: clk::Clk, +} + +struct CPUFreqDTDriver { + _pdev: platform::Device, +} + +#[vtable] +impl opp::ConfigOps for CPUFreqDTDriver {} + +#[vtable] +impl cpufreq::Driver for CPUFreqDTDriver { + type Data =3D (); + type PData =3D Arc; + + fn init(policy: &mut cpufreq::Policy) -> Result { + let cpu =3D policy.cpu(); + let dev =3D cpu::from_cpu(cpu)?; + let mut mask =3D Cpumask::new()?; + + mask.set(cpu); + + let token =3D match find_supply_names(dev, cpu) { + Some(names) =3D> Some( + opp::Config::::new() + .set_regulator_names(names)? + .set(dev)?, + ), + _ =3D> None, + }; + + // Get OPP-sharing information from "operating-points-v2" bindings. + let fallback =3D match opp::Table::of_sharing_cpus(dev, &mut mask)= { + Ok(()) =3D> false, + Err(e) =3D> { + if e !=3D ENOENT { + return Err(e); + } + + // "operating-points-v2" not supported. If the platform ha= sn't + // set sharing CPUs, fallback to all CPUs share the `Polic= y` + // for backward compatibility. + opp::Table::sharing_cpus(dev, &mut mask).is_err() + } + }; + + // Initialize OPP tables for all policy cpus. + // + // For platforms not using "operating-points-v2" bindings, we do t= his + // before updating policy cpus. Otherwise, we will end up creating + // duplicate OPPs for the CPUs. + // + // OPPs might be populated at runtime, don't fail for error here u= nless + // it is -EPROBE_DEFER. + let mut opp_table =3D match opp::Table::from_of_cpumask(dev, &mut = mask) { + Ok(table) =3D> table, + Err(e) =3D> { + if e =3D=3D EPROBE_DEFER { + return Err(e); + } + + // The table is added dynamically ? + opp::Table::from_dev(dev)? + } + }; + + // The OPP table must be initialized, statically or dynamically, b= y this point. + opp_table.opp_count()?; + + // Set sharing cpus for fallback scenario. + if fallback { + mask.set_all(); + opp_table.set_sharing_cpus(&mut mask)?; + } + + let mut transition_latency =3D opp_table.max_transition_latency() = as u32; + if transition_latency =3D=3D 0 { + transition_latency =3D cpufreq::ETERNAL_LATENCY; + } + + let freq_table =3D opp_table.to_cpufreq_table()?; + let clk =3D policy + .set_freq_table(freq_table.table()) + .set_dvfs_possible_from_any_cpu() + .set_suspend_freq((opp_table.suspend_freq() / 1000) as u32) + .set_transition_latency(transition_latency) + .set_clk(dev, None)?; + + mask.copy(policy.cpus()); + + Ok(Arc::new( + CPUFreqDTDevice { + opp_table, + freq_table, + mask, + token, + clk, + }, + GFP_KERNEL, + )?) + } + + fn exit(_policy: &mut cpufreq::Policy, _data: Option) -> = Result<()> { + Ok(()) + } + + fn online(_policy: &mut cpufreq::Policy) -> Result<()> { + // We did light-weight tear down earlier, nothing to do here. + Ok(()) + } + + fn offline(_policy: &mut cpufreq::Policy) -> Result<()> { + // Preserve policy->data and don't free resources on light-weight + // tear down. + Ok(()) + } + + fn suspend(policy: &mut cpufreq::Policy) -> Result<()> { + policy.generic_suspend() + } + + fn verify(data: &mut cpufreq::PolicyData) -> Result<()> { + data.generic_verify() + } + + fn target_index(policy: &mut cpufreq::Policy, index: u32) -> Result<()= > { + let data =3D match policy.data::() { + Some(data) =3D> data, + None =3D> return Err(ENOENT), + }; + + let freq =3D data.freq_table.freq(index.try_into().unwrap())? as u= size; + data.opp_table.set_rate(freq * 1000) + } + + fn get(policy: &mut cpufreq::Policy) -> Result { + policy.generic_get() + } + + fn set_boost(_policy: &mut cpufreq::Policy, _state: i32) -> Result<()>= { + Ok(()) + } + + fn register_em(policy: &mut cpufreq::Policy) { + policy.register_em_opp() + } +} + +kernel::of_device_table!( + OF_TABLE, + MODULE_OF_TABLE, + ::IdInfo, + [ + (of::DeviceId::new(c_str!("operating-points-v2")), ()) + ] +); + +impl platform::Driver for CPUFreqDTDriver { + type IdInfo =3D (); + const OF_ID_TABLE: Option> =3D Some(&OF_TABL= E); + + fn probe(pdev: &mut platform::Device, _id_info: Option<&Self::IdInfo>)= -> Result>> { + cpufreq::Registration::::new_foreign_owned( + pdev.as_ref(), + c_str!("cpufreq-dt"), + (), + cpufreq::flags::NEED_INITIAL_FREQ_CHECK | cpufreq::flags::IS_C= OOLING_DEV, + true, + )?; + + let drvdata =3D KBox::new(Self { _pdev: pdev.clone() }, GFP_KERNEL= )?; + + Ok(drvdata.into()) + } +} + +module_platform_driver! { + type: CPUFreqDTDriver, + name: "cpufreq_dt", + author: "Viresh Kumar ", + description: "Generic CPUFreq DT driver", + license: "GPL v2", +} --=20 2.31.1.272.g89b43f80a514