1、介绍

uboot的设备树可以在uboot运行过程中进行一些配置,uboot可以根据配置,选择不同的设备树,这种方式在已经在armLinux kernel中和pc端已经应用了一段时间了。

fdt非常的易用,通过节点和属性进行设备的配置,通过分层的进行设备的管理。当有好几种不同的板子的时候,可以使用同一套源码,仅修改一下设备树就可以完成对板级硬件的匹配。

2、设备树的获取与设置

设备树可以通过kernel源码进行获取,uboot引导kernel启动,kernel中的设备树放到uboot 中也是可以直接运行使用的。

通过以下代码进行配置设备树

#define CONFIG_DEFAULT_DEVICE_TREE “”

设备树通过编译,可以编译至uboot的尾部、也可以嵌入在uboot当中。另外的还可以通过设置fdtcontroladdr环境变量,使设备树加载在某一地址当中,通过这种方式可以通过板级信息进行加载不同的设备树,达到相同代码,不同板子的适配目的。

3、uboot如何获取dtb

[common/board_f.c]
static init_fnc_t init_sequence_f[] = {
···
#ifdef CONFIG_OF_CONTROL
	fdtdec_setup,//获取设备树地址
#endif
···
#ifdef CONFIG_OF_CONTROL
	fdtdec_prepare_fdt,//判断设备树地址是否正确
#endif
···
	reserve_fdt,//为fdt分配内存
···
	reloc_fdt,//重载fdt
···
	NULL,
};

以上是board_init_f当中对于fdt的操作。

int fdtdec_setup(void)
{
#if CONFIG_IS_ENABLED(OF_CONTROL)
# ifdef CONFIG_OF_EMBED
	/* Get a pointer to the FDT */
	gd->fdt_blob = __dtb_dt_begin;//如果使用
# elif defined CONFIG_OF_SEPARATE
#  ifdef CONFIG_SPL_BUILD
	/* FDT is at end of BSS unless it is in a different memory region */
	if (IS_ENABLED(CONFIG_SPL_SEPARATE_BSS))
		gd->fdt_blob = (ulong *)&_image_binary_end;
	else
		gd->fdt_blob = (ulong *)&__bss_end;
#  else
	/* FDT is at end of image */
	gd->fdt_blob = (ulong *)&_end;
#  endif
# elif defined(CONFIG_OF_HOSTFILE)
	if (sandbox_read_fdt_from_file()) {
		puts("Failed to read control FDT\n");
		return -1;
	}
# endif
# ifndef CONFIG_SPL_BUILD
	/* Allow the early environment to override the fdt address */
	gd->fdt_blob = (void *)getenv_ulong("fdtcontroladdr", 16,
						(uintptr_t)gd->fdt_blob);
# endif
#endif
	return fdtdec_prepare_fdt();
}
int fdtdec_prepare_fdt(void)
{
	if (!gd->fdt_blob || ((uintptr_t)gd->fdt_blob & 3) ||
	    fdt_check_header(gd->fdt_blob)) {
	···//如果有错则打印一些信息,主要进行以上的判断
		return -1;
	}
	return 0;//如果无错误,则直接返回0
}

对设备树的地址进行判断,检查设备树的头,确认设备树正确。

4、uboot设备树接口

gd->fdt_blob已经设置成了dtb的地址了。
注意,fdt提供的接口都是以gd->fdt_blob(dtb的地址)为参数的。

以下只简单说明几个接口的功能。
另外,用节点在dtb中的偏移地址来表示一个节点。也就是节点变量node中,存放的是节点的偏移地址

    • lib/fdtdec.c中
      • fdt_path_offset
        int fdt_path_offset(const void *fdt, const char *path)
        eg:node = fdt_path_offset(gd->fdt_blob, “/aliases”);
        功能:获得dtb下某个节点的路径path的偏移。这个偏移就代表了这个节点。
      • fdt_getprop
        const void *fdt_getprop(const void *fdt, int nodeoffset, const char *name, int *lenp)
        eg: mac = fdt_getprop(gd->fdt_blob, node, “mac-address”, &len);
        功能:获得节点node的某个字符串属性值。
      • fdtdec_get_int_array、fdtdec_get_byte_array
        int fdtdec_get_int_array(const void *blob, int node, const char *prop_name, u32 *array, int count)
        eg: ret = fdtdec_get_int_array(blob, node, “interrupts”, cell, ARRAY_SIZE(cell));
        功能:获得节点node的某个整形数组属性值。
      • fdtdec_get_addr
        fdt_addr_t fdtdec_get_addr(const void *blob, int node, const char *prop_name)
        eg:fdtdec_get_addr(blob, node, “reg”);
        功能:获得节点node的地址属性值。
      • fdtdec_get_config_int、fdtdec_get_config_bool、fdtdec_get_config_string
        功能:获得config节点下的整形属性、bool属性、字符串等等。
      • fdtdec_get_chosen_node
        int fdtdec_get_chosen_node(const void *blob, const char *name)
        功能:获得chosen下的name节点的偏移
      • fdtdec_get_chosen_prop
        const char *fdtdec_get_chosen_prop(const void *blob, const char *name)
        功能:获得chosen下name属性的值
    • lib/fdtdec_common.c中
      • fdtdec_get_int
        int fdtdec_get_int(const void *blob, int node, const char *prop_name, int default_val)
        eg: bus->udelay = fdtdec_get_int(blob, node, “i2c-gpio,delay-us”, DEFAULT_UDELAY);
        功能:获得节点node的某个整形属性值。
      • fdtdec_get_uint
        功能:获得节点node的某个无符号整形属性值。

以上的接口配合uboot的设备驱动模型,驱动开发会简单很多,后期再具体分析一个通过设备树开发的驱动。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐