Skip to content

Commit

Permalink
Merge pull request torvalds#565 from wedsonaf/gpio-irq
Browse files Browse the repository at this point in the history
rust: gpio: add support for registering irq chips with gpio chip.
  • Loading branch information
wedsonaf authored Nov 27, 2021
2 parents 0028943 + fbba2d5 commit fbb7b1a
Show file tree
Hide file tree
Showing 3 changed files with 321 additions and 4 deletions.
28 changes: 28 additions & 0 deletions rust/helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#include <linux/security.h>
#include <asm/io.h>
#include <linux/irq.h>
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdomain.h>
#include <linux/amba/bus.h>

__noreturn void rust_helper_BUG(void)
Expand Down Expand Up @@ -374,6 +376,32 @@ void *rust_helper_irq_data_get_irq_chip_data(struct irq_data *d)
}
EXPORT_SYMBOL_GPL(rust_helper_irq_data_get_irq_chip_data);

struct irq_chip *rust_helper_irq_desc_get_chip(struct irq_desc *desc)
{
return irq_desc_get_chip(desc);
}
EXPORT_SYMBOL_GPL(rust_helper_irq_desc_get_chip);

void *rust_helper_irq_desc_get_handler_data(struct irq_desc *desc)
{
return irq_desc_get_handler_data(desc);
}
EXPORT_SYMBOL_GPL(rust_helper_irq_desc_get_handler_data);

void rust_helper_chained_irq_enter(struct irq_chip *chip,
struct irq_desc *desc)
{
chained_irq_enter(chip, desc);
}
EXPORT_SYMBOL_GPL(rust_helper_chained_irq_enter);

void rust_helper_chained_irq_exit(struct irq_chip *chip,
struct irq_desc *desc)
{
chained_irq_exit(chip, desc);
}
EXPORT_SYMBOL_GPL(rust_helper_chained_irq_exit);

/* We use bindgen's --size_t-is-usize option to bind the C size_t type
* as the Rust usize type, so we can use it in contexts where Rust
* expects a usize like slice (array) indices. usize is defined to be
Expand Down
181 changes: 177 additions & 4 deletions rust/kernel/gpio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ use core::{
pin::Pin,
};

#[cfg(CONFIG_GPIOLIB_IRQCHIP)]
pub use irqchip::{ChipWithIrqChip, RegistrationWithIrqChip};

/// The direction of a gpio line.
pub enum LineDirection {
/// Direction is input.
Expand Down Expand Up @@ -143,14 +146,13 @@ impl<T: Chip> Registration<T> {
parent: &dyn device::RawDevice,
data: T::Data,
) -> Result {
// SAFETY: We never move out of `this`.
let this = unsafe { self.get_unchecked_mut() };

if this.parent.is_some() {
if self.parent.is_some() {
// Already registered.
return Err(Error::EINVAL);
}

// SAFETY: We never move out of `this`.
let this = unsafe { self.get_unchecked_mut() };
{
let gc = this.gc.get_mut();

Expand Down Expand Up @@ -299,3 +301,174 @@ unsafe extern "C" fn set_callback<T: Chip>(
let data = unsafe { T::Data::borrow(bindings::gpiochip_get_data(gc)) };
T::set(data, offset, value != 0);
}

#[cfg(CONFIG_GPIOLIB_IRQCHIP)]
mod irqchip {
use super::*;
use crate::irq;

/// A gpio chip that includes an irq chip.
pub trait ChipWithIrqChip: Chip {
/// Implements the irq flow for the gpio chip.
fn handle_irq_flow(
_data: <Self::Data as PointerWrapper>::Borrowed<'_>,
_desc: &irq::Descriptor,
_domain: &irq::Domain,
);
}

/// A registration of a gpio chip that includes an irq chip.
pub struct RegistrationWithIrqChip<T: ChipWithIrqChip> {
reg: Registration<T>,
irq_chip: UnsafeCell<bindings::irq_chip>,
parent_irq: u32,
}

impl<T: ChipWithIrqChip> RegistrationWithIrqChip<T> {
/// Creates a new [`RegistrationWithIrqChip`] but does not register it yet.
///
/// It is allowed to move.
pub fn new() -> Self {
Self {
reg: Registration::new(),
irq_chip: UnsafeCell::new(bindings::irq_chip::default()),
parent_irq: 0,
}
}

/// Registers a gpio chip and its irq chip with the rest of the kernel.
pub fn register<U: irq::Chip<Data = T::Data>>(
mut self: Pin<&mut Self>,
gpio_count: u16,
base: Option<i32>,
parent: &dyn device::RawDevice,
data: T::Data,
parent_irq: u32,
) -> Result {
if self.reg.parent.is_some() {
// Already registered.
return Err(Error::EINVAL);
}

// SAFETY: We never move out of `this`.
let this = unsafe { self.as_mut().get_unchecked_mut() };

// Initialise the irq_chip.
{
let irq_chip = this.irq_chip.get_mut();
irq_chip.name = parent.name().as_char_ptr();

// SAFETY: The gpio subsystem configures a pointer to `gpio_chip` as the irq chip
// data, so we use `IrqChipAdapter` to convert to the `T::Data`, which is the same
// as `irq::Chip::Data` per the bound above.
unsafe { irq::init_chip::<IrqChipAdapter<U>>(irq_chip) };
}

// Initialise gc irq state.
{
let girq = &mut this.reg.gc.get_mut().irq;
girq.chip = this.irq_chip.get();
// SAFETY: By leaving `parent_handler_data` set to `null`, the gpio subsystem
// initialises it to a pointer to the gpio chip, which is what `FlowHandler<T>`
// expects.
girq.parent_handler = unsafe { irq::new_flow_handler::<FlowHandler<T>>() };
girq.num_parents = 1;
girq.parents = &mut this.parent_irq;
this.parent_irq = parent_irq;
girq.default_type = bindings::IRQ_TYPE_NONE;
girq.handler = Some(bindings::handle_bad_irq);
}

// SAFETY: `reg` is pinned when `self` is.
let pinned = unsafe { self.map_unchecked_mut(|r| &mut r.reg) };
pinned.register(gpio_count, base, parent, data)
}
}

impl<T: ChipWithIrqChip> Default for RegistrationWithIrqChip<T> {
fn default() -> Self {
Self::new()
}
}

// SAFETY: `RegistrationWithIrqChip` doesn't offer any methods or access to fields when shared
// between threads or CPUs, so it is safe to share it.
unsafe impl<T: ChipWithIrqChip> Sync for RegistrationWithIrqChip<T> {}

// SAFETY: Registration with and unregistration from the gpio subsystem (including irq chips for
// them) can happen from any thread. Additionally, `T::Data` (which is dropped during
// unregistration) is `Send`, so it is ok to move `Registration` to different threads.
unsafe impl<T: ChipWithIrqChip> Send for RegistrationWithIrqChip<T> where T::Data: Send {}

struct FlowHandler<T: ChipWithIrqChip>(PhantomData<T>);

impl<T: ChipWithIrqChip> irq::FlowHandler for FlowHandler<T> {
type Data = *mut bindings::gpio_chip;

fn handle_irq_flow(gc: *mut bindings::gpio_chip, desc: &irq::Descriptor) {
// SAFETY: `FlowHandler` is only used in gpio chips, and it is removed when the gpio is
// unregistered, so we know that `gc` must still be valid. We also know that the value
// stored as gpio data was returned by `T::Data::into_pointer` again because
// `FlowHandler` is a private structure only used in this way.
let data = unsafe { T::Data::borrow(bindings::gpiochip_get_data(gc)) };

// SAFETY: `gc` is valid (see comment above), so we can dereference it.
let domain = unsafe { irq::Domain::from_ptr((*gc).irq.domain) };

T::handle_irq_flow(data, desc, &domain);
}
}

/// Adapter from an irq chip with `gpio_chip` pointer as context to one where the gpio chip
/// data is passed as context.
struct IrqChipAdapter<T: irq::Chip>(PhantomData<T>);

impl<T: irq::Chip> irq::Chip for IrqChipAdapter<T> {
type Data = *mut bindings::gpio_chip;
const TO_USE: irq::ToUse = T::TO_USE;

fn ack(gc: *mut bindings::gpio_chip, irq_data: &irq::IrqData) {
// SAFETY: `IrqChipAdapter` is a private struct, only used when the data stored in the
// gpio chip is known to come from `T::Data`, and only valid while the gpio chip is
// registered, so `gc` is valid.
let data = unsafe { T::Data::borrow(bindings::gpiochip_get_data(gc as _)) };
T::ack(data, irq_data);
}

fn mask(gc: *mut bindings::gpio_chip, irq_data: &irq::IrqData) {
// SAFETY: `IrqChipAdapter` is a private struct, only used when the data stored in the
// gpio chip is known to come from `T::Data`, and only valid while the gpio chip is
// registered, so `gc` is valid.
let data = unsafe { T::Data::borrow(bindings::gpiochip_get_data(gc as _)) };
T::mask(data, irq_data);
}

fn unmask(gc: *mut bindings::gpio_chip, irq_data: &irq::IrqData) {
// SAFETY: `IrqChipAdapter` is a private struct, only used when the data stored in the
// gpio chip is known to come from `T::Data`, and only valid while the gpio chip is
// registered, so `gc` is valid.
let data = unsafe { T::Data::borrow(bindings::gpiochip_get_data(gc as _)) };
T::unmask(data, irq_data);
}

fn set_type(
gc: *mut bindings::gpio_chip,
irq_data: &mut irq::LockedIrqData,
flow_type: u32,
) -> Result<irq::ExtraResult> {
// SAFETY: `IrqChipAdapter` is a private struct, only used when the data stored in the
// gpio chip is known to come from `T::Data`, and only valid while the gpio chip is
// registered, so `gc` is valid.
let data = unsafe { T::Data::borrow(bindings::gpiochip_get_data(gc as _)) };
T::set_type(data, irq_data, flow_type)
}

fn set_wake(gc: *mut bindings::gpio_chip, irq_data: &irq::IrqData, on: bool) -> Result {
// SAFETY: `IrqChipAdapter` is a private struct, only used when the data stored in the
// gpio chip is known to come from `T::Data`, and only valid while the gpio chip is
// registered, so `gc` is valid.
let data = unsafe { T::Data::borrow(bindings::gpiochip_get_data(gc as _)) };
T::set_wake(data, irq_data, on)
}
}
}
116 changes: 116 additions & 0 deletions rust/kernel/irq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,3 +289,119 @@ impl Type {
/// The interrupt is triggered while the signal is held low.
pub const LEVEL_LOW: u32 = bindings::IRQ_TYPE_LEVEL_LOW;
}

/// Wraps the kernel's `struct irq_desc`.
///
/// # Invariants
///
/// The pointer `Descriptor::ptr` is non-null and valid.
pub struct Descriptor {
pub(crate) ptr: *mut bindings::irq_desc,
}

impl Descriptor {
/// Constructs a new `struct irq_desc` wrapper.
///
/// # Safety
///
/// The pointer `ptr` must be non-null and valid for the lifetime of the returned object.
unsafe fn from_ptr(ptr: *mut bindings::irq_desc) -> Self {
// INVARIANT: The safety requirements ensure the invariant.
Self { ptr }
}

/// Calls `chained_irq_enter` and returns a guard that calls `chained_irq_exit` once dropped.
///
/// It is meant to be used by chained irq handlers to dispatch irqs to the next handlers.
pub fn enter_chained(&self) -> ChainedGuard<'_> {
// SAFETY: By the type invariants, `ptr` is always non-null and valid.
let irq_chip = unsafe { bindings::irq_desc_get_chip(self.ptr) };

// SAFETY: By the type invariants, `ptr` is always non-null and valid. `irq_chip` was just
// returned from `ptr`, so it is still valid too.
unsafe { bindings::chained_irq_enter(irq_chip, self.ptr) };
ChainedGuard {
desc: self,
irq_chip,
}
}
}

/// A guard to call `chained_irq_exit` after `chained_irq_enter` was called.
///
/// It is also used as evidence that a previous `chained_irq_enter` was called. So there are no
/// public constructors and it is only created after indeed calling `chained_irq_enter`.
pub struct ChainedGuard<'a> {
desc: &'a Descriptor,
irq_chip: *mut bindings::irq_chip,
}

impl Drop for ChainedGuard<'_> {
fn drop(&mut self) {
// SAFETY: The lifetime of `ChainedGuard` guarantees that `self.desc` remains valid, so it
// also guarantess `irq_chip` (which was returned from it) and `self.desc.ptr` (guaranteed
// by the type invariants).
unsafe { bindings::chained_irq_exit(self.irq_chip, self.desc.ptr) };
}
}

/// Wraps the kernel's `struct irq_domain`.
///
/// # Invariants
///
/// The pointer `Domain::ptr` is non-null and valid.
pub struct Domain {
ptr: *mut bindings::irq_domain,
}

impl Domain {
/// Constructs a new `struct irq_domain` wrapper.
///
/// # Safety
///
/// The pointer `ptr` must be non-null and valid for the lifetime of the returned object.
pub(crate) unsafe fn from_ptr(ptr: *mut bindings::irq_domain) -> Self {
// INVARIANT: The safety requirements ensure the invariant.
Self { ptr }
}

/// Invokes the chained handler of the given hw irq of the given domain.
///
/// It requires evidence that `chained_irq_enter` was called, which is done by passing a
/// `ChainedGuard` instance.
pub fn generic_handle_chained(&self, hwirq: u32, _guard: &ChainedGuard<'_>) {
// SAFETY: `ptr` is valid by the type invariants.
unsafe { bindings::generic_handle_domain_irq(self.ptr, hwirq) };
}
}

/// A high-level irq flow handler.
pub trait FlowHandler {
/// The data associated with the handler.
type Data: PointerWrapper;

/// Implements the irq flow for the given descriptor.
fn handle_irq_flow(data: <Self::Data as PointerWrapper>::Borrowed<'_>, desc: &Descriptor);
}

/// Returns the raw irq flow handler corresponding to the (high-level) one defined in `T`.
///
/// # Safety
///
/// The caller must ensure that the value stored in the irq handler data (as returned by
/// `irq_desc_get_handler_data`) is the result of calling [`PointerWrapper::into_pointer] for the
/// [`T::Data`] type.
pub(crate) unsafe fn new_flow_handler<T: FlowHandler>() -> bindings::irq_flow_handler_t {
Some(irq_flow_handler::<T>)
}

unsafe extern "C" fn irq_flow_handler<T: FlowHandler>(desc: *mut bindings::irq_desc) {
// SAFETY: By the safety requirements of `new_flow_handler`, we know that the value returned by
// `irq_desc_get_handler_data` comes from calling `T::Data::into_pointer`. `desc` is valid by
// the C API contract.
let data = unsafe { T::Data::borrow(bindings::irq_desc_get_handler_data(desc)) };

// SAFETY: The C API guarantees that `desc` is valid for the duration of this call, which
// outlives the lifetime returned by `from_desc`.
T::handle_irq_flow(data, &unsafe { Descriptor::from_ptr(desc) });
}

0 comments on commit fbb7b1a

Please sign in to comment.