Commit 17e70f0c authored by Daniel Almeida's avatar Daniel Almeida Committed by Danilo Krummrich
Browse files

rust: platform: add irq accessors



These accessors can be used to retrieve a irq::Registration and
irq::ThreadedRegistration from a platform device by
index or name. Alternatively, drivers can retrieve an IrqRequest from a
bound platform device for later use.

These accessors ensure that only valid IRQ lines can ever be registered.

Reviewed-by: default avatarAlice Ryhl <aliceryhl@google.com>
Tested-by: default avatarJoel Fernandes <joelagnelf@nvidia.com>
Tested-by: default avatarDirk Behme <dirk.behme@de.bosch.com>
Signed-off-by: default avatarDaniel Almeida <daniel.almeida@collabora.com>
Link: https://lore.kernel.org/r/20250811-topics-tyr-request_irq2-v9-5-0485dcd9bcbf@collabora.com


[ Remove expect(dead_code) from IrqRequest::new(), re-format macros and
  macro invocations to not exceed 100 characters line length. - Danilo ]
Signed-off-by: default avatarDanilo Krummrich <dakr@kernel.org>
parent 135d4052
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -111,7 +111,6 @@ impl<'a> IrqRequest<'a> {
    /// # Safety
    ///
    /// - `irq` should be a valid IRQ number for `dev`.
    #[expect(dead_code)]
    pub(crate) unsafe fn new(dev: &'a Device<Bound>, irq: u32) -> Self {
        // INVARIANT: `irq` is a valid IRQ number for `dev`.
        IrqRequest { dev, irq }
+176 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@
    driver,
    error::{from_result, to_result, Result},
    io::{mem::IoRequest, Resource},
    irq::{self, IrqRequest},
    of,
    prelude::*,
    types::Opaque,
@@ -284,6 +285,181 @@ pub fn io_request_by_name(&self, name: &CStr) -> Option<IoRequest<'_>> {
    }
}

macro_rules! define_irq_accessor_by_index {
    (
        $(#[$meta:meta])* $fn_name:ident,
        $request_fn:ident,
        $reg_type:ident,
        $handler_trait:ident
    ) => {
        $(#[$meta])*
        pub fn $fn_name<'a, T: irq::$handler_trait + 'static>(
            &'a self,
            flags: irq::Flags,
            index: u32,
            name: &'static CStr,
            handler: impl PinInit<T, Error> + 'a,
        ) -> Result<impl PinInit<irq::$reg_type<T>, Error> + 'a> {
            let request = self.$request_fn(index)?;

            Ok(irq::$reg_type::<T>::new(
                request,
                flags,
                name,
                handler,
            ))
        }
    };
}

macro_rules! define_irq_accessor_by_name {
    (
        $(#[$meta:meta])* $fn_name:ident,
        $request_fn:ident,
        $reg_type:ident,
        $handler_trait:ident
    ) => {
        $(#[$meta])*
        pub fn $fn_name<'a, T: irq::$handler_trait + 'static>(
            &'a self,
            flags: irq::Flags,
            irq_name: &CStr,
            name: &'static CStr,
            handler: impl PinInit<T, Error> + 'a,
        ) -> Result<impl PinInit<irq::$reg_type<T>, Error> + 'a> {
            let request = self.$request_fn(irq_name)?;

            Ok(irq::$reg_type::<T>::new(
                request,
                flags,
                name,
                handler,
            ))
        }
    };
}

impl Device<Bound> {
    /// Returns an [`IrqRequest`] for the IRQ at the given index, if any.
    pub fn irq_by_index(&self, index: u32) -> Result<IrqRequest<'_>> {
        // SAFETY: `self.as_raw` returns a valid pointer to a `struct platform_device`.
        let irq = unsafe { bindings::platform_get_irq(self.as_raw(), index) };

        if irq < 0 {
            return Err(Error::from_errno(irq));
        }

        // SAFETY: `irq` is guaranteed to be a valid IRQ number for `&self`.
        Ok(unsafe { IrqRequest::new(self.as_ref(), irq as u32) })
    }

    /// Returns an [`IrqRequest`] for the IRQ at the given index, but does not
    /// print an error if the IRQ cannot be obtained.
    pub fn optional_irq_by_index(&self, index: u32) -> Result<IrqRequest<'_>> {
        // SAFETY: `self.as_raw` returns a valid pointer to a `struct platform_device`.
        let irq = unsafe { bindings::platform_get_irq_optional(self.as_raw(), index) };

        if irq < 0 {
            return Err(Error::from_errno(irq));
        }

        // SAFETY: `irq` is guaranteed to be a valid IRQ number for `&self`.
        Ok(unsafe { IrqRequest::new(self.as_ref(), irq as u32) })
    }

    /// Returns an [`IrqRequest`] for the IRQ with the given name, if any.
    pub fn irq_by_name(&self, name: &CStr) -> Result<IrqRequest<'_>> {
        // SAFETY: `self.as_raw` returns a valid pointer to a `struct platform_device`.
        let irq = unsafe { bindings::platform_get_irq_byname(self.as_raw(), name.as_char_ptr()) };

        if irq < 0 {
            return Err(Error::from_errno(irq));
        }

        // SAFETY: `irq` is guaranteed to be a valid IRQ number for `&self`.
        Ok(unsafe { IrqRequest::new(self.as_ref(), irq as u32) })
    }

    /// Returns an [`IrqRequest`] for the IRQ with the given name, but does not
    /// print an error if the IRQ cannot be obtained.
    pub fn optional_irq_by_name(&self, name: &CStr) -> Result<IrqRequest<'_>> {
        // SAFETY: `self.as_raw` returns a valid pointer to a `struct platform_device`.
        let irq = unsafe {
            bindings::platform_get_irq_byname_optional(self.as_raw(), name.as_char_ptr())
        };

        if irq < 0 {
            return Err(Error::from_errno(irq));
        }

        // SAFETY: `irq` is guaranteed to be a valid IRQ number for `&self`.
        Ok(unsafe { IrqRequest::new(self.as_ref(), irq as u32) })
    }

    define_irq_accessor_by_index!(
        /// Returns a [`irq::Registration`] for the IRQ at the given index.
        request_irq_by_index,
        irq_by_index,
        Registration,
        Handler
    );
    define_irq_accessor_by_name!(
        /// Returns a [`irq::Registration`] for the IRQ with the given name.
        request_irq_by_name,
        irq_by_name,
        Registration,
        Handler
    );
    define_irq_accessor_by_index!(
        /// Does the same as [`Self::request_irq_by_index`], except that it does
        /// not print an error message if the IRQ cannot be obtained.
        request_optional_irq_by_index,
        optional_irq_by_index,
        Registration,
        Handler
    );
    define_irq_accessor_by_name!(
        /// Does the same as [`Self::request_irq_by_name`], except that it does
        /// not print an error message if the IRQ cannot be obtained.
        request_optional_irq_by_name,
        optional_irq_by_name,
        Registration,
        Handler
    );

    define_irq_accessor_by_index!(
        /// Returns a [`irq::ThreadedRegistration`] for the IRQ at the given index.
        request_threaded_irq_by_index,
        irq_by_index,
        ThreadedRegistration,
        ThreadedHandler
    );
    define_irq_accessor_by_name!(
        /// Returns a [`irq::ThreadedRegistration`] for the IRQ with the given name.
        request_threaded_irq_by_name,
        irq_by_name,
        ThreadedRegistration,
        ThreadedHandler
    );
    define_irq_accessor_by_index!(
        /// Does the same as [`Self::request_threaded_irq_by_index`], except
        /// that it does not print an error message if the IRQ cannot be
        /// obtained.
        request_optional_threaded_irq_by_index,
        optional_irq_by_index,
        ThreadedRegistration,
        ThreadedHandler
    );
    define_irq_accessor_by_name!(
        /// Does the same as [`Self::request_threaded_irq_by_name`], except that
        /// it does not print an error message if the IRQ cannot be obtained.
        request_optional_threaded_irq_by_name,
        optional_irq_by_name,
        ThreadedRegistration,
        ThreadedHandler
    );
}

// SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic
// argument.
kernel::impl_device_context_deref!(unsafe { Device });