From nobody Sun Dec 14 19:11:21 2025 Received: from mail-pl1-f169.google.com (mail-pl1-f169.google.com [209.85.214.169]) (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 420611EE7A1 for ; Mon, 21 Apr 2025 07:22:48 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.169 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745220170; cv=none; b=qK1MJ/fkakMlxPQ9oiFGWcbdA/LvKYfs29DzrAa4kQHUi2nlJO02183HazVzdn8t5UpP2XRe8yIQsOJzw6q4jHISfa9vU6WxbbAmqEEJLTJqE4wKpOiXshL9Dnt2fgJMMjPV/eYXgEvrz4argTp6KURdrgM2KcxOQaOx5tYCr2s= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745220170; c=relaxed/simple; bh=oQ/uumB9dZEnQv7fbXO+2/DavkdF+8BaxTejDAZiJqM=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=IGWYEqbV7Kal1HSybmI7DaRiqXdVSHzWMk3Q3tXV1YY43gXHXBAvdWxpYeDiaIaIpJLG+U2wObMUf3oIwDUG/jYcEOBeshLA8/Ur8AYvL58zWN56tuyJe+MCH7vPKVykkBgmpjJnAznbNGNUyqiXQhruLUmMNERtl9jVbNbusJQ= 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=W1oWHbBG; arc=none smtp.client-ip=209.85.214.169 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="W1oWHbBG" Received: by mail-pl1-f169.google.com with SMTP id d9443c01a7336-22c3407a87aso55766115ad.3 for ; Mon, 21 Apr 2025 00:22:48 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1745220167; x=1745824967; 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=Br+IpXn10n+9CXIhhXnelWYoEuJ5Sc8cDeYzc7Zl6Z4=; b=W1oWHbBGZ1ci59ZKo3ZlzpDDcDPhIvu/tUtzfp0L4G8paC2KTxNZ9ytj7DaB37vcBK bfMLMPRmwcVPPW9w1qqwp568mxAEEVvuLTcPclAbQCooICZsE3I1KYuzT4lcNrKbkA9K IRRek8cZQntTadSe9aMeDkvUPzf/Lbk91ZbbNnAij1Wg+gxMAoO7JmRSTSQnaHFuT+dO 1UPOk4/ABwf5iBW1PiUTz7od0NJNAMS+rpPUZukZ97ZwFzprkJghCaj86hE9xD6NdUGR Eb2fKUiaLioiFlhc0f0Mgcr63vuP7oodrHwhwrs9/sARCIaBstVEHEJHKhwQ5whFM3qf kDeQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1745220167; x=1745824967; 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=Br+IpXn10n+9CXIhhXnelWYoEuJ5Sc8cDeYzc7Zl6Z4=; b=Kps1TJ5n80CY9kev4MTcs+b+BP35+AeqiHzKPQ4Cu8bIykqG0TZu22/eDA7n285W4G Z9iIDBlcAaOQUXHVO2zE3tPGFqnfkS5l28+Ypb98hbq2eQNc0emCEbvYEcoWUoyh69pG yGyetT5dV+N/d1XAztIsKcuCQLbInZBpseLSdg2MBnxxMobXcXFmgPvoKUrYtMgaE5xk k/CP1AAm7IOI+29YuhfbTjiZ/aIXTZxS4A+ixkeMkPr/8hF6CwOwqgP3xDTErMSH5GlX 3MoEcEVLBKeADRMuhWqOzEakqJs6QKt1P6RBFU301xsALiFkfKtsTlaZ3d9ScAoFF9CM D3og== X-Forwarded-Encrypted: i=1; AJvYcCXzWOqshnZ6qtAPjHVrc46ngeHscwZk5EE/YCwwvZ+X2NLwyZ9Gaa0D04WtQDM4HjmqvaUfwAprJnin3PE=@vger.kernel.org X-Gm-Message-State: AOJu0Yzc+Ok35CkfZdEqzb7wpZhzcUbwATEig1Dg9uzXJK+ioz/3Jw4q ejO6u2L9vSFdkDyQ+PBYhrfkqEk3C//Lyk5h/lsvtKemvgB+D0iBcQDqWyTf3Ac= X-Gm-Gg: ASbGncvtl9cvhQO4zF41vkuv8tMe781TXq6syfcEnOLhdm8IJYujrtGKJ1RvxHCz4Lt g9pZ1MoZwxhJ3Zul5VhVxS4M2eEJbiPXvkcJqx9rcM2eL77qnvIouhvdnKtCZBvdwQaZVby3TIk 8nAy2WasCicteXtYOY0jIU+YwfrGJ3h0mp7DI1JmhcfklAYWIHqQ6AE24jALHkhxVyA/uOd/z7x jm6wWAK869pzHsKV2/GwDOwSpYipS+xARu2pIyOhYWiexjkMF35ZQfodsd0GcWJSgYFZBq0+3YK P/NHkJCF2vj1v4VqX02nBAY1DTxe4xXWlxzvEzprGQ== X-Google-Smtp-Source: AGHT+IF3+oCxTMMz+R3DrRhQUE2THmmZKmnz5F87I0hadILJtWNneQEBdeLOmXBL2HrlyI2RGFtiQQ== X-Received: by 2002:a17:902:ecc8:b0:224:2717:7993 with SMTP id d9443c01a7336-22c5362062amr153799585ad.45.1745220167576; Mon, 21 Apr 2025 00:22:47 -0700 (PDT) Received: from localhost ([122.172.83.32]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-22c50eb48a4sm59089785ad.133.2025.04.21.00.22.46 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Apr 2025 00:22:47 -0700 (PDT) From: Viresh Kumar To: "Rafael J. Wysocki" , Miguel Ojeda , Danilo Krummrich , Yury Norov , 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 , Stephen Boyd , Nishanth Menon , rust-for-linux@vger.kernel.org, Manos Pitsidianakis , =?UTF-8?q?Alex=20Benn=C3=A9e?= , Joakim Bech , Rob Herring , Burak Emir , Rasmus Villemoes , Russell King , linux-clk@vger.kernel.org, Michael Turquette , linux-kernel@vger.kernel.org Subject: [PATCH V11 01/15] rust: cpumask: Add few more helpers Date: Mon, 21 Apr 2025 12:52:08 +0530 Message-Id: <944ee57ad1b190f6975c739802b4d93166bd00c5.1745218975.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" Add few more cpumask helpers that are required by the Rust abstraction. Signed-off-by: Viresh Kumar Acked-by: Yury Norov [NVIDIA] --- rust/helpers/cpumask.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/rust/helpers/cpumask.c b/rust/helpers/cpumask.c index 2d380a86c34a..eb10598a0242 100644 --- a/rust/helpers/cpumask.c +++ b/rust/helpers/cpumask.c @@ -7,16 +7,41 @@ void rust_helper_cpumask_set_cpu(unsigned int cpu, struct= cpumask *dstp) cpumask_set_cpu(cpu, dstp); } =20 +void rust_helper___cpumask_set_cpu(unsigned int cpu, struct cpumask *dstp) +{ + __cpumask_set_cpu(cpu, dstp); +} + void rust_helper_cpumask_clear_cpu(int cpu, struct cpumask *dstp) { cpumask_clear_cpu(cpu, dstp); } =20 +void rust_helper___cpumask_clear_cpu(int cpu, struct cpumask *dstp) +{ + __cpumask_clear_cpu(cpu, dstp); +} + +bool rust_helper_cpumask_test_cpu(int cpu, struct cpumask *srcp) +{ + return cpumask_test_cpu(cpu, srcp); +} + void rust_helper_cpumask_setall(struct cpumask *dstp) { cpumask_setall(dstp); } =20 +bool rust_helper_cpumask_empty(struct cpumask *srcp) +{ + return cpumask_empty(srcp); +} + +bool rust_helper_cpumask_full(struct cpumask *srcp) +{ + return cpumask_full(srcp); +} + unsigned int rust_helper_cpumask_weight(struct cpumask *srcp) { return cpumask_weight(srcp); --=20 2.31.1.272.g89b43f80a514 From nobody Sun Dec 14 19:11:21 2025 Received: from mail-pl1-f172.google.com (mail-pl1-f172.google.com [209.85.214.172]) (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 6A3B220F09B for ; Mon, 21 Apr 2025 07:22:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745220174; cv=none; b=sFAk/V7/xp/rosilZA5Zjy0YlE3ukUZRh/ABJ5tS1/dZayrON9HYW7MZNwKVq/ZZDsPKd4U1m+ev5b0FfYP3d8kVlSR8tHHz/haiT2fsaccOj2UBzKxTGJMfwkgmnOGqrGKbmUMhlWoyTlQFu2RIvAPHIiXWMclEEySAoKQSO+c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745220174; c=relaxed/simple; bh=6bdBeyQA7PvDMni0+v4HEV40R5KCJ3SAKrl8WzzUVVY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=utKftpz4+xws1Xjnm+WouzoWXEgUBrShSLedQnHIPxrcTGKmaq+1qK7F5T2KtHnBd8Q1cHWuaTE1E3mOTl4bN8wKuKcwDeb/HH9BtqgERUtiGRQbAhd4POAzEn+HCEW7bflgb41B1zHubgimWOGGvPA6FU+ooZNYtGuoT4gcGeg= 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=TjHaxDtZ; arc=none smtp.client-ip=209.85.214.172 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="TjHaxDtZ" Received: by mail-pl1-f172.google.com with SMTP id d9443c01a7336-22c33677183so42226765ad.2 for ; Mon, 21 Apr 2025 00:22:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1745220172; x=1745824972; 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=pYpmBeA7UxnY21oeYlIxfV+okNCfPfqyF8VN8X/0Ne0=; b=TjHaxDtZNjCA62TPnrlRd0CW6W8XZN1sD8GmimcoutNFHWLkLxG1SNJkLQRPKuSHFL o+Hw0VlhiRtBtKhWjeykn01GVlIAIDYBtRSe5eUssm35awfi7GZcnUw+rbAHIjCPmUcu SE2vxmry6dHqrrxaYpiD2yqYyid0lteShE7Ttl+MZKgia0zfIXqHODSJScueyI+i+m2n 7pH37D0b6upw6Qdbc1E+wIuoU/NhRcvayP0jUclVUE7/h8tBmrsxXOFp3X6N1PqDEjO+ NxyG2y/PePuUQ/twFwCGj3/egW1YLYC8tmYzBytkJ1zYhG5w+2lEgN+Npw1J/7ic4qnI CECg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1745220172; x=1745824972; 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=pYpmBeA7UxnY21oeYlIxfV+okNCfPfqyF8VN8X/0Ne0=; b=Z5hA9v2loxKmuCN1WjdQWyCj+HDwW9nGlu7EC56mY1qgdrFm6Z4tYRTib9flQ4YjW3 fpQSh0VAaBjwTapQXn2PKx8pCLv0w1p7G6Re2GzhPXoUm18bEismbFDTxNOuHDPyHO7L wO1y1Jo1ELY647uYPzORTF8RNazxHGfMrNldP+El31ie6ZH10whtf0TbhBU4Lz63eaQb Na2hM8xxN+3TKvjfma1UW92CcWrnCfxM9W7+mRK2ZT+cBNiyp0eRQF4LnRlugJcL7AX6 xCM+DHJ62e3blYLqnklZ+GrCrVlUTVmlsWkbAtH8JPPqxnZCIIH8LYU/+hjB9Rd8x4bY TkGw== X-Forwarded-Encrypted: i=1; AJvYcCXD/2KbFaLaPcH0Jt579p1bJiHbJsL/7gfI9qYMFWxOBMfP1TSahP5yeZwp1HKGEv3NlbeffvQPhKvYa7Q=@vger.kernel.org X-Gm-Message-State: AOJu0YwkJNijaEmxzW7zM/K5zDc9Ms6H9BxcQd4GUkF3txdJrXJrdBQ7 VhOk1zatkoUqY1AEjTpwrl/vMd9+knneF24vyDMebUrN51XbkZJzGS2NULOhRaQ= X-Gm-Gg: ASbGnctnRLrBtie93pxoMlTE/d90GBMGo78ZnIdCGzuz2BVc8o+oGd6wO+rBMZXPHIf mx6dpsWZoiIPadaK28E3kkzmVdhAdMLHeJfr5a6Z4Ua/WGpGUwRNRXYNOLTMIfvjVqpApyUdfW6 rQzgmsfrjTj72zN/Qwj5YA8IHJ1N3AfhwAZ1DHY0WqPkFVDOApMjFjLR/kpsa4PLL+CBdhitSOW UU2ZSRsdxW8HdmPUcg8ge93QbNDnocyw8QIkWy6T7MFgG5eYFNl1vnXHb7zyJ86BTkuyiwrH+JD yP0Fx4sNrrn/3QF1JvTGn0e87tMSb/qeQQt5Aj1IrppdEXxYfddw X-Google-Smtp-Source: AGHT+IFgHdhMJfK4QGzdQrrlcnlF6l1kzsZgx4KMtOxNf9FpUUpVRDonqnuL7JYZDvqFFnKoz+ql/Q== X-Received: by 2002:a17:902:f682:b0:224:912:153 with SMTP id d9443c01a7336-22c53573d25mr174757935ad.5.1745220171548; Mon, 21 Apr 2025 00:22:51 -0700 (PDT) Received: from localhost ([122.172.83.32]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-22c50eb5349sm59226245ad.157.2025.04.21.00.22.50 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Apr 2025 00:22:51 -0700 (PDT) From: Viresh Kumar To: "Rafael J. Wysocki" , Miguel Ojeda , Danilo Krummrich , Viresh Kumar , Yury Norov , 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: linux-pm@vger.kernel.org, Vincent Guittot , Stephen Boyd , Nishanth Menon , rust-for-linux@vger.kernel.org, Manos Pitsidianakis , =?UTF-8?q?Alex=20Benn=C3=A9e?= , Joakim Bech , Rob Herring , Burak Emir , Rasmus Villemoes , Russell King , linux-clk@vger.kernel.org, Michael Turquette , linux-kernel@vger.kernel.org Subject: [PATCH V11 02/15] rust: cpumask: Add initial abstractions Date: Mon, 21 Apr 2025 12:52:09 +0530 Message-Id: <4a8f23fd447897cb76ccc46d03cc30a2086a0e90.1745218975.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" Add initial Rust abstractions for struct cpumask, covering a subset of its APIs. Additional APIs can be added as needed. These abstractions will be used in upcoming Rust support for cpufreq and OPP frameworks. Signed-off-by: Viresh Kumar Reviewed-by: Yury Norov [NVIDIA] --- rust/kernel/cpumask.rs | 330 +++++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 2 files changed, 331 insertions(+) create mode 100644 rust/kernel/cpumask.rs diff --git a/rust/kernel/cpumask.rs b/rust/kernel/cpumask.rs new file mode 100644 index 000000000000..c90bfac9346a --- /dev/null +++ b/rust/kernel/cpumask.rs @@ -0,0 +1,330 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! CPU Mask abstractions. +//! +//! C header: [`include/linux/cpumask.h`](srctree/include/linux/cpumask.h) + +use crate::{ + alloc::{AllocError, Flags}, + prelude::*, + types::Opaque, +}; + +#[cfg(CONFIG_CPUMASK_OFFSTACK)] +use core::ptr::{self, NonNull}; + +#[cfg(not(CONFIG_CPUMASK_OFFSTACK))] +use core::mem::MaybeUninit; + +use core::ops::{Deref, DerefMut}; + +/// A CPU Mask. +/// +/// Rust abstraction for the C `struct cpumask`. +/// +/// # Invariants +/// +/// A [`Cpumask`] instance always corresponds to a valid C `struct cpumask= `. +/// +/// The callers must ensure that the `struct cpumask` is valid for access = and +/// remains valid for the lifetime of the returned reference. +/// +/// ## Examples +/// +/// The following example demonstrates how to update a [`Cpumask`]. +/// +/// ``` +/// use kernel::bindings; +/// use kernel::cpumask::Cpumask; +/// +/// fn set_clear_cpu(ptr: *mut bindings::cpumask, set_cpu: u32, clear_cpu:= i32) { +/// // SAFETY: The `ptr` is valid for writing and remains valid for th= e lifetime of the +/// // returned reference. +/// let mask =3D unsafe { Cpumask::as_mut_ref(ptr) }; +/// +/// mask.set(set_cpu); +/// mask.clear(clear_cpu); +/// } +/// ``` +#[repr(transparent)] +pub struct Cpumask(Opaque); + +impl Cpumask { + /// Creates a mutable reference to an existing `struct cpumask` pointe= r. + /// + /// # Safety + /// + /// The caller must ensure that `ptr` is valid for writing and remains= valid for the lifetime + /// of the returned reference. + pub unsafe fn as_mut_ref<'a>(ptr: *mut bindings::cpumask) -> &'a mut S= elf { + // SAFETY: Guaranteed by the safety requirements of the function. + // + // INVARIANT: The caller ensures that `ptr` is valid for writing a= nd remains valid for the + // lifetime of the returned reference. + unsafe { &mut *ptr.cast() } + } + + /// Creates a reference to an existing `struct cpumask` pointer. + /// + /// # Safety + /// + /// The caller must ensure that `ptr` is valid for reading and remains= valid for the lifetime + /// of the returned reference. + pub unsafe fn as_ref<'a>(ptr: *const bindings::cpumask) -> &'a Self { + // SAFETY: Guaranteed by the safety requirements of the function. + // + // INVARIANT: The caller ensures that `ptr` is valid for reading a= nd remains valid for the + // lifetime of the returned reference. + unsafe { &*ptr.cast() } + } + + /// Obtain the raw `struct cpumask` pointer. + pub fn as_raw(&self) -> *mut bindings::cpumask { + let this: *const Self =3D self; + this.cast_mut().cast() + } + + /// Set `cpu` in the cpumask. + /// + /// ATTENTION: Contrary to C, this Rust `set()` method is non-atomic. + /// This mismatches kernel naming convention and corresponds to the C + /// function `__cpumask_set_cpu()`. + #[inline] + pub fn set(&mut self, cpu: u32) { + // SAFETY: By the type invariant, `self.as_raw` is a valid argumen= t to `__cpumask_set_cpu`. + unsafe { bindings::__cpumask_set_cpu(cpu, self.as_raw()) }; + } + + /// Clear `cpu` in the cpumask. + /// + /// ATTENTION: Contrary to C, this Rust `clear()` method is non-atomic. + /// This mismatches kernel naming convention and corresponds to the C + /// function `__cpumask_clear_cpu()`. + #[inline] + pub fn clear(&mut self, cpu: i32) { + // SAFETY: By the type invariant, `self.as_raw` is a valid argumen= t to + // `__cpumask_clear_cpu`. + unsafe { bindings::__cpumask_clear_cpu(cpu, self.as_raw()) }; + } + + /// Test `cpu` in the cpumask. + /// + /// Equivalent to the kernel's `cpumask_test_cpu` API. + #[inline] + pub fn test(&self, cpu: i32) -> bool { + // SAFETY: By the type invariant, `self.as_raw` is a valid argumen= t to `cpumask_test_cpu`. + unsafe { bindings::cpumask_test_cpu(cpu, self.as_raw()) } + } + + /// Set all CPUs in the cpumask. + /// + /// Equivalent to the kernel's `cpumask_setall` API. + #[inline] + pub fn setall(&mut self) { + // SAFETY: By the type invariant, `self.as_raw` is a valid argumen= t to `cpumask_setall`. + unsafe { bindings::cpumask_setall(self.as_raw()) }; + } + + /// Checks if cpumask is empty. + /// + /// Equivalent to the kernel's `cpumask_empty` API. + #[inline] + pub fn empty(&self) -> bool { + // SAFETY: By the type invariant, `self.as_raw` is a valid argumen= t to `cpumask_empty`. + unsafe { bindings::cpumask_empty(self.as_raw()) } + } + + /// Checks if cpumask is full. + /// + /// Equivalent to the kernel's `cpumask_full` API. + #[inline] + pub fn full(&self) -> bool { + // SAFETY: By the type invariant, `self.as_raw` is a valid argumen= t to `cpumask_full`. + unsafe { bindings::cpumask_full(self.as_raw()) } + } + + /// Get weight of the cpumask. + /// + /// Equivalent to the kernel's `cpumask_weight` API. + #[inline] + pub fn weight(&self) -> u32 { + // SAFETY: By the type invariant, `self.as_raw` is a valid argumen= t to `cpumask_weight`. + unsafe { bindings::cpumask_weight(self.as_raw()) } + } + + /// Copy cpumask. + /// + /// Equivalent to the kernel's `cpumask_copy` API. + #[inline] + pub fn copy(&self, dstp: &mut Self) { + // SAFETY: By the type invariant, `Self::as_raw` is a valid argume= nt to `cpumask_copy`. + unsafe { bindings::cpumask_copy(dstp.as_raw(), self.as_raw()) }; + } +} + +/// A CPU Mask pointer. +/// +/// Rust abstraction for the C `struct cpumask_var_t`. +/// +/// # Invariants +/// +/// A [`CpumaskVar`] instance always corresponds to a valid C `struct cpum= ask_var_t`. +/// +/// The callers must ensure that the `struct cpumask_var_t` is valid for a= ccess and remains valid +/// for the lifetime of [`CpumaskVar`]. +/// +/// ## Examples +/// +/// The following example demonstrates how to create and update a [`Cpumas= kVar`]. +/// +/// ``` +/// use kernel::cpumask::CpumaskVar; +/// +/// let mut mask =3D CpumaskVar::new_zero(GFP_KERNEL).unwrap(); +/// +/// assert!(mask.empty()); +/// mask.set(2); +/// assert!(mask.test(2)); +/// mask.set(3); +/// assert!(mask.test(3)); +/// assert_eq!(mask.weight(), 2); +/// +/// let mask2 =3D CpumaskVar::try_clone(&mask).unwrap(); +/// assert!(mask2.test(2)); +/// assert!(mask2.test(3)); +/// assert_eq!(mask2.weight(), 2); +/// ``` +pub struct CpumaskVar { + #[cfg(CONFIG_CPUMASK_OFFSTACK)] + ptr: NonNull, + #[cfg(not(CONFIG_CPUMASK_OFFSTACK))] + mask: Cpumask, +} + +impl CpumaskVar { + /// Creates a zero-initialized instance of the [`CpumaskVar`]. + pub fn new_zero(_flags: Flags) -> Result { + Ok(Self { + #[cfg(CONFIG_CPUMASK_OFFSTACK)] + ptr: { + let mut ptr: *mut bindings::cpumask =3D ptr::null_mut(); + + // SAFETY: It is safe to call this method as the reference= to `ptr` is valid. + // + // INVARIANT: The associated memory is freed when the `Cpu= maskVar` goes out of + // scope. + unsafe { bindings::zalloc_cpumask_var(&mut ptr, _flags.as_= raw()) }; + NonNull::new(ptr.cast()).ok_or(AllocError)? + }, + + #[cfg(not(CONFIG_CPUMASK_OFFSTACK))] + // SAFETY: FFI type is valid to be zero-initialized. + // + // INVARIANT: The associated memory is freed when the `Cpumask= Var` goes out of scope. + mask: unsafe { core::mem::zeroed() }, + }) + } + + /// Creates an instance of the [`CpumaskVar`]. + /// + /// # Safety + /// + /// The caller must ensure that the returned [`CpumaskVar`] is properl= y initialized before + /// getting used. + pub unsafe fn new(_flags: Flags) -> Result { + Ok(Self { + #[cfg(CONFIG_CPUMASK_OFFSTACK)] + ptr: { + let mut ptr: *mut bindings::cpumask =3D ptr::null_mut(); + + // SAFETY: It is safe to call this method as the reference= to `ptr` is valid. + // + // INVARIANT: The associated memory is freed when the `Cpu= maskVar` goes out of + // scope. + unsafe { bindings::alloc_cpumask_var(&mut ptr, _flags.as_r= aw()) }; + NonNull::new(ptr.cast()).ok_or(AllocError)? + }, + #[cfg(not(CONFIG_CPUMASK_OFFSTACK))] + // SAFETY: Guaranteed by the safety requirements of the functi= on. + // + // INVARIANT: The associated memory is freed when the `Cpumask= Var` goes out of scope. + mask: unsafe { MaybeUninit::uninit().assume_init() }, + }) + } + + /// Creates a mutable reference to an existing `struct cpumask_var_t` = pointer. + /// + /// # Safety + /// + /// The caller must ensure that `ptr` is valid for writing and remains= valid for the lifetime + /// of the returned reference. + pub unsafe fn as_mut_ref<'a>(ptr: *mut bindings::cpumask_var_t) -> &'a= mut Self { + // SAFETY: Guaranteed by the safety requirements of the function. + // + // INVARIANT: The caller ensures that `ptr` is valid for writing a= nd remains valid for the + // lifetime of the returned reference. + unsafe { &mut *ptr.cast() } + } + + /// Creates a reference to an existing `struct cpumask_var_t` pointer. + /// + /// # Safety + /// + /// The caller must ensure that `ptr` is valid for reading and remains= valid for the lifetime + /// of the returned reference. + pub unsafe fn as_ref<'a>(ptr: *const bindings::cpumask_var_t) -> &'a S= elf { + // SAFETY: Guaranteed by the safety requirements of the function. + // + // INVARIANT: The caller ensures that `ptr` is valid for reading a= nd remains valid for the + // lifetime of the returned reference. + unsafe { &*ptr.cast() } + } + + /// Clones cpumask. + pub fn try_clone(cpumask: &Cpumask) -> Result { + // SAFETY: The returned cpumask_var is initialized right after thi= s call. + let mut cpumask_var =3D unsafe { Self::new(GFP_KERNEL) }?; + + cpumask.copy(&mut cpumask_var); + Ok(cpumask_var) + } +} + +// Make [`CpumaskVar`] behave like a pointer to [`Cpumask`]. +impl Deref for CpumaskVar { + type Target =3D Cpumask; + + #[cfg(CONFIG_CPUMASK_OFFSTACK)] + fn deref(&self) -> &Self::Target { + // SAFETY: The caller owns CpumaskVar, so it is safe to deref the = cpumask. + unsafe { &*self.ptr.as_ptr() } + } + + #[cfg(not(CONFIG_CPUMASK_OFFSTACK))] + fn deref(&self) -> &Self::Target { + &self.mask + } +} + +impl DerefMut for CpumaskVar { + #[cfg(CONFIG_CPUMASK_OFFSTACK)] + fn deref_mut(&mut self) -> &mut Cpumask { + // SAFETY: The caller owns CpumaskVar, so it is safe to deref the = cpumask. + unsafe { self.ptr.as_mut() } + } + + #[cfg(not(CONFIG_CPUMASK_OFFSTACK))] + fn deref_mut(&mut self) -> &mut Cpumask { + &mut self.mask + } +} + +impl Drop for CpumaskVar { + fn drop(&mut self) { + #[cfg(CONFIG_CPUMASK_OFFSTACK)] + // SAFETY: By the type invariant, `self.as_raw` is a valid argumen= t to `free_cpumask_var`. + unsafe { + bindings::free_cpumask_var(self.as_raw()) + }; + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index de07aadd1ff5..75f78f6bfaa6 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -42,6 +42,7 @@ pub mod block; #[doc(hidden)] pub mod build_assert; +pub mod cpumask; pub mod cred; pub mod device; pub mod device_id; --=20 2.31.1.272.g89b43f80a514 From nobody Sun Dec 14 19:11:21 2025 Received: from mail-pf1-f173.google.com (mail-pf1-f173.google.com [209.85.210.173]) (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 6E4D11C5D7A for ; Mon, 21 Apr 2025 07:22:55 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745220176; cv=none; b=IR0eG3MYR4+dPGq6P5yzhq14S1WG7/+d26s3sK6Ur+shn0I5ykVw/wznht+RWYyRaRwcQfyy6YkWyUqBnyzoAGDMK6KCwErKYyMZd3/ohtlWPfdBDJRYay6nMRdh/N/7UaDwsBGkpNxPo63fyPlT5dfEmykLHAd7aXjmRpQqg4c= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745220176; c=relaxed/simple; bh=pRm5rikPg836Cpaht6T2Bti6G/gsdEEiL/oreW2ehWs=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=nb/dX6CVsTz5t34lAdzYAxIFDIN3wEIlo0xBTzhuUBhsLuF7zU+MkBgXn4W7KLRnyY4lJIRv8K8sDfImGc8PDK6Y63e82K0zwwcLvye1UXkbL5/GG7awCE9zBGyz5rm8wld+va05yguyAMjwR0xZtWvFsNAan8Q1Y1nbUXxSWAE= 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=dRpbkr5m; arc=none smtp.client-ip=209.85.210.173 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="dRpbkr5m" Received: by mail-pf1-f173.google.com with SMTP id d2e1a72fcca58-736a7e126c7so2975368b3a.3 for ; Mon, 21 Apr 2025 00:22:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1745220175; x=1745824975; 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=eaMUD1BDVL417dtmG3+/1YWJeUCWl78+KVSs+NX3TJI=; b=dRpbkr5mOExVNv/LE5aQ0NXF1krTL2421Ot2emyRxVPV2+PcXVB4vugScyqF90S5/U a2/+H7z0fKiYGgMqTUSEmCzXqj9MhlJ7gRl0R8yjdwLRMiwmCJIwk53HGdW4lyZRhhWL SKmDDQ1jFawIf2PZoubxTW2+lr43PpVxiyKqbsCAFpRvogJP7tTI8oPFNv87acCsbkQu b/aTZKUUbVlGI3aYElo0qRKLAPpKv6AkX/L12hmcLz8Am/X8oCzuAcP2u7pS9GZ9HbiW p5MdrfFlrnS0sqjTp5mZPlM4uVBzTBvW0K1wEZOYVbEWNeKMHdR3Fv4KMnQSC/50teeh xFuQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1745220175; x=1745824975; 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=eaMUD1BDVL417dtmG3+/1YWJeUCWl78+KVSs+NX3TJI=; b=MwbAZRF3iESgTLjkdNN28/o6Alo6UChwwwpBN6Rw8opBVA85ok/OZFY5AMRw7vmmON RI0yOqR2J7JKZRchBx1mryul4yGyuh6SVsYqShS15BizMdA3CVoq/0uABKouEyy0XMci a6zSUvVJhEbAAgMfUOmhDmvfNVkblTAAsCSBNxUa18KpiuYOBH3UyEMeiDjLH1sUzjdQ gx8WpbDQKhsHNksHXZvllPlbmgdNe2+W9RyckuzNnD84Qf0dZnVoci1L0EbSm+7ioZaR AC/qGLtc4ThwJGLg5rc/63vi+WiK9Idjt0MOqumU1CwFRICzjZVvI4qzs1aCmFjaO5E/ Py5w== X-Forwarded-Encrypted: i=1; AJvYcCW8tORcSkcNPJVDoQVETTNwMNRb+2y5sI6JB6h8P8njFUcvtjRRh0RugiTckK0IrAySGhu9p3Dw4A0Ax2I=@vger.kernel.org X-Gm-Message-State: AOJu0Ywll7pnq7pfGBJYmD2xf2KgxZ2bY08P7a6rY/bFBeRDLi7X+P7x 3BmbQcjzYuXSUA/Aq49pjqAK+bvdVie6ujjV1/5HKP7FDjVXFmHRcpVmbV57veo= X-Gm-Gg: ASbGncvx5HgyH62oPSgkPEOfAhNsEBQ9qhQDNrg7WkWq9kQLOn/mhuRuZCZgA9AgRBp jtMQIBLmy7coEeSOzujCvAIpOd6sq9SEHU6DuE4ek04Eu9oXPHu+wIfjmpsBFLH9r4RSnIVfnTU 9ZD7raee2quMJ4F1O50k3N0LU0lWcDh8bznkzameJ8AtVNbvwKFS/CquGjbJ9PIaqqvFALyo+L2 5HRBQFFCnMPGDrHj4ZJdxZKItNJKZ6JBMuU4ct85DDBhzvYLx2vJzGGShZqMuJZ+qrey4p5S9EL V90jgUkrg8V62EEnY36fCbhmyp+96B817XnQqvG7WQ== X-Google-Smtp-Source: AGHT+IHNDpp0wsf3Dmxq9+k3By7x6rJU597c7yr2fhSHUbNSUXO88WHLo2YiD1Xe1S6gEIR8PM3vPg== X-Received: by 2002:a05:6a20:6f8d:b0:1f5:619a:8f73 with SMTP id adf61e73a8af0-203cbd2058bmr14110631637.26.1745220174840; Mon, 21 Apr 2025 00:22:54 -0700 (PDT) Received: from localhost ([122.172.83.32]) by smtp.gmail.com with ESMTPSA id 41be03b00d2f7-b0db13a41a2sm4998462a12.21.2025.04.21.00.22.53 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Apr 2025 00:22:54 -0700 (PDT) From: Viresh Kumar To: "Rafael J. Wysocki" , Miguel Ojeda , Danilo Krummrich , 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 , Stephen Boyd , Nishanth Menon , 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 03/15] MAINTAINERS: Add entry for Rust cpumask API Date: Mon, 21 Apr 2025 12:52:10 +0530 Message-Id: <9115c43e4eae9fd958344e7befc945e0e649a6c5.1745218975.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" Update the MAINTAINERS file to include the Rust abstractions for cpumask API. Yury has indicated that he does not wish to maintain the Rust code but would like to be listed as a reviewer. Signed-off-by: Viresh Kumar Reviewed-by: Yury Norov --- MAINTAINERS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 96b827049501..bd7c54af4fd4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6237,6 +6237,12 @@ L: linux-riscv@lists.infradead.org S: Maintained F: drivers/cpuidle/cpuidle-riscv-sbi.c =20 +CPUMASK API [RUST] +M: Viresh Kumar +R: Yury Norov +S: Maintained +F: rust/kernel/cpumask.rs + CRAMFS FILESYSTEM M: Nicolas Pitre S: Maintained --=20 2.31.1.272.g89b43f80a514 From nobody Sun Dec 14 19:11:21 2025 Received: from mail-pl1-f169.google.com (mail-pl1-f169.google.com [209.85.214.169]) (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 B96CB21322B for ; Mon, 21 Apr 2025 07:22:59 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.169 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745220181; cv=none; b=los9o9XfvHF7fPP7nDMO9HXuQms0F4mpUaHg+tpRwiv9Cpl5dA0pZsm+0HEXI9j4IFtLuDPdOzNRIQGnCjhOCODdaM9Qqct84dhihoWF5FWqXmvyCxPxzLizMlqSmjgBV7XFSS9n4pKjA4JQpbmAyTdG0lG7DYNqP5K8bs78/Fc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745220181; c=relaxed/simple; bh=4HWksdgns8ejVb++Q4NJVdKDSuHEDHdMmvi+j+YhGlo=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=nR2Fxqqo6Y/CU1T0nnUHjxmXAOaylyG3IzXXlPH77/elgTzvQdZEkXhZ7Qq+w9IsZvJVF1Rb19SIvdeB8wYkKBtj32cfa3Eyjoq5Dgzc/IBrH07NkJ6Vvb2htN+o36JH7bIS7uDr6zmPH69Hc5EAFBvyDVFoApdH5R89D2g4IzI= 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=gCVK+MGo; arc=none smtp.client-ip=209.85.214.169 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="gCVK+MGo" Received: by mail-pl1-f169.google.com with SMTP id d9443c01a7336-227914acd20so36992395ad.1 for ; Mon, 21 Apr 2025 00:22:59 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1745220179; x=1745824979; 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=WNsggH78Gujq22EIg5QA+ygoUlPHiqAj8S6vH5ayXvk=; b=gCVK+MGoFcq8T9M7dKRL77rn/KjhTtQugWexbw7oL6x6lHjgpoVTDBAZk5dj/yctiJ 3PU2r+3r9RIHBMC6yZFidl2woDvGCQuns5GN523MHPB3QXCfZ03zuKJWjrOUNMh0acqD CETr+G0uAEwCL1iOf17Yak4l+jutYIa4Tyd/KyBMR5A7IkJIz/nq0J7FBR6ikXC/ITYH RW4b6OSE08Yzk/6kyGFxhUVfFpaOtBCA8MCW7M0Kef0mLJc9UquPDcKLLh/cwkCbOHru pBYlpwTSI5jdeyIAdGzgipZhaimAKElli1+x0HmasukEb0FuGPMXVu7JLsCP1LULFX3g AHHQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1745220179; x=1745824979; 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=WNsggH78Gujq22EIg5QA+ygoUlPHiqAj8S6vH5ayXvk=; b=q39pQbrgcd3gyNq+jQ0sEczOogI45a7d4keG8kHPTCup7YY2hF/wZRmtWBobck5wzQ IlEsf9mSWMdXRSmlypEHxezVyMuEweFbGsEgKJ6Tc5V2QGsSSgngTpQvk8lyrJKEHLZD K/Y+j6cUZRKGzoe8UzHG/Nti1TNOWtZrV5+poDHCLVSoL9oeW7ioI+8OBoyJWSP5x4CS Vt4y27KzCnQ7hmxvP5+PER/v4eOSadDBTjngLgG3WxoEwvVqM7LL0z3NOlmCoN1H/zlG QpJvrKidg9+CDGYXyhv+JugjwMaWZc/mjRc6BjACoAgYAB9lYP6rIOacTJm3ZI7A5MHT 1siQ== X-Forwarded-Encrypted: i=1; AJvYcCV3iU9Uchc37FTeskPEbvYqO8LoZILToP/3MBXcV16gh1aDMHBEp1hjisjzbH3hSqWavOklwcAIbxEtilA=@vger.kernel.org X-Gm-Message-State: AOJu0YyIWW/wSY5hH7M+PVWc4bMDFRx6w84Yy6iFnnoO2j7VDNORXDJI HjgXkdFvaBgIefjYx6MafZt2DXAyuHmEVMrqud5IjV8hAdE7d9wvvypm7fNFauY= X-Gm-Gg: ASbGncs9GvLzElLSksTWO3PvM1NWMq2Opsul9ADRuNLVl6B2NOwxH+fi0DN2p+GXKFS 4pBXzCDmN/iDomlZbB6HmpCGXY5etYRF5eRQ32SZ1m5FTRchlauIMwCezWzpYHOzSHFct64Q0Rr WaEJlfmlzpti15Wr/5SCOeut7BLF92oo+/8DPd5I6ZS+wEv44Uh8kDxa1wXCQWfCsM7gbKWhOrn 5CJVtB4Gj11pj1J8cQoPBxWo9lhVmewRurvvizzSKwEBt0alHOIxQV+rv9eD0Db4rBM6JoQQWIO aC1VRr9LQyerVg++KsIZbWLAynqSNQDPj1Rdmtolng== X-Google-Smtp-Source: AGHT+IFmAEZmVfF2/jJIFd2AX+TRAi14+CXqAGbQyZTBUCMo9uzwZXqwwPnyIMPXsyXmqJRV3PygbA== X-Received: by 2002:a17:902:cf05:b0:21f:98fc:8414 with SMTP id d9443c01a7336-22c50d8c10fmr163080145ad.26.1745220178930; Mon, 21 Apr 2025 00:22:58 -0700 (PDT) Received: from localhost ([122.172.83.32]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-73dbfaacf48sm6086222b3a.148.2025.04.21.00.22.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Apr 2025 00:22:58 -0700 (PDT) From: Viresh Kumar To: "Rafael J. Wysocki" , Miguel Ojeda , Danilo Krummrich , 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 , Michael Turquette , Stephen Boyd Cc: Viresh Kumar , linux-pm@vger.kernel.org, Vincent Guittot , Nishanth Menon , 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, Daniel Almeida , linux-kernel@vger.kernel.org Subject: [PATCH V11 04/15] rust: clk: Add helpers for Rust code Date: Mon, 21 Apr 2025 12:52:11 +0530 Message-Id: <97741e1c82feecbd650183c6a52d2e8bbafad79f.1745218975.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" Non-trivial C macros and inlined C functions cannot be used directly in the Rust code and are used via functions ("helpers") that wrap those so that they can be called from Rust. In order to prepare for adding Rust abstractions for the clock APIs, add clock helpers required by the Rust implementation. Reviewed-by: Daniel Almeida Signed-off-by: Viresh Kumar --- MAINTAINERS | 1 + rust/bindings/bindings_helper.h | 1 + rust/helpers/clk.c | 66 +++++++++++++++++++++++++++++++++ rust/helpers/helpers.c | 1 + 4 files changed, 69 insertions(+) create mode 100644 rust/helpers/clk.c diff --git a/MAINTAINERS b/MAINTAINERS index bd7c54af4fd4..608689342aaf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5883,6 +5883,7 @@ F: include/dt-bindings/clock/ F: include/linux/clk-pr* F: include/linux/clk/ F: include/linux/of_clk.h +F: rust/helpers/clk.c X: drivers/clk/clkdev.c =20 COMMON INTERNET FILE SYSTEM CLIENT (CIFS and SMB3) diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index ab37e1d35c70..f53d6e1a21f2 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers/clk.c b/rust/helpers/clk.c new file mode 100644 index 000000000000..6d04372c9f3b --- /dev/null +++ b/rust/helpers/clk.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +/* + * The "inline" implementation of below helpers are only available when + * CONFIG_HAVE_CLK or CONFIG_HAVE_CLK_PREPARE aren't set. + */ +#ifndef CONFIG_HAVE_CLK +struct clk *rust_helper_clk_get(struct device *dev, const char *id) +{ + return clk_get(dev, id); +} + +void rust_helper_clk_put(struct clk *clk) +{ + clk_put(clk); +} + +int rust_helper_clk_enable(struct clk *clk) +{ + return clk_enable(clk); +} + +void rust_helper_clk_disable(struct clk *clk) +{ + clk_disable(clk); +} + +unsigned long rust_helper_clk_get_rate(struct clk *clk) +{ + return clk_get_rate(clk); +} + +int rust_helper_clk_set_rate(struct clk *clk, unsigned long rate) +{ + return clk_set_rate(clk, rate); +} +#endif + +#ifndef CONFIG_HAVE_CLK_PREPARE +int rust_helper_clk_prepare(struct clk *clk) +{ + return clk_prepare(clk); +} + +void rust_helper_clk_unprepare(struct clk *clk) +{ + clk_unprepare(clk); +} +#endif + +struct clk *rust_helper_clk_get_optional(struct device *dev, const char *i= d) +{ + return clk_get_optional(dev, id); +} + +int rust_helper_clk_prepare_enable(struct clk *clk) +{ + return clk_prepare_enable(clk); +} + +void rust_helper_clk_disable_unprepare(struct clk *clk) +{ + clk_disable_unprepare(clk); +} diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index e1c21eba9b15..ae595c9cd91b 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -11,6 +11,7 @@ #include "bug.c" #include "build_assert.c" #include "build_bug.c" +#include "clk.c" #include "cpumask.c" #include "cred.c" #include "device.c" --=20 2.31.1.272.g89b43f80a514 From nobody Sun Dec 14 19:11:21 2025 Received: from mail-pj1-f44.google.com (mail-pj1-f44.google.com [209.85.216.44]) (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 00C3821421D for ; Mon, 21 Apr 2025 07:23:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.44 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745220185; cv=none; b=RmIaHw0ZlT15LCbJHOSDcAcvga4TMK8lvLq7lKJPjxMY8nmfpaNV+AY/GOsQYP5St8iMqwYk4RYkUuUcIJXUlBilC/H0h5V4j3P4af2/7wJaMpJRFFlIak1spC1j5W2dGNmbuMgkUA/4WGg1WBzGs7c3clhrWZw6reSNAmZRaXk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745220185; c=relaxed/simple; bh=OK9LP8IuB4dZ4tNPJ7BqHK7tgUFvTgGIoVzWhWAVPdY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=FKPIT5lFH8AQgLuMW5j6eF7fPEczoa6Op49iPhD4oGd36Kj1tjaGb+doupFqAmx+SgUpN0UZCGc98TsnDS4TrNETaQiC7+EPCmVZ0/3lB/oEbtsYJ3E3/r62g/qsAD+PFJMDhjEYSjQCRiWyL6h8cIrlsvTDcsRxNoDcdQpoSYE= 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=M1uCaZDl; arc=none smtp.client-ip=209.85.216.44 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="M1uCaZDl" Received: by mail-pj1-f44.google.com with SMTP id 98e67ed59e1d1-30549dacd53so2855667a91.1 for ; Mon, 21 Apr 2025 00:23:03 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1745220183; x=1745824983; 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=JtiIzVJT0XSWQl5nAIRSPSyCufgx9olJIUW0gvaR0zI=; b=M1uCaZDl8ZuuVrXq4TUUL++JNgnHWs5QsYSKl0tPyNQayOy9dKOeFk2THoVExq7BLE Dy6cGYijQMxN4ND5sb4LuxnYJ9Qp/1bBAz7MUbm29THxgsLcY67QzvRlcDkr5NphjZp7 RI36sx3T/nnvRK94U2pqj2Msqr/D/ecwK2Enk2buXxYz8tuwsXIHu2A9pOorD/ZJmjKf yL9cvkVS5gUgBFS0g4Zh98vHUQLfERX+q7RVtoRhl+pp+6OnT0Y04MW3XV+I1EU4nV9d Dj97yQKXc+mdaiGaMfijG56N47UbqGgJ0uD9umRVVbWRZGYV6an7M83NI8dT+p5bOVz6 T0pg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1745220183; x=1745824983; 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=JtiIzVJT0XSWQl5nAIRSPSyCufgx9olJIUW0gvaR0zI=; b=b7ApWFVHf5iPH7onwR0CMXc+xKaiwYA3U3wtCNSrSwRN5OkrHIFMEuiXPZ4jJJbn/M eaM6mNvZVOF0efGK++AvOaNf+8C/FhAX22qsOHqUgCvLsHQ0+ZyDfy4TI5WLodjlXbab NXjZ4BRl2u9R02tGHTSEdvBCJ7rNDwSmnoeeZoAUt5js78pZ+5dllYXhaWq/NYEjVU47 cfQ9cd5icEcdyt72aq+m9dQM+R5EeNgSlPMwwIZGhyGfSbCnL4ZLO5qWdw9EEiQGhWHg nTHN66yURo6kr3ak0idtLGM07KXQVjFv8Kc+ve0tueiAfG8wEWM5hIE5oJCZAG1SocX9 9Z+g== X-Forwarded-Encrypted: i=1; AJvYcCVF/WnQxBeBV2QnaPM/Hx8vA5Sfdy3Z9SSWgx3UhOI41pYCLYORhO3FyG4z3ZLVHQCU3piCO90Yt3i1HsA=@vger.kernel.org X-Gm-Message-State: AOJu0YwAeI7/vtbhfPYJNHrQdSst32iPl029afE7N45lI/79Bq2C6zo2 jnv8VZA87bX8cXmvD0WH7pCXgQV4p9Jj8IsnQJCoQYFcQYVB7UmzC9ctMJBZxhA= X-Gm-Gg: ASbGnctR5NNDpwypeJUoPOFLjrRIc9oITRwhXEqOqLIi8SR4E5sLgs7k42c2Dm3N6mB 9i9R8sbHgmDkDmfJj04uXIfALu8++/N5cDj84OtZGOCPQ0kNqfsiumu6T6fIPRwC6ylNGfM6cPK hVUcI2zC6zz1GXKs/LqXPCo3CnGZyjMemJr1bx0MgZZ+um8GgSwvzWU92DRSoQWFBQZE5CUK++m HjIdcESKiZ1OzLPfQpHgu+ZBvx3VSv0k8lAfgQERNvomIeGxpjthrVsiUTk+R5yu9eVKd0kz/b8 zWRqdm8cM+tvqFXxDdGWfqKovzNloXxMGj4tXTVxLA== X-Google-Smtp-Source: AGHT+IG/K1n9MVVWhTOCeTnXsbYnm9I3PSY9j+F3zLgSt0JORaaGUxrZBw1+JjRYJ5ORjK0iHn/kNQ== X-Received: by 2002:a17:90a:e18f:b0:2ee:c30f:33c9 with SMTP id 98e67ed59e1d1-3087c36106amr16259582a91.14.1745220183161; Mon, 21 Apr 2025 00:23:03 -0700 (PDT) Received: from localhost ([122.172.83.32]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3087dee8956sm6708675a91.8.2025.04.21.00.23.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Apr 2025 00:23:02 -0700 (PDT) From: Viresh Kumar To: "Rafael J. Wysocki" , Miguel Ojeda , Danilo Krummrich , Michael Turquette , 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 , Nishanth Menon , 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, Daniel Almeida , linux-kernel@vger.kernel.org Subject: [PATCH V11 05/15] rust: clk: Add initial abstractions Date: Mon, 21 Apr 2025 12:52:12 +0530 Message-Id: <4207f5bf540de148e1bd31a88dc75eaac1aaf897.1745218975.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" Add initial abstractions for the clk APIs. These provide the minimal functionality needed for common use cases, making them straightforward to introduce in the first iteration. These will be used by Rust based cpufreq / OPP layers to begin with. Tested-by: Daniel Almeida Reviewed-by: Daniel Almeida Signed-off-by: Viresh Kumar --- MAINTAINERS | 1 + rust/kernel/clk.rs | 318 +++++++++++++++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 2 + 3 files changed, 321 insertions(+) create mode 100644 rust/kernel/clk.rs diff --git a/MAINTAINERS b/MAINTAINERS index 608689342aaf..12cde55579a0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5884,6 +5884,7 @@ F: include/linux/clk-pr* F: include/linux/clk/ F: include/linux/of_clk.h F: rust/helpers/clk.c +F: rust/kernel/clk.rs X: drivers/clk/clkdev.c =20 COMMON INTERNET FILE SYSTEM CLIENT (CIFS and SMB3) diff --git a/rust/kernel/clk.rs b/rust/kernel/clk.rs new file mode 100644 index 000000000000..698e05cd41f8 --- /dev/null +++ b/rust/kernel/clk.rs @@ -0,0 +1,318 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Clock abstractions. +//! +//! C header: [`include/linux/clk.h`](srctree/include/linux/clk.h) +//! +//! Reference: + +use crate::{ + device::Device, + error::{from_err_ptr, to_result, Result}, + ffi::c_ulong, + prelude::*, +}; + +use core::{ops::Deref, ptr}; + +/// The frequency unit. +/// +/// Represents a frequency in hertz, wrapping a [`c_ulong`] value. +/// +/// ## Examples +/// +/// ``` +/// use kernel::clk::Hertz; +/// +/// let hz =3D 1_000_000_000; +/// let rate =3D Hertz(hz); +/// +/// assert_eq!(rate.as_hz(), hz); +/// assert_eq!(rate, Hertz(hz)); +/// assert_eq!(rate, Hertz::from_khz(hz / 1_000)); +/// assert_eq!(rate, Hertz::from_mhz(hz / 1_000_000)); +/// assert_eq!(rate, Hertz::from_ghz(hz / 1_000_000_000)); +/// ``` +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct Hertz(pub c_ulong); + +impl Hertz { + /// Create a new instance from kilohertz (kHz) + pub fn from_khz(khz: c_ulong) -> Self { + Self(khz * 1_000) + } + + /// Create a new instance from megahertz (MHz) + pub fn from_mhz(mhz: c_ulong) -> Self { + Self(mhz * 1_000_000) + } + + /// Create a new instance from gigahertz (GHz) + pub fn from_ghz(ghz: c_ulong) -> Self { + Self(ghz * 1_000_000_000) + } + + /// Get the frequency in hertz + pub fn as_hz(&self) -> c_ulong { + self.0 + } + + /// Get the frequency in kilohertz + pub fn as_khz(&self) -> c_ulong { + self.0 / 1_000 + } + + /// Get the frequency in megahertz + pub fn as_mhz(&self) -> c_ulong { + self.0 / 1_000_000 + } + + /// Get the frequency in gigahertz + pub fn as_ghz(&self) -> c_ulong { + self.0 / 1_000_000_000 + } +} + +impl From for c_ulong { + fn from(freq: Hertz) -> Self { + freq.0 + } +} + +/// A reference-counted clock. +/// +/// Rust abstraction for the C [`struct clk`]. +/// +/// # Invariants +/// +/// A [`Clk`] instance holds either a pointer to a valid [`struct clk`] cr= eated by the C portion of +/// the kernel or a NULL pointer. +/// +/// Instances of this type are reference-counted. Calling [`Clk::get`] ens= ures that the allocation +/// remains valid for the lifetime of the [`Clk`]. +/// +/// ## Examples +/// +/// The following example demonstrates how to obtain and configure a clock= for a device. +/// +/// ``` +/// use kernel::c_str; +/// use kernel::clk::{Clk, Hertz}; +/// use kernel::device::Device; +/// use kernel::error::Result; +/// +/// fn configure_clk(dev: &Device) -> Result { +/// let clk =3D Clk::get(dev, Some(c_str!("apb_clk")))?; +/// +/// clk.prepare_enable()?; +/// +/// let expected_rate =3D Hertz::from_ghz(1); +/// +/// if clk.rate() !=3D expected_rate { +/// clk.set_rate(expected_rate)?; +/// } +/// +/// clk.disable_unprepare(); +/// Ok(()) +/// } +/// ``` +/// +/// [`struct clk`]: https://docs.kernel.org/driver-api/clk.html +#[repr(transparent)] +pub struct Clk(*mut bindings::clk); + +impl Clk { + /// Gets [`Clk`] corresponding to a [`Device`] and a connection id. + /// + /// Equivalent to the kernel's [`clk_get`] API. + /// + /// [`clk_get`]: https://docs.kernel.org/core-api/kernel-api.html#c.cl= k_get + pub fn get(dev: &Device, name: Option<&CStr>) -> Result { + let con_id =3D if let Some(name) =3D name { + name.as_ptr() + } else { + ptr::null() + }; + + // SAFETY: It is safe to call [`clk_get`] for a valid device point= er. + // + // INVARIANT: The reference-count is decremented when [`Clk`] goes= out of scope. + Ok(Self(from_err_ptr(unsafe { + bindings::clk_get(dev.as_raw(), con_id) + })?)) + } + + /// Obtain the raw [`struct clk`] pointer. + #[inline] + pub fn as_raw(&self) -> *mut bindings::clk { + self.0 + } + + /// Enable the clock. + /// + /// Equivalent to the kernel's [`clk_enable`] API. + /// + /// [`clk_enable`]: https://docs.kernel.org/core-api/kernel-api.html#c= .clk_enable + #[inline] + pub fn enable(&self) -> Result { + // SAFETY: By the type invariants, self.as_raw() is a valid argume= nt for [`clk_enable`]. + to_result(unsafe { bindings::clk_enable(self.as_raw()) }) + } + + /// Disable the clock. + /// + /// Equivalent to the kernel's [`clk_disable`] API. + /// + /// [`clk_disable`]: https://docs.kernel.org/core-api/kernel-api.html#= c.clk_disable + #[inline] + pub fn disable(&self) { + // SAFETY: By the type invariants, self.as_raw() is a valid argume= nt for [`clk_disable`]. + unsafe { bindings::clk_disable(self.as_raw()) }; + } + + /// Prepare the clock. + /// + /// Equivalent to the kernel's [`clk_prepare`] API. + /// + /// [`clk_prepare`]: https://docs.kernel.org/core-api/kernel-api.html#= c.clk_prepare + #[inline] + pub fn prepare(&self) -> Result { + // SAFETY: By the type invariants, self.as_raw() is a valid argume= nt for [`clk_prepare`]. + to_result(unsafe { bindings::clk_prepare(self.as_raw()) }) + } + + /// Unprepare the clock. + /// + /// Equivalent to the kernel's [`clk_unprepare`] API. + /// + /// [`clk_unprepare`]: https://docs.kernel.org/core-api/kernel-api.htm= l#c.clk_unprepare + #[inline] + pub fn unprepare(&self) { + // SAFETY: By the type invariants, self.as_raw() is a valid argume= nt for [`clk_unprepare`]. + unsafe { bindings::clk_unprepare(self.as_raw()) }; + } + + /// Prepare and enable the clock. + /// + /// Equivalent to calling [`Clk::prepare`] followed by [`Clk::enable`]. + #[inline] + pub fn prepare_enable(&self) -> Result { + // SAFETY: By the type invariants, self.as_raw() is a valid argume= nt for + // [`clk_prepare_enable`]. + to_result(unsafe { bindings::clk_prepare_enable(self.as_raw()) }) + } + + /// Disable and unprepare the clock. + /// + /// Equivalent to calling [`Clk::disable`] followed by [`Clk::unprepar= e`]. + #[inline] + pub fn disable_unprepare(&self) { + // SAFETY: By the type invariants, self.as_raw() is a valid argume= nt for + // [`clk_disable_unprepare`]. + unsafe { bindings::clk_disable_unprepare(self.as_raw()) }; + } + + /// Get clock's rate. + /// + /// Equivalent to the kernel's [`clk_get_rate`] API. + /// + /// [`clk_get_rate`]: https://docs.kernel.org/core-api/kernel-api.html= #c.clk_get_rate + #[inline] + pub fn rate(&self) -> Hertz { + // SAFETY: By the type invariants, self.as_raw() is a valid argume= nt for [`clk_get_rate`]. + Hertz(unsafe { bindings::clk_get_rate(self.as_raw()) }) + } + + /// Set clock's rate. + /// + /// Equivalent to the kernel's [`clk_set_rate`] API. + /// + /// [`clk_set_rate`]: https://docs.kernel.org/core-api/kernel-api.html= #c.clk_set_rate + #[inline] + pub fn set_rate(&self, rate: Hertz) -> Result { + // SAFETY: By the type invariants, self.as_raw() is a valid argume= nt for [`clk_set_rate`]. + to_result(unsafe { bindings::clk_set_rate(self.as_raw(), rate.as_h= z()) }) + } +} + +impl Drop for Clk { + fn drop(&mut self) { + // SAFETY: By the type invariants, self.as_raw() is a valid argume= nt for [`clk_put`]. + unsafe { bindings::clk_put(self.as_raw()) }; + } +} + +/// A reference-counted optional clock. +/// +/// A lightweight wrapper around an optional [`Clk`]. An [`OptionalClk`] r= epresents a [`Clk`] that +/// a driver can function without but may improve performance or enable ad= ditional features when +/// available. +/// +/// # Invariants +/// +/// An [`OptionalClk`] instance encapsulates a [`Clk`] with either a valid= [`struct clk`] or `NULL` +/// pointer. +/// +/// Instances of this type are reference-counted. Calling [`OptionalClk::g= et`] ensures that the +/// allocation remains valid for the lifetime of the [`OptionalClk`]. +/// +/// ## Examples +/// +/// The following example demonstrates how to obtain and configure an opti= onal clock for a device. +/// The code functions correctly whether or not the clock is available. +/// +/// ``` +/// use kernel::c_str; +/// use kernel::clk::{OptionalClk, Hertz}; +/// use kernel::device::Device; +/// use kernel::error::Result; +/// +/// fn configure_clk(dev: &Device) -> Result { +/// let clk =3D OptionalClk::get(dev, Some(c_str!("apb_clk")))?; +/// +/// clk.prepare_enable()?; +/// +/// let expected_rate =3D Hertz::from_ghz(1); +/// +/// if clk.rate() !=3D expected_rate { +/// clk.set_rate(expected_rate)?; +/// } +/// +/// clk.disable_unprepare(); +/// Ok(()) +/// } +/// ``` +/// +/// [`struct clk`]: https://docs.kernel.org/driver-api/clk.html +pub struct OptionalClk(Clk); + +impl OptionalClk { + /// Gets [`OptionalClk`] corresponding to a [`Device`] and a connectio= n id. + /// + /// Equivalent to the kernel's [`clk_get_optional`] API. + /// + /// [`clk_get_optional`]: https://docs.kernel.org/core-api/kernel-api.= html#c.clk_get_optional + pub fn get(dev: &Device, name: Option<&CStr>) -> Result { + let con_id =3D if let Some(name) =3D name { + name.as_ptr() + } else { + ptr::null() + }; + + // SAFETY: It is safe to call [`clk_get_optional`] for a valid dev= ice pointer. + // + // INVARIANT: The reference-count is decremented when [`OptionalCl= k`] goes out of scope. + Ok(Self(Clk(from_err_ptr(unsafe { + bindings::clk_get_optional(dev.as_raw(), con_id) + })?))) + } +} + +// Make [`OptionalClk`] behave like [`Clk`]. +impl Deref for OptionalClk { + type Target =3D Clk; + + fn deref(&self) -> &Clk { + &self.0 + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 75f78f6bfaa6..f4dcfefe94be 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -42,6 +42,8 @@ pub mod block; #[doc(hidden)] pub mod build_assert; +#[cfg(CONFIG_COMMON_CLK)] +pub mod clk; pub mod cpumask; pub mod cred; pub mod device; --=20 2.31.1.272.g89b43f80a514 From nobody Sun Dec 14 19:11:21 2025 Received: from mail-pl1-f176.google.com (mail-pl1-f176.google.com [209.85.214.176]) (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 64E312561A1 for ; Mon, 21 Apr 2025 07:23:07 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.176 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745220188; cv=none; b=Az9GQWrL+cGTapLFdgLdo+6k7UeGbMYelddeU6bksEAlMCWS8jHxd9yfDpBTe290F9rk8f5Lq+IjfLo5NjBevUueCXCYBMAJQUJOthQSZH0bydDfzXji0GhHC+ubjp33MCqfxbUPOkUoLiPC7iuDVXZAgGSLGuUGHUKW0BbUmFo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745220188; c=relaxed/simple; bh=RPs39+G/2/WRBdJP3KwTQnQlR3Lp/kvURzUIgtDgvwA=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=cYCLMyYomVHpaJFLMxnSHthFm/cBDiwo1ScWFNOV0xgjx/GDe8Vg340S7rCStt1K83ANn4IbSU8oH2LmpGdy50HWfeVjMZnz7g2BIBscsmN6Ubz6vHmN8gvfU0Np1vH3nINKz2i6dkIz3h1cmOiEBUWiZO/DFmczcbkrAVpf7hc= 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=XAfcsTJb; arc=none smtp.client-ip=209.85.214.176 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="XAfcsTJb" Received: by mail-pl1-f176.google.com with SMTP id d9443c01a7336-224191d92e4so36703115ad.3 for ; Mon, 21 Apr 2025 00:23:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1745220186; x=1745824986; 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=06XWI3zWvI8xB81BSzBcpZnXhq7zBjUBfZT4cp51CDs=; b=XAfcsTJbmJL+OSKmyD0WVm243+wjfepWXYh3R7aeLQlzDMSnP/r/MQEgqe17BMucAX s5PULFCXBcEXtR026bMFAxICr1WX2ersvA3SVA1pbSNICCJMnb3vmXzuXhUT+gNbNCHl tj4mkrUbu4cLe0hIk1Z99pD5T6XtKY18X6GnCPaarqahEhRkv9HXYeuOsyzpk1nCseO3 kH/EZB2WipXcNSVzf32IVsAb1hCpDlF8oCi3pika/0lSCkYg7w4n1NmmKS+godqFojyp 9lDSl2eN/oVDnflMpxXDvM3gOdip+5cKUTjN22ka48tZjD/gOvGZweBL0BwPcXpLqwcg HZPA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1745220186; x=1745824986; 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=06XWI3zWvI8xB81BSzBcpZnXhq7zBjUBfZT4cp51CDs=; b=og2b4aWX35BC5mLYMC7Z2N7l8HVyDnsl1jl2Y29X828qfDSN9rJhA4h/dbHgVRAThp lP6ShkVXabUVvrgvdHBTIUJJuYPQpCB4cq/ku05ywnDzB5ThrkglpQ7WSooV3twDyRjJ MeKPWFjs6hV88GX/G4MUYrlX/rXCUZZd5Q/s2aAZhUZ0WOP66CzkeBkKu6EXpKxxmS71 hz2PGYv7WXs3aQftGZHm0CfioDEO8vu08gg2FbJofImv1ZN1WsO1FBMWUPc1hkfv7UUh fPIhOzVD9ggLVAnZmVb3B2LKlX3QUIIA6FG3bOiHnQnnZUaU6oWFlxwKZKBsMvtlkNKX Xi0g== X-Forwarded-Encrypted: i=1; AJvYcCV/82yqjlf9u/zBN2crMip9cEoV6EQQTss7BvGyC6Ej2OfztMK548O/LGUydZnfEG84+CE0RJ1Ttm0rYc8=@vger.kernel.org X-Gm-Message-State: AOJu0Yy6mWW4aW+grIvucVWSjQTD/rRbWXRmMoQTMWfUQmOmoZ86zE4n TB6CoOOII4ZytiaJ9bYS2CHsqVCVpgLdgCR1PJW3gGXuGCZDvH7HTourro+OyrE= X-Gm-Gg: ASbGnctYK6eyVwue4I3vKp/ro/ZlzLRJD8D0scVjZcwDWJb/HiHgTeqgyg2urCv4BQX XXz31CAft/6KsXL2DE08+2iqHdDgApowKuvX8FKhzLt2jWsh0h2Iyu5MlxRo+cVfqxpDiepYG9e bdWY4n9IgO6IEXP6rEiK4Pz8ZQ4gAbCvTkgv74Xjie5aZLZJIFDTRXqxzfGReGLFJiusUYkPdNq 068EPbtzNxx92ENe1vZLcIOKu4lZPxW8zKsn5fJoCOIGmpXvgy3DEnVVWQW3T38JoMW9GNHi+jD pQumqztOvgxwNDRNhe3ETjw/f95fJuGyxc9LQSB7Pg== X-Google-Smtp-Source: AGHT+IHzFs+Yl0YKuPCOslDLAjE9ywj82yfkuqAwtgX61FbjT05TSlY35lL/GVySQmYtWNQHRuL2Lw== X-Received: by 2002:a17:902:dac9:b0:224:a74:28c2 with SMTP id d9443c01a7336-22c535aa4femr163703795ad.29.1745220186519; Mon, 21 Apr 2025 00:23:06 -0700 (PDT) Received: from localhost ([122.172.83.32]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-22c50bf31e7sm59281455ad.55.2025.04.21.00.23.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Apr 2025 00:23:06 -0700 (PDT) From: Viresh Kumar To: "Rafael J. Wysocki" , Miguel Ojeda , Danilo Krummrich , 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 , Stephen Boyd , Nishanth Menon , 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 , Anisse Astier , linux-kernel@vger.kernel.org Subject: [PATCH V11 06/15] rust: macros: enable use of hyphens in module names Date: Mon, 21 Apr 2025 12:52:13 +0530 Message-Id: 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" From: Anisse Astier Some modules might need naming that contains hyphens "-" to match the auto-probing by name in the platform devices that comes from the device tree. But rust identifiers cannot contain hyphens, so replace the module name by an underscore anywhere we'd use it as an identifier. Signed-off-by: Anisse Astier Reviewed-by: Alice Ryhl [Viresh: Replace "-" with '-', and fix line length checkpatch warnings] Signed-off-by: Viresh Kumar --- rust/macros/module.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/rust/macros/module.rs b/rust/macros/module.rs index a9418fbc9b44..27cc72d474f0 100644 --- a/rust/macros/module.rs +++ b/rust/macros/module.rs @@ -185,7 +185,9 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream { =20 let info =3D ModuleInfo::parse(&mut it); =20 - let mut modinfo =3D ModInfoBuilder::new(info.name.as_ref()); + /* Rust does not allow hyphens in identifiers, use underscore instead = */ + let name_identifier =3D info.name.replace('-', "_"); + let mut modinfo =3D ModInfoBuilder::new(name_identifier.as_ref()); if let Some(author) =3D info.author { modinfo.emit("author", &author); } @@ -310,14 +312,15 @@ mod __module_init {{ #[doc(hidden)] #[link_section =3D \"{initcall_section}\"] #[used] - pub static __{name}_initcall: extern \"C\" fn() -> ker= nel::ffi::c_int =3D __{name}_init; + pub static __{name_identifier}_initcall: extern \"C\" = fn() -> + kernel::ffi::c_int =3D __{name_identifier}_init; =20 #[cfg(not(MODULE))] #[cfg(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS)] core::arch::global_asm!( r#\".section \"{initcall_section}\", \"a\" - __{name}_initcall: - .long __{name}_init - . + __{name_identifier}_initcall: + .long __{name_identifier}_init - . .previous \"# ); @@ -325,7 +328,7 @@ mod __module_init {{ #[cfg(not(MODULE))] #[doc(hidden)] #[no_mangle] - pub extern \"C\" fn __{name}_init() -> kernel::ffi::c_= int {{ + pub extern \"C\" fn __{name_identifier}_init() -> kern= el::ffi::c_int {{ // SAFETY: This function is inaccessible to the ou= tside due to the double // module wrapping it. It is called exactly once b= y the C side via its // placement above in the initcall section. @@ -335,13 +338,13 @@ mod __module_init {{ #[cfg(not(MODULE))] #[doc(hidden)] #[no_mangle] - pub extern \"C\" fn __{name}_exit() {{ + pub extern \"C\" fn __{name_identifier}_exit() {{ // SAFETY: // - This function is inaccessible to the outside = due to the double // module wrapping it. It is called exactly once= by the C side via its // unique name, - // - furthermore it is only called after `__{name}= _init` has returned `0` - // (which delegates to `__init`). + // - furthermore it is only called after `__{name_= identifier}_init` has + // returned `0` (which delegates to `__init`). unsafe {{ __exit() }} }} =20 @@ -381,6 +384,7 @@ unsafe fn __exit() {{ ", type_ =3D info.type_, name =3D info.name, + name_identifier =3D name_identifier, modinfo =3D modinfo.buffer, initcall_section =3D ".initcall6.init" ) --=20 2.31.1.272.g89b43f80a514 From nobody Sun Dec 14 19:11:21 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 2F2B6259C8D for ; Mon, 21 Apr 2025 07:23:11 +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=1745220193; cv=none; b=ebDPUXLRz+cx3Y4zxcvs4yQl1Md5Gqq4Y9fUlA80tn7OoAla+DMSLd64dRMuu4orHBWmNcesDSSb8ITaJOvGVuKJTrceD8air/2Pfzc2yrmThSxU1kPMrWeJyEj6qtHXXNeph88lEtPHpUv7w2r7d5s5sofSLCSuTa65WuvFCUA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745220193; c=relaxed/simple; bh=FXN7MqRrTtWb3HqWkB4bVWXQmRagu7xa10NZ2Fw72s8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=EAvJsl3EjdGZLdHfq2sM/sujEpfw2NWyO7wV+sEgKrR3x+lJoFvZQ/DhujfpuckVxOOdK7d2VEFQ49NlDKkIs8hBplCMSgql0EkRTfHWfPscWaWggItRfjU5dcNbpZTDxPNyRRyGv8/NfkXk6umFyR26gaDJN3Hpw8qyZqC+rOw= 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=Qf3q1yQd; 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="Qf3q1yQd" Received: by mail-pl1-f181.google.com with SMTP id d9443c01a7336-2254e0b4b79so55295115ad.2 for ; Mon, 21 Apr 2025 00:23:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1745220190; x=1745824990; 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=8I9rp3cxJf7RfTSYMTTz5L7zI5/p8FTKb0IR9cJ6SsU=; b=Qf3q1yQdG3zOTxzKwSb/7kSsynDOuFd7KyStFoald5YZ9HEYBQjsujcoUb63oiBfQi lIF+xvFkh9oBKAfpOiHrdx77rdkcHBv9Xz64SUXQbqYCD4fh9LDBlrR69FJ0puI7F1AZ kV0n4Wkb5JNE4ggOM7f5W/rPowm9C652x+wxICsk3uyiihUgkJJHNIM2BVGx7Ysi5ZO1 hxUMvmJD8C/Kcyky262ts2R/JInlnG2X2f5YD5A4YbYnDpaujnL7BmfnxlXXb9xI70j5 gif2VDwjLTV9MMgA8BF2XYh1c8I1UpbiUVQ9Yypy/m0U4yjUtQIW4C472YzcTmkAQxfB /V5A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1745220190; x=1745824990; 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=8I9rp3cxJf7RfTSYMTTz5L7zI5/p8FTKb0IR9cJ6SsU=; b=ejMa3WPFP9UGEVcYZ542Fwfcad2oX8WpChMe30mQ88rHP6V9FZn/6bKeDngHzzGHvS 0PdRLxEwcqLRuJioi09AokLIZGx2UTDd/3IaF2aX/lg/M4p6+BShRPsntTWfsXazkr/u jyWzvSmLZmM9JVaeIT0kNWYxbxXZO2uEJZdABDpynsvZ9DksLHxQCN6CEEfaTT8lpZ+8 zxLGb8etKD7A22GIpkB5oO3kwNuW4K6y+Iubx/jlPR66ZeBsPLCQIMBtOT2BKUTrs7Cs jIkP+LQxf0f0qQ5VPg+dexqUQ8ayJftHDDaROLGh4/D2T1PmMfCWCSuBt2HePDZvHaLO T86A== X-Forwarded-Encrypted: i=1; AJvYcCX0TFUGu1ZSE8/3f1Slkr1bFVOsXY+S1D4CO+Jn2XYjIdD8MohusIlql+2NtxOKhyRIRze5xXt44ri38EA=@vger.kernel.org X-Gm-Message-State: AOJu0YxaRSJvb8td9ccbjE7RpGpGaqqtjKADRdkSx5k4AJDgVZFiG4xh 3Ah70c6bqhjn5k7wLY+ok5CsfTlXySZ3SN5VDUaWiI7FmiJ1SLD/KoIEERqeIVE= X-Gm-Gg: ASbGncsoPnQFCJocHBDhiJJ+AglU22BZP/I0XqZUS4RLh32nILVKRZCAsToweQHiYI3 KIHIVq0vrbTYexOllGjAy++6KO2Z27WjSzkK0H8p03RofFjikJHNFX66zT6fagnpzil2tm/0k89 kmBeyyS0Y2oi75ztcAsXdPbP6S1attotFuUnIYRZemHSU2bdycxXxfZ3riKmNZmqK9njUdgJQu6 V17tgJ5kRJPSuUkQJgJrGXptGq6s75nofIk8mCSiOEBQZJ+tNsdJLCJuFuEmIrhhFxr0Stcjvmo L5GRiMyuBmAXn1M0AkHpbKM2bZpsIdCsXCcXsVUTow== X-Google-Smtp-Source: AGHT+IHFViiSqvN/JjpJph9siMmkqqAkklZQddDpCT/0uxRK48dl3jb0Ucb0eHxYFvXN2Sctr5P1hQ== X-Received: by 2002:a17:902:c40f:b0:227:e74a:a05a with SMTP id d9443c01a7336-22c5361d29emr158103045ad.44.1745220190470; Mon, 21 Apr 2025 00:23:10 -0700 (PDT) Received: from localhost ([122.172.83.32]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-22c50bf5589sm59396595ad.102.2025.04.21.00.23.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Apr 2025 00:23:09 -0700 (PDT) From: Viresh Kumar To: "Rafael J. Wysocki" , Miguel Ojeda , Danilo Krummrich , 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 , Thomas Gleixner , Peter Zijlstra Cc: Viresh Kumar , linux-pm@vger.kernel.org, Vincent Guittot , Stephen Boyd , Nishanth Menon , 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 07/15] rust: cpu: Add from_cpu() Date: Mon, 21 Apr 2025 12:52:14 +0530 Message-Id: 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 implements cpu::from_cpu(), which returns a reference to Device for a CPU. The C struct is created at initialization time for CPUs and is never freed and so ARef isn't returned from this function. The new helper will be used by Rust based cpufreq drivers. Signed-off-by: Viresh Kumar --- MAINTAINERS | 1 + rust/bindings/bindings_helper.h | 1 + rust/kernel/cpu.rs | 30 ++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 1 + 4 files changed, 33 insertions(+) create mode 100644 rust/kernel/cpu.rs diff --git a/MAINTAINERS b/MAINTAINERS index 12cde55579a0..475abf72869c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6155,6 +6155,7 @@ F: include/linux/cpuhotplug.h F: include/linux/smpboot.h F: kernel/cpu.c F: kernel/smpboot.* +F: rust/kernel/cpu.rs =20 CPU IDLE TIME MANAGEMENT FRAMEWORK M: "Rafael J. Wysocki" diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index f53d6e1a21f2..ac92c67d2c38 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/kernel/cpu.rs b/rust/kernel/cpu.rs new file mode 100644 index 000000000000..10c5c3b25873 --- /dev/null +++ b/rust/kernel/cpu.rs @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Generic CPU definitions. +//! +//! C header: [`include/linux/cpu.h`](srctree/include/linux/cpu.h) + +use crate::{bindings, device::Device, error::Result, prelude::ENODEV}; + +/// Creates a new instance of CPU's device. +/// +/// # Safety +/// +/// Reference counting is not implemented for the CPU device in the C code= . When a CPU is +/// hot-unplugged, the corresponding CPU device is unregistered, but its a= ssociated memory +/// is not freed. +/// +/// Callers must ensure that the CPU device is not used after it has been = unregistered. +/// This can be achieved, for example, by registering a CPU hotplug notifi= er and removing +/// any references to the CPU device within the notifier's callback. +pub unsafe fn from_cpu(cpu: u32) -> Result<&'static Device> { + // SAFETY: It is safe to call `get_cpu_device()` for any CPU. + let ptr =3D unsafe { bindings::get_cpu_device(cpu) }; + if ptr.is_null() { + return Err(ENODEV); + } + + // SAFETY: The pointer returned by `get_cpu_device()`, if not `NULL`, = is a valid pointer to + // a `struct device` and is never freed by the C code. + Ok(unsafe { Device::as_ref(ptr) }) +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index f4dcfefe94be..db372f806875 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -44,6 +44,7 @@ pub mod build_assert; #[cfg(CONFIG_COMMON_CLK)] pub mod clk; +pub mod cpu; pub mod cpumask; pub mod cred; pub mod device; --=20 2.31.1.272.g89b43f80a514 From nobody Sun Dec 14 19:11:21 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 BBB2F25A2B7 for ; Mon, 21 Apr 2025 07:23:15 +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=1745220198; cv=none; b=IkarjhCO9dnl/dAMQJKZ5vVyQRPqaXZGpk1YXwubFRuBwQo92Qe3kiYdG3iNYLHco1oJxc+rHA1rvJRDSWw/61CLy1wFkCyrxv2eoMB7/1PefPwb5t26bzVxUS8r1xF4D+/dDRS5arngWtgC45CDNdwwIGXots+ZbiEace53Fos= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745220198; c=relaxed/simple; bh=IgrvvDSM4MOiJ2z3dbx97A/9zxZ6OY5SnxorgwJ16t8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=LOhv75Gwt1+qDiRRmdC4FefB/b/DaWUJR3n/A+0NxxKQAz4zh/Lj7XFXEjpojD7quJfw+1Jad6a/PtpniWd6fJ3+TuJeZsBIinPYtN9ylOTXdifyjhCbGhdyRdMmxRM68INPznTmEkRd2Se3UF1Tkvnw0HcmNFPVrtaXqDGNaJI= 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=R9u3w1kG; 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="R9u3w1kG" Received: by mail-pl1-f181.google.com with SMTP id d9443c01a7336-22622ddcc35so54848835ad.2 for ; Mon, 21 Apr 2025 00:23:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1745220195; x=1745824995; 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=QXxxE+P/evV1DzV09qUbFo55+wR1rTVmeWl7/3Fr4p4=; b=R9u3w1kGMzrhF27+ZWhPAma4ysKz1Jzu3Ycp6dcdWNOiclY9nYdljv7mSbmxRoekn8 GOpbho+OZ1agqBiYHwhpPEi/vOA9kzaZoIuhOJ3DO3bZO52hiG2hYsNwwMU8U1Rd2ohu 6u04bS02Y/fZ9kWv+3VBN1kqSOkDnNySU7RpuYfk/HIDbrcC/yn/UIDU0/yImZzyM9Tk 1PCUytMjNubnjUl2Yi6OkcZwPImTXMxaBBoh0nyw465la1euwm40ErWp6H1V4FFcvmUR SFLbfO5P5ILLdqa5ciPpkep2qbQhXNSuV/ooX7o8xlqJeZj7lliWaKrrwlZLfdTdmc4/ Mn8g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1745220195; x=1745824995; 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=QXxxE+P/evV1DzV09qUbFo55+wR1rTVmeWl7/3Fr4p4=; b=TEKuUpBdyn1wxxWtw67QKgkAoOiUkmIGhGmqZCDyOb+cUWel2TLoRvRZfesYSmpwjh +Oux9/bCzYTqnbVsAJ7F5TgZ7GG5YJjGJOeSmiTqiCuYzEmjEmqPvWUkA4M71crGZVuY JdKppcGbry14o8trvVcgHt1KlCSlYvUbDCfTyzGwA6HrAF/c87cg9yp9DSosq3fZliiM Pjci5Wmaz5zgRlixMY0RBQmkkvn+ClLK3zV1ovRoJS+6AIxUuaW5M/q3o+PJ0u2BnqXs tR2XYQfGixeW8g0lMto5WKZa7Saqzd8fS40DS5KJEyeBjB1NusattU0xfkvDKOfimi+l 6nKQ== X-Forwarded-Encrypted: i=1; AJvYcCVU80QapFGbgGqk9lAsLHSpKHl999L5vqHnwQZM+bTOrAWy+fUr9PYDdn5ZTxWk1dx6VA6k3XUiP6BBdIY=@vger.kernel.org X-Gm-Message-State: AOJu0YwM7OMsvYRNITvnSEjRL5jPqPqCEvdZdSWXDT/WoG7/DPXjKdO5 +XwmOLgnQAimjxtOnF/FLb7LjjmmrsKn7nF6nvaCh8ma7nzdd0yB8cPvCrb6xQ0= X-Gm-Gg: ASbGncvCkJVSYbWwZ6a1eyyiej04mnv1t7PIo6+2UrASnT33yVYYDvrenN5CLzn7SOS 3xdsV7UzF/MX2q7LRHirZwcyReszD8If+pzVWlEkLE4mk1loLXTMf2aNBDGH4DL4vvQzkycYTJZ zKAjI35tDQ4NyF/3Phhl8sDH1XQJHG2F0qIDOEqcv86HBVs6CVwDJ+lTa0CxP3MM34fshB380+c UvqhTZcl9OC30pUUGnu+wNRBXDDxRoqcpxqUMl5KSK/j5LP5aW/ZgLMiB6oTgbkzLIGqvnmX9Tv FfrIhw7SYMyL8UEy39zP0D29KuMJfUJWd6y9vU5b0g== X-Google-Smtp-Source: AGHT+IGjYOY8MYM3wuYTxzOki3xVQUi/ko18IW+gWlldXnS9ea9ckb/rLACdtggxTUFon7ZxxACpxg== X-Received: by 2002:a17:902:cec8:b0:215:a179:14ca with SMTP id d9443c01a7336-22c5357a118mr148720895ad.2.1745220194839; Mon, 21 Apr 2025 00:23:14 -0700 (PDT) Received: from localhost ([122.172.83.32]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-22c50eb03d2sm59270645ad.142.2025.04.21.00.23.13 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Apr 2025 00:23:14 -0700 (PDT) From: Viresh Kumar To: "Rafael J. Wysocki" , Miguel Ojeda , Danilo Krummrich , 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 , Viresh Kumar , Nishanth Menon , Stephen Boyd 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 08/15] rust: opp: Add initial abstractions for OPP framework Date: Mon, 21 Apr 2025 12:52:15 +0530 Message-Id: <7d085916e9c9548aef96ccb07ce709122a0be6cd.1745218975.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 initial Rust abstractions for the Operating Performance Points (OPP) framework. This includes bindings for `struct dev_pm_opp` and `struct dev_pm_opp_data`, laying the groundwork for further OPP integration. Signed-off-by: Viresh Kumar --- MAINTAINERS | 1 + rust/bindings/bindings_helper.h | 1 + rust/kernel/lib.rs | 2 + rust/kernel/opp.rs | 299 ++++++++++++++++++++++++++++++++ 4 files changed, 303 insertions(+) create mode 100644 rust/kernel/opp.rs diff --git a/MAINTAINERS b/MAINTAINERS index 475abf72869c..931e418f89ed 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18165,6 +18165,7 @@ F: Documentation/devicetree/bindings/opp/ F: Documentation/power/opp.rst F: drivers/opp/ F: include/linux/pm_opp.h +F: rust/kernel/opp.rs =20 OPL4 DRIVER M: Clemens Ladisch diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index ac92c67d2c38..529f22891e0b 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index db372f806875..11d333c8c673 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -68,6 +68,8 @@ #[cfg(CONFIG_NET)] pub mod net; pub mod of; +#[cfg(CONFIG_PM_OPP)] +pub mod opp; pub mod page; #[cfg(CONFIG_PCI)] pub mod pci; diff --git a/rust/kernel/opp.rs b/rust/kernel/opp.rs new file mode 100644 index 000000000000..8f0493a8b6e8 --- /dev/null +++ b/rust/kernel/opp.rs @@ -0,0 +1,299 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Operating performance points. +//! +//! This module provides rust abstractions for interacting with the OPP su= bsystem. +//! +//! C header: [`include/linux/pm_opp.h`](srctree/include/linux/pm_opp.h) +//! +//! Reference: + +use crate::{ + clk::Hertz, + device::Device, + error::{code::*, to_result, Result}, + ffi::c_ulong, + types::{ARef, AlwaysRefCounted, Opaque}, +}; + +use core::ptr; + +/// The voltage unit. +/// +/// Represents voltage in microvolts, wrapping a [`c_ulong`] value. +/// +/// ## Examples +/// +/// ``` +/// use kernel::opp::MicroVolt; +/// +/// let raw =3D 90500; +/// let volt =3D MicroVolt(raw); +/// +/// assert_eq!(usize::from(volt), raw); +/// assert_eq!(volt, MicroVolt(raw)); +/// ``` +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct MicroVolt(pub c_ulong); + +impl From for c_ulong { + #[inline] + fn from(volt: MicroVolt) -> Self { + volt.0 + } +} + +/// The power unit. +/// +/// Represents power in microwatts, wrapping a [`c_ulong`] value. +/// +/// ## Examples +/// +/// ``` +/// use kernel::opp::MicroWatt; +/// +/// let raw =3D 1000000; +/// let power =3D MicroWatt(raw); +/// +/// assert_eq!(usize::from(power), raw); +/// assert_eq!(power, MicroWatt(raw)); +/// ``` +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct MicroWatt(pub c_ulong); + +impl From for c_ulong { + #[inline] + fn from(power: MicroWatt) -> Self { + power.0 + } +} + +/// Handle for a dynamically created [`OPP`]. +/// +/// The associated [`OPP`] is automatically removed when the [`Token`] is = dropped. +/// +/// ## Examples +/// +/// The following example demonstrates how to create an [`OPP`] dynamicall= y. +/// +/// ``` +/// use kernel::clk::Hertz; +/// use kernel::device::Device; +/// use kernel::error::Result; +/// use kernel::opp::{Data, MicroVolt, Token}; +/// use kernel::types::ARef; +/// +/// fn create_opp(dev: &ARef, freq: Hertz, volt: MicroVolt, level:= u32) -> Result { +/// let data =3D Data::new(freq, volt, level, false); +/// +/// // OPP is removed once token goes out of scope. +/// data.add_opp(dev) +/// } +/// ``` +pub struct Token { + dev: ARef, + freq: Hertz, +} + +impl Token { + /// Dynamically adds an [`OPP`] and returns a [`Token`] that removes i= t on drop. + fn new(dev: &ARef, mut data: Data) -> Result { + // SAFETY: The requirements are satisfied by the existence of [`De= vice`] and its safety + // requirements. + to_result(unsafe { bindings::dev_pm_opp_add_dynamic(dev.as_raw(), = &mut data.0) })?; + Ok(Self { + dev: dev.clone(), + freq: data.freq(), + }) + } +} + +impl Drop for Token { + fn drop(&mut self) { + // SAFETY: The requirements are satisfied by the existence of [`De= vice`] and its safety + // requirements. + unsafe { bindings::dev_pm_opp_remove(self.dev.as_raw(), self.freq.= into()) }; + } +} + +/// OPP data. +/// +/// Rust abstraction for the C `struct dev_pm_opp_data`, used to define op= erating performance +/// points (OPPs) dynamically. +/// +/// ## Examples +/// +/// The following example demonstrates how to create an [`OPP`] with [`Dat= a`]. +/// +/// ``` +/// use kernel::clk::Hertz; +/// use kernel::device::Device; +/// use kernel::error::Result; +/// use kernel::opp::{Data, MicroVolt, Token}; +/// use kernel::types::ARef; +/// +/// fn create_opp(dev: &ARef, freq: Hertz, volt: MicroVolt, level:= u32) -> Result { +/// let data =3D Data::new(freq, volt, level, false); +/// +/// // OPP is removed once token goes out of scope. +/// data.add_opp(dev) +/// } +/// ``` +#[repr(transparent)] +pub struct Data(bindings::dev_pm_opp_data); + +impl Data { + /// Creates a new instance of [`Data`]. + /// + /// This can be used to define a dynamic OPP to be added to a device. + pub fn new(freq: Hertz, volt: MicroVolt, level: u32, turbo: bool) -> S= elf { + Self(bindings::dev_pm_opp_data { + turbo, + freq: freq.into(), + u_volt: volt.into(), + level, + }) + } + + /// Adds an [`OPP`] dynamically. + /// + /// Returns a [`Token`] that ensures the OPP is automatically removed + /// when it goes out of scope. + #[inline] + pub fn add_opp(self, dev: &ARef) -> Result { + Token::new(dev, self) + } + + /// Returns the frequency associated with this OPP data. + #[inline] + fn freq(&self) -> Hertz { + Hertz(self.0.freq) + } +} + +/// A reference-counted Operating performance point (OPP). +/// +/// Rust abstraction for the C `struct dev_pm_opp`. +/// +/// # Invariants +/// +/// The pointer stored in `Self` is non-null and valid for the lifetime of= the [`OPP`]. +/// +/// Instances of this type are reference-counted. The reference count is i= ncremented by the +/// `dev_pm_opp_get` function and decremented by `dev_pm_opp_put`. The Rus= t type `ARef` +/// 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. +#[repr(transparent)] +pub struct OPP(Opaque); + +/// SAFETY: It is okay to send the ownership of [`OPP`] across thread boun= daries. +unsafe impl Send for OPP {} + +/// SAFETY: It is okay to access [`OPP`] through shared references from ot= her threads because we're +/// either accessing properties that don't change or that are properly syn= chronised by C code. +unsafe impl Sync for OPP {} + +/// SAFETY: The type invariants guarantee that [`OPP`] is always refcounte= d. +unsafe impl AlwaysRefCounted for OPP { + fn inc_ref(&self) { + // SAFETY: The existence of a shared reference means that the refc= ount is nonzero. + unsafe { bindings::dev_pm_opp_get(self.0.get()) }; + } + + unsafe fn dec_ref(obj: ptr::NonNull) { + // SAFETY: The safety requirements guarantee that the refcount is = nonzero. + unsafe { bindings::dev_pm_opp_put(obj.cast().as_ptr()) } + } +} + +impl OPP { + /// Creates an owned reference to a [`OPP`] from a valid pointer. + /// + /// The refcount is incremented by the C code and will be decremented = by `dec_ref` when the + /// [`ARef`] object is dropped. + /// + /// # Safety + /// + /// The caller must ensure that `ptr` is valid and the refcount of the= [`OPP`] is incremented. + /// The caller must also ensure that it doesn't explicitly drop the re= fcount of the [`OPP`], as + /// the returned [`ARef`] object takes over the refcount increment on = the underlying object and + /// the same will be dropped along with it. + pub unsafe fn from_raw_opp_owned(ptr: *mut bindings::dev_pm_opp) -> Re= sult> { + let ptr =3D ptr::NonNull::new(ptr).ok_or(ENODEV)?; + + // SAFETY: The safety requirements guarantee the validity of the p= ointer. + // + // INVARIANT: The reference-count is decremented when [`OPP`] goes= out of scope. + Ok(unsafe { ARef::from_raw(ptr.cast()) }) + } + + /// Creates a reference to a [`OPP`] from a valid pointer. + /// + /// The refcount is not updated by the Rust API unless the returned re= ference is converted to + /// an [`ARef`] object. + /// + /// # Safety + /// + /// The caller must ensure that `ptr` is valid and remains valid for t= he duration of `'a`. + #[inline] + pub unsafe fn from_raw_opp<'a>(ptr: *mut bindings::dev_pm_opp) -> Resu= lt<&'a Self> { + // SAFETY: The caller guarantees that the pointer is not dangling = and stays valid for the + // duration of 'a. The cast is okay because [`OPP`] is `repr(trans= parent)`. + Ok(unsafe { &*ptr.cast() }) + } + + #[inline] + fn as_raw(&self) -> *mut bindings::dev_pm_opp { + self.0.get() + } + + /// Returns the frequency of an [`OPP`]. + pub fn freq(&self, index: Option) -> Hertz { + let index =3D index.unwrap_or(0); + + // SAFETY: By the type invariants, we know that `self` owns a refe= rence, so it is safe to + // use it. + Hertz(unsafe { bindings::dev_pm_opp_get_freq_indexed(self.as_raw()= , index) }) + } + + /// Returns the voltage of an [`OPP`]. + #[inline] + pub fn voltage(&self) -> MicroVolt { + // SAFETY: By the type invariants, we know that `self` owns a refe= rence, so it is safe to + // use it. + MicroVolt(unsafe { bindings::dev_pm_opp_get_voltage(self.as_raw())= }) + } + + /// Returns the level of an [`OPP`]. + #[inline] + pub fn level(&self) -> u32 { + // SAFETY: By the type invariants, we know that `self` owns a refe= rence, so it is safe to + // use it. + unsafe { bindings::dev_pm_opp_get_level(self.as_raw()) } + } + + /// Returns the power of an [`OPP`]. + #[inline] + pub fn power(&self) -> MicroWatt { + // SAFETY: By the type invariants, we know that `self` owns a refe= rence, so it is safe to + // use it. + MicroWatt(unsafe { bindings::dev_pm_opp_get_power(self.as_raw()) }) + } + + /// Returns the required pstate of an [`OPP`]. + #[inline] + pub fn required_pstate(&self, index: u32) -> u32 { + // SAFETY: By the type invariants, we know that `self` owns a refe= rence, so it is safe to + // use it. + unsafe { bindings::dev_pm_opp_get_required_pstate(self.as_raw(), i= ndex) } + } + + /// Returns true if the [`OPP`] is turbo. + #[inline] + pub fn is_turbo(&self) -> bool { + // SAFETY: By the type invariants, we know that `self` owns a refe= rence, so it is safe to + // use it. + unsafe { bindings::dev_pm_opp_is_turbo(self.as_raw()) } + } +} --=20 2.31.1.272.g89b43f80a514 From nobody Sun Dec 14 19:11:21 2025 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 From nobody Sun Dec 14 19:11:21 2025 Received: from mail-pg1-f180.google.com (mail-pg1-f180.google.com [209.85.215.180]) (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 1B1F725A352 for ; Mon, 21 Apr 2025 07:23:22 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.180 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745220205; cv=none; b=aZHXOstM1r4WX1o9GNlfMxDKCzVXujVyq3CwTzR0QQmqPlXQ3q/VtnEGx08y/zbjg3g19rabrkIQ27qn4tZouRcJpobrtt7WYmi7Vw/EvdFQihFVCzAOnx48jYtn96zIBUQZHVPtb9erCN9m6WqVoMzraZKqZZ5eobB8631HMp8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745220205; c=relaxed/simple; bh=XX3U6Tw7oOsV2P/lZuKJuHf6QBrB6qmqZ43oqb9nxh0=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=fN8ntNdoxOOfcsTGnNdnGp2eZP1T8VkzKSFl7YrregR9kklUtZziJ4yMpiSUQ6UwvQPm4yPYlIpm6J3pTtk+yu+IW2/Yck++4z3Iqc1faBb5PcSS1UYXMa7Fi2C0OxlqGyN7q55tUXYt3Q6PlmDYil04tUlD3xk6Huw7xC19Ees= 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=nqya0186; arc=none smtp.client-ip=209.85.215.180 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="nqya0186" Received: by mail-pg1-f180.google.com with SMTP id 41be03b00d2f7-af548cb1f83so3651582a12.3 for ; Mon, 21 Apr 2025 00:23:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1745220202; x=1745825002; 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=XUcfURvD53WGE5bWpmH47J8MJLL7ClfsqTtjc7lNE8o=; b=nqya0186JqjskxIsDfC3ZnvvAlaU62g4q31MnEqE1L96tYTNiFy4HDdeRAiXMzLWBd +NjRErkznawJa6uOUV/VvWpnVrsJeKIHsiKIob5ZBexwPlHnJ+l4mjbF6wFrGagMtJZM wC35oKuWEHoAjQdRdc1aowtcmAIiGKhlr2zLc+STNxhCTjQZLOLbRxxyqw7G+ni+1ttM 0z/yS2ht2NeHJemYjX3Gfels4l5K9Fn0A4oqzVEZUZ744fzOCEmCaWFOVJBMwWQJ7/b1 XAGRavxOdEJnS0cBLAQ9OJX6c3B7/6VBiJiCnuxA62vFRs+C4rmnD6K02Gf0TGdLtqAn YC4A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1745220202; x=1745825002; 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=XUcfURvD53WGE5bWpmH47J8MJLL7ClfsqTtjc7lNE8o=; b=JOnt7AvidqfcGKxMgmYJo8YzM0pTfWQKztOP2QlCm8eCe6U5gh+QLMv/RLN9eGWBd0 aq5/30mSsmxz2F1HvplyG1ZI0mxL9R3u1Fpxvx+XuhdAyRHj3MjOfZm3q+Qt1UGYKi8S YvfQHCpEAp6YnrXXM6eRpavPnUOmheHtwrtg8aOis+/tm+usfOuVHrpPbvMEj8BzPatS 3yFsMXgEcO6BmO8FKMK8ee/vHQ5AII/OI2nCi80vqywNNx9JkBb+JDOxD6/Y0/2ElD0P 4140VHVrKZjlGlyppcLIbwKTa9D5HoUHeZn4x5Bd1PNYVrfX9j32md7bXkDKqY3jIQ7s fZpA== X-Forwarded-Encrypted: i=1; AJvYcCWK4rbD6etEwZ8FlySVJGILOUfhd44aHSEAbztEhS0ao9fCJd13QDLKFpfTE/L0pufTPtXId1Q3IAsl1DI=@vger.kernel.org X-Gm-Message-State: AOJu0YwJjOb4IUWVsgOt/dCNZGP0ovqQMccwPrQiVqwIvd9+C3uAb98F 6v5KxmN9xW+iOItMxepSXwM0VjeZl2qe5EKwZQVjITGULWxvOimLKFW1GWcmoqA= X-Gm-Gg: ASbGnctto55aVEx1CLzLeySZcbGxKrOWlT8Gk8kqWJtuoHlkcAxbSUH0+BPAVV/QbXq ozmj8oMfy+ylKK25iUl8GwMt2VMq7V1ruC0tRY6ERjHCCaRBJuYN0pO1C+fUlC6C0O14Hv2B6M4 h4FFccYlTyDd4jrSX9spPOxl5k+Tn5kJuB2ezQ+sWzUUx/0bdGZoMQXhP1t4G+cyXS3fiBr/Hg4 HuLx+k1lc9sgW8/iP0RO9pqCJ7MXUEZEITPVkV7ESoJ8OqSubVVQ/Hax9bE8c2hzMQIcDu4APeS wpWwWMk3lWWtSG8whCFWeksKrK2F7vYuK/O8Q0VnVg== X-Google-Smtp-Source: AGHT+IGxPhzDWRwZnKMMdpMmqReoI1r2YjBUlr0I5yZMN4NwiAXmwekxI1KwiPI6yxVBzAfNSrnxIA== X-Received: by 2002:a17:902:db0f:b0:215:7421:262 with SMTP id d9443c01a7336-22c5357f020mr172416085ad.12.1745220202204; Mon, 21 Apr 2025 00:23:22 -0700 (PDT) Received: from localhost ([122.172.83.32]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-22c50bf5382sm59318555ad.81.2025.04.21.00.23.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Apr 2025 00:23:21 -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 10/15] rust: opp: Add abstractions for the configuration options Date: Mon, 21 Apr 2025 12:52:17 +0530 Message-Id: <5394d62f356422c95866af129402878db453f25a.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 the OPP core configuration options, enabling safe access to various configurable aspects of the OPP framework. Signed-off-by: Viresh Kumar --- rust/kernel/opp.rs | 295 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 293 insertions(+), 2 deletions(-) diff --git a/rust/kernel/opp.rs b/rust/kernel/opp.rs index 062c2a04c52f..8b61ea8748a3 100644 --- a/rust/kernel/opp.rs +++ b/rust/kernel/opp.rs @@ -12,12 +12,29 @@ clk::Hertz, cpumask::{Cpumask, CpumaskVar}, device::Device, - error::{code::*, from_err_ptr, to_result, Error, Result}, + error::{code::*, from_err_ptr, from_result, to_result, Error, Result, = VTABLE_DEFAULT_ERROR}, ffi::c_ulong, + prelude::*, + str::CString, types::{ARef, AlwaysRefCounted, Opaque}, }; =20 -use core::ptr; +use core::{marker::PhantomData, ptr}; + +use macros::vtable; + +/// Creates a null-terminated slice of pointers to [`Cstring`]s. +fn to_c_str_array(names: &[CString]) -> Result> { + // Allocated a null-terminated vector of pointers. + let mut list =3D KVec::with_capacity(names.len() + 1, GFP_KERNEL)?; + + for name in names.iter() { + list.push(name.as_ptr() as _, GFP_KERNEL)?; + } + + list.push(ptr::null(), GFP_KERNEL)?; + Ok(list) +} =20 /// The voltage unit. /// @@ -205,6 +222,280 @@ pub enum SearchType { Ceil, } =20 +/// OPP configuration callbacks. +/// +/// Implement this trait to customize OPP clock and regulator setup for yo= ur device. +#[vtable] +pub trait ConfigOps { + /// This is typically used to scale clocks when transitioning between = OPPs. + #[inline] + fn config_clks(_dev: &Device, _table: &Table, _opp: &OPP, _scaling_dow= n: bool) -> Result<()> { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// This provides access to the old and new OPPs, allowing for safe re= gulator adjustments. + #[inline] + fn config_regulators( + _dev: &Device, + _opp_old: &OPP, + _opp_new: &OPP, + _data: *mut *mut bindings::regulator, + _count: u32, + ) -> Result<()> { + build_error!(VTABLE_DEFAULT_ERROR) + } +} + +/// OPP configuration token. +/// +/// Returned by the OPP core when configuration is applied to a [`Device`]= . The associated +/// configuration is automatically cleared when the token is dropped. +pub struct ConfigToken(i32); + +impl Drop for ConfigToken { + fn drop(&mut self) { + // SAFETY: This is the same token value returned by the C code via= `dev_pm_opp_set_config`. + unsafe { bindings::dev_pm_opp_clear_config(self.0) }; + } +} + +/// OPP configurations. +/// +/// Rust abstraction for the C `struct dev_pm_opp_config`. +/// +/// ## Examples +/// +/// The following example demonstrates how to set OPP property-name config= uration for a [`Device`]. +/// +/// ``` +/// use kernel::device::Device; +/// use kernel::error::Result; +/// use kernel::opp::{Config, ConfigOps, ConfigToken}; +/// use kernel::str::CString; +/// use kernel::types::ARef; +/// use kernel::macros::vtable; +/// +/// #[derive(Default)] +/// struct Driver; +/// +/// #[vtable] +/// impl ConfigOps for Driver {} +/// +/// fn configure(dev: &ARef) -> Result { +/// let name =3D CString::try_from_fmt(fmt!("{}", "slow"))?; +/// +/// // The OPP configuration is cleared once the [`ConfigToken`] goes = out of scope. +/// Config::::new() +/// .set_prop_name(name)? +/// .set(dev) +/// } +/// ``` +#[derive(Default)] +pub struct Config +where + T: Default, +{ + clk_names: Option>, + prop_name: Option, + regulator_names: Option>, + supported_hw: Option>, + + // Tuple containing (required device, index) + required_dev: Option<(ARef, u32)>, + _data: PhantomData, +} + +impl Config { + /// Creates a new instance of [`Config`]. + #[inline] + pub fn new() -> Self { + Self::default() + } + + /// Initializes clock names. + pub fn set_clk_names(mut self, names: KVec) -> Result { + if self.clk_names.is_some() { + return Err(EBUSY); + } + + if names.is_empty() { + return Err(EINVAL); + } + + self.clk_names =3D Some(names); + Ok(self) + } + + /// Initializes property name. + pub fn set_prop_name(mut self, name: CString) -> Result { + if self.prop_name.is_some() { + return Err(EBUSY); + } + + self.prop_name =3D Some(name); + Ok(self) + } + + /// Initializes regulator names. + pub fn set_regulator_names(mut self, names: KVec) -> Result { + if self.regulator_names.is_some() { + return Err(EBUSY); + } + + if names.is_empty() { + return Err(EINVAL); + } + + self.regulator_names =3D Some(names); + + Ok(self) + } + + /// Initializes required devices. + pub fn set_required_dev(mut self, dev: ARef, index: u32) -> Re= sult { + if self.required_dev.is_some() { + return Err(EBUSY); + } + + self.required_dev =3D Some((dev, index)); + Ok(self) + } + + /// Initializes supported hardware. + pub fn set_supported_hw(mut self, hw: KVec) -> Result { + if self.supported_hw.is_some() { + return Err(EBUSY); + } + + if hw.is_empty() { + return Err(EINVAL); + } + + self.supported_hw =3D Some(hw); + Ok(self) + } + + /// Sets the configuration with the OPP core. + /// + /// The returned [`ConfigToken`] will remove the configuration when dr= opped. + pub fn set(self, dev: &Device) -> Result { + let (_clk_list, clk_names) =3D match &self.clk_names { + Some(x) =3D> { + let list =3D to_c_str_array(x)?; + let ptr =3D list.as_ptr(); + (Some(list), ptr) + } + None =3D> (None, ptr::null()), + }; + + let (_regulator_list, regulator_names) =3D match &self.regulator_n= ames { + Some(x) =3D> { + let list =3D to_c_str_array(x)?; + let ptr =3D list.as_ptr(); + (Some(list), ptr) + } + None =3D> (None, ptr::null()), + }; + + let prop_name =3D self + .prop_name + .as_ref() + .map_or(ptr::null(), |p| p.as_char_ptr()); + + let (supported_hw, supported_hw_count) =3D self + .supported_hw + .as_ref() + .map_or((ptr::null(), 0), |hw| (hw.as_ptr(), hw.len() as u32)); + + let (required_dev, required_dev_index) =3D self + .required_dev + .as_ref() + .map_or((ptr::null_mut(), 0), |(dev, idx)| (dev.as_raw(), *idx= )); + + let mut config =3D bindings::dev_pm_opp_config { + clk_names, + config_clks: if T::HAS_CONFIG_CLKS { + Some(Self::config_clks) + } else { + None + }, + prop_name, + regulator_names, + config_regulators: if T::HAS_CONFIG_REGULATORS { + Some(Self::config_regulators) + } else { + None + }, + supported_hw, + supported_hw_count, + + required_dev, + required_dev_index, + }; + + // SAFETY: The requirements are satisfied by the existence of [`De= vice`] and its safety + // requirements. The OPP core guarantees not to access fields of [= `Config`] after this call + // and so we don't need to save a copy of them for future use. + let ret =3D unsafe { bindings::dev_pm_opp_set_config(dev.as_raw(),= &mut config) }; + if ret < 0 { + Err(Error::from_errno(ret)) + } else { + Ok(ConfigToken(ret)) + } + } + + /// Config's clk callback. + /// + /// SAFETY: Called from C. Inputs must be valid pointers. + extern "C" fn config_clks( + dev: *mut bindings::device, + opp_table: *mut bindings::opp_table, + opp: *mut bindings::dev_pm_opp, + _data: *mut kernel::ffi::c_void, + scaling_down: bool, + ) -> kernel::ffi::c_int { + from_result(|| { + // SAFETY: 'dev' is guaranteed by the C code to be valid. + let dev =3D unsafe { Device::get_device(dev) }; + T::config_clks( + &dev, + // SAFETY: 'opp_table' is guaranteed by the C code to be v= alid. + &unsafe { Table::from_raw_table(opp_table, &dev) }, + // SAFETY: 'opp' is guaranteed by the C code to be valid. + unsafe { OPP::from_raw_opp(opp)? }, + scaling_down, + ) + .map(|()| 0) + }) + } + + /// Config's regulator callback. + /// + /// SAFETY: Called from C. Inputs must be valid pointers. + extern "C" fn config_regulators( + dev: *mut bindings::device, + old_opp: *mut bindings::dev_pm_opp, + new_opp: *mut bindings::dev_pm_opp, + regulators: *mut *mut bindings::regulator, + count: kernel::ffi::c_uint, + ) -> kernel::ffi::c_int { + from_result(|| { + // SAFETY: 'dev' is guaranteed by the C code to be valid. + let dev =3D unsafe { Device::get_device(dev) }; + T::config_regulators( + &dev, + // SAFETY: 'old_opp' is guaranteed by the C code to be val= id. + unsafe { OPP::from_raw_opp(old_opp)? }, + // SAFETY: 'new_opp' is guaranteed by the C code to be val= id. + unsafe { OPP::from_raw_opp(new_opp)? }, + regulators, + count, + ) + .map(|()| 0) + }) + } +} + /// A reference-counted OPP table. /// /// Rust abstraction for the C `struct opp_table`. --=20 2.31.1.272.g89b43f80a514 From nobody Sun Dec 14 19:11:21 2025 Received: from mail-pf1-f179.google.com (mail-pf1-f179.google.com [209.85.210.179]) (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 A9FFA25A658 for ; Mon, 21 Apr 2025 07:23:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.179 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745220209; cv=none; b=AfZUn9GUB5RcF/Br2LsI5nW58xFDrmUnkLPL0QXdMHCNmuq5spVqk72ZWzeNuPlBa9206EKDrva/nzF8f/Mhq9htXu4RMRi5WCzPlYJk2pofa2zO/Lv3GIvuxrpF/xne5Lbn/qWi+dVrjP242JTtUWeoL50YHocwSCzJeY0gkY4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745220209; c=relaxed/simple; bh=XMVRemRDB+USIyH6+v0V8k2TtHpJ0xAOzgLjiptbO0c=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=La5Tf0Ahpy8KFQMBi0koL0uughqgPEgR1QwEeGmm/TbGABy1u4pTdxZQZz7qFiaT9d2ZZdPyqZZ681S7JMIPVj7m8Yf8B5Bmjji9N+uTgagLxAiQb0GoaBo+tVmuantmq/7n0h8bIwZlkG2uhb1SwQh8yN315D6FVl9ygB9HrZg= 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=XLSSHLZw; arc=none smtp.client-ip=209.85.210.179 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="XLSSHLZw" Received: by mail-pf1-f179.google.com with SMTP id d2e1a72fcca58-736bfa487c3so3181547b3a.1 for ; Mon, 21 Apr 2025 00:23:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1745220207; x=1745825007; 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=7tTGfWa+XR18CgmLZL098GuduPchQV1dpPmnVmRAGRA=; b=XLSSHLZwSPbIF8r0OcwOXKesv6C/AQM7gZ+tqerDCUB9asa1swp+CtGY9jCZxiJMJS pL9w4ZgG3fQnKD1ScHMJb5m2fW1ufLbuLB0Ir2D7rPkdyFIHgXNltdyr9AiWl45xrCf/ hI/Z5y+B8cj3XScqU3ZZ5CxPOIXJJX91TtnXYs/ndJzUP8F/1sHV00lZK+kAr9/JS0Vg FmkA5r2AWDiER+3M4RyKu1nDANGmvZ+61sBdZY9SUrEJlKK6NsveeT9UKVBZQgEgcuym RVzuc+3HIMpd1v4gtTAVv5pKHzdAA7cU6hpCLUspqojb9DlOPsUcwIC/RlsO/Pd0I5Ih AGeA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1745220207; x=1745825007; 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=7tTGfWa+XR18CgmLZL098GuduPchQV1dpPmnVmRAGRA=; b=RXz21EOzF5GTW0XWQrQZHM28udg+oafdh4pXYUH5zx1Yfd5P9aJKhYMYL2v3ZHTjRG c41mPwdMnAfUcnTn1FtC20XlSZ07wCQrjWmVvzCK9EWVu9ED8VyUDrKSzuYg2us9P7bS Sfy/TUZ/tWJ/5N6XLBfwl6jAmJthz/HdbuK0WNwjvqfrTgW/qQYqIRe26C4UeOE/UTxh G8XqJcuOmC7ElNQAb6d366YbJlVmWYynpCaAwSN1FLiLXl18n3ZLKTxkobOwC7g96qbq zYB7ZTxs5v/wWBbRqU9dXGwiGEXpgcFhb2Fe673erfb4KNp9OeyFKwA5XTdz95lMTMAU G/kw== X-Forwarded-Encrypted: i=1; AJvYcCWwO7eT3qOQL5Xm0gzk2frIG7faqkMAdMivjEe0j1T79w1zz52fxWzmDB8G8BpD75m2Ufs8DxFjis/u/Uo=@vger.kernel.org X-Gm-Message-State: AOJu0YxwmgNIQaWm8oDftnIT0TSw8oWHESAYuV42+iBNxbQyb+Ah8skk uFt70BW1liracADa7knpmWRXpzw5fL9YfI7LDhERLDgNuuu5AA56KWj60fCjuFg= X-Gm-Gg: ASbGncsKbWtnlWJeQJZuIwDjPh1JTOJPhVfisBJ3m9zG91LRmzhVGRrNAqKcT/H71Jx ul4IMwerJQ7MQtdc8WwOJmNtEVQXEkSY3rAzFiXuLH+/kQE/nF15/qmmkUOpzTCVOWUuBU4htaY sVt3rh2iGuUTcW62l2CI4mpk8IzbvqKO29oaFBV1O+3By+8lnZdQczDgPuKGVUDE1024PMNZv0q fexIDDcQKD1lMvEhYwc7aCkxLP0I63QhwNs2momrVAUbswwO1VSTOelMJYeFC3VYp12h81a4V0J x1UB6ZBlRRANJqEv9MvUgecmcRnQ7EIP54D/Qpozrw== X-Google-Smtp-Source: AGHT+IFjJtFg0oARjKoCdKj8ac4jdmbXnB/8wMyyNuzy89hrSl6KALaSX9+y/Rlv9M5tCIGVLSfGZw== X-Received: by 2002:a05:6a20:c887:b0:1f3:397d:86f1 with SMTP id adf61e73a8af0-203cbc538d8mr15555909637.16.1745220206870; Mon, 21 Apr 2025 00:23:26 -0700 (PDT) Received: from localhost ([122.172.83.32]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-73dbfab289asm5866738b3a.153.2025.04.21.00.23.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Apr 2025 00:23:26 -0700 (PDT) From: Viresh Kumar To: "Rafael J. Wysocki" , Miguel Ojeda , Danilo Krummrich , 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 , Viresh Kumar Cc: linux-pm@vger.kernel.org, Vincent Guittot , Stephen Boyd , Nishanth Menon , 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 11/15] rust: cpufreq: Add initial abstractions for cpufreq framework Date: Mon, 21 Apr 2025 12:52:18 +0530 Message-Id: <2187facc51586cb3eb1dfff0d2d893680572caf0.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 initial Rust abstractions for the cpufreq core. This includes basic representations for cpufreq flags, relation types, and the cpufreq table. Signed-off-by: Viresh Kumar --- MAINTAINERS | 1 + rust/bindings/bindings_helper.h | 1 + rust/helpers/cpufreq.c | 10 + rust/helpers/helpers.c | 1 + rust/kernel/cpufreq.rs | 364 ++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 2 + 6 files changed, 379 insertions(+) create mode 100644 rust/helpers/cpufreq.c create mode 100644 rust/kernel/cpufreq.rs diff --git a/MAINTAINERS b/MAINTAINERS index 931e418f89ed..aa56eacbda71 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6142,6 +6142,7 @@ F: drivers/cpufreq/ F: include/linux/cpufreq.h F: include/linux/sched/cpufreq.h F: kernel/sched/cpufreq*.c +F: rust/kernel/cpufreq.rs F: tools/testing/selftests/cpufreq/ =20 CPU HOTPLUG diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helpe= r.h index 529f22891e0b..7c1d78f68076 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers/cpufreq.c b/rust/helpers/cpufreq.c new file mode 100644 index 000000000000..7c1343c4d65e --- /dev/null +++ b/rust/helpers/cpufreq.c @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +#ifdef CONFIG_CPU_FREQ +void rust_helper_cpufreq_register_em_with_opp(struct cpufreq_policy *polic= y) +{ + cpufreq_register_em_with_opp(policy); +} +#endif diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index ae595c9cd91b..df1fcfb3adf3 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -12,6 +12,7 @@ #include "build_assert.c" #include "build_bug.c" #include "clk.c" +#include "cpufreq.c" #include "cpumask.c" #include "cred.c" #include "device.c" diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs new file mode 100644 index 000000000000..279956c6d056 --- /dev/null +++ b/rust/kernel/cpufreq.rs @@ -0,0 +1,364 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! CPU frequency scaling. +//! +//! This module provides rust abstractions for interacting with the cpufre= q subsystem. +//! +//! C header: [`include/linux/cpufreq.h`](srctree/include/linux/cpufreq.h) +//! +//! Reference: + +use crate::{ + error::{code::*, to_result, Result}, + ffi::c_ulong, + prelude::*, +}; + +use core::{ + pin::Pin, +}; + +/// Default transition latency value in nanoseconds. +pub const ETERNAL_LATENCY_NS: u32 =3D bindings::CPUFREQ_ETERNAL as u32; + +/// CPU frequency driver flags. +pub mod flags { + /// Driver needs to update internal limits even if frequency remains u= nchanged. + pub const NEED_UPDATE_LIMITS: u16 =3D 1 << 0; + + /// Platform where constants like `loops_per_jiffy` are unaffected by = frequency changes. + pub const CONST_LOOPS: u16 =3D 1 << 1; + + /// Register driver as a thermal cooling device automatically. + pub const IS_COOLING_DEV: u16 =3D 1 << 2; + + /// Supports multiple clock domains with per-policy governors in `cpu/= cpuN/cpufreq/`. + pub const HAVE_GOVERNOR_PER_POLICY: u16 =3D 1 << 3; + + /// Allows post-change notifications outside of the `target()` routine. + pub const ASYNC_NOTIFICATION: u16 =3D 1 << 4; + + /// Ensure CPU starts at a valid frequency from the driver's freq-tabl= e. + pub const NEED_INITIAL_FREQ_CHECK: u16 =3D 1 << 5; + + /// Disallow governors with `dynamic_switching` capability. + pub const NO_AUTO_DYNAMIC_SWITCHING: u16 =3D 1 << 6; +} + +/// Relations from the C code. +const CPUFREQ_RELATION_L: u32 =3D 0; +const CPUFREQ_RELATION_H: u32 =3D 1; +const CPUFREQ_RELATION_C: u32 =3D 2; + +/// Can be used with any of the above values. +const CPUFREQ_RELATION_E: u32 =3D 1 << 2; + +/// CPU frequency selection relations. +/// +/// CPU frequency selection relations, each optionally marked as "efficien= t". +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Relation { + /// Select the lowest frequency at or above target. + Low(bool), + /// Select the highest frequency below or at target. + High(bool), + /// Select the closest frequency to the target. + Close(bool), +} + +impl Relation { + // Construct from a C-compatible `u32` value. + fn new(val: u32) -> Result { + let efficient =3D val & CPUFREQ_RELATION_E !=3D 0; + + Ok(match val & !CPUFREQ_RELATION_E { + CPUFREQ_RELATION_L =3D> Self::Low(efficient), + CPUFREQ_RELATION_H =3D> Self::High(efficient), + CPUFREQ_RELATION_C =3D> Self::Close(efficient), + _ =3D> return Err(EINVAL), + }) + } +} + +impl From for u32 { + // Convert to a C-compatible `u32` value. + fn from(rel: Relation) -> Self { + let (mut val, efficient) =3D match rel { + Relation::Low(e) =3D> (CPUFREQ_RELATION_L, e), + Relation::High(e) =3D> (CPUFREQ_RELATION_H, e), + Relation::Close(e) =3D> (CPUFREQ_RELATION_C, e), + }; + + if efficient { + val |=3D CPUFREQ_RELATION_E; + } + + val + } +} + +/// Policy data. +/// +/// Rust abstraction for the C `struct cpufreq_policy_data`. +/// +/// # Invariants +/// +/// A [`PolicyData`] instance always corresponds to a valid C `struct cpuf= req_policy_data`. +/// +/// The callers must ensure that the `struct cpufreq_policy_data` is valid= for access and remains +/// valid for the lifetime of the returned reference. +#[repr(transparent)] +pub struct PolicyData(Opaque); + +impl PolicyData { + /// Creates a mutable reference to an existing `struct cpufreq_policy_= data` pointer. + /// + /// # Safety + /// + /// The caller must ensure that `ptr` is valid for writing and remains= valid for the lifetime + /// of the returned reference. + #[inline] + pub unsafe fn from_raw_mut<'a>(ptr: *mut bindings::cpufreq_policy_data= ) -> &'a mut Self { + // SAFETY: Guaranteed by the safety requirements of the function. + // + // INVARIANT: The caller ensures that `ptr` is valid for writing a= nd remains valid for the + // lifetime of the returned reference. + unsafe { &mut *ptr.cast() } + } + + /// Returns a raw pointer to the underlying C `cpufreq_policy_data`. + #[inline] + pub fn as_raw(&self) -> *mut bindings::cpufreq_policy_data { + let this: *const Self =3D self; + this.cast_mut().cast() + } + + /// Wrapper for `cpufreq_generic_frequency_table_verify`. + #[inline] + pub fn generic_verify(&self) -> Result<()> { + // SAFETY: By the type invariant, the pointer stored in `self` is = valid. + to_result(unsafe { bindings::cpufreq_generic_frequency_table_verif= y(self.as_raw()) }) + } +} + +/// The frequency table index. +/// +/// Represents index with a frequency table. +/// +/// # Invariants +/// +/// The index must correspond to a valid entry in the [`Table`] it is used= for. +#[derive(Copy, Clone, PartialEq, Eq, Debug)] +pub struct TableIndex(usize); + +impl TableIndex { + /// Creates an instance of [`TableIndex`]. + /// + /// # Safety + /// + /// The caller must ensure that `index` correspond to a valid entry in= the [`Table`] it is used + /// for. + pub unsafe fn new(index: usize) -> Self { + // INVARIANT: The caller ensures that `index` correspond to a vali= d entry in the [`Table`]. + Self(index) + } +} + +impl From for usize { + #[inline] + fn from(index: TableIndex) -> Self { + index.0 + } +} + +/// CPU frequency table. +/// +/// Rust abstraction for the C `struct cpufreq_frequency_table`. +/// +/// # Invariants +/// +/// A [`Table`] instance always corresponds to a valid C `struct cpufreq_f= requency_table`. +/// +/// The callers must ensure that the `struct cpufreq_frequency_table` is v= alid for access and +/// remains valid for the lifetime of the returned reference. +/// +/// ## Examples +/// +/// The following example demonstrates how to read a frequency value from = [`Table`]. +/// +/// ``` +/// use kernel::cpufreq::{Policy, TableIndex}; +/// +/// fn show_freq(policy: &Policy) { +/// let table =3D policy.freq_table().unwrap(); +/// +/// // SAFETY: Index is a valid entry in the table. +/// let index =3D unsafe { TableIndex::new(0) }; +/// +/// pr_info!("The frequency at index 0 is: {:?}\n", table.freq(index).= unwrap()); +/// pr_info!("The flags at index 0 is: {}\n", table.flags(index)); +/// pr_info!("The data at index 0 is: {}\n", table.data(index)); +/// } +/// ``` +#[repr(transparent)] +pub struct Table(Opaque); + +impl Table { + /// Creates a reference to an existing C `struct cpufreq_frequency_tab= le` pointer. + /// + /// # Safety + /// + /// The caller must ensure that `ptr` is valid for reading and remains= valid for the lifetime + /// of the returned reference. + #[inline] + pub unsafe fn from_raw<'a>(ptr: *const bindings::cpufreq_frequency_tab= le) -> &'a Self { + // SAFETY: Guaranteed by the safety requirements of the function. + // + // INVARIANT: The caller ensures that `ptr` is valid for reading a= nd remains valid for the + // lifetime of the returned reference. + unsafe { &*ptr.cast() } + } + + /// Returns the raw mutable pointer to the C `struct cpufreq_frequency= _table`. + #[inline] + pub fn as_raw(&self) -> *mut bindings::cpufreq_frequency_table { + let this: *const Self =3D self; + this.cast_mut().cast() + } + + /// Returns frequency at `index` in the [`Table`]. + #[inline] + pub fn freq(&self, index: TableIndex) -> Result { + // SAFETY: By the type invariant, the pointer stored in `self` is = valid and `index` is + // guaranteed to be valid by its safety requirements. + Ok(Hertz::from_khz(unsafe { + (*self.as_raw().add(index.into())).frequency.try_into()? + })) + } + + /// Returns flags at `index` in the [`Table`]. + #[inline] + pub fn flags(&self, index: TableIndex) -> u32 { + // SAFETY: By the type invariant, the pointer stored in `self` is = valid and `index` is + // guaranteed to be valid by its safety requirements. + unsafe { (*self.as_raw().add(index.into())).flags } + } + + /// Returns data at `index` in the [`Table`]. + #[inline] + pub fn data(&self, index: TableIndex) -> u32 { + // SAFETY: By the type invariant, the pointer stored in `self` is = valid and `index` is + // guaranteed to be valid by its safety requirements. + unsafe { (*self.as_raw().add(index.into())).driver_data } + } +} + +/// CPU frequency table owned and pinned in memory, created from a [`Table= Builder`]. +pub struct TableBox { + entries: Pin>, +} + +impl TableBox { + /// Constructs a new [`TableBox`] from a [`KVec`] of entries. + /// + /// # Errors + /// + /// Returns `EINVAL` if the entries list is empty. + #[inline] + fn new(entries: KVec) -> Result { + if entries.is_empty() { + return Err(EINVAL); + } + + Ok(Self { + // Pin the entries to memory, since we are passing its pointer= to the C code. + entries: Pin::new(entries), + }) + } + + /// Returns a raw pointer to the underlying C `cpufreq_frequency_table= `. + #[inline] + fn as_raw(&self) -> *const bindings::cpufreq_frequency_table { + // The pointer is valid until the table gets dropped. + self.entries.as_ptr() + } +} + +impl Deref for TableBox { + type Target =3D Table; + + fn deref(&self) -> &Self::Target { + // SAFETY: The caller owns TableBox, it is safe to deref. + unsafe { Self::Target::from_raw(self.as_raw()) } + } +} + +/// CPU frequency table builder. +/// +/// This is used by the CPU frequency drivers to build a frequency table d= ynamically. +/// +/// ## Examples +/// +/// The following example demonstrates how to create a CPU frequency table. +/// +/// ``` +/// use kernel::cpufreq::{TableBuilder, TableIndex}; +/// use kernel::clk::Hertz; +/// +/// let mut builder =3D TableBuilder::new(); +/// +/// // Adds few entries to the table. +/// builder.add(Hertz::from_mhz(700), 0, 1).unwrap(); +/// builder.add(Hertz::from_mhz(800), 2, 3).unwrap(); +/// builder.add(Hertz::from_mhz(900), 4, 5).unwrap(); +/// builder.add(Hertz::from_ghz(1), 6, 7).unwrap(); +/// +/// let table =3D builder.to_table().unwrap(); +/// +/// // SAFETY: Index values correspond to valid entries in the table. +/// let (index0, index2) =3D unsafe { (TableIndex::new(0), TableIndex::new= (2)) }; +/// +/// assert_eq!(table.freq(index0), Ok(Hertz::from_mhz(700))); +/// assert_eq!(table.flags(index0), 0); +/// assert_eq!(table.data(index0), 1); +/// +/// assert_eq!(table.freq(index2), Ok(Hertz::from_mhz(900))); +/// assert_eq!(table.flags(index2), 4); +/// assert_eq!(table.data(index2), 5); +/// ``` +#[derive(Default)] +#[repr(transparent)] +pub struct TableBuilder { + entries: KVec, +} + +impl TableBuilder { + /// Creates a new instance of [`TableBuilder`]. + #[inline] + pub fn new() -> Self { + Self { + entries: KVec::new(), + } + } + + /// Adds a new entry to the table. + pub fn add(&mut self, freq: Hertz, flags: u32, driver_data: u32) -> Re= sult<()> { + // Adds the new entry at the end of the vector. + Ok(self.entries.push( + bindings::cpufreq_frequency_table { + flags, + driver_data, + frequency: freq.as_khz() as u32, + }, + GFP_KERNEL, + )?) + } + + /// Consumes the [`TableBuilder`] and returns [`TableBox`]. + pub fn to_table(mut self) -> Result { + // Add last entry to the table. + self.add(Hertz(c_ulong::MAX), 0, 0)?; + + TableBox::new(self.entries) + } +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 11d333c8c673..871fcdc09b35 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -45,6 +45,8 @@ #[cfg(CONFIG_COMMON_CLK)] pub mod clk; pub mod cpu; +#[cfg(CONFIG_CPU_FREQ)] +pub mod cpufreq; pub mod cpumask; pub mod cred; pub mod device; --=20 2.31.1.272.g89b43f80a514 From nobody Sun Dec 14 19:11:21 2025 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 6B38720E70C for ; Mon, 21 Apr 2025 07:23:31 +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=1745220214; cv=none; b=sLBJPmpZypgjpaPi/xzR3Mt9pjK43euxWl5CFS1qerh9Hnh9GbKTVreFKttVL+WYdS+cOrvCS8kWDpV9TsRb5EZDNGDd8F5ACBAyA/TcpLzWWyZ/lAXSm2SNE/sntsKxEomOx1fPi20qmXEyYqPn3S81c3gDMS1+N4HNN8Tfnwo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745220214; c=relaxed/simple; bh=c8B0zdfOKcl3kZLvMDCl9OXUOPTxNxGJhE5CEd5zVYE=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=gckxbQ8U9g3IeeBkKBhqKpsQPtWOqQAxcC35lgqAMjly6nIGQOJrCR90LotS7LLEFf6enn3qhKgFu++W3uxToRLZ53a36sLUZGcP+gVVTOBWqdv2Tqz78jSfThViGaAP5eCXj4kGeJmPA91I4POENtRtKu8UfG7vW/6BIry40gQ= 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=VvgObXlN; 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="VvgObXlN" Received: by mail-pg1-f177.google.com with SMTP id 41be03b00d2f7-ae727e87c26so2442224a12.0 for ; Mon, 21 Apr 2025 00:23:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1745220211; x=1745825011; 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=bpL6jiCJ5lYzq5wgbdxMXB6h/Nv5zlcQqf0W5EJ/Rk0=; b=VvgObXlNUf1tsV3K8RVAr+bsbrjw0hYqHLQOA2jJWEq8a2208pxGo7be9h1ZzrlFDk SJTja+7fDVdX/nxY8tdqEeZwaPPqDldhFShG2v/G9QRWJcxeiXZsC5u5E4Hfzrb2Maom YoXTD5PjTn97MAGo0SF+KKcSkU56S1EyKBAQbzCR0BFKbFTeSKkIeDQFRDK+rbRG0FHF hSzOa2o3y6tISIuqh4DGaxlmyl5o4CaPOY1quJ6Ka5GGFxfoyxvd+tM1Y0I4U5OELBI6 AQKB7CSnOegxOvc6aXFACB96imwzI63hQfBX+6jU36cOUpbrj9yXYkQzhYl0ckGvHkGB aPlg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1745220211; x=1745825011; 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=bpL6jiCJ5lYzq5wgbdxMXB6h/Nv5zlcQqf0W5EJ/Rk0=; b=AM4eOk7+8hEPXtr1jgiduL9vBpCtsB+43zfHvMJp3TmhUFM23bA5dFDHtsaNvbxjsB H2XjWde2q9aPpiExH+vBSFDIT0VBCp4J0duVR8fb9/22loULl4aCrV99Sqizv4EI9Q8N //l42BeriDufTKdd6x6a8DimxS5Om3KvE+BBbuYM2iHKn/KEGyP/4BSV/dHMLze5bnXk kNcgwxqc2fhefJJ6N4J/bCvFbkeTc+uWtgHPLIaZEOMtHFFD0M93Eqn5cGwi1OPDf64q E1lGomYzKm8f0g+tJJdBrXCRmaWyycFeuJsitjtKikmzVJiGrqschlejksZo1PAuFYVt d/gw== X-Forwarded-Encrypted: i=1; AJvYcCUeUpGtlG5PVAynPnahZ+d6BiyCxqTAltoweWKhUZfmO8KZx4G7JCqhnTTxCHK3GlC2h3sPmFDREfopnV8=@vger.kernel.org X-Gm-Message-State: AOJu0YyV8E7PAJay3rf7Wf9kmrnL68DluNAzkdT6mMVaioM99vi/gj+h R8BZYRihvCnc7LGCDQ1VbNltJgBlNjzXKsHxsqzluS5P+74ncvE37pddbiXBglU= X-Gm-Gg: ASbGnctJFw4aVtr89YWlyYKzMiOihy9QqtjEb1oOY4Zhm5Rdk1iRPyt7uNvcpY5qacn sugb8d6Hhg17wYYe5ylshxa7yP6lDn6aUdmrSBXBo47M4qfc11v0H6MTvLV2kp25p6QNYXTbQzW plLerEb+gd/B23AN+iZjkCS6k+ofw7xTrBQsm0O+5TlmiiyX36Ack3nozvbTA4CFz0BdkD2rHpR Is96j2B/cpylv+PUzaK7Vpwj/6NRRWmdRJ1veQOHDU4q32gs8wpdqKQAs+bfR3DCIaMIry0gApF uuJOw4MQSsqBqIBRi3M4pPNpui+IjwKM+riCVFoOP2CyZEeV6Wdg X-Google-Smtp-Source: AGHT+IGMFli0VYy3euttDRfjCMAL4Q976zJoX2syJmV5AdZsjrJANhQGEqaD1LKm13BERvqoBFhSsQ== X-Received: by 2002:a17:90b:548c:b0:2ee:6d08:7936 with SMTP id 98e67ed59e1d1-3087bb64809mr15511261a91.20.1745220210690; Mon, 21 Apr 2025 00:23:30 -0700 (PDT) Received: from localhost ([122.172.83.32]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3087e0febecsm5883921a91.39.2025.04.21.00.23.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Apr 2025 00:23:30 -0700 (PDT) From: Viresh Kumar To: "Rafael J. Wysocki" , Miguel Ojeda , Danilo Krummrich , 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 , Danilo Krummrich Cc: linux-pm@vger.kernel.org, Vincent Guittot , Stephen Boyd , Nishanth Menon , 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 12/15] rust: cpufreq: Extend abstractions for policy and driver ops Date: Mon, 21 Apr 2025 12:52:19 +0530 Message-Id: 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" Extend the cpufreq abstractions to include support for policy handling and driver operations. Signed-off-by: Viresh Kumar --- rust/kernel/cpufreq.rs | 457 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 456 insertions(+), 1 deletion(-) diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs index 279956c6d056..5e0bfd03fd30 100644 --- a/rust/kernel/cpufreq.rs +++ b/rust/kernel/cpufreq.rs @@ -9,15 +9,24 @@ //! Reference: =20 use crate::{ - error::{code::*, to_result, Result}, + clk::{Clk, Hertz}, + cpumask, + device::Device, + error::{code::*, from_err_ptr, to_result, Result, VTABLE_DEFAULT_ERROR= }, ffi::c_ulong, prelude::*, + types::ForeignOwnable, + types::Opaque, }; =20 use core::{ + ops::{Deref, DerefMut}, pin::Pin, + ptr, }; =20 +use macros::vtable; + /// Default transition latency value in nanoseconds. pub const ETERNAL_LATENCY_NS: u32 =3D bindings::CPUFREQ_ETERNAL as u32; =20 @@ -362,3 +371,449 @@ pub fn to_table(mut self) -> Result { TableBox::new(self.entries) } } + +/// CPU frequency policy. +/// +/// Rust abstraction for the C `struct cpufreq_policy`. +/// +/// # Invariants +/// +/// A [`Policy`] instance always corresponds to a valid C `struct cpufreq_= policy`. +/// +/// The callers must ensure that the `struct cpufreq_policy` is valid for = access and remains valid +/// for the lifetime of the returned reference. +/// +/// ## Examples +/// +/// The following example demonstrates how to create a CPU frequency table. +/// +/// ``` +/// use kernel::cpufreq::{ETERNAL_LATENCY_NS, Policy}; +/// +/// fn update_policy(policy: &mut Policy) { +/// policy +/// .set_dvfs_possible_from_any_cpu(true) +/// .set_fast_switch_possible(true) +/// .set_transition_latency_ns(ETERNAL_LATENCY_NS); +/// +/// pr_info!("The policy details are: {:?}\n", (policy.cpu(), policy.c= ur())); +/// } +/// ``` +#[repr(transparent)] +pub struct Policy(Opaque); + +impl Policy { + /// Creates a reference to an existing `struct cpufreq_policy` pointer. + /// + /// # Safety + /// + /// The caller must ensure that `ptr` is valid for reading and remains= valid for the lifetime + /// of the returned reference. + #[inline] + pub unsafe fn from_raw<'a>(ptr: *const bindings::cpufreq_policy) -> &'= a Self { + // SAFETY: Guaranteed by the safety requirements of the function. + // + // INVARIANT: The caller ensures that `ptr` is valid for reading a= nd remains valid for the + // lifetime of the returned reference. + unsafe { &*ptr.cast() } + } + + /// Creates a mutable reference to an existing `struct cpufreq_policy`= pointer. + /// + /// # Safety + /// + /// The caller must ensure that `ptr` is valid for writing and remains= valid for the lifetime + /// of the returned reference. + #[inline] + pub unsafe fn from_raw_mut<'a>(ptr: *mut bindings::cpufreq_policy) -> = &'a mut Self { + // SAFETY: Guaranteed by the safety requirements of the function. + // + // INVARIANT: The caller ensures that `ptr` is valid for writing a= nd remains valid for the + // lifetime of the returned reference. + unsafe { &mut *ptr.cast() } + } + + /// Returns a raw mutable pointer to the C `struct cpufreq_policy`. + #[inline] + fn as_raw(&self) -> *mut bindings::cpufreq_policy { + let this: *const Self =3D self; + this.cast_mut().cast() + } + + #[inline] + fn as_ref(&self) -> &bindings::cpufreq_policy { + // SAFETY: By the type invariant, the pointer stored in `self` is = valid. + unsafe { &*self.as_raw() } + } + + #[inline] + fn as_mut_ref(&mut self) -> &mut bindings::cpufreq_policy { + // SAFETY: By the type invariant, the pointer stored in `self` is = valid. + unsafe { &mut *self.as_raw() } + } + + /// Returns the primary CPU for the [`Policy`]. + #[inline] + pub fn cpu(&self) -> u32 { + self.as_ref().cpu + } + + /// Returns the minimum frequency for the [`Policy`]. + #[inline] + pub fn min(&self) -> Hertz { + Hertz::from_khz(self.as_ref().min as usize) + } + + /// Set the minimum frequency for the [`Policy`]. + #[inline] + pub fn set_min(&mut self, min: Hertz) -> &mut Self { + self.as_mut_ref().min =3D min.as_khz() as u32; + self + } + + /// Returns the maximum frequency for the [`Policy`]. + #[inline] + pub fn max(&self) -> Hertz { + Hertz::from_khz(self.as_ref().max as usize) + } + + /// Set the maximum frequency for the [`Policy`]. + #[inline] + pub fn set_max(&mut self, max: Hertz) -> &mut Self { + self.as_mut_ref().max =3D max.as_khz() as u32; + self + } + + /// Returns the current frequency for the [`Policy`]. + #[inline] + pub fn cur(&self) -> Hertz { + Hertz::from_khz(self.as_ref().cur as usize) + } + + /// Returns the suspend frequency for the [`Policy`]. + #[inline] + pub fn suspend_freq(&self) -> Hertz { + Hertz::from_khz(self.as_ref().suspend_freq as usize) + } + + /// Sets the suspend frequency for the [`Policy`]. + #[inline] + pub fn set_suspend_freq(&mut self, freq: Hertz) -> &mut Self { + self.as_mut_ref().suspend_freq =3D freq.as_khz() as u32; + self + } + + /// Provides a wrapper to the generic suspend routine. + #[inline] + pub fn generic_suspend(&mut self) -> Result<()> { + // SAFETY: By the type invariant, the pointer stored in `self` is = valid. + to_result(unsafe { bindings::cpufreq_generic_suspend(self.as_mut_r= ef()) }) + } + + /// Provides a wrapper to the generic get routine. + #[inline] + pub fn generic_get(&self) -> Result { + // SAFETY: By the type invariant, the pointer stored in `self` is = valid. + Ok(unsafe { bindings::cpufreq_generic_get(self.cpu()) }) + } + + /// Provides a wrapper to the register with energy model using the OPP= core. + #[cfg(CONFIG_PM_OPP)] + #[inline] + pub fn register_em_opp(&mut self) { + // SAFETY: By the type invariant, the pointer stored in `self` is = valid. + unsafe { bindings::cpufreq_register_em_with_opp(self.as_mut_ref())= }; + } + + /// Gets [`cpumask::Cpumask`] for a cpufreq [`Policy`]. + #[inline] + pub fn cpus(&mut self) -> &mut cpumask::Cpumask { + // SAFETY: The pointer to `cpus` is valid for writing and remains = valid for the lifetime of + // the returned reference. + unsafe { cpumask::CpumaskVar::as_mut_ref(&mut self.as_mut_ref().cp= us) } + } + + /// Sets clock for the [`Policy`]. + /// + /// # Safety + /// + /// The caller must guarantee that the returned [`Clk`] is not dropped= while it is getting used + /// by the C code. + pub unsafe fn set_clk(&mut self, dev: &Device, name: Option<&CStr>) ->= Result { + let clk =3D Clk::get(dev, name)?; + self.as_mut_ref().clk =3D clk.as_raw(); + Ok(clk) + } + + /// Allows / disallows frequency switching code to run on any CPU. + #[inline] + pub fn set_dvfs_possible_from_any_cpu(&mut self, val: bool) -> &mut Se= lf { + self.as_mut_ref().dvfs_possible_from_any_cpu =3D val; + self + } + + /// Returns if fast switching of frequencies is possible or not. + #[inline] + pub fn fast_switch_possible(&self) -> bool { + self.as_ref().fast_switch_possible + } + + /// Enables / disables fast frequency switching. + #[inline] + pub fn set_fast_switch_possible(&mut self, val: bool) -> &mut Self { + self.as_mut_ref().fast_switch_possible =3D val; + self + } + + /// Sets transition latency (in nanoseconds) for the [`Policy`]. + #[inline] + pub fn set_transition_latency_ns(&mut self, latency_ns: u32) -> &mut S= elf { + self.as_mut_ref().cpuinfo.transition_latency =3D latency_ns; + self + } + + /// Sets cpuinfo `min_freq`. + #[inline] + pub fn set_cpuinfo_min_freq(&mut self, min_freq: Hertz) -> &mut Self { + self.as_mut_ref().cpuinfo.min_freq =3D min_freq.as_khz() as u32; + self + } + + /// Sets cpuinfo `max_freq`. + #[inline] + pub fn set_cpuinfo_max_freq(&mut self, max_freq: Hertz) -> &mut Self { + self.as_mut_ref().cpuinfo.max_freq =3D max_freq.as_khz() as u32; + self + } + + /// Set `transition_delay_us`, i.e. the minimum time between successiv= e frequency change + /// requests. + #[inline] + pub fn set_transition_delay_us(&mut self, transition_delay_us: u32) ->= &mut Self { + self.as_mut_ref().transition_delay_us =3D transition_delay_us; + self + } + + /// Returns reference to the CPU frequency [`Table`] for the [`Policy`= ]. + pub fn freq_table(&self) -> Result<&Table> { + if self.as_ref().freq_table.is_null() { + return Err(EINVAL); + } + + // SAFETY: The `freq_table` is guaranteed to be valid for reading = and remains valid for the + // lifetime of the returned reference. + Ok(unsafe { Table::from_raw(self.as_ref().freq_table) }) + } + + /// Sets the CPU frequency [`Table`] for the [`Policy`]. + /// + /// # Safety + /// + /// The caller must guarantee that the [`Table`] is not dropped while = it is getting used by the + /// C code. + #[inline] + pub unsafe fn set_freq_table(&mut self, table: &Table) -> &mut Self { + self.as_mut_ref().freq_table =3D table.as_raw(); + self + } + + /// Returns the [`Policy`]'s private data. + pub fn data(&mut self) -> Option<::Borrowed<'_>>= { + if self.as_ref().driver_data.is_null() { + None + } else { + // SAFETY: The data is earlier set from [`set_data`]. + Some(unsafe { T::borrow(self.as_ref().driver_data) }) + } + } + + /// Sets the private data of the [`Policy`] using a foreign-ownable wr= apper. + /// + /// # Errors + /// + /// Returns `EBUSY` if private data is already set. + fn set_data(&mut self, data: T) -> Result<()> { + if self.as_ref().driver_data.is_null() { + // Transfer the ownership of the data to the foreign interface. + self.as_mut_ref().driver_data =3D ::into_= foreign(data) as _; + Ok(()) + } else { + Err(EBUSY) + } + } + + /// Clears and returns ownership of the private data. + fn clear_data(&mut self) -> Option { + if self.as_ref().driver_data.is_null() { + None + } else { + let data =3D Some( + // SAFETY: The data is earlier set by us from [`set_data`]= . It is safe to take + // back the ownership of the data from the foreign interfa= ce. + unsafe { ::from_foreign(self.as_ref()= .driver_data) }, + ); + self.as_mut_ref().driver_data =3D ptr::null_mut(); + data + } + } +} + +/// CPU frequency policy created from a CPU number. +/// +/// This struct represents the CPU frequency policy obtained for a specifi= c CPU, providing safe +/// access to the underlying `cpufreq_policy` and ensuring proper cleanup = when the `PolicyCpu` is +/// dropped. +struct PolicyCpu<'a>(&'a mut Policy); + +impl<'a> PolicyCpu<'a> { + fn from_cpu(cpu: u32) -> Result { + // SAFETY: It is safe to call `cpufreq_cpu_get` for any valid CPU. + let ptr =3D from_err_ptr(unsafe { bindings::cpufreq_cpu_get(cpu) }= )?; + + Ok(Self( + // SAFETY: The `ptr` is guaranteed to be valid and remains val= id for the lifetime of + // the returned reference. + unsafe { Policy::from_raw_mut(ptr) }, + )) + } +} + +impl<'a> Deref for PolicyCpu<'a> { + type Target =3D Policy; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +impl<'a> DerefMut for PolicyCpu<'a> { + fn deref_mut(&mut self) -> &mut Policy { + self.0 + } +} + +impl<'a> Drop for PolicyCpu<'a> { + fn drop(&mut self) { + // SAFETY: The underlying pointer is guaranteed to be valid for th= e lifetime of `self`. + unsafe { bindings::cpufreq_cpu_put(self.0.as_raw()) }; + } +} + +/// CPU frequency driver. +/// +/// Implement this trait to provide a CPU frequency driver and its callbac= ks. +/// +/// Reference: +#[vtable] +pub trait Driver { + /// Driver's name. + const NAME: &'static CStr; + + /// Driver's flags. + const FLAGS: u16; + + /// Boost support. + const BOOST_ENABLED: bool; + + /// Policy specific data. + /// + /// Require that `PData` implements `ForeignOwnable`. We guarantee to = never move the underlying + /// wrapped data structure. + type PData: ForeignOwnable; + + /// Driver's `init` callback. + fn init(policy: &mut Policy) -> Result; + + /// Driver's `exit` callback. + fn exit(_policy: &mut Policy, _data: Option) -> Result<()= > { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `online` callback. + fn online(_policy: &mut Policy) -> Result<()> { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `offline` callback. + fn offline(_policy: &mut Policy) -> Result<()> { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `suspend` callback. + fn suspend(_policy: &mut Policy) -> Result<()> { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `resume` callback. + fn resume(_policy: &mut Policy) -> Result<()> { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `ready` callback. + fn ready(_policy: &mut Policy) { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `verify` callback. + fn verify(data: &mut PolicyData) -> Result<()>; + + /// Driver's `setpolicy` callback. + fn setpolicy(_policy: &mut Policy) -> Result<()> { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `target` callback. + fn target(_policy: &mut Policy, _target_freq: u32, _relation: Relation= ) -> Result<()> { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `target_index` callback. + fn target_index(_policy: &mut Policy, _index: TableIndex) -> Result<()= > { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `fast_switch` callback. + fn fast_switch(_policy: &mut Policy, _target_freq: u32) -> u32 { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `adjust_perf` callback. + fn adjust_perf(_policy: &mut Policy, _min_perf: usize, _target_perf: u= size, _capacity: usize) { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `get_intermediate` callback. + fn get_intermediate(_policy: &mut Policy, _index: TableIndex) -> u32 { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `target_intermediate` callback. + fn target_intermediate(_policy: &mut Policy, _index: TableIndex) -> Re= sult<()> { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `get` callback. + fn get(_policy: &mut Policy) -> Result { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `update_limits` callback. + fn update_limits(_policy: &mut Policy) { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `bios_limit` callback. + fn bios_limit(_policy: &mut Policy, _limit: &mut u32) -> Result<()> { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `set_boost` callback. + fn set_boost(_policy: &mut Policy, _state: i32) -> Result<()> { + build_error!(VTABLE_DEFAULT_ERROR) + } + + /// Driver's `register_em` callback. + fn register_em(_policy: &mut Policy) { + build_error!(VTABLE_DEFAULT_ERROR) + } +} --=20 2.31.1.272.g89b43f80a514 From nobody Sun Dec 14 19:11:21 2025 Received: from mail-pg1-f169.google.com (mail-pg1-f169.google.com [209.85.215.169]) (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 866D521018A for ; Mon, 21 Apr 2025 07:23:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.215.169 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745220218; cv=none; b=SYoXa0LgqJG4V1FVrvLzXYX3bBPugIy17rP72pM650O8Yfb1EZKguYYoyKxh/Nw9z9aSXW1eXaUggt9GrYM6jyddbTWCoRQCs1OAeQSZ1nIfod5ZTD2zJjHEuZjk0y2Mr4aOMqbjUoTrkRuLJJoJNx+3B4DJ9GtSJRHKJYEWthc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745220218; c=relaxed/simple; bh=gZd31rqZQhyAUQq8i0iGf8Lw8ZGXe9O0x5ZPzGsOCHA=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=mtjdDW94qbs5cYTXI0CMBSmMMqCT2m/ba8t+CCsIs8rYZTGTzakakfkyOLfy0zzlyuA+JT3sk7ozda4uV9ZpngrApGrmNOpFrjUpkCPxclFILzTZpozjh6QslDE0ORkUyy/vdmEIqp4CG0EOFz/4bmDKtmvGsc3+ncRrzj19Vhs= 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=dMV6VXTR; arc=none smtp.client-ip=209.85.215.169 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="dMV6VXTR" Received: by mail-pg1-f169.google.com with SMTP id 41be03b00d2f7-af589091049so2645085a12.1 for ; Mon, 21 Apr 2025 00:23:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1745220215; x=1745825015; 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=fAkrxQebm2AdzzaPBvbtGv5AqozQEpcrJo8iSbqCLmQ=; b=dMV6VXTRDxidNxlH/oZMO8QSSgThcBI7Bu37hKCqmKpEenI+LWc42EPruJpbMNW73R yS9fHj0g0N4oTRMXAY7a6WdYhvTiCGr2GboqQbl+2kp70dooN3OomBnbbTxxsZ34vqU8 zd1bZJvP6ZXixHRURkfczO0zqGmzgMRXPeVaLgahdLxyVL5gIk7qmBWcbC4Dfci4/cH8 Ghaik+oQIOXd0Wn4ZGacCGNFi1OKYym+bJHiJjCVMCrrNKTrSVuOHgKE7f1GgUPIDQxt y6G0jMNv1OBPrk5pUXhgN0Vo8G0lgFTUBcNBWMSmTahQLZF+VO3Jo0APR/6TPglMLf0K 6QyQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1745220215; x=1745825015; 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=fAkrxQebm2AdzzaPBvbtGv5AqozQEpcrJo8iSbqCLmQ=; b=tmzZEjpmTp/2AaxnbG4hQiq5a46cXPfiB3+CaY/q7mgHLyDxsjEp9sdGbG4Nf7O/1R 5x25waIRPu4AIb9rJy+cSj6EPnxcaTsvv0KpyPF/E4lZEpVfrLz8KQTGy+afTuVcDLmt gUT34uWaLn4YLju7ECleu0F9LJeUj8mp84ST9IZvOR8i44ZoIK3Ht4tQkMgHluqvSFqj cFg9Q080gICNrCjPSwDS+K9cKlCaSk+O/cymfTEvsORywxmAh6S7ytUxzBJateermJcJ pU6yDr61VlHKntDSTKL7j9idoK4C3QAWDL55ntklOHv0RH+RvcjzRogYafkaI5+x90w1 E5rg== X-Forwarded-Encrypted: i=1; AJvYcCWPm3CgIMgN8+J008qq8O1rqDVGIVMCppLl1QgcLpVf1Se7bm9pAn0QQeJ3EdMbFQymhbEh6k7kS6BFAMA=@vger.kernel.org X-Gm-Message-State: AOJu0YxVoP3qM82/ugz23WEN7UzYDM+4iMzHn5oZFHZs0hzd/YpoJjhC da8Ya6yYwSS6tBT1lpevhjlsPDAfQ41GXLqGmavaL9OsOf1s+17311WtToSyRRk= X-Gm-Gg: ASbGncunsVdnVIhdGLnHkLhne5QqdedcG57bs/FYDceif1pIj+BEuC7YUFA4ApNvPqq WjGvGiDV7djY0FH1AzljECygmav2EhYd0m+VIbHWrUaezC0cmDn/iaUxA9SJH9nU/beEPpjoBm3 eSX2oDvwJPhJDWPlQdBoTcau+CXu2s+tHpbGtnLDeOwAT9xOINNR/VL6cT+5Qq5d5kQPnwOGjVm MuklogBoq0Wv/BUaKL+SiIeG3j5n/BCrXq+eXwwc3F+1pvJc+xgXesM9ZvgS3vKrzMVqFxgZjku jyjJhK5BMHZl8e5HnG0HdNiP3ylhU3FY6wF2x0iPuWpQAOAuIp3T X-Google-Smtp-Source: AGHT+IEcUZHsrqwtkbhC9SlJDWdZKMj7QHRVNVR7YAvHyuD1w71yIIS226WqC5lIgajPW/vq6BqrRg== X-Received: by 2002:a17:903:19e3:b0:224:1af1:87f4 with SMTP id d9443c01a7336-22c5358625amr184712855ad.22.1745220214596; Mon, 21 Apr 2025 00:23:34 -0700 (PDT) Received: from localhost ([122.172.83.32]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-22c50bdda51sm59571685ad.30.2025.04.21.00.23.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Apr 2025 00:23:34 -0700 (PDT) From: Viresh Kumar To: "Rafael J. Wysocki" , Miguel Ojeda , Danilo Krummrich , 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 , Danilo Krummrich Cc: linux-pm@vger.kernel.org, Vincent Guittot , Stephen Boyd , Nishanth Menon , 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 13/15] rust: cpufreq: Extend abstractions for driver registration Date: Mon, 21 Apr 2025 12:52:20 +0530 Message-Id: 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" Extend the cpufreq abstractions to support driver registration from Rust. Signed-off-by: Viresh Kumar --- rust/kernel/cpufreq.rs | 490 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 488 insertions(+), 2 deletions(-) diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs index 5e0bfd03fd30..8628f0bada02 100644 --- a/rust/kernel/cpufreq.rs +++ b/rust/kernel/cpufreq.rs @@ -12,14 +12,18 @@ clk::{Clk, Hertz}, cpumask, device::Device, - error::{code::*, from_err_ptr, to_result, Result, VTABLE_DEFAULT_ERROR= }, - ffi::c_ulong, + devres::Devres, + error::{code::*, from_err_ptr, from_result, to_result, Result, VTABLE_= DEFAULT_ERROR}, + ffi::{c_char, c_ulong}, prelude::*, types::ForeignOwnable, types::Opaque, }; =20 use core::{ + cell::UnsafeCell, + marker::PhantomData, + mem::MaybeUninit, ops::{Deref, DerefMut}, pin::Pin, ptr, @@ -27,6 +31,9 @@ =20 use macros::vtable; =20 +/// Maximum length of CPU frequency driver's name. +const CPUFREQ_NAME_LEN: usize =3D bindings::CPUFREQ_NAME_LEN as usize; + /// Default transition latency value in nanoseconds. pub const ETERNAL_LATENCY_NS: u32 =3D bindings::CPUFREQ_ETERNAL as u32; =20 @@ -817,3 +824,482 @@ fn register_em(_policy: &mut Policy) { build_error!(VTABLE_DEFAULT_ERROR) } } + +/// CPU frequency driver Registration. +/// +/// ## Examples +/// +/// The following example demonstrates how to register a cpufreq driver. +/// +/// ``` +/// use kernel::{ +/// cpu, cpufreq, +/// c_str, +/// device::Device, +/// macros::vtable, +/// sync::Arc, +/// }; +/// struct FooDevice; +/// +/// #[derive(Default)] +/// struct FooDriver; +/// +/// #[vtable] +/// impl cpufreq::Driver for FooDriver { +/// const NAME: &'static CStr =3D c_str!("cpufreq-foo"); +/// const FLAGS: u16 =3D cpufreq::flags::NEED_INITIAL_FREQ_CHECK | cpu= freq::flags::IS_COOLING_DEV; +/// const BOOST_ENABLED: bool =3D true; +/// +/// type PData =3D Arc; +/// +/// fn init(policy: &mut cpufreq::Policy) -> Result { +/// // Initialize here +/// Ok(Arc::new(FooDevice, GFP_KERNEL)?) +/// } +/// +/// fn exit(_policy: &mut cpufreq::Policy, _data: Option)= -> Result<()> { +/// 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: cpufreq::Tabl= eIndex) -> Result<()> { +/// // Update CPU frequency +/// Ok(()) +/// } +/// +/// fn get(policy: &mut cpufreq::Policy) -> Result { +/// policy.generic_get() +/// } +/// } +/// +/// fn foo_probe(dev: &Device) { +/// cpufreq::Registration::::new_foreign_owned(dev).unwrap(= ); +/// } +/// ``` +#[repr(transparent)] +pub struct Registration(KBox>, PhantomData); + +/// SAFETY: `Registration` doesn't offer any methods or access to fields w= hen shared between threads +/// or CPUs, so it is safe to share it. +unsafe impl Sync for Registration {} + +#[allow(clippy::non_send_fields_in_send_ty)] +/// SAFETY: Registration with and unregistration from the cpufreq subsyste= m can happen from any +/// thread. +unsafe impl Send for Registration {} + +impl Registration { + const VTABLE: bindings::cpufreq_driver =3D bindings::cpufreq_driver { + name: Self::copy_name(T::NAME), + boost_enabled: T::BOOST_ENABLED, + flags: T::FLAGS, + + // Initialize mandatory callbacks. + init: Some(Self::init_callback), + verify: Some(Self::verify_callback), + + // Initialize optional callbacks based on the traits of `T`. + setpolicy: if T::HAS_SETPOLICY { + Some(Self::setpolicy_callback) + } else { + None + }, + target: if T::HAS_TARGET { + Some(Self::target_callback) + } else { + None + }, + target_index: if T::HAS_TARGET_INDEX { + Some(Self::target_index_callback) + } else { + None + }, + fast_switch: if T::HAS_FAST_SWITCH { + Some(Self::fast_switch_callback) + } else { + None + }, + adjust_perf: if T::HAS_ADJUST_PERF { + Some(Self::adjust_perf_callback) + } else { + None + }, + get_intermediate: if T::HAS_GET_INTERMEDIATE { + Some(Self::get_intermediate_callback) + } else { + None + }, + target_intermediate: if T::HAS_TARGET_INTERMEDIATE { + Some(Self::target_intermediate_callback) + } else { + None + }, + get: if T::HAS_GET { + Some(Self::get_callback) + } else { + None + }, + update_limits: if T::HAS_UPDATE_LIMITS { + Some(Self::update_limits_callback) + } else { + None + }, + bios_limit: if T::HAS_BIOS_LIMIT { + Some(Self::bios_limit_callback) + } else { + None + }, + online: if T::HAS_ONLINE { + Some(Self::online_callback) + } else { + None + }, + offline: if T::HAS_OFFLINE { + Some(Self::offline_callback) + } else { + None + }, + exit: if T::HAS_EXIT { + Some(Self::exit_callback) + } else { + None + }, + suspend: if T::HAS_SUSPEND { + Some(Self::suspend_callback) + } else { + None + }, + resume: if T::HAS_RESUME { + Some(Self::resume_callback) + } else { + None + }, + ready: if T::HAS_READY { + Some(Self::ready_callback) + } else { + None + }, + set_boost: if T::HAS_SET_BOOST { + Some(Self::set_boost_callback) + } else { + None + }, + register_em: if T::HAS_REGISTER_EM { + Some(Self::register_em_callback) + } else { + None + }, + // SAFETY: All zeros is a valid value for `bindings::cpufreq_drive= r`. + ..unsafe { MaybeUninit::zeroed().assume_init() } + }; + + const fn copy_name(name: &'static CStr) -> [c_char; CPUFREQ_NAME_LEN] { + let src =3D name.as_bytes_with_nul(); + let mut dst =3D [0; CPUFREQ_NAME_LEN]; + + build_assert!(src.len() <=3D CPUFREQ_NAME_LEN); + + let mut i =3D 0; + while i < src.len() { + dst[i] =3D src[i]; + i +=3D 1; + } + + dst + } + + /// Registers a CPU frequency driver with the cpufreq core. + pub fn new() -> Result { + // We can't use `&Self::VTABLE` directly because the cpufreq core = modifies some fields in + // the C `struct cpufreq_driver`, which requires a mutable referen= ce. + let mut drv =3D KBox::new(UnsafeCell::new(Self::VTABLE), GFP_KERNE= L)?; + + // SAFETY: `drv` is guaranteed to be valid for the lifetime of `Re= gistration`. + to_result(unsafe { bindings::cpufreq_register_driver(drv.get_mut()= ) })?; + + Ok(Self(drv, PhantomData)) + } + + /// Same as [`Registration::new`], but does not return a [`Registratio= n`] instance. + /// + /// Instead the [`Registration`] is owned by [`Devres`] and will be re= voked / dropped, once the + /// device is detached. + pub fn new_foreign_owned(dev: &Device) -> Result<()> { + Devres::new_foreign_owned(dev, Self::new()?, GFP_KERNEL) + } +} + +/// CPU frequency driver callbacks. +impl Registration { + /// Driver's `init` callback. + /// + /// SAFETY: Called from C. Inputs must be valid pointers. + extern "C" fn init_callback(ptr: *mut bindings::cpufreq_policy) -> ker= nel::ffi::c_int { + from_result(|| { + // SAFETY: The `ptr` is guaranteed to be valid by the contract= with the C code for the + // lifetime of `policy`. + let policy =3D unsafe { Policy::from_raw_mut(ptr) }; + + let data =3D T::init(policy)?; + policy.set_data(data)?; + Ok(0) + }) + } + + /// Driver's `exit` callback. + /// + /// SAFETY: Called from C. Inputs must be valid pointers. + extern "C" fn exit_callback(ptr: *mut bindings::cpufreq_policy) { + // SAFETY: The `ptr` is guaranteed to be valid by the contract wit= h the C code for the + // lifetime of `policy`. + let policy =3D unsafe { Policy::from_raw_mut(ptr) }; + + let data =3D policy.clear_data(); + let _ =3D T::exit(policy, data); + } + + /// Driver's `online` callback. + /// + /// SAFETY: Called from C. Inputs must be valid pointers. + extern "C" fn online_callback(ptr: *mut bindings::cpufreq_policy) -> k= ernel::ffi::c_int { + from_result(|| { + // SAFETY: The `ptr` is guaranteed to be valid by the contract= with the C code for the + // lifetime of `policy`. + let policy =3D unsafe { Policy::from_raw_mut(ptr) }; + T::online(policy).map(|()| 0) + }) + } + + /// Driver's `offline` callback. + /// + /// SAFETY: Called from C. Inputs must be valid pointers. + extern "C" fn offline_callback(ptr: *mut bindings::cpufreq_policy) -> = kernel::ffi::c_int { + from_result(|| { + // SAFETY: The `ptr` is guaranteed to be valid by the contract= with the C code for the + // lifetime of `policy`. + let policy =3D unsafe { Policy::from_raw_mut(ptr) }; + T::offline(policy).map(|()| 0) + }) + } + + /// Driver's `suspend` callback. + /// + /// SAFETY: Called from C. Inputs must be valid pointers. + extern "C" fn suspend_callback(ptr: *mut bindings::cpufreq_policy) -> = kernel::ffi::c_int { + from_result(|| { + // SAFETY: The `ptr` is guaranteed to be valid by the contract= with the C code for the + // lifetime of `policy`. + let policy =3D unsafe { Policy::from_raw_mut(ptr) }; + T::suspend(policy).map(|()| 0) + }) + } + + /// Driver's `resume` callback. + /// + /// SAFETY: Called from C. Inputs must be valid pointers. + extern "C" fn resume_callback(ptr: *mut bindings::cpufreq_policy) -> k= ernel::ffi::c_int { + from_result(|| { + // SAFETY: The `ptr` is guaranteed to be valid by the contract= with the C code for the + // lifetime of `policy`. + let policy =3D unsafe { Policy::from_raw_mut(ptr) }; + T::resume(policy).map(|()| 0) + }) + } + + /// Driver's `ready` callback. + /// + /// SAFETY: Called from C. Inputs must be valid pointers. + extern "C" fn ready_callback(ptr: *mut bindings::cpufreq_policy) { + // SAFETY: The `ptr` is guaranteed to be valid by the contract wit= h the C code for the + // lifetime of `policy`. + let policy =3D unsafe { Policy::from_raw_mut(ptr) }; + T::ready(policy); + } + + /// Driver's `verify` callback. + /// + /// SAFETY: Called from C. Inputs must be valid pointers. + extern "C" fn verify_callback(ptr: *mut bindings::cpufreq_policy_data)= -> kernel::ffi::c_int { + from_result(|| { + // SAFETY: The `ptr` is guaranteed to be valid by the contract= with the C code for the + // lifetime of `policy`. + let data =3D unsafe { PolicyData::from_raw_mut(ptr) }; + T::verify(data).map(|()| 0) + }) + } + + /// Driver's `setpolicy` callback. + /// + /// SAFETY: Called from C. Inputs must be valid pointers. + extern "C" fn setpolicy_callback(ptr: *mut bindings::cpufreq_policy) -= > kernel::ffi::c_int { + from_result(|| { + // SAFETY: The `ptr` is guaranteed to be valid by the contract= with the C code for the + // lifetime of `policy`. + let policy =3D unsafe { Policy::from_raw_mut(ptr) }; + T::setpolicy(policy).map(|()| 0) + }) + } + + /// Driver's `target` callback. + /// + /// SAFETY: Called from C. Inputs must be valid pointers. + extern "C" fn target_callback( + ptr: *mut bindings::cpufreq_policy, + target_freq: u32, + relation: u32, + ) -> kernel::ffi::c_int { + from_result(|| { + // SAFETY: The `ptr` is guaranteed to be valid by the contract= with the C code for the + // lifetime of `policy`. + let policy =3D unsafe { Policy::from_raw_mut(ptr) }; + T::target(policy, target_freq, Relation::new(relation)?).map(|= ()| 0) + }) + } + + /// Driver's `target_index` callback. + /// + /// SAFETY: Called from C. Inputs must be valid pointers. + extern "C" fn target_index_callback( + ptr: *mut bindings::cpufreq_policy, + index: u32, + ) -> kernel::ffi::c_int { + from_result(|| { + // SAFETY: The `ptr` is guaranteed to be valid by the contract= with the C code for the + // lifetime of `policy`. + let policy =3D unsafe { Policy::from_raw_mut(ptr) }; + + // SAFETY: The C code guarantees that `index` corresponds to a= valid entry in the + // frequency table. + let index =3D unsafe { TableIndex::new(index as usize) }; + + T::target_index(policy, index).map(|()| 0) + }) + } + + /// Driver's `fast_switch` callback. + /// + /// SAFETY: Called from C. Inputs must be valid pointers. + extern "C" fn fast_switch_callback( + ptr: *mut bindings::cpufreq_policy, + target_freq: u32, + ) -> kernel::ffi::c_uint { + // SAFETY: The `ptr` is guaranteed to be valid by the contract wit= h the C code for the + // lifetime of `policy`. + let policy =3D unsafe { Policy::from_raw_mut(ptr) }; + T::fast_switch(policy, target_freq) + } + + /// Driver's `adjust_perf` callback. + extern "C" fn adjust_perf_callback( + cpu: u32, + min_perf: usize, + target_perf: usize, + capacity: usize, + ) { + if let Ok(mut policy) =3D PolicyCpu::from_cpu(cpu) { + T::adjust_perf(&mut policy, min_perf, target_perf, capacity); + } + } + + /// Driver's `get_intermediate` callback. + /// + /// SAFETY: Called from C. Inputs must be valid pointers. + extern "C" fn get_intermediate_callback( + ptr: *mut bindings::cpufreq_policy, + index: u32, + ) -> kernel::ffi::c_uint { + // SAFETY: The `ptr` is guaranteed to be valid by the contract wit= h the C code for the + // lifetime of `policy`. + let policy =3D unsafe { Policy::from_raw_mut(ptr) }; + + // SAFETY: The C code guarantees that `index` corresponds to a val= id entry in the + // frequency table. + let index =3D unsafe { TableIndex::new(index as usize) }; + + T::get_intermediate(policy, index) + } + + /// Driver's `target_intermediate` callback. + /// + /// SAFETY: Called from C. Inputs must be valid pointers. + extern "C" fn target_intermediate_callback( + ptr: *mut bindings::cpufreq_policy, + index: u32, + ) -> kernel::ffi::c_int { + from_result(|| { + // SAFETY: The `ptr` is guaranteed to be valid by the contract= with the C code for the + // lifetime of `policy`. + let policy =3D unsafe { Policy::from_raw_mut(ptr) }; + + // SAFETY: The C code guarantees that `index` corresponds to a= valid entry in the + // frequency table. + let index =3D unsafe { TableIndex::new(index as usize) }; + + T::target_intermediate(policy, index).map(|()| 0) + }) + } + + /// Driver's `get` callback. + extern "C" fn get_callback(cpu: u32) -> kernel::ffi::c_uint { + PolicyCpu::from_cpu(cpu).map_or(0, |mut policy| T::get(&mut policy= ).map_or(0, |f| f)) + } + + /// Driver's `update_limit` callback. + extern "C" fn update_limits_callback(cpu: u32) { + if let Ok(mut policy) =3D PolicyCpu::from_cpu(cpu) { + T::update_limits(&mut policy); + } + } + + /// Driver's `bios_limit` callback. + /// + /// SAFETY: Called from C. Inputs must be valid pointers. + extern "C" fn bios_limit_callback(cpu: i32, limit: *mut u32) -> kernel= ::ffi::c_int { + from_result(|| { + let mut policy =3D PolicyCpu::from_cpu(cpu as u32)?; + + // SAFETY: `limit` is guaranteed by the C code to be valid. + T::bios_limit(&mut policy, &mut (unsafe { *limit })).map(|()| = 0) + }) + } + + /// Driver's `set_boost` callback. + /// + /// SAFETY: Called from C. Inputs must be valid pointers. + extern "C" fn set_boost_callback( + ptr: *mut bindings::cpufreq_policy, + state: i32, + ) -> kernel::ffi::c_int { + from_result(|| { + // SAFETY: The `ptr` is guaranteed to be valid by the contract= with the C code for the + // lifetime of `policy`. + let policy =3D unsafe { Policy::from_raw_mut(ptr) }; + T::set_boost(policy, state).map(|()| 0) + }) + } + + /// Driver's `register_em` callback. + /// + /// SAFETY: Called from C. Inputs must be valid pointers. + extern "C" fn register_em_callback(ptr: *mut bindings::cpufreq_policy)= { + // SAFETY: The `ptr` is guaranteed to be valid by the contract wit= h the C code for the + // lifetime of `policy`. + let policy =3D unsafe { Policy::from_raw_mut(ptr) }; + T::register_em(policy); + } +} + +impl Drop for Registration { + /// Unregisters with the cpufreq core. + fn drop(&mut self) { + // SAFETY: `self.0` is guaranteed to be valid for the lifetime of = `Registration`. + unsafe { bindings::cpufreq_unregister_driver(self.0.get_mut()) }; + } +} --=20 2.31.1.272.g89b43f80a514 From nobody Sun Dec 14 19:11:21 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 B1BCE25C703 for ; Mon, 21 Apr 2025 07:23:38 +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=1745220220; cv=none; b=KNha0LPjclqlt0U/FnLBRHVGJ6pPm8WCmxVBlkyJ5fsW1NEzCWy2ExZuyePbajPcVA4mOJQQ4NVU+c+OOsWqfEfK6SC6zKKdiT7DuNQ8DIXfq0Fky5UF3p3E4S4iGbWbQ86PkEwcC7sWBopYIoEzKK8g3+O/M7Es6yWWu/6EcLo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745220220; c=relaxed/simple; bh=ZJOJWcwqtx8d8xu+lxGA8TNMktPEoGoBFWcawtAMpbY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=j3RTiTOtDaIe/SAOm1KpkVg5RVK4XRszexPo7dqx3cHn4+qW9MTeaXk3gm9a+QmaXwja8FdaE78UPjkJiRr1/d4GllI+agmi0pe/UoxjNs0z/tUZk9O+0F32Lv5Uroo5OYZL4+TQHZhQY/sbcq6jAjd2hsfJM2V/3N5koQ+Dy9A= 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=dJv652ZE; 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="dJv652ZE" Received: by mail-pl1-f181.google.com with SMTP id d9443c01a7336-223fd89d036so44646255ad.1 for ; Mon, 21 Apr 2025 00:23:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1745220218; x=1745825018; 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=H3pQlqLezjnktSQ7+5L21Axz5wFzz+Z2e/cSw6NoPQ4=; b=dJv652ZEcQfCXa0RNr34erD5xSbJOhKY/6sAwlcmAAPp4irZjFSuC2xb0cmKZT/Dyp taIpcDURihaaRWRO1K89P6ecIxFqzEfj1gqKTsPY4h1YY0lagaID2aMH+l0La1+d2Fpn FWAQnRBwjlomoU6Ai5D6iKS3rnE5T6/n4xTwcb89au3oefNDE9FJTz0Bf5X9bQXl67sN SMDcR5uOifLGcNdoziZYm2Wq7ir1a0cI1YoPwZWA9y5Ge+s7YumRRrzPkc2aQt2sNAAm WLOz9ypNIqR+PnMihvXdeLe9kjsJvikgPCfyiAi8x30omvdoULxGL8sPQO5puYPOLNmz AMfQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1745220218; x=1745825018; 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=H3pQlqLezjnktSQ7+5L21Axz5wFzz+Z2e/cSw6NoPQ4=; b=k4MM4A2vypZ/3meiKcZouJSym4yo7kMz/bIulZGTLUbKEKh19AEkz+/E/m8F8A36EU GBSneVNp0xXREj/ShfyITZ6GaNxEDOctzQws1dAia56iwXPP6y/gfBiOPw8i9G/8evWE CBzL2tej3P2cxaYCmr4S48Fuj3QSw9nk5pa8H8yvzsUjHtDz7VLSxX6xvXnsOC2TgA3j RikPks3yxcaS/T8MniwHEXDGLnWCl7TM9G2Vny9BY5K9BwfMjkjQgIAcNbDm5W87MXQD s0pjC6zU/ANOTUJ9I/vuXT7yIg5Xg0l0C5CGRDZGvvtE2n1yOYPg4yB59tIY35IhxlIi JTDw== X-Forwarded-Encrypted: i=1; AJvYcCWx9gMpAGmng5dxADxPWfLYik3HGE5vHzu4ryWHWg9JOMqmGM5iwijErWjZgbkBPVcEk3zCj9X0x5MEmm0=@vger.kernel.org X-Gm-Message-State: AOJu0YxOF+zC6GUb6Wh1tC0UZtasUkjfcVwbyCJssO8ZCm2UdbxHirlB CWzKD9CVR7O6HJG+Rh7W9UKgotls368fr6pBfjPEF71DXHxyzvYjEPIU0wS0nE8= X-Gm-Gg: ASbGnctzkTylscyRO8kQFW7Hl7PmRVrHc7GcleckeMN7iIy3JyNRaXf6q7UzRrtrMPW AW6T4E2o0iOqiS12duAWFcsIFQQvtLK73JFcYlTG61Qczp0inHVgHQZIrDkQTrXilBQRMZGCToY 0H3+eS7DOIlLARRqiAnq/IU96fQCGVXrLZHm8uX+AkB8COh64modcgoOLHBdU+hQegBG/jWcusN 0ULazZ4gjdLcn6R/ha40DkjO85EZ3UC/BEQLXC14c2I+abwLjk4LCkNvvls8mtGTNL/zYvzhZOW WuATet9pVKds+3ATLQZhPX22dJo8i8MQVxUnTBDVrw== X-Google-Smtp-Source: AGHT+IHlVDoLLN+L2gJp3wbagUJcT5yEg2+t7LybVtpznFOf8VXDnXYsgSPRwIYaml1Cy16RF0TexQ== X-Received: by 2002:a17:902:f70d:b0:224:2175:b0cd with SMTP id d9443c01a7336-22c535a9bbemr150380835ad.26.1745220217965; Mon, 21 Apr 2025 00:23:37 -0700 (PDT) Received: from localhost ([122.172.83.32]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-22c50eb4292sm59122615ad.150.2025.04.21.00.23.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Apr 2025 00:23:37 -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 14/15] rust: opp: Extend OPP abstractions with cpufreq support Date: Mon, 21 Apr 2025 12:52:21 +0530 Message-Id: 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" Extend the OPP abstractions to include support for interacting with the cpufreq core, including the ability to retrieve frequency tables from OPP table. Signed-off-by: Viresh Kumar --- rust/kernel/opp.rs | 70 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/rust/kernel/opp.rs b/rust/kernel/opp.rs index 8b61ea8748a3..985d90dc0671 100644 --- a/rust/kernel/opp.rs +++ b/rust/kernel/opp.rs @@ -19,6 +19,69 @@ types::{ARef, AlwaysRefCounted, Opaque}, }; =20 +#[cfg(CONFIG_CPU_FREQ)] +/// Frequency table implementation. +mod freq { + use super::*; + use crate::cpufreq; + use core::ops::Deref; + + /// OPP frequency table. + /// + /// A [`cpufreq::Table`] created from [`Table`]. + pub struct FreqTable { + dev: ARef, + ptr: *mut bindings::cpufreq_frequency_table, + } + + impl FreqTable { + /// Creates a new instance of [`FreqTable`] from [`Table`]. + pub(crate) fn new(table: &Table) -> Result { + let mut ptr: *mut bindings::cpufreq_frequency_table =3D ptr::n= ull_mut(); + + // SAFETY: The requirements are satisfied by the existence of = [`Device`] and its safety + // requirements. + to_result(unsafe { + bindings::dev_pm_opp_init_cpufreq_table(table.dev.as_raw()= , &mut ptr) + })?; + + Ok(Self { + dev: table.dev.clone(), + ptr, + }) + } + + /// Returns a reference to the underlying [`cpufreq::Table`]. + #[inline] + fn table(&self) -> &cpufreq::Table { + // SAFETY: The `ptr` is guaranteed by the C code to be valid. + unsafe { cpufreq::Table::from_raw(self.ptr) } + } + } + + impl Deref for FreqTable { + type Target =3D cpufreq::Table; + + #[inline] + fn deref(&self) -> &Self::Target { + self.table() + } + } + + impl Drop for FreqTable { + fn drop(&mut self) { + // SAFETY: The pointer was created via `dev_pm_opp_init_cpufre= q_table`, and is only + // freed here. + unsafe { + bindings::dev_pm_opp_free_cpufreq_table(self.dev.as_raw(),= &mut self.as_raw()) + }; + } + } +} + +#[cfg(CONFIG_CPU_FREQ)] +pub use freq::FreqTable; + use core::{marker::PhantomData, ptr}; =20 use macros::vtable; @@ -751,6 +814,13 @@ pub fn adjust_voltage( }) } =20 + /// Creates [`FreqTable`] from [`Table`]. + #[cfg(CONFIG_CPU_FREQ)] + #[inline] + pub fn cpufreq_table(&mut self) -> Result { + FreqTable::new(self) + } + /// Configures device with [`OPP`] matching the frequency value. #[inline] pub fn set_rate(&self, freq: Hertz) -> Result<()> { --=20 2.31.1.272.g89b43f80a514 From nobody Sun Dec 14 19:11:21 2025 Received: from mail-pf1-f179.google.com (mail-pf1-f179.google.com [209.85.210.179]) (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 0BF6025D1FA for ; Mon, 21 Apr 2025 07:23:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.179 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745220225; cv=none; b=YKaT6a8Jf0868+8630pvSUim8SojkUyp5jRM2XC40kQmV53gtij2ikJbs3DJQ1ATRMD0UbocLqFXVpvpYfWHPVruKELeDgJR6+DCqgrouN1qHJFLoTiGO7t9ZkukBg89sfGl2ErixO6uIajsFuwcrWdIIKkBmQW8dnXhcRqK6Dk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1745220225; c=relaxed/simple; bh=ZIS3z7iPsFidXafxV93b2Yi3FI3yu6TnEKFMXRSjxlU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=b/tF/iZ2zRyRm/B2PDO8/Mh5Bx24TdxDkN9DhElLpYUm8o12C6DPA6g8YrSCJi8iSnsELqWSununxhtvuqhD4uom8+mK03aKdtZeJhqlSvI0iyOEp0wCXzbtB5JAwyS1jfV4eg8wvx2MpYZJdE5wHYrpKAXiHN/9rr99OFRs3Bw= 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=L9g3SjPQ; arc=none smtp.client-ip=209.85.210.179 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="L9g3SjPQ" Received: by mail-pf1-f179.google.com with SMTP id d2e1a72fcca58-7376dd56eccso3784118b3a.0 for ; Mon, 21 Apr 2025 00:23:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1745220222; x=1745825022; 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=c+dWCxW0qa//qOeop3seiJ+PvmpaQk8MdxFASKGEdIg=; b=L9g3SjPQUkY2Ahb7iB6jLTk0tcBGsMIDJuATanEfqotDmN56kcDMkZPxxqejq0kVKu zcoZAl5fbZ6wvId0ae2hYH3Pj6UQY4F7d/+LUOMN1LkWXIeg/f3ytytCg/ZymGUiABGA DoJlI5erqlYCM8c2B3TYx806h23pa+T91LVmvuH83Ux9y3qQEo0cmHUgYisE5qCuZ8r8 a3U/MiapCJdBTYsZJQ1MU9n5a3PXb5lxLrp7ZXq2kYbgB1RmwY7inBTJk+M+iHc4tmO3 Xlk/fBE2tr3KT3mHZ+xNDbDcZqvBxfe5QJ6haEjU4MVGc4yVd6UcUHXgYc/sJC7jkeTy 9Kqg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1745220222; x=1745825022; 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=c+dWCxW0qa//qOeop3seiJ+PvmpaQk8MdxFASKGEdIg=; b=kdOLjnsjOaEIXmE2XzOrBmfr+KNWpiuxtRBHdq14IO/hxjngpFTRFfV4E9ZYVIMs9h CEtAhLLupZQcUuzrxufl+iSXxk0QAJ8j0CIbJ2dzrqVwrjYUMpOmoz0nKC/N60O/ApbY L6F6VCCkXzy4yZzBZ9Qv4K+bU19liZKwHxvi/EHhvsJZa3bL4M+DYvh0+SZHRWbUmmLM DpkGUPNtKbIYhrrsEZzYXrTgdur0lI+24+lVZfxThpWbBIGoTQzQjP/lF5ZWw2NK43Zc /6D81DUmcoaqXTUsPI+nvc0eBAz7auKDxpZ+XDlNB/axT4ZROhO2xpceV+d/tNEfFcp/ hh6g== X-Forwarded-Encrypted: i=1; AJvYcCWtZFvkatcmNzhBg0G2HQJ4TRVqSVW86LxtRrmJPnlaQEUs2hVKYANhEIXn6Dvc0oWdDQfXYCG5qZ+VdO4=@vger.kernel.org X-Gm-Message-State: AOJu0YxX6XnHgtl+ZPe6VIhqDVmrJ7i3LSh+9TS2I5PDzmnlvXs5wIYt pq9cJS1WOn9zppNEYvy3Fvlj+f27bkAxVxRYGXL4O+t8q+6RM1P66dTqi4FOa9U= X-Gm-Gg: ASbGncuacAL23G4NPjSnw3pe5vI10NRk/N2L8u+uBqQkUlPD5/YFnCNc+ET3cmlO9WT g+pznfZS2BrFT0xcpQ6d9WaAhwCtEiAmBkhlar5QM8CnXeesl5VoiN/0Z73VB71YfKtnHPeCcBL iutHjkQnikwUY5puJjKKVSXOcaf+39q97oOdrSY0bes+HPUV2tN5nOYsh27/s1dS5jTUMkHQlBn nspPMUP5A6llbph/pUilMad0OIwSbIogI8mLkMcB5f51BL6bl78VlbSYuRDrOc7RbwxJ/VYYxXy Kd+fPLDxmSJKpN/N9+JwKuZIW/N1mb+PKM/wAWUUoQ== X-Google-Smtp-Source: AGHT+IE3ihNLzBum2t3EhVaSfOP4Ld6WJ65y89kj52Y27XwztxFnklLO3F7HnSJou6+pglNZWyyDOw== X-Received: by 2002:a05:6a00:3cc2:b0:736:5b85:a911 with SMTP id d2e1a72fcca58-73dc149bf14mr16837159b3a.8.1745220222266; Mon, 21 Apr 2025 00:23:42 -0700 (PDT) Received: from localhost ([122.172.83.32]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-73dbfaeb282sm6081227b3a.176.2025.04.21.00.23.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Apr 2025 00:23:41 -0700 (PDT) From: Viresh Kumar To: "Rafael J. Wysocki" , Miguel Ojeda , Danilo Krummrich , 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 , Danilo Krummrich Cc: linux-pm@vger.kernel.org, Vincent Guittot , Stephen Boyd , Nishanth Menon , 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 15/15] cpufreq: Add Rust-based cpufreq-dt driver Date: Mon, 21 Apr 2025 12:52:22 +0530 Message-Id: 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 a Rust-based implementation of the cpufreq-dt driver, covering most of the functionality provided by the existing C version. Some features, such as retrieving platform data from `cpufreq-dt-platdev.c`, are still pending. The driver has been tested with QEMU, and frequency scaling works as expected. Signed-off-by: Viresh Kumar --- drivers/cpufreq/Kconfig | 12 ++ drivers/cpufreq/Makefile | 1 + drivers/cpufreq/rcpufreq_dt.rs | 229 +++++++++++++++++++++++++++++++++ 3 files changed, 242 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 22ab45209f9b..d38526b8e063 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..4bc74d6e361d --- /dev/null +++ b/drivers/cpufreq/rcpufreq_dt.rs @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Rust based implementation of the cpufreq-dt driver. + +use kernel::{ + c_str, + clk::Clk, + cpu, cpufreq, + cpumask::CpumaskVar, + device::{Core, 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 prop_name =3D CString::try_from_fmt(fmt!("{}-supply", name)).ok()?; + dev.property_present(&prop_name) + .then(|| CString::try_from_fmt(fmt!("{name}")).ok()) + .flatten() +} + +/// Finds supply name for the CPU from DT. +fn find_supply_names(dev: &Device, cpu: u32) -> Option> { + // Try "cpu0" for older DTs, fallback to "cpu". + let name =3D (cpu =3D=3D 0) + .then(|| find_supply_name_exact(dev, "cpu0")) + .flatten() + .or_else(|| 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, + _mask: CpumaskVar, + _token: Option, + _clk: Clk, +} + +#[derive(Default)] +struct CPUFreqDTDriver; + +#[vtable] +impl opp::ConfigOps for CPUFreqDTDriver {} + +#[vtable] +impl cpufreq::Driver for CPUFreqDTDriver { + const NAME: &CStr =3D c_str!("cpufreq-dt"); + const FLAGS: u16 =3D cpufreq::flags::NEED_INITIAL_FREQ_CHECK | cpufreq= ::flags::IS_COOLING_DEV; + const BOOST_ENABLED: bool =3D true; + + type PData =3D Arc; + + fn init(policy: &mut cpufreq::Policy) -> Result { + let cpu =3D policy.cpu(); + // SAFETY: The CPU device is only used during init; it won't get h= ot-unplugged. The cpufreq + // core registers with CPU notifiers and the cpufreq core/driver = won't use the CPU device, + // once the CPU is hot-unplugged. + let dev =3D unsafe { cpu::from_cpu(cpu)? }; + let mut mask =3D CpumaskVar::new_zero(GFP_KERNEL)?; + + mask.set(cpu); + + let token =3D find_supply_names(dev, cpu) + .map(|names| { + opp::Config::::new() + .set_regulator_names(names)? + .set(dev) + }) + .transpose()?; + + // 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) if e =3D=3D ENOENT =3D> { + // "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() + } + Err(e) =3D> return Err(e), + }; + + // 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.setall(); + opp_table.set_sharing_cpus(&mut mask)?; + } + + let mut transition_latency =3D opp_table.max_transition_latency_ns= () as u32; + if transition_latency =3D=3D 0 { + transition_latency =3D cpufreq::ETERNAL_LATENCY_NS; + } + + policy + .set_dvfs_possible_from_any_cpu(true) + .set_suspend_freq(opp_table.suspend_freq()) + .set_transition_latency_ns(transition_latency); + + let freq_table =3D opp_table.cpufreq_table()?; + // SAFETY: The `freq_table` is not dropped while it is getting use= d by the C code. + unsafe { policy.set_freq_table(&freq_table) }; + + // SAFETY: The returned `clk` is not dropped while it is getting u= sed by the C code. + let clk =3D unsafe { policy.set_clk(dev, None)? }; + + mask.copy(policy.cpus()); + + Ok(Arc::new( + CPUFreqDTDevice { + opp_table, + freq_table, + _mask: mask, + _token: token, + _clk: 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: cpufreq::TableInd= ex) -> Result<()> { + let Some(data) =3D policy.data::() else { + return Err(ENOENT); + }; + + let freq =3D data.freq_table.freq(index)?; + data.opp_table.set_rate(freq) + } + + 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: &platform::Device, + _id_info: Option<&Self::IdInfo>, + ) -> Result>> { + cpufreq::Registration::::new_foreign_owned(pdev.a= s_ref())?; + + let drvdata =3D KBox::new(Self {}, 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