arm_gic_driver/version/mod.rs
1use tock_registers::{interfaces::*, registers::*};
2
3pub mod v2;
4pub mod v3;
5
6use crate::define::*;
7
8#[cfg(feature = "rdif")]
9mod rdif;
10
11/// 通用 trait:为一组 ReadWrite<u32> 寄存器设置某一位
12trait IrqVecWriteable {
13 fn set_irq_bit(&self, intid: u32);
14 fn clear_irq_bit(&self, intid: u32);
15}
16trait IrqVecReadable {
17 fn get_irq_bit(&self, intid: u32) -> bool;
18}
19
20impl IrqVecWriteable for [ReadWrite<u32>] {
21 fn set_irq_bit(&self, index: u32) {
22 let reg_index = (index / 32) as usize;
23 let bit = 1 << (index % 32);
24 // For GIC ISENABLER/ISPENDR/ISACTIVER etc, writing 1 sets the bit
25 // Writing 0 has no effect, so we can safely write only the target bit
26 self[reg_index].set(bit);
27 }
28 fn clear_irq_bit(&self, intid: u32) {
29 let reg_index = (intid / 32) as usize;
30 let bit = 1 << (intid % 32);
31 let old = self[reg_index].get();
32 if old & bit == 0 {
33 return; // Already cleared
34 }
35 self[reg_index].set(old & !bit);
36 }
37}
38
39impl IrqVecReadable for [ReadWrite<u32>] {
40 fn get_irq_bit(&self, index: u32) -> bool {
41 let reg_index = (index / 32) as usize;
42 let bit = 1 << (index % 32);
43 self[reg_index].get() & bit != 0
44 }
45}
46
47/// Parse interrupt configuration from device tree interrupt specifier.
48///
49/// This function interprets device tree interrupt specifiers according to the
50/// ARM GIC device tree binding specification. It supports various interrupt types
51/// and configurations as defined by the Linux kernel's GIC driver.
52///
53/// Based on Linux GIC driver's `gic_irq_domain_translate` function.
54///
55/// # Arguments
56///
57/// * `itr` - Interrupt specifier array from device tree. The format depends on
58/// the interrupt type:
59/// - SGI: `[sgi_id]` - Single parameter for SGI 0-15
60/// - PPI/SPI: `[type, number, flags]` - Standard 3-parameter format
61/// - Extended: `[type, number, flags]` - For ESPI/EPPI/LPI/Partitioned PPI
62///
63/// # Interrupt Types (first parameter)
64///
65/// - `0` (SPI): Shared Peripheral Interrupt
66/// - `1` (PPI): Private Peripheral Interrupt
67/// - `2` (ESPI): Extended SPI (starting at ID 4096)
68/// - `3` (EPPI): Extended PPI (starting at ID 1056)
69/// - `4` (LPI): Locality-specific Peripheral Interrupt
70/// - `5` (PARTITION): Partitioned PPI
71///
72/// # Interrupt Flags (third parameter)
73///
74/// - `0x1`: Edge rising
75/// - `0x2`: Edge falling
76/// - `0x3`: Edge both
77/// - `0x4`: Level high
78/// - `0x8`: Level low
79///
80/// # Returns
81///
82/// Returns `Ok(IrqConfig)` with the parsed interrupt configuration, or
83/// `Err(&'static str)` if the input is invalid.
84///
85/// # Errors
86///
87/// - "Invalid IRQ configuration: need at least 3 parameters" - Insufficient parameters
88/// - "Invalid IRQ type" - Unknown interrupt type
89/// - "IRQ_TYPE_NONE is not allowed for IRQ type" - Missing trigger configuration
90/// - "Invalid IRQ trigger type" - Unknown trigger flags
91///
92/// # Examples
93///
94/// ```no_run
95/// use arm_gic_driver::{fdt_parse_irq_config, IntId, Trigger};
96///
97/// // SGI interrupt
98/// let sgi_config = fdt_parse_irq_config(&[5]).unwrap();
99/// assert!(sgi_config.id.is_sgi());
100///
101/// // SPI interrupt (type=0, number=42, level-high)
102/// let spi_config = fdt_parse_irq_config(&[0, 42, 4]).unwrap();
103/// assert_eq!(spi_config.id.to_u32(), 74); // 32 + 42
104/// assert_eq!(spi_config.trigger, Trigger::Level);
105///
106/// // PPI interrupt (type=1, number=2, edge-rising)
107/// let ppi_config = fdt_parse_irq_config(&[1, 2, 1]).unwrap();
108/// assert_eq!(spi_config.id.to_u32(), 18); // 16 + 2
109/// assert_eq!(ppi_config.trigger, Trigger::Edge);
110/// ```
111pub fn fdt_parse_irq_config(itr: &[u32]) -> Result<IrqConfig, &'static str> {
112 // Handle single parameter case (SGI)
113 if itr.len() == 1 && itr[0] < 16 {
114 return Ok(IrqConfig {
115 id: IntId::sgi(itr[0]),
116 trigger: Trigger::Edge, // SGI is always edge-triggered
117 });
118 }
119
120 // Need at least 3 parameters for full specification
121 if itr.len() < 3 {
122 return Err("Invalid IRQ configuration: need at least 3 parameters");
123 }
124
125 // Interrupt type constants (from Linux kernel)
126 const SPI: u32 = 0; // Shared Peripheral Interrupt
127 const PPI: u32 = 1; // Private Peripheral Interrupt
128 const ESPI: u32 = 2; // Extended SPI
129 const EPPI: u32 = 3; // Extended PPI
130 const LPI: u32 = 4; // Locality-specific Peripheral Interrupt
131 const PARTITION: u32 = 5; // Partitioned PPI
132
133 // Base interrupt IDs for extended interrupts
134 const ESPI_BASE_INTID: u32 = 4096;
135 const EPPI_BASE_INTID: u32 = 1056;
136
137 // IRQ type sense mask (from Linux include/linux/irq.h)
138 const IRQ_TYPE_NONE: u32 = 0x00000000;
139 const IRQ_TYPE_EDGE_RISING: u32 = 0x00000001;
140 const IRQ_TYPE_EDGE_FALLING: u32 = 0x00000002;
141 const IRQ_TYPE_EDGE_BOTH: u32 = IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING;
142 const IRQ_TYPE_LEVEL_HIGH: u32 = 0x00000004;
143 const IRQ_TYPE_LEVEL_LOW: u32 = 0x00000008;
144 const IRQ_TYPE_SENSE_MASK: u32 = 0x0000000f;
145
146 let irq_type = itr[0];
147 let irq_num = itr[1];
148 let irq_flags = itr[2] & IRQ_TYPE_SENSE_MASK;
149
150 // Calculate hardware interrupt ID based on type
151 let hwirq = match irq_type {
152 SPI => {
153 // SPI: hwirq = param[1] + 32
154 SPI_RANGE.start + irq_num
155 }
156 PPI => {
157 // PPI: hwirq = param[1] + 16
158 PPI_RANGE.start + irq_num
159 }
160 ESPI => {
161 // ESPI: hwirq = param[1] + ESPI_BASE_INTID
162 ESPI_BASE_INTID + irq_num
163 }
164 EPPI => {
165 // EPPI: hwirq = param[1] + EPPI_BASE_INTID
166 EPPI_BASE_INTID + irq_num
167 }
168 LPI => {
169 // LPI: hwirq = param[1]
170 irq_num
171 }
172 PARTITION => {
173 // Partitioned PPI: special handling
174 if irq_num >= 16 {
175 EPPI_BASE_INTID + irq_num - 16
176 } else {
177 16 + irq_num
178 }
179 }
180 _ => {
181 return Err("Invalid IRQ type");
182 }
183 };
184
185 // Create IntId from hardware interrupt ID
186 let intid = unsafe { IntId::raw(hwirq) };
187
188 // Determine trigger type from flags
189 let trigger = match irq_flags {
190 IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_BOTH => Trigger::Edge,
191 IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW => Trigger::Level,
192 IRQ_TYPE_NONE if irq_type == PARTITION => {
193 // Partitioned PPIs can have IRQ_TYPE_NONE, default to level
194 Trigger::Level
195 }
196 IRQ_TYPE_NONE => {
197 return Err("IRQ_TYPE_NONE is not allowed for IRQ type");
198 }
199 _ => {
200 return Err("Invalid IRQ trigger type");
201 }
202 };
203
204 Ok(IrqConfig { id: intid, trigger })
205}