本文共 11157 字,大约阅读时间需要 37 分钟。
在 u-boot 中, DM 是 uclass device driver 以及三者相关函数的总体uclass device driver 相关结构体 driver 在定义的时候就根据 其 自身的id成员被 分为了 XXX uclass device 在定义的时候就根据 其 自身的name成员 暗含了 与 driver 绑定的条件函数 初始化 在(initf_dm/initr_dm)的时候,为每一个设备(设备树中的节点/U_BOOT_DEVICE声明的结构体)做以下动作 1. 初始化了 device 结构体 2. device_bind_common 实现driver 、uclass 与该 device 绑定(即三者绑定) 3. 调用了 driver 中的 bind 函数 为该设备类做初始化 在 例如 serial_init/mmc_initialize的时候 1. 调用 driver 中的 probe 函数 为设备做初始化 使用设备 1. 通过 uclass_get_device_xxx 获取 device 句柄 2. 通过 uclass 提供的函数 yyy 来控制设备
doc/driver-model/design.rstu-boot 将 驱动和 设备 做了分离 ,分成了三部分(这里没涉及 uclass 的core 部分) Uclass Driver Device假设 我用 cmdline 去访问 ,那么涉及的代码就是 4部分(这里没涉及 uclass 的core 部分) cmd cmd/demo.c Uclass drivers/demo/demo_uclass.c include/dm-demo.h Driver demo-shape.c demo-simple.c(一个文件对应一个驱动) Device demo-pdata.c(虽然是一个文件,但是这里有5个设备)
make sandbox_defconfigmake./u-boot -d u-boot.dtb=> demo list // 查看 已经注册的 且 属于 UCLASS_DEMO 类的 设备有多少个 (5个)Demo uclass entries:entry 0 - instance 05292d40, ops 08000001, platdata 08000000entry 1 - instance 05292e00, ops 08000002, platdata 08000000entry 2 - instance 05292ec0, ops 08000001, platdata 08000003entry 3 - instance 05292f80, ops 08000002, platdata 08000004entry 4 - instance 05293040, ops 08000001, platdata 08000004=> demo hello 0r@@@@@@@e@@@@@@@d@@@@@@@r@@@@@@@e@@@@@@@d@@@@@@@=> demo hello 2g r@ e@@ e@@@ n@@@@ g@@@@@ => demo hello 4 y@@@ e@@@@@ l@@@@@@@l@@@@@@@ o@@@@@ w@@@ // 看起来 设备 0 2 4 用的是同一个驱动 , 实际证明也是如此 , 用的是 demo_shape_drv=> demo hello 1Hello from 05292e00: red 4=> demo hello 3Hello from 05292f80: yellow 6// 看起来 设备 1 3 用的是同一个驱动 , 实际证明也是如此 , 用的是 demo_simple_drv
// 如果是 demo hello 1cmd(cmd/demo.c) do_demo uclass_get_device(UCLASS_DEMO, devnum, &demo_dev); demo_hello(demo_dev, ch);uclass(drivers/demo/demo_uclass.c) demo_hello struct demo_ops *ops = device_get_ops(dev); ops->hello(dev, ch);Driver(demo-simple.c) simple_hello const struct dm_demo_pdata *pdata = dev_get_platdata(dev); printf("Hello from %08x: %s %d\n", (uint)map_to_sysmem(dev), pdata->colour, pdata->sides);Device(demo-pdata.c) static const struct dm_demo_pdata red_square = { .colour = "red", .sides = 4. }; U_BOOT_DEVICE(demo1) = { .name = "demo_simple_drv", .platdata = &red_square, };
uclass(drivers/demo/demo_uclass.c) 通过 UCLASS_DRIVER(demo) 注册 UCLASS_DEMO 类Driver(demo-simple.c) 通过 U_BOOT_DRIVER(demo_simple_drv) 注册 驱动, 并绑定 到 UCLASS_DEMODevice(demo-pdata.c) 通过 U_BOOT_DEVICE(demo0) 注册设备,并绑定到 特定的driver消费者(例如cmd) 通过 uclass_get_device(UCLASS_DEMO, devnum, &demo_dev); 获取到设备 通过 设备类 提供的 操作函数 demo_hello(demo_dev, ch); 来控制设备 u-boot 引入 driver model以前 消费者想要操作一个设备,需要知道以下信息 A. 设备的物理地址是什么 B. 设备的驱动是什么,以及提供了什么APIu-boot 引入 driver model之后 消费者想要操作一个设备,不需要知道A和B,只需要知道下面的就行了 1. 该设备属于哪个类 2. 该类的操作函数
以上都是从消费者角度考虑的,但是没考虑 driver model 的核心实现(即 uclass的核心实现)
而我认为 initf_dm 和 initr_dm 就是 核心实现的初始化
看了一些博客先来个总体感觉1. initf_dm 和 initr_dm 的过程是类似的,下面以initf_dm 为例讲述2. initf_dm 目的在于 初始化一个 树型 数据结构 ,里面存储的是 Device,并初始化 gd->dm_root3. Device 是 从 U_BOOT_DEVICE 和 fdt 中找的4. 在 对 Device 进行 树型 排列的过程中,还要 针对 每一个 Device 做一些 recipe5. recipe 包括 为 Device 找到 Driver , 为 Device 找到 UCLASS6. 因为 Device 是 树型结构,操作一个Device 的时候还可能对 parents 做一些动作
u-boot 运行过程中 board_init_f initf_dm bootstage_start(BOOTSTAGE_ID_ACCUM_DM_F, "dm_f"); ret = dm_init_and_scan(1); // 主要是负责 root driver和root device 的bind 和 probe // core/root.c 中的 U_BOOT_DRIVER(root_driver) // core/root.c 中的 UCLASS_DRIVER(root) // drivers/core/device.c 中的 device_bind_common 动态创建了 U_BOOT_DEVICE(root) dm_init INIT_LIST_HEAD(&DM_UCLASS_ROOT_NON_CONST); device_bind_by_name(NULL, false, &root_info, &DM_ROOT_NON_CONST); drv = lists_driver_lookup_name(info->name); device_bind_common(parent, drv, info->name, (void *)info->platdata, 0, ofnode_null(), platdata_size, devp); uclass_bind_device 没有 drv->bind (((gd_t *)gd)->dm_root)->node = offset_to_ofnode(0); device_probe((((gd_t *)gd)->dm_root)) device_ofdata_to_platdata 没有dev->parent uclass_pre_probe_device clk_set_defaults(dev, 0); 没有drv->probe uclass_post_probe_device // 主要是负责 U_BOOT_DEVICE 声明的 device 和 对应的 driver bind/probe dm_scan_platdata(1) lists_bind_drivers((((gd_t *)gd)->dm_root), pre_reloc_only); bind_drivers_pass const int n_ents = ll_entry_count(struct driver_info, driver_info) = 0; return 0 // 主要是负责 fdt 声明的 device 和 对应的 driver bind/probe // 设备树预处理文件 : output/arch/arm/dts/.s3c64xx-ok6410a.dtb.dts.tmp dm_extended_scan_fdt(gd->fdt_blob,1); dm_scan_fdt(blob, pre_reloc_only); dm_scan_fdt_node(gd->dm_root, blob, 0, pre_reloc_only); 查看节点有没有enable ,没有的话(sdhci@7C300000,sdhci@7C400000)什么都不做,返回.有的话继续 for_each_node lists_bind_fdt // 没有 compatible string 的 node 什么都不做 (chosen,aliases,memory,config,) // 有 compatible 且没有子节点 对每个节点(interrupt-controller@10490000,clock@1800000,serial0@7F005000,sdhci@7C200000) driver_check_compatible device_bind_with_driver_data device_bind_common uclass_bind_device 有 drv->bind则执行bind,没 drv->bind则不执行 // 有 compatible 且有 子节点 对每个节点(包括该节点pinctrl@7f008000和所有子节点gpa-gpq) device_bind_with_driver_data device_bind_common uclass_bind_device 有 drv->bind dm_scan_other(1); bootstage_accum(BOOTSTAGE_ID_ACCUM_DM_F); board_init_r initr_dm gd->dm_root_f = gd->dm_root; gd->dm_root = ((void *)0); bootstage_start(BOOTSTAGE_ID_ACCUM_DM_R, "dm_r"); ret = dm_init_and_scan(0); dm_init(0) dm_scan_platdata(0) dm_extended_scan_fdt(gd->fdt_blob,0); dm_scan_other(0); bootstage_accum(BOOTSTAGE_ID_ACCUM_DM_R); initr_dm_devices // null
初始化流程 initf_dm dm_init_and_scan(true) // pre_reloc_only = 1 // device_bind_by_name // if (pre_reloc_only && !(drv->flags & DM_FLAG_PRE_RELOC)) return; // return表示不做下面的初始化动作 // 也就是说 该设备 需要在 重定位之前初始化 ,则 drv->flags & DM_FLAG_PRE_RELOC != 0 // 这里初始化的设备就是 drv->flags & DM_FLAG_PRE_RELOC != 0 的设备 // 哪些驱动 是 DM_FLAG_PRE_RELOC // pinctrl@7f008000以及子节点 // clock@1800000 // serial0@7F005000 initr_dm dm_init_and_scan(false) // pre_reloc_only = 0消费者(其实这个消费者也是DM架构的一部分,只不过是核心部分的较外层部分) initf_dm initf_dm 之后 initr_dm 之前的消费者(serial_init) 调用device_probe次数:2 serial_init // 主要作用是调用drv->probe 完成硬件的初始化 serial_find_console_or_panic serial_check_stdout uclass_get_device_by_of_offset uclass_get_device_tail device_probe(dev); if (dev->parent) device_probe(dev->parent); if (drv->probe) drv->probe(dev); initr_dm initr_dm 之后的消费者 调用device_probe次数:(18+104)
uclass UCLASS_DRIVER(gpio)struct uclass_driver _u_boot_list_2_uclass_2_gpio __attribute__((__aligned__(4))) __attribute__((unused, section(".u_boot_list_2_""uclass""_2_""gpio"))) = { .id = UCLASS_GPIO, .name = "gpio", .flags = (1 << 0), .post_probe = gpio_post_probe, .post_bind = gpio_post_bind, .pre_remove = gpio_pre_remove, .per_device_auto_alloc_size = sizeof(struct gpio_dev_priv),};Driver U_BOOT_DRIVER(s3c64xx_gpio)struct driver _u_boot_list_2_driver_2_s3c64xx_gpio __attribute__((__aligned__(4))) __attribute__((unused, section(".u_boot_list_2_""driver""_2_""s3c64xx_gpio"))) = { .name = "s3c64xx_gpio", .id = UCLASS_GPIO, .of_match = s3c64xx_gpio_ids, .bind = s3c64xx_gpio_bind, .probe = s3c64xx_gpio_probe, .ops = &gpio_s3c_ops, .flags = (1 << 2),};Device U_BOOT_DEVICE(demo0) 设备树
uclass 遍历: struct uclass_driver *uclass = ll_entry_start(struct uclass_driver, uclass); const int n_ents = ll_entry_count(struct uclass_driver, uclass); struct uclass_driver *entry; for (entry = uclass; entry != uclass + n_ents; entry++) 遍历的应用: lists_uclass_lookup Driver 遍历: 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++) 遍历的应用: lists_driver_lookup_nameDevice 遍历: for (offset = fdt_first_subnode(blob, offset); offset > 0; offset = fdt_next_subnode(blob, offset)) 遍历2: gpio_bank_t *base = (gpio_bank_t *)devfdt_get_addr(parent); for (node = fdt_first_subnode(blob, dev_of_offset(parent)), bank = base; node > 0; node = fdt_next_subnode(blob, node), bank ++) 遍历的应用 : dm_scan_fdt_node s3c64xx_gpio_bind
Uclass Driver Device 只是三个结构体群组driver mode 的初始化作用是 将三个结构体建立联系 // device_bind _common 阶段实现driver 、uclass、device 三者的对接
uclass_driver 主要的成员是 uclass_driver 和 dev_head 链表。 有一个全局变量 ./root.c:139: INIT_LIST_HEAD(&DM_UCLASS_ROOT_NON_CONST); dev_head 是一个链表头, 用来链接该类下的所有设备。 可以通过 uclass_foreach_dev(dev, uc) 遍历该class 下的所有设备。 uclass_driver 是针对某一类设备提供的通用操作接口 通过 UCLASS_DRIVER(_name) 宏申明 uclass 层通过 udevice->driver->ops 获取对应 driver 的操作接口。driver 声明: 通过 U_BOOT_DRIVER(__name) 宏声明。 bind 如果 driver 实现了 bind 接口,则会被调用 probe driver 一般都有对应的 probe 接口 通过 device_probe(struct udevice *dev) 调用 需要注意的是driver 的 bind 接口调用的比 probe 接口早, 大部分在 dm_init_and_scan 中就被调用了。 ops driver 一般会提供 ops 操作接口,供上一层调用。
dm_init_and_scan(bool pre_reloc_only) 1.根据名称 (U_BOOT_DEVICE 中和 driver 的 name,或者 dts 和 driver 的 compatible) 匹配到对应的 driver 2.调用device_bind_common 函数 2.1.生成 udevice // dev = calloc(1, sizeof(struct udevice)); 2.2.绑定 udevice 和 driver // dev->driver = drv; 2.3 绑定 udevice 和 uclass // dev->uclass = uc; 2.3.根据 driver 中的uclass id 找到对应的 uclass driver,并生成相应的 uclass, 并把该设备挂到 uclass 的dev_head之下 // ret = uclass_bind_device(dev); 2.4.调用 driver 的 bind 函数 // if (drv->bind) drv->bind(dev);其他 // 并不存在实际意义上的设备的驱动 //例如MMC 子系统中的 mmc_blk 驱动 // 驱动位于抽象层,它不和具体的硬件设备直接交互,并不适合用一个 dts(dts 是用来描述具体的硬件信息的) 节点或者 U_BOOT_DEVICE(_name) 宏来为这个驱动显示的申明设备。 1. 该驱动主要是把所有的 mmc 设备注册到更上一层的 blk 子系统中 2. 向 blk 层提供操作 mmc 设备的 blk_ops,向下通过mmc uclass 提供的统一接口控制 mmc 设备。 3. 调用device_bind_xxx 系列 API 来完成驱动和设备和更上一层 uclass 之间的 bind
// include/dm/uclass.huclass_foreach_devuclass_get_device_xxx
此时三者已经绑定成功// 以串口为例serial_init serial_find_console_or_panic serial_check_stdout uclass_get_device_by_of_offset uclass_get_device_tail device_probe(dev); if (dev->parent) device_probe(dev->parent); if (drv->probe) drv->probe(dev);
调用时序 bind : initf_dm中/initr_dm中 // post_bind: Called after a new device is bound to this uclass // device_bind_common probe : initf_dm后/initr_dm后 // post_probe: Called after a new device is probed // 模块驱动(mmc/gpio/i2c/serial) lib 中提供的 函数A 封装了 device_probe // 一般在 (mmc_initialize/serial_init) 的时候调用 该 函数A功能: bind : 一般是 xxx-uclass.c 提供的,驱动文件拿来 赋值 给 bind 成员 probe : 硬件初始化
1. 通过 uclass_get_device_xxx 获取 device 句柄2. 通过 uclass 提供的函数 yyy 来控制设备
转载地址:http://lbigi.baihongyu.com/