Linux驱动开发笔记(五)——设备树(中)——节点的标准属性
一、标准属性
视频:第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 { // 首地址为0x02000000compatible = "fsl,aips-bus", "simple-bus";#address-cells = <1>;#size-cells = <1>;reg = <0x02000000 0x100000>; // 首地址为0x02000000,长度为0x100000ranges;…………
// 939行:i2c1: i2c@021a0000 { // 首地址为021a0000#address-cells = <1>;#size-cells = <0>;compatible = "fsl,imx6ul-i2c", "fsl,imx21-i2c";reg = <0x021a0000 0x4000>; // 首地址为0x021a0000,长度为0x4000interrupts = <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了。