在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上的某個中斷時再分配和建立。