什么是irq域,我读内核文档( https://www.kernel.org/doc/Documentation/IRQ-domain.txt )他们说:
注册为唯一irqchips的中断控制器的数量呈上升趋势:例如,GPIO控制器等不同types的子驱动程序通过将它们的中断处理程序build模为irqchips(即有效的级联中断控制器),避免重新实现与IRQ核心系统相同的callback机制。
GPIO控制器如何被称为中断控制器?
什么是linux irq域名,为什么他们需要?
在Documentation / IRQ-domain.txt的第一段中有详细记录 ,所以我会假设你已经知道了。 如果不是,请询问关于该文档的内容。 下面的文本解释了如何使用IRQ域API以及它是如何工作的。
GPIO控制器如何被称为中断控制器?
让我使用max732x.c驱动程序作为参考( 驱动程序代码 )来回答这个问题。 这是一个GPIO驱动程序,它也像中断控制器一样,所以它应该是IRQ域API如何工作的一个很好的例子。
为了完全理解进一步的解释,让我们先看看MAX732x机制。 数据表中的应用电路(为我们的例子简化):
当P0-P7引脚电平发生变化时,MAX7325将在INT引脚产生中断。 (在SoC上运行)可以通过I2C(SCL / SDA引脚)读取P0-P7引脚的状态,并为每个P0-P7引脚产生单独的中断。 这就是为什么这个驱动程序充当中断控制器 。
考虑下一个配置:
“某些器件”在P4引脚上改变电平,诱使MAX7325产生中断。 MAX7325的中断连接到GPIO4 IP核(SoC内部),它使用该GPIO4模块的第29行向CPU通知中断。 所以我们可以说MAX7325 级联到GPIO4控制器。 GPIO4也充当中断控制器,并且级联到GIC中断控制器。
让我们在设备树中声明上面的配置。 我们可以使用Documentation / devicetree / bindings / gpio / gpio-max732x.txt中的绑定作为参考:
expander: max7325@6d { compatible = "maxim,max7325"; reg = <0x6d>; gpio-controller; #gpio-cells = <2>; interrupt-controller; #interrupt-cells = <2>; interrupt-parent = <&gpio4>; interrupts = <29 IRQ_TYPE_EDGE_FALLING>; };
属性的含义如下:
interrupt-controller
属性定义设备产生中断; 在“某些设备”节点中将需要进一步使用此节点作为interrupt-parent
节点。 #interrupt-cells
定义interrupts
属性的格式; 在我们的例子中,行号为2
:1,中断类型为1 interrupt-parent
和interrupts
属性正在描述中断线路连接 假设我们有MAX7325的驱动程序和“某些设备”的驱动程序。 当然,两者都在CPU中运行。 在“Some device”驱动程序中,当“Some device”改变MAX7325的P4引脚电平时,我们要为事件请求中断。 我们先在设备树中声明:
some_device: some_device@1c { reg = <0x1c>; interrupt-parent = <&expander>; interrupts = <4 IRQ_TYPE_EDGE_RISING>; };
现在我们可以做这样的事情(在“某些设备”驱动程序中):
devm_request_threaded_irq(core->dev, core->gpio_irq, NULL, some_device_isr, IRQF_TRIGGER_RISING | IRQF_ONESHOT, dev_name(core->dev), core);
而some_device_isr()
P4引脚上的电平从低电平变为高电平(上升沿)时,会调用some_device_isr()。 怎么运行的? 从左到右,如果你看上面的图片:
所有这些行为都是在硬件层面上进行的。 让我们看看软件层面发生了什么。 它实际上是倒退的(从图片的右边到左边):
handle_domain_irq()
,然后调用generic_handle_irq()
。 有关详细信息,请参阅Documentation / gpio / driver.txt 。 现在我们在SoC的GPIO控制器IRQ处理程序中。 generic_handle_irq()
来运行处理程序,该处理程序针对每个特定的引脚进行设置。 例如看看它是如何在omap_gpio_irq_handler()中完成的 。 现在我们在MAX7325中断处理程序。 handle_nested_irq()
,以便连接到MAX7325的设备(在本例中为“某个设备”IRQ处理程序handle_nested_irq()
所有IRQ处理程序将在max732x_irq_handler()
线程 GIC驱动程序,GPIO驱动程序和MAX7325驱动程序 – 他们都使用IRQ域API来将这些驱动程序表示为中断控制器。 让我们来看看在MAX732x驱动程序中是如何完成的。 它在这个提交中被添加了。 通过阅读IRQ域的文档并寻找这个提交,很容易弄清楚它是如何工作的。 该提交最有趣的部分是这一行(在max732x_irq_handler()
)中:
handle_nested_irq(irq_find_mapping(chip->gpio_chip.irqdomain, level));
irq_find_mapping()
将通过硬件IRQ号(使用IRQ域映射函数)来查找linux IRQ号。 然后handle_nested_irq()
函数将被调用,这将运行“某些设备”驱动程序的IRQ处理程序。
由于许多GPIO驱动程序都以相同的方式使用IRQ域,因此决定将该代码提取到GPIOLIB框架,更具体地说是GPIOLIB_IRQCHIP。 从Documentation/gpio/driver.txt
:
为帮助处理GPIO irqchips的设置和管理以及相关的irqdomain和资源分配回调,gpiolib有一些可以通过选择
GPIOLIB_IRQCHIP
Kconfig符号来启用的GPIOLIB_IRQCHIP
:
gpiochip_irqchip_add()
:增加一个irqchip到gpiochip。 它会将芯片的struct gpio_chip*
传递给所有IRQ回调struct gpio_chip*
,所以回调gpio_chip
需要将gpio_chip
嵌入到状态容器中,并使用container_of()
获取指向容器的指针。 (请参阅Documentation/driver-model/design-patterns.txt
)
gpiochip_set_chained_irqchip()
:从父IRQ为gpio_chip
设置一个链接的irq处理程序,并将struct gpio_chip*
作为处理程序数据传递。 (通知处理程序数据,因为irqchip数据很可能被父级irqchip使用!)这是针对链接类型的芯片。 如果NULL
作为处理程序传递,这也用于设置一个嵌套的irqchip。
该提交将IRQ域API转换为MAX732x驱动程序中的GPIOLIB_IRQCHIP API。
进一步的讨论在这里:
这是我在include / linux / irqdomain.h中找到的一个注释:
中断控制器的“域”数据结构。 这可以被定义为一个irq域控制器。 也就是说,它处理给定中断域的硬件和虚拟中断号之间的映射。
我认为它的实际结构是irq_domain。