<< back | index | forward >>
Writing a Linux driver always imposes the risk of crashing the whole kernel. Additionally drivers are hard to debug, so whenever possible, writing kernel code should be avoided.
In order to achieve this, Linux provides a generalized user-space I/O driver (UIO). The idea is to move the logic of the driver itself into the user space and only have a very simple kernel module that tells the generalized driver which addresses etc. to use.
First we have to let linux know, that there is a UIO device and at which addresses it is. To do so, the device tree of the reference design must be edited.
Details about location of devicetree files, how to build them, etc. can be found in the Enclustra Build Environment - HowTo Guide[2]
In our case, we add the section below:
uio_fpga_base@43C10000{
status = "okay";
compatible = "generic-uio";
reg = < 0x43C10000 0x1000 >;
};
The full device-tree file is available in [root]/uio_driver/zx5-obru-uio.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-uio.dtb zx5-obru-uio.dts
The output file zx5-obru-uio.dts 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 good thing about using UIO: you don't have to write any Kernel code. We will use the generic driver that is built-in.
However, to do so, we need to configure the Kernel correctly.
The following configuration options are required in the Kernel to correctly run the generic UIO driver:
UIO_PDRV_GENIRQ=M UIO_DMEM_GENIRQ=M
It is easiest to just open menuconfig and search for the configuration names using '/'. After configuring the kernel correctly, you can have to rebuild it. For details about kernel configuration and building the kernel, see Enclustra Build Environment - HowTo Guide [2].
Now copy the resulting uImage ([root]/bsp-xilinx/sources/xilinx-linux/arch/arm/boot/uImage) to the boot partition of your SD card.
Note that it is required to build the UIO drivers mentioned above as module (M) and not as built-in (*). The reason is, that probe must be called manually with additional parameters to use them. If they are built-in, this is not possible.
Because of the changed configuration, the kernel does no more contain the generic UIO driver as built-in (default), so we have to provide it in rootfs. To do so, follow the steps below:
Most likely you still have your console open from step 4. If not, open one, set all environment vairables as required for building the kernel (see Enclustra Build Environment - HowTo Guide [2]) and navigate to the linux build directory ([root]/bsp-xilinx/sources/xilinx-linux).
Now, the optional modules must be built:;
make modules
To install the modules on your SD Card (rootfs partition), execute the command below:
make modules_install INSTALL_MOD_PATH=[path-to-your-sd-card-rootfs-partition]
In my case this is:
make modules_install INSTALL_MOD_PATH=/media/obruendl/rootfs
Follow the steps below to load the kernel module:
Boot the target device.
Load the generic UIO driver:
modprobe uio_pdrv_genirq of_id="generic-uio"
The of_id="generic_uio" parameter configures the driver to be loaded for all devicetree entries with this compatible string (like the one we created).
You should now see a new device named uio0 popping up in the /dev folder. Note that UIO devices do not have a readable name but they are numbered starting at zero. This is not very handy. However, there are ways to acquire more information. These are described below.
The plain numbering of UIO devices without a human readable name can easily lead to confusion. Luckily, there are some ways to find out more about a UIO device.
One way to learn more about UIO devices is suing the /sys/class/uio directory structure. For example:
# cat /sys/class/uio/uio0/name
uio_fpga_base
Another even more handy way, is the lsuio utility. It is not enabled by default but it can easily be enabled when configuring buildroot. Just search (using '/') for "lsuio" and enable the corresponding package. If lsuio is present you can use it as described below:
# lsuio
uio0: name=uio_fpga_base, version=devicetree, events=0
mape[0]: addr=0x43C10000, size=4096
A small user space application is provided along with the example in order to show how to use the UIO driver from user space. The application is provided as Xilinx SDK project. The source file can be found in [root]/uio_driver/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]/uio_driver
- Press Finish
In SDK you should now see a project called uio_test. 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 uio_test.elf file to the directory /root/uio_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/uio_driver
... and start the application. Before, the UIO driver must be loaded of course.
modprobe uio_pdrv_genirq of_id="generic_uio"
./uio_test.elf
You should now see the following output:
Hello World
version=0xAB12CD34
year=2020
sw-version=0x0000ABCD
Note that the "year" output may change according to the year you built the FPGA bitstream in.
In this chapter, a simple UIO based device driver was utilized. It was used from a very simple application. To achieve this, we did not have to write a single line of kernel code.
Of course only the very simplest case of a driver is covered, but this should be a good starting point to base your own development of a real (and more complex) driver on.
<< back | index | forward >>
