4.4.3. ARM64中断初始化
4.4.3.1. init_IRQ
ARM64的中断初始化主要在init_IRQ中完成,包括中断栈的初始化以及中断控制器的初始化
void __init init_IRQ(void)
{
init_irq_stacks(); //中断栈的初始化,中断栈将从vmalloc区域分配,每个CPU的中断栈指针将保存在全局per-cpu变量irq_stack_ptr中
irqchip_init(); //初始化irq控制器,并注册irq_domain
if (!handle_arch_irq)
panic("No interrupt controller found.");
if (system_uses_irq_prio_masking()) {
/*
* Now that we have a stack for our IRQ handler, set
* the PMR/PSR pair to a consistent state.
*/
WARN_ON(read_sysreg(daif) & PSR_A_BIT);
local_daif_restore(DAIF_PROCCTX_NOIRQ);
}
}
void __init irqchip_init(void)
{
//扫描__irqchip_of_table,匹配DTB中定义的中断控制器,匹配成功则调用中断控制器设置的初始化函数
of_irq_init(__irqchip_of_table);
acpi_probe_device_table(irqchip);
}
4.4.3.2. of_irq_init
首先看一下__irqchip_of_table的来历
//include/linux/of.h
#if defined(CONFIG_OF) && !defined(MODULE)
#define _OF_DECLARE(table, name, compat, fn, fn_type) \
static const struct of_device_id __of_table_##name \
__used __section(__##table##_of_table) \
= { .compatible = compat, \
.data = (fn == (fn_type)NULL) ? fn : fn }
#else
#define _OF_DECLARE(table, name, compat, fn, fn_type) \
static const struct of_device_id __of_table_##name \
__attribute__((unused)) \
= { .compatible = compat, \
.data = (fn == (fn_type)NULL) ? fn : fn }
#endif
#define OF_DECLARE_1(table, name, compat, fn) \
_OF_DECLARE(table, name, compat, fn, of_init_fn_1)
#define OF_DECLARE_1_RET(table, name, compat, fn) \
_OF_DECLARE(table, name, compat, fn, of_init_fn_1_ret)
#define OF_DECLARE_2(table, name, compat, fn) \
_OF_DECLARE(table, name, compat, fn, of_init_fn_2)
//include/linux/irqchip.h
#define IRQCHIP_DECLARE(name, compat, fn) OF_DECLARE_2(irqchip, name, compat, fn)
对于每种中断控制器,都会通过IRQCHIP_DECLARE静态声明自己的struct of_device_id 结构体。下面针对GIC-V4进行分析
IRQCHIP_DECLARE(gic_400, "arm,gic-400", gic_of_init);
等价于
static const struct of_device_id __of_table_gic-400 \
__used __section("__irqchip_of_table") \
= { .compatible = "arm,gic-400" , \
.data = gic_of_init }
不同的中断控制器有对应的IRQCHIP_DECLARE声明,他们都位于__irqchip_of_table段,of_irq_init就是遍历__irqchip_of_table段中所有的中断控制器, 对其执行相应的中断控制器初始化
void __init of_irq_init(const struct of_device_id *matches)
{
const struct of_device_id *match;
struct device_node *np, *parent = NULL;
struct of_intc_desc *desc, *temp_desc;
struct list_head intc_desc_list, intc_parent_list;
INIT_LIST_HEAD(&intc_desc_list); //初始化两个list,分别为dts节点中init-controller属性的list和init-controller-parent属性的list
INIT_LIST_HEAD(&intc_parent_list);
for_each_matching_node_and_match(np, matches, &match) { //遍历每个中断控制器为其建立intc_dest,并设置每个中断控制器的parent
if (!of_property_read_bool(np, "interrupt-controller") || //如果当前节点不是interrupt-controller或者处于disabled状态则继续遍历
!of_device_is_available(np))
continue;
if (WARN(!match->data, "of_irq_init: no init function for %s\n",
match->compatible))
continue;
/*
* Here, we allocate and populate an of_intc_desc with the node
* pointer, interrupt-parent device_node etc.
*/
desc = kzalloc(sizeof(*desc), GFP_KERNEL); //为desc分配空间
if (!desc) {
of_node_put(np);
goto err;
}
desc->irq_init_cb = match->data; //中断初始化回调,match->data就是上面的__of_table_gic-400.data
desc->dev = of_node_get(np);
desc->interrupt_parent = of_irq_find_parent(np); //解析当前节点的parent
if (desc->interrupt_parent == np)
desc->interrupt_parent = NULL;
list_add_tail(&desc->list, &intc_desc_list);
}
/*
* The root irq controller is the one without an interrupt-parent.
* That one goes first, followed by the controllers that reference it,
* followed by the ones that reference the 2nd level controllers, etc.
*/
while (!list_empty(&intc_desc_list)) {
/*
* Process all controllers with the current 'parent'.
* First pass will be looking for NULL as the parent.
* The assumption is that NULL parent means a root controller.
*/
list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {
int ret;
if (desc->interrupt_parent != parent)
continue;
list_del(&desc->list);
of_node_set_flag(desc->dev, OF_POPULATED);
pr_debug("of_irq_init: init %pOF (%p), parent %p\n",
desc->dev,
desc->dev, desc->interrupt_parent);
//执行中断初始化函数
ret = desc->irq_init_cb(desc->dev,
desc->interrupt_parent);
if (ret) {
of_node_clear_flag(desc->dev, OF_POPULATED);
kfree(desc);
continue;
}
/*
* This one is now set up; add it to the parent list so
* its children can get processed in a subsequent pass.
*/
list_add_tail(&desc->list, &intc_parent_list);
}
/* Get the next pending parent that might have children */
desc = list_first_entry_or_null(&intc_parent_list,
typeof(*desc), list);
if (!desc) {
pr_err("of_irq_init: children remain, but no parents\n");
break;
}
list_del(&desc->list);
parent = desc->dev;
kfree(desc);
}
list_for_each_entry_safe(desc, temp_desc, &intc_parent_list, list) {
list_del(&desc->list);
kfree(desc);
}
err:
list_for_each_entry_safe(desc, temp_desc, &intc_desc_list, list) {
list_del(&desc->list);
of_node_put(desc->dev);
kfree(desc);
}
}
4.4.3.3. gic_of_init
//drivers/irqchip/irq-gic.c
int __init
gic_of_init(struct device_node *node, struct device_node *parent)
{
struct gic_chip_data *gic;
int irq, ret;
if (WARN_ON(!node))
return -ENODEV;
if (WARN_ON(gic_cnt >= CONFIG_ARM_GIC_MAX_NR))
return -EINVAL;
gic = &gic_data[gic_cnt];
ret = gic_of_setup(gic, node); //根据device node初始化gic
if (ret)
return ret;
/*
* Disable split EOI/Deactivate if either HYP is not available
* or the CPU interface is too small.
*/
if (gic_cnt == 0 && !gic_check_eoimode(node, &gic->raw_cpu_base))
static_branch_disable(&supports_deactivate_key);
ret = __gic_init_bases(gic, &node->fwnode); //此函数中会设置中断处理的主函数
if (ret) {
gic_teardown(gic);
return ret;
}
if (!gic_cnt) {
gic_init_physaddr(node);
gic_of_setup_kvm_info(node);
}
if (parent) {
irq = irq_of_parse_and_map(node, 0);
gic_cascade_irq(gic_cnt, irq);
}
if (IS_ENABLED(CONFIG_ARM_GIC_V2M))
gicv2m_init(&node->fwnode, gic_data[gic_cnt].domain);
gic_cnt++;
return 0;
}
static int __init __gic_init_bases(struct gic_chip_data *gic,
struct fwnode_handle *handle)
{
char *name;
int i, ret;
if (WARN_ON(!gic || gic->domain))
return -EINVAL;
if (gic == &gic_data[0]) {
/*
* Initialize the CPU interface map to all CPUs.
* It will be refined as each CPU probes its ID.
* This is only necessary for the primary GIC.
*/
for (i = 0; i < NR_GIC_CPU_IF; i++)
gic_cpu_map[i] = 0xff;
#ifdef CONFIG_SMP
set_smp_cross_call(gic_raise_softirq);
#endif
cpuhp_setup_state_nocalls(CPUHP_AP_IRQ_GIC_STARTING,
"irqchip/arm/gic:starting",
gic_starting_cpu, NULL);
//设置中断顶层处理函数handle_arch_irq为gic_handle_irq,此为中断处理的主函数
set_handle_irq(gic_handle_irq);
if (static_branch_likely(&supports_deactivate_key))
pr_info("GIC: Using split EOI/Deactivate mode\n");
}
if (static_branch_likely(&supports_deactivate_key) && gic == &gic_data[0]) {
name = kasprintf(GFP_KERNEL, "GICv2");
gic_init_chip(gic, NULL, name, true);
} else {
name = kasprintf(GFP_KERNEL, "GIC-%d", (int)(gic-&gic_data[0]));
gic_init_chip(gic, NULL, name, false); //进一步初始化gic_chip
}
ret = gic_init_bases(gic, handle); //中断控制器初始化
if (ret)
kfree(name);
return ret;
}