我正在使用带有Zynq XC72010的自定义开发板,用于运行Linux 4.5内核。我正在为我们正在测试的芯片开发设备驱动程序,我在尝试将GPIO线绑定到软件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>;
};
};
我正在尝试为&#39; axi_gpio_0&#39;的第2针分配一个中断。在&#39;设备内部。
浏览谷歌产生了3种在驱动程序代码中绑定中断的常用方法:
/* 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都不能成功绑定中断。
为了尝试方法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
引脚2作为中断信号。我可以将intc
和axi_gpio_0
同时用作interrupt-parent
,并将其映射到相同的IRQ编号(我从cat /proc/interrupts
看到)。因此,忽略信号的极性,如何根据axi_gpio_0
引脚2的切换确保触发注册的中断?
编辑2:
我跟踪了向中断控制器请求驱动器的低电平有效中断的问题: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控制器上。控制器是Zynq可编程逻辑模块内部的Xilinx IP模块,该控制器无法触发GPIO引脚上的中断(原因我不知道)。我把电路板上的中断引脚焊接到一个不同的,更通用的控制器上的GPIO引脚上,现在Linux正在和我玩得很好。
总而言之,匹配compatible = "xlnx,xps-gpio-1.00.a";
的GPIO控制器无法绑定到Linux中的软件中断。如果您遇到此问题,请使用不同的GPIO控制器。
感谢大家的帮助。
答案 0 :(得分:0)
使用方法3的设备树节点,您应该能够使用irq_of_parse_and_map(i2c_client->dev.of_node, 0)
检索IRQ。
然后可以使用devm_request_threaded_irq()
完成检索到的IRQ。