[uboot] (番外篇)uboot 驱动模型

本文详细介绍了uboot的驱动模型(DM),包括DM的引入、功能、整体架构、主要组成部分、相关API、设备表达方式以及DM的初始化和工作流程。DM简化了驱动的访问接口,提高了兼容性和标准化。文章通过uclass和udevice的关系,以及如何启用DM功能,逐步揭示了DM的运作机制。同时,还探讨了如何从设备树中解析和创建udevice,以及DM的初始化过程和设备的probe工作流程。
摘要由CSDN通过智能技术生成

[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、调用关系框架图

DM下的接口调用流程

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
  • 128
    点赞
  • 326
    收藏
    觉得还不错? 一键收藏
  • 17
    评论
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值