summaryrefslogtreecommitdiff
path: root/documentation/6.components/device-driver/pic/pic_irq_domain.md
blob: 8f13dfa32377240409c075e4edfef3adb6c7861e (plain)
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