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
9 use crate::{
10 bindings,
11 error,
12 prelude::*,
13 str::CString,
14 types::Opaque, //
15 };
16 use 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.
27 pub 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
55 struct 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
cstring_to_c(mcs: &Option<CString>) -> *const kernel::ffi::c_char63 fn 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
69 impl BuiltAttributes {
as_mut_ptr(&self) -> *mut bindings::soc_device_attribute70 fn as_mut_ptr(&self) -> *mut bindings::soc_device_attribute {
71 self.inner.get()
72 }
73 }
74
75 impl Attributes {
build(self) -> BuiltAttributes76 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.
95 pub struct Registration {
96 #[pin]
97 attr: BuiltAttributes,
98 soc_dev: NonNull<bindings::soc_device>,
99 }
100
101 // SAFETY: We provide no operations through `&Registration`.
102 unsafe impl Sync for Registration {}
103
104 // SAFETY: All pointers are normal allocations, not thread-specific.
105 unsafe impl Send for Registration {}
106
107 #[pinned_drop]
108 impl PinnedDrop for Registration {
drop(self: Pin<&mut Self>)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
115 impl Registration {
116 /// Register a new SoC device
new(attr: Attributes) -> impl PinInit<Self, Error>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 }
136