1. 程式人生 > 實用技巧 >u-boot kernel driver的理解

u-boot kernel driver的理解

一、u-boot driver

1. uclass

// uclass的私有資料指標

// 對應的uclass driver

// 連結串列頭,連線所屬的所有udevice

// 連結串列節點,用於把uclass連線到uclass_root連結串列上

2. uclass driver -- spi-uclass.c

連線到uc_drv

例項:

3. udevice

連線到dev_head

// 該udevice對應的driver

4.driver (udevice對應的)

連線到udevice->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;