From nobody Wed Oct 8 22:16:22 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 0F82F29CB2A for ; Mon, 23 Jun 2025 18:09:00 +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=1750702143; cv=none; b=i2REabJ7CEtuuC3etGVmBWKH4pC2AI+de2IT/NbOTlbH4d6vgsxcRmcuvKjvOApV5Vh4YUp9tQjQYLMtzpG0hlN6QLlGGlYsRIwP27vBqa85MwtwH5LfGmFUyhYauXYp4OyFCpfYl4X694cqhohh6Swo4++CkkiYFOEORrexM8E= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750702143; c=relaxed/simple; bh=Vjiw3nx7Fa76oWlj0bQ8HbzDqgDUK+mjxz6Q/hPhj3w=; h=From:Date:Subject:MIME-Version:Message-Id:In-Reply-To:To:Cc: Content-Type:References; b=AV8uwumzBh/7dA1QtBoEmQIPYV13LpazCsGQ46Sr7wJlDmfxu1b4n1l2ODLl0C7KP+hjEioU3k4ePAMk6PqPoqEbj0j+74pQgQcoepvQPfu90pZIW4cunOqj0pmKW3wtuID3MRoFzOLRLMH/GncyjBDsGcoArVMJimD7ctzzqDc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=samsung.com; spf=pass smtp.mailfrom=samsung.com; dkim=pass (1024-bit key) header.d=samsung.com header.i=@samsung.com header.b=k+cZBL72; arc=none smtp.client-ip=210.118.77.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="k+cZBL72" Received: from eucas1p1.samsung.com (unknown [182.198.249.206]) by mailout2.w1.samsung.com (KnoxPortal) with ESMTP id 20250623180859euoutp02317f1231f98bee33cc5d04308c4a9e02~LvlIBraEz2670826708euoutp02L for ; Mon, 23 Jun 2025 18:08:59 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout2.w1.samsung.com 20250623180859euoutp02317f1231f98bee33cc5d04308c4a9e02~LvlIBraEz2670826708euoutp02L DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1750702139; bh=oiZxyd/rOfRBrJ7bPBapMe43Tp3efYzMkYp4AvOkByE=; h=From:Date:Subject:In-Reply-To:To:Cc:References:From; b=k+cZBL72hrSspFCQ7ATuDT6hj75tS3dIfpo1R4sekiE5wuu/nc1lp8RwbgF48RPb7 CMEBcm7wa5aDjhb6I7dMxlbI23bNHQxL6WMiPH2g2kV0mP7vFZHmimJHhj/0tW+gP/ dcAxVIu9czl1mqBQvoI8BH2I1Zml2KBg5AwHQ0Ho= Received: from eusmtip1.samsung.com (unknown [203.254.199.221]) by eucas1p1.samsung.com (KnoxPortal) with ESMTPA id 20250623180858eucas1p1815f6d6815b1c715baad94810cefacd5~LvlG83x7-0556705567eucas1p1d; Mon, 23 Jun 2025 18:08:58 +0000 (GMT) Received: from AMDC4942.eu.corp.samsungelectronics.net (unknown [106.210.136.40]) by eusmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250623180857eusmtip1198e19606acb11b221532b8626ec28c7~LvlFt1m0X2097520975eusmtip1D; Mon, 23 Jun 2025 18:08:57 +0000 (GMT) From: Michal Wilczynski Date: Mon, 23 Jun 2025 20:08:49 +0200 Subject: [PATCH v5 1/9] rust: pwm: Add Kconfig and basic data structures Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Message-Id: <20250623-rust-next-pwm-working-fan-for-sending-v5-1-0ca23747c23e@samsung.com> In-Reply-To: <20250623-rust-next-pwm-working-fan-for-sending-v5-0-0ca23747c23e@samsung.com> To: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Michal Wilczynski , Drew Fustini , Guo Ren , Fu Wei , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Paul Walmsley , Palmer Dabbelt , Albert Ou , Alexandre Ghiti , Marek Szyprowski , Benno Lossin , Michael Turquette , Stephen Boyd , Benno Lossin Cc: linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-riscv@lists.infradead.org, devicetree@vger.kernel.org, linux-clk@vger.kernel.org X-Mailer: b4 0.15-dev X-CMS-MailID: 20250623180858eucas1p1815f6d6815b1c715baad94810cefacd5 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" X-RootMTR: 20250623180858eucas1p1815f6d6815b1c715baad94810cefacd5 X-EPHeader: CA X-CMS-RootMailID: 20250623180858eucas1p1815f6d6815b1c715baad94810cefacd5 References: <20250623-rust-next-pwm-working-fan-for-sending-v5-0-0ca23747c23e@samsung.com> Introduce the foundational support for PWM abstractions in Rust. This commit adds the `RUST_PWM_ABSTRACTIONS` Kconfig option to enable the feature, along with the necessary build-system support and C helpers. It also introduces the first set of safe wrappers for the PWM subsystem, covering the basic data carrying C structs and enums: - `Polarity`: A safe wrapper for `enum pwm_polarity`. - `Waveform`: A wrapper for `struct pwm_waveform`. - `Args`: A wrapper for `struct pwm_args`. - `State`: A wrapper for `struct pwm_state`. These types provide memory safe, idiomatic Rust representations of the core PWM data structures and form the building blocks for the abstractions that will follow. Signed-off-by: Michal Wilczynski --- MAINTAINERS | 6 ++ drivers/pwm/Kconfig | 13 +++ rust/bindings/bindings_helper.h | 1 + rust/helpers/helpers.c | 1 + rust/helpers/pwm.c | 20 ++++ rust/kernel/lib.rs | 2 + rust/kernel/pwm.rs | 198 ++++++++++++++++++++++++++++++++++++= ++++ 7 files changed, 241 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 0c1d245bf7b84f8a78b811e0c9c5a3edc09edc22..a575622454a2ef57ce055c8a8c4= 765fa4fddc490 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20073,6 +20073,12 @@ F: include/linux/pwm.h F: include/linux/pwm_backlight.h K: pwm_(config|apply_might_sleep|apply_atomic|ops) =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..cfddeae0eab3523f04f361fb41c= cd1345c0c937b 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 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_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 16fa9bca5949b85e8d4cdcfe8e6886124f72d8d8..60879e6d794ce0f87e39caafc54= 95bf5e8acf8f0 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -31,6 +31,7 @@ #include "platform.c" #include "pci.c" #include "pid_namespace.c" +#include "pwm.c" #include "rbtree.c" #include "rcu.c" #include "refcount.c" diff --git a/rust/helpers/pwm.c b/rust/helpers/pwm.c new file mode 100644 index 0000000000000000000000000000000000000000..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 6b4774b2b1c37f4da1866e993be6230bc6715841..ce1d08b14e456905dbe7b625bbb= 8ca8b08deae2a 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -105,6 +105,8 @@ pub mod seq_file; pub mod sizes; mod static_assert; +#[cfg(CONFIG_RUST_PWM_ABSTRACTIONS)] +pub mod pwm; #[doc(hidden)] pub mod std_vendor; pub mod str; diff --git a/rust/kernel/pwm.rs b/rust/kernel/pwm.rs new file mode 100644 index 0000000000000000000000000000000000000000..ed681b228c414e7ae8bf80ca649= ad497c9dc4ec3 --- /dev/null +++ b/rust/kernel/pwm.rs @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2025 Samsung Electronics Co., Ltd. +// Author: Michal Wilczynski + +//! PWM subsystem abstractions. +//! +//! C header: [`include/linux/pwm.h`](srctree/include/linux/pwm.h). + +use crate::{ + bindings, + prelude::*, + types::Opaque, +}; +use core::convert::TryFrom; + +/// Maximum size for the hardware-specific waveform representation buffer. +/// +/// From C: `#define WFHWSIZE 20` +pub const WFHW_MAX_SIZE: usize =3D 20; + +/// PWM polarity. Mirrors [`enum pwm_polarity`](srctree/include/linux/pwm.= h). +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Polarity { + /// Normal polarity (duty cycle defines the high period of the signal). + Normal, + + /// Inversed polarity (duty cycle defines the low period of the signal= ). + Inversed, +} + +impl TryFrom for Polarity { + type Error =3D Error; + + fn try_from(polarity: bindings::pwm_polarity) -> Result { + match polarity { + bindings::pwm_polarity_PWM_POLARITY_NORMAL =3D> Ok(Polarity::N= ormal), + bindings::pwm_polarity_PWM_POLARITY_INVERSED =3D> Ok(Polarity:= :Inversed), + _ =3D> Err(EINVAL), + } + } +} + +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 [`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=E2=80=99s start. + pub duty_offset_ns: u64, +} + +impl From for Waveform { + fn from(wf: bindings::pwm_waveform) -> Self { + Waveform { + period_length_ns: wf.period_length_ns, + duty_length_ns: wf.duty_length_ns, + duty_offset_ns: wf.duty_offset_ns, + } + } +} + +impl From for bindings::pwm_waveform { + fn from(wf: Waveform) -> Self { + bindings::pwm_waveform { + period_length_ns: wf.period_length_ns, + duty_length_ns: wf.duty_length_ns, + duty_offset_ns: wf.duty_offset_ns, + } + } +} + +/// Wrapper for board-dependent PWM arguments [`struct pwm_args`](srctree/= include/linux/pwm.h). +#[repr(transparent)] +pub struct Args(Opaque); + +impl Args { + /// Creates an `Args` wrapper from a C struct pointer. + /// + /// # Safety + /// + /// The caller must ensure that `c_args_ptr` is a valid, non-null 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) -> Result { + // SAFETY: `self.0.get()` returns a pointer to the `bindings::pwm_= args` + // managed by the `Opaque` wrapper. This pointer is guaranteed to = be + // valid and aligned for the lifetime of `self`. + let raw_polarity =3D unsafe { (*self.0.get()).polarity }; + Polarity::try_from(raw_polarity) + } +} + +/// Wrapper for PWM state [`struct pwm_state`](srctree/include/linux/pwm.h= ). +#[repr(transparent)] +pub struct State(bindings::pwm_state); + +impl Default for State { + fn default() -> Self { + Self::new() + } +} + +impl State { + /// Creates a new zeroed `State`. + pub fn new() -> Self { + State(bindings::pwm_state::default()) + } + + /// Creates a `State` wrapper by taking ownership of a C `pwm_state` 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) -> Result { + Polarity::try_from(self.0.polarity) + } + + /// Sets the polarity of the PWM signal. + pub fn set_polarity(&mut self, polarity: Polarity) { + self.0.polarity =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; + } +} --=20 2.34.1 From nobody Wed Oct 8 22:16:22 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 9A7D32D3A66 for ; Mon, 23 Jun 2025 18:09:03 +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=1750702147; cv=none; b=PuOAyt0OmYPZyaHW/A8+fPxSNhbh1CWyP73+U2XzW3NyKz6rDjnQNCQxCh1hTowNi/nrYvD/AnqOKLqpx3ZKL342TXEGJCO0FShWA60AogdXR7Wh8Ws8y+Xb/fMhYmjjuJ3vlVpNk7YEvJkQWnKRShLWGg4RE7qkJJaW4iE99ZI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750702147; c=relaxed/simple; bh=/91B2CzNMGbRXodYmqOCSlR4bAJtUWi40/XcvLYxN8U=; h=From:Date:Subject:MIME-Version:Message-Id:In-Reply-To:To:Cc: Content-Type:References; b=B8DJEz8H11kn3jQmh4PTAke7IbACXf5iikgXjyAeCzVNGpegqwc3UBznpIW10UUt1YxW1Ogg3r86ir6RUc/elpcRS0+BpnN9mkmgPFSJSipa7NNPhfRpachKEKPq8Ou0d06rBsFlVuQtKYjLQcp5fwzhSC7Hg4kTbyVx6O84fs0= 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=Wr9cReU7; 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="Wr9cReU7" Received: from eucas1p2.samsung.com (unknown [182.198.249.207]) by mailout2.w1.samsung.com (KnoxPortal) with ESMTP id 20250623180900euoutp02866b251c70d3e33d8f346455175f27b9~LvlI3178Z2685426854euoutp02F for ; Mon, 23 Jun 2025 18:09:00 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout2.w1.samsung.com 20250623180900euoutp02866b251c70d3e33d8f346455175f27b9~LvlI3178Z2685426854euoutp02F DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1750702140; bh=dOEnl37kGZbQWiL7Vd5sBbmWXQBqlqQFKLHeDuUvdbM=; h=From:Date:Subject:In-Reply-To:To:Cc:References:From; b=Wr9cReU7XYTZnDyDxY8/k9ccarqfjdr1zZ01qakij5t6n29XRKjVRq9pVcZA/GXBr O5E/MD20ibCmu/Dx60ZJNRAJXLYxChiBNBx4S03nc7eUYL3H2rIGSEJQnj0Fb0sNST eeFLlIynLU1fQOzJ9P6UWj+yL98rsX2nUWOMiUPQ= Received: from eusmtip1.samsung.com (unknown [203.254.199.221]) by eucas1p1.samsung.com (KnoxPortal) with ESMTPA id 20250623180859eucas1p10ebb40f33046d52618ba738ebbbaa664~LvlIBokMx2898428984eucas1p1V; Mon, 23 Jun 2025 18:08:59 +0000 (GMT) Received: from AMDC4942.eu.corp.samsungelectronics.net (unknown [106.210.136.40]) by eusmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250623180858eusmtip1324346555bbdcfb27d05677fe34e757c~LvlG5dpgr3150531505eusmtip1x; Mon, 23 Jun 2025 18:08:58 +0000 (GMT) From: Michal Wilczynski Date: Mon, 23 Jun 2025 20:08:50 +0200 Subject: [PATCH v5 2/9] rust: pwm: Add core 'Device' and 'Chip' object wrappers 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: <20250623-rust-next-pwm-working-fan-for-sending-v5-2-0ca23747c23e@samsung.com> In-Reply-To: <20250623-rust-next-pwm-working-fan-for-sending-v5-0-0ca23747c23e@samsung.com> To: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Michal Wilczynski , Drew Fustini , Guo Ren , Fu Wei , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Paul Walmsley , Palmer Dabbelt , Albert Ou , Alexandre Ghiti , Marek Szyprowski , Benno Lossin , Michael Turquette , Stephen Boyd , Benno Lossin Cc: linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-riscv@lists.infradead.org, devicetree@vger.kernel.org, linux-clk@vger.kernel.org X-Mailer: b4 0.15-dev X-CMS-MailID: 20250623180859eucas1p10ebb40f33046d52618ba738ebbbaa664 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" X-RootMTR: 20250623180859eucas1p10ebb40f33046d52618ba738ebbbaa664 X-EPHeader: CA X-CMS-RootMailID: 20250623180859eucas1p10ebb40f33046d52618ba738ebbbaa664 References: <20250623-rust-next-pwm-working-fan-for-sending-v5-0-0ca23747c23e@samsung.com> Building on the basic data types, this commit introduces the central object abstractions for the PWM subsystem: Device and Chip. It also includes the core trait implementations that make the Chip wrapper a complete, safe, and managed object. The main components of this change are: - Device and Chip Structs: These structs wrap the underlying struct pwm_device and struct pwm_chip C objects, providing safe, idiomatic methods to access their fields. - Core Trait Implementations for Chip: - AlwaysRefCounted: Links the Chip's lifetime to its embedded struct device reference counter. This enables automatic lifetime management via ARef. - Send and Sync: Marks the Chip wrapper as safe for use across threads. This is sound because the C core handles all necessary locking for the underlying object's state. These wrappers and traits form a robust foundation for building PWM drivers in Rust. Signed-off-by: Michal Wilczynski --- rust/kernel/pwm.rs | 216 +++++++++++++++++++++++++++++++++++++++++++++++++= +++- 1 file changed, 214 insertions(+), 2 deletions(-) diff --git a/rust/kernel/pwm.rs b/rust/kernel/pwm.rs index ed681b228c414e7ae8bf80ca649ad497c9dc4ec3..3865b43ec47df6cb0c09bc74a22= 8535512b6b1a8 100644 --- a/rust/kernel/pwm.rs +++ b/rust/kernel/pwm.rs @@ -8,10 +8,12 @@ =20 use crate::{ bindings, + device, + error, prelude::*, - types::Opaque, + types::{ARef, AlwaysRefCounted, ForeignOwnable, Opaque}, }; -use core::convert::TryFrom; +use core::{convert::TryFrom, ptr::NonNull}; =20 /// Maximum size for the hardware-specific waveform representation buffer. /// @@ -196,3 +198,213 @@ pub fn set_usage_power(&mut self, usage_power: bool) { self.0.usage_power =3D usage_power; } } + +/// Wrapper for a PWM device [`struct pwm_device`](srctree/include/linux/p= wm.h). +#[repr(transparent)] +pub struct Device(Opaque); + +impl Device { + /// Creates a reference to a [`Device`] from a valid C pointer. + /// + /// # Safety + /// + /// The caller must ensure that `ptr` is valid and remains valid for t= he lifetime of the + /// returned [`Device`] reference. + pub(crate) unsafe fn as_ref<'a>(ptr: *mut bindings::pwm_device) -> &'a= Self { + // SAFETY: The safety requirements guarantee the validity of the d= ereference, while the + // `Device` type being transparent makes the cast ok. + unsafe { &*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::as_ref's safety conditions must be met. + unsafe { Chip::as_ref((*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`](srctree/include= /linux/pwm.h)). +#[repr(transparent)] +pub struct Chip(Opaque); + +impl Chip { + /// Creates a reference to a [`Chip`] from a valid pointer. + /// + /// # Safety + /// + /// The caller must ensure that `ptr` is valid and remains valid for t= he lifetime of the + /// returned [`Chip`] reference. + pub(crate) unsafe fn as_ref<'a>(ptr: *mut bindings::pwm_chip) -> &'a S= elf { + // SAFETY: The safety requirements guarantee the validity of the d= ereference, while the + // `Chip` type being transparent makes the cast ok. + unsafe { &*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) -> &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()) = }; + + // SAFETY: The only way to create a chip is through Chip::new, whi= ch initializes + // this pointer. + unsafe { &*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 refcoun= ting + /// on its embedded `struct device`. + pub fn new( + parent_dev: &device::Device, + npwm: u32, + sizeof_priv: usize, + drvdata: T, + ) -> 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: The pointer is valid, so we can create a temporary ref to set = data. + let chip_ref =3D unsafe { &*chip_ptr_as_self }; + chip_ref.set_drvdata(drvdata); + + // 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 {} --=20 2.34.1 From nobody Wed Oct 8 22:16:22 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 766F52BCF48 for ; Mon, 23 Jun 2025 18:09:04 +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=1750702147; cv=none; b=rADeCMXTVPMmkk0ZeqhTn3gTRU7aU5Npt+CTI0OFitZ6A9D8ySuEEYW0zJJC7CZg04h4SSodLRVQC8rqyEtVfLjP7W/PTaplISsuDCbGkyzkGZhfsAemznCi+xjiwZ65LxVgP/J1TOSG7qy2cSown8UmotIBBA1lCBYdcdLxO1A= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750702147; c=relaxed/simple; bh=KSOUO8dQWEaYaDhLPjiHFojfmybtJLULr9lDKrlJz6s=; h=From:Date:Subject:MIME-Version:Message-Id:In-Reply-To:To:Cc: Content-Type:References; b=uEQnuDHIB+TSd+gPsiJaysgqyrVEheX967/3BSaXcFwIiD1laLiuydF6aCnBVXp5XIllLLeGaN9l40mTD1z68OfqZt+IffKVdWNJH1meZTWYfVOTOIfJIQ2Jw+cgLcD5jQ3sLd3as5Ife1YfcKMJyMA55qXm66yJtyYeGxXIVVY= 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=MviDwa3p; 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="MviDwa3p" Received: from eucas1p2.samsung.com (unknown [182.198.249.207]) by mailout1.w1.samsung.com (KnoxPortal) with ESMTP id 20250623180901euoutp01438afc1356d8cda752f78c535e41f416~LvlKIJIHi0365003650euoutp010 for ; Mon, 23 Jun 2025 18:09:01 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.w1.samsung.com 20250623180901euoutp01438afc1356d8cda752f78c535e41f416~LvlKIJIHi0365003650euoutp010 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1750702141; bh=iN0qq5V9fYuxdgNB+mZLYEP6BI1dGPpucQhZt1+QoiQ=; h=From:Date:Subject:In-Reply-To:To:Cc:References:From; b=MviDwa3pucoSnx+HC1iFPMxUO/3vfEAg+SyaYYXmK6bBzX+BSn2AdkhJfPclZjIrO ckx2C+B28KmCw9eoBtoVzEGMIERTMDMmgMnFMd09yMuxIBvadkx1eEDW9jwkC18+kE abtrIby9UQoUt2+L+7oW6cN3F1smskHTQGi3GV0M= Received: from eusmtip1.samsung.com (unknown [203.254.199.221]) by eucas1p2.samsung.com (KnoxPortal) with ESMTPA id 20250623180900eucas1p2ffbd79e79f690189ae89aefcc3793e50~LvlJNuFkn1026210262eucas1p2A; Mon, 23 Jun 2025 18:09:00 +0000 (GMT) Received: from AMDC4942.eu.corp.samsungelectronics.net (unknown [106.210.136.40]) by eusmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250623180859eusmtip1b7c7acea70254f2d1986f28247af6b73~LvlIFjfIY0518905189eusmtip1U; Mon, 23 Jun 2025 18:08:59 +0000 (GMT) From: Michal Wilczynski Date: Mon, 23 Jun 2025 20:08:51 +0200 Subject: [PATCH v5 3/9] rust: pwm: Add driver operations trait and registration support 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: <20250623-rust-next-pwm-working-fan-for-sending-v5-3-0ca23747c23e@samsung.com> In-Reply-To: <20250623-rust-next-pwm-working-fan-for-sending-v5-0-0ca23747c23e@samsung.com> To: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Michal Wilczynski , Drew Fustini , Guo Ren , Fu Wei , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Paul Walmsley , Palmer Dabbelt , Albert Ou , Alexandre Ghiti , Marek Szyprowski , Benno Lossin , Michael Turquette , Stephen Boyd , Benno Lossin Cc: linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-riscv@lists.infradead.org, devicetree@vger.kernel.org, linux-clk@vger.kernel.org X-Mailer: b4 0.15-dev X-CMS-MailID: 20250623180900eucas1p2ffbd79e79f690189ae89aefcc3793e50 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" X-RootMTR: 20250623180900eucas1p2ffbd79e79f690189ae89aefcc3793e50 X-EPHeader: CA X-CMS-RootMailID: 20250623180900eucas1p2ffbd79e79f690189ae89aefcc3793e50 References: <20250623-rust-next-pwm-working-fan-for-sending-v5-0-0ca23747c23e@samsung.com> Complete the PWM abstraction layer by adding the final components required to implement and register a driver. The main additions are: - PwmOps Trait: An interface that drivers can implement to provide their hardware specific logic. It mirrors the C pwm_ops interface, providing hooks for standard PWM operations like apply, request, and waveform handling. - FFI VTable and Adapter: The Adapter struct, PwmOpsVTable wrapper, and create_pwm_ops function are introduced. This scaffolding handles the unsafe FFI translation, bridging the gap between the idiomatic PwmOps trait and the C kernel's function-pointer-based vtable. - Registration Guard: A final RAII guard that uses the vtable to safely register a Chip with the PWM core via pwmchip_add. Its Drop implementation guarantees that pwmchip_remove is always called, preventing resource leaks. With this patch, the PWM abstraction layer is now complete and ready to be used for writing PWM chip drivers in Rust. Signed-off-by: Michal Wilczynski --- rust/kernel/pwm.rs | 486 +++++++++++++++++++++++++++++++++++++++++++++++++= +++- 1 file changed, 483 insertions(+), 3 deletions(-) diff --git a/rust/kernel/pwm.rs b/rust/kernel/pwm.rs index 3865b43ec47df6cb0c09bc74a228535512b6b1a8..25bc07a3df1d43467a3a6ec8f23= 62ae8f770360a 100644 --- a/rust/kernel/pwm.rs +++ b/rust/kernel/pwm.rs @@ -8,12 +8,13 @@ =20 use crate::{ bindings, - device, - error, + device::{self, Bound}, + devres::Devres, + error::{self, to_result}, prelude::*, types::{ARef, AlwaysRefCounted, ForeignOwnable, Opaque}, }; -use core::{convert::TryFrom, ptr::NonNull}; +use core::{convert::TryFrom, marker::PhantomData, ptr::NonNull}; =20 /// Maximum size for the hardware-specific waveform representation buffer. /// @@ -408,3 +409,482 @@ unsafe impl Send for Chip {} // 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 {} + +/// A resource guard that ensures `pwmchip_remove` is called on drop. +/// +/// This struct is intended to be managed by the `devres` framework by tra= nsferring its ownership +/// via [`Devres::new_foreign_owned`]. This ties the lifetime of the PWM c= hip registration +/// to the lifetime of the underlying device. +pub struct Registration { + chip: ARef, +} + +impl Registration { + /// Registers a PWM chip with the PWM subsystem. + /// + /// Transfers its ownership to the `devres` framework, which ties its = lifetime + /// to the parent device. + /// On unbind of the parent device, the `devres` entry will be dropped= , automatically + /// calling `pwmchip_remove`. This function should be called from the = driver's `probe`. + pub fn new_foreign_owned( + dev: &device::Device, + chip: ARef, + ops_vtable: &'static PwmOpsVTable, + ) -> Result { + let c_chip_ptr =3D chip.as_raw(); + + // SAFETY: `c_chip_ptr` is valid because the `ARef` that own= s it exists. + // The vtable pointer is also valid. This sets the `.ops` field on= the C struct. + unsafe { + (*c_chip_ptr).ops =3D ops_vtable.as_raw(); + } + + // SAFETY: `c_chip_ptr` points to a valid chip with its ops initia= lized. + // `__pwmchip_add` is the C function to register the chip with the= PWM core. + unsafe { + to_result(bindings::__pwmchip_add(c_chip_ptr, core::ptr::null_= mut()))?; + } + + let registration =3D Registration { chip }; + + Devres::new_foreign_owned(dev, registration, GFP_KERNEL)?; + + Ok(()) + } +} + +impl Drop for Registration { + fn drop(&mut self) { + let chip_raw =3D self.chip.as_raw(); + + // SAFETY: `chip_raw` points to a chip that was successfully regis= tered. + // `bindings::pwmchip_remove` is the correct C function to unregis= ter it. + // This `drop` implementation is called automatically by `devres` = on driver unbind. + unsafe { + bindings::pwmchip_remove(chip_raw); + } + } +} + +/// 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: &Chip, + _pwm: &Device, + _state: &State, + _parent_dev: &device::Device, + ) -> Result { + Err(ENOTSUPP) + } + + /// Optional hook for when a PWM device is requested. + fn request(_chip: &Chip, _pwm: &Device, _parent_dev: &device::Device) -> Result { + Ok(()) + } + + /// Optional hook for when a PWM device is freed. + fn free(_chip: &Chip, _pwm: &Device, _parent_dev: &device::Device) {} + + /// Optional hook for capturing a PWM signal. + fn capture( + _chip: &Chip, + _pwm: &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: &Chip, + _pwm: &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: &Chip, + _pwm: &Device, + _wf: &Waveform, + ) -> Result<(c_int, 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: &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: &Chip, + _pwm: &Device, + _parent_dev: &device::Device, + ) -> Result { + Err(ENOTSUPP) + } + + /// Write a hardware-specific waveform configuration to the hardware. + fn write_waveform( + _chip: &Chip, + _pwm: &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 c_void) -> Res= ult { + 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 c_void) -> Result= { + 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 + /// + /// 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, + ) -> c_int { + // SAFETY: This block relies on the function's safety contract: th= e C caller + // provides valid pointers. `Chip::as_ref` and `Device::as_ref` ar= e `unsafe fn` + // whose preconditions are met by this contract. + let (chip, pwm) =3D unsafe { (Chip::as_ref(c), Device::as_ref(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 + /// + /// Pointers from C must be valid. + unsafe extern "C" fn request_callback( + c: *mut bindings::pwm_chip, + p: *mut bindings::pwm_device, + ) -> c_int { + // SAFETY: PWM core guarentees `c` and `p` are valid pointers. + let (chip, pwm) =3D unsafe { (Chip::as_ref(c), Device::as_ref(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 + /// + /// 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::as_ref(c), Device::as_ref(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 + /// + /// 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, + ) -> c_int { + // SAFETY: Relies on the function's contract that `c` and `p` are = valid pointers. + let (chip, pwm, result) =3D unsafe { (Chip::as_ref(c), Device::as_= ref(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 + /// + /// 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, + ) -> c_int { + // SAFETY: Relies on the function's contract that `c` and `p` are = valid pointers. + let (chip, pwm) =3D unsafe { (Chip::as_ref(c), Device::as_ref(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 + /// + /// 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 c_void, + ) -> c_int { + // SAFETY: Relies on the function's contract that `c` and `p` are = valid pointers. + let (chip, pwm, wf) =3D unsafe { (Chip::as_ref(c), Device::as_ref(= p), Waveform::from(*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 + /// + /// 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 c_void, + w: *mut bindings::pwm_waveform, + ) -> c_int { + // SAFETY: Relies on the function's contract that `c` and `p` are = valid pointers. + let (chip, pwm) =3D unsafe { (Chip::as_ref(c), Device::as_ref(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 + /// + /// 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 c_void, + ) -> c_int { + // SAFETY: Relies on the function's contract that `c` and `p` are = valid pointers. + let (chip, pwm) =3D unsafe { (Chip::as_ref(c), Device::as_ref(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 + /// + /// 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 c_void, + ) -> c_int { + // SAFETY: Relies on the function's contract that `c` and `p` are = valid pointers. + let (chip, pwm) =3D unsafe { (Chip::as_ref(c), Device::as_ref(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`](srctree/include/linux/pwm.h). +#[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 Wed Oct 8 22:16:22 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 06AF72D3A90 for ; Mon, 23 Jun 2025 18:09:04 +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=1750702148; cv=none; b=KLC4di2ZqGw69zjLy5OHC43ItspfamB3wbcdoX4A9feHDIO4DJQwVG9gjhXvC0O0ZhC/4rs3vvbRsv2t+8WxkoNpH3vh3HeokwW39hjLcGGtGw1e5bVdiWzbb5L4DBQcpHA7XR4ce+1HsqHcneiHI/p1+hRCZJay3NXI5zsvXIw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750702148; c=relaxed/simple; bh=KbwSzURNXEjOTP/QI14UyvcFnu1Gt4QNy6E8XZwWQ3Q=; h=From:Date:Subject:MIME-Version:Message-Id:In-Reply-To:To:Cc: Content-Type:References; b=oggitg1GiwfNGsagvpHhstBU4wN0Ln7niZnjowsTjuYj7dnVkUpDpXoxEf1NJYLmuSGLF5gm+4Zj/vwcMq66eYtk97qxV2Y3J2BvHnUnI816x/8ytpJ/ur1YkfHyui1bmUMiGVCSOyWRQZZDIC5pK7te3GTMBwcT6JAQEy6if3s= 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=HdUQQ+Zy; 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="HdUQQ+Zy" Received: from eucas1p1.samsung.com (unknown [182.198.249.206]) by mailout1.w1.samsung.com (KnoxPortal) with ESMTP id 20250623180903euoutp01eb3a52bfc3de40fa92007ae35eb76aff~LvlLOtepc0365003650euoutp013 for ; Mon, 23 Jun 2025 18:09:03 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.w1.samsung.com 20250623180903euoutp01eb3a52bfc3de40fa92007ae35eb76aff~LvlLOtepc0365003650euoutp013 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1750702143; bh=MzKGJold40W6KwMD9G1I7OUk80RqJSKo3z6A54ZPiIs=; h=From:Date:Subject:In-Reply-To:To:Cc:References:From; b=HdUQQ+Zy9Y3xDjP3yWlP5u39hwKny+ac0rE44cRD++h0o85cnwZVqxx6DnmHqwdlg B7SqlRh7umF9ZBuwpS6fEgZsB7BUWoUPATuLQ88MnXSpmR6oA3FtFhC+NIMKPq4wea VqXP0VgrK1AYvkuTa2n39XYzEbyWDwemGHK1rdvk= Received: from eusmtip1.samsung.com (unknown [203.254.199.221]) by eucas1p2.samsung.com (KnoxPortal) with ESMTPA id 20250623180902eucas1p2960477c0a44f05e991747312b0ae0ff0~LvlKcQLR01026710267eucas1p2O; Mon, 23 Jun 2025 18:09:02 +0000 (GMT) Received: from AMDC4942.eu.corp.samsungelectronics.net (unknown [106.210.136.40]) by eusmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250623180900eusmtip168e018a89325a22edf3361ff9769d12b~LvlJRxoux3197731977eusmtip1Q; Mon, 23 Jun 2025 18:09:00 +0000 (GMT) From: Michal Wilczynski Date: Mon, 23 Jun 2025 20:08:52 +0200 Subject: [PATCH v5 4/9] 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: <20250623-rust-next-pwm-working-fan-for-sending-v5-4-0ca23747c23e@samsung.com> In-Reply-To: <20250623-rust-next-pwm-working-fan-for-sending-v5-0-0ca23747c23e@samsung.com> To: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Michal Wilczynski , Drew Fustini , Guo Ren , Fu Wei , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Paul Walmsley , Palmer Dabbelt , Albert Ou , Alexandre Ghiti , Marek Szyprowski , Benno Lossin , Michael Turquette , Stephen Boyd , Benno Lossin Cc: linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-riscv@lists.infradead.org, devicetree@vger.kernel.org, linux-clk@vger.kernel.org X-Mailer: b4 0.15-dev X-CMS-MailID: 20250623180902eucas1p2960477c0a44f05e991747312b0ae0ff0 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" X-RootMTR: 20250623180902eucas1p2960477c0a44f05e991747312b0ae0ff0 X-EPHeader: CA X-CMS-RootMailID: 20250623180902eucas1p2960477c0a44f05e991747312b0ae0ff0 References: <20250623-rust-next-pwm-working-fan-for-sending-v5-0-0ca23747c23e@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 | 318 ++++++++++++++++++++++++++++++++++++++++++= ++++ 4 files changed, 330 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index a575622454a2ef57ce055c8a8c4765fa4fddc490..879870471e86dcec4a0e8f5c45d= 2cc3409411fdd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21402,6 +21402,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 cfddeae0eab3523f04f361fb41ccd1345c0c937b..a675b3bd68392d1b05a47a2a139= 0c5606647ca15 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 + tristate "TH1520 PWM support" + depends on RUST_PWM_ABSTRACTIONS + help + This option enables the driver for the PWM controller found on the + T-HEAD TH1520 SoC. + + 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..a410747095327a315a6bcd24ae3= 43ce7857fe323 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -66,6 +66,7 @@ obj-$(CONFIG_PWM_STMPE) +=3D pwm-stmpe.o obj-$(CONFIG_PWM_SUN4I) +=3D pwm-sun4i.o obj-$(CONFIG_PWM_SUNPLUS) +=3D pwm-sunplus.o obj-$(CONFIG_PWM_TEGRA) +=3D pwm-tegra.o +obj-$(CONFIG_PWM_TH1520) +=3D pwm_th1520.o obj-$(CONFIG_PWM_TIECAP) +=3D pwm-tiecap.o obj-$(CONFIG_PWM_TIEHRPWM) +=3D pwm-tiehrpwm.o obj-$(CONFIG_PWM_TWL) +=3D pwm-twl.o diff --git a/drivers/pwm/pwm_th1520.rs b/drivers/pwm/pwm_th1520.rs new file mode 100644 index 0000000000000000000000000000000000000000..a77c45cef9cf8f02a25db9d42c4= 5cd0df565b0ec --- /dev/null +++ b/drivers/pwm/pwm_th1520.rs @@ -0,0 +1,318 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2025 Samsung Electronics Co., Ltd. +// Author: Michal Wilczynski + +//! Rust T-HEAD TH1520 PWM driver +//! +//! Limitations: +//! - The period and duty cycle are controlled by 32-bit hardware register= s, +//! limiting the maximum resolution. +//! - The driver supports continuous output mode only; one-shot mode is not +//! implemented. +//! - The controller hardware provides up to 6 PWM channels. +//! + +use core::ops::Deref; +use kernel::{ + c_str, + clk::Clk, + device::{Bound, Core, Device}, + devres, + io::mem::IoMem, + 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; + +fn ns_to_cycles(ns: u64, rate_hz: u64) -> u64 { + const NSEC_PER_SEC_U64: u64 =3D time::NSEC_PER_SEC as u64; + + match ns.checked_mul(rate_hz) { + Some(product) =3D> product / NSEC_PER_SEC_U64, + None =3D> u64::MAX, + } +} + +fn cycles_to_ns(cycles: u64, rate_hz: u64) -> u64 { + const NSEC_PER_SEC_U64: u64 =3D time::NSEC_PER_SEC as u64; + + // Round up + let Some(numerator) =3D cycles + .checked_mul(NSEC_PER_SEC_U64) + .and_then(|p| p.checked_add(rate_hz - 1)) + else { + return u64::MAX; + }; + + numerator / rate_hz +} + +/// 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 round_waveform_tohw( + chip: &pwm::Chip, + _pwm: &pwm::Device, + wf: &pwm::Waveform, + ) -> Result<(c_int, Self::WfHw)> { + let data: &Self =3D chip.drvdata(); + + 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() as u64; + + let period_cycles =3D ns_to_cycles(wf.period_length_ns, rate_hz).m= in(u32::MAX as u64); + let mut duty_cycles =3D ns_to_cycles(wf.duty_length_ns, rate_hz).m= in(u32::MAX as u64); + + let mut ctrl_val =3D PWM_CONTINUOUS_MODE; + + if wf.duty_offset_ns =3D=3D 0 { + ctrl_val |=3D PWM_FPOUT; + } else { + duty_cycles =3D period_cycles - duty_cycles; + } + + let wfhw =3D Th1520WfHw { + period_cycles: period_cycles as u32, + duty_cycles: duty_cycles as u32, + ctrl_val, + enabled: true, + }; + + dev_dbg!( + chip.device(), + "Requested: period {}ns, duty {}ns, offset {}ns -> HW: period = {} cyc, duty {} cyc, ctrl 0x{:x}\n", + wf.period_length_ns, + wf.duty_length_ns, + wf.duty_offset_ns, + wfhw.period_cycles, + wfhw.duty_cycles, + wfhw.ctrl_val + ); + + Ok((0, wfhw)) + } + + fn round_waveform_fromhw( + chip: &pwm::Chip, + _pwm: &pwm::Device, + wfhw: &Self::WfHw, + wf: &mut pwm::Waveform, + ) -> Result { + let data: &Self =3D chip.drvdata(); + let rate_hz =3D data.clk.rate().as_hz() as u64; + + wf.period_length_ns =3D cycles_to_ns(wfhw.period_cycles as u64, ra= te_hz); + + let duty_cycles =3D wfhw.duty_cycles as u64; + + if (wfhw.ctrl_val & PWM_FPOUT) !=3D 0 { + wf.duty_length_ns =3D cycles_to_ns(duty_cycles, rate_hz); + wf.duty_offset_ns =3D 0; + } else { + let period_cycles =3D wfhw.period_cycles as u64; + let original_duty_cycles =3D period_cycles.saturating_sub(duty= _cycles); + + wf.duty_length_ns =3D cycles_to_ns(original_duty_cycles, rate_= hz); + // We can't recover the original non-zero offset, so we just s= et it + // to a representative non-zero value. + wf.duty_offset_ns =3D 1; + } + + Ok(0) + } + + fn read_waveform( + chip: &pwm::Chip, + pwm: &pwm::Device, + parent_dev: &Device, + ) -> Result { + let data: &Self =3D chip.drvdata(); + let hwpwm =3D pwm.hwpwm(); + let iomem_accessor =3D data.iomem.access(parent_dev)?; + let iomap =3D iomem_accessor.deref(); + + let ctrl =3D iomap.try_read32(th1520_pwm_ctrl(hwpwm))?; + let period_cycles =3D iomap.try_read32(th1520_pwm_per(hwpwm))?; + let duty_cycles =3D iomap.try_read32(th1520_pwm_fp(hwpwm))?; + + let wfhw =3D Th1520WfHw { + period_cycles, + duty_cycles, + ctrl_val: ctrl, + enabled: duty_cycles !=3D 0, + }; + + dev_dbg!( + chip.device(), + "PWM-{}: read_waveform: Read hw state - period: {}, duty: {}, = ctrl: 0x{:x}, enabled: {}", + hwpwm, + wfhw.period_cycles, + wfhw.duty_cycles, + wfhw.ctrl_val, + wfhw.enabled + ); + + Ok(wfhw) + } + + fn write_waveform( + chip: &pwm::Chip, + pwm: &pwm::Device, + wfhw: &Self::WfHw, + parent_dev: &Device, + ) -> Result { + let data: &Self =3D chip.drvdata(); + let hwpwm =3D pwm.hwpwm(); + let iomem_accessor =3D data.iomem.access(parent_dev)?; + let iomap =3D iomem_accessor.deref(); + let was_enabled =3D pwm.state().enabled(); + + if !wfhw.enabled { + if was_enabled { + iomap.try_write32(wfhw.ctrl_val, th1520_pwm_ctrl(hwpwm))?; + iomap.try_write32(0, th1520_pwm_fp(hwpwm))?; + iomap.try_write32(wfhw.ctrl_val | PWM_CFG_UPDATE, th1520_p= wm_ctrl(hwpwm))?; + } + return Ok(()); + } + + iomap.try_write32(wfhw.ctrl_val, th1520_pwm_ctrl(hwpwm))?; + iomap.try_write32(wfhw.period_cycles, th1520_pwm_per(hwpwm))?; + iomap.try_write32(wfhw.duty_cycles, th1520_pwm_fp(hwpwm))?; + iomap.try_write32(wfhw.ctrl_val | PWM_CFG_UPDATE, th1520_pwm_ctrl(= hwpwm))?; + + // The `PWM_START` bit must be written in a separate, final transa= ction, and + // only when enabling the channel from a disabled state. + if !was_enabled { + iomap.try_write32(wfhw.ctrl_val | PWM_START, th1520_pwm_ctrl(h= wpwm))?; + } + + dev_dbg!( + chip.device(), + "PWM-{}: Wrote (per: {}, duty: {})", + hwpwm, + wfhw.period_cycles, + wfhw.duty_cycles, + ); + + 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; + +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()?; + + // TODO: Get exclusive ownership of the clock to prevent rate chan= ges. + // The Rust equivalent of `clk_rate_exclusive_get()` is not yet av= ailable. + // This should be updated once it is implemented. + 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 drvdata =3D KBox::new(Th1520PwmDriverData { iomem, clk }, GFP_= KERNEL)?; + let chip =3D pwm::Chip::new(dev, MAX_PWM_NUM, 0, drvdata)?; + + pwm::Registration::new_foreign_owned(dev, chip, &TH1520_PWM_OPS)?; + + Ok(KBox::new(Th1520PwmPlatformDriver, GFP_KERNEL)?.into()) + } +} + +kernel::module_platform_driver! { + type: Th1520PwmPlatformDriver, + name: "pwm-th1520", + authors: ["Michal Wilczynski "], + description: "T-HEAD TH1520 PWM driver", + license: "GPL v2", +} --=20 2.34.1 From nobody Wed Oct 8 22:16:22 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 925CF2D4B5A for ; Mon, 23 Jun 2025 18:09:07 +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=1750702150; cv=none; b=VPn/uTX4egAs/SkjIfAp9LPKgp7nQe9JS6T7tVQyKhpv9SVNV7l9XqHkHTDpx/c4qFB9A0gOpkXKUBeGDP9OvWyxlkz/heK4XndbuX7bSpyHUQDPv+WhyYCFk5qqJ8fxlK0dUa6LzkQBKj8zUVY1AT37pO9rkxovX9XUFHs4hBw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750702150; c=relaxed/simple; bh=t5gAhXcbGZrJUALl7qia8n51ELevCqFMIQiyrK+I6FA=; h=From:Date:Subject:MIME-Version:Message-Id:In-Reply-To:To:Cc: Content-Type:References; b=bU0WAmOTAW12az81cTVHP6UMalFtTxyLbKrAvjZQXmjKg4nxbYPOm3AQ/eZBvPuvm80df/Y50BE+5RzxgW02L6WvjLEuGLQ+f8hnfebSbuWJf9nn1ai0ZbCQEFvVWWi8Wix+J8i/UsgDlX1JB8gpKomn5TTkgiQ6IRC8q4tjNLw= 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=tPQiCC0k; 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="tPQiCC0k" Received: from eucas1p1.samsung.com (unknown [182.198.249.206]) by mailout1.w1.samsung.com (KnoxPortal) with ESMTP id 20250623180904euoutp01c8d5c633827203e74b244777b2082fa1~LvlMb38km1009510095euoutp01B for ; Mon, 23 Jun 2025 18:09:04 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.w1.samsung.com 20250623180904euoutp01c8d5c633827203e74b244777b2082fa1~LvlMb38km1009510095euoutp01B DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1750702144; bh=trQX8cKrsBk4VTrpk3Inc8jpVfnsWnUW5rwO3OnDizQ=; h=From:Date:Subject:In-Reply-To:To:Cc:References:From; b=tPQiCC0k/EuUT2q53gZv1TUD24x8hpQQ4ixi7DBVEbVARgrYRAVVROtCIXpTRt0AA Iuwe+ii0iq8DO2TPvdO/VsKTj0BN8dlZL+3UmwnJl/Ma5cCJlmr5oXGukwjelzvzaH mKevpM7bCw7mq4IqpTDdxIGJTwQlsUdunRQwqdwY= Received: from eusmtip1.samsung.com (unknown [203.254.199.221]) by eucas1p2.samsung.com (KnoxPortal) with ESMTPA id 20250623180903eucas1p2d5d4397349bc0ed7c4fc912d243ef371~LvlLxDGlW0176401764eucas1p20; Mon, 23 Jun 2025 18:09:03 +0000 (GMT) Received: from AMDC4942.eu.corp.samsungelectronics.net (unknown [106.210.136.40]) by eusmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250623180902eusmtip123474fdb10d6d553fb26db7310ab7130~LvlKfbqwt0652006520eusmtip1s; Mon, 23 Jun 2025 18:09:02 +0000 (GMT) From: Michal Wilczynski Date: Mon, 23 Jun 2025 20:08:53 +0200 Subject: [PATCH v5 5/9] 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: <20250623-rust-next-pwm-working-fan-for-sending-v5-5-0ca23747c23e@samsung.com> In-Reply-To: <20250623-rust-next-pwm-working-fan-for-sending-v5-0-0ca23747c23e@samsung.com> To: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Michal Wilczynski , Drew Fustini , Guo Ren , Fu Wei , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Paul Walmsley , Palmer Dabbelt , Albert Ou , Alexandre Ghiti , Marek Szyprowski , Benno Lossin , Michael Turquette , Stephen Boyd , Benno Lossin Cc: linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-riscv@lists.infradead.org, devicetree@vger.kernel.org, linux-clk@vger.kernel.org X-Mailer: b4 0.15-dev X-CMS-MailID: 20250623180903eucas1p2d5d4397349bc0ed7c4fc912d243ef371 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" X-RootMTR: 20250623180903eucas1p2d5d4397349bc0ed7c4fc912d243ef371 X-EPHeader: CA X-CMS-RootMailID: 20250623180903eucas1p2d5d4397349bc0ed7c4fc912d243ef371 References: <20250623-rust-next-pwm-working-fan-for-sending-v5-0-0ca23747c23e@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. Link: https://lore.kernel.org/all/9e8a12db-236d-474c-b110-b3be96edf057@sams= ung.com/ [1] Reviewed-by: Drew Fustini Acked-by: Stephen Boyd Signed-off-by: Michal Wilczynski --- 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 Wed Oct 8 22:16:22 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 D6EA82D4B7A for ; Mon, 23 Jun 2025 18:09:07 +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=1750702150; cv=none; b=lEZDHAaYpGWjytbS0u9IN0R5OZinMsyWDdWPeP4I1DEBCYt0sR0Rn87hl5gZ15E6sMqY1ISGEt/+ndM5n99QzqQe8A5gE7U/JDG3VKhiwKW6Q3EGopReq2gFv+hsnakQKV4yXV36Fihc8rnciWUvFNCVSjCDqP4pdDV194lyldM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750702150; c=relaxed/simple; bh=j0ilmlWvuGDdPVedpFQAMvM2nuPCAwx7uovcp5sfOBM=; h=From:Date:Subject:MIME-Version:Message-Id:In-Reply-To:To:Cc: Content-Type:References; b=NM+BguDz+bGUdtHetOCnqYNkmfvET2LyLikwTnupTQO1W7VeoDzMgO1r69ymBUQY9e2jATOaR/SsbOrRhnqz5bh+gkLuARDayFW9Afo96cMgvIUn5Q3118GdngY7Xj+ZjRgW4BXVhijXKoV2D0tJiLnbpwo+kGryqZEw4/39uBU= 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=GHpIjf+S; 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="GHpIjf+S" Received: from eucas1p1.samsung.com (unknown [182.198.249.206]) by mailout1.w1.samsung.com (KnoxPortal) with ESMTP id 20250623180906euoutp01d52b33d46d49c65f4cda0b229fa956fa~LvlOM9RR70981509815euoutp01W for ; Mon, 23 Jun 2025 18:09:06 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.w1.samsung.com 20250623180906euoutp01d52b33d46d49c65f4cda0b229fa956fa~LvlOM9RR70981509815euoutp01W DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1750702146; bh=fxnoWq4OQxd1ZCOVWNMF+siSVgYpXr1G0SglMgA6Kss=; h=From:Date:Subject:In-Reply-To:To:Cc:References:From; b=GHpIjf+SUtMankg1fbnQ/AgoEi3gyvSeq6ORdlkV/ymD+3XeVu7rH/S77LEWC1I4Z KImR67J7RxaPblNITdxs3mn/Ia+0BeMyWrtL4Hq8H5u3DTf5gnjYMzuEE3DjHH4D7h 6qejFXikWzu5Pd3S6bM6b8V7vZUuUP6v2lt7MerE= Received: from eusmtip1.samsung.com (unknown [203.254.199.221]) by eucas1p1.samsung.com (KnoxPortal) with ESMTPA id 20250623180904eucas1p14ef216c771b51f15c2abde0761f403d1~LvlM4NKyB1351513515eucas1p1x; Mon, 23 Jun 2025 18:09:04 +0000 (GMT) Received: from AMDC4942.eu.corp.samsungelectronics.net (unknown [106.210.136.40]) by eusmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250623180903eusmtip115509ed09dbc6b2279198159e31cb4a0~LvlLs1wn13197731977eusmtip1R; Mon, 23 Jun 2025 18:09:03 +0000 (GMT) From: Michal Wilczynski Date: Mon, 23 Jun 2025 20:08:54 +0200 Subject: [PATCH v5 6/9] 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: <20250623-rust-next-pwm-working-fan-for-sending-v5-6-0ca23747c23e@samsung.com> In-Reply-To: <20250623-rust-next-pwm-working-fan-for-sending-v5-0-0ca23747c23e@samsung.com> To: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Michal Wilczynski , Drew Fustini , Guo Ren , Fu Wei , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Paul Walmsley , Palmer Dabbelt , Albert Ou , Alexandre Ghiti , Marek Szyprowski , Benno Lossin , Michael Turquette , Stephen Boyd , Benno Lossin Cc: linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-riscv@lists.infradead.org, devicetree@vger.kernel.org, linux-clk@vger.kernel.org, Krzysztof Kozlowski X-Mailer: b4 0.15-dev X-CMS-MailID: 20250623180904eucas1p14ef216c771b51f15c2abde0761f403d1 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" X-RootMTR: 20250623180904eucas1p14ef216c771b51f15c2abde0761f403d1 X-EPHeader: CA X-CMS-RootMailID: 20250623180904eucas1p14ef216c771b51f15c2abde0761f403d1 References: <20250623-rust-next-pwm-working-fan-for-sending-v5-0-0ca23747c23e@samsung.com> Add the Device Tree binding documentation for the T-HEAD TH1520 SoC PWM controller. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Michal Wilczynski --- .../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 879870471e86dcec4a0e8f5c45d2cc3409411fdd..8a55e8d88394a269aa152e80a03= c439a36e6062e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21394,6 +21394,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 Wed Oct 8 22:16:22 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 A53A32D5425 for ; Mon, 23 Jun 2025 18:09:08 +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=1750702151; cv=none; b=gaO+m010bZQ3C8kPwI6f4z3QD/RLNeE8INEtnXGh5/5dfUH3ygTjx5U8D9mvDZiBpOFvvF14c/hgKIqExgHLuLiofSOToIG1UYvAH1p1izGABOchGWrQ5hXOYsqa2cN2yBMbEofqsHtL/lnp38+bT00iIpHAS12GEYWc3ebJmiU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750702151; 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=WZMbdNX8t4jHsXArOB+N09p0BAiae+Bma5daFZbidp2IEakKErPIhtwzKF9NRVitvR0MrmRZhPfDAEjIkBKn5o7EBLPE7F5jY8P7kRUaaD9HRmJUmRKcCG53wPl3aZajzk/BDx8E3PosQGTT5vbmIG0gHnS684Z3uz+siqY+GsA= 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=rWH7m2oy; 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="rWH7m2oy" Received: from eucas1p2.samsung.com (unknown [182.198.249.207]) by mailout1.w1.samsung.com (KnoxPortal) with ESMTP id 20250623180906euoutp0131d92e96bdf03d019d119eaa40d486ed~LvlOwFMiC0365003650euoutp01_ for ; Mon, 23 Jun 2025 18:09:06 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.w1.samsung.com 20250623180906euoutp0131d92e96bdf03d019d119eaa40d486ed~LvlOwFMiC0365003650euoutp01_ DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1750702146; bh=tb9Nm7oCBfF8pKU9GqtVQRGVuCwKCYbxFHqowy9m/jM=; h=From:Date:Subject:In-Reply-To:To:Cc:References:From; b=rWH7m2oyN6gu98wJozSlJjltEr2Srv/0tSQhdLx1p8jiau87ZxsDYuWbaH6GEy4Vk Rh6wxYOGORtg/WUSOXO8rzeWvCU0NNt4QhvV9ErtyIGIE2LFxp9vp2X8ovSnQF+DOV 7ra6hz5YuyiqAnsMCEayPdcumjYH8e2TpMUY+MHQ= Received: from eusmtip1.samsung.com (unknown [203.254.199.221]) by eucas1p2.samsung.com (KnoxPortal) with ESMTPA id 20250623180906eucas1p2e4fdbfd01056269518796549c61f1687~LvlOFjew21026710267eucas1p2P; Mon, 23 Jun 2025 18:09:06 +0000 (GMT) Received: from AMDC4942.eu.corp.samsungelectronics.net (unknown [106.210.136.40]) by eusmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250623180904eusmtip18d1815e5ee3ac2b028b0d05e32954a12~LvlM8Cnx-3150531505eusmtip1y; Mon, 23 Jun 2025 18:09:04 +0000 (GMT) From: Michal Wilczynski Date: Mon, 23 Jun 2025 20:08:55 +0200 Subject: [PATCH v5 7/9] 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: <20250623-rust-next-pwm-working-fan-for-sending-v5-7-0ca23747c23e@samsung.com> In-Reply-To: <20250623-rust-next-pwm-working-fan-for-sending-v5-0-0ca23747c23e@samsung.com> To: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Michal Wilczynski , Drew Fustini , Guo Ren , Fu Wei , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Paul Walmsley , Palmer Dabbelt , Albert Ou , Alexandre Ghiti , Marek Szyprowski , Benno Lossin , Michael Turquette , Stephen Boyd , Benno Lossin Cc: linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-riscv@lists.infradead.org, devicetree@vger.kernel.org, linux-clk@vger.kernel.org X-Mailer: b4 0.15-dev X-CMS-MailID: 20250623180906eucas1p2e4fdbfd01056269518796549c61f1687 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" X-RootMTR: 20250623180906eucas1p2e4fdbfd01056269518796549c61f1687 X-EPHeader: CA X-CMS-RootMailID: 20250623180906eucas1p2e4fdbfd01056269518796549c61f1687 References: <20250623-rust-next-pwm-working-fan-for-sending-v5-0-0ca23747c23e@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 Wed Oct 8 22:16:22 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 8A45E4A24 for ; Mon, 23 Jun 2025 18:09:09 +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=1750702152; cv=none; b=MkIkzivG6gvU3Q2VnSJbaRizBkjhcnMCksyVq997K3J7c3zKo6S5jYBi0phTa9ErSgu5lYmyQWxl1wTvykGzEKQB4B3XQuH+NfLU9cblbQ3AaMLxnMjUQ9uEkRqeUaHuSKoJrXtkjfwSiw82FfISjaNi98DEVmcJKlLZifYTpIw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750702152; c=relaxed/simple; bh=N+2CmuFEvaFrCFvEiZxIZGe87T5yZ+GUUMGzk60ZImg=; h=From:Date:Subject:MIME-Version:Message-Id:In-Reply-To:To:Cc: Content-Type:References; b=jIvxxCAx/A/I7omnuzV8XZC6mCGWhETB1UZNsyXCsHQM9DRSqcqILoOdDh+VYJEsJTS1OUrwedmXOYdrI2b57hl1TjJRm+hxzTQrNZscqqtuxSxt4PEzKdNdNhMwBIKjwfJBneBvi7vZ5mc63nkVhGd6N7v3iGLL4SU8jECqTss= 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=HvJJlnYw; 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="HvJJlnYw" Received: from eucas1p1.samsung.com (unknown [182.198.249.206]) by mailout1.w1.samsung.com (KnoxPortal) with ESMTP id 20250623180908euoutp0165f00cf3e4cbda4c826ebc0f0899ab82~LvlP3HsPj1012910129euoutp01D for ; Mon, 23 Jun 2025 18:09:08 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.w1.samsung.com 20250623180908euoutp0165f00cf3e4cbda4c826ebc0f0899ab82~LvlP3HsPj1012910129euoutp01D DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1750702148; bh=ZbpbiGCkq+bc/Hnq9ouJryKcDkGL17+XHPFX3q85en0=; h=From:Date:Subject:In-Reply-To:To:Cc:References:From; b=HvJJlnYw91mtQTslU9pSyW75IO0Fp81cDOHpetm1Ept0i5LkqjQkF/AQ4JLCNGnJ6 232q5tJ5RFVt/r7QtjsMIS4sUz9cvivTi++3xLVknKqevr/WEnmigntHDJDJKzQvrW RjGh5Gcp//0uA+B6zT8EEkENM4JmAb7J4QfcR2I4= Received: from eusmtip1.samsung.com (unknown [203.254.199.221]) by eucas1p1.samsung.com (KnoxPortal) with ESMTPA id 20250623180907eucas1p10c0ca6b667debcc8139402d97e4ef800~LvlPST4yO1788417884eucas1p1w; Mon, 23 Jun 2025 18:09:07 +0000 (GMT) Received: from AMDC4942.eu.corp.samsungelectronics.net (unknown [106.210.136.40]) by eusmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250623180906eusmtip1c80b1909abb3da9ce10cdb023e99b4c6~LvlOJr2CH2097520975eusmtip1F; Mon, 23 Jun 2025 18:09:06 +0000 (GMT) From: Michal Wilczynski Date: Mon, 23 Jun 2025 20:08:56 +0200 Subject: [PATCH v5 8/9] 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: <20250623-rust-next-pwm-working-fan-for-sending-v5-8-0ca23747c23e@samsung.com> In-Reply-To: <20250623-rust-next-pwm-working-fan-for-sending-v5-0-0ca23747c23e@samsung.com> To: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Michal Wilczynski , Drew Fustini , Guo Ren , Fu Wei , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Paul Walmsley , Palmer Dabbelt , Albert Ou , Alexandre Ghiti , Marek Szyprowski , Benno Lossin , Michael Turquette , Stephen Boyd , Benno Lossin Cc: linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-riscv@lists.infradead.org, devicetree@vger.kernel.org, linux-clk@vger.kernel.org X-Mailer: b4 0.15-dev X-CMS-MailID: 20250623180907eucas1p10c0ca6b667debcc8139402d97e4ef800 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" X-RootMTR: 20250623180907eucas1p10c0ca6b667debcc8139402d97e4ef800 X-EPHeader: CA X-CMS-RootMailID: 20250623180907eucas1p10c0ca6b667debcc8139402d97e4ef800 References: <20250623-rust-next-pwm-working-fan-for-sending-v5-0-0ca23747c23e@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 Wed Oct 8 22:16:22 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 40B992D5C81 for ; Mon, 23 Jun 2025 18:09:11 +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=1750702153; cv=none; b=SQso0PbRQZdK4qCE38Is3gTqm22P4yY/TLFTnIHL4kfvFHOz7g8sHg81hAqPpHQoQopxDEoUF9rmheGyxXt0P0XBEPUNSWtekNlZg6eOlv8cPMdCM89+PNFgfElZyEGNjtvPVqNAc54JtXNOYdTEU6c1NCIKTbhYQkQI9e9C3bQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1750702153; c=relaxed/simple; bh=pQpdhYmfxqh3Yfxmei5sPv11PaCzGkgZVRufLVaDNj8=; h=From:Date:Subject:MIME-Version:Message-Id:In-Reply-To:To:Cc: Content-Type:References; b=EGabdtuSJRosDXbc/t2tmXmq5d5eVBDB9E/wg/r8tmQs5JUVoG8QEPLRAQFcKh7qjmagpBKT1tWahFNbVfcAIdsW2K5t/TX/DMU0AXYoqjI4RqcKqrBSo0DXpvpUS7GEYe87tzBJlwyYhGwOWaJThs0IEJKgSjFs4kyfEpuN3mc= 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=l4i87764; 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="l4i87764" Received: from eucas1p1.samsung.com (unknown [182.198.249.206]) by mailout1.w1.samsung.com (KnoxPortal) with ESMTP id 20250623180909euoutp01d4def5fd6d0155fe17b38a99373ae836~LvlRltErd0365003650euoutp01D for ; Mon, 23 Jun 2025 18:09:09 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 mailout1.w1.samsung.com 20250623180909euoutp01d4def5fd6d0155fe17b38a99373ae836~LvlRltErd0365003650euoutp01D DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=samsung.com; s=mail20170921; t=1750702149; bh=Thc/4MnT4r5I8ag38cbivyQhpbuX345C1nEB0lzCSSU=; h=From:Date:Subject:In-Reply-To:To:Cc:References:From; b=l4i87764c4t/HnfCZ+5UFqdEXrcuyOHrgrER0xN/t/TVnHg4hlvR9qO570ZnZVc/L kvcuvLqzLNdTL2k6/aby4ydB5Ov+RPGcXiu5/em1DLzZR5NEtQSjidLcpUUo8Yx3tA y0pRn2r81/6FZWiAguXjBeicofEge1/Q4QfiGz84= Received: from eusmtip1.samsung.com (unknown [203.254.199.221]) by eucas1p1.samsung.com (KnoxPortal) with ESMTPA id 20250623180908eucas1p1a3494bba009ff1d7b7d5d59f915e9927~LvlQeI11R2898428984eucas1p1Y; Mon, 23 Jun 2025 18:09:08 +0000 (GMT) Received: from AMDC4942.eu.corp.samsungelectronics.net (unknown [106.210.136.40]) by eusmtip1.samsung.com (KnoxPortal) with ESMTPA id 20250623180907eusmtip10853b0d9e99b673eabdf4475ccf85ae0~LvlPWboOD3150531505eusmtip1z; Mon, 23 Jun 2025 18:09:07 +0000 (GMT) From: Michal Wilczynski Date: Mon, 23 Jun 2025 20:08:57 +0200 Subject: [PATCH v5 9/9] 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: <20250623-rust-next-pwm-working-fan-for-sending-v5-9-0ca23747c23e@samsung.com> In-Reply-To: <20250623-rust-next-pwm-working-fan-for-sending-v5-0-0ca23747c23e@samsung.com> To: =?utf-8?q?Uwe_Kleine-K=C3=B6nig?= , Miguel Ojeda , Alex Gaynor , Boqun Feng , Gary Guo , =?utf-8?q?Bj=C3=B6rn_Roy_Baron?= , Andreas Hindborg , Alice Ryhl , Trevor Gross , Danilo Krummrich , Michal Wilczynski , Drew Fustini , Guo Ren , Fu Wei , Rob Herring , Krzysztof Kozlowski , Conor Dooley , Paul Walmsley , Palmer Dabbelt , Albert Ou , Alexandre Ghiti , Marek Szyprowski , Benno Lossin , Michael Turquette , Stephen Boyd , Benno Lossin Cc: linux-kernel@vger.kernel.org, linux-pwm@vger.kernel.org, rust-for-linux@vger.kernel.org, linux-riscv@lists.infradead.org, devicetree@vger.kernel.org, linux-clk@vger.kernel.org X-Mailer: b4 0.15-dev X-CMS-MailID: 20250623180908eucas1p1a3494bba009ff1d7b7d5d59f915e9927 X-Msg-Generator: CA Content-Type: text/plain; charset="utf-8" X-RootMTR: 20250623180908eucas1p1a3494bba009ff1d7b7d5d59f915e9927 X-EPHeader: CA X-CMS-RootMailID: 20250623180908eucas1p1a3494bba009ff1d7b7d5d59f915e9927 References: <20250623-rust-next-pwm-working-fan-for-sending-v5-0-0ca23747c23e@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