https://blog.csdn.net/u010388659/article/details/81253383
在Linux3.x版本后,arch/arm/plat-xxx和arch/arm/mach-xxx中,描述板级细节的代码(比如platform_device、i2c_board_info等)被大量取消,取而代之的是设备树,其目录位于arch/arm/boot/dts
1个dts文件+n个dtsi文件,它们编译而成的dtb文件就是真正的设备树
-
imx6dl-hummingboard.dts
-
|_imx6dl.dtsi
-
|
|_imx6qdl.dtsi
-
|_imx6qdl-microsom.dtsi
-
|_imx6qdl-microsom-ar8035.dtsi
下面分别是是imx6dl-hummingboard.dts以及imx6dl.dtsi文件,我们以它们为例来分析,不难发现dts文件内容很少,只有一些板级的特征,大部分公共的硬件描述都在dtsi文件中
-
/dts-v1/;
-
#include "imx6dl.dtsi"
-
#include "imx6qdl-microsom.dtsi"
-
#include "imx6qdl-microsom-ar8035.dtsi"
-
-
/ {
-
model =
"SolidRun HummingBoard DL/Solo";
-
compatible =
"solidrun,hummingboard",
"fsl,imx6dl";
-
-
ir_recv: ir-receiver {
-
compatible =
"gpio-ir-receiver";
-
gpios = <&gpio1
2
1>;
-
pinctrl-names =
"default";
-
pinctrl
-0 = <&pinctrl_hummingboard_gpio1_2>;
-
};
-
-
regulators {
-
compatible =
"simple-bus";
-
-
reg_3p3v:
3p3v {
-
compatible =
"regulator-fixed";
-
regulator-name =
"3P3V";
-
regulator-min-microvolt = <
3300000>;
-
regulator-max-microvolt = <
3300000>;
-
regulator-always-on;
-
};
-
}
-
-
&i2c1 {
-
pinctrl-names =
"default";
-
pinctrl
-0 = <&pinctrl_hummingboard_i2c1>;
-
-
rtc: pcf8523@
68 {
-
compatible =
"nxp,pcf8523";
-
reg = <
0x68>;
-
};
-
};
-
/ {
-
aliases {
-
-
/*省略无关代码*/
-
}
-
soc {
-
#address-cells = <1>;
-
#size-cells = <1>;
-
compatible =
"simple-bus";
-
interrupt-parent = <&intc>;
-
ranges;
-
-
/*省略无关代码*/
-
-
timer@
00a00600 {
-
compatible =
"arm,cortex-a9-twd-timer";
-
reg = <
0x00a00600
0x20>;
-
interrupts = <
1
13
0xf01>;
-
clocks = <&clks IMX6QDL_CLK_TWD>;
-
};
-
-
aips-bus@
02000000 {
/* AIPS1 */
-
compatible =
"fsl,aips-bus",
"simple-bus";
-
#address-cells = <1>;
-
#size-cells = <1>;
-
reg = <
0x02000000
0x100000>;
-
ranges;
-
-
/*省略无关代码*/
-
-
gpio1: gpio@
0209c000 {
-
compatible =
"fsl,imx6q-gpio",
"fsl,imx35-gpio";
-
reg = <
0x0209c000
0x4000>;
-
interrupts = <
0
66 IRQ_TYPE_LEVEL_HIGH>,
-
<
0
67 IRQ_TYPE_LEVEL_HIGH>;
-
gpio-controller;
-
#gpio-cells = <2>;
-
interrupt-controller;
-
#interrupt-cells = <2>;
-
};
-
-
/*省略无关代码*/
-
-
i2c1: i2c@
021a0000 {
-
#address-cells = <1>;
-
#size-cells = <0>;
-
compatible =
"fsl,imx6q-i2c",
"fsl,imx21-i2c";
-
reg = <
0x021a0000
0x4000>;
-
interrupts = <
0
36 IRQ_TYPE_LEVEL_HIGH>;
-
clocks = <&clks IMX6QDL_CLK_I2C1>;
-
status =
"disabled";
-
};
-
-
};
-
/*省略无关代码*/
-
};
-
};
/ {}
,称为根节点。节点的标准结构是xxx@yyy{…},xxx是节点的名字,yyy则不是必须的,其值为节点的地址(寄存器地址或其他地址),比如i2c1: i2c@021a0000
中的就是一个i2c控制器的寄存器基地址,rtc: pcf8523@68
中的就是这个rtc设备的i2c地址i2c@021a0000
,虽然它在名字后面跟了地址,但是正式的设置是在reg属性中设置的比如:reg = <0x021a0000 0x4000>;
reg的格式通常为
,0x021a0000是寄存器基地址,0x4000是长度。address 和length的个数是可变的,由父节点的属性#address-cells
和#size-cells
决定,比如节点i2c@021a0000
的父节点是aips-bus@02000000
,其#address-cells
和#size-cells
均为1,所以下面的i2c节点的reg属性就有一个address 和length,而i2c节点本身#address-cells
和#size-cells
分别为1和0,所以其下的rtc: pcf8523@68
的reg属性就只有一个0x68(i2c地址)了"fsl,imx6dl"
这个字符串,因为系统启动后,将根据根节点的compatible来判断cpu信息,并由此进行初始化Documentation/devicetree/bindings/i2c/i2c-imx.txt
就描述了imx平台的i2c属性设置方法;Documentation/devicetree/bindings/fb
就描述了lcd、lvds这类属性设置方法rtc: pcf8523@68 {}
首先,内核必须要知道dtb文件的地址,这由U-boot来告诉内核,详见U-boot引导内核流程分析 第6节。只要内核知晓了dtb文件的地址,那么驱动就可以通过一些API任意获取设备树的内部信息
对于3.x版本之后的内核,platform、i2c、spi等设备不再需要在mach-xxx中注册,驱动程序将直接和设备树里的设备节点进行配对,是通过设备节点中的compatible(兼容性)来与设备节点进行配对的,这里只做简单介绍,具体的应用详见 基于i2c子系统的驱动分析、 基于platform总线的驱动分析
-
/*定义的of_match_table*/
-
static
const
struct of_device_id pcf8523_of_match[] = {
-
{ .compatible =
"nxp,pcf8523" },
-
{ }
-
};
-
-
/*driver 结构体中的of_match_table*/
-
static
struct i2c_driver pcf8523_driver = {
-
.driver = {
-
.name = DRIVER_NAME,
-
.owner = THIS_MODULE,
-
.of_match_table = of_match_ptr(pcf8523_of_match),
-
},
-
.probe = pcf8523_probe,
-
.id_table = pcf8523_id,
-
};
compatible = "pcf8523";
,显然相对于驱动id_table中的"nxp,pcf8523"
,他遗漏了nxp字段,但是驱动却仍然可以匹配上,因为别名匹配对compatible中字符串里第二个字段敏感当修改或编写驱动时,常常需要修改gpio、时钟、中断等等参数,以前都是在mach-xxx中的device设置的,现在则要在节点里设置,然后驱动用特殊的API来获取
int of_device_is_compatible(const struct device_node *device, const char *name)
来判断当前节点是否包含指定的compatible(兼容性)
-
/*imx6dl.dtsi中gpio1控制器的定义节点*/
-
gpio1: gpio@
0209c000 {
-
compatible =
"fsl,imx6q-gpio",
"fsl,imx35-gpio";
-
reg = <
0x0209c000
0x4000>;
-
interrupts = <
0
66 IRQ_TYPE_LEVEL_HIGH>,
-
<
0
67 IRQ_TYPE_LEVEL_HIGH>;
-
gpio-controller;
-
#gpio-cells = <2>;
-
interrupt-controller;
-
#interrupt-cells = <2>;
-
};
-
-
/*imx6qdl-sabreauto.dtsi中某个设备节点*/
-
max7310_reset: max7310-reset {
-
compatible =
"gpio-reset";
-
reset-gpios = <&gpio1
15
1>;
-
reset-delay-us = <
1>;
-
#reset-cells = <0>;
-
};
xxx-gpios
(xxx我们可以随便起),这样驱动才能通过特定API从识别该属性,并转换成具体的gpio号reset-gpios = <&gpio1 15 1>;
这格式是什么意思呢?&gpio1 15
引用了gpio1节点,故此处含义为gpio1_15这个引脚;最后一个参数1则代表低电平有效,0则为高电平有效。至于gpio1_15具体对应哪个引脚,在imx6的手册上都有详细描述#gpio-cells = <2>;
,所以才有两个参数;某些soc的gpio节点中会设置为#gpio-cells = <1>;
,那么可以不写最后一个参数xxx-gpios
相同;最后一个是编号index,当节点中有n个同名的xxx-gpios时,可以通过它来获取特定的那个gpio,同一节点中gpio同名情况很少存在,所以我们都把index设为0gpio = of_get_named_gpio(node, "reset-gpios", index);
gpio = of_get_gpio(node, index);
假设某设备节点需要一个gpio中断
-
/*先确定中断所在的组*/
-
interrupt-
parent = <&gpio6>;
-
-
/*表示中断,GPIO6中的第8个IO,2为触发类型,下降沿触发*/
-
interrupts = <
8
2>;
中断号 =irq_of_parse_and_map(node, index)
函数返回值来得到中断号所谓的自定义属性,有点类似于老内核中的platform_data,我们在设备节点中可以随意添加自定义属性,比如下面这个节点里面的属性都是我们自己定义的
-
reg_3p3v: 3p3v {
-
compatible =
"regulator-fixed";
-
regulator-name =
"3P3V";
-
regulator-
min-microvolt = <
3300000>;
-
regulator-
max-microvolt = <
3300000>;
-
regulator-always-on;
-
};
of_property_read_u32(node, "regulator-min-microvolt", µvolt);
-
int of_property_read_u8(
const
struct device_node *np,
const
char *propname, u8 *out_value)
-
-
int of_property_read_u16(
const
struct device_node *np,
const
char *propname, u16 *out_value)
bool of_property_read_bool(const struct device_node *np, const char *propname)
of_property_read_string(node, "regulator-name", &string)
-
/*带有数组的某个节点*/
-
L2: cache-
controller@
1e00a000 {
-
compatible =
"arm,pl310-cache";
-
arm,
data-latency = <
1
1
1>;
-
arm,tag-latency = <
1
1
1>;
-
};
-
-
/*驱动中使用API来读取数组, &data为输出型参数*/
-
of_property_read_u32_array(node,
"arm,pl310-cache", &
data, ARRAY_SIZE(
data));