kernel/pci/irq.rs
1// SPDX-License-Identifier: GPL-2.0
2
3//! PCI interrupt infrastructure.
4
5use super::Device;
6use crate::{
7 bindings,
8 device,
9 device::Bound,
10 devres,
11 error::to_result,
12 irq::{
13 self,
14 IrqRequest, //
15 },
16 prelude::*,
17 str::CStr,
18 sync::aref::ARef, //
19};
20use core::ops::RangeInclusive;
21
22/// IRQ type flags for PCI interrupt allocation.
23#[derive(Debug, Clone, Copy)]
24pub enum IrqType {
25 /// INTx interrupts.
26 Intx,
27 /// Message Signaled Interrupts (MSI).
28 Msi,
29 /// Extended Message Signaled Interrupts (MSI-X).
30 MsiX,
31}
32
33impl IrqType {
34 /// Convert to the corresponding kernel flags.
35 const fn as_raw(self) -> u32 {
36 match self {
37 IrqType::Intx => bindings::PCI_IRQ_INTX,
38 IrqType::Msi => bindings::PCI_IRQ_MSI,
39 IrqType::MsiX => bindings::PCI_IRQ_MSIX,
40 }
41 }
42}
43
44/// Set of IRQ types that can be used for PCI interrupt allocation.
45#[derive(Debug, Clone, Copy, Default)]
46pub struct IrqTypes(u32);
47
48impl IrqTypes {
49 /// Create a set containing all IRQ types (MSI-X, MSI, and INTx).
50 pub const fn all() -> Self {
51 Self(bindings::PCI_IRQ_ALL_TYPES)
52 }
53
54 /// Build a set of IRQ types.
55 ///
56 /// # Examples
57 ///
58 /// ```ignore
59 /// // Create a set with only MSI and MSI-X (no INTx interrupts).
60 /// let msi_only = IrqTypes::default()
61 /// .with(IrqType::Msi)
62 /// .with(IrqType::MsiX);
63 /// ```
64 pub const fn with(self, irq_type: IrqType) -> Self {
65 Self(self.0 | irq_type.as_raw())
66 }
67
68 /// Get the raw flags value.
69 const fn as_raw(self) -> u32 {
70 self.0
71 }
72}
73
74/// Represents an allocated IRQ vector for a specific PCI device.
75///
76/// This type ties an IRQ vector to the device it was allocated for,
77/// ensuring the vector is only used with the correct device.
78#[derive(Clone, Copy)]
79pub struct IrqVector<'a> {
80 dev: &'a Device<Bound>,
81 index: u32,
82}
83
84impl<'a> IrqVector<'a> {
85 /// Creates a new [`IrqVector`] for the given device and index.
86 ///
87 /// # Safety
88 ///
89 /// - `index` must be a valid IRQ vector index for `dev`.
90 /// - `dev` must point to a [`Device`] that has successfully allocated IRQ vectors.
91 unsafe fn new(dev: &'a Device<Bound>, index: u32) -> Self {
92 Self { dev, index }
93 }
94
95 /// Returns the raw vector index.
96 fn index(&self) -> u32 {
97 self.index
98 }
99}
100
101impl<'a> TryInto<IrqRequest<'a>> for IrqVector<'a> {
102 type Error = Error;
103
104 fn try_into(self) -> Result<IrqRequest<'a>> {
105 // SAFETY: `self.as_raw` returns a valid pointer to a `struct pci_dev`.
106 let irq = unsafe { bindings::pci_irq_vector(self.dev.as_raw(), self.index()) };
107 if irq < 0 {
108 return Err(crate::error::Error::from_errno(irq));
109 }
110 // SAFETY: `irq` is guaranteed to be a valid IRQ number for `&self`.
111 Ok(unsafe { IrqRequest::new(self.dev.as_ref(), irq as u32) })
112 }
113}
114
115/// Represents an IRQ vector allocation for a PCI device.
116///
117/// This type ensures that IRQ vectors are properly allocated and freed by
118/// tying the allocation to the lifetime of this registration object.
119///
120/// # Invariants
121///
122/// The [`Device`] has successfully allocated IRQ vectors.
123struct IrqVectorRegistration {
124 dev: ARef<Device>,
125}
126
127impl IrqVectorRegistration {
128 /// Allocate and register IRQ vectors for the given PCI device.
129 ///
130 /// Allocates IRQ vectors and registers them with devres for automatic cleanup.
131 /// Returns a range of valid IRQ vectors.
132 fn register<'a>(
133 dev: &'a Device<Bound>,
134 min_vecs: u32,
135 max_vecs: u32,
136 irq_types: IrqTypes,
137 ) -> Result<RangeInclusive<IrqVector<'a>>> {
138 // SAFETY:
139 // - `dev.as_raw()` is guaranteed to be a valid pointer to a `struct pci_dev`
140 // by the type invariant of `Device`.
141 // - `pci_alloc_irq_vectors` internally validates all other parameters
142 // and returns error codes.
143 let ret = unsafe {
144 bindings::pci_alloc_irq_vectors(dev.as_raw(), min_vecs, max_vecs, irq_types.as_raw())
145 };
146
147 to_result(ret)?;
148 let count = ret as u32;
149
150 // SAFETY:
151 // - `pci_alloc_irq_vectors` returns the number of allocated vectors on success.
152 // - Vectors are 0-based, so valid indices are [0, count-1].
153 // - `pci_alloc_irq_vectors` guarantees `count >= min_vecs > 0`, so both `0` and
154 // `count - 1` are valid IRQ vector indices for `dev`.
155 let range = unsafe { IrqVector::new(dev, 0)..=IrqVector::new(dev, count - 1) };
156
157 // INVARIANT: The IRQ vector allocation for `dev` above was successful.
158 let irq_vecs = Self { dev: dev.into() };
159 devres::register(dev.as_ref(), irq_vecs, GFP_KERNEL)?;
160
161 Ok(range)
162 }
163}
164
165impl Drop for IrqVectorRegistration {
166 fn drop(&mut self) {
167 // SAFETY:
168 // - By the type invariant, `self.dev.as_raw()` is a valid pointer to a `struct pci_dev`.
169 // - `self.dev` has successfully allocated IRQ vectors.
170 unsafe { bindings::pci_free_irq_vectors(self.dev.as_raw()) };
171 }
172}
173
174impl Device<device::Bound> {
175 /// Returns a [`kernel::irq::Registration`] for the given IRQ vector.
176 pub fn request_irq<'a, T: crate::irq::Handler + 'static>(
177 &'a self,
178 vector: IrqVector<'a>,
179 flags: irq::Flags,
180 name: &'static CStr,
181 handler: impl PinInit<T, Error> + 'a,
182 ) -> impl PinInit<irq::Registration<T>, Error> + 'a {
183 pin_init::pin_init_scope(move || {
184 let request = vector.try_into()?;
185
186 Ok(irq::Registration::<T>::new(request, flags, name, handler))
187 })
188 }
189
190 /// Returns a [`kernel::irq::ThreadedRegistration`] for the given IRQ vector.
191 pub fn request_threaded_irq<'a, T: crate::irq::ThreadedHandler + 'static>(
192 &'a self,
193 vector: IrqVector<'a>,
194 flags: irq::Flags,
195 name: &'static CStr,
196 handler: impl PinInit<T, Error> + 'a,
197 ) -> impl PinInit<irq::ThreadedRegistration<T>, Error> + 'a {
198 pin_init::pin_init_scope(move || {
199 let request = vector.try_into()?;
200
201 Ok(irq::ThreadedRegistration::<T>::new(
202 request, flags, name, handler,
203 ))
204 })
205 }
206
207 /// Allocate IRQ vectors for this PCI device with automatic cleanup.
208 ///
209 /// Allocates between `min_vecs` and `max_vecs` interrupt vectors for the device.
210 /// The allocation will use MSI-X, MSI, or INTx interrupts based on the `irq_types`
211 /// parameter and hardware capabilities. When multiple types are specified, the kernel
212 /// will try them in order of preference: MSI-X first, then MSI, then INTx interrupts.
213 ///
214 /// The allocated vectors are automatically freed when the device is unbound, using the
215 /// devres (device resource management) system.
216 ///
217 /// # Arguments
218 ///
219 /// * `min_vecs` - Minimum number of vectors required.
220 /// * `max_vecs` - Maximum number of vectors to allocate.
221 /// * `irq_types` - Types of interrupts that can be used.
222 ///
223 /// # Returns
224 ///
225 /// Returns a range of IRQ vectors that were successfully allocated, or an error if the
226 /// allocation fails or cannot meet the minimum requirement.
227 ///
228 /// # Examples
229 ///
230 /// ```
231 /// # use kernel::{ device::Bound, pci};
232 /// # fn no_run(dev: &pci::Device<Bound>) -> Result {
233 /// // Allocate using any available interrupt type in the order mentioned above.
234 /// let vectors = dev.alloc_irq_vectors(1, 32, pci::IrqTypes::all())?;
235 ///
236 /// // Allocate MSI or MSI-X only (no INTx interrupts).
237 /// let msi_only = pci::IrqTypes::default()
238 /// .with(pci::IrqType::Msi)
239 /// .with(pci::IrqType::MsiX);
240 /// let vectors = dev.alloc_irq_vectors(4, 16, msi_only)?;
241 /// # Ok(())
242 /// # }
243 /// ```
244 pub fn alloc_irq_vectors(
245 &self,
246 min_vecs: u32,
247 max_vecs: u32,
248 irq_types: IrqTypes,
249 ) -> Result<RangeInclusive<IrqVector<'_>>> {
250 IrqVectorRegistration::register(self, min_vecs, max_vecs, irq_types)
251 }
252}