[uboot] uboot流程系列:
[project X] tiny210(s5pv210)上电启动流程(BL0-BL2)
[project X] tiny210(s5pv210)从存储设备加载代码到DDR
[uboot] (第一章)uboot流程——概述
[uboot] (第二章)uboot流程——uboot-spl编译流程
[uboot] (第三章)uboot流程——uboot-spl代码流程
[uboot] (第四章)uboot流程——uboot编译流程
[uboot] (第五章)uboot流程——uboot启动流程
[uboot] (番外篇)global_data介绍
[uboot] (番外篇)uboot relocation介绍
[uboot] (番外篇)uboot之fdt介绍
建议先看《[uboot] (番外篇)uboot之fdt介绍》,了解一下uboot的fdt的功能,在驱动模型中会使用到。
==============================================================================================================
一、说明
1、uboot的驱动模型简单介绍
uboot引入了驱动模型(driver model),这种驱动模型为驱动的定义和访问接口提供了统一的方法。提高了驱动之间的兼容性以及访问的标准型。
uboot驱动模型和kernel中的设备驱动模型类似,但是又有所区别。
在后续我们将驱动模型(driver model)简称为DM,其实在uboot里面也是这样简称的。
具体细节建议参考./doc/driver-model/README.txt
2、如何使能uboot的DM功能
(1)配置CONFIG_DM
在configs/tiny210_defconfig中定义了如下:
CONFIG_DM=y
(2)使能相应的uclass driver的config。
DM和uclass是息息相关的,如果我们希望在某个模块引入DM,那么就需要使用相应模块的uclass driver来代替旧版的通用driver。
关于uclass我们会在后续继续说明。
以serial为例,为了在serial中引入DM,我在configs/tiny210_defconfig0中打开了CONFIG_DM_SERIAL宏,如下
CONFIG_DM_SERIAL=y
看driver/serial/Makefile
ifdef CONFIG_DM_SERIAL
obj-y += serial-uclass.o ## 引入dm的serial core驱动
else
obj-y += serial.o ## 通用的serial core驱动
endif
## 可以发现编译出来的serial core的驱动代码是不一样的。
(3)对应设备驱动也要引入dm的功能
其设备驱动主要是实现和底层交互,为uclass层提供接口。后续再具体说明。
__后续都以serial-uclass进行说明
二、uboot DM整体架构
1、DM的四个组成部分
uboot的DM主要有四个组成部分
- udevice
简单就是指设备对象,可以理解为kernel中的device。 - driver
udevice的驱动,可以理解为kernel中的device_driver。和底层硬件设备通信,并且为设备提供面向上层的接口。 - uclass
先看一下README.txt中关于uclass的说明:
Uclass - a group of devices which operate in the same way. A uclass provides
a way of accessing individual devices within the group, but always
using the same interface. For example a GPIO uclass provides
operations for get/set value. An I2C uclass may have 10 I2C ports,
4 with one driver, and 6 with another.
uclass,使用相同方式的操作集的device的组。相当于是一种抽象。uclass为那些使用相同接口的设备提供了统一的接口。
例如,GPIO uclass提供了get/set接口。再例如,一个I2C uclass下可能有10个I2C端口,4个使用一个驱动,另外6个使用另外一个驱动。
- uclass_driver
对应uclass的驱动程序。主要提供uclass操作时,如绑定udevice时的一些操作。
2、调用关系框架图
3、相互之间的关系
结合上图来看:
- 上层接口都是和uclass的接口直接通讯。
- uclass可以理解为一些具有相同属性的udevice对外操作的接口,uclass的驱动是uclass_driver,主要为上层提供接口。
- udevice的是指具体设备的抽象,对应驱动是driver,driver主要负责和硬件通信,为uclass提供实际的操作集。
- udevice找到对应的uclass的方式主要是通过:udevice对应的driver的id和uclass对应的uclass_driver的id是否匹配。
- udevice会和uclass绑定。driver会和udevice绑定。uclass_driver会和uclass绑定。
这里先简单介绍一下:uclass和udevice都是动态生成的。在解析fdt中的设备的时候,会动态生成udevice。
然后找到udevice对应的driver,通过driver中的uclass id得到uclass_driver id。从uclass链表中查找对应的uclass是否已经生成,没有生成的话则动态生成uclass。
4、GD中和DM相关的部分
typedef struct global_data {
#ifdef CONFIG_DM
struct udevice *dm_root; /* Root instance for Driver Model */
// DM中的根设备,也是uboot中第一个创建的udevice,也就对应了dts里的根节点。
struct udevice *dm_root_f; /* Pre-relocation root instance */
// 在relocation之前DM中的根设备
struct list_head uclass_root; /* Head of core tree */
// uclass链表,所有被udevice匹配的uclass都会被挂载到这个链表上
#endif
} gd_t;
三、DM四个主要组成部分详细介绍
后续以数据结构、如何定义、存放位置、如何获取四个部分进行说明进行说明
0、uclass id
每一种uclass都有自己对应的ID号。定义于其uclass_driver中。其附属的udevice的driver中的uclass id必须与其一致。
所有uclass id定义于include/dm/uclass-id.h中
列出部分id如下
enum uclass_id {
/* These are used internally by driver model */
UCLASS_ROOT = 0,
UCLASS_DEMO,
UCLASS_CLK, /* Clock source, e.g. used by peripherals */
UCLASS_PINCTRL, /* Pinctrl (pin muxing/configuration) device */
UCLASS_SERIAL, /* Serial UART */
}
1、uclass
- (1)数据结构
struct uclass {
void *priv; // uclass的私有数据指针
struct uclass_driver *uc_drv; // 对应的uclass driver
struct list_head dev_head; // 链表头,连接所属的所有udevice
struct list_head sibling_node; // 链表节点,用于把uclass连接到uclass_root链表上
};
(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实现了这个功能。
有如下API:
int uclass_get(enum uclass_id key, struct uclass **ucp);
// 从gd->uclass_root链表获取对应的uclass
2、uclass_driver
- (1)数据结构
include/dm/uclass.h
struct uclass_driver {
const char *name; // 该uclass_driver的命令
enum uclass_id id; // 对应的uclass id
/* 以下函数指针主要是调用时机的区别 */
int (*post_bind)(struct udevice *dev); // 在udevice被绑定到该uclass之后调用
int (*pre_unbind)(struct udevice *dev); // 在udevice被解绑出该uclass之前调用
int (*pre_probe)(struct udevice *dev); // 在该uclass的一个udevice进行probe之前调用
int (*post_probe)(struct udevice *dev); // 在该uclass的一个udevice进行probe之后调用
int (*pre_remove)(struct udevice *dev);// 在该uclass的一个udevice进行remove之前调用
int (*child_post_bind)(struct udevice *dev); // 在该uclass的一个udevice的一个子设备被绑定到该udevice之后调用
int (*child_pre_probe)(struct udevice *dev); // 在该uclass的一个udevice的一个子设备进行probe之前调用
int (*init)(struct uclass *class); // 安装该uclass的时候调用
int (*destroy)(struct uclass *class); // 销毁该uclass的时候调用
int priv_auto_alloc_size; // 需要为对应的uclass分配多少私有数据
int per_device_auto_alloc_size; //
int per_device_platdata_auto_alloc_size; //
int per_child_auto_alloc_size; //
int per_child_platdata_auto_alloc_size; //
const void *ops; //操作集合
uint32_t flags; // 标识为
};
- (2)如何定义
通过UCLASS_DRIVER来定义uclass_driver.
以serial-uclass为例
UCLASS_DRIVER(serial) = {
.id = UCLASS_SERIAL,
.name = "serial",
.flags = DM_UC_FLAG_SEQ_ALIAS,
.post_probe = serial_post_probe,
.pre_remove = serial_pre_remove,
.per_device_auto_alloc_size = sizeof(struct serial_dev_priv),
};
UCLASS_DRIVER实现如下:
#define UCLASS_DRIVER(__name) \
ll_entry_declare(struct uclass_driver, __name, uclass)
#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)))
关于ll_entry_declare我们在《[uboot] (第六章)uboot流程——命令行模式以及命令处理介绍》已经介绍过了
最终得到一个如下结构体
struct uclass_driver _u_boot_list_2_uclass_2_serial = {
.id = UCLASS_SERIAL, // 设置对应的uclass id
.name = "serial",
.flags = DM_UC_F