void __init init_IRQ(void)
{
int ret;
if (IS_ENABLED(CONFIG_OF) && !machine_desc->init_irq)
irqchip_init();
else // init_irq成员定义为imx6ul_init_irq,会走这个分支
machine_desc->init_irq();
if (IS_ENABLED(CONFIG_OF) && IS_ENABLED(CONFIG_CACHE_L2X0) &&
(machine_desc->l2c_aux_mask || machine_desc->l2c_aux_val)) {
if (!outer_cache.write_sec)
outer_cache.write_sec = machine_desc->l2c_write_sec;
ret = l2x0_of_init(machine_desc->l2c_aux_val,
machine_desc->l2c_aux_mask);
if (ret)
pr_err("L2C: failed to init: %d\n", ret);
}
}
看imx6ul_init_irq
static void __init imx6ul_init_irq(void)
{
imx_gpc_check_dt();
imx_init_revision_from_anatop();
imx_src_init();
irqchip_init();
}
看imx_gpc_check_dt
void __init imx_gpc_check_dt(void)
{
struct device_node *np;
// 应该是定义在 imx6ul.dtsi
np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-gpc");
if (WARN_ON(!np))
return;
if (WARN_ON(!of_find_property(np, "interrupt-controller", NULL))) {
pr_warn("Outdated DT detected, suspend/resume will NOT work\n");
/* map GPC, so that at least CPUidle and WARs keep working */
gpc_base = of_iomap(np, 0);
}
}
设备节点如下:
gpc: gpc@020dc000 {
compatible = "fsl,imx6ul-gpc", "fsl,imx6q-gpc";
reg = <0x020dc000 0x4000>;
interrupt-controller;
#interrupt-cells = <3>;
interrupts = <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>;
interrupt-parent = <&intc>;
fsl,mf-mix-wakeup-irq = <0x7c00000 0x7d00 0x0 0x1400640>;
};
看 imx_init_revision_from_anatop
void __init imx_init_revision_from_anatop(void)
{
struct device_node *np;
void __iomem *anatop_base;
unsigned int revision;
u32 digprog;
u16 offset = ANADIG_DIGPROG;
np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-anatop");
anatop_base = of_iomap(np, 0);
WARN_ON(!anatop_base);
if (of_device_is_compatible(np, "fsl,imx6sl-anatop")) // 不会走这个分支
offset = ANADIG_DIGPROG_IMX6SL;
if (of_device_is_compatible(np, "fsl,imx7d-anatop")) // 不会走这个分支
offset = ANADIG_DIGPROG_IMX7D;
根据offset偏移然后去读寄存器的值
digprog = readl_relaxed(anatop_base + offset);
iounmap(anatop_base);
switch (digprog & 0xff) {
case 0:
if (digprog >> 8 & 0x01)
revision = IMX_CHIP_REVISION_2_0;
else
revision = IMX_CHIP_REVISION_1_0;
break;
case 1:
revision = IMX_CHIP_REVISION_1_1;
break;
case 2:
revision = IMX_CHIP_REVISION_1_2;
break;
case 3:
revision = IMX_CHIP_REVISION_1_3;
break;
case 4:
revision = IMX_CHIP_REVISION_1_4;
break;
case 5:
/*
* i.MX6DQ TO1.5 is defined as Rev 1.3 in Data Sheet, marked
* as 'D' in Part Number last character.
*/
revision = IMX_CHIP_REVISION_1_5;
break;
default:
/*
* Fail back to return raw register value instead of 0xff.
* It will be easy know version information in SOC if it
* can't recongized by known version. And some chip like
* i.MX7D soc digprog value match linux version format,
* needn't map again and direct use register value.
*/
revision = digprog & 0xff;
}
mxc_set_cpu_type(digprog >> 16 & 0xff);
imx_set_soc_revision(revision);
}
看 mxc_set_cpu_type
// 判断 cpu类型,赋值给 soc_id 字串
void mxc_set_cpu_type(unsigned int type)
{
__mxc_cpu_type = type;
}
看 mxc_set_arch_type
// 判断arch,似乎没怎么用到
void mxc_set_arch_type(unsigned int type)
{
__mxc_arch_type = type;
}
看 imx_src_init
void __init imx_src_init(void)
{
struct device_node *np;
u32 val;
np = of_find_compatible_node(NULL, NULL, "fsl,imx51-src");
if (!np)
return;
src_base = of_iomap(np, 0);
WARN_ON(!src_base);
//这个if没跑到
if (cpu_is_imx7d()) {
val = readl_relaxed(src_base + SRC_M4RCR);
if (((val & BIT(3)) == BIT(3)) && !(val & BIT(0)))
m4_is_enabled = true;
else
m4_is_enabled = false;
return;
}
// 详见下
imx_reset_controller.of_node = np;
if (IS_ENABLED(CONFIG_RESET_CONTROLLER))
reset_controller_register(&imx_reset_controller);
/*
* force warm reset sources to generate cold reset
* for a more reliable restart
*/
spin_lock(&src_lock);
val = readl_relaxed(src_base + SRC_SCR);
/* bit 4 is m4c_non_sclr_rst on i.MX6SX */
if (cpu_is_imx6sx() && ((val &
(1 << BP_SRC_SCR_SW_OPEN_VG_RST)) == 0))
m4_is_enabled = true;
else
m4_is_enabled = false;
val &= ~(1 << BP_SRC_SCR_WARM_RESET_ENABLE);
writel_relaxed(val, src_base + SRC_SCR);
spin_unlock(&src_lock);
}
compatible 为 “fsl,imx51-src” 对应的节点为:
imx6ul.dtsi
src: src@020d8000 {
compatible = "fsl,imx6ul-src", "fsl,imx51-src";
reg = <0x020d8000 0x4000>;
interrupts = <GIC_SPI 91 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>;
#reset-cells = <1>;
};
看 irqchip_init
void __init irqchip_init(void)
{
of_irq_init(__irqchip_of_table);
// 空函数
acpi_irq_init();
}
看
/**
* of_irq_init - Scan and init matching interrupt controllers in DT
* @matches: 0 terminated array of nodes to match and init function to call
*
* This function scans the device tree for matching interrupt controller nodes,
* and calls their initialization functions in order with parents first.
*/
void __init of_irq_init(const struct of_device_id *matches)
{
struct device_node *np, *parent = NULL;
struct intc_desc *desc, *temp_desc;
struct list_head intc_desc_list, intc_parent_list;
INIT_LIST_HEAD(&intc_desc_list);
INIT_LIST_HEAD(&intc_parent_list);
// 遍历所有的node,寻找定义了interrupt-controller属性的node,如果定义了interrupt-controller属性则说明该node就是一个中断控制器。详细分析见下
for_each_matching_node(np, matches) {
if (!of_find_property(np, "interrupt-controller", NULL) ||
!of_device_is_available(np))
continue;
/*
* Here, we allocate and populate an intc_desc with the node
* pointer, interrupt-parent device_node etc.
*/
desc = kzalloc(sizeof(*desc), GFP_KERNEL);
if (WARN_ON(!desc))
goto err;
// 此处会有两个node符合条件,分别为interrupt-controller和gpc
desc->dev = np;
desc->interrupt_parent = of_irq_find_parent(np);
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) {
const struct of_device_id *match;
int ret;
// typedef int (*of_irq_init_cb_t)(struct device_node *, struct device_node *);
of_irq_init_cb_t irq_init_cb;
if (desc->interrupt_parent != parent)
continue;
list_del(&desc->list);
// 传入一个node设备树节点,让它与给定的一组的matches匹配,返回其中最合适的matches
match = of_match_node(matches, desc->dev);
if (WARN(!match->data,
"of_irq_init: no init function for %s\n",
match->compatible)) {
kfree(desc);
continue;
}
pr_debug("of_irq_init: init %s @ %p, parent %p\n",
match->compatible,
desc->dev, desc->interrupt_parent);
//jl_test 能走到这儿的也只有两个节点,分别为interrupt-controller和gpc
pr_err("irq_test:%s:%d:node_name=%s:compatible=%s\n", __func__, __LINE__, desc->dev->name, match->compatible);
irq_init_cb = (of_irq_init_cb_t)match->data;
// gic_of_init 和 imx_gpc_init 接下来要重点分析这两个函数了,见3
ret = irq_init_cb(desc->dev, desc->interrupt_parent);
if (ret) {
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);
kfree(desc);
}
}
关于reset framework 详见这篇
关于for_each_matching_node,详见这篇