ubootuboot 2020.04 DM驱动模式 -- 架构分析
Posted ZHONGCAI0901
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ubootuboot 2020.04 DM驱动模式 -- 架构分析相关的知识,希望对你有一定的参考价值。
相关文章
1.《【uboot】imx6ull uboot 2020.04源码下载和编译环境配置》
2.《【uboot】uboot 2020.04 DM驱动模式 – Demo体验》
1. DM驱动模式简介
uboot引入了驱动模型(driver model)简称为DM,这种驱动模型为驱动的定义和访问接口提供了统一的方法。提高了驱动之间的兼容性以及访问的标准型。它主要包含以下4个成员:
- udevice:它就是指设备对象,一个driver的实例。
- driver:udevice的驱动,硬件外设的driver。
- uclass:一个uboot驱动类,收集类似的驱动程序。
- uclass_driver:uclass对应的driver
下面举例gpio DM驱动模型来介绍它们之间的联系,如下:
在uboot全局变量中保存了gd->uclass_root
,它就是uclass的head。我们可以通过成员struct list_head sibling_node
遍历整个uclass list,然后找到想要的uclass。它们之间的关系如下:
在uboot命令里面有一个dm
命令,可以打印整个DM驱动模型tree,可以帮助我们更好的理解DM。在uboot命令行输入:dm tree
,结果如下:
=> dm tree
Class Index Probed Driver Name
-----------------------------------------------------------
root 0 [ + ] root_driver root_driver
thermal 0 [ ] imx_thermal |-- imx_thermal
simple_bus 0 [ + ] generic_simple_bus |-- soc
simple_bus 1 [ + ] generic_simple_bus | |-- aips-bus@2000000
simple_bus 2 [ ] generic_simple_bus | | |-- spba-bus@2000000
gpio 0 [ + ] gpio_mxc | | |-- gpio@209c000
gpio 1 [ + ] gpio_mxc | | |-- gpio@20a0000
gpio 2 [ + ] gpio_mxc | | |-- gpio@20a4000
gpio 3 [ + ] gpio_mxc | | |-- gpio@20a8000
gpio 4 [ + ] gpio_mxc | | |-- gpio@20ac000
eth 0 [ ] fecmxc | | |-- ethernet@20b4000
eth_phy_ge 0 [ ] eth_phy_generic_drv | | | |-- ethernet-phy@2
eth_phy_ge 1 [ ] eth_phy_generic_drv | | | `-- ethernet-phy@1
simple_bus 3 [ ] generic_simple_bus | | |-- anatop@20c8000
simple_bus 4 [ ] generic_simple_bus | | |-- snvs@20cc000
pinctrl 0 [ + ] imx6-pinctrl | | `-- iomuxc@20e0000
pinconfig 0 [ ] pinconfig | | |-- csi1grp
pinconfig 1 [ + ] pinconfig | | |-- enet1grp
pinconfig 2 [ + ] pinconfig | | |-- enet2grp
pinconfig 3 [ ] pinconfig | | |-- flexcan1grp
pinconfig 4 [ ] pinconfig | | |-- flexcan2grp
pinconfig 5 [ ] pinconfig | | |-- i2c1grp
pinconfig 6 [ ] pinconfig | | |-- i2c1grp_gpio
pinconfig 7 [ ] pinconfig | | |-- i2c2grp
pinconfig 8 [ + ] pinconfig | | |-- lcdifdatgrp
pinconfig 9 [ + ] pinconfig | | |-- lcdifctrlgrp
pinconfig 10 [ ] pinconfig | | |-- qspigrp
pinconfig 11 [ ] pinconfig | | |-- sai2grp
pinconfig 12 [ ] pinconfig | | |-- pwm1grp
pinconfig 13 [ ] pinconfig | | |-- sim2grp
pinconfig 14 [ ] pinconfig | | |-- spi4grp
pinconfig 15 [ ] pinconfig | | |-- tscgrp
pinconfig 16 [ ] pinconfig | | |-- uart1grp
pinconfig 17 [ ] pinconfig | | |-- uart2grp
pinconfig 18 [ ] pinconfig | | |-- usbotg1grp
pinconfig 19 [ + ] pinconfig | | |-- usdhc1grp
pinconfig 20 [ ] pinconfig | | |-- usdhc1grp100mhz
pinconfig 21 [ ] pinconfig | | |-- usdhc1grp200mhz
pinconfig 22 [ ] pinconfig | | |-- usdhc2grp
pinconfig 23 [ + ] pinconfig | | |-- usdhc2grp_8bit
pinconfig 24 [ ] pinconfig | | |-- usdhc2grp_8bit_100mhz
pinconfig 25 [ ] pinconfig | | |-- usdhc2grp_8bit_200mhz
pinconfig 26 [ ] pinconfig | | `-- wdoggrp
simple_bus 5 [ + ] generic_simple_bus | |-- aips-bus@2100000
usb 0 [ ] ci-udc-otg | | |-- usbg1
usb 1 [ ] ci-udc-otg | | |-- usbg2
usb 0 [ ] ehci_mx6 | | |-- usb@2184000
usb 1 [ ] ehci_mx6 | | |-- usb@2184200
eth 1 [ ] fecmxc | | |-- ethernet@2188000
mmc 0 [ + ] fsl-esdhc-mmc | | |-- usdhc@2190000
blk 0 [ ] mmc_blk | | | `-- usdhc@2190000.blk
mmc 1 [ + ] fsl-esdhc-mmc | | |-- usdhc@2194000
blk 1 [ + ] mmc_blk | | | `-- usdhc@2194000.blk
i2c 0 [ ] i2c_mxc | | |-- i2c@21a0000
i2c 1 [ ] i2c_mxc | | |-- i2c@21a4000
video 0 [ + ] mxs_video | | |-- lcdif@21c8000
vidconsole 0 [ + ] vidconsole0 | | | `-- lcdif@21c8000.vidconsole0
spi 0 [ ] fsl_qspi | | `-- spi@21e0000
spi_flash 0 [ ] spi_flash_std | | `-- n25q256a@0
simple_bus 6 [ ] generic_simple_bus | `-- aips-bus@2200000
pinctrl 1 [ ] imx6-pinctrl | `-- iomuxc-snvs@2290000
regulator 0 [ ] fixed regulator |-- regulator-sd1-vmmc
regulator 1 [ ] fixed regulator |-- regulator-can-3v3
spi 1 [ ] soft_spi |-- spi4
gpio 5 [ ] 74x164 | `-- gpio@0
demo 0 [ ] demo_imx6ull_drv |-- test_demo@0
demo 1 [ ] demo_imx6ull_drv |-- test_demo@1
demo 2 [ ] demo_imx6ull_drv |-- test_demo@2
demo 3 [ ] demo_imx6ull_drv `-- test_demo@3
=>
1.1 struct udevice的定义
/**
* struct udevice - 它是一个driver的实例
*
* 它包含有关设备的信息,设备是绑定到特定port或peripheral的驱动(本质上是一个驱动程序实例)。
*
* 设备将通过'bind'调用产生,可能是由于U_BOOT_DEVICE()宏(在这种情况下platdata是非null)或设备
* 树中的一个节点(在这种情况下of_offset是 >= 0)。在后一种情况下,我们将在driver的ofdata_to_platdata
* 方法中实现Device Tree信息转换为platdata(如果设备有一个设备树节点,就在probe方法之前调用)。
*
* platdata, priv和uclass_priv都可以通过driver来手动分配空间,或者你可以使用struct driver和struct uclass_driver
* 的auto_alloc_size成员来让driver model自动分配空间。
*/
struct udevice {
const struct driver *driver; // 这个设备使用的driver
const char *name; // 设备名,通常是FDT节点名
void *platdata; // 该设备的配置数据
void *parent_platdata; // 这个设备的父总线配置数据
void *uclass_platdata; // 这个设备的uclass配置数据
ofnode node; // 此设备对应设备树节点的引用
ulong driver_data; // 此设备与匹配的驱动struct udevice_id中的data成员数据
struct udevice *parent; // 这个设备的父类,或者是顶级设备那么就为NULL。
void *priv; // 此设备的私有数据
struct uclass *uclass; // 指向这个设备uclass的指针
void *uclass_priv; // 对于这个设备uclass的私有数据
void *parent_priv; // 对于这个设备parent的私有数据
struct list_head uclass_node; // uclass通过它来链接设备(uclass_node可以获取当前udevice的地址,它被挂载在对应id uclass的uc->dev_head链表中,uclass_foreach_dev可以遍历对应id uclass的所有udevice。)
struct list_head child_head; // 此设备的子设备列表(如果当前设备为parent,那么child的sibling_node将会在child_head链表上)
struct list_head sibling_node; // 在设备列表中的下一个设备(sibling_node可以获取当前udevice的地址,通过它可以遍历挂在链表上的下一个udevice。)
uint32_t flags; // 此设备的标志,DM_FLAG_...开头,如:DM_FLAG_ALLOC_UCLASS_PDATA等
int req_seq; // 此设备请求的序列号(-1 = any)
int seq; // 为该设备分配的序列号(-1 = none)。这是在设备probe时设置的,并且在设备的uclass中是唯一的。
#ifdef CONFIG_DEVRES
// 当CONFIG_DEVRES被定义时,devm_kmalloc()和friends会添加到这个列表中。这样分配的内存将在设备被移除/解除绑定时自动释放。
struct list_head devres_head; // 与此设备相关联的memory allocations列表。
#endif
};
1.2 struct driver的定义
/**
* struct driver - 一个功能或硬件外设的driver
*
* driver含有创建新设备和删除设备的方法,设备由platdata或者device tree节点提供的信息来设置自己
*(我们通过查找与of_match匹配的compatible字符串进行配对)。
*
* 基本上drivers都属于一个uclass,代表同一类型的一类设备。drivers的公共元素可以在uclass中实现,
* 或者uclass可以为其中的drivers提供一致的接口。
*/
struct driver {
char *name; // device名称
enum uclass_id id; // 标记driver属于哪个uclass的id
const struct udevice_id *of_match; // 要匹配的compatible字符串列表,以及每个字符串的标识数据。
int (*bind)(struct udevice *dev); // 绑定device到它的driver时被调用
int (*probe)(struct udevice *dev); // 当探测一个device时被调用,例如:激活它。
int (*remove)(struct udevice *dev); // 当移除一个device时被调用,例如:停用它。
int (*unbind)(struct udevice *dev); // 解除device和driver绑定时被调用
int (*ofdata_to_platdata)(struct udevice *dev); // 在probe设备之前调用,解码device tree数据。
int (*child_post_bind)(struct udevice *dev); // 在一个新的child设备被绑定之后调用
int (*child_pre_probe)(struct udevice *dev); // 在probe child设备之前调用。设备已分配内存,但尚未探测。
int (*child_post_remove)(struct udevice *dev); // 在子设备被移除后调用。设备已经分配了内存,但是它的device_remove()方法已经被调用。
int (*handle_interrupts)(struct udevice *dev);
int priv_auto_alloc_size; // 如果非零,它就是"udevice->priv"指针中分配的私有数据的大小。如果为0,则driver负责分配所需私有数据的空间。
int platdata_auto_alloc_size; // 如果非零,它就是"udevice->platdata"指针中分配的私有数据的大小。这通常只对设备树感知的驱动程序有用(那些带有of_match的驱动程序),因为使用platdata的驱动程序将在U_BOOT_DEVICE()实例化中提供数据。
int per_child_auto_alloc_size; // 每个device都可以含有父设备拥有的私有数据。如果需要,该值是非零,将自动分配该值。
int per_child_platdata_auto_alloc_size; // 一个总线习惯存储关于它的子节点的信息。如果非零,则分配该数据的大小空间,并将保存到子节点的parent_platdata指针中。
const void *ops; // driver的具体操作,这通常是一个由driver定义的函数指针列表,用于实现uclass所需的驱动程序函数。
uint32_t flags; // driver flags - see DM_FLAGS_...
};
1.3 struct uclass的定义
/**
* struct uclass - 一个uboot驱动类,收集类似的驱动程序。
*
* 一个uclass提供了一个特定功能的接口,该功能由一个或多个driver实现。每个driver都属于一个uclass,
* 即使它是该uclass中唯一的driver。列举一个GPIO uclass的例子,它提供了更改为读输入、设置或清除输出状态等功能。
* 这些可能是SoC GPIO banks、I2C GPIO扩展器和PMIC IO lines等的driver,所有这些都通过uclass以统一的方式提供。
*/
struct uclass {
void *priv; // 这个uclass的私有数据
struct uclass_driver *uc_drv; // uclass本身的驱动程序,不要与“struct driver”混淆。
struct list_head dev_head; // 这个uclass中的设备列表(当设备的bind方法被调用时,设备被附加到它们的类上。)
struct list_head sibling_node; // uclass链表中的下一个uclass
};
1.4 struct uclass_driver的定义
/**
* struct uclass_driver - uclass对应的driver
*
* uclass_driver为一组相关的驱动程序提供了同一的接口。
*/
struct uclass_driver {
const char *name; // uclass driver的名称
enum uclass_id id; // 这个uclass的ID号
int (*post_bind)(struct udevice *dev); // 在一个新设备绑定到这个uclass后被调用
int (*pre_unbind)(struct udevice *dev); // 在设备从该uclass解绑定之前调用
int (*pre_probe)(struct udevice *dev); // 在probe一个新设备之前调用
int (*post_probe)(struct udevice *dev); // 在probe一个新设备之后调用
int (*pre_remove)(struct udevice *dev); // 在移除设备之前调用
int (*child_post_bind)(struct udevice *dev); // 在这个uclass的child绑定到一个设备之后被调用
int (*child_pre_probe)(struct udevice *dev); // 在这个uclass的child被probed之前被调用
int (*child_post_probe)(struct udevice *dev); // 在这个uclass的child被probed之后被调用
int (*init)(struct uclass *class); // 在创建一个新的uclass时被调用
int (*destroy)(struct uclass *class); // 在uclass被销毁时被调用
int priv_auto_alloc_size; // 如果非零,它就是"uclass->priv"指针中分配的私有数据的大小。如果为0,则uclass driver负责分配所需私有数据的空间。
int per_device_auto_alloc_size; // 每个device都可以将uclass拥有的私有数据保存在'dev->uclass_priv'。如果该值是非零,将自动分配该值大小的空间。
int per_device_platdata_auto_alloc_size; // 每个device都可以将uclass拥有的平台数据保存在'dev->uclass_platdata'。如果该值是非零,将自动分配该值大小的空间。
int per_child_auto_alloc_size; // 每个子设备(在这个uclass中一个parent的child)可以保存device/uclass中的parent数据。地址为'dev->parent_priv'
int per_child_platdata_auto_alloc_size; // 一个总线习惯保存关于它的子节点的信息。如果非零,将分配到子设备的'dev->parent_platdata'指针中。
const void *ops; // uclass的相关操作,为uclass内的设备提供一致的接口。
uint32_t flags; // 这个uclass的标志(DM_UC_…)
};
2. DM模型root节点的初始化
dm初始化的接口在dm_init_and_scan
中,初始化流程主要有两次,入口函数分别是static int initf_dm(void)
和static int initr_dm(void)
。第一次是在重定位之前,调用的是initf_dm函数。第二次是在重定位之后,调用的是initr_dm函数。
static int initf_dm(void)
{
#if defined(CONFIG_DM) && defined(CONFIG_SYS_MALLOC_F_LEN)
int ret;
ret = dm_init_and_scan(true); // 调用dm_init_and_scan对DM进行初始化和设备的解析
if (ret)
return ret;
#endif
return 0;
}
#ifdef CONFIG_DM
static int initr_dm(void)
{
int ret;
/* Save the pre-reloc driver model and start a new one */
gd->dm_root_f = gd->dm_root; // 存储relocate之前的根设备
gd->dm_root = NULL;
ret = dm_init_and_scan(false); // 调用dm_init_and_scan对DM进行初始化和设备的解析
if (ret)
return ret;
return 0;
}
#endif
int dm_init_and_scan(bool pre_reloc_only)
{
int ret;
// 1. 将根节点绑定到gd->dm_root上,初始化根节点设备
ret = dm_init(IS_ENABLED(CONFIG_OF_LIVE));
// 2. 搜索使用宏U_BOOT_DEVICE定义的设备进行驱动匹配,也就是bind子节点(不推荐U_BOOT_DEVICE定义设备)
ret = dm_scan_platdata(pre_reloc_only);
// 3. 在其他地方(设备树)搜索设备并进行驱动匹配,然后bind。(推荐Device Tree定义设备)
ret = dm_extended_scan_fdt(gd->fdt_blob, pre_reloc_only);
// 4. 暂未使用
ret = dm_scan_other(pre_reloc_only);
return 0;
}
下面是DM root初始化dm_init()函数的调用关系如下所示:
[root.c]dm_init(); // 主要是初始化driver model的root实例(gd->dm_root和gd->uclass_root)
[device.c]device_bind_by_name(NULL, false, &root_info, &DM_ROOT_NON_CONST);// 创建一个device并将其绑定到driver,这是一个帮助器函数,用于绑定不使用设备树的设备。
[list.c]lists_driver_lookup_name(“root_driver”); // 通过driver name “root_driver”遍历整个driver list,找到U_BOOT_DRIVER(root_driver)定义的driver地址
[device.c]device_bind_common(NULL, drv, “root_driver”, NULL, driver_data, node, 0, devp); // 创建udevice dm_root和uclass root,并将driver root_driver、udevice dm_root和uclass root三者进行绑定。
[uclass.c]uclass_get(drv->id, &uc); // 根据ID UCLASS_ROOT获取对应的uclass,第一次运行它不存在,所以创建它。
[uclass.c]uclass_find(); // 第一次运行无法找到对应的uclass UCLASS_ROOT。
[uclass.c]uclass_add(); //在未找到的情况下,就会在列表中创建一个新的root uclass。
[device.c]calloc(1, size); // 创建第一个udevice dm_root并初始化;
[uclass.c]uclass_bind_device(dev); // 将udevice dm_root与root uclass进行绑定,设备udevice dm_root连接到root uclass的uc->dev_head设备链表中。
[device.c]device_probe(gd->dm_root); // 探测设备udevice dm_root并激活它
下面对涉及到的主要函数进行分析。
2.1 dm_init()函数分析
/**
* dm_init() - 初始化Driver Model
*
* 这个函数将初始化root driver tree和root class tree。在任何使用DM之前都需要调用它。
*/
int dm_init(bool of_live)
{
int ret;
// 初始化uclass_root链表头
INIT_LIST_HEAD(&DM_UCLASS_ROOT_NON_CONST); // gd->uclass_root
// 创建一个device dm_root并将其绑定到driver name “root_driver”。
ret = device_bind_by_name(NULLubootuboot 2020.04 Pinctrl子系统分析和使用
3DM速报:英伟达驱动更新 稳定RTX3080/90,育碧创立刺客信条“姐妹会”