QCOM GIC 分析(ARM64)

来源:互联网 发布:程序员被勒索1000万 编辑:程序博客网 时间:2024/06/06 04:16

1 Device Tree Node

                interrupt-controller@f9000000 {
                        compatible = "qcom,msm-qgic2";
                        interrupt-controller;
                        #interrupt-cells = <0x3>;
                        reg = <0xf9000000 0x1000 0xf9002000 0x1000>;
                        linux,phandle = <0x1>;
                        phandle = <0x1>;
                };

对应的驱动, IRQCHIP_DECLARE(msm_qgic2, "qcom,msm-qgic2", gic_of_init);

2 何时调用gic_of_init


init/main.c start_kernel()-》init_IRQ()


arch/arm64/kernel/irq.c

void __init init_IRQ(void)
{
        irqchip_init();
        if (!handle_arch_irq)
                panic("No interrupt controller found.");
}


drivers/irqchip/irqchip.c

void __init irqchip_init(void)
{
        of_irq_init(__irqchip_begin);
}

drivers/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)
{
        struct device_node *np, *parent = NULL;
        struct 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(np, matches) {
                if (!of_find_property(np, "interrupt-controller", NULL))     / 查找中断控制器
                        continue;
                /*
                 * Here, we allocate and populate an intc_desc with the node
                 * pointer, interrupt-parent device_node etc.
                 */
                desc = kzalloc(sizeof(*desc), GFP_KERNEL);
                if (WARN_ON(!desc))
                        goto err;

                desc->dev = 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) {
                        const struct of_device_id *match;
                        int ret;
                        of_irq_init_cb_t irq_init_cb;

                        if (desc->interrupt_parent != parent)    /寻找跟中断控制器
                                continue;

                        list_del(&desc->list);
                        match = of_match_node(matches, desc->dev);
                        if (WARN(!match->data,
                            "of_irq_init: no init function for %s\n",
                            match->compatible)) {
                                kfree(desc);
                                continue;
                        }

                        pr_debug("of_irq_init: init %s @ %p, parent %p\n",
                                 match->compatible,
                                 desc->dev, desc->interrupt_parent);
                        irq_init_cb = (of_irq_init_cb_t)match->data;    /调用相应的初始化函数
                        ret = irq_init_cb(desc->dev, desc->interrupt_parent);
                        if (ret) {
                                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(&intc_parent_list, typeof(*desc), list);
                if (list_empty(&intc_parent_list) || !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);
                kfree(desc);
        }
}


3. 中断控制器初始化

根据device tree的设置,gic_of_init被调用。



int __init gic_of_init(struct device_node *node, struct device_node *parent)
{
        void __iomem *cpu_base;
        void __iomem *dist_base;
        u32 percpu_offset;
        int irq;

        if (WARN_ON(!node))
                return -ENODEV;

        dist_base = of_iomap(node, 0);                              / Dist 对应的内存地址
        WARN(!dist_base, "unable to map gic dist registers\n");

        cpu_base = of_iomap(node, 1);                             / CPU 对应的内存地址
        WARN(!cpu_base, "unable to map gic cpu registers\n");

        if (of_property_read_u32(node, "cpu-offset", &percpu_offset))
                percpu_offset = 0;

        gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset, node); 

        if (parent) {
                irq = irq_of_parse_and_map(node, 0);
                gic_cascade_irq(gic_cnt, irq);
        }
        gic_cnt++;
        return 0;
}


void __init gic_init_bases(unsigned int gic_nr, int irq_start,
                           void __iomem *dist_base, void __iomem *cpu_base,
                           u32 percpu_offset, struct device_node *node)
{
        irq_hw_number_t hwirq_base;
        struct gic_chip_data *gic;
        int gic_irqs, irq_base, i;

        BUG_ON(gic_nr >= MAX_GIC_NR);

        gic = &gic_data[gic_nr];

        {                       /* Normal, sane GIC... */
                WARN(percpu_offset,
                     "GIC_NON_BANKED not enabled, ignoring %08x offset!",
                     percpu_offset);
                gic->dist_base.common_base = dist_base;
                gic->cpu_base.common_base = cpu_base;
                gic_set_base_accessor(gic, gic_get_common_base);
        }

        /*
         * Initialize the CPU interface map to all CPUs.
         * It will be refined as each CPU probes its ID.
         */
        for (i = 0; i < NR_GIC_CPU_IF; i++)
                gic_cpu_map[i] = 0xff;

        /*
         * For primary GICs, skip over SGIs.
         * For secondary GICs, skip over PPIs, too.
         */
        if (gic_nr == 0 && (irq_start & 31) > 0) {
                hwirq_base = 16;
                if (irq_start != -1)
                        irq_start = (irq_start & ~31) + 16;
        } else {
                hwirq_base = 32;
        }

        /*
         * Find out how many interrupts are supported.
         * The GIC only supports up to 1020 interrupt sources.
         */
        gic_irqs = readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_CTR) & 0x1f;
        gic_irqs = (gic_irqs + 1) * 32;
        if (gic_irqs > 1020)
                gic_irqs = 1020;
        gic->gic_irqs = gic_irqs;

       gic_irqs -= hwirq_base; /* calculate # of irqs to allocate */
        irq_base = irq_alloc_descs(irq_start, 16, gic_irqs, numa_node_id());
        if (IS_ERR_VALUE(irq_base)) {
                WARN(1, "Cannot allocate irq_descs @ IRQ%d, assuming pre-allocated\n",
                     irq_start);
                irq_base = irq_start;
        }
        gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base,
                                    hwirq_base, &gic_irq_domain_ops, gic);
        if (WARN_ON(!gic->domain))
                return;

#ifdef CONFIG_SMP
        set_smp_cross_call(gic_raise_softirq);
        register_cpu_notifier(&gic_cpu_notifier);
#endif

        set_handle_irq(gic_handle_irq);   /这儿处理GIC的中断下回专门讲解

        gic_chip.flags |= gic_arch_extn.flags;
        gic_dist_init(gic);
        gic_cpu_init(gic);
        gic_pm_init(gic);


}

至此,中断控制器初始化完成。


0 0
原创粉丝点击