中断初始化流程一

发布时间:2024年01月06日

Linux内核中断初始化的流程主要包括以下几个步骤:

  1. 初始化GIC(Generic Interrupt Controller):GIC是用于管理中断的控制器,Linux内核首先需要初始化GIC,包括配置GIC的寄存器和相关参数。
  2. 配置中断控制器:根据硬件平台和中断控制器的类型,Linux内核需要配置中断控制器,包括设置中断的优先级、子优先级、触发方式等。
  3. 注册中断处理函数:Linux内核需要为每个中断注册一个中断处理函数,该函数会在中断发生时被调用。中断处理函数通常会根据中断的类型执行相应的操作,例如处理硬件事件、执行任务队列等。
  4. 启动中断:在完成上述初始化后,Linux内核会启动中断。当硬件设备产生中断时,GIC会将中断发送给相应的CPU,CPU执行相应的中断处理函数。

从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的功能主要如下:

文章来源:https://blog.csdn.net/weixin_44740132/article/details/135137078
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。