什么是irq域,我阅读了内核文档(https://www.kernel.org/doc/Documentation/IRQ-domain.txt)他们说:



GPIO Controller 如何称为中断 Controller ?

最佳答案



它已在Documentation/IRQ-domain.txt的第一段中完美记录,因此我假设您已经知道它。如果不是,请询问有关该文档的不清楚之处。下面的文本说明了如何使用IRQ域API及其工作方式。



让我使用max732x.c驱动程序作为引用(driver code)回答这个问题。它是一个GPIO驱动程序,它也像中断 Controller 一样工作,因此它应该是IRQ域API工作原理的一个很好的例子。

body 水平

为了完全理解进一步的解释,让我们首先了解一下MAX732x的原理。 datasheet的应用电路(在我们的示例中已简化):

linux - 什么是Linux irq域,为什么需要它们?-LMLPHP

当P0-P7引脚上的电压电平发生变化时,MAX7325将在INT引脚上产生中断。驱动程序(在SoC上运行)可以通过I2C(SCL/SDA引脚)读取P0-P7引脚的状态,并为每个P0-P7引脚生成单独的中断。这就是为什么此驱动程序充当中断 Controller 的原因。

考虑下一个配置:

linux - 什么是Linux irq域,为什么需要它们?-LMLPHP

“某些设备”改变P4引脚上的电平,诱使MAX7325产生中断。来自MAX7325的中断连接到GPIO4 IP内核(在SoC内部),它使用该GPIO4模块的29号线通知CPU有关中断的信息。因此,可以说MAX7325是级联的至GPIO4 Controller 。 GPIO4还充当中断 Controller ,并且级联到GIC中断 Controller 。

设备树

让我们在设备树中声明以上配置。我们可以使用Documentation/devicetree/bindings/gpio/gpio-max732x.txt的绑定(bind)作为引用:

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-parentinterrupts属性描述中断线连接

  • 假设我们有MAX7325的驱动程序和“某些设备”的驱动程序。当然,两者都在CPU中运行。在“某些设备”驱动程序中,当“某些设备”更改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);
    

    每当MAX7325的P4引脚上的电平从低到高(上升沿)时,就会调用some_device_isr()。这个怎么运作?从左到右,如果您看向上方的图片,请执行以下操作:
  • “某些设备”在MAX7325的P4上更改电平
  • MAX7325改变其INT引脚的电平
  • GPIO4模块被配置为捕获此类更改,因此它会生成对GIC
  • 的中断
  • GIC通知CPU

  • 所有这些 Action 都是在硬件级别上发生的。让我们看看在软件级别上发生了什么。实际上,它向后退(从图片的右到左):
  • CPU现在位于GIC中断处理程序中的中断上下文中。从gic_handle_irq()调用handle_domain_irq(),然后又调用generic_handle_irq()。有关详细信息,请参见Documentation/gpio/driver.txt。现在我们在SoC的GPIO Controller IRQ处理程序中。
  • SoC的GPIO驱动程序还调用generic_handle_irq()来运行处理程序,该处理程序针对每个特定的引脚设置。例如,在omap_gpio_irq_handler()中查看它是如何完成的。现在我们处于MAX7325 IRQ处理程序中。
  • MAX7325 IRQ处理程序(here)调用handle_nested_irq(),以便连接到MAX7325的设备的所有IRQ处理程序(在本例中为“某些设备” IRQ处理程序)将在max732x_irq_handler()线程
  • 中调用
    最后,
  • ,“某些设备”驱动程序的IRQ处理程序称为

  • IRQ域API

    GIC驱动程序,GPIO驱动程序和MAX7325驱动程序-它们都使用IRQ域API将这些驱动程序表示为中断 Controller 。让我们看一下如何在MAX732x驱动程序中完成它。它是在this commit中添加的。只需阅读IRQ域文档并查看此提交,就很容易弄清楚它是如何工作的。该提交最有趣的部分是这一行(在max732x_irq_handler()中):

    handle_nested_irq(irq_find_mapping(chip->gpio_chip.irqdomain, level));
    
    irq_find_mapping()将按硬件IRQ号找到Linux IRQ号(使用IRQ域映射函数)。然后将调用handle_nested_irq()函数,该函数将运行“某些设备”驱动程序的IRQ处理程序。

    GPIOLIB_IRQCHIP

    由于许多GPIO驱动程序以相同的方式使用IRQ域,因此决定将该代码提取到GPIOLIB框架中,更具体地讲,将其提取到GPIOLIB_IRQCHIP中。从Documentation/gpio/driver.txt:



    This commit在MAX732x驱动程序中将IRQ域API转换为GPIOLIB_IRQCHIP API。

    下一个问题

    进一步的讨论在这里:
  • part 2
  • part 3
  • 10-06 13:31