mirror of
https://github.com/torvalds/linux.git
synced 2025-12-07 20:06:24 +00:00
Pull driver core updates from Danilo Krummrich:
"Arch Topology:
- Move parse_acpi_topology() from arm64 to common code for reuse in
RISC-V
CPU:
- Expose housekeeping CPUs through /sys/devices/system/cpu/housekeeping
- Print a newline (or 0x0A) instead of '(null)' reading
/sys/devices/system/cpu/nohz_full when nohz_full= is not set
debugfs
- Remove (broken) 'no-mount' mode
- Remove redundant access mode checks in debugfs_get_tree() and
debugfs_create_*() functions
Devres:
- Remove unused devm_free_percpu() helper
- Move devm_alloc_percpu() from device.h to devres.h
Firmware Loader:
- Replace simple_strtol() with kstrtoint()
- Do not call cancel_store() when no upload is in progress
kernfs:
- Increase struct super_block::maxbytes to MAX_LFS_FILESIZE
- Fix a missing unwind path in __kernfs_new_node()
Misc:
- Increase the name size in struct auxiliary_device_id to 40
characters
- Replace system_unbound_wq with system_dfl_wq and add WQ_PERCPU to
alloc_workqueue()
Platform:
- Replace ERR_PTR() with IOMEM_ERR_PTR() in platform ioremap
functions
Rust:
- Auxiliary:
- Unregister auxiliary device on parent device unbind
- Move parent() to impl Device; implement device context aware
parent() for Device<Bound>
- Illustrate how to safely obtain a driver's device private data
when calling from an auxiliary driver into the parant device
driver
- DebugFs:
- Implement support for binary large objects
- Device:
- Let probe() return the driver's device private data as pinned
initializer, i.e. impl PinInit<Self, Error>
- Implement safe accessor for a driver's device private data for
Device<Bound> (returned reference can't out-live driver binding
and guarantees the correct private data type)
- Implement AsBusDevice trait, to be used by class device
abstractions to derive the bus device type of the parent device
- DMA:
- Store raw pointer of allocation as NonNull
- Use start_ptr() and start_ptr_mut() to inherit correct
mutability of self
- FS:
- Add file::Offset type alias
- I2C:
- Add abstractions for I2C device / driver infrastructure
- Implement abstractions for manual I2C device registrations
- I/O:
- Use "kernel vertical" style for imports
- Define ResourceSize as resource_size_t
- Move ResourceSize to top-level I/O module
- Add type alias for phys_addr_t
- Implement Rust version of read_poll_timeout_atomic()
- PCI:
- Use "kernel vertical" style for imports
- Move I/O and IRQ infrastructure to separate files
- Add support for PCI interrupt vectors
- Implement TryInto<IrqRequest<'a>> for IrqVector<'a> to convert
an IrqVector bound to specific pci::Device into an IrqRequest
bound to the same pci::Device's parent Device
- Leverage pin_init_scope() to get rid of redundant Result in IRQ
methods
- PinInit:
- Add {pin_}init_scope() to execute code before creating an
initializer
- Platform:
- Leverage pin_init_scope() to get rid of redundant Result in IRQ
methods
- Timekeeping:
- Implement abstraction of udelay()
- Uaccess:
- Implement read_slice_partial() and read_slice_file() for
UserSliceReader
- Implement write_slice_partial() and write_slice_file() for
UserSliceWriter
sysfs:
- Prepare the constification of struct attribute"
* tag 'driver-core-6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core: (75 commits)
rust: pci: fix build failure when CONFIG_PCI_MSI is disabled
debugfs: Fix default access mode config check
debugfs: Remove broken no-mount mode
debugfs: Remove redundant access mode checks
driver core: Check drivers_autoprobe for all added devices
driver core: WQ_PERCPU added to alloc_workqueue users
driver core: replace use of system_unbound_wq with system_dfl_wq
tick/nohz: Expose housekeeping CPUs in sysfs
tick/nohz: avoid showing '(null)' if nohz_full= not set
sysfs/cpu: Use DEVICE_ATTR_RO for nohz_full attribute
kernfs: fix memory leak of kernfs_iattrs in __kernfs_new_node
fs/kernfs: raise sb->maxbytes to MAX_LFS_FILESIZE
mod_devicetable: Bump auxiliary_device_id name size
sysfs: simplify attribute definition macros
samples/kobject: constify 'struct foo_attribute'
samples/kobject: add is_visible() callback to attribute group
sysfs: attribute_group: enable const variants of is_visible()
sysfs: introduce __SYSFS_FUNCTION_ALTERNATIVE()
sysfs: transparently handle const pointers in ATTRIBUTE_GROUPS()
sysfs: attribute_group: allow registration of const attribute
...
192 lines
5.5 KiB
Rust
192 lines
5.5 KiB
Rust
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
//! Rust Platform driver sample.
|
|
|
|
//! ACPI match table test
|
|
//!
|
|
//! This demonstrates how to test an ACPI-based Rust platform driver using QEMU
|
|
//! with a custom SSDT.
|
|
//!
|
|
//! Steps:
|
|
//!
|
|
//! 1. **Create an SSDT source file** (`ssdt.dsl`) with the following content:
|
|
//!
|
|
//! ```asl
|
|
//! DefinitionBlock ("", "SSDT", 2, "TEST", "VIRTACPI", 0x00000001)
|
|
//! {
|
|
//! Scope (\_SB)
|
|
//! {
|
|
//! Device (T432)
|
|
//! {
|
|
//! Name (_HID, "LNUXBEEF") // ACPI hardware ID to match
|
|
//! Name (_UID, 1)
|
|
//! Name (_STA, 0x0F) // Device present, enabled
|
|
//! Name (_CRS, ResourceTemplate ()
|
|
//! {
|
|
//! Memory32Fixed (ReadWrite, 0xFED00000, 0x1000)
|
|
//! })
|
|
//! }
|
|
//! }
|
|
//! }
|
|
//! ```
|
|
//!
|
|
//! 2. **Compile the table**:
|
|
//!
|
|
//! ```sh
|
|
//! iasl -tc ssdt.dsl
|
|
//! ```
|
|
//!
|
|
//! This generates `ssdt.aml`
|
|
//!
|
|
//! 3. **Run QEMU** with the compiled AML file:
|
|
//!
|
|
//! ```sh
|
|
//! qemu-system-x86_64 -m 512M \
|
|
//! -enable-kvm \
|
|
//! -kernel path/to/bzImage \
|
|
//! -append "root=/dev/sda console=ttyS0" \
|
|
//! -hda rootfs.img \
|
|
//! -serial stdio \
|
|
//! -acpitable file=ssdt.aml
|
|
//! ```
|
|
//!
|
|
//! Requirements:
|
|
//! - The `rust_driver_platform` must be present either:
|
|
//! - built directly into the kernel (`bzImage`), or
|
|
//! - available as a `.ko` file and loadable from `rootfs.img`
|
|
//!
|
|
//! 4. **Verify it worked** by checking `dmesg`:
|
|
//!
|
|
//! ```
|
|
//! rust_driver_platform LNUXBEEF:00: Probed with info: '0'.
|
|
//! ```
|
|
//!
|
|
|
|
use kernel::{
|
|
acpi, c_str,
|
|
device::{
|
|
self,
|
|
property::{FwNodeReferenceArgs, NArgs},
|
|
Core,
|
|
},
|
|
of, platform,
|
|
prelude::*,
|
|
str::CString,
|
|
sync::aref::ARef,
|
|
};
|
|
|
|
struct SampleDriver {
|
|
pdev: ARef<platform::Device>,
|
|
}
|
|
|
|
struct Info(u32);
|
|
|
|
kernel::of_device_table!(
|
|
OF_TABLE,
|
|
MODULE_OF_TABLE,
|
|
<SampleDriver as platform::Driver>::IdInfo,
|
|
[(of::DeviceId::new(c_str!("test,rust-device")), Info(42))]
|
|
);
|
|
|
|
kernel::acpi_device_table!(
|
|
ACPI_TABLE,
|
|
MODULE_ACPI_TABLE,
|
|
<SampleDriver as platform::Driver>::IdInfo,
|
|
[(acpi::DeviceId::new(c_str!("LNUXBEEF")), Info(0))]
|
|
);
|
|
|
|
impl platform::Driver for SampleDriver {
|
|
type IdInfo = Info;
|
|
const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
|
|
const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = Some(&ACPI_TABLE);
|
|
|
|
fn probe(
|
|
pdev: &platform::Device<Core>,
|
|
info: Option<&Self::IdInfo>,
|
|
) -> impl PinInit<Self, Error> {
|
|
let dev = pdev.as_ref();
|
|
|
|
dev_dbg!(dev, "Probe Rust Platform driver sample.\n");
|
|
|
|
if let Some(info) = info {
|
|
dev_info!(dev, "Probed with info: '{}'.\n", info.0);
|
|
}
|
|
|
|
if dev.fwnode().is_some_and(|node| node.is_of_node()) {
|
|
Self::properties_parse(dev)?;
|
|
}
|
|
|
|
Ok(Self { pdev: pdev.into() })
|
|
}
|
|
}
|
|
|
|
impl SampleDriver {
|
|
fn properties_parse(dev: &device::Device) -> Result {
|
|
let fwnode = dev.fwnode().ok_or(ENOENT)?;
|
|
|
|
if let Ok(idx) =
|
|
fwnode.property_match_string(c_str!("compatible"), c_str!("test,rust-device"))
|
|
{
|
|
dev_info!(dev, "matched compatible string idx = {}\n", idx);
|
|
}
|
|
|
|
let name = c_str!("compatible");
|
|
let prop = fwnode.property_read::<CString>(name).required_by(dev)?;
|
|
dev_info!(dev, "'{name}'='{prop:?}'\n");
|
|
|
|
let name = c_str!("test,bool-prop");
|
|
let prop = fwnode.property_read_bool(c_str!("test,bool-prop"));
|
|
dev_info!(dev, "'{name}'='{prop}'\n");
|
|
|
|
if fwnode.property_present(c_str!("test,u32-prop")) {
|
|
dev_info!(dev, "'test,u32-prop' is present\n");
|
|
}
|
|
|
|
let name = c_str!("test,u32-optional-prop");
|
|
let prop = fwnode.property_read::<u32>(name).or(0x12);
|
|
dev_info!(dev, "'{name}'='{prop:#x}' (default = 0x12)\n");
|
|
|
|
// A missing required property will print an error. Discard the error to
|
|
// prevent properties_parse from failing in that case.
|
|
let name = c_str!("test,u32-required-prop");
|
|
let _ = fwnode.property_read::<u32>(name).required_by(dev);
|
|
|
|
let name = c_str!("test,u32-prop");
|
|
let prop: u32 = fwnode.property_read(name).required_by(dev)?;
|
|
dev_info!(dev, "'{name}'='{prop:#x}'\n");
|
|
|
|
let name = c_str!("test,i16-array");
|
|
let prop: [i16; 4] = fwnode.property_read(name).required_by(dev)?;
|
|
dev_info!(dev, "'{name}'='{prop:?}'\n");
|
|
let len = fwnode.property_count_elem::<u16>(name)?;
|
|
dev_info!(dev, "'{name}' length is {len}\n");
|
|
|
|
let name = c_str!("test,i16-array");
|
|
let prop: KVec<i16> = fwnode.property_read_array_vec(name, 4)?.required_by(dev)?;
|
|
dev_info!(dev, "'{name}'='{prop:?}' (KVec)\n");
|
|
|
|
for child in fwnode.children() {
|
|
let name = c_str!("test,ref-arg");
|
|
let nargs = NArgs::N(2);
|
|
let prop: FwNodeReferenceArgs = child.property_get_reference_args(name, nargs, 0)?;
|
|
dev_info!(dev, "'{name}'='{prop:?}'\n");
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl Drop for SampleDriver {
|
|
fn drop(&mut self) {
|
|
dev_dbg!(self.pdev.as_ref(), "Remove Rust Platform driver sample.\n");
|
|
}
|
|
}
|
|
|
|
kernel::module_platform_driver! {
|
|
type: SampleDriver,
|
|
name: "rust_driver_platform",
|
|
authors: ["Danilo Krummrich"],
|
|
description: "Rust Platform driver",
|
|
license: "GPL v2",
|
|
}
|