gic driver

在kernel發生中斷後,會跳轉到彙編代碼entry-armv.S中__irq_svc處,進而調用handle_arch_irq,每個中斷控制器的drivver會使用如下:
handle_arch_irq = gic_handle_irq
將自己的中斷入口處理函數賦值給這個指針,這裏是gic的入口函數,從而進入GIC驅動,進行後續的中斷處理

32位arm的入口c函數/arch/arm/kernel/entry-armv.S:43:
ldr r1, =handle_arch_irq
每個架構的控制器都需要實現這個入口函數,handle_arch_irq = xxxx;
老的方式都要實現在machine 結構體的填充裏面實現handle_arch_irq = xxxx;

gic的實現方式:

set_handle_irq(gic_handle_irq);//gic_handle_irq即爲gic實現的入口函數
void __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
{
    if (handle_arch_irq)
        return;

    handle_arch_irq = handle_irq;
}

以下是dts方式的流程:
gic的代碼初始化入口在

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);
    WARN(!dist_base, "unable to map gic dist registers\n");

    cpu_base = of_iomap(node, 1);
    WARN(!cpu_base, "unable to map gic cpu registers\n");

    /*
     * Disable split EOI/Deactivate if either HYP is not available
     * or the CPU interface is too small.
     */
    if (gic_cnt == 0 && !gic_check_eoimode(node, &cpu_base))
        static_key_slow_dec(&supports_deactivate);

    if (of_property_read_u32(node, "cpu-offset", &percpu_offset))
        percpu_offset = 0;
    printk("enter %s %d\n",__FILE__,__LINE__);
    __gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset,//gic的初始化動作在這裏發生
             &node->fwnode);
    if (!gic_cnt)
        gic_init_physaddr(node);

    if (parent) {//只有存在父節點的時候纔會進入該分支,目前不需要進入
        printk("enter %s %d\n",__FILE__,__LINE__);
        irq = irq_of_parse_and_map(node, 0);
        gic_cascade_irq(gic_cnt, irq);
    }

    if (IS_ENABLED(CONFIG_ARM_GIC_V2M))
        gicv2m_of_init(node, gic_data[gic_cnt].domain);

    gic_cnt++;
    return 0;
}

static void __init __gic_init_bases(unsigned int gic_nr, int irq_start,
               void __iomem *dist_base, void __iomem *cpu_base,
               u32 percpu_offset, struct fwnode_handle *handle)
{
    irq_hw_number_t hwirq_base;
    struct gic_chip_data *gic;//描述gic控制器的結構體
    int gic_irqs, irq_base, i;

    BUG_ON(gic_nr >= MAX_GIC_NR);

    gic_check_cpu_features();

    gic = &gic_data[gic_nr];//一個成員就代表一個描述gic控制器的結構體
#ifdef CONFIG_GIC_NON_BANKED //一般不使能
    if (percpu_offset) { /* Frankein-GIC without banked registers... */
        unsigned int cpu;

        gic->dist_base.percpu_base = alloc_percpu(void __iomem *);
        gic->cpu_base.percpu_base = alloc_percpu(void __iomem *);
        if (WARN_ON(!gic->dist_base.percpu_base ||
                !gic->cpu_base.percpu_base)) {
            free_percpu(gic->dist_base.percpu_base);
            free_percpu(gic->cpu_base.percpu_base);
            return;
        }

        for_each_possible_cpu(cpu) {
            u32 mpidr = cpu_logical_map(cpu);
            u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
            unsigned long offset = percpu_offset * core_id;
            *per_cpu_ptr(gic->dist_base.percpu_base, cpu) = dist_base + offset;
            *per_cpu_ptr(gic->cpu_base.percpu_base, cpu) = cpu_base + offset;
        }

        gic_set_base_accessor(gic, gic_get_percpu_base);
    } else
#endif
    {           /* 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);
    }

    /*
     * 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控制器支持的最大中斷線
    gic_irqs = (gic_irqs + 1) * 32;
    if (gic_irqs > 1020)
        gic_irqs = 1020;
    gic->gic_irqs = gic_irqs;
//如果使用了dts的方式probo gic driver,則走這個分支,else是legacy的方式
    if (handle) {       /* DT/ACPI */
        printk("%s %d\n",__FILE__,__LINE__);
        //這裏主要是爲當前的中斷控制器創建一個domain,並且註冊這個domain,其中gic_irq_domain_hierarchy_ops是domain的ops結構體,
    其成員有分配線性中斷表,釋放該表,以及從hw irq到vir irq的轉化成員函數,這三個成員函數是doamin的核心實現
        gic->domain = irq_domain_create_linear(handle, gic_irqs,
                               &gic_irq_domain_hierarchy_ops,
                               gic);
    } else {        /* Legacy support */
        /*
         * For primary GICs, skip over SGIs.
         * For secondary GICs, skip over PPIs, too.
         */
         printk("enter %s %d\n",__FILE__,__LINE__);
        if (gic_nr == 0 && (irq_start & 31) > 0) {
            printk("enter %s %d\n",__FILE__,__LINE__);
            hwirq_base = 16;
            if (irq_start != -1)
                irq_start = (irq_start & ~31) + 16;
        } else {
        printk("enter %s %d\n",__FILE__,__LINE__);
            hwirq_base = 32;
        }

        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(NULL, gic_irqs, irq_base,
                    hwirq_base, &gic_irq_domain_ops, gic);
    }

    if (WARN_ON(!gic->domain))
        return;

    if (gic_nr == 0) {
//gic_nr ==0表示爲root級gic控制器
        /*
         * Initialize the CPU interface map to all CPUs.
         * It will be refined as each CPU probes its ID.
         * This is only necessary for the primary GIC.
         */
         printk("enter %s %d\n",__FILE__,__LINE__);
        for (i = 0; i < NR_GIC_CPU_IF; i++)
            gic_cpu_map[i] = 0xff;
#ifdef CONFIG_SMP
        set_smp_cross_call(gic_raise_softirq);
        register_cpu_notifier(&gic_cpu_notifier);
#endif
        set_handle_irq(gic_handle_irq);//這一步很關鍵,這裏指定了中斷髮生後進入的第一個中斷c入口。當中斷信號到來以後,/arch/arm/kernel/entry-armv.S:43:      ldr     r1, =handle_arch_irq 會跳轉到該函數指針,這裏將gic_handle_irq 賦值給該函數指針,所以當中斷信號發生後,會跳轉到gic_handle_irq這個函數中去識別硬件中斷源。
        if (static_key_true(&supports_deactivate))
            pr_info("GIC: Using split EOI/Deactivate mode\n");
    }

    gic_dist_init(gic);
    gic_cpu_init(gic);
    gic_pm_init(gic);
}
//domain的三個核心成員函數,分別用於硬件中斷號和系統中斷號的轉換,線性映射表的創建,線性映射表的撤銷
static const struct irq_domain_ops gic_irq_domain_hierarchy_ops = {
    .translate = gic_irq_domain_translate,
    .alloc = gic_irq_domain_alloc,
    .free = irq_domain_free_irqs_top,
};

此時初始化完成,在初始化的時候既沒有給hwirq分配對應的virq,也沒有建立二者之間的映射,這部分工作會到後面有人引用GIC上的某個中斷時再分配和建立。
某個驅動再申請註冊某個中斷的時候,會先申請一個軟件中斷號,對於有dts支持的系統,此時纔去映射一個軟件中斷,即這部分工作會到後面有人引用GIC上的某個中斷時再分配和建立。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章