文章目录
- 一、数据结构
- 1、udevice
- 2、driver
- 3、uclass
- 4、uclass\_driver
- 5、 总结
- 6、device\_probe
- 二、常用接口
- 1、udevice 创建接口
- 1) device_bind_with_driver_data
- 2) device_bind
- 3) device_bind_by_name
- 2、uclass 操作函数
- 1) uclass_get
- 2) uclass_get_name
- 3) uclass_find_device
- 4) uclass_find_first_device
- 5) uclass_find_first_device
- 6) uclass_find_device_by_name
- 7) uclass_find_device_by_seq
- 8) uclass_find_device_by_ofnode
- 3、device_probe 封装接口
- 1) uclass_get_device
- 2) uclass_get_device_by_name
- 3) uclass_get_device_by_seq
- 4) uclass_get_device_by_ofnode
- 6) uclass_first_device
- 7) uclass_next_device
作者: 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 中连接对应的 drvconst 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_headstruct list_head child_head; // 在子设备创建时设置, 连接子设备的 sibling_nodestruct list_head sibling_node; // 在 device_bind_common 中设置, 连接到 parent->child_headuint32_t flags; int req_seq;int seq; // // 在 device_probe 中设置, 表示 device 是第几个被注册, 由于他的唯一性, 因此也可以通过 seq 查找设备.
#ifdef CONFIG_DEVRESstruct 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 的 drvfor (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 drvstruct list_head dev_head; // 用于连接所属的 udevicestruct 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; // 所属的 uclassint (*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_sizeint 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 的组织架构.
第一个阶段通过扫描 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 分配一些列空间如下.
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 的初始化顺序.
-
进行一系列回调
回调 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); --> // 在这里设置共有的硬件特性
- 调用 pinctrl 设置 default 的 pin 脚状态.
int device_probe(struct udevice *dev)
{const struct driver *drv;int size = 0;int ret;int seq;if (!dev)return -EINVAL;// 检查是否已经完成 probeif (dev->flags & DM_FLAG_ACTIVATED)return 0;// 获取 drvdrv = dev->driver;assert(drv);// 为 dev->priv 分配空间 drv->priv_auto_alloc_sizeif (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_sizesize = 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 的接口.