在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上的某个中断时再分配和建立。