u-boot kernel driver的理解

一、u-boot driver

u-boot kernel driver的理解

1. uclass

u-boot kernel driver的理解

 

 

 // uclass的私有数据指针

// 对应的uclass driver

// 链表头,连接所属的所有udevice

// 链表节点,用于把uclass连接到uclass_root链表上

 

2. uclass driver   --  spi-uclass.c

连接到uc_drv

u-boot kernel driver的理解

 

 

 实例:

 

 u-boot kernel driver的理解

 

 

 

3. udevice

连接到dev_head

u-boot kernel driver的理解

 

 

 // 该udevice对应的driver

 

4.driver (udevice对应的)

连接到udevice->driver

u-boot kernel driver的理解

 

 

实例:

u-boot kernel driver的理解

 

具体结构见

u-boot kernel driver的理解

 

 

相互之间的关系

结合上图来看:

  • 上层接口都是和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。

 

 

DM的初始化

 

主要工作

  • DM的初始化

    • 创建根设备root的udevice,存放在gd->dm_root中。
      根设备其实是一个虚拟设备,主要是为uboot的其他设备提供一个挂载点。
    • 初始化uclass链表gd->uclass_root
  • DM中udevice和uclass的解析

    • udevice的创建和uclass的创建
    • udevice和uclass的绑定
    • uclass_driver和uclass的绑定
    • driver和udevice的绑定
    • 部分driver函数的调用

dm初始化的接口在dm_init_and_scan中。

 

 

DM的初始化——dm_init

这里就完成的DM的初始化了
(1)创建根设备root的udevice,存放在gd->dm_root中。
(2)初始化uclass链表gd->uclass_root

从平台设备中解析udevice和uclass——dm_scan_platdata

从dtb中解析udevice和uclass——dm_scan_fdt

lists_bind_fdt是从dtb中解析udevice和uclass的核心。

dm_scan_fdt

  dm_scan_fdt_node

    lists_bind_fdt

      device_bind

在device_bind中实现了udevice和uclass的创建和绑定以及一些初始化操作

 

device_bind的实现如下(去除部分代码)
driver/core/device.c

int device_bind(struct udevice *parent, const struct driver *drv,
        const char *name, void *platdata, int of_offset,
        struct udevice **devp)
// parent:父设备
// drv:设备对应的driver
// name:设备名称
// platdata:设备的平台数据指针
// of_offset:在dtb中的偏移,即代表了其dts节点
// devp:所创建的udevice的指针,用于返回
{
    struct udevice *dev;
    struct uclass *uc;
    int size, ret = 0;

    ret = uclass_get(drv->id, &uc);
        // 获取driver id对应的uclass,如果uclass原先并不存在,那么会在这里创建uclass并其uclass_driver进行绑定

    dev = calloc(1, sizeof(struct udevice));
        // 分配一个udevice

    dev->platdata = platdata; // 设置udevice的平台数据指针
    dev->name = name; // 设置udevice的name
    dev->of_offset = of_offset; // 设置udevice的dts节点偏移
    dev->parent = parent; // 设置udevice的父设备
    dev->driver = drv;    // 设置udevice的对应的driver,相当于driver和udevice的绑定
    dev->uclass = uc;    // 设置udevice的所属uclass

    dev->seq = -1;
    dev->req_seq = -1;
    if (CONFIG_IS_ENABLED(OF_CONTROL) && CONFIG_IS_ENABLED(DM_SEQ_ALIAS)) {
        /*
         * Some devices, such as a SPI bus, I2C bus and serial ports
         * are numbered using aliases.
         *
         * This is just a 'requested' sequence, and will be
         * resolved (and ->seq updated) when the device is probed.
         */
        if (uc->uc_drv->flags & DM_UC_FLAG_SEQ_ALIAS) {
            if (uc->uc_drv->name && of_offset != -1) {
                fdtdec_get_alias_seq(gd->fdt_blob,
                        uc->uc_drv->name, of_offset,
                        &dev->req_seq);
            }
                    // 设置udevice的alias请求序号
        }
    }

    if (!dev->platdata && drv->platdata_auto_alloc_size) {
        dev->flags |= DM_FLAG_ALLOC_PDATA;
        dev->platdata = calloc(1, drv->platdata_auto_alloc_size);
                // 为udevice分配平台数据的空间,由driver中的platdata_auto_alloc_size决定
    }

    size = uc->uc_drv->per_device_platdata_auto_alloc_size;
    if (size) {
        dev->flags |= DM_FLAG_ALLOC_UCLASS_PDATA;
        dev->uclass_platdata = calloc(1, size);
                // 为udevice分配给其所属uclass使用的平台数据的空间,由所属uclass的driver中的per_device_platdata_auto_alloc_size决定
    }

    /* put dev into parent's successor list */
    if (parent)
        list_add_tail(&dev->sibling_node, &parent->child_head);
        // 添加到父设备的子设备链表中

    ret = uclass_bind_device(dev);
        // uclass和udevice进行绑定,主要是实现了将udevice链接到uclass的设备链表中

    /* if we fail to bind we remove device from successors and free it */
    if (drv->bind) {
        ret = drv->bind(dev);
        // 执行udevice对应driver的bind函数
    }

    if (parent && parent->driver->child_post_bind) {
        ret = parent->driver->child_post_bind(dev);
        // 执行父设备的driver的child_post_bind函数
    }
    if (uc->uc_drv->post_bind) {
        ret = uc->uc_drv->post_bind(dev);
        if (ret)
            goto fail_uclass_post_bind;
        // 执行所属uclass的post_bind函数
    }

    if (devp)
        *devp = dev;
        // 将udevice进行返回

    dev->flags |= DM_FLAG_BOUND;
        // 设置已经绑定的标志
        // 后续可以通过dev->flags & DM_FLAG_ACTIVATED或者device_active宏来判断设备是否已经被激活

    return 0;

注意,这里只是绑定,即调用了driver的bind函数,但是设备还没有真正激活,也就是还没有执行设备的probe函数。

 

经过前面的DM初始化以及设备解析之后,我们只是建立了udevice和uclass之间的绑定关系。但是此时udevice还没有被probe,其对应设备还没有被激活。
激活一个设备主要是通过device_probe函数,所以在介绍DM的工作流程前,先说明device_probe函数。

 

 

通过uclass来获取一个udevice并且进行probe

由模块自己实现。例如serial则需要在serial的初始化过程中,选择需要的udevice进行probe。

 

driver/serial/serial-uclass.c

serial_init

  serial_find_console_or_panic

    serial_check_stdout

      device_probe

可以去xxx-uclass.c中寻找 device_probe

 

driver/core/device.c

int device_probe(struct udevice *dev) { conststruct driver *drv; int size = 0; int ret; int seq; if (dev->flags & DM_FLAG_ACTIVATED) return0; // 表示这个设备已经被激活了 drv = dev->driver; assert(drv); // 获取这个设备对应的driver/* Allocate private data if requested and not reentered */if (drv->priv_auto_alloc_size && !dev->priv) { dev->priv = alloc_priv(drv->priv_auto_alloc_size, drv->flags); // 为设备分配私有数据 } /* Allocate private data if requested and not reentered */ size = dev->uclass->uc_drv->per_device_auto_alloc_size; if (size && !dev->uclass_priv) { dev->uclass_priv = calloc(1, size); // 为设备所属uclass分配私有数据 } // 这里过滤父设备的probe seq = uclass_resolve_seq(dev); if (seq < 0) { ret = seq; goto fail; } dev->seq = seq; dev->flags |= DM_FLAG_ACTIVATED; // 设置udevice的激活标志 ret = uclass_pre_probe_device(dev); // uclass在probe device之前的一些函数的调用if (drv->ofdata_to_platdata && dev->of_offset >= 0) { ret = drv->ofdata_to_platdata(dev); // 调用driver中的ofdata_to_platdata将dts信息转化为设备的平台数据 } if (drv->probe) { ret = drv->probe(dev); // 调用driver的probe函数,到这里设备才真正激活了 } ret = uclass_post_probe_device(dev); return ret; }

 

主要工作归纳如下:

  • 分配设备的私有数据
  • 对父设备进行probe
  • 执行probe device之前uclass需要调用的一些函数
  • 调用driver的ofdata_to_platdata,将dts信息转化为设备的平台数据
  • 调用driver的probe函数
  • 执行probe device之后uclass需要调用的一些函数

uclass的接口调用

  • 可以通过先从root_uclass链表中提取对应的uclass,然后通过uclass->uclass_driver->ops来进行接口调用,这种方法比较具有通用性。

 

再说一点自己对u-boot下驱动的理解:

比如qspi flash,我们会通过sf probe和sf read write在u-boot命令行中访问qspi flash

但这其中涉及两个u-boot驱动

  • qspi controller驱动
  • qspi flash颗粒驱动

typedefstruct global_data { #ifdef CONFIG_DMstruct 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;

上一篇:按键驱动之同步互斥、阻塞机制


下一篇:linux驱动学习——驱动大致框架