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 */
    ...
};