linux 设备树

linux 设备树linux设备树参考地址http://blog.csdn.net/green1900/article/details/45646095http://www.cnblogs.com/xiaojiang1025/p/6131381.htmlhttp://blog.csdn.net/21cnbao/article/details/84575461.为什么要使用设备树(DeviceTree

大家好,又见面了,我是你们的朋友全栈君。

linux 设备树


参考地址
http://blog.csdn.net/green1900/article/details/45646095
http://www.cnblogs.com/xiaojiang1025/p/6131381.html
http://blog.csdn.net/21cnbao/article/details/8457546


1.为什么要使用设备树(Device Tree)?

在以前的内核源码中,存在大量对板级细节信息描述的代码,这些代码充斥在/arch/arm/plat-xxx和/arch/arm/mach-xxx目录,对内核而言这些platform设备、resource、i2c_board_info、spi_board_info以及各种硬件的platform_data绝大多数纯属垃圾冗余代码。为了解决这一问题,ARM内核版本3.x之后引入了原先在Power PC等其他体系架构已经使用的Flattened Device Tree。DTS不是arm的专利

在使用了设备树后,对于同一SOC的不同主板,只需更换设备树文件.dtb即可实现不同主板的无差异支持,而无需更换内核文件。


2.设备树的的组成和结构

设备树可以描述的信息包括了
1. CPU的数量和类别、
2. 内存基地址和大小、
3. 总线和桥、
4. 外设连接、
5. 中断控制器和中断使用情况、
6. GPIO控制器和GPIO使用情况、
7. Clock控制器和Clock使用情况。
需要注意的是,设备树对于可热插拔的热备不进行具体描述,它只描述用于控制该热插拔设备的控制器

2.1设备树的组成

设备树包含了DTC(device tree compiler) , DTS(device tree resource) 和 DTB(device tree blob),简单来说,dts是源码,dtc是编译器,dtb是生成的可执行文件
此处输入图片的描述

2.1.1 DTS和DTSI

.dts和.dtsi是一种ASCII文本的设备树描述,此文本格式非常适合人们阅读,基本上,一个.dts对应一种ARM设备,放在arch/arm/boot/dts目录,由于一个soc对应好多个不同的开发板,每个开发板有一个.dts,所以这些dts势必有共同部分,为了减少代码的屯余,设备树将这些共同部分提炼保存在dtsi中,供不同的dts使用,dtsi文件类似于c语言的头文件

2.1.2 DTC

DTC为编译工具,它可以将.dts文件编译成.dtb文件。DTC的源码位于内核的scripts/dtc目录,内核选中CONFIG_OF,编译内核的时候,主机可执行程序DTC就会被编译出来

2.1.3 DTB

DTB设备由DTC编译后的二进制格式的设备树描述,可以由linux内核解析,uboot这样的bootloader也可以识别.dtb,有两种使用方式,一种是bootloader启动内核过程中会先读取dtb到文件中;第二种是把dtb和zImage打包在一起做成一个印象文件,firefly-3399就是采用这种方式,打包生成了boot.img

2.1.4 绑定(bingding)

对于Device Tree中的结点和属性具体是如何来描述设备的硬件细节的,一般需要文档来进行讲解,文档的后缀名一般为.txt。这些文档位于内核的Documentation/devicetree/bindings目录,其下又分为很多子目录

2.1.5 Bootloader 使用dtb

在Uboot中,可以从NAND、SD或者TFTP等任意介质将.dtb读入内存,假设.dtb放入的内存地址为0x71000000,之后可在Uboot运行命令fdt addr命令设置.dtb的地址,如:
U-Boot> fdt addr 0x71000000
fdt的其他命令就变地可以使用,如fdt resize、fdt print等
对于ARM来讲,可以透过bootz kernel_addr initrd_address dtb_address的命令来启动内核,即dtb_address作为bootz或者bootm的最后一次参数,第一个参数为内核映像的地址,第二个参数为initrd的地址,若不存在initrd,可以用 -代替,第三个就是dtb地址

2.2设备树框架

设备树用树状结构描述设备信息,它有以下几种特性
1. 每个设备树文件都有一个根节点,每个设备都是一个节点。
2. 节点间可以嵌套,形成父子关系,这样就可以方便的描述设备间的关系。
3. 每个设备的属性都用一组key-value对(键值对)来描述。
4. 每个属性的描述用;结束


3. 设备树语法

设备树是一颗树,书上的每个节点由节点和属性组成,属性是键值对

下面这个是rk3399-fpga.dts

#include "rk3399.dtsi"  //包含了公共部分
/ {
        model = "Rockchip RK3399 FPGA Board";
        compatible = "rockchip,fpga", "rockchip,rk3399"; //根节点兼容性分析,下面具体分析
        chosen {
                bootargs = "init=/init console=uart,mmio32,0xff1a0000";
        };
        memory@00000000 { //子节点 memory@00000000节点名
                device_type = "memory";
                reg = <0x0 0x00000000 0x0 0x20000000>;
        };
};
&uart2 { //使用了引用
        status = "okay";
        clocks = <&xin24m>, <&xin24m>;
};

3.1根节点兼容性

compatible = "rockchip,fpga", "rockchip,rk3399";
上面是根节点的兼容属性,定义了整个系统(设备级别)的名称,通过这个属性就可以判断出它启动的是什么设备。它的组织形式是&lt;manufacture&gt;&lt;model&gt;,在实际中一般包括两个或两个以上的兼容字符串,上面第一个是"rockchip,fpga",第二个是"rockchip,rk3399",我们来看第二个,manufacture是板子级别的名字,“rockchip”代表的是瑞芯微公司,model是芯片级别的,“rk3399”是瑞芯微公司一个soc的名称

我们从源码中找出rk3399的两个dts,可以看出第一个兼容字符串的model不同,第二个完全相同

rk3399-firefly-linux.dts
compatible = "rockchip,rk3399-firefly-linux", "rockchip,rk3399";
rk3399-fpga.dts
compatible = "rockchip,fpga", "rockchip,rk3399";

3.2节点名

理论个节点名只要是长度不超过31个字符的ASCII字符串即可,Linux内核还约定设备名应写成形如[@]的形式,其中name就是设备名,最长可以是31个字符长度。unit_address一般是设备地址,用来唯一标识一个节点
Linux中的设备树还包括几个特殊的节点,比如chosen,chosen节点不描述一个真实设备,而是用于firmware传递一些数据给OS,比如bootloader传递内核启动参数给内核

chosen{
    bootargs = "console=ttySAC2,115200";
    stdout-path=&serial_2;
};

3.3引用

当我们找一个节点的时候,我们必须书写完整的节点路径,这样当一个节点嵌套比较深的时候就不是很方便,所以,设备树允许我们用下面的形式为节点标注引用(起别名),借以省去冗长的路径。这样就可以实现类似函数调用的效果

3.KEY

在设备树中,键值对是描述属性的方式,比如,Linux驱动中可以通过设备节点中的”compatible”这个属性查找设备节点
inux设备树语法中定义了一些具有规范意义的属性,包括:compatible, address, interrupt等,这些信息能够在内核初始化找到节点的时候,自动解析生成相应的设备信息。此外,还有一些Linux内核定义好的,一类设备通用的有默认意义的属性,这些属性一般不能被内核自动解析生成相应的设备信息,但是内核已经编写的相应的解析提取函数,常见的有 “mac_addr”,”gpio”,”clock”,”power”。”regulator” 等等。

3.1.compatible

设备节点中对应的节点信息已经被内核构造成struct platform_device。驱动可以通过相应的函数从中提取信息。主要有三种方法提取信息

    1、compatible属性是用来查找节点
    2、通过节点名查找指定节点
    3、节点路径查找指定节点

看一个使用compatible提取属性的例子

#dts
    gpio_demo: gpio_demo {
        status = "okay";
        compatible = "firefly,rk3399-gpio";                  
    };
#驱动代码
static struct of_device_id firefly_match_table[] = {
    { .compatible = "firefly,rk3399-gpio",}, //完全相同
    {}, //最后一个成员一定是空,因为相关的操作API会读取这个数组直到遇到一个空。
};

3.2address

  • #address-cells,用来描述子节点”reg”属性的地址表中用来描述首地址的cell的数量
  • #size-cells,用来描述子节点”reg”属性的地址表中用来描述地址长度的cell的数量。
        pinctrl: pinctrl {
                compatible = "rockchip,rk3399-pinctrl";
                #address-cells = <0x2>;
                #size-cells = <0x2>;
                gpio0: gpio0@ff720000 {
                        compatible = "rockchip,gpio-bank";
                        reg = <0x0 0xff720000 0x0 0x100>;
                        //前两个数字表示一个地址0x0 0xff720000
                        //后两个数字表示一个地址跨度 0x100
                };
            ...
    }

3.3interrupts

一个计算机系统中大量设备都是通过中断请求CPU服务的,所以设备节点中就需要在指定中断

  • interrupt-controller 一个空属性用来声明这个node接收中断信号,即这个node是一个中断控制器
  • #interrupt-cells,是中断控制器节点的属性,用来标识这个控制器需要几个单位做中断描述符,用来描述子节点中”interrupts”属性使用了父节点中的interrupts属性的具体的哪个值。一般,如果父节点的该属性的值是3,则子节点的interrupts一个cell的三个32bits整数值分别为:<中断域 中断 触发方式>,如果父节点的该属性是2,则是<中断 触发方式>
  • interrupt-parent,标识此设备节点属于哪一个中断控制器,如果没有设置这个属性,会自动依附父节点的
  • interrupts,一个中断标识符列表,表示每一个中断输出信号

3.4gpio

  • gpio-controller,用来说明该节点描述的是一个gpio控制器
  • #gpio-cells,用来描述gpio使用节点的属性一个cell的内容,即 `属性 = <&引用GPIO节点别名 GPIO标号 工作模式>
firefly-gpio = <&gpio0 12 GPIO_ACTIVE_HIGH>;          /* GPIO0_B4 */
firefly-irq-gpio = <&gpio4 29 IRQ_TYPE_EDGE_RISING>;  /* GPIO4_D5 */  

4.DTB的加载过程

参考地址
http://blog.csdn.net/green1900/article/details/45646095
此处输入图片的描述
http://blog.csdn.net/lichengtongxiazai/article/details/38941913
此处输入图片的描述

总的归纳为:

① kernel入口处获取到uboot传过来的.dtb镜像的基地址

② 通过early_init_dt_scan()函数来获取kernel初始化时需要的bootargs和cmd_line等系统引导参数。

③ 调用unflatten_device_tree函数来解析dtb文件,构建一个由device_node结构连接而成的单向链表,并使用全局变量of_allnodes保存这个链表的头指针。

④ 内核调用OF的API接口,获取of_allnodes链表信息来初始化内核其他子系统、设备等。


5.API调用

#来查找在dtb中的根节点
unsigned long __init of_get_flat_dt_root(void)

# 根据deice_node结构的full_name参数,在全局链表of_allnodes中,查找合适的device_node
struct device_node *of_find_node_by_path(const char *path)

#若from=NULL,则在全局链表of_allnodes中根据name查找合适的device_node
struct device_node *of_find_node_by_name(struct device_node *from,const char *name)

#根据设备类型查找相应的device_node
struct device_node *of_find_node_by_type(struct device_node *from,const char *type)

# 根据compatible字符串查找device_node
struct device_node *of_find_compatible_node(struct device_node *from,const char *type, const char *compatible)

#根据节点属性的name查找device_node
struct device_node *of_find_node_with_property(struct device_node *from,const char *prop_name)

#根据compat参数与device node的compatible匹配,返回匹配度
int of_device_is_compatible(const struct device_node *device,const char *compat)

#获得父节点的device node
struct device_node *of_get_parent(const struct device_node *node)

#读取该设备的第index个irq号
unsigned int irq_of_parse_and_map(struct device_node *dev, int index)

#读取该设备的第index个irq号,并填充一个irq资源结构体
int of_irq_to_resource(struct device_node *dev, int index, struct resource *r)

#获取该设备的irq个数
int of_irq_count(struct device_node *dev)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/147938.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号