U-boot 驱动模型
跟Linux内核类似,U-boot现在也在驱动层使用驱动模型(Driver Model)了,几个核心的结构体:
udevice结构体:表示一个设备
#include<dm/device.h>
struct udevice {
const struct driver *driver; // device 对应的driver
const char *name; // 该udevice的名字
void *platdata;
void *parent_platdata;
void *uclass_platdata;
ofnode node; // 对应的设备树节点
ulong driver_data;
struct udevice *parent; // 父设备
void *priv; // 私有数据的指针
struct uclass *uclass; //所属的uclass
void *uclass_priv;
void *parent_priv;
struct list_head uclass_node;
struct list_head child_head;
struct list_head sibling_node;
uint32_t flags;
int req_seq;
int seq;
#ifdef CONFIG_DEVRES
struct list_head devres_head;
#endif
}
udevice的生成方式有两种:
- 代码中调用U_BOOT_DEVICE宏来定义设备资源,实际为一个设备实例。
- 将设备描述信息写在对应的DTS文件中,然后编译成DTB,最终由uboot解析设备树后动态生成。
创建一个设备后,为了服从统一的管理,该结构体会被连接到DM模型下:
- 将udevice连接到对应的uclass中,uclass主要用来管理着同一类的驱动
- 有父子关系的udevice,还会连接到udevice->child_head链表下,方便调用
跟udevice相关的API接口:主要作用就是根据uclass_id,查找对应的uclass,然后根据索引值或者名称,来查找到对应的udevice。
// 通过索引从uclass中获取udevice
int uclass_get_device(enum uclass_id id, int index, struct udevice **devp);
// 通过设备名从uclass中获取udevice
int uclass_get_device_by_name(enum uclass_id id, const char *name,
struct udevice **devp);
int uclass_get_device_by_seq(enum uclass_id id, int seq, struct udevice **devp);
int uclass_get_device_by_of_offset(enum uclass_id id, int node,
struct udevice **devp);
int uclass_get_device_by_phandle(enum uclass_id id, struct udevice *parent,
const char *name, struct udevice **devp);
int uclass_first_device(enum uclass_id id, struct udevice **devp);
int uclass_first_device_err(enum uclass_id id, struct udevice **devp);
int uclass_next_device(struct udevice **devp);
int uclass_resolve_seq(struct udevice *dev);
driver结构体:udevice设备对应的驱动
#include<dm/device.h>
struct driver {
char *name;
enum uclass_id id;
const struct udevice_id *of_match;
int (*bind)(struct udevice *dev);
int (*probe)(struct udevice *dev);
int (*remove)(struct udevice *dev);
int (*unbind)(struct udevice *dev);
int (*ofdata_to_platdata)(struct udevice *dev);
int (*child_post_bind)(struct udevice *dev);
int (*child_pre_probe)(struct udevice *dev);
int (*child_post_remove)(struct udevice *dev);
int priv_auto_alloc_size;
int platdata_auto_alloc_size;
int per_child_auto_alloc_size;
int per_child_platdata_auto_alloc_size;
const void *ops; /* driver-specific operations */
uint32_t flags;
};
uclass结构体:某一类设备的抽象,提供统一的接口
#include<dm/uclass.h>
struct uclass {
void *priv; // uclass的私有数据
struct uclass_driver *uc_drv; // 该uclass对应的driver,提供该类设备的统一接口
struct list_head dev_head; // 该uclass类下的所有udevice设备
struct list_head sibling_node; //下一个uclass节点
#ifdef CONFIG_USING_KERNEL_DTB_V2
struct list_head *u_boot_dev_head;
#endif
};
uclass是uboot自动生成的,并且不是所有uclass都会生成,有对应uclass_driver并且有被udevice匹配到的uclass才会生成。所有生成的uclass都会被挂载gd->uclass_root链表上。可以通过下面的API函数,直接遍历链表gd->uclass_root链表并且根据uclass_id来获取到相应的uclass。
int uclass_get(enum uclass_id key, struct uclass **ucp);
// 从gd->uclass_root链表获取对应的uclass
U-boot使用global_data管理着整个Uboot的全局变量,其中dm_root,dm_root_f,uclass_root用来管理整个DM模型。
typedef struct global_data {
...
#ifdef CONFIG_DM
struct udevice *dm_root; /* Root instance for Driver Model DM模型的根设备 */
struct udevice *dm_root_f; /* Pre-relocation root instance 重定向前的根设备 */
struct list_head uclass_root; /* Head of core tree uclass链表的头 */
#endif
...
}
uclass_driver结构体:设备类驱动,某一类设备统一的驱动,uclass对应的driver,使用uclass_driver表示:
#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; //
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; // 该uclass对应的操作集合
uint32_t flags; //
}
uclass_driver主要通过UCLASS_DRIVER来定义:
UCLASS_DRIVER(regulator) = {
.id = UCLASS_REGULATOR,
.name = "regulator",
.post_bind = regulator_post_bind,
.pre_probe = regulator_pre_probe,
.per_device_platdata_auto_alloc_size =
sizeof(struct dm_regulator_uclass_platdata),
};
/* Declare a new 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)))
上面的宏展开后,就变成了下面的形式:
struct uclass_driver _u_boot_list_2_uclass_2_regulator = {
.id = UCLASS_REGULATOR,
.post_bind = regulator_post_bind,
.pre_probe = regulator_pre_probe,
.name = "regulator",
}
//同时存放在段._u_boot_list_2_uclass_2_regulator中,也就是section段的内容
想要获取uclass_driver需要先获取uclass_driver table。
struct uclass_driver *uclass = ll_entry_start(struct uclass_driver, uclass);
// 会根据.u_boot_list_2_uclass_1的段地址来得到uclass_driver table的地址
const int n_ents = ll_entry_count(struct uclass_driver, uclass);
// 获得uclass_driver table的长度
struct uclass_driver *lists_uclass_lookup(enum uclass_id id)
// 从uclass_driver table中获取uclass id为id的uclass_driver。
上面的几个API函数,可以根据uclass_id来查找到对应的uclass_driver,进而操作对应的uclass下的udevice。
uclass代表驱动的一个类别,uclass_driver是uclass的驱动程序,为uclass提供统一操作接口。而对于不同类型的驱动,就需要uclass_id来区分。每一种类型的设备uclass都有唯一对应的uclass_id,贯穿设备模型,也是udevice与uclass相关联的关键之处。
enum uclass_id {
/* These are used internally by driver model */
UCLASS_ROOT = 0,
UCLASS_DEMO,
UCLASS_TEST,
UCLASS_TEST_FDT,
UCLASS_TEST_BUS,
UCLASS_TEST_PROBE,
...
/* U-Boot uclasses start here - in alphabetical order */
UCLASS_ACPI_PMC, /* (x86) Power-management controller (PMC) */
UCLASS_ADC, /* Analog-to-digital converter */
UCLASS_AHCI, /* SATA disk controller */
UCLASS_AUDIO_CODEC, /* Audio codec with control and data path */
UCLASS_AXI, /* AXI bus */
UCLASS_BLK, /* Block device */
UCLASS_BOARD, /* Device information from hardware */
...
};