rust: pwm: Add Kconfig and basic data structures

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>
Link: https://patch.msgid.link/20251016-rust-next-pwm-working-fan-for-sending-v16-2-a5df2405d2bd@samsung.com
Signed-off-by: Uwe Kleine-König <ukleinek@kernel.org>
This commit is contained in:
Michal Wilczynski 2025-10-16 15:38:02 +02:00 committed by Uwe Kleine-König
parent ce284f8820
commit 7b3dce814a
7 changed files with 146 additions and 0 deletions

View File

@ -20763,6 +20763,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

View File

@ -819,4 +819,16 @@ 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
depends on RUST
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

View File

@ -72,6 +72,7 @@
#include <linux/pm_opp.h>
#include <linux/poll.h>
#include <linux/property.h>
#include <linux/pwm.h>
#include <linux/random.h>
#include <linux/refcount.h>
#include <linux/regulator/consumer.h>

View File

@ -43,6 +43,7 @@
#include "poll.c"
#include "processor.c"
#include "property.c"
#include "pwm.c"
#include "rbtree.c"
#include "rcu.c"
#include "refcount.c"

20
rust/helpers/pwm.c Normal file
View File

@ -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);
}

View File

@ -129,6 +129,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;

102
rust/kernel/pwm.rs Normal file
View File

@ -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 periods 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
}
}