summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Hindborg <a.hindborg@samsung.com>2023-03-03 14:32:11 +0100
committerDanilo Krummrich <dakr@redhat.com>2023-11-22 22:00:54 +0100
commitc30a9e11509e5d4b009b0cd35f141d00a04cfbdf (patch)
treed64b04b583508c8f68b351361d803fe3a6332840
parent0dd158cd9917aa0a00f2d3bab904da2d43ef3db5 (diff)
rust: pci: add IRQ and misc methods
Based on https://github.com/wedsonaf/linux/commit/02541e65a7e778c0049fed86ae49302bc07abed3
-rw-r--r--rust/kernel/pci.rs134
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)
}
}