本文记录了在 RK3566 平台上,U-Boot 中设备树的加载与使用过程,并对设备模型(Device Model, DM)的核心机制进行了分析。文章主要包括以下内容:
U-Boot 中设备树的加载
U-Boot DM 模型的创建机制及其核心实现
RK3566 平台上 DM 模型的具体构造流程
dm 模型数据结构
常见的接口说明
一、设备树加载使用
dts 即 Device Tree Source 设备树源码, Device Tree 用来描述 soc 的硬件信息. 更多请参考 Linux设备树 - DTS语法、节点、设备树解析等 本文主要描述 rk3566 中的加载使用流程. 当前主流的 dm 模型树, 都是由设备树构建出来的.
1、概述
rk3566 中对 dtb 的组成分为两个部分 uboot 和 kernel . 并不是单纯的只用 uboot 和只用 kernel , 两者都用到了. 他们在内存中的分布如下. 下图描述的是 RK3566 的 dtb img 位置.
rk3566 uboot 中设备树的加载有两个阶段 , 首先使用 uboot arch/arm/dts
中的设备树, 之后再加载使用 kernel dtb 中的设备树.
2、第一阶段
ruboot 的 dtb 由两个宏决定.编译请参考: 【u-boot】u-boot对设备树的节点解析 , 上图的 rk3566 uboot 镜像对应的配置如下.
1 2 3 4 5 6 7 8 9 10 11 12 13 CONFIG_OF_CONTROL=y CONFIG_OF_EMBED is not set CONFIG_OF_SEPARATE=y CONFIG_USING_KERNEL_DTB=y
在 common/board_f.c
中定义了 fdtdec_setup 函数用来解析 dts.
1 2 3 4 5 6 7 static const init_fnc_t init_sequence_f[] = { setup_mon_len, #ifdef CONFIG_OF_CONTROL fdtdec_setup, #endif ... };
1) fdtdec_setup
当使用 CONFIG_OF_EMBED 的方式时, dtb 被打包成到 uboo.bin 文件中, 通过 __dtb_dt_begin
符号来获取 dtb 地址
没有定义 CONFIG_OF_EMBED 且定义了 CONFIG_OF_SEPARATE u-boot.dtb 和 u-boot.bin 分离. u-boot.dtb 放在 u-boot.bin 后面, 通过 _end
符号来获取 dtb 地址.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 int fdtdec_setup (void ) {...... # ifdef CONFIG_OF_EMBED # ifdef CONFIG_SPL_BUILD gd->fdt_blob = __dtb_dt_spl_begin; # else gd->fdt_blob = __dtb_dt_begin; # endif # elif defined CONFIG_OF_SEPARATE # ifdef CONFIG_SPL_BUILD if (IS_ENABLED (CONFIG_SPL_SEPARATE_BSS)) gd->fdt_blob = (ulong *)&_image_binary_end; else gd->fdt_blob = (ulong *)&__bss_end; # else gd->fdt_blob = (ulong *)&_end; ...... return fdtdec_prepare_fdt (); }
2) 总结
rk 3566 中 u-boot.dtb 和 u-boot.bin 分离. u-boot.dtb 放在 u-boot.bin 后面 , 通过 _end
符号来获取 dtb 地址 . 设备树被保存进 gd->fdt_blob
中.
3、第二阶段
1) kernle dtb 编译打包
对应的文件 kernel/scripts/mkmultidtb.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 def main (): if (len (sys.argv) < 2 ) or (sys.argv[1 ] == '-h' ): print __doc__ sys.exit(2 ) BOARD = sys.argv[1 ] TARGET_DTBS = DTBS[BOARD] target_dtb_list = '' default_dtb = True for dtb, value in TARGET_DTBS.items(): if default_dtb: ori_file = 'arch/arm64/boot/dts/rockchip/' + dtb + '.dtb' shutil.copyfile(ori_file, "rk-kernel.dtb" ) target_dtb_list += 'rk-kernel.dtb ' default_dtb = False new_file = dtb + value + '.dtb' ori_file = 'arch/arm64/boot/dts/rockchip/' + dtb + '.dtb' shutil.copyfile(ori_file, new_file) target_dtb_list += ' ' + new_file print target_dtb_list os.system('scripts/resource_tool logo.bmp logo_kernel.bmp logo720.bmp logo_kernel720.bmp' + target_dtb_list) os.system('rm ' + target_dtb_list) if __name__ == '__main__' : main()
打包 arch/arm64/boot/dts/rockchip/
目录下的 dtb 文件为一个新文件并命名为 rk-kernel.dtb
将 rk-kernel.dtb 和 logo.bmp 等文件打包进 resource.img .
删掉生成的 rk-kernel.dtb .
2) 加载流程
如果定义了 CONFIG_USING_KERNEL_DTB 才会调用 init_kernel_dtb()
函数
从环境变量获取 fdt_addr. 它由 ENV_MEM_LAYOUT_SETTINGS 指定. 这里指定为 0x0a100000
1 2 3 4 5 6 7 8 9 #define ENV_MEM_LAYOUT_SETTINGS \ "scriptaddr=0x00c00000\0" \ "pxefile_addr_r=0x00e00000\0" \ "fdt_addr_r=0x0a100000\0" \ "kernel_addr_no_low_bl32_r=0x00280000\0" \ "kernel_addr_r=0x00a80000\0" \ "kernel_addr_c=0x04080000\0" \ "ramdisk_addr_r=0x0a200000\0"
调用 rockchip_read_dtb_file((void *)fdt_addr);
, 在 resource.img 中搜索 rk-kernel.dtb , 找到之后把它加载到 ftd_addr .
更新 gd->fdt_blob = (void *)fdt_addr;
即指定 dtb 为加载的 rk-kernel.dtb
调用链如下所示.
1 2 3 4 5 6 7 8 9 10 board_init () --> init_kernel_dtb () --> rockchip_read_dtb_file ((void *)fdt_addr); --> rockchip_read_resource_dtb (fdt_addr, &hash, &hash_size); --> file = get_file_info (DEFAULT_DTB_FILE); --> rockchip_read_resource_file (fdt_addr, file->name, 0 , 0 ); dtb_okay: gd->fdt_blob = (void *)fdt_addr; gd->flags |= GD_FLG_KDTB_READY; dm_scan_fdt ((void *)gd->fdt_blob, false );
2.1) board_init
如果定义了 CONFIG_USING_KERNEL_DTB 才会调用 init_kernel_dtb()
函数
1 2 3 4 5 6 7 8 9 10 11 int board_init (void ) {...... #ifdef CONFIG_USING_KERNEL_DTB #ifdef CONFIG_MTD_BLK board_mtd_blk_map_partitions (); #endif init_kernel_dtb (); #endif ...... }
2.2) init_kernel_dtb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 int init_kernel_dtb (void ) { ulong fdt_addr = 0 ; void *ufdt_blob; int ret = -ENODEV; if (gd->ram_size <= SZ_128M) fdt_addr = env_get_ulong ("fdt_addr1_r" , 16 , 0 ); if (!fdt_addr) fdt_addr = env_get_ulong ("fdt_addr_r" , 16 , 0 ); ...... ret = rockchip_read_dtb_file ((void *)fdt_addr); if (!ret) { if (!dtb_check_ok ((void *)fdt_addr, (void *)gd->fdt_blob)) { ret = -EINVAL; printf ("Kernel dtb mismatch this platform!\n" ); } else { goto dtb_okay; } } ...... dtb_okay: ufdt_blob = (void *)gd->fdt_blob; gd->fdt_blob = (void *)fdt_addr; ...... return 0 ; }
2.3) rockchip_read_dtb_file
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 int rockchip_read_dtb_file (void *fdt_addr) { int hash_size = 0 ; int ret = -1 ; u32 fdt_size; char *hash; resource_traverse_init_list (); ...... ret = rockchip_read_resource_dtb (fdt_addr, &hash, &hash_size); if (ret) { printf ("Failed to load DTB, ret=%d\n" , ret); return ret; } if (fdt_check_header (fdt_addr)) { printf ("Invalid DTB magic !\n" ); return -EBADF; } fdt_size = fdt_totalsize (fdt_addr); ...... return 0 ; }
2.4) rockchip_read_resource_dtb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 #define DEFAULT_DTB_FILE "rk-kernel.dtb" int rockchip_read_resource_dtb (void *fdt_addr, char **hash, int *hash_size) { struct resource_file *file = NULL ; int ret; #ifdef CONFIG_ROCKCHIP_HWID_DTB file = resource_read_hwid_dtb (); #endif if (!file) file = get_file_info (DEFAULT_DTB_FILE); if (!file) return -ENODEV; ret = rockchip_read_resource_file (fdt_addr, file->name, 0 , 0 ); if (ret < 0 ) return ret; if (fdt_check_header (fdt_addr)) return -EBADF; *hash = file->hash; *hash_size = file->hash_size; printf ("DTB: %s\n" , file->name); return 0 ; }
参考: 瑞芯微RK3399设备树传递分析
3) 总结
rk3566 从环境变量 fdt_addr_r
获取 fdt_addr
的地址 0x0a100000. 然后在 resource.img
中搜索 rk-kernel.dtb
, 找到之后把它加载到 ftd_addr
. 最后更新 gd->fdt_blob = (void *)fdt_addr
; 即指定 dtb 为加载的 rk-kernel.dtb
.
二、dm 模型
md(driver model) 驱动模型, 就是为驱动定义一个统一的访问接口, 提高代码的管理和使用效率.本质是以树状的形式组织各个设备驱动 . 每一个模块就是一个树枝 . 如下图所示. 该图展示了 dm 模型的树形结构. 它由树根 gd->md_root 向下延伸, 按照 dts 中的树状结构形式组织各个设备驱动模块(树枝). 它和 dts 中的树状结构是完全对应的.
rk3566 的 U-Boot 的 dm 树构建分三次构筑 , 前两次使用 uboot dts 构筑. 最后一次使用 kernel 的 dtb 进行构筑.
第一次构筑在 initf_dm(void) 中主要通过调用 dm_scan_fdt()
初始化配置了 u-boot,dm-pre-reloc;
等属性节点的外设.
第二次构筑在 initr_dm(void) 中主要通过调用 dm_scan_fdt()
重新创建一颗 dm 树, 再解析一遍带有 u-boot,dm-pre-reloc;
属性的设备节点的外设.
第三次构筑在 board_init(void) 中调用 dm_scan_fdt()
首先删掉 uboot 中带有 u-boot,dm-pre-reloc;
的设备节点, 之后使用 kernel dtb 初始化所有 okay 节点.
整个 uboot 中有两棵这样的树 , 第一棵树在第一次创建它被保存在gd->dm_root_f
, 第二棵树在第二次创建, 在第三次对这颗树进行补充, 它被保存在 gd->dm_root
.
1、树的创建
dm 模型有两种创建方式, 一种是通过 driver_info 来创建, 需要注意的是 driver_info 描述的是 udevice 而非 driver , 这种方式不需要设备树直接创建. 常用的方式就是宏 U_BOOT_DEVICE
. 也可以像树根那样手动创建一个结构.
1 2 #define U_BOOT_DEVICE(__name) \ ll_entry_declare(struct driver_info, __name, driver_info)
另一种则是通过设备树创建. 无论采用那种方式最终都是调用 device_bind_common
来创建并连接 uclass , uclass_driver , udevice , driver . 如下图所示
通过两次调用 device_bind_common
这个函数就可以创建图中的结构关系, 第一次调用创建 UCLASS_SYSRESET(uclass)
, sysreset_syscon_reboot(udev)
, 并且建立和sysreset(uc_drv)
, sysreset_syscon_reboot(drv)
之间的关系. 第二次调用则在UCLASS_SYSRESET(uclass)
, 上面追加了 mytest
. 由此可见这个函数是贯穿整个 dm 模型的核心. 因此优先分析这个函数.
1) device_bind_common
这个函数是 uboot 用来创建 dm 树枝的核心函数. 理解了这个函数就理解了 dm 模型的创建.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 static int device_bind_common (struct udevice *parent, const struct driver *drv, const char *name, void *platdata, ulong driver_data, ofnode node, uint of_platdata_size, struct udevice **devp) { struct udevice *dev; struct uclass *uc; int size, ret = 0 ; if (devp) *devp = NULL ; if (!name) return -EINVAL; ret = uclass_get (drv->id, &uc); if (ret) { debug ("Missing uclass for driver %s\n" , drv->name); return ret; } #ifdef CONFIG_USING_KERNEL_DTB if (gd->flags & GD_FLG_RELOC) { if (drv->id == UCLASS_MMC || drv->id == UCLASS_RKNAND || drv->id == UCLASS_SPI_FLASH || drv->id == UCLASS_MTD || drv->id == UCLASS_PCI || drv->id == UCLASS_AHCI) { if ((gd->flags & GD_FLG_KDTB_READY) && (drv->id == UCLASS_MMC)) return 0 ; list_for_each_entry (dev, &uc->dev_head, uclass_node) { if (!strcmp (name, dev->name)) { debug ("%s do not bind dev already in list %s\n" ,__func__, dev->name); dev->node = node; return 0 ; } } } struct udevice *n; list_for_each_entry_safe (dev, n, &uc->dev_head, uclass_node) { if (!strcmp (name, dev->name) && (dev_read_bool (dev, "u-boot,dm-pre-reloc" ) || dev_read_bool (dev, "u-boot,dm-spl" ))) { if (drv->id == UCLASS_CRYPTO || drv->id == UCLASS_WDT) { debug ("%s do not delete uboot dev: %s\n" , __func__, dev->name); return 0 ; } else if (drv->id == UCLASS_REGULATOR) { } else { list_del_init (&dev->uclass_node); } } } } #endif dev = calloc (1 , sizeof (struct udevice)); if (!dev) return -ENOMEM; INIT_LIST_HEAD (&dev->sibling_node); INIT_LIST_HEAD (&dev->child_head); INIT_LIST_HEAD (&dev->uclass_node); #ifdef CONFIG_DEVRES INIT_LIST_HEAD (&dev->devres_head); #endif dev->platdata = platdata; dev->driver_data = driver_data; dev->name = name; dev->node = node; dev->parent = parent; dev->driver = drv; dev->uclass = uc; dev->seq = -1 ; dev->req_seq = -1 ; if (CONFIG_IS_ENABLED (OF_CONTROL) && CONFIG_IS_ENABLED (DM_SEQ_ALIAS)) { if (uc->uc_drv->flags & DM_UC_FLAG_SEQ_ALIAS) { if (uc->uc_drv->name && ofnode_valid (node)) { dev_read_alias_seq (dev, &dev->req_seq); } } } if (drv->platdata_auto_alloc_size) { bool alloc = !platdata; if (CONFIG_IS_ENABLED (OF_PLATDATA)) { if (of_platdata_size) { dev->flags |= DM_FLAG_OF_PLATDATA; if (of_platdata_size < drv->platdata_auto_alloc_size) alloc = true ; } } if (alloc) { dev->flags |= DM_FLAG_ALLOC_PDATA; dev->platdata = calloc (1 , drv->platdata_auto_alloc_size); if (!dev->platdata) { ret = -ENOMEM; goto fail_alloc1; } if (CONFIG_IS_ENABLED (OF_PLATDATA) && platdata) { memcpy (dev->platdata, platdata, of_platdata_size); } } } size = uc->uc_drv->per_device_platdata_auto_alloc_size; if (size) { dev->flags |= DM_FLAG_ALLOC_UCLASS_PDATA; dev->uclass_platdata = calloc (1 , size); if (!dev->uclass_platdata) { ret = -ENOMEM; goto fail_alloc2; } } if (parent) { size = parent->driver->per_child_platdata_auto_alloc_size; if (!size) { size = parent->uclass->uc_drv-> per_child_platdata_auto_alloc_size; } if (size) { dev->flags |= DM_FLAG_ALLOC_PARENT_PDATA; dev->parent_platdata = calloc (1 , size); if (!dev->parent_platdata) { ret = -ENOMEM; goto fail_alloc3; } } } if (parent) list_add_tail (&dev->sibling_node, &parent->child_head); ret = uclass_bind_device (dev); if (ret) goto fail_uclass_bind; if (drv->bind) { ret = drv->bind (dev); if (ret) goto fail_bind; } if (parent && parent->driver->child_post_bind) { ret = parent->driver->child_post_bind (dev); if (ret) goto fail_child_post_bind; } if (uc->uc_drv->post_bind) { ret = uc->uc_drv->post_bind (dev); if (ret) goto fail_uclass_post_bind; } if (parent) pr_debug ("Bound device %s to %s\n" , dev->name, parent->name); if (devp) *devp = dev; dev->flags |= DM_FLAG_BOUND; return 0 ; ......
1.1) uclass_get
1 2 3 4 5 6 7 8 9 10 11 12 13 int uclass_get (enum uclass_id id, struct uclass **ucp) { struct uclass *uc; *ucp = NULL ; uc = uclass_find (id); if (!uc) return uclass_add (id, ucp); *ucp = uc; return 0 ; }
uclass_add
创建一个对应 uclass_id 的 uclass 并且会在 UCLASS_DRIVER 定义的列表中查找对应 id 的 uclss_driver. 并对其进行绑定. 绑定之后回调 uc_drv->init(uc)
, 同时为 uclass->priv
分配 uc_drv->priv_auto_alloc_size
大小的空间 , 如果找不到对应 id 的 uclsss_driver 则返回 err.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 static int uclass_add (enum uclass_id id, struct uclass **ucp) { struct uclass_driver *uc_drv; struct uclass *uc; int ret; *ucp = NULL ; uc_drv = lists_uclass_lookup (id); if (!uc_drv) { debug ("Cannot find uclass for id %d: please add the UCLASS_DRIVER() declaration for this UCLASS_... id\n" ,id); return -EPFNOSUPPORT; } uc = calloc (1 , sizeof (*uc)); if (!uc) return -ENOMEM; if (uc_drv->priv_auto_alloc_size) { uc->priv = calloc (1 , uc_drv->priv_auto_alloc_size); if (!uc->priv) { ret = -ENOMEM; goto fail_mem; } } uc->uc_drv = uc_drv; INIT_LIST_HEAD (&uc->sibling_node); INIT_LIST_HEAD (&uc->dev_head); list_add (&uc->sibling_node, &DM_UCLASS_ROOT_NON_CONST); if (uc_drv->init) { ret = uc_drv->init (uc); if (ret) goto fail; } ...... return ret; }
1.2) uclass_bind_device
将 dev->uclass_node
连接到 uc->dev_head
回调父设备的的 dev->parent->uclass->uc_drv->child_post_bind()
接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 int uclass_bind_device (struct udevice *dev) { struct uclass *uc; int ret; uc = dev->uclass; list_add_tail (&dev->uclass_node, &uc->dev_head); if (dev->parent) { struct uclass_driver *uc_drv = dev->parent->uclass->uc_drv; if (uc_drv->child_post_bind) { ret = uc_drv->child_post_bind (dev); if (ret) goto err; } } return 0 ; err: list_del (&dev->uclass_node); return ret; }
1.3) 总结
在 gd->uclass_root 中搜索 uclss, 查找到则返回对应的 uclss, 找到直接返回
前面没找到 uclss, 创建一个对应 uclass_id 的 uclass 并且会在 UCLASS_DRIVER 定义的列表中查找对应 id 的 uclss_driver. 并对其进行绑定. 绑定之后回调 uc_drv->init(uc)
, 同时为 uclass->priv
分配 uc_drv->priv_auto_alloc_size
大小的空间 , 如果找不到对应 id 的 uclsss_driver 则返回 err.
创建一个 udevice 并对其成员变量进行初始化, dev->driver = drv;
、 dev->uclass = uc;
等.
为 udev 的成员变量 platdata, uclass_platdata, parent_platdata 分配空间. 他们的 size 决定因素如下.
1 2 3 platdata ==> drv->platdata_auto_alloc_size uclass_platdata ==> uc->uc_drv->per_device_platdata_auto_alloc_size; parent_platdata ==> parent->driver->per_child_platdata_auto_alloc_size;
将 dev->sibling_node 连接到 parent->child_head 父节点, 即上图用于连接 udevice 之间的线
将 dev->uclass_node 连接到 uc->dev_head, 即上图中用于连接 udevice 和 uclass 之间的线.
设置dev->flags |= DM_FLAG_BOUND
整个过程依次回调的接口如下, 常用的接口为 dev->drv->bind(dev)
1 2 3 4 5 dev->uc->uc_drv->init (uc); dev->parent->uclass->uc_drv->child_post_bind (dev); dev->drv->bind (dev); dev->parent->driver->child_post_bind (dev); dev->uc->uc_drv->post_bind (dev);
如果代码已经重定位, 在创建 udevice 时需要对设备进行判定. 如果 drv->id 是 UCLASS_MMC UCLASS_RKNAND UCLASS_SPI_FLASH UCLASS_MTD UCLASS_PCI UCLASS_AHCI
这些中的一个, 且该设备已经创建则直接返回. 如果该设备节点设置了 u-boot,dm-pre-reloc
或者 u-boot,dm-spl
, 除了 UCLASS_CRYPTO UCLASS_CRYPTO
这两个 id 的设备都将从 uclass 链表中删除.
2) 树根 gd->dm_root
dm_init 用于构建树根 , 首先判断 gd->dm_root
如果已经注册了就返回错误. 然后初始化 gd->uclass_root
链表. 最后调用 device_bind_by_name
构建树根.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int dm_init (bool of_live) { int ret; if (gd->dm_root) { dm_warn ("Virtual root driver already exists!\n" ); return -EINVAL; } INIT_LIST_HEAD (&DM_UCLASS_ROOT_NON_CONST); ...... ret = device_bind_by_name (NULL , false , &root_info, &DM_ROOT_NON_CONST); ...... ret = device_probe (DM_ROOT_NON_CONST); if (ret) return ret; return 0 ; }
树根的构建并没有使用设备树而是使用root_info
来构建的. 相关定义如下.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #define DM_ROOT_NON_CONST (((gd_t *)gd)->dm_root) #define DM_UCLASS_ROOT_NON_CONST (((gd_t *)gd)->uclass_root) static const struct driver_info root_info = { .name = "root_driver" , }; U_BOOT_DRIVER (root_driver) = { .name = "root_driver" , .id = UCLASS_ROOT, .priv_auto_alloc_size = sizeof (struct root_priv), };
device_bind_by_name
首先遍历 U_BOOT_DRIVER
定义的驱动列表, 查找 name(root_driver) 对应的驱动. 他的定义如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 int device_bind_by_name (struct udevice *parent, bool pre_reloc_only, const struct driver_info *info, struct udevice **devp) { struct driver *drv; uint platdata_size = 0 ; drv = lists_driver_lookup_name (info->name); if (!drv) return -ENOENT; if (pre_reloc_only && !(drv->flags & DM_FLAG_PRE_RELOC)) return -EPERM; #if CONFIG_IS_ENABLED(OF_PLATDATA) platdata_size = info->platdata_size; #endif return device_bind_common (parent, drv, info->name, (void *)info->platdata, 0 , ofnode_null (), platdata_size, devp); }
device_bind_by_name
调用 device_bind_common 创建树根. 树根的结构如下所示.
3) 使用设备树创建 dm 模型
dm 模型中通过 dm_scan_fdt 扫描设备树并创建树枝.
3.1) dm_scan_fdt
1 2 3 4 5 6 7 8 9 10 11 12 13 int dm_scan_fdt (const void *blob, bool pre_reloc_only) {#if CONFIG_IS_ENABLED(OF_LIVE) if (of_live_active ()) return dm_scan_fdt_live (gd->dm_root, gd->of_root, pre_reloc_only); else #endif return dm_scan_fdt_node (gd->dm_root, blob, 0 , pre_reloc_only); }
在 .config 中搜索配置如下
1 2 ➜ grep "CONFIG_OF_LIVE" -rn .config 733:CONFIG_OF_LIVE=y
dm_scan_fdt_live
, dm_scan_fdt_node
, 这两个函数的实现其实是一样的. 只有一些宏的配置略有差异如下图所示. 标出了两者代码关键的地方.
文章就以 dm_scan_fdt_live 进行分析, 因为 rk3566 是配置了 CONFIG_OF_LIVE 这个宏的.
3.1.1) dm_init_and_scan
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #if CONFIG_IS_ENABLED(OF_LIVE) static int dm_scan_fdt_live (struct udevice *parent, const struct device_node *node_parent, bool pre_reloc_only) { struct device_node *np; int ret = 0 , err; for (np = node_parent->child; np; np = np->sibling) { if (pre_reloc_only && #ifdef CONFIG_USING_KERNEL_DTB (!of_find_property (np, "u-boot,dm-pre-reloc" , NULL ) && !of_find_property (np, "u-boot,dm-spl" , NULL ))) #else !of_find_property (np, "u-boot,dm-pre-reloc" , NULL )) #endif continue ; if (!of_device_is_available (np)) { pr_debug (" - ignoring disabled device\n" ); continue ; } err = lists_bind_fdt (parent, np_to_ofnode (np), NULL ); if (err && !ret) { ret = err; debug ("%s: ret=%d\n" , np->name, ret); } if (!pre_reloc_only && !strcmp (np->name, "firmware" )) ret = device_bind_driver_to_node (gd->dm_root, "firmware" , np->name, np_to_ofnode (np), NULL ); } if (ret) dm_warn ("Some drivers failed to bind\n" ); return ret; } #endif
该函数的主要功能是扫描 parent
节点下的子节点, 并且根据宏的配置决定如何解析 dts. 整理如下
宏相关配置
解析的节点(配置了这些 dts 属性才会解析, 少一个都不行)
配置CONFIG_OF_LIVE
配置pre_reloc_only=1
status = okay;
+ u-boot,dm-pre-reloc
配置CONFIG_OF_LIVE
配置pre_reloc_only=1
配置 CONFIG_USING_KERNEL_DTB
status = okay;
+ u-boot,dm-pre-reloc;
+ u-boot,dm-spl;
配置CONFIG_OF_LIVE
没有配置 pre_reloc_only=1
status = okay;
3.1.2) lists_bind_fdt
这个函数遍历 node 节点中的 compatible 属性, 并和 U_BOOT_DRIVER 定义的 driver->of_match 进行匹配. 匹配成功调用 device_bind_common 创建 dm 模型中的树枝.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 int lists_bind_fdt (struct udevice *parent, ofnode node, struct udevice **devp) { struct driver *driver = ll_entry_start (struct driver, driver); const int n_ents = ll_entry_count (struct driver, driver); const struct udevice_id *id; struct driver *entry; struct udevice *dev; bool found = false ; const char *name, *compat_list, *compat; int compat_length, i; int result = 0 ; int ret = 0 ; if (devp) *devp = NULL ; name = ofnode_get_name (node); pr_debug ("bind node %s\n" , name); compat_list = ofnode_get_property (node, "compatible" , &compat_length); if (!compat_list) { ...... } for (i = 0 ; i < compat_length; i += strlen (compat) + 1 ) { compat = compat_list + i; pr_debug (" - attempt to match compatible string '%s'\n" , compat); for (entry = driver; entry != driver + n_ents; entry++) { ret = driver_check_compatible (entry->of_match, &id, compat); if (!ret) break ; } if (entry == driver + n_ents) continue ; pr_debug (" - found match at '%s'\n" , entry->name); ret = device_bind_with_driver_data (parent, entry, name, id->data, node, &dev); if (ret == -ENODEV) { pr_debug ("Driver '%s' refuses to bind\n" , entry->name); continue ; } if (ret) { dm_warn ("Error binding driver '%s': %d\n" , entry->name, ret); return ret; } else { found = true ; if (devp) *devp = dev; } break ; } if (!found && !result && ret != -ENODEV) pr_debug ("No match for node '%s'\n" , name); return result; }
3.1.2.1) device_bind_with_driver_data
1 2 3 4 5 6 7 8 9 int device_bind_with_driver_data (struct udevice *parent, const struct driver *drv, const char *name, ulong driver_data, ofnode node, struct udevice **devp) { return device_bind_common (parent, drv, name, NULL , driver_data, node, 0 , devp); }
3.1.2.2) driver_check_compatible
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 static int driver_check_compatible (const struct udevice_id *of_match, const struct udevice_id **of_idp, const char *compat) { if (!of_match) return -ENOENT; while (of_match->compatible) { if (!strcmp (of_match->compatible, compat)) { *of_idp = of_match; return 0 ; } of_match++; } return -ENOENT; }
3.2 ) 总结
dm_init_and_scan 首先遍历根节点下的子节点, 根据宏的配置决定解析哪些 dts.
宏相关配置
解析的节点(配置了这些 dts 属性才会解析, 少一个都不行)
配置CONFIG_OF_LIVE
配置pre_reloc_only=1
status = okay;
+ u-boot,dm-pre-reloc
配置CONFIG_OF_LIVE
配置pre_reloc_only=1
配置 CONFIG_USING_KERNEL_DTB
status = okay;
+ u-boot,dm-pre-reloc;
+ u-boot,dm-spl;
没有配置CONFIG_OF_LIVE
配置pre_reloc_only=1
status = okay;
+ u-boot,dm-pre-reloc
+ u-boot,dm-tpl
+ u-boot,dm-spl
没有配置CONFIG_OF_LIVE
配置pre_reloc_only=1
配置CONFIG_TPL_BUILD
status = okay;
+ u-boot,dm-pre-reloc
+ u-boot,dm-tpl
没有配置CONFIG_OF_LIVE
配置pre_reloc_only=1
配置CONFIG_SPL_BUILD
status = okay;
+ u-boot,dm-pre-reloc
+ u-boot,dm-spl
没有配置 pre_reloc_only=1
status = okay;
对于需要解析的节点, 获取节点的 compatible 属性. 使用该属性和 driver->of_match 进行匹配. 如下图所示, 该图示例了 rockchip_display 这个设备的匹配. 左边是 driver, 右边是 dts.
匹配成功则调用 device_bind_common 创建 dm 模型, 如下所示的结构关系.
2、 rk3566 dm 的构建
有了前面的知识我们就可以从整体来分析 rk3566 的 dm 模型树的构建过程了. 前面说过构建过程分为三个阶段. 他们分别如下.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 static const init_fnc_t init_sequence_f[] = { ...... initf_dm, ...... } static int initf_dm (void ){ #if defined(CONFIG_DM) && CONFIG_VAL(SYS_MALLOC_F_LEN) int ret; bootstage_start (BOOTSTATE_ID_ACCUM_DM_F, "dm_f" ); ret = dm_init_and_scan (true ); bootstage_accum (BOOTSTATE_ID_ACCUM_DM_F); if (ret) return ret; #endif ...... return 0 ; }
这里需要注意的是 dm_init_and_scan
传入了 true
. 因此 pre_reloc_only = 1
1 2 3 4 5 6 7 8 9 10 dm_init_and_scan (true ); --> dm_init (IS_ENABLED (CONFIG_OF_LIVE)); --> dm_scan_platdata (pre_reloc_only); --> lists_bind_drivers (DM_ROOT_NON_CONST, pre_reloc_only); --> for (entry = info; entry != info + n_ents; entry++){ --> device_bind_by_name (parent, pre_reloc_only, entry, &dev); --> device_bind_common (...); --> } dm_extended_scan_fdt (gd->fdt_blob, pre_reloc_only); --> ret = dm_scan_fdt (gd->fdt_blob, pre_reloc_only); -->
第⼀阶段候首先使用 /u-boot/arch/arm/dts/
目录下的 dts 编译出来的 dtb 初始化硬件. 这个阶段只需要加载 emmc、nand、cru、grf、uart 等模块.他们由 status = okay;
, u-boot,dm-pre-reloc;
, u-boot,dm-spl;
等属性指定, 具体请参考文章 3.2 的总结. 第⼀阶段为了速度和效率,会删除⼀些属性,也可以通过 defconfig ⾥的 CONFIG_OF_SPL_REMOVE_PROPS
指定属性
1 CONFIG_OF_SPL_REMOVE_PROPS="clock-names interrupt-parent assigned-clocks assigned-clock-rates assigned-clock-parents"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 static init_fnc_t init_sequence_r[] = { ...... #ifdef CONFIG_DM initr_dm, #endif ...... #if defined(CONFIG_ARM) || defined(CONFIG_NDS32) || defined(CONFIG_RISCV) board_init, #endif ...... }; #ifdef CONFIG_DM static int initr_dm (void ) { int ret; gd->dm_root_f = gd->dm_root; gd->dm_root = NULL ; #ifdef CONFIG_TIMER gd->timer = NULL ; #endif bootstage_start (BOOTSTATE_ID_ACCUM_DM_R, "dm_r" ); ret = dm_init_and_scan (false ); bootstage_accum (BOOTSTATE_ID_ACCUM_DM_R); if (ret) return ret; #ifdef CONFIG_TIMER_EARLY ret = dm_timer_init (); if (ret) return ret; #endif return 0 ; } #endif
第二阶段将第一阶段创建的 dm 模型树保存到 gd->dm_root_f
. 设置 gd->dm_root
为空, 之后从新再创建一遍, 注意这里传入的是 false. 因此会解析设备树二级节点的所有 okay
节点 . 注意这时候还是使用的 uboot 的 dtb.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 static init_fnc_t init_sequence_r[] = { ...... #ifdef CONFIG_DM initr_dm, #endif ...... #if defined(CONFIG_ARM) || defined(CONFIG_NDS32) || defined(CONFIG_RISCV) board_init, #endif ...... }; board_init () --> init_kernel_dtb () --> rockchip_read_dtb_file ((void *)fdt_addr); dtb_okay: gd->fdt_blob = (void *)fdt_addr; gd->flags |= GD_FLG_KDTB_READY; dm_scan_fdt ((void *)gd->fdt_blob, false );
第三阶段首加载 kernel 的 dtb, 然后使用 kernel 的 dtb 调用 dm_scan_fdt 扫描设备树并且解析所有根目录下的二级子节点, 对于具有 okay
的子节点进行匹配并且创建 dm 模型树枝.
三、数据结构
1、udevice
用于描述具体的硬件设备, 在当前的 dm 模型中, 在 uboot 启动的时候扫描 dts 自动创建. 详情参考 dts 加载和 dm 模型的本质 . 其中需要关注的数据结构有 priv 常用于设置设备硬件私有数据结构 . 通过接口void *dev_get_priv(struct udevice *dev)
返回.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 struct udevice { const struct driver *driver; const char *name; void *platdata; void *parent_platdata; void *uclass_platdata; ofnode node; ulong driver_data; struct udevice *parent; void *priv; struct uclass *uclass; void *uclass_priv; void *parent_priv; struct list_head uclass_node; struct list_head child_head; struct list_head sibling_node; uint32_t flags; int req_seq; int seq; #ifdef CONFIG_DEVRES struct list_head devres_head; #endif };
2、driver
对应的 udevice 的驱动, 其中 probe 用于驱动的初始化. 已经 probe 就表示该设备已就绪可以使用. ofdata_to_platdata 接口在 probe 之前调用, 用于解析设备树. ops 则用于创建 drv 真的的操作接口. 通过 i2c_get_ops(dev)
返回该接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 struct driver { char *name; enum uclass_id id; const struct udevice_id *of_match; int (*bind)(struct udevice *dev); int (*probe)(struct udevice *dev); int (*remove)(struct udevice *dev); int (*unbind)(struct udevice *dev); int (*ofdata_to_platdata)(struct udevice *dev); int (*child_post_bind)(struct udevice *dev); int (*child_pre_probe)(struct udevice *dev); int (*child_post_remove)(struct udevice *dev); int priv_auto_alloc_size; int platdata_auto_alloc_size; int per_child_auto_alloc_size; int per_child_platdata_auto_alloc_size; const void *ops; uint32_t flags; };
drv 需要手动创建, 通过 U_BOOT_DRIVER 创建.
1 2 3 4 5 6 7 #define U_BOOT_DRIVER(__name) \ ll_entry_declare(struct driver, __name, driver) #define ll_entry_declare(_type, _name, _list) \ _type _u_boot_list_2_##_list##_2_##_name __aligned(4) \ __attribute__((unused, \ section(".u_boot_list_2_" #_list"_2_" #_name)))
展开后得到.
1 struct driver _u_boot_list_2_driver_2___name __aligned(4 ) __attribute__((unused, section (".u_boot_list_2_driver_2___name" )));
因此该宏将对应的 drv 编译到指定的段 u_boot_list_2_driver_2_
中. 可以通过以下接口获取对应的 drv.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 struct driver *lists_driver_lookup_name (const char *name){ struct driver *drv = ll_entry_start (struct driver, driver); const int n_ents = ll_entry_count (struct driver, driver); struct driver *entry; for (entry = drv; entry != drv + n_ents; entry++) { if (!strcmp (name, entry->name)) return entry; } return NULL ; }
3、uclass
在创建 udevice 时自动创建, 管理一类设备, 即同类的 udevice 由 uclass 进行统一管理. 每一个 uclalss 都有一个唯一的 uc_drv->uclass_id 进行描述.
1 2 3 4 5 6 struct uclass { void *priv; struct uclass_driver *uc_drv; struct list_head dev_head; struct list_head sibling_node; };
4、uclass_driver
给出该类设备的统一接口. post_probe 接口常用来设置该类设备共有属性, 例如 i2c 的速率. post_bind 接口则常设置为 dm_scan_fdt_dev 用于扫描并创建其下的子设备 udevice . 和 driver 类似通过 UCLASS_DRIVER
创建
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 struct uclass_driver { const char *name; enum uclass_id id; int (*post_bind)(struct udevice *dev); int (*pre_unbind)(struct udevice *dev); int (*pre_probe)(struct udevice *dev); int (*post_probe)(struct udevice *dev); int (*pre_remove)(struct udevice *dev); int (*child_post_bind)(struct udevice *dev); int (*child_pre_probe)(struct udevice *dev); int (*init)(struct uclass *class ); int (*destroy)(struct uclass *class ); int priv_auto_alloc_size; int per_device_auto_alloc_size; int per_device_platdata_auto_alloc_size; int per_child_auto_alloc_size; int per_child_platdata_auto_alloc_size; const void *ops; uint32_t flags; };
5、 总结
udevice , driver , uclass , uclass_driver 他们四为位一体, 在 uboot 中扫描 dts 自动创建. 以 i2c 为例进行说明. 下图展示了 rk3566 i2c 的组织架构.
第一个阶段 通过扫描 dts 创建了 i2c0 控制器 i2c2: i2c@fe5b0000
的 udevice 然后以及挂在该子设备 pmic 和 rk817_fg 对应的 device. 需要注意第一个阶段只会创建 device 并不会对硬件进行初始化(probe).
第二阶段 , 即调用 probe 初始化硬件, 注意和 linux 内核自动 probe 不同. uboot 的设计理念是, 即用即初始化, 不用不初始化 . 因此 porbe 是手动调用的. 需要初始化硬件的时候手动调用 probe 函数进行初始化. 在初始化硬件(probe)的时候会检测其父设备的硬件是否已经初始化(probe), 如果父设备没有准备好则先初始化父设备. 如上图所示, 在 probe pmic 的时候会检测 i2c0 是否已经 probe, i2c0 没有 probe 则先调用 i2c0 的 probe 初始化 i2c0, 再调用 i2c class 提供的dm_i2c_read dm_i2c_write
等统一接口在 pmic 的 probe 中初始化 pmic. 核心接口为 device_probe.
6、device_probe
检查标志位 DM_FLAG_ACTIVATED
判断是否已经完成 probe, 如果已经 probe 则直接返回.
为 dev 分配一些列空间如下.
1 2 3 4 5 dev->priv ==> drv->priv_auto_alloc_size dev->uclass_priv ==> dev->uclass->uc_drv->per_device_auto_alloc_size dev->parent_priv ==> dev->parent->driver->per_child_auto_alloc_size ==> dev->parent->uclass->uc_drv->per_child_auto_alloc_size
调用父设备的 probe 函数, 遍历 uclass 中的子设备在没有用到的 seq 中返回一个空闲的 seq 设置 dev->seq 这个 seq 可以表示 dev 的初始化顺序, 第一个被初始化的 dev 的 seq 为 0, 第二个为 1 以此类推. 因此可以通过 seq 判断 dev 的初始化顺序.
进行一系列回调
1 2 3 4 5 6 回调 dev->uclass->uc_drv->pre_probe (dev); --> 回调 dev->parent->uclass->uc_drv->child_pre_probe (dev); --> 回调 dev->parent->driver->child_pre_probe (dev); --> 回调 dev->drv->ofdata_to_platdata (dev); --> 回调 dev->drv->probe (dev); --> 回调 dev->uclass->uc_drv->post_probe (dev); -->
调用 pinctrl 设置 default 的 pin 脚状态.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 int device_probe (struct udevice *dev) { const struct driver *drv; int size = 0 ; int ret; int seq; if (!dev) return -EINVAL; if (dev->flags & DM_FLAG_ACTIVATED) return 0 ; drv = dev->driver; assert (drv); if (drv->priv_auto_alloc_size && !dev->priv) { dev->priv = alloc_priv (drv->priv_auto_alloc_size, drv->flags); if (!dev->priv) { ret = -ENOMEM; goto fail; } } size = dev->uclass->uc_drv->per_device_auto_alloc_size; if (size && !dev->uclass_priv) { dev->uclass_priv = calloc (1 , size); if (!dev->uclass_priv) { ret = -ENOMEM; goto fail; } } if (dev->parent) { size = dev->parent->driver->per_child_auto_alloc_size; if (!size) { size = dev->parent->uclass->uc_drv->per_child_auto_alloc_size; } if (size && !dev->parent_priv) { dev->parent_priv = alloc_priv (size, drv->flags); if (!dev->parent_priv) { ret = -ENOMEM; goto fail; } } ret = device_probe (dev->parent); if (ret) goto fail; if (dev->flags & DM_FLAG_ACTIVATED) return 0 ; } seq = uclass_resolve_seq (dev); if (seq < 0 ) { ret = seq; goto fail; } dev->seq = seq; dev->flags |= DM_FLAG_ACTIVATED; if (dev->parent && device_get_uclass_id (dev) != UCLASS_PINCTRL) pinctrl_select_state (dev, "default" ); ret = uclass_pre_probe_device (dev); if (ret) goto fail; if (dev->parent && dev->parent->driver->child_pre_probe) { ret = dev->parent->driver->child_pre_probe (dev); if (ret) goto fail; } if (drv->ofdata_to_platdata && dev_has_of_node (dev)) { ret = drv->ofdata_to_platdata (dev); if (ret) goto fail; } if (drv->probe) { ret = drv->probe (dev); if (ret) { dev->flags &= ~DM_FLAG_ACTIVATED; goto fail; } } ret = uclass_post_probe_device (dev); if (ret) goto fail_uclass; if (dev->parent && device_get_uclass_id (dev) == UCLASS_PINCTRL) pinctrl_select_state (dev, "default" ); return 0 ; fail_uclass: if (device_remove (dev, DM_REMOVE_NORMAL)) { dm_warn ("%s: Device '%s' failed to remove on error path\n" , __func__, dev->name); } fail: dev->flags &= ~DM_FLAG_ACTIVATED; dev->seq = -1 ; device_free (dev); return ret; }
四、常用接口
1、udevice 创建接口
1) device_bind_with_driver_data
设用设备节点 node 创建并返回一个 udevice
1 2 3 4 5 6 7 int device_bind_with_driver_data (struct udevice *parent,const struct driver *drv, const char *name, ulong driver_data, ofnode node, struct udevice **devp)
2) device_bind
使用设备树偏移地址 of_offset 创建 udevice
1 2 3 4 5 6 7 int device_bind (struct udevice *parent, const struct driver *drv,const char *name, void *platdata, int of_offset, struct udevice **devp) ;
3) device_bind_by_name
使用 driver_info 创建 udevice, 不使用设备树.
1 2 3 4 5 int device_bind_by_name (struct udevice *parent, bool pre_reloc_only, const struct driver_info *info, struct udevice **devp) ;
2、uclass 操作函数
1) uclass_get
通过 uclass_id 返回对应的 uclass, 没有则创建一个 uclass 返回.
1 int uclass_get (enum uclass_id id, struct uclass **ucp) ;
2) uclass_get_name
通过 uclass_id 返回 uclass 的 name
1 const char *uclass_get_name (enum uclass_id id) ;
3) uclass_find_device
返回对应 uclass_id 的 uclass 对应的设备链表上的第 index 个 udevice
1 int uclass_find_device (enum uclass_id id, int index, struct udevice **devp) ;
4) uclass_find_first_device
返回对应 uclass_id 的 uclass 对应的设备链表上的第一个 udevice
1 int uclass_find_first_device(enum uclass_id id, struct udevice **devp);
5) uclass_find_first_device
返回所属 uclass 链表的下一个 udevice
1 int uclass_find_next_device (struct udevice **devp) ;
6) uclass_find_device_by_name
返回对应 uclass_id 的 uclass 的设备链表上对应 name 的 udevice
1 int uclass_find_device_by_name (enum uclass_id id, const char *name, struct udevice **devp) ;
7) uclass_find_device_by_seq
uclass_id 的 uclass 的设备链表上通过 seq 查找 udevice
1 int uclass_find_device_by_seq (enum uclass_id id, int seq_or_req_seq, bool find_req_seq, struct udevice **devp)
8) uclass_find_device_by_ofnode
uclass_id 的 uclass 的设备链表查找对应 node 的 device
1 int uclass_find_device_by_ofnode (enum uclass_id id, ofnode node, struct udevice **devp
3、device_probe 封装接口
1) uclass_get_device
返回 uclass_id 的 uclass 的设备链表上第 index 个 udevice 并进行 device_probe
1 int uclass_get_device (enum uclass_id id, int index, struct udevice **devp) ;
2) uclass_get_device_by_name
返回 uclass_id 的 uclass 的设备链表上对应 name 的 udevice 并进行 device_probe
1 int uclass_get_device_by_name (enum uclass_id id, const char *name, struct udevice **devp) ;
3) uclass_get_device_by_seq
返回 uclass_id 的 uclass 的设备链表上对应 seq 的 udevice 并进行 device_probe
1 int uclass_get_device_by_seq (enum uclass_id id, int seq, struct udevice **devp) ;
4) uclass_get_device_by_ofnode
返回 uclass_id 的 uclass 的设备链表上对应设备节点 node 的 udevice 并进行 device_probe
1 int uclass_get_device_by_ofnode (enum uclass_id id, ofnode node, struct udevice **devp) ;
6) uclass_first_device
返回 uclass_id 的 uclass 的设备链表上第一个 udevice 并进行 device_probe
1 int uclass_first_device (enum uclass_id id, struct udevice **devp) ;
7) uclass_next_device
返回所属 uclass 链表上的下一个 udevice 并进行 device_probe
1 int uclass_next_device(struct udevice **devp);
简单总结: 如果只需要返回某个 udevice 则使用带 find 的接口, 如果需要返回并且 probe 则使用带 get 的接口.