Files
Linus Torvalds 59e6295fac Merge tag 'driver-core-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core
Pull driver core updates from Danilo Krummrich:
 "container_of:

   - Apply typeof_member(), remove the local __mptr variable to
     eliminate variable shadowing warnings on nested container_of()
     calls, and remove unnecessary parentheses

  core:

   - Add driver name to probe debug print for initcall_debug

   - Avoid repeatedly printing the same 'Fixed dependency cycle' log

   - Unwind device_add() on attribute creation failure in
     attribute_container_add_class_device()

   - Remove statistics group if encryption group creation fails in
     transport_add_class_device()

  debugfs:

   - Fix lockdown check for mmap_prepare()

   - Warn if file creation failed due to uninitialized debugfs

  device property:

   - Implement fw_devlink support for software nodes by adding
     software_node_add_links(), which creates fwnode links from
     DEV_PROP_REF properties to enable automatic probe ordering. Add
     kunit-managed fwnode helpers and test coverage

   - Fix infinite loop in fwnode_for_each_child_node() when the
     secondary fwnode has more than one child. Add test cases

   - Fix out-of-bounds access in software_node_get_reference_args() when
     called with index -1 (UINT_MAX)

   - Refactor to use RAII approach with __free()

   - Add Bartosz Golaszewski as software node reviewer

  firmware loader:

   - Fix race where a sysfs fallback request can complete before being
     queued as pending, leading to a use-after-free on the next fallback
     request

   - Reject 0-size built-in firmware and fail the build on empty
     firmware files in CONFIG_EXTRA_FIRMWARE

  kobject:

   - Provide __KOBJ_ATTR() and __KOBJ_ATTR_RO/WO() initialization macros
     and allow the constification of kobject attributes, enabling them
     to reside in read-only memory

  platform:

   - Provide platform_device_set_of_node(), platform_device_set_fwnode(),
     and platform_device_set_of_node_from_dev() helpers that encapsulate
     firmware node reference counting for dynamically allocated platform
     devices

     Convert all in-tree users that manually assigned dev.of_node or
     dev.fwnode, fixing a pre-existing refcount bug in powermac. Switch
     to counting references of all firmware node types, not only OF
     nodes

   - Unify the release path for dynamically allocated platform devices
     by removing platform_device_release_full(). Amend the fwnode setter
     API contract to warn if a primary software node is overwritten. Add
     KUnit tests for correct software node removal on device
     unregistration

  Rust:

   - Auxiliary:
       - Add registration_data_with() closure-based API for invariant
         ForLt types

   - Debugfs:
       - Migrate BinaryWriter and BinaryReaderMut trait requirements
         from kernel::transmute traits to zerocopy traits

   - Device:
       - Add BoundInternal device context and InternalBoundContext trait
         for bus abstractions that need internal access to a bound
         device.
       - Make the lifetime on Core and CoreInternal invariant to prevent
         coercion to shorter lifetimes

   - Devres:
       - Fix race between concurrent revokers where the losing revoker
         could return before the winning revoker finished dropping the
         inner data, causing use-after-free.
       - Ensure revocation is complete before the device finishes
         unbinding by making the synchronization bidirectional.
       - Add DevresLt<F: ForLt>, a wrapper around Devres that shortens
         'static back to the caller's borrow scope. Implement ForLt and
         CovariantForLt for Bar, IoMem, and ExclusiveIoMem

   - Driver:
       - Switch from index-based to pointer-based device ID info lookup,
         storing static references in driver_data. Centralize device ID
         handling in device_id.rs, removing the open-coded ACPI/OF
         matching logic and duplicate ID table from driver.rs

   - I/O:
       - Make I/O regions typed (with a dynamically-sized Region type
         for the existing untyped case), create view types representing
         subregions of a mapped I/O region, and add io_project!() for
         safely creating subviews.
       - Split Io into a base trait (IoBase) and an extension trait (Io)
         with a blanket implementation, preventing implementers from
         overriding provided methods that unsafe code relies on.
       - Add a SysMem backend for shared system memory with volatile
         access, and make Coherent implement Io via an I/O view type.
         Add IoSysMap as sum type of Mmio and SysMem. Add copying
         methods (memcpy_{from,to}io()) and read_val()/write_val() for
         typed access.
       - Replace dma_read!()/dma_write!() with io_read!()/io_write!()
         for primitives and copying methods for aggregates; drop the old
         macros. Convert nova-core to use I/O projection.
       - Fix internal shortcut rule dispatch in the register!() macro,
         remove unused rule arguments, and use path fragments for alias
         destinations

   - IRQ:
       - Make irq::Registration compatible with lifetime-bound drivers
         by removing the 'static bound on Handler/ThreadedHandler and
         replacing Devres<RegistrationInner> with direct
         request_irq()/free_irq() calls. Handlers can now directly own
         lifetime-bound device resources

   - PCI:
       - Convert IrqVectorRegistration to a lifetime-annotated owning
         type, giving drivers explicit control over the allocation
         lifetime. IrqVector embeds a resolved IrqRequest, making the
         conversion infallible. Remove the redundant
         request_irq()/request_threaded_irq() wrappers from pci::Device.
       - Add pci_irq_type() C helper and expose it via irq_type() on
         IrqVectorRegistration and IrqVector, returning PCI_IRQ_MSIX,
         PCI_IRQ_MSI, or PCI_IRQ_INTX.
       - Mark pci::Device refcount methods inline

   - Serdev:
       - Add Rust abstractions for the serial device bus, including
         serdev::Driver trait, serdev::Device wrapping struct
         serdev_device, and serdev::Adapter implementing
         RegistrationOps. Includes a sample driver. Markus Probst takes
         over as serdev maintainer for both C and Rust code

   - Misc:
       - Split ForLt into a base trait (providing the Of<'a> GAT) and an
         unsafe CovariantForLt subtrait guaranteeing covariance,
         enabling invariant types (e.g. those containing Mutex<&'bound T>)
         to participate in the ForLt abstraction.
       - Fix Coherent read past EOF returning -ERANGE instead of zero.
       - Fix firmware example UB by avoiding null-pointer ARef

  misc:
   - Avoid iattr allocation in kernfs listxattr by using
     kernfs_iattrs_noalloc().
   - Unregister SoC bus on early device registration failure.
   - Remove unused DMA_FENCE_TRACE Kconfig symbol.
   - Fix /sys/module path in comment.
   - Refactor ISA bus init to remove nested blocks.
   - Remove redundant nodemask clears in numa_init().
   - Add kernel-doc for fwnode_operations and sys_soc.h, mark
     internal property data as private for kernel-doc, and add
     property.h/fwnode.h to driver-api infrastructure docs.
   - Add MAINTAINERS entry for sys_soc.h"

* tag 'driver-core-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core: (129 commits)
  rust: pci: expose the allocated interrupt type
  PCI: Add pci_irq_type() to query the allocated interrupt type
  rust: pci: remove request_irq() and request_threaded_irq() from Device
  rust: pci: resolve IRQ in index() and embed IrqRequest in IrqVector
  rust: pci: convert IrqVectorRegistration to a lifetime-managed owning type
  kernfs: avoid iattr allocation in listxattr
  rust: serdev: use ThisModule::as_ptr() instead of field access
  ACPI/IORT: use platform_device_set_fwnode()
  ACPI/APMT: use platform_device_set_fwnode()
  firmware_loader: do not queue completed sysfs fallback requests
  rust: pci: Mark Device refcount methods inline
  rust: irq: make Registration compatible with lifetime-bound drivers
  rust: net/phy: remove expansion from doc
  rust: dma: return zero for Coherent reads past EOF
  rust: io: register: use path fragment for alias destination
  rust: io: register: remove unused rule arguments
  rust: io: register: dispatch shortcut rules internally
  MAINTAINERS: add sys_soc.h to DRIVER CORE
  rust: debugfs: remove unsafe blocks from traits impl for Vec
  rust: debugfs: migrate debugfs traits requirements to zerocopy
  ...
2026-08-19 10:42:18 -07:00

229 lines
6.8 KiB
Rust

// SPDX-License-Identifier: GPL-2.0
//! Rust based implementation of the cpufreq-dt driver.
use kernel::{
clk::Clk,
cpu,
cpufreq, //
cpumask::CpumaskVar,
device::{
Core,
Device, //
},
error::code::*,
macros::vtable,
module_platform_driver,
of,
opp,
platform, //
prelude::*,
str::CString,
sync::Arc,
};
/// Finds exact supply name from the OF node.
fn find_supply_name_exact(dev: &Device, name: &str) -> Option<CString> {
let prop_name = CString::try_from_fmt(fmt!("{name}-supply")).ok()?;
dev.fwnode()?
.property_present(&prop_name)
.then(|| CString::try_from_fmt(fmt!("{name}")).ok())
.flatten()
}
/// Finds supply name for the CPU from DT.
fn find_supply_names(dev: &Device, cpu: cpu::CpuId) -> Option<KVec<CString>> {
// Try "cpu0" for older DTs, fallback to "cpu".
(cpu.as_u32() == 0)
.then(|| find_supply_name_exact(dev, "cpu0"))
.flatten()
.or_else(|| find_supply_name_exact(dev, "cpu"))
.and_then(|name| kernel::kvec![name].ok())
}
/// Represents the cpufreq dt device.
struct CPUFreqDTDevice {
opp_table: opp::Table,
freq_table: opp::FreqTable,
_mask: CpumaskVar,
_token: Option<opp::ConfigToken>,
_clk: Clk,
}
#[derive(Default)]
struct CPUFreqDTDriver;
#[vtable]
impl opp::ConfigOps for CPUFreqDTDriver {}
#[vtable]
impl cpufreq::Driver for CPUFreqDTDriver {
const NAME: &'static CStr = c"cpufreq-dt";
const FLAGS: u16 = cpufreq::flags::NEED_INITIAL_FREQ_CHECK | cpufreq::flags::IS_COOLING_DEV;
const BOOST_ENABLED: bool = true;
type PData = Arc<CPUFreqDTDevice>;
fn init(policy: &mut cpufreq::Policy) -> Result<Self::PData> {
let cpu = policy.cpu();
// SAFETY: The CPU device is only used during init; it won't get hot-unplugged. The cpufreq
// core registers with CPU notifiers and the cpufreq core/driver won't use the CPU device,
// once the CPU is hot-unplugged.
let dev = unsafe { cpu::from_cpu(cpu)? };
let mut mask = CpumaskVar::new_zero(GFP_KERNEL)?;
mask.set(cpu);
let token = find_supply_names(dev, cpu)
.map(|names| {
opp::Config::<Self>::new()
.set_regulator_names(names)?
.set(dev)
})
.transpose()?;
// Get OPP-sharing information from "operating-points-v2" bindings.
let fallback = match opp::Table::of_sharing_cpus(dev, &mut mask) {
Ok(()) => false,
Err(e) if e == ENOENT => {
// "operating-points-v2" not supported. If the platform hasn't
// set sharing CPUs, fallback to all CPUs share the `Policy`
// for backward compatibility.
opp::Table::sharing_cpus(dev, &mut mask).is_err()
}
Err(e) => return Err(e),
};
// Initialize OPP tables for all policy cpus.
//
// For platforms not using "operating-points-v2" bindings, we do this
// before updating policy cpus. Otherwise, we will end up creating
// duplicate OPPs for the CPUs.
//
// OPPs might be populated at runtime, don't fail for error here unless
// it is -EPROBE_DEFER.
let mut opp_table = match opp::Table::from_of_cpumask(dev, &mut mask) {
Ok(table) => table,
Err(e) => {
if e == EPROBE_DEFER {
return Err(e);
}
// The table is added dynamically ?
opp::Table::from_dev(dev)?
}
};
// The OPP table must be initialized, statically or dynamically, by this point.
opp_table.opp_count()?;
// Set sharing cpus for fallback scenario.
if fallback {
mask.setall();
opp_table.set_sharing_cpus(&mut mask)?;
}
let mut transition_latency = opp_table.max_transition_latency_ns() as u32;
if transition_latency == 0 {
transition_latency = cpufreq::DEFAULT_TRANSITION_LATENCY_NS;
}
policy
.set_dvfs_possible_from_any_cpu(true)
.set_suspend_freq(opp_table.suspend_freq())
.set_transition_latency_ns(transition_latency);
let freq_table = opp_table.cpufreq_table()?;
// SAFETY: The `freq_table` is not dropped while it is getting used by the C code.
unsafe { policy.set_freq_table(&freq_table) };
// SAFETY: The returned `clk` is not dropped while it is getting used by the C code.
let clk = unsafe { policy.set_clk(dev, None)? };
mask.copy(policy.cpus());
Ok(Arc::new(
CPUFreqDTDevice {
opp_table,
freq_table,
_mask: mask,
_token: token,
_clk: clk,
},
GFP_KERNEL,
)?)
}
fn exit(_policy: &mut cpufreq::Policy, _data: Option<Self::PData>) -> Result {
Ok(())
}
fn online(_policy: &mut cpufreq::Policy) -> Result {
// We did light-weight tear down earlier, nothing to do here.
Ok(())
}
fn offline(_policy: &mut cpufreq::Policy) -> Result {
// Preserve policy->data and don't free resources on light-weight
// tear down.
Ok(())
}
fn suspend(policy: &mut cpufreq::Policy) -> Result {
policy.generic_suspend()
}
fn verify(data: &mut cpufreq::PolicyData) -> Result {
data.generic_verify()
}
fn target_index(policy: &mut cpufreq::Policy, index: cpufreq::TableIndex) -> Result {
let Some(data) = policy.data::<Self::PData>() else {
return Err(ENOENT);
};
let freq = data.freq_table.freq(index)?;
data.opp_table.set_rate(freq)
}
fn get(policy: &mut cpufreq::Policy) -> Result<u32> {
policy.generic_get()
}
fn set_boost(_policy: &mut cpufreq::Policy, _state: i32) -> Result {
Ok(())
}
fn register_em(policy: &mut cpufreq::Policy) {
policy.register_em_opp()
}
}
kernel::of_device_table!(
OF_TABLE,
<CPUFreqDTDriver as platform::Driver>::IdInfo,
[(of::DeviceId::new(c"operating-points-v2"), ())]
);
impl platform::Driver for CPUFreqDTDriver {
type IdInfo = ();
type Data<'bound> = Self;
const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
fn probe<'bound>(
pdev: &'bound platform::Device<Core<'_>>,
_id_info: Option<&'bound Self::IdInfo>,
) -> impl PinInit<Self, Error> + 'bound {
cpufreq::Registration::<CPUFreqDTDriver>::new_foreign_owned(pdev.as_ref())?;
Ok(Self {})
}
}
module_platform_driver! {
type: CPUFreqDTDriver,
name: "cpufreq-dt",
authors: ["Viresh Kumar <viresh.kumar@linaro.org>"],
description: "Generic CPUFreq DT driver",
license: "GPL v2",
}