Search code examples
linuxembeddedembedded-linuxdevice-tree

Two interrupts are defined in system-user.dtsi file but why only one interrupt in /proc/interrupts?


There is a design in VIVADO for DMA transfer. I want to use uio device and I have a /dev/uio0 directory along with information in /sys/class/uio/uio0/maps/map0 directory. In my pl.dtsi I have two interrupts and I have added them in the system-user.dtsi file as:

/include/ "system-conf.dtsi"
/ {
    chosen {
            bootargs = "earlycon clk_ignore_unused uio_pdrv_genirq.of_id=generic-uio rootwait quiet loglevel=0";
            stdout-path = "serial0:115200n8";
        };
};

&axi_dma_0 {
    clock-names = "s_axi_lite_aclk", "m_axi_sg_aclk", "m_axi_mm2s_aclk", "m_axi_s2mm_aclk";
    clocks = <&clkc 15>, <&clkc 15>, <&clkc 15>, <&clkc 15>;
    interrupt-names = "mm2s_introut", "s2mm_introut";
    compatible = "generic-uio";
    reg = <0x40400000 0x10000>;
    interrupt-parent = <&intc>;
    interrupts = <0 29 4 0 30 4>;

    
};

However after booting (I have used petalinux to build the booing image and other files) the board in the /proc/interrupts I see a entry

32:          0          0 GIC-0  61 Level     dma

I assumed 61 is for mm2s as it is corresponding to 61 - 32 = 29. Should not I see another entry for 30 in /proc/interupts?

EDIT: When dmesg | grep -C3 dma in my computer petalinux project directory I get the following.

dmesg | grep -C3 dma
[    0.179479] APIC: Switch to symmetric I/O mode setup
[    0.179481] DMAR: Host address width 36
[    0.179483] DMAR: DRHD base: 0x000000fed90000 flags: 0x1
[    0.179488] DMAR: dmar0: reg_base_addr fed90000 ver 1:0 cap c9008020660262 ecap f0105a
[    0.179490] DMAR: RMRR base: 0x000000ded2e000 end: 0x000000ded3cfff
[    0.179492] DMAR-IR: IOAPIC id 2 under DRHD base  0xfed90000 IOMMU 0
[    0.179493] DMAR-IR: HPET id 0 under DRHD base 0xfed90000
--
[    0.529581] system 00:03: [io  0x0a30-0x0a4f] has been reserved
[    0.529584] system 00:03: [io  0x0a00-0x0a0f] has been reserved
[    0.529773] system 00:04: [io  0x04d0-0x04d1] has been reserved
[    0.530002] pnp 00:05: [dma 0 disabled]
[    0.530295] system 00:07: [mem 0xfed1c000-0xfed1ffff] has been reserved
[    0.530298] system 00:07: [mem 0xfed10000-0xfed17fff] has been reserved
[    0.530299] system 00:07: [mem 0xfed18000-0xfed18fff] has been reserved

However if I dmesg | grep -C3 dma in my board petalinux directory I get nothing: On the other hand while booting the board throuh qspi I get the following messages.

SF: Detected s25fl256s1 with page size 256 Bytes, erase size 64 KiB, total 32 MiB
device 0 offset 0x9c0000, size 0x40000
SF: 262144 bytes @ 0x9c0000 Read: OK
QSPI: Trying to boot script at 3000000
## Executing script at 03000000
Wrong image format for "source" command
QSPI: SCRIPT FAILED: continuing...
JTAG: Trying to boot script at 3000000
## Executing script at 03000000
Wrong image format for "source" command
JTAG: SCRIPT FAILED: continuing...
switch to partitions #0, OK
mmc0 is current device
Scanning mmc 0:1...
Found U-Boot script /boot.scr
3016 bytes read in 16 ms (183.6 KiB/s)
## Executing script at 03000000
Trying to load boot images from qspi
SF: Detected s25fl256s1 with page size 256 Bytes, erase size 64 KiB, total 32 MiB
device 0 offset 0xa80000, size 0x1500000
SF: 22020096 bytes @ 0xa80000 Read: OK
Wrong Image Format for bootm command
ERROR: can't get kernel image!
Booting using Fit image failed
device 0 offset 0xa00000, size 0x600000
SF: 6291456 bytes @ 0xa00000 Read: OK
device 0 offset 0x1000000, size 0xf80000
SF: 16252928 bytes @ 0x1000000 Read: OK
Wrong Image Format for bootm command
ERROR: can't get kernel image!
Booting using Separate images failed
Trying to load boot images from jtag
Wrong Image Format for bootm command
ERROR: can't get kernel image!
Trying to load boot images from mmc0
17834024 bytes read in 1655 ms (10.3 MiB/s)
## Loading kernel from FIT Image at 10000000 ...
   Using 'conf-system-top.dtb' configuration
   Verifying Hash Integrity ... OK
   Trying 'kernel-1' kernel subimage
     Description:  Linux kernel
     Type:         Kernel Image
     Compression:  uncompressed
     Data Start:   0x1000010c
     Data Size:    4654376 Bytes = 4.4 MiB
     Architecture: ARM
     OS:           Linux
     Load Address: 0x00200000
     Entry Point:  0x00200000
     Hash algo:    sha256
     Hash value:   1cf7ebde3d2e20e4297f3bb94fd5b62628d7a00343786bd751f76bab9335fbf9
   Verifying Hash Integrity ... sha256+ OK
## Loading ramdisk from FIT Image at 10000000 ...
   Using 'conf-system-top.dtb' configuration
   Verifying Hash Integrity ... OK
   Trying 'ramdisk-1' ramdisk subimage
     Description:  petalinux-image-minimal
     Type:         RAMDisk Image
     Compression:  uncompressed
     Data Start:   0x10475524
     Data Size:    13157774 Bytes = 12.5 MiB
     Architecture: ARM
     OS:           Linux
     Load Address: unavailable
     Entry Point:  unavailable
     Hash algo:    sha256
     Hash value:   6acad62316441a792f0dc018eb8fdfa293b6358057f648e5a5484309e7d100b5
   Verifying Hash Integrity ... sha256+ OK
## Loading fdt from FIT Image at 10000000 ...
   Using 'conf-system-top.dtb' configuration
   Verifying Hash Integrity ... OK
   Trying 'fdt-system-top.dtb' fdt subimage
     Description:  Flattened Device Tree blob
     Type:         Flat Device Tree
     Compression:  uncompressed
     Data Start:   0x10470740
     Data Size:    19734 Bytes = 19.3 KiB
     Architecture: ARM
     Hash algo:    sha256
     Hash value:   da2cb926234ba4ea5339484c96b5f02f265fa00d2f6e8b50f1bf21d7c9113506
   Verifying Hash Integrity ... sha256+ OK
   Booting using the fdt blob at 0x10470740
Working FDT set to 10470740
   Loading Kernel Image
   Loading Ramdisk to 2f373000, end 2ffff58e ... OK
   Loading Device Tree to 2f36b000, end 2f372d15 ... OK
Working FDT set to 2f36b000

Starting kernel ...

Booting Linux on physical CPU 0x0
Linux version 6.1.5-xilinx-v2023.1 (oe-user@oe-host) (arm-xilinx-linux-gnueabi-gcc (GCC) 12.2.0, GNU ld (GNU Binutils) 2.39.0.20220819) #1 SMP PREEMPT Fri Apr 21 07:47:58 UTC 2023
CPU: ARMv7 Processor [413fc090] revision 0 (ARMv7), cr=18c5387d
CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction cache
OF: fdt: Machine model: xlnx,zynq-7000
earlycon: cdns0 at MMIO 0xe0001000 (options '115200n8')
printk: bootconsole [cdns0] enabled
INIT: version 3.04 booting
Starting udev
Starting version 251.8+
hwclock: can't open '/dev/misc/rtc': No such file or directory
Fri Mar  9 12:34:56 UTC 2018
hwclock: can't open '/dev/misc/rtc': No such file or directory
Configuring packages on first boot....
 (This may take several minutes. Please do not power off the machine.)
Running postinst /etc/rpm-postinsts/100-sysvinit-inittab...
update-rc.d: /etc/init.d/run-postinsts exists during rc.d purge (continuing)
 Removing any system startup links for run-postinsts ...
  /etc/rcS.d/S99run-postinsts
INIT: Entering runlevel: 5
Configuring network interfaces... udhcpc: started, v1.35.0
udhcpc: broadcasting discover
udhcpc: broadcasting discover
udhcpc: broadcasting discover
udhcpc: no lease, forking to background
done.
Starting OpenBSD Secure Shell server: sshd
  generating ssh RSA host key...
  generating ssh ECDSA host key...
  generating ssh ED25519 host key...
done.
Starting rpcbind daemon...done.
starting statd: done
hwclock: can't open '/dev/misc/rtc': No such file or directory
Starting internet superserver: inetd.
NFS daemon support not enabled in kernel
Starting syslogd/klogd: done
Starting tcf-agent: OK

Solution

  • Introduction

    When using Userspace IO (UIO) its not possible to have more than one interrupt per device. This is because:

    • The kernel code does not support this. If we look at the code in __uio_register_device() where it calls request_irq() there is only provision for a registering a single interrupt.

    • The API to user space has no mechanism to distinguish between multiple interrupts.

    However, there is no reason why you can't create a second UIO device which just has a an interrupt property. In general this would look something like:

    / {
        dma0 {
            compatible = "generic-uio";
            reg = <0x40400000 0x10000>;
            clock-names = "s_axi_lite_aclk", "m_axi_sg_aclk", "m_axi_mm2s_aclk", "m_axi_s2mm_aclk";
            clocks = <&clkc 15>, <&clkc 15>, <&clkc 15>, <&clkc 15>;
            interrupt-parent = <&intc>;
            interrupts = <0 29 4>;
        };
    
        dma1 {
            compatible = "generic-uio";
            interrupt-parent = <&intc>;
            interrupts = <0 30 4>;
        };
    };
    

    The clocks, register space and first interrupt will be managed through one UIO device and the second interrupt will be available through the other UIO device.

    PetaLinux

    (I'm not a PetaLinux user so this is only from what I've read and may need correcting.)

    In this specific instance it will look a little different. The PetaLinux tools will have generated a pl.dtsi file with a DMA device node in it. This node will be constructed assuming that the Xilinx DMA Engine driver in the kernel is being used. To use UIO for this device we have a couple of options: replace the generated node with a new one or update the existing generated node.

    Replace the generated node

    We can simply mark the node which is in the pl.dtsi file to be deleted, so that it never appears in the generated DTB. This is simple, but means that some information has to be copied from pl.dtsi to system-user.dtsi. This is simple, but does create a maintenance problem (if for example a change to the design moved the address of the registers it would be necessary to update the address in system-user.dtsi while the update to pl.dtsi would happen automatically).

    /include/ "system-conf.dtsi"
    / {
        chosen {
            bootargs = "earlycon clk_ignore_unused uio_pdrv_genirq.of_id=generic-uio rootwait quiet loglevel=0";
            stdout-path = "serial0:115200n8";
        };
    };
    
    &amba_pl {
        /delete-node/ &axi_dma_0;
    
        dma0 {
            compatible = "generic-uio";
            reg = <0x40400000 0x10000>;
            clock-names = "s_axi_lite_aclk", "m_axi_sg_aclk", "m_axi_mm2s_aclk", "m_axi_s2mm_aclk";
            clocks = <&clkc 15>, <&clkc 15>, <&clkc 15>, <&clkc 15>;
            interrupt-parent = <&intc>;
            interrupts = <0 29 4>;
        };
    
        dma1 {
            compatible = "generic-uio";
            interrupt-parent = <&intc>;
            interrupts = <0 30 4>;
        };
    };
    

    Update the generated node

    The other technique we could use is to update the node which has been generated by the PetaLinux tools in pl.dtsi. This reduces the amount of information which has to be copied over into system-user.dtsi (although I can't see a way to avoid copying the second interrupt number unfortunately).

    /include/ "system-conf.dtsi"
    / {
        chosen {
            bootargs = "earlycon clk_ignore_unused uio_pdrv_genirq.of_id=generic-uio rootwait quiet loglevel=0";
            stdout-path = "serial0:115200n8";
        };
    };
    
    &amba_pl {   
        dma1 {
            compatible = "generic-uio";
            interrupt-parent = <&intc>;
            interrupts = <0 30 4>;
        };
    };
    
    &axi_dma_0 {
        compatible = "generic-uio";
        linux,uio-name = "dma0";
    };
    

    Note that we don't need to copy over the clocks, reg or interrupts properties in this case (although only the first interrupt in the interrupts property will be used).

    We also add a name for the UIO device so that it doesn't use the default (which will be dma).

    There will be a lot of properties in the &axi_dma_0 node which are only relevant when the Xilinx DMA Engine driver is being used. These could be deleted (using /delete-property/) but they should be harmless as the UIO driver won't recognise them.

    User space driver

    You then need to work out which /dev/uio device file corresponds to which device. Ideally your driver would open all the /sys/class/uio/uio<n>/name files to work out which UIO device corresponds to the devices dma0 and dma1. This way your code will still work in case you later add extra UIO devices to the device tree and the device numbers change.

    Then open /dev/uio<x> and /dev/uio<y> and use select(2) or poll(2) to wait for either of the interrupts to occur.