中断
中断是外围设备通知处理器的一种机制。
1. 中断控制器
外围设备不是把中断请求直接发送给处理器,而是发给中断控制器,由中断控制器转发给处理器。
不同种类的中断控制器的访问方法存在差异,为了屏蔽差异,内核定义了中断控制器描述符irq_chip,每种中断控制器自定义各种操作函数。GIC v2控制器的描述符如下:
drivers/irqchip/irq-gic.c
tatic const struct irq_chip gic_chip = {
.irq_mask = gic_mask_irq,
.irq_unmask = gic_unmask_irq,
.irq_eoi = gic_eoi_irq,
.irq_set_type = gic_set_type,
.irq_get_irqchip_state = gic_irq_get_irqchip_state,
.irq_set_irqchip_state = gic_irq_set_irqchip_state,
.flags = IRQCHIP_SET_TYPE_MASKED |
IRQCHIP_SKIP_SET_WAKE |
IRQCHIP_MASK_ON_SUSPEND,
};
2. 中断域
一个大型系统可能有多个中断控制器,这些中断控制器可以级联,一个中断控制器作为中断源连接到另一个中断控制器,但只有一个中断控制器作为根控制器直接连接到处理器。为了把每个中断控制器本地的硬件中断映射到全局唯一的Linux中断号(也称为虚拟中断),内核定义了中断域irq_domain,每个中断控制器由自己的中断域。
2.1. 创建中断域
中断控制器的驱动程序使用分配函数irq_domain_add_*()创建和注册中断域。
2.2. 创建映射
创建中断域以后,需要向中断域添加硬件中断号到Linux中断号的映射,内核提供了函数irq_create_mapping:
unsigned int irq_create_mapping(struct irq_domain *host, irq_hw_number_t hwirq);
输入参数是中断域和硬件中断号,返回Linux中断号。
该函数首先分配Linux中断号,然后把硬件中断号到Linux中断号的映射添加到中断域。
2.3. 查找映射
中断处理程序需要根据硬件中断号查找Linux中断号,内核提供了函数irq_find_mapping:
unsigned int irq_find_mapping(struct irq_domain *host, irq_hw_number_t hwirq);
输入参数是中断域和硬件中断号,返回Linux中断号。
3. 中断控制器驱动初始化
3.1. 设备树源文件
ARM64架构使用扁平设备树(Flattened Device Tree,FDT)描述板卡的硬件信息,好处是可以把板卡的特定的代码从内核中删除,编译生成通用的板卡无关的内核。
设备树源文件是文本文件,扩展名是“.dts”,需要在设备树源文件中描述中断的相关信息:
(1)中断控制器的信息
(2)对于作为中断源的外围设备,需要描述设备连接到哪个中断控制器,使用哪个硬件中断号
参考arch/arm64/boot/dts/arm/foundation-v8.dtsi、arch/arm64/boot/dts/arm/foundation-v8-gicv2.dtsi。
3.2. 中断控制器匹配表
在GIC v2控制器的驱动程序中,定义了多个类型为of_device_id的静态变量,成员compatible是驱动程序支持的设备的名称,成员data是初始化函数,编译器把这些静态变量放在专用的节“__irqchip_of_table”里面。
我们把节“__irqchip_of_table”称为中断控制器匹配表,里面每个表项的格式是结构体of_device_id。
drivers/irqchip/irq-gic.c
IRQCHIP_DECLARE(gic_400, "arm,gic-400", gic_of_init);
...
IRQCHIP_DECLARE(cortex_a15_gic, "arm,cortex-a15-gic", gic_of_init);
IRQCHIP_DECLARE(cortex_a9_gic, "arm,cortex-a9-gic", gic_of_init);
...
把宏IRQCHIP_DECLARE展开以后是:
static const struct of_device_id __of_table_cortex_gic_400
__section(__irqchip_of_table)
= { .compatible = "arm,gic-g400",
.data = gic_of_init }
...
static const struct of_device_id __of_table_cortex_a15_gic
__section(__irqchip_of_table)
= { .compatible = "arm,cortex-a15-gic",
.data = gic_of_init }
static const struct of_device_id __of_table_cortex_a9_gic
__section(__irqchip_of_table)
= { .compatible = "arm,cortex-a9-gic",
.data = gic_of_init }
...
3.3. 初始化
在内核初始化的时候,匹配设备树文件中的中断控制器的属性“compatible”和内核的中断控制器匹配表,找到合适的中断控制器驱动程序,执行驱动程序的初始化函数。
start_kernel() -> init_IRQ() -> irqchip_init()
drivers/irqchip/irqchip.c
void __init irqchip_init(void)
{
of_irq_init(__irqchip_of_table); // 参数是中断控制器匹配表的起始地址__irqchip_of_table
...
}
(1)函数of_irq_init
driver/of/irq.c
/**
* of_irq_init - Scan and init matching interrupt controllers in DT
* @matches: 0 terminated array of nodes to match and init function to call
*
* This function scans the device tree for matching interrupt controller nodes,
* and calls their initialization functions in order with parents first.
*/
void __init of_irq_init(const struct of_device_id *matches)
{
const struct of_device_id *match;
struct device_node *np, *parent = NULL;
struct of_intc_desc *desc, *temp_desc;
struct list_head intc_desc_list, intc_parent_list;
INIT_LIST_HEAD(&intc_desc_list);
INIT_LIST_HEAD(&intc_parent_list);
for_each_matching_node_and_match(np, matches, &match) { /* 遍历设备树文件的设备节点。如果属性compatible和中断控制器匹配表中的任何一条表项的字段compatible匹配,处理如下 */
if (!of_property_read_bool(np, "interrupt-controller") ||
!of_device_is_available(np)) /* 如果没有节点属性interrupt-controller,说明设备不是中断控制器,忽略该设备 */
continue;
if (WARN(!match->data, "of_irq_init: no init function for %s\n",
match->compatible))
continue;
/*
* Here, we allocate and populate an of_intc_desc with the node
* pointer, interrupt-parent device_node etc.
*/
desc = kzalloc(sizeof(*desc), GFP_KERNEL); /* 分配一个of_intc_desc实例 */
if (WARN_ON(!desc)) {
of_node_put(np);
goto err;
}
desc->irq_init_cb = match->data; /* 成员irq_init_cb保存初始化函数 */
desc->dev = of_node_get(np); /* 成员dev保存本设备的device_node */
desc->interrupt_parent = of_irq_find_parent(np); /* 成员interrupt保存父设备 */
if (desc->interrupt_parent == np)
desc->interrupt_parent = NULL;
list_add_tail(&desc->list, &intc_desc_list); /* 把of_intc_desc实例添加到链表intc_desc_list中 */
}
/*
* The root irq controller is the one without an interrupt-parent.
* That one goes first, followed by the controllers that reference it,
* followed by the ones that reference the 2nd level controllers, etc.
*/
while (!list_empty(&intc_desc_list)) { /* 遍历链表intc_desc_list,从根设备开始,先执行父设备的初始化函数,然后执行子设备的初始化函数 */
/*
* Process all
ARM64架构中断处理

本文深入探讨ARM64架构下的中断处理机制,包括中断控制器、中断域的概念,以及GIC v2控制器的具体实现。文章详细讲解了中断的硬件与软件处理流程,中断线程化的优势,中断的亲和性设置,处理器间中断的应用,以及如何在代码中禁用和启用中断。
最低0.47元/天 解锁文章
2928

被折叠的 条评论
为什么被折叠?



