|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
| 2 | + |
| 3 | +//! Interrupts and interrupt chips. |
| 4 | +//! |
| 5 | +//! See <https://www.kernel.org/doc/Documentation/core-api/genericirq.rst>. |
| 6 | +//! |
| 7 | +//! C headers: [`include/linux/irq.h`](../../../../include/linux/irq.h) and |
| 8 | +//! [`include/linux/interrupt.h`](../../../../include/linux/interrupt.h). |
| 9 | +
|
| 10 | +#![allow(dead_code)] |
| 11 | + |
| 12 | +use crate::{bindings, c_types, from_kernel_result, types::PointerWrapper, Error, Result}; |
| 13 | +use core::ops::Deref; |
| 14 | + |
| 15 | +type IrqHwNumber = bindings::irq_hw_number_t; |
| 16 | + |
| 17 | +/// Wraps the kernel's `struct irq_data`. |
| 18 | +/// |
| 19 | +/// # Invariants |
| 20 | +/// |
| 21 | +/// The pointer `IrqData::ptr` is non-null and valid. |
| 22 | +pub struct IrqData { |
| 23 | + ptr: *mut bindings::irq_data, |
| 24 | +} |
| 25 | + |
| 26 | +impl IrqData { |
| 27 | + /// Creates a new `IrqData` instance from a raw pointer. |
| 28 | + /// |
| 29 | + /// # Safety |
| 30 | + /// |
| 31 | + /// Callers must ensure that `ptr` is non-null and valid when the function is called, and that |
| 32 | + /// it remains valid for the lifetime of the return [`IrqData`] instance. |
| 33 | + unsafe fn from_ptr(ptr: *mut bindings::irq_data) -> Self { |
| 34 | + // INVARIANTS: By the safety requirements, the instance we're creating satisfies the type |
| 35 | + // invariants. |
| 36 | + Self { ptr } |
| 37 | + } |
| 38 | + |
| 39 | + /// Returns the hardware irq number. |
| 40 | + pub fn hwirq(&self) -> IrqHwNumber { |
| 41 | + // SAFETY: By the type invariants, it's ok to dereference `ptr`. |
| 42 | + unsafe { (*self.ptr).hwirq } |
| 43 | + } |
| 44 | +} |
| 45 | + |
| 46 | +/// Wraps the kernel's `struct irq_data` when it is locked. |
| 47 | +/// |
| 48 | +/// Being locked allows additional operations to be performed on the data. |
| 49 | +pub struct LockedIrqData(IrqData); |
| 50 | + |
| 51 | +impl LockedIrqData { |
| 52 | + /// Sets the high-level irq flow handler to the builtin one for level-triggered irqs. |
| 53 | + pub fn set_level_handler(&mut self) { |
| 54 | + // SAFETY: By the type invariants of `self.0`, we know `self.0.ptr` is valid. |
| 55 | + unsafe { bindings::irq_set_handler_locked(self.0.ptr, Some(bindings::handle_level_irq)) }; |
| 56 | + } |
| 57 | + |
| 58 | + /// Sets the high-level irq flow handler to the builtin one for edge-triggered irqs. |
| 59 | + pub fn set_edge_handler(&mut self) { |
| 60 | + // SAFETY: By the type invariants of `self.0`, we know `self.0.ptr` is valid. |
| 61 | + unsafe { bindings::irq_set_handler_locked(self.0.ptr, Some(bindings::handle_edge_irq)) }; |
| 62 | + } |
| 63 | + |
| 64 | + /// Sets the high-level irq flow handler to the builtin one for bad irqs. |
| 65 | + pub fn set_bad_handler(&mut self) { |
| 66 | + // SAFETY: By the type invariants of `self.0`, we know `self.0.ptr` is valid. |
| 67 | + unsafe { bindings::irq_set_handler_locked(self.0.ptr, Some(bindings::handle_bad_irq)) }; |
| 68 | + } |
| 69 | +} |
| 70 | + |
| 71 | +impl Deref for LockedIrqData { |
| 72 | + type Target = IrqData; |
| 73 | + |
| 74 | + fn deref(&self) -> &Self::Target { |
| 75 | + &self.0 |
| 76 | + } |
| 77 | +} |
| 78 | + |
| 79 | +/// Extra information returned by some of the [`Chip`] methods on success. |
| 80 | +pub enum ExtraResult { |
| 81 | + /// Indicates that the caller (irq core) will update the descriptor state. |
| 82 | + None = bindings::IRQ_SET_MASK_OK as _, |
| 83 | + |
| 84 | + /// Indicates that the callee (irq chip implementation) already updated the descriptor state. |
| 85 | + NoCopy = bindings::IRQ_SET_MASK_OK_NOCOPY as _, |
| 86 | + |
| 87 | + /// Same as [`ExtraResult::None`] in terms of updating descriptor state. It is used in stacked |
| 88 | + /// irq chips to indicate that descendant chips should be skipped. |
| 89 | + Done = bindings::IRQ_SET_MASK_OK_DONE as _, |
| 90 | +} |
| 91 | + |
| 92 | +/// An irq chip. |
| 93 | +/// |
| 94 | +/// It is a trait for the functions defined in [`struct irq_chip`]. |
| 95 | +/// |
| 96 | +/// [`struct irq_chip`]: ../../../include/linux/irq.h |
| 97 | +pub trait Chip: Sized { |
| 98 | + /// The type of the context data stored in the irq chip and made available on each callback. |
| 99 | + type Data: PointerWrapper; |
| 100 | + |
| 101 | + /// The methods to use to populate [`struct irq_chip`]. This is typically populated with |
| 102 | + /// [`declare_irq_chip_operations`]. |
| 103 | + const TO_USE: ToUse; |
| 104 | + |
| 105 | + /// Called at the start of a new interrupt. |
| 106 | + fn ack(data: &<<Self::Data as PointerWrapper>::Borrowed as Deref>::Target, irq_data: &IrqData); |
| 107 | + |
| 108 | + /// Masks an interrupt source. |
| 109 | + fn mask(data: &<<Self::Data as PointerWrapper>::Borrowed as Deref>::Target, irq_data: &IrqData); |
| 110 | + |
| 111 | + /// Unmasks an interrupt source. |
| 112 | + fn unmask( |
| 113 | + data: &<<Self::Data as PointerWrapper>::Borrowed as Deref>::Target, |
| 114 | + irq_data: &IrqData, |
| 115 | + ); |
| 116 | + |
| 117 | + /// Sets the flow type of an interrupt. |
| 118 | + /// |
| 119 | + /// The flow type is a combination of the constants in [`Type`]. |
| 120 | + fn set_type( |
| 121 | + _data: &<<Self::Data as PointerWrapper>::Borrowed as Deref>::Target, |
| 122 | + _irq_data: &mut LockedIrqData, |
| 123 | + _flow_type: u32, |
| 124 | + ) -> Result<ExtraResult> { |
| 125 | + Ok(ExtraResult::None) |
| 126 | + } |
| 127 | + |
| 128 | + /// Enables or disables power-management wake-on of an interrupt. |
| 129 | + fn set_wake( |
| 130 | + _data: &<<Self::Data as PointerWrapper>::Borrowed as Deref>::Target, |
| 131 | + _irq_data: &IrqData, |
| 132 | + _on: bool, |
| 133 | + ) -> Result { |
| 134 | + Ok(()) |
| 135 | + } |
| 136 | +} |
| 137 | + |
| 138 | +/// Initialises `chip` with the callbacks defined in `T`. |
| 139 | +/// |
| 140 | +/// # Safety |
| 141 | +/// |
| 142 | +/// The caller must ensure that the value stored in the irq chip data is the result of calling |
| 143 | +/// [`PointerWrapper::into_pointer] for the [`T::Data`] type. |
| 144 | +pub(crate) unsafe fn init_chip<T: Chip>(chip: &mut bindings::irq_chip) { |
| 145 | + chip.irq_ack = Some(irq_ack_callback::<T>); |
| 146 | + chip.irq_mask = Some(irq_mask_callback::<T>); |
| 147 | + chip.irq_unmask = Some(irq_unmask_callback::<T>); |
| 148 | + |
| 149 | + if T::TO_USE.set_type { |
| 150 | + chip.irq_set_type = Some(irq_set_type_callback::<T>); |
| 151 | + } |
| 152 | + |
| 153 | + if T::TO_USE.set_wake { |
| 154 | + chip.irq_set_wake = Some(irq_set_wake_callback::<T>); |
| 155 | + } |
| 156 | +} |
| 157 | + |
| 158 | +/// Represents which fields of [`struct irq_chip`] should be populated with pointers. |
| 159 | +/// |
| 160 | +/// This is typically populated with the [`declare_irq_chip_operations`] macro. |
| 161 | +pub struct ToUse { |
| 162 | + /// The `irq_set_type` field of [`struct irq_chip`]. |
| 163 | + pub set_type: bool, |
| 164 | + |
| 165 | + /// The `irq_set_wake` field of [`struct irq_chip`]. |
| 166 | + pub set_wake: bool, |
| 167 | +} |
| 168 | + |
| 169 | +/// A constant version where all values are to set to `false`, that is, all supported fields will |
| 170 | +/// be set to null pointers. |
| 171 | +pub const USE_NONE: ToUse = ToUse { |
| 172 | + set_type: false, |
| 173 | + set_wake: false, |
| 174 | +}; |
| 175 | + |
| 176 | +/// Defines the [`Chip::TO_USE`] field based on a list of fields to be populated. |
| 177 | +#[macro_export] |
| 178 | +macro_rules! declare_irq_chip_operations { |
| 179 | + () => { |
| 180 | + const TO_USE: $crate::irq::ToUse = $crate::irq::USE_NONE; |
| 181 | + }; |
| 182 | + ($($i:ident),+) => { |
| 183 | + const TO_USE: $crate::irq::ToUse = |
| 184 | + $crate::irq::ToUse { |
| 185 | + $($i: true),+ , |
| 186 | + ..$crate::irq::USE_NONE |
| 187 | + }; |
| 188 | + }; |
| 189 | +} |
| 190 | + |
| 191 | +/// Enables or disables power-management wake-on for the given irq number. |
| 192 | +pub fn set_wake(irq: u32, on: bool) -> Result { |
| 193 | + // SAFETY: Just an FFI call, there are no extra requirements for safety. |
| 194 | + let ret = unsafe { bindings::irq_set_irq_wake(irq, on as _) }; |
| 195 | + if ret < 0 { |
| 196 | + Err(Error::from_kernel_errno(ret)) |
| 197 | + } else { |
| 198 | + Ok(()) |
| 199 | + } |
| 200 | +} |
| 201 | + |
| 202 | +unsafe extern "C" fn irq_ack_callback<T: Chip>(irq_data: *mut bindings::irq_data) { |
| 203 | + // SAFETY: The safety requirements of `init_chip`, which is the only place that uses this |
| 204 | + // callback, ensure that the value stored as irq chip data comes from a previous call to |
| 205 | + // `PointerWrapper::into_pointer`. |
| 206 | + let data = unsafe { T::Data::borrow(bindings::irq_data_get_irq_chip_data(irq_data)) }; |
| 207 | + |
| 208 | + // SAFETY: The value returned by `IrqData` is only valid until the end of this function, and |
| 209 | + // `irq_data` is guaranteed to be valid until then (by the contract with C code). |
| 210 | + T::ack(&data, unsafe { &IrqData::from_ptr(irq_data) }) |
| 211 | +} |
| 212 | + |
| 213 | +unsafe extern "C" fn irq_mask_callback<T: Chip>(irq_data: *mut bindings::irq_data) { |
| 214 | + // SAFETY: The safety requirements of `init_chip`, which is the only place that uses this |
| 215 | + // callback, ensure that the value stored as irq chip data comes from a previous call to |
| 216 | + // `PointerWrapper::into_pointer`. |
| 217 | + let data = unsafe { T::Data::borrow(bindings::irq_data_get_irq_chip_data(irq_data)) }; |
| 218 | + |
| 219 | + // SAFETY: The value returned by `IrqData` is only valid until the end of this function, and |
| 220 | + // `irq_data` is guaranteed to be valid until then (by the contract with C code). |
| 221 | + T::mask(&data, unsafe { &IrqData::from_ptr(irq_data) }) |
| 222 | +} |
| 223 | + |
| 224 | +unsafe extern "C" fn irq_unmask_callback<T: Chip>(irq_data: *mut bindings::irq_data) { |
| 225 | + // SAFETY: The safety requirements of `init_chip`, which is the only place that uses this |
| 226 | + // callback, ensure that the value stored as irq chip data comes from a previous call to |
| 227 | + // `PointerWrapper::into_pointer`. |
| 228 | + let data = unsafe { T::Data::borrow(bindings::irq_data_get_irq_chip_data(irq_data)) }; |
| 229 | + |
| 230 | + // SAFETY: The value returned by `IrqData` is only valid until the end of this function, and |
| 231 | + // `irq_data` is guaranteed to be valid until then (by the contract with C code). |
| 232 | + T::unmask(&data, unsafe { &IrqData::from_ptr(irq_data) }) |
| 233 | +} |
| 234 | + |
| 235 | +unsafe extern "C" fn irq_set_type_callback<T: Chip>( |
| 236 | + irq_data: *mut bindings::irq_data, |
| 237 | + flow_type: c_types::c_uint, |
| 238 | +) -> c_types::c_int { |
| 239 | + from_kernel_result! { |
| 240 | + // SAFETY: The safety requirements of `init_chip`, which is the only place that uses this |
| 241 | + // callback, ensure that the value stored as irq chip data comes from a previous call to |
| 242 | + // `PointerWrapper::into_pointer`. |
| 243 | + let data = unsafe { T::Data::borrow(bindings::irq_data_get_irq_chip_data(irq_data)) }; |
| 244 | + |
| 245 | + // SAFETY: The value returned by `IrqData` is only valid until the end of this function, and |
| 246 | + // `irq_data` is guaranteed to be valid until then (by the contract with C code). |
| 247 | + let ret = T::set_type(&data, &mut LockedIrqData(unsafe { IrqData::from_ptr(irq_data) }), flow_type)?; |
| 248 | + Ok(ret as _) |
| 249 | + } |
| 250 | +} |
| 251 | + |
| 252 | +unsafe extern "C" fn irq_set_wake_callback<T: Chip>( |
| 253 | + irq_data: *mut bindings::irq_data, |
| 254 | + on: c_types::c_uint, |
| 255 | +) -> c_types::c_int { |
| 256 | + from_kernel_result! { |
| 257 | + // SAFETY: The safety requirements of `init_chip`, which is the only place that uses this |
| 258 | + // callback, ensure that the value stored as irq chip data comes from a previous call to |
| 259 | + // `PointerWrapper::into_pointer`. |
| 260 | + let data = unsafe { T::Data::borrow(bindings::irq_data_get_irq_chip_data(irq_data)) }; |
| 261 | + |
| 262 | + // SAFETY: The value returned by `IrqData` is only valid until the end of this function, and |
| 263 | + // `irq_data` is guaranteed to be valid until then (by the contract with C code). |
| 264 | + T::set_wake(&data, unsafe { &IrqData::from_ptr(irq_data) }, on != 0)?; |
| 265 | + Ok(0) |
| 266 | + } |
| 267 | +} |
| 268 | + |
| 269 | +/// Contains constants that describes how an interrupt can be triggered. |
| 270 | +/// |
| 271 | +/// It is tagged with `non_exhaustive` to prevent users from instantiating it. |
| 272 | +#[non_exhaustive] |
| 273 | +pub struct Type; |
| 274 | + |
| 275 | +impl Type { |
| 276 | + /// The interrupt cannot be triggered. |
| 277 | + pub const NONE: u32 = bindings::IRQ_TYPE_NONE; |
| 278 | + |
| 279 | + /// The interrupt is triggered when the signal goes from low to high. |
| 280 | + pub const EDGE_RISING: u32 = bindings::IRQ_TYPE_EDGE_RISING; |
| 281 | + |
| 282 | + /// The interrupt is triggered when the signal goes from high to low. |
| 283 | + pub const EDGE_FALLING: u32 = bindings::IRQ_TYPE_EDGE_FALLING; |
| 284 | + |
| 285 | + /// The interrupt is triggered when the signal goes from low to high and when it goes to high |
| 286 | + /// to low. |
| 287 | + pub const EDGE_BOTH: u32 = bindings::IRQ_TYPE_EDGE_BOTH; |
| 288 | + |
| 289 | + /// The interrupt is triggered while the signal is held high. |
| 290 | + pub const LEVEL_HIGH: u32 = bindings::IRQ_TYPE_LEVEL_HIGH; |
| 291 | + |
| 292 | + /// The interrupt is triggered while the signal is held low. |
| 293 | + pub const LEVEL_LOW: u32 = bindings::IRQ_TYPE_LEVEL_LOW; |
| 294 | +} |
0 commit comments