diff options
author | Andreas Hindborg <a.hindborg@samsung.com> | 2023-03-03 14:32:11 +0100 |
---|---|---|
committer | Danilo Krummrich <dakr@redhat.com> | 2023-11-22 22:00:54 +0100 |
commit | c30a9e11509e5d4b009b0cd35f141d00a04cfbdf (patch) | |
tree | d64b04b583508c8f68b351361d803fe3a6332840 | |
parent | 0dd158cd9917aa0a00f2d3bab904da2d43ef3db5 (diff) |
rust: pci: add IRQ and misc methods
Based on https://github.com/wedsonaf/linux/commit/02541e65a7e778c0049fed86ae49302bc07abed3
-rw-r--r-- | rust/kernel/pci.rs | 134 |
1 files changed, 130 insertions, 4 deletions
diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index a9d73b9d66c4..3f3a684caf23 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -8,11 +8,14 @@ use crate::{ bindings, device, driver, - error::{from_result, to_result, Result}, + error::{from_result, to_result, Error, Result}, + io_mem::Resource, + irq, str::CStr, types::ForeignOwnable, ThisModule, }; +use core::fmt; /// An adapter for the registration of PCI drivers. pub struct Adapter<T: Driver>(T); @@ -176,6 +179,7 @@ macro_rules! define_pci_id_table { }; }; } +pub use define_pci_id_table; /// A PCI driver pub trait Driver { @@ -186,7 +190,9 @@ pub trait Driver { /// /// Require that `Data` implements `ForeignOwnable`. We guarantee to /// never move the underlying wrapped data structure. This allows - type Data: ForeignOwnable + Send + Sync + driver::DeviceRemoval = (); + // TODO: Data Send + Sync ? + //type Data: ForeignOwnable + Send + Sync + driver::DeviceRemoval = (); + type Data: ForeignOwnable + driver::DeviceRemoval = (); /// The type holding information about each device id supported by the driver. type IdInfo: 'static = (); @@ -214,11 +220,131 @@ pub trait Driver { /// The field `ptr` is non-null and valid for the lifetime of the object. pub struct Device { ptr: *mut bindings::pci_dev, + res_taken: u64, } impl Device { - unsafe fn from_ptr(ptr: *mut bindings::pci_dev) -> Self { - Self { ptr } + pub unsafe fn from_ptr(ptr: *mut bindings::pci_dev) -> Self { + Self { ptr, res_taken: 0 } + } + + pub unsafe fn as_ptr(&self) -> *mut bindings::pci_dev { + self.ptr + } + + pub fn enable_device_mem(&self) -> Result { + let ret = unsafe { bindings::pci_enable_device_mem(self.ptr) }; + if ret != 0 { + Err(Error::from_errno(ret)) + } else { + Ok(()) + } + } + + pub fn set_master(&self) { + unsafe { bindings::pci_set_master(self.ptr) }; + } + + pub fn select_bars(&self, flags: core::ffi::c_ulong) -> i32 { + unsafe { bindings::pci_select_bars(self.ptr, flags) } + } + + pub fn request_selected_regions(&self, bars: i32, name: &'static CStr) -> Result { + let ret = + unsafe { bindings::pci_request_selected_regions(self.ptr, bars, name.as_char_ptr()) }; + if ret != 0 { + Err(Error::from_errno(ret)) + } else { + Ok(()) + } + } + + pub fn take_resource(&mut self, index: usize) -> Option<Resource> { + let pdev = unsafe { &*self.ptr }; + + // Fail if the index is beyond the end or if it has already been taken. + if index >= pdev.resource.len() || self.res_taken & (1 << index) != 0 { + return None; + } + + self.res_taken |= 1 << index; + Resource::new(pdev.resource[index].start, pdev.resource[index].end) + } + + pub fn irq(&self) -> Option<u32> { + let pdev = unsafe { &*self.ptr }; + + if pdev.irq == 0 { + None + } else { + Some(pdev.irq) + } + } + + pub fn alloc_irq_vectors(&mut self, min_vecs: u32, max_vecs: u32, flags: u32) -> Result<u32> { + let ret = unsafe { + bindings::pci_alloc_irq_vectors_affinity( + self.ptr, + min_vecs, + max_vecs, + flags, + core::ptr::null_mut(), + ) + }; + if ret < 0 { + Err(Error::from_errno(ret)) + } else { + Ok(ret as _) + } + } + + pub fn alloc_irq_vectors_affinity( + &mut self, + min_vecs: u32, + max_vecs: u32, + pre: u32, + post: u32, + flags: u32, + ) -> Result<u32> { + let mut affd = bindings::irq_affinity { + pre_vectors: pre, + post_vectors: post, + ..bindings::irq_affinity::default() + }; + + let ret = unsafe { + bindings::pci_alloc_irq_vectors_affinity( + self.ptr, + min_vecs, + max_vecs, + flags | bindings::PCI_IRQ_AFFINITY, + &mut affd, + ) + }; + if ret < 0 { + Err(Error::from_errno(ret)) + } else { + Ok(ret as _) + } + } + + pub fn free_irq_vectors(&mut self) { + unsafe { bindings::pci_free_irq_vectors(self.ptr) }; + } + + pub fn request_irq<T: irq::Handler>( + &self, + index: u32, + data: T::Data, + name_args: fmt::Arguments<'_>, + ) -> Result<irq::Registration<T>> { + let ret = unsafe { bindings::pci_irq_vector(self.ptr, index) }; + if ret < 0 { + return Err(Error::from_errno(ret)); + } + crate::pr_info!("Setting up IRQ: {}\n", ret); + + irq::Registration::try_new(ret as _, data, irq::flags::SHARED, name_args) } } |