xref: /linux/rust/kernel/soc.rs (revision eb3dad518e4da48ab6c6df16aa8895b8b0bd6ecf)
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 
63 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 {
70     fn as_mut_ptr(&self) -> *mut bindings::soc_device_attribute {
71         self.inner.get()
72     }
73 }
74 
75 impl 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.
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 {
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
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