1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
|
@page page_device_pic_irq_domain PIC virtual IRQ domain
# Virtual IRQ allocation (`pic.c`)
How RT-Thread turns many interrupt controllers into one **global IRQ space** drivers can use.
---
## Global table `_pirq_hash[]`
- Size: **`MAX_HANDLERS`** (Kconfig, default 256)
- Each slot is a **`struct rt_pic_irq`** with **`irq`**, **`hwirq`**, **`pic`**, ISR list, optional **`parent`**
Allocation pointer **`_pirq_hash_idx`**:
| Region | Indices | Purpose |
| --- | --- | --- |
| **IPI** | `0 … RT_MAX_IPI-1` | SMP inter-processor interrupts |
| **PIC domains** | `_pirq_hash_idx` upward | Each **`rt_pic_linear_irq`** takes a contiguous block |
**`rt_pic_config_ipi`** uses fixed low indices. Peripheral IRQs come from **`rt_pic_config_irq`**.
---
## Per-controller window
**`rt_pic_linear_irq(pic, irq_nr)`** (under global lock):
```c
pic->irq_start = _pirq_hash_idx; /* first virtual IRQ for this PIC */
pic->irq_nr = irq_nr;
pic->pirqs = &_pirq_hash[pic->irq_start];
_pirq_hash_idx += irq_nr;
```
Example (GICv2 with 988 SPIs):
- **`irq_nr = max_irq + 1 - 32`** (SGI/PPI handled separately)
- Virtual IRQs might be **`[RT_MAX_IPI, RT_MAX_IPI + irq_nr)`**
- **`irq_index = hwirq - 32`** for SPI hwirq 32 → index 0
---
## Mapping API
```c
int rt_pic_config_irq(struct rt_pic *pic, int irq_index, int hwirq);
int rt_pic_config_ipi(struct rt_pic *pic, int ipi_index, int hwirq);
```
| Input | Meaning |
| --- | --- |
| **`irq_index`** | Offset within this PIC's window |
| **`hwirq`** | Hardware ID in that controller |
| **Return value** | **Global virtual IRQ** = **`pic->irq_start + irq_index`** |
Lookup:
| API | Returns |
| --- | --- |
| **`rt_pic_find_irq(pic, irq_index)`** | **`&pic->pirqs[irq_index]`** |
| **`rt_pic_find_pirq(pic, virtual_irq)`** | **`pirq`** if **`virtual_irq`** in range |
| **`irq2pirq(irq)`** | Global lookup for **`rt_pic_irq_*`** wrappers |
---
## `struct rt_pic_irq` fields
| Field | Role |
| --- | --- |
| **`irq`** | Virtual IRQ (global); **`-1`** = slot unused |
| **`hwirq`** | Controller-specific line |
| **`mode`** | Edge/level (`RT_IRQ_MODE_*`) |
| **`pic`** | Owning **`struct rt_pic *`** |
| **`parent`** | Cascade parent **`pirq`** |
| **`children_nodes`** | Routed children if **`RT_PIC_F_IRQ_ROUTING`** |
| **`isr`** | Handler chain |
---
## IPI vs device IRQ
| | IPI | Device IRQ |
| --- | --- | --- |
| Config | **`rt_pic_config_ipi`** | **`rt_pic_config_irq`** or **`irq_map`** |
| Affinity | All CPUs set in **`config_ipi`** | Per **`irq_set_affinity`** |
| Dispatch | GIC trap reads hwirq < 16 → **`rt_pic_find_ipi`** | hwirq ≥ 32 → **`rt_pic_find_irq`** |
---
## Debug
With MSH: **`list_irq`** / **`list_irq all`** ( **`pic.c`** ) dumps virtual IRQ, PIC name, hwirq, mode, handlers.
**`rt_pic_linear_irq`** comment: intended for production PIC registration; **`rt_pic_cancel_irq`** only for failed init teardown.
---
## Pitfalls
| Issue | Mitigation |
| --- | --- |
| **`MAX_HANDLERS` too small** | Sum all PIC **`irq_nr`** + IPI + headroom |
| Use hwirq as **`rt_pic_attach_irq` argument** | Always use **virtual `irq`** from **`rt_dm_dev_get_irq`** / **`irq_map`** |
| Re-**`config_irq`** on busy slot | **`config_pirq`** asserts empty ISR lists if PIC changes |
## See also
- @ref page_device_pic_core
- @ref page_device_pic_ofw
|