Search code examples
linux-kernelzynq

Can only instantiate 1 generic-uio device


I'm trying to expose interrupts to userspace using the uio_pdrv_genirq driver. However I can only instantiate 1 device in the devicetree, all subsequent devices fail the probe. The system is a zynq-7000 and the kernel version is 3.9.0-xilinx.

The devicetree:

/ {
...
amba@0 {
    ...

    gic: intc@f8f01000 {
        interrupt-controller;
        compatible = "arm,cortex-a9-gic";
        #interrupt-cells = <3>;
        reg = <0xf8f01000 0x1000>,
              <0xf8f00100 0x0100>;
    };

    interrupt_91@0x43C90000 {
        compatible = "generic-uio";
        reg = < 0x43C90000 0x1000 >;
        interrupts = < 0 59 1 >; //add 32 to get the interrupt number
        interrupt-parent = <&gic>;
    } ;

    interrupter_90@0x43CA0000 {
        compatible = "generic-uio";
        reg = < 0x43CA0000 0x1000 >;
        interrupts = < 0 58 1 >; //add 32 to get the interrupt number
        interrupt-parent = <&gic>;
    } ;
    ...
};

dmesg output:

dmesg | grep uio
uio_pdrv_genirq 43ca0000.interrupter_90: unable to register uio device
uio_pdrv_genirq: probe of 43ca0000.interrupter_90 failed with error 1

kernel config:

CONFIG_UIO=y
# CONFIG_UIO_CIF is not set
CONFIG_UIO_PDRV_GENIRQ=y
# CONFIG_UIO_DMEM_GENIRQ is not set
# CONFIG_UIO_AEC is not set
# CONFIG_UIO_SERCOS3 is not set
# CONFIG_UIO_PCI_GENERIC is not set
# CONFIG_UIO_NETX is not set

I'm sure I got this working on a Zedboard earlier, I have no idea what the issue could be here.


Solution

  • Alright, this turned out to be a problem in the kernel source that I was using.

    The lines:

    if (ret)
        goto err_get_minor;
    

    in drivers/uio/uio.c and the lines:

    if (ret) {
        dev_err(&pdev->dev, "unable to register uio device\n");
        goto bad1;
    }
    

    in drivers/uio/uio_pdrv_genirq.c must both be changed so that the if statement reads if (ret < 0).

    The reason for this is that the uio_get_minor function (whose return value, ret, they are using) returns the minor number assigned. This is 0, 1, 2, ..., etc. It's clear that the first device (minor id = 0) registered fine but the second device (minor id = 1) failed. This explains the error message "failed with error 1" which was the minor id and not EPERM as I'd initially assumed.

    The repository I'm using is https://github.com/Trenz-Electronic/linux-te-3.9 for future reference.

    EDIT: Actually, the same problem exists in the mainline kernel, I'll post a patch.