kernel/soc.rs
1// SPDX-License-Identifier: GPL-2.0
2
3// Copyright (C) 2025 Google LLC.
4
5//! SoC Driver Abstraction.
6//!
7//! C header: [`include/linux/sys_soc.h`](srctree/include/linux/sys_soc.h)
8
9use crate::{
10 bindings,
11 error,
12 prelude::*,
13 str::CString,
14 types::Opaque, //
15};
16use core::ptr::NonNull;
17
18/// Attributes for a SoC device.
19///
20/// These are both exported to userspace under /sys/devices/socX and provided to other drivers to
21/// match against via `soc_device_match` (not yet available in Rust) to enable quirks or
22/// device-specific support where necessary.
23///
24/// All fields are freeform - they have no specific formatting, just defined meanings.
25/// For example, the [`machine`](`Attributes::machine`) field could be "DB8500" or
26/// "Qualcomm Technologies, Inc. SM8560 HDK", but regardless it should identify a board or product.
27pub struct Attributes {
28 /// Should generally be a board ID or product ID. Examples
29 /// include DB8500 (ST-Ericsson) or "Qualcomm Technologies, inc. SM8560 HDK".
30 ///
31 /// If this field is not populated, the SoC infrastructure will try to populate it from
32 /// `/model` in the device tree.
33 pub machine: Option<CString>,
34 /// The broader class this SoC belongs to. Examples include ux500
35 /// (for DB8500) or Snapdragon (for SM8650).
36 ///
37 /// On chips with ARM firmware supporting SMCCC v1.2+, this may be a JEDEC JEP106 manufacturer
38 /// identification.
39 pub family: Option<CString>,
40 /// The manufacturing revision of the part. Frequently this is MAJOR.MINOR, but not always.
41 pub revision: Option<CString>,
42 /// Serial Number - uniquely identifies a specific SoC. If present, should be unique (buying a
43 /// replacement part should change it if present). This field cannot be matched on and is
44 /// solely present to export through /sys.
45 pub serial_number: Option<CString>,
46 /// SoC ID - identifies a specific SoC kind in question, sometimes more specifically than
47 /// `machine` if the same SoC is used in multiple products. Some devices use this to specify a
48 /// SoC name, e.g. "I.MX??", and others just print an ID number (e.g. Tegra and Qualcomm).
49 ///
50 /// On chips with ARM firmware supporting SMCCC v1.2+, this may be a JEDEC JEP106 manufacturer
51 /// identification (the family value) followed by a colon and then a 4-digit ID value.
52 pub soc_id: Option<CString>,
53}
54
55struct BuiltAttributes {
56 // While `inner` has pointers to `_backing`, it is to the interior of the `CStrings`, not
57 // `backing` itself, so it does not need to be pinned.
58 _backing: Attributes,
59 // `Opaque` makes us `!Unpin`, as the registration holds a pointer to `inner` when used.
60 inner: Opaque<bindings::soc_device_attribute>,
61}
62
63fn cstring_to_c(mcs: &Option<CString>) -> *const kernel::ffi::c_char {
64 mcs.as_ref()
65 .map(|cs| cs.as_char_ptr())
66 .unwrap_or(core::ptr::null())
67}
68
69impl BuiltAttributes {
70 fn as_mut_ptr(&self) -> *mut bindings::soc_device_attribute {
71 self.inner.get()
72 }
73}
74
75impl Attributes {
76 fn build(self) -> BuiltAttributes {
77 BuiltAttributes {
78 inner: Opaque::new(bindings::soc_device_attribute {
79 machine: cstring_to_c(&self.machine),
80 family: cstring_to_c(&self.family),
81 revision: cstring_to_c(&self.revision),
82 serial_number: cstring_to_c(&self.serial_number),
83 soc_id: cstring_to_c(&self.soc_id),
84 data: core::ptr::null(),
85 custom_attr_group: core::ptr::null(),
86 }),
87 _backing: self,
88 }
89 }
90}
91
92#[pin_data(PinnedDrop)]
93/// Registration handle for your soc_dev. If you let it go out of scope, your soc_dev will be
94/// unregistered.
95pub struct Registration {
96 #[pin]
97 attr: BuiltAttributes,
98 soc_dev: NonNull<bindings::soc_device>,
99}
100
101// SAFETY: We provide no operations through `&Registration`.
102unsafe impl Sync for Registration {}
103
104// SAFETY: All pointers are normal allocations, not thread-specific.
105unsafe impl Send for Registration {}
106
107#[pinned_drop]
108impl PinnedDrop for Registration {
109 fn drop(self: Pin<&mut Self>) {
110 // SAFETY: Device always contains a live pointer to a soc_device that can be unregistered
111 unsafe { bindings::soc_device_unregister(self.soc_dev.as_ptr()) }
112 }
113}
114
115impl Registration {
116 /// Register a new SoC device
117 pub fn new(attr: Attributes) -> impl PinInit<Self, Error> {
118 try_pin_init!(Self {
119 attr: attr.build(),
120 soc_dev: {
121 // SAFETY:
122 // * The struct provided through attr is backed by pinned data next to it,
123 // so as long as attr lives, the strings pointed to by the struct will too.
124 // * `attr` is pinned, so the pinned data won't move.
125 // * If it returns a device, and so others may try to read this data, by
126 // caller invariant, `attr` won't be released until the device is.
127 let raw_soc = error::from_err_ptr(unsafe {
128 bindings::soc_device_register(attr.as_mut_ptr())
129 })?;
130
131 NonNull::new(raw_soc).ok_or(EINVAL)?
132 },
133 }? Error)
134 }
135}