中断初始化流程一

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

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

从start_kernel 调用 init_IRQ开始

中断初始化流程一_第1张图片

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

中断初始化流程一_第2张图片

你可能感兴趣的:(linux,内核,linux)