Introduction and Problem
The AMD Zynq Ultrascale+ contains many available interrupt sources in its design. Most notably, are the interrupt channels available between the PL (Programmable Logic / FPGA) and the PS (processing system). This enables FPGA IP within the device to trigger interrupts in software. Although this high-level concept is relatively simple, the intricate details can be quite complex. While there are numerous potential topics to explore in this area of the system, this article will provide a concise overview of determining the mapping of interrupt lines and specifying them within the device tree in Linux.
The concepts discussed here can be applied to numerous interrupt sources accessible from peripherals in the ZU+. However, the emphasis here is placed explicitly on the PL -> PS interrupts. This is because these interrupts are unique to each design and are not defined in existing dtsi files, unlike other peripherals. This distinction warrants separate consideration.
PL – PS Interrupt Mapping
Since the ZU+ is an AMP (asymmetric multiprocessing) system with multiple discrete processors, it necessarily contains multiple interrupt controllers. Since the focus of this article is on how to map these for use in Linux, we will be focussing on the APU which utilizes the GIC-400 controller from Arm.
There are two banks of PL -> PS interrupts available to the APU. These are named “PL_PS_Group0” and PL_PS_Group1”
The table below from AMD’s Zynq Ultrascale+ Device Technical Reference Manual shows how these interrupt lines are mapped into the interrupt controller:
……
Thus, an interrupt line connected from the PL to PL_PS_Group0[0] would be IRQ Number 121, while another connected to PL_PS_Group0[1] would be IRQ Number 122. Likewise, an interrupt connected to PL_PS_Group1[0] would be assigned IRQ Number 136 and so forth.
Determining the Device Tree Mapping
Now, let us look at a device connected to one of these interrupts, and how this will map into the device tree. For this example, we will be using an AMD Framebuffer Write DMA channel with an interrupt output connected to the PS. The concepts here assume that the connected device already has an existing Linux device driver that handles interrupts and that it is present in the system.
If we look at the IPI diagram below, we can see that the interrupt output from the DMA is connected to pl_ps_irq0[0]. As per our example above, this means that our IRQ Number for the line is 121.
Let us now consider the Linux device tree portion. There are two important bindings, the interrupt line mapping (“interrupts” property) and the “parent” property. The parent is the specific interrupt controller instance that is registered in Linux that manages this interrupt line – in this case it is the GIC, and in the ZU+ device tree, this can be referenced with the “gic” handle.
Above we identified our interrupt line as 121. However, the key here is that to translate this into the numbers used by device tree, we must subtract 32. Therefore, 121 – 32 = 89 which becomes the line we specify in the device tree.
Thus our abbreviated device tree snippet becomes something like below:
v_frmbuf_wr_0: v_frmbuf_wr@a0010000 {
compatible = "xlnx,v-frmbuf-wr-2.5", "xlnx,axi-frmbuf-wr-v2.2";
interrupt-parent = <&gic>;
interrupts = <0 89 4>;
.....
};
The key takeaway here, is the following:
[DT Interrupt Line #] = [GIC IRQ Number] – 32
Summary
In this article, we explained the basic concept of PL to PS interrupts along with how to map these into device tree properly to utilize PL IP that have existing Linux drivers. We would be interested to hear your thoughts and experience in utilizing the PL -> PS interrupts in Linux. What other related topics would be of interest to you in the future? Leave a comment below to tell us!
As always we hope you found the article informative and enlightening and we encourage you to reach out to us directly with any questions you might have!