【uboot】uboot 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】uboot 2020.04 DM驱动模式 -- 架构分析_第1张图片
在uboot全局变量中保存了gd->uclass_root,它就是uclass的head。我们可以通过成员struct list_head sibling_node遍历整个uclass list,然后找到想要的uclass。它们之间的关系如下:
【uboot】uboot 2020.04 DM驱动模式 -- 架构分析_第2张图片

在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(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;
}

2.2 device_bind_by_name()函数分析

/**
 * 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);
}

2.3 device_probe()函数分析

/**
 * 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;
}

2.4 device_ofdata_to_platdata()函数分析

/**
 * 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;
}

3. DM模型Device Tree节点的设备初始化

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后被调用

下面对涉及到的主要函数进行分析。

3.1 dm_scan_fdt_node函数分析

/**
 * 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;
}

3.2 lists_bind_fdt函数分析

/**
 * 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;
}

3.3 device_bind_common函数分析

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;
}

3.4 uclass_get和uclass_find函数分析

/**
 * 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;
}

3.5 uclass_add函数分析

/**
 * 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;
}

3.6 lists_uclass_lookup函数分析

/**
 * 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;
}

3.7 uclass_bind_device函数分析

/**
 * 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;
}

你可能感兴趣的:(uboot,uboot)