<< back | index | forward >>
Interrupts are an essential functionality for embedded devices. So let's add an IRQ to our IOCTLdriver example.
Explanations in this section do not start from scratch but based on the state after the IOCTL Driver Tutorial.
Please follow the steps described in Interrupts for UIO Driver
The FPGA Design for both examples is the same. If you already build the design for the UIO example with IRQs, you do not have to modify it.
First we have to let Linux know about our device. To do so, we edit the devicetree.
Details about location of devicetree files, how to build them, etc. can be found in the Enclustra Build Environment - HowTo Guide
In our case, we add the section below to the default devicetree:
fpga_base@43C10000{
status = "okay";
compatible = "psi,fpga-base";
reg = < 0x43C10000 0x1000 >;
somevalue = < 0x9876 >;
interrupt-parent = <&intc>;
interrupts = <0x0 (61-32) 1>
};
The meaning of the lines added is described in detail in Interrupts for UIO Driver. Therefore the description is omitted here.
The full device-tree file is available in [root]/ioctl_driver_irq/zx5-obru-ioctl_irq.dts
The easiest way to compile the edited devicetree, is copying it to the folder [root]/bsp-xilinx/sources/xilinx-linux/arch/arm/boot/dts directory of your Enclustra Build Environment.
The device-tree can then be compiled into a devicetree-blob using the command below (from within the dts drectiory mentioned above):
dtc -O dtb -o zx5-obru-ioctl-irq.dtb zx5-obru-ioctl-irq.dts
The output file zx5-obru-ioctl-irq.dtb must be copied to the boot partition of the SD card and renamed to devicetree.dtb (as expected by the boot process) and hence replace de default devicetree.dtb file.
The Kernel code from the IOCTL driver example without IRQ needs to be extended with functionality for registering the interrupt handling function, the interrupt handling function itself and IOCTL operations to interact with the interrupts.
Instead of describing any of the changes in detail here, explanatory comments were added to the code. So it is suggested that you read through the code.
The code certainly has some rough edges and it is not fully thread-safe. I decided to not clutter the code with many lines of kernel code in order to not obfuscate the things that are shown by this example: Registering and using an interrupt.
The concept for IRQ handling of this driver is, that a user space application calls an IOCTL (WAIT_IRQ) and is waked, when an interrupt arrives.
The details about how to build kernel modules for the Enclustra Embedded Build Environment are explained in Enclustra Build Environment - HowTo Guide [2].
A small script allowing building a driver with a single command is provided: [root]/ioctl_driver_irq/compile.sh. The script is of course closely related to the makefile [root]/ioctl_driver_irq/Makefile.
Note that you must edit the BSP_XILINX variable in the compile.sh script to match your system before building the driver.
To build the driver, just navigate to the [root]/ioctl_driver_irq directory and execute the follwoing command:
./compile.sh
Now copy the fpga_base_ioctl_irq.ko kernel object file to the directory /root/ioctl_driver_irq of your SD Card (rootfs partition).
Follow the steps below to load the kernel module:
Boot the target device.
Then navigate to the correct directory on your rootfs ...
cd /root/ioctl_driver_irq
... and load the module
insmod fpga_base_ioctl_irq.ko
You should now see the prints from the probe function:
Somevalue read from dt = 00009876
Registering IRQ 47 in fpga_base
Registered miscdev in fpga_base
So the module was loaded and the corresponding devicetree entry was found. As a result the probe function was called. We can also see that the somevalue property was acquired from the devicetree correctly and that the interrupt was registered.
We can also see that the entry fpga_base popped up in the /dev folder:
# ls /dev/fpga*
/dev/fpga_base
The name is defined in the driver-code:
...
priv->mdev.name = "fpga_base";
...
We can also see that a corresponding folder is added to sys/class/misc (becaue it is a miscdevice):
# cd /sys/class/misc
# ls
cpu_dma_latency memory_bandwidth ubi_ctrl
fpga_base network_latency vga_arbiter
loop-control network_throughput watchdog
To find out whether the interrupt is configured correctly, we can execute the command below:
# cat /proc/interrupts
CPU0 CPU1
...
47: 3160 0 GIC-0 61 Edge ufpga_base
...
First we can see that the IRQ61 is registered for our device. We can also see how many IRQs were handled by each CPU (in this case 3160 IRQs were handled by CPU0). The IRQ number at the very beginning of the line matches the print from the driver probe function (Registering IRQ 47 in fpga_base)
A small user space application is provided along with the example in order to show how to use the IOCTL driver including IRQs from user space. The application is provided as Xilinx SDK project. The source file can be found in [root]/ioctl_driver_irq/app/src/helloworld.c.
The program only has a hand full of lines and explanatory comments. So it is not described here in more detail. Just have a look at the sources.
To build the application, follow the steps below:
- Start SDK
- Create/choose an empty workspace
- Click File > Import
- Choose General > Existing Projects into Workspace
- Select the folder [root]/ioctl_irq_driver
- Press Finish
In SDK you should now see a project called ioctl_test_irq. By default SDK should automatically build the project and produce a *.elf file. If you disabled automatic build in SDK, you have to manually build the project using Project > Build All.
Now copy the ioctl_test.elf file to the directory /root/ioctl_driver of your SD Card (rootfs partition).
Follow the steps below to load the kernel module:
Boot the target device.
Then navigate to the correct directory on your rootfs ...
cd /root/ioctl_driver_irq
... and start the application. Before, the driver must be loaded of course.
insmod ./fpga_base_ioctl_irq.ko
./ioctl_test_irq.elf
You should now see the following output (containing user space printf() and printk() from the driver):
Hello World
fpga_base open
version=0xAB12CD34
year=2020
Sw Version = 123
swversion=0x123
Received IRQ
Received IRQ
...
The Received IRQ messages occur one after the other with one second space in between. They are triggered by the interrupt.
In this chapter, a simple IOCTL based device driver using IRQs was built and installed. It was used from a very simple user space application.
Of course the IRQ handling within the driver is minimal for this simple example. However, it serves as a good starting point to write more complex drivers.
<< back | index | forward >>
