设备树就像C语言一样,C语言有自己的语法特点,设备树也有自己的语法特点,接下来就来简单了解一些设备树语法。
#include
进行头文件包含。#include
#include "imx6ull.dtsi"
头文件包含有两种方式,一种是尖括号<>,代表在系统默认目录下进行寻找。一种是双引号"",代表在当前目录下进行寻找。
一般来说xxx.dtsi文件是用来描述CPU内部外设的一些基本信息,拿imx6ull.dtsi来举例
#include
#include
#include
#include "imx6ull-pinfunc.h"
#include "imx6ull-pinfunc-snvs.h"
#include "skeleton.dtsi"
/ {
aliases {
can0 = &flexcan1;
can1 = &flexcan2;
ethernet0 = &fec1;
ethernet1 = &fec2;
gpio0 = &gpio1;
gpio1 = &gpio2;
gpio2 = &gpio3;
gpio3 = &gpio4;
...........................
};
cpus {
#address-cells = <1>;
#size-cells = <0>;
cpu0: cpu@0 {
compatible = "arm,cortex-a7";
device_type = "cpu";
reg = <0>;
clock-latency = <61036>; /* two CLK32 periods */
operating-points = <
/* kHz uV */
996000 1275000
792000 1225000
528000 1175000
396000 1025000
198000 950000
>;
fsl,soc-operating-points = <
/* KHz uV */
996000 1175000
792000 1175000
528000 1175000
396000 1175000
198000 1175000
>;
fsl,low-power-run;
clocks = <&clks IMX6UL_CLK_ARM>,
<&clks IMX6UL_CLK_PLL2_BUS>,
<&clks IMX6UL_CLK_PLL2_PFD2>,
<&clks IMX6UL_CA7_SECONDARY_SEL>,
<&clks IMX6UL_CLK_STEP>,
<&clks IMX6UL_CLK_PLL1_SW>,
<&clks IMX6UL_CLK_PLL1_SYS>,
<&clks IMX6UL_PLL1_BYPASS>,
<&clks IMX6UL_CLK_PLL1>,
<&clks IMX6UL_PLL1_BYPASS_SRC>,
<&clks IMX6UL_CLK_OSC>;
clock-names = "arm", "pll2_bus", "pll2_pfd2_396m", "secondary_sel", "step",
"pll1_sw", "pll1_sys", "pll1_bypass", "pll1", "pll1_bypass_src", "osc";
};
};
intc: interrupt-controller@00a01000 {
compatible = "arm,cortex-a7-gic";
#interrupt-cells = <3>;
interrupt-controller;
reg = <0x00a01000 0x1000>,
<0x00a02000 0x100>;
};
clocks {
#address-cells = <1>;
#size-cells = <0>;
ckil: clock@0 {
compatible = "fixed-clock";
reg = <0>;
#clock-cells = <0>;
clock-frequency = <32768>;
clock-output-names = "ckil";
};
osc: clock@1 {
compatible = "fixed-clock";
reg = <1>;
#clock-cells = <0>;
clock-frequency = <24000000>;
clock-output-names = "osc";
};
ipp_di0: clock@2 {
compatible = "fixed-clock";
reg = <2>;
#clock-cells = <0>;
clock-frequency = <0>;
clock-output-names = "ipp_di0";
};
ipp_di1: clock@3 {
compatible = "fixed-clock";
reg = <3>;
#clock-cells = <0>;
clock-frequency = <0>;
clock-output-names = "ipp_di1";
};
};
soc {
#address-cells = <1>;
#size-cells = <1>;
compatible = "simple-bus";
interrupt-parent = <&gpc>;
ranges;
busfreq {
compatible = "fsl,imx_busfreq";
clocks = <&clks IMX6UL_CLK_PLL2_PFD2>, <&clks IMX6UL_CLK_PLL2_198M>,
<&clks IMX6UL_CLK_PLL2_BUS>, <&clks IMX6UL_CLK_ARM>,
<&clks IMX6UL_CLK_PLL3_USB_OTG>, <&clks IMX6UL_CLK_PERIPH>,
<&clks IMX6UL_CLK_PERIPH_PRE>, <&clks IMX6UL_CLK_PERIPH_CLK2>,
<&clks IMX6UL_CLK_PERIPH_CLK2_SEL>, <&clks IMX6UL_CLK_OSC>,
<&clks IMX6UL_CLK_AHB>, <&clks IMX6UL_CLK_AXI>,
<&clks IMX6UL_CLK_PERIPH2>, <&clks IMX6UL_CLK_PERIPH2_PRE>,
<&clks IMX6UL_CLK_PERIPH2_CLK2>, <&clks IMX6UL_CLK_PERIPH2_CLK2_SEL>,
<&clks IMX6UL_CLK_STEP>, <&clks IMX6UL_CLK_MMDC_P0_FAST>, <&clks IMX6UL_PLL1_BYPASS_SRC>,
<&clks IMX6UL_PLL1_BYPASS>, <&clks IMX6UL_CLK_PLL1_SYS>, <&clks IMX6UL_CLK_PLL1_SW>,
<&clks IMX6UL_CLK_PLL1>;
clock-names = "pll2_pfd2_396m", "pll2_198m", "pll2_bus", "arm", "pll3_usb_otg",
"periph", "periph_pre", "periph_clk2", "periph_clk2_sel", "osc",
"ahb", "ocram", "periph2", "periph2_pre", "periph2_clk2", "periph2_clk2_sel",
"step", "mmdc", "pll1_bypass_src", "pll1_bypass", "pll1_sys", "pll1_sw", "pll1";
fsl,max_ddr_freq = <400000000>;
};
pmu {
compatible = "arm,cortex-a7-pmu";
interrupts = <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>;
status = "disabled";
};
ocrams: sram@00900000 {
compatible = "fsl,lpm-sram";
reg = <0x00900000 0x4000>;
};
ocrams_ddr: sram@00904000 {
compatible = "fsl,ddr-lpm-sram";
reg = <0x00904000 0x1000>;
};
ocram: sram@00905000 {
compatible = "mmio-sram";
reg = <0x00905000 0x1B000>;
};
dma_apbh: dma-apbh@01804000 {
compatible = "fsl,imx6ul-dma-apbh", "fsl,imx28-dma-apbh";
reg = <0x01804000 0x2000>;
interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "gpmi0", "gpmi1", "gpmi2", "gpmi3";
#dma-cells = <1>;
dma-channels = <4>;
clocks = <&clks IMX6UL_CLK_APBHDMA>;
};
gpmi: gpmi-nand@01806000{
compatible = "fsl,imx6ull-gpmi-nand", "fsl, imx6ul-gpmi-nand";
#address-cells = <1>;
#size-cells = <1>;
reg = <0x01806000 0x2000>, <0x01808000 0x4000>;
reg-names = "gpmi-nand", "bch";
interrupts = <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
interrupt-names = "bch";
clocks = <&clks IMX6UL_CLK_GPMI_IO>,
<&clks IMX6UL_CLK_GPMI_APB>,
<&clks IMX6UL_CLK_GPMI_BCH>,
<&clks IMX6UL_CLK_GPMI_BCH_APB>,
<&clks IMX6UL_CLK_PER_BCH>;
clock-names = "gpmi_io", "gpmi_apb", "gpmi_bch",
"gpmi_bch_apb", "per1_bch";
dmas = <&dma_apbh 0>;
dma-names = "rx-tx";
status = "disabled";
};
..............................
tsc: tsc@02040000 {
compatible = "fsl,imx6ul-tsc";
reg = <0x02040000 0x4000>, <0x0219c000 0x4000>;
interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_IPG>,
<&clks IMX6UL_CLK_ADC2>;
clock-names = "tsc", "adc";
status = "disabled";
};
pwm1: pwm@02080000 {
compatible = "fsl,imx6ul-pwm", "fsl,imx27-pwm";
reg = <0x02080000 0x4000>;
interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_PWM1>,
<&clks IMX6UL_CLK_PWM1>;
clock-names = "ipg", "per";
#pwm-cells = <2>;
};
pwm2: pwm@02084000 {
compatible = "fsl,imx6ul-pwm", "fsl,imx27-pwm";
reg = <0x02084000 0x4000>;
interrupts = <GIC_SPI 84 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_DUMMY>,
<&clks IMX6UL_CLK_DUMMY>;
clock-names = "ipg", "per";
#pwm-cells = <2>;
};
pwm3: pwm@02088000 {
compatible = "fsl,imx6ul-pwm", "fsl,imx27-pwm";
reg = <0x02088000 0x4000>;
interrupts = <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_PWM3>,
<&clks IMX6UL_CLK_PWM3>;
clock-names = "ipg", "per";
#pwm-cells = <2>;
};
pwm4: pwm@0208c000 {
compatible = "fsl,imx6ul-pwm", "fsl,imx27-pwm";
reg = <0x0208c000 0x4000>;
interrupts = <GIC_SPI 86 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_DUMMY>,
<&clks IMX6UL_CLK_DUMMY>;
clock-names = "ipg", "per";
#pwm-cells = <2>;
};
比如上述设备树的头文件中有描述了中断控制器、时钟、PWM、DMA等各种内部外设的基本信息。
2、设备树的设备节点
设备树是采用设备节点的方式来进行板级信息的描述,一个外设可以通过若干个设备节点进行描述。
2.1、设备节点的命名
设备节点的命名有两种方式,第一种是node-name@unit-address
,节点名字@设备地址,比如一个描述PWM设备的节点可以命名为PWM0@20000000
,意思是设备PWM0的地址为0x20000000。第二种方式为label: node-name@unit-address
,标签名:节点名字@设备地址,这种方式跟第一种相比,在节点名字前面多了一个标签,并用冒号隔开。使用第二种命方式的好处是可以通过标签名直接访问节点并修改节点属性。使用第二种方式描述一个节点可以是pwm0:PWM0@20000000
。无论使用哪种方式命名,设备节点都应该简单易懂,让使用者能通过设备节点知道该设备的具体功能。
2.2、设备节点的属性
一个设备由很多属性,将这些属性组合起来就可以描述一个具体的设备。
2.2.1、根节点
根节点是整个设备树的起始,一个设备有且只有一个根节点,根节点的表示形式为/
。
imx6ull.dtsi文件
imx6ull-14x14-evk.dts
上面分别是一个dtsi和dtc的代码截屏,可以看到imx6ull-14x14-evk.dts包含了imx6ull.dtsi。这时就会出现了一个问题,两个文件里面都有根节点,在头文件被展开后,imx6ull-14x14-evk.dts中就会出现两个根节点,这难道不会编译报错吗。其实并不会报错,因为两个根节点在编译的时候,会被合并到一起。
2.2.2 、compatible属性
compatible也叫兼容属性,这是一个非常重要的属性,设备与驱动的匹配、系统的启动都跟这个属性相关。
根节点的compatible属性
在没有设备树之前,内核的启动是要通过uboot传递的一个叫machine id的变量给内核,内核将这个machine id进行匹配,如果匹配成功就启动,否则就不启动
MACHINE_START(SMDKV210, "SMDKV210")
/* Maintainer: Kukjin Kim */
.phys_io = S3C_PA_UART & 0xfff00000,
.io_pg_offst = (((u32)S3C_VA_UART) >> 18) & 0xfffc,
.boot_params = S5P_PA_SDRAM + 0x100,
.init_irq = s5pv210_init_irq,
.map_io = smdkv210_map_io,
.init_machine = smdkv210_machine_init,
#ifdef CONFIG_S5P_HIGH_RES_TIMERS
.timer = &s5p_systimer,
#else
.timer = &s3c24xx_timer,
#endif
MACHINE_END
#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结构体中的nr成员变量为内核的机器代码,而在上面的代码中,nr为MACH_TYPE_SMDKV210
,在mach-types.h文件中可以找到MACH_TYPE_SMDKV210宏定义对应的机器码为2456.
在使用设备树的情况下,机器码被compatible属性代替
static const char *imx6ul_dt_compat[] __initconst = {
"fsl,imx6ul",
"fsl,imx6ull",
NULL,
};
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
上面的代码是imx6ull的设备树的设备结构体,可以看到有一个叫dt_compat的成员变量,这个成员变量被赋值为imx6ul_dt_compat,而imx6ul_dt_compat里面就有两个属性,一个是"fsl,imx6ul",另外一个是"fsl,imx6ull"。这两个属性正好跟前面讲的imx6ull-14x14-evk.dts文件中的compatible属性相匹配。
mytestled: atkled{
compatible = "fsl,imx6ull atkled";
#address-cells = <1>;
#size-cells = <1>;
reg = < 0x20E02F4 0x04 // SW_PAD_CTL_PAD_GPIO1_IO03
0x20E0068 0x04 // SW_MUX_CTL_PAD_GPIO1_IO03
0x209C000 0x4000 // GPIO1
>;
status = "okay";
};
驱动端
static const struct of_device_id led_of_match[] = {
{.compatible = "fsl,imx6ull atkled",},
{},
};
MODULE_DEVICE_TABLE(of, led_of_match);
static struct platform_driver led_driver = {
.probe = led_probe,
.remove = led_remove,
.driver = {
.name = "led_dtb",
.of_match_table = led_of_match,
},
};
设备端的compatible和驱动端的compatible属性相同,当驱动被加载进内核时,相关的probe函数就会被执行。
2.2.4 status属性
status属性用来描述设备的状态,其状态可以是下列的值
当设备状态为"okay"时,设备表示可用,驱动可以正常获取设备信息。
当设备状态为"disable"时,设备表示不可用,驱动不能正常获取设备信息。
spi4 {
compatible = "spi-gpio";
#address-cells = <1>;
#size-cells = <0>;
gpio_spi: gpio_spi@0 {
compatible = "fairchild,74hc595";
reg = <0>;
};
};
aips3: aips-bus@02200000 {
compatible = "fsl,aips-bus", "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
dcp: dcp@02280000 {
compatible = "fsl,imx6sl-dcp";
reg = <0x02280000 0x4000>;
};
};
在上面代码中,spi4节点的#address-cells为1,#size-cells为0,代表子节点的reg属性地址信息为1个32位的字节,子节点的reg属性长度为0。所以在第8行中spi4的子节点reg属性为reg = <0>
,通常表示spi设备的地址。
而aips3节点的#address-cells为1,#size-cells为1,代表子节点的reg属性地址信息为1个32位的字节,子节点的reg属性长度为1。所以在第19行中aips3的子节点reg属性为reg = <0x02280000 0x4000>
,代表dcp设备的起始地址为0x02280000 ,长度为0x4000。
通常#address-cells和#size-cells两个属性组合起来可以代表设备的起始地址和长度。
gpio4: gpio@020a8000 {
compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
reg = <0x020a8000 0x4000>;
interrupts = <GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
};
比如gpio4这个节点的reg属性为reg = <0x020a8000 0x4000>
,就代表gpio4这个设备的起始地址为0x020a8000,地址长度为0x4000。
&i2c2 {
clock_frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c2>;
status = "okay";
ft5426:ft5426@38{
compatible = "edt,edt-ft5426";
reg = <0x38>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_tsc
&pinctrl_tsc_reset >;
interrupt-parent = <&gpio1>;
interrupts = <9 0>;
rst_gpio = <&gpio5 9 GPIO_ACTIVE_LOW>;
interrupt_gpio = <&gpio1 9 GPIO_ACTIVE_LOW>;
};
}
比如在i2c2中有一个子节点名字为ft5426,其reg属性为reg = <0x38>
,代表ft5426的i2c地址为0x38。
直接修改
i2c1: i2c@021a0000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";
reg = <0x021a0000 0x4000>;
interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_I2C1>;
status = "okay";
// 添加子节点
at24c04:at24c04@038{
compatible = "fsl,at24c04";
reg = <0x38>;
status = "okay";
};
};
通过标号修改
i2c1: i2c@021a0000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";
reg = <0x021a0000 0x4000>;
interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6UL_CLK_I2C1>;
status = "okay";
};
&i2c1{
// 添加子节点
at24c04:at24c04@038{
compatible = "fsl,at24c04";
reg = <0x38>;
status = "okay";
};
};