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