1.《【uboot】imx6ull uboot 2020.04源码下载和编译环境配置》
2.《【uboot】uboot 2020.04 DM驱动模式 – Demo体验》
uboot引入了驱动模型(driver model)简称为DM,这种驱动模型为驱动的定义和访问接口提供了统一的方法。提高了驱动之间的兼容性以及访问的标准型。它主要包含以下4个成员:
下面举例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
=>
/**
* 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
};
/**
* 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_...
};
/**
* 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
};
/**
* 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_…)
};
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并激活它
下面对涉及到的主要函数进行分析。
/**
* 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(NULL, false, &root_info, &DM_ROOT_NON_CONST); // root_info.name = "root_driver" DM_ROOT_NON_CONST = gd->dm_root
#if CONFIG_IS_ENABLED(OF_CONTROL)
DM_ROOT_NON_CONST->node = offset_to_ofnode(0);
#endif
// 探测设备udevice dm_root并激活它
ret = device_probe(DM_ROOT_NON_CONST);
return 0;
}
/**
* device_bind_by_name: 创建一个device并将其绑定到driver
*
* 这是一个帮助器函数,用于绑定不使用设备树的设备。
*/
int device_bind_by_name(struct udevice *parent, bool pre_reloc_only,
const struct driver_info *info, struct udevice **devp)
{
struct driver *drv;
uint platdata_size = 0;
// 通过driver name遍历整个driver list,找到U_BOOT_DRIVER(root_driver)定义的driver地址
drv = lists_driver_lookup_name(info->name); // info->name = "root_driver"
#if CONFIG_IS_ENABLED(OF_PLATDATA)
platdata_size = info->platdata_size;
#endif
// 创建udevice dm_root和uclass root,并将driver root_driver、udevice dm_root和uclass root三者进行绑定。
return device_bind_common(parent, drv, info->name,
(void *)info->platdata, 0, ofnode_null(), platdata_size,
devp);
}
/**
* device_probe() - 探测一个设备并激活它
*
* 激活一个设备以便它可以随时使用。首先探查它的所有父节点。
*/
int device_probe(struct udevice *dev)
{
const struct driver *drv;
int ret;
int seq;
// 检测该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);
if (dev->flags & DM_FLAG_ACTIVATED)
return 0;
}
/**
* uclass_resolve_seq() - 解析device的序列号
*
* 在"dev->seq = -1"时,然后"dev->req_seq = -1"代表自动分配一个序列号,"dev->req_seq > 0"代表分配
* 指定的序列号。如果请求的序列号正在使用中,那么该设备将被分配另一个序列号。
*
* 注意,该函数不会改变设备的seq值,dev->seq需要手动赋值修改。
*/
seq = uclass_resolve_seq(dev);
dev->seq = seq;
// 标记该设备处于激活状态。
dev->flags |= 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");
/**
* uclass_pre_probe_device() - 处理一个即将被probed的设备
*
* 先执行uclass需要的任何预处理,然后才可以探测它。这包括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, 0);
}
// 执行该设备的driver的probe函数,激活该设备。
if (drv->probe) {
ret = drv->probe(dev);
}
/**
* uclass_post_probe_device() - 处理一个刚刚被probed过的设备
*
* 对uclass探测设备后,需要执行的操作。
*/
ret = uclass_post_probe_device(dev);
if (dev->parent && device_get_uclass_id(dev) == UCLASS_PINCTRL)
pinctrl_select_state(dev, "default");
return 0;
}
/**
* device_ofdata_to_platdata() - 设备读取平台数据
*
* 读取设备的平台数据(通常是从设备树中),以便提供探测设备所需的信息。
*/
int device_ofdata_to_platdata(struct udevice *dev)
{
const struct driver *drv;
int size = 0;
int ret;
// DM_FLAG_PLATDATA_VALID代表platdata已经读取,当设备被移除时要清除。
if (dev->flags & DM_FLAG_PLATDATA_VALID)
return 0;
drv = dev->driver; // 获取该设备对应的driver
// 如果udevice dev->priv需要申请空间,driver的priv_auto_alloc_size为非零,则分配私有数据,但已分配就不重复申请。
if (drv->priv_auto_alloc_size && !dev->priv) {
dev->priv = alloc_priv(drv->priv_auto_alloc_size, drv->flags);
}
// 如果udevice隶属的uclass dev->uclass_priv需要申请空间,uclass driver的per_device_auto_alloc_size为非零,则给uclass分配私有数据,但已分配就不重复申请。
size = dev->uclass->uc_drv->per_device_auto_alloc_size;
if (size && !dev->uclass_priv) {
dev->uclass_priv = alloc_priv(size, dev->uclass->uc_drv->flags);
}
/* 为这个child dev分配parent data空间 */
if (dev->parent) {
size = dev->parent->driver->per_child_auto_alloc_size;
if (!size) {
size = dev->parent->uclass->uc_drv->per_child_auto_alloc_size;
}
if (size && !dev->parent_priv) {
dev->parent_priv = alloc_priv(size, drv->flags);
}
}
// 在driver的probe设备之前调用,先解码device tree数据。
if (drv->ofdata_to_platdata &&
(CONFIG_IS_ENABLED(OF_PLATDATA) || dev_has_of_node(dev))) {
ret = drv->ofdata_to_platdata(dev);
}
dev->flags |= DM_FLAG_PLATDATA_VALID; // 标记该设备已经解析device tree数据。
return 0;
}
dm_extended_scan_fdt
函数是在其他地方(设备树)搜索设备并进行驱动匹配,然后bind。下面是函数的调用关系如下所示:
[root.c]dm_extended_scan_fdt(gd->fdt_blob, false); // 在设备树中搜索设备并进行驱动匹配,然后bind。
[root.c]dm_scan_fdt(gd->fdt_blob, false); // 扫描设备树并绑定驱动程序
[root.c]dm_scan_fdt_node(gd->dm_root, gd->fdt_blob, 0, false); // 扫描设备树并绑定一个节点的驱动程序
[lists.c]lists_bind_fdt(gd->dm_root, offset_to_ofnode(0), NULL, false); // 绑定设备树节点,它会给绑定的设备树节点创建一个新udevice,并使用@parent作为其父设备。
[device.c]device_bind_with_driver_data(parent, entry, name, id->data, node, &dev); // 创建一个设备并且绑定到driver。
[device.c]device_bind_common(parent, drv, name, NULL, driver_data, node, 0, devp); // 创建一个设备并且绑定到driver。
[uclass.c]uclass_get(drv->id, &uc); // 根据ID获取一个uclass,如果它不存在就创建它。
[uclass.c]uclass_find(); // 根据id找到对应的uclass。
[uclass.c]uclass_add(); //在未找到的情况下,就会在列表中创建一个新的uclass。
[device.c]calloc(1, size); // 1:创建一个新的udevice并初始化; 2:给dev->platdata、dev->uclass_platdata、dev->parent_platdata分配空间
[device.c]list_add_tail(&dev->sibling_node, &parent->child_head); // 将新dev放到父节点的列表中
[uclass.c]uclass_bind_device(dev);// 将udevice与uclass进行绑定,设备连接到uclass的设备列表中。
[U_BOOT_DRIVER]drv->bind(dev); // device绑定成功后,就会调用drv->bind。
[U_BOOT_DRIVER]parent->driver->child_post_bind(dev); //在一个新的child被绑定后,就会调用parent driver的child_post_bind(dev);
[UCLASS_DRIVER]uc->uc_drv->post_bind(dev); // 在一个新设备绑定到这个uclass后被调用
下面对涉及到的主要函数进行分析。
/**
* parent = gd->dm_root;
* blob = gd->fdt_blob;
* offset = 0 ;
* pre_reloc_only = false;
*
*/
static int dm_scan_fdt_node(struct udevice *parent, const void *blob,
int offset, bool pre_reloc_only)
{
int ret = 0, err;
// 从设备树的根节点gd->fdt_blob开始遍历
for (offset = fdt_first_subnode(blob, offset);
offset > 0;
offset = fdt_next_subnode(blob, offset)) {
// 根据offset获取节点的name
const char *node_name = fdt_get_name(blob, offset, NULL);
/**
* fdtdec_get_is_enabled() - 检查节点是否enable。
* 它会检查“status”属性,如果存在并且status为'ok',则返回1,否则为0。
*/
if (!fdtdec_get_is_enabled(blob, offset)) {
pr_debug(" - ignoring disabled device\n");
continue;
}
/**
* lists_bind_fdt() - 绑定设备树节点
* 它会给绑定的设备树节点创建一个新udevice,并使用@parent作为其父设备。
*/
err = lists_bind_fdt(parent, offset_to_ofnode(offset), NULL,
pre_reloc_only);
if (err && !ret) {
ret = err;
debug("%s: ret=%d\n", node_name, ret);
}
}
return ret;
}
/**
* lists_bind_fdt() - 绑定设备树节点
* 它会给绑定的设备树节点创建一个新udevice,并使用@parent作为其父设备。
*/
int lists_bind_fdt(struct udevice *parent, ofnode node, struct udevice **devp,
bool pre_reloc_only)
{
/**
在编译生成的uboot.map中,可以看到被定义的struct driver list定义在指定的代码段位置:
.u_boot_list_2_driver_2_i2c_mxc
0x000000008786bac4 0x48 drivers/i2c/built-in.o
0x000000008786bac4 _u_boot_list_2_driver_2_i2c_mxc
.u_boot_list_2_driver_2_imx6_pinctrl
0x000000008786bb0c 0x48 drivers/built-in.o
0x000000008786bb0c _u_boot_list_2_driver_2_imx6_pinctrl
ll_entry_start(struct driver, driver)返回指向内存中driver list的首地址。
ll_entry_count(struct driver, driver)返回driver list的个数。
*/
struct driver *driver = ll_entry_start(struct driver, driver);
const int n_ents = ll_entry_count(struct driver, driver);
const struct udevice_id *id;
struct driver *entry;
struct udevice *dev;
const char *name, *compat_list, *compat;
int compat_length, i;
// 根据node节点获取该节点的名称
name = ofnode_get_name(node);
log_debug("bind node %s\n", name);
/**
* ofnode_get_property() - 根据node节点获取compatible属性字符串list,compat_length为整个字符串的长度。
*
* 例如设备树dts文件中定义的node,如下:
* iomuxc: iomuxc@20e0000 {
* compatible = "fsl,imx6ul-iomuxc";
* reg = <0x020e0000 0x4000>;
* };
*/
compat_list = ofnode_get_property(node, "compatible", &compat_length); // compat_list = "fsl,imx6ul-iomuxc"
/*
*遍历兼容字符串列表,尝试匹配每个字符串兼容字符串,以便我们按优先级顺序匹配从第一个字符串到最后一个。
*/
for (i = 0; i < compat_length; i += strlen(compat) + 1) {
compat = compat_list + i;
// 遍历所有U_BOOT_DRIVER定义的driver,并且和设备树中的"compatible"进行比较是否相等。
// 相等说明设备已经找对应的driver,并且break。
for (entry = driver; entry != driver + n_ents; entry++) {
ret = driver_check_compatible(entry->of_match, &id,
compat);
if (!ret)
break;
}
// 如果entry == driver + n_ents,说明已经遍历到driver尾部,未找到对应的driver。
// 然后continue返回从compat_list中查找下一个属性进行匹配。
if (entry == driver + n_ents)
continue;
// 大部分设备都是uboot重定位之后再初始化的,我们主要分析重定位之后情况,所以这里pre_reloc_only就等于false。
if (pre_reloc_only) {
if (!dm_ofnode_pre_reloc(node) &&
!(entry->flags & DM_FLAG_PRE_RELOC)) {
log_debug("Skipping device pre-relocation\n");
return 0;
}
}
/**
* device_bind_with_driver_data() - 创建一个设备并且绑定到driver。
* 设置一个新device连接到driver,在这种情况下,驱动通过提供driver_data的匹配表与设备匹配。
* 一旦绑定,设备就存在,但在调用 device_probe() 之前尚未激活。
*/
ret = device_bind_with_driver_data(parent, entry, name, id->data, node, &dev);
if (ret) {
dm_warn("Error binding driver '%s': %d\n", entry->name, ret);
return ret;
} else {
found = true;
if (devp)
*devp = dev;
}
break;
}
return result;
}
static int device_bind_common(struct udevice *parent, const struct driver *drv,
const char *name, void *platdata,
ulong driver_data, ofnode node,
uint of_platdata_size, struct udevice **devp)
{
struct udevice *dev;
struct uclass *uc;
int size, ret = 0;
/**
* uclass_get() - 根据ID获取一个uclass,如果它不存在就创建它。
*
* 每个类都由一个ID标识,一个从0到n-1的数字,其中n是类的数量。这个函数允许根据类的ID查找类。
*/
ret = uclass_get(drv->id, &uc);
if (ret) {
debug("Missing uclass for driver %s\n", drv->name);
return ret;
}
// 创建一个新的device,申请一个struct udevice空间。
dev = calloc(1, sizeof(struct udevice));
if (!dev)
return -ENOMEM;
// 初始化新device的相关列表头
INIT_LIST_HEAD(&dev->sibling_node);
INIT_LIST_HEAD(&dev->child_head);
INIT_LIST_HEAD(&dev->uclass_node);
#ifdef CONFIG_DEVRES
INIT_LIST_HEAD(&dev->devres_head);
#endif
// 初始化新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;
// dev->seq为该设备分配的序列号(-1 = none)。这是在设备probe时设置的,并且在设备的uclass中是唯一的。
// dev->req_seq为此设备请求的序列号(-1 = any)
dev->seq = -1;
dev->req_seq = -1;
if (CONFIG_IS_ENABLED(DM_SEQ_ALIAS) &&
(uc->uc_drv->flags & DM_UC_FLAG_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 (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);
}
}
if (drv->platdata_auto_alloc_size) {
bool alloc = !platdata;
if (alloc) {
dev->flags |= DM_FLAG_ALLOC_PDATA;
dev->platdata = calloc(1, drv->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);
}
if (parent) {
size = parent->driver->per_child_platdata_auto_alloc_size;
if (!size) {
size = parent->uclass->uc_drv->per_child_platdata_auto_alloc_size;
}
if (size) {
dev->flags |= DM_FLAG_ALLOC_PARENT_PDATA;
dev->parent_platdata = calloc(1, size);
}
}
/* sibling_node对应该设备,并将它添加到parent的child_head设备列表中。 */
if (parent)
list_add_tail(&dev->sibling_node, &parent->child_head);
/**
* uclass_bind_device() - 将udevice与uclass进行关联
*
* 将设备连接到uclass的设备列表中。
*/
ret = uclass_bind_device(dev);
if (ret)
goto fail_uclass_bind;
/* device绑定成功后,就会调用drv->bind。 */
if (drv->bind) {
ret = drv->bind(dev);
}
/* 在一个新的child被绑定后,就会调用parent的parent->driver->child_post_bind(dev); */
if (parent && parent->driver->child_post_bind) {
ret = parent->driver->child_post_bind(dev);
}
/* 在一个新设备绑定到这个uclass后被调用 */
if (uc->uc_drv->post_bind) {
ret = uc->uc_drv->post_bind(dev);
}
if (devp)
*devp = dev;
dev->flags |= DM_FLAG_BOUND;
return 0;
}
/**
* uclass_get() - 根据ID获取一个uclass,如果它不存在就创建它。
*
* 每个类都由一个ID标识,一个从0到n-1的数字,其中n是类的数量。这个函数允许根据类的ID查找类。
*/
int uclass_get(enum uclass_id id, struct uclass **ucp)
{
struct uclass *uc;
*ucp = NULL;
uc = uclass_find(id); // 根据uclass的id找到它。
if (!uc)
return uclass_add(id, ucp); // 在列表中创建新的uclass
*ucp = uc;
return 0;
}
struct uclass *uclass_find(enum uclass_id key)
{
struct uclass *uc;
// dm_root是Driver Model的根实例,它必须先被初始化。
if (!gd->dm_root)
return NULL;
/*
* TODO([email protected]): Optimise this, perhaps moving the found
* node to the start of the list, or creating a linear array mapping
* id to node.
*
* 遍历uclass_root是否有对应uclass_id key。
*/
list_for_each_entry(uc, &gd->uclass_root, sibling_node) {
if (uc->uc_drv->id == key)
return uc;
}
return NULL;
}
/**
* uclass_add() - 在列表中创建新的uclass
* @id: 要创建的Id号
* @ucp: 返回指向uclass的指针,错误时返回NULL
* @return 0 on success, -ve on error
*
* 新的uclass被添加到列表中,每个id只能对应一个uclass。
*/
static int uclass_add(enum uclass_id id, struct uclass **ucp)
{
struct uclass_driver *uc_drv;
struct uclass *uc;
int ret;
*ucp = NULL;
/**
* lists_uclass_lookup() - 根据uclass的ID查找UCLASS_DRIVER()定义对应的uclass_driver
* id: uclass的ID
*/
uc_drv = lists_uclass_lookup(id);
if (!uc_drv) {
debug("Cannot find uclass for id %d: please add the UCLASS_DRIVER() declaration for this UCLASS_... id\n", id);
return -EPFNOSUPPORT;
}
//申请struct uclass空间
uc = calloc(1, sizeof(*uc));
if (!uc)
return -ENOMEM;
// 如果非零,它就是"uclass->priv"指针中分配的私有数据的大小。如果为0,则uclass driver负责分配所需私有数据的空间。
if (uc_drv->priv_auto_alloc_size) {
uc->priv = calloc(1, uc_drv->priv_auto_alloc_size);
if (!uc->priv) {
ret = -ENOMEM;
goto fail_mem;
}
}
uc->uc_drv = uc_drv; // 将id查到到的uclass_driver绑定新的uclass上,以后可以通过uclass直接找到对应的uclass_driver。
INIT_LIST_HEAD(&uc->sibling_node); // 初始化sibling_node列表头,它是指向uclass链表中的下一个uclass。在list_for_each_entry遍历gd->uclass_root链表时会用到。
INIT_LIST_HEAD(&uc->dev_head); // 初始化这个uclass dev_head设备列表头(当device的bind方法被调用时,device被绑定到它们的uclass上。)
list_add(&uc->sibling_node, &DM_UCLASS_ROOT_NON_CONST);// 将sibling_node添加到gd->uclass_root列表。
// 如果uclass_driver的init被定义,那么就会调用该函数。
if (uc_drv->init) {
ret = uc_drv->init(uc);
if (ret)
goto fail;
}
// 到此新的uclass初始化完成,并返回赋值给*ucp。
*ucp = uc;
return 0;
//当出现错误时,释放相关内存,并返回错误码。
fail:
if (uc_drv->priv_auto_alloc_size) {
free(uc->priv);
uc->priv = NULL;
}
list_del(&uc->sibling_node);
fail_mem:
free(uc);
return ret;
}
/**
* lists_uclass_lookup() - 根据uclass的ID查找对应的uclass_driver
* id: uclass的ID
*/
struct uclass_driver *lists_uclass_lookup(enum uclass_id id)
{
/**
在编译生成的uboot.map中,可以看到被定义的struct uclass_driver list定义在指定的代码段位置:
.u_boot_list_2_uclass_2_gpio
0x000000008786c25c 0x4c drivers/gpio/built-in.o
0x000000008786c25c _u_boot_list_2_uclass_2_gpio
.u_boot_list_2_uclass_2_i2c
0x000000008786c2a8 0x4c drivers/i2c/built-in.o
0x000000008786c2a8 _u_boot_list_2_uclass_2_i2c
ll_entry_start(struct uclass_driver, uclass)返回通过UCLASS_DRIVER定义指向内存中uclass_driver list的首地址。
ll_entry_count(struct uclass_driver, uclass)返回通过UCLASS_DRIVER定义uclass_driver list的个数。
*/
struct uclass_driver *uclass = ll_entry_start(struct uclass_driver, uclass);
const int n_ents = ll_entry_count(struct uclass_driver, uclass);
struct uclass_driver *entry;
// 遍历每个uclass_driver,并且判断是否uclass_id ID匹配,匹配则然后该uclass_driver地址。
for (entry = uclass; entry != uclass + n_ents; entry++) {
if (entry->id == id)
return entry;
}
return NULL;
}
/**
* uclass_bind_device() - 将udevice与uclass进行关联
*
* 将设备连接到uclass的设备列表中。
*/
int uclass_bind_device(struct udevice *dev)
{
struct uclass *uc;
int ret;
// 1. 通过udevice获取对应的uclass
uc = dev->uclass;
// 2. 将udevice添加到uclass uc->dev_head的链接中
list_add_tail(&dev->uclass_node, &uc->dev_head);
// 3. 判断该设备是否存在parent节点
if (dev->parent) {
// 3.1 获取parent节点的uclass_driver
struct uclass_driver *uc_drv = dev->parent->uclass->uc_drv;
// 3.2 判断parent节点是否定义child_post_bind,在这个parent的child绑定之后需要调用。
if (uc_drv->child_post_bind) {
ret = uc_drv->child_post_bind(dev);
}
}
return 0;
}