Xilinx Zynq平台上的Linux 4.5 GPIO通过Devicetree中断
我使用的是定制开发板和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>; }; }; 我试图在’device’内的’axi_gpio_0’的引脚2上分配一个中断. 浏览谷歌产生了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 = %dn",desc_to_gpio(device->interrupt_gpio)); irq = gpiod_to_irq(device->interrupt_gpio); printk("device: IRQ = %dn",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: %dn",ret); /* Method 2 */ device->interrupt_gpio = devm_gpiod_get_optional(&i2c_client->dev,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: %dn",desc_to_gpio(device->interrupt_gpio)); dev_info(&i2c_client->dev,"IRQ Number: %dn",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,ret); } } else { dev_err(&i2c_client->dev,"Failed to get interrupt GPIO pinn"); } /* Method 3 */ dev_info(&i2c_client->dev,"IRQ requested: %dn",i2c_client->irq); ret = devm_request_threaded_irq(&i2c_client->dev,i2c_client->irq,IRQF_ONESHOT | IRQF_TRIGGER_LOW,ret); 我尝试了所有这些方法和各种devicetree配置的组合,但它们都没有实现我需要的功能. 方法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,我调整了devicetree: 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,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控制器. 谢谢大家的帮助. 解决方法
使用方法3的设备树节点,您应该能够使用irq_of_parse_and_map(i2c_client-> dev.of_node,0)检索IRQ.
然后可以使用devm_request_threaded_irq()完成检索到的IRQ. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |