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
|
@page page_device_pic_msi PIC and PCI MSI
# MSI / MSI-X at the PIC layer
PCI **message-signalled interrupts** need an interrupt controller that can **allocate a vector**, **build the MSI message** (address + data), and **deliver** it into the same virtual IRQ space as wired IRQs.
PCI enable path: @ref page_device_pci_msi. Wiring to PIC: this page.
---
## What the MSI-capable PIC must do
| Responsibility | Typical `rt_pic_ops` |
| --- | --- |
| Hand out a **virtual IRQ** per MSI/MSI-X vector | **`irq_alloc_msi`** / **`irq_free_msi`** |
| Tell PCI what to write to the device | **`irq_compose_msi_msg`**, **`irq_write_msi_msg`** |
| Mask / unmask at hardware | Often **`irq_mask`/`irq_unmask`** + **`rt_pci_msi_*`** helpers |
| Reach the CPU interrupt architecture | Usually **cascade** to a parent PIC line (@ref page_device_pic_cascade) or a **dedicated status ISR** that demuxes bits |
The PIC driver does **not** replace PCI config-space MSI capability setup — that stays in **`pci/msi/`**. The PIC side owns **vector backing** and **message programming**.
---
## Call flow (PCI → PIC)
```
pci/msi: rt_pci_msi_setup_irqs(pdev, nvec, type)
|
v
pdev->msi_pic->ops->irq_alloc_msi(pic, msi_desc)
| reserve hw line / table entry
| rt_pic_config_irq → virtual irq
| optional rt_pic_cascade(child_pirq, parent_irq)
|
v
irq_compose_msi_msg(pirq, &msg) → rt_pci_msi_write_msg
|
v
Driver: rt_pic_attach_irq(virtual_irq, handler, …)
```
**`struct rt_pci_device::msi_pic`** must point at the **`struct rt_pic`** that implements these ops (from DT **`msi-parent`**, **`msi-map`**, or board setup).
---
## Implementing `irq_alloc_msi`
Typical steps inside the provider:
1. Pick a free **hwirq** / table index (bitmap or hardware allocator).
2. **`rt_pic_config_irq(pic, index, hwirq)`** — obtain **global virtual IRQ** for **`rt_pic_attach_irq`**.
3. If this PIC is not CPU-root: **`parent_irq = parent_pic->ops->irq_map(...)`** then **`rt_pic_cascade(pirq, parent_irq)`** so the parent line fires when MSI is triggered.
4. Store **`msi_desc`** on **`pirq`** if the PCI layer needs it for mask/unmask.
5. Return **virtual IRQ** (not hwirq) to **`pci/msi/irq.c`**.
**`irq_free_msi`**: **`rt_pic_uncascade`**, release index, clear bitmap.
---
## `irq_compose_msi_msg`
Fill **`struct rt_pci_msi_msg`**:
| Field | Meaning |
| --- | --- |
| **`address_lo` / `address_hi`** | Doorbell or MSI frame write target |
| **`data`** | Vector data written by device |
SoC-specific: address may be a dedicated MSI window or a parent SPI doorbell — implement per TRM, not in the generic framework.
---
## MSI vs MSI-X
| | MSI | MSI-X |
| --- | --- | --- |
| Vectors | Often power-of-two block, may need **contiguous** virtual IRQs | Per-table-entry allocation common |
| Table | Config space | Often **BAR-mapped** table — map BAR before unmask |
| PIC | Same ops; PCI layer passes count / type | Same ops; one **`irq_alloc_msi`** per entry typical |
---
## DT (optional)
Platforms may describe:
- **`msi-parent`** — phandle to MSI-capable interrupt controller
- **`msi-map` / `msi-map-mask`** — map PCI requester ID to controller + specifier
Parsing is SoC-specific; result must be **`pdev->msi_pic`** before **`rt_pci_msi_enable`**.
---
## Pitfalls
| Issue | Mitigation |
| --- | --- |
| **`RT_PCI_MSI` disabled** | Ops stubbed; PCI MSI APIs no-op |
| **`msi_pic` NULL** | Set in host/PCI probe before enable |
| Return **hwirq** to PCI | Return **virtual IRQ** from **`irq_alloc_msi`** |
| No parent link | MSI never reaches CPU — cascade or demux ISR |
| MSI-X table unmapped | Enable only after BAR iomap in PCI setup |
## See also
- @ref page_device_pci_msi
- @ref page_device_pic_cascade
- @ref page_device_pic_irq_domain
- @ref page_device_pic_core
|