irq-gic-v3.c之gic_of_init

时间:2022-03-24 04:13:48
irq-gic-v3.c的最后一句话是
736 IRQCHIP_DECLARE(gic_v3, "arm,gic-v3", gic_of_init);
有前面的博文可知会在start_kernel的是时候调用gic_of_init,我们来看看gic_of_init 到底做了什么事情.
我们会在dts文件中定义gic的hw register的地址。这个地址是物理地址.
652行得到这个地址后,通过of_iomap映射后,cpu才能访问.
659行通过gic的寄存器对其gic的版本信息。
660行,如果发现当前gic 版本不是v3或者v4 则退出.
720行通过gic 寄存器对其当前support gic的个数,v3的话不能超过1020个.
689~713行是给gic_data 结构体赋值.
713 行设定gic中断的回调函数.


641 static int __init gic_of_init(struct device_node *node, struct device_node *parent)
642 {
643         void __iomem *dist_base;
644         void __iomem **redist_base;
645         u64 redist_stride;
646         u32 redist_regions;
647         u32 reg;
648         int gic_irqs;
649         int err;
650         int i;
651 
652         dist_base = of_iomap(node, 0);
653         if (!dist_base) {
654                 pr_err("%s: unable to map gic dist registers\n",
655                         node->full_name);
656                 return -ENXIO;
657         }
658 
659         reg = readl_relaxed(dist_base + GICD_PIDR2) & GIC_PIDR2_ARCH_MASK;
660         if (reg != GIC_PIDR2_ARCH_GICv3 && reg != GIC_PIDR2_ARCH_GICv4) {
661                 pr_err("%s: no distributor detected, giving up\n",
662                         node->full_name);
663                 err = -ENODEV;
664                 goto out_unmap_dist;
665         }
666 
667         if (of_property_read_u32(node, "#redistributor-regions", &redist_regions))
668                 redist_regions = 1;
669 
670         redist_base = kzalloc(sizeof(*redist_base) * redist_regions, GFP_KERNEL);
671         if (!redist_base) {
672                 err = -ENOMEM;
673                 goto out_unmap_dist;
674         }
675 
676         for (i = 0; i < redist_regions; i++) {
677                 redist_base[i] = of_iomap(node, 1 + i);
678                 if (!redist_base[i]) {
679                         pr_err("%s: couldn't map region %d\n",
680                                node->full_name, i);
681                         err = -ENODEV;
682                         goto out_unmap_rdist;
683                 }
684         }
685 
686         if (of_property_read_u64(node, "redistributor-stride", &redist_stride))
687                 redist_stride = 0;
688 
689         gic_data.dist_base = dist_base;
690         gic_data.redist_base = redist_base;
691         gic_data.redist_regions = redist_regions;
692         gic_data.redist_stride = redist_stride;
693 
694         /*
695          * Find out how many interrupts are supported.
696          * The GIC only supports up to 1020 interrupt sources (SGI+PPI+SPI)
697          */
698         gic_irqs = readl_relaxed(gic_data.dist_base + GICD_TYPER) & 0x1f;
699         gic_irqs = (gic_irqs + 1) * 32;
700         if (gic_irqs > 1020)
701                 gic_irqs = 1020;
702         gic_data.irq_nr = gic_irqs;
703 
704         gic_data.domain = irq_domain_add_tree(node, &gic_irq_domain_ops,
705                                               &gic_data);
706         gic_data.rdist = alloc_percpu(typeof(*gic_data.rdist));
707 
708         if (WARN_ON(!gic_data.domain) || WARN_ON(!gic_data.rdist)) {
709                 err = -ENOMEM;
710                 goto out_free;
711         }
712 
713         set_handle_irq(gic_handle_irq);
714 
715         gic_smp_init();
716         gic_dist_init();
717         gic_cpu_init();
718         gic_cpu_pm_init();
719 
720         return 0;


下来我们看看gic_smp_init的实现.
509 static void gic_smp_init(void)
510 {
511         set_smp_cross_call(gic_raise_softirq);
512         register_cpu_notifier(&gic_cpu_notifier);
513 }
511行给__smp_cross_call赋值gic_raise_softirq,这样当系统调用smp_cross_call是就会调用到
gic_raise_softirq。
gic_raise_softirq 定义如下是向cpumsk 中的cpu发送sgi的irq.


 static void gic_raise_softirq(const struct cpumask *mask, unsigned int irq)
485 {
486         int cpu;
487 
488         if (WARN_ON(irq >= 16))
489                 return;
490 
491         /*
492          * Ensure that stores to Normal memory are visible to the
493          * other CPUs before issuing the IPI.
494          */
495         smp_wmb();
496 
497         for_each_cpu_mask(cpu, *mask) {
498                 u64 cluster_id = cpu_logical_map(cpu) & ~0xffUL;
499                 u16 tlist;
500 
501                 tlist = gic_compute_target_list(&cpu, mask, cluster_id);
502                 gic_send_sgi(cluster_id, tlist, irq);
503         }
504 
505         /* Force the above writes to ICC_SGI1R_EL1 to be executed */
506         isb();
507 }
gic_smp_init的512 行注册一个通知链,当其他从cpu online时候就会调用gic_secondary_init 来初始化gic。
432 static struct notifier_block gic_cpu_notifier = {
433         .notifier_call = gic_secondary_init,
434         .priority = 100,
435 };


下来看看第二个函数gic_dist_init();
由于调用这个函数的时候其他cpu还没有跑起来,因此在317行将所有的irq都发给cpu0
295 static void __init gic_dist_init(void)
296 {
297         unsigned int i;
298         u64 affinity;
299         void __iomem *base = gic_data.dist_base;
300 
301         /* Disable the distributor */
302         writel_relaxed(0, base + GICD_CTLR);
303         gic_dist_wait_for_rwp();
304 
305         gic_dist_config(base, gic_data.irq_nr, gic_dist_wait_for_rwp);
306 
307         /* Enable distributor with ARE, Group1 */
308         writel_relaxed(GICD_CTLR_ARE_NS | GICD_CTLR_ENABLE_G1A | GICD_CTLR_ENABLE_G1,
309                        base + GICD_CTLR);
310 
311         /*
312          * Set all global interrupts to the boot CPU only. ARE must be
313          * enabled.
314          */
315         affinity = gic_mpidr_to_affinity(cpu_logical_map(smp_processor_id()));
316         for (i = 32; i < gic_data.irq_nr; i++)
317                 writeq_relaxed(affinity, base + GICD_IROUTER + i * 8);
318 }
gic_cpu_init();这个函数cpu和sgi的关系。gic_cpu_pm_init();就是注册了一个通知链,用于cpu进入pm和离开pm的处理。一般不会修改到.