device_node和platform_device的生成流程

1. machine_desc的匹配

这篇》有介绍DT_MACHINE_START的一些初始化操作,匹配上就会在后续的初始化中调用DT_MACHINE_START的成员来初始化系统的设备树,时钟,中断等

复制代码
start_kernel
    setup_arch(&command_line);
		mdesc = setup_machine_fdt(atags_vaddr) //返回成功匹配的machine_desc
        ....
        unflatten_device_tree(); //将设备树解析成device_node

1.1 setup_machine_fdt

early_init_dt_verify校验设备树,和初始化设备树指针;然后of_flat_dt_match_machine匹配MACHINE_START定义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;
}

1.2 early_init_dt_verify

将设备树指针给到initial_boot_params,供后续直接对设备树操作的api使用

复制代码
bool __init early_init_dt_verify(void *params)
{
	if (!params)
		return false;

	/* check device tree validity */
	if (fdt_check_header(params))
		return false;

	/* Setup flat device-tree pointer */
	initial_boot_params = params;
	of_fdt_crc32 = crc32_be(~0, initial_boot_params,
				fdt_totalsize(initial_boot_params));
	return true;
}

1.3 of_flat_dt_match_machine

of_flat_dt_match去匹配设备树跟machine_desc的dt_compat成员

复制代码
const void * __init of_flat_dt_match_machine(const void *default_match,
		const void * (*get_next_compat)(const char * const**))
{
	const void *data = NULL;
	const void *best_data = default_match;
	const char *const *compat;
	unsigned long dt_root;
	unsigned int best_score = ~1, score = 0;

	dt_root = of_get_flat_dt_root();
	while ((data = get_next_compat(&compat))) {
		score = of_flat_dt_match(dt_root, compat);
		if (score > 0 && score < best_score) {
			best_data = data;
			best_score = score;
		}
	}
	if (!best_data) {
		const char *prop;
		int size;

		pr_err("\n unrecognized device tree list:\n[ ");

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

	pr_info("Machine model: %s\n", of_flat_dt_get_machine_name());

	return best_data;
}

1.4 of_flat_dt_match

of_fdt_is_compatible通过设备树指针initial_boot_params找到兼容属性

复制代码
static int __init of_flat_dt_match(unsigned long node, const char *const *compat)
{
	unsigned int tmp, score = 0;

	if (!compat)
		return 0;

	while (*compat) {
		tmp = of_fdt_is_compatible(initial_boot_params, node, *compat);
		if (tmp && (score == 0 || (tmp < score)))
			score = tmp;
		compat++;
	}

	return score;
}

1.5 of_fdt_is_compatible

通过fdt_getprop直接获取compatible 属性,并比较字符串来看匹配上没

复制代码
static int of_fdt_is_compatible(const void *blob,
		      unsigned long node, const char *compat)
{
	const char *cp;
	int cplen;
	unsigned long l, score = 0;

	cp = fdt_getprop(blob, node, "compatible", &cplen);
	if (cp == NULL)
		return 0;
	while (cplen > 0) {
		score++;
		if (of_compat_cmp(cp, compat, strlen(compat)) == 0)
			return score;
		l = strlen(cp) + 1;
		cp += l;
		cplen -= l;
	}

	return 0;
}

2. device_node的生成

通过上面赋值的initial_boot_params,去将设备树解析成device_node结构的树

复制代码
void __init unflatten_device_tree(void)
{
	__unflatten_device_tree(initial_boot_params, NULL, &of_root,
				early_init_dt_alloc_memory_arch, false);

	/* Get pointer to "/chosen" and "/aliases" nodes for use everywhere */
	of_alias_scan(early_init_dt_alloc_memory_arch);

	unittest_unflatten_overlay_base();
}

2.1 unflatten_dt_nodes

通过populate_node填充device_node

复制代码
static int unflatten_dt_nodes(const void *blob,
			      void *mem,
			      struct device_node *dad,
			      struct device_node **nodepp)
{
	struct device_node *root;
	int offset = 0, depth = 0, initial_depth = 0;
#define FDT_MAX_DEPTH	64
	struct device_node *nps[FDT_MAX_DEPTH];
	void *base = mem;
	bool dryrun = !base;

	if (nodepp)
		*nodepp = NULL;

	/*
	 * We're unflattening device sub-tree if @dad is valid. There are
	 * possibly multiple nodes in the first level of depth. We need
	 * set @depth to 1 to make fdt_next_node() happy as it bails
	 * immediately when negative @depth is found. Otherwise, the device
	 * nodes except the first one won't be unflattened successfully.
	 */
	if (dad)
		depth = initial_depth = 1;

	root = dad;
	nps[depth] = dad;

	for (offset = 0;
	     offset >= 0 && depth >= initial_depth;
	     offset = fdt_next_node(blob, offset, &depth)) {
		if (WARN_ON_ONCE(depth >= FDT_MAX_DEPTH))
			continue;

		if (!IS_ENABLED(CONFIG_OF_KOBJ) &&
		    !of_fdt_device_is_available(blob, offset))
			continue;

		if (!populate_node(blob, offset, &mem, nps[depth],
				   &nps[depth+1], dryrun))
			return mem - base;

		if (!dryrun && nodepp && !*nodepp)
			*nodepp = nps[depth+1];
		if (!dryrun && !root)
			root = nps[depth+1];
	}

	if (offset < 0 && offset != -FDT_ERR_NOTFOUND) {
		pr_err("Error %d processing FDT\n", offset);
		return -EINVAL;
	}

	/*
	 * Reverse the child list. Some drivers assumes node order matches .dts
	 * node order
	 */
	if (!dryrun)
		reverse_nodes(root);

	return mem - base;
}

2.2 populate_node

unflatten_dt_alloc分配节点,并通过populate_properties填充各属性

复制代码
static bool populate_node(const void *blob,
			  int offset,
			  void **mem,
			  struct device_node *dad,
			  struct device_node **pnp,
			  bool dryrun)
{
	struct device_node *np;
	const char *pathp;
	unsigned int l, allocl;

	pathp = fdt_get_name(blob, offset, &l);
	if (!pathp) {
		*pnp = NULL;
		return false;
	}

	allocl = ++l;

	np = unflatten_dt_alloc(mem, sizeof(struct device_node) + allocl,
				__alignof__(struct device_node));
	if (!dryrun) {
		char *fn;
		of_node_init(np);
		np->full_name = fn = ((char *)np) + sizeof(*np);

		memcpy(fn, pathp, l);

		if (dad != NULL) {
			np->parent = dad;
			np->sibling = dad->child;
			dad->child = np;
		}
	}

	populate_properties(blob, offset, mem, np, pathp, dryrun);
	if (!dryrun) {
		np->name = of_get_property(np, "name", NULL);
		if (!np->name)
			np->name = "<NULL>";
	}

	*pnp = np;
	return true;
}

3. paltform_device的生成

3.1 init_machine

MACHINE_START中含有一个.dt_compat成员,根据设备树的compatible属性来--锚定具体的machine_desc,;后续也会用init_machine来通过of_platform_populate,构造platfoem_device

复制代码
static void __init xxx_dt_init_machine(void)
{
        /* mmp_entry_vector_init(); */

        of_platform_populate(NULL, of_default_bus_match_table,
                             xxx_auxdata_lookup, NULL);

}

static const char *XXX_dt_board_compat[] __initdata = {
        "id-xxx,id-yyy",
        NULL,
};

DT_MACHINE_START(XXX_DT, "XXX (Device Tree Support)")
        .map_io         = mmp_map_io,
        .init_irq       = irqchip_init,
        .init_time      = xxx_init_time,
        .reserve        = xxx_reserve,
        .init_machine   = xxx_dt_init_machine,
        .dt_compat      = xxx_dt_board_compat,
        .restart        = xxx_arch_restart,
MACHINE_END

3.2 什么时候生成

系统初始化跑完了,在最后会执行kernel_init这个1号进程;去初始化initcall,和其他的一些初始化操作,最后成为用户层的init进程

复制代码
start_kernel
    arch_call_rest_init
        kernel_init
            kernel_init_freeable
                do_basic_setup()
                    do_initcalls()

static int __init customize_machine(void)
{
        /*
         * customizes platform devices, or adds new ones
         * On DT based machines, we fall back to populating the
         * machine from the device tree, if no callback is provided,
         * otherwise we would always need an init_machine callback.
         */
        if (machine_desc->init_machine)
                machine_desc->init_machine();

        return 0;
}
arch_initcall(customize_machine);

3.3 什么节点会被生成

1.一般情况下,只对设备树中根的一级子节点进行转换,也就是多级子节点(子节点的子节点)并不处理。

2.但是存在一种特殊情况,就是当某个根子节点的compatible属性为"simple-bus"、"simple-mfd"、"isa"、"arm,amba-bus"时,当前节点中的一级子节点将会被转换成platform_device节点。

3.节点中必须有compatible属性。

复制代码
const struct of_device_id of_default_bus_match_table[] = {
	{ .compatible = "simple-bus", },
	{ .compatible = "simple-mfd", },
	{ .compatible = "isa", },
#ifdef CONFIG_ARM_AMBA
	{ .compatible = "arm,amba-bus", },
#endif /* CONFIG_ARM_AMBA */
	{} /* Empty terminated list */
};

3.4 生成platfoem_device

终于到了产生platfoem_device地方:

(1)不再直接使用设备树,而是使用前面生成的device_node

(2)matches作为匹配表(of_default_bus_match_table); lookup作为platform_data(xxx_auxdata_lookup)

(3)生成细节见我们的《这篇

复制代码
int of_platform_populate(struct device_node *root,
			const struct of_device_id *matches,
			const struct of_dev_auxdata *lookup,
			struct device *parent)
{
	struct device_node *child;
	int rc = 0;

	root = root ? of_node_get(root) : of_find_node_by_path("/");
	if (!root)
		return -EINVAL;

	pr_debug("%s()\n", __func__);
	pr_debug(" starting at: %pOF\n", root);

	for_each_child_of_node(root, child) {
		rc = of_platform_bus_create(child, matches, lookup, parent, true);
		if (rc) {
			of_node_put(child);
			break;
		}
	}
	of_node_set_flag(root, OF_POPULATED_BUS);

	of_node_put(root);
	return rc;
}
相关推荐
小糖学代码10 小时前
LLM系列:1.python入门:3.布尔型对象
linux·开发语言·python
shizhan_cloud10 小时前
Shell 函数的知识与实践
linux·运维
Deng87234734811 小时前
代码语法检查工具
linux·服务器·windows
霍夫曼13 小时前
UTC时间与本地时间转换问题
java·linux·服务器·前端·javascript
月熊13 小时前
在root无法通过登录界面进去时,通过原本的普通用户qiujian如何把它修改为自己指定的用户名
linux·运维·服务器
大江东去浪淘尽千古风流人物14 小时前
【DSP】向量化操作的误差来源分析及其经典解决方案
linux·运维·人工智能·算法·vr·dsp开发·mr
赖small强15 小时前
【Linux驱动开发】NOR Flash 技术原理与 Linux 系统应用全解析
linux·驱动开发·nor flash·芯片内执行
IT运维爱好者16 小时前
【Linux】LVM理论介绍、实战操作
linux·磁盘扩容·lvm
LEEE@FPGA16 小时前
ZYNQ MPSOC linux hello world
linux·运维·服务器
郝学胜-神的一滴16 小时前
Linux定时器编程:深入理解setitimer函数
linux·服务器·开发语言·c++·程序人生