uboot驱动模型介绍

1、uboot驱动模型简介

1.1、什么是Uboot驱动模型

学过Linux的朋友基本都知道Linux的设备驱动模型,Uboot根据Linux的驱动模型架构,也引入了Uboot的驱动模型(driver model :DM)。
这种驱动模型为驱动的定义和访问接口提供了统一的方法。提高了驱动之间的兼容性以及访问的标准型,uboot驱动模型和kernel中的设备驱动模型类似。

1.2、为什么要有驱动模型呢

1,提高代码的可重用性:为了能够使代码在不同硬件平台,不同体系架构下运行,必须要最大限度的提高代码的可重用性。
2,高内聚,低耦合:分层的思想也是为了达到这一目标,低耦合体现在对外提供统一的抽象访问接口,高内聚将相关度紧密的集中抽象实现。
3,便于管理:在不断发展过程中,硬件设备越来越多,驱动程序也越来越多,为了更好的管理驱动,也需要一套优秀的驱动架构!

1.3、如何使用uboot的DM模型

DM模型的使用,可以通过menuconfig来配置。make menuconfig
①:menuconfig配置全局DM模型
Device Drivers -> Generic Driver Options -> Enable Driver Model
通过上面的路径来打开Driver Model模型,最终配置在.config文件中,CONFIG_DM=y
②:指定某个驱动的DM模型
全局的DM模型打开后,我们对于不通的驱动模块,使能或者失能DM功能。如MMC驱动为例:
Device Drivers -> MMC Host controller Support -> Enable MMC controllers using Driver Model
最终反映在.config文件中的CONFIG_DM_MMC=y
在对应的驱动中,可以看到判断#if !CONFIG_IS_ENABLED(DM_MMC),来判断是否打开DM驱动模型。
在管理驱动的Makefile文件中,也能看到obj-KaTeX parse error: Expected group after '_' at position 8: (CONFIG_̲(SPL_)DM_MMC) += mmc-uclass.o,来判断是否将驱动模型加入到编译选项中。
总之,我们要打开DM模型,最后反映在几个配置信息上:

  • CONFIG_DM=y,全局DM模型打开
  • CONFIG_DM_XXX=y,某个驱动的DM模型的打开
  • 可以通过Kconifg、Makefile来查看对应宏的编译情况

2、DM模型整体架构

2.1、DM的四个组成部分

  • udevice
    驱动程序实例,就是指设备对象,可以理解为kernel中的device。
  • driver
    udevice的驱动,和底层硬件设备通信,并且为设备提供面向上层的接口。
  • uclass
    管理着对应某一个类别下的所有的udevice。
    例如:一个IIC驱动程序,其驱动程序框架是一致的,只有一种,但是IIC驱动的设备可以有很多,如EEPROM,MCU等;所有在这里呢,dev_head链表就是用来管理该驱动类下的所有的设备。
  • uclass_driver
    对应uclass的驱动程序。主要提供uclass操作时,如绑定、激活、移除udevice时的一些操作接口。

2.2、相互之间的关系

  • driver会和udevice绑定。uclass_driver会和uclass绑定。udevice会和uclass绑定。
  • udevice找到对应的uclass的方式主要是通过:udevice对应的driver的id和uclass对应的uclass_driver的id是否匹配。
    这里先简单介绍一下:uclass和udevice都是动态生成的。在解析设备树中的设备或直接定义的平台设备的时候,会动态生成udevice。然后找到udevice对应的driver,通过driver中的uclass id得到uclass_driver id。从uclass链表中查找对应的uclass是否已经生成,没有生成的话则动态生成uclass。

3、DM四个主要组成部分详细介绍

后续以数据结构、如何定义、存放位置、如何获取四个部分进行说明。

3.0、uclass id

每一种uclass都有自己对应的ID号。定义于其uclass_driver中。其附属的udevice的driver中的uclass id必须与其一致。
所有uclass id定义于include/dm/uclass-id.h中
列出部分id如下

1.enum uclass_id {  
2.    /* These are used internally by driver model */  
3.    UCLASS_ROOT = 0,  
4.    UCLASS_DEMO,  
5.    UCLASS_TEST,  
6.    UCLASS_TEST_FDT,  
7.    UCLASS_TEST_FDT_MANUAL,  
8.    UCLASS_TEST_BUS,  
9.    UCLASS_TEST_PROBE,  
10.   UCLASS_TEST_DUMMY,  
11.        ......  
12.}  

3.1、uclass

(1)数据结构

1.struct uclass {  
2.    void *priv;  // uclass的私有数据指针  
3.    struct uclass_driver *uc_drv; // 对应的uclass driver  
4.    struct list_head dev_head; // 链表头,连接所属的所有udevice  
5.    struct list_head sibling_node; // 链表节点,用于把uclass连接到uclass_root链表上
6.}; 

(2)如何定义
uclass是uboot自动生成。并且不是所有uclass都会生成,有对应uclass driver并且有被udevice匹配到的uclass才会生成。
具体参考后面的uboot DM初始化一节。或者参考uclass_add实现。
(3)存放位置
所有生成的uclass都会被挂载gd->uclass_root链表上。

(4)如何获取、API
直接遍历链表gd->uclass_root链表并且根据uclass id来获取到相应的uclass。
具体uclass_get->uclass_find实现了这个功能。

1.int uclass_get(enum uclass_id id, struct uclass **ucp)  
2.{  
3.    struct uclass *uc;  
4.  
5.    /* Immediately fail if driver model is not set up */  
6.    if (!gd->uclass_root)  
7.        return -EDEADLK;  
8.    *ucp = NULL;  
9.    uc = uclass_find(id);  
10.    if (!uc) {  
11.        if (CONFIG_IS_ENABLED(OF_PLATDATA_INST))  
12.            return -ENOENT;  
13.        return uclass_add(id, ucp);  
14.    }  
15.    *ucp = uc;  
16.  
17.    return 0;  
18.}  

3.2、uclass_driver

(1)数据结构
include/dm/uclass.h

1.struct uclass_driver {  
2.    const char *name; // 该uclass_driver的名称  
3.    enum uclass_id id; // 对应的uclass id  
4./* 以下函数指针主要是调用时机的区别 */  
5.    int (*post_bind)(struct udevice *dev); // 在udevice被绑定到该uclass之后调用(bind相关函数指针在device_bind_common函数中调用)  
6.    int (*pre_unbind)(struct udevice *dev); // 在udevice被解绑出该uclass之前调用 (在device_unbind函数中调用) 
7.    int (*pre_probe)(struct udevice *dev); // 在该uclass的一个udevice进行probe之前调用 (probe相关函数指针在device_probe函数中调用) 
8.    int (*post_probe)(struct udevice *dev); // 在该uclass的一个udevice进行probe之后调用  
9.    int (*pre_remove)(struct udevice *dev);// 在该uclass的一个udevice进行remove之前调用  (在device_remove函数中调用)
10.    int (*child_post_bind)(struct udevice *dev); // 在该uclass的一个udevice的一个子设备被绑定到该udevice之后调用  
11.    int (*child_pre_probe)(struct udevice *dev); // 在该uclass的一个udevice的一个子设备进行probe之前调用  
12.    int (*child_post_probe)(struct udevice *dev);// 在该uclass的一个udevice的一个子设备进行probe之后调用  
13.    int (*init)(struct uclass *class); // 安装该uclass的时候调用(在uclass_add中调用)  
14.    int (*destroy)(struct uclass *class); // 销毁该uclass的时候调用  
15.    int priv_auto;// 如果非零,它就是"uclass->priv"指针中分配的uclass私有数据的大小。  
16.    int per_device_auto;  // uclass拥有的私有数据,保存在'dev->uclass_priv_'。如果该值是非零,将自动分配该值大小的空间。
17.    int per_device_plat_auto;  	//uclass拥有的平台数据,保存在'dev->uclass_plat_'。如果该值是非零,将自动分配该值大小的空间
18.    int per_child_auto;   //子设备(在这个uclass中一个parent的child)可以保存的parent私有数据。地址为'dev->parent_priv_'
19.    int per_child_plat_auto;  // 子设备可以保存的parent平台数据。如果非零,将分配到子设备的'dev->parent_plat_'指针中。
20.    uint32_t flags; // 这个uclass的标志
21.};  

(2)如何定义
通过UCLASS_DRIVER来定义uclass_driver.
以serial-uclass为例

1.UCLASS_DRIVER(serial) = {  
2.    .id     = UCLASS_SERIAL,  
3.    .name       = "serial",  
4.    .flags      = DM_UC_FLAG_SEQ_ALIAS,  
5.    .post_probe = serial_post_probe,  
6.    .pre_remove = serial_pre_remove,  
7.    .per_device_auto    = sizeof(struct serial_dev_priv),  
8.};  

UCLASS_DRIVER实现如下:

1.#define UCLASS_DRIVER(__name)                       \  
2.    ll_entry_declare(struct uclass_driver, __name, uclass)  
3.   
4.#define ll_entry_declare(_type, _name, _list)               \  
5.    _type _u_boot_list_2_##_list##_2_##_name __aligned(4)       \  
6.            __attribute__((unused,              \  
7.            section(".u_boot_list_2_"#_list"_2_"#_name)))  

最终得到一个如下结构体:

1.struct uclass_driver  _u_boot_list_2_uclass_2_serial __aligned(4) \  
2.__attribute__((unused,  section(".u_boot_list_2_uclass_2_serial"))) = {    
3.    .id        = UCLASS_SERIAL,   // 设置对应的uclass id    
4.    .name        = "serial",    
5.    .flags        = DM_UC_FLAG_SEQ_ALIAS,       
6.    .post_probe    = serial_post_probe,    
7.    .pre_remove    = serial_pre_remove,    
8.    .per_device_auto = sizeof(struct serial_dev_priv),    
9.}   

(3)存放位置
通过上述,我们知道serial的uclass_driver结构体_u_boot_list_2_uclass_2_serial被存放到.u_boot_list_2_uclass_2_serial段中(section段内容)。
通过查看u-boot.map得到如下

1..u_boot_list_2_uclass_1  
2.                0x23e368e0        0x0 drivers/built-in.o  
3..u_boot_list_2_uclass_2_gpio  
4.                0x23e368e0       0x48 drivers/gpio/built-in.o  
5.                0x23e368e0             _u_boot_list_2_uclass_2_gpio  // gpio uclass driver的符号  
6..u_boot_list_2_uclass_2_root  
7.                0x23e36928       0x48 drivers/built-in.o  
8.                0x23e36928              _u_boot_list_2_uclass_2_root // root uclass drvier的符号  
9..u_boot_list_2_uclass_2_serial  
10.                0x23e36970       0x48 drivers/serial/built-in.o  
11.                0x23e36970          _u_boot_list_2_uclass_2_serial // serial uclass driver的符号  
12..u_boot_list_2_uclass_2_simple_bus  
13.                0x23e369b8       0x48 drivers/built-in.o  
14.                0x23e369b8                _u_boot_list_2_uclass_2_simple_bus  
15..u_boot_list_2_uclass_3  
16.                0x23e36a00        0x0 drivers/built-in.o  
17.                0x23e36a00                . = ALIGN (0x4)  

最终,所有uclass driver结构体以列表的形式被放在.u_boot_list_2_uclass_1和.u_boot_list_2_uclass_3的区间中。
这个列表简称uclass_driver table。
(4)如何获取、API
想要获取uclass_driver需要先获取uclass_driver table。
可以通过以下宏来获取uclass_driver table

1. struct uclass_driver *uclass =  
2.    ll_entry_start(struct uclass_driver, uclass_driver); // 会根据.u_boot_list_2_uclass_driver _1的段地址来得到uclass_driver table的首地址  
3. const int n_ents = ll_entry_count(struct uclass_driver, uclass_driver);//获取.u_boot_list_2_uclass_driver _3的段地址,减去.u_boot_list_2_uclass_driver _1的段地址得到uclass_driver table的长度  

接着通过遍历这个uclass_driver table,得到相应的uclass_driver。
有如下API(从driver table中获取指定id的uclass_driver)

1.struct uclass_driver *lists_uclass_lookup(enum uclass_id id)    
2.{  
3.    struct uclass_driver *uclass =  
4.        ll_entry_start(struct uclass_driver, uclass_driver);  
5.    const int n_ents = ll_entry_count(struct uclass_driver, uclass_driver);  
6.    struct uclass_driver *entry;  
7.   
8.    for (entry = uclass; entry != uclass + n_ents; entry++) {  
9.        if (entry->id == id)  
10.            return entry;  
11.    }  
12.   
13.    return NULL;  
14.}  

3.3、udevice

(1)数据结构
include/dm/device.h

1.struct udevice {  
2.    const struct driver *driver;  //此设备使用的驱动程序  
3.    const char *name;   //设备名称,通常为 FDT 节点名称  
4.    void *plat_;     //此设备的配置数据  
5.    void *parent_plat_;   //提供给父设备使用的配置数据  
6.    void *uclass_plat_;    //提供给所属uclass使用的配置数据  
7.    ulong driver_data;    //驱动程序数据  
8.    struct udevice *parent; // 父设备  
9.    void *priv_;  // 私有数据的指针  
10.    struct uclass *uclass; // 所属uclass  
11.    void *uclass_priv_;  // 提供给所属uclass使用的私有数据指针  
12.    void *parent_priv_;   // 提供给其父设备使用的私有数据指针  
13.    struct list_head uclass_node;  // 用于连接到其所属uclass的链表上  
14.    struct list_head child_head;   // 链表头,连接其子设备  
15.    struct list_head sibling_node;  //设备列表中的下一个设备  
16.#if !CONFIG_IS_ENABLED(OF_PLATDATA_RT)  
17.    u32 flags_;    //设备标识  
18.#endif  
19.    int seq_;  
20.#if CONFIG_IS_ENABLED(OF_REAL)  
21.    ofnode node_;  
22.#endif  
23.#if CONFIG_IS_ENABLED(DEVRES)  
24.    struct list_head devres_head;  
25.#endif  
26.#if CONFIG_IS_ENABLED(DM_DMA)  
27.    ulong dma_offset;  
28.#endif  
29.};  

(2)如何定义
1,通过宏U_BOOT_DRVINFO静态定义
2,在dtb存在的情况下,由uboot解析dtb后动态生成,后续在“uboot DM的初始化”一节中具体说明。
(3)存放位置

  • 连接到对应uclass中
    也就是会连接到uclass->dev_head中
  • 连接到父设备的子设备链表中
    也就是会连接到udevice->child_head中,并且最终的根设备是gd->dm_root这个根设备。
    (4)如何获取、API
  • 从uclass中获取udevice
    遍历uclass->dev_head,获取对应的udevice。有如下API
1.int uclass_get_device(enum uclass_id id, int index, struct udevice **devp); // 通过索引从uclass中获取udevice  
2.int uclass_get_device_by_name(enum uclass_id id, const char *name, struct udevice **devp);  // 通过设备名从uclass中获取udevice  
3.int uclass_get_device_by_seq(enum uclass_id id, int seq, struct udevice **devp); // 通过序列从uclass中获取udevice  
4.int uclass_get_device_by_of_offset(enum uclass_id id, int node, struct udevice **devp);  //通过设备偏移量从uclass中获取udevice  
5.int uclass_get_device_by_ofnode(enum uclass_id id, ofnode node,struct udevice **devp);  //通过设备树节点从uclass中获取udevice  
6.int uclass_get_device_by_driver(enum uclass_id id, const struct driver *drv, struct udevice **devp);  //通过驱动从uclass中获取udevice  
7.int uclass_first_device(enum uclass_id id, struct udevice **devp);  //获取uclass下的第一个设备  
8.int uclass_next_device(struct udevice **devp);  //获取uclass中的下一个udevice  

这些相关的API(uclass.c),主要作用就是根据uclass_id,查找对应的uclass,然后根据索引值或者名称,来查找到对应的udevice,之后调用device_probe函数激活设备。

3.4、driver

和uclass_driver方式是相似的。
(1)数据结构
include/dm/device.h

1.struct driver {  
2.    char *name; // device名称  
3.    enum uclass_id id; // 标记driver属于哪个uclass的id  
4.    const struct udevice_id *of_match; // 要匹配的compatible字符串列表,以及每个字符串的标识数据。  
5.    int (*bind)(struct udevice *dev); // 绑定device到它的driver时被调用(在device_bind_common函数中调用)
6.    int (*probe)(struct udevice *dev); // 探测一个device时被调用,例如:激活它(在device_probe函数中调用)
7.    int (*remove)(struct udevice *dev); // 移除一个device时被调用,例如:停用它(在device_remove函数中调用)
8.    int (*unbind)(struct udevice *dev); // 解除device和driver绑定时被调用(在device_unbind函数中调用)  
9.    int (*of_to_plat)(struct udevice *dev); // 在probe设备之前调用,解码device tree数据(在device_probe函数中调用)
10.    int (*child_post_bind)(struct udevice *dev); // 在目标设备的一个子设备被绑定之后,调用(在device_bind_common函数中调用)  
11.    int (*child_pre_probe)(struct udevice *dev); // 在目标设备的一个子设备被probe之前,调用(在device_probe函数中调用)  
12.    int (*child_post_remove)(struct udevice *dev); // 在目标设备的一个子设备被remove之后,调用(在device_remove函数中调用)  
13.    int priv_auto; // 如果非零,它就是"udevice->priv"指针中分配的私有数据的大小。如果为0,则driver负责分配所需私有数据的空间。  
14.    int plat_auto; // 如果非零,它就是"udevice->plat_"指针中分配的平台数据的大小。这通常只对设备树感知的驱动程序有用(那些带有of_match的驱动程序)  
15.    int per_child_auto; // device包含的父设备的私有数据。如果需要,该值是非零,将自动分配该数据所需的空间,并保存到子节点的parent_priv_指针中。  
16.    int per_child_plat_auto; // device包含的父设备的平台数据。如果非零,则分配该数据所需的空间,并保存到子节点的parent_plat_指针中。  
17.    const void *ops; // driver的具体操作,这通常是一个由driver定义的函数指针列表,用于实现device所需的驱动程序函数。  
18.    uint32_t flags; // driver flags - see DM_FLAGS_...  
19.#if CONFIG_IS_ENABLED(ACPIGEN)  
20.    struct acpi_ops *acpi_ops;  
21.#endif  
22.};  

(2)如何定义
通过U_BOOT_DRIVER来定义一个driver
以s5pv210为例:
driver/serial/serial_s5p.c

1.U_BOOT_DRIVER(serial_s5p) = {  
2.    .name   = "serial_s5p",  
3.    .id = UCLASS_SERIAL,  
4.    .of_match = s5p_serial_ids,  
5.    .of_to_plat = s5p_serial_of_to_plat,  
6.    .plat_auto  = sizeof(struct s5p_serial_plat),  
7.    .probe = s5p_serial_probe,  
8.    .ops    = &s5p_serial_ops,  
9.};  

U_BOOT_DRIVER实现如下:

1.#define U_BOOT_DRIVER(__name)                        \  
2.    ll_entry_declare(struct driver, __name, driver)  
3.   
4.#define ll_entry_declare(_type, _name, _list)               \  
5.    _type _u_boot_list_2_##_list##_2_##_name __aligned(4)       \  
6.            __attribute__((unused,              \  
7.            section(".u_boot_list_2_"#_list"_2_"#_name)))  

最终得到如下一个结构体

1.struct driver _u_boot_list_2_driver_2_serial_s5p= {  
2.    .name   = "serial_s5p",  
3.    .id = UCLASS_SERIAL,  
4.    .of_match = s5p_serial_ids,  
5.    .of_to_plat = s5p_serial_of_to_plat,  
6.    .plat_auto  = sizeof(struct s5p_serial_plat),  
7.    .probe = s5p_serial_probe,  
8.    .ops    = &s5p_serial_ops,  
9.};  

(3)存放位置
通过上述,我们知道serial_s5p的driver结构体_u_boot_list_2_driver_2_serial_s5p被存放在.u_boot_list_2_driver_2_serial_s5p段中。
通过查看u-boot.map得到如下

1..u_boot_list_2_driver_1  
2.                0x23e36754        0x0 drivers/built-in.o  
3..u_boot_list_2_driver_2_gpio_exynos  
4.                0x23e36754       0x44 drivers/gpio/built-in.o  
5.                0x23e36754                _u_boot_list_2_driver_2_gpio_exynos  
6..u_boot_list_2_driver_2_root_driver  
7.                0x23e36798       0x44 drivers/built-in.o  
8.                0x23e36798                _u_boot_list_2_driver_2_root_driver  
9..u_boot_list_2_driver_2_serial_s5p  
10.                0x23e367dc       0x44 drivers/serial/built-in.o  
11.                0x23e367dc                _u_boot_list_2_driver_2_serial_s5p  
12..u_boot_list_2_driver_2_simple_bus_drv  
13.                0x23e36820       0x44 drivers/built-in.o  
14.                0x23e36820                _u_boot_list_2_driver_2_simple_bus_drv  
15..u_boot_list_2_driver_3  
16.                0x23e36864        0x0 drivers/built-in.o  

最终,所有driver结构体以列表的形式被放在.u_boot_list_2_driver_1和.u_boot_list_2_driver_3的区间中。
这个列表简称driver table。
(4)如何获取、API
想要获取driver需要先获取driver table。
可以通过以下宏来获取driver table

1.    struct driver *drv =  
2.        ll_entry_start(struct driver, driver);  
3.// 会根据.u_boot_list_2_driver_1的段地址来得到uclass_driver table的地址  
4.  
5.    const int n_ents = ll_entry_count(struct driver, driver);  
6.// 获得driver table的长度  

接着通过遍历这个driver table,得到相应的driver。

1.struct driver *lists_driver_lookup_name(const char *name)  
2.// 从driver table中获取名字为name的driver。

4、uboot 中设备的表达

4.1、说明

uboot中可以通过两种方法来添加设备

  • 通过直接定义平台设备(这种方式基本上不使用)
  • 通过在设备树添加设备信息
    注意:这里只是设备的定义,最终还是会被uboot解析成udevice结构体的。

4.2、直接定义平台设备(这种方式除了根设备外基本上不使用)

(1)通过U_BOOT_DRVINFO宏来进行定义或者直接定义struct driver_info结构体
(2)U_BOOT_DRVINFO宏以ns16550_serial为例

1.U_BOOT_DRVINFO(devkit8000_uart) = {  
2.    "ns16550_serial",  
3.    &devkit8000_serial  
4.};  

U_BOOT_DRVINFO实现如下:
和上述的U_BOOT_DRIVER类似,

1.#define U_BOOT_DRVINFO(__name)                      \  
2.    ll_entry_declare(struct driver_info, __name, driver_info)  

注:在老版本的uboot中,没有U_BOOT_DRVINFO,而是用U_BOOT_DEVICE替代

(3)直接定义struct driver_info结构体,以根设备为例 uboot会创建一个根设备root,作为所有设备的根设备,root的定义如下:

1.static struct driver_info root_info = {  
2.    .name       = "root_driver",  
3.};  

4.3、在设备树添加设备信息

在对应的dts文件中添加相应的设备节点和信息,以tiny210的serial为例:
arch/arm/dts/s5pv210-tiny210.dts

/dts-v1/;
#include "skeleton.dtsi"
/{
        aliases {
                console = "/serial@e2900000";
        };  
 
        serial@e2900000 {
                compatible = "samsung,exynos4210-uart";
                reg = <0xe2900000 0x100>;
                interrupts = <0 51 0>; 
                id = <0>;
        };  
};

5、uboot DM的初始化

5.1、主要工作

  • DM根设备的初始化
    创建根设备root的udevice,存放在gd->dm_root中。
    根设备其实是一个虚拟设备,主要是为uboot的其他设备提供一个挂载点。
    初始化uclass链表gd->uclass_root
  • DM中udevice和uclass的解析
    udevice的创建和uclass的创建
    udevice和uclass的绑定
    uclass_driver和uclass的绑定
    driver和udevice的绑定
    部分driver和uclass_driver函数的调用

5.2、入口说明

dm初始化的接口在dm_init_and_scan中。
可以发现在uboot relocate之前的initf_dm和之后的initr_dm都调用了这个函数。

1.static int initf_dm(void)  
2.{  
3.#if defined(CONFIG_DM) && CONFIG_VAL(SYS_MALLOC_F_LEN)  
4.    int ret;  
5.    bootstage_start(BOOTSTAGE_ID_ACCUM_DM_F, "dm_f");  
6.    ret = dm_init_and_scan(true);   // 调用dm_init_and_scan对DM进行初始化和设备的解析  
7.    bootstage_accum(BOOTSTAGE_ID_ACCUM_DM_F);  
8.    if (ret)  
9.        return ret;  
10.    if (IS_ENABLED(CONFIG_TIMER_EARLY)) {  
11.        ret = dm_timer_init();  
12.        if (ret)  
13.            return ret;  
14.    }  
15.#endif  
16.   
17.    return 0;  
18.} 
1.#ifdef CONFIG_DM  
2.static int initr_dm(void)  
3.{  
4.    int ret;  
5.    /* Save the pre-reloc driver model and start a new one */  
6.    gd->dm_root_f = gd->dm_root;  // 存储relocate之前的根设备  
7.    gd->dm_root = NULL;  
8.#ifdef CONFIG_TIMER  
9.    gd->timer = NULL;  
10.#endif  
11.    bootstage_start(BOOTSTAGE_ID_ACCUM_DM_R, "dm_r");  
12.    ret = dm_init_and_scan(false);  // 调用dm_init_and_scan对DM进行初始化和设备的解析  
13.    bootstage_accum(BOOTSTAGE_ID_ACCUM_DM_R);  
14.    if (ret)  
15.        return ret;  
16.    return 0;  
17.}  
18.#endif 

主要区别在于参数。
首先说明一下dts节点中的“u-boot,dm-pre-reloc”属性,当设置了这个属性时,则表示这个设备在relocate之前就需要使用。

当dm_init_and_scan的参数为true时,只会对带有“u-boot,dm-pre-reloc”属性的节点进行解析。而当参数为false的时候,则会对所有节点都进行解析。

5.3、DM初始化流程

1.initr_dm  
2.    dm_init_and_scan  //对DM进行初始化和设备的解析  
3.        dm_init    //初始化DM根设备  
4.            device_bind_by_name(NULL, false, &root_info,&DM_ROOT_NON_CONST)  
5.                //DM_ROOT_NON_CONST是指根设备udevice,root_info为driver_info结构体类型,表示根设备的					设备信息  
6.                //device_bind_by_name会查找和设备信息匹配的driver,然后创建对应的udevice和uclass并进					行绑定,最后放在DM_ROOT_NON_CONST中  
7.                lists_driver_lookup_name(info->name)  //根据driver_info的name成员找到对应的driver												结构体  
8.                    ll_entry_start(struct driver, driver)   //获取.u_boot_list_2_driver_1的段地址,													作为driver table的首地址  
9.                    ll_entry_count(struct driver, driver)   //获取driver table的长度  
10.                device_bind_common      //根据driver结构体,创建根设备的uclass结构体和udevice结构体									并进行绑定  
11.                    uclass_get(drv->id, &uc)   //根据uclass_id获取uclass结构体  
12.                        uclass_find        //通过匹配uclass->uclass_driver->id获取uclass结构体  
13.                        uclass_add         //上一步未获取到uclass结构体则创建对应的uclass结构体,并与										其对应的uclass_driver进行绑定  
14.                            lists_uclass_lookup     //获取id对应的uclass_driver结构体,未获取到则报												错返回  
15.                     uc->uc_drv = uc_drv    //将uclass与uclass_driver进行绑定  
16.                             list_add       //将uclass结构体添加到global_data->uclass_root链										    表中 
17.                    dev->driver = drv     //绑定udevice与driver  
18.                    uclass_bind_device     //绑定uclass结构体和udevice结构体,主要是实现了将udevice										链接到uclass的设备链表中  
19.            device_probe    //对根设备进行probe操作  
20.        dm_scan  
21.            dm_scan_plat   //从平台设备中解析udevice和uclass并绑定  
22.                lists_bind_drivers   //根设备的udevice作为父节点,以参数传入  
23.                    bind_drivers_pass  
24.                        device_bind_by_name  //根据传入的driver_info信息,创建对应的udevice和uclass										 并进行绑定  
25.            dm_extended_scan    //从dtb中解析udevice和uclass并绑定  
26.                dm_scan_fdt  
27.                    dm_scan_fdt_node  
28.                        //传入参数,parent=gd->dm_root,表示以root设备作为父设备开始解析  
29.                        //传参parent_node为设备树根节点  
30.                        //pre_reloc_only=0,不只是解析relotion之前的设备  
31.                        ofnode_first_subnode    //获取设备树的第一个子节点  
32.                        ofnode_next_subnode     //查找下一个子节点  
33.                        ofnode_is_enabled       //判断节点状态是否是disable,如果是的话直接忽略  
34.                        lists_bind_fdt     //从dtb中解析udevice和uclass并绑定  
35.                            ll_entry_start(struct driver, driver)    // 获取driver table地址  
36.                            ll_entry_count(struct driver, driver)    // 获取driver table长度  
37.                            ofnode_get_property(node, "compatible", &compat_length)     //获取设备															树节点compatible属性  
38.                            driver_check_compatible      //判断driver中的of_match->compatibile字段													和dts节点是否匹配  
39.                            device_bind_with_driver_data  
40.                                device_bind_common       //根据driver结构体,创建对应设备的uclass结           													构体和udevice结构体并进行绑定  
41.                dm_scan_fdt_ofnode_path     //一些节点本身不是设备,但是包含一些设备,遍历其包含的设备  
42.                    ofnode_path       //找到节点下包含的设备  
43.                    dm_scan_fdt_node   
44.            dm_scan_other    

6、DM各部分的绑定关系

udevice与driver的绑定:
1,设备树形式的udevice:通过驱动的of_match和compatible属性来配对,绑定。
2,U_BOOT_DRVINFO定义的udevice:通过name属性来绑定。

udevice与uclass的绑定:udevice内的driver下的uclass_id,来与uclass对应的uclass_driver的uclass_id进行匹配。

uclass与uclass_driver的绑定:已知udevice内的driver下的uclass_id,创建uclass的同时,通过`uclass_id找到对应的uclass_driver对象,然后将uclass_driver绑定到uclass上!

7、DM工作流程

经过前面的DM初始化以及设备解析之后,我们只是建立了udevice和uclass之间的绑定关系。但是此时udevice还没有被probe,其对应设备还没有被激活。
激活一个设备主要是通过device_probe函数,所以在DM的工作流程的最后一步就是调用device_probe函数。

7.1 工作流程简单说明

(1)代码支持
以mtd-uclass为例:
<1>mtd-uclass.c中定义一个uclass_driver

1.UCLASS_DRIVER(mtd) = {  
2.    .id     = UCLASS_MTD,   //注意这里的uclass id  
3.    .name       = "mtd",  
4.    .per_device_auto    = sizeof(struct mtd_info),  
5.};  

<2>定义设备
通过设备树定义

1.&nand_controller {  
2.    compatible="marvell,mvebu-pxa3xx-nand";   //注意这里的compatible  
3.    status = "okay";  
4.    label = "pxa3xx_nand-0";  
5.    nand-rb = <0>;  
6.    marvell,nand-keep-config;  
7.    nand-on-flash-bbt;  
8.    nand-ecc-strength = <4>;  
9.    nand-ecc-step-size = <512>;  
10.};  

直接定义

1.U_BOOT_DRVINFO(paa3xx_nand) = {  
2.    .name  = "pxa3xx-nand",    //注意这里的name  
3.};  

<3>定义设备驱动

1.U_BOOT_DRIVER(pxa3xx_nand) = {  
2.    .name = "pxa3xx-nand",   //注意这里的name  
3.    .id = UCLASS_MTD,    //注意这里的uclass_id  
4.    .of_match = pxa3xx_nand_dt_ids,  
5.    .probe = pxa3xx_nand_probe,  
6.    .priv_auto  = sizeof(struct pxa3xx_nand_info) +  
7.        sizeof(struct pxa3xx_nand_host) * CONFIG_SYS_MAX_NAND_DEVICE,  
8.};  
9.  
10.static const struct udevice_id pxa3xx_nand_dt_ids[] = {  
11.    {  
12.        .compatible = "marvell,mvebu-pxa3xx-nand",  //注意这里的compatible  
13.        .data = PXA3XX_NAND_VARIANT_ARMADA370,  
14.    },  
15.    {  
16.        .compatible = "marvell,armada-8k-nand-controller",  
17.        .data = PXA3XX_NAND_VARIANT_ARMADA_8K,  
18.    },  
19.    {}  
20.};  

<4>定义初始化入口

1.void board_nand_init(void)  
2.{  
3.    struct udevice *dev;  
4.    int ret;  
5.  
6.    ret = uclass_get_device_by_driver(UCLASS_MTD,  
7.            DM_DRIVER_GET(pxa3xx_nand), &dev);  
8.    if (ret && ret != -ENODEV) {  
9.        pr_err("Failed to initialize %s. (error %d)\n", dev->name,  
10.               ret);  
11.    }  
12.}  

(2)udevice和对应uclass的创建
在DM初始化的过程中uboot自己创建对应的udevice和uclass。
(3)udevice和对应uclass的绑定
在DM初始化的过程中uboot自己实现将udevice绑定到对应的uclass中。
(4)对应udevice的probe
由模块自己实现。例如nand则需要在初始化过程中,选择需要的udevice进行probe。初始化流程如下initr_nand->nand_init->board_nand_init->uclass_get_device_by_driver->
uclass_get_device_tail->device_probe。
uclass_get_device_by_driver函数,通过uclass_id获取对应uclass,从uclass的设备链表中获取udevice,udevice->driver与DM_DRIVER_GET(pxa3xx_nand)匹配上,则调用udevice对应的driver进行probe。

你可能感兴趣的:(u-boot,linux,驱动开发)