zoukankan      html  css  js  c++  java
  • 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
    ", 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
    ", 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
    ", 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'
    ", 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)
     
  • 相关阅读:
    Angularjs中文教程
    IE兼容性 css处理常见
    手写画板实现并转化成图片
    canvas 最基本简单的示例
    凡科 网站地址
    IOS学习之路二十二(UIAlertView获得文本框内容及添加北京图片)
    IOS学习之路十四(用TableView做的新闻客户端展示页面)
    IOS开发之路二十一(UIWebView加载本地html)
    iOS学习之路十三(动态调整UITableViewCell的高度)
    IOS学习之路十二(UITableView下拉刷新页面)
  • 原文地址:https://www.cnblogs.com/djw316/p/10235492.html
Copyright © 2011-2022 走看看