作者: baron

一、数据结构

1、udevice

  用于描述具体的硬件设备, 在当前的 dm 模型中, 在 uboot 启动的时候扫描 dts 自动创建. 详情参考 dts 加载和 dm 模型的本质. 其中需要关注的数据结构有 priv 常用于设置设备硬件私有数据结构. 通过接口void *dev_get_priv(struct udevice *dev)返回.

struct udevice {  
    const struct driver *driver;   // 在 device_bind_common 中连接对应的 drv
    const char *name;              // 自动创建时, 由匹配到的 drv->name 设置. 也可以通过参数传入设置.
    void *platdata;                // 在 device_bind_common 由 drv->platdata_auto_alloc_size 指定
    void *parent_platdata;         // 在 device_bind_common 由 parent->driver->per_child_platdata_auto_alloc_size;
    void *uclass_platdata;         // 在 device_bind_common 由 uc->uc_drv->per_device_platdata_auto_alloc_size; 指定
    ofnode node;
    ulong driver_data;              // 由  device_bind_common 传入的参数设置
    struct udevice *parent;         // 在 device_bind_common 设置, 连接父设备
    void *priv;                     // 在 device_probe 中设置,由 dev->drv->priv_auto_alloc_size 决定大小
    struct uclass *uclass;          // 一般在 device_bind_common 中由 
    void *uclass_priv;              // 在 device_probe 中设置,由 dev->uclass->uc_drv->per_device_auto_alloc_size 
    // 在 device_probe 中设置, 由 dev->parent->driver->per_child_auto_alloc_size 指定大小. 
    // 如果不存在则由 dev->parent->uclass->uc_drv->per_child_auto_alloc_size 指定大小
    void *parent_priv;   
    struct list_head uclass_node;  // 在 device_bind_common 中的 uclass_bind_device 设置, 连接到 uc->dev_head
    struct list_head child_head;   // 在子设备创建时设置, 连接子设备的 sibling_node
    struct list_head sibling_node; // 在 device_bind_common 中设置, 连接到 parent->child_head
    uint32_t flags;          
    int req_seq;
    int seq;                       //  // 在 device_probe 中设置, 表示 device 是第几个被注册, 由于他的唯一性, 因此也可以通过 seq 查找设备.
#ifdef CONFIG_DEVRES
    struct list_head devres_head;
#endif
};

2、driver

  对应的 udevice 的驱动, 其中 probe 用于驱动的初始化. 已经 probe 就表示该设备已就绪可以使用. ofdata_to_platdata 接口在 probe 之前调用, 用于解析设备树. ops 则用于创建 drv 真的的操作接口.

struct driver {
    char *name;
    enum uclass_id id;
    const struct udevice_id *of_match;
    int (*bind)(struct udevice *dev);               // 第三个在 device_bind_common 中被调用, 这个接口比较常用
    int (*probe)(struct udevice *dev);              // 在 device_probe 中第 5 个被调用
    int (*remove)(struct udevice *dev);      
    int (*unbind)(struct udevice *dev);
    int (*ofdata_to_platdata)(struct udevice *dev); // 在 device_probe 中第 4 个被调用
    int (*child_post_bind)(struct udevice *dev);    // 在子设备的 device_bind_common 中第四个被调用
    int (*child_pre_probe)(struct udevice *dev);    // 在 device_probe 中第 3 个被调用
    int (*child_post_remove)(struct udevice *dev);
    int priv_auto_alloc_size;       // 在 device_probe 中指定  dev->priv 的大小
    int platdata_auto_alloc_size;   // 在 device_bind_common 中指定 dev->platdata 的大小
    int per_child_auto_alloc_size;  // 在 device_probe 中指定 child_dev->parent_priv 的大小
    int per_child_platdata_auto_alloc_size;
    const void *ops;    /* driver-specific operations */
    uint32_t flags;
};

drv 需要手动创建, 通过 U_BOOT_DRIVER 创建.

#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)))

  展开后得到.

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.

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;

    // 遍历所有的 drv 返回对应 name 的 drv
    for (entry = drv; entry != drv + n_ents; entry++) {
        if (!strcmp(name, entry->name))
            return entry;
    }

    /* Not found */
    return NULL;
}

3、uclass

  在创建 udevice 时自动创建, 管理一类设备, 即同类的 udevice 由 uclass 进行统一管理. 每一个 uclalss 都有一个唯一的 uc_drv->uclass_id 进行描述.

struct uclass {
    void *priv;                        // 在 device_bind_common 由 uc_drv->priv_auto_alloc_size 指定
    struct uclass_driver *uc_drv;      // 对应的 uclass drv
    struct list_head dev_head;         // 用于连接所属的 udevice
    struct list_head sibling_node;     // 连接到 gd->uclass_root
};

4、uclass_driver

  给出该类设备的统一接口. post_probe 接口常用来设置该类设备共有属性, 例如 i2c 的速率. post_bind 接口则常设置为 dm_scan_fdt_dev 用于扫描并创建其下的子设备 udevice. 和 driver 类似通过 UCLASS_DRIVER创建

struct uclass_driver {
    const char *name;
    enum uclass_id id;                                 // 所属的 uclass
    int (*post_bind)(struct udevice *dev);             // 第五个在 device_bind_common 中被调用
    int (*pre_unbind)(struct udevice *dev);
    int (*pre_probe)(struct udevice *dev);             // 在 device_probe 中第 1 个被调用
    int (*post_probe)(struct udevice *dev);            // 在 device_probe 中第 6 个被调用
    int (*pre_remove)(struct udevice *dev);
    int (*child_post_bind)(struct udevice *dev);       // 第二个在 device_bind_common 中被调用
    int (*child_pre_probe)(struct udevice *dev);       // 在 device_probe 中第 2 个被调用 
    int (*init)(struct uclass *class);                 // 第一个在 device_bind_common 中被调用
    int (*destroy)(struct uclass *class);
    int priv_auto_alloc_size;
    int per_device_auto_alloc_size;                    // 在 device_probe 中为 dev->uclass_priv 分配 dev->uclass->uc_drv->per_device_auto_alloc_size
    int per_device_platdata_auto_alloc_size;           // 在 device_bind_common 中指 child_dev->uclass_platdata 的大小
    int per_child_auto_alloc_size;                     // 在 device_probe 中如果 dev->parent->driver->per_child_auto_alloc_size 不存在则由它指定 child_dev->uclass_priv 大小.
    int per_child_platdata_auto_alloc_size;            // 在 device_bind_common 中指定 child_dev->parent_platdata 的大小
    const void *ops;                                  // 
    uint32_t flags;
};

5、 总结

  udevice, driver, uclass, uclass_driver 他们四为位一体, 在 uboot 中扫描 dts 自动创建. 以 i2c 为例进行说明. 下图展示了 rk3566 i2c 的组织架构.

rk36566 uboot - dm 模型数据结构与常见接口-LMLPHP

  第一个阶段通过扫描 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

  1. 检查标志位 DM_FLAG_ACTIVATED 判断是否已经完成 probe, 如果已经 probe 则直接返回.
  2. 为 dev 分配一些列空间如下.
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 // 如果前面的不存在则使用这个

  1. 调用父设备的 probe 函数, 遍历 uclass 中的子设备在没有用到的 seq 中返回一个空闲的 seq 设置 dev->seq 这个 seq 可以表示 dev 的初始化顺序, 第一个被初始化的 dev 的 seq 为 0, 第二个为 1 以此类推. 因此可以通过 seq 判断 dev 的初始化顺序.

  2. 进行一系列回调

回调 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); -->         // 常用接口用于解析 dts.
回调 dev->drv->probe(dev); -->                      // 常用接口用于初始化硬件的 probe 接口.
回调 dev->uclass->uc_drv->post_probe(dev); -->      // 在这里设置共有的硬件特性
  1. 调用 pinctrl 设置 default 的 pin 脚状态.
int device_probe(struct udevice *dev)
{
    const struct driver *drv;
    int size = 0;
    int ret;
    int seq;

    if (!dev)
        return -EINVAL;

    // 检查是否已经完成 probe
    if (dev->flags & DM_FLAG_ACTIVATED)
        return 0;
    
    // 获取 drv
    drv = dev->driver;
    assert(drv);

    // 为 dev->priv 分配空间 drv->priv_auto_alloc_size
    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;
        }
    }

    // 为 dev->uclass_priv 分配空间 dev->uclass->uc_drv->per_device_auto_alloc_size
    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;
        }
    }

    //  为 dev->parent_priv 分配空间 dev->parent->driver->per_child_auto_alloc_size
    // 如果没有 per_child_auto_alloc_size 则根据 dev->parent->uclass->uc_drv->per_child_auto_alloc_size 分配空间
    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;
            }
        }

        // 调用父设备的 probe 函数
        ret = device_probe(dev->parent);
        if (ret)
            goto fail;

        if (dev->flags & DM_FLAG_ACTIVATED)
            return 0;
    }

    // 遍历 uclass 中的子设备在没有用到的 seq 中返回一个空闲的 seq
    // 这个 seq 可以表示 dev 的初始化顺序, 第一个被初始化的 dev 的 seq 为 0, 第二个为 1 以此类推
    // 可以通过 seq 判断 dev 的初始化顺序.
    seq = uclass_resolve_seq(dev);
    if (seq < 0) {
        ret = seq;
        goto fail;
    }
    dev->seq = seq;

    // 设置标志位
    dev->flags |= DM_FLAG_ACTIVATED;

    // 回调 pinctrl 设置 pin 脚状态
    if (dev->parent && device_get_uclass_id(dev) != UCLASS_PINCTRL)
        pinctrl_select_state(dev, "default");

    // 回调 dev->uclass->uc_drv->pre_probe(dev);
    // 回调 dev->parent->uclass->uc_drv->child_pre_probe(dev);
    ret = uclass_pre_probe_device(dev);
    if (ret)
        goto fail;
 
	// 回调  dev->parent->driver->child_pre_probe(dev);
    if (dev->parent && dev->parent->driver->child_pre_probe) {
        ret = dev->parent->driver->child_pre_probe(dev);
        if (ret)
            goto fail;
    }

	// 回调 dev->drv->ofdata_to_platdata(dev);
    if (drv->ofdata_to_platdata && dev_has_of_node(dev)) {
        ret = drv->ofdata_to_platdata(dev);
        if (ret)
            goto fail;
    }
 
	// 回调 dev->drv->probe(dev);
    if (drv->probe) {
        ret = drv->probe(dev);
        if (ret) {
            dev->flags &= ~DM_FLAG_ACTIVATED;
            goto fail;
        }
    }

	// 回调 dev->uclass->uc_drv->post_probe(dev);
    ret = uclass_post_probe_device(dev);
    if (ret)
        goto fail_uclass;

	// 回调 pinctrl 再次设置 pin 脚状态
    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

// parent: 父设备
// driver: 创建设备的 drv
// name: 设备名称, 一般设置的和 drv name 相同
// driver_data: drv 的私有数据
// node: 该设备对应的 dts 节点, 可以为 null
// devp: 返回创建的 udevice
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

// parent: 父设备
// driver: 创建设备的 drv
// name: 设备名称, 一般设置的和 drv name 相同
// platdata: 设置 dev->platedata
// of_offset: 设备节点的偏移, 可以为 -1
// devp: 返回创建的 udevice
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, 不使用设备树.

// parent: 父设备
// pre_reloc_only: 是否已经重定位
// driver_info: udevice 设备描述结构
// devp: 返回创建的 udevice
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 返回.

int uclass_get(enum uclass_id id, struct uclass **ucp);

2) uclass_get_name

通过 uclass_id 返回 uclass 的 name

const char *uclass_get_name(enum uclass_id id);

3) uclass_find_device

返回对应 uclass_id 的 uclass 对应的设备链表上的第 index 个 udevice

int uclass_find_device(enum uclass_id id, int index, struct udevice **devp);

4) uclass_find_first_device

返回对应 uclass_id 的 uclass 对应的设备链表上的第一个 udevice

int uclass_find_first_device(enum uclass_id id, struct udevice **devp);

5) uclass_find_first_device

返回所属 uclass 链表的下一个 udevice

int uclass_find_next_device(struct udevice **devp);

6) uclass_find_device_by_name

返回对应 uclass_id 的 uclass 的设备链表上对应 name 的 udevice

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

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

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

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

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

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

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

int uclass_first_device(enum uclass_id id, struct udevice **devp);

7) uclass_next_device

返回所属 uclass 链表上的下一个 udevice 并进行 device_probe

int uclass_next_device(struct udevice **devp);

简单总结: 如果只需要返回某个 udevice 则使用带 find 的接口, 如果需要返回并且 probe 则使用带 get 的接口.

03-15 07:55