Linux驱动开发学习笔记【3】:设备树

目录

一、什么是设备树

二、DTS、DTB和DTC的关系

三、DTS基本语法

四、创建小型的设备树模板

五、设备树在系统中的体现

六、特殊节点

1、aliases节点

2、chosen节点

七、特殊的属性

1、compatible属性(兼容性属性)

2、 model属性

3、status属性

4、 #address-cells和 #size-cells属性

5、 reg属性

6、 ranges属性

7、 device_type属性

八、Linux内核的OF操作函数


一、什么是设备树

设备树(Device Tree),将这个词分开就是“设备”和“树”,描述设备树的文件叫DTS(Device Tree Source),这个DTS 文件采用树形结构描述板级设备,也就是开发板上的设备信息,比如CPU 数量、 内存基地址、IIC 接口上接了哪些设备、SPI 接口上接了哪些设备等等,如图所示:

 

二、DTS、DTB和DTC的关系

1、DTS相当于.c,就是设备树源码文件

2、DTC工具相当于gcc编译器,将.dts编译成.dtb,DTC工具源码在 Linux内核的 scripts/dtc目录下,

3、DTB是将DTS编译以后得到的二进制文件

通过 make dtbs 编译所有的dts文件

也可以编译指定的dtbs,比如:make imx6ull-alientek-emmc.dtb

在编译dtb文件的时候,可以通过 \arch\arm\boot\dts\Makefile 指定要编译的dts设备树源码文件

三、DTS基本语法

设备树也有头文件,扩展名为.dtsi。可以将一款SOC他的其他所有设备/平台的共有的信息提出来,作为一个通用的.dtsi文件

1、DTS也以  开始,/ 是整个设备树的跟节点

2,从根节点开始描述设备信息

 

3,在根节点外有一些&cpu0这样的语句是“追加“

 

4,节点命名

label:node-name@unit-address

label:是节点的标签,后面才是名字,可以没有,比如

intc: interrupt-controller@00a01000 完整的名字是interrupt-controller@00a01000

在后面追加的时候直接使用&intc

node-name:节点名字

unit-address:一般都是外设寄存器的起始地址,有时候是I2C的设备地址,或者其他含义,具体节点具体分析

四、创建小型的设备树模板

/dts-v1

#include .h
#include .dtsi

/ {
    /* skeleton.dtsi文件*/
    #address-cells = <1>;
    #size-cells = <1>;
    
    chosen {
      stdout-path = &uart1;  
    };
    
    aliases {
	};

	cpus {
	};

	intc: interrupt-controller@00a01000 {
	};

	clocks {
	};

	soc {
	};
 
    memory {
    };
    
    reserved-memory{
    };
    
    ...
}

五、设备树在系统中的体现

Linux 内核在启动的时候会解析DTB 文件,然后在/proc/device-tree 目录下生成相应的设备树节点文件

Linux 内核是如何解析DTB 文件的,流程如图:

六、特殊节点

1、aliases节点

2、chosen节点

主要目的就是将uboot里面bootargs环境变量值,传递给Linux内核作为命令行参数,command line。uboot里面bootargs值为:

bootargs=console=ttymxc0,115200 root=/dev/nfs rw 
nfsroot=192.168.199.158:/home/denghengli/linux/nfs/rootfs ip=192.168.199.20:192.168.199.158:192.168.199.1:255.255.255.0::eth0:off
//说明:192.168.199.158为ubuntu主机ip,192.168.199.20为开发板ip

linux内核cmdline值为:

Kernel command line: console=ttymxc0,115200 root=/dev/nfs rw 
nfsroot=192.168.199.158:/home/denghengli/linux/nfs/rootfs ip=192.168.199.20:192.168.199.158:192.168.199.1:255.255.255.0::eth0:off

那么uboot是如何向kernel传递bootargs的呢?

经过查看发现chosen节点中包含bootargs属性,属性值和uboot的bootargs一致。uboot接触过dtb,最终通过bootz 80800000 – 83000000 来启动内核。经过分析判断uboot拥有bootargs环境变量和dtb,因此最有可能“作案”。最终发现在uboot的fdt_chosen函数中会查找chosen节点,并且在里面添加bootargs属性,属性值为bootargs变量值。

七、特殊的属性

1、compatible属性(兼容性属性)

(1)值是字符串。ompatible属性用于将设备和驱动绑定起来。字符串列表用于选择设备所要使用的驱动程序, compatible属性的值格式如下所示:"manufacturer,model",其中 manufacturer表示厂商, model一般是模块对应的驱动名字。比如imx6ull-alientek-emmc.dts中 sound节点是 I.MX6U-ALPHA开发板的音频设备节点:

compatible = "fsl,imx6ul-evk-wm8960","fsl,imx-audio-wm8960";
其中 fsl表示厂商是飞思卡尔,“imx6ul-evk-wm8960”和 imx-audio-wm8960”表示驱动模块名字。
sound这个设备首先使用第一个兼容值在 Linux内核里面查找,看看能不能找到与之匹配的驱动文件,
如果没有找到的话就使用第二个兼容值查。

(2)一般驱动程序文件都会有一个 OF匹配表,此 OF匹配表保存着一些 compatible值,如果设备节点的 compatible属性值和 OF匹配表中的任何一个值相等,那么就表示设备可以使用这个驱动。

static const struct of_device_id imx_wm8960_dt_ids[] = {
	{ .compatible = "fsl,imx-audio-wm8960", },
	{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_wm8960_dt_ids);

static struct platform_driver imx_wm8960_driver = {
	.driver = {
		.name = "imx-wm8960",
		.pm = &snd_soc_pm_ops,
		.of_match_table = imx_wm8960_dt_ids,
	},
	.probe = imx_wm8960_probe,
	.remove = imx_wm8960_remove,
};
module_platform_driver(imx_wm8960_driver);

(3)根节点/下面的compatible。内核启动的时候会检查是否支持此平台,或机器。

不使用设备树的时候通过machine id来判断内核是否支持此机器:

#define MACHINE_START(_type,_name) \
static const struct machine_desc __mach_desc_##_type \
 __used \
 __attribute__((__section__(".arch.info.init"))) = { \
.nr = MACH_TYPE_##_type, \
.name = _name,
 
#define MACHINE_END \
};


MACHINE_START(MX35_3DS, "Freescale MX35PDK")
/* Maintainer: Freescale Semiconductor, Inc */
.atag_offset = 0x100,
.map_io = mx35_map_io,
.init_early = imx35_init_early,
.init_irq = mx35_init_irq,
.init_time = mx35pdk_timer_init,
.init_machine = mx35_3ds_init,
.reserve = mx35_3ds_reserve,
.restart = mxc_restart,
MACHINE_END


展开以后:
static const struct machine_desc __mach_desc_MX35_3DS __used
 __attribute__((__section__(".arch.info.init"))) = {
.nr = MACH_TYPE_MX35_3DS, //机器ID
.name = "Freescale MX35PDK",
.atag_offset = 0x100,
.map_io = mx35_map_io,
.init_early = imx35_init_early,
.init_irq = mx35_init_irq,
.init_time = mx35pdk_timer_init,
.init_machine = mx35_3ds_init,
.reserve = mx35_3ds_reserve,
.restart = mxc_restart,
};

使用设备树的时候不使用机器ID,而是使用根节点/下的compatible:

#define DT_MACHINE_START(_name, _namestr) \
static const struct machine_desc __mach_desc_##_name \
 __used \
 __attribute__((__section__(".arch.info.init"))) = { \
.nr = ~0, \
.name = _namestr,


DT_MACHINE_START(IMX6UL, "Freescale i.MX6 Ultralite (Device Tree)")
.map_io = imx6ul_map_io,
.init_irq = imx6ul_init_irq,
.init_machine = imx6ul_init_machine,
.init_late = imx6ul_init_late,
.dt_compat = imx6ul_dt_compat,
MACHINE_END


展开以后:
static const struct machine_desc __mach_desc_IMX6UL __used
 __attribute__((__section__(".arch.info.init"))) = {
.nr = ~0,
.name = "Freescale i.MX6 Ultralite (Device Tree)",
.map_io = imx6ul_map_io,
.init_irq = imx6ul_init_irq,
.init_machine = imx6ul_init_machine,
.init_late = imx6ul_init_late,
.dt_compat = imx6ul_dt_compat,
};

2、 model属性

model属性值也是一个字符串,一般 model属性描述设备模块信息,比如名字什么的,比如:

model = "wm8960-audio";

3、status属性

status属性看名字就知道是和设备状态有关的, status属性值也是字符串,字符串是设备的状态信息,可选的状态如表

4、 #address-cells和 #size-cells属性

在有子节点的设备中,用于描述子节点的地址信息。 #address-cells属性值决定了子节点 reg属性中地址信息所占用的字长 (32位 ),#size-cells属性值决定了子节点 reg属性中长度信息所占的字长 (32位 )。 #address-cells和 #size-cells表明了子节点应该如何编写 reg属性值,一般 reg属性都是和地址有关的内容,和地址相关的信息有两种:起始地址和地址长度, reg属性的格式一为:

reg = <address1 length1 address2 length2 address3 length3…………>

5、 reg属性

一般用于描述设备地址空间资源信息,一般都是某个外 设的寄存器地址范围信息

reg = <0x4600 0x100>;

6、 ranges属性

ranges属性值可以为空或者按照 (child-bus-address,parent-bus-address,length)格式编写的数字矩阵, ranges是一个地址映射 /转换表, ranges属性每个项目由子地址、父地址和地址空间长度这三部分组成:

child-bus-address:子总线地址空间的物理地址,由父节点的 #address-cells确定此物理地址所占用的字长。

parent-bus-address :父总线地址空间的物理地址,同样由父节点的 #address-cells确定此物理地址所占用的字长。

length: 子地址空间的长度,由父节点的 #size-cells确定此地址 长度所占用的字长。

如果 ranges属性值为空值,说明子地址空间和父地 址空间完全相同,不需要进行地址转换,

7、 device_type属性

device_type属性值为字符串, IEEE 1275会用到此属性,用于描述设备的 FCode,但是设备树没有 FCode,所以此属性也被抛弃了。此属性只能用于 cpu节点或者 memory节点。imx6ull.dtsi的 cpu0节点用到了此属性

八、Linux内核的OF操作函数

1、在驱动中想要获取设备树中的节点或属性信息,需要使用OF函数,这些 OF函数原型都定义在 include/linux/of.h文件中。

2,驱动要想获取到设备树节点内容,首先要找到节点。

 

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值