U-boot 下 DM 驱动模型的相关笔记 要注意的关键两点: 1. DM 驱动模型的一般流程 bind->ofdata_to_platdata( 可选 )->probe 2. 启动, bind 操作时单独完成的,主要完成设备和驱动的绑定,以及 node 注: node 是匹配到驱动的设备节点 之间的连 接。 ofdata_toplatdata( 可选 )->probe 则是在 deviceprobe 函数中完成的。 明确了以上两点,接下来就开始展开分析 U-boot 下设计到驱动模型的一半流程,我依据的是 U-boot 2018.03 版 本 和 DM 相关的初始化流程主要有两次,入口函数分别是 static int initf_dm(void) 和 static int initr_dm(void) 。 第一次是在重定位之前,调用的是 initf_dm 函数,函数的调用关系如下所示: initf_dm// 执行 bind 操作,初始化一个 dm 模型的树形结构 dm_init_and_scan(true)// 初始化根节点设备,并 bind 根节点的带有 u-boot,dm-pre-reloc 属性的一级子节点。 dm_init// 将根节点绑定到 gd->dm_root 上,初始化根节点设备 dm_scan_platdata// 搜索使用宏 U_BOOT_DEVICE 定义的设备进行驱动匹配,也就是 bind 子节点 dm_extended_scan_fdt// 在其他地方(设备树)搜索设备并进行驱动匹配,然后 bind dm_scan_fdt// 在设备树种搜索设备并进行驱动匹配,然后 bind dm_scan_fdt_node// 具体绑定设备的入口,在该函数中会确定设备是否具有 boot,dm-pre-reloc 属性,如果没有则不会绑定 lists_bind_fdt// 搜索可以匹配到该设备的驱动 device_bind_with_driver_data// 如果匹配到进行绑定 device_bind_common// 匹配设备和驱动,并将设备节点和 parent 节点建立联系,也就是建立树形结构 uclass_bind_device// 将该设备挂在对应的 U_CLASS 链表上 至此, dm 相关的一半 bind 流程就介绍完了,执行完 initr_dm 之后,内存中就有了一个深度最深为 2 (只有根节 点时,深度为 1 )的树形结构,根节点挂在 gd->dm_root 上。 同样的, static int initr_dm(void) 执行了类似的操作,只不过, gd->dm_root_f = gd->dm_root; gd->dm_root = NULL; 重定位之后,首先将 gd->dm_root 的值赋值给 gd->dm_rootf ;然后清零了 gd->dm_root ,接下来才是重新执行 dminitandscan(false) 操作, dm_init_and_scan(false)// 初始化根节点设备,并 bind 根节点的所有子节点 ************ 省略,见 initf_dm 函数流程 bind 操作完成之后,就可以对设备进行 probe 操作了,示例一下 sdram 的初始化流程,我用的板子是基于 STM32f767igt 的 YYFISH board 。和 sdram 基于 fmc ,并且除了 sdram , fmc 下还挂了一个 4Gbit 的 nand flash , 设备树相关的源码如下: &fmc { pinctrl-0 = <&fmc_pins>; pinctrl-names = "default"; status = "okay"; #address-cells = <1>; #size-cells = <1>; /* Memory configuration from sdram datasheet MT48LC_4M32_B2B5-6A */ bank1: bank@0 { st,sdram-control = /bits/ 8 CAS_3 SDCLK_2 RD_BURST_EN RD_PIPE_DL_1>; st,sdram-timing = /bits/ 8 TRP_2 TRCD_2>; /* refcount = (64msec/total_row_sdram)*freq - 20 */ st,sdram-refcount = < 1421 >; }; bank3: stm32_nand { reg = <0xA0000080 0x20>; /*Nand flash configuretion from flash datasheet MT29F4G08ABADA*/ compatible = "micron,mt29f4g", "st,nand-flash"; st,nand-control = /bits/ 8 FMC_TCLR_6 FMC_TAR_6 FMC_ECCPS_512>; st,nand-timing = /bits/ 8 MEMHIZ_VALE_2>; u-boot,dm-pre-reloc; }; }; 从设备树文件可以看出, fmc 下挂了两个节点,但是 bank3;stm32_nand 是我后来添加的,为了增加 nand_flash 的支持,所以 stm32_nand 就是 fmc 的一级子节点。 fmc 是 root 的子节点,树形结构的示意图如下 root fmc stm32_nand 根据上面对 initf_dm 和 initr_dm 的分析,可知,这两个函数只是完成了 root 的一级子节点的树形创建,并且 sdram 的初始化是在重定向之前,也就是执行了 initf_dm 之后完成的(稍后会具体分析)。所以 initf_dm 执行之 后,内存中只有如下的一个树形图: root fmc 这时候是没有 nandflash 这个设备节点的。 nandflash 这个设备节点的绑定,我放在了 fmc 这个节点的 probe 流程 中,具体是在 ofdata_to_platdata 这个接口函数中完成的。具体如下所示: dev_for_each_subnode(bank_node, dev) { /* extract the bank index from DT */ bank_name = (char *)ofnode_get_name(bank_node); if (!strncmp("stm32_nand", bank_name, strlen("stm32_nand")))//iysheng { lists_bind_fdt(dev, bank_node, NULL); continue; } } 从上述代码可以看出,当我 device_probe fmc 这个设备节点的时候,我会首先调用对应 driver 的 ofdata_toplatdata 函数接口,在这个接口函数中,会遍历子节点,如果发现名字是以 stm32nand 开头的,那么 会将这个设备节点进行进行 bind 操作,执行完这句话之后,设备树中的 fmc 节点下就会出现一个 stm32_nand 子 节点,这时候树的深度从 2 变为了 3 。 接着会调用 fmc 节点对应驱动的 probe 接口函数,具体如下: static int stm32_fmc_probe(struct udevice *dev) { ****** 省略一些内容,和 sdram 初始化相关的内容 if (device_has_children(dev)) { struct udevice * child_devp; int index = 0; while(!device_get_child(dev, index++, &child_devp)) { ret = device_probe(child_devp); if (ret < 0) break; } } } 可以看出, stm32_fmc_probe 的时候,会尝试 probe fmc 节点下的所有子节点。因为之前已经添加了 stm32_nand 节点,这时候就会去 probe stm32_nand 那个设备了。 接下来简述一下 sdram 的 probe 流程。函数调用过程如下: int dram_init(void) uclass_get_device(UCLASS_RAM, 0, &dev) uclass_find_device(id, index, &dev) uclass_get_device_tail(dev, ret, devp) device_probe(dev) device_probe(dev->parent)// 递归 probe 父节点 uclass_resolve_seq// 父节点都 probe 之后,会分配一个 seq 给该设备 dev->flags |= DM_FLAG_ACTIVATED// 设置该设备的 flag 为激活状态 pinctrl_select_state(dev, "default")// 和引脚相关的初始化设置 < 需要进一步分析 > dev->parent->driver->child_pre_probe(dev)// 执行父节点驱动的 child_pre_probe 接口函数 drv->ofdata_to_platdata(dev)// 执行设备驱动的 ofdata_to_platdata 接口函数 clk_set_defaults(dev)// 设备时钟相关的设置 drv->probe(dev)// 调用设备驱动的 probe 接口函数 uclass_post_probe_device(dev)// 调用所属 CLASS 驱动的 post_probe 接口函数 至此, sdram 的初始化流程介绍了一下,从上可以看出, device_probe 过程中,会首先调用 ofdata_to_platdata 接口函数,然后才会执行 probe 接口函数。
|