什么是irq域,我读了内核文档(https://www.kernel.org/doc/Documentation/IRQ-domain.txt),他们说:
注册为唯一irqchips的中断控制器的数量显示出上升趋势:例如,GPIO控制器等不同类型的子驱动器通过将其中断处理程序建模为irqchips,即实际上是级联中断控制器,避免重新实现与IRQ核心系统相同的回调机制.
如何将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
property定义该设备生成中断; 需要进一步使用此节点,如interrupt-parent
"某些设备"节点.
#interrupt-cells
定义interrupts
财产的格式; 在我们的例子中,它是2
:1个单元用于行号,1个单元用于中断类型
interrupt-parent
和interrupts
属性描述中断线连接
假设我们有MAX7325的驱动程序和"Some device"的驱动程序.当然,两者都在CPU中运行.在"Some device"驱动程序中,当"某些器件"在MAX7325的P4引脚上改变电平时,我们要求事件中断.让我们首先在设备树中声明:
some_device: some_device@1c { reg = <0x1c>; interrupt-parent = <&expander>; interrupts = <4 IRQ_TYPE_EDGE_RISING>; };
现在我们可以做这样的事情(在"Some device"驱动程序中):
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()
每当MAX7325的P4引脚电平从低电平变为高电平(上升沿)时,将被调用.这个怎么运作?从左到右,如果您查看上图:
"某些器件"在MAX7325的P4上改变电平
MAX7325的INT引脚电平变化
GPIO4模块配置为捕获这样的更改,因此它会生成GIC中断
GIC通知CPU
所有这些操作都发生在硬件级别上.让我们看看软件层面发生了什么.它实际上是倒退的(从图片的右侧到左侧):
CPU现在处于GIC中断处理程序的中断上下文中.从gic_handle_irq()调用handle_domain_irq()
,然后调用generic_handle_irq()
.有关详细信息,请参阅Documentation/gpio/driver.txt.现在我们在SoC的GPIO控制器IRQ处理程序中.
SoC的GPIO驱动程序还调用generic_handle_irq()
运行处理程序,该处理程序为每个特定引脚设置.例如,请参阅omap_gpio_irq_handler()中的完成方式.现在我们在MAX7325 IRQ处理程序中.
MAX7325 IRQ处理程序(此处)调用handle_nested_irq()
,以便连接到MAX7325的设备的所有IRQ处理程序(在我们的例子中为"某些设备"IRQ处理程序)将在max732x_irq_handler()
线程中调用
最后,调用"Some device"驱动程序的IRQ处理程序
GIC驱动程序,GPIO驱动程序和MAX7325驱动程序 - 它们都使用IRQ域API将这些驱动程序表示为中断控制器.我们来看看它是如何在MAX732x驱动程序中完成的.它已添加到此提交中.通过阅读IRQ域文档并查看此提交,很容易弄清楚它是如何工作的.该提交中最有趣的部分是这一行(in max732x_irq_handler()
):
handle_nested_irq(irq_find_mapping(chip->gpio_chip.irqdomain, level));
irq_find_mapping()
将通过硬件IRQ号找到linux IRQ号(使用IRQ域映射功能).然后handle_nested_irq()
将调用函数,它将运行"Some device"驱动程序的IRQ处理程序.
由于许多GPIO驱动程序以相同的方式使用IRQ域,因此决定将该代码提取到GPIOLIB框架,更具体地说是GPIOLIB_IRQCHIP.来自Documentation/gpio/driver.txt
:
为了帮助处理GPIO irqchips的设置和管理以及相关的irqdomain和资源分配回调,gpiolib有一些帮助器可以通过选择
GPIOLIB_IRQCHIP
Kconfig符号来启用:
gpiochip_irqchip_add()
:将一个irqchip添加到gpiochip.它会将struct gpio_chip*
芯片传递给所有IRQ回调,因此回调需要将gpio_chip
其嵌入其状态容器中并使用获取指向容器的指针container_of()
.(见Documentation/driver-model/design-patterns.txt
)
gpiochip_set_chained_irqchip()
:gpio_chip
为父IRQ 设置一个链式irq处理程序, 并传递struct gpio_chip*
as处理程序数据.(注意处理程序数据,因为irqchip数据很可能被父irqchip使用!)这是针对链式芯片的.如果NULL
作为处理程序传递,这也用于设置嵌套的irqchip .
此提交将IRQ域API转换为MAX732x驱动程序中的GPIOLIB_IRQCHIP API.
进一步讨论如下:
第2部分
第3部分