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

标签 linux linux-kernel linux-device-driver embedded-linux interrupt

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

The number of interrupt controllers registered as unique irqchips show a rising tendency: for example subdrivers of different kinds such as GPIO controllers avoid reimplementing identical callback mechanisms as the IRQ core system by modeling their interrupt handlers as irqchips, i.e. in effect cascading interrupt controllers.



GPIO Controller 如何称为中断 Controller ?

最佳答案

What are linux irq domains, why are they needed?



它在 Documentation/IRQ-domain.txt 的第一段中得到了完美的记录。 ,所以我假设你已经知道了。如果不是 - 请询问该文档有哪些不清楚的地方。下面的文本解释了如何使用 IRQ 域 API 及其工作原理。

How GPIO controller can be called as interrupt controller?



让我用 回答这个问题驱动程序作为引用( driver code )。它是一个 GPIO 驱动程序,它也充当中断 Controller ,因此它应该是 IRQ 域 API 如何工作的一个很好的例子。

body 水平

为了完全理解进一步的解释,让我们先看看 MAX732x 的机制。应用电路来自datasheet (为我们的示例简化):

MAX7325 Typical Application Circuit

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

考虑下一个配置:

interrupt cascading

“某些设备”改变 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 的格式属性(property);在我们的例子中是 2 : 1 个单元格用于行号,1 个单元格用于中断类型
  • interrupt-parentinterrupts属性描述中断线连接

  • 假设我们有 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()每次当 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 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() 中被调用。线程
  • 最后,“Some device”驱动程序的 IRQ 处理程序被称为

  • IRQ 域 API

    GIC 驱动程序、GPIO 驱动程序和 MAX7325 驱动程序——它们都使用 IRQ 域 API 将这些驱动程序表示为中断 Controller 。让我们来看看它是如何在 MAX732x 驱动程序中完成的。它是在 this 中添加的犯罪。只需阅读 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 :

    To help out in handling the set-up and management of GPIO irqchips and the associated irqdomain and resource allocation callbacks, the gpiolib has some helpers that can be enabled by selecting the GPIOLIB_IRQCHIP Kconfig symbol:

    • gpiochip_irqchip_add(): adds an irqchip to a gpiochip. It will pass the struct gpio_chip* for the chip to all IRQ callbacks, so the callbacks need to embed the gpio_chip in its state container and obtain a pointer to the container using container_of(). (See Documentation/driver-model/design-patterns.txt)

    • gpiochip_set_chained_irqchip(): sets up a chained irq handler for a gpio_chip from a parent IRQ and passes the struct gpio_chip* as handler data. (Notice handler data, since the irqchip data is likely used by the parent irqchip!) This is for the chained type of chip. This is also used to set up a nested irqchip if NULL is passed as handler.



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

    下一个问题

    进一步的讨论在这里:
  • part 2
  • part 3
  • 关于linux - 什么是 linux irq 域,为什么需要它们?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34371352/

    相关文章:

    linux - 无滴答内核、isolcpus、nohz_full 和 rcu_nocbs

    linux - 使用 uinput 在 Linux 中模拟绝对鼠标移动

    自定义 linux pcie 驱动程序 MSI 中断

    linux-kernel - 用可加载的模块替换内核内置模块

    linux-kernel - 这是Linux内核崩溃吗?我该如何解决?

    linux - linux内核中的大尺寸kmalloc kmalloc

    Linux命令: show content of all files

    python - 如何在 python 或 bash 脚本中向文件名中包含的数字添加常量?

    linux - bash:如何从文件中提取数千行非连续行

    linux - 为什么 '/./,/^$/!d'在sed中有任何作用?