···758758 To compile this driver as a module, choose M here: the module759759 will be called pwm-tegra.760760761761+config PWM_TH1520762762+ tristate "TH1520 PWM support"763763+ depends on RUST764764+ select RUST_PWM_ABSTRACTIONS765765+ help766766+ This option enables the driver for the PWM controller found on the767767+ T-HEAD TH1520 SoC.768768+769769+ To compile this driver as a module, choose M here; the module770770+ will be called pwm-th1520. If you are unsure, say N.771771+761772config PWM_TIECAP762773 tristate "ECAP PWM support"763774 depends on ARCH_OMAP2PLUS || ARCH_DAVINCI_DA8XX || ARCH_KEYSTONE || ARCH_K3 || COMPILE_TEST···839828840829 To compile this driver as a module, choose M here: the module841830 will be called pwm-xilinx.831831+832832+ config RUST_PWM_ABSTRACTIONS833833+ bool834834+ depends on RUST835835+ help836836+ This option enables the safe Rust abstraction layer for the PWM837837+ subsystem. It provides idiomatic wrappers and traits necessary for838838+ writing PWM controller drivers in Rust.839839+840840+ The abstractions handle resource management (like memory and reference841841+ counting) and provide safe interfaces to the underlying C core,842842+ allowing driver logic to be written in safe Rust.842843843844endif
···11+// SPDX-License-Identifier: GPL-2.022+// Copyright (c) 2025 Samsung Electronics Co., Ltd.33+// Author: Michal Wilczynski <m.wilczynski@samsung.com>44+55+//! Rust T-HEAD TH1520 PWM driver66+//!77+//! Limitations:88+//! - The period and duty cycle are controlled by 32-bit hardware registers,99+//! limiting the maximum resolution.1010+//! - The driver supports continuous output mode only; one-shot mode is not1111+//! implemented.1212+//! - The controller hardware provides up to 6 PWM channels.1313+//! - Reconfiguration is glitch free - new period and duty cycle values are1414+//! latched and take effect at the start of the next period.1515+//! - Polarity is handled via a simple hardware inversion bit; arbitrary1616+//! duty cycle offsets are not supported.1717+//! - Disabling a channel is achieved by configuring its duty cycle to zero to1818+//! produce a static low output. Clearing the `start` does not reliably1919+//! force the static inactive level defined by the `INACTOUT` bit. Hence2020+//! this method is not used in this driver.2121+//!2222+2323+use core::ops::Deref;2424+use kernel::{2525+ c_str,2626+ clk::Clk,2727+ device::{Bound, Core, Device},2828+ devres,2929+ io::mem::IoMem,3030+ of, platform,3131+ prelude::*,3232+ pwm, time,3333+};3434+3535+const TH1520_MAX_PWM_NUM: u32 = 6;3636+3737+// Register offsets3838+const fn th1520_pwm_chn_base(n: u32) -> usize {3939+ (n * 0x20) as usize4040+}4141+4242+const fn th1520_pwm_ctrl(n: u32) -> usize {4343+ th1520_pwm_chn_base(n)4444+}4545+4646+const fn th1520_pwm_per(n: u32) -> usize {4747+ th1520_pwm_chn_base(n) + 0x084848+}4949+5050+const fn th1520_pwm_fp(n: u32) -> usize {5151+ th1520_pwm_chn_base(n) + 0x0c5252+}5353+5454+// Control register bits5555+const TH1520_PWM_START: u32 = 1 << 0;5656+const TH1520_PWM_CFG_UPDATE: u32 = 1 << 2;5757+const TH1520_PWM_CONTINUOUS_MODE: u32 = 1 << 5;5858+const TH1520_PWM_FPOUT: u32 = 1 << 8;5959+6060+const TH1520_PWM_REG_SIZE: usize = 0xB0;6161+6262+fn ns_to_cycles(ns: u64, rate_hz: u64) -> u64 {6363+ const NSEC_PER_SEC_U64: u64 = time::NSEC_PER_SEC as u64;6464+6565+ (match ns.checked_mul(rate_hz) {6666+ Some(product) => product,6767+ None => u64::MAX,6868+ }) / NSEC_PER_SEC_U646969+}7070+7171+fn cycles_to_ns(cycles: u64, rate_hz: u64) -> u64 {7272+ const NSEC_PER_SEC_U64: u64 = time::NSEC_PER_SEC as u64;7373+7474+ // TODO: Replace with a kernel helper like `mul_u64_u64_div_u64_roundup`7575+ // once available in Rust.7676+ let numerator = cycles7777+ .saturating_mul(NSEC_PER_SEC_U64)7878+ .saturating_add(rate_hz - 1);7979+8080+ numerator / rate_hz8181+}8282+8383+/// Hardware-specific waveform representation for TH1520.8484+#[derive(Copy, Clone, Debug, Default)]8585+struct Th1520WfHw {8686+ period_cycles: u32,8787+ duty_cycles: u32,8888+ ctrl_val: u32,8989+ enabled: bool,9090+}9191+9292+/// The driver's private data struct. It holds all necessary devres managed resources.9393+#[pin_data(PinnedDrop)]9494+struct Th1520PwmDriverData {9595+ #[pin]9696+ iomem: devres::Devres<IoMem<TH1520_PWM_REG_SIZE>>,9797+ clk: Clk,9898+}9999+100100+// This `unsafe` implementation is a temporary necessity because the underlying `kernel::clk::Clk`101101+// type does not yet expose `Send` and `Sync` implementations. This block should be removed102102+// as soon as the clock abstraction provides these guarantees directly.103103+// TODO: Remove those unsafe impl's when Clk will support them itself.104104+105105+// SAFETY: The `devres` framework requires the driver's private data to be `Send` and `Sync`.106106+// We can guarantee this because the PWM core synchronizes all callbacks, preventing concurrent107107+// access to the contained `iomem` and `clk` resources.108108+unsafe impl Send for Th1520PwmDriverData {}109109+110110+// SAFETY: The same reasoning applies as for `Send`. The PWM core's synchronization111111+// guarantees that it is safe for multiple threads to have shared access (`&self`)112112+// to the driver data during callbacks.113113+unsafe impl Sync for Th1520PwmDriverData {}114114+115115+impl pwm::PwmOps for Th1520PwmDriverData {116116+ type WfHw = Th1520WfHw;117117+118118+ fn round_waveform_tohw(119119+ chip: &pwm::Chip<Self>,120120+ _pwm: &pwm::Device,121121+ wf: &pwm::Waveform,122122+ ) -> Result<pwm::RoundedWaveform<Self::WfHw>> {123123+ let data = chip.drvdata();124124+ let mut status = 0;125125+126126+ if wf.period_length_ns == 0 {127127+ dev_dbg!(chip.device(), "Requested period is 0, disabling PWM.\n");128128+129129+ return Ok(pwm::RoundedWaveform {130130+ status: 0,131131+ hardware_waveform: Th1520WfHw {132132+ enabled: false,133133+ ..Default::default()134134+ },135135+ });136136+ }137137+138138+ let rate_hz = data.clk.rate().as_hz() as u64;139139+140140+ let mut period_cycles = ns_to_cycles(wf.period_length_ns, rate_hz).min(u64::from(u32::MAX));141141+142142+ if period_cycles == 0 {143143+ dev_dbg!(144144+ chip.device(),145145+ "Requested period {} ns is too small for clock rate {} Hz, rounding up.\n",146146+ wf.period_length_ns,147147+ rate_hz148148+ );149149+150150+ period_cycles = 1;151151+ status = 1;152152+ }153153+154154+ let mut duty_cycles = ns_to_cycles(wf.duty_length_ns, rate_hz).min(u64::from(u32::MAX));155155+156156+ let mut ctrl_val = TH1520_PWM_CONTINUOUS_MODE;157157+158158+ let is_inversed = wf.duty_length_ns > 0159159+ && wf.duty_offset_ns > 0160160+ && wf.duty_offset_ns >= wf.period_length_ns.saturating_sub(wf.duty_length_ns);161161+ if is_inversed {162162+ duty_cycles = period_cycles - duty_cycles;163163+ } else {164164+ ctrl_val |= TH1520_PWM_FPOUT;165165+ }166166+167167+ let wfhw = Th1520WfHw {168168+ // The cast is safe because the value was clamped with `.min(u64::from(u32::MAX))`.169169+ period_cycles: period_cycles as u32,170170+ duty_cycles: duty_cycles as u32,171171+ ctrl_val,172172+ enabled: true,173173+ };174174+175175+ dev_dbg!(176176+ chip.device(),177177+ "Requested: {}/{} ns [+{} ns] -> HW: {}/{} cycles, ctrl 0x{:x}, rate {} Hz\n",178178+ wf.duty_length_ns,179179+ wf.period_length_ns,180180+ wf.duty_offset_ns,181181+ wfhw.duty_cycles,182182+ wfhw.period_cycles,183183+ wfhw.ctrl_val,184184+ rate_hz185185+ );186186+187187+ Ok(pwm::RoundedWaveform {188188+ status,189189+ hardware_waveform: wfhw,190190+ })191191+ }192192+193193+ fn round_waveform_fromhw(194194+ chip: &pwm::Chip<Self>,195195+ _pwm: &pwm::Device,196196+ wfhw: &Self::WfHw,197197+ wf: &mut pwm::Waveform,198198+ ) -> Result {199199+ let data = chip.drvdata();200200+ let rate_hz = data.clk.rate().as_hz() as u64;201201+202202+ if wfhw.period_cycles == 0 {203203+ dev_dbg!(204204+ chip.device(),205205+ "HW state has zero period, reporting as disabled.\n"206206+ );207207+ *wf = pwm::Waveform::default();208208+ return Ok(());209209+ }210210+211211+ wf.period_length_ns = cycles_to_ns(u64::from(wfhw.period_cycles), rate_hz);212212+213213+ let duty_cycles = u64::from(wfhw.duty_cycles);214214+215215+ if (wfhw.ctrl_val & TH1520_PWM_FPOUT) != 0 {216216+ wf.duty_length_ns = cycles_to_ns(duty_cycles, rate_hz);217217+ wf.duty_offset_ns = 0;218218+ } else {219219+ let period_cycles = u64::from(wfhw.period_cycles);220220+ let original_duty_cycles = period_cycles.saturating_sub(duty_cycles);221221+222222+ // For an inverted signal, `duty_length_ns` is the high time (period - low_time).223223+ wf.duty_length_ns = cycles_to_ns(original_duty_cycles, rate_hz);224224+ // The offset is the initial low time, which is what the hardware register provides.225225+ wf.duty_offset_ns = cycles_to_ns(duty_cycles, rate_hz);226226+ }227227+228228+ Ok(())229229+ }230230+231231+ fn read_waveform(232232+ chip: &pwm::Chip<Self>,233233+ pwm: &pwm::Device,234234+ parent_dev: &Device<Bound>,235235+ ) -> Result<Self::WfHw> {236236+ let data = chip.drvdata();237237+ let hwpwm = pwm.hwpwm();238238+ let iomem_accessor = data.iomem.access(parent_dev)?;239239+ let iomap = iomem_accessor.deref();240240+241241+ let ctrl = iomap.try_read32(th1520_pwm_ctrl(hwpwm))?;242242+ let period_cycles = iomap.try_read32(th1520_pwm_per(hwpwm))?;243243+ let duty_cycles = iomap.try_read32(th1520_pwm_fp(hwpwm))?;244244+245245+ let wfhw = Th1520WfHw {246246+ period_cycles,247247+ duty_cycles,248248+ ctrl_val: ctrl,249249+ enabled: duty_cycles != 0,250250+ };251251+252252+ dev_dbg!(253253+ chip.device(),254254+ "PWM-{}: read_waveform: Read hw state - period: {}, duty: {}, ctrl: 0x{:x}, enabled: {}",255255+ hwpwm,256256+ wfhw.period_cycles,257257+ wfhw.duty_cycles,258258+ wfhw.ctrl_val,259259+ wfhw.enabled260260+ );261261+262262+ Ok(wfhw)263263+ }264264+265265+ fn write_waveform(266266+ chip: &pwm::Chip<Self>,267267+ pwm: &pwm::Device,268268+ wfhw: &Self::WfHw,269269+ parent_dev: &Device<Bound>,270270+ ) -> Result {271271+ let data = chip.drvdata();272272+ let hwpwm = pwm.hwpwm();273273+ let iomem_accessor = data.iomem.access(parent_dev)?;274274+ let iomap = iomem_accessor.deref();275275+ let duty_cycles = iomap.try_read32(th1520_pwm_fp(hwpwm))?;276276+ let was_enabled = duty_cycles != 0;277277+278278+ if !wfhw.enabled {279279+ dev_dbg!(chip.device(), "PWM-{}: Disabling channel.\n", hwpwm);280280+ if was_enabled {281281+ iomap.try_write32(wfhw.ctrl_val, th1520_pwm_ctrl(hwpwm))?;282282+ iomap.try_write32(0, th1520_pwm_fp(hwpwm))?;283283+ iomap.try_write32(284284+ wfhw.ctrl_val | TH1520_PWM_CFG_UPDATE,285285+ th1520_pwm_ctrl(hwpwm),286286+ )?;287287+ }288288+ return Ok(());289289+ }290290+291291+ iomap.try_write32(wfhw.ctrl_val, th1520_pwm_ctrl(hwpwm))?;292292+ iomap.try_write32(wfhw.period_cycles, th1520_pwm_per(hwpwm))?;293293+ iomap.try_write32(wfhw.duty_cycles, th1520_pwm_fp(hwpwm))?;294294+ iomap.try_write32(295295+ wfhw.ctrl_val | TH1520_PWM_CFG_UPDATE,296296+ th1520_pwm_ctrl(hwpwm),297297+ )?;298298+299299+ // The `TH1520_PWM_START` bit must be written in a separate, final transaction, and300300+ // only when enabling the channel from a disabled state.301301+ if !was_enabled {302302+ iomap.try_write32(wfhw.ctrl_val | TH1520_PWM_START, th1520_pwm_ctrl(hwpwm))?;303303+ }304304+305305+ dev_dbg!(306306+ chip.device(),307307+ "PWM-{}: Wrote {}/{} cycles",308308+ hwpwm,309309+ wfhw.duty_cycles,310310+ wfhw.period_cycles,311311+ );312312+313313+ Ok(())314314+ }315315+}316316+317317+#[pinned_drop]318318+impl PinnedDrop for Th1520PwmDriverData {319319+ fn drop(self: Pin<&mut Self>) {320320+ self.clk.disable_unprepare();321321+ }322322+}323323+324324+struct Th1520PwmPlatformDriver;325325+326326+kernel::of_device_table!(327327+ OF_TABLE,328328+ MODULE_OF_TABLE,329329+ <Th1520PwmPlatformDriver as platform::Driver>::IdInfo,330330+ [(of::DeviceId::new(c_str!("thead,th1520-pwm")), ())]331331+);332332+333333+impl platform::Driver for Th1520PwmPlatformDriver {334334+ type IdInfo = ();335335+ const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);336336+337337+ fn probe(338338+ pdev: &platform::Device<Core>,339339+ _id_info: Option<&Self::IdInfo>,340340+ ) -> Result<Pin<KBox<Self>>> {341341+ let dev = pdev.as_ref();342342+ let request = pdev.io_request_by_index(0).ok_or(ENODEV)?;343343+344344+ let clk = Clk::get(dev, None)?;345345+346346+ clk.prepare_enable()?;347347+348348+ // TODO: Get exclusive ownership of the clock to prevent rate changes.349349+ // The Rust equivalent of `clk_rate_exclusive_get()` is not yet available.350350+ // This should be updated once it is implemented.351351+ let rate_hz = clk.rate().as_hz();352352+ if rate_hz == 0 {353353+ dev_err!(dev, "Clock rate is zero\n");354354+ return Err(EINVAL);355355+ }356356+357357+ if rate_hz > time::NSEC_PER_SEC as usize {358358+ dev_err!(359359+ dev,360360+ "Clock rate {} Hz is too high, not supported.\n",361361+ rate_hz362362+ );363363+ return Err(EINVAL);364364+ }365365+366366+ let chip = pwm::Chip::new(367367+ dev,368368+ TH1520_MAX_PWM_NUM,369369+ try_pin_init!(Th1520PwmDriverData {370370+ iomem <- request.iomap_sized::<TH1520_PWM_REG_SIZE>(),371371+ clk <- clk,372372+ }),373373+ )?;374374+375375+ pwm::Registration::register(dev, chip)?;376376+377377+ Ok(KBox::new(Th1520PwmPlatformDriver, GFP_KERNEL)?.into())378378+ }379379+}380380+381381+kernel::module_pwm_platform_driver! {382382+ type: Th1520PwmPlatformDriver,383383+ name: "pwm-th1520",384384+ authors: ["Michal Wilczynski <m.wilczynski@samsung.com>"],385385+ description: "T-HEAD TH1520 PWM driver",386386+ license: "GPL v2",387387+}
+6
include/linux/pwm.h
···488488#define pwmchip_add(chip) __pwmchip_add(chip, THIS_MODULE)489489void pwmchip_remove(struct pwm_chip *chip);490490491491+/*492492+ * For FFI wrapper use only:493493+ * The Rust PWM abstraction needs this to properly free the pwm_chip.494494+ */495495+void pwmchip_release(struct device *dev);496496+491497int __devm_pwmchip_add(struct device *dev, struct pwm_chip *chip, struct module *owner);492498#define devm_pwmchip_add(dev, chip) __devm_pwmchip_add(dev, chip, THIS_MODULE)493499
···121121pub mod print;122122pub mod processor;123123pub mod ptr;124124+#[cfg(CONFIG_RUST_PWM_ABSTRACTIONS)]125125+pub mod pwm;124126pub mod rbtree;125127pub mod regulator;126128pub mod revocable;
+735
rust/kernel/pwm.rs
···11+// SPDX-License-Identifier: GPL-2.022+// Copyright (c) 2025 Samsung Electronics Co., Ltd.33+// Author: Michal Wilczynski <m.wilczynski@samsung.com>44+55+//! PWM subsystem abstractions.66+//!77+//! C header: [`include/linux/pwm.h`](srctree/include/linux/pwm.h).88+99+use crate::{1010+ bindings,1111+ container_of,1212+ device::{self, Bound},1313+ devres,1414+ error::{self, to_result},1515+ prelude::*,1616+ types::{ARef, AlwaysRefCounted, Opaque}, //1717+};1818+use core::{marker::PhantomData, ptr::NonNull};1919+2020+/// Represents a PWM waveform configuration.2121+/// Mirrors struct [`struct pwm_waveform`](srctree/include/linux/pwm.h).2222+#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]2323+pub struct Waveform {2424+ /// Total duration of one complete PWM cycle, in nanoseconds.2525+ pub period_length_ns: u64,2626+2727+ /// Duty-cycle active time, in nanoseconds.2828+ ///2929+ /// For a typical normal polarity configuration (active-high) this is the3030+ /// high time of the signal.3131+ pub duty_length_ns: u64,3232+3333+ /// Duty-cycle start offset, in nanoseconds.3434+ ///3535+ /// Delay from the beginning of the period to the first active edge.3636+ /// In most simple PWM setups this is `0`, so the duty cycle starts3737+ /// immediately at each period’s start.3838+ pub duty_offset_ns: u64,3939+}4040+4141+impl From<bindings::pwm_waveform> for Waveform {4242+ fn from(wf: bindings::pwm_waveform) -> Self {4343+ Waveform {4444+ period_length_ns: wf.period_length_ns,4545+ duty_length_ns: wf.duty_length_ns,4646+ duty_offset_ns: wf.duty_offset_ns,4747+ }4848+ }4949+}5050+5151+impl From<Waveform> for bindings::pwm_waveform {5252+ fn from(wf: Waveform) -> Self {5353+ bindings::pwm_waveform {5454+ period_length_ns: wf.period_length_ns,5555+ duty_length_ns: wf.duty_length_ns,5656+ duty_offset_ns: wf.duty_offset_ns,5757+ }5858+ }5959+}6060+6161+/// Describes the outcome of a `round_waveform` operation.6262+#[derive(Debug, Clone, Copy, PartialEq, Eq)]6363+pub enum RoundingOutcome {6464+ /// The requested waveform was achievable exactly or by rounding values down.6565+ ExactOrRoundedDown,6666+6767+ /// The requested waveform could only be achieved by rounding up.6868+ RoundedUp,6969+}7070+7171+/// Wrapper for a PWM device [`struct pwm_device`](srctree/include/linux/pwm.h).7272+#[repr(transparent)]7373+pub struct Device(Opaque<bindings::pwm_device>);7474+7575+impl Device {7676+ /// Creates a reference to a [`Device`] from a valid C pointer.7777+ ///7878+ /// # Safety7979+ ///8080+ /// The caller must ensure that `ptr` is valid and remains valid for the lifetime of the8181+ /// returned [`Device`] reference.8282+ pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::pwm_device) -> &'a Self {8383+ // SAFETY: The safety requirements guarantee the validity of the dereference, while the8484+ // `Device` type being transparent makes the cast ok.8585+ unsafe { &*ptr.cast::<Self>() }8686+ }8787+8888+ /// Returns a raw pointer to the underlying `pwm_device`.8989+ fn as_raw(&self) -> *mut bindings::pwm_device {9090+ self.0.get()9191+ }9292+9393+ /// Gets the hardware PWM index for this device within its chip.9494+ pub fn hwpwm(&self) -> u32 {9595+ // SAFETY: `self.as_raw()` provides a valid pointer for `self`'s lifetime.9696+ unsafe { (*self.as_raw()).hwpwm }9797+ }9898+9999+ /// Gets a reference to the parent `Chip` that this device belongs to.100100+ pub fn chip<T: PwmOps>(&self) -> &Chip<T> {101101+ // SAFETY: `self.as_raw()` provides a valid pointer. (*self.as_raw()).chip102102+ // is assumed to be a valid pointer to `pwm_chip` managed by the kernel.103103+ // Chip::from_raw's safety conditions must be met.104104+ unsafe { Chip::<T>::from_raw((*self.as_raw()).chip) }105105+ }106106+107107+ /// Gets the label for this PWM device, if any.108108+ pub fn label(&self) -> Option<&CStr> {109109+ // SAFETY: self.as_raw() provides a valid pointer.110110+ let label_ptr = unsafe { (*self.as_raw()).label };111111+ if label_ptr.is_null() {112112+ return None;113113+ }114114+115115+ // SAFETY: label_ptr is non-null and points to a C string116116+ // managed by the kernel, valid for the lifetime of the PWM device.117117+ Some(unsafe { CStr::from_char_ptr(label_ptr) })118118+ }119119+120120+ /// Sets the PWM waveform configuration and enables the PWM signal.121121+ pub fn set_waveform(&self, wf: &Waveform, exact: bool) -> Result {122122+ let c_wf = bindings::pwm_waveform::from(*wf);123123+124124+ // SAFETY: `self.as_raw()` provides a valid `*mut pwm_device` pointer.125125+ // `&c_wf` is a valid pointer to a `pwm_waveform` struct. The C function126126+ // handles all necessary internal locking.127127+ let ret = unsafe { bindings::pwm_set_waveform_might_sleep(self.as_raw(), &c_wf, exact) };128128+ to_result(ret)129129+ }130130+131131+ /// Queries the hardware for the configuration it would apply for a given132132+ /// request.133133+ pub fn round_waveform(&self, wf: &mut Waveform) -> Result<RoundingOutcome> {134134+ let mut c_wf = bindings::pwm_waveform::from(*wf);135135+136136+ // SAFETY: `self.as_raw()` provides a valid `*mut pwm_device` pointer.137137+ // `&mut c_wf` is a valid pointer to a mutable `pwm_waveform` struct that138138+ // the C function will update.139139+ let ret = unsafe { bindings::pwm_round_waveform_might_sleep(self.as_raw(), &mut c_wf) };140140+141141+ to_result(ret)?;142142+143143+ *wf = Waveform::from(c_wf);144144+145145+ if ret == 1 {146146+ Ok(RoundingOutcome::RoundedUp)147147+ } else {148148+ Ok(RoundingOutcome::ExactOrRoundedDown)149149+ }150150+ }151151+152152+ /// Reads the current waveform configuration directly from the hardware.153153+ pub fn get_waveform(&self) -> Result<Waveform> {154154+ let mut c_wf = bindings::pwm_waveform::default();155155+156156+ // SAFETY: `self.as_raw()` is a valid pointer. We provide a valid pointer157157+ // to a stack-allocated `pwm_waveform` struct for the kernel to fill.158158+ let ret = unsafe { bindings::pwm_get_waveform_might_sleep(self.as_raw(), &mut c_wf) };159159+160160+ to_result(ret)?;161161+162162+ Ok(Waveform::from(c_wf))163163+ }164164+}165165+166166+/// The result of a `round_waveform_tohw` operation.167167+#[derive(Debug, Clone, Copy, PartialEq, Eq)]168168+pub struct RoundedWaveform<WfHw> {169169+ /// A status code, 0 for success or 1 if values were rounded up.170170+ pub status: c_int,171171+ /// The driver-specific hardware representation of the waveform.172172+ pub hardware_waveform: WfHw,173173+}174174+175175+/// Trait defining the operations for a PWM driver.176176+pub trait PwmOps: 'static + Sized {177177+ /// The driver-specific hardware representation of a waveform.178178+ ///179179+ /// This type must be [`Copy`], [`Default`], and fit within `PWM_WFHWSIZE`.180180+ type WfHw: Copy + Default;181181+182182+ /// Optional hook for when a PWM device is requested.183183+ fn request(_chip: &Chip<Self>, _pwm: &Device, _parent_dev: &device::Device<Bound>) -> Result {184184+ Ok(())185185+ }186186+187187+ /// Optional hook for capturing a PWM signal.188188+ fn capture(189189+ _chip: &Chip<Self>,190190+ _pwm: &Device,191191+ _result: &mut bindings::pwm_capture,192192+ _timeout: usize,193193+ _parent_dev: &device::Device<Bound>,194194+ ) -> Result {195195+ Err(ENOTSUPP)196196+ }197197+198198+ /// Convert a generic waveform to the hardware-specific representation.199199+ /// This is typically a pure calculation and does not perform I/O.200200+ fn round_waveform_tohw(201201+ _chip: &Chip<Self>,202202+ _pwm: &Device,203203+ _wf: &Waveform,204204+ ) -> Result<RoundedWaveform<Self::WfHw>> {205205+ Err(ENOTSUPP)206206+ }207207+208208+ /// Convert a hardware-specific representation back to a generic waveform.209209+ /// This is typically a pure calculation and does not perform I/O.210210+ fn round_waveform_fromhw(211211+ _chip: &Chip<Self>,212212+ _pwm: &Device,213213+ _wfhw: &Self::WfHw,214214+ _wf: &mut Waveform,215215+ ) -> Result {216216+ Err(ENOTSUPP)217217+ }218218+219219+ /// Read the current hardware configuration into the hardware-specific representation.220220+ fn read_waveform(221221+ _chip: &Chip<Self>,222222+ _pwm: &Device,223223+ _parent_dev: &device::Device<Bound>,224224+ ) -> Result<Self::WfHw> {225225+ Err(ENOTSUPP)226226+ }227227+228228+ /// Write a hardware-specific waveform configuration to the hardware.229229+ fn write_waveform(230230+ _chip: &Chip<Self>,231231+ _pwm: &Device,232232+ _wfhw: &Self::WfHw,233233+ _parent_dev: &device::Device<Bound>,234234+ ) -> Result {235235+ Err(ENOTSUPP)236236+ }237237+}238238+239239+/// Bridges Rust `PwmOps` to the C `pwm_ops` vtable.240240+struct Adapter<T: PwmOps> {241241+ _p: PhantomData<T>,242242+}243243+244244+impl<T: PwmOps> Adapter<T> {245245+ const VTABLE: PwmOpsVTable = create_pwm_ops::<T>();246246+247247+ /// # Safety248248+ ///249249+ /// `wfhw_ptr` must be valid for writes of `size_of::<T::WfHw>()` bytes.250250+ unsafe fn serialize_wfhw(wfhw: &T::WfHw, wfhw_ptr: *mut c_void) -> Result {251251+ let size = core::mem::size_of::<T::WfHw>();252252+253253+ build_assert!(size <= bindings::PWM_WFHWSIZE as usize);254254+255255+ // SAFETY: The caller ensures `wfhw_ptr` is valid for `size` bytes.256256+ unsafe {257257+ core::ptr::copy_nonoverlapping(258258+ core::ptr::from_ref::<T::WfHw>(wfhw).cast::<u8>(),259259+ wfhw_ptr.cast::<u8>(),260260+ size,261261+ );262262+ }263263+264264+ Ok(())265265+ }266266+267267+ /// # Safety268268+ ///269269+ /// `wfhw_ptr` must be valid for reads of `size_of::<T::WfHw>()` bytes.270270+ unsafe fn deserialize_wfhw(wfhw_ptr: *const c_void) -> Result<T::WfHw> {271271+ let size = core::mem::size_of::<T::WfHw>();272272+273273+ build_assert!(size <= bindings::PWM_WFHWSIZE as usize);274274+275275+ let mut wfhw = T::WfHw::default();276276+ // SAFETY: The caller ensures `wfhw_ptr` is valid for `size` bytes.277277+ unsafe {278278+ core::ptr::copy_nonoverlapping(279279+ wfhw_ptr.cast::<u8>(),280280+ core::ptr::from_mut::<T::WfHw>(&mut wfhw).cast::<u8>(),281281+ size,282282+ );283283+ }284284+285285+ Ok(wfhw)286286+ }287287+288288+ /// # Safety289289+ ///290290+ /// `dev` must be a valid pointer to a `bindings::device` embedded within a291291+ /// `bindings::pwm_chip`. This function is called by the device core when the292292+ /// last reference to the device is dropped.293293+ unsafe extern "C" fn release_callback(dev: *mut bindings::device) {294294+ // SAFETY: The function's contract guarantees that `dev` points to a `device`295295+ // field embedded within a valid `pwm_chip`. `container_of!` can therefore296296+ // safely calculate the address of the containing struct.297297+ let c_chip_ptr = unsafe { container_of!(dev, bindings::pwm_chip, dev) };298298+299299+ // SAFETY: `c_chip_ptr` is a valid pointer to a `pwm_chip` as established300300+ // above. Calling this FFI function is safe.301301+ let drvdata_ptr = unsafe { bindings::pwmchip_get_drvdata(c_chip_ptr) };302302+303303+ // SAFETY: The driver data was initialized in `new`. We run its destructor here.304304+ unsafe { core::ptr::drop_in_place(drvdata_ptr.cast::<T>()) };305305+306306+ // Now, call the original release function to free the `pwm_chip` itself.307307+ // SAFETY: `dev` is the valid pointer passed into this callback, which is308308+ // the expected argument for `pwmchip_release`.309309+ unsafe {310310+ bindings::pwmchip_release(dev);311311+ }312312+ }313313+314314+ /// # Safety315315+ ///316316+ /// Pointers from C must be valid.317317+ unsafe extern "C" fn request_callback(318318+ chip_ptr: *mut bindings::pwm_chip,319319+ pwm_ptr: *mut bindings::pwm_device,320320+ ) -> c_int {321321+ // SAFETY: PWM core guarentees `chip_ptr` and `pwm_ptr` are valid pointers.322322+ let (chip, pwm) = unsafe { (Chip::<T>::from_raw(chip_ptr), Device::from_raw(pwm_ptr)) };323323+324324+ // SAFETY: The PWM core guarantees the parent device exists and is bound during callbacks.325325+ let bound_parent = unsafe { chip.bound_parent_device() };326326+ match T::request(chip, pwm, bound_parent) {327327+ Ok(()) => 0,328328+ Err(e) => e.to_errno(),329329+ }330330+ }331331+332332+ /// # Safety333333+ ///334334+ /// Pointers from C must be valid.335335+ unsafe extern "C" fn capture_callback(336336+ chip_ptr: *mut bindings::pwm_chip,337337+ pwm_ptr: *mut bindings::pwm_device,338338+ res: *mut bindings::pwm_capture,339339+ timeout: usize,340340+ ) -> c_int {341341+ // SAFETY: Relies on the function's contract that `chip_ptr` and `pwm_ptr` are valid342342+ // pointers.343343+ let (chip, pwm, result) = unsafe {344344+ (345345+ Chip::<T>::from_raw(chip_ptr),346346+ Device::from_raw(pwm_ptr),347347+ &mut *res,348348+ )349349+ };350350+351351+ // SAFETY: The PWM core guarantees the parent device exists and is bound during callbacks.352352+ let bound_parent = unsafe { chip.bound_parent_device() };353353+ match T::capture(chip, pwm, result, timeout, bound_parent) {354354+ Ok(()) => 0,355355+ Err(e) => e.to_errno(),356356+ }357357+ }358358+359359+ /// # Safety360360+ ///361361+ /// Pointers from C must be valid.362362+ unsafe extern "C" fn round_waveform_tohw_callback(363363+ chip_ptr: *mut bindings::pwm_chip,364364+ pwm_ptr: *mut bindings::pwm_device,365365+ wf_ptr: *const bindings::pwm_waveform,366366+ wfhw_ptr: *mut c_void,367367+ ) -> c_int {368368+ // SAFETY: Relies on the function's contract that `chip_ptr` and `pwm_ptr` are valid369369+ // pointers.370370+ let (chip, pwm, wf) = unsafe {371371+ (372372+ Chip::<T>::from_raw(chip_ptr),373373+ Device::from_raw(pwm_ptr),374374+ Waveform::from(*wf_ptr),375375+ )376376+ };377377+ match T::round_waveform_tohw(chip, pwm, &wf) {378378+ Ok(rounded) => {379379+ // SAFETY: `wfhw_ptr` is valid per this function's safety contract.380380+ if unsafe { Self::serialize_wfhw(&rounded.hardware_waveform, wfhw_ptr) }.is_err() {381381+ return EINVAL.to_errno();382382+ }383383+ rounded.status384384+ }385385+ Err(e) => e.to_errno(),386386+ }387387+ }388388+389389+ /// # Safety390390+ ///391391+ /// Pointers from C must be valid.392392+ unsafe extern "C" fn round_waveform_fromhw_callback(393393+ chip_ptr: *mut bindings::pwm_chip,394394+ pwm_ptr: *mut bindings::pwm_device,395395+ wfhw_ptr: *const c_void,396396+ wf_ptr: *mut bindings::pwm_waveform,397397+ ) -> c_int {398398+ // SAFETY: Relies on the function's contract that `chip_ptr` and `pwm_ptr` are valid399399+ // pointers.400400+ let (chip, pwm) = unsafe { (Chip::<T>::from_raw(chip_ptr), Device::from_raw(pwm_ptr)) };401401+ // SAFETY: `deserialize_wfhw`'s safety contract is met by this function's contract.402402+ let wfhw = match unsafe { Self::deserialize_wfhw(wfhw_ptr) } {403403+ Ok(v) => v,404404+ Err(e) => return e.to_errno(),405405+ };406406+407407+ let mut rust_wf = Waveform::default();408408+ match T::round_waveform_fromhw(chip, pwm, &wfhw, &mut rust_wf) {409409+ Ok(()) => {410410+ // SAFETY: `wf_ptr` is guaranteed valid by the C caller.411411+ unsafe {412412+ *wf_ptr = rust_wf.into();413413+ };414414+ 0415415+ }416416+ Err(e) => e.to_errno(),417417+ }418418+ }419419+420420+ /// # Safety421421+ ///422422+ /// Pointers from C must be valid.423423+ unsafe extern "C" fn read_waveform_callback(424424+ chip_ptr: *mut bindings::pwm_chip,425425+ pwm_ptr: *mut bindings::pwm_device,426426+ wfhw_ptr: *mut c_void,427427+ ) -> c_int {428428+ // SAFETY: Relies on the function's contract that `chip_ptr` and `pwm_ptr` are valid429429+ // pointers.430430+ let (chip, pwm) = unsafe { (Chip::<T>::from_raw(chip_ptr), Device::from_raw(pwm_ptr)) };431431+432432+ // SAFETY: The PWM core guarantees the parent device exists and is bound during callbacks.433433+ let bound_parent = unsafe { chip.bound_parent_device() };434434+ match T::read_waveform(chip, pwm, bound_parent) {435435+ // SAFETY: `wfhw_ptr` is valid per this function's safety contract.436436+ Ok(wfhw) => match unsafe { Self::serialize_wfhw(&wfhw, wfhw_ptr) } {437437+ Ok(()) => 0,438438+ Err(e) => e.to_errno(),439439+ },440440+ Err(e) => e.to_errno(),441441+ }442442+ }443443+444444+ /// # Safety445445+ ///446446+ /// Pointers from C must be valid.447447+ unsafe extern "C" fn write_waveform_callback(448448+ chip_ptr: *mut bindings::pwm_chip,449449+ pwm_ptr: *mut bindings::pwm_device,450450+ wfhw_ptr: *const c_void,451451+ ) -> c_int {452452+ // SAFETY: Relies on the function's contract that `chip_ptr` and `pwm_ptr` are valid453453+ // pointers.454454+ let (chip, pwm) = unsafe { (Chip::<T>::from_raw(chip_ptr), Device::from_raw(pwm_ptr)) };455455+456456+ // SAFETY: The PWM core guarantees the parent device exists and is bound during callbacks.457457+ let bound_parent = unsafe { chip.bound_parent_device() };458458+459459+ // SAFETY: `wfhw_ptr` is valid per this function's safety contract.460460+ let wfhw = match unsafe { Self::deserialize_wfhw(wfhw_ptr) } {461461+ Ok(v) => v,462462+ Err(e) => return e.to_errno(),463463+ };464464+ match T::write_waveform(chip, pwm, &wfhw, bound_parent) {465465+ Ok(()) => 0,466466+ Err(e) => e.to_errno(),467467+ }468468+ }469469+}470470+471471+/// VTable structure wrapper for PWM operations.472472+/// Mirrors [`struct pwm_ops`](srctree/include/linux/pwm.h).473473+#[repr(transparent)]474474+pub struct PwmOpsVTable(bindings::pwm_ops);475475+476476+// SAFETY: PwmOpsVTable is Send. The vtable contains only function pointers477477+// and a size, which are simple data types that can be safely moved across478478+// threads. The thread-safety of calling these functions is handled by the479479+// kernel's locking mechanisms.480480+unsafe impl Send for PwmOpsVTable {}481481+482482+// SAFETY: PwmOpsVTable is Sync. The vtable is immutable after it is created,483483+// so it can be safely referenced and accessed concurrently by multiple threads484484+// e.g. to read the function pointers.485485+unsafe impl Sync for PwmOpsVTable {}486486+487487+impl PwmOpsVTable {488488+ /// Returns a raw pointer to the underlying `pwm_ops` struct.489489+ pub(crate) fn as_raw(&self) -> *const bindings::pwm_ops {490490+ &self.0491491+ }492492+}493493+494494+/// Creates a PWM operations vtable for a type `T` that implements `PwmOps`.495495+///496496+/// This is used to bridge Rust trait implementations to the C `struct pwm_ops`497497+/// expected by the kernel.498498+pub const fn create_pwm_ops<T: PwmOps>() -> PwmOpsVTable {499499+ // SAFETY: `core::mem::zeroed()` is unsafe. For `pwm_ops`, all fields are500500+ // `Option<extern "C" fn(...)>` or data, so a zeroed pattern (None/0) is valid initially.501501+ let mut ops: bindings::pwm_ops = unsafe { core::mem::zeroed() };502502+503503+ ops.request = Some(Adapter::<T>::request_callback);504504+ ops.capture = Some(Adapter::<T>::capture_callback);505505+506506+ ops.round_waveform_tohw = Some(Adapter::<T>::round_waveform_tohw_callback);507507+ ops.round_waveform_fromhw = Some(Adapter::<T>::round_waveform_fromhw_callback);508508+ ops.read_waveform = Some(Adapter::<T>::read_waveform_callback);509509+ ops.write_waveform = Some(Adapter::<T>::write_waveform_callback);510510+ ops.sizeof_wfhw = core::mem::size_of::<T::WfHw>();511511+512512+ PwmOpsVTable(ops)513513+}514514+515515+/// Wrapper for a PWM chip/controller ([`struct pwm_chip`](srctree/include/linux/pwm.h)).516516+#[repr(transparent)]517517+pub struct Chip<T: PwmOps>(Opaque<bindings::pwm_chip>, PhantomData<T>);518518+519519+impl<T: PwmOps> Chip<T> {520520+ /// Creates a reference to a [`Chip`] from a valid pointer.521521+ ///522522+ /// # Safety523523+ ///524524+ /// The caller must ensure that `ptr` is valid and remains valid for the lifetime of the525525+ /// returned [`Chip`] reference.526526+ pub(crate) unsafe fn from_raw<'a>(ptr: *mut bindings::pwm_chip) -> &'a Self {527527+ // SAFETY: The safety requirements guarantee the validity of the dereference, while the528528+ // `Chip` type being transparent makes the cast ok.529529+ unsafe { &*ptr.cast::<Self>() }530530+ }531531+532532+ /// Returns a raw pointer to the underlying `pwm_chip`.533533+ pub(crate) fn as_raw(&self) -> *mut bindings::pwm_chip {534534+ self.0.get()535535+ }536536+537537+ /// Gets the number of PWM channels (hardware PWMs) on this chip.538538+ pub fn num_channels(&self) -> u32 {539539+ // SAFETY: `self.as_raw()` provides a valid pointer for `self`'s lifetime.540540+ unsafe { (*self.as_raw()).npwm }541541+ }542542+543543+ /// Returns `true` if the chip supports atomic operations for configuration.544544+ pub fn is_atomic(&self) -> bool {545545+ // SAFETY: `self.as_raw()` provides a valid pointer for `self`'s lifetime.546546+ unsafe { (*self.as_raw()).atomic }547547+ }548548+549549+ /// Returns a reference to the embedded `struct device` abstraction.550550+ pub fn device(&self) -> &device::Device {551551+ // SAFETY:552552+ // - `self.as_raw()` provides a valid pointer to `bindings::pwm_chip`.553553+ // - The `dev` field is an instance of `bindings::device` embedded554554+ // within `pwm_chip`.555555+ // - Taking a pointer to this embedded field is valid.556556+ // - `device::Device` is `#[repr(transparent)]`.557557+ // - The lifetime of the returned reference is tied to `self`.558558+ unsafe { device::Device::from_raw(&raw mut (*self.as_raw()).dev) }559559+ }560560+561561+ /// Gets the typed driver specific data associated with this chip's embedded device.562562+ pub fn drvdata(&self) -> &T {563563+ // SAFETY: `pwmchip_get_drvdata` returns the pointer to the private data area,564564+ // which we know holds our `T`. The pointer is valid for the lifetime of `self`.565565+ unsafe { &*bindings::pwmchip_get_drvdata(self.as_raw()).cast::<T>() }566566+ }567567+568568+ /// Returns a reference to the parent device of this PWM chip's device.569569+ ///570570+ /// # Safety571571+ ///572572+ /// The caller must guarantee that the parent device exists and is bound.573573+ /// This is guaranteed by the PWM core during `PwmOps` callbacks.574574+ unsafe fn bound_parent_device(&self) -> &device::Device<Bound> {575575+ // SAFETY: Per the function's safety contract, the parent device exists.576576+ let parent = unsafe { self.device().parent().unwrap_unchecked() };577577+578578+ // SAFETY: Per the function's safety contract, the parent device is bound.579579+ // This is guaranteed by the PWM core during `PwmOps` callbacks.580580+ unsafe { parent.as_bound() }581581+ }582582+583583+ /// Allocates and wraps a PWM chip using `bindings::pwmchip_alloc`.584584+ ///585585+ /// Returns an [`ARef<Chip>`] managing the chip's lifetime via refcounting586586+ /// on its embedded `struct device`.587587+ pub fn new(588588+ parent_dev: &device::Device,589589+ num_channels: u32,590590+ data: impl pin_init::PinInit<T, Error>,591591+ ) -> Result<ARef<Self>> {592592+ let sizeof_priv = core::mem::size_of::<T>();593593+ // SAFETY: `pwmchip_alloc` allocates memory for the C struct and our private data.594594+ let c_chip_ptr_raw =595595+ unsafe { bindings::pwmchip_alloc(parent_dev.as_raw(), num_channels, sizeof_priv) };596596+597597+ let c_chip_ptr: *mut bindings::pwm_chip = error::from_err_ptr(c_chip_ptr_raw)?;598598+599599+ // SAFETY: The `drvdata` pointer is the start of the private area, which is where600600+ // we will construct our `T` object.601601+ let drvdata_ptr = unsafe { bindings::pwmchip_get_drvdata(c_chip_ptr) };602602+603603+ // SAFETY: We construct the `T` object in-place in the allocated private memory.604604+ unsafe { data.__pinned_init(drvdata_ptr.cast())? };605605+606606+ // SAFETY: `c_chip_ptr` points to a valid chip.607607+ unsafe {608608+ (*c_chip_ptr).dev.release = Some(Adapter::<T>::release_callback);609609+ }610610+611611+ // SAFETY: `c_chip_ptr` points to a valid chip.612612+ // The `Adapter`'s `VTABLE` has a 'static lifetime, so the pointer613613+ // returned by `as_raw()` is always valid.614614+ unsafe {615615+ (*c_chip_ptr).ops = Adapter::<T>::VTABLE.as_raw();616616+ }617617+618618+ // Cast the `*mut bindings::pwm_chip` to `*mut Chip`. This is valid because619619+ // `Chip` is `repr(transparent)` over `Opaque<bindings::pwm_chip>`, and620620+ // `Opaque<T>` is `repr(transparent)` over `T`.621621+ let chip_ptr_as_self = c_chip_ptr.cast::<Self>();622622+623623+ // SAFETY: `chip_ptr_as_self` points to a valid `Chip` (layout-compatible with624624+ // `bindings::pwm_chip`) whose embedded device has refcount 1.625625+ // `ARef::from_raw` takes this pointer and manages it via `AlwaysRefCounted`.626626+ Ok(unsafe { ARef::from_raw(NonNull::new_unchecked(chip_ptr_as_self)) })627627+ }628628+}629629+630630+// SAFETY: Implements refcounting for `Chip` using the embedded `struct device`.631631+unsafe impl<T: PwmOps> AlwaysRefCounted for Chip<T> {632632+ #[inline]633633+ fn inc_ref(&self) {634634+ // SAFETY: `self.0.get()` points to a valid `pwm_chip` because `self` exists.635635+ // The embedded `dev` is valid. `get_device` increments its refcount.636636+ unsafe {637637+ bindings::get_device(&raw mut (*self.0.get()).dev);638638+ }639639+ }640640+641641+ #[inline]642642+ unsafe fn dec_ref(obj: NonNull<Chip<T>>) {643643+ let c_chip_ptr = obj.cast::<bindings::pwm_chip>().as_ptr();644644+645645+ // SAFETY: `obj` is a valid pointer to a `Chip` (and thus `bindings::pwm_chip`)646646+ // with a non-zero refcount. `put_device` handles decrement and final release.647647+ unsafe {648648+ bindings::put_device(&raw mut (*c_chip_ptr).dev);649649+ }650650+ }651651+}652652+653653+// SAFETY: `Chip` is a wrapper around `*mut bindings::pwm_chip`. The underlying C654654+// structure's state is managed and synchronized by the kernel's device model655655+// and PWM core locking mechanisms. Therefore, it is safe to move the `Chip`656656+// wrapper (and the pointer it contains) across threads.657657+unsafe impl<T: PwmOps + Send> Send for Chip<T> {}658658+659659+// SAFETY: It is safe for multiple threads to have shared access (`&Chip`) because660660+// the `Chip` data is immutable from the Rust side without holding the appropriate661661+// kernel locks, which the C core is responsible for. Any interior mutability is662662+// handled and synchronized by the C kernel code.663663+unsafe impl<T: PwmOps + Sync> Sync for Chip<T> {}664664+665665+/// A resource guard that ensures `pwmchip_remove` is called on drop.666666+///667667+/// This struct is intended to be managed by the `devres` framework by transferring its ownership668668+/// via [`devres::register`]. This ties the lifetime of the PWM chip registration669669+/// to the lifetime of the underlying device.670670+pub struct Registration<T: PwmOps> {671671+ chip: ARef<Chip<T>>,672672+}673673+674674+impl<T: 'static + PwmOps + Send + Sync> Registration<T> {675675+ /// Registers a PWM chip with the PWM subsystem.676676+ ///677677+ /// Transfers its ownership to the `devres` framework, which ties its lifetime678678+ /// to the parent device.679679+ /// On unbind of the parent device, the `devres` entry will be dropped, automatically680680+ /// calling `pwmchip_remove`. This function should be called from the driver's `probe`.681681+ pub fn register(dev: &device::Device<Bound>, chip: ARef<Chip<T>>) -> Result {682682+ let chip_parent = chip.device().parent().ok_or(EINVAL)?;683683+ if dev.as_raw() != chip_parent.as_raw() {684684+ return Err(EINVAL);685685+ }686686+687687+ let c_chip_ptr = chip.as_raw();688688+689689+ // SAFETY: `c_chip_ptr` points to a valid chip with its ops initialized.690690+ // `__pwmchip_add` is the C function to register the chip with the PWM core.691691+ unsafe {692692+ to_result(bindings::__pwmchip_add(c_chip_ptr, core::ptr::null_mut()))?;693693+ }694694+695695+ let registration = Registration { chip };696696+697697+ devres::register(dev, registration, GFP_KERNEL)698698+ }699699+}700700+701701+impl<T: PwmOps> Drop for Registration<T> {702702+ fn drop(&mut self) {703703+ let chip_raw = self.chip.as_raw();704704+705705+ // SAFETY: `chip_raw` points to a chip that was successfully registered.706706+ // `bindings::pwmchip_remove` is the correct C function to unregister it.707707+ // This `drop` implementation is called automatically by `devres` on driver unbind.708708+ unsafe {709709+ bindings::pwmchip_remove(chip_raw);710710+ }711711+ }712712+}713713+714714+/// Declares a kernel module that exposes a single PWM driver.715715+///716716+/// # Examples717717+///718718+///```ignore719719+/// kernel::module_pwm_platform_driver! {720720+/// type: MyDriver,721721+/// name: "Module name",722722+/// authors: ["Author name"],723723+/// description: "Description",724724+/// license: "GPL v2",725725+/// }726726+///```727727+#[macro_export]728728+macro_rules! module_pwm_platform_driver {729729+ ($($user_args:tt)*) => {730730+ $crate::module_platform_driver! {731731+ $($user_args)*732732+ imports_ns: ["PWM"],733733+ }734734+ };735735+}
+8
rust/macros/module.rs
···9898 description: Option<String>,9999 alias: Option<Vec<String>>,100100 firmware: Option<Vec<String>>,101101+ imports_ns: Option<Vec<String>>,101102}102103103104impl ModuleInfo {···113112 "license",114113 "alias",115114 "firmware",115115+ "imports_ns",116116 ];117117 const REQUIRED_KEYS: &[&str] = &["type", "name", "license"];118118 let mut seen_keys = Vec::new();···139137 "license" => info.license = expect_string_ascii(it),140138 "alias" => info.alias = Some(expect_string_array(it)),141139 "firmware" => info.firmware = Some(expect_string_array(it)),140140+ "imports_ns" => info.imports_ns = Some(expect_string_array(it)),142141 _ => panic!("Unknown key \"{key}\". Valid keys are: {EXPECTED_KEYS:?}."),143142 }144143···196193 if let Some(firmware) = info.firmware {197194 for fw in firmware {198195 modinfo.emit("firmware", &fw);196196+ }197197+ }198198+ if let Some(imports) = info.imports_ns {199199+ for ns in imports {200200+ modinfo.emit("import_ns", &ns);199201 }200202 }201203