说明
- 类似于linux,为了规范、统一驱动适配和驱动接口调用,uboot定义了一套驱动模型(Driver Model),简称DM。
- 本文基于:u-boot-2021.10。
优点
- 为同一类ip的驱动定义了统一的操作接口,DM在软件层面做了一定的抽象。
- 分层设计,将上层使用、设备以及驱动实现区分开来,降低了耦合性。
核心概念/数据结构
- DM模型抽象出了以下四个概念/数据结构。
- uclass
- uclass_driver
- udevice
- driver
uclass
-
uclass(uboot class)是同一类外设(I2C、GPIO等)总的组织结构体,定义如下:
// file:include/dm/uclass.h
/**- struct uclass - a U-Boot drive class, collecting together similar drivers
- A uclass provides an interface to a particular function, which is
- implemented by one or more drivers. Every driver belongs to a uclass even
- if it is the only driver in that uclass. An example uclass is GPIO, which
- provides the ability to change read inputs, set and clear outputs, etc.
- There may be drivers for on-chip SoC GPIO banks, I2C GPIO expanders and
- PMIC IO lines, all made available in a unified way through the uclass.
- @priv_: Private data for this uclass (do not access outside driver model)
- @uc_drv: The driver for the uclass itself, not to be confused with a
- 'struct driver'
- @dev_head: List of devices in this uclass (devices are attached to their
- uclass when their bind method is called)
- @sibling_node: Next uclass in the linked list of uclasses
*/
struct uclass {
void *priv_;
struct uclass_driver *uc_drv;
struct list_head dev_head;
struct list_head sibling_node;
};
-
uclass 包含
- uc_drv: uclass driver。
- dev_head: udevice list(设备列表)。
- sibling_node: 链表的下一个uclass结构。
uclass_driver
-
uclass_driver是uclass的驱动,并不是具体硬件驱动,做一些uclass通用的的准备/回收等工作。
// file:include/dm/uclass.h
struct uclass_driver {
const char *name;
enum uclass_id id;
int (*post_bind)(struct udevice *dev);
int (*pre_unbind)(struct udevice *dev);
int (*pre_probe)(struct udevice *dev);
int (*post_probe)(struct udevice *dev);
int (*pre_remove)(struct udevice *dev);
int (*child_post_bind)(struct udevice *dev);
int (*child_pre_probe)(struct udevice *dev);
int (*child_post_probe)(struct udevice *dev);
int (*init)(struct uclass *class);
int (*destroy)(struct uclass *class);
int priv_auto;
int per_device_auto;
int per_device_plat_auto;
int per_child_auto;
int per_child_plat_auto;
uint32_t flags;
}; -
使用宏UCLASS_DRIVER定义一个uclass driver,每一类ip会定义一个,如下:
//file: drivers/timer/timer-uclass.c
UCLASS_DRIVER(timer) = {
.id = UCLASS_TIMER,
.name = "timer",
.pre_probe = timer_pre_probe,
.flags = DM_UC_FLAG_SEQ_ALIAS,
.post_probe = timer_post_probe,
.per_device_auto = sizeof(struct timer_dev_priv),
}; -
预定义的uclass id。
//file: include/dm/uclass-id.h
enum uclass_id {
/* These are used internally by driver model */
UCLASS_ROOT = 0,
UCLASS_DEMO,
UCLASS_TEST,
UCLASS_TEST_FDT,
UCLASS_TEST_FDT_MANUAL,
UCLASS_TEST_BUS,
UCLASS_TEST_PROBE,
UCLASS_TEST_DUMMY,
UCLASS_TEST_DEVRES,
UCLASS_TEST_ACPI,
...
}
udevice
-
udevice是具体硬件的实例,例如:dts中配置了两个timer,就会有两个timer udevice,uboot会将udevice和它所属的uclass以及具体的驱动driver绑定起来。
//file:include/dm/device.h
struct udevice {
const struct driver *driver;
const char *name;
void *plat_;
void *parent_plat_;
void *uclass_plat_;
ulong driver_data;
struct udevice *parent;
void *priv_;
struct uclass *uclass;
void *uclass_priv_;
void *parent_priv_;
struct list_head uclass_node;
struct list_head child_head;
struct list_head sibling_node;
#if !CONFIG_IS_ENABLED(OF_PLATDATA_RT)
u32 flags_;
#endif
int seq_;
#if !CONFIG_IS_ENABLED(OF_PLATDATA)
ofnode node_;
#endif
#ifdef CONFIG_DEVRES
struct list_head devres_head;
#endif
#if CONFIG_IS_ENABLED(DM_DMA)
ulong dma_offset;
#endif
};
driver
-
具体硬件的驱动,对应每个硬件的驱动实现,例如:dw wdt(drivers/watchdog/designware_wdt.c), mtk wdt(drivers/watchdog/mtk_wdt.c) 。
//file: 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 (*of_to_plat)(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;
int plat_auto;
int per_child_auto;
int per_child_plat_auto;
const void ops; / driver-specific operations */
uint32_t flags;
#if CONFIG_IS_ENABLED(ACPIGEN)
struct acpi_ops *acpi_ops;
#endif
};- const void *ops; 是该驱动支持的接口。
-
ops 指向该类ip统一驱动接口结构体,在include目录的头文件中有各种ip需要支持的ops接口,例如:
include/thermal.h //温度传感器需要支持的接口(ops)
include/wdt.h //wdt需要支持的ops
include/timer.h //timer需要支持的ops- include/timer.h
struct timer_ops {
/**- @get_count: Get the current timer count
- @dev: The timer device
- This function may be called at any time after the driver is probed.
- All necessary initialization must be completed by the time probe()
- returns. The count returned by this functions should be monotonic.
- This function must succeed.
- Return: The current 64-bit timer count
*/
u64 (*get_count)(struct udevice *dev);
};
- include/timer.h
-
新思定时器(dw apb timer)定义:
static const struct timer_ops dw_apb_timer_ops = {
.get_count = dw_apb_timer_get_count,
};U_BOOT_DRIVER(dw_apb_timer) = {
.name = "dw_apb_timer",
.id = UCLASS_TIMER,
.ops = &dw_apb_timer_ops,
.probe = dw_apb_timer_probe,
.of_match = dw_apb_timer_ids,
.of_to_plat = dw_apb_timer_of_to_plat,
.remove = dw_apb_timer_remove,
.priv_auto = sizeof(struct dw_apb_timer_priv),
}; -
使用宏U_BOOT_DRIVER定义一个驱动实例
/* Declare a new U-Boot driver */
#define U_BOOT_DRIVER(__name)
ll_entry_declare(struct driver, __name, driver)
联系
- uclass和udevice是动态生成的。
- udevice在解析fdt中的设备的时候自动生成,然后udevice找到对应的driver,driver中保存了uclass_id,根据它找到uclass_driver_id,从uclass链表中查找对应的uclass是否已经生成,若没有生成,则动态生成,重点是解析设备树,生成udevice,并找到对应的driver。
代码流程
-
模型的全局数据结构,包含根节点。
typedef struct global_data {
// dts中的根节点,第一个创建的udevice
struct udevice *dm_root;// relocation之前的根设备 struct udevice *dm_root_f; // uclass的链表, 挂的是有udevice的uclass struct list_head uclass_root;
} gd_t;
-
初始化与扫描设备树
int dm_init_and_scan(bool pre_reloc_only)
{
int ret;ret = dm_init(CONFIG_IS_ENABLED(OF_LIVE)); if (ret) { debug("dm_init() failed: %d\n", ret); return ret; } if (!CONFIG_IS_ENABLED(OF_PLATDATA_INST)) { ret = dm_scan(pre_reloc_only); if (ret) { log_debug("dm_scan() failed: %d\n", ret); return ret; } } return 0;
}
DM模型初始化(dm_init)
int dm_init(bool of_live)
{
gd->uclass_root = &DM_UCLASS_ROOT_S_NON_CONST;
//初始化uclass_root链表头
INIT_LIST_HEAD(DM_UCLASS_ROOT_NON_CONST);
//创建一个device dm_root并将其绑定到driver name "root_driver"。
device_bind_by_name(NULL, false, &root_info,
&DM_ROOT_NON_CONST);
//探测设备udevice dm_root并激活它
ret = device_probe(DM_ROOT_NON_CONST);
}
- 待补充
DM模型Device Tree节点的设备初始化(dm_scan)
- 待补充
绑定udevice和driver(device_bind_common)
- 待补充
使用
-
整体使能
配置:CONFIG_DM
-
不同类型外设需要专门使能
串口配置:CONFIG_DM_SERIAL
wdt配置:CONFIG_WDT