linux设备树

设备树的节点和属性

设备树dts文件由节点和属性构成。每个节点都是一个设备的抽象。

节点的格式

标签: 节点名称@单元地址{
 属性1 = ...
 属性2 = ...
 子节点...
}

标签:为节点提供了一个唯一的标识符。这个标识符用于在设备树的其他节点属性引用本节点。标签不是必须的,如果没有标签,也可以通过“节点名称@单元地址”来引用当前节点。

节点名称:使用字母开头(根节点用\表示)

单元地址:@为分隔符,后面是实际的单元地址,它的值与reg属性的第一个地址一致,若没有reg属性值,则可以省略单元地址

节点属性:节点的大括号{ }中包含的内容是节点属性, 一个节点可以包含多个属性信息,属性包括自定义属性和标准属性。

比如:

/ {
	cpus {
		#address-cells = <1>;
		#size-cells = <0>;

		cpu0: cpu@0 {
			compatible = "arm,cortex-a7";
			device_type = "cpu";
			reg = <0>;
			clock-latency = <61036>; 
			}
		}
	}

标准属性

model属性:用于指定设备的制造商和型号,多个字符串使用“,”分隔开。
compatible属性:由一个或多个字符串组成,可以用来与驱动程序支持的设备列表的compatible字段匹配。
status属性:用于指示设备的操作状态,通过status可以禁用或启用设备。
reg属性:描述设备资源在其父总线定义的地址空间内的地址,通常用于表示一块寄存器的起始地址和长度。
#address-cells 和 #size-cells:这两个属性同时存在,前者决定了子节点reg属性中地址信息所占用的字长,后者决定了长度信息所占的字长。
ranges属性:是一个地址映射/转换表,由子地址、父地址和地址空间长度这三部分组成:
- child-bus-address:子总线地址空间的物理地址, 由父节点的#address-cells 确定此物理地址所占用的字长;
- parent-bus-address:父总线地址空间的物理地址,同样由父节点的#address-cells 确定此物理地址所占用的字长;
- length:子地址空间的长度,由父节点的#size-cells 确定此地址长度所占用的字长;

属性的取值

属性的取值可以是:没有值,字符串,32位数字,引用其他节点

字符串用“”表示;

32位数字和引用其他节点用<>表示;

cpu@0 {
        compatible = "arm,cortex-a7";
        device_type = "cpu";
        reg = <0>;
        memory = <&memory@0 1>;
    };

linux使用设备树

生成platform设备列表

查找machine_desc

每个单板都会定义自己的struct machine_desc结构体,如下所示:

#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

static const char *imx6ul_dt_compat[] __initconst = {
	"fsl,imx6ul",
	"fsl,imx6ull",
	NULL,
};

DT_MACHINE_START(IMX6UL, "Freescale i.MX6 Ultralite (Device Tree)")
	.map_io		= imx6ul_map_io,
	.init_irq	= imx6ul_init_irq,
	.init_machine	= imx6ul_init_machine,
	.init_late	= imx6ul_init_late,
	.dt_compat	= imx6ul_dt_compat,
MACHINE_END

在setup_machine_fdt函数中,会轮询所有定义在.arch.info.init字段的struct machine_desc结构体,将它的dt_compat字段与设备树的根节点的compatible字段作比较,找到最匹配的struct machine_desc结构体。

调用过程:start_kernel -> setup_arch -> setup_machine_fdt

解析dtb

dtb是dts文件编译后生成的,无法直接给内核使用,所以内核需要先解析dtb,生成设备树的树形结构。

unflatten_device_tree函数会解析dtb文件,生成设备树列表,存储在struct device_node *of_root中。

调用过程:start_kernel -> setup_arch -> unflatten_device_tree

void __init unflatten_device_tree(void)
{
	__unflatten_device_tree(initial_boot_params, &of_root,
				early_init_dt_alloc_memory_arch);

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

解析设备树

找到匹配的machine_desc之后,则通过调用machine_desc->init_machine函数来解析设备树。调用时机是在init进程当中调用arch_initcall定义的customize_machine函数,如下所示:

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.
	 */
	of_iommu_init();
	if (machine_desc->init_machine)
		machine_desc->init_machine();  //解析设备树入口
#ifdef CONFIG_OF
	else
		of_platform_populate(NULL, of_default_bus_match_table,
					NULL, NULL);
#endif
	return 0;
}
arch_initcall(customize_machine);

customize_machine的调用过程:kernel_init -> kernel_init_freeable -> do_basic_setup -> do_initcalls

machine_desc->init_machine()会调用到 of_platform_populate函数去解析设备树,如上面定义的函数imx6ul_init_machine:

static void __init imx6ul_init_machine(void)
{
	struct device *parent;

	parent = imx_soc_device_init();
	if (parent == NULL)
		pr_warn("failed to initialize soc device\n");

	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
	imx6ul_enet_init();
	imx_anatop_init();
	imx6ul_pm_init();
}

of_platform_populate函数会轮询of_root的每个节点,注册platform设备,生成设备列表

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("/"); //会查找到全局指针of_root
	if (!root)
		return -EINVAL;

	for_each_child_of_node(root, child) {
		rc = of_platform_bus_create(child, matches, lookup, parent, true); //为每个节点注册platfom设备
		if (rc)
			break;
	}
	of_node_set_flag(root, OF_POPULATED_BUS);

	of_node_put(root);
	return rc;
}

小结

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值