uboot的驅動模型理解


uboot的驅動模型,簡稱dm, 具體細節建議參考./doc/driver-model/README.txt
關於dm的三個概念:
uclass:一組同類型的devices,uclass為同一個group的device,提供一個相同的接口。比如:I2C、GPIO等
driver:上層的接口,英文原文解釋是“some code which talks to a peripheral and presents a higher-level
    interface to it.”
device:driver的一個實例,綁定到一個具體的端口或者外設。(driver和device是不是可以類比於程序與進程,進程是程序的一個實例)
 
每一類uclass,需要在代碼中用下面的方式來定義,以spi-uclass為例:
 
UCLASS_DRIVER(spi) = {
    .id        = UCLASS_SPI,
    .name        = "spi",
    .flags        = DM_UC_FLAG_SEQ_ALIAS,
    .post_bind    = spi_post_bind,
    .post_probe    = spi_post_probe,
    .child_pre_probe = spi_child_pre_probe,
    .per_device_auto_alloc_size = sizeof(struct dm_spi_bus),
    .per_child_auto_alloc_size = sizeof(struct spi_slave),
    .per_child_platdata_auto_alloc_size =
            sizeof(struct dm_spi_slave_platdata),
    .child_post_bind = spi_child_post_bind,
};

 

通過對宏定義UCLASS_DRIVER的展開
/* 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_spi
並且存在 .u_boot_list_2_uclass_2_spi段。
但是我們如何通過ID UCLASS_SPI來找到對應的uclass結構體呢?
struct uclass_driver *lists_uclass_lookup(enum uclass_id id)
{
// 會根據.u_boot_list_2_uclass_1的段地址來得到uclass_driver table的地址
    struct uclass_driver *uclass =
        ll_entry_start(struct uclass_driver, uclass);
 
// 獲得uclass_driver table的長度
    const int n_ents = ll_entry_count(struct uclass_driver, uclass);
    struct uclass_driver *entry;
 
    for (entry = uclass; entry != uclass + n_ents; entry++) {
        if (entry->id == id)
            return entry;
    }
 
    return NULL;
}
可以通過函數lists_uclass_lookup(enum uclass_id id)來查找。
 
另外,driver也是類似
/* Declare a new U-Boot driver */
#define U_BOOT_DRIVER(__name)                        \
    ll_entry_declare(struct driver, __name, driver)
 
#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)))
 
U_BOOT_DRIVER(tegra114_spi) = {
    .name    = "tegra114_spi",
    .id    = UCLASS_SPI,
    .of_match = tegra114_spi_ids,
    .ops    = &tegra114_spi_ops,
    .ofdata_to_platdata = tegra114_spi_ofdata_to_platdata,
    .platdata_auto_alloc_size = sizeof(struct tegra_spi_platdata),
    .priv_auto_alloc_size = sizeof(struct tegra114_spi_priv),
    .probe    = tegra114_spi_probe,
};
這樣我們就能得到一個結構體,
ll_entry_declare(struct driver, tegra114_spi, driver)
 
struct driver _u_boot_list_2_driver_2_tegra114_spi
                             __aligned(4)        \
            __attribute__((unused,                \
            section(".u_boot_list_2_driver_2_tegra114_spi")))

 

存儲在段,.u_boot_list_2_driver_2_tegra114_spi
但是這些段,在uboot實際加載的時候,又是如何加載到鏈表中去的呢!
 
首先,還是初始化列表init_sequence_f里的函數initf_dm
static int initf_dm(void)
{
#if defined(CONFIG_DM) && defined(CONFIG_SYS_MALLOC_F_LEN)
    int ret;
 
    ret = dm_init_and_scan(true);
    if (ret)
        return ret;
#endif
#ifdef CONFIG_TIMER_EARLY
    ret = dm_timer_init();
    if (ret)
        return ret;
#endif
 
    return 0;
}
dm_init_and_scan(),代碼分析如下
int dm_init_and_scan(bool pre_reloc_only)
{
    int ret;
 
    /*創建udevice和uclass空鏈表,創建根設備(root device)*/
    ret = dm_init();
    if (ret) {
        debug("dm_init() failed: %d\n", ret);
        return ret;
    }
    /*掃描U_BOOT_DEVICE定義的設備,與U_BOOT_DRIVER定義的driver進行查找,並綁定相應driver*/
    ret = dm_scan_platdata(pre_reloc_only);
    if (ret) {
        debug("dm_scan_platdata() failed: %d\n", ret);
        return ret;
    }
 
    if (CONFIG_IS_ENABLED(OF_CONTROL)) {
        /*掃描由FDT設備樹文件定義的設備,與U_BOOT_DRIVER定義的driver進行查找,並綁定相應driver*/
        ret = dm_scan_fdt(gd->fdt_blob, pre_reloc_only);
        if (ret) {
            debug("dm_scan_fdt() failed: %d\n", ret);
            return ret;
        }
    }
    ret = dm_scan_other(pre_reloc_only);
    if (ret)
        return ret;
 
    return 0;
}
分三個部分:
dm_init():創建udevice和uclass空鏈表,創建根設備(root device)
dm_scan_platdata():調用函數lists_bind_drivers,掃描U_BOOT_DEVICE定義的設備,與U_BOOT_DRIVER定義的driver進行查找,創建udevice,並綁定相應driver。
dm_scan_fdt():掃描由FDT設備樹文件定義的設備,與U_BOOT_DRIVER定義的driver進行查找,創建udevice,並綁定相應driver。
int lists_bind_drivers(struct udevice *parent, bool pre_reloc_only)
{
    /*從分段,.u_boot_list_2_driver_info中來查找*/
    struct driver_info *info =
        ll_entry_start(struct driver_info, driver_info);
 
    const int n_ents = ll_entry_count(struct driver_info, driver_info);
    struct driver_info *entry;
    struct udevice *dev;
    int result = 0;
    int ret;
 
    for (entry = info; entry != info + n_ents; entry++) {
        /*將driver_info列表里面的name,依次與driver列表里面的名字,進行匹配查找,然后進行綁定*/
        ret = device_bind_by_name(parent, pre_reloc_only, entry, &dev);
        if (ret && ret != -EPERM) {
            dm_warn("No match for driver '%s'\n", entry->name);
            if (!result || ret != -ENOENT)
                result = ret;
        }
    }
 
    return result;
}
 
int device_bind_by_name(struct udevice *parent, bool pre_reloc_only,
            const struct driver_info *info, struct udevice **devp)
{
    struct driver *drv;
 
    /*從driver list中查找info的名字*/
    drv = lists_driver_lookup_name(info->name);
    if (!drv)
        return -ENOENT;
    if (pre_reloc_only && !(drv->flags & DM_FLAG_PRE_RELOC))
        return -EPERM;
    
    /*創建udevice,綁定*/
    return device_bind(parent, drv, info->name, (void *)info->platdata,
               -1, devp);
}
U_BOOT_DEVICE的宏定義,注意與U_BOOT_DRIVER的區別:
 
#define U_BOOT_DEVICE(__name)                        \
    ll_entry_declare(struct driver_info, __name, driver_info)

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM