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
相关推荐
超级大只老咪4 小时前
快速进制转换
笔记·算法
嵩山小老虎5 小时前
Windows 10/11 安装 WSL2 并配置 VSCode 开发环境(C 语言 / Linux API 适用)
linux·windows·vscode
Fleshy数模5 小时前
CentOS7 安装配置 MySQL5.7 完整教程(本地虚拟机学习版)
linux·mysql·centos
a41324475 小时前
ubuntu 25 安装vllm
linux·服务器·ubuntu·vllm
Fᴏʀ ʏ꯭ᴏ꯭ᴜ꯭.7 小时前
Keepalived VIP迁移邮件告警配置指南
运维·服务器·笔记
一只自律的鸡7 小时前
【Linux驱动】bug处理 ens33找不到IP
linux·运维·bug
17(无规则自律)7 小时前
【CSAPP 读书笔记】第二章:信息的表示和处理
linux·嵌入式硬件·考研·高考
!chen7 小时前
linux服务器静默安装Oracle26ai
linux·运维·服务器
ling___xi7 小时前
《计算机网络》计网3小时期末速成课各版本教程都可用谢稀仁湖科大版都可用_哔哩哔哩_bilibili(笔记)
网络·笔记·计算机网络
REDcker8 小时前
Linux 文件描述符与 Socket 选项操作详解
linux·运维·网络