Linux驱动开发笔记(五)——设备树(中)——节点的标准属性

发布于:2025-07-29 ⋅ 阅读:(19) ⋅ 点赞:(0)

一、标准属性

视频:第6.6讲 Linux设备树详解-设备树中的标准属性_哔哩哔哩_bilibili

对应《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.81.pdf》43.3.3部分

英文资料可见04、参考资料/Power_ePAPR_APPROVED_v1.12.pdf的2.3部分

1.1 compatible

        每一个具体的设备节点下都有compatible属性,其内容是字符串类型,用于描述设备的兼容性。

        格式为:

"manufacturer,model" 

        其中manufacturer为厂商,model一般是模块对应的驱动名字。

        一般驱动程序文件都会有一个OF兼容性列表,保存着可以兼容的属性,如果其他设备节点的compatible值和OF列表中有相同的字符串,就表示设备可以使用这个驱动。

1.2 model

        也是一个字符串,描述模块信息、名字等。

1.3 status

        也是字符串,描述设备的状态信息

        一般在头文件dtsi文件中都是默认disabled,使用时在dts中追加修改为okay。

1.4 #address-cells 和  #size-cells 和 reg

       #address-cells:32位unsigned int,决定了子节点reg属性中地址信息所占用的字长(32位),

       #size-cells:32位unsigned int,决定了子节点reg属性中长度信息所占的字长(32位)。

       reg属性一般都是和地址有关的内容,reg属性的格式为:

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

        比如imx6ull.dtsi中:

// 837行:
aips1: aips-bus@02000000 {    // 首地址为0x02000000
			compatible = "fsl,aips-bus", "simple-bus";
			#address-cells = <1>;
			#size-cells = <1>;
			reg = <0x02000000 0x100000>;    // 首地址为0x02000000,长度为0x100000
			ranges;

    …………
// 939行:
	i2c1: i2c@021a0000 {    // 首地址为021a0000
		#address-cells = <1>;
		#size-cells = <0>;
		compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";
		reg = <0x021a0000 0x4000>;    // 首地址为0x021a0000,长度为0x4000
		interrupts = <GIC_SPI 36 IRQ_TYPE_LEVEL_HIGH>;
		clocks = <&clks IMX6UL_CLK_I2C1>;
		status = "disabled";
	};

};

        aips1中的address-cells和size-cells规定的是它的子节点,也就是i2c1的reg属性,而不是aips1自己的reg属性,表示i2c1中的reg。                                                                                                  同理,i2c1的address-cells和size-cells规定的是i2c1的子节点reg属性,而不是i2c1自己的reg属性,其addres=0,表示没有length的值,相当于设置了起始地址,而没有设置地址长度。

       如果父节点中#address-cells = <2>,#size-cells = <1>,然后子节点的reg = <a b c d e f>(这里用字母代指),那么<a b>为地址,c为地址长度,<d e>为地址,f为长度,依然是地址、长度交替。

        那么这里i2c1并没有子节点,为什么要写#address、#size呢?因为还有&,在imx6ull-alientek-emmc.dts中搜索&i2c1就可以找到:

&i2c1 {
    …………
	mag3110@0e {
		reg = <0x0e>;
        …………
	};

	fxls8471@1e {
		reg = <0x1e>;
        …………
	};
};

1.5 rag

        合并到1.4一块写了。

1.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属性值为空值,说明子地址空间和父地址空间完全相同,不需要进行地址转换。

1.7 name

        name属性用于指定节点名称,字符串类型。

        此属性已弃用,自己写设备树的时候可以不加name,但是老版本的代码还是能看到这个东西。

1.8 device_type

        device_type属性在IEEE 1275中用于描述设备的FCode编程模型。由于ePAPR没有FCode,因此该属性的已被弃用。为了与IEEE 1275衍生的设备树兼容,只在cpu和memory节点上能看到这个东西。

1.9 根节点的 compatible

第6.7讲 Linux设备树详解-根节点下的compatible属性作用_哔哩哔哩_bilibili

        1.1中提到每一个具体的设备节点下都有compatible属性,用来找驱动。但是根节点下也能见到compatible属性,用于判断内核是否支持这个设备。

        内核只能在指定的板子上运行。Linux内核启动时会通过根节点的compoatible属性查看是否支持此设备,如果支持的话设备就会启动Linux内核。

        1.9.1 没有设备树

        (这部分不重要,直接看有设备树的部分就行)

       在那个没有设备树的年代,uboot会向Linux内核传递一个叫做machine id的值,也就是设备ID。内核针对每个支持的设备,都用MACHINE_START和MACHINE_END来定义一个machine_desc结构体来描述这个设备。

        for example,在/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/arch/arm/mach-imx/mach-mx35_3ds.c中能看到:

MACHINE_START(MX35_3DS, "Freescale MX35PDK")
	/* Maintainer: Freescale Semiconductor, Inc */
	.atag_offset = 0x100,
    …………
MACHINE_END

        其中 MACHINE_START 和 MACHINE_END 定义在linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/arch/arm/include/asm/mach/arch.h:

#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")代入可得:

#define MACHINE_START(_type,_name)            \
static const struct machine_desc __mach_desc_MX35_3DS __used
 __attribute__((__section__(".arch.info.init"))) = {
    .nr        = MACH_TYPE_MX35_3DS,     // 替换_type   这个就是machine id
    .name        = "Freescale MX35PDK",  // 替换_name
	.atag_offset = 0x100,
    …………
};

        MACH_TYPE_MX35_3DS在哪里定义?在/home/for/linux/imx6ull/linux/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/include/generated/mach-types.h中可以找到,里面罗列了所有内核支持的设备,有匹配项则表示内核可以在该设备上运行:

        1.9.2 有设备树

        linus说要有设备树,于是便有了设备树。有了设备树以后就不再使用machine id了,而是根节点下的compatible。

// 定义于linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/arch/arm/mach-imx/mach-imx6ul.c
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

和MACHINE_START相比,DT_MACHINE_START只是修改了.nr这一行,表示不再使用.nr:

// 定义于linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/arch/arm /include/asm/mach/arch.h
#define DT_MACHINE_START(_name, _namestr)		\
static const struct machine_desc __mach_desc_##_name	\
 __used							\
 __attribute__((__section__(".arch.info.init"))) = {	\
	.nr		= ~0,				\
	.name		= _namestr,

arch/arm/mach-imx/mach-imx6ul.c中可以看到:

static const char *imx6ul_dt_compat[] __initconst = {
	"fsl,imx6ul",
	"fsl,imx6ull",
	NULL,
};

        说明该内核支持"fsl,imx6ul"、"fsl,imx6ull",现在看看arch/arm/boot/dts/imx6ull-alientek-emmc.dts的compatible:

        非常的巧啊,这个内核能在imx6ull上跑起来。如果你改掉这个compatible,再编译为dtb,再cp到tftproot下,重启板子,就会发现板子卡在starting kernel了。


网站公告

今日签到

点亮在社区的每一天
去签到