Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'pwm/for-7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/ukleinek/linux

Pull pwm updates from Uwe Kleine-König:
"There are a few patches adapting to changes in Rust land which seems
to be the norm since there is a pwm driver written in Rust. Other than
that just a few cleanups and a single fix for the tiehrpwm driver that
came in too late for making it into v6.19.

Thanks to Andy Shevchenko, Bartosz Golaszewski, Daniel Almeida and
Michal Wilczynski for reviews in this cycle, and to Alice Ryhl, Ben
Zong-You Xie, Gokul Praveen, Kari Argillander, Markus Probst, Raag
Jadav, Shankari Anand, Tamir Duberstein and Vladimir Zapolskiy for
code contributions"

* tag 'pwm/for-7.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/ukleinek/linux:
pwm: Remove redundant check in pwm_ops_check()
pwm: tiehrpwm: Enable pwmchip's parent device before setting configuration
pwm: Update MAINTAINER entry
rust: pwm: Add __rust_helper to helpers
rust: pwm: Simplify to_result call sites and unsafe blocks
rust: pwm: Fix potential memory leak on init error
dt-bindings: pwm: nxp,lpc32xx-pwm: Specify clocks property as mandatory
pwm: th1520: Replace `kernel::c_str!` with C-Strings
pwm: dwc: Use size macro
pwm: Emit native configuration in /sys/kernel/debug/pwm
rust: pwm: Add UnregisteredChip wrapper around Chip
rust: pwm: Update ARef and AlwaysRefCounted imports to use sync::aref

+103 -81
+4
Documentation/devicetree/bindings/pwm/nxp,lpc3220-pwm.yaml
··· 27 27 required: 28 28 - compatible 29 29 - reg 30 + - clocks 30 31 - '#pwm-cells' 31 32 32 33 allOf: ··· 37 36 38 37 examples: 39 38 - | 39 + #include <dt-bindings/clock/lpc32xx-clock.h> 40 + 40 41 pwm@4005c000 { 41 42 compatible = "nxp,lpc3220-pwm"; 42 43 reg = <0x4005c000 0x4>; 44 + clocks = <&clk LPC32XX_CLK_PWM1>; 43 45 #pwm-cells = <3>; 44 46 }; 45 47
+2 -4
MAINTAINERS
··· 21151 21151 S: Maintained 21152 21152 Q: https://patchwork.ozlabs.org/project/linux-pwm/list/ 21153 21153 T: git https://git.kernel.org/pub/scm/linux/kernel/git/ukleinek/linux.git 21154 - F: Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml 21155 21154 F: Documentation/devicetree/bindings/pwm/ 21156 21155 F: Documentation/driver-api/pwm.rst 21157 - F: drivers/gpio/gpio-mvebu.c 21158 21156 F: drivers/pwm/ 21159 - F: drivers/video/backlight/pwm_bl.c 21160 21157 F: include/dt-bindings/pwm/ 21161 21158 F: include/linux/pwm.h 21162 - F: include/linux/pwm_backlight.h 21163 21159 K: pwm_(config|apply_might_sleep|apply_atomic|ops) 21160 + K: (devm_)?pwmchip_(add|alloc|remove) 21161 + K: pwm_(round|get|set)_waveform 21164 21162 21165 21163 PWM SUBSYSTEM BINDINGS [RUST] 21166 21164 M: Michal Wilczynski <m.wilczynski@samsung.com>
+23 -7
drivers/pwm/core.c
··· 1699 1699 1700 1700 if (ops->write_waveform) { 1701 1701 if (!ops->round_waveform_tohw || 1702 - !ops->round_waveform_fromhw || 1703 - !ops->write_waveform) 1702 + !ops->round_waveform_fromhw) 1704 1703 return false; 1705 1704 1706 1705 if (PWM_WFHWSIZE < ops->sizeof_wfhw) { ··· 2637 2638 2638 2639 for (i = 0; i < chip->npwm; i++) { 2639 2640 struct pwm_device *pwm = &chip->pwms[i]; 2640 - struct pwm_state state, hwstate; 2641 + struct pwm_state state; 2642 + int err; 2641 2643 2642 2644 pwm_get_state(pwm, &state); 2643 - pwm_get_state_hw(pwm, &hwstate); 2644 2645 2645 2646 seq_printf(s, " pwm-%-3d (%-20.20s):", i, pwm->label); 2646 2647 ··· 2656 2657 seq_puts(s, ", usage_power"); 2657 2658 seq_puts(s, "\n"); 2658 2659 2659 - seq_printf(s, " actual configuration: %3sabled, %llu/%llu ns, %s polarity", 2660 - hwstate.enabled ? "en" : "dis", hwstate.duty_cycle, hwstate.period, 2661 - hwstate.polarity ? "inverse" : "normal"); 2660 + if (pwmchip_supports_waveform(chip)) { 2661 + struct pwm_waveform wf; 2662 + 2663 + err = pwm_get_waveform_might_sleep(pwm, &wf); 2664 + if (!err) 2665 + seq_printf(s, " actual configuration: %lld/%lld [+%lld]", 2666 + wf.duty_length_ns, wf.period_length_ns, wf.duty_offset_ns); 2667 + else 2668 + seq_printf(s, " actual configuration: read out error: %pe\n", ERR_PTR(err)); 2669 + } else { 2670 + struct pwm_state hwstate; 2671 + 2672 + err = pwm_get_state_hw(pwm, &hwstate); 2673 + if (!err) 2674 + seq_printf(s, " actual configuration: %3sabled, %llu/%llu ns, %s polarity", 2675 + hwstate.enabled ? "en" : "dis", hwstate.duty_cycle, hwstate.period, 2676 + hwstate.polarity ? "inverse" : "normal"); 2677 + else 2678 + seq_printf(s, " actual configuration: read out error: %pe", ERR_PTR(err)); 2679 + } 2662 2680 2663 2681 seq_puts(s, "\n"); 2664 2682 }
+2 -1
drivers/pwm/pwm-dwc.c
··· 22 22 #include <linux/pci.h> 23 23 #include <linux/pm_runtime.h> 24 24 #include <linux/pwm.h> 25 + #include <linux/sizes.h> 25 26 26 27 #include "pwm-dwc.h" 27 28 28 29 /* Elkhart Lake */ 29 30 static const struct dwc_pwm_info ehl_pwm_info = { 30 31 .nr = 2, 31 - .size = 0x1000, 32 + .size = SZ_4K, 32 33 }; 33 34 34 35 static int dwc_pwm_init_one(struct device *dev, struct dwc_pwm_drvdata *ddata, unsigned int idx)
+2 -4
drivers/pwm/pwm-tiehrpwm.c
··· 237 237 if (period_cycles < 1) 238 238 period_cycles = 1; 239 239 240 - pm_runtime_get_sync(pwmchip_parent(chip)); 241 - 242 240 /* Update clock prescaler values */ 243 241 ehrpwm_modify(pc->mmio_base, TBCTL, TBCTL_CLKDIV_MASK, tb_divval); 244 242 ··· 287 289 288 290 if (!(duty_cycles > period_cycles)) 289 291 ehrpwm_write(pc->mmio_base, cmp_reg, duty_cycles); 290 - 291 - pm_runtime_put_sync(pwmchip_parent(chip)); 292 292 293 293 return 0; 294 294 } ··· 373 377 { 374 378 int err; 375 379 bool enabled = pwm->state.enabled; 380 + 381 + guard(pm_runtime_active)(pwmchip_parent(chip)); 376 382 377 383 if (state->polarity != pwm->state.polarity) { 378 384 if (enabled) {
+2 -3
drivers/pwm/pwm_th1520.rs
··· 22 22 23 23 use core::ops::Deref; 24 24 use kernel::{ 25 - c_str, 26 25 clk::Clk, 27 26 device::{Bound, Core, Device}, 28 27 devres, ··· 326 327 OF_TABLE, 327 328 MODULE_OF_TABLE, 328 329 <Th1520PwmPlatformDriver as platform::Driver>::IdInfo, 329 - [(of::DeviceId::new(c_str!("thead,th1520-pwm")), ())] 330 + [(of::DeviceId::new(c"thead,th1520-pwm"), ())] 330 331 ); 331 332 332 333 impl platform::Driver for Th1520PwmPlatformDriver { ··· 371 372 }), 372 373 )?; 373 374 374 - pwm::Registration::register(dev, chip)?; 375 + chip.register()?; 375 376 376 377 Ok(Th1520PwmPlatformDriver) 377 378 }
+3 -3
rust/helpers/pwm.c
··· 4 4 5 5 #include <linux/pwm.h> 6 6 7 - struct device *rust_helper_pwmchip_parent(const struct pwm_chip *chip) 7 + __rust_helper struct device *rust_helper_pwmchip_parent(const struct pwm_chip *chip) 8 8 { 9 9 return pwmchip_parent(chip); 10 10 } 11 11 12 - void *rust_helper_pwmchip_get_drvdata(struct pwm_chip *chip) 12 + __rust_helper void *rust_helper_pwmchip_get_drvdata(struct pwm_chip *chip) 13 13 { 14 14 return pwmchip_get_drvdata(chip); 15 15 } 16 16 17 - void rust_helper_pwmchip_set_drvdata(struct pwm_chip *chip, void *data) 17 + __rust_helper void rust_helper_pwmchip_set_drvdata(struct pwm_chip *chip, void *data) 18 18 { 19 19 pwmchip_set_drvdata(chip, data); 20 20 }
+65 -59
rust/kernel/pwm.rs
··· 13 13 devres, 14 14 error::{self, to_result}, 15 15 prelude::*, 16 - types::{ARef, AlwaysRefCounted, Opaque}, // 16 + sync::aref::{ARef, AlwaysRefCounted}, 17 + types::Opaque, // 17 18 }; 18 - use core::{marker::PhantomData, ptr::NonNull}; 19 + use core::{ 20 + marker::PhantomData, 21 + ops::Deref, 22 + ptr::NonNull, // 23 + }; 19 24 20 25 /// Represents a PWM waveform configuration. 21 26 /// Mirrors struct [`struct pwm_waveform`](srctree/include/linux/pwm.h). ··· 129 124 // SAFETY: `self.as_raw()` provides a valid `*mut pwm_device` pointer. 130 125 // `&c_wf` is a valid pointer to a `pwm_waveform` struct. The C function 131 126 // handles all necessary internal locking. 132 - let ret = unsafe { bindings::pwm_set_waveform_might_sleep(self.as_raw(), &c_wf, exact) }; 133 - to_result(ret) 127 + to_result(unsafe { bindings::pwm_set_waveform_might_sleep(self.as_raw(), &c_wf, exact) }) 134 128 } 135 129 136 130 /// Queries the hardware for the configuration it would apply for a given ··· 159 155 160 156 // SAFETY: `self.as_raw()` is a valid pointer. We provide a valid pointer 161 157 // to a stack-allocated `pwm_waveform` struct for the kernel to fill. 162 - let ret = unsafe { bindings::pwm_get_waveform_might_sleep(self.as_raw(), &mut c_wf) }; 163 - 164 - to_result(ret)?; 158 + to_result(unsafe { bindings::pwm_get_waveform_might_sleep(self.as_raw(), &mut c_wf) })?; 165 159 166 160 Ok(Waveform::from(c_wf)) 167 161 } ··· 175 173 } 176 174 177 175 /// Trait defining the operations for a PWM driver. 178 - pub trait PwmOps: 'static + Sized { 176 + pub trait PwmOps: 'static + Send + Sync + Sized { 179 177 /// The driver-specific hardware representation of a waveform. 180 178 /// 181 179 /// This type must be [`Copy`], [`Default`], and fit within `PWM_WFHWSIZE`. ··· 260 258 core::ptr::from_ref::<T::WfHw>(wfhw).cast::<u8>(), 261 259 wfhw_ptr.cast::<u8>(), 262 260 size, 263 - ); 264 - } 261 + ) 262 + }; 265 263 266 264 Ok(()) 267 265 } ··· 281 279 wfhw_ptr.cast::<u8>(), 282 280 core::ptr::from_mut::<T::WfHw>(&mut wfhw).cast::<u8>(), 283 281 size, 284 - ); 285 - } 282 + ) 283 + }; 286 284 287 285 Ok(wfhw) 288 286 } ··· 308 306 // Now, call the original release function to free the `pwm_chip` itself. 309 307 // SAFETY: `dev` is the valid pointer passed into this callback, which is 310 308 // the expected argument for `pwmchip_release`. 311 - unsafe { 312 - bindings::pwmchip_release(dev); 313 - } 309 + unsafe { bindings::pwmchip_release(dev) }; 314 310 } 315 311 316 312 /// # Safety ··· 408 408 match T::round_waveform_fromhw(chip, pwm, &wfhw, &mut rust_wf) { 409 409 Ok(()) => { 410 410 // SAFETY: `wf_ptr` is guaranteed valid by the C caller. 411 - unsafe { 412 - *wf_ptr = rust_wf.into(); 413 - }; 411 + unsafe { *wf_ptr = rust_wf.into() }; 414 412 0 415 413 } 416 414 Err(e) => e.to_errno(), ··· 582 584 /// 583 585 /// Returns an [`ARef<Chip>`] managing the chip's lifetime via refcounting 584 586 /// on its embedded `struct device`. 585 - pub fn new( 586 - parent_dev: &device::Device, 587 + #[allow(clippy::new_ret_no_self)] 588 + pub fn new<'a>( 589 + parent_dev: &'a device::Device<Bound>, 587 590 num_channels: u32, 588 591 data: impl pin_init::PinInit<T, Error>, 589 - ) -> Result<ARef<Self>> { 592 + ) -> Result<UnregisteredChip<'a, T>> { 590 593 let sizeof_priv = core::mem::size_of::<T>(); 591 594 // SAFETY: `pwmchip_alloc` allocates memory for the C struct and our private data. 592 595 let c_chip_ptr_raw = ··· 600 601 let drvdata_ptr = unsafe { bindings::pwmchip_get_drvdata(c_chip_ptr) }; 601 602 602 603 // SAFETY: We construct the `T` object in-place in the allocated private memory. 603 - unsafe { data.__pinned_init(drvdata_ptr.cast())? }; 604 + unsafe { data.__pinned_init(drvdata_ptr.cast()) }.inspect_err(|_| { 605 + // SAFETY: It is safe to call `pwmchip_put()` with a valid pointer obtained 606 + // from `pwmchip_alloc()`. We will not use pointer after this. 607 + unsafe { bindings::pwmchip_put(c_chip_ptr) } 608 + })?; 604 609 605 610 // SAFETY: `c_chip_ptr` points to a valid chip. 606 - unsafe { 607 - (*c_chip_ptr).dev.release = Some(Adapter::<T>::release_callback); 608 - } 611 + unsafe { (*c_chip_ptr).dev.release = Some(Adapter::<T>::release_callback) }; 609 612 610 613 // SAFETY: `c_chip_ptr` points to a valid chip. 611 614 // The `Adapter`'s `VTABLE` has a 'static lifetime, so the pointer 612 615 // returned by `as_raw()` is always valid. 613 - unsafe { 614 - (*c_chip_ptr).ops = Adapter::<T>::VTABLE.as_raw(); 615 - } 616 + unsafe { (*c_chip_ptr).ops = Adapter::<T>::VTABLE.as_raw() }; 616 617 617 618 // Cast the `*mut bindings::pwm_chip` to `*mut Chip`. This is valid because 618 619 // `Chip` is `repr(transparent)` over `Opaque<bindings::pwm_chip>`, and ··· 622 623 // SAFETY: `chip_ptr_as_self` points to a valid `Chip` (layout-compatible with 623 624 // `bindings::pwm_chip`) whose embedded device has refcount 1. 624 625 // `ARef::from_raw` takes this pointer and manages it via `AlwaysRefCounted`. 625 - Ok(unsafe { ARef::from_raw(NonNull::new_unchecked(chip_ptr_as_self)) }) 626 + let chip = unsafe { ARef::from_raw(NonNull::new_unchecked(chip_ptr_as_self)) }; 627 + 628 + Ok(UnregisteredChip { chip, parent_dev }) 626 629 } 627 630 } 628 631 ··· 634 633 fn inc_ref(&self) { 635 634 // SAFETY: `self.0.get()` points to a valid `pwm_chip` because `self` exists. 636 635 // The embedded `dev` is valid. `get_device` increments its refcount. 637 - unsafe { 638 - bindings::get_device(&raw mut (*self.0.get()).dev); 639 - } 636 + unsafe { bindings::get_device(&raw mut (*self.0.get()).dev) }; 640 637 } 641 638 642 639 #[inline] ··· 643 644 644 645 // SAFETY: `obj` is a valid pointer to a `Chip` (and thus `bindings::pwm_chip`) 645 646 // with a non-zero refcount. `put_device` handles decrement and final release. 646 - unsafe { 647 - bindings::put_device(&raw mut (*c_chip_ptr).dev); 648 - } 647 + unsafe { bindings::put_device(&raw mut (*c_chip_ptr).dev) }; 649 648 } 650 649 } 651 650 ··· 651 654 // structure's state is managed and synchronized by the kernel's device model 652 655 // and PWM core locking mechanisms. Therefore, it is safe to move the `Chip` 653 656 // wrapper (and the pointer it contains) across threads. 654 - unsafe impl<T: PwmOps + Send> Send for Chip<T> {} 657 + unsafe impl<T: PwmOps> Send for Chip<T> {} 655 658 656 659 // SAFETY: It is safe for multiple threads to have shared access (`&Chip`) because 657 660 // the `Chip` data is immutable from the Rust side without holding the appropriate 658 661 // kernel locks, which the C core is responsible for. Any interior mutability is 659 662 // handled and synchronized by the C kernel code. 660 - unsafe impl<T: PwmOps + Sync> Sync for Chip<T> {} 663 + unsafe impl<T: PwmOps> Sync for Chip<T> {} 661 664 662 - /// A resource guard that ensures `pwmchip_remove` is called on drop. 663 - /// 664 - /// This struct is intended to be managed by the `devres` framework by transferring its ownership 665 - /// via [`devres::register`]. This ties the lifetime of the PWM chip registration 666 - /// to the lifetime of the underlying device. 667 - pub struct Registration<T: PwmOps> { 665 + /// A wrapper around `ARef<Chip<T>>` that ensures that `register` can only be called once. 666 + pub struct UnregisteredChip<'a, T: PwmOps> { 668 667 chip: ARef<Chip<T>>, 668 + parent_dev: &'a device::Device<Bound>, 669 669 } 670 670 671 - impl<T: 'static + PwmOps + Send + Sync> Registration<T> { 671 + impl<T: PwmOps> UnregisteredChip<'_, T> { 672 672 /// Registers a PWM chip with the PWM subsystem. 673 673 /// 674 674 /// Transfers its ownership to the `devres` framework, which ties its lifetime 675 675 /// to the parent device. 676 676 /// On unbind of the parent device, the `devres` entry will be dropped, automatically 677 677 /// calling `pwmchip_remove`. This function should be called from the driver's `probe`. 678 - pub fn register(dev: &device::Device<Bound>, chip: ARef<Chip<T>>) -> Result { 679 - let chip_parent = chip.device().parent().ok_or(EINVAL)?; 680 - if dev.as_raw() != chip_parent.as_raw() { 681 - return Err(EINVAL); 682 - } 683 - 684 - let c_chip_ptr = chip.as_raw(); 678 + pub fn register(self) -> Result<ARef<Chip<T>>> { 679 + let c_chip_ptr = self.chip.as_raw(); 685 680 686 681 // SAFETY: `c_chip_ptr` points to a valid chip with its ops initialized. 687 682 // `__pwmchip_add` is the C function to register the chip with the PWM core. 688 - unsafe { 689 - to_result(bindings::__pwmchip_add(c_chip_ptr, core::ptr::null_mut()))?; 690 - } 683 + to_result(unsafe { bindings::__pwmchip_add(c_chip_ptr, core::ptr::null_mut()) })?; 691 684 692 - let registration = Registration { chip }; 685 + let registration = Registration { 686 + chip: ARef::clone(&self.chip), 687 + }; 693 688 694 - devres::register(dev, registration, GFP_KERNEL) 689 + devres::register(self.parent_dev, registration, GFP_KERNEL)?; 690 + 691 + Ok(self.chip) 695 692 } 693 + } 694 + 695 + impl<T: PwmOps> Deref for UnregisteredChip<'_, T> { 696 + type Target = Chip<T>; 697 + 698 + fn deref(&self) -> &Self::Target { 699 + &self.chip 700 + } 701 + } 702 + 703 + /// A resource guard that ensures `pwmchip_remove` is called on drop. 704 + /// 705 + /// This struct is intended to be managed by the `devres` framework by transferring its ownership 706 + /// via [`devres::register`]. This ties the lifetime of the PWM chip registration 707 + /// to the lifetime of the underlying device. 708 + struct Registration<T: PwmOps> { 709 + chip: ARef<Chip<T>>, 696 710 } 697 711 698 712 impl<T: PwmOps> Drop for Registration<T> { ··· 713 705 // SAFETY: `chip_raw` points to a chip that was successfully registered. 714 706 // `bindings::pwmchip_remove` is the correct C function to unregister it. 715 707 // This `drop` implementation is called automatically by `devres` on driver unbind. 716 - unsafe { 717 - bindings::pwmchip_remove(chip_raw); 718 - } 708 + unsafe { bindings::pwmchip_remove(chip_raw) }; 719 709 } 720 710 } 721 711