中断控制器声明宏IRQCHIP_DECLARE
分析
定义一个中断控制器的时候,一般使用这个宏,下面是分析这个宏是怎么将你定义的irqchip结构参与设备树匹配并最终被调用的。
#define IRQCHIP_DECLARE(name,compat,fn)
name
表示控制器名字compat
匹配设备树中的compatiblefn
匹配后调用,类似于probe
实例展开
文件[/drivers/irqchip/irq-gic.c]中,以IRQCHIP_DECLARE(gic_400, "arm,gic-400", gic_of_init);
为例,展开:
static const struct of_device_id __of_table_gic_400 __used __section(__irqchip_of_table) = {
.compatible = "arm,gic-400",
.data = (gic_of_init == (of_init_fn_2)((void *)0)) ? gic_of_init : gic_of_init
}
__used
就是__attribute__((used))
,用来控制编译时不被优化掉未使用的结构体变量__section(__irqchip_of_table)
展开就是__attribute__((__section__(__irqchip_of_table)))
, 用来控制链接时将结构体变量放入__irqchip_of_table
段
目前可以看到,这个宏的功能就是定义一个struct of_device_id
结构体,根据宏参数初始化此结构体,并指定链接时放入__irqchip_of_table
段中。
问题来了,这怎么就能参与设备树中中断控制器节点的匹配了呢?
irqchip初始化
先从文件[drivers/irqchip/irqchip.c]中的初始化代码分析,
-
外部引用了
__irqchip_of_table
数组符号,这个符号并不是在C代码中定义的,打开内核链接脚本vmlinux.lds
,可以找到:.init.data : ...略... __irqchip_of_table = .; KEEP(*(__irqchip_of_table)) KEEP(*(__irqchip_of_table_end)) . = ALIGN(8);
__irqchip_of_table
数组符号是在此处被定义的,而#define IRQCHIP_DECLARE(name,compat,fn)
宏定义的irqchip控制器数据结构都是放在__irqchip_of_table
数组符号的后面,并且最末尾放置上述C文件中定义的__irqchip_of_table_end
,这是此段最后一个struct of_device_id
数据。 -
回来接着分析,
irqchip_init
初始化函数中,将所有IRQCHIP_DECLARE定义的irq控制器组成的数组指针传入of_irq_init
函数void __init of_irq_init(const struct of_device_id *matches) { // ...略... // 遍历设备树中与matches元素compatible匹配的节点 for_each_matching_node_and_match(np, matches, &match) { // 要求设备树节点中定义属性"interrupt-controller" if (!of_property_read_bool(np, "interrupt-controller") || !of_device_is_available(np)) continue; // ...略... // 组装设备树中断描述结构(struct of_intc_desc *desc)链表 // 每一项包含设备树对应device_node、父device_node、init_cb desc->irq_init_cb = match->data; // 从of_device_id中取init_cb desc->dev = of_node_get(np); desc->interrupt_parent = of_irq_find_parent(np); // 通过此节点中是否指定"interrupt-parent",并且指定节点中是否定义"#interrupt-cells"来判断 // ...略... list_add_tail(&desc->list, &intc_desc_list); } // 遍历上述匹配的链表 while (!list_empty(&intc_desc_list)) { list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) { int ret; // 这里保证处理最终的父节点 if (desc->interrupt_parent != parent) continue; list_del(&desc->list); // ...略... // 这里调用init_cb ret = desc->irq_init_cb(desc->dev, desc->interrupt_parent); // ...略... } // ...略... } // ...略... }
-
可以看到,基本就是将IRQCHIP_DECLARE注册的数组,和设备树进行匹配,匹配成功的元素,被调用其注册init_cb的回调
总结
IRQCHIP_DECLARE
此宏是定义了一个struct of_device_id
结构体- 此结构体通过控制链接属性被放到内核链接脚本中定义的符号
__irqchip_of_table
下面 - 内核启动时,此符号被
irqchip_init
代码以extern的方式作为数组访问,传入of_irq_init
初始化流程 - 而后将此数组与设备树进行匹配(通过
compatible
、interrupt-controller
、interrupt-parent
、#interrupt-cells
等限制信息) - 匹配后的项目中被调用init_cb回调