From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mailout1.w1.samsung.com (mailout1.w1.samsung.com [210.118.77.11]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 76A892D323E for ; Mon, 23 Jun 2025 18:09:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=210.118.77.11 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750702146; cv=none; b=lf7z/xxDVW1QgF7NxB9INxaD8ZC1ubWz0nl8EvsYXucdWUu5XSDbxrIt2JudP3tXClrf1NNd1LznxNurP7gvSmpyotbN040jLKJsy26t31JaaSMDv0qgDCSXktI9ceM6oRdESfAcwxB18UwkedsjTggbrcpZd2hKJbPiIYnU274= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750702146; c=relaxed/simple; bh=Vjiw3nx7Fa76oWlj0bQ8HbzDqgDUK+mjxz6Q/hPhj3w=; h=From:Date:Subject:MIME-Version:Message-Id:In-Reply-To:To:Cc: Content-Type:References; b=t3dpbbEOT/CSLcByYqYTpQj8nW22pwAnkA5cpTReV3T5GKhYieZre3GbWQCcT+a6XIiIbzsis5QHeUHfbCJ6nN/o4nqCB+8fzLyNivTiGmWG4I6f4c3JPBnlGSX1Czt+DRwxOt/fu3OHW4SuHAFvNng5cGmvMLk19ZKBluukzfw= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=k+cZBL72; arc=none smtp.client-ip=210.118.77.11 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=samsung.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b="k+cZBL72" Received: from eucas1p1.samsung.com (unknown [182.198.249.206]) by mailout1.w1.samsung.com (KnoxPortal) with ESMTP id 20250623180859euoutp012e9c24a7ea83db319976c2b3cba5b32b~LvlIDQ9my3140931409euoutp01h for ; Mon, 23 Jun 2025 18:08:59 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.w1.samsung.com 20250623180859euoutp012e9c24a7ea83db319976c2b3cba5b32b~LvlIDQ9my3140931409euoutp01h DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1750702139; bh=oiZxyd/rOfRBrJ7bPBapMe43Tp3efYzMkYp4AvOkByE=; h=From:Date:Subject:In-Reply-To:To:Cc:References:From; b=k+cZBL72hrSspFCQ7ATuDT6hj75tS3dIfpo1R4sekiE5wuu/nc1lp8RwbgF48RPb7 CMEBcm7wa5aDjhb6I7dMxlbI23bNHQxL6WMiPH2g2kV0mP7vFZHmimJHhj/0tW+gP/ dcAxVIu9czl1mqBQvoI8BH2I1Zml2KBg5AwHQ0Ho= Received: from eusmtip1.samsung.com (unknown [203.254.199.221]) by eucas1p1.samsung.com (KnoxPortal) with ESMTPA id 20250623180858eucas1p1815f6d6815b1c715baad94810cefacd5~LvlG83x7-0556705567eucas1p1d; Mon, 23 Jun 2025 18:08:58 +0000 (GMT) Received: from AMDC4942.eu.corp.samsungelectronics.net (unknown [106.210.136.40]) by eusmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250623180857eusmtip1198e19606acb11b221532b8626ec28c7~LvlFt1m0X2097520975eusmtip1D; Mon, 23 Jun 2025 18:08:57 +0000 (GMT) From: Michal Wilczynski Date: Mon, 23 Jun 2025 20:08:49 +0200 Subject: [PATCH v5 1/9] rust: pwm: Add Kconfig and basic data structures Precedence: bulk X-Mailing-List: rust-for-linux@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Message-Id: <20250623-rust-next-pwm-working-fan-for-sending-v5-1-0ca23747c23e@samsung.com> In-Reply-To: <20250623-rust-next-pwm-working-fan-for-sending-v5-0-0ca23747c23e@samsung.com> To: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Michal Wilczynski , Drew Fustini , Guo Ren , Fu Wei , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Paul Walmsley , Palmer Dabbelt , Albert Ou , Alexandre Ghiti , Marek Szyprowski , Benno Lossin , Michael Turquette , Stephen Boyd , Benno Lossin Cc: linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-riscv@lists.infradead.org, devicetree@vger.kernel.org, linux-clk@vger.kernel.org X-Mailer: b4 0.15-dev X-CMS-MailID: 20250623180858eucas1p1815f6d6815b1c715baad94810cefacd5 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" X-RootMTR: 20250623180858eucas1p1815f6d6815b1c715baad94810cefacd5 X-EPHeader: CA X-CMS-RootMailID: 20250623180858eucas1p1815f6d6815b1c715baad94810cefacd5 References: <20250623-rust-next-pwm-working-fan-for-sending-v5-0-0ca23747c23e@samsung.com> Introduce the foundational support for PWM abstractions in Rust. This commit adds the `RUST_PWM_ABSTRACTIONS` Kconfig option to enable the feature, along with the necessary build-system support and C helpers. It also introduces the first set of safe wrappers for the PWM subsystem, covering the basic data carrying C structs and enums: - `Polarity`: A safe wrapper for `enum pwm_polarity`. - `Waveform`: A wrapper for `struct pwm_waveform`. - `Args`: A wrapper for `struct pwm_args`. - `State`: A wrapper for `struct pwm_state`. These types provide memory safe, idiomatic Rust representations of the core PWM data structures and form the building blocks for the abstractions that will follow. Signed-off-by: Michal Wilczynski --- MAINTAINERS | 6 ++ drivers/pwm/Kconfig | 13 +++ rust/bindings/bindings_helper.h | 1 + rust/helpers/helpers.c | 1 + rust/helpers/pwm.c | 20 ++++ rust/kernel/lib.rs | 2 + rust/kernel/pwm.rs | 198 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 241 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 0c1d245bf7b84f8a78b811e0c9c5a3edc09edc22..a575622454a2ef57ce055c8a8c4765fa4fddc490 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20073,6 +20073,12 @@ F: include/linux/pwm.h F: include/linux/pwm_backlight.h K: pwm_(config|apply_might_sleep|apply_atomic|ops) +PWM SUBSYSTEM BINDINGS [RUST] +M: Michal Wilczynski +S: Maintained +F: rust/helpers/pwm.c +F: rust/kernel/pwm.rs + PXA GPIO DRIVER M: Robert Jarzmik L: linux-gpio@vger.kernel.org diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index d9bcd1e8413eaed1602d6686873e263767c58f5f..cfddeae0eab3523f04f361fb41ccd1345c0c937b 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -790,4 +790,17 @@ config PWM_XILINX To compile this driver as a module, choose M here: the module will be called pwm-xilinx. + config RUST_PWM_ABSTRACTIONS + bool "Rust PWM abstractions support" + depends on RUST + depends on PWM=y + help + This option enables the safe Rust abstraction layer for the PWM + subsystem. It provides idiomatic wrappers and traits necessary for + writing PWM controller drivers in Rust. + + The abstractions handle resource management (like memory and reference + counting) and provide safe interfaces to the underlying C core, + allowing driver logic to be written in safe Rust. + endif diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 693cdd01f9290fa01375cf78cac0e5a90df74c6c..6fe7dd529577952bf7adb4fe0526b0d5fbd6f3bd 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -64,6 +64,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 16fa9bca5949b85e8d4cdcfe8e6886124f72d8d8..60879e6d794ce0f87e39caafc5495bf5e8acf8f0 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -31,6 +31,7 @@ #include "platform.c" #include "pci.c" #include "pid_namespace.c" +#include "pwm.c" #include "rbtree.c" #include "rcu.c" #include "refcount.c" diff --git a/rust/helpers/pwm.c b/rust/helpers/pwm.c new file mode 100644 index 0000000000000000000000000000000000000000..d75c588863685d3990b525bb1b84aa4bc35ac397 --- /dev/null +++ b/rust/helpers/pwm.c @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2025 Samsung Electronics Co., Ltd. +// Author: Michal Wilczynski + +#include + +struct device *rust_helper_pwmchip_parent(const struct pwm_chip *chip) +{ + return pwmchip_parent(chip); +} + +void *rust_helper_pwmchip_get_drvdata(struct pwm_chip *chip) +{ + return pwmchip_get_drvdata(chip); +} + +void rust_helper_pwmchip_set_drvdata(struct pwm_chip *chip, void *data) +{ + pwmchip_set_drvdata(chip, data); +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 6b4774b2b1c37f4da1866e993be6230bc6715841..ce1d08b14e456905dbe7b625bbb8ca8b08deae2a 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -105,6 +105,8 @@ pub mod seq_file; pub mod sizes; mod static_assert; +#[cfg(CONFIG_RUST_PWM_ABSTRACTIONS)] +pub mod pwm; #[doc(hidden)] pub mod std_vendor; pub mod str; diff --git a/rust/kernel/pwm.rs b/rust/kernel/pwm.rs new file mode 100644 index 0000000000000000000000000000000000000000..ed681b228c414e7ae8bf80ca649ad497c9dc4ec3 --- /dev/null +++ b/rust/kernel/pwm.rs @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2025 Samsung Electronics Co., Ltd. +// Author: Michal Wilczynski + +//! PWM subsystem abstractions. +//! +//! C header: [`include/linux/pwm.h`](srctree/include/linux/pwm.h). + +use crate::{ + bindings, + prelude::*, + types::Opaque, +}; +use core::convert::TryFrom; + +/// Maximum size for the hardware-specific waveform representation buffer. +/// +/// From C: `#define WFHWSIZE 20` +pub const WFHW_MAX_SIZE: usize = 20; + +/// PWM polarity. Mirrors [`enum pwm_polarity`](srctree/include/linux/pwm.h). +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Polarity { + /// Normal polarity (duty cycle defines the high period of the signal). + Normal, + + /// Inversed polarity (duty cycle defines the low period of the signal). + Inversed, +} + +impl TryFrom for Polarity { + type Error = Error; + + fn try_from(polarity: bindings::pwm_polarity) -> Result { + match polarity { + bindings::pwm_polarity_PWM_POLARITY_NORMAL => Ok(Polarity::Normal), + bindings::pwm_polarity_PWM_POLARITY_INVERSED => Ok(Polarity::Inversed), + _ => Err(EINVAL), + } + } +} + +impl From for bindings::pwm_polarity { + fn from(polarity: Polarity) -> Self { + match polarity { + Polarity::Normal => bindings::pwm_polarity_PWM_POLARITY_NORMAL, + Polarity::Inversed => bindings::pwm_polarity_PWM_POLARITY_INVERSED, + } + } +} + +/// Represents a PWM waveform configuration. +/// Mirrors struct [`struct pwm_waveform`](srctree/include/linux/pwm.h). +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub struct Waveform { + /// Total duration of one complete PWM cycle, in nanoseconds. + pub period_length_ns: u64, + + /// Duty-cycle active time, in nanoseconds. + /// + /// For a typical normal polarity configuration (active-high) this is the + /// high time of the signal. + pub duty_length_ns: u64, + + /// Duty-cycle start offset, in nanoseconds. + /// + /// Delay from the beginning of the period to the first active edge. + /// In most simple PWM setups this is `0`, so the duty cycle starts + /// immediately at each period’s start. + pub duty_offset_ns: u64, +} + +impl From for Waveform { + fn from(wf: bindings::pwm_waveform) -> Self { + Waveform { + period_length_ns: wf.period_length_ns, + duty_length_ns: wf.duty_length_ns, + duty_offset_ns: wf.duty_offset_ns, + } + } +} + +impl From for bindings::pwm_waveform { + fn from(wf: Waveform) -> Self { + bindings::pwm_waveform { + period_length_ns: wf.period_length_ns, + duty_length_ns: wf.duty_length_ns, + duty_offset_ns: wf.duty_offset_ns, + } + } +} + +/// Wrapper for board-dependent PWM arguments [`struct pwm_args`](srctree/include/linux/pwm.h). +#[repr(transparent)] +pub struct Args(Opaque); + +impl Args { + /// Creates an `Args` wrapper from a C struct pointer. + /// + /// # Safety + /// + /// The caller must ensure that `c_args_ptr` is a valid, non-null pointer + /// to `bindings::pwm_args` and that the pointed-to data is valid + /// for the duration of this function call (as data is copied). + unsafe fn from_c_ptr(c_args_ptr: *const bindings::pwm_args) -> Self { + // SAFETY: Caller guarantees `c_args_ptr` is valid. We dereference it to copy. + Args(Opaque::new(unsafe { *c_args_ptr })) + } + + /// Returns the period of the PWM signal in nanoseconds. + pub fn period(&self) -> u64 { + // SAFETY: `self.0.get()` returns a pointer to the `bindings::pwm_args` + // managed by the `Opaque` wrapper. This pointer is guaranteed to be + // valid and aligned for the lifetime of `self` because `Opaque` owns a copy. + unsafe { (*self.0.get()).period } + } + + /// Returns the polarity of the PWM signal. + pub fn polarity(&self) -> Result { + // SAFETY: `self.0.get()` returns a pointer to the `bindings::pwm_args` + // managed by the `Opaque` wrapper. This pointer is guaranteed to be + // valid and aligned for the lifetime of `self`. + let raw_polarity = unsafe { (*self.0.get()).polarity }; + Polarity::try_from(raw_polarity) + } +} + +/// Wrapper for PWM state [`struct pwm_state`](srctree/include/linux/pwm.h). +#[repr(transparent)] +pub struct State(bindings::pwm_state); + +impl Default for State { + fn default() -> Self { + Self::new() + } +} + +impl State { + /// Creates a new zeroed `State`. + pub fn new() -> Self { + State(bindings::pwm_state::default()) + } + + /// Creates a `State` wrapper by taking ownership of a C `pwm_state` value. + pub(crate) fn from_c(c_state: bindings::pwm_state) -> Self { + State(c_state) + } + + /// Gets the period of the PWM signal in nanoseconds. + pub fn period(&self) -> u64 { + self.0.period + } + + /// Sets the period of the PWM signal in nanoseconds. + pub fn set_period(&mut self, period_ns: u64) { + self.0.period = period_ns; + } + + /// Gets the duty cycle of the PWM signal in nanoseconds. + pub fn duty_cycle(&self) -> u64 { + self.0.duty_cycle + } + + /// Sets the duty cycle of the PWM signal in nanoseconds. + pub fn set_duty_cycle(&mut self, duty_ns: u64) { + self.0.duty_cycle = duty_ns; + } + + /// Returns `true` if the PWM signal is enabled. + pub fn enabled(&self) -> bool { + self.0.enabled + } + + /// Sets the enabled state of the PWM signal. + pub fn set_enabled(&mut self, enabled: bool) { + self.0.enabled = enabled; + } + + /// Gets the polarity of the PWM signal. + pub fn polarity(&self) -> Result { + Polarity::try_from(self.0.polarity) + } + + /// Sets the polarity of the PWM signal. + pub fn set_polarity(&mut self, polarity: Polarity) { + self.0.polarity = polarity.into(); + } + + /// Returns `true` if the PWM signal is configured for power usage hint. + pub fn usage_power(&self) -> bool { + self.0.usage_power + } + + /// Sets the power usage hint for the PWM signal. + pub fn set_usage_power(&mut self, usage_power: bool) { + self.0.usage_power = usage_power; + } +} -- 2.34.1