Linux驱动开发笔记(五)——设备树(上)

内容详见《【正点原子】I.MX6U嵌入式Linux驱动开发指南》四十三章

开发板:imx6ull mini

虚拟机:VMware17

ubuntu:ubuntu20.04

一、什么是设备树

视频:第6.1讲 Linux设备树详解-什么是设备树?_哔哩哔哩_bilibili

对应《指南》43.1部分

uboot启动时需要内核zImage 和 .dtb文件,其中dtb便是由设备树文件dts(Device Tree Source)转换而来。dts文件用树形结构来描述板级信息,即开发板上的设备信息,如CPU数量、 内存基地址、IIC接了哪些设备等等。

bootcmd命令中,80800000就是zImage在RAM中的存放起始地址,83000000就是设备树在RAM中的存储地址。

复制代码
tftp 80800000 zImage;tftp 83000000 imx6ull-alientek-emmc.dtb;bootz 80800000 - 83000000

没有设备树以前,设备的板级信息等都在.c文件中进行配置,最后会被编码到内核里面,又臭又长。因此引入了设备树,用一个专用的文件格式.dts来描述板级信息,将这些玩意与linux分离。

如果不同的板子有相同的信息,就可以将这部分提取出来作为一个通用文件.dtsi,其他的.dts文件直接引用就可以。

一般.dts描述板级信息,.dtsi描述SOC级信息。

二、DTS文件

视频:第6.2讲 Linux设备树详解-DTS文件以及组织形式_哔哩哔哩_bilibili

对应《指南》43.2~3部分

2.1 编译为dtb

命令:

bash 复制代码
# 进入到Linux源码根目录下
make all     # 全部编译,包括zImage、.ko文件等等
make dtbs    # 将当前内核里所有.dts文件编译为.dtb
make XXX.dtb # 将指定的dts文件编译为dtb

示例:

bash 复制代码
cd ............/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/
make dtbs
cd arch/arm/boot/dts/
ls *.dtb     # 此时应当可以看到一大堆的dts文件

2.2 dts语法

打开两个文件:linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek/arch/arm/boot/dts下的imx6ull-alientek-emmc.dts和imx6ull.dtsi。

打开两个文档:(4、参考资料)中的Devicetree SpecificationV0.2.pdf和Power_ePAPR_APPROVED_v1.12.pdf

/dts-v1/;

首先在imx6ull-alientek-emmc.dts开头可以看到:

cpp 复制代码
/dts-v1/;

(解释在power_ePAPR_APPROVED_v1.12.pdf的96页:)

The /dts-v1/; shall be present to identify the file as a version 1 DTS (dts files without this tag will be treated by dtc as being in the obsolete version 0, which uses a different format for integers in addition to other small but incompatible changes).

/dts-v1/; 必须写上,将文件标识为版本1 DTS(没有此标签的DTS文件将被dtc视为过时的版本0,而旧格式在整数的表示方式等方面与版本1不兼容)

节点命名规则

(Power_ePAPR_APPROVED_v1.12.pdf 15页)

《指南》43.3.2部分

节点名完整写法为:node-name@unit-address。其中node-name为设备名,unit-address一般是外设寄存器的起始地址,但也不一定,比如下面的例子。需要具体分析。

cpp 复制代码
// imx6ull.dtsi 1096行
i2c4: i2c@021f8000 {    // i2c为name,021f8000为i2c4寄存器起始绝对地址
    ............
};

// imx6ull-alientek-emmc.dts 245行
&i2c1 {
	mag3110@0e {    
        // 这里mag3110并不是IMX6ULL的外设,而是一个IIC外设,
        // 因此0e也不是外设寄存器起始地址,而是一个IIC地址
        ............
	};
};

// imx6ull.dtsi 94行
intc: interrupt-controller@00a01000 {  
    //↑这里有个冒号,冒号前面是一个标签label,后面才是节点名
    // 这样可以通过 &label 来访问该节点,如&intc就可以访问interrupt-controller@00a01000节点
    // 比如上面的&i2c1,就可以在imx6ull.dtsi 939行找到"i2c1: i2c@021a0000"
    // &定义的节点内容如果没有则追加,如果已存在则替换,比如&i2c1里的内容就会替换原本i2c1: i2c@021a0000的内容
    ............
};

层级结构

Power_ePAPR_APPROVED_v1.12.pdf 14页

"/"表示设备树文件的根节点,每个设备树文件只有一个根节点。

imx6ull.dtsi和imx6ull-alientek-emmc.dts这两个文件都有一个"/"根节点,这两个"/"根节点的内容会合并成一个根节点。

2.3 设备树在系统中的体现

第6.4讲 Linux设备树详解-设备树在根文件系统中的体现以及添加自定义节点_哔哩哔哩_bilibili

系统启动后可以在根文件系统里看到设备树的节点信息。

内核启动时会解析设备树文件,并在/proc/device-tree目录下生成相应的设备树节点文件,存放设备树信息。cd到该路径下使用ls命令,可以看到很到诸如chosen、memory、reserved-memory、backlight等文件。

这与imx6ull-alientek-emmc.dts中的结构一致:

cat查看model的内容:

这与imx6ull-alientek-emmc.dts中model的属性一致:

cd到soc/aips-bus@02100000/i2c@021a0000下,可以看到这些文件:

但是imx6ull.dtsi的939行下只能看到:

clock-frequency、fxls8471@1e、mag3110@0e、name、printrl-0、printrl-names是从哪来的?用&追加的。在imx6ull-alientek-emmc.dts的245行可以找到:

其中还修改了status,使用cat status可以看到status被覆盖为了okay。

2.4 其他特殊节点

第6.5讲 Linux设备树详解-设备树特殊节点_哔哩哔哩_bilibili

《指南》43.6部分

2.4.1 aliases

在imx6ull.dtsi的开头可以看到:

bash 复制代码
/ {
	aliases {
		can0 = &flexcan1;
		can1 = &flexcan2;
        ............
    };
};

同时,/soc/下定义了flexcan1:

bash 复制代码
/ {
    soc {
        flexcan1: can@02090000{
            ......
        }
        ......
    };
    ......
}; 

那么,can0 = &flexcan1; 等价于 can0 = &{/soc/can@02090000}; 或 can0 = "/soc/can@02090000";

aliases节点的主要功能就是定义别名,以方便访问节点。不过一般会在节点命名的时候会加上label,然后通过&label来访问节点。

2.4.2 chosen

主要目的是将uboot里的bootargs环境变量的值传给linux内核作为命令行参数cmd line

在串口cd /proc/device-tree/chosen,可以看到bootargs、name、stdout-path三个。

但是imx6ull-alientek-emmc.dts中的chosen节点只定义了stdout-path一个:

cpp 复制代码
	chosen {
		stdout-path = &uart1;
	};

所以bootargs是从来的?(name先不管)当执行bootcmd中的bootz 80800000 - 83000000这句时,bootz层层调用,最终fdt_chosen函数读取bootargs的值并传给/chosen节点,供内核使用:

在alientek_uboot/common/fdt_common下可以找到fdt_chosen:

cpp 复制代码
int fdt_chosen(void *fdt){
	int   nodeoffset;
	int   err;
	char  *str;		/* used to set string properties */

	err = fdt_check_header(fdt);
	if (err < 0) {
		printf("fdt_chosen: %s\n", fdt_strerror(err));
		return err;
	}

	/* find or create "/chosen" node.  如果存在子节点/chosen,则返回其偏移nodeoffset,否则创建并返回nodeoffset*/
	nodeoffset = fdt_find_or_add_subnode(fdt, 0, "chosen");  // fdt为指向设备树文件的指针,0表示根节点, chosen为要查找的子节点名称
	if (nodeoffset < 0)	// 失败
		return nodeoffset;

	str = getenv("bootargs");  // 从uboot的环境变量中读取bootargs的值
	if (str) {  // 如果bootargs不为空
		err = fdt_setprop(fdt, nodeoffset, "bootargs", str,  // 通过fdt_setprop向/chosen节点添加或修改bootargs属性
				  strlen(str) + 1);
		if (err < 0) {  // 失败
			printf("WARNING: could not set bootargs %s.\n",
			       fdt_strerror(err));
			return err;
		}
	}

	return fdt_fixup_stdout(fdt, nodeoffset);
}

2.4 修改节点

2.4.1添加自定义节点

为方便查看,直接在imx6ull-alientek-emmc.dts的根节点下添加自定义节点:

cpp 复制代码
	// 自定义节点。添加到imx6ull-alientek-emmc.dts的148行
	mytestnode: mytest@0101 {

	};
bash 复制代码
# VSCODE终端
cd .../linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek  # 到内核根目录下
make dtbs    # 编译
cp arch/arm/boot/dts/imx6ull-alientek-emmc.dtb .../tftpboot/ -f  # 复制到自己存放zImage和dtb文件的目录下,如果忘了哪个路径就用vi /etc/default/tftpd-hpa命令看看

# 串口
reboot    # 重启开发板

ls /proc/device-tree/   # 此时应当能看到mytest@0101

(打错字了↓)

2.4.2 使用&进行追加

在imx6ull-alientek-emmc.dts文件最末尾添加:

cpp 复制代码
&intc{
	mytestnode{

	};
};
// 追加给imx6ull.dtsi 94行的intc
bash 复制代码
# VSCODE终端
cd .../linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek  # 到内核根目录下
make dtbs    # 编译
cp arch/arm/boot/dts/imx6ull-alientek-emmc.dtb .../tftpboot/ -f  # 复制到自己存放zImage和dtb文件的目录下,如果忘了哪个路径就用vi /etc/default/tftpd-hpa命令看看

# 串口
reboot    # 重启开发板

cd /proc/device-tree/interrupt-controller@00a01000/
ls        # 此时能看到mytestnode
相关推荐
帽儿山的枪手23 分钟前
HVV期间,如何使用SSH隧道绕过内外网隔离限制?
linux·网络协议·安全
charlie1145141911 小时前
设计自己的小传输协议 导论与概念
c++·笔记·qt·网络协议·设计·通信协议
邹诗钰-电子信息工程1 小时前
嵌入式基础知识复习(C语言)
linux·c语言·vim
瀚高PG实验室2 小时前
CentOS 8 安装HGDB V4.5 psql命令执行报错
linux·运维·centos·瀚高数据库
小醉你真好2 小时前
6、CentOS 9 安装 Docker
linux·docker·centos
xiaoli23272 小时前
课题学习笔记3——SBERT
笔记·学习·nlp·bert
平生不喜凡桃李4 小时前
Linux 线程概念与控制
java·linux·运维
獭.獭.4 小时前
Linux -- 文件【中】
linux
Magnetic_h4 小时前
【iOS】类和分类的加载过程
笔记·学习·ios·objective-c·xcode
无敌的牛5 小时前
Linux重定向的理解
linux·运维·服务器