RT-Thread RTOS
An open source embedded real-time operating system
PIC cascade and routing

Cascaded interrupt controllers

When a secondary PIC (PMIC, GPIO aggregator, PCIe INTx block, GICv2m MSI frame) sits below a parent line on another PIC.


Concepts

Device hwirq → Child PIC → parent virtual IRQ → Root PIC (GIC) → CPU
| | |
irq_parse irq_map cascade link
Link Set by
Child pirq->hwirq Child **irq_parse**
Child virtual irq Child **irq_map** → **rt_pic_config_irq**
Parent line **rt_pic_cascade(child_pirq, parent_virtual_irq)**

<tt>rt_pic_cascade</tt> / <tt>rt_pic_uncascade</tt>

rt_err_t rt_pic_cascade(struct rt_pic_irq *pirq, int parent_irq);
Step Effect
**pirq->parent** Parent **pirq** from **irq2pirq(parent_irq)**
Copy **priority**, **affinity** from parent
**RT_PIC_F_IRQ_ROUTING** Insert **pirq** on **parent->children_nodes**

**rt_pic_uncascade** clears parent link and removes from children list.

**parent_irq** must be a virtual IRQ on the parent PIC (often from parent **irq_map**).


Dispatch modes

A — Hardware routing (<tt>RT_PIC_F_IRQ_ROUTING</tt>)

Parent **rt_pic_handle_isr** walks **children_nodes**, ack/ISR/eoi each child.

Used when parent trap fires for a shared line and children are registered in software tree.

B — Dedicated parent ISR (no routing flag)

Parent IRQ has its own handler that polls child status and calls **rt_pic_handle_isr** per child.

Rockchip PCIe INTx (pcie-dw-rockchip.c):

  • GIC **legacy** IRQ → **rockchip_pcie_legacy_isr**
  • Reads **PCIE_CLIENT_INTR_STATUS_LEGACY**, per pin **rt_pic_find_irq(&intx_pic, pin)** → **rt_pic_handle_isr**

Child **intx_pic** has **irq_parse/irq_map** for PCI **interrupt-map**; cascade to GIC is via DT mapping the legacy line, not **rt_pic_cascade**.

C — MSI / MSI-X child PIC

MSI provider allocates a line on the child PIC, then wires it to the parent:

parent_irq = parent_pic->ops->irq_map(parent_pic, parent_hwirq, mode);
irq = rt_pic_config_irq(msi_pic, index, hwirq);
rt_pic_cascade(pirq, parent_irq);

PCI sees the child virtual IRQ from **irq_alloc_msi**. Hardware delivery is parent-specific — see PIC and PCI MSI.


Parent helpers

After cascade, child drivers may call:

rt_pic_irq_parent_mask(pirq);
rt_pic_irq_parent_unmask(pirq);
rt_pic_irq_parent_eoi(pirq);
/* … */

Rockchip RK8xx PMIC IRQ chip

**bsp/rockchip/dm/mfd/rk8xx.c** — reference cascade:

  1. PMIC **rk8xx->irq** (GPIO/IRQ from SoC) → **rk8xx_pmic_isr** wakes thread
  2. Child **rk8xx_irqchip**: **rt_pic_linear_irq**, **irq_map** does:
    irq = rt_pic_config_irq(pic, hwirq, hwirq);
    rt_pic_cascade(pirq, rk8xx_ic->rk8xx->irq); /* PMIC pin → PMIC sub-IRQ */
  3. **irq_parse**: one cell = sub-hwirq index
  4. Thread scans status registers → **rt_pic_handle_isr** per pending bit

DT: **interrupts** on RK8xx children point at **interrupt-controller** sub-node.


Design checklist

Step Action
1 Parent PIC up (**rt_pic_init**)
2 Child **rt_pic_linear_irq**
3 Child **irq_map** → virtual IRQ
4 **rt_pic_cascade(child_pirq, parent_irq)**
5 **rt_ofw_data(child_ic_np) = child_pic**
6 Parent ISR or **RT_PIC_F_IRQ_ROUTING**

Pitfalls

Issue Mitigation
Cascade before parent mapped **irq_map** parent first
Wrong **parent_irq** Must be virtual IRQ on parent PIC
Level IRQ EOI order Child clear source before parent EOI
Missing routing + no demux ISR Children never run

See also