这一篇我们学习uboot中的驱动模型的初始化,在uboot中,驱动模型被称为Driver Model,简称DM。这种驱动模型为uboot中的各类驱动提供了统一的接口。

1. 数据结构及概念

DM模型主要依赖于下面四种数据结构:

  1. udevice,具有硬件设备的抽象, 和driver实例相关
  2. driver,特定udevice的硬件驱动,包含了驱动的绑定、初始化、probe和卸载等函数。使用U_BOOT_DRIVER来注册。需要声明所属的uclass。
  3. uclass,维护一类驱动,例如显示部分有lcdif驱动,display controller驱动,他们都在驱动中声明自己属于UCLASS_VIDEO类。又例如所有的MIPI驱动都属于UCLASS_DSI_HOST类。
  4. uclass_driver,在我们写的驱动中,我们会使用UCLASS_DRIVER来注册一个uclass_driver对象。这个uclass驱动维护了这一类硬件驱动的接口,为上层的调用提供了统一的接口。

我这里以GPIO为例,绘制了这四种数据结构的依赖关系。首先是DM框架中所定义的关于GPIO的UCLASS DRIVER,这个driver只有三个统一的接口,gpio_post_probegpio_post_bindgpio_pre_remove。在以spi中的gpio操作为例,gpio_request->gpio_to_device拿着传入的gpio number遍历UCLASS_GPIO下所有GPIO udevice,获取GPIO的描述符(包含udevice信息和GPIO的偏移量),此时已经找到了这个GPIO所对应的udevice,也就找到了驱动函数gpio_mxc,然后设置输入输出方向函数会调用这个驱动的相应函数来实现指定的输入输出方向。

2. DM模型的初始化:dm_init

在全局数据global_data定义了DM根节点,dm初始化的接口在dm_init_and_scan(bool pre_reloc_only)中,初始化流程主要有两次,入口函数分别是static int initf_dm(void)static int initr_dm(void)。第一次是在重定位之前,调用的是initf_dm函数。第二次是在重定位之后,调用的是initr_dm函数。

typedef struct global_data {// dts中的根节点,第一个创建的udevicestruct udevice  *dm_root;// relocation之前的根设备struct udevice  *dm_root_f;// uclass的链表, 挂的是有udevice的uclassstruct list_head uclass_root;
} gd_

DM root初始化函数dm_init。

int dm_init(bool of_live)
{gd->uclass_root = &DM_UCLASS_ROOT_S_NON_CONST;//初始化uclass_root链表头INIT_LIST_HEAD(DM_UCLASS_ROOT_NON_CONST);//创建一个device dm_root并将其绑定到driver name “root_driver”。device_bind_by_name(NULL, false, &root_info,&DM_ROOT_NON_CONST);//探测设备udevice dm_root并激活它ret = device_probe(DM_ROOT_NON_CONST);
}

2.1 device_bind_by_name分析

lists_driver_lookup_name通过driver name遍历整个driver list,找到U_BOOT_DRIVER(root_driver)定义的driver地址。

device_bind_common创建udevice dm_root和uclass root,并将driver root_driver、udevice dm_root和uclass root三者进行绑定。

int device_bind_by_name(struct udevice *parent, bool pre_reloc_only,const struct driver_info *info, struct udevice **devp)
{struct driver *drv;uint plat_size = 0;int ret;//通过driver name遍历整个driver list,找到U_BOOT_DRIVER(root_driver)定义的driver地址drv = lists_driver_lookup_name(info->name);//创建udevice dm_root和uclass root,并将driver root_driver、udevice dm_root和uclass root三者进行绑定。ret = device_bind_common(parent, drv, info->name, (void *)info->plat, 0,ofnode_null(), plat_size, devp);return ret;
}

2.2 device_probe分析

DM_ROOT_NON_CONST代表gd中得dm_root:(((gd_t *)gd)->dm_root)device_probe是一个通用的函数, 其大概步骤如下:

  1. 检测该device是否已经激活,已激活就直接返回。
  2. 获取该设备对应的driver。
  3. 读取设备的平台数据。
  4. 如果该设备存在parent,那么先probe parent设备,确保所有的parent dev都被probed。
  5. 标记该设备处于激活状态。
  6. 处理除root device的pinctrl之外的所有设备,对于pinctrl device不进行pinctrl的设置,因为设备可能还没有被probed。
  7. 如果配置了IOMMU,则需要先打开IOMMU。
  8. 预使能设备,包括uclass的pre-probe()和父uclass的child_pre_probe()方法。
  9. 处理{clocks/clock-parents/clock-rates}属性配置时钟。
  10. 执行该设备的driver的probe函数,激活该设备。
  11. uclass_post_probe_device() 处理一个刚刚被probed过的设备。
  12. 最后处理IOMUX驱动,如果是IOMUX驱动则需要设置pinctl的默认状态。
int device_probe(struct udevice *dev)
{const struct driver *drv;int ret;// 检测该device是否已经激活,已激活就直接返回。if (dev->flags & DM_FLAG_ACTIVATED)return 0;drv = dev->driver; // 获取该设备对应的driver//device_ofdata_to_platdata() - 设备读取平台数据ret = device_ofdata_to_platdata(dev);// 如果该设备存在parent,那么先probe parent设备,确保所有的parent dev都被probed。if (dev->parent) {ret = device_probe(dev->parent);}// 标记该设备处于激活状态。dev_or_flags(dev, DM_FLAG_ACTIVATED);//处理除root device的pinctrl之外的所有设备,对于pinctrl device不进行pinctrl的设置,因为设备可能还没有被probed。if (dev->parent && device_get_uclass_id(dev) != UCLASS_PINCTRL)pinctrl_select_state(dev, "default");//如果配置了IOMMU,则需要先打开IOMMUif (CONFIG_IS_ENABLED(IOMMU) && dev->parent &&(device_get_uclass_id(dev) != UCLASS_IOMMU)) {ret = dev_iommu_enable(dev);}//预使能设备,包括uclass的pre-probe()和父uclass的child_pre_probe()方法。ret = uclass_pre_probe_device(dev);if (dev->parent && dev->parent->driver->child_pre_probe) {ret = dev->parent->driver->child_pre_probe(dev);}//只处理具有有效ofnode的设备if (dev_of_valid(dev) && !(dev->driver->flags & DM_FLAG_IGNORE_DEFAULT_CLKS)) {// 处理{clocks/clock-parents/clock-rates}属性配置时钟ret = clk_set_defaults(dev, CLK_DEFAULTS_PRE);}// 执行该设备的driver的probe函数,激活该设备。if (drv->probe) {ret = drv->probe(dev);}//uclass_post_probe_device() - 处理一个刚刚被probed过的设备ret = uclass_post_probe_device(dev);//设置pinctl的默认状态if (dev->parent && device_get_uclass_id(dev) == UCLASS_PINCTRL)pinctrl_select_state(dev, "default");return 0;
}

3. DM模型Device Tree节点的设备初始化:dm_scan(pre_reloc_only)

dm_scan函数是在其他地方(设备树)搜索设备并进行驱动匹配,然后bind。主要分为三步:

  1. dm_scan_plat,搜索并绑定所有的驱动到根节点(((gd_t *)gd)->dm_root)上;
  2. dm_extended_scan,扫描dtb文件;
  3. dm_scan_other,留给厂商自定义覆盖的弱函数。

本节主要展开分析dm_extended_scan函数,下面是这个函数的流程:

dm_extended_scan(bool pre_reloc_only)
-->dm_scan_fdt(pre_reloc_only)-->dm_scan_fdt_node(gd->dm_root, ofnode_root(), pre_reloc_only)-->lists_bind_fdt(parent, node, NULL, NULL, pre_reloc_only)
-->for : dm_scan_fdt_ofnode_path(nodes[i], pre_reloc_only)-->dm_scan_fdt_node(gd->dm_root, node, pre_reloc_only)

首先这个函数定义了需要扫描的三个父节点,以下的扫描和绑定都是基于这三个节点展开的。

const char * const nodes[] = {"/chosen","/clocks","/firmware"
};

Device Tree设备初始化的核心函数是dm_scan_fdt_node->lists_bind_fdt,扫描设备树遍历父节点,并且将驱动绑定和设备树节点绑定。

步骤:

  1. ofnode_get_property() ,根据node节点获取compatible属性字符串list;
  2. 遍历兼容字符串列表,driver_check_compatible尝试匹配每个字符串兼容字符串,以便我们按优先级顺序匹配从第一个字符串到最后一个;
  3. 找到匹配的驱动,device_bind_with_driver_data() 创建一个设备并且绑定到driver。

4. device_bind_common

device_bind_common用于绑定设备树节点和驱动,主要有三处调用,分别是device_bind_by_namedevice_bind_with_driver_datadevice_binddevice_bind_by_name 在dm模型初始化的时候来初始化根设备global_data->dm_rootdevice_bind_with_driver_data在扫描设备树并绑定驱动的时候调用;device_bind在可以选择其余驱动的bind函数中调用,手动绑定想要的设备树节点和驱动。例如下图中apple笔记本的pinctl驱动:

绑定的主要步骤:

  1. uclass_get() 根据driver->id获取一个uclass(.id = UCLASS_PINCTRL),如果它不存在就创建它。每个类都由一个ID标识,一个从0到n-1的数字,其中n是类的数量。这个函数允许根据类的ID查找类。

    uclass_get(drv->id, &uc)
    
  2. 创建一个新的device,申请一个struct udevice空间

    dev = calloc(1, sizeof(struct udevice))
    
  3. 初始化新device的相关列表头

    INIT_LIST_HEAD(&dev->sibling_node);
    INIT_LIST_HEAD(&dev->child_head);
    INIT_LIST_HEAD(&dev->uclass_node);
    INIT_LIST_HEAD(&dev->devres_head);
    
  4. 初始化新udevice相关数据,将新udevice和传入的driver、uclass进行绑定。

    dev->platdata = platdata;-------->传入
    dev->driver_data = driver_data;-->传入
    dev->name = name;------>传入
    dev->node = node;
    dev->parent = parent;
    dev->driver = drv;----->传入
    dev->uclass = uc;
    
  5. dev->seq为该设备分配的序列号(-1 = none)。这是在设备probe时设置的,并且在设备的uclass中是唯一的。dev->req_seq为此设备请求的序列号(-1 = any)

    dev->seq = -1;
    dev->req_seq = -1;
    if (CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)) {if (uc->uc_drv->name && ofnode_valid(node))dev_read_alias_seq(dev, &dev->req_seq);
    } else {dev->req_seq = uclass_find_next_free_req_seq(drv->id);
    }
    
  6. sibling_node对应该设备,并将它添加到parent的child_head设备列表中

    if (parent)list_add_tail(&dev->sibling_node, &parent->child_head);
    
  7. uclass_bind_device() 将udevice与uclass进行关联

    uclass_bind_device(dev);
    
  8. device绑定成功后,就会调用drv->bind

    if (drv->bind) {ret = drv->bind(dev);
    }
    
  9. 在一个新的child被绑定后,就会调用parent的parent->driver->child_post_bind(dev)

    if (parent && parent->driver->child_post_bind) {ret = parent->driver->child_post_bind(dev);
    }
    
  10. 在一个新设备绑定到这个uclass后被调用

    if (uc->uc_drv->post_bind) {ret = uc->uc_drv->post_bind(dev);
    }
    

5. 总结

对于DM模型初始化来说,uboot会在启动序列中使用dm_init创建一个dm_root(udevice)并将其绑定到“root_driver”(driver),然后device_probe来激活这个设备。第二步使用dm_scan来绑定设备树中的设备和驱动到dm_root下面。

Uboot中的DM驱动模型相关推荐

  1. u-boot下的DM驱动模型

    U-boot 下DM驱动模型的相关笔记 要注意的关键两点: DM驱动模型的一般流程bind->ofdata_to_platdata(可选)->probe 启动,bind操作时单独完成的,主 ...

  2. 【uboot】uboot 2020.04 DM驱动模式 -- Demo体验

    文章目录 1. 前言 2. uboot的驱动模型简介 3. U_BOOT_CMD(do_demo)分析 4. 执行命令demo list 5. 执行命令demo hello 6. 执行命令demo l ...

  3. yang模型中rpc_领域驱动模型(DDD)设计讲解

    一. 什么是领域驱动模型(DDD)? 领域驱动模型一种设计思想,我们又称为DDD设计思想.是一种为了解决传统设计思想带来的维护困难,沟通困难和交互困难而产生的一种新的思想.也解决了在部分公司中,一个项 ...

  4. 在U-boot中添加以太网驱动-Nazgul

    当定义CONFIG_CMD_NET和CONFIG_CMD_PING,编译之后执行ping命令,告警没有找到以太网. 因此,需要打开U-boot的网络功能, u-boot-sunxi-sunxi中没有找 ...

  5. 详解sd协议以及裸机和u-boot中的sd卡驱动(2)

    3. sd卡驱动 3.1 引入 经过第2章我们知道,要想实现读写sd卡,需要按照sd协议规定的基本传输单位(命令.响应.数据)以及流程(初始化.读.写),向sd卡发送信号或者从sd卡接收信号. 为了简 ...

  6. uboot usb驱动模型

    关于uboot驱动模型,这篇文章讲得很好,不再描述: [uboot] (番外篇)uboot 驱动模型_ooonebook的博客-CSDN博客 U-Boot Driver Model领域模型设计 lin ...

  7. 从串口驱动的移植看linux2.6内核中的驱动模型 platform device platform driver【转】...

    转自:http://blog.csdn.net/bonnshore/article/details/7979705 写在前面的话: 博主新开了个人站点:你也可以在这里看到这篇文章,点击打开链接 本文是 ...

  8. uboot中 使用i2c

    uboot中i2c读写有2种方式,一种使用uboot驱动模型,通过宏 CONFIG_DM_I2C定义,另一种是传统方式,通过宏CONFIG_SYS_I2C定义. 传统方式--SYS_I2C 1.设置总 ...

  9. Linux设备驱动模型1——简介和底层架构

    以下内容源于朱有鹏<物联网大讲堂>课程的学习整理,如有侵权,请告知删除. 一.linux设备驱动模型简介 1.什么是设备驱动模型? (1)类class.总线bus.设备device.驱动d ...

最新文章

  1. 201705-201706 任务书单
  2. 把 Eclipse 中的工程 Push 到 Github(适用 Windows 平台)
  3. 互联网架构“高并发”到底怎么玩?
  4. 洛谷 - P2051 [AHOI2009]中国象棋(计数dp)
  5. linux 64 32 编译支持,在64位linux上编译32位程序 for i386 intel
  6. mysql数据库事务有几种特性_面试官:你能说说事务的几个特性是啥?有哪几种隔离级别?...
  7. 华为VLAN聚合原理与实验
  8. 图层照片如何扣头发丝
  9. 那一年,创业 vs 阿里(下):阿里篇
  10. 深入理解加载FBX模型文件
  11. 在vscode中新建html文件的两种方法
  12. Linux下SD卡格式化,为SD卡分区
  13. Unhandled exception in al.exe(KERNELBASE.DLL):0xE06D7363:Microsoft C++Exception
  14. 你能说更多关于崩坏3琪亚娜的细节吗
  15. 设计师专属的导航网站
  16. 重装系统教程(适合小白)
  17. 一文讲透IC 芯片生产流程:从设计到制造与封装。
  18. Python视觉深度学习系列教程 第二卷 第9章 Kaggle竞赛:Cat与Dog
  19. 安装和控制DNS服务器
  20. 微信小程序中使用slot插槽

热门文章

  1. 深入理解搜索引擎——开篇
  2. 运算符优先级,对象深拷贝
  3. vue 实现强制类型转换 ,将数字变为字符串,字符串变为数字,数组数字变为字符串、js对象转数组
  4. 结合DVWA的反射型XSS浅析
  5. Maching Learning
  6. 华清远见嵌入式Linux驱动开发培训班
  7. 魔方机器人matlab编程,C++实现超赞的解魔方的机器人代码
  8. Uni-App 云打包使用自有证书的步骤
  9. 星空的征途:浪潮如何用三个力加速行业AI?
  10. 运行npm run dev报错 internal/modules/cjs/loader.js:883 throw err;