Linux 4.5 GPIO Interrupt Through Devicetree on Xilinx Zynq Platform
<p>I am using a custom development board with a Zynq XC72010 used to run a Linux 4.5 kernel. I am developing a device driver for a chip we are testing in house and I am having a lot of issues trying to bind a GPIO line to a software IRQ. So far I have tried a few methods and exhausted any google search I can think of. The relevant parts of my devicetree configuration:</p>

<pre><code>/ {
    compatible = "xlnx,zynq-7000";

    amba {
        compatible = "simple-bus";
        #address-cells = &lt;1&gt;;
        #size-cells = &lt;1&gt;;
        interrupt-parent = &lt;&amp;intc&gt;;
        ranges;

        intc: interrupt-controller@f8f01000 {
            compatible = "arm,cortex-a9-gic";
            #interrupt-cells = &lt;3&gt;;
            interrupt-controller;
            reg = &lt;0xF8F01000 0x1000&gt;,
                  &lt;0xF8F00100 0x100&gt;;
        };

        i2c0: i2c@e0004000 {
            compatible = "cdns,i2c-r1p10";
            status = "disabled";
            clocks = &lt;&amp;clkc 38&gt;;
            interrupt-parent = &lt;&amp;intc&gt;;
            interrupts = &lt;0 25 4&gt;;
            reg = &lt;0xe0004000 0x1000&gt;;
            #address-cells = &lt;1&gt;;
            #size-cells = &lt;0&gt;;

            // I WANT INTERRUPT TO TRIGGER
            // ON THIS DEVICE (axi_gpio_0, pin 2)
            device: device@48 {
                compatible = "device,name";
                reg = &lt;0x48&gt;;
                reset-gpios = &lt;&amp;axi_gpio_0 1 0&gt;;
                interrupt-parent = &lt;&amp;axi_gpio_0&gt;;
                interrupt-gpios = &lt;&amp;axi_gpio_0 2 0&gt;;
            };
        };
    };

    amba_pl {
        #address-cells = &lt;1&gt;;
        #size-cells = &lt;1&gt;;
        compatible = "simple-bus";
        ranges ;
        axi_gpio_0: gpio@41200000 {
            #gpio-cells = &lt;2&gt;;
            compatible = "xlnx,xps-gpio-1.00.a";
            gpio-controller;
            interrupt-parent = &lt;&amp;intc&gt;;
            interrupts = &lt;0 31 4&gt;;
            reg = &lt;0x41200000 0x10000&gt;;
            xlnx,all-inputs = &lt;0x0&gt;;
            xlnx,all-inputs-2 = &lt;0x0&gt;;
            xlnx,all-outputs = &lt;0x0&gt;;
            xlnx,all-outputs-2 = &lt;0x0&gt;;
            xlnx,dout-default = &lt;0x00000000&gt;;
            xlnx,dout-default-2 = &lt;0x00000000&gt;;
            xlnx,gpio-width = &lt;0x10&gt;;
            xlnx,gpio2-width = &lt;0x20&gt;;
            xlnx,interrupt-present = &lt;0x1&gt;;
            xlnx,is-dual = &lt;0x0&gt;;
            xlnx,tri-default = &lt;0xFFFFFFFF&gt;;
            xlnx,tri-default-2 = &lt;0xFFFFFFFF&gt;;
    };
};
</code></pre>

<p>I am trying to assign an interrupt to pin 2 of 'axi_gpio_0' inside of 'device'.</p>

<p>Browsing google yielded 3 common methods for binding the interrupt in driver code:</p>

<pre><code>/* Method 1 */
device-&gt;interrupt_gpio = devm_gpiod_get_optional(&amp;i2c_client-&gt;dev,
                                        "interrupt", GPIOD_IN);

if(IS_ERR(device-&gt;interrupt_gpio))
    return PTR_ERR(device-&gt;interrupt_gpio);

printk("device: Interrupt GPIO = %d\n",desc_to_gpio(device-&gt;interrupt_gpio));

irq = gpiod_to_irq(device-&gt;interrupt_gpio);
printk("device: IRQ = %d\n",irq);

ret = devm_request_threaded_irq(&amp;i2c_client-&gt;dev, irq,
    NULL, device_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
    "device", device);
if (ret != 0)
    dev_err(&amp;i2c_client-&gt;dev, "Failed to request IRQ: %d\n", ret);



/* Method 2 */
device-&gt;interrupt_gpio = devm_gpiod_get_optional(&amp;i2c_client-&gt;dev,
    "interrupt", GPIOD_ASIS);

if (IS_ERR(device-&gt;interrupt_gpio))
    return PTR_ERR(device-&gt;interrupt_gpio);


if (device-&gt;interrupt_gpio) {
    dev_info(&amp;i2c_client-&gt;dev, "Found interrupt GPIO: %d\n",desc_to_gpio(device-&gt;interrupt_gpio));
    dev_info(&amp;i2c_client-&gt;dev, "IRQ Number: %d\n",gpiod_to_irq(device-&gt;interrupt_gpio));

    gpio_request(desc_to_gpio(device-&gt;interrupt_gpio), "DEVICE_INT");    // Request a GPIO pin from the driver
    gpio_direction_input(desc_to_gpio(device-&gt;interrupt_gpio));           // Set GPIO as input
    gpio_set_debounce(desc_to_gpio(device-&gt;interrupt_gpio), 50);          // Set a 50ms debounce, adjust to your needs
    gpio_export(desc_to_gpio(device-&gt;interrupt_gpio), false);                    // The GPIO will appear in /sys/class/gpio

    ret = request_irq(gpiod_to_irq(device-&gt;interrupt_gpio),             // requested interrupt
                   (irq_handler_t) irqHandler,  // pointer to handler function
                   IRQF_TRIGGER_RISING,         // interrupt mode flag
                   "DEVICE_IRQ_HANDLER",        // used in /proc/interrupts
                   NULL);                       // the *dev_id shared interrupt lines, NULL is okay
    if (ret != 0) {
        dev_err(&amp;i2c_client-&gt;dev,
            "Failed to request IRQ: %d\n", ret);
    }
}
else {
    dev_err(&amp;i2c_client-&gt;dev, "Failed to get interrupt GPIO pin\n");
}



/* Method 3 */
dev_info(&amp;i2c_client-&gt;dev, "IRQ requested: %d\n", i2c_client-&gt;irq);

ret = devm_request_threaded_irq(&amp;i2c_client-&gt;dev, i2c_client-&gt;irq,
NULL, device_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW,
"device", device);
if (ret != 0)
    dev_err(&amp;i2c_client-&gt;dev, "Failed to request IRQ: %d\n", ret);
</code></pre>

<p>I tried combinations of all of these methods and various devicetree configurations, but none of them achieved the functionality I need.</p>

<p>Method 1 results in this output:</p>

<pre><code>device: Interrupt GPIO = 892
device: IRQ = -6
device 0-0048: Failed to request IRQ: -22
</code></pre>

<p>Method 2 results in this output:</p>

<pre><code>device 0-0048: Found interrupt GPIO: 892
device 0-0048: IRQ Number: -6
device 0-0048: Failed to request IRQ: -22
</code></pre>

<p>So, trying to use the descriptor GPIO and the old GPIO api's are both unsuccessful in binding an interrupt.</p>

<p>To try method 3, I tweaked the devicetree:</p>

<pre><code>device: device@48 {
    compatible = "device,name";
    reg = &lt;0x48&gt;;
    interrupt-parent = &lt;&amp;axi_gpio_0&gt;; // or &lt;&amp;intc&gt;?
    interrupts = &lt;0 2 0x02&gt;; // trying to grab pin 2
};
</code></pre>

<p>Method 3 results in this output:</p>

<pre><code>genirq: Setting trigger mode 2 for irq 168 failed (gic_set_type+0x0/0x48)
device 0-0048: IRQ requested: 168
genirq: Setting trigger mode 8 for irq 168 failed (gic_set_type+0x0/0x48)
device 0-0048: Failed to request IRQ: -22
</code></pre>

<p>It seems the problem is assigning a software interrupt to a specific GPIO in Linux. I don't see what I'm missing here. Any advice is appreciated.</p>

<p>EDIT 1:</p>

<p>I found out that Linux doesn't like low-level interrupts for whatever reason. Changing method 3 to:</p>

<pre><code>device: device@48 {
    compatible = "device,name";
    reg = &lt;0x48&gt;;
    interrupt-parent = &lt;&amp;axi_gpio_0&gt;;
    interrupts = &lt;0 2 0x04&gt;;
};
</code></pre>

<p>And driver code to:</p>

<pre><code>dev_info(&amp;i2c_client-&gt;dev, "IRQ requested: %d\n", i2c_client-&gt;irq);

ret = devm_request_threaded_irq(&amp;i2c_client-&gt;dev, i2c_client-&gt;irq,
NULL, device_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_HIGH,
"device", device);
if (ret != 0)
    dev_err(&amp;i2c_client-&gt;dev, "Failed to request IRQ: %d\n", ret);
</code></pre>

<p>allows me to successfully request an IRQ. However, my signal is active low, so this doesn't really help me much. Also, I'm not sure that this method is referencing <code>axi_gpio_0</code> pin 2 as the interrupt signal. I can use both <code>intc</code> and <code>axi_gpio_0</code> as <code>interrupt-parent</code> and it maps to the same IRQ number (I see this from <code>cat /proc/interrupts</code>). So, ignoring the polarity of the signal, how do I make sure that my registered interrupt is triggered based on the toggling of <code>axi_gpio_0</code> pin 2?</p>

<p>EDIT 2:</p>

<p>I traced the issue with requesting an active-low interrupt to the driver for the interrupt controller: <code>kernel/drivers/irqchip/irq-gic.c</code>. This section of code is what causes the problem:</p>

<pre><code>static int gic_set_type(struct irq_data *d, unsigned int type)
{
    void __iomem *base = gic_dist_base(d);
    unsigned int gicirq = gic_irq(d);

    /* Interrupt configuration for SGIs can't be changed */
    if (gicirq &lt; 16)
        return -EINVAL;

    /* SPIs have restrictions on the supported types */
    if (gicirq &gt;= 32 &amp;&amp; type != IRQ_TYPE_LEVEL_HIGH &amp;&amp;
                type != IRQ_TYPE_EDGE_RISING)
        return -EINVAL;

    return gic_configure_irq(gicirq, type, base, NULL);
}
</code></pre>

<p>Hacking the kernel is not at all what I want to do, but commenting out:</p>

<pre><code>/* SPIs have restrictions on the supported types */
/*if (gicirq &gt;= 32 &amp;&amp; type != IRQ_TYPE_LEVEL_HIGH &amp;&amp;
            type != IRQ_TYPE_EDGE_RISING)
return -EINVAL;*/
</code></pre>

<p>allows me to request an active-low interrupt. For testing purposes, this should work temporarily.</p>

<p>UPDATE:</p>

<p>I have successfully created an IRQ on a GPIO pin. My problem was with the GPIO controller I was using. The controller was a Xilinx IP block inside of the Zynq Programmable Logic block and this controller is unable to trigger interrupts on GPIO pins (for reasons unknown to me). I soldered the interrupt pin on the board I was working on to a GPIO pin on a different, more generic controller and now Linux is playing nicely with me.</p>

<p>To summarize, a GPIO controller that matches <code>compatible = "xlnx,xps-gpio-1.00.a";</code> is unable to bind to software interrupts in Linux. If you have this problem, USE A DIFFERENT GPIO CONTROLLER.</p>

<p>Thanks everyone for the help.</p>