1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
| sysirq: interrupt-controller@10200220 { compatible = "mediatek,mt6592-sysirq", "mediatek,mt6577-sysirq"; interrupt-controller; #interrupt-cells = <3>; interrupt-parent = <&gic>; reg = <0x10200220 0x1c>; };
gic: interrupt-controller@10211000 { compatible = "arm,cortex-a7-gic"; interrupt-controller; #interrupt-cells = <3>; interrupt-parent = <&gic>; reg = <0x10211000 0x1000>, <0x10212000 0x1000>; }; ```C Linux操作系统通过加载DTS将GIC的硬件信息装载到特定内存位置,GIC的驱动程序运行时通过DTS的API读取到这些硬件信息(例如寄存器地址)来控制中断的处理。
物理中断号的映射 --------
GIC驱动程序初始化时,会向系统申请中断描述符。中断描述符是全局变量,外设驱动request_irq传入的第一个参数便是中断描述符的索引。外设根据DTS中对应的物理中断号和其所在的中断Domain,便可以得到外设的虚拟中断id(即中断描述符的索引) ```C static int __init 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");
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;
__gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset, &node->fwnode); } 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) {
gic_irqs = readl_relaxed(gic_data_dist_base(gic) + GIC_DIST_CTR) & 0x1f; gic_irqs = (gic_irqs + 1) * 32; if (gic_irqs > 1020) gic_irqs = 1020; gic->gic_irqs = gic_irqs;
if (handle) { gic->domain = irq_domain_create_linear(handle, gic_irqs, &gic_irq_domain_hierarchy_ops, gic); } else {
if (gic_nr == 0 && (irq_start & 31) > 0) { hwirq_base = 16; if (irq_start != -1) irq_start = (irq_start & ~31) + 16; } else { hwirq_base = 32; }
gic_irqs -= hwirq_base;
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); }
}
|