Skip to content

Commit ef0b595

Browse files
committed
rust: add support for defining irq chips.
This is used by the PL061 driver. Signed-off-by: Wedson Almeida Filho <[email protected]>
1 parent 4946eec commit ef0b595

File tree

4 files changed

+312
-0
lines changed

4 files changed

+312
-0
lines changed

rust/helpers.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <linux/platform_device.h>
1313
#include <linux/security.h>
1414
#include <asm/io.h>
15+
#include <linux/irq.h>
1516

1617
__noreturn void rust_helper_BUG(void)
1718
{
@@ -323,6 +324,19 @@ void rust_helper_write_seqcount_end(seqcount_t *s)
323324
}
324325
EXPORT_SYMBOL_GPL(rust_helper_write_seqcount_end);
325326

327+
void rust_helper_irq_set_handler_locked(struct irq_data *data,
328+
irq_flow_handler_t handler)
329+
{
330+
irq_set_handler_locked(data, handler);
331+
}
332+
EXPORT_SYMBOL_GPL(rust_helper_irq_set_handler_locked);
333+
334+
void *rust_helper_irq_data_get_irq_chip_data(struct irq_data *d)
335+
{
336+
return irq_data_get_irq_chip_data(d);
337+
}
338+
EXPORT_SYMBOL_GPL(rust_helper_irq_data_get_irq_chip_data);
339+
326340
/* We use bindgen's --size_t-is-usize option to bind the C size_t type
327341
* as the Rust usize type, so we can use it in contexts where Rust
328342
* expects a usize like slice (array) indices. usize is defined to be

rust/kernel/bindings_helper.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
#include <linux/of_platform.h>
2020
#include <linux/security.h>
2121
#include <asm/io.h>
22+
#include <linux/irq.h>
23+
#include <linux/interrupt.h>
24+
#include <linux/irqdomain.h>
2225

2326
// `bindgen` gets confused at certain things
2427
const gfp_t BINDINGS_GFP_KERNEL = GFP_KERNEL;

rust/kernel/irq.rs

Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! Interrupts and interrupt chips.
4+
//!
5+
//! See <https://www.kernel.org/doc/Documentation/core-api/genericirq.rst>.
6+
//!
7+
//! C headers: [`include/linux/irq.h`](../../../../include/linux/irq.h) and
8+
//! [`include/linux/interrupt.h`](../../../../include/linux/interrupt.h).
9+
10+
#![allow(dead_code)]
11+
12+
use crate::{bindings, c_types, from_kernel_result, types::PointerWrapper, Error, Result};
13+
use core::ops::Deref;
14+
15+
type IrqHwNumber = bindings::irq_hw_number_t;
16+
17+
/// Wraps the kernel's `struct irq_data`.
18+
///
19+
/// # Invariants
20+
///
21+
/// The pointer `IrqData::ptr` is non-null and valid.
22+
pub struct IrqData {
23+
ptr: *mut bindings::irq_data,
24+
}
25+
26+
impl IrqData {
27+
/// Creates a new `IrqData` instance from a raw pointer.
28+
///
29+
/// # Safety
30+
///
31+
/// Callers must ensure that `ptr` is non-null and valid when the function is called, and that
32+
/// it remains valid for the lifetime of the return [`IrqData`] instance.
33+
unsafe fn from_ptr(ptr: *mut bindings::irq_data) -> Self {
34+
// INVARIANTS: By the safety requirements, the instance we're creating satisfies the type
35+
// invariants.
36+
Self { ptr }
37+
}
38+
39+
/// Returns the hardware irq number.
40+
pub fn hwirq(&self) -> IrqHwNumber {
41+
// SAFETY: By the type invariants, it's ok to dereference `ptr`.
42+
unsafe { (*self.ptr).hwirq }
43+
}
44+
}
45+
46+
/// Wraps the kernel's `struct irq_data` when it is locked.
47+
///
48+
/// Being locked allows additional operations to be performed on the data.
49+
pub struct LockedIrqData(IrqData);
50+
51+
impl LockedIrqData {
52+
/// Sets the high-level irq flow handler to the builtin one for level-triggered irqs.
53+
pub fn set_level_handler(&mut self) {
54+
// SAFETY: By the type invariants of `self.0`, we know `self.0.ptr` is valid.
55+
unsafe { bindings::irq_set_handler_locked(self.0.ptr, Some(bindings::handle_level_irq)) };
56+
}
57+
58+
/// Sets the high-level irq flow handler to the builtin one for edge-triggered irqs.
59+
pub fn set_edge_handler(&mut self) {
60+
// SAFETY: By the type invariants of `self.0`, we know `self.0.ptr` is valid.
61+
unsafe { bindings::irq_set_handler_locked(self.0.ptr, Some(bindings::handle_edge_irq)) };
62+
}
63+
64+
/// Sets the high-level irq flow handler to the builtin one for bad irqs.
65+
pub fn set_bad_handler(&mut self) {
66+
// SAFETY: By the type invariants of `self.0`, we know `self.0.ptr` is valid.
67+
unsafe { bindings::irq_set_handler_locked(self.0.ptr, Some(bindings::handle_bad_irq)) };
68+
}
69+
}
70+
71+
impl Deref for LockedIrqData {
72+
type Target = IrqData;
73+
74+
fn deref(&self) -> &Self::Target {
75+
&self.0
76+
}
77+
}
78+
79+
/// Extra information returned by some of the [`Chip`] methods on success.
80+
pub enum ExtraResult {
81+
/// Indicates that the caller (irq core) will update the descriptor state.
82+
None = bindings::IRQ_SET_MASK_OK as _,
83+
84+
/// Indicates that the callee (irq chip implementation) already updated the descriptor state.
85+
NoCopy = bindings::IRQ_SET_MASK_OK_NOCOPY as _,
86+
87+
/// Same as [`ExtraResult::None`] in terms of updating descriptor state. It is used in stacked
88+
/// irq chips to indicate that descendant chips should be skipped.
89+
Done = bindings::IRQ_SET_MASK_OK_DONE as _,
90+
}
91+
92+
/// An irq chip.
93+
///
94+
/// It is a trait for the functions defined in [`struct irq_chip`].
95+
///
96+
/// [`struct irq_chip`]: ../../../include/linux/irq.h
97+
pub trait Chip: Sized {
98+
/// The type of the context data stored in the irq chip and made available on each callback.
99+
type Data: PointerWrapper;
100+
101+
/// The methods to use to populate [`struct irq_chip`]. This is typically populated with
102+
/// [`declare_irq_chip_operations`].
103+
const TO_USE: ToUse;
104+
105+
/// Called at the start of a new interrupt.
106+
fn ack(data: &<<Self::Data as PointerWrapper>::Borrowed as Deref>::Target, irq_data: &IrqData);
107+
108+
/// Masks an interrupt source.
109+
fn mask(data: &<<Self::Data as PointerWrapper>::Borrowed as Deref>::Target, irq_data: &IrqData);
110+
111+
/// Unmasks an interrupt source.
112+
fn unmask(
113+
data: &<<Self::Data as PointerWrapper>::Borrowed as Deref>::Target,
114+
irq_data: &IrqData,
115+
);
116+
117+
/// Sets the flow type of an interrupt.
118+
///
119+
/// The flow type is a combination of the constants in [`Type`].
120+
fn set_type(
121+
_data: &<<Self::Data as PointerWrapper>::Borrowed as Deref>::Target,
122+
_irq_data: &mut LockedIrqData,
123+
_flow_type: u32,
124+
) -> Result<ExtraResult> {
125+
Ok(ExtraResult::None)
126+
}
127+
128+
/// Enables or disables power-management wake-on of an interrupt.
129+
fn set_wake(
130+
_data: &<<Self::Data as PointerWrapper>::Borrowed as Deref>::Target,
131+
_irq_data: &IrqData,
132+
_on: bool,
133+
) -> Result {
134+
Ok(())
135+
}
136+
}
137+
138+
/// Initialises `chip` with the callbacks defined in `T`.
139+
///
140+
/// # Safety
141+
///
142+
/// The caller must ensure that the value stored in the irq chip data is the result of calling
143+
/// [`PointerWrapper::into_pointer] for the [`T::Data`] type.
144+
pub(crate) unsafe fn init_chip<T: Chip>(chip: &mut bindings::irq_chip) {
145+
chip.irq_ack = Some(irq_ack_callback::<T>);
146+
chip.irq_mask = Some(irq_mask_callback::<T>);
147+
chip.irq_unmask = Some(irq_unmask_callback::<T>);
148+
149+
if T::TO_USE.set_type {
150+
chip.irq_set_type = Some(irq_set_type_callback::<T>);
151+
}
152+
153+
if T::TO_USE.set_wake {
154+
chip.irq_set_wake = Some(irq_set_wake_callback::<T>);
155+
}
156+
}
157+
158+
/// Represents which fields of [`struct irq_chip`] should be populated with pointers.
159+
///
160+
/// This is typically populated with the [`declare_irq_chip_operations`] macro.
161+
pub struct ToUse {
162+
/// The `irq_set_type` field of [`struct irq_chip`].
163+
pub set_type: bool,
164+
165+
/// The `irq_set_wake` field of [`struct irq_chip`].
166+
pub set_wake: bool,
167+
}
168+
169+
/// A constant version where all values are to set to `false`, that is, all supported fields will
170+
/// be set to null pointers.
171+
pub const USE_NONE: ToUse = ToUse {
172+
set_type: false,
173+
set_wake: false,
174+
};
175+
176+
/// Defines the [`Chip::TO_USE`] field based on a list of fields to be populated.
177+
#[macro_export]
178+
macro_rules! declare_irq_chip_operations {
179+
() => {
180+
const TO_USE: $crate::irq::ToUse = $crate::irq::USE_NONE;
181+
};
182+
($($i:ident),+) => {
183+
const TO_USE: $crate::irq::ToUse =
184+
$crate::irq::ToUse {
185+
$($i: true),+ ,
186+
..$crate::irq::USE_NONE
187+
};
188+
};
189+
}
190+
191+
/// Enables or disables power-management wake-on for the given irq number.
192+
pub fn set_wake(irq: u32, on: bool) -> Result {
193+
// SAFETY: Just an FFI call, there are no extra requirements for safety.
194+
let ret = unsafe { bindings::irq_set_irq_wake(irq, on as _) };
195+
if ret < 0 {
196+
Err(Error::from_kernel_errno(ret))
197+
} else {
198+
Ok(())
199+
}
200+
}
201+
202+
unsafe extern "C" fn irq_ack_callback<T: Chip>(irq_data: *mut bindings::irq_data) {
203+
// SAFETY: The safety requirements of `init_chip`, which is the only place that uses this
204+
// callback, ensure that the value stored as irq chip data comes from a previous call to
205+
// `PointerWrapper::into_pointer`.
206+
let data = unsafe { T::Data::borrow(bindings::irq_data_get_irq_chip_data(irq_data)) };
207+
208+
// SAFETY: The value returned by `IrqData` is only valid until the end of this function, and
209+
// `irq_data` is guaranteed to be valid until then (by the contract with C code).
210+
T::ack(&data, unsafe { &IrqData::from_ptr(irq_data) })
211+
}
212+
213+
unsafe extern "C" fn irq_mask_callback<T: Chip>(irq_data: *mut bindings::irq_data) {
214+
// SAFETY: The safety requirements of `init_chip`, which is the only place that uses this
215+
// callback, ensure that the value stored as irq chip data comes from a previous call to
216+
// `PointerWrapper::into_pointer`.
217+
let data = unsafe { T::Data::borrow(bindings::irq_data_get_irq_chip_data(irq_data)) };
218+
219+
// SAFETY: The value returned by `IrqData` is only valid until the end of this function, and
220+
// `irq_data` is guaranteed to be valid until then (by the contract with C code).
221+
T::mask(&data, unsafe { &IrqData::from_ptr(irq_data) })
222+
}
223+
224+
unsafe extern "C" fn irq_unmask_callback<T: Chip>(irq_data: *mut bindings::irq_data) {
225+
// SAFETY: The safety requirements of `init_chip`, which is the only place that uses this
226+
// callback, ensure that the value stored as irq chip data comes from a previous call to
227+
// `PointerWrapper::into_pointer`.
228+
let data = unsafe { T::Data::borrow(bindings::irq_data_get_irq_chip_data(irq_data)) };
229+
230+
// SAFETY: The value returned by `IrqData` is only valid until the end of this function, and
231+
// `irq_data` is guaranteed to be valid until then (by the contract with C code).
232+
T::unmask(&data, unsafe { &IrqData::from_ptr(irq_data) })
233+
}
234+
235+
unsafe extern "C" fn irq_set_type_callback<T: Chip>(
236+
irq_data: *mut bindings::irq_data,
237+
flow_type: c_types::c_uint,
238+
) -> c_types::c_int {
239+
from_kernel_result! {
240+
// SAFETY: The safety requirements of `init_chip`, which is the only place that uses this
241+
// callback, ensure that the value stored as irq chip data comes from a previous call to
242+
// `PointerWrapper::into_pointer`.
243+
let data = unsafe { T::Data::borrow(bindings::irq_data_get_irq_chip_data(irq_data)) };
244+
245+
// SAFETY: The value returned by `IrqData` is only valid until the end of this function, and
246+
// `irq_data` is guaranteed to be valid until then (by the contract with C code).
247+
let ret = T::set_type(&data, &mut LockedIrqData(unsafe { IrqData::from_ptr(irq_data) }), flow_type)?;
248+
Ok(ret as _)
249+
}
250+
}
251+
252+
unsafe extern "C" fn irq_set_wake_callback<T: Chip>(
253+
irq_data: *mut bindings::irq_data,
254+
on: c_types::c_uint,
255+
) -> c_types::c_int {
256+
from_kernel_result! {
257+
// SAFETY: The safety requirements of `init_chip`, which is the only place that uses this
258+
// callback, ensure that the value stored as irq chip data comes from a previous call to
259+
// `PointerWrapper::into_pointer`.
260+
let data = unsafe { T::Data::borrow(bindings::irq_data_get_irq_chip_data(irq_data)) };
261+
262+
// SAFETY: The value returned by `IrqData` is only valid until the end of this function, and
263+
// `irq_data` is guaranteed to be valid until then (by the contract with C code).
264+
T::set_wake(&data, unsafe { &IrqData::from_ptr(irq_data) }, on != 0)?;
265+
Ok(0)
266+
}
267+
}
268+
269+
/// Contains constants that describes how an interrupt can be triggered.
270+
///
271+
/// It is tagged with `non_exhaustive` to prevent users from instantiating it.
272+
#[non_exhaustive]
273+
pub struct Type;
274+
275+
impl Type {
276+
/// The interrupt cannot be triggered.
277+
pub const NONE: u32 = bindings::IRQ_TYPE_NONE;
278+
279+
/// The interrupt is triggered when the signal goes from low to high.
280+
pub const EDGE_RISING: u32 = bindings::IRQ_TYPE_EDGE_RISING;
281+
282+
/// The interrupt is triggered when the signal goes from high to low.
283+
pub const EDGE_FALLING: u32 = bindings::IRQ_TYPE_EDGE_FALLING;
284+
285+
/// The interrupt is triggered when the signal goes from low to high and when it goes to high
286+
/// to low.
287+
pub const EDGE_BOTH: u32 = bindings::IRQ_TYPE_EDGE_BOTH;
288+
289+
/// The interrupt is triggered while the signal is held high.
290+
pub const LEVEL_HIGH: u32 = bindings::IRQ_TYPE_LEVEL_HIGH;
291+
292+
/// The interrupt is triggered while the signal is held low.
293+
pub const LEVEL_LOW: u32 = bindings::IRQ_TYPE_LEVEL_LOW;
294+
}

rust/kernel/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ pub mod chrdev;
4747
mod error;
4848
pub mod file;
4949
pub mod file_operations;
50+
pub mod irq;
5051
pub mod miscdev;
5152
pub mod pages;
5253
pub mod power;

0 commit comments

Comments
 (0)