gic driver

xiaoxiao2021-02-28  48

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

转载请注明原文地址: https://www.6miu.com/read-2631071.html

最新回复(0)