内核启动时的中断和时钟的初始化流程

总结

内核通过start_kernel从汇编进入c世界,在用serup_arch设置体系架构的时候,会返回当前机器的machine_desc;然后init_IRQ和time_init来初始化中断。时钟子系统

start_kernel
    setup_arch(&command_line);
		mdesc = setup_machine_fdt(atags_vaddr) //返回成功匹配的machine_desc
		machine_desc = mdesc; //赋值给全局变量machine_desc,以供后续初始化等操作使用,比如给下面的中断和时钟子系统使用

    ......
    init_IRQ
		machine_desc->init_irq()
			irqchip_init
				of_irq_init(__irqchip_of_table) //IRQCHIP_DECLARE(gic_v3, "arm,gic-v3", gic_of_init)
    ......
    time_init
        if (machine_desc->init_time)
            machine_desc->init_time 
				of_clk_init(NULL);
				timer_probe();
        else
            of_clk_init(NULL);
                for_each_matching_node_and_match(np, matches, &match) // CLK_OF_DECLARE(asr1803_clk, "asr,asr1803-clock", asr1803_clk_init);由晶振+pll给cpu核心和外设产生时钟
            timer_probe();
                for_each_matching_node_and_match(np, __timer_of_table, &match) //TIMER_OF_DECLARE(mmp_timer, "mrvl,mmp-timer", mmp_dt_init_timer);定时器外设

setup_arch

setup_machine_fdt

of_flat_dt_match_machine去匹配设备树,和DT_MACHINE_START定义的machine_desc中的兼容属性,并返回这个machine_desc

const struct machine_desc * __init setup_machine_fdt(void *dt_virt)
{
	const struct machine_desc *mdesc, *mdesc_best = NULL;

#if defined(CONFIG_ARCH_MULTIPLATFORM) || defined(CONFIG_ARM_SINGLE_ARMV7M)
	DT_MACHINE_START(GENERIC_DT, "Generic DT based system")
		.l2c_aux_val = 0x0,
		.l2c_aux_mask = ~0x0,
	MACHINE_END

	mdesc_best = &__mach_desc_GENERIC_DT;
#endif

	if (!dt_virt || !early_init_dt_verify(dt_virt))
		return NULL;

	mdesc = of_flat_dt_match_machine(mdesc_best, arch_get_next_mach);

	if (!mdesc) {
		const char *prop;
		int size;
		unsigned long dt_root;

		early_print("\nError: unrecognized/unsupported "
			    "device tree compatible list:\n[ ");

		dt_root = of_get_flat_dt_root();
		prop = of_get_flat_dt_prop(dt_root, "compatible", &size);
		while (size > 0) {
			early_print("'%s' ", prop);
			size -= strlen(prop) + 1;
			prop += strlen(prop) + 1;
		}
		early_print("]\n\n");

		dump_machine_table(); /* does not return */
	}

	/* We really don't want to do this, but sometimes firmware provides buggy data */
	if (mdesc->dt_fixup)
		mdesc->dt_fixup();

	early_init_dt_scan_nodes();

	/* Change machine number to match the mdesc we're using */
	__machine_arch_type = mdesc->nr;

	return mdesc;
}

arch_get_next_mach

__arch_info_begin就是在链接脚本vmlinux.lds.S定义,在这个里面找打一个个machine_desc,并返回当前的machine_desc的dt_compat成员,来匹配设备树

static const void * __init arch_get_next_mach(const char *const **match)
{
	static const struct machine_desc *mdesc = __arch_info_begin;
	const struct machine_desc *m = mdesc;

	if (m >= __arch_info_end)
		return NULL;

	mdesc++;
	*match = m->dt_compat;
	return m;
}

//arch/arm/kernel/vmlinux.lds.S

.init.arch.info : {
		__arch_info_begin = .;
		*(.arch.info.init)
		__arch_info_end = .;
	}
......
ASSERT((__proc_info_end - __proc_info_begin), "missing CPU support")
ASSERT((__arch_info_end - __arch_info_begin), "no machine record defined")

DT_MACHINE_START

arch/arm/include/asm/mach/arch.h的DT_MACHINE_START将machine_desc放进__arch_info_begin -- __arch_info_end的段空间中

extern const struct machine_desc __arch_info_begin[], __arch_info_end[];
#define for_each_machine_desc(p)			\
	for (p = __arch_info_begin; p < __arch_info_end; p++)

/*
 * Set of macros to define architecture features.  This is built into
 * a table by the linker.
 */
#define MACHINE_START(_type,_name)			\
static const struct machine_desc __mach_desc_##_type	\
 __used							\
 __attribute__((__section__(".arch.info.init"))) = {	\
	.nr		= MACH_TYPE_##_type,		\
	.name		= _name,

#define MACHINE_END				\
};

#define DT_MACHINE_START(_name, _namestr)		\
static const struct machine_desc __mach_desc_##_name	\
 __used							\
 __attribute__((__section__(".arch.info.init"))) = {	\
	.nr		= ~0,				\
	.name		= _namestr,

#endif

dt_compat

ARM Linux 3.x在引入设备树之后,MACHINE_START变更为DT_MACHINE_START,其中含有一个.dt_compat成员,用于表明相关的设备与.dts中根节点的兼容属性兼容关系

/ {
        model = "ASR 1828(C260SA) CPE";
        compatible = "asr,1803-evb", "asr,1803";
		.......
}

static const char *asr1803_dt_board_compat[] __initdata = {
        "asr,1803-evb",
        NULL,
};
DT_MACHINE_START(ASR1803_DT, "ASR ASR1803 (Device Tree Support)")
        .map_io         = mmp_map_io,
        .init_irq       = irqchip_init,
        .init_time      = mmp_init_time,
        .reserve        = asr1803_reserve,
        .init_machine   = asr1803_dt_init_machine,
        .dt_compat      = asr1803_dt_board_compat,
        .restart        = mmp_arch_restart,
MACHINE_END

init_IRQ

init_IRQ会调用machine_desc->init_irq,根据上面DT_MACHINE_START的定义,也即irqchip_init;中断控制器驱动会定义IRQCHIP_DECLARE(gic_v3, "arm,gic-v3", gicv3_of_init);经过_OF_DECLARE展开,就会产生__irqchip_of_table;因为还没初始化deivce-bus-driver这套去走match/probe自动回调驱动的probe,又要兼容设备树,只能前期去主动匹配后,调用初始化函数

#define _OF_DECLARE(table, name, compat, fn, fn_type)			\
	static const struct of_device_id __of_table_##name		\
		__used __section(__##table##_of_table)			\
		__aligned(__alignof__(struct of_device_id))		\
		 = { .compatible = compat,				\
		     .data = (fn == (fn_type)NULL) ? fn : fn  }


void __init irqchip_init(void)
{
        of_irq_init(__irqchip_of_table);
        acpi_probe_device_table(irqchip);
}

然后通过for_each_matching_node_and_match 来解析设备树节点,并在后续主动调用.data,也即gicv3_of_init。主要代码为desc->irq_init_cb = match->data; desc->irq_init_cb(desc->dev,desc->interrupt_parent);

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);
	INIT_LIST_HEAD(&intc_parent_list);

	for_each_matching_node_and_match(np, matches, &match) {
		if (!of_property_read_bool(np, "interrupt-controller") ||
				!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);
		if (!desc) {
			of_node_put(np);
			goto err;
		}

		desc->irq_init_cb = match->data;
		desc->dev = of_node_get(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) {
			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);
	}
}

time_init

time_init会调用machine_desc->init_time,根据上面DT_MACHINE_START的定义,也即mmp_init_time;主要使用of_clk_init和timer_probe初始化时钟

static void __init mmp_init_time(void)
{
        enable_pxawdt_clock();

        /* Reset and Enable the Timer set0, Select the timer set's fast clock source as 3.25MHz */
        /* The actual clock value for each timer in the timer set are decided by the TMR_CCR */
        /* The MIPSRAM actually uses the 2nd timer of timer set0 to count LPM periods*/
        __raw_writel(APBC_APBCLK | APBC_RST, APBC_MMPX_TIMER0);
        __raw_writel(APBC_APBCLK | APBC_FNCLK | APBC_FNCLKSEL(3),
                        APBC_MMPX_TIMER0);

#if 0
        /* Reset and Enable the Timer set0, Select the timer set's fast clock source as 3.25MHz */
        /* MIPSRAM uses this timer to time LPM periods */
        __raw_writel(APBC_APBCLK | APBC_RST, APBC_MMPX_TIMER1);
        __raw_writel(APBC_APBCLK | APBC_FNCLK | APBC_FNCLKSEL(3),
                                APBC_MMPX_TIMER1);
#else
        /*
        * the CP timer(used as cp watchdog timer) is not routed to AP, AP uses
        * cp timer as timer tick, cp uses APB Timer1(d4016000)
        * cp timer is fixed at 13MHZ, here to enable and reset the core timer
        */
        __raw_writel(APBC_APBCLK | APBC_FNCLK | APBC_RST, APBCP_TICER);
        __raw_writel(APBC_APBCLK | APBC_FNCLK, APBCP_TICER);
#endif

        of_clk_init(NULL);
        timer_probe();
        coresight_debug_init();
}

of_clk_init

CLK_OF_DECLARE(asr1803_clk, "asr,asr1803-clock", asr1803_clk_init);经过_OF_DECLARE展开,就会产生__clk_of_table;然后通过for_each_matching_node_and_match 来解析设备树节点,并在后续主动调用.data,也即asr1803_clk_init;它由晶振+pll给cpu核心和外设产生时钟;主要代码为:

matches = &__clk_of_table; (匹配)

parent->clk_init_cb = match->data; (初始化回调函数)

clk_provider->clk_init_cb(clk_provider->np); (执行回调)

void __init of_clk_init(const struct of_device_id *matches)
{
	const struct of_device_id *match;
	struct device_node *np;
	struct clock_provider *clk_provider, *next;
	bool is_init_done;
	bool force = false;
	LIST_HEAD(clk_provider_list);

	if (!matches)
		matches = &__clk_of_table;

	/* First prepare the list of the clocks providers */
	for_each_matching_node_and_match(np, matches, &match) {
		struct clock_provider *parent;

		if (!of_device_is_available(np))
			continue;

		parent = kzalloc(sizeof(*parent), GFP_KERNEL);
		if (!parent) {
			list_for_each_entry_safe(clk_provider, next,
						 &clk_provider_list, node) {
				list_del(&clk_provider->node);
				of_node_put(clk_provider->np);
				kfree(clk_provider);
			}
			of_node_put(np);
			return;
		}

		parent->clk_init_cb = match->data;
		parent->np = of_node_get(np);
		list_add_tail(&parent->node, &clk_provider_list);
	}

	while (!list_empty(&clk_provider_list)) {
		is_init_done = false;
		list_for_each_entry_safe(clk_provider, next,
					&clk_provider_list, node) {
			if (force || parent_ready(clk_provider->np)) {

				/* Don't populate platform devices */
				of_node_set_flag(clk_provider->np,
						 OF_POPULATED);

				clk_provider->clk_init_cb(clk_provider->np);
				of_clk_set_defaults(clk_provider->np, true);

				list_del(&clk_provider->node);
				of_node_put(clk_provider->np);
				kfree(clk_provider);
				is_init_done = true;
			}
		}

		/*
		 * We didn't manage to initialize any of the
		 * remaining providers during the last loop, so now we
		 * initialize all the remaining ones unconditionally
		 * in case the clock parent was not mandatory
		 */
		if (!is_init_done)
			force = true;
	}
}

timer_probe

TIMER_OF_DECLARE(mmp_timer, "mrvl,mmp-timer", mmp_dt_init_timer); 经过_OF_DECLARE展开,就会产生__timer_of_table;然后通过for_each_matching_node_and_match 来解析设备树节点,并在后续主动调用.data,也即mmp_dt_init_timer;主要代码为:

for_each_matching_node_and_match(np, __timer_of_table, &match); (匹配)

init_func_ret = match->data; (初始化回调函数)

init_func_ret(np); (执行回调)

extern struct of_device_id __timer_of_table[];

static const struct of_device_id __timer_of_table_sentinel
	__used __section(__timer_of_table_end);

void __init timer_probe(void)
{
	struct device_node *np;
	const struct of_device_id *match;
	of_init_fn_1_ret init_func_ret;
	unsigned timers = 0;
	int ret;

	for_each_matching_node_and_match(np, __timer_of_table, &match) {
		if (!of_device_is_available(np))
			continue;

		init_func_ret = match->data;

		ret = init_func_ret(np);
		if (ret) {
			if (ret != -EPROBE_DEFER)
				pr_err("Failed to initialize '%pOF': %d\n", np,
				       ret);
			continue;
		}

		timers++;
	}

	timers += acpi_probe_device_table(timer);

	if (!timers)
		pr_crit("%s: no matching timers found\n", __func__);
}

  • 7
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
东南亚位于我国倡导推进的“一带一路”海陆交汇地带,作为当今全球发展最为迅速的地区之一,近年来区域内生产总值实现了显著且稳定的增长。根据东盟主要经济体公布的最新数据,印度尼西亚2023年国内生产总值(GDP)增长5.05%;越南2023年经济增长5.05%;马来西亚2023年经济增速为3.7%;泰国2023年经济增长1.9%;新加坡2023年经济增长1.1%;柬埔寨2023年经济增速预计为5.6%。 东盟国家在“一带一路”沿线国家中的总体GDP经济规模、贸易总额与国外直接投资均为最大,因此有着举足轻重的地位和作用。当前,东盟与中国已互相成为双方最大的交易伙伴。中国-东盟贸易总额已从2013年的443亿元增长至 2023年合计超逾6.4万亿元,占中国外贸总值的15.4%。在过去20余年中,东盟国家不断在全球多变的格局里面临挑战并寻求机遇。2023东盟国家主要经济体受到国内消费、国外投资、货币政策、旅游业复苏、和大宗商品出口价企稳等方面的提振,经济显现出稳步增长态势和强韧性的潜能。 本调研报告旨在深度挖掘东南亚市场的增长潜力与发展机会,分析东南亚市场竞争态势、销售模式、客户偏好、整体市场营商环境,为国内企业出海开展业务提供客观参考意见。 本文核心内容: 市场空间:全球行业市场空间、东南亚市场发展空间。 竞争态势:全球份额,东南亚市场企业份额。 销售模式:东南亚市场销售模式、本地代理商 客户情况:东南亚本地客户及偏好分析 营商环境:东南亚营商环境分析 本文纳入的企业包括国外及印尼本土企业,以及相关上下游企业等,部分名单 QYResearch是全球知名的大型咨询公司,行业涵盖各高科技行业产业链细分市场,横跨如半导体产业链(半导体设备及零部件、半导体材料、集成电路、制造、封测、分立器件、传感器、光电器件)、光伏产业链(设备、硅料/硅片、电池片、组件、辅料支架、逆变器、电站终端)、新能源汽车产业链(动力电池及材料、电驱电控、汽车半导体/电子、整车、充电桩)、通信产业链(通信系统设备、终端设备、电子元器件、射频前端、光模块、4G/5G/6G、宽带、IoT、数字经济、AI)、先进材料产业链(金属材料、高分子材料、陶瓷材料、纳米材料等)、机械制造产业链(数控机床、工程机械、电气机械、3C自动化、工业机器人、激光、工控、无人机)、食品药品、医疗器械、农业等。邮箱:market@qyresearch.com

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值