系统启动时,dts 是怎么被加载的

http://blog.csdn.net/wlwl0071986/article/details/C%22+class=%5C%22CopyToClipboard%5C%22+title=%5C%22copy%5C%22+style=%5C%22color:+rgb%28160,+160,+160%29;+text-decoration:++background-image:++background-color:++border:++padding:+0+margin:+0px+10px+0px+0+font-size:+9+background-position:+initial++background-repeat:+initial+
DTS是Device Tree Source的缩写,用来描述设备的硬件细节。在过去的ARM Linux中,arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着大量的垃圾代码,相当多数的代码只是在描述板级细节,而这些板级细节对于内核来讲,不过是垃圾,如板上的platform设备、resource、i2c_board_info、spi_board_info以及各种硬件的platform_data。为了去掉这些垃圾代码,Linux采用DTS这种新的数据结构来描述硬件设备。采用Device
Tree后,许多硬件的细节可以直接透过它传递给Linux,而不再需要在kernel中进行大量的冗余编码。
& & & &有关DTS的语法格式,网上有很多资料,这里我就不再赘述了。本文主要讲Linux是怎样加载DTS设备节点的流程。下面以高通QCT8974平台(Linux内核)为例进行讲解:
(1)使用DTS注册平台总线的过程
& & & &在讲注册过程之前,我们先看看DTS是怎样描述平台总线结构的,以i2c总线为例:
从上面可知,系统平台上挂载了很多总线,如i2c、spi、uart等等,每一个总线分别被描述为一个节点。Linux在启动后,到C入口时,会执行以下操作,加载系统平台上的总线和设备:
start_kernel() --& setup_arch() --& unflatten_device_tree()&
unflatten_device_tree()的代码如下:
&在执行完unflatten_device_tree()后,DTS节点信息被解析出来,保存到allnodes链表中,allnodes会在后面被用到。
随后,当系统启动到board文件时,会调用.init_machine,高通8974平台对应的是msm8974_init()。接着调用of_platform_populate(....)接口,加载平台总线和平台设备。至此,系统平台上的所有已配置的总线和设备将被注册到系统中。注意:不是dtsi文件中所有的节点都会被注册,在注册总线和设备时,会对dts节点的状态作一个判断,如果节点里面的status属性没有被定义,或者status属性被定义了并且值被设为“ok”或者“okay”,其他情况则不被注册到系统中。
(2)使用DTS注册总线设备的过程
& & & &上面讲了Linux怎样使用DTS注册平台总线和平台设备到系统中,那么其他设备,例如i2c、spi设备是怎样注册到系统中的呢?下面我们就以i2c设备为例,讲解Linux怎样注册i2c设备到系统中。
&以高通8974平台为例,在注册i2c总线时,会调用到qup_i2c_probe()接口,该接口用于申请总线资源和添加i2c适配器。在成功添加i2c适配器后,会调用of_i2c_register_devices()接口。此接口会解析i2c总线节点的子节点(挂载在该总线上的i2c设备节点),获取i2c设备的地址、中断号等硬件信息。然后调用request_module()加载设备对应的驱动文件,调用i2c_new_device(),生成i2c设备。此时设备和驱动都已加载,于是drvier里面的probe方法将被调用。后面流程就和之前一样了。
& & & &简而言之,Linux采用DTS描述设备硬件信息后,省去了大量板文件垃圾信息。Linux在开机启动阶段,会解析DTS文件,保存到全局链表allnodes中,在掉用.init_machine时,会跟据allnodes中的信息注册平台总线和设备。值得注意的是,加载流程并不是按找从树根到树叶的方式递归注册,而是只注册根节点下的第一级子节点,第二级及之后的子节点暂不注册。Linux系统下的设备大多都是挂载在平台总线下的,因此在平台总线被注册后,会根据allnodes节点的树结构,去寻找该总线的子节点,所有的子节点将被作为设备注册到该总线上。
本文已收录于以下专栏:
相关文章推荐
安卓(android)6.0高通平台下设备树专题视频讲解【全国独家+设备树视频教程】
1. 标准输入与输出
  我们知道,执行一个shell命令行时通常会自动打开三个标准文件,即标准输入文件(stdin),通常对应终 端的键盘;标准输出文件(stdout)和标准错误输出文件(st...
DTS是Device Tree Source的缩写,用来描述设备的硬件细节。在过去的ARM Linux中,arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着大量的垃圾代码,...
DTS是Device Tree Source的缩写,用来描述设备的硬件细节。在过去的ARM Linux中,arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着大量的垃圾代码,...
分类: Linux Kernel
20:20 1561人阅读 评论(0) 收藏 举报
LinuxkernelDTS设备树driver
一. 启动条件
通常从系统上电执行的boot loader的代码, 而要从boot loader跳转到linux kernel的第一条指令处执行需要一些特定的条件。
From : http://blog.csdn.net/wlwl0071986/article/details/8896718
DTS是Device Tree Source的缩写,用来描述设...
原文地址:Linux设备驱动中得 DTS文件加载过程 作者:taiping6365
From:http://m.blog.csdn.net/blog/liliyaya/9188193...
在\kernel\of\fdt.c 中有如下初始化函数
注释上:展开设备树,创建device_nodes到全局变量allnodes中
void __init unflatten_de...
在arch/arm/mach-msm/board-8909.c中:
DT_MACHINE_START(MSM8909_DT,
"Qualcomm Technologies, Inc. MSM ...
他的最新文章
讲师:李江龙
讲师:司徒正美
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)一. 启动条件
通常从系统上电执行的boot loader的代码, 而要从boot loader跳转到linux kernel的第一条指令处执行需要一些特定的条件。
这里讨论下进入到linux kernel时必须具备的一些条件,这一般是boot loader在跳转到kernel之前要完成的:
1. CPU必须处于SVC(supervisor)模式,并且IRQ和FIQ中断都是禁止的;
2. MMU(内存管理单元)必须是关闭的, 此时虚拟地址就是物理地址;
3. 数据cache(Data cache)必须是关闭的
4. 指令cache(Instruction cache)可以是打开的,也可以是关闭的,这个没有强制要求;
5. CPU 通用寄存器0 (r0)必须是 0;
6. CPU 通用寄存器1 (r1)必须是 ARM Linux machine type (关于machine type, 我们后面会有讲解)
7. CPU 通用寄存器2 (r2) 必须是 kernel parameter list 的物理地址(parameter list 是由boot loader传递给kernel,用来描述设备信息属性的列表)。
二. starting kernel
KERNEL_RAM_ADDR arch/arm/kernel/head.S 0xc0008000 kernel在RAM中的虚拟地址
PAGE_OFFSET include/asm-arm/memeory.h 0xc0000000 内核空间的起始虚拟地址
TEXT_OFFSET arch/arm/Makefile 0x 内核在RAM中起始位置相对于RAM起始地址的偏移
在 arch/arm/kernel/head-common.S 中:
__mmap_switched, %function
00035: __mmap_switched:
adr r3, __switch_data + 4
r3!, {r4, r5, r6, r7}
cmp r4, r5
@ Copy data segment if needed
fp, [r4], #4
fp, [r5], #4
mov fp, #0
@ Clear BSS (and zero fp)
cmp r6, r7
fp, [r6],#4
r3, {r4, r5, r6, sp}
str r9, [r4]
@ Save processor ID
str r1, [r5]
@ Save machine type
bic r4, r0, #CR_A
@ Clear 'A' bit
r6, {r0, r4}
@ Save control register values
start_kernel
asmlinkage void __init start_kernel(void)-&
void __init setup_arch(char **cmdline_p)-&
mdesc = setup_machine_fdt(__atags_pointer);
//通过atags_pointer获取(devtree = phys_to_virt(dt_phys))
void __init unflatten_device_tree(void);
void __init unflatten_device_tree(void)
__unflatten_device_tree(initial_boot_params, &allnodes,
early_init_dt_alloc_memory_arch);
/* Get pointer to "/chosen" and "/aliasas" nodes for use everywhere */
of_alias_scan(early_init_dt_alloc_memory_arch);
这些工作完成解析DTS文件。保存到全局链表allnodes中。
随后,当系统启动到board文件时,会调用.init_machine,高通平台对应的是msm_init()。接着调用of_platform_populate(….)接口,加载平台总线和平台设备。至此,系统平台上的所有已配置的总线和设备将被注册到系统中。注意:不是dtsi文件中所有的节点都会被注册,在注册总线和设备时,会对dts节点的状态作一个判断,如果节点里面的status属性没有被定义,或者status属性被定义了并且值被设为“ok”或者“okay”,其他情况则不被注册到系统中。
void __init msm8610_init(void)
struct of_dev_auxdata *adata = msm8610_auxdata_
if (socinfo_init() & 0)
pr_err("%s: socinfo_init() failed\n", __func__);
msm8610_init_gpiomux();
board_dt_populate(adata);
msm8610_add_drivers();
在注册i2c总线时,会调用到qup_i2c_probe()接口,该接口用于申请总线资源和添加i2c适配器。在成功添加i2c适配器后,会调用of_i2c_register_devices()接口。此接口会解析i2c总线节点的子节点(挂载在该总线上的i2c设备节点),获取i2c设备的地址、中断号等硬件信息。然后调用request_module()加载设备对应的驱动文件,调用i2c_new_device(),生成i2c设备。此时设备和驱动都已加载,于是drvier里面的probe方法将被调用。
本文已收录于以下专栏:
相关文章推荐
一、前提新版基于ARM的Linux都会基于Device Tree去代替之前的device驱动。更加多的了解Device Tree可以访问宝哥的Bolg:ARM Linux 3.x的设备树(Device...
DTS是Device Tree Source的缩写,用来描述设备的硬件细节。在过去的ARM Linux中,arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着大量的垃圾代码,...
DTS是Device Tree Source的缩写,用来描述设备的硬件细节。在过去的ARM Linux中,arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着大量的垃圾代码,...
Linux驱动先注册总线,总线上可以先挂device,也可以先挂driver,那么究竟怎么控制先后的顺序呢。
Linux系统使用两种方式去加载系统中的模块:动态和静态。
静态加载:将所有模块的程序...
在arch/arm/mach-msm/board-8909.c中:
DT_MACHINE_START(MSM8909_DT,
"Qualcomm Technologies, Inc. MSM ...
以MT7620a为例阐述dts如何匹配driver
openwrtmt7620的SDK默认配置常用的外设都没有被使能,默认编译出来的固件在/dev目录下无法查到如I2C-0\SPI0等节点,还以为ke...
准备开始看高通8974平台的代码,在看到i2c, platform驱动zhu
最近在搞驱动,发现被dtsi坑死了,研究了一下打算总结在这里防止以后被坑,当然自己去找是很辛苦的,但是有时候也是有必要的。
之前我们用的dtsi是msm8916-mtp.dtsi,现在新加入了msm...
安卓(android)6.0高通平台下设备树专题视频讲解【全国独家+设备树视频教程】
http://blog.csdn.net/wlwl0071986/article/details/C%22+class=%5C%22CopyToClipboard%5C%22+ti...
他的最新文章
讲师:李江龙
讲师:司徒正美
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)如何为BBB制作cape(或:如何在系统启动时自动加载dtbo) - BeagleBone论坛 -
中国电子技术论坛 -
最好最受欢迎电子论坛!
后使用快捷导航没有帐号?
如何为BBB制作cape(或:如何在系统启动时自动加载dtbo)
14:05:57  
如果你买来BBB是为了搞跟硬件相关的项目,那你八成需要制作一个cape。cape是BBB官方的叫法,其实就是指BBB的软件和硬件外设。通过学习device tree我们了解到BBB是使用capemgr和device tree来控制cape的,通过向$SLOTS传入dtbo文件来加载某个cape,像这样:echo BB-ADC & $SLOTS
但有时候我们不想每次都手动输入这样一条命令来启用某个cape,而是想让它开机自动启动。这时就需要用到本文讲的内容了。
要做到开机自动启动,你只需要增加一个eeprom(官方推荐的型号是CAT24C256),并把它接到特定的引脚上,里面写上符合规定格式的内容就行了。系统在启动时会检查特定引脚上有没有符合规定格式的eeprom,如果有的话,就按照eeprom里面的内容自动加载相应的dtbo文件。
还记得我们 cat $SLOTS 看到了什么吗?
cat /sys/devices/bone_capemgr.*/slots0: 54:PF---1: 55:PF---2: 56:PF---3: 57:PF---4: ff:P-O-L Bone-LT-eMMC-2G,00A0,Texas Instrument,BB-BONE-EMMC-2G5: ff:P-O-L Bone-Black-HDMI,00A0,Texas Instrument,BB-BONELT-HDMI
这里前4项为什么是空的呢?因为它们就是给那些有EEPROM的实体cape预留的位置。不难看出,这样的实体cape最多只能插4个。
二、eeprom的连接
这里的知识比较零散,我将分点阐述:
eeprom的地址必须在0x54到0x57之间,否则系统不会加载。科普一下:不管什么牌子的eeprom芯片,它们的地址都是一样的,高4位是1010,低3位对应着芯片的A2,A1,A0这3个引脚的电平(有的芯片只有A1和A0),所以通过外接电路就能改变eeprom的地址。也就是说eeprom的地址只可能是0x50到0x57这8种,如果A2保持高电平,那么就只有0x54到0x57这4个地址可用了(这就是SRM(BBB官方参考手册)里eeprom电路中把A2接高电平的原因。eeprom必须连接到BBB的I2C2_SCL和I2C2_SDA引脚上(在系统中看到的是i2c-1)。因为I2C2这两个引脚的默认功能就是i2c功能。这也告诉我们,程序中尽量不要永久改变这两个引脚的功能复用,否则就没法加载cape了。BBB最多只支持同时插4个eeprom,它们的地址必须互不相同。如果一次插入多个eeprom,会依次读取之。准确来说,读取顺序就是从0x54到0x57的顺序。为什么如此强调overlay的加载顺序呢?因为一旦前面加载的overlay占用了某些片上资源,其他overlay就不能再用了。比如我要做的LCD使用的引脚跟默认加载的HDMI用的引脚是有重叠的,当系统启动时首先加载了LCD的overlay,那么HDMI就不能再加载了。eeprom上WP引脚是写保护用的,一旦上拉,就不能进行写入了。所以自己做电路的时候可以把它悬空或者拉低。
三、eeprom的读写
下面先介绍如何进行读写,再介绍该写入什么东西。
首先确定eeprom的地址。由A0,A1,A2三个引脚的电平确,按照上面刚刚说的,比如我把A0,A1接地,A2拉高,地址就是0x54。
然后在命令行操作:
cd /sys/bus/i2c/devices/1-0054/&&#到eeprom目录中cat eeprom | hexdump -C&&#读取eeprom内容并以字符形式显示echo -e &\xaa\x55\x33\xeeA1Beaglebone LCD4 Cape\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0000A1BeagleboardToys\x00BB-BONE-LCD4-01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00& & eeprom&&#写入内容cat eeprom | hexdump -C&&#确认是否写入成功
其中\x开头的字符代表按ASCII码值写入。上面就写入了官方LCD4 cape的eeprom内容。如果你想自制LCD cape,使用的LCD面板参数跟官方的一样的话,那么系统使用的驱动程序就是一样的,所以就可以直接用上面的eeprom内容加载BBB自带的dtbo文件了,就不必自己重写dtbo文件了。
写入内容时,要按照SRM里的标准(如下图),最重要的是要把头6个字节\xaa\x55\x33\xeeA1以及后面的版本号00A1和要调用的dtbo文件名BB-BONE-LCD4-01写对,其他的比Number of Pins什么的都无所谓。空余的地方必须用\x00补全,用其他字符会产生错误。
经过上面的方法配置好eeprom后,系统就能在启动时自动加载对应的dtbo文件了。但是这里还是再强调一下dts文件编写的注意事项。
四、编写dts文件的注意事项
文件名必须是 boardname-version.dts 的形式,比如 BB-BONE-LCD4-01-00A0.dts。这里面BB-BONE-LCD4-01就是boardname,00A0就是version号。(其实dts的名字无所谓了。。关键是编译出来的dtbo名字必须是它,为了统一,就都这么规定吧)。version必须是00AX的形式,X从0开始按版本依次增加,而且,想命名00A1,必须有00A0的存在才行!不能跨越版本!dts文件里面会有part-number和version这两项,其内容必须跟文件名相符!part-number就是boardname。编译完的dtbo文件必须放到 /lib/firmware/ 目录中才可以加载。
OK,上面介绍了所有cape通用的内容。具体某个cape需要加载特定的驱动,那就是如何写dts文件和配置驱动的问题了,在此就不做讨论啦。
五、其实不用eeprom也能做——uEnv.txt
其实不用eeprom的话,也可以通过修改uEnv.txt文件来实现自动加载dtbo文件。USB连接好BBB以后在电脑里会出现一个盘符,里面有一个叫做uEnv.txt的文件。通过它可以设置系统启动时加载或禁止加载的dtbo。比如我想在系统启加载BB-ADC。我们就可以打开它,在下面添加一行:
optargs=quiet capemgr.enable_partno=BB-ADC
然后安全弹出这个盘符,重启BBB就行啦。
我们知道BBB启动会自动加载HDMI,而HDMI与LCD公用了部分引脚。如果我们想启动后再插上LCD,然后 echo BB-BONE-LCD4-01 & $SLOTS 来加载LCD cape的话,会提示你File exists,就是因为HDMI已经首先加载了,那些引脚就不能再动了。但我们可以配置uEnv.txt使得启动时不自动加载HDMI,方法是在uEnv.txt中添加一行:
optargs=quiet capemgr.disable_partno=BB-BONELT-HDMI,BB-BONELT-HDMIN
注意,如果想同时实现上面两个,即禁用HDMI并加载ADC的话,不能简简单单把上面两句话写进去,应该合并成一句话:
optargs=quiet capemgr.disable_partno=BB-BONELT-HDMI,BB-BONELT-HDMIN capemgr.enable_partno=BB-ADC
否则会失败。
所以,使用eeprom的意义就在于方便啦。什么也不用配置,买回来插上就能用。如果你爱折腾的话,那么实现方法就多了去啦。
六、其实不用eeprom也能做——systemd
上面说的修改uEnv.txt的方法其实跟用eeprom的效果是差不多的——一个不能用的话另一个也不能用= =!
6月的Angstrom系统(终端输入 uname -a 查看你的BBB里装的是哪个版本的系统)有bug,如果你自己编译了一个dtbo文件,即便放进 /lib/firmware 目录下也不能自动加载(/lib/firmware里原本就有的可以加载是因为它们已经被编译进内核了)。所以除非你自己编译一遍系统,否则不能用这个办法在启动时自动加载自己的cape。刚说了用uEnv.txt跟用eeprom差不多……所以也不行。
9月的Angstrom系统稍微修改了这个bug,但还是不好用——系统启动时会在加载cape那步停留60秒才继续……本来Angstrom是以启动速度快见长的,只需10秒。这一下拖了太多后腿了。(而且9月的Angstrom系统增添了新的bug——LCD电阻触摸屏指针会漂移!想自己解决这个问题当然依旧只能重新编译系统——不是每个人都有耐心编译系统玩的!所以即便它是新系统,我还是果断使用6月的吧,至少触摸屏正常。)
总之,无论哪个版本的Angstrom系统,eeprom和uEnv.txt法都不太好使,下面就介绍一个完全不同的招数:使用systemd。
Step by step教学:
在/etc/systemd/system目录下新建一个文件,命名mystartup.service(名字可以自定),内容如下:
[Unit]Description=My script[Service]ExecStart=/home/root/mystartup.sh[Install]WantedBy=multi-user.target
Description是写给自己看的注释,可以随便写。mystartup.sh是要启动时自动执行的脚本。
注意:不能写成ExecStart=/bin/sh /path/to/script.sh这样的,直接按上面给出的例子写就好了。
然后在 /home/root 目录新建一个mystartup.sh,内容如下:
#!/bin/shecho BB-YOUR-CAPE & /sys/devices/bone_capemgr.8/slots
1、这里必须用sh脚本,不能用bash脚本。
2、sh脚本中不能使用bone_capemgr.*这样的通配符,必须是bone_capemgr.8或者.9(根据你的系统来写)
3、当然,dtbo文件还是必须得放在 /lib/firmware 下才行。
最后执行命令:
systemctl enable myscript.service
就可以了。重启BBB会发现成功加载了dtbo。
如果你执行dmesg | grep capemgr,会发现加载dtbo的时间点跟用eeprom或uEnv.txt不同,所以跟启动以后手动输入 echo 命令的效果类似。
我当然推荐第六种方法。预计在找到更好的办法之前,这就是我将来在Development Kit里采用的办法了。想要使用我的Kit的话建议掌握这个小技巧。
最后这句写给能看懂的人,看不懂就不必深究了:crontab @reboot 在BBB的Angstrom里不好使,不用尝试了。似乎会被系统kill掉。
22:03:55  
盼星星盼月亮终于盼来了楼主~
感谢楼主分享~
好人一生平安~
只有小组成员才能发言,
10个成员聚集在这个小组
创建小组步骤
创建小组创建自己的地盘
个性设置精心打造小组空间
邀请好友邀请好友加入我的小组
小组升级小组积分升级赢得社区推荐
Powered by
供应链服务
版权所有 (C) 深圳华强聚丰电子科技有限公司win7启动不了,因为显示windows 正在加载文件然后自动修复,但无法解决,每次都是这样,求救!怎么办啊?_百度知道
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。
win7启动不了,因为显示windows 正在加载文件然后自动修复,但无法解决,每次都是这样,求救!怎么办啊?
显示windows 正在加载文件然后进入系统修复界面,扫描一番后说问题无法解决,然后点“完成”就关机了,下次再启动还是这样,根本开不了机,怎么回事啊?
我有更好的答案
好,进入安全模式界面,win7系统有个”修复计算机“功能,点击进入,启动“自动修复”,系统会自动查找异常原因并修复,开机时连续按F8键
无法进入安全模式,它自动地进入自动修复,这和安全模式的自动修复也是一样的
那就只有恢复或者重做系统了
采纳率:31%
来自团队:
里面有一个修复计算机你试试这个功能如果不行还原系统或者重装
建议重装一个系统
这个就只有重装了。
其他1条回答
为您推荐:
其他类似问题
win7的相关知识
换一换
回答问题,赢新手礼包一、驅動中的probe函數如何被調用?
&&& 首先,我們知道驅動執行的起始函數是init函數。以I2C驅動爲例。
(cm36283.c)
static int __init cm36283_init(void)
int err = 0;
&span style=&color:#FF0000;&&err = i2c_add_driver(&cm36283_driver);&/span&
module_init(cm36283_init);
#define i2c_add_driver(driver) \
i2c_register_driver(THIS_MODULE, driver)
(i2c-core.c)
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
/* Can't register until after driver model init */
if (unlikely(WARN_ON(!i2c_bus_type.p)))
return -EAGAIN;
/* add the driver to the list of i2c drivers in the driver core */
driver-&driver.owner =
driver-&driver.bus = &i2c_bus_
/* When registration returns, the driver core
* will have called probe() for all matching-but-unbound devices.
&span style=&color:#FF0000;&&res = driver_register(&driver-&driver);&/span&
/* Drivers should switch to dev_pm_ops instead. */
if (driver-&suspend)
pr_warn(&i2c-core: driver [%s] using legacy suspend method\n&,
driver-&driver.name);
if (driver-&resume)
pr_warn(&i2c-core: driver [%s] using legacy resume method\n&,
driver-&driver.name);
pr_debug(&i2c-core: driver [%s] registered\n&, driver-&driver.name);
INIT_LIST_HEAD(&driver-&clients);
/* Walk the adapters that are already present */
i2c_for_each_dev(driver, __process_new_driver);
EXPORT_SYMBOL(i2c_register_driver);
(driver.c)
int driver_register(struct device_driver *drv)
struct device_driver *
BUG_ON(!drv-&bus-&p);
if ((drv-&bus-&probe && drv-&probe) ||
(drv-&bus-&remove && drv-&remove) ||
(drv-&bus-&shutdown && drv-&shutdown))
printk(KERN_WARNING &Driver '%s' needs updating - please use &
&bus_type methods\n&, drv-&name);
other = driver_find(drv-&name, drv-&bus);
if (other) {
printk(KERN_ERR &Error: Driver '%s' is already registered, &
&aborting...\n&, drv-&name);
return -EBUSY;
&span style=&color:#FF0000;&&ret = bus_add_driver(drv);&/span&
ret = driver_add_groups(drv, drv-&groups);
bus_remove_driver(drv);
int bus_add_driver(struct device_driver *drv)
struct bus_type *
struct driver_private *
int error = 0;
bus = bus_get(drv-&bus);
return -EINVAL;
pr_debug(&bus: '%s': add driver %s\n&, bus-&name, drv-&name);
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
error = -ENOMEM;
goto out_put_
klist_init(&priv-&klist_devices, NULL, NULL);
priv-&driver =
priv-&kobj.kset = bus-&p-&drivers_
error = kobject_init_and_add(&priv-&kobj, &driver_ktype, NULL,
&%s&, drv-&name);
if (error)
klist_add_tail(&priv-&knode_bus, &bus-&p-&klist_drivers);
if (drv-&bus-&p-&drivers_autoprobe) {
&span style=&color:#FF6666;&&error = driver_attach(drv);&/span&
if (error)
module_add_driver(drv-&owner, drv);
error = driver_create_file(drv, &driver_attr_uevent);
if (error) {
printk(KERN_ERR &%s: uevent attr (%s) failed\n&,
__func__, drv-&name);
error = driver_add_attrs(bus, drv);
if (error) {
/* How the hell do we get out of this pickle? Give up */
printk(KERN_ERR &%s: driver_add_attrs(%s) failed\n&,
__func__, drv-&name);
if (!drv-&suppress_bind_attrs) {
error = add_bind_files(drv);
if (error) {
/* Ditto */
printk(KERN_ERR &%s: add_bind_files(%s) failed\n&,
__func__, drv-&name);
kobject_uevent(&priv-&kobj, KOBJ_ADD);
out_unregister:
kobject_put(&priv-&kobj);
kfree(drv-&p);
drv-&p = NULL;
out_put_bus:
bus_put(bus);
}&&& driver註冊到bus上時,同時會建立uevent屬性文件及bus-&attr數組中的屬性文件(如果bus-&attr有的話)。
int driver_attach(struct device_driver *drv)
return bus_for_each_dev(drv-&bus, NULL, drv, __driver_attach);
EXPORT_SYMBOL_GPL(driver_attach);
int bus_for_each_dev(struct bus_type *bus, struct device *start,
void *data, int (*fn)(struct device *, void *))
struct klist_
struct device *
int error = 0;
return -EINVAL;
klist_iter_init_node(&bus-&p-&klist_devices, &i,
(start ? &start-&p-&knode_bus : NULL));
while ((dev = next_device(&i)) && !error)
error = &span style=&color:#FF6666;&&fn(dev, data);&/span&
klist_iter_exit(&i);
EXPORT_SYMBOL_GPL(bus_for_each_dev);
static int __driver_attach(struct device *dev, void *data)
struct device_driver *drv =
* Lock device and try to bind to it. We drop the error
* here and always return 0, because we need to keep trying
* to bind to devices and some drivers will return an error
* simply if it didn't support the device.
* driver_probe_device() will spit a warning if there
* is an error.
&strong&if (!driver_match_device(drv, dev))&/strong&
if (dev-&parent) /* Needed for USB */
device_lock(dev-&parent);
device_lock(dev);
if (!dev-&driver)
&strong&&span style=&color:#FF6666;&&driver_probe_device(drv, dev);&/span&&/strong&
device_unlock(dev);
if (dev-&parent)
device_unlock(dev-&parent);
&& && driver_match_device调用bus上的match函数进行匹配。具体可以参照
&&& 匹配成功后,会调用driver的probe函数。
int driver_probe_device(struct device_driver *drv, struct device *dev)
int ret = 0;
if (!device_is_registered(dev))
return -ENODEV;
pr_debug(&bus: '%s': %s: matched device %s with driver %s\n&,
drv-&bus-&name, __func__, dev_name(dev), drv-&name);
pm_runtime_get_noresume(dev);
pm_runtime_barrier(dev);
&span style=&color:#FF6666;&&ret = really_probe(dev, drv);&/span&
pm_runtime_put_sync(dev);
}static int really_probe(struct device *dev, struct device_driver *drv)
int ret = 0;
atomic_inc(&probe_count);
pr_debug(&bus: '%s': %s: probing driver %s with device %s\n&,
drv-&bus-&name, __func__, drv-&name, dev_name(dev));
WARN_ON(!list_empty(&dev-&devres_head));
dev-&driver =
/* If using pinctrl, bind pins now before probing */
ret = pinctrl_bind_pins(dev);
goto probe_
if (driver_sysfs_add(dev)) {
printk(KERN_ERR &%s: driver_sysfs_add(%s) failed\n&,
__func__, dev_name(dev));
goto probe_
&span style=&background-color: rgb(204, 204, 255);&&if (dev-&bus-&probe) {
ret = dev-&bus-&probe(dev);
goto probe_
} else if (drv-&probe) {
&span style=&color:#FF6666;&&ret = drv-&probe(dev);&/span&
goto probe_
driver_bound(dev);
pr_debug(&bus: '%s': %s: bound device %s to driver %s\n&,
drv-&bus-&name, __func__, dev_name(dev), drv-&name);
probe_failed:
devres_release_all(dev);
driver_sysfs_remove(dev);
dev-&driver = NULL;
if (ret == -EPROBE_DEFER) {
/* Driver requested deferred probing */
dev_info(dev, &Driver %s requests probe deferral\n&, drv-&name);
driver_deferred_probe_add(dev);
} else if (ret != -ENODEV && ret != -ENXIO) {
/* driver matched but the probe failed */
printk(KERN_WARNING
&%s: probe of %s failed with error %d\n&,
drv-&name, dev_name(dev), ret);
pr_debug(&%s: probe of %s rejects match %d\n&,
drv-&name, dev_name(dev), ret);
* Ignore errors returned by -&probe so that the next driver can try
* its luck.
atomic_dec(&probe_count);
wake_up(&probe_waitqueue);
}&&&& 如果bus-&probe爲空,就執行drv-&probe
二、驅動中的probe函數參數如何產生的?
根據函數參數傳遞可以跟蹤到,是從bus_for_each_dev中獲得參數的。
int driver_attach(struct device_driver *drv)
    return bus_for_each_dev(&span style=&color:#FF0000;&&&strong&drv-&bus, NULL, drv, __driver_attach&/strong&&/span&);
EXPORT_SYMBOL_GPL(driver_attach);
int bus_for_each_dev(struct bus_type *bus, struct device *start,
void *data, int (*fn)(struct device *, void *))
struct klist_
struct device *
int error = 0;
return -EINVAL;
klist_iter_init_node(&bus-&p-&klist_devices, &i,
(start ? &start-&p-&knode_bus : NULL));
while ((&strong&&span style=&color:#FF0000;&&dev = next_device(&i)&/span&&/strong&) && !error)
error = fn(dev, data);
klist_iter_exit(&i);
&&& 這樣可以看到klist_iter_init_node實際調用klist_iter_init_node(&bus-&p-&klist_devices, &i,NULL),所以,跟蹤klist_iter_init_node函數,發現並沒做什麼相關重要的東西。
void klist_iter_init_node(struct klist *k, struct klist_iter *i,
struct klist_node *n)
i-&i_klist =
i-&i_cur =
kref_get(&n-&n_ref);
EXPORT_SYMBOL_GPL(klist_iter_init_node);
static struct device *next_device(struct klist_iter *i)
struct klist_node *n = klist_next(i);
struct device *dev = NULL;
struct device_private *dev_
dev_prv =&span style=&color:#FF0000;&& to_device_private_bus(n);&/span&
&span style=&color:#FF0000;&&dev = dev_prv-&&/span&
}&&& 可以看出,只要得到dev_prv就可以得到dev。下面的問題就是dev_prv中的dev如何被添加進去的呢?
#define to_device_private_bus(obj) \
container_of(obj, struct device_private, knode_bus)
這裏引用其他一些文章解釋:
先上几个struct:
struct klist_iter {
&&&&&&&& struct& klist&&&&&&&&&&&&&&&& *i_
&&&&&&&& struct& klist_node&&&&& *i_
struct klist {
&&&&&&&& spinlock_t&&&&&&&&&&&&&&&&& k_
&&&&&&&& struct& list_head&&&&&&& k_
&&&&&&&& void&&&&&&&&&&&&&&&&&&& (*get)(struct klist_node *);
&&&&&&&& void&&&&&&&&&&&&&&&&&&& (*put)(struct klist_node *);
} __attribute__ ((aligned (sizeof(void*))));
struct klist_node {
&&&&&&&& void&&&&&&&&&&&&&&&&&&& *n_&& /* never access directly */
&&&&&&&& struct&&& list_head&&&&&&& n_
&&&&&&&& struct&&& kref&&&&&&&&&&&&&&&&& n_
struct kref {
&&&&&&&& atomic_t&&&
& & & & &其中的klist_iter_init_node(&bus-&p-&klist_devices, &i,(start ?&start-&p-&knode_bus :NULL))作用是定义个klist_iter指向此klist,以便以后直接使用,如图:
static struct device *next_device(struct klist_iter *i)
struct klist_node *n =&span style=&color:#FF0000;&& &strong&klist_next(i);&/strong&&/span&
struct device *dev = NULL;
struct device_private *dev_
dev_prv = to_device_private_bus(n);
dev = dev_prv-&
(klist.c)
struct klist_node *klist_next(struct klist_iter *i)
void (*put)(struct klist_node *) = i-&i_klist-&
struct klist_node *last = i-&i_
&span style=&color:#FFCCCC;&& &span style=&color:#009900;&&// NULL&/span&&/span&
struct klist_node *
spin_lock(&i-&i_klist-&k_lock);
if (last) {
next = to_klist_node(last-&n_node.next);
if (!klist_dec_and_del(last))
put = NULL;
&span style=&color:#FF0000;&&next = to_klist_node(i-&i_klist-&k_list.next);&/span&
i-&i_cur = NULL;
while (next != to_klist_node(&i-&i_klist-&k_list)) {
if (likely(!knode_dead(next))) {
kref_get(&next-&n_ref);
i-&i_cur =
next = to_klist_node(next-&n_node.next);
spin_unlock(&i-&i_klist-&k_lock);
if (put && last)
put(last);
return i-&i_
EXPORT_SYMBOL_GPL(klist_next);
這裏可以知道i-&i_cur=NULL,i-&i_klist=&bus-&p-&klist_devices。to_klist_node從klist中取得下一個node賦值給next,然後執行一次while循環裏的內容,將next賦值給i-&i_cur。這樣就得到了klist_node.& to_device_private_bus通過container_of 得到struct device_private。下圖說明其中的關係。
&&& 由此,可以知道DTS解析後的設備都是通過klist和klist_node掛載到bus上的。
三、 DTS的解析掛載
dts文件編譯時通過dtc被編譯成了dtb文件。然後在內核啓動時該文件被解析。DTS节点信息保存到allnodes鏈表中。
start_kernel() --& setup_arch() --& unflatten_device_tree()
随后,当系统启动到board文件时,会调用.init_machine,高通8974平台对应的是msm8974_init()。接着调用of_platform_populate(....)接口,加载平台总线和平台设备。
(kernel/arch/arm/mach-msm/board-8226.c)
void __init msm8226_init(void)
&span style=&color:#FF0000;&&board_dt_populate(adata);&/span&
msm8226_add_drivers();
(kernel/arch/arm/mach-msm/board-dt.c)
void __init board_dt_populate(struct of_dev_auxdata *adata)
&span style=&color:#CC0000;&&of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);&/span&
/* Explicitly parent the /soc devices to the root node to preserve
* the kernel ABI (sysfs structure, etc) until userspace is updated
&span style=&color:#FF0000;&&of_platform_populate(of_find_node_by_path(&/soc&),
of_default_bus_match_table, adata, NULL);&/span&
&&&&&& 上面兩句話,一個是以“/”爲根進行遍歷;一個是遍歷allnodes鏈找到“/soc”,並以這一節點爲根節點進行遍歷。
(kernel/driver/of/platform.c)
int of_platform_populate(struct device_node *root,
const struct of_device_id *matches,
const struct of_dev_auxdata *lookup,
struct device *parent)
struct device_node *
int rc = 0;
root = root ? of_node_get(root) : of_find_node_by_path(&/&);
if (!root)
return -EINVAL;
for_each_child_of_node(root, child) {
rc = of_platform_bus_create(child, matches, lookup, parent, true);
of_node_put(root);
#endif /* CONFIG_OF_ADDRESS */
&&&&& of_platform_populate遍歷根節點下的所有節點,分配建立platform device。for_each_child_of_node實現遍歷父節點下的所有兄弟節點及孩子節點。&&&&
#define for_each_child_of_node(parent, child) \
for (child = of_get_next_child(parent, NULL); child != NULL; \
child = of_get_next_child(parent, child))struct device_node *of_get_next_child(const struct device_node *node,
struct device_node *prev)
struct device_node *
read_lock(&devtree_lock);
next = prev ? prev-&sibling : node-&
for (; next = next-&sibling)
if (of_node_get(next))
of_node_put(prev);
read_unlock(&devtree_lock);
EXPORT_SYMBOL(of_get_next_child);
(kernel/driver/of/platform.c)
static int of_platform_bus_create(struct device_node *bus,
const struct of_device_id *matches,
const struct of_dev_auxdata *lookup,
struct device *parent, bool strict)
const struct of_dev_auxdata *
struct device_node *
struct platform_device *
const char *bus_id = NULL;
void *platform_data = NULL;
int rc = 0;
/* Make sure it has a compatible property */
if (strict && (!of_get_property(bus, &compatible&, NULL))) {
pr_debug(&%s() - skipping %s, no compatible prop\n&,
__func__, bus-&full_name);
auxdata = of_dev_lookup(lookup, bus);
if (auxdata) {
bus_id = auxdata-&
platform_data = auxdata-&platform_
if (of_device_is_compatible(bus, &arm,primecell&)) {
of_amba_device_create(bus, bus_id, platform_data, parent);
&span style=&color:#FF0000;&&dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);&/span&
if (!dev || !of_match_node(matches, bus))
for_each_child_of_node(bus, child) {
pr_debug(&
create child: %s\n&, child-&full_name);
rc = of_platform_bus_create(child, matches, lookup, &dev-&dev, strict);
of_node_put(child);
struct platform_device *of_platform_device_create_pdata(
struct device_node *np,
const char *bus_id,
void *platform_data,
struct device *parent)
struct platform_device *
if (!of_device_is_available(np))
return NULL;
&strong&&span style=&color:#FF0000;&&dev = of_device_alloc(np, bus_id, parent);&/span&&/strong&
return NULL;
#if defined(CONFIG_MICROBLAZE)
dev-&archdata.dma_mask = 0xffffffffUL;
dev-&dev.coherent_dma_mask = DMA_BIT_MASK(sizeof(dma_addr_t) * 8);
dev-&dev.bus = &platform_bus_
dev-&dev.platform_data = platform_
/* We do not fill the DMA ops for platform devices by default.
* This is currently the responsibility of the platform code
* to do such, possibly using a device notifier
if (of_device_add(dev) != 0) {
platform_device_put(dev);
return NULL;
&&&&& of_device_alloc根據allnodes的device node分配platform device空間。然後設置platform device的bus總線。
struct platform_device *of_device_alloc(struct device_node *np,
const char *bus_id,
struct device *parent)
struct platform_device *
int rc, i, num_reg = 0, num_
struct resource *res, temp_
dev = platform_device_alloc(&&, -1);
return NULL;
/* count the io and irq resources */
if (of_can_translate_address(np))
while (of_address_to_resource(np, num_reg, &temp_res) == 0)
num_reg++;
num_irq = of_irq_count(np);
/* Populate the resource table */
if (num_irq || num_reg) {
res = kzalloc(sizeof(*res) * (num_irq + num_reg), GFP_KERNEL);
if (!res) {
platform_device_put(dev);
return NULL;
dev-&num_resources = num_reg + num_
dev-&resource =
for (i = 0; i & num_ i++, res++) {
rc = of_address_to_resource(np, i, res);
WARN_ON(rc);
WARN_ON(of_irq_to_resource_table(np, res, num_irq) != num_irq);
&span style=&color:#FF0000;&& dev-&dev.of_node = of_node_get(np);&/span&
#if defined(CONFIG_MICROBLAZE)
dev-&dev.dma_mask = &dev-&archdata.dma_
dev-&dev.parent =
if (bus_id)
dev_set_name(&dev-&dev, &%s&, bus_id);
of_device_make_bus_id(&dev-&dev);
EXPORT_SYMBOL(of_device_alloc);&&&&&&& 分配platform device空間後,注意將device node賦值給dev-&dev.of_node,這也就是爲什麼經常在probe函數中見到通過pdev-&dev.of_node得到dts 設備節點,並從該節點中讀取相關信息。
I2C platform device驅動加載在i2c-qup.c文件中。下面是i2c-qup platform device驅動加載及i2c-client的驅動加載。
(i2c-qup.c)
static int __devinit
qup_i2c_probe(struct platform_device *pdev)
struct qup_i2c_dev *
struct resource
*qup_mem, *gsbi_mem, *qup_io, *gsbi_io, *
struct resource
*in_irq, *out_irq, *err_
struct clk
dt_gpios[I2C_GPIOS_DT_CNT];
bool use_device_tree = pdev-&dev.of_
struct msm_i2c_platform_data *
gsbi_mem = NULL;
dev_dbg(&pdev-&dev, &qup_i2c_probe\n&);
if (use_device_tree) {
pdata = devm_kzalloc(&pdev-&dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
ret = msm_i2c_rsrcs_dt_to_pdata_map(pdev, pdata, dt_gpios);
goto get_res_
pdata = pdev-&dev.platform_
if (!pdata) {
dev_err(&pdev-&dev, &platform data not initialized\n&);
return -ENOSYS;
qup_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
&qup_phys_addr&);
if (!qup_mem) {
dev_err(&pdev-&dev,
&platform_get_resource_byname(qup_phys_addr) failed\n&);
ret = -ENODEV;
goto get_res_
* We only have 1 interrupt for new hardware targets and in_irq,
* out_irq will be NULL for those platforms
in_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
&qup_in_intr&);
out_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
&qup_out_intr&);
err_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
&qup_err_intr&);
if (!err_irq) {
dev_err(&pdev-&dev, &no error irq resource?\n&);
ret = -ENODEV;
goto get_res_
qup_io = request_mem_region(qup_mem-&start, resource_size(qup_mem),
pdev-&name);
if (!qup_io) {
dev_err(&pdev-&dev, &QUP region already claimed\n&);
ret = -EBUSY;
goto get_res_
if (!pdata-&use_gsbi_shared_mode) {
gsbi_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
&gsbi_qup_i2c_addr&);
if (!gsbi_mem) {
dev_dbg(&pdev-&dev, &Assume BLSP\n&);
* BLSP core does not need protocol programming so this
* resource is not expected
goto blsp_core_
gsbi_io = request_mem_region(gsbi_mem-&start,
resource_size(gsbi_mem),
pdev-&name);
if (!gsbi_io) {
dev_err(&pdev-&dev, &GSBI region already claimed\n&);
ret = -EBUSY;
goto err_res_
blsp_core_init:
clk = clk_get(&pdev-&dev, &core_clk&);
if (IS_ERR(clk)) {
dev_err(&pdev-&dev, &Could not get core_clk\n&);
ret = PTR_ERR(clk);
goto err_clk_get_
pclk = clk_get(&pdev-&dev, &iface_clk&);
if (IS_ERR(pclk)) {
dev_err(&pdev-&dev, &Could not get iface_clk\n&);
ret = PTR_ERR(pclk);
clk_put(clk);
goto err_clk_get_
/* We support frequencies upto FAST Mode(400KHz) */
if (pdata-&clk_freq &= 0 ||
pdata-&clk_freq & 400000) {
dev_err(&pdev-&dev, &clock frequency not supported\n&);
ret = -EIO;
goto err_config_
dev = kzalloc(sizeof(struct qup_i2c_dev), GFP_KERNEL);
if (!dev) {
ret = -ENOMEM;
goto err_alloc_dev_
dev-&dev = &pdev-&
if (in_irq)
dev-&in_irq = in_irq-&
if (out_irq)
dev-&out_irq = out_irq-&
dev-&err_irq = err_irq-&
if (in_irq && out_irq)
dev-&num_irqs = 3;
dev-&num_irqs = 1;
dev-&clk =
dev-&pclk =
dev-&base = ioremap(qup_mem-&start, resource_size(qup_mem));
if (!dev-&base) {
ret = -ENOMEM;
goto err_ioremap_
/* Configure GSBI block to use I2C functionality */
if (gsbi_mem) {
dev-&gsbi = ioremap(gsbi_mem-&start, resource_size(gsbi_mem));
if (!dev-&gsbi) {
ret = -ENOMEM;
goto err_gsbi_
for (i = 0; i & ARRAY_SIZE(i2c_rsrcs); ++i) {
if (use_device_tree && i & I2C_GPIOS_DT_CNT) {
dev-&i2c_gpios[i] = dt_gpios[i];
res = platform_get_resource_byname(pdev, IORESOURCE_IO,
i2c_rsrcs[i]);
dev-&i2c_gpios[i] = res ? res-&start : -1;
platform_set_drvdata(pdev, dev);
dev-&one_bit_t = (USEC_PER_SEC/pdata-&clk_freq) + 1;
dev-&pdata =
dev-&clk_ctl = 0;
dev-&pos = 0;
ret = i2c_qup_clk_path_init(pdev, dev);
if (ret) {
dev_err(&pdev-&dev,
&Failed to init clock path-voting data structs. err:%d&, ret);
/* disable i2c_qup_clk_path_xxx() functionality */
dev-&pdata-&master_id = 0;
if (dev-&pdata-&src_clk_rate &= 0) {
dev_info(&pdev-&dev,
&No src_clk_rate specified in platfrom data\n&);
dev_info(&pdev-&dev, &Using default clock rate %dHz\n&,
DEFAULT_CLK_RATE);
dev-&pdata-&src_clk_rate = DEFAULT_CLK_RATE;
ret = clk_set_rate(dev-&clk, dev-&pdata-&src_clk_rate);
dev_info(&pdev-&dev, &clk_set_rate(core_clk, %dHz):%d\n&,
dev-&pdata-&src_clk_rate, ret);
clk_prepare_enable(dev-&clk);
clk_prepare_enable(dev-&pclk);
* If bootloaders leave a pending interrupt on certain GSBI's,
* then we reset the core before registering for interrupts.
writel_relaxed(1, dev-&base + QUP_SW_RESET);
if (qup_i2c_poll_state(dev, 0, true) != 0)
goto err_reset_
clk_disable_unprepare(dev-&clk);
clk_disable_unprepare(dev-&pclk);
* We use num_irqs to also indicate if we got 3 interrupts or just 1.
* If we have just 1, we use err_irq as the general purpose irq
* and handle the changes in ISR accordingly
* Per Hardware guidelines, if we have 3 interrupts, they are always
* edge triggering, and if we have 1, it's always level-triggering
if (dev-&num_irqs == 3) {
ret = request_irq(dev-&in_irq, qup_i2c_interrupt,
IRQF_TRIGGER_RISING, &qup_in_intr&, dev);
if (ret) {
dev_err(&pdev-&dev, &request_in_irq failed\n&);
goto err_request_irq_
* We assume out_irq exists if in_irq does since platform
* configuration either has 3 interrupts assigned to QUP or 1
ret = request_irq(dev-&out_irq, qup_i2c_interrupt,
IRQF_TRIGGER_RISING, &qup_out_intr&, dev);
if (ret) {
dev_err(&pdev-&dev, &request_out_irq failed\n&);
free_irq(dev-&in_irq, dev);
goto err_request_irq_
ret = request_irq(dev-&err_irq, qup_i2c_interrupt,
IRQF_TRIGGER_RISING, &qup_err_intr&, dev);
if (ret) {
dev_err(&pdev-&dev, &request_err_irq failed\n&);
free_irq(dev-&out_irq, dev);
free_irq(dev-&in_irq, dev);
goto err_request_irq_
ret = request_irq(dev-&err_irq, qup_i2c_interrupt,
IRQF_TRIGGER_HIGH, &qup_err_intr&, dev);
if (ret) {
dev_err(&pdev-&dev, &request_err_irq failed\n&);
goto err_request_irq_
disable_irq(dev-&err_irq);
if (dev-&num_irqs == 3) {
disable_irq(dev-&in_irq);
disable_irq(dev-&out_irq);
i2c_set_adapdata(&dev-&adapter, dev);
dev-&adapter.algo = &qup_i2c_
strlcpy(dev-&adapter.name,
&QUP I2C adapter&,
sizeof(dev-&adapter.name));
dev-&adapter.nr = pdev-&
dev-&adapter.dev.parent = &pdev-&
if (pdata-&msm_i2c_config_gpio)
pdata-&msm_i2c_config_gpio(dev-&adapter.nr, 1);
mutex_init(&dev-&mlock);
dev-&pwr_state = MSM_I2C_PM_SUSPENDED;
/* If the same AHB clock is used on Modem side
* switch it on here itself and don't switch it
* on and off during suspend and resume.
if (dev-&pdata-&keep_ahb_clk_on)
clk_prepare_enable(dev-&pclk);
ret = i2c_add_numbered_adapter(&dev-&adapter);
if (ret) {
dev_err(&pdev-&dev, &i2c_add_adapter failed\n&);
if (dev-&num_irqs == 3) {
free_irq(dev-&out_irq, dev);
free_irq(dev-&in_irq, dev);
free_irq(dev-&err_irq, dev);
if (dev-&dev-&of_node) {
dev-&adapter.dev.of_node = pdev-&dev.of_
&strong&&span style=&color:#CC0000;&&
of_i2c_register_devices(&dev-&adapter);&/span&&/strong&
pm_runtime_set_autosuspend_delay(&pdev-&dev, MSEC_PER_SEC);
pm_runtime_use_autosuspend(&pdev-&dev);
pm_runtime_enable(&pdev-&dev);
err_request_irq_failed:
if (dev-&gsbi)
iounmap(dev-&gsbi);
err_reset_failed:
clk_disable_unprepare(dev-&clk);
clk_disable_unprepare(dev-&pclk);
i2c_qup_clk_path_teardown(dev);
err_gsbi_failed:
iounmap(dev-&base);
err_ioremap_failed:
kfree(dev);
err_alloc_dev_failed:
err_config_failed:
clk_put(clk);
clk_put(pclk);
err_clk_get_failed:
if (gsbi_mem)
release_mem_region(gsbi_mem-&start, resource_size(gsbi_mem));
err_res_failed:
release_mem_region(qup_mem-&start, resource_size(qup_mem));
get_res_failed:
if (pdev-&dev.of_node)
kfree(pdata);
}i2c、spi等都是platform bus,在msm8974_init()時调用of_platform_populate(....)接口註冊到系統中。這樣在i2c bus驅動中就可以調用of_i2c_register_devices,將i2c設備註冊到i2c bus上。
(of_i2c.c)
void of_i2c_register_devices(struct i2c_adapter *adap)
struct device_node *
/* Only register child devices if the adapter has a node pointer set */
if (!adap-&dev.of_node)
dev_dbg(&adap-&dev, &of_i2c: walking child nodes\n&);
for_each_child_of_node(adap-&dev.of_node, node) {
struct i2c_board_info info = {};
struct dev_archdata dev_ad = {};
const __be32 *
dev_dbg(&adap-&dev, &of_i2c: register %s\n&, node-&full_name);
if (of_modalias_node(node, info.type, sizeof(info.type)) & 0) {
dev_err(&adap-&dev, &of_i2c: modalias failure on %s\n&,
node-&full_name);
addr = of_get_property(node, &reg&, &len);
if (!addr || (len & sizeof(int))) {
dev_err(&adap-&dev, &of_i2c: invalid reg on %s\n&,
node-&full_name);
info.addr = be32_to_cpup(addr);
if (info.addr & (1 && 10) - 1) {
dev_err(&adap-&dev, &of_i2c: invalid addr=%x on %s\n&,
info.addr, node-&full_name);
info.irq = irq_of_parse_and_map(node, 0);
info.of_node = of_node_get(node);
info.archdata = &dev_
request_module(&%s%s&, I2C_MODULE_PREFIX, info.type);
&span style=&color:#FF0000;&&result = i2c_new_device(adap, &info);&/span&
if (result == NULL) {
dev_err(&adap-&dev, &of_i2c: Failure registering %s\n&,
node-&full_name);
of_node_put(node);
irq_dispose_mapping(info.irq);
EXPORT_SYMBOL(of_i2c_register_devices);
of_i2c_register_devices從解析後的allnodes鏈表信息取出節點,解析具體信息後,放到stuct i2c_board_info中,用於建立i2c-client。
(I2c-core.c)
struct i2c_client *
i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
struct i2c_client *
client = kzalloc(sizeof *client, GFP_KERNEL);
if (!client)
return NULL;
client-&adapter =
client-&dev.platform_data = info-&platform_
if (info-&archdata)
client-&dev.archdata = *info-&
client-&flags = info-&
client-&addr = info-&
client-&irq = info-&
strlcpy(client-&name, info-&type, sizeof(client-&name));
/* Check for address validity */
status = i2c_check_client_addr_validity(client);
if (status) {
dev_err(&adap-&dev, &Invalid %d-bit I2C address 0x%02hx\n&,
client-&flags & I2C_CLIENT_TEN ? 10 : 7, client-&addr);
goto out_err_
/* Check for address business */
status = i2c_check_addr_busy(adap, client-&addr);
if (status)
client-&dev.parent = &client-&adapter-&
client-&dev.bus = &i2c_bus_
client-&dev.type = &i2c_client_
client-&dev.of_node = info-&of_
/* For 10-bit clients, add an arbitrary offset to avoid collisions */
dev_set_name(&client-&dev, &%d-%04x&, i2c_adapter_id(adap),
client-&addr | ((client-&flags & I2C_CLIENT_TEN)
? 0xa000 : 0));
&strong&&span style=&color:#FF0000;&&status = device_register(&client-&dev);&/span&&/strong&
if (status)
dev_dbg(&adap-&dev, &client [%s] registered with bus id %s\n&,
client-&name, dev_name(&client-&dev));
dev_err(&adap-&dev, &Failed to register i2c client %s at 0x%02x &
&(%d)\n&, client-&name, client-&addr, status);
out_err_silent:
kfree(client);
return NULL;
EXPORT_SYMBOL_GPL(i2c_new_device);
int device_register(struct device *dev)
device_initialize(dev);
&span style=&color:#FF0000;&&return device_add(dev);&/span&
int device_add(struct device *dev)
struct device *parent = NULL;
struct kobject *
struct class_interface *class_
int error = -EINVAL;
dev = get_device(dev);
if (!dev-&p) {
error = device_private_init(dev);
if (error)
* for statically allocated devices, which should all be converted
* some day, we need to initialize the name. We prevent reading back
* the name, and force the use of dev_name()
if (dev-&init_name) {
dev_set_name(dev, &%s&, dev-&init_name);
dev-&init_name = NULL;
/* subsystems can specify simple device enumeration */
if (!dev_name(dev) && dev-&bus && dev-&bus-&dev_name)
dev_set_name(dev, &%s%u&, dev-&bus-&dev_name, dev-&id);
if (!dev_name(dev)) {
error = -EINVAL;
goto name_
pr_debug(&device: '%s': %s\n&, dev_name(dev), __func__);
parent = get_device(dev-&parent);
kobj = get_device_parent(dev, parent);
dev-&kobj.parent =
/* use parent numa_node */
if (parent)
set_dev_node(dev, dev_to_node(parent));
/* first, register with generic layer. */
/* we require the name to be set before, and pass NULL */
error = kobject_add(&dev-&kobj, dev-&kobj.parent, NULL);
if (error)
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
error = device_create_file(dev, &uevent_attr);
if (error)
goto attrE
if (MAJOR(dev-&devt)) {
error = device_create_file(dev, &devt_attr);
if (error)
goto ueventattrE
error = device_create_sys_dev_entry(dev);
if (error)
goto devtattrE
devtmpfs_create_node(dev);
error = device_add_class_symlinks(dev);
if (error)
goto SymlinkE
error = device_add_attrs(dev);
if (error)
goto AttrsE
&strong&&span style=&color:#CC0000;&& error = bus_add_device(dev);&/span&&/strong&&pre name=&code& class=&cpp&&
if (error)
error = dpm_sysfs_add(dev);
if (error)
device_pm_add(dev);
/* Notify clients of device addition.
This call must come
* after dpm_sysfs_add() and before kobject_uevent().
if (dev-&bus)
blocking_notifier_call_chain(&dev-&bus-&p-&bus_notifier,
BUS_NOTIFY_ADD_DEVICE, dev);
kobject_uevent(&dev-&kobj, KOBJ_ADD);
bus_probe_device(dev);
if (parent)
klist_add_tail(&dev-&p-&knode_parent,
&parent-&p-&klist_children);
if (dev-&class) {
mutex_lock(&dev-&class-&p-&mutex);
/* tie the class to the device */
klist_add_tail(&dev-&knode_class,
&dev-&class-&p-&klist_devices);
/* notify any interfaces that the device is here */
list_for_each_entry(class_intf,
&dev-&class-&p-&interfaces, node)
if (class_intf-&add_dev)
class_intf-&add_dev(dev, class_intf);
mutex_unlock(&dev-&class-&p-&mutex);
put_device(dev);
bus_remove_device(dev);
device_remove_attrs(dev);
AttrsError:
device_remove_class_symlinks(dev);
SymlinkError:
if (MAJOR(dev-&devt))
devtmpfs_delete_node(dev);
if (MAJOR(dev-&devt))
device_remove_sys_dev_entry(dev);
devtattrError:
if (MAJOR(dev-&devt))
device_remove_file(dev, &devt_attr);
ueventattrError:
device_remove_file(dev, &uevent_attr);
attrError:
kobject_uevent(&dev-&kobj, KOBJ_REMOVE);
kobject_del(&dev-&kobj);
cleanup_device_parent(dev);
if (parent)
put_device(parent);
name_error:
kfree(dev-&p);
dev-&p = NULL;
&pre name=&code& class=&cpp&&int bus_add_device(struct device *dev)
struct bus_type *bus = bus_get(dev-&bus);
int error = 0;
if (bus) {
pr_debug(&bus: '%s': add device %s\n&, bus-&name, dev_name(dev));
error = device_add_attrs(bus, dev);
if (error)
error = sysfs_create_link(&bus-&p-&devices_kset-&kobj,
&dev-&kobj, dev_name(dev));
if (error)
error = sysfs_create_link(&dev-&kobj,
&dev-&bus-&p-&subsys.kobj, &subsystem&);
if (error)
&strong&&span style=&color:#CC0000;&&klist_add_tail(&dev-&p-&knode_bus, &bus-&p-&klist_devices);&/span&&/strong&
out_subsys:
sysfs_remove_link(&bus-&p-&devices_kset-&kobj, dev_name(dev));
device_remove_attrs(bus, dev);
bus_put(dev-&bus);
&&&&&&& 這樣,i2c-client device掛載到了i2c bus上。當i2c 設備驅動添加到系統時,就出現了在前幾部分的東西了。從bus上取下i2c-client與i2c-driver進行匹配。
&&&&&&&&&&& 雖然i2-client怎麼掛載到i2c-bus上知道了(由qup_i2c_probe調用of_i2c_register_device掛載)。新的問題又來了:qup_i2c_probe參數是platform_device,i2c-bus又是如何建立platform_device的。
(kernel/include/linux/init.h)
#define arch_initcall(fn)
&span style=&color:#FF0000;&&__define_initcall(&3&,fn,3)&/span&#define __define_initcall(level,fn,id) \
static initcall_t __initcall_##fn##id __used \
__attribute__((__section__(&.initcall& level &.init&))) = fn
&&&&&&& 將i2c-bus的driver init函數放到.initcall3.init 代碼段中。
&&&&&&& 之所以,i2c-bus的driver和i2c 設備的驅動init函數使用的不同的函數放到.initcall代碼段,主要原因是後面調用.initcall中的函數執行順序是按.initcall段的函數順序進行的(按照initcall的level從0到7依次存放的)。
(kernel/include/linux/vmlinux.lds.h)
#define INIT_CALLS_LEVEL(level)                        \
        VMLINUX_SYMBOL(__initcall##level##_start) = .;        \
        *(.initcall##level##.init)                \
        *(.initcall##level##s.init)   
#define INIT_CALLS
VMLINUX_SYMBOL(__initcall_start) = .;
*(.initcallearly.init)
INIT_CALLS_LEVEL(0)
INIT_CALLS_LEVEL(1)
INIT_CALLS_LEVEL(2)
INIT_CALLS_LEVEL(3)
INIT_CALLS_LEVEL(4)
INIT_CALLS_LEVEL(5)
INIT_CALLS_LEVEL(rootfs)
INIT_CALLS_LEVEL(6)
INIT_CALLS_LEVEL(7)
VMLINUX_SYMBOL(__initcall_end) = .;
&&&&&& 那麼現在根據前面部分內容,知道driver_register將platform bus上的platform device取下來,與driver進行匹配。到此終於理順了從dts節點信息到驅動註冊的完整過程。
四、module_init()中的驅動初始化後函數何時被調用
(kernel/include/linux/init.h)
#define module_init(x)&&&&&&& __initcall(x);
#define __initcall(fn)&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& device_initcall(fn)
#define device_initcall(fn)&&&&&&&&&&&&&&& __define_initcall(&6&,fn,6)
#define __define_initcall(level,fn,id) /
&&&&&&& static initcall_t __initcall_##fn##id __used /
&&&&&&& __attribute__((__section__(&.initcall& level &.init&))) = fn
initcall_t(typedef int (*initcall_t)(void))
這樣就將module_init傳遞的fn函數放到了制定的.initcall6.init&代碼段中。
在linux& /init/main.c 中,
static void __init do_initcalls(void)
  initcall_t *
  call = &__initcall_
   (*call)();
   call++;
  } while (call & &__initcall_end);
  /* Make sure there is no pending stuff from the initcall sequence */
  flush_scheduled_tasks();
通過do_initcalls就可以吧__initcall section段的函數調用起來。
本文已收录于以下专栏:
相关文章推荐
转载本文解释linux下设备和驱动的不同注册顺序时设备probe的时机;增加两个case以解决PCI/USB等可热插拔设备不同插入过程的probe时机的疑问。
Linux 2.6的设备驱动模型中,所...
硬件资源越来越庞大和复杂,内核的另一个挑战就是要便捷的管理这些资源。同时,面对如此之多的平台不同的CPU
,管理机制需要统一适用,这就需要对资源的管理抽象到更加通用的层次。CPU中各个模块都需要时钟驱...
(1) 对应外设时钟的开启
struct clk=clk_get(NULL,"adc");
clk.enable();
之后adc对应的时钟位就能使能。
struct clk *clk_get...
原文地址:http://blog.csdn.net/ziseliuxingzh/article/details/6741640
在platform机制下,板级文件BSP一般定义了设备的资源,在系...
1、通过文件名可以看出,这个文件是和平台有关的函数
2、声明了结构体struct
platform_object,作为platform_device平台设备的载体
http://devicetree.org/Device_Tree_Usage
如果一个driver需要兼容两套设定,可以通过acpi_device_id 来区分
static const struct acpi_device_id hns_enet_acpi_match[] ...
Linux kernel 是怎么将 devicetree中的内容生成plateform_device
1,实现场景(以Versatile Express V2M为例说明其过程)
宋宝华 Barry Song
ARM Device Tree起源
Linus Torvalds在日的ARM Linux邮件列表宣称“this whole ARM th...
boot_linux_from_mmc
|-> dt_table_offset=((uint32_t)image_addr+page_size+kernel_actual_ramdis...
他的最新文章
讲师:李江龙
讲师:司徒正美
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)

我要回帖

 

随机推荐