From nobody Sun Feb 8 18:38:40 2026 Received: from mail-pg1-f177.google.com (mail-pg1-f177.google.com [209.85.215.177]) (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 8CF5D25A2DA for ; Mon, 21 Apr 2025 07:23:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.177 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745220201; cv=none; b=Ez7ubSAsTvqxnC1Aic/TBnAdy3PT6HPChYVaIW1DBvpc4Tc6ggYiaXDXsPAh4+iG4nVCn0rp/WVrrWtjqiIdwJEKGp67oOpJtkcSD+EwRn4nBTfwFtFK9JktOlly/gMWC7F6ezECxFwEAb8zjmali8Ttp3krHdRYw2fVMBOe9sw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745220201; c=relaxed/simple; bh=+X2YVqXBMR0GAb+Qzmn1JnotjL7SA/w1KlYoI+NyTFk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=KcUzhW9wbA245hE45KgGYoAT06hSARMdDxPZe4s0CJ0XJzRYdc2yfzkeD82NT/AMGcV8v6gX6ftvNoP7vD/pMK17Ls9An7VZ7/FLW1vEgfKKd/3pESTo3X083UfB3xtxTBqD6eLVPJhxjvIPG0RS4PkrW36waKBE1EEYy89kTPI= 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=cE7/WMTF; arc=none smtp.client-ip=209.85.215.177 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="cE7/WMTF" Received: by mail-pg1-f177.google.com with SMTP id 41be03b00d2f7-af9925bbeb7so2434789a12.3 for ; Mon, 21 Apr 2025 00:23:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1745220199; x=1745824999; 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=eJphPp+lOUAYpl+NwhMQQkQWpPNw1Lq7XnupXLrP610=; b=cE7/WMTF/n5WNom6pTth1PW2pReKtdBiRr92NNZi79zUkpTGN4Bud3bUNG/Dn/8SRW 6GcJx45zCzO6P2WVllwQH2SYbxreIoObqe4hCSzRcyHkpof9sLo8/kKNZQXMRf2jv9S+ 5f2+UPro/bU/XX7LKrcOwzeG688/sFFhnr9kU+RgJ8ORqe2g4Bf57NUXmspU27pgP9Cg 46ETfX4mFyzOW8EnbewMDlUBIV2Y4kWimPg38E04oeSNzeiWpMI59oBwz43s/MxDsnkl mxIPF+NFBWrzx1tCIzHIx9AJI+UGVaOZrrJeCgUetkdxE9MO/mkWP0YFc8dik18TV7rZ IzRg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1745220199; x=1745824999; 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=eJphPp+lOUAYpl+NwhMQQkQWpPNw1Lq7XnupXLrP610=; b=apYcU2DwBOG9vu/74CJKNHuXCAl5QasqFiNh6B46Qtu0D+O/L2Hj+VUMxFqLGCOuq7 M46xeXExcx/4Pqb86WTBzHLaxWPYYMJSD4nI3o0vMegecPFCWACJ/GXl2szad7nPjsUY KvrC5nxi5h9TDuY4rHZ52EIXFSuU48HKxrF+X06yvBVNIq1Yu7hPw+0t1AkErsl/Q/IP 1AtzL7NrImm7PQX9rmfP4LF9RUDw0wpBZZ4jmk6D3HWax3WcqZYKgwC3sV5+Z6mNfNcA Z0n3zR6UC5pOxMZv/wKxWQcSWF71CJXjet4DtXDRXNsU7GrigeEZC2gisLT6+0rELtAV Ntng== X-Forwarded-Encrypted: i=1; AJvYcCUmDnQ637CfH7B6LBQuTyw62aC1RvnOd0huZQjQ75fVRG6fdaL5O7Hh+w1JTHNES71k6e5PNEphVfkiuks=@vger.kernel.org X-Gm-Message-State: AOJu0Yy74T+qxoAKvS/nxK7uqhUkeu3nf5mVe3NwL9BJ4h8oU+DZhd2h LdR655ZMu4g5iNq1Wul9WrmQSv7CpwxqQ4CYLwtuGzI2XV8ElMcJaYg7bW2OXzY= X-Gm-Gg: ASbGnctmHWXYX34kDCJ3yZSwnyCmsZlrjgOVkocFYpi7gKKyO8QjQcHvBeRbdMprhSp qXvGHzZv4q2W55lrWNHO2ShabgutmpbpjpqbF0xPYvO4LCWG4lgtqHbgIVlV7Xj96jTpWA3k39X v1uoKszuavXFXnyCYNo+u5wCrwIgzJG1/S0TAXq6AQJCt0YSbNFQcTPfCXAGQazb3jY30b5acg8 3Vylf+43PtNAGZ061vjV2wsP20uJVwpmC41/R+4HpissclTrjvpXq3zgwwYlfsIl2+LWNIsAxab eY59utIcDKEt3l1haHKh+caUUjMMisXnY+rpFRwPm0+Pf7sxEqTA X-Google-Smtp-Source: AGHT+IHMtpy31PU6cXL9N0kB1GJWnAWIcrfy6ks/vIFWUZYnnQQ6+ZKS7z9jAboVuZW0tlPRpR0WSw== X-Received: by 2002:a17:90a:dfc5:b0:2fa:1a23:c01d with SMTP id 98e67ed59e1d1-3087bb6bcaamr15421603a91.21.1745220198571; Mon, 21 Apr 2025 00:23:18 -0700 (PDT) Received: from localhost ([122.172.83.32]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3087e1149bcsm5857281a91.41.2025.04.21.00.23.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Apr 2025 00:23:18 -0700 (PDT) From: Viresh Kumar To: "Rafael J. Wysocki" , Miguel Ojeda , Danilo Krummrich , Viresh Kumar , Nishanth Menon , Stephen Boyd , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?UTF-8?q?Bj=C3=B6rn=20Roy=20Baron?= , Benno Lossin , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich Cc: Viresh Kumar , linux-pm@vger.kernel.org, Vincent Guittot , rust-for-linux@vger.kernel.org, Manos Pitsidianakis , =?UTF-8?q?Alex=20Benn=C3=A9e?= , Joakim Bech , Rob Herring , Yury Norov , Burak Emir , Rasmus Villemoes , Russell King , linux-clk@vger.kernel.org, Michael Turquette , linux-kernel@vger.kernel.org Subject: [PATCH V11 09/15] rust: opp: Add abstractions for the OPP table Date: Mon, 21 Apr 2025 12:52:16 +0530 Message-Id: <2caa3652b43424e90fe775d7fb4f8057675c974f.1745218976.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" Introduce Rust abstractions for `struct opp_table`, enabling access to OPP tables from Rust. Signed-off-by: Viresh Kumar --- rust/kernel/opp.rs | 485 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 484 insertions(+), 1 deletion(-) diff --git a/rust/kernel/opp.rs b/rust/kernel/opp.rs index 8f0493a8b6e8..062c2a04c52f 100644 --- a/rust/kernel/opp.rs +++ b/rust/kernel/opp.rs @@ -10,8 +10,9 @@ =20 use crate::{ clk::Hertz, + cpumask::{Cpumask, CpumaskVar}, device::Device, - error::{code::*, to_result, Result}, + error::{code::*, from_err_ptr, to_result, Error, Result}, ffi::c_ulong, types::{ARef, AlwaysRefCounted, Opaque}, }; @@ -171,6 +172,467 @@ fn freq(&self) -> Hertz { } } =20 +/// [`OPP`] search options. +/// +/// ## Examples +/// +/// Defines how to search for an [`OPP`] in a [`Table`] relative to a freq= uency. +/// +/// ``` +/// use kernel::clk::Hertz; +/// use kernel::error::Result; +/// use kernel::opp::{OPP, SearchType, Table}; +/// use kernel::types::ARef; +/// +/// fn find_opp(table: &Table, freq: Hertz) -> Result> { +/// let opp =3D table.opp_from_freq(freq, Some(true), None, SearchType= ::Exact)?; +/// +/// pr_info!("OPP frequency is: {:?}\n", opp.freq(None)); +/// pr_info!("OPP voltage is: {:?}\n", opp.voltage()); +/// pr_info!("OPP level is: {}\n", opp.level()); +/// pr_info!("OPP power is: {:?}\n", opp.power()); +/// +/// Ok(opp) +/// } +/// ``` +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum SearchType { + /// Match the exact frequency. + Exact, + /// Find the highest frequency less than or equal to the given value. + Floor, + /// Find the lowest frequency greater than or equal to the given value. + Ceil, +} + +/// A reference-counted OPP table. +/// +/// Rust abstraction for the C `struct opp_table`. +/// +/// # Invariants +/// +/// The pointer stored in `Self` is non-null and valid for the lifetime of= the [`Table`]. +/// +/// Instances of this type are reference-counted. +/// +/// ## Examples +/// +/// The following example demonstrates how to get OPP [`Table`] for a [`Cp= umask`] and set its +/// frequency. +/// +/// ``` +/// use kernel::clk::Hertz; +/// use kernel::cpumask::Cpumask; +/// use kernel::device::Device; +/// use kernel::error::Result; +/// use kernel::opp::Table; +/// use kernel::types::ARef; +/// +/// fn get_table(dev: &ARef, mask: &mut Cpumask, freq: Hertz) -> R= esult { +/// let mut opp_table =3D Table::from_of_cpumask(dev, mask)?; +/// +/// if opp_table.opp_count()? =3D=3D 0 { +/// return Err(EINVAL); +/// } +/// +/// pr_info!("Max transition latency is: {} ns\n", opp_table.max_trans= ition_latency_ns()); +/// pr_info!("Suspend frequency is: {:?}\n", opp_table.suspend_freq()); +/// +/// opp_table.set_rate(freq)?; +/// Ok(opp_table) +/// } +/// ``` +pub struct Table { + ptr: *mut bindings::opp_table, + dev: ARef, + em: bool, + of: bool, + cpus: Option, +} + +/// SAFETY: It is okay to send ownership of [`Table`] across thread bounda= ries. +unsafe impl Send for Table {} + +/// SAFETY: It is okay to access [`Table`] through shared references from = other threads because +/// we're either accessing properties that don't change or that are proper= ly synchronised by C code. +unsafe impl Sync for Table {} + +impl Table { + /// Creates a new reference-counted [`Table`] from a raw pointer. + /// + /// # Safety + /// + /// Callers must ensure that `ptr` is valid and non-null. + unsafe fn from_raw_table(ptr: *mut bindings::opp_table, dev: &ARef) -> Self { + // SAFETY: By the safety requirements, ptr is valid and its refcou= nt will be incremented. + // + // INVARIANT: The reference-count is decremented when [`Table`] go= es out of scope. + unsafe { bindings::dev_pm_opp_get_opp_table_ref(ptr) }; + + Self { + ptr, + dev: dev.clone(), + em: false, + of: false, + cpus: None, + } + } + + /// Creates a new reference-counted [`Table`] instance for a [`Device`= ]. + pub fn from_dev(dev: &Device) -> Result { + // SAFETY: The requirements are satisfied by the existence of the = [`Device`] and its safety + // requirements. + // + // INVARIANT: The reference-count is incremented by the C code and= is decremented when + // [`Table`] goes out of scope. + let ptr =3D from_err_ptr(unsafe { bindings::dev_pm_opp_get_opp_tab= le(dev.as_raw()) })?; + + Ok(Self { + ptr, + dev: dev.into(), + em: false, + of: false, + cpus: None, + }) + } + + /// Creates a new reference-counted [`Table`] instance for a [`Device`= ] based on device tree + /// entries. + #[cfg(CONFIG_OF)] + pub fn from_of(dev: &ARef, index: i32) -> Result { + // SAFETY: The requirements are satisfied by the existence of the = [`Device`] and its safety + // requirements. + // + // INVARIANT: The reference-count is incremented by the C code and= is decremented when + // [`Table`] goes out of scope. + to_result(unsafe { bindings::dev_pm_opp_of_add_table_indexed(dev.a= s_raw(), index) })?; + + // Get the newly created [`Table`]. + let mut table =3D Self::from_dev(dev)?; + table.of =3D true; + + Ok(table) + } + + /// Remove device tree based [`Table`]. + #[cfg(CONFIG_OF)] + #[inline] + fn remove_of(&self) { + // SAFETY: The requirements are satisfied by the existence of the = [`Device`] and its safety + // requirements. We took the reference from [`from_of`] earlier, i= t is safe to drop the + // same now. + unsafe { bindings::dev_pm_opp_of_remove_table(self.dev.as_raw()) }; + } + + /// Creates a new reference-counted [`Table`] instance for a [`Cpumask= `] based on device tree + /// entries. + #[cfg(CONFIG_OF)] + pub fn from_of_cpumask(dev: &Device, cpumask: &mut Cpumask) -> Result<= Self> { + // SAFETY: The cpumask is valid and the returned pointer will be o= wned by the [`Table`] + // instance. + // + // INVARIANT: The reference-count is incremented by the C code and= is decremented when + // [`Table`] goes out of scope. + to_result(unsafe { bindings::dev_pm_opp_of_cpumask_add_table(cpuma= sk.as_raw()) })?; + + // Fetch the newly created table. + let mut table =3D Self::from_dev(dev)?; + table.cpus =3D Some(CpumaskVar::try_clone(cpumask)?); + + Ok(table) + } + + /// Remove device tree based [`Table`] for a [`Cpumask`]. + #[cfg(CONFIG_OF)] + #[inline] + fn remove_of_cpumask(&self, cpumask: &Cpumask) { + // SAFETY: The cpumask is valid and we took the reference from [`f= rom_of_cpumask`] earlier, + // it is safe to drop the same now. + unsafe { bindings::dev_pm_opp_of_cpumask_remove_table(cpumask.as_r= aw()) }; + } + + /// Returns the number of [`OPP`]s in the [`Table`]. + pub fn opp_count(&self) -> Result { + // SAFETY: The requirements are satisfied by the existence of [`De= vice`] and its safety + // requirements. + let ret =3D unsafe { bindings::dev_pm_opp_get_opp_count(self.dev.a= s_raw()) }; + if ret < 0 { + Err(Error::from_errno(ret)) + } else { + Ok(ret as u32) + } + } + + /// Returns max clock latency (in nanoseconds) of the [`OPP`]s in the = [`Table`]. + #[inline] + pub fn max_clock_latency_ns(&self) -> usize { + // SAFETY: The requirements are satisfied by the existence of [`De= vice`] and its safety + // requirements. + unsafe { bindings::dev_pm_opp_get_max_clock_latency(self.dev.as_ra= w()) } + } + + /// Returns max volt latency (in nanoseconds) of the [`OPP`]s in the [= `Table`]. + #[inline] + pub fn max_volt_latency_ns(&self) -> usize { + // SAFETY: The requirements are satisfied by the existence of [`De= vice`] and its safety + // requirements. + unsafe { bindings::dev_pm_opp_get_max_volt_latency(self.dev.as_raw= ()) } + } + + /// Returns max transition latency (in nanoseconds) of the [`OPP`]s in= the [`Table`]. + #[inline] + pub fn max_transition_latency_ns(&self) -> usize { + // SAFETY: The requirements are satisfied by the existence of [`De= vice`] and its safety + // requirements. + unsafe { bindings::dev_pm_opp_get_max_transition_latency(self.dev.= as_raw()) } + } + + /// Returns the suspend [`OPP`]'s frequency. + #[inline] + pub fn suspend_freq(&self) -> Hertz { + // SAFETY: The requirements are satisfied by the existence of [`De= vice`] and its safety + // requirements. + Hertz(unsafe { bindings::dev_pm_opp_get_suspend_opp_freq(self.dev.= as_raw()) }) + } + + /// Synchronizes regulators used by the [`Table`]. + #[inline] + pub fn sync_regulators(&self) -> Result<()> { + // SAFETY: The requirements are satisfied by the existence of [`De= vice`] and its safety + // requirements. + to_result(unsafe { bindings::dev_pm_opp_sync_regulators(self.dev.a= s_raw()) }) + } + + /// Gets sharing CPUs. + #[inline] + pub fn sharing_cpus(dev: &Device, cpumask: &mut Cpumask) -> Result<()>= { + // SAFETY: The requirements are satisfied by the existence of [`De= vice`] and its safety + // requirements. + to_result(unsafe { bindings::dev_pm_opp_get_sharing_cpus(dev.as_ra= w(), cpumask.as_raw()) }) + } + + /// Sets sharing CPUs. + pub fn set_sharing_cpus(&mut self, cpumask: &mut Cpumask) -> Result<()= > { + // SAFETY: The requirements are satisfied by the existence of [`De= vice`] and its safety + // requirements. + to_result(unsafe { + bindings::dev_pm_opp_set_sharing_cpus(self.dev.as_raw(), cpuma= sk.as_raw()) + })?; + + if let Some(mask) =3D self.cpus.as_mut() { + // Update the cpumask as this will be used while removing the = table. + cpumask.copy(mask); + } + + Ok(()) + } + + /// Gets sharing CPUs from device tree. + #[cfg(CONFIG_OF)] + #[inline] + pub fn of_sharing_cpus(dev: &Device, cpumask: &mut Cpumask) -> Result<= ()> { + // SAFETY: The requirements are satisfied by the existence of [`De= vice`] and its safety + // requirements. + to_result(unsafe { + bindings::dev_pm_opp_of_get_sharing_cpus(dev.as_raw(), cpumask= .as_raw()) + }) + } + + /// Updates the voltage value for an [`OPP`]. + #[inline] + pub fn adjust_voltage( + &self, + freq: Hertz, + volt: MicroVolt, + volt_min: MicroVolt, + volt_max: MicroVolt, + ) -> Result<()> { + // SAFETY: The requirements are satisfied by the existence of [`De= vice`] and its safety + // requirements. + to_result(unsafe { + bindings::dev_pm_opp_adjust_voltage( + self.dev.as_raw(), + freq.into(), + volt.into(), + volt_min.into(), + volt_max.into(), + ) + }) + } + + /// Configures device with [`OPP`] matching the frequency value. + #[inline] + pub fn set_rate(&self, freq: Hertz) -> Result<()> { + // SAFETY: The requirements are satisfied by the existence of [`De= vice`] and its safety + // requirements. + to_result(unsafe { bindings::dev_pm_opp_set_rate(self.dev.as_raw()= , freq.into()) }) + } + + /// Configures device with [`OPP`]. + #[inline] + pub fn set_opp(&self, opp: &OPP) -> Result<()> { + // SAFETY: The requirements are satisfied by the existence of [`De= vice`] and its safety + // requirements. + to_result(unsafe { bindings::dev_pm_opp_set_opp(self.dev.as_raw(),= opp.as_raw()) }) + } + + /// Finds [`OPP`] based on frequency. + pub fn opp_from_freq( + &self, + freq: Hertz, + available: Option, + index: Option, + stype: SearchType, + ) -> Result> { + let raw_dev =3D self.dev.as_raw(); + let index =3D index.unwrap_or(0); + let mut rate =3D freq.into(); + + let ptr =3D from_err_ptr(match stype { + SearchType::Exact =3D> { + if let Some(available) =3D available { + // SAFETY: The requirements are satisfied by the exist= ence of [`Device`] and + // its safety requirements. The returned pointer will = be owned by the new + // [`OPP`] instance. + unsafe { + bindings::dev_pm_opp_find_freq_exact_indexed( + raw_dev, rate, index, available, + ) + } + } else { + return Err(EINVAL); + } + } + + // SAFETY: The requirements are satisfied by the existence of = [`Device`] and its safety + // requirements. The returned pointer will be owned by the new= [`OPP`] instance. + SearchType::Ceil =3D> unsafe { + bindings::dev_pm_opp_find_freq_ceil_indexed(raw_dev, &mut = rate, index) + }, + + // SAFETY: The requirements are satisfied by the existence of = [`Device`] and its safety + // requirements. The returned pointer will be owned by the new= [`OPP`] instance. + SearchType::Floor =3D> unsafe { + bindings::dev_pm_opp_find_freq_floor_indexed(raw_dev, &mut= rate, index) + }, + })?; + + // SAFETY: The `ptr` is guaranteed by the C code to be valid. + unsafe { OPP::from_raw_opp_owned(ptr) } + } + + /// Finds [`OPP`] based on level. + pub fn opp_from_level(&self, mut level: u32, stype: SearchType) -> Res= ult> { + let raw_dev =3D self.dev.as_raw(); + + let ptr =3D from_err_ptr(match stype { + // SAFETY: The requirements are satisfied by the existence of = [`Device`] and its safety + // requirements. The returned pointer will be owned by the new= [`OPP`] instance. + SearchType::Exact =3D> unsafe { bindings::dev_pm_opp_find_leve= l_exact(raw_dev, level) }, + + // SAFETY: The requirements are satisfied by the existence of = [`Device`] and its safety + // requirements. The returned pointer will be owned by the new= [`OPP`] instance. + SearchType::Ceil =3D> unsafe { + bindings::dev_pm_opp_find_level_ceil(raw_dev, &mut level) + }, + + // SAFETY: The requirements are satisfied by the existence of = [`Device`] and its safety + // requirements. The returned pointer will be owned by the new= [`OPP`] instance. + SearchType::Floor =3D> unsafe { + bindings::dev_pm_opp_find_level_floor(raw_dev, &mut level) + }, + })?; + + // SAFETY: The `ptr` is guaranteed by the C code to be valid. + unsafe { OPP::from_raw_opp_owned(ptr) } + } + + /// Finds [`OPP`] based on bandwidth. + pub fn opp_from_bw(&self, mut bw: u32, index: i32, stype: SearchType) = -> Result> { + let raw_dev =3D self.dev.as_raw(); + + let ptr =3D from_err_ptr(match stype { + // The OPP core doesn't support this yet. + SearchType::Exact =3D> return Err(EINVAL), + + // SAFETY: The requirements are satisfied by the existence of = [`Device`] and its safety + // requirements. The returned pointer will be owned by the new= [`OPP`] instance. + SearchType::Ceil =3D> unsafe { + bindings::dev_pm_opp_find_bw_ceil(raw_dev, &mut bw, index) + }, + + // SAFETY: The requirements are satisfied by the existence of = [`Device`] and its safety + // requirements. The returned pointer will be owned by the new= [`OPP`] instance. + SearchType::Floor =3D> unsafe { + bindings::dev_pm_opp_find_bw_floor(raw_dev, &mut bw, index) + }, + })?; + + // SAFETY: The `ptr` is guaranteed by the C code to be valid. + unsafe { OPP::from_raw_opp_owned(ptr) } + } + + /// Enables the [`OPP`]. + #[inline] + pub fn enable_opp(&self, freq: Hertz) -> Result<()> { + // SAFETY: The requirements are satisfied by the existence of [`De= vice`] and its safety + // requirements. + to_result(unsafe { bindings::dev_pm_opp_enable(self.dev.as_raw(), = freq.into()) }) + } + + /// Disables the [`OPP`]. + #[inline] + pub fn disable_opp(&self, freq: Hertz) -> Result<()> { + // SAFETY: The requirements are satisfied by the existence of [`De= vice`] and its safety + // requirements. + to_result(unsafe { bindings::dev_pm_opp_disable(self.dev.as_raw(),= freq.into()) }) + } + + /// Registers with the Energy model. + #[cfg(CONFIG_OF)] + pub fn of_register_em(&mut self, cpumask: &mut Cpumask) -> Result<()> { + // SAFETY: The requirements are satisfied by the existence of [`De= vice`] and its safety + // requirements. + to_result(unsafe { + bindings::dev_pm_opp_of_register_em(self.dev.as_raw(), cpumask= .as_raw()) + })?; + + self.em =3D true; + Ok(()) + } + + /// Unregisters with the Energy model. + #[cfg(all(CONFIG_OF, CONFIG_ENERGY_MODEL))] + #[inline] + fn of_unregister_em(&self) { + // SAFETY: The requirements are satisfied by the existence of [`De= vice`] and its safety + // requirements. We registered with the EM framework earlier, it i= s safe to unregister now. + unsafe { bindings::em_dev_unregister_perf_domain(self.dev.as_raw()= ) }; + } +} + +impl Drop for Table { + fn drop(&mut self) { + // SAFETY: By the type invariants, we know that `self` owns a refe= rence, so it is safe + // to relinquish it now. + unsafe { bindings::dev_pm_opp_put_opp_table(self.ptr) }; + + #[cfg(CONFIG_OF)] + { + #[cfg(CONFIG_ENERGY_MODEL)] + if self.em { + self.of_unregister_em(); + } + + if self.of { + self.remove_of(); + } else if let Some(cpumask) =3D self.cpus.take() { + self.remove_of_cpumask(&cpumask); + } + } + } +} + /// A reference-counted Operating performance point (OPP). /// /// Rust abstraction for the C `struct dev_pm_opp`. @@ -184,6 +646,27 @@ fn freq(&self) -> Hertz { /// represents a pointer that owns a reference count on the [`OPP`]. /// /// A reference to the [`OPP`], &[`OPP`], isn't refcounted by the Rust cod= e. +/// +/// ## Examples +/// +/// The following example demonstrates how to get [`OPP`] corresponding to= a frequency value and +/// configure the device with it. +/// +/// ``` +/// use kernel::clk::Hertz; +/// use kernel::error::Result; +/// use kernel::opp::{SearchType, Table}; +/// +/// fn configure_opp(table: &Table, freq: Hertz) -> Result { +/// let opp =3D table.opp_from_freq(freq, Some(true), None, SearchType= ::Exact)?; +/// +/// if opp.freq(None) !=3D freq { +/// return Err(EINVAL); +/// } +/// +/// table.set_opp(&opp) +/// } +/// ``` #[repr(transparent)] pub struct OPP(Opaque); =20 --=20 2.31.1.272.g89b43f80a514