超详细Uboot驱动开发(三)Uboot驱动模型 时讯
#头条创作挑战赛#
文章目录
3.1、什么是Uboot驱动模型3.2、为什么要有驱动模型呢3.3、如何使用uboot的DM模型①:menuconfig配置全局DM模型②:指定某个驱动的DM模型3.4、DM模型数据结构① global_data② uclass③ uclass_driver④ uclass_id⑤ udevice⑥ driver3.5、DM驱动模型之上帝视角3.6、DM模型——Udevice与driver绑定① dm_init② lists_bind_fdt3.7、DM模型——probe探测函数的执行3.8、DM模型——uclass与uclass_driver绑定3.9参考文档3.1、什么是Uboot驱动模型
学过Linux的朋友基本都知道Linux的设备驱动模型,Uboot根据Linux的驱动模型架构,也引入了Uboot的驱动模型(driver model :DM)。
(资料图片)
这种驱动模型为驱动的定义和访问接口提供了统一的方法。提高了驱动之间的兼容性以及访问的标准型,uboot驱动模型和kernel中的设备驱动模型类似。
3.2、为什么要有驱动模型呢
提高代码的可重用性:为了能够使代码在不同硬件平台,不同体系架构下运行,必须要最大限度的提高代码的可重用性。高内聚,低耦合:分层的思想也是为了达到这一目标,低耦合体现在对外提供统一的抽象访问接口,高内聚将相关度紧密的集中抽象实现。便于管理:在不断发展过程中,硬件设备越来越多,驱动程序也越来越多,为了更好的管理驱动,也需要一套优秀的驱动架构!3.3、如何使用uboot的DM模型
①:menuconfig配置全局DM模型
Device Drivers -> Generic Driver Options -> Enable Driver Model
通过上面的路径来打开Driver Model模型,最终配置在.config文件中,CONFIG_DM=y
②:指定某个驱动的DM模型
全局的DM模型打开后,我们对于不通的驱动模块,使能或者失能DM功能。如MMC驱动为例:
Device Drivers -> MMC Host controller Support -> Enable MMC controllers using Driver Model
最终反映在.config文件中的CONFIG_DM_MMC=y
在对应的驱动中,可以看到判断#if !CONFIG_IS_ENABLED(DM_MMC),来判断是否打开DM驱动模型。
在管理驱动的Makefile文件中,也能看到obj-$(CONFIG_$(SPL_)DM_MMC) += mmc-uclass.o,来判断是否将驱动模型加入到编译选项中。
总之,我们要打开DM模型,最后反映在几个配置信息上:
CONFIG_DM=y,全局DM模型打开CONFIG_DM_XXX=y,某个驱动的DM模型的打开可以通过Kconifg、Makefile来查看对应宏的编译情况3.4、DM模型数据结构
要想了解DM模型整套驱动框架,我们必须先了解它的一砖一瓦!也就是组成驱动框架的各个数据结构。
① global_data
typedef struct global_data {...#ifdef CONFIG_DMstruct udevice*dm_root;/* Root instance for Driver Model */struct udevice*dm_root_f;/* Pre-relocation root instance */struct list_head uclass_root;/* Head of core tree */#endif...}
global_data,管理着整个Uboot的全局变量,其中dm_root,dm_root_f,uclass_root用来管理整个DM模型。这几个变量代表什么意思呢?
dm_root:DM模型的根设备dm_root_f:重定向前的根设备uclass_root:uclass链表的头这几个变量,最终要的作用就是:管理整个模型中的udevice设备信息和uclass驱动类。
② uclass
我们首先看一下uclass这个结构体
/** * struct uclass - a U-Boot drive class, collecting together similar drivers * * A uclass provides an interface to a particular function, which is * implemented by one or more drivers. Every driver belongs to a uclass even * if it is the only driver in that uclass. An example uclass is GPIO, which * provides the ability to change read inputs, set and clear outputs, etc. * There may be drivers for on-chip SoC GPIO banks, I2C GPIO expanders and * PMIC IO lines, all made available in a unified way through the uclass. * * @priv: Private data for this uclass * @uc_drv: The driver for the uclass itself, not to be confused with a * "struct driver" * @dev_head: List of devices in this uclass (devices are attached to their * uclass when their bind method is called) * @sibling_node: Next uclass in the linked list of uclasses */struct uclass {void *priv;//uclass的私有数据struct uclass_driver *uc_drv;//uclass类的操作函数集合struct list_head dev_head;//该uclass的所有设备struct list_head sibling_node;//下一个uclass的节点};
根据注释,我们就可以了解到,uclass相当于老师,管理着对应某一个类别下的所有的udevice。
总结:uclass,来管理该类型下的所有设备,并且有对应的uclass_driver驱动。
定义uclass是uboot自动生成的,并且不是所有uclass都会生成,有对应uclass_driver并且有被udevice匹配到的uclass才会生成。
存放所有生成的uclass都会被挂载gd->uclass_root链表上。
相关APIint uclass_get(enum uclass_id key, struct uclass **ucp);// 从gd->uclass_root链表获取对应的ucla ss
③ uclass_driver
正如上面,我们看到了uclass类所包含uclass_driver结构体,uclass_driver正如其名,它就是uclass的驱动程序。其主要作用是:为uclass提供统一管理的接口,结构体如下:
/** * struct uclass_driver - Driver for the uclass * * A uclass_driver provides a consistent interface to a set of related * drivers. */struct uclass_driver { const char *name; // 该uclass_driver的命令 enum uclass_id id; // 对应的uclass id/* 以下函数指针主要是调用时机的区别 */ int (*post_bind)(struct udevice *dev); // 在udevice被绑定到该uclass之后调用 int (*pre_unbind)(struct udevice *dev); // 在udevice被解绑出该uclass之前调用 int (*pre_probe)(struct udevice *dev); // 在该uclass的一个udevice进行probe之前调用 int (*post_probe)(struct udevice *dev); // 在该uclass的一个udevice进行probe之后调用 int (*pre_remove)(struct udevice *dev);// 在该uclass的一个udevice进行remove之前调用 int (*child_post_bind)(struct udevice *dev); // 在该uclass的一个udevice的一个子设备被绑定到该udevice之后调用 int (*child_pre_probe)(struct udevice *dev); // 在该uclass的一个udevice的一个子设备进行probe之前调用 int (*init)(struct uclass *class); // 安装该uclass的时候调用 int (*destroy)(struct uclass *class); // 销毁该uclass的时候调用 int priv_auto_alloc_size; // 需要为对应的uclass分配多少私有数据 int per_device_auto_alloc_size; // int per_device_platdata_auto_alloc_size; // int per_child_auto_alloc_size; // int per_child_platdata_auto_alloc_size; // const void *ops; //操作集合 uint32_t flags; // 标识为};
定义uclass_driver主要通过UCLASS_DRIVER来定义,这里就简单说明一下底层代码,耐心看哦!
UCLASS_DRIVER(pinctrl) = {.id = UCLASS_PINCTRL,.post_bind = pinctrl_post_bind,.flags = DM_UC_FLAG_SEQ_ALIAS,.name = "pinctrl",};
/* 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_pinctrl = {.id = UCLASS_PINCTRL,.post_bind = pinctrl_post_bind,.flags = DM_UC_FLAG_SEQ_ALIAS,.name = "pinctrl",}//同时存放在段._u_boot_list_2_uclass_2_pinctrl中,也就是section段的内容
存放由上面结构体可得,其定义之后都被存放在了段._u_boot_list_2_uclass_2_pinctrl中,那么去哪里可以看到呢?
在u-boot.map文件中搜索,._u_boot_list_2_uclass_2_pinctrl,就可以查到程序中定义的所有驱动程序。
这里相信大家会有疑问,为什么是uclass_2呢?我们大概看一下,也会看到uclass_1和uclass_3,这两个代表什么呢?往下看!
相关APIstruct uclass_driver *uclass = ll_entry_start(struct uclass_driver, uclass); // 会根据.u_boot_list_2_uclass_1的段地址来得到uclass_driver table的地址 const int n_ents = ll_entry_count(struct uclass_driver, uclass);// 获得uclass_driver table的长度struct uclass_driver *lists_uclass_lookup(enum uclass_id id)// 从uclass_driver table中获取uclass id为id的uclass_driver。
正如注释描述,上文中提到的uclass_1和uclass_3起到定位作用,用于计算uclass_2的长度!
上述的API,主要用于根据uclass_id来查找到对应的uclass_driver,进而操作对应的uclass下的udevice。
④ uclass_id
我们在uclass_driver中,看到一个uclass_id类型,这种类型与uclass有什么关系呢?
我们知道,uclass代表驱动的一个类别,uclass_driver是uclass的驱动程序,为uclass提供统一操作接口。而对于不同类型的驱动,就需要uclass_id来区分了!
事实上,每一种类型的设备``uclass都有唯一对应的uclass_id,贯穿设备模型,也是udevice与uclass`相关联的关键之处。
enum uclass_id {/* These are used internally by driver model */UCLASS_ROOT = 0,UCLASS_DEMO,UCLASS_TEST,UCLASS_TEST_FDT,UCLASS_TEST_BUS,UCLASS_TEST_PROBE,....../* U-Boot uclasses start here - in alphabetical order */UCLASS_ACPI_PMC,/* (x86) Power-management controller (PMC) */UCLASS_ADC,/* Analog-to-digital converter */UCLASS_AHCI,/* SATA disk controller */UCLASS_AUDIO_CODEC,/* Audio codec with control and data path */UCLASS_AXI,/* AXI bus */UCLASS_BLK,/* Block device */UCLASS_BOARD,/* Device information from hardware */......};
在这里,我们就把他当作一个设备识别的标志即可!
⑤ udevice
/** * struct udevice - An instance of a driver * * This holds information about a device, which is a driver bound to a * particular port or peripheral (essentially a driver instance). * */struct udevice {const struct driver *driver;//device 对应的driverconst char *name;//device 的名称void *platdata;void *parent_platdata;void *uclass_platdata;ofnode node;//设备树节点ulong driver_data;struct udevice *parent;//父设备void *priv;// 私有数据的指针struct uclass *uclass;//驱动所属的uclassvoid *uclass_priv;void *parent_priv;struct list_head uclass_node;struct list_head child_head;struct list_head sibling_node;uint32_t flags;int req_seq;int seq;#ifdef CONFIG_DEVRESstruct list_head devres_head;#endif};
定义**硬编码:**代码中调用U_BOOT_DEVICE宏来定义设备资源,实际为一个设备实例。**设备树:**将设备描述信息写在对应的DTS文件中,然后编译成DTB,最终由uboot解析设备树后动态生成的。传参方式:通过命令行或者接口将设备资源信息传递进来,非常灵活。存放udevice是最基础的一个设备单元,我们把它作为一个独立的个体,上层所有的操作,最终都与该结构体有关。
我们创建一个设备后,为了服从统一的管理,该结构体会被连接到DM模型下,并入到机制中。那么udevice会被连接到哪里呢?
将udevice连接到对应的uclass中,uclass主要用来管理着同一类的驱动除此之外,有父子关系的udevice,还会连接到udevice->child_head链表下,方便调用大概可以理解为下面这样:
相关API#define uclass_foreach_dev(pos, uc) \ list_for_each_entry(pos, &uc->dev_head, uclass_node)#define uclass_foreach_dev_safe(pos, next, uc) \ list_for_each_entry_safe(pos, next, &uc->dev_head, uclass_node)int uclass_get_device(enum uclass_id id, int index, struct udevice **devp); // 通过索引从uclass中获取udeviceint uclass_get_device_by_name(enum uclass_id id, const char *name, // 通过设备名从uclass中获取udevice struct udevice **devp);int uclass_get_device_by_seq(enum uclass_id id, int seq, struct udevice **devp);int uclass_get_device_by_of_offset(enum uclass_id id, int node, struct udevice **devp);int uclass_get_device_by_phandle(enum uclass_id id, struct udevice *parent, const char *name, struct udevice **devp);int uclass_first_device(enum uclass_id id, struct udevice **devp);int uclass_first_device_err(enum uclass_id id, struct udevice **devp);int uclass_next_device(struct udevice **devp);int uclass_resolve_seq(struct udevice *dev);
这些相关的API,主要作用就是根据uclass_id,查找对应的uclass,然后根据索引值或者名称,来查找到对应的udevice
⑥ driver
struct driver {char *name;//驱动名称enum uclass_id id;//驱动所对应的uclass_idconst struct udevice_id *of_match;//匹配函数int (*bind)(struct udevice *dev);//绑定函数int (*probe)(struct udevice *dev);//注册函数int (*remove)(struct udevice *dev);int (*unbind)(struct udevice *dev);int (*ofdata_to_platdata)(struct udevice *dev);int (*child_post_bind)(struct udevice *dev);int (*child_pre_probe)(struct udevice *dev);int (*child_post_remove)(struct udevice *dev);int priv_auto_alloc_size;int platdata_auto_alloc_size;int per_child_auto_alloc_size;int per_child_platdata_auto_alloc_size;const void *ops;/* driver-specific operations */uint32_t flags;#if CONFIG_IS_ENABLED(ACPIGEN)struct acpi_ops *acpi_ops;#endif};
定义driver对象,主要通过U_BOOT_DRIVER来定义
U_BOOT_DRIVER(xxx_pinctrl) = {.name= "xxx_pinctrl",.id= UCLASS_PINCTRL,.of_match= xxx_pinctrl_match,.priv_auto_alloc_size = sizeof(struct xxx_pinctrl),.ops= &xxx_pinctrl_ops,.probe= xxx_v2s_pinctrl_probe,.remove = xxx_v2s_pinctrl_remove,};
/* 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)))
通过上面的定义,最终我们定义的结构体如下:
struct driver _u_boot_list_2_driver_2_xxx_pinctrl = {.name= "xxx_pinctrl",.id= UCLASS_PINCTRL,.of_match= xxxx_pinctrl_match,.priv_auto_alloc_size = sizeof(struct xxx_pinctrl),.ops= &xxxx_pinctrl_ops,.probe= xxxx_pinctrl_probe,.remove = xxxx_pinctrl_remove,}//同时存放在段._u_boot_list_2_driver_2_xxx_pinctrl中
存放由上面结构体可得,其定义之后都被存放在了段._u_boot_list_2_driver_2_xxx中,那么去哪里可以看到呢?
在u-boot.map文件中搜索,._u_boot_list_2_driver,就可以查到程序中定义的所有驱动程序。
最终,所有driver结构体以列表的形式被放在.u_boot_list_2_driver_1和.u_boot_list_2_driver_3的区间中。
相关API/*先获取driver table 表*/struct driver *drv = ll_entry_start(struct driver, driver);// 会根据.u_boot_list_2_driver_1的段地址来得到uclass_driver table的地址 const int n_ents = ll_entry_count(struct driver, driver);// 通过.u_boot_list_2_driver_3的段地址 减去 .u_boot_list_2_driver_1的段地址 获得driver table的长度/*遍历所有的driver*/struct driver *lists_driver_lookup_name(const char *name)// 从driver table中获取名字为name的driver。
正如注释描述,上文中提到的driver_1和driver_3起到定位作用,用于计算driver_2的长度!
上述的API,主要用于根据name来查找到对应的driver驱动程序。
综上,DM模型相关的数据结构介绍完毕,整体设计的架构如下:
正如红线部分,如何实现driver和udevice的绑定、uclass、uclass_driver的绑定呢?
要想真正搞懂这些,我们不得不去深入到DM的初始化流程。
3.5、DM驱动模型之上帝视角
从对象设计的角度来看,Uboot的驱动模型可以分为静态形式和动态形式。
静态模式:对象是离散的,和其他对象分隔开,减小对象复杂度,利于模块化设计。动态模式:运行态表达形式的对象是把所有的静态对象组合成层次视图,有清晰的数据关联视图在静态模式下,驱动模型主要将对象分为udevice和driver,即设备和驱动程序,两个就像火车的两条轨道,永远也不会产生交集,驱动和设备可以想注册多少就注册多少。
我们看一下udevice的描述:
/** * struct udevice - An instance of a driver * * This holds information about a device, which is a driver bound to a * particular port or peripheral (essentially a driver instance). * */
udevice是driver的一个实例,两个不相交的铁轨,终归也是想要发生爱情的。那么如何让其产生交集呢?这就是动态模式需要做的工作了!
**在动态模式下,**引入了uclass和uclass_driver两个数据结构,实现了对udevice和driver的管理。
看一下uclass和uclass_driver两个结构体的说明:
/** * struct uclass - a U-Boot drive class, collecting together similar drivers * *//** * struct uclass_driver - Driver for the uclass * * A uclass_driver provides a consistent interface to a set of related * drivers. * */
uclass:设备组公共属性对象,作为udevice的一个属性,主要用来管理某个驱动类的所有的设备。uclass_driver:设备组公共行为对象,uclass的驱动程序,主要将uclass管理的设备和驱动实现绑定、注册,移除等操作。通过这两个结构体的引入,可以将毫不相关的udevice是driver关联起来!
udevice与driver的绑定:通过驱动的of_match和compatible属性来配对,绑定。
udevice与uclass的绑定:udevice内的driver下的uclass_id,来与uclass对应的uclass_driver的uclass_id进行匹配。
uclass与uclass_driver的绑定:已知udevice内的driver下的uclass_id,创建uclass的同时,通过``uclass_id找到对应的uclass_driver对象,然后将uclass_driver绑定到uclass`上!
整体结构如下:
3.6、DM模型——Udevice与driver绑定
DM的初始化分为两个部分,一个是在relocate重定向之前的初始化:initf_dm,一个是在relocate重定向之后的初始化:initr_dm。
我们对比这两个函数:
static int initf_dm(void){#if defined(CONFIG_DM) && CONFIG_VAL(SYS_MALLOC_F_LEN)int ret;bootstage_start(BOOTSTAGE_ID_ACCUM_DM_F, "dm_f");ret = dm_init_and_scan(true);//这里为truebootstage_accum(BOOTSTAGE_ID_ACCUM_DM_F);if (ret)return ret;#endif#ifdef CONFIG_TIMER_EARLYret = dm_timer_init();if (ret)return ret;#endifreturn 0;}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;gd->dm_root = NULL;#ifdef CONFIG_TIMERgd->timer = NULL;#endifbootstage_start(BOOTSTAGE_ID_ACCUM_DM_R, "dm_r");ret = dm_init_and_scan(false);//这里为falsebootstage_accum(BOOTSTAGE_ID_ACCUM_DM_R);if (ret)return ret;return 0;}
两个均调用了dm_init_and_scan这个接口,这两个的关键区别在于参数的不同。
首先说明一下dts节点中的“u-boot,dm-pre-reloc”属性,当设置了这个属性时,则表示这个设备在relocate之前就需要使用。当dm_init_and_scan的参数为true时,只会对带有“u-boot,dm-pre-reloc”属性的节点进行解析。而当参数为false的时候,则会对所有节点都进行解析。DM初始化的大体步骤如下:
① dm_init
int dm_init(bool of_live){int ret;if (gd->dm_root) {dm_warn("Virtual root driver already exists!\n");return -EINVAL;}INIT_LIST_HEAD(&DM_UCLASS_ROOT_NON_CONST);#if defined(CONFIG_NEEDS_MANUAL_RELOC)fix_drivers();fix_uclass();fix_devices();#endifret = device_bind_by_name(NULL, false, &root_info, &DM_ROOT_NON_CONST);//查找root_driver驱动,并绑定if (ret)return ret;#if CONFIG_IS_ENABLED(OF_CONTROL)# if CONFIG_IS_ENABLED(OF_LIVE)if (of_live)DM_ROOT_NON_CONST->node = np_to_ofnode(gd->of_root);else#endifDM_ROOT_NON_CONST->node = offset_to_ofnode(0);#endifret = device_probe(DM_ROOT_NON_CONST);//probe激活root_driver驱动if (ret)return ret;return 0;}
dm_init这个函数,名字起的容易让人误导,这个函数主要做的就是初始化了根设备root_driver,根据这个跟设备,初始化了global_data中的dm_root、uclass_root。
② lists_bind_fdt
这个函数主要用来查找子设备,并且根据查找到的子设备,进而查找对应驱动进行绑定!即:实现了driver和device的绑定。
int lists_bind_fdt(struct udevice *parent, ofnode node, struct udevice **devp, bool pre_reloc_only){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;bool found = false;const char *name, *compat_list, *compat;int compat_length, i;int result = 0;int ret = 0;if (devp)*devp = NULL;name = ofnode_get_name(node);log_debug("bind node %s\n", name);compat_list = ofnode_get_property(node, "compatible", &compat_length);//得到compatible属性,用于匹配driver驱动if (!compat_list) {if (compat_length == -FDT_ERR_NOTFOUND) {log_debug("Device "%s" has no compatible string\n", name);return 0;}dm_warn("Device tree error at node "%s"\n", name);return compat_length;}/* * Walk through the compatible string list, attempting to match each * compatible string in order such that we match in order of priority * from the first string to the last. */for (i = 0; i < compat_length; i += strlen(compat) + 1) {compat = compat_list + i;log_debug(" - attempt to match compatible string "%s"\n", compat);for (entry = driver; entry != driver + n_ents; entry++) {//循环判断所有驱动是否匹配ret = driver_check_compatible(entry->of_match, &id, compat);if (!ret)break;}if (entry == driver + n_ents)continue;if (pre_reloc_only) {if (!ofnode_pre_reloc(node) && !(entry->flags & DM_FLAG_PRE_RELOC)) {log_debug("Skipping device pre-relocation\n");return 0;}}log_debug(" - found match at "%s": "%s" matches "%s"\n", entry->name, entry->of_match->compatible, id->compatible);ret = device_bind_with_driver_data(parent, entry, name, id->data, node, &dev);//该函数,用于创建udevice对象,并与查找到的driver绑定if (ret == -ENODEV) {log_debug("Driver "%s" refuses to bind\n", entry->name);continue;}if (ret) {dm_warn("Error binding driver "%s": %d\n", entry->name,ret);return ret;} else {found = true;if (devp)*devp = dev;}break;}if (!found && !result && ret != -ENODEV)log_debug("No match for node "%s"\n", name);return result;}
lists_bind_fdt这个函数,主要用来扫描设备树中的各个节点;
根据扫描到的udevice设备信息,通过compatible来匹配compatible相同的driver,匹配成功后,就会创建对应的struct udevice结构体,它会同时指向设备资源和driver,这样设备资源和driver就绑定在一起了。
3.7、DM模型——probe探测函数的执行
上文呢,dm_init只是负责初始化并绑定了udevice和driver,那么probe探测函数的执行,当然是在该驱动初始化的时候喽!
详细代码在这里就不展开来叙述了!
在MMC驱动初始化后,有没有注意到mmc_probe这个函数,该函数就是间接调用了我们驱动编写的probe函数。
执行流程在上面已经很清楚了:根据uclass_id,调用``uclass_get_device_by_seq来得到udevice,进而调用device_probe来找到对应驱动的probe`。
int device_probe(struct udevice *dev){const struct driver *drv;int ret;int seq;if (!dev)return -EINVAL;if (dev->flags & DM_FLAG_ACTIVATED)return 0;drv = dev->driver;//获取driverassert(drv);ret = device_ofdata_to_platdata(dev);if (ret)goto fail;/* Ensure all parents are probed */if (dev->parent) {//父设备proberet = device_probe(dev->parent);if (ret)goto fail;/* * The device might have already been probed during * the call to device_probe() on its parent device * (e.g. PCI bridge devices). Test the flags again * so that we don"t mess up the device. */if (dev->flags & DM_FLAG_ACTIVATED)return 0;}seq = uclass_resolve_seq(dev);if (seq < 0) {ret = seq;goto fail;}dev->seq = seq;dev->flags |= DM_FLAG_ACTIVATED;/* * Process pinctrl for everything except the root device, and * continue regardless of the result of pinctrl. Don"t process pinctrl * settings for pinctrl devices since the device may not yet be * probed. */if (dev->parent && device_get_uclass_id(dev) != UCLASS_PINCTRL)pinctrl_select_state(dev, "default");if (CONFIG_IS_ENABLED(POWER_DOMAIN) && dev->parent && (device_get_uclass_id(dev) != UCLASS_POWER_DOMAIN) && !(drv->flags & DM_FLAG_DEFAULT_PD_CTRL_OFF)) {ret = dev_power_domain_on(dev);if (ret)goto fail;}ret = uclass_pre_probe_device(dev);if (ret)goto fail;if (dev->parent && dev->parent->driver->child_pre_probe) {ret = dev->parent->driver->child_pre_probe(dev);if (ret)goto fail;}/* Only handle devices that have a valid ofnode */if (dev_of_valid(dev)) {/* * Process "assigned-{clocks/clock-parents/clock-rates}" * properties */ret = clk_set_defaults(dev, 0);if (ret)goto fail;}if (drv->probe) {ret = drv->probe(dev);//调用驱动的probeif (ret)goto fail;}ret = uclass_post_probe_device(dev);if (ret)goto fail_uclass;if (dev->parent && device_get_uclass_id(dev) == UCLASS_PINCTRL)pinctrl_select_state(dev, "default");return 0;fail_uclass:if (device_remove(dev, DM_REMOVE_NORMAL)) {dm_warn("%s: Device "%s" failed to remove on error path\n",__func__, dev->name);}fail:dev->flags &= ~DM_FLAG_ACTIVATED;dev->seq = -1;device_free(dev);return ret;}
主要工作归纳如下:
根据udevice获取driver然后判断是否父设备被probe对父设备进行probe调用driver的probe函数3.8、DM模型——uclass与uclass_driver绑定
uclass与uclass_driver绑定,也是在驱动probe之后,确保该驱动存在,设备存在,最后为该驱动绑定uclass与uclass_driver,为上层提供统一接口。
回到上文的驱动流程图,看到mmc_do_preinit这个函数了嘛?里面调用了ret = uclass_get(UCLASS_MMC, &uc);,该函数才是真正的将uclass与uclass_driver绑定。
int uclass_get(enum uclass_id id, struct uclass **ucp){struct uclass *uc;*ucp = NULL;uc = uclass_find(id);if (!uc)return uclass_add(id, ucp);*ucp = uc;return 0;}
uclass_get主要实现了:根据uclass_id查找对应的uclass是否被添加到global_data->uclass_root链表中,如果没有添加到,就调用uclass_add函数,实现uclass与uclass_driver的绑定,并将其添加到global_data->uclass_root链表中。
static int uclass_add(enum uclass_id id, struct uclass **ucp){struct uclass_driver *uc_drv;struct uclass *uc;int ret;*ucp = NULL;uc_drv = lists_uclass_lookup(id);//根据uclass_id查找到对应的driverif (!uc_drv) {debug("Cannot find uclass for id %d: please add the UCLASS_DRIVER() declaration for this UCLASS_... id\n", id);/* * Use a strange error to make this case easier to find. When * a uclass is not available it can prevent driver model from * starting up and this failure is otherwise hard to debug. */return -EPFNOSUPPORT;}uc = calloc(1, sizeof(*uc));if (!uc)return -ENOMEM;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;//uclass与uclass_driver绑定INIT_LIST_HEAD(&uc->sibling_node);INIT_LIST_HEAD(&uc->dev_head);list_add(&uc->sibling_node, &DM_UCLASS_ROOT_NON_CONST);//添加到global_data->uclass_root链表中if (uc_drv->init) {ret = uc_drv->init(uc);if (ret)goto fail;}*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;}
好啦,到这里基本就把Uboot的DM模型全部理清楚啦,耗时一个周,总感觉想要自己去讲明白,真的不是一件容易的事情呢!
如果对你们有帮助,记得点个赞哦!
推荐阅读
地震发生时躲在哪里?地震发生时躲在哪里存活率较高?
关于到现在蚂蚁庄园小鸡问答知识科普:地震发生时躲在什么空间里自救存活率较高这个话题,相信很多小伙伴都是非常有兴趣了解的吧,因为这个 【详细】
卫生许可证查询官网 食品卫生许可证查询系统?
卫生许可证查询官网食品卫生许可证查询系统?卫生许可证查询要怎么查?我们浏览器搜索 全国食品经营许可社会公众查询第一个搜索结果是我们要 【详细】
华硕电脑售后服务点?上海华硕笔记本服务点盘点
上海华硕笔记本修理点华硕电脑售后服务点?华硕在全国各地都有服务点。以下是上海华硕的服务点:1、上海松江区乐都路251号乐都大厦13楼J~K座 【详细】
发展可再生能源是为什么 发展可再生能源有什么意义?
不可再生能源在自然界已经形成了上亿年,短期内无法恢复。随着大规模开发利用,储量越来越少,总有一天会枯竭。那么,什么是不可再生能源, 【详细】
中国面积最大的湖 中国面积最大的湖排行榜前十名
青海湖是中国最大的湖泊,总面积4456平方公里,周长360多公里,著名的太湖因为面积不到它的一半而显得微不足道。接下来,小编将带你了解中 【详细】
相关新闻
- 超详细Uboot驱动开发(三)Uboot驱动模型 时讯
- 安装智能视频行为识别系统,解锁异常行为识别功能
- 今日视点:展望2023:值得关注的十大网络安全趋势
- 热门看点:京东金榜第一、天猫热卖榜第一!开卖一周后,一加Ace 2势头更猛了
- 版号恢复后的游戏行业:大厂重获王牌,小项目倒在天亮前
- 黑客组织ATM吃了谁的蛋糕?_世界今日讯
- CFX借电信一飞冲天,CNTM能否靠雅虎再创佳绩?_天天头条
- 「文13」什么是机器学习?它和深度学习、人工智能有什么关系?
- 环球热议:新冠病毒又变异!代号CH1.1新毒株又将席卷全球?
- 【环球新要闻】贾跃亭及乐视控股已无财产可执行
- 天天快看点丨发现4000光年外外星人?每18分钟向地球发送无线电信号
- 一加率先开火,态度很不温和,Redmi:狠起来我连自己都打
- 比亚迪“超级混动”,DM、DMi、DMp,都代表啥意思?有什么区别?
- 焦点讯息:价值超过400000英镑!亚马逊发布新卖家激励措施,建议收藏
- 廉洁征兵!请您监督 全球快消息
- 天天新资讯:10个监控 Linux 系统性能的工具和命令
- 深圳发改委:登记机构应当运用区块链等相关技术 对数据产权登记信息进行上链保存_环球速读
- 劲爆!iOS 15.4.1 ra1ncloud 已发布,确实能越狱_观察
- iPhone14Pro系列大降价,二手手机如何选?
- 【环球时快讯】Snap AR推出光线追踪技术