Linux内核中断初始化的流程主要包括以下几个步骤:
从start_kernel 调用 init_IRQ开始
init_IRQ代码如下:
void __init init_IRQ(void)
{
init_irq_stacks();
irqchip_init();
if (!handle_arch_irq)
panic("No interrupt controller found.");
}
init_irq_stacks为每个cpu初始化中断栈空间
irqchip_init 初始化和注册中断控制器
irqchip_init源码如下:
void __init irqchip_init(void)
{
of_irq_init(__irqchip_of_table);
acpi_probe_device_table(irqchip);
}
of_irq_init(__irqchip_of_table);函数找到中断控制器,并通过回调初始化中断控制器
acpi_probe_device_table函数是Linux内核中的一个函数,用于在系统启动过程中探测和初始化ACPI(Advanced Configuration and Power Interface)设备表。
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) {//找到matches中每一个device_node,并帮对应的of_device_id给match
if (!of_property_read_bool(np, "interrupt-controller") ||
!of_device_is_available(np)) //判断np是不是中断控制器或者np是否可用
continue;
if (WARN(!match->data, "of_irq_init: no init function for %s\n",
match->compatible))//如果match->data是空,打印警告
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);//分配空间
if (WARN_ON(!desc)) {
of_node_put(np);
goto err;
}
desc->irq_init_cb = match->data;
desc->dev = of_node_get(np);
desc->interrupt_parent = of_irq_find_parent(np);
if (desc->interrupt_parent == np)
desc->interrupt_parent = NULL;
list_add_tail(&desc->list, &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)) { //判断中断控制器链表是否为空
/*
* Process all controllers with the current 'parent'.
* First pass will be looking for NULL as the parent.
* The assumption is that NULL parent means a root controller.
*/
list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {
int ret;
if (desc->interrupt_parent != parent)
continue;
list_del(&desc->list);
of_node_set_flag(desc->dev, OF_POPULATED);
pr_debug("of_irq_init: init %pOF (%p), parent %p\n",
desc->dev,
desc->dev, desc->interrupt_parent);
ret = desc->irq_init_cb(desc->dev,
desc->interrupt_parent);//调用中断控制器初始化函数
if (ret) {
of_node_clear_flag(desc->dev, OF_POPULATED);
kfree(desc);
continue;
}
/*
* This one is now set up; add it to the parent list so
* its children can get processed in a subsequent pass.
*/
list_add_tail(&desc->list, &intc_parent_list);//将中断控制器描述符添加到父链表中
}
/* Get the next pending parent that might have children */
desc = list_first_entry_or_null(&intc_parent_list,
typeof(*desc), list);
if (!desc) {
pr_err("of_irq_init: children remain, but no parents\n");
break;
}
list_del(&desc->list);
parent = desc->dev;
kfree(desc);
}
list_for_each_entry_safe(desc, temp_desc, &intc_parent_list, list) {
list_del(&desc->list);
kfree(desc);
}
err:
list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {
list_del(&desc->list);
of_node_put(desc->dev);
kfree(desc);
}
}
如上match->data来自__irqchip_of_table,__irqchip_of_table的定义如下:
IRQCHIP_DECLARE(gic_v3, "arm,gic-v3", gic_of_init);//感兴趣的话可以去查看IRQCHIP_DECLARE的定义。
desc->irq_init_cb既是gic_of_init函数,如下:
static int __init gic_of_init(struct device_node *node, struct device_node *parent)
{
void __iomem *dist_base;
struct redist_region *rdist_regs;
u64 redist_stride;
u32 nr_redist_regions;
int err, i;
dist_base = of_iomap(node, 0);
if (!dist_base) {
pr_err("%pOF: unable to map gic dist registers\n", node);
return -ENXIO;
}
err = gic_validate_dist_version(dist_base);
if (err) {
pr_err("%pOF: no distributor detected, giving up\n", node);
goto out_unmap_dist;
}
if (of_property_read_u32(node, "#redistributor-regions", &nr_redist_regions))//读取设备树中的值
nr_redist_regions = 1;
rdist_regs = kzalloc(sizeof(*rdist_regs) * nr_redist_regions, GFP_KERNEL);
if (!rdist_regs) {
err = -ENOMEM;
goto out_unmap_dist;
}
for (i = 0; i < nr_redist_regions; i++) {
struct resource res;
int ret;
ret = of_address_to_resource(node, 1 + i, &res);
rdist_regs[i].redist_base = of_iomap(node, 1 + i);//内存地址映射
if (ret || !rdist_regs[i].redist_base) {
pr_err("%pOF: couldn't map region %d\n", node, i);
err = -ENODEV;
goto out_unmap_rdist;
}
rdist_regs[i].phys_base = res.start;
}
if (of_property_read_u64(node, "redistributor-stride", &redist_stride))//读取设备树中的值
redist_stride = 0;
err = gic_init_bases(dist_base, rdist_regs, nr_redist_regions,
redist_stride, &node->fwnode);//gic 初始化
if (err)
goto out_unmap_rdist;
gic_populate_ppi_partitions(node);
gic_of_setup_kvm_info(node);
return 0;
out_unmap_rdist:
for (i = 0; i < nr_redist_regions; i++)
if (rdist_regs[i].redist_base)
iounmap(rdist_regs[i].redist_base);
kfree(rdist_regs);
out_unmap_dist:
iounmap(dist_base);
return err;
}
gic_of_init会从设备树中读取一些信息后,然后调用gic_init_bases。
如下设备树信息:
gic: interrupt-controller@12000000 {
compatible = "arm,gic-v3";
#interrupt-cells = <3>;
#address-cells = <2>;
#size-cells = <2>;
ranges;
redistributor-stride = <0x0 0x20000>; // 128KB stride
#redistributor-regions = <1>;
interrupt-controller;
reg = <0x0 0x12000000 0 0x20000>, // GICD
<0x0 0x12040000 0 0x100000>; // GICR
interrupts = <1 9 4>;
};
gic_init_bases的功能主要如下: