博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
OK6410A 开发板 (三) 19 u-boot-2021.01 boot 解析 U-boot 镜像运行部分 driver model
阅读量:4285 次
发布时间:2019-05-27

本文共 11157 字,大约阅读时间需要 37 分钟。

  • 总体简述
在 u-boot 中, DM 是 uclass device driver 以及三者相关函数的总体uclass device driver 相关结构体	driver 在定义的时候就根据 其 自身的id成员被 分为了 XXX uclass	device 在定义的时候就根据 其 自身的name成员 暗含了 与 driver 绑定的条件函数	初始化		在(initf_dm/initr_dm)的时候,为每一个设备(设备树中的节点/U_BOOT_DEVICE声明的结构体)做以下动作			1. 初始化了 device 结构体			2. device_bind_common 实现driver 、uclass 与该 device 绑定(即三者绑定)			3. 调用了 driver 中的 bind 函数 为该设备类做初始化		在 例如 serial_init/mmc_initialize的时候			1. 调用 driver 中的 probe 函数 为设备做初始化		使用设备		1. 通过 uclass_get_device_xxx 获取 device 句柄		2. 通过 uclass 提供的函数 yyy 来控制设备

对driver mode 来一次感官认识

  • 文档查阅
doc/driver-model/design.rstu-boot 将 驱动和 设备 做了分离 ,分成了三部分(这里没涉及 uclass 的core 部分) 	Uclass Driver Device假设 我用 cmdline 去访问 ,那么涉及的代码就是 4部分(这里没涉及 uclass 的core 部分) 	cmd		cmd/demo.c	Uclass		drivers/demo/demo_uclass.c		include/dm-demo.h	Driver		demo-shape.c  demo-simple.c(一个文件对应一个驱动)	Device		demo-pdata.c(虽然是一个文件,但是这里有5个设备)
  • 运行感知
make sandbox_defconfigmake./u-boot -d u-boot.dtb=> demo list  // 查看 已经注册的 且 属于 UCLASS_DEMO 类的 设备有多少个 (5个)Demo uclass entries:entry 0 - instance 05292d40, ops 08000001, platdata 08000000entry 1 - instance 05292e00, ops 08000002, platdata 08000000entry 2 - instance 05292ec0, ops 08000001, platdata 08000003entry 3 - instance 05292f80, ops 08000002, platdata 08000004entry 4 - instance 05293040, ops 08000001, platdata 08000004=> demo hello 0r@@@@@@@e@@@@@@@d@@@@@@@r@@@@@@@e@@@@@@@d@@@@@@@=> demo hello 2g       r@      e@@     e@@@    n@@@@   g@@@@@  => demo hello 4  y@@@   e@@@@@ l@@@@@@@l@@@@@@@ o@@@@@   w@@@  // 看起来 设备 0 2 4 用的是同一个驱动 , 实际证明也是如此 , 用的是 demo_shape_drv=> demo hello 1Hello from 05292e00: red 4=> demo hello 3Hello from 05292f80: yellow 6// 看起来 设备 1 3 用的是同一个驱动 , 实际证明也是如此 , 用的是 demo_simple_drv
  • 代码流程分析
// 如果是 demo hello 1cmd(cmd/demo.c)	do_demo		uclass_get_device(UCLASS_DEMO, devnum, &demo_dev);		demo_hello(demo_dev, ch);uclass(drivers/demo/demo_uclass.c)	demo_hello		struct demo_ops *ops = device_get_ops(dev);		ops->hello(dev, ch);Driver(demo-simple.c)	simple_hello		const struct dm_demo_pdata *pdata = dev_get_platdata(dev);		printf("Hello from %08x: %s %d\n", (uint)map_to_sysmem(dev), pdata->colour, pdata->sides);Device(demo-pdata.c)	static const struct dm_demo_pdata red_square = {
.colour = "red", .sides = 4. }; U_BOOT_DEVICE(demo1) = {
.name = "demo_simple_drv", .platdata = &red_square, };
  • 代码功能分析
uclass(drivers/demo/demo_uclass.c)	通过 UCLASS_DRIVER(demo) 注册 UCLASS_DEMO 类Driver(demo-simple.c)	通过 U_BOOT_DRIVER(demo_simple_drv) 注册 驱动, 并绑定 到 UCLASS_DEMODevice(demo-pdata.c)	通过 U_BOOT_DEVICE(demo0) 注册设备,并绑定到 特定的driver消费者(例如cmd)	通过 uclass_get_device(UCLASS_DEMO, devnum, &demo_dev); 获取到设备	通过 设备类 提供的 操作函数  demo_hello(demo_dev, ch); 来控制设备	u-boot 引入 driver model以前	消费者想要操作一个设备,需要知道以下信息		A. 设备的物理地址是什么		B. 设备的驱动是什么,以及提供了什么APIu-boot 引入 driver model之后	消费者想要操作一个设备,不需要知道A和B,只需要知道下面的就行了		1. 该设备属于哪个类		2. 该类的操作函数

以上都是从消费者角度考虑的,但是没考虑 driver model 的核心实现(即 uclass的核心实现)

而我认为 initf_dm 和 initr_dm 就是 核心实现的初始化


driver mode 的初始化

看了一些博客先来个总体感觉1. initf_dm 和 initr_dm 的过程是类似的,下面以initf_dm 为例讲述2. initf_dm 目的在于 初始化一个 树型 数据结构 ,里面存储的是 Device,并初始化 gd->dm_root3. Device 是 从 U_BOOT_DEVICE 和 fdt 中找的4. 在 对 Device 进行 树型 排列的过程中,还要 针对  每一个 Device 做一些 recipe5. recipe 包括 为 Device 找到 Driver , 为 Device 找到 UCLASS6. 因为 Device 是 树型结构,操作一个Device 的时候还可能对 parents 做一些动作
u-boot 运行过程中	board_init_f 		initf_dm			bootstage_start(BOOTSTAGE_ID_ACCUM_DM_F, "dm_f");			ret = dm_init_and_scan(1);								// 主要是负责 root driver和root device 的bind 和 probe				// core/root.c 中的 U_BOOT_DRIVER(root_driver)				// core/root.c 中的 UCLASS_DRIVER(root)				// drivers/core/device.c 中的 device_bind_common 动态创建了 U_BOOT_DEVICE(root)				dm_init					INIT_LIST_HEAD(&DM_UCLASS_ROOT_NON_CONST);					device_bind_by_name(NULL, false, &root_info, &DM_ROOT_NON_CONST);						drv = lists_driver_lookup_name(info->name);						device_bind_common(parent, drv, info->name, (void *)info->platdata, 0, ofnode_null(), platdata_size, devp);							uclass_bind_device							没有 drv->bind					(((gd_t *)gd)->dm_root)->node = offset_to_ofnode(0);					device_probe((((gd_t *)gd)->dm_root))						device_ofdata_to_platdata						没有dev->parent						uclass_pre_probe_device						clk_set_defaults(dev, 0);						没有drv->probe						uclass_post_probe_device										// 主要是负责 U_BOOT_DEVICE 声明的 device 和 对应的 driver  bind/probe				dm_scan_platdata(1)					lists_bind_drivers((((gd_t *)gd)->dm_root), pre_reloc_only);						bind_drivers_pass							const int n_ents = ll_entry_count(struct driver_info, driver_info) = 0;							return 0				// 主要是负责 fdt 声明的 device 和 对应的 driver  bind/probe				// 设备树预处理文件 : output/arch/arm/dts/.s3c64xx-ok6410a.dtb.dts.tmp				dm_extended_scan_fdt(gd->fdt_blob,1);					dm_scan_fdt(blob, pre_reloc_only);						dm_scan_fdt_node(gd->dm_root, blob, 0, pre_reloc_only);							查看节点有没有enable ,没有的话(sdhci@7C300000,sdhci@7C400000)什么都不做,返回.有的话继续							for_each_node							lists_bind_fdt								// 没有 compatible string 的 node 									什么都不做 (chosen,aliases,memory,config,)								// 有 compatible 且没有子节点									对每个节点(interrupt-controller@10490000,clock@1800000,serial0@7F005000,sdhci@7C200000)									driver_check_compatible									device_bind_with_driver_data										device_bind_common											uclass_bind_device											有 drv->bind则执行bind,没 drv->bind则不执行								// 有 compatible 且有 子节点									对每个节点(包括该节点pinctrl@7f008000和所有子节点gpa-gpq)									device_bind_with_driver_data										device_bind_common											uclass_bind_device											有 drv->bind				dm_scan_other(1);			bootstage_accum(BOOTSTAGE_ID_ACCUM_DM_F);	board_init_r		initr_dm			gd->dm_root_f = gd->dm_root;			gd->dm_root = ((void *)0);						bootstage_start(BOOTSTAGE_ID_ACCUM_DM_R, "dm_r");			ret = dm_init_and_scan(0);				dm_init(0)				dm_scan_platdata(0)				dm_extended_scan_fdt(gd->fdt_blob,0);				dm_scan_other(0);			bootstage_accum(BOOTSTAGE_ID_ACCUM_DM_R);		initr_dm_devices			// null

initf_dm initr_dm两者的区别

初始化流程	initf_dm		dm_init_and_scan(true) // pre_reloc_only = 1		// device_bind_by_name		// 		if (pre_reloc_only && !(drv->flags & DM_FLAG_PRE_RELOC)) return; // return表示不做下面的初始化动作		//	也就是说 该设备 需要在 重定位之前初始化 ,则 drv->flags & DM_FLAG_PRE_RELOC != 0		// 这里初始化的设备就是 drv->flags & DM_FLAG_PRE_RELOC != 0 的设备		// 哪些驱动 是 DM_FLAG_PRE_RELOC 		// pinctrl@7f008000以及子节点		// clock@1800000		// serial0@7F005000			initr_dm		dm_init_and_scan(false) // pre_reloc_only = 0消费者(其实这个消费者也是DM架构的一部分,只不过是核心部分的较外层部分)	initf_dm		initf_dm 之后 initr_dm 之前的消费者(serial_init)		调用device_probe次数:2		serial_init // 主要作用是调用drv->probe 完成硬件的初始化			serial_find_console_or_panic				serial_check_stdout					uclass_get_device_by_of_offset						uclass_get_device_tail							device_probe(dev);								if (dev->parent) device_probe(dev->parent);							if (drv->probe) drv->probe(dev);	initr_dm 		initr_dm 之后的消费者		调用device_probe次数:(18+104)

driver mode 的初始化和 Uclass Driver Device

    1. Uclass Driver Device 的展开
uclass UCLASS_DRIVER(gpio)struct uclass_driver _u_boot_list_2_uclass_2_gpio __attribute__((__aligned__(4))) __attribute__((unused, section(".u_boot_list_2_""uclass""_2_""gpio"))) = {
.id = UCLASS_GPIO, .name = "gpio", .flags = (1 << 0), .post_probe = gpio_post_probe, .post_bind = gpio_post_bind, .pre_remove = gpio_pre_remove, .per_device_auto_alloc_size = sizeof(struct gpio_dev_priv),};Driver U_BOOT_DRIVER(s3c64xx_gpio)struct driver _u_boot_list_2_driver_2_s3c64xx_gpio __attribute__((__aligned__(4))) __attribute__((unused, section(".u_boot_list_2_""driver""_2_""s3c64xx_gpio"))) = {
.name = "s3c64xx_gpio", .id = UCLASS_GPIO, .of_match = s3c64xx_gpio_ids, .bind = s3c64xx_gpio_bind, .probe = s3c64xx_gpio_probe, .ops = &gpio_s3c_ops, .flags = (1 << 2),};Device U_BOOT_DEVICE(demo0) 设备树
    1. 初始化中对三个结构体群组的遍历
uclass	遍历:		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;		for (entry = uclass; entry != uclass + n_ents; entry++)	遍历的应用:		lists_uclass_lookup		Driver 	遍历:		struct driver *drv = ll_entry_start(struct driver, driver);		const int n_ents = ll_entry_count(struct driver, driver);		struct driver *entry;		for (entry = drv; entry != drv + n_ents; entry++)	遍历的应用:		lists_driver_lookup_nameDevice 	遍历: for (offset = fdt_first_subnode(blob, offset); offset > 0; offset = fdt_next_subnode(blob, offset))	遍历2:		gpio_bank_t *base = (gpio_bank_t *)devfdt_get_addr(parent);		for (node = fdt_first_subnode(blob, dev_of_offset(parent)), bank = base; node > 0; node = fdt_next_subnode(blob, node), bank ++)	遍历的应用 : 		dm_scan_fdt_node		s3c64xx_gpio_bind
    1. driver mode 的初始化 做了什么,简单描述
Uclass Driver Device 只是三个结构体群组driver mode 的初始化作用是 将三个结构体建立联系 // device_bind _common 阶段实现driver 、uclass、device 三者的对接
uclass_driver 	主要的成员是 uclass_driver 和 dev_head 链表。	有一个全局变量 ./root.c:139:   INIT_LIST_HEAD(&DM_UCLASS_ROOT_NON_CONST);	dev_head 		是一个链表头, 用来链接该类下的所有设备。		可以通过 uclass_foreach_dev(dev, uc) 遍历该class 下的所有设备。	uclass_driver 		是针对某一类设备提供的通用操作接口		通过 UCLASS_DRIVER(_name) 宏申明		uclass 层通过 udevice->driver->ops 获取对应 driver 的操作接口。driver	声明:		通过 U_BOOT_DRIVER(__name) 宏声明。	bind		如果 driver 实现了 bind 接口,则会被调用	probe		driver 一般都有对应的 probe 接口		通过 device_probe(struct udevice *dev) 调用		需要注意的是driver 的 bind 接口调用的比 probe 接口早, 大部分在 dm_init_and_scan 中就被调用了。	ops 		driver 一般会提供 ops 操作接口,供上一层调用。
dm_init_and_scan(bool pre_reloc_only) 	1.根据名称 (U_BOOT_DEVICE 中和 driver 的 name,或者 dts 和 driver 的 compatible) 匹配到对应的 driver	2.调用device_bind_common 函数		2.1.生成 udevice // dev = calloc(1, sizeof(struct udevice));		2.2.绑定 udevice 和 driver // dev->driver = drv;		2.3 绑定 udevice 和 uclass // dev->uclass = uc;		2.3.根据 driver 中的uclass id 找到对应的 uclass driver,并生成相应的 uclass, 并把该设备挂到 uclass 的dev_head之下 // ret = uclass_bind_device(dev);		2.4.调用 driver 的 bind 函数 // if (drv->bind) drv->bind(dev);其他 	// 并不存在实际意义上的设备的驱动 	//例如MMC 子系统中的 mmc_blk 驱动	// 驱动位于抽象层,它不和具体的硬件设备直接交互,并不适合用一个 dts(dts 是用来描述具体的硬件信息的) 节点或者 U_BOOT_DEVICE(_name) 宏来为这个驱动显示的申明设备。	1. 该驱动主要是把所有的 mmc 设备注册到更上一层的 blk 子系统中	2. 向 blk 层提供操作 mmc 设备的 blk_ops,向下通过mmc uclass 提供的统一接口控制 mmc 设备。	3. 调用device_bind_xxx 系列 API 来完成驱动和设备和更上一层 uclass 之间的 bind

driver mode 的消费者(核心外围) 做了什么,简单描述

  • 初始化后对三个结构体群组的遍历
// include/dm/uclass.huclass_foreach_devuclass_get_device_xxx
  • 做了什么工作(probe)
此时三者已经绑定成功// 以串口为例serial_init	serial_find_console_or_panic		serial_check_stdout			uclass_get_device_by_of_offset				uclass_get_device_tail					device_probe(dev);						if (dev->parent) device_probe(dev->parent);						if (drv->probe) drv->probe(dev);

driver 结构体中的 bind 和 probe 的区别

调用时序	bind 	: initf_dm中/initr_dm中 		// post_bind: Called after a new device is bound to this uclass		// device_bind_common 	probe   : initf_dm后/initr_dm后 		// post_probe: Called after a new device is probed		// 模块驱动(mmc/gpio/i2c/serial) lib 中提供的 函数A 封装了 device_probe		// 一般在 (mmc_initialize/serial_init) 的时候调用 该 函数A功能:	bind	: 一般是 xxx-uclass.c 提供的,驱动文件拿来 赋值 给 bind 成员	probe 	: 硬件初始化

driver mode 的消费者(真正消费者) 做了什么,简单描述

1. 通过 uclass_get_device_xxx 获取 device 句柄2. 通过 uclass 提供的函数 yyy 来控制设备

转载地址:http://lbigi.baihongyu.com/

你可能感兴趣的文章
hadoop HDFS原理基础知识
查看>>
数据挖掘十大算法----EM算法(最大期望算法)
查看>>
android StrictMode应用
查看>>
TabHost的两种使用方法
查看>>
Android---TextView属性详解
查看>>
K近邻算法基础:KD树的操作
查看>>
数据挖掘十大算法--K近邻算法
查看>>
android对话框(Dialog)的用法
查看>>
Android使用Application总结
查看>>
android启动第一个界面时即闪屏的核心代码(两种方式)
查看>>
数据挖掘十大经典算法(详解)
查看>>
数据挖掘十大算法--K-均值聚类算法
查看>>
java中常用的日期格式化(全)
查看>>
POI操作Excel导入和导出
查看>>
java的md5加密算法代码
查看>>
jdbc连接数据库
查看>>
Android开发四大组件概述
查看>>
Hadoop主要子项目介绍(Pig Zookeeper Hbase Hive Sqoop Avro Chukwa Cassandra )
查看>>
Android四大组件--Activity详解
查看>>
android四大组件--android service详解
查看>>