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