Linux 设备树(三) 设备树语法

设备树就像C语言一样,C语言有自己的语法特点,设备树也有自己的语法特点,接下来就来简单了解一些设备树语法。

  • 1、头文件包含
    设备树跟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文件
    Linux 设备树(三) 设备树语法_第1张图片
    imx6ull-14x14-evk.dts
    Linux 设备树(三) 设备树语法_第2张图片
    上面分别是一个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.
Linux 设备树(三) 设备树语法_第3张图片
在使用设备树的情况下,机器码被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属性相匹配。

  • 设备节点compatible属性
    设备节点的compatible属性主要用来跟驱动进行匹配。当设备的compatible与驱动的compatible的名字相匹配的时候,驱动程序就会被执行,从而从设备树中获取相应的设备属性。
    设备端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.3 、model属性
    model属性可以用来描述设备模块的信息,通常为字符串。
    在这里插入图片描述

2.2.4 status属性
status属性用来描述设备的状态,其状态可以是下列的值
Linux 设备树(三) 设备树语法_第4张图片
当设备状态为"okay"时,设备表示可用,驱动可以正常获取设备信息。
当设备状态为"disable"时,设备表示不可用,驱动不能正常获取设备信息。

  • 2.2.5 、#address-cells 和#size-cells 属性
    在32位系统中,#address-cells 和#size-cells 属性通常为4字节32位,#address-cells 和#size-cells 属性可以用在任何的子设备节点中。#address-cells描述了子节点中reg属性的地址信息,#size-cells描述了子节点中reg属性的长度信息。
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两个属性组合起来可以代表设备的起始地址和长度。

  • 2.2.6、reg属性
    reg属性一般用来描述设备的地址空间资源情况或者设备的地址信息。
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。

  • 3、修改设备节点
    修改设备树节点有两种方法,一种是直接修改,一种是通过标签进行修改

直接修改

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";
		
			};

};

你可能感兴趣的:(linux)