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的处理。一般不会修改到.