Re: [PATCH v15 2/7] rust: pwm: Add Kconfig and basic data structures

Elle Rhumsaa
2025-10-01T20:52:54Z

On Tue, Sep 30, 2025 at 02:20:33PM +0200, Michal Wilczynski wrote:
> 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`.
> - `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.
>
> Tested-by: Drew Fustini <fustini@kernel.org>
> Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
> Reviewed-by: Elle Rhumsaa <elle@weathered-steel.dev>
> Signed-off-by: Michal Wilczynski <m.wilczynski@samsung.com>
> ---
> MAINTAINERS | 8 ++++
> 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 | 102 ++++++++++++++++++++++++++++++++++++++++
> 7 files changed, 147 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index fe168477caa45799dfe07de2f54de6d6a1ce0615..5d7c0676c1d00a02b3d7db2de88b039c08c99c6e 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -20387,6 +20387,14 @@ 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 <m.wilczynski@samsung.com>
> +L: linux-pwm@vger.kernel.org
> +L: rust-for-linux@vger.kernel.org
> +S: Maintained
> +F: rust/helpers/pwm.c
> +F: rust/kernel/pwm.rs
> +
> PXA GPIO DRIVER
> M: Robert Jarzmik <robert.jarzmik@free.fr>
> L: linux-gpio@vger.kernel.org
> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
> index f00ce973dddf651287168b44228574f4d5c28dc0..2b608f4378138775ee3ba4d53f682952e1914118 100644
> --- a/drivers/pwm/Kconfig
> +++ b/drivers/pwm/Kconfig
> @@ -800,4 +800,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 84d60635e8a9baef1f1a1b2752dc0fa044f8542f..7a06ee5781eadc9f21ccd456b574a9cb152cd58c 100644
> --- a/rust/bindings/bindings_helper.h
> +++ b/rust/bindings/bindings_helper.h
> @@ -66,6 +66,7 @@
> #include <linux/pm_opp.h>
> #include <linux/poll.h>
> #include <linux/property.h>
> +#include <linux/pwm.h>
> #include <linux/refcount.h>
> #include <linux/regulator/consumer.h>
> #include <linux/sched.h>
> diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
> index 7cf7fe95e41dd51717050648d6160bebebdf4b26..861052ffffaff60e9c2e8109e55f3b6158ff2281 100644
> --- a/rust/helpers/helpers.c
> +++ b/rust/helpers/helpers.c
> @@ -35,6 +35,7 @@
> #include "platform.c"
> #include "poll.c"
> #include "property.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 <m.wilczynski@samsung.com>
> +
> +#include <linux/pwm.h>
> +
> +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 ed53169e795c0badf548025a57f946fa18bc73e3..e339b552f9650803b1efa1eb8ecc6fe9d2c56563 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -117,6 +117,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..beabf0086a2f1beea01e0b0a9f6540c601f77a49
> --- /dev/null
> +++ b/rust/kernel/pwm.rs
> @@ -0,0 +1,102 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// Copyright (c) 2025 Samsung Electronics Co., Ltd.
> +// Author: Michal Wilczynski <m.wilczynski@samsung.com>
> +
> +//! PWM subsystem abstractions.
> +//!
> +//! C header: [`include/linux/pwm.h`](srctree/include/linux/pwm.h).
> +
> +use crate::{
> + bindings,
> + prelude::*,
> + types::Opaque,
> +};
> +use core::convert::TryFrom;
> +
> +/// 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<bindings::pwm_polarity> for Polarity {
> + type Error = Error;
> +
> + fn try_from(polarity: bindings::pwm_polarity) -> Result<Self, Error> {
> + match polarity {
> + bindings::pwm_polarity_PWM_POLARITY_NORMAL => Ok(Polarity::Normal),
> + bindings::pwm_polarity_PWM_POLARITY_INVERSED => Ok(Polarity::Inversed),
> + _ => Err(EINVAL),
> + }
> + }
> +}
> +
> +impl From<Polarity> 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<bindings::pwm_waveform> 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<Waveform> 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 PWM state [`struct pwm_state`](srctree/include/linux/pwm.h).
> +#[repr(transparent)]
> +pub struct State(bindings::pwm_state);
> +
> +impl State {
> + /// 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)
> + }
> +
> + /// Returns `true` if the PWM signal is enabled.
> + pub fn enabled(&self) -> bool {
> + self.0.enabled
> + }
> +}
>
> --
> 2.34.1