文章目录
系列文章:
Linux驱动开发——字符设备驱动开发
Linux驱动开发——LED驱动开发
Linux驱动开发——新字符设备驱动开发
1 什么是设备树?
设备树(Device Tree),将这个词分开就是“设备”和“树”,描述设备树的文件叫做 DTS(Device Tree Source),这个 DTS 文件采用树形结构描述板级设备,也就是开发板上的设备信息,比如CPU 数量、 内存基地址、IIC 接口上接了哪些设备、SPI 接口上接了哪些设备等等,如下图所示:
在以前的内核中,还没有采用设备树,内核源码中有大量的arch/arm/mach-xxx和arch/arm/plat-xxx文件夹,用于存储不同平台的板级信息,但随着芯片产业的发展,加入内核中的板级信息日益庞大,导致内核“虚胖”,所以引入了PowerPC等架构已经采用的设备树。将这些描述板级硬件信息的内容都从Linux中分离,用一个专属的文件格式类描述,这个专属的文件就叫做设备树,文件扩展名为.dts。一个SOC可以出很多不同的班子,不同的板子肯定有相同的部分信息,将这些相同信息提取到一个文件中,其他dts文件引用这个文件,避免重复定义,这个通用文件就是dtsi文件,有点类似于C语言中的头文件。
2 DTS、DTB和DTC
设备树源文件为dts文件,该文件编译之后,成为dtb文件,而编译dts文件的工具是DTC工具。
该工具是以源码的形式存在于内核源码中的,位于kernel/scripts/dtc目录下
可以使用dtc工具编译dts文件:
这里是Android系统内核,首先需要执行defconfig配置:
make ARCH=arm64 rockchip_defconfig android-11.config
然后编译
make ARCH=arm64 dtbs
输出如下:
CALL scripts/checksyscalls.sh
DTC arch/arm64/boot/dts/rockchip/px30-ad-d6-anx6345.dtb
DTC arch/arm64/boot/dts/rockchip/px30-ad-r35-mb-rk618-dual-lvds.dtb
DTC arch/arm64/boot/dts/rockchip/px30-ad-r35-mb-rk618-hdmi.dtb
DTC arch/arm64/boot/dts/rockchip/px30-ad-r35-mb-rk618-hdmi-lvds.dtb
DTC arch/arm64/boot/dts/rockchip/px30-ad-r35-mb-rk618-lvds.dtb
DTC arch/arm64/boot/dts/rockchip/px30-evb-ddr3-lvds-v10.dtb
DTC arch/arm64/boot/dts/rockchip/px30-evb-ddr3-v10.dtb
DTC arch/arm64/boot/dts/rockchip/px30-evb-ddr3-v10-avb.dtb
...
也可以单独编译某一个dts文件
make ARCH=arm64 rockchip/rk3568-atk-evb1-ddr4-v10.dtb
默认内核编译的脚本中会包含源码树文件的编译。
3 DTS语法
3.1 dtsi头文件
dtsi头文件类似于C语言中的头文件,包含一些公共的信息,可以在其他dts文件中导入
#include "rk3568-atk-evb1-ddr4-v10.dtsi"
#include "rk3568-linux.dtsi"
dts文件中除了可以引用dtsi文件,还可以引用.h头文件,甚至也可以引用dts文件。但是遵从编写习惯,还是将dtsi文件作为设备树的头文件,而不使用.h头文件。
一般.dtsi文件中描述的是SOC的内部外设信息,比如cpu架构、主频、外设寄存器地址范围等。
#include <dt-bindings/clock/rk3568-cru.h>
...
#include <dt-bindings/thermal/thermal.h>
#include "rk3568-dram-default-timing.dtsi"
/ {
compatible = "rockchip,rk3568";
interrupt-parent = <&gic>;
#address-cells = <2>;
#size-cells = <2>;
aliases {
csi2dphy0 = &csi2_dphy0;
csi2dphy1 = &csi2_dphy1;
csi2dphy2 = &csi2_dphy2;
dsi0 = &dsi0;
dsi1 = &dsi1;
ethernet0 = &gmac0;
ethernet1 = &gmac1;
gpio0 = &gpio0;
gpio1 = &gpio1;
gpio2 = &gpio2;
gpio3 = &gpio3;
gpio4 = &gpio4;
i2c0 = &i2c0;
...
mmc0 = &sdhci;
...
serial0 = &uart0;
...
spi0 = &spi0;
...
};
cpus {
#address-cells = <2>;
#size-cells = <0>;
cpu0: cpu@0 {
device_type = "cpu";
compatible = "arm,cortex-a55";
reg = <0x0 0x0>;
enable-method = "psci";
clocks = <&scmi_clk 0>;
operating-points-v2 = <&cpu0_opp_table>;
cpu-idle-states = <&CPU_SLEEP>;
#cooling-cells = <2>;
dynamic-power-coefficient = <187>;
};
cpu1: cpu@100 {
device_type = "cpu";
compatible = "arm,cortex-a55";
reg = <0x0 0x100>;
enable-method = "psci";
clocks = <&scmi_clk 0>;
operating-points-v2 = <&cpu0_opp_table>;
cpu-idle-states = <&CPU_SLEEP>;
};
cpu2: cpu@200 {
device_type = "cpu";
compatible = "arm,cortex-a55";
reg = <0x0 0x200>;
enable-method = "psci";
clocks = <&scmi_clk 0>;
operating-points-v2 = <&cpu0_opp_table>;
cpu-idle-states = <&CPU_SLEEP>;
};
cpu3: cpu@300 {
device_type = "cpu";
compatible = "arm,cortex-a55";
reg = <0x0 0x300>;
enable-method = "psci";
clocks = <&scmi_clk 0>;
operating-points-v2 = <&cpu0_opp_table>;
cpu-idle-states = <&CPU_SLEEP>;