一文详解 | Linux设备树框架及驱动移植实例
点击左上方蓝色“混说Linux”,选择“设为星标”
1
一、设备树概念
引入了设备树之后,驱动代码只负责处理驱动的逻辑,而关于设备的具体信息存放到设备树文件中,这样,如果只是硬件接口信息的变化而没有驱动逻辑的变化,驱动开发者只需要修改设备树文件信息,不需要改写驱动代码。比如在ARM Linux内,一个.dts(device tree source)文件对应一个ARM的machine,一般放置在内核的"arch/arm/boot/dts/"目录内,比如exynos4412参考板的板级设备树文件就是"arch/arm/boot/dts/exynos4412-origen.dts"。这个文件可以通过 $make dtbs 命令编译成二进制的 .dtb 文件供内核驱动使用。
基于同样的软件分层设计的思想,由于一个SoC可能对应多个machine,如果每个machine的设备树都写成一个完全独立的.dts文件,那么势必相当一些.dts文件有重复的部分,为了解决这个问题,Linux设备树目录把一个SoC公用的部分或者多个machine共同的部分提炼为相应的.dtsi文件。这样每个.dts就只有自己差异的部分,公有的部分只需要"include"相应的.dtsi文件, 这样就使整个设备树的管理更加有序。
二、设备树框架
设备树用树状结构描述设备信息,它有以下几种特性:
1. 每个设备树文件都有一个根节点,每个设备都是一个节点。
2. 节点间可以嵌套,形成父子关系,这样就可以方便的描述设备间的关系。
3. 每个设备的属性都用一组key-value对(键值对)来描述。
4. 每个属性的描述用 ; 结束。
所以,一个设备树的基本框架可以写成下面这个样子,一般来说,/表示板子,它的子节点node1表示SoC上的某个控制器,控制器中的子节点node2表示挂接在这个控制器上的设备(们)。
/{ //根节点
node1{ //node1是节点名,是/的子节点
key=value; //node1的属性
...
node2{ //node2是node1的子节点
key=value; //node2的属性
...
}
} //node1的描述到此为止
node3{
key=value;
...
}
}
上面的节点名是 firmware ,节点路径是 /firmware@0203f000 ,这点要注意,因为根据节点名查找节点的API的参数是不能有"@xxx"这部分的。
三、KEY
Linux设备树语法中定义了一些具有规范意义的属性,包括:compatible, address, interrupt 等,这些信息能够在内核初始化找到节点的时候,自动解析生成相应的设备信息。此外,还有一些Linux内核定义好的,一类设备通用的有默认意义的属性,这些属性一般不能被内核自动解析生成相应的设备信息,但是内核已经编写的相应的解析提取函数,常见的有 "mac_addr","gpio","clock","power"。"regulator" 等等。
1. compatible
(几乎)所有的设备都需要与CPU的IO口相连,所以其IO端口信息就需要在设备节点节点中说明。常用的属性有:
#address-cells,用来描述子节点"reg"属性的地址表中用来描述首地址的cell的数量;
#size-cells,用来描述子节点"reg"属性的地址表中用来描述地址长度的cell的数量。
3. interrupts
interrupt-controller 一个空属性用来声明这个node接收中断信号,即这个node是一个中断控制器。
#interrupt-cells,是中断控制器节点的属性,用来标识这个控制器需要几个单位做中断描述符,用来描述子节点中"interrupts"属性使用了父节点中的interrupts属性的具体的哪个值。一般,如果父节点的该属性的值是3,则子节点的interrupts一个cell的三个32bits整数值分别为:<中断域 中断 触发方式>,如果父节点的该属性是2,则是<中断 触发方式>。
interrupt-parent,标识此设备节点属于哪一个中断控制器,如果没有设置这个属性,会自动依附父节点的。
interrupts,一个中断标识符列表,表示每一个中断输出信号。
在这种趋势下,内核中原本的中断源直接到中断号的方式已经很难继续发展了,为了解决这些问题,linux kernel的大牛们就创造了irq domain(中断域)这个概念。domain在内核中有很多,除了irq domain,还有power domain,clock domain等等,所谓domain,就是领域,范围的意思,也就是说,任何的定义出了这个范围就没有意义了。
如上所述,系统中所有的interrupt controller会形成树状结构,对于每个interrupt controller都可以连接若干个外设的中断请求(interrupt source,中断源),interrupt controller会对连接其上的interrupt source(根据其在Interrupt controller中物理特性)进行编号(也就是HW interrupt ID了)。
有了irq domain这个概念之后,这个编号仅仅限制在本interrupt controller范围内,有了这样的设计,CPU(Linux 内核)就可以根据级联的规则一级一级的找到想要访问的中断。当然,通常我们关心的只是内核中的中断号,具体这个中断号是怎么找到相应的中断源的,我们作为程序员往往不需要关心,除了在写设备树的时候,设备树就是要描述嵌入式软件开发中涉及的所有硬件信息,所以,设备树就需要准确的描述硬件上处理中断的这种树状结构,如此,就有了我们的interrupt-parant这样的概念:用来连接这样的树状结构的上下级,用于表示这个中断归属于哪个interrupt controller,比如,一个接在GPIO上的按键,它的组织形式就是:
中断源--interrupt parent-->GPIO--interrupt parent-->GIC1--interrupt parent-->GIC2--...-->CPU
有了parant,我们就可以使用一级一级的偏移量来最终获得当前中断的绝对编号,这里,可以看出,在我板子上的dm9000的的设备节点中,它的"interrupt-parent"引用了"exynos4x12-pinctrl.dtsi"(被板级设备树的exynos4412.dtsi包含)中的gpx0节点:
4. GPIO
"gpio-controller",用来说明该节点描述的是一个gpio控制器。
"#gpio-cells",用来描述gpio使用节点的属性一个cell的内容,即 `属性 = <&引用GPIO节点别名 GPIO标号 工作模式>。
5. 驱动自定义key
针对具体的设备,有部分属性很难做到通用,需要驱动自己定义好,通过内核的属性提取解析函数进行值的获取,比如dm9000节点中的下面这句就是自定义的节点属性,用以表示配置EEPROM不可用。
四、VALUE
1. 字符串信息
2. 32bit无符号整型数组信息
3. 二进制数数组
4. 字符串哈希表
5. 混合形式
五、设备树/驱动移植实例
勾选相应的选项将dm9000的驱动编译进内核。
make menuconfig
[*] Networking support --->
Networking options --->
<*> Packet socket
<*>Unix domain sockets
[*] TCP/IP networking
[*] IP: kernel level autoconfiguration
Device Drivers --->
[*] Network device support --->
[*] Ethernet driver support (NEW) --->
<*> DM9000 support
File systems --->
[*] Network File Systems (NEW) --->
<*> NFS client support
[*] NFS client support for NFS version 3
[*] NFS client support for the NFSv3 ACL protocol extension
[*] Root file system on NFS
原文:https://www.cnblogs.com/xiaojiang1025/p/6131381.html
版权归原作者所有,如有侵权,请联系删除。
关注微信公众号『混说Linux』,后台点击 关于混说 即可添加作者微信。
往期推荐