c - Linux 4.5 GPIO 中断通过 Xilinx Zynq 平台上的 Devicetree

标签 c linux linux-device-driver xilinx device-tree

我使用的是带有 Zynq XC72010 的定制开发板,用于运行 Linux 4.5 内核。我正在为我们正在内部测试的芯片开发设备驱动程序,我在尝试将 GPIO 线绑定(bind)到软件 IRQ 时遇到了很多问题。到目前为止,我已经尝试了几种方法并用尽了我能想到的任何谷歌搜索。我的设备树配置的相关部分:

/ {
    compatible = "xlnx,zynq-7000";

    amba {
        compatible = "simple-bus";
        #address-cells = <1>;
        #size-cells = <1>;
        interrupt-parent = <&intc>;
        ranges;

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

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

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

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

我正在尝试为“设备”内的“axi_gpio_0”的引脚 2 分配一个中断。

浏览 google 得到了 3 种在驱动程序代码中绑定(bind)中断的常用方法:

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

if(IS_ERR(device->interrupt_gpio))
    return PTR_ERR(device->interrupt_gpio);

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

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

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



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

if (IS_ERR(device->interrupt_gpio))
    return PTR_ERR(device->interrupt_gpio);


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

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

    ret = request_irq(gpiod_to_irq(device->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(&i2c_client->dev,
            "Failed to request IRQ: %d\n", ret);
    }
}
else {
    dev_err(&i2c_client->dev, "Failed to get interrupt GPIO pin\n");
}



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

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

我尝试了所有这些方法和各种设备树配置的组合,但它们都没有实现我需要的功能。

方法 1 的结果如下:

device: Interrupt GPIO = 892
device: IRQ = -6
device 0-0048: Failed to request IRQ: -22

方法 2 的结果如下:

device 0-0048: Found interrupt GPIO: 892
device 0-0048: IRQ Number: -6
device 0-0048: Failed to request IRQ: -22

因此,尝试使用描述符 GPIO 和旧的 GPIO api 都无法成功绑定(bind)中断。

为了尝试方法 3,我调整了设备树:

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

方法 3 的结果如下:

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

问题似乎是将软件中断分配给 Linux 中的特定 GPIO。我不明白我在这里错过了什么。任何建议表示赞赏。

编辑 1:

我发现 Linux 出于某种原因不喜欢低级中断。将方法 3 更改为:

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

和驱动程序代码:

dev_info(&i2c_client->dev, "IRQ requested: %d\n", i2c_client->irq);

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

允许我成功请求 IRQ。但是,我的信号是低电平有效,所以这对我帮助不大。此外,我不确定此方法是否引用 axi_gpio_0 pin 2 作为中断信号。我可以同时使用 intcaxi_gpio_0 作为 interrupt-parent 并且它映射到相同的 IRQ 号(我从 cat/过程/中断)。那么,忽略信号的极性,我如何确保根据 axi_gpio_0 引脚 2 的切换触发我注册的中断?

编辑 2:

我跟踪了向中断 Controller 的驱动程序请求低电平有效中断的问题:kernel/drivers/irqchip/irq-gic.c。这部分代码是导致问题的原因:

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 < 16)
        return -EINVAL;

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

    return gic_configure_irq(gicirq, type, base, NULL);
}

破解内核根本不是我想做的,而是注释掉:

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

允许我请求低电平有效中断。出于测试目的,这应该暂时有效。

更新:

我已经成功地在 GPIO 引脚上创建了一个 IRQ。我的问题出在我使用的 GPIO Controller 上。 Controller 是 Zynq 可编程逻辑 block 内的 Xilinx IP block ,该 Controller 无法触发 GPIO 引脚上的中断(我不知道原因)。我将正在处理的电路板上的中断引脚焊接到另一个更通用的 Controller 上的 GPIO 引脚,现在 Linux 可以很好地配合我使用。

总而言之,匹配 compatible = "xlnx,xps-gpio-1.00.a"; 的 GPIO Controller 无法绑定(bind)到 Linux 中的软件中断。如果您遇到此问题,请使用不同的 GPIO Controller 。

感谢大家的帮助。

最佳答案

使用方法 3 的设备树节点,您应该能够使用 irq_of_parse_and_map(i2c_client->dev.of_node, 0) 检索 IRQ。

然后可以像使用 devm_request_threaded_irq() 那样请求检索到的 IRQ。

关于c - Linux 4.5 GPIO 中断通过 Xilinx Zynq 平台上的 Devicetree,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39212771/

相关文章:

linux - 在内核 block I/O 层拦截数据

c - UTF-8 到 Unicode 转换

python - 在 C 中为函数指针回调函数编写 Python ctypes

linux - 编写一个简单的类似 bash 的命令提示符,支持自动完成(处理 tab 键)

linux - 如何在 shell 脚本中杀死等待的子进程

linux - 意外启用防火墙 - 到端口 22 的 SSH 连接被拒绝

linux - virtio中guest notifier和host notifier为什么分别使用ioeventfd和irqfd?

linux-kernel - 如何在内核中分配不可缓存的物理内存?

c - 如何知道Linux中进程的分配方式?

c - linux下哪个驱动控制x86串口ttyS0