From nobody Sat Oct 11 12:08:54 2025 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 5DBB32C2AA2 for ; Tue, 10 Jun 2025 12:53:34 +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=1749560019; cv=none; b=ft/sEInmnmlVJA7R4NFmMxOM45KWpbfN9haSP7O9G8sZZSvex9h2SlpKflpNeNwWnPHMeUBeMz73gxk6KjQ4DOlwH6wDGYFJWBq1/ZPMKYplz8dS785eV+Etn7wSuEkOi8cK7GxF+0gsREAWxPLjCWYZOjMRZVwmTsc6KCBl+Cs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749560019; c=relaxed/simple; bh=uu1ICBCvjDVZqf0CSaxS0dFjXYTB7w47PthcJ/AVjh0=; h=From:Date:Subject:MIME-Version:Message-Id:In-Reply-To:To:Cc: Content-Type:References; b=dh0W7SlEYFIXUHuZD9zF8KmeGRkw9XJbG6G5cabGfJY2vRAIJVvWQMTAURDJ9Rjxxm2jpc2bW3cv781QC5sfnDRw8DPxBtZsLCxaEaIm5NFlioIqZuYZRU/wkL1XOMr4p75aQBICZEnrABlbJyXV8gvIvnoblNOiCgk+EmnMuCE= 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=BYdBzJls; 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="BYdBzJls" Received: from eucas1p2.samsung.com (unknown [182.198.249.207]) by mailout1.w1.samsung.com (KnoxPortal) with ESMTP id 20250610125332euoutp0197b7d3e0ee55b1b32a681012a8e63f59~Hr4-nPk2o1190611906euoutp01T for ; Tue, 10 Jun 2025 12:53:32 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.w1.samsung.com 20250610125332euoutp0197b7d3e0ee55b1b32a681012a8e63f59~Hr4-nPk2o1190611906euoutp01T DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1749560012; bh=SlvixoLgo7166EMoVtRGLcHTj2lOCLeU5xYXQU6Q38Y=; h=From:Date:Subject:In-Reply-To:To:Cc:References:From; b=BYdBzJlsPRs4bO4dkMhixuhjWkWEl6dsIi8QsANLaUb512pJykL/EDr9qDmoCEXZz RlBVzygWMgo1zsIV6J/HmO08/hjMGK2ipWzUw85p5sDKc/tlZ9BjwRjAO3KKaeHOAM Yzn0Y3Gl1scTed0/FnYhZPBMQ9+23D1UXTut5u/8= Received: from eusmtip1.samsung.com (unknown [203.254.199.221]) by eucas1p2.samsung.com (KnoxPortal) with ESMTPA id 20250610125332eucas1p2da441aa44760236527afc82495af95d1~Hr4_5eA5w1177811778eucas1p2z; Tue, 10 Jun 2025 12:53:32 +0000 (GMT) Received: from AMDC4942.eu.corp.samsungelectronics.net (unknown [106.210.136.40]) by eusmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250610125330eusmtip1020b3091342afe55161202b2809bcb66~Hr49rpmA_0451804518eusmtip1C; Tue, 10 Jun 2025 12:53:30 +0000 (GMT) From: Michal Wilczynski Date: Tue, 10 Jun 2025 14:52:49 +0200 Subject: [PATCH v2 1/7] rust: Add basic PWM abstractions Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Message-Id: <20250610-rust-next-pwm-working-fan-for-sending-v2-1-753e2955f110@samsung.com> In-Reply-To: <20250610-rust-next-pwm-working-fan-for-sending-v2-0-753e2955f110@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: 20250610125332eucas1p2da441aa44760236527afc82495af95d1 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" X-RootMTR: 20250610125332eucas1p2da441aa44760236527afc82495af95d1 X-EPHeader: CA X-CMS-RootMailID: 20250610125332eucas1p2da441aa44760236527afc82495af95d1 References: <20250610-rust-next-pwm-working-fan-for-sending-v2-0-753e2955f110@samsung.com> Introduce safe Rust abstractions for the Linux PWM subsystem. These abstractions provide ergonomic, lifetime managed wrappers around the core C data structures and functions, enabling the development of PWM chip drivers in safe Rust. This initial version provides the core building blocks for writing a PWM chip provider driver, with a focus on safety, resource management, and idiomatic Rust patterns. The main components are: Ownership and Lifetime Management: - The pwm::Chip type, an ARef managed wrapper for struct pwm_chip, correctly handles the object's lifetime by using the embedded struct device's reference counter. - A pwm::Registration RAII guard ensures that a call to register a chip (pwmchip_add) is always paired with a call to unregister it (pwmchip_remove), preventing resource leaks. Safe Type Wrappers: - Safe, idiomatic Rust types (Polarity, Waveform, State, Args, Device) are provided to abstract away the raw C structs and enums. The State wrapper holds its data by value, avoiding unnecessary heap allocations. Driver Operations (PwmOps): - A generic PwmOps trait allows drivers to implement the standard PWM operations. It uses an associated type (WfHw) for the driver's hardware specific waveform data, moving unsafe serialization logic into the abstraction layer. The trait exposes the modern waveform API (round_waveform_tohw, write_waveform, etc.) as well as the other standard kernel callbacks (get_state, request, apply). - A create_pwm_ops function generates a C-compatible vtable from a PwmOps implementor. This foundational layer is designed to be used by subsequent patches to implement specific PWM chip drivers in Rust. 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 | 864 ++++++++++++++++++++++++++++++++++++= ++++ 7 files changed, 907 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index f2668b81115cb85bc94275e9554330969989fabf..5589c0d2253bcb04e78d7b89ef6= ef0ed41121d77 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19990,6 +19990,12 @@ F: include/linux/pwm.h F: include/linux/pwm_backlight.h K: pwm_(config|apply_might_sleep|apply_atomic|ops) =20 +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..03c5a100a03e2acdccf8a46b9c7= 0b736b630bd3a 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. =20 + config RUST_PWM_ABSTRACTIONS + bool "Rust PWM abstractions support" + depends on RUST + depends on PWM=3Dy + help + This option enables the safe Rust abstraction layer for the PWM + subsystem. It provides idiomatic wrappers and traits necessary f= or + writing PWM controller drivers in Rust. + + The abstractions handle resource management (like memory and ref= erence + 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_helpe= r.h index 693cdd01f9290fa01375cf78cac0e5a90df74c6c..6fe7dd529577952bf7adb4fe052= 6b0d5fbd6f3bd 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 0f1b5d11598591bc62bb6439747211af164b76d6..73902d8bd87e93cb3bc3c501360= c37e29e8dde19 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -30,6 +30,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..d75c588863685d3990b525bb1b8= 4aa4bc35ac397 --- /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 d652c92633b82525f37e5cd8a040d268e0c191d1..d634593d5e8084049cc22daadb5= 019de139599fe 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -106,6 +106,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..b5839703c49ed7b9aa61723a7e5= 06f5cb4dd665d --- /dev/null +++ b/rust/kernel/pwm.rs @@ -0,0 +1,864 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2025 Samsung Electronics Co., Ltd. +// Author: Michal Wilczynski + +//! PWM (Pulse Width Modulator) abstractions. +//! +//! This module provides safe Rust abstractions for working with the Linux +//! kernel's PWM subsystem, leveraging types generated by `bindgen` +//! from `` and `drivers/pwm/core.c`. + +use crate::{ + bindings, + device::{self, Bound}, + error::{self, to_result, Result}, + prelude::*, + str::CStr, + types::{ARef, AlwaysRefCounted, ForeignOwnable, Opaque}, +}; +use core::{marker::PhantomData, mem::ManuallyDrop, ptr::NonNull}; + +/// Maximum size for the hardware-specific waveform representation buffer. +/// From C: #define WFHWSIZE 20 +pub const WFHW_MAX_SIZE: usize =3D 20; + +/// PWM polarity. Mirrors `enum pwm_polarity`. +#[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 From for Polarity { + fn from(polarity: bindings::pwm_polarity) -> Self { + match polarity { + bindings::pwm_polarity_PWM_POLARITY_NORMAL =3D> Polarity::Norm= al, + bindings::pwm_polarity_PWM_POLARITY_INVERSED =3D> Polarity::In= versed, + _ =3D> Polarity::Normal, + } + } +} + +impl From for bindings::pwm_polarity { + fn from(polarity: Polarity) -> Self { + match polarity { + Polarity::Normal =3D> bindings::pwm_polarity_PWM_POLARITY_NORM= AL, + Polarity::Inversed =3D> bindings::pwm_polarity_PWM_POLARITY_IN= VERSED, + } + } +} + +/// Represents a PWM waveform configuration. Mirrors struct pwm_waveform. +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] +pub struct Waveform { + /// Total duration of one complete PWM cycle, in nanoseconds. + pub period_length_ns: u64, + + /// Duration the PWM signal is in its "active" state during one period, + /// in nanoseconds. For a typical "normal" polarity configuration wher= e active is high, + /// this represents the high time of the signal. + pub duty_length_ns: u64, + + /// Time delay from the start of the period to the first active edge + /// of the duty cycle, in nanoseconds. For many simpler PWM configurat= ions, + /// this is 0, meaning the duty cycle's active phase starts at the beg= inning + /// of the period. + 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`). +#[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 poin= ter + /// 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` o= wns a copy. + unsafe { (*self.0.get()).period } + } + + /// Returns the polarity of the PWM signal. + pub fn polarity(&self) -> Polarity { + // 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 =3D unsafe { (*self.0.get()).polarity }; + Polarity::from(raw_polarity) + } +} + +/// Wrapper for PWM state (`struct pwm_state`). +#[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` v= alue. + 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 =3D 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 =3D 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 =3D enabled; + } + + /// Gets the polarity of the PWM signal. + pub fn polarity(&self) -> Polarity { + Polarity::from(self.0.polarity) + } + + /// Sets the polarity of the PWM signal. + pub fn set_polarity(&mut self, polarity: Polarity) { + self.0.polarity =3D polarity.into(); + } + + /// Returns `true` if the PWM signal is configured for power usage hin= t. + 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 =3D usage_power; + } +} + +/// Wrapper for a PWM device/channel (`struct pwm_device`). +#[repr(transparent)] +pub struct Device(Opaque); + +impl Device { + /// Creates a temporary `&mut Device` from a raw C pointer for use in = callbacks. + /// + /// It returns a mutable reference (`&mut Self`) because the underlyin= g C APIs + /// for PWM operations use non-const pointers (`struct pwm_device *`).= This + /// signals that the functions in the vtable are permitted to mutate t= he + /// device's state (e.g., by writing to hardware registers). Using `&m= ut` + /// allows the `PwmOps` trait to accurately model this behavior and le= verage + /// Rust's aliasing rules for greater safety. + /// + /// # Safety + /// The caller must ensure that `ptr` is a valid, non-null pointer to + /// `bindings::pwm_device` that is properly initialized. + /// The `pwm_device` must remain valid for the lifetime `'a`. + /// The caller must also ensure that Rust's aliasing rules are upheld. + pub(crate) unsafe fn from_ptr<'a>(ptr: *mut bindings::pwm_device) -> &= 'a mut Self { + // SAFETY: Caller guarantees `ptr` is valid and meets lifetime/ali= asing. + // `Self` is `#[repr(transparent)]`, so casting is valid. + unsafe { &mut *ptr.cast::() } + } + + /// Returns a raw pointer to the underlying `pwm_device`. + fn as_raw(&self) -> *mut bindings::pwm_device { + self.0.get() + } + + /// Gets the hardware PWM index for this device within its chip. + pub fn hwpwm(&self) -> u32 { + // SAFETY: `self.as_raw()` provides a valid pointer for `self`'s l= ifetime. + unsafe { (*self.as_raw()).hwpwm } + } + + /// Gets a reference to the parent `Chip` that this device belongs to. + pub fn chip(&self) -> &Chip { + // SAFETY: `self.as_raw()` provides a valid pointer. (*self.as_raw= ()).chip + // is assumed to be a valid pointer to `pwm_chip` managed by the k= ernel. + // Chip::from_ptr's safety conditions must be met. + unsafe { Chip::from_ptr((*self.as_raw()).chip) } + } + + /// Gets the label for this PWM device, if any. + pub fn label(&self) -> Option<&CStr> { + // SAFETY: self.as_raw() provides a valid pointer. + let label_ptr =3D unsafe { (*self.as_raw()).label }; + if label_ptr.is_null() { + None + } else { + // SAFETY: label_ptr is non-null and points to a C string + // managed by the kernel, valid for the lifetime of the PWM de= vice. + Some(unsafe { CStr::from_char_ptr(label_ptr) }) + } + } + + /// Gets a copy of the board-dependent arguments for this PWM device. + pub fn args(&self) -> Args { + // SAFETY: self.as_raw() gives a valid pointer to `pwm_device`. + // The `args` field is a valid `pwm_args` struct embedded within `= pwm_device`. + // `Args::from_c_ptr`'s safety conditions are met by providing thi= s pointer. + unsafe { Args::from_c_ptr(&(*self.as_raw()).args) } + } + + /// Gets a copy of the current state of this PWM device. + pub fn state(&self) -> State { + // SAFETY: `self.as_raw()` gives a valid pointer. `(*self.as_raw()= ).state` + // is a valid `pwm_state` struct. `State::from_c` copies this data. + State::from_c(unsafe { (*self.as_raw()).state }) + } + + /// Returns `true` if the PWM signal is currently enabled based on its= state. + pub fn is_enabled(&self) -> bool { + self.state().enabled() + } +} + +/// Wrapper for a PWM chip/controller (`struct pwm_chip`). +#[repr(transparent)] +pub struct Chip(Opaque); + +impl Chip { + /// Creates a temporary `&mut Chip` from a raw C pointer for use in ca= llbacks. + /// + /// It returns a mutable reference (`&mut Self`) because the underlyin= g C APIs + /// for PWM operations use non-const pointers (`struct pwm_chip *`). T= his + /// signals that the functions in the vtable are permitted to mutate t= he + /// chip's state (e.g., by calling `set_drvdata` or through operations= that + /// modify hardware registers). Using `&mut` is essential for these ca= ses. + /// + /// # Safety + /// The caller must ensure that `ptr` is a valid, non-null pointer to + /// `bindings::pwm_chip` that is properly initialized. + /// The `pwm_chip` must remain valid for the lifetime `'a`. + /// The caller must also ensure that Rust's aliasing rules are upheld. + pub(crate) unsafe fn from_ptr<'a>(ptr: *mut bindings::pwm_chip) -> &'a= mut Self { + // SAFETY: Caller guarantees `ptr` is valid and meets lifetime/ali= asing. + // `Self` is `#[repr(transparent)]`, so casting is valid. + unsafe { &mut *ptr.cast::() } + } + + /// Returns a raw pointer to the underlying `pwm_chip`. + pub(crate) fn as_raw(&self) -> *mut bindings::pwm_chip { + self.0.get() + } + + /// Gets the number of PWM channels (hardware PWMs) on this chip. + pub fn npwm(&self) -> u32 { + // SAFETY: `self.as_raw()` provides a valid pointer for `self`'s l= ifetime. + unsafe { (*self.as_raw()).npwm } + } + + /// Returns `true` if the chip supports atomic operations for configur= ation. + pub fn is_atomic(&self) -> bool { + // SAFETY: `self.as_raw()` provides a valid pointer for `self`'s l= ifetime. + unsafe { (*self.as_raw()).atomic } + } + + /// Returns a reference to the embedded `struct device` abstraction. + pub fn device(&self) -> &device::Device { + // SAFETY: `self.as_raw()` provides a valid pointer to `bindings::= pwm_chip`. + // The `dev` field is an instance of `bindings::device` embedded w= ithin `pwm_chip`. + // Taking a pointer to this embedded field is valid. + // `device::Device` is `#[repr(transparent)]`. + // The lifetime of the returned reference is tied to `self`. + let dev_field_ptr =3D unsafe { core::ptr::addr_of!((*self.as_raw()= ).dev) }; + // SAFETY: `dev_field_ptr` is a valid pointer to `bindings::device= `. + // Casting and dereferencing is safe due to `repr(transparent)` an= d lifetime. + unsafe { &*(dev_field_ptr.cast::()) } + } + + /// Returns a reference to the parent device of this PWM chip's device. + pub fn parent_device(&self) -> Option<&device::Device> { + self.device().parent() + } + + /// Gets the *typed* driver-specific data associated with this chip's = embedded device. + pub fn drvdata(&self) -> Option<&T> { + // SAFETY: `self.as_raw()` gives a valid pwm_chip pointer. + // `bindings::pwmchip_get_drvdata` is the C function to retrieve d= river data. + let ptr =3D unsafe { bindings::pwmchip_get_drvdata(self.as_raw()) = }; + if ptr.is_null() { + None + } else { + // SAFETY: `ptr` is non-null. Caller ensures `T` is the correc= t type. + // Lifetime of data is managed by the driver that set it. + unsafe { Some(&*(ptr.cast::())) } + } + } + + /// Sets the *typed* driver-specific data associated with this chip's = embedded device. + pub fn set_drvdata(&self, data: T) { + // SAFETY: `self.as_raw()` gives a valid pwm_chip pointer. + // `bindings::pwmchip_set_drvdata` is the C function to set driver= data. + // `data.into_foreign()` provides a valid `*mut c_void`. + unsafe { bindings::pwmchip_set_drvdata(self.as_raw(), data.into_fo= reign().cast()) } + } + + /// Allocates and wraps a PWM chip using `bindings::pwmchip_alloc`. + /// + /// Returns an `ARef` managing the chip's lifetime via refcounti= ng + /// on its embedded `struct device`. + pub fn new(parent_dev: &device::Device, npwm: u32, sizeof_priv: usize)= -> Result> { + // SAFETY: `parent_device_for_dev_field.as_raw()` is valid. + // `bindings::pwmchip_alloc` returns a valid `*mut bindings::pwm_c= hip` (refcount 1) + // or an ERR_PTR. + let c_chip_ptr_raw =3D + unsafe { bindings::pwmchip_alloc(parent_dev.as_raw(), npwm, si= zeof_priv) }; + + let c_chip_ptr: *mut bindings::pwm_chip =3D error::from_err_ptr(c_= chip_ptr_raw)?; + + // Cast the `*mut bindings::pwm_chip` to `*mut Chip`. This is vali= d because + // `Chip` is `repr(transparent)` over `Opaque`= , and + // `Opaque` is `repr(transparent)` over `T`. + let chip_ptr_as_self =3D c_chip_ptr.cast::(); + + // SAFETY: `chip_ptr_as_self` points to a valid `Chip` (layout-com= patible with + // `bindings::pwm_chip`) whose embedded device has refcount 1. + // `ARef::from_raw` takes this pointer and manages it via `AlwaysR= efCounted`. + Ok(unsafe { ARef::from_raw(NonNull::new_unchecked(chip_ptr_as_self= )) }) + } +} + +// SAFETY: Implements refcounting for `Chip` using the embedded `struct de= vice`. +unsafe impl AlwaysRefCounted for Chip { + #[inline] + fn inc_ref(&self) { + // SAFETY: `self.0.get()` points to a valid `pwm_chip` because `se= lf` exists. + // The embedded `dev` is valid. `get_device` increments its refcou= nt. + unsafe { + bindings::get_device(core::ptr::addr_of_mut!((*self.0.get()).d= ev)); + } + } + + #[inline] + unsafe fn dec_ref(obj: NonNull) { + let c_chip_ptr =3D obj.cast::().as_ptr(); + + // SAFETY: `obj` is a valid pointer to a `Chip` (and thus `binding= s::pwm_chip`) + // with a non-zero refcount. `put_device` handles decrement and fi= nal release. + unsafe { + bindings::put_device(core::ptr::addr_of_mut!((*c_chip_ptr).dev= )); + } + } +} + +// SAFETY: `Chip` is a wrapper around `*mut bindings::pwm_chip`. The under= lying C +// structure's state is managed and synchronized by the kernel's device mo= del +// and PWM core locking mechanisms. Therefore, it is safe to move the `Chi= p` +// wrapper (and the pointer it contains) across threads. +unsafe impl Send for Chip {} + +// SAFETY: It is safe for multiple threads to have shared access (`&Chip`)= because +// the `Chip` data is immutable from the Rust side without holding the app= ropriate +// kernel locks, which the C core is responsible for. Any interior mutabil= ity is +// handled and synchronized by the C kernel code. +unsafe impl Sync for Chip {} + +/// Manages the registration of a PWM chip, ensuring `pwmchip_remove` is c= alled on drop. +pub struct Registration { + chip: ManuallyDrop>, +} + +impl Registration { + /// Registers a PWM chip (obtained via `Chip::new`) with the PWM subsy= stem. + /// + /// Takes an `ARef`. On `Drop` of the returned `Registration` ob= ject, + /// `pwmchip_remove` is called for the chip. + pub fn new(chip: ARef, ops_vtable: &'static PwmOpsVTable) -> Res= ult { + // Get the raw C pointer from ARef. + let c_chip_ptr =3D chip.as_raw().cast::(); + + // SAFETY: `c_chip_ptr` is valid (guaranteed by ARef existing). + // `ops_vtable.as_raw()` provides a valid `*const bindings::pwm_op= s`. + // `bindings::__pwmchip_add` preconditions (valid pointers, ops se= t on chip) are met. + unsafe { + (*c_chip_ptr).ops =3D ops_vtable.as_raw(); + to_result(bindings::__pwmchip_add(c_chip_ptr, core::ptr::null_= mut()))?; + } + Ok(Registration { + chip: ManuallyDrop::new(chip), + }) + } +} + +impl Drop for Registration { + fn drop(&mut self) { + let chip =3D &**self.chip; + let chip_raw: *mut bindings::pwm_chip =3D chip.as_raw(); + + // SAFETY: `chip_raw` points to a chip that was successfully regis= tered via `Self::new`. + // `bindings::pwmchip_remove` is the correct C function to unregis= ter it. + unsafe { + bindings::pwmchip_remove(chip_raw); + ManuallyDrop::drop(&mut self.chip); // Drops the ARef + } + } +} + +/// Trait defining the operations for a PWM driver. +pub trait PwmOps: 'static + Sized { + /// The driver-specific hardware representation of a waveform. + /// This type must be `Copy`, `Default`, and fit within `WFHW_MAX_SIZE= `. + type WfHw: Copy + Default; + + /// Optional hook to atomically apply a new PWM config. + fn apply( + _chip: &mut Chip, + _pwm: &mut Device, + _state: &State, + _parent_dev: &device::Device, + ) -> Result { + Err(ENOTSUPP) + } + + /// Optional hook for when a PWM device is requested. + fn request(_chip: &mut Chip, _pwm: &mut Device, _parent_dev: &device::= Device) -> Result { + Ok(()) + } + + /// Optional hook for when a PWM device is freed. + fn free(_chip: &mut Chip, _pwm: &mut Device, _parent_dev: &device::Dev= ice) {} + + /// Optional hook for capturing a PWM signal. + fn capture( + _chip: &mut Chip, + _pwm: &mut Device, + _result: &mut bindings::pwm_capture, + _timeout: usize, + _parent_dev: &device::Device, + ) -> Result { + Err(ENOTSUPP) + } + + /// Optional hook to get the current hardware state. + fn get_state( + _chip: &mut Chip, + _pwm: &mut Device, + _state: &mut State, + _parent_dev: &device::Device, + ) -> Result { + Err(ENOTSUPP) + } + + /// Convert a generic waveform to the hardware-specific representation. + /// This is typically a pure calculation and does not perform I/O. + fn round_waveform_tohw( + _chip: &mut Chip, + _pwm: &mut Device, + _wf: &Waveform, + ) -> Result<(i32, Self::WfHw)> { + Err(ENOTSUPP) + } + + /// Convert a hardware-specific representation back to a generic wavef= orm. + /// This is typically a pure calculation and does not perform I/O. + fn round_waveform_fromhw( + _chip: &mut Chip, + _pwm: &Device, + _wfhw: &Self::WfHw, + _wf: &mut Waveform, + ) -> Result { + Err(ENOTSUPP) + } + + /// Read the current hardware configuration into the hardware-specific= representation. + fn read_waveform( + _chip: &mut Chip, + _pwm: &mut Device, + _parent_dev: &device::Device, + ) -> Result { + Err(ENOTSUPP) + } + + /// Write a hardware-specific waveform configuration to the hardware. + fn write_waveform( + _chip: &mut Chip, + _pwm: &mut Device, + _wfhw: &Self::WfHw, + _parent_dev: &device::Device, + ) -> Result { + Err(ENOTSUPP) + } +} +/// Bridges Rust `PwmOps` to the C `pwm_ops` vtable. +struct Adapter { + _p: PhantomData, +} + +impl Adapter { + /// # Safety + /// `wfhw_ptr` must be valid for writes of `size_of::()` byte= s. + unsafe fn serialize_wfhw(wfhw: &T::WfHw, wfhw_ptr: *mut core::ffi::c_v= oid) -> Result { + let size =3D core::mem::size_of::(); + if size > WFHW_MAX_SIZE { + return Err(EINVAL); + } + + // SAFETY: The caller ensures `wfhw_ptr` is valid for `size` bytes. + unsafe { + core::ptr::copy_nonoverlapping(wfhw as *const _ as *const u8, = wfhw_ptr.cast(), size); + } + + Ok(()) + } + + /// # Safety + /// `wfhw_ptr` must be valid for reads of `size_of::()` bytes. + unsafe fn deserialize_wfhw(wfhw_ptr: *const core::ffi::c_void) -> Resu= lt { + let size =3D core::mem::size_of::(); + if size > WFHW_MAX_SIZE { + return Err(EINVAL); + } + + let mut wfhw =3D T::WfHw::default(); + // SAFETY: The caller ensures `wfhw_ptr` is valid for `size` bytes. + unsafe { + core::ptr::copy_nonoverlapping(wfhw_ptr.cast(), &mut wfhw as *= mut _ as *mut u8, size); + } + + Ok(wfhw) + } + + /// # Safety + /// C-callback. Pointers from C must be valid. + unsafe extern "C" fn apply_callback( + c: *mut bindings::pwm_chip, + p: *mut bindings::pwm_device, + s: *const bindings::pwm_state, + ) -> i32 { + // SAFETY: This block relies on the function's safety contract: th= e C caller + // provides valid pointers. `Chip::from_ptr` and `Device::from_ptr= ` are `unsafe fn` + // whose preconditions are met by this contract. + let (chip, pwm) =3D unsafe { (Chip::from_ptr(c), Device::from_ptr(= p)) }; + let parent_dev =3D match chip.parent_device() { + Some(dev) =3D> dev, + None =3D> { + return EINVAL.to_errno(); + } + }; + + // SAFETY: The PWM core guarantees callbacks only happen on a live= , bound device. + let bound_parent =3D + unsafe { &*(parent_dev as *const device::Device as *const devi= ce::Device) }; + + // SAFETY: The state provided by the callback is guaranteed to be = valid + let state =3D State::from_c(unsafe { *s }); + match T::apply(chip, pwm, &state, bound_parent) { + Ok(()) =3D> 0, + Err(e) =3D> e.to_errno(), + } + } + + /// # Safety + /// C-callback. Pointers from C must be valid. + unsafe extern "C" fn request_callback( + c: *mut bindings::pwm_chip, + p: *mut bindings::pwm_device, + ) -> i32 { + // SAFETY: PWM core guarentees `c` and `p` are valid pointers. + let (chip, pwm) =3D unsafe { (Chip::from_ptr(c), Device::from_ptr(= p)) }; + let parent_dev =3D match chip.parent_device() { + Some(dev) =3D> dev, + None =3D> { + return EINVAL.to_errno(); + } + }; + + let bound_parent =3D + // SAFETY: The PWM core guarantees the device is bound during callbacks. + unsafe { &*(parent_dev as *const device::Device as *const devi= ce::Device) }; + match T::request(chip, pwm, bound_parent) { + Ok(()) =3D> 0, + Err(e) =3D> e.to_errno(), + } + } + + /// # Safety + /// C-callback. Pointers from C must be valid. + unsafe extern "C" fn free_callback(c: *mut bindings::pwm_chip, p: *mut= bindings::pwm_device) { + // SAFETY: Relies on the function's contract that `c` and `p` are = valid pointers. + let (chip, pwm) =3D unsafe { (Chip::from_ptr(c), Device::from_ptr(= p)) }; + let parent_dev =3D match chip.parent_device() { + Some(dev) =3D> dev, + None =3D> { + return; + } + }; + + let bound_parent =3D + // SAFETY: The PWM core guarantees the device is bound during callbacks. + unsafe { &*(parent_dev as *const device::Device as *const devi= ce::Device) }; + T::free(chip, pwm, bound_parent); + } + + /// # Safety + /// C-callback. Pointers from C must be valid. + unsafe extern "C" fn capture_callback( + c: *mut bindings::pwm_chip, + p: *mut bindings::pwm_device, + res: *mut bindings::pwm_capture, + timeout: usize, + ) -> i32 { + // SAFETY: Relies on the function's contract that `c` and `p` are = valid pointers. + let (chip, pwm, result) =3D unsafe { (Chip::from_ptr(c), Device::f= rom_ptr(p), &mut *res) }; + let parent_dev =3D match chip.parent_device() { + Some(dev) =3D> dev, + None =3D> { + return EINVAL.to_errno(); + } + }; + + let bound_parent =3D + // SAFETY: The PWM core guarantees the device is bound during cal= lbacks. + unsafe { &*(parent_dev as *const device::Device as *const devi= ce::Device) }; + match T::capture(chip, pwm, result, timeout, bound_parent) { + Ok(()) =3D> 0, + Err(e) =3D> e.to_errno(), + } + } + + /// # Safety + /// C-callback. Pointers from C must be valid. + unsafe extern "C" fn get_state_callback( + c: *mut bindings::pwm_chip, + p: *mut bindings::pwm_device, + s: *mut bindings::pwm_state, + ) -> i32 { + // SAFETY: Relies on the function's contract that `c` and `p` are = valid pointers. + let (chip, pwm) =3D unsafe { (Chip::from_ptr(c), Device::from_ptr(= p)) }; + let parent_dev =3D match chip.parent_device() { + Some(dev) =3D> dev, + None =3D> { + return EINVAL.to_errno(); + } + }; + let bound_parent =3D + // SAFETY: The PWM core guarantees the device is bound during callbacks. + unsafe { &*(parent_dev as *const device::Device as *const devi= ce::Device) }; + let mut rust_state =3D State::new(); + match T::get_state(chip, pwm, &mut rust_state, bound_parent) { + Ok(()) =3D> { + // SAFETY: `s` is guaranteed valid by the C caller. + unsafe { + *s =3D rust_state.0; + }; + 0 + } + Err(e) =3D> e.to_errno(), + } + } + + /// # Safety + /// C-callback. Pointers from C must be valid. + unsafe extern "C" fn round_waveform_tohw_callback( + c: *mut bindings::pwm_chip, + p: *mut bindings::pwm_device, + w: *const bindings::pwm_waveform, + wh: *mut core::ffi::c_void, + ) -> i32 { + // SAFETY: Relies on the function's contract that `c` and `p` are = valid pointers. + let (chip, pwm, wf) =3D + unsafe { (Chip::from_ptr(c), Device::from_ptr(p), Waveform::fr= om(*w)) }; + match T::round_waveform_tohw(chip, pwm, &wf) { + Ok((status, wfhw)) =3D> { + // SAFETY: `wh` is valid per this function's safety contra= ct. + if unsafe { Self::serialize_wfhw(&wfhw, wh) }.is_err() { + return EINVAL.to_errno(); + } + status + } + Err(e) =3D> e.to_errno(), + } + } + + /// # Safety + /// C-callback. Pointers from C must be valid. + unsafe extern "C" fn round_waveform_fromhw_callback( + c: *mut bindings::pwm_chip, + p: *mut bindings::pwm_device, + wh: *const core::ffi::c_void, + w: *mut bindings::pwm_waveform, + ) -> i32 { + // SAFETY: Relies on the function's contract that `c` and `p` are = valid pointers. + let (chip, pwm) =3D unsafe { (Chip::from_ptr(c), Device::from_ptr(= p)) }; + // SAFETY: `deserialize_wfhw`'s safety contract is met by this fun= ction's contract. + let wfhw =3D match unsafe { Self::deserialize_wfhw(wh) } { + Ok(v) =3D> v, + Err(e) =3D> return e.to_errno(), + }; + + let mut rust_wf =3D Waveform::default(); + match T::round_waveform_fromhw(chip, pwm, &wfhw, &mut rust_wf) { + Ok(ret) =3D> { + // SAFETY: `w` is guaranteed valid by the C caller. + unsafe { + *w =3D rust_wf.into(); + }; + ret + } + Err(e) =3D> e.to_errno(), + } + } + + /// # Safety + /// C-callback. Pointers from C must be valid. + unsafe extern "C" fn read_waveform_callback( + c: *mut bindings::pwm_chip, + p: *mut bindings::pwm_device, + wh: *mut core::ffi::c_void, + ) -> i32 { + // SAFETY: Relies on the function's contract that `c` and `p` are = valid pointers. + let (chip, pwm) =3D unsafe { (Chip::from_ptr(c), Device::from_ptr(= p)) }; + let parent_dev =3D match chip.parent_device() { + Some(dev) =3D> dev, + None =3D> { + return EINVAL.to_errno(); + } + }; + + let bound_parent =3D + // SAFETY: The PWM core guarantees the device is bound during callbacks. + unsafe { &*(parent_dev as *const device::Device as *const devi= ce::Device) }; + match T::read_waveform(chip, pwm, bound_parent) { + // SAFETY: `wh` is valid per this function's safety contract. + Ok(wfhw) =3D> match unsafe { Self::serialize_wfhw(&wfhw, wh) }= { + Ok(()) =3D> 0, + Err(e) =3D> e.to_errno(), + }, + Err(e) =3D> e.to_errno(), + } + } + + /// # Safety + /// C-callback. Pointers from C must be valid. + unsafe extern "C" fn write_waveform_callback( + c: *mut bindings::pwm_chip, + p: *mut bindings::pwm_device, + wh: *const core::ffi::c_void, + ) -> i32 { + // SAFETY: Relies on the function's contract that `c` and `p` are = valid pointers. + let (chip, pwm) =3D unsafe { (Chip::from_ptr(c), Device::from_ptr(= p)) }; + let parent_dev =3D match chip.parent_device() { + Some(dev) =3D> dev, + None =3D> { + return EINVAL.to_errno(); + } + }; + + let bound_parent =3D + // SAFETY: The PWM core guarantees the device is bound during cal= lbacks. + unsafe { &*(parent_dev as *const device::Device as *const devi= ce::Device) }; + // SAFETY: `wh` is valid per this function's safety contract. + let wfhw =3D match unsafe { Self::deserialize_wfhw(wh) } { + Ok(v) =3D> v, + Err(e) =3D> return e.to_errno(), + }; + match T::write_waveform(chip, pwm, &wfhw, bound_parent) { + Ok(()) =3D> 0, + Err(e) =3D> e.to_errno(), + } + } +} +/// VTable structure wrapper for PWM operations. Mirrors `struct pwm_ops`. +#[repr(transparent)] +pub struct PwmOpsVTable(Opaque); + +// SAFETY: PwmOpsVTable is Send. The vtable contains only function pointers +// and a size, which are simple data types that can be safely moved across +// threads. The thread-safety of calling these functions is handled by the +// kernel's locking mechanisms. +unsafe impl Send for PwmOpsVTable {} +// SAFETY: PwmOpsVTable is Sync. The vtable is immutable after it is creat= ed, +// so it can be safely referenced and accessed concurrently by multiple th= reads +// e.g. to read the function pointers. +unsafe impl Sync for PwmOpsVTable {} + +impl PwmOpsVTable { + /// Returns a raw pointer to the underlying `pwm_ops` struct. + pub(crate) fn as_raw(&self) -> *const bindings::pwm_ops { + self.0.get() + } +} + +/// Creates a PWM operations vtable for a type `T` that implements `PwmOps= `. +/// +/// This is used to bridge Rust trait implementations to the C `struct pwm= _ops` +/// expected by the kernel. +pub const fn create_pwm_ops() -> PwmOpsVTable { + // SAFETY: `core::mem::zeroed()` is unsafe. For `pwm_ops`, all fields = are + // `Option` or data, so a zeroed pattern (None/0) = is valid initially. + let mut ops: bindings::pwm_ops =3D unsafe { core::mem::zeroed() }; + + ops.apply =3D Some(Adapter::::apply_callback); + ops.request =3D Some(Adapter::::request_callback); + ops.free =3D Some(Adapter::::free_callback); + ops.capture =3D Some(Adapter::::capture_callback); + ops.get_state =3D Some(Adapter::::get_state_callback); + + ops.round_waveform_tohw =3D Some(Adapter::::round_waveform_tohw_cal= lback); + ops.round_waveform_fromhw =3D Some(Adapter::::round_waveform_fromhw= _callback); + ops.read_waveform =3D Some(Adapter::::read_waveform_callback); + ops.write_waveform =3D Some(Adapter::::write_waveform_callback); + ops.sizeof_wfhw =3D core::mem::size_of::(); + + PwmOpsVTable(Opaque::new(ops)) +} --=20 2.34.1 From nobody Sat Oct 11 12:08:54 2025 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 516562D4B71 for ; Tue, 10 Jun 2025 12:53:38 +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=1749560021; cv=none; b=uhiqW8+OpFPvhKyqiJUm5AxFvv3wIfbVeemMzhKDnNqDIJBExwssxt6gnmYoufPPSORg93YkjxYWJKfx14JD6dRh5eKwcM6mhtEkGL9I/IJp7aVj31ZCIsJ32zghSP+w8eNQEjyCLq3+7cqHpKcOBrN2qNzFLRklEAztKUGiUMQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749560021; c=relaxed/simple; bh=VF5q+wBp9kye6JQLhwc0n05CcCw9LlKgrG19B7rxH5Y=; h=From:Date:Subject:MIME-Version:Message-Id:In-Reply-To:To:Cc: Content-Type:References; b=uJw5LLDUI0nBtqhqqUWKuboI5D8sTt6Tz53SAtdX3F7GJfiIf+23WhBGnGrFMlQS7Mqrk5s00ZAZQjf9bd80tTSwXfliKTH0iT2Su5HvkDQdyQuTFMYajQSjISQlVxDRqQCflqZnmBbuHcvW/WeP+tqMun/irn+3MMZpzBmcOXQ= 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=O7f2QURT; 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="O7f2QURT" Received: from eucas1p1.samsung.com (unknown [182.198.249.206]) by mailout1.w1.samsung.com (KnoxPortal) with ESMTP id 20250610125334euoutp01a8c6e733dff1d49fdbac711cf032d393~Hr5A3ZVhl1190611906euoutp01X for ; Tue, 10 Jun 2025 12:53:34 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.w1.samsung.com 20250610125334euoutp01a8c6e733dff1d49fdbac711cf032d393~Hr5A3ZVhl1190611906euoutp01X DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1749560014; bh=XnU++sOJANJB3QM+54tjoJboUyue4aR10yImzgN9Ir0=; h=From:Date:Subject:In-Reply-To:To:Cc:References:From; b=O7f2QURTPGaH/+zJRRJBq6EnMgQyJqep+z4jwzQnCufOvHJVCASyfYb7TLCwLAWoP XGLgGNweTFUsG6tF/qM2zGg8kRlfK8Og8Ivl0lc0Pc4zwR9x3NShbd8k6IcRcC3Uzt nAfbMmbmjrJ9co98lXBtDW+FbbatBqnjE5G49xFM= Received: from eusmtip1.samsung.com (unknown [203.254.199.221]) by eucas1p1.samsung.com (KnoxPortal) with ESMTPA id 20250610125333eucas1p16126b64a0f447a5e9a5ad553d9d7d79d~Hr5ALMeiU0875208752eucas1p16; Tue, 10 Jun 2025 12:53:33 +0000 (GMT) Received: from AMDC4942.eu.corp.samsungelectronics.net (unknown [106.210.136.40]) by eusmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250610125332eusmtip1870d7d868d8f899ab0bd39780d123beb~Hr4_9rhV10363103631eusmtip1P; Tue, 10 Jun 2025 12:53:32 +0000 (GMT) From: Michal Wilczynski Date: Tue, 10 Jun 2025 14:52:50 +0200 Subject: [PATCH v2 2/7] pwm: Add Rust driver for T-HEAD TH1520 SoC Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Message-Id: <20250610-rust-next-pwm-working-fan-for-sending-v2-2-753e2955f110@samsung.com> In-Reply-To: <20250610-rust-next-pwm-working-fan-for-sending-v2-0-753e2955f110@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: 20250610125333eucas1p16126b64a0f447a5e9a5ad553d9d7d79d X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" X-RootMTR: 20250610125333eucas1p16126b64a0f447a5e9a5ad553d9d7d79d X-EPHeader: CA X-CMS-RootMailID: 20250610125333eucas1p16126b64a0f447a5e9a5ad553d9d7d79d References: <20250610-rust-next-pwm-working-fan-for-sending-v2-0-753e2955f110@samsung.com> Introduce a PWM driver for the T-HEAD TH1520 SoC, written in Rust and utilizing the safe PWM abstractions from the preceding commit. The driver implements the pwm::PwmOps trait using the modern waveform API (round_waveform_tohw, write_waveform, etc.) to support configuration of period, duty cycle, and polarity for the TH1520's PWM channels. Resource management is handled using idiomatic Rust patterns. The PWM chip object is allocated via pwm::Chip::new and its registration with the PWM core is managed by the pwm::Registration RAII guard. This ensures pwmchip_remove is always called when the driver unbinds, preventing resource leaks. Device managed resources are used for the MMIO region, and the clock lifecycle is correctly managed in the driver's private data Drop implementation. The driver's core logic is written entirely in safe Rust, with no unsafe blocks. Signed-off-by: Michal Wilczynski --- MAINTAINERS | 1 + drivers/pwm/Kconfig | 10 ++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm_th1520.rs | 287 ++++++++++++++++++++++++++++++++++++++++++= ++++ 4 files changed, 299 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 5589c0d2253bcb04e78d7b89ef6ef0ed41121d77..966ce515c8bfefdff1975bb716a= 267435ec0feae 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21319,6 +21319,7 @@ F: drivers/mailbox/mailbox-th1520.c F: drivers/net/ethernet/stmicro/stmmac/dwmac-thead.c F: drivers/pinctrl/pinctrl-th1520.c F: drivers/pmdomain/thead/ +F: drivers/pwm/pwm_th1520.rs F: drivers/reset/reset-th1520.c F: include/dt-bindings/clock/thead,th1520-clk-ap.h F: include/dt-bindings/power/thead,th1520-power.h diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 03c5a100a03e2acdccf8a46b9c70b736b630bd3a..be05658a568cb9156ef623caf54= ff1aaba898d01 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -719,6 +719,16 @@ config PWM_TEGRA To compile this driver as a module, choose M here: the module will be called pwm-tegra. =20 +config PWM_TH1520_RUST + tristate "TH1520 PWM support (Rust)" + depends on RUST_PWM_ABSTRACTIONS + help + This option enables the driver for the PWM controller found on the + T-HEAD TH1520 SoC. This driver is written in Rust. + + To compile this driver as a module, choose M here; the module + will be called pwm-th1520. If you are unsure, say N. + config PWM_TIECAP tristate "ECAP PWM support" depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX || ARCH_KEYSTONE || ARCH_= K3 || COMPILE_TEST diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index 96160f4257fcb0e0951581af0090615c0edf5260..d41b1940df903ba2036d8e3ed93= efcd66834b7ab 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -73,3 +73,4 @@ obj-$(CONFIG_PWM_TWL_LED) +=3D pwm-twl-led.o obj-$(CONFIG_PWM_VISCONTI) +=3D pwm-visconti.o obj-$(CONFIG_PWM_VT8500) +=3D pwm-vt8500.o obj-$(CONFIG_PWM_XILINX) +=3D pwm-xilinx.o +obj-$(CONFIG_PWM_TH1520_RUST) +=3D pwm_th1520.o diff --git a/drivers/pwm/pwm_th1520.rs b/drivers/pwm/pwm_th1520.rs new file mode 100644 index 0000000000000000000000000000000000000000..9e43474f5123b51c49035d71219= 303a606c20a5a --- /dev/null +++ b/drivers/pwm/pwm_th1520.rs @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2025 Samsung Electronics Co., Ltd. +// Author: Michal Wilczynski + +//! Rust T-HEAD TH1520 PWM driver + +use core::ops::Deref; +use kernel::{ + c_str, + clk::Clk, + device::{Bound, Core, Device}, + devres, + error::{code::*, Result}, + io::mem::IoMem, + math::KernelMathExt, + of, platform, + prelude::*, + pwm, time, +}; + +const MAX_PWM_NUM: u32 =3D 6; + +// Register offsets +const fn th1520_pwm_chn_base(n: u32) -> usize { + (n * 0x20) as usize +} +const fn th1520_pwm_ctrl(n: u32) -> usize { + th1520_pwm_chn_base(n) +} +const fn th1520_pwm_per(n: u32) -> usize { + th1520_pwm_chn_base(n) + 0x08 +} +const fn th1520_pwm_fp(n: u32) -> usize { + th1520_pwm_chn_base(n) + 0x0c +} + +// Control register bits +const PWM_START: u32 =3D 1 << 0; +const PWM_CFG_UPDATE: u32 =3D 1 << 2; +const PWM_CONTINUOUS_MODE: u32 =3D 1 << 5; +const PWM_FPOUT: u32 =3D 1 << 8; + +const TH1520_PWM_REG_SIZE: usize =3D 0xB0; + +/// Hardware-specific waveform representation for TH1520. +#[derive(Copy, Clone, Debug, Default)] +struct Th1520WfHw { + period_cycles: u32, + duty_cycles: u32, + ctrl_val: u32, + enabled: bool, +} + +/// The driver's private data struct. It holds all necessary devres-manage= d resources. +struct Th1520PwmDriverData { + iomem: devres::Devres>, + clk: Clk, +} + +impl pwm::PwmOps for Th1520PwmDriverData { + type WfHw =3D Th1520WfHw; + + fn get_state( + chip: &mut pwm::Chip, + pwm: &mut pwm::Device, + state: &mut pwm::State, + parent_dev: &Device, + ) -> Result { + let data: &Self =3D chip.drvdata().ok_or(EINVAL)?; + let hwpwm =3D pwm.hwpwm(); + let iomem_guard =3D data.iomem.access(parent_dev)?; + let iomap =3D iomem_guard.deref(); + let ctrl =3D iomap.read32(th1520_pwm_ctrl(hwpwm)); + let period_cycles =3D iomap.read32(th1520_pwm_per(hwpwm)); + let duty_cycles =3D iomap.read32(th1520_pwm_fp(hwpwm)); + + state.set_enabled(duty_cycles !=3D 0); + + let rate_hz =3D data.clk.rate().as_hz(); + let period_ns =3D (period_cycles as u64) + .mul_div(time::NSEC_PER_SEC as u64, rate_hz as u64) + .unwrap_or(0); + state.set_period(period_ns); + + let duty_ns =3D (duty_cycles as u64) + .mul_div(time::NSEC_PER_SEC as u64, rate_hz as u64) + .unwrap_or(0); + state.set_duty_cycle(duty_ns); + + if (ctrl & PWM_FPOUT) !=3D 0 { + state.set_polarity(pwm::Polarity::Normal); + } else { + state.set_polarity(pwm::Polarity::Inversed); + } + + Ok(()) + } + + fn round_waveform_tohw( + chip: &mut pwm::Chip, + pwm: &mut pwm::Device, + wf: &pwm::Waveform, + ) -> Result<(i32, Self::WfHw)> { + let data: &Self =3D chip.drvdata().ok_or(EINVAL)?; + let hwpwm =3D pwm.hwpwm(); + + if wf.duty_offset_ns !=3D 0 { + dev_err!(chip.device(), "PWM-{}: Duty offset not supported\n",= hwpwm); + return Err(ENOTSUPP); + } + + if wf.period_length_ns =3D=3D 0 { + return Ok(( + 0, + Th1520WfHw { + enabled: false, + ..Default::default() + }, + )); + } + + let rate_hz =3D data.clk.rate().as_hz(); + + let period_cycles =3D wf + .period_length_ns + .mul_div(rate_hz as u64, time::NSEC_PER_SEC as u64) + .ok_or(EINVAL)?; + if period_cycles > u32::MAX as u64 { + dev_err!( + chip.device(), + "PWM-{}: Calculated period {} cycles is out of range\n", + hwpwm, + period_cycles + ); + return Err(EINVAL); + } + + let duty_cycles =3D wf + .duty_length_ns + .mul_div(rate_hz as u64, time::NSEC_PER_SEC as u64) + .ok_or(EINVAL)?; + if duty_cycles > period_cycles { + dev_err!( + chip.device(), + "PWM-{}: Duty {}ns > period {}ns\n", + hwpwm, + wf.duty_length_ns, + wf.period_length_ns + ); + return Err(EINVAL); + } + + let mut ctrl_val =3D PWM_CONTINUOUS_MODE; + if pwm.state().polarity() =3D=3D pwm::Polarity::Normal { + ctrl_val |=3D PWM_FPOUT; + } + + let wfhw =3D Th1520WfHw { + period_cycles: period_cycles as u32, + duty_cycles: duty_cycles as u32, + ctrl_val, + enabled: true, + }; + + dev_dbg!( + chip.device(), + "wfhw -- Period: {}, Duty: {}, Ctrl: 0x{:x}\n", + wfhw.period_cycles, + wfhw.duty_cycles, + wfhw.ctrl_val + ); + Ok((0, wfhw)) + } + + fn write_waveform( + chip: &mut pwm::Chip, + pwm: &mut pwm::Device, + wfhw: &Self::WfHw, + parent_dev: &Device, + ) -> Result { + let data: &Self =3D chip.drvdata().ok_or(EINVAL)?; + let hwpwm =3D pwm.hwpwm(); + let iomem_guard =3D data.iomem.access(parent_dev)?; + let iomap =3D iomem_guard.deref(); + let was_enabled =3D pwm.state().enabled(); + + if !wfhw.enabled { + if was_enabled { + let mut ctrl =3D iomap.read32(th1520_pwm_ctrl(hwpwm)); + + ctrl &=3D !PWM_CFG_UPDATE; + + iomap.write32(ctrl, th1520_pwm_ctrl(hwpwm)); + iomap.write32(0, th1520_pwm_fp(hwpwm)); + iomap.write32(ctrl | PWM_CFG_UPDATE, th1520_pwm_ctrl(hwpwm= )); + } + return Ok(()); + } + + let ctrl =3D wfhw.ctrl_val & !PWM_CFG_UPDATE; + + iomap.write32(ctrl, th1520_pwm_ctrl(hwpwm)); + iomap.write32(wfhw.period_cycles, th1520_pwm_per(hwpwm)); + iomap.write32(wfhw.duty_cycles, th1520_pwm_fp(hwpwm)); + iomap.write32(wfhw.ctrl_val | PWM_CFG_UPDATE, th1520_pwm_ctrl(hwpw= m)); + + if !was_enabled { + iomap.write32(wfhw.ctrl_val | PWM_START, th1520_pwm_ctrl(hwpwm= )); + } + + Ok(()) + } +} + +impl Drop for Th1520PwmDriverData { + fn drop(&mut self) { + self.clk.disable_unprepare(); + } +} + +static TH1520_PWM_OPS: pwm::PwmOpsVTable =3D pwm::create_pwm_ops::(); + +struct Th1520PwmPlatformDriver { + _registration: pwm::Registration, +} + +kernel::of_device_table!( + OF_TABLE, + MODULE_OF_TABLE, + ::IdInfo, + [(of::DeviceId::new(c_str!("thead,th1520-pwm")), ())] +); + +impl platform::Driver for Th1520PwmPlatformDriver { + type IdInfo =3D (); + const OF_ID_TABLE: Option> =3D Some(&OF_TABL= E); + + fn probe( + pdev: &platform::Device, + _id_info: Option<&Self::IdInfo>, + ) -> Result>> { + let dev =3D pdev.as_ref(); + let resource =3D pdev.resource(0).ok_or(ENODEV)?; + let iomem =3D pdev.ioremap_resource_sized::(r= esource)?; + let clk =3D Clk::get(pdev.as_ref(), None)?; + + clk.prepare_enable()?; + + let rate_hz =3D clk.rate().as_hz(); + if rate_hz =3D=3D 0 { + dev_err!(dev, "Clock rate is zero\n"); + return Err(EINVAL); + } + + if rate_hz > time::NSEC_PER_SEC as usize { + dev_err!( + dev, + "Clock rate {} Hz is too high, not supported.\n", + rate_hz + ); + return Err(ERANGE); + } + + let chip =3D pwm::Chip::new(dev, MAX_PWM_NUM, 0)?; + + let drvdata =3D KBox::new(Th1520PwmDriverData { iomem, clk }, GFP_= KERNEL)?; + chip.set_drvdata(drvdata); + + let registration =3D pwm::Registration::new(chip, &TH1520_PWM_OPS)= ?; + + Ok(KBox::new( + Th1520PwmPlatformDriver { + _registration: registration, + }, + GFP_KERNEL, + )? + .into()) + } +} + +kernel::module_platform_driver! { + type: Th1520PwmPlatformDriver, + name: "pwm-th1520", + author: "Michal Wilczynski ", + description: "T-HEAD TH1520 PWM driver", + license: "GPL v2", +} --=20 2.34.1 From nobody Sat Oct 11 12:08:54 2025 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 CC1072D8DA5 for ; Tue, 10 Jun 2025 12:53:40 +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=1749560023; cv=none; b=lbKxp5+bFkXPHhEY7Ic2WmzoxtEk9rWEWYFlPxMExCkQ8BCXJOwWd6HjoTHGb3GoGaMi+G5UD+SuHRPZZTgWk47yzdpW7tlsYv8cSKnvV0VT3hwRmg2a/vHUBFCl2PYBghuY4/STYpuNKxKnTX6H5vIOLJADbAIdOPIe2dDxiTc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749560023; c=relaxed/simple; bh=0Lxu7HehLH7DoW/xoCT1Ko+GlSwTKO8zio/z/Fw+EXg=; h=From:Date:Subject:MIME-Version:Message-Id:In-Reply-To:To:Cc: Content-Type:References; b=GzNb6PhKtsh1m0oTnwYqMKgTKCwRGH/eZmh2GD3SC3AiB7/QTDJcnvO3St/Yay/CWzEUFXBMuD3lTF8jcjNcgd8I0lM5JclFPv4CB535j3Of0I0di0U5p98DRehVJXvIkeXl98ao9t6aK9H7NoJhUzVyrOWfRV7F1WhZqzUFwNk= 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=t2tVslox; 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="t2tVslox" Received: from eucas1p2.samsung.com (unknown [182.198.249.207]) by mailout1.w1.samsung.com (KnoxPortal) with ESMTP id 20250610125335euoutp01849916be4a2a32caab1c9615b9d533c7~Hr5Cfa9jo1086610866euoutp012 for ; Tue, 10 Jun 2025 12:53:35 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.w1.samsung.com 20250610125335euoutp01849916be4a2a32caab1c9615b9d533c7~Hr5Cfa9jo1086610866euoutp012 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1749560015; bh=bJ5VvscQYJn7ykJa1oEwOsbdE9rQgFXktHrSp9bPepw=; h=From:Date:Subject:In-Reply-To:To:Cc:References:From; b=t2tVslox2bnOIpBG59uP9yLmoELioWWWWFFAHf8n8oemawHupZS6bLnWpz8kpntuw 4kllgfy6xyToBFw4vpAyMefN1/Pe8xKS5hcT5lCCpbIuQv/nHUcE4qGdE9ieNBuFc9 oEF6LnJ+LKodk2uFdengJDDHGW+ZYScDQfqVrK1w= Received: from eusmtip1.samsung.com (unknown [203.254.199.221]) by eucas1p2.samsung.com (KnoxPortal) with ESMTPA id 20250610125334eucas1p25545871cc703378afed320da70c2d2f3~Hr5BaaLph1179011790eucas1p24; Tue, 10 Jun 2025 12:53:34 +0000 (GMT) Received: from AMDC4942.eu.corp.samsungelectronics.net (unknown [106.210.136.40]) by eusmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250610125333eusmtip1bc9d2a9075043a948cec114b0c3acaa3~Hr5APZd950451804518eusmtip1E; Tue, 10 Jun 2025 12:53:33 +0000 (GMT) From: Michal Wilczynski Date: Tue, 10 Jun 2025 14:52:51 +0200 Subject: [PATCH v2 3/7] clk: thead: Mark essential bus clocks as CLK_IGNORE_UNUSED Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Message-Id: <20250610-rust-next-pwm-working-fan-for-sending-v2-3-753e2955f110@samsung.com> In-Reply-To: <20250610-rust-next-pwm-working-fan-for-sending-v2-0-753e2955f110@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: 20250610125334eucas1p25545871cc703378afed320da70c2d2f3 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" X-RootMTR: 20250610125334eucas1p25545871cc703378afed320da70c2d2f3 X-EPHeader: CA X-CMS-RootMailID: 20250610125334eucas1p25545871cc703378afed320da70c2d2f3 References: <20250610-rust-next-pwm-working-fan-for-sending-v2-0-753e2955f110@samsung.com> Probing peripherals in the AON and PERI domains, such as the PVT thermal sensor and the PWM controller, can lead to boot hangs or unresponsive devices on the LPi4A board. The root cause is that their parent bus clocks ('CLK_CPU2AON_X2H' and the 'CLK_PERISYS_APB' clocks) are automatically gated by the kernel's power-saving mechanisms when the bus is perceived as idle. Alternative solutions were investigated, including modeling the parent bus in the Device Tree with 'simple-pm-bus' or refactoring the clock driver's parentage. The 'simple-pm-bus' approach is not viable due to the lack of defined bus address ranges in the hardware manual and its creation of improper dependencies on the 'pm_runtime' API for consumer drivers. Therefore, applying the'`CLK_IGNORE_UNUSED' flag directly to the essential bus clocks is the most direct and targeted fix. This prevents the kernel from auto-gating these buses and ensures peripherals remain accessible. This change fixes the boot hang associated with the PVT sensor and resolves the functional issues with the PWM controller. [1] - https://lore.kernel.org/all/9e8a12db-236d-474c-b110-b3be96edf057@sams= ung.com/ Signed-off-by: Michal Wilczynski Reviewed-by: Drew Fustini --- drivers/clk/thead/clk-th1520-ap.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/clk/thead/clk-th1520-ap.c b/drivers/clk/thead/clk-th15= 20-ap.c index ebfb1d59401d05443716eb0029403b01775e8f73..cf7f6bd428a0faa4611b3fc61ed= bbc6690e565d9 100644 --- a/drivers/clk/thead/clk-th1520-ap.c +++ b/drivers/clk/thead/clk-th1520-ap.c @@ -792,11 +792,12 @@ static CCU_GATE(CLK_AON2CPU_A2X, aon2cpu_a2x_clk, "ao= n2cpu-a2x", axi4_cpusys2_ac 0x134, BIT(8), 0); static CCU_GATE(CLK_X2X_CPUSYS, x2x_cpusys_clk, "x2x-cpusys", axi4_cpusys2= _aclk_pd, 0x134, BIT(7), 0); -static CCU_GATE(CLK_CPU2AON_X2H, cpu2aon_x2h_clk, "cpu2aon-x2h", axi_aclk_= pd, 0x138, BIT(8), 0); +static CCU_GATE(CLK_CPU2AON_X2H, cpu2aon_x2h_clk, "cpu2aon-x2h", axi_aclk_= pd, + 0x138, BIT(8), CLK_IGNORE_UNUSED); static CCU_GATE(CLK_CPU2PERI_X2H, cpu2peri_x2h_clk, "cpu2peri-x2h", axi4_c= pusys2_aclk_pd, 0x140, BIT(9), CLK_IGNORE_UNUSED); static CCU_GATE(CLK_PERISYS_APB1_HCLK, perisys_apb1_hclk, "perisys-apb1-hc= lk", perisys_ahb_hclk_pd, - 0x150, BIT(9), 0); + 0x150, BIT(9), CLK_IGNORE_UNUSED); static CCU_GATE(CLK_PERISYS_APB2_HCLK, perisys_apb2_hclk, "perisys-apb2-hc= lk", perisys_ahb_hclk_pd, 0x150, BIT(10), CLK_IGNORE_UNUSED); static CCU_GATE(CLK_PERISYS_APB3_HCLK, perisys_apb3_hclk, "perisys-apb3-hc= lk", perisys_ahb_hclk_pd, --=20 2.34.1 From nobody Sat Oct 11 12:08:54 2025 Received: from mailout2.w1.samsung.com (mailout2.w1.samsung.com [210.118.77.12]) (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 447A12D8DD1 for ; Tue, 10 Jun 2025 12:53:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=210.118.77.12 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749560022; cv=none; b=asua2dv+Am9fbNdOZLj6/Opb/ak5G09zE3jI13xf3G9z/W0jZx5m+KG4hnmwvQy2o7s3LbVmEpM6oBnuzwyS5IeY9iZKMNKjvq/cVVUPpunMGZLx73L2W5BFMwIzE5Hp3PjCCK3oT2ldFB/Tg3dQrqXm2g83yJcuvaK1nUBUuOA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749560022; c=relaxed/simple; bh=48x/rydmg82Iaf49YkAuMdU8lSYwyIM9coz3aQ2bb+8=; h=From:Date:Subject:MIME-Version:Message-Id:In-Reply-To:To:Cc: Content-Type:References; b=G/PvAkfpOhW3ElHQHRAeZR/XCLy3BgxgvBjfmDigpMpLdd5rq0Z40v/xaFOJ75CeddGGF1hIcFbkBrfy3gwPuz48+ncZzqwDIqsqiSZlGa4jCIkL9Z/jqEblv2vG7A071ErP4OWJ3M4/CwwwO+OK2SfAvOcKGsU4YUjvt1z2/Ts= 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=Vp0arjpS; arc=none smtp.client-ip=210.118.77.12 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="Vp0arjpS" Received: from eucas1p1.samsung.com (unknown [182.198.249.206]) by mailout2.w1.samsung.com (KnoxPortal) with ESMTP id 20250610125336euoutp02bcd9cefce442e405a867bdcb9aac4cc8~Hr5DP5Euv2423124231euoutp02e for ; Tue, 10 Jun 2025 12:53:36 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout2.w1.samsung.com 20250610125336euoutp02bcd9cefce442e405a867bdcb9aac4cc8~Hr5DP5Euv2423124231euoutp02e DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1749560016; bh=U1BFpz8AOqtpZK/c00oYQ7184+LbfCfDBOBEZyhBjmo=; h=From:Date:Subject:In-Reply-To:To:Cc:References:From; b=Vp0arjpSECjkGuyFfYsyHv9k46HHqp7GO5TEWqoYOBVF2hfU5egay38O3aelpbi8C CfVz7cf1Uw7lbJi0MsZauu/RyldFHsKjGefTNsWSk8rc96DdmLnHmI1CBDwQNQ7laB F+QabDpeytOY5vhsroB6zf2wMQByZ6f8yqwKZMAo= Received: from eusmtip1.samsung.com (unknown [203.254.199.221]) by eucas1p2.samsung.com (KnoxPortal) with ESMTPA id 20250610125336eucas1p2ea5eaa740364b6db5007e0849465402d~Hr5Cuh2FN2039520395eucas1p26; Tue, 10 Jun 2025 12:53:36 +0000 (GMT) Received: from AMDC4942.eu.corp.samsungelectronics.net (unknown [106.210.136.40]) by eusmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250610125334eusmtip140b099d5ad8a6eb3335aba01d06715c3~Hr5Bei-mr0192501925eusmtip10; Tue, 10 Jun 2025 12:53:34 +0000 (GMT) From: Michal Wilczynski Date: Tue, 10 Jun 2025 14:52:52 +0200 Subject: [PATCH v2 4/7] dt-bindings: pwm: thead: Add T-HEAD TH1520 PWM controller Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Message-Id: <20250610-rust-next-pwm-working-fan-for-sending-v2-4-753e2955f110@samsung.com> In-Reply-To: <20250610-rust-next-pwm-working-fan-for-sending-v2-0-753e2955f110@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: 20250610125336eucas1p2ea5eaa740364b6db5007e0849465402d X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" X-RootMTR: 20250610125336eucas1p2ea5eaa740364b6db5007e0849465402d X-EPHeader: CA X-CMS-RootMailID: 20250610125336eucas1p2ea5eaa740364b6db5007e0849465402d References: <20250610-rust-next-pwm-working-fan-for-sending-v2-0-753e2955f110@samsung.com> Add the Device Tree binding documentation for the T-HEAD TH1520 SoC PWM controller. Signed-off-by: Michal Wilczynski Reviewed-by: Krzysztof Kozlowski --- .../devicetree/bindings/pwm/thead,th1520-pwm.yaml | 48 ++++++++++++++++++= ++++ MAINTAINERS | 1 + 2 files changed, 49 insertions(+) diff --git a/Documentation/devicetree/bindings/pwm/thead,th1520-pwm.yaml b/= Documentation/devicetree/bindings/pwm/thead,th1520-pwm.yaml new file mode 100644 index 0000000000000000000000000000000000000000..855aec59ac53c430adc84927123= 5686e87b10e6c --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/thead,th1520-pwm.yaml @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/thead,th1520-pwm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: T-HEAD TH1520 PWM controller + +maintainers: + - Michal Wilczynski + +allOf: + - $ref: pwm.yaml# + +properties: + compatible: + const: thead,th1520-pwm + + reg: + maxItems: 1 + + clocks: + items: + - description: SoC PWM clock + + "#pwm-cells": + const: 3 + +required: + - compatible + - reg + - clocks + +unevaluatedProperties: false + +examples: + - | + #include + soc { + #address-cells =3D <2>; + #size-cells =3D <2>; + pwm@ffec01c000 { + compatible =3D "thead,th1520-pwm"; + reg =3D <0xff 0xec01c000 0x0 0x4000>; + clocks =3D <&clk CLK_PWM>; + #pwm-cells =3D <3>; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 966ce515c8bfefdff1975bb716a267435ec0feae..c8a4a2d4f55af213fa6ad2911f9= 72990210ec950 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21311,6 +21311,7 @@ F: Documentation/devicetree/bindings/firmware/thead= ,th1520-aon.yaml F: Documentation/devicetree/bindings/mailbox/thead,th1520-mbox.yaml F: Documentation/devicetree/bindings/net/thead,th1520-gmac.yaml F: Documentation/devicetree/bindings/pinctrl/thead,th1520-pinctrl.yaml +F: Documentation/devicetree/bindings/pwm/thead,th1520-pwm.yaml F: Documentation/devicetree/bindings/reset/thead,th1520-reset.yaml F: arch/riscv/boot/dts/thead/ F: drivers/clk/thead/clk-th1520-ap.c --=20 2.34.1 From nobody Sat Oct 11 12:08:54 2025 Received: from mailout2.w1.samsung.com (mailout2.w1.samsung.com [210.118.77.12]) (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 BE1352D4B74 for ; Tue, 10 Jun 2025 12:53:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=210.118.77.12 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749560026; cv=none; b=AoiZvAdGISHnXbxkA7fkHvHbSV7j7ZABB4bzT5h7MaoPkwF/ZkNk3Z3Jfttiw0d34s60LPn9Zs1Eg4XGiz4BAlz5C6OzS55qP5z+lG/052zuPflnjBfLlB30aRqQh8XoBV8GUeaaz0eWd8jVqdfXZVC7Lst2jFBMm9bHJAgGR0A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749560026; c=relaxed/simple; bh=G3Rcxmg/8RGJT9WdkJVO9/yG6cY6SHgkWJ+0LoMKMUY=; h=From:Date:Subject:MIME-Version:Message-Id:In-Reply-To:To:Cc: Content-Type:References; b=GQV3dMDe5YAAI7QltL8ylXfcAHygVEHm3k6FyHny4xmoznmWda4DF+LOYrsGHDX99Jo3cfaIWX3nMWbe6RJrT9kgNS6S63umqLRZCpRR8AsDzZbAICXMHJgIElBX1MIslQrg5OhQcCmlLsJhpLs+Bnz2gO60muD9OmLoS+mBoH8= 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=clUIV76d; arc=none smtp.client-ip=210.118.77.12 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="clUIV76d" Received: from eucas1p2.samsung.com (unknown [182.198.249.207]) by mailout2.w1.samsung.com (KnoxPortal) with ESMTP id 20250610125338euoutp02df92bfc096895c00231646e76da1d154~Hr5FQFuxo2422824228euoutp02V for ; Tue, 10 Jun 2025 12:53:38 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout2.w1.samsung.com 20250610125338euoutp02df92bfc096895c00231646e76da1d154~Hr5FQFuxo2422824228euoutp02V DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1749560018; bh=tb9Nm7oCBfF8pKU9GqtVQRGVuCwKCYbxFHqowy9m/jM=; h=From:Date:Subject:In-Reply-To:To:Cc:References:From; b=clUIV76dl5UfTlFGzMX5zmGymOdPybEfb/LDHZ3K9+6hAx750wCnORyDENGxvi1VR VUiqO2k2VPrHM/Ag3M8ek9z03opG/6HDn48M0L9H4mjX0r8pwkfAAheE+iXdL8Jd+l 6hV2p1fO6gH+gbaWW08pe1i73IiLcfNsgxBNIHQ4= Received: from eusmtip1.samsung.com (unknown [203.254.199.221]) by eucas1p1.samsung.com (KnoxPortal) with ESMTPA id 20250610125337eucas1p199f9e3199e73f1a92462f39e611f07fe~Hr5EApVaM0876108761eucas1p16; Tue, 10 Jun 2025 12:53:37 +0000 (GMT) Received: from AMDC4942.eu.corp.samsungelectronics.net (unknown [106.210.136.40]) by eusmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250610125336eusmtip1090c51bf4f6eb899745cb3e8c08a627d~Hr5Cy3fPm0193101931eusmtip1B; Tue, 10 Jun 2025 12:53:36 +0000 (GMT) From: Michal Wilczynski Date: Tue, 10 Jun 2025 14:52:53 +0200 Subject: [PATCH v2 5/7] riscv: dts: thead: Add PWM controller node Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Message-Id: <20250610-rust-next-pwm-working-fan-for-sending-v2-5-753e2955f110@samsung.com> In-Reply-To: <20250610-rust-next-pwm-working-fan-for-sending-v2-0-753e2955f110@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: 20250610125337eucas1p199f9e3199e73f1a92462f39e611f07fe X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" X-RootMTR: 20250610125337eucas1p199f9e3199e73f1a92462f39e611f07fe X-EPHeader: CA X-CMS-RootMailID: 20250610125337eucas1p199f9e3199e73f1a92462f39e611f07fe References: <20250610-rust-next-pwm-working-fan-for-sending-v2-0-753e2955f110@samsung.com> Add the Device Tree node for the T-HEAD TH1520 SoC's PWM controller. Signed-off-by: Michal Wilczynski --- arch/riscv/boot/dts/thead/th1520.dtsi | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/riscv/boot/dts/thead/th1520.dtsi b/arch/riscv/boot/dts/th= ead/th1520.dtsi index 1db0054c4e093400e9dbebcee5fcfa5b5cae6e32..26996422e1efe5d2dde68819c2c= ec1c3fa782a23 100644 --- a/arch/riscv/boot/dts/thead/th1520.dtsi +++ b/arch/riscv/boot/dts/thead/th1520.dtsi @@ -490,6 +490,13 @@ uart2: serial@ffec010000 { status =3D "disabled"; }; =20 + pwm: pwm@ffec01c000 { + compatible =3D "thead,th1520-pwm"; + reg =3D <0xff 0xec01c000 0x0 0x4000>; + clocks =3D <&clk CLK_PWM>; + #pwm-cells =3D <3>; + }; + clk: clock-controller@ffef010000 { compatible =3D "thead,th1520-clk-ap"; reg =3D <0xff 0xef010000 0x0 0x1000>; --=20 2.34.1 From nobody Sat Oct 11 12:08:54 2025 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 C65BB2DCBF8 for ; Tue, 10 Jun 2025 12:53:41 +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=1749560024; cv=none; b=cDeTmd3nGjObgDRbkyBoZEubOAIKLsrbPtiUf+Jaar/Zia2jbOfqftlfy0pjgaql0HZiau+DwD8M7PHUGNmnOt7rD82xWBCgRzv5lkRVsdz9D1vS29i8cYxyR9Fc48Fv112AoPP/B/9v7q43yWaJJL7opRbyRm7Lww7Kpv1yjxg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749560024; c=relaxed/simple; bh=N+2CmuFEvaFrCFvEiZxIZGe87T5yZ+GUUMGzk60ZImg=; h=From:Date:Subject:MIME-Version:Message-Id:In-Reply-To:To:Cc: Content-Type:References; b=kpkB+9kkDB95lIjtItsEqNrjEqlCAeW+iPRD+zPUZGyq6DAXg1QlO6Oxz28q+OtYgjldK3mT2jAXYgL8IdIq6XEKmcjkWhaSbpdwIfhbINOnnsAqhMWf6KblyLTEcJqOKm/5lm8m2iPTwd+IPwdAqkHMT+NBVizR1vnVusNWQDg= 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=cIxLsqOl; 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="cIxLsqOl" Received: from eucas1p2.samsung.com (unknown [182.198.249.207]) by mailout1.w1.samsung.com (KnoxPortal) with ESMTP id 20250610125339euoutp01a142348eb0bb1207b8a59badca923ec8~Hr5F1HN_S0936409364euoutp01k for ; Tue, 10 Jun 2025 12:53:39 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.w1.samsung.com 20250610125339euoutp01a142348eb0bb1207b8a59badca923ec8~Hr5F1HN_S0936409364euoutp01k DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1749560019; bh=ZbpbiGCkq+bc/Hnq9ouJryKcDkGL17+XHPFX3q85en0=; h=From:Date:Subject:In-Reply-To:To:Cc:References:From; b=cIxLsqOlqL5smjQJhKfE0p2NtQhIqk3inVujlXCUMuuhc0FiluKcwfumEnVHq86I9 nCB43Po5eSGrmAcUZfDQ+dGL23bpz0ki0WtDNvwCLfm+Prhwrgp32hZxjX8OzppdED uo8TVd1UG4nKIKt+CiXYEE2M0AzZWCHeiPj4IYcY= Received: from eusmtip1.samsung.com (unknown [203.254.199.221]) by eucas1p2.samsung.com (KnoxPortal) with ESMTPA id 20250610125338eucas1p2cc606517da2482af0a1cfdfb4b51b1c3~Hr5FQ-k451180711807eucas1p23; Tue, 10 Jun 2025 12:53:38 +0000 (GMT) Received: from AMDC4942.eu.corp.samsungelectronics.net (unknown [106.210.136.40]) by eusmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250610125337eusmtip1d2797a7d750959ffd87abb5b2651733e~Hr5EFEfLP0363103631eusmtip1R; Tue, 10 Jun 2025 12:53:37 +0000 (GMT) From: Michal Wilczynski Date: Tue, 10 Jun 2025 14:52:54 +0200 Subject: [PATCH v2 6/7] riscv: dts: thead: Add PVT node Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Message-Id: <20250610-rust-next-pwm-working-fan-for-sending-v2-6-753e2955f110@samsung.com> In-Reply-To: <20250610-rust-next-pwm-working-fan-for-sending-v2-0-753e2955f110@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: 20250610125338eucas1p2cc606517da2482af0a1cfdfb4b51b1c3 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" X-RootMTR: 20250610125338eucas1p2cc606517da2482af0a1cfdfb4b51b1c3 X-EPHeader: CA X-CMS-RootMailID: 20250610125338eucas1p2cc606517da2482af0a1cfdfb4b51b1c3 References: <20250610-rust-next-pwm-working-fan-for-sending-v2-0-753e2955f110@samsung.com> Add PVT DT node for thermal sensor. Signed-off-by: Michal Wilczynski --- arch/riscv/boot/dts/thead/th1520.dtsi | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/arch/riscv/boot/dts/thead/th1520.dtsi b/arch/riscv/boot/dts/th= ead/th1520.dtsi index 26996422e1efe5d2dde68819c2cec1c3fa782a23..bef30780034e06b07aa29b27b02= 25ea891a4b531 100644 --- a/arch/riscv/boot/dts/thead/th1520.dtsi +++ b/arch/riscv/boot/dts/thead/th1520.dtsi @@ -669,6 +669,17 @@ padctrl_aosys: pinctrl@fffff4a000 { thead,pad-group =3D <1>; }; =20 + pvt: pvt@fffff4e000 { + compatible =3D "moortec,mr75203"; + reg =3D <0xff 0xfff4e000 0x0 0x80>, + <0xff 0xfff4e080 0x0 0x100>, + <0xff 0xfff4e180 0x0 0x680>, + <0xff 0xfff4e800 0x0 0x600>; + reg-names =3D "common", "ts", "pd", "vm"; + clocks =3D <&aonsys_clk>; + #thermal-sensor-cells =3D <1>; + }; + gpio@fffff52000 { compatible =3D "snps,dw-apb-gpio"; reg =3D <0xff 0xfff52000 0x0 0x1000>; --=20 2.34.1 From nobody Sat Oct 11 12:08:54 2025 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 F1A422C178E for ; Tue, 10 Jun 2025 12:53:42 +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=1749560027; cv=none; b=TVC7cmafdiCo8bHuJv7SO/bFwuTfg6zuDukbVHTyMybf1vC1U/KPEhcq255x9/OuM7PKYj0ethzXkVUq2w40uW5g7wk5D5Zj6scizT7y4NNG/sCQfujJiOHE/cQZWAZHBvNWJ1ySGf9J759BfxKRdJJeYcQhwuXLWFETlHyQT40= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1749560027; c=relaxed/simple; bh=pQpdhYmfxqh3Yfxmei5sPv11PaCzGkgZVRufLVaDNj8=; h=From:Date:Subject:MIME-Version:Message-Id:In-Reply-To:To:Cc: Content-Type:References; b=oHK6Gc1YWiXMTOHKZn8Mf4+8FnAG/AEjmtoMxkK6iALldo3KI5iX/gPUnqf6fkalysV+eGs2f9F2A/+URCf8fcbTtHLhsSI3POaV6+3mjvUTckX8gO0w5l9vm5XdjCPWN3+FK5OpN5FIUK/S66sIc/nC+fzSzPeLf7N4cvJkNGM= 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=mWM8bADX; 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="mWM8bADX" Received: from eucas1p2.samsung.com (unknown [182.198.249.207]) by mailout1.w1.samsung.com (KnoxPortal) with ESMTP id 20250610125340euoutp015adec7c3640f22bc118519b7ffe9cd46~Hr5HAmKIZ1190611906euoutp01h for ; Tue, 10 Jun 2025 12:53:40 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.w1.samsung.com 20250610125340euoutp015adec7c3640f22bc118519b7ffe9cd46~Hr5HAmKIZ1190611906euoutp01h DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1749560020; bh=Thc/4MnT4r5I8ag38cbivyQhpbuX345C1nEB0lzCSSU=; h=From:Date:Subject:In-Reply-To:To:Cc:References:From; b=mWM8bADXn63hFahBoMnLO4Nh8lbMIvj0dudd/rnnA83EXTAyJQwTChmCJyI5vWmnl z8diroM9ZP/8YQVyhGN87PKA3wuqYD+a0IlZzxe5r7pyEu4OkCY1wcQQN0oj3KsNwU 6RCT+5LlVw5bewT/ERQ5xcu4qAbWy2eqOwOmxsZk= Received: from eusmtip1.samsung.com (unknown [203.254.199.221]) by eucas1p2.samsung.com (KnoxPortal) with ESMTPA id 20250610125340eucas1p2288ca1486b0d4a94abceafe9eaf6718d~Hr5GgtAUY2070520705eucas1p2i; Tue, 10 Jun 2025 12:53:40 +0000 (GMT) Received: from AMDC4942.eu.corp.samsungelectronics.net (unknown [106.210.136.40]) by eusmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250610125338eusmtip12ae44043145e908a3b5b8fca4249dab0~Hr5FVRFhG0074100741eusmtip1u; Tue, 10 Jun 2025 12:53:38 +0000 (GMT) From: Michal Wilczynski Date: Tue, 10 Jun 2025 14:52:55 +0200 Subject: [PATCH v2 7/7] riscv: dts: thead: Add PWM fan and thermal control Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Message-Id: <20250610-rust-next-pwm-working-fan-for-sending-v2-7-753e2955f110@samsung.com> In-Reply-To: <20250610-rust-next-pwm-working-fan-for-sending-v2-0-753e2955f110@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: 20250610125340eucas1p2288ca1486b0d4a94abceafe9eaf6718d X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" X-RootMTR: 20250610125340eucas1p2288ca1486b0d4a94abceafe9eaf6718d X-EPHeader: CA X-CMS-RootMailID: 20250610125340eucas1p2288ca1486b0d4a94abceafe9eaf6718d References: <20250610-rust-next-pwm-working-fan-for-sending-v2-0-753e2955f110@samsung.com> Add Device Tree nodes to enable a PWM controlled fan and it's associated thermal management for the Lichee Pi 4A board. This enables temperature-controlled active cooling for the Lichee Pi 4A board based on SoC temperature. Signed-off-by: Michal Wilczynski --- arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts | 67 +++++++++++++++++++= ++++ 1 file changed, 67 insertions(+) diff --git a/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts b/arch/riscv= /boot/dts/thead/th1520-lichee-pi-4a.dts index 4020c727f09e8e2286fdc7fecd79dbd8eba69556..c58c2085ca92a3234f1350500ce= dae4157f0c35f 100644 --- a/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts +++ b/arch/riscv/boot/dts/thead/th1520-lichee-pi-4a.dts @@ -28,9 +28,76 @@ aliases { chosen { stdout-path =3D "serial0:115200n8"; }; + + thermal-zones { + cpu-thermal { + polling-delay =3D <1000>; + polling-delay-passive =3D <1000>; + thermal-sensors =3D <&pvt 0>; + + trips { + fan_config0: fan-trip0 { + temperature =3D <39000>; + hysteresis =3D <5000>; + type =3D "active"; + }; + + fan_config1: fan-trip1 { + temperature =3D <50000>; + hysteresis =3D <5000>; + type =3D "active"; + }; + + fan_config2: fan-trip2 { + temperature =3D <60000>; + hysteresis =3D <5000>; + type =3D "active"; + }; + }; + + cooling-maps { + map-active-0 { + cooling-device =3D <&fan 1 1>; + trip =3D <&fan_config0>; + }; + + map-active-1 { + cooling-device =3D <&fan 2 2>; + trip =3D <&fan_config1>; + }; + + map-active-2 { + cooling-device =3D <&fan 3 3>; + trip =3D <&fan_config2>; + }; + }; + }; + }; + + fan: pwm-fan { + pinctrl-names =3D "default"; + pinctrl-0 =3D <&fan_pins>; + compatible =3D "pwm-fan"; + #cooling-cells =3D <2>; + pwms =3D <&pwm 1 10000000 0>; + cooling-levels =3D <0 66 196 255>; + }; + }; =20 &padctrl0_apsys { + fan_pins: fan-0 { + pwm1-pins { + pins =3D "GPIO3_3"; /* PWM1 */ + function =3D "pwm"; + bias-disable; + drive-strength =3D <25>; + input-disable; + input-schmitt-disable; + slew-rate =3D <0>; + }; + }; + uart0_pins: uart0-0 { tx-pins { pins =3D "UART0_TXD"; --=20 2.34.1