What are linux irq domains, why are they needed?
它在Documentation/IRQ-domain.txt的第一段中完美记录,所以我假设你已经知道了.如果不是 – 请询问有关该文档的不清楚之处.下面的文本说明了如何使用IRQ域API及其工作原理.
How GPIO controller can be called as interrupt controller?
让我用max732x.c驱动程序作为参考来回答这个问题(driver code).它是一个GPIO驱动程序,它也像中断控制器,所以它应该是IRQ域API如何工作的一个很好的例子.
物理水平
为了完全理解进一步的解释,我们先来看看MAX732x的机制.应用电路datasheet(我们的例子简化):
当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中断控制器.
设备树
expander: max7325@6d {
compatible = "maxim,max7325";
reg = <0x6d>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
interrupt-parent = ;
interrupts = <29 IRQ_TYPE_EDGE_FALLING>;
};
属性的含义如下:
> interrupt-controller属性定义设备生成中断;需要进一步将此节点用作“Some device”节点中的中断父节点.
>#interrupt-cells定义中断属性的格式;在我们的例子中,它是行号的2:1单元格和中断类型的1个单元格
> interrupt-parent和interrupts属性描述了中断线连接
假设我们有MAX7325的驱动程序和“Some device”的驱动程序.当然,两者都在CPU中运行.在“Some device”驱动程序中,当“某些器件”在MAX7325的P4引脚上改变电平时,我们要求事件中断.让我们首先在设备树中声明:
some_device: some_device@1c {
reg = <0x1c>;
interrupt-parent = ;
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);
每当MAX7325的P4引脚电平从低电平变为高电平(上升沿)时,将调用some_device_isr().怎么运行的?从左到右,如果您查看上图:
>“某些器件”在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处理程序(here)调用handle_nested_irq(),以便连接到MAX7325的设备的所有IRQ处理程序(在我们的例子中为“某些设备”IRQ处理程序)将在max732x_irq_handler()线程中调用
>最后,调用“Some device”驱动程序的IRQ处理程序
IRQ域API
GIC驱动程序,GPIO驱动程序和MAX7325驱动程序 – 它们都使用IRQ域API将这些驱动程序表示为中断控制器.我们来看看MAX732x驱动程序是如何完成的.它是在this提交中添加的.通过阅读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()函数,它将运行“Some device”驱动程序的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提交将IRQ域API转换为MAX732x驱动程序中的GPIOLIB_IRQCHIP API.
下一个问题
进一步讨论如下: