From patchwork Wed Apr 16 06:39:18 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 881780 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 D880522539E for ; Wed, 16 Apr 2025 06:39:55 +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=1744785597; cv=none; b=QL55x5Zc5s++ZDwmrVVHQSrBCsQl+AxyRQSVgZsS8SC/+8ckrrTOp9lj0Yd8Jgc5Um+4XTXCPU0UDt845jvzTs/yL2Z+hCLlCTMOiNz9/cnzlVbqibyIBc0j6vaVgG/Lwb5v68omKYSq67AXPTuKn8PWJSrl8CEroX9mbo43cTc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744785597; c=relaxed/simple; bh=oQ/uumB9dZEnQv7fbXO+2/DavkdF+8BaxTejDAZiJqM=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=aVR3GtKUqXk/zuU/7X8K+7SyKGryCMKKoUsd4wZscT0xkgXCgxJJfOVj4XHCwHzDtO6eS1ODhaHhO1vL6gIv8hOJXTKpN4IwhKdcjeNnNFwxa0Jvdp4+JRb08Dbm1g9TYMHuOaB6gTJyzMXFYFNMiUaMLQ+B3X+Xn98W6Nrbdog= 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=ib2FhzMV; 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="ib2FhzMV" Received: by mail-pf1-f179.google.com with SMTP id d2e1a72fcca58-7398d65476eso307821b3a.1 for ; Tue, 15 Apr 2025 23:39:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1744785595; x=1745390395; 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=ib2FhzMVdjhE8Ima3d9pvbNLIsGNzlzhF2/5JbBYFtPhiJu6X34TUzC5OyrjOXxYNl XjZAvQd8notnbNpecYZ337tWqvrmsZ0Qrl5gmXYq+ZfhO3Wxb/FkbDypFqc78hugsM7J wVkSntcD58FAONVG0HOQz0t+RSkB5pzy9b7b1wUT8Wpntee2pa9e/VejZ0RLUHaJAlRJ ffzGRo3dgTWW6suM29zduUHYDfbSWgcoIRu8q8uGwXmCHLgvEyJdh9ro86MpMN59Yg9P c2yKs3Su0sxTxwGnChcajqLNejHprJQzCSBG7SIRlEMRWDcEgzuoNjgJ/DZLlbg4BAf3 /rHg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1744785595; x=1745390395; 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=RyQU+VOjCj9SxwMctWEb7r7hQlNBEhepRIORj6HbU7T68GDwb0eZXPC1RX/JtScGCR zuw8Zoe/ld1+YAtG4pvA8pCkQ8F0cMCxFXEHmd6z5B1Wzeopf2QmWO4nZdP/4Ue6eg5p 0IM+j2BmKNDeIRkSZu3bJuE+sY+X91Yv2RF/tKtMbmowO0ftbLEJYPAZYngWYIpwIadZ fhRazAP30hCYmOS8hMxop5zYiHCly37Y4HByI0yjnqzuFSVsiE2cFKTUkGCuEPdhyF+5 s3nGJsxt5l2PU5mddqmN0xxxWpGb19EtOlpdciIIi5JHnjKzegexptbI7VRJN8zveALa XVuQ== X-Forwarded-Encrypted: i=1; AJvYcCWawTIMLS3XXAlANdPgF+P4dFbPj3x3A1iD5Ivgji/IEgvbnB3kiC+GJfD3MiAGjt2APeTVmmSqeA==@vger.kernel.org X-Gm-Message-State: AOJu0YwerB4seUTE0TVM2i7GztGuqZZTXA1mVVNbqvbf0ijaAPIWwaKb Lw1Tv6Rje7PWXc8bAGspoZtpp/kAkPmPvRrnWmogCm7lOT3cYPUb05wIVe7GuCA= X-Gm-Gg: ASbGncu3kxVw7XMLCTTwmDhMPgAZUx6V7FzLMxQK5xX/UAZ2xiDwdsyHAhLPqr0QlwS WwkTqmcpDwkq1oQ4pUfVkMSNRzyAQdY8ZqC68XxZ4lwKxVa1opY1fU7P2y30eatTwowm/8k7T3u Zs1+n/4E9q8jWCtp1bJYx2SqtrsDfZVU+wyBG8WDzAUoUSGPgAOrmc4t/AmQUtkxC9qvB2LFz8F Fr8tqT7mWoX6JHp/wpt8YYfJ46Bn14lmXejF3gEDye4+yxgAvPtLit+hBzv8n1MRZXa/qjS15F3 ZIe7N65DLuRYNFCI056mpqX1rbhwINdL02n8wjSPEA== X-Google-Smtp-Source: AGHT+IFdD6luOUWiu9XjQ7/e9KFVr+7wVcpXj2xETy5cd4GCfwB2LGWz+opg97LDR1wqgUuvWsquiA== X-Received: by 2002:a05:6a00:4098:b0:736:b400:b58f with SMTP id d2e1a72fcca58-73c26b73c05mr1127405b3a.0.1744785595123; Tue, 15 Apr 2025 23:39:55 -0700 (PDT) Received: from localhost ([122.172.83.32]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-73bd22f8850sm9582141b3a.87.2025.04.15.23.39.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 15 Apr 2025 23:39:54 -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?B?= =?utf-8?q?j=C3=B6rn_Roy_Baron?= , 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_Benn?= =?utf-8?q?=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 V10 01/15] rust: cpumask: Add few more helpers Date: Wed, 16 Apr 2025 12:09:18 +0530 Message-Id: <944ee57ad1b190f6975c739802b4d93166bd00c5.1744783509.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-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Add few more cpumask helpers that are required by the Rust abstraction. Signed-off-by: Viresh Kumar --- 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); } +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); } +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); } +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); From patchwork Wed Apr 16 06:39:20 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 881779 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 40DA022D7A5 for ; Wed, 16 Apr 2025 06:40:02 +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=1744785604; cv=none; b=TyWmPM6M1kzt70AfyIgvwunrq2Rol4iHozk3o478iHhNxWrPHjCwn4k9aSC/3MskvNCFCuAk8JhCC9XKVgfxkDPo4Ia5JGNZJw5MWu2CfguGqVYHc6ITJbhhvqg0R1iQ5pS8zJufLP81uH+Rw5SlNVKO5sKAXfx36ctTx+/14nw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744785604; c=relaxed/simple; bh=pRm5rikPg836Cpaht6T2Bti6G/gsdEEiL/oreW2ehWs=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=NZzR4kpG3woCyhCkIRPcKBdqWh4m40oO9aUqfCoBE3qeeDd6clJ7aOJZA4vGUy6VG37whSZgLlfmZ3zH2W/k5Veg/35m/h9iPvvf+THc2dBuehOE6nt6TtTtaDSuYMQ4iKiSUFf5D21VHS5XN1Krnc1zKxfi3qGYH+pxk/X82/4= 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=JhrTT8/m; 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="JhrTT8/m" Received: by mail-pl1-f172.google.com with SMTP id d9443c01a7336-2240b4de12bso87195675ad.2 for ; Tue, 15 Apr 2025 23:40:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1744785602; x=1745390402; 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=JhrTT8/m6kO4m1EAju7eGEsIHuUpRJFWF1FdOvfrMlmY8vdAC0MKW92hpOqLqIeD3u C91iwFkVxT9dlwB7IPoQiEch9vxGD9JZwTpKADUy542/ZXjDrR80cTQ+QtYbjH4Cvi3k ZT5pqaH/w+rTipU32vW7bx5E5gP1WqRpDNi1+7KTzkCo5OjtcsQYsD09bBal5lgmVyIV RNGSuguuqi/ASd1VOp/wdEzjEC4/eENq0TaSJjXJUmIm59x+6VS7c7CMVTqJE1Zzalbs xHoZkCoyF6S01/ZP4kCq+S+vBViL2B6fngMgXJOBTcqF4UclE9+fnanYGihsQazr3qeG q9HA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1744785602; x=1745390402; 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=mqktS+DiW0gW6ye/PxuVGca5oTxKqT1V9+eZ+K4iI7SAcKGZSFxpvmvIvc6imEWH7r mn325NqjAg9ggYdx2mLbG7Vlk1RjHcczZMCrpkBEiyB3l2nja3vMArWBAbPgiyJpNt+l Fo24UCfBk48HewiXUzLUXmB4hTlZp6S6gchs1hCwdozmvRNhfaMbaGpH9LZF5wCJ/Ey2 l+EJz3SE8EXLhVyc3D0tUD3mXz7xXvp0+2NbX5HpSiTDb8/9tFuipA+SsvwRqu8AZGpI HnPK4TLtypqEIm3ALYTy/hN4t+g70A0YYJsS/JaIFxhzLn8KBdbi8pOPIXIy3XQ6O9Ny wy9Q== X-Forwarded-Encrypted: i=1; AJvYcCXS760YQEcEfOAYdcH6rI8+LOr9DeYOKWZwMLRmQ+nVRsFWEy0sNQjLeeOYYTsSOkiOjrbuWqV/yQ==@vger.kernel.org X-Gm-Message-State: AOJu0YzfhiveXNfMdfUmhy22RIaRMteyG+32i8cXdlQ23lmhkorFrgJp lITQbs3uJmfhCG0w4BOVM/0rkb7fDJDLfi1kldv4Uh3GSofShW12Byo3GueUZ90= X-Gm-Gg: ASbGncuFjb5bVBYs+b3UjFau7nPcmn+vxUIDRR6/V2/vPsWGSAPGjCQsfHvKK5cr1Z5 uC52BjiA+aBPpm84xxIBVh1uaHolcc2Ur4hha8GGOh3L+bOfCjtrHuzH2sRPwfTRGk+9HwITL4/ tKxp6b8p4yzdI5zCRY/mZrbP187mddEE4jfuo/xqCR8IUtJpSHZaIgF4//qFz24VSIkrDS+Rlt6 lCWF6BRHysm+0IL/yDFYIuQIXu/h0BrL+OJkhkjYUUv+FYun78mhP2EmBWcipSApHmIohiVJM7F b8FUuEBu7o9Ezo4h55dhVIM+s05FQ2OA3vBdR6ExoA== X-Google-Smtp-Source: AGHT+IEZ0BjxMEufYyWXS1DlCdKaeRakmruSioO3vp2nVRyRnB2tMQN8Qlo8XDozRJzOoVI66wZ2Yw== X-Received: by 2002:a17:903:1aaf:b0:224:1781:a947 with SMTP id d9443c01a7336-22c358de2cdmr12563915ad.21.1744785602483; Tue, 15 Apr 2025 23:40:02 -0700 (PDT) Received: from localhost ([122.172.83.32]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-22c33f1d3cbsm6459055ad.97.2025.04.15.23.40.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 15 Apr 2025 23:40:02 -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_Roy_Baron?= , 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_Benn?= =?utf-8?q?=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 V10 03/15] MAINTAINERS: Add entry for Rust cpumask API Date: Wed, 16 Apr 2025 12:09:20 +0530 Message-Id: <9115c43e4eae9fd958344e7befc945e0e649a6c5.1744783509.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-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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 +CPUMASK API [RUST] +M: Viresh Kumar +R: Yury Norov +S: Maintained +F: rust/kernel/cpumask.rs + CRAMFS FILESYSTEM M: Nicolas Pitre S: Maintained From patchwork Wed Apr 16 06:39:22 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 881778 Received: from mail-pl1-f175.google.com (mail-pl1-f175.google.com [209.85.214.175]) (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 B20C122FDFF for ; Wed, 16 Apr 2025 06:40:11 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.175 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744785613; cv=none; b=lma8PXcSlO3Ne6NYndDOL6eVKxZWNmFuztkKg6g6HoLa2FxNcvhtjiANgz/JvSj1OCStZ37Y1X3aLStUymitJwfPyToWu9OBd/wzPoJFVwl2TM6+G33jTsQpf4B7V4tYOReZ0wpEogwrjwZFktDvNxTOdxacOpYuGuVYRBb2XbA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744785613; c=relaxed/simple; bh=OK9LP8IuB4dZ4tNPJ7BqHK7tgUFvTgGIoVzWhWAVPdY=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=qr+ZYK4mbUGJT1n1KEJ5iZHJpnLPIjQancSnp1VhLB8uPl8TCCPp0ZyyV1odSsJgYoXVhcuKZ7rGakz+sM8ShyabGy+/QLKCmq9TWpK+OZQEYglHMycNHCaVkDCdQ9xn8JpR9RltzV6KnjHJrl+GwLFdKPyN7XBlhlHieRJrqeI= 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=TULXhWD/; arc=none smtp.client-ip=209.85.214.175 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="TULXhWD/" Received: by mail-pl1-f175.google.com with SMTP id d9443c01a7336-224019ad9edso85114925ad.1 for ; Tue, 15 Apr 2025 23:40:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1744785611; x=1745390411; 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=TULXhWD/CRn2zolmJ1YU3NrM0E/0iVD12hRBEbyVoA7ynkS5eeJEdkJ26ZStL6Hh5q jQTOL0x+MCiACebhOHgmOW2Fz9RUOVJulpg6c2hcYd+ipTqNxI4UYYT9CFh87x9AAjDu RMxIeE4SUEwvPwT1AdTperQBnY85VqEz9e1wANDfBqDNeEzHgi7KU8yDGTYj25U1DL8l hIk3/l/NgreJ1UDcz1dLNHer44oHgehSMdHeym5l2XFvaicVsEfvB4UXF0oJr1Ltw+Jp pCDiKCD2UDAEyvUyBPLvkrn5JKPLxFvjt42VdnV0pbNfN+TSLQiXlGmjHI9eYrFm63Xi 7U7A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1744785611; x=1745390411; 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=gbzYLuELURogI5g9qBDoHxAOzQBS9pA2NIMojgAOgqo2bvZ6GwUkJQMomoLXbnzG4y PkP9hyXLpRLXRykkc4ynONt2ZFYvBeS9Bm3ud4rH9cERix/ZQW6MJiXD/YnheyXw8/de NoK900Dgr1KImC/9mAtMJ/pruEHw6e1Xpcavg6DTJfHIU3Lx6rwW+kSzuDj3FOtJ4VtX PqhQ8ou/8sJMo76ARv3FW60n7L2ipoBewdyTxHc6kSCBd64kZFPfJ9WltAFnTx+rxZV3 V8QmipeD1vl1XngAf0dmCc+TuxhYVSJSkCl3cw8b37iBxyKpXArtNmQNfsnZ+tewNtAE FFjw== X-Forwarded-Encrypted: i=1; AJvYcCWoh5fSCFftnVq7YqiiMIDu0Jq7qAwWtPTYWmlpfD102BhwDNJ4Jm/TvXzO7St9rEWWZy24T1ZZCg==@vger.kernel.org X-Gm-Message-State: AOJu0YzOBIZvVChvcwh+wz11m+nOBIoXNdebSPHIwgxbQgG8yfuFPMR7 rYP/v/ptRfkWejs0dGd3q2sTZOO4hjZhN/U/xGit4bxNHoWEmH/dca4a5TKuMtA= X-Gm-Gg: ASbGncs+NB111t6XrGf95Qmt8232rWG2xQIufRCZx27gcHlWly5vAkyo1icLvvMnqEu TYPfHApfMdz46GC746I79YAy8SqYvjvlu6mEWvN5czQgx07sHC7olqC+mE4pYghJOWajBfi7NCz PxD206uLywqeBWbAqUKjdkZf64Q6xIKzeKlAYSebVRqeXTt4JtglE32OmSmhkoumJXF7Qtsxac+ +wBU7vOCC11bNf1mt7vdmEVdS3fYNkI3Ax3hlAtz123TMVu81lsgwYgmLweM+Wbn+Fhvg58MZyA igHw4yRorZdIRIr5yt0WTGdqq1a1igNPLImgKfmPHg== X-Google-Smtp-Source: AGHT+IEWuhQLaBTU4M/wgFAv18zSV246NrzKwik2ssZOtfWRe58J3TX0f/3yOuGQ17po2sEwvCNH9g== X-Received: by 2002:a17:903:238a:b0:216:644f:bc0e with SMTP id d9443c01a7336-22c3590dcadmr13395765ad.24.1744785610853; Tue, 15 Apr 2025 23:40:10 -0700 (PDT) Received: from localhost ([122.172.83.32]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-22c33fe8de8sm6377705ad.248.2025.04.15.23.40.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 15 Apr 2025 23:40:10 -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?B?= =?utf-8?q?j=C3=B6rn_Roy_Baron?= , 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_Benn?= =?utf-8?q?=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 V10 05/15] rust: clk: Add initial abstractions Date: Wed, 16 Apr 2025 12:09:22 +0530 Message-Id: <4207f5bf540de148e1bd31a88dc75eaac1aaf897.1744783509.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-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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 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 = 1_000_000_000; +/// let rate = 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`] created by the C portion of +/// the kernel or a NULL pointer. +/// +/// Instances of this type are reference-counted. Calling [`Clk::get`] ensures 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 = Clk::get(dev, Some(c_str!("apb_clk")))?; +/// +/// clk.prepare_enable()?; +/// +/// let expected_rate = Hertz::from_ghz(1); +/// +/// if clk.rate() != 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.clk_get + pub fn get(dev: &Device, name: Option<&CStr>) -> Result { + let con_id = if let Some(name) = name { + name.as_ptr() + } else { + ptr::null() + }; + + // SAFETY: It is safe to call [`clk_get`] for a valid device pointer. + // + // 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 argument 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 argument 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 argument 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.html#c.clk_unprepare + #[inline] + pub fn unprepare(&self) { + // SAFETY: By the type invariants, self.as_raw() is a valid argument 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 argument 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::unprepare`]. + #[inline] + pub fn disable_unprepare(&self) { + // SAFETY: By the type invariants, self.as_raw() is a valid argument 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 argument 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 argument for [`clk_set_rate`]. + to_result(unsafe { bindings::clk_set_rate(self.as_raw(), rate.as_hz()) }) + } +} + +impl Drop for Clk { + fn drop(&mut self) { + // SAFETY: By the type invariants, self.as_raw() is a valid argument for [`clk_put`]. + unsafe { bindings::clk_put(self.as_raw()) }; + } +} + +/// A reference-counted optional clock. +/// +/// A lightweight wrapper around an optional [`Clk`]. An [`OptionalClk`] represents a [`Clk`] that +/// a driver can function without but may improve performance or enable additional 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::get`] ensures that the +/// allocation remains valid for the lifetime of the [`OptionalClk`]. +/// +/// ## Examples +/// +/// The following example demonstrates how to obtain and configure an optional 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 = OptionalClk::get(dev, Some(c_str!("apb_clk")))?; +/// +/// clk.prepare_enable()?; +/// +/// let expected_rate = Hertz::from_ghz(1); +/// +/// if clk.rate() != 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 connection 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 = if let Some(name) = name { + name.as_ptr() + } else { + ptr::null() + }; + + // SAFETY: It is safe to call [`clk_get_optional`] for a valid device pointer. + // + // INVARIANT: The reference-count is decremented when [`OptionalClk`] 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 = 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; From patchwork Wed Apr 16 06:39:24 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 881777 Received: from mail-pf1-f169.google.com (mail-pf1-f169.google.com [209.85.210.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 430F1230BD9 for ; Wed, 16 Apr 2025 06:40:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.169 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744785621; cv=none; b=V6WWD6oBPMzLsj04x5EuO3lydmjFnvRSfLkBAEw+WUgSozHWnFKI35zBpg8oEIVDCXis0aWMAOVD6NB9+EJIFjx3DhlcwokY+8TuxloUq1/ioWn1xGVN849mGYRxZTK51dgp3htQQTYZTJ++qvjHhia3vuAWc3w+ejaBAT4Nuak= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744785621; c=relaxed/simple; bh=FXN7MqRrTtWb3HqWkB4bVWXQmRagu7xa10NZ2Fw72s8=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=eQn5RGbZP+o9m+gVQBU24wJt/f4O6/AtLDyDPtQBxb5hYZq631CeajXRVdtWQUBtFwpkpyAX8ki59g2aGKSXy0kKX4SmsKPl7LF2mxDPNvOTHUiZGU+i8P8B9rT7stK4hcX8uCKfiNZtYYmhDGnsnxsLTAXf+3mXSV0RLncxm8M= 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=Xidwe0V7; arc=none smtp.client-ip=209.85.210.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="Xidwe0V7" Received: by mail-pf1-f169.google.com with SMTP id d2e1a72fcca58-736b0c68092so5487913b3a.0 for ; Tue, 15 Apr 2025 23:40:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1744785618; x=1745390418; 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=Xidwe0V7wfxWDgHsAStGUguKV3Skl485YhNSuWeLrDUvT2Mbu7kFP7XXwkWmc+Zk2I ekQNNSfdFw63oaDlO4RKTkEpIXB9ahHvFulnWhSYE+IioINb/X834cLcQfuPeXGUyV5J jBwHurnbItPYW9B42bsbmiZoE4BpvpvYtK0vzCVLJTnizVBuMleOXUwkh77Tn0pslFg7 N03mjesuZLvqTUNqH5LOCf3Euywf3HQ6ifpmHjCQVjO9PTyQTmKmve4J05XCd/XE6lWy MI9IZ/qzK/wBW1GZDjiO2kerJF5cUc61EDne5KqhncI7Sa0yysHE+Tghy3DER15q1HlX ZCjQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1744785618; x=1745390418; 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=w48CuCkPtEOsvZzHoD1ir+WYSMqhs0KSnYKH79MRsMiOoTJsx3l2PUUXP4FiFMFOTb 7t7PZwfaKzPq5txcSznZvX6aQxucoXauQsbLMFTOJ5FglxqgZOGMkW4dg/sEpfoh81p4 bIOfTh+BEXFqA6Rgm6CaTo6KLvJZerjKOadl2tAF+qO6c+ooUDcjJi1UAFLHeWFXeEut 5wmv3lSObDC/hjRw/RSDUIAg8dDyViUAu2eQPzAVaSiIHUDDsbevpesfsKrawxh/UKQs j80/L7dBifd1AXBodQmDkO2Qxdynr6CFJTcSkIx3sW8xjJM57DtqysHOdkJFmP7p98m/ pgGA== X-Forwarded-Encrypted: i=1; AJvYcCXHM+MzNmixcwfQ19hUNBOYhVERKFu+J6rUkIagUjVcDEaoqrZOyZULyhddYYmccpakYCpuSa9jog==@vger.kernel.org X-Gm-Message-State: AOJu0YxePJsPEBJMeBgbq6QyssBcvN5TJXbp/PH8Tg0gfJr/MVvddh2v eImfDrblta5qLVE4GVJ5XxPnMUbCEvAh0NIqwrK4dVJhe1zGLyMNIINe6Sbh094= X-Gm-Gg: ASbGncvS+gWEuMJlQAt87HqlUOdLaEbT5kBwyzPxKmoJpqtlNbFw166D2OhshGPARqK 6h4s6ikMSk5dz+VNEoHON7R1I+/EP2m/Ci4vDMrAPi3TA51syK19bgHq+YSIAXT24sAxu0IjKQU K37NaDVkitdVmdFlqBV3o6a6cL0SwrYtaQ8DTzw/O0zqRy4hER5PSyOrkI3tr4sMtWrGeYPBDL7 iMZlDU29xWoa/ZZ3TirVgsyNrVZMu7WM92X6SDEkDjCLNhNfy1C7QZgrKkl7RmyHn2P0sFp0NQL CymY1MJrkABd+SsFC2BNyOyR1qobIA2AXH6+nxyA3A== X-Google-Smtp-Source: AGHT+IF0ex/iD+sUrbitNrvOanXTZ6pYvDsy9aAY8JPu8xZYMYJcxxzu0L7Lfm7GAzJbTIuYn066Rw== X-Received: by 2002:a05:6a20:e68f:b0:1ee:c8e7:203c with SMTP id adf61e73a8af0-203b3eea03dmr933373637.24.1744785618538; Tue, 15 Apr 2025 23:40:18 -0700 (PDT) Received: from localhost ([122.172.83.32]) by smtp.gmail.com with ESMTPSA id 41be03b00d2f7-b0b2221bb77sm582080a12.71.2025.04.15.23.40.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 15 Apr 2025 23:40:18 -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_Roy_Baron?= , 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_Benn?= =?utf-8?q?=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 V10 07/15] rust: cpu: Add from_cpu() Date: Wed, 16 Apr 2025 12:09:24 +0530 Message-Id: X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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 CPU IDLE TIME MANAGEMENT FRAMEWORK M: "Rafael J. Wysocki" diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.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 associated 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 notifier 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 = 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; From patchwork Wed Apr 16 06:39:26 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 881776 Received: from mail-pl1-f175.google.com (mail-pl1-f175.google.com [209.85.214.175]) (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 8FCF82356CD for ; Wed, 16 Apr 2025 06:40:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.175 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744785629; cv=none; b=LwlZhBdZM5x8x91fkgupA/8TcVLm5S4THzMUDSCqK8ryU2jEz10iQh1Msq9WNqc1PLeqiZRgzSAnK31mdGD/OAB+hkXuPE9uI757aHHT/Jx6qgRtiyySOwyfdf/MFOS8PN9WrXuGInZwmCP0MJawlo72JkRQPFBx97aX+ezIyM8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744785629; c=relaxed/simple; bh=wX73VZYHmPeMIrJ9k0YbY3LA3cWnBUH+KwsxFLck6gk=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=LmYhvZsh2j2ubc8WPZoL7/2aGOG3pHeP7RBZZl30OvBGKJAr0COtlITcH+bvxKppINpMjVqz/Z8wbGCP97Cw327uCPq9ns/7TgBWWjsVlt8x7MboS3tmtun00H7VuC3sDU2F8jxfSY4VG8YcAnCsqgOV3bA9skmYYmTDvYJE/Go= 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=NE+58O49; arc=none smtp.client-ip=209.85.214.175 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="NE+58O49" Received: by mail-pl1-f175.google.com with SMTP id d9443c01a7336-227d6b530d8so63200445ad.3 for ; Tue, 15 Apr 2025 23:40:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1744785627; x=1745390427; 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=+S2sEL5meSUB9+YQljONr3D0FsAJqb7ubVG6yrKMgHo=; b=NE+58O49FuwsIWF5lV5vdMcOQ2ApVd2VqJacnc3GijIcifvXEpPcKi8kuIVRUvJnD9 0YjpIbglaQMbLqUewKhqIxSa3fHTwHXq9ETrB5ueJKlUObsZoCQsZ4ALfUgI9iZwKlG5 tp71AIkwrBhOuEocFdlnOd0NozrO8P11PfQ88KtXj706yQormBQ6ZMJBabGwAIk86SRw UCK7bYs8yAvJtcooDgAXBK0gSuX4ZzjS6zZW4kr0C7yFTE1FhCVqkT7RgnLaEkUmve+D zQyW+Ici4KOrmGuvR0lJGV2kqoGyt7h+o1Ed9ashsLJ9jB0JLm2u35EsfAtq1XAQ1frY JWAg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1744785627; x=1745390427; 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=+S2sEL5meSUB9+YQljONr3D0FsAJqb7ubVG6yrKMgHo=; b=E2Dp9R1zEkT8zp09gd7jWpHUEQvxRD8wwYlQk6OTI2Xqgncd9j6ZHjeMZI4wMWa6bl nVRgbu/31vpEKXVN9dsEyO++wGqJy5NIdW2CdE9tYIVgu2mka86DMnCKFjtIIFid34zl b4iM7eUR4CYflmZmjzxnRkQ9bIFL0JFsL5fnblxTVcCs3sPCKGPyxoHNOCpMDoaYULQH hK0d3INEByiis+uQxKTBEJ8wF6jmneQWZLKuVg+0mhn9DFQiWYoChZikzc3hh4JYvUWH OZxIa77RH6XEpZzH59xzB9Fa7wzt4zTFJMOz/7HijZpwbTn69HYiluNLuBk/vmvE5cVq dXBw== X-Forwarded-Encrypted: i=1; AJvYcCUUtbPVl70qdzpEosy54XdLJkksE72Lp+WqmZvmwE8J1Q8SOTCCfpHHza1cBteIBYskVVX40E4+Ug==@vger.kernel.org X-Gm-Message-State: AOJu0Ywmxz4cX5lGHN93tRBwMEwtlG7BNhwr4vhR3q4yacuMk4ZjooCq l54cLVjxxILCVvOOI6v4AsA86V6KGqNmhY96WppJq2EKT+edJvIKIF2DvYGXZNM= X-Gm-Gg: ASbGncv0IB2UaAg4LMTf2fWPEByLNcgfQpzbr909RG/c+jjE2d/JZtGoiX0RKRe7tWP 5QMc7NxoE0fRuswvC5EdJuKuU3El0O41J/Yc5kaEx33DRWR7Vx8KKpRo2Y5mdt8EfzmUldIGgH3 j+Ei5i6IRuGheouKQmKgkL0bigqOq0j8MamGBnM29tPVZZZvM6CoHpjOm+bWDIBmL4kQ+I+ubuJ 0d9fFVV6h4h1MNczrYA2f3ZFbZ71L5b81WBe1cjc7kR1fR5BvLtsVQilkQXcKtpfDTJOw4iSMJz L81Qe/qP9Xb+EWs0YJqiHPiR8rBqx+ndcnH1hEIoEV9G07hOG6Bb X-Google-Smtp-Source: AGHT+IEpDWc7qWL9wYy3dPe93hrwkiv4ulv+62ZSCaFTby+C1vglRJZGtIhXDSWvfozhxZ4xpY80TQ== X-Received: by 2002:a17:903:2a85:b0:221:7eae:163b with SMTP id d9443c01a7336-22c359a2402mr12147645ad.46.1744785626802; Tue, 15 Apr 2025 23:40:26 -0700 (PDT) Received: from localhost ([122.172.83.32]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-22c33f1d118sm6444095ad.86.2025.04.15.23.40.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 15 Apr 2025 23:40:26 -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?B?= =?utf-8?q?j=C3=B6rn_Roy_Baron?= , 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_Benn?= =?utf-8?q?=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 V10 09/15] rust: opp: Add abstractions for the OPP table Date: Wed, 16 Apr 2025 12:09:26 +0530 Message-Id: <1fcad3f3da1e516293a7df65b3ffdd037a33831a.1744783509.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-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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 ec4f67c8aab4..389bcd5e5711 100644 --- a/rust/kernel/opp.rs +++ b/rust/kernel/opp.rs @@ -10,8 +10,9 @@ 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 { } } +/// [`OPP`] search options. +/// +/// ## Examples +/// +/// Defines how to search for an [`OPP`] in a [`Table`] relative to a frequency. +/// +/// ``` +/// 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 = 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 [`Cpumask`] 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) -> Result { +/// let mut opp_table = Table::from_of_cpumask(dev, mask)?; +/// +/// if opp_table.opp_count()? == 0 { +/// return Err(EINVAL); +/// } +/// +/// pr_info!("Max transition latency is: {} ns\n", opp_table.max_transition_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 boundaries. +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 properly 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 refcount will be incremented. + // + // INVARIANT: The reference-count is decremented when [`Table`] goes 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 = from_err_ptr(unsafe { bindings::dev_pm_opp_get_opp_table(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.as_raw(), index) })?; + + // Get the newly created [`Table`]. + let mut table = Self::from_dev(dev)?; + table.of = 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, it 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 { + // SAFETY: The cpumask is valid and the returned pointer will be owned 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(cpumask.as_raw()) })?; + + // Fetch the newly created table. + let mut table = Self::from_dev(dev)?; + table.cpus = 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 [`from_of_cpumask`] earlier, + // it is safe to drop the same now. + unsafe { bindings::dev_pm_opp_of_cpumask_remove_table(cpumask.as_raw()) }; + } + + /// Returns the number of [`OPP`]s in the [`Table`]. + pub fn opp_count(&self) -> Result { + // SAFETY: The requirements are satisfied by the existence of [`Device`] and its safety + // requirements. + let ret = unsafe { bindings::dev_pm_opp_get_opp_count(self.dev.as_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 [`Device`] and its safety + // requirements. + unsafe { bindings::dev_pm_opp_get_max_clock_latency(self.dev.as_raw()) } + } + + /// 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 [`Device`] 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 [`Device`] 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 [`Device`] 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 [`Device`] and its safety + // requirements. + to_result(unsafe { bindings::dev_pm_opp_sync_regulators(self.dev.as_raw()) }) + } + + /// Gets sharing CPUs. + #[inline] + pub fn sharing_cpus(dev: &Device, cpumask: &mut Cpumask) -> Result<()> { + // SAFETY: The requirements are satisfied by the existence of [`Device`] and its safety + // requirements. + to_result(unsafe { bindings::dev_pm_opp_get_sharing_cpus(dev.as_raw(), 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 [`Device`] and its safety + // requirements. + to_result(unsafe { + bindings::dev_pm_opp_set_sharing_cpus(self.dev.as_raw(), cpumask.as_raw()) + })?; + + if let Some(mask) = 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 [`Device`] 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 [`Device`] 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 [`Device`] 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 [`Device`] 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 = self.dev.as_raw(); + let index = index.unwrap_or(0); + let mut rate = freq.into(); + + let ptr = from_err_ptr(match stype { + SearchType::Exact => { + if let Some(available) = available { + // 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. + 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 => 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 => 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) -> Result> { + let raw_dev = self.dev.as_raw(); + + let ptr = 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 => unsafe { bindings::dev_pm_opp_find_level_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 => 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 => 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 = self.dev.as_raw(); + + let ptr = from_err_ptr(match stype { + // The OPP core doesn't support this yet. + SearchType::Exact => 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 => 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 => 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 [`Device`] 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 [`Device`] and its safety + // requirements. + to_result(unsafe { bindings::dev_pm_opp_disable(self.dev.as_raw(), freq.into()) }) + } + + /// Registers with 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 [`Device`] and its safety + // requirements. + to_result(unsafe { + bindings::dev_pm_opp_of_register_em(self.dev.as_raw(), cpumask.as_raw()) + })?; + + self.em = true; + Ok(()) + } + + // Unregisters with Energy model. + #[cfg(all(CONFIG_OF, CONFIG_ENERGY_MODEL))] + #[inline] + fn of_unregister_em(&self) { + // SAFETY: The requirements are satisfied by the existence of [`Device`] and its safety + // requirements. We registered with the EM framework earlier, it is 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 reference, 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) = 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 code. +/// +/// ## 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 = table.opp_from_freq(freq, Some(true), None, SearchType::Exact)?; +/// +/// if opp.freq(None) != freq { +/// return Err(EINVAL); +/// } +/// +/// table.set_opp(&opp) +/// } +/// ``` #[repr(transparent)] pub struct OPP(Opaque); From patchwork Wed Apr 16 06:39:28 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 881775 Received: from mail-pf1-f171.google.com (mail-pf1-f171.google.com [209.85.210.171]) (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 D1E11239594 for ; Wed, 16 Apr 2025 06:40:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744785638; cv=none; b=r6et+A6+eh9rT/C+5OBN/7LnlLDbHXrJctvIjx66r/sxB3gqLiBaq7i19x0B8ikVxPoKqy1QOb4gsRj5MjRUCpYgfj7fRA5nPQpiUrhnRY12hanHKMoD/vkRraJObKIf76H0JS9Dcg36nbZA1/fRI6G6r6CQiEBMyO2V5kceRPo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744785638; c=relaxed/simple; bh=ziZgMQLsFplAbFdyhmmL3J7VdGL8acvZSZ5DzxK0wlU=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=EgzGIXywpxwzSMVtmQ1vRPCG8urg1Ng1lCUWa5hgy+HFDh7R52FYHNwJhYBJAOJt0QKDM3XTyzNWoVvxZbEXu2lsp4dDcJB0D6QFHBdxU9uPcoFY4oz/t4NI8KFipr1orfO/NT0psUsrx1dsuM9F3g6KZrzDe8rNYa1iuOm+NtI= 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=SWgHoD2X; arc=none smtp.client-ip=209.85.210.171 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="SWgHoD2X" Received: by mail-pf1-f171.google.com with SMTP id d2e1a72fcca58-73712952e1cso6139860b3a.1 for ; Tue, 15 Apr 2025 23:40:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1744785635; x=1745390435; 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=DW1qs6atmS8GbQWfQI5DosVxmJVfV2TCrrT1uWb7BkM=; b=SWgHoD2Xa6Z7f28gRvXoZJlbM1O1UfIcv73uSP9L/b6z3FapP1GK5N/NDaqU76qczY 9BDvSD80fEXH8OFCx224A9raVdF6XloMbysjLYMyfc76Oy4zjaQW6HdIZuBj74PNDauz AAmwcoSAXRO2B0Qf4ccEf9IJuzIoPpSSXKIyT+Lj1wRbSfLKgheakQ145o5i1nooVX8c KA13gaZJcnoSwlVnXg3RNM6r1MUcrpwHIk+pVRBtAl2+032rLsuIrvBhPeOzYIQU69sj Br85u5BLpDA33Q2OegaZB8DwE8BnxwzrhQtYbbijwnqH9OGQafwywdatpUTMq03i6R6+ bS4w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1744785635; x=1745390435; 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=DW1qs6atmS8GbQWfQI5DosVxmJVfV2TCrrT1uWb7BkM=; b=BRloMzhyTbXjIG6MeLd5DprpWbukr55AG8meykOmQeG/8E5jimcslyS0OmUc4ZQTM+ xjvkWch7OE0rnd2oolmd2zrzlhYyiK7PnvDeSrYsq/aZGROs/lUbEAGY3uPs+esl1ldh jV3SiczgWY0cQZJlGn9VuzXgWleOeZTZ3EGaWH24zYmOFka+Jn+D99c9v9+2/9VvsWNM C/Za0VYcUYaEq0LZTZEQdxoUkgmXtjRuKDdwYONAMz9g4b2ocir63m/0zFylHFC3iay9 WjjL+P9cpLKHcoVCzhD7Wc7NF2zkWoVwqaQi6ycDzX2vnuvZQ868U40u1wJYrqJUAjPP zGzg== X-Gm-Message-State: AOJu0YygAz/wte3wqVWlrwtzYzd9LKmwf+MXbq5mTycSt/1SjkzBP4Zs X/Cu5zGc0cNdk7UCOItl8PDV61c8d7YftEDtMzHac4tSeGGHkMOv5ksyxrB2IC4= X-Gm-Gg: ASbGncsCAidTtVoEA3X5fJPJEYcYddBQ7ypY3hJkFJUguC7J46pRYWhh0Janp8WSCWk rkzQLe3UDAV2GYiMPvXJf7CKkk3kx8iSE1kMO9BXvGvk9FG5QdnBstcc/iUML0/p0gKJgunlHLs 6PYhcZZLk6gchbNKBvyIrMcj1aE4Q/oVH978dEySgS914rN90wiIcyLpN1O8y3sfva86a3PEMv7 UKsXKtpVm2pP2i+NshALYpZ9CQwOoHBAJfGspwwPcfCGZ//Pd+aIMHfxfff1ITDRC30WjICm3Hz Ba+u5iJNmXT8lu6Tw938eNTDLMzU2BeFMV91VwvsWQ== X-Google-Smtp-Source: AGHT+IGivDluYSNRbYVAREJ0mxGQVA4d18wcvk2K0U1bpJcwJQn6cow74PXYi6k7LWWUakBsB3u0kA== X-Received: by 2002:a05:6a21:9183:b0:1f5:9098:e42e with SMTP id adf61e73a8af0-203b3e4fe98mr929777637.7.1744785635125; Tue, 15 Apr 2025 23:40:35 -0700 (PDT) Received: from localhost ([122.172.83.32]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-73bd2198958sm9647372b3a.30.2025.04.15.23.40.34 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 15 Apr 2025 23:40:34 -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_Roy_Baron?= , 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_Benn?= =?utf-8?q?=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 V10 11/15] rust: cpufreq: Add initial abstractions for cpufreq framework Date: Wed, 16 Apr 2025 12:09:28 +0530 Message-Id: X-Mailer: git-send-email 2.31.1.272.g89b43f80a514 In-Reply-To: References: Precedence: bulk X-Mailing-List: linux-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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 | 348 ++++++++++++++++++++++++++++++++ rust/kernel/lib.rs | 2 + 6 files changed, 363 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/ CPU HOTPLUG diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.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 *policy) +{ + 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..d9face425d47 --- /dev/null +++ b/rust/kernel/cpufreq.rs @@ -0,0 +1,348 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! CPU frequency scaling. +//! +//! This module provides rust abstractions for interacting with the cpufreq 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 = bindings::CPUFREQ_ETERNAL as u32; + +/// CPU frequency driver flags. +pub mod flags { + /// Driver needs to update internal limits even if frequency remains unchanged. + pub const NEED_UPDATE_LIMITS: u16 = 1 << 0; + + /// Platform where constants like `loops_per_jiffy` are unaffected by frequency changes. + pub const CONST_LOOPS: u16 = 1 << 1; + + /// Register driver as a thermal cooling device automatically. + pub const IS_COOLING_DEV: u16 = 1 << 2; + + /// Supports multiple clock domains with per-policy governors in `cpu/cpuN/cpufreq/`. + pub const HAVE_GOVERNOR_PER_POLICY: u16 = 1 << 3; + + /// Allows post-change notifications outside of the `target()` routine. + pub const ASYNC_NOTIFICATION: u16 = 1 << 4; + + /// Ensure CPU starts at a valid frequency from the driver's freq-table. + pub const NEED_INITIAL_FREQ_CHECK: u16 = 1 << 5; + + /// Disallow governors with `dynamic_switching` capability. + pub const NO_AUTO_DYNAMIC_SWITCHING: u16 = 1 << 6; +} + +// Relations from the C code. +const CPUFREQ_RELATION_L: u32 = 0; +const CPUFREQ_RELATION_H: u32 = 1; +const CPUFREQ_RELATION_C: u32 = 2; + +// Can be used with any of the above values. +const CPUFREQ_RELATION_E: u32 = 1 << 2; + +/// CPU frequency selection relations. +/// +/// CPU frequency selection relations, each optionally marked as "efficient". +#[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 = val & CPUFREQ_RELATION_E != 0; + + Ok(match val & !CPUFREQ_RELATION_E { + CPUFREQ_RELATION_L => Self::Low(efficient), + CPUFREQ_RELATION_H => Self::High(efficient), + CPUFREQ_RELATION_C => Self::Close(efficient), + _ => return Err(EINVAL), + }) + } +} + +impl From for u32 { + // Convert to a C-compatible `u32` value. + fn from(rel: Relation) -> Self { + let (mut val, efficient) = match rel { + Relation::Low(e) => (CPUFREQ_RELATION_L, e), + Relation::High(e) => (CPUFREQ_RELATION_H, e), + Relation::Close(e) => (CPUFREQ_RELATION_C, e), + }; + + if efficient { + val |= 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 cpufreq_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 and 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 = 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_verify(self.as_raw()) }) + } +} + +/// CPU frequency table. +/// +/// Rust abstraction for the C `struct cpufreq_frequency_table`. +/// +/// # Invariants +/// +/// A [`Table`] instance always corresponds to a valid C `struct cpufreq_frequency_table`. +/// +/// The callers must ensure that the `struct cpufreq_frequency_table` is valid 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; +/// +/// fn show_freq(policy: &Policy) { +/// let table = policy.freq_table().unwrap(); +/// +/// // SAFETY: The index values passed are correct. +/// unsafe { +/// pr_info!("The frequency at index 0 is: {:?}\n", table.freq(0).unwrap()); +/// pr_info!("The flags at index 0 is: {}\n", table.flags(0)); +/// pr_info!("The data at index 0 is: {}\n", table.data(0)); +/// } +/// } +/// ``` +#[allow(dead_code)] +#[repr(transparent)] +pub struct Table(Opaque); + +impl Table { + /// Creates a reference to an existing C `struct cpufreq_frequency_table` 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_table) -> &'a Self { + // SAFETY: Guaranteed by the safety requirements of the function. + // + // INVARIANT: The caller ensures that `ptr` is valid for reading and 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 = self; + this.cast_mut().cast() + } + + /// Returns frequency at `index` in the [`Table`]. + /// + /// # Safety + /// + /// The caller must ensure that `index` corresponds to a valid table entry. + #[inline] + pub unsafe fn freq(&self, index: usize) -> Result { + // SAFETY: By the type invariant, the pointer stored in `self` is valid and `index` is + // guaranteed to be valid by the safety requirements of the function. + Ok(Hertz::from_khz(unsafe { + (*self.as_raw().add(index)).frequency.try_into()? + })) + } + + /// Returns flags at `index` in the [`Table`]. + /// + /// # Safety + /// + /// The caller must ensure that `index` corresponds to a valid table entry. + #[inline] + pub unsafe fn flags(&self, index: usize) -> u32 { + // SAFETY: By the type invariant, the pointer stored in `self` is valid and `index` is + // guaranteed to be valid by the safety requirements of the function. + unsafe { (*self.as_raw().add(index)).flags } + } + + /// Returns data at `index` in the [`Table`]. + /// + /// # Safety + /// + /// The caller must ensure that `index` corresponds to a valid table entry. + #[inline] + pub unsafe fn data(&self, index: usize) -> u32 { + // SAFETY: By the type invariant, the pointer stored in `self` is valid and `index` is + // guaranteed to be valid by the safety requirements of the function. + unsafe { (*self.as_raw().add(index)).driver_data } + } +} + +/// CPU frequency table owned and pinned in memory, created from a [`TableBuilder`]. +pub struct TableBox { + #[allow(dead_code)] + 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 = 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 dynamically. +/// +/// ## Examples +/// +/// The following example demonstrates how to create a CPU frequency table. +/// +/// ``` +/// use kernel::cpufreq::TableBuilder; +/// use kernel::clk::Hertz; +/// +/// let mut builder = 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 = builder.to_table().unwrap(); +/// +/// // SAFETY: The index values passed are correct. +/// unsafe { +/// assert_eq!(table.freq(0), Ok(Hertz::from_mhz(700))); +/// assert_eq!(table.flags(0), 0); +/// assert_eq!(table.data(0), 1); +/// +/// assert_eq!(table.freq(2), Ok(Hertz::from_mhz(900))); +/// assert_eq!(table.flags(2), 4); +/// assert_eq!(table.data(2), 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) -> Result<()> { + // 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; From patchwork Wed Apr 16 06:39:30 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 881774 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 D079A22DFB6 for ; Wed, 16 Apr 2025 06:40:43 +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=1744785646; cv=none; b=ST+YQpVj+kF6iiMW4oDSYEfJTM0UIrnBVoLp8NFIKtJpa/SEgsCyprsj7x/wntd0/Mfp+x2V2G9pgcpOFEAQzduMPJ2DGbm2mPbc7W+/qG9GGEbI1qH6/Bg1WStxTi6NCG7t3l5NWC3sJYE5tdefrjrZljp11gHtyDX0u5mhm4E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744785646; c=relaxed/simple; bh=Sr9rAuYr8ID2mLrVyqriEpQmNGbv/dVay/wcUnsNZrQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=quNNtCRMU+hC/uFZ83QL/Iua9v6hr9zBkZKrKhGQVWJHtsRikGPQzIKo7cax8blNQOXFukuDC4gkXOgxhR6TgZ+87OQjswpCwS4FfGzfmNaNBnIbOa0z/tslKHVofzgaILWAYE3ZZkfMGWnxZEwGFy7qvPdDWDQ1LBbamyEXuKg= 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=pN4f8XE3; 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="pN4f8XE3" Received: by mail-pl1-f176.google.com with SMTP id d9443c01a7336-2255003f4c6so66524485ad.0 for ; Tue, 15 Apr 2025 23:40:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1744785643; x=1745390443; 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=nra1/y1QUgh6AgleDK5Vc1vrrwimHbIG9HYBJFdcQfg=; b=pN4f8XE3AAP2AUk+0k3P7ww21BxPUHaP1+uEk/UT6Frdm0CpdZ8IlR+x3DS+KkyehF zsrsAn2PWKid1gIavzA3kewAoybBxnUb9F5hVcgKY67O2Ne9N+rezZKedLn5pL5gbbrE Yp6J+0X/F53m+sJnOXNohCidhiclgwqoAGY9p+QYwIz4F8amoA33D48u745FZ5n4yoiY IX1GorBt7VWHTmzGi3iI/B9tOuxsukiZp5qKhvrmqQKacUCaXJwrvOAqJJ5J+IU36Acj HGRpg4dLJkezVRBSJ7HjSkt7/gPJO/Qm1X9uMmKSv5oOGF1wBf+z/cHs0XPBD6tuTn3u 0x7A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1744785643; x=1745390443; 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=nra1/y1QUgh6AgleDK5Vc1vrrwimHbIG9HYBJFdcQfg=; b=TKom3bnMnWe3bCsg9G46sH8FIWeh8fbdbCgAOi4PidBNFOS2PKiQCVcKfvYNT29JZb 4a35Izq+12/97sSSlav+buoUZ7dafhnrFRMsJp9Tx9S5AVtIe/F5trgb0+FmNCsff9ja xNRnZdalO0tgAVh+AYQN9RNQOJrPerA6bZoZnbzz/heLQAaEyTWawNRrvrrtQUhU49qR trxpdwkgHkMDfNkHDJEBZ5v26V2Ww3WkBIM248Tu781LdGXNaj1rVMZUcP5JoHNQ23Bj 4MLEb1PY0ew5HuVDH167Cp7e8fQac8yPS80awPyimjDzCA2bRKQoILhyxs7WaH7hObQl 8RjA== X-Gm-Message-State: AOJu0Yy6bj/Rc+CCQA8z5zC/Q8TJmPFxGnd5w7KZQEXJwxbrgbxoBhpj sQTjwd7LilUO8BOv5nbIPZtuXrENUuyilktVP+fslhkP+H5eRRlOdc0wM97fP5U= X-Gm-Gg: ASbGncumZRQoqLBbd996kAmdxl1QH1A82WrlfqkUO0VjChMdGqKz63IwfMKtWXfFpHO lCmb1xUk1OyWyBwh4CsmqYNAGa+THDbBqUVamOkkedr3EQ7qWcXkbcrKSPaHEXCdqphqSsbt8LT gt1oCuWgrZWO7DUGh1+GiZGv8KbTB2+wgjCxzN+n9C2OaxZ5tRkO3T77Aa3qsYzaEnl/q+gRoTc pEPgxwDK3vfRL1cnt8Jv2bAjNd3aFSXVTwQcSUJwmSeGywvzZM7j2b2dTmX4OvlvH1IhOzmqbOF F576PXxtWJz6aZp1hBPndDei+dgNtnRnizxq1oa4lQ== X-Google-Smtp-Source: AGHT+IHfv4TXGee8ischTd2l0mhcpY+jrfYOqJPhK7dDPNeUP1SWyHXE6UuQWPB+/HGeogEvX68lJA== X-Received: by 2002:a17:903:1247:b0:220:ca39:d453 with SMTP id d9443c01a7336-22c358d878cmr9655275ad.17.1744785642912; Tue, 15 Apr 2025 23:40:42 -0700 (PDT) Received: from localhost ([122.172.83.32]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-22c33fa5febsm6384525ad.117.2025.04.15.23.40.41 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 15 Apr 2025 23:40:42 -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?B?= =?utf-8?q?j=C3=B6rn_Roy_Baron?= , 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_Benn?= =?utf-8?q?=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 V10 13/15] rust: cpufreq: Extend abstractions for driver registration Date: Wed, 16 Apr 2025 12:09:30 +0530 Message-Id: <8d04ef19d7a16610dbf0dfb5c9a611c6e1e3e318.1744783509.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-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Extend the cpufreq abstractions to support driver registration from Rust. Signed-off-by: Viresh Kumar --- rust/kernel/cpufreq.rs | 480 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 477 insertions(+), 3 deletions(-) diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs index fd605921add4..87a54a8af198 100644 --- a/rust/kernel/cpufreq.rs +++ b/rust/kernel/cpufreq.rs @@ -9,24 +9,31 @@ //! Reference: use crate::{ + alloc::AllocError, 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, }; use core::{ + marker::PhantomData, + mem::MaybeUninit, ops::{Deref, DerefMut}, pin::Pin, - ptr, + ptr::{self, NonNull}, }; use macros::vtable; +// Maximum length of CPU frequency driver's name. +const CPUFREQ_NAME_LEN: usize = bindings::CPUFREQ_NAME_LEN as usize; + /// Default transition latency value in nanoseconds. pub const ETERNAL_LATENCY_NS: u32 = bindings::CPUFREQ_ETERNAL as u32; @@ -801,3 +808,470 @@ 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 = c_str!("cpufreq-foo"); +/// const FLAGS: u16 = cpufreq::flags::NEED_INITIAL_FREQ_CHECK | cpufreq::flags::IS_COOLING_DEV; +/// const BOOST_ENABLED: bool = true; +/// +/// type PData = 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: u32) -> 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(NonNull, PhantomData); + +// SAFETY: `Registration` doesn't offer any methods or access to fields when 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 subsystem can happen from any +// thread. +unsafe impl Send for Registration {} + +impl Registration { + const VTABLE: bindings::cpufreq_driver = 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_driver`. + ..unsafe { MaybeUninit::zeroed().assume_init() } + }; + + const fn copy_name(name: &'static CStr) -> [c_char; CPUFREQ_NAME_LEN] { + let src = name.as_bytes_with_nul(); + let mut dst = [0; CPUFREQ_NAME_LEN]; + + build_assert!(src.len() <= CPUFREQ_NAME_LEN); + + let mut i = 0; + while i < src.len() { + dst[i] = src[i]; + i += 1; + } + + dst + } + + /// Registers a CPU frequency driver with the cpufreq core. + pub fn new() -> Result { + let drv: *const bindings::cpufreq_driver = &Self::VTABLE; + let drv = drv.cast_mut(); + + // SAFETY: It is safe to register the driver with the cpufreq core in the kernel C code. + to_result(unsafe { bindings::cpufreq_register_driver(drv) })?; + + Ok(Self( + NonNull::new(drv.cast()).ok_or(AllocError)?, + PhantomData, + )) + } + + /// Same as [`Registration::new`], but does not return a [`Registration`] instance. + /// + /// Instead the [`Registration`] is owned by [`Devres`] and will be revoked / dropped, once the + /// device is detached. + pub fn new_foreign_owned(dev: &Device) -> Result<()> { + Devres::new_foreign_owned(dev, Self::new()?, GFP_KERNEL)?; + Ok(()) + } +} + +// 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) -> 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 = unsafe { Policy::from_raw_mut(ptr) }; + + let data = 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 with the C code for the + // lifetime of `policy`. + let policy = unsafe { Policy::from_raw_mut(ptr) }; + + let data = policy.clear_data(); + let _ = 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) -> 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 = 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 = 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 = 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) -> 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 = 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 with the C code for the + // lifetime of `policy`. + let policy = 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 = 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 = 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 = 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 = unsafe { Policy::from_raw_mut(ptr) }; + 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 with the C code for the + // lifetime of `policy`. + let policy = 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) = 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 with the C code for the + // lifetime of `policy`. + let policy = unsafe { Policy::from_raw_mut(ptr) }; + 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 = unsafe { Policy::from_raw_mut(ptr) }; + 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) = 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 = 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 = 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 with the C code for the + // lifetime of `policy`. + let policy = unsafe { Policy::from_raw_mut(ptr) }; + T::register_em(policy); + } +} + +impl Drop for Registration { + // Removes the `Registration` from the kernel, if it has initialized successfully earlier. + fn drop(&mut self) { + // SAFETY: The driver was earlier registered from `new`. + unsafe { bindings::cpufreq_unregister_driver(self.0.as_ptr()) }; + } +} From patchwork Wed Apr 16 06:39:32 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Viresh Kumar X-Patchwork-Id: 881773 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 11BE924338F for ; Wed, 16 Apr 2025 06:40:51 +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=1744785652; cv=none; b=iRAhxLjUBs5wAim1lCdOm/+ke0KPM4OS7NV7TyVy9s8N1X87ILpXO/HtngIIDuAQgDHxEZNCEgerz0AskFVlh2DWsbek7T64AVnmy/wF8NJO9VYe9Hjd9hHm6DJdnED2ACr1ajv1K0ks7iIXDv/N5Hee5QHWNJcR/u+q/C/Fy5Y= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1744785652; c=relaxed/simple; bh=YCUW7M3FpgyiiQGjw35St6tdqJBkxe3Qu+wxpKDUfUw=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=FOssZAHU6FUpIQLCGB3fZTtLMKC5oGkyUnmX2i1GEOw4IJfEDxgHsAbtoc9zQ1NU2RbwUj5D6O5Jwo8ixyOmyBVvQp0oekqwtI/bCgJI0wsqJUe1uHhI19GeZpQUWYJFY2JKvQE7MYNTD5y9CR2CdImHClsElPprm16k5rUnnr8= 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=fDjFcA9C; 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="fDjFcA9C" Received: by mail-pl1-f181.google.com with SMTP id d9443c01a7336-223f4c06e9fso3694135ad.1 for ; Tue, 15 Apr 2025 23:40:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1744785650; x=1745390450; 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=N/mvtnXNN1pJbPS5ASieAHCptDhOrKeUchHsYheFMok=; b=fDjFcA9CZoYgTGPZhwnpsEmiQ5GSqU2n+9xaxnJOwqbxzl6fenRcSlbGMAhcfY0vOc B8AV4jl7z1f08auin1FDl2qNLmBQqZ59udosBXnXlb/xJ9NdYCdycKKPexrtvHaFaz3+ 0DkAwIp5xatzThJENXrKa4XKsYxRYXKwLPI2jLXZNNsN2REq98xe8QsNA7XZXqikzURG m1Ts6cy3tVyTApQyjsvCRprWMbINGopICeWV3NMs6AKfgEsAcWgDMyl8c6lCj2X+BV/o u4pzaMuR5fRKByh2X9K1n63S0yQCpGGJTqXA+49kv6BEI1Rxm6aq7QQ0eoyVUNnvbq8e bXFg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1744785650; x=1745390450; 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=N/mvtnXNN1pJbPS5ASieAHCptDhOrKeUchHsYheFMok=; b=MHdmLh4dajthTGgPWmVIxfzsO/zmkIwgRoy7uiKnpZ1L+D8AYlVJlZB2R3UEAfTQh7 EWjKCEHyY4IkCq0vu1tOOlkZeKelHwAGusCuV8J36nVdmL+6WD6x8fCxJqZ2g5XZuEGv 8eZieaJsj7X1mC14puuprZAHWKwiy4FiuACTccw/KOIT78hf5uRUqFne/kdGYvEh5VSZ UXjPQ88xTLICNEWH6GktoY5biH80aQ0B9R6zYFYoaSdUDPqQD1lRpfpIZT3CACojChQ1 LdjSKu7TYyq9xvZxgXlXJr2NBK2VQ6go9ncOKoqlL5ZLyUozfcXCIZNK1eAcMR2aBu5w itOw== X-Gm-Message-State: AOJu0YxcZbRH+0DxEqCO34yRz8CQfqhkTTrEhTtmtC9qWPS7ZNg1JNda czwChMuxYtHUKWC4T60D4glHjFc2xm7KODCMHdVfPoSl8C0VZnVDICV7G5jmwPc= X-Gm-Gg: ASbGncvVjiEKPjrOpjjWKrCECAIYTlgmsuiXIkQvLIGay1El4RWxRuMFwqjYAZdabFx HBS4p5/UZux1U4hI4AhdLS/RlFWMGFSj6ZlQhAhukRCKoZeVOeFaGEHwgIIuOR25n+X79zG9tFF lpImDYcb5UnWLTjDiPA/HZDXDG5/werm+Zs4xaezMCz6JoGViy42kBq1otHYHI95kDzdFKdB50E i/XOhMvDhLaEhXK/fOUhNhwOKFdlNursczgfjE+UTknUIOZ5NCmPJbw3tbPR+dVBu3l9bDB55PS eZ2pIdz8dM2SIAjNj2uuTUd5hrSMDIMcjmm3QRgD1g== X-Google-Smtp-Source: AGHT+IHsyZn4vaNkqCMpGot94/NaRC26FvkgWryRgtlbTWHkBqMeg8qk1kmY2mX8fUHHHMTa/r6A2g== X-Received: by 2002:a17:902:f683:b0:223:fb95:b019 with SMTP id d9443c01a7336-22c35f16999mr9717555ad.24.1744785650447; Tue, 15 Apr 2025 23:40:50 -0700 (PDT) Received: from localhost ([122.172.83.32]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-22c33f1cd56sm6491425ad.83.2025.04.15.23.40.49 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 15 Apr 2025 23:40:49 -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?B?= =?utf-8?q?j=C3=B6rn_Roy_Baron?= , 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_Benn?= =?utf-8?q?=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 V10 15/15] cpufreq: Add Rust-based cpufreq-dt driver Date: Wed, 16 Apr 2025 12:09:32 +0530 Message-Id: <312f14531b97a7530446cc44e38d6dbe10fef083.1744783509.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-pm@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 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 | 233 +++++++++++++++++++++++++++++++++ 3 files changed, 246 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 If in doubt, say N. +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) += cpufreq_governor.o obj-$(CONFIG_CPU_FREQ_GOV_ATTR_SET) += cpufreq_governor_attr_set.o obj-$(CONFIG_CPUFREQ_DT) += cpufreq-dt.o +obj-$(CONFIG_CPUFREQ_DT_RUST) += rcpufreq_dt.o obj-$(CONFIG_CPUFREQ_DT_PLATDEV) += cpufreq-dt-platdev.o obj-$(CONFIG_CPUFREQ_VIRT) += virtual-cpufreq.o diff --git a/drivers/cpufreq/rcpufreq_dt.rs b/drivers/cpufreq/rcpufreq_dt.rs new file mode 100644 index 000000000000..81ede13909b7 --- /dev/null +++ b/drivers/cpufreq/rcpufreq_dt.rs @@ -0,0 +1,233 @@ +// 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 = 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 = (cpu == 0) + .then(|| find_supply_name_exact(dev, "cpu0")) + .flatten() + .or_else(|| find_supply_name_exact(dev, "cpu"))?; + + let mut list = KVec::with_capacity(1, GFP_KERNEL).ok()?; + list.push(name, GFP_KERNEL).ok()?; + + Some(list) +} + +// Represents the cpufreq dt device. +struct CPUFreqDTDevice { + opp_table: opp::Table, + freq_table: opp::FreqTable, + #[allow(dead_code)] + mask: CpumaskVar, + #[allow(dead_code)] + token: Option, + #[allow(dead_code)] + clk: Clk, +} + +#[derive(Default)] +struct CPUFreqDTDriver; + +#[vtable] +impl opp::ConfigOps for CPUFreqDTDriver {} + +#[vtable] +impl cpufreq::Driver for CPUFreqDTDriver { + const NAME: &CStr = c_str!("cpufreq-dt"); + const FLAGS: u16 = cpufreq::flags::NEED_INITIAL_FREQ_CHECK | cpufreq::flags::IS_COOLING_DEV; + const BOOST_ENABLED: bool = true; + + type PData = Arc; + + fn init(policy: &mut cpufreq::Policy) -> Result { + let cpu = policy.cpu(); + // SAFETY: The CPU device is only used during init; it won't get hot-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 = unsafe { cpu::from_cpu(cpu)? }; + let mut mask = CpumaskVar::new_zero(GFP_KERNEL)?; + + mask.set(cpu); + + let token = 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 = match opp::Table::of_sharing_cpus(dev, &mut mask) { + Ok(()) => false, + Err(e) if e == ENOENT => { + // "operating-points-v2" not supported. If the platform hasn't + // set sharing CPUs, fallback to all CPUs share the `Policy` + // for backward compatibility. + opp::Table::sharing_cpus(dev, &mut mask).is_err() + } + Err(e) => return Err(e), + }; + + // Initialize OPP tables for all policy cpus. + // + // For platforms not using "operating-points-v2" bindings, we do this + // 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 unless + // it is -EPROBE_DEFER. + let mut opp_table = match opp::Table::from_of_cpumask(dev, &mut mask) { + Ok(table) => table, + Err(e) => { + if e == EPROBE_DEFER { + return Err(e); + } + + // The table is added dynamically ? + opp::Table::from_dev(dev)? + } + }; + + // The OPP table must be initialized, statically or dynamically, by 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 = opp_table.max_transition_latency_ns() as u32; + if transition_latency == 0 { + transition_latency = 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 = opp_table.cpufreq_table()?; + // SAFETY: The `freq_table` is not dropped while it is getting used by the C code. + unsafe { policy.set_freq_table(&freq_table) }; + + // SAFETY: The returned `clk` is not dropped while it is getting used by the C code. + let clk = unsafe { policy.set_clk(dev, None)? }; + + mask.copy(policy.cpus()); + + Ok(Arc::new( + CPUFreqDTDevice { + opp_table, + freq_table, + mask, + token, + clk, + }, + GFP_KERNEL, + )?) + } + + fn exit(_policy: &mut cpufreq::Policy, _data: Option) -> Result<()> { + Ok(()) + } + + fn online(_policy: &mut cpufreq::Policy) -> Result<()> { + // We did light-weight tear down earlier, nothing to do here. + Ok(()) + } + + fn offline(_policy: &mut cpufreq::Policy) -> Result<()> { + // Preserve policy->data and don't free resources on light-weight + // tear down. + Ok(()) + } + + fn suspend(policy: &mut cpufreq::Policy) -> Result<()> { + policy.generic_suspend() + } + + fn verify(data: &mut cpufreq::PolicyData) -> Result<()> { + data.generic_verify() + } + + fn target_index(policy: &mut cpufreq::Policy, index: u32) -> Result<()> { + let Some(data) = policy.data::() else { + return Err(ENOENT); + }; + + // SAFETY: `index` is guaranteed to be valid by the C API. + let freq = unsafe { data.freq_table.freq(index.try_into()?)? }; + 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 = (); + const OF_ID_TABLE: Option> = Some(&OF_TABLE); + + fn probe( + pdev: &platform::Device, + _id_info: Option<&Self::IdInfo>, + ) -> Result>> { + cpufreq::Registration::::new_foreign_owned(pdev.as_ref())?; + + let drvdata = 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", +}