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); //映射GIC 分发器的寄存器地址
WARN(!dist_base, “unable to map gic dist registers\n”);
cpu_base = of_iomap(node, 1); //映射GIC CPU INTERFACE的寄存器地址
WARN(!cpu_base, “unable to map gic cpu registers\n”);
if (of_property_read_u32(node, “cpu-offset”, &percpu_offset))
percpu_offset = 0; //处理cpu-offset属性
//gic_init_bases是GIC的主要处理过程
gic_init_bases(gic_cnt, -1, dist_base, cpu_base, percpu_offset, node);
if (!gic_cnt)
gic_init_physaddr(node);
//处理interrupt级联关系
if (parent) {
irq = irq_of_parse_and_map(node, 0);//解析second GIC的interrupt属性并进行mapping,返回的是irq number
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;
}
void __init gic_init_bases(unsigned int gic_nr, int irq_start,
void __iomem *dist_base, void __iomem *cpu_base,
u32 percpu_offset, struct device_node node)
{
irq_hw_number_t hwirq_base;
struct gic_chip_data gic;
int gic_irqs, irq_base, i;
BUG_ON(gic_nr >= MAX_GIC_NR);
gic = &gic_data[gic_nr];
/
* Initialize the CPU interface map to all CPUs.
* It will be refined as each CPU probes its ID. /
for (i = 0; i < NR_GIC_CPU_IF; i++)
gic_cpu_map[i] = 0xff;
/
* 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_irqs = (gic_irqs + 1) * 32;
if (gic_irqs > 1020)
gic_irqs = 1020;
gic->gic_irqs = gic_irqs;
/gic_irqs保存了GIC支持的最大中断数量,从GICD_TYPER寄存器中的低五位读出,如果读出值为N, 那么最大支持的中断数目是32(N+1)。GIC规定的最大中断数据不超过1020/
if (node) { / DT case /
gic->domain = irq_domain_add_linear(node, gic_irqs,
&gic_irq_domain_hierarchy_ops,
gic);
} else { / Non-DT case /
/
* For primary GICs, skip over SGIs.
* For secondary GICs, skip over PPIs, too. /
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_nr表示GIC_NUMBER, 0表示root GIC,hwirq表示硬件中断id,并不是GIC上的每个中断id都需要映射到Linux 中断系统中的id。例如软件中断SGI,用于cpu之间的通信,没有必要进行 中断ID的映射。hwirq_base 表示GIC上需要进行id map的base,16表示忽略掉16个SGI。对于系统
中的其他GIC,其PPI也没有必要进行mapping,因此hwirq_base=32/
gic_irqs -= hwirq_base; / calculate # of irqs to allocate */
/*从gic_irqs 中减去那些不需要mapping的interrupt ID /
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;
}
/分配中断描述符 如果irq_start大于0,那么说明是指定IRQ number的分配,这种情况,irq_start=-1
不用指定IRQ NUMBER。让其自动搜索,参数2是起始搜索的IRQ NUMBER. gic_irqs则指明了要分配的
irq_number 的数目。/
gic->domain = irq_domain_add_legacy(node, gic_irqs, irq_base,
hwirq_base, &gic_irq_domain_ops, gic);
/向系统中注册一个irq_domain的数据结构,关于irq_domain 后面专门分析/
}
if (WARN_ON(!gic->domain))
return;
if (gic_nr == 0) {
#ifdef CONFIG_SMP
set_smp_cross_call(gic_raise_softirq);
/设定一个多个cpu直接通信的callback函数。当一个cpu core上的软件控制行为需要传递到其他的cpu上时,
就会调用这个函数set_smp_cross_call,对于GIC,这里实际上触发IPI中断/
register_cpu_notifier(&gic_cpu_notifier);
/注册cpu的通知事件,当cpu 状态发生变化时,GIC driver需要接收到这些事件,并对GIC 的
cpu interface进行设定/
#endif
set_handle_irq(gic_handle_irq);
/设定所有中断的处理函数入口/
}
gic_dist_init(gic);
gic_cpu_init(gic);
gic_pm_init(gic);
/
GIC的电源初始化,主要分配两个per cpu的内存。这些内存在系统进行sleep状态时保存PPI的寄存器状态信息,在resume的时候,写回寄存器。对于root GIC,
需要注册一个和电源管理的时间通知事件回掉函数。
*/
}
static void __init gic_dist_init(struct gic_chip_data *gic)
{
unsigned int i;
u32 cpumask;
unsigned int gic_irqs = gic->gic_irqs; //GIC支持的IRQ数量
void __iomem base = gic_data_dist_base(gic); //获取GIC Distributor分发器基地址
writel_relaxed(GICD_DISABLE, base + GIC_DIST_CTRL);
/
GIC_DIST_CTRL寄存器用来控制全局中断向CPU Interface分发,写入0表示不向CPU Interface发送中断请求信号
即为关闭全部中断请求。 /
/
* Set all global interrupts to this CPU only.
*/
cpumask = gic_get_cpumask(gic);
cpumask |= cpumask << 8;
cpumask |= cpumask << 16;
for (i = 32; i < gic_irqs; i += 4)
/设定每个SPI类型的中断都是只送达该CPU/
writel_relaxed(cpumask, base + GIC_DIST_TARGET + i * 4 / 4);
/配置GIC Distributor的其他寄存器/
gic_dist_config(base, gic_irqs, NULL);
writel_relaxed(GICD_ENABLE, base + GIC_DIST_CTRL);
}
static void gic_cpu_init(struct gic_chip_data *gic)
{
void __iomem *dist_base = gic_data_dist_base(gic); //获取 GIC Distrbutor的基地址
void __iomem base = gic_data_cpu_base(gic); //获取 GIC CPU Interface的基地址
unsigned int cpu_mask, cpu = smp_processor_id(); //获取CPU的逻辑ID
int i;
/
* Get what the GIC says our CPU mask is. /
BUG_ON(cpu >= NR_GIC_CPU_IF);
cpu_mask = gic_get_cpumask(gic); //获取cpu_mask
gic_cpu_map[cpu] = cpu_mask;
/
* Clear our mask from the other map entries in case they’re
* still undefined.
*/
for (i = 0; i < NR_GIC_CPU_IF; i++)
if (i != cpu)
gic_cpu_map[i] &= ~cpu_mask;
/设定SGI PPI的初始值/
gic_cpu_config(dist_base, NULL);
//让所有的interrupt interface都可以送达CPU
writel_relaxed(GICC_INT_PRI_THRESHOLD, base + GIC_CPU_PRIMASK);
//设定cpu interface的control register,enable group-0的中断,disable group-1的中断,group 0的interrupt source
//触发irq中断,而非fiq中断
gic_cpu_if_up();
}