Linux基础 -- 中断子系统之级联中断
Linux 级联中断 (irq_set_chained_handler_and_data
) 详解
1. irq_set_chained_handler_and_data()
介绍
1.1. API 定义
void irq_set_chained_handler_and_data(unsigned int irq, irq_flow_handler_t handler, void *data);
-
参数
irq
:需要绑定处理函数的中断号(通常是主中断控制器的 IRQ)。handler
:级联处理函数,在该 IRQ 触发时被调用。data
:传递给handler
的私有数据。
-
功能
- 绑定
handler
到irq
,在irq
触发时调用。 - 适用于级联中断控制器,处理主控制器 IRQ 并触发次级控制器中断。
- 绑定
-
作用
- 用于 级联(Cascaded)中断控制器,如 GPIO 控制器、FPGA 软核等。
- 在 多级中断控制(如 ARM GIC + GPIO 控制器)中,GPIO 控制器通常级联到 GIC。
2. 级联(Cascaded)中断的实现全过程
2.1. 级联中断背景
- 主中断控制器(Primary Interrupt Controller)
- 例如 ARM GIC(Generic Interrupt Controller)。
- 次级(子)中断控制器(Secondary Interrupt Controller)
- 例如 GPIO 控制器、FPGA 软核中断控制器等。
2.2. 级联中断工作流程
- 设备触发次级中断控制器上的中断。
- 次级中断控制器向主中断控制器发送中断请求。
- 主中断控制器检测到 IRQ,调用
irq_set_chained_handler_and_data()
绑定的处理函数。 - 级联处理函数读取次级中断控制器状态。
- 触发适当的次级 IRQ 处理。
3. 级联中断完整实现
3.1. 设备树 (DTS) 配置
my_gpio: gpio-controller@10000000 {
compatible = "mycompany,my-gpio";
reg = <0x10000000 0x1000>;
interrupt-parent = <&gic>;
interrupts = <30 IRQ_TYPE_LEVEL_HIGH>; /* 级联到 GIC 的 IRQ */
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
};
3.2. 级联中断驱动实现
(1) 绑定 irq_set_chained_handler_and_data()
static struct irq_chip my_gpio_irq_chip = {
.name = "my-gpio",
.irq_mask = my_gpio_mask_irq,
.irq_unmask = my_gpio_unmask_irq,
.irq_ack = my_gpio_ack_irq,
};
static void my_gpio_irq_handler(struct irq_desc *desc)
{
struct irq_chip *chip = irq_desc_get_chip(desc);
struct my_gpio_chip *gpio = irq_desc_get_handler_data(desc);
unsigned int irq;
/* Acknowledge 主控制器 */
chained_irq_enter(chip, desc);
/* 读取次级控制器的 IRQ 状态 */
irq = my_gpio_read_irq_status(gpio);
/* 触发子级中断 */
if (irq)
generic_handle_irq(irq);
/* 退出中断 */
chained_irq_exit(chip, desc);
}
static int my_gpio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct my_gpio_chip *gpio;
int parent_irq;
gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL);
if (!gpio)
return -ENOMEM;
/* 获取主中断控制器 IRQ 号 */
parent_irq = platform_get_irq(pdev, 0);
if (parent_irq < 0)
return parent_irq;
/* 绑定级联中断处理函数 */
irq_set_chained_handler_and_data(parent_irq, my_gpio_irq_handler, gpio);
return 0;
}
(2) 触发子级中断
static irqreturn_t my_gpio_irq_handler(int irq, void *dev_id)
{
unsigned int child_irq = get_mapped_irq_for_gpio(irq);
if (child_irq)
generic_handle_irq(child_irq);
return IRQ_HANDLED;
}
4. 关键 API 解析
(1) irq_set_chained_handler_and_data()
- 绑定
handler
到irq
,用于在irq
触发时自动执行。 - 只能用于级联中断,不可用于普通设备 IRQ 处理。
(2) generic_handle_irq(irq)
- 触发子级 IRQ,通知
irq_desc
进行处理。
(3) chained_irq_enter()
/ chained_irq_exit()
- 进入/退出链式中断处理,确保正确处理 主控制器的 IRQ 处理流程。
5. 级联中断的完整流程
- 外设触发子控制器 IRQ(如 GPIO 中断)。
- 子控制器触发主控制器 IRQ(如
GIC_IRQ_30
)。 - 主控制器调用
my_gpio_irq_handler()
。 my_gpio_irq_handler()
读取子控制器状态。generic_handle_irq()
触发子 IRQ 进行处理。- 子 IRQ 驱动完成处理。
6. 总结
irq_set_chained_handler_and_data()
主要用于 绑定级联中断处理函数,不用于普通设备 IRQ 处理。- 级联中断需要 主控制器和次级控制器配合,手动调用
generic_handle_irq()
处理子级 IRQ。 chained_irq_enter()/chained_irq_exit()
需要在 主 IRQ 处理前后调用 以保证正确性。irq_chip
结构体定义了 子控制器的中断管理方式(屏蔽、中断确认等)。