对于可提供中断这种功能的IC来说,具体中断IC应该具有哪些功能参见IC描述,下面我们着重
讲解GIC,并且是GIC-V3版本的IC。
对于中断控制器来说,内核对其抽象,用数据结构struct irq_chip来对其描述。对于GIC来说,实现下面对象:
static struct irq_chip gic_chip = { .name = "GICv3", .irq_mask = gic_mask_irq, .irq_unmask = gic_unmask_irq, .irq_eoi = gic_eoi_irq, .irq_set_type = gic_set_type, .irq_set_affinity = gic_set_affinity, .irq_get_irqchip_state = gic_irq_get_irqchip_state, .irq_set_irqchip_state = gic_irq_set_irqchip_state, .flags = IRQCHIP_SET_TYPE_MASKED, };
static struct irq_chip gic_eoimode1_chip = { .name = "GICv3", .irq_mask = gic_eoimode1_mask_irq, .irq_unmask = gic_unmask_irq, .irq_eoi = gic_eoimode1_eoi_irq, .irq_set_type = gic_set_type, .irq_set_affinity = gic_set_affinity, .irq_get_irqchip_state = gic_irq_get_irqchip_state, .irq_set_irqchip_state = gic_irq_set_irqchip_state, .irq_set_vcpu_affinity = gic_irq_set_vcpu_affinity, .flags = IRQCHIP_SET_TYPE_MASKED, };
当然,这里设置的函数都是对GIC控制器进行的操作。
对于中断控制来说,Linux内核定义了一个宏,用于在系统启动时候可以对GIC提供的初始化函数调用:
IRQCHIP_DECLARE
所以,这里我们定义GIC初始化如下:
IRQCHIP_DECLARE(gic_v3, "arm,gic-v3", gic_of_init);
这样的话,在内核启动时,会对这些定义的IRQCHIP_DECLARE宏下的回调函数调用,以对中断控制器初始化。
这里我们就是调用函数gic_of_init。
函数gic_of_init实现如下: static int __init gic_of_init(struct device_node *node, struct device_node *parent) { void __iomem *dist_base; struct redist_region *rdist_regs; u64 redist_stride; u32 nr_redist_regions; int err, i;
dist_base = of_iomap(node, 0); if (!dist_base) { pr_err("%s: unable to map gic dist registers\n", node->full_name); return -ENXIO; }
err = gic_validate_dist_version(dist_base); if (err) { pr_err("%s: no distributor detected, giving up\n", node->full_name); goto out_unmap_dist; }
if (of_property_read_u32(node, "#redistributor-regions", &nr_redist_regions)) nr_redist_regions = 1;
rdist_regs = kzalloc(sizeof(*rdist_regs) * nr_redist_regions, GFP_KERNEL); if (!rdist_regs) { err = -ENOMEM; goto out_unmap_dist; }
for (i = 0; i < nr_redist_regions; i++) { struct resource res; int ret;
ret = of_address_to_resource(node, 1 + i, &res); rdist_regs[i].redist_base = of_iomap(node, 1 + i); if (ret || !rdist_regs[i].redist_base) { pr_err("%s: couldn't map region %d\n", node->full_name, i); err = -ENODEV; goto out_unmap_rdist; } rdist_regs[i].phys_base = res.start; }
if (of_property_read_u64(node, "redistributor-stride", &redist_stride)) redist_stride = 0;
err = gic_init_bases(dist_base, rdist_regs, nr_redist_regions, redist_stride, &node->fwnode); if (err) goto out_unmap_rdist;
gic_populate_ppi_partitions(node); gic_of_setup_kvm_info(node); return 0;
out_unmap_rdist: for (i = 0; i < nr_redist_regions; i++) if (rdist_regs[i].redist_base) iounmap(rdist_regs[i].redist_base); kfree(rdist_regs); out_unmap_dist: iounmap(dist_base); return err; }
这里,我们需要了解GIC控制器提供的基础寄存器,然后对其配置。
static int __init gic_init_bases(void __iomem *dist_base, struct redist_region *rdist_regs, u32 nr_redist_regions, u64 redist_stride, struct fwnode_handle *handle) { u32 typer; int gic_irqs; int err;
if (!is_hyp_mode_available()) static_key_slow_dec(&supports_deactivate);
if (static_key_true(&supports_deactivate)) pr_info("GIC: Using split EOI/Deactivate mode\n");
gic_data.fwnode = handle; gic_data.dist_base = dist_base; gic_data.redist_regions = rdist_regs; gic_data.nr_redist_regions = nr_redist_regions; gic_data.redist_stride = redist_stride;
gicv3_enable_quirks();
/* * Find out how many interrupts are supported. * The GIC only supports up to 1020 interrupt sources (SGI+PPI+SPI) */ typer = readl_relaxed(gic_data.dist_base + GICD_TYPER); gic_data.rdists.id_bits = GICD_TYPER_ID_BITS(typer); gic_irqs = GICD_TYPER_IRQS(typer); if (gic_irqs > 1020) gic_irqs = 1020; gic_data.irq_nr = gic_irqs;
gic_data.domain = irq_domain_create_tree(handle, &gic_irq_domain_ops, &gic_data); gic_data.rdists.rdist = alloc_percpu(typeof(*gic_data.rdists.rdist));
if (WARN_ON(!gic_data.domain) || WARN_ON(!gic_data.rdists.rdist)) { err = -ENOMEM; goto out_free; }
set_handle_irq(gic_handle_irq);
if (IS_ENABLED(CONFIG_ARM_GIC_V3_ITS) && gic_dist_supports_lpis()) its_init(handle, &gic_data.rdists, gic_data.domain);
gic_smp_init(); gic_dist_init(); gic_cpu_init(); gic_cpu_pm_init();
return 0;
out_free: if (gic_data.domain) irq_domain_remove(gic_data.domain); free_percpu(gic_data.rdists.rdist); return err; }
这里最为重要的是关注函数set_handle_irq(),其设置了GIC控制器中断发生时,中断调用的处理函数,这里调用函数
gic_handle_irq()
此中断处理程序会根据GIC中断控制器状态,获取中断号,然后调用不同的中断处理程序。
static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) { u32 irqnr;
do { irqnr = gic_read_iar();
if (likely(irqnr > 15 && irqnr < 1020) || irqnr >= 8192) { int err;
if (static_key_true(&supports_deactivate)) gic_write_eoir(irqnr);
err = handle_domain_irq(gic_data.domain, irqnr, regs); if (err) { WARN_ONCE(true, "Unexpected interrupt received!\n"); if (static_key_true(&supports_deactivate)) { if (irqnr < 8192) gic_write_dir(irqnr); } else { gic_write_eoir(irqnr); } } continue; }
if (irqnr < 16) { gic_write_eoir(irqnr); if (static_key_true(&supports_deactivate)) gic_write_dir(irqnr); #ifdef CONFIG_SMP /* * Unlike GICv2, we don't need an smp_rmb() here. * The control dependency from gic_read_iar to * the ISB in gic_write_eoir is enough to ensure * that any shared data read by handle_IPI will * be read after the ACK. */ handle_IPI(irqnr, regs); #else WARN_ONCE(true, "Unexpected SGI received!\n"); #endif continue; } } while (irqnr != ICC_IAR1_EL1_SPURIOUS); }