Adding a parallel camera to a custom board means finishing interrupt wiring that the processor's device tree include only half covers: the imaging half's local interrupt steer has its GIC line, while the parallel-camera half's steer — a separate block at its own address — appears with no interrupts property, and the manual's interrupt table skips it. Your sensor hangs off that second half's I2C, so the missing line is on your critical path. This page gives you the fixed offset that turns any GIC SPI cell into the Linux IRQ number you will see on the board, a node skeleton for the second steer, and the counter test that proves both. If you are still weighing the interface itself, why parallel DVP beats CSI for a microcontroller covers that choice first.
Why does a GIC SPI number become Linux IRQ N+32?
The GIC architecture reserves interrupt IDs 0-31 for per-core software interrupts (0-15) and private peripheral interrupts (16-31), so shared peripheral interrupts start at ID 32, and the kernel's GIC driver maps that ID one-to-one onto the Linux IRQ number: GIC_SPI 320 lands at IRQ 352.
In the device tree, the first cell of the interrupts property picks the class — GIC_SPI for a line shared across the chip, GIC_PPI for a per-core one — the second cell is the SPI index counted from zero, and the third cell carries the trigger flags, IRQ_TYPE_LEVEL_HIGH for a steer's level output. The driver's translation adds 32 to the second cell to produce the GIC interrupt ID, and Linux numbering uses that ID unchanged.
The GIC architecture caps one controller at 1020 interrupt sources, so valid SPI indices run 0 through 987 and land on Linux IRQs 32 through 1019. A second cell above 987 means the number you copied is already an interrupt ID: subtract 32 before you write the cell.
| What the manual prints | What you write | What Linux shows |
|---|---|---|
| SPI 0 (first external line) | <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH> |
IRQ 32 |
| SPI 320 (imaging steer) | <GIC_SPI 320 IRQ_TYPE_LEVEL_HIGH> |
IRQ 352 |
| SPI 987 (top of the range) | <GIC_SPI 987 IRQ_TYPE_LEVEL_HIGH> |
IRQ 1019 |
What does the imaging half's GIC_SPI 320 become on the board?
Add 32: the imaging half's local interrupt steer, requested as <GIC_SPI 320 IRQ_TYPE_LEVEL_HIGH>, runs on Linux IRQ 352, and a clean boot must show the steer's handler counted on line 352 of /proc/interrupts.
The arithmetic is one line — 320 + 32 = 352 — and it ties three layers together: the manual prints the SPI cell, the device tree carries it, the running kernel reports 352. Run this check before you touch the parallel half, because it proves the offset on your kernel, your driver set and your boot.
The steer's children do not extend the arithmetic. The imaging half's I2C controller sits on steer input 8, but its Linux IRQ comes from the steer's own IRQ domain as a freshly allocated number. Read it in /proc/interrupts after the driver binds; do not compute it.
Which GIC line does the parallel-camera half's IRQ_STEER use?
Its own dedicated line — never 320. The manual's interrupt table lists the imaging half's steer but omits the parallel half's, so take the SPI number from the current manual revision or the vendor SDK's interrupt header, then add 32 exactly as before.
A local interrupt steer folds one block's interrupt sources onto exactly one output line at the GIC, so two steer instances mean two GIC lines and two different SPI numbers. The memory map shows the split: the imaging half's steer occupies 0x58220000 and takes GIC_SPI 320, while the parallel half appears in the device tree only as its CSR block in the 0x5826_xxxx range, with no interrupts property at all.
Work the missing number through three sources, in this order:
- The interrupt chapter of the manual revision you build against. The system-interrupt table (Table 3-2 in this manual) lists the imaging half's IRQ_STEER and skips the parallel half's; a later revision may close the gap, and preproduction tables carry a change notice.
- The interrupt enumerator header in the silicon vendor's SDK, which lists one entry per block with its GIC number; the parallel steer appears there under its base address.
- The vendor's support channel, quoting the base address 0x5826_xxxx. For preproduction silicon this is the fastest honest route, and the vendor routes unpublished numbers through its field applications engineers — that is the proper path, not a guess.
Once the number n is in hand, the conversion is the same one line as everywhere else: Linux IRQ = n + 32.
How do you add the parallel half's irqsteer device tree node?
Clone the imaging node's shape and change three things: the unit address and register to the parallel half's base, the label, and the interrupts cell to the parallel half's own <GIC_SPI n IRQ_TYPE_LEVEL_HIGH>.
Keep the compatible string identical to the working imaging instance — the same driver binds both blocks, and a second compatible string buys nothing. Keep interrupt-controller and interrupt-parent = <&gic>, give the node a label of its own so children can point at it, and set reg to the base address plus the block length from the manual's memory-map chapter.
/* Skeleton: parallel-camera half's local interrupt steer.
BASE = the CSR base address (0x5826_xxxx family in this generation)
n = the SPI number sourced from the manual revision or SDK header */
irqsteer_csi_pi: interrupt-controller@BASE {
/* compatible: copy the string from the working imaging instance */
interrupt-controller;
interrupt-parent = <&gic>;
interrupts = <GIC_SPI n IRQ_TYPE_LEVEL_HIGH>;
/* reg = <BASE block-length-from-the-memory-map> */
};
Your sensor hangs off this half's own I2C controller — the one wired to CI_PI_CSI_I2C_SCL and CI_PI_CSI_I2C_SDA on the CSI_RESET and CSI_EN pads — so that controller becomes a child of this node, with interrupt-parent = <&irqsteer_csi_pi> and an interrupts cell holding the steer input index. The ownership rule is the same as everywhere in a capture chain: an interrupt belongs to the node of the block that physically raises it, the way controls live on the right node elsewhere in a pipeline.
Is the parallel half's I2C also steer input 8?
Treat 8 as the working hypothesis, then prove it on hardware. The manual prints input 8 for the imaging half's I2C; the block chapter (Table 2-12) presents the two halves as matching structures, so the second instance is expected to mirror the first — expected, not stated.
Input numbering is built into the steer block itself, and symmetric instantiation normally preserves the order. The counter test in the next section settles it: request the child at input 8, run bus traffic, and read the child's counter. If the parent line counts while the child stays flat and the kernel logs unhandled steer inputs during traffic, pull the input table for this instance from the SDK header and re-index the child. A wrong input index costs nothing — the handler simply listens on the wrong input until you correct it.
One trap to skip: a completed I2C transaction proves the wiring to the pads, not the interrupt route. Judge the route by the child's counter, never by the sensor's acknowledge.
How do you prove the mapping on real hardware?
Drive one known event and read two counters. Request the I2C child, run bus traffic, and watch /proc/interrupts: the child's count must move with traffic, the parent line must move with it, and no other line on the board may gain a count.
- Boot with the imaging nodes alone and confirm line 352 exists with the steer driver's name. That pins the +32 arithmetic on your kernel.
- Add the parallel node with your sourced number n, boot, and confirm a new line numbered n + 32 carrying the same driver's name.
- Request the I2C child at input 8 and read the sensor's ID register in a loop.
- The child's counter and the parent's must both move with the loop. That is the mapping proven end to end.
- Diff /proc/interrupts against the previous boot. A line that gained counts without your traffic is a neighbor whose SPI you got wrong.
With the counters moving, the interrupt phase closes. What follows is usually the capture chain itself: the first image on an embedded vision prototype guide covers the first-frame milestone, and media-ctl pipeline setup: fix capture node failures on embedded SoCs picks up the capture-node stage after that. If frames arrive but come out shifted or torn, the fault has moved from interrupts to wiring and timing, and parallel camera data bus alignment is the next stop.
When does reusing the imaging half's numbers go wrong?
A second steer cannot share the first one's line. Point the parallel node at GIC_SPI 320 and the steer driver claims a line that already belongs to the imaging half — the probe fails, or both steers count each other's child events and neither bus's counter tracks its own traffic.
The failure modes sort cleanly by symptom:
- The parallel steer's probe fails outright — the SPI number you wrote is not this block's line; re-source it from the header or the support channel.
- The probe succeeds but the I2C child never counts — right line, wrong input index; re-check the 8 against the SDK header.
- The line counts only while the imaging half is busy — you bound the imaging steer's address by mistake; check the unit address in your node.
- Another peripheral's counters drop to zero after your change — its line got enabled by your cell; stop, re-source, and re-run the diff test.
Numbers on preproduction silicon also move between manual revisions. Write the revision into a comment next to the interrupts property and re-diff the interrupt table whenever the manual updates — the same discipline as re-checking a camera module's hardware revision compatibility before you reorder.
What do you record before you commit the node?
Pin four facts next to the interrupts property: the manual revision the number came from, the steer base address it belongs to, the child input index you proved, and the /proc/interrupts lines that showed it working.
- Manual revision and table number, in a comment. Interrupt tables get renumbered between revisions; the comment is the only place a number's origin survives.
- n and n + 32 written together. Whoever debugs /proc/interrupts next needs both sides of the conversion.
- The child input index with its evidence. Input 8, proven by the bus-traffic counter test with the date. An assumed index is the fault that survives longest.
- The pad assignment. CI_PI_CSI_I2C_SCL and CI_PI_CSI_I2C_SDA on the CSI_RESET and CSI_EN pads tie the sensor physically to this half; record them with the node.
- A before/after copy of /proc/interrupts. A few lines that settle any later question about a stolen interrupt.
When the interrupt side is settled and the open question becomes the capture hardware, the frame grabbers category lists the boards that take a parallel camera bus into a host, and the board-level cameras category with the camera finder lists the modules and mounts these custom boards pair with. The systems topic page collects the rest of the embedded firmware notes. If a chain that counts interrupts still drops frames, tell us about the board.
CoaXPress, Camera Link and GigE cards
58 models · from $189.00Questions
Does the +32 offset apply to PPI interrupts as well?
No — PPIs use +16. Private peripheral interrupts occupy GIC IDs 16-31, and the cell you write is the PPI index counted from 0, so a timer written as GIC_PPI 14 lands on hwirq 30 and Linux IRQ 30. Only the GIC_SPI class takes the +32 offset this page works through.
Why does my irqsteer child's Linux IRQ number sit far above the parent's?
The steer's driver registers its own IRQ domain and hands each enabled input a fresh Linux IRQ from that domain. Those numbers follow allocation order, not arithmetic, so parent plus 8 is not a number you can compute. Only the parent line follows the GIC rule of SPI cell plus 32; read the children from /proc/interrupts after they bind.
What does IRQ_TYPE_LEVEL_HIGH mean, and when would I use another trigger type?
The third cell of the interrupts property sets the trigger: LEVEL_HIGH for a line asserted and held high, LEVEL_LOW for the inverse, EDGE_RISING or EDGE_FALLING for pulses. A steer's aggregated output is a level line, so LEVEL_HIGH is the normal choice; copy whatever the working imaging node uses rather than inventing a value.
Does the SPI-plus-32 rule hold on newer GIC implementations?
Yes. The ID layout is fixed by the architecture: software interrupts at 0-15, private peripherals at 16-31, shared peripherals from 32 upward. The device tree still takes the SPI index and the driver still adds 32. What changes per SoC generation is which index each block received, so re-source that for every new chip.
Can I hang the parallel half's I2C controller off the imaging half's irqsteer node?
No. The controller inside the parallel-camera half is wired to that half's own local steer, so a child entry under the imaging node requests a steer input that never fires. The bus driver then probes and times out waiting for completion interrupts that will never arrive, and the fault reads as a dead sensor rather than a wrong node.