00. 目录
文章目录
01. 概述
Linux3.x以后的版本才引入了设备树,设备树用于描述一个硬件平台的板级细节。 后面我们写的驱动需要依赖设备树,所以在这里先演示如何编译设备树、加载设备树。
具体原理请参考Linux设备树相关博客。
02. 设备树编译
2.1 使用内核中的dtc工具编译
首先我们需要编译好内核(通常只需一次编译好内核,编译内核的时候会生成的dtc工具),内核编译的位置在 kernel
目录中 ,内核中的dtc工具位置在 kernel/scripts/dtc/dtc
。
dtc工具用法如下
deng@local:~/code/x3399$ ./kernel/scripts/dtc/dtc --help
Usage: dtc [options] <input file>
Options: -[qI:O:o:V:d:R:S:p:a:fb:i:H:sW:E:@Ahv]
-q, --quiet
Quiet: -q suppress warnings, -qq errors, -qqq all
-I, --in-format <arg>
Input formats are:
dts - device tree source text
dtb - device tree blob
fs - /proc/device-tree style directory
-o, --out <arg>
Output file
-O, --out-format <arg>
Output formats are:
dts - device tree source text
dtb - device tree blob
asm - assembler source
-V, --out-version <arg>
Blob version to produce, defaults to 17 (for dtb and asm output)
-d, --out-dependency <arg>
Output dependency file
-R, --reserve <arg>
Make space for <number> reserve map entries (for dtb and asm output)
-S, --space <arg>
Make the blob at least <bytes> long (extra space)
-p, --pad <arg>
Add padding to the blob of <bytes> long (extra space)
-a, --align <arg>
Make the blob align to the <bytes> (extra space)
-b, --boot-cpu <arg>
Set the physical boot cpu
-f, --force
Try to produce output even if the input tree has errors
-i, --include <arg>
Add a path to search for include files
-s, --sort
Sort nodes and properties before outputting (useful for comparing trees)
-H, --phandle <arg>
Valid phandle formats are:
legacy - "linux,phandle" properties only
epapr - "phandle" properties only
both - Both "linux,phandle" and "phandle" properties
-W, --warning <arg>
Enable/disable warnings (prefix with "no-")
-E, --error <arg>
Enable/disable errors (prefix with "no-")
-@, --symbols
Enable generation of symbols
-A, --auto-alias
Enable auto-alias of labels
-h, --help
Print this help and exit
-v, --version
Print version and exit
deng@local:~/code/x3399$
dtc工具使用示例如下:
# 编译 dts 为 dtb
kernel/scripts/dtc/dtc -I dts -O dtb -o xxx.dtb xxx.dts
实际使用示例,此处为伪代码,仅供参考使用,了解即可:
kernel/scripts/dtc/dtc -I dts -O dtb -o x3399-linux.dtb x3399-linux.dts
内核使用dtc工具的命令大致如上所示,实际上设备树中有非常多的依赖关系, 这些依赖关系通过Makefile文件去处理,所以一般情况下, 设备树不仅仅只是通过一个dtc命令就能将编译出来的。
2.2 在内核源码中编译设备树(推荐使用)
我们可以尝试着通过内核的构建脚本去编译设备树,我们所要用到的设备树文件都存放在 /home/deng/code/x3399/kernel/arch/arm64/boot/dts/
里面。
前面提到了编译内核时会自动去编译设备树,但是编译内核很耗时,所以我们推荐使用如下命令只编译设备树。
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- npi_v7_defconfig
make ARCH=arm -j4 CROSS_COMPILE=arm-linux-gnueabihf- dtbs
示例:
deng@local:~/code/x3399/kernel$ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- i3399_defconfig
deng@local:~/code/x3399/kernel$ make ARCH=arm64 -j4 CROSS_COMPILE=aarch64-linux-gnu- dtbs
如果在内核源码中执行了 make distclean
则必须执行第一条命令,它用于生成默认配置文件, 如果执行过一次就没有必要再次执行,当然再次执行也没有什么问题。 第二条命令开始编译设备树, 参数“-j4”指定多少个线程编译,根据自己电脑实际情况设置,越大编译越快,当然也可以不设置,设备树编译本来就很快。
编译成功后生成的设备树文件(.dtb)位于源码目录下的 内核源码/arch/arm64/boot/dts
, 开发板适配的设备树文件名为 x3399-linux.dtb
。
/home/deng/code/x3399/kernel/arch/arm64/boot/dts/rockchip
03. 加载设备树
3.1 设备树加载方法
替换设备树有下面几种方法。
- 第一种,简单直接,设备树是在编译到内核中的,所以重新烧写内核这种方式肯定可行。但是烧写内核比较麻烦,不推荐也不做过多的讲解。
- 第二种,将我们编译好的设备树或者设备树插件替换掉开发板里面原有的。
我们只介绍第二种,将编译好的新设备树文件,替换开发板对应目录下的旧设备树文件即可。
3.2 检查设备树加载情况
我们在原来的设备树上添加了新的节点,led_test
, 在该节点下有一个设备为 rgb_led_red
,我们可以通过以下的方式加载并查看新的设备树是否生效了, 新节点是否添加。
通过SCP或NFS将编译的设备树拷贝到开发板上。替换 /usr/lib/linux-image-4.19.35-imx6/imx6ull-mmc-npi.dtb 。
uboot在启动的时候负责该目录的设备文件加载到内存,供内核解析使用。输入 sudo reboot
命令重启开发板即可。
设备树中的设备树节点在文件系统中有与之对应的文件,位于“/proc/device-tree”目录。进入“/proc/device-tree”目录如下所示。
[root@rk3399:/]# ls /proc/device-tree/
'#address-cells' power-management@ff310000
'#size-cells' psci
__symbols__ pwm@ff420000
adc-keys pwm@ff420010
aliases pwm@ff420020
amba pwm@ff420030
backlight qos@ffa58000
chosen qos@ffa5c000
cif_isp@ff910000 qos@ffa60080
cif_isp@ff920000 qos@ffa60100
clock-controller@ff760000 qos@ffa60180
compatible qos@ffa70000
cpuinfo qos@ffa70080
cpus qos@ffa74000
ddr_timing qos@ffa76000
dfi@ff630000 qos@ffa90000
display-subsystem qos@ffa98000
dmc qos@ffaa0000
dp-sound qos@ffaa0080
dp@fec00000 qos@ffaa8000
dsi@ff960000 qos@ffaa8080
dsi@ff968000 qos@ffab0000
dummy-codec qos@ffab0080
dummy_cpll qos@ffab8000
dummy_vpll qos@ffac0000
dw-hdmi-audio qos@ffac0080
dwmmc@fe310000 qos@ffac8000
dwmmc@fe320000 qos@ffac8080
edp@ff970000 qos@ffad0000
efuse@ff690000 qos@ffad8080
energy-costs qos@ffae0000
es8323-sound ramoops
ethernet@fe300000 reserved-memory
external-camera-clock rga@ff680000
external-gmac-clock rkisp1@ff910000
fiq-debugger rkisp1@ff920000
gpio-keys rktimer@ff850000
gpu@ff9a0000 rkvdec@ff660000
hdmi-hdcp2@ff988000 rockchip-i2s-sound
hdmi-sound rockchip-suspend
hdmi@ff940000 rockchip-system-monitor
i2c@ff110000 saradc@ff100000
i2c@ff120000 sdhci@fe330000
i2c@ff130000 sdio-pwrseq
i2c@ff140000 serial-number
i2c@ff150000 serial@ff180000
i2c@ff160000 serial@ff190000
i2c@ff3c0000 serial@ff1a0000
i2c@ff3d0000 serial@ff1b0000
i2c@ff3e0000 serial@ff370000
i2s@ff880000 spdif-out
i2s@ff890000 spdif-sound
i2s@ff8a0000 spdif@ff870000
iep@ff670000 spi@ff1c0000
interrupt-controller@fee00000 spi@ff1d0000
interrupt-parent spi@ff1e0000
iommu@ff650800 spi@ff1f0000
iommu@ff660480 spi@ff200000
iommu@ff670800 spi@ff350000
iommu@ff8f3f00 syscon@ff320000
iommu@ff903f00 syscon@ff770000
iommu@ff914000 test-power
iommu@ff924000 thermal-zones
leds timer
memory tsadc@ff260000
mipi-dphy-tx1rx1@ff968000 usb0
model usb1
name usb@fe340000
nocp-cci-msch0@ffa86000 usb@fe380000
nocp-cci-msch1@ffa8e000 usb@fe3a0000
nocp-gpu-msch0@ffa86400 usb@fe3c0000
nocp-gpu-msch1@ffa8e400 usb@fe3e0000
nocp-hp-msch0@ffa86800 vcc-phy-regulator
nocp-hp-msch1@ffa8e800 vcc-sd
nocp-lp-msch0@ffa86c00 vcc3v3-sys
nocp-lp-msch1@ffa8ec00 vcc5v0-host-regulator
nocp-video-msch0@ffa87000 vcc5v0-sys
nocp-video-msch1@ffa8f000 vccadc-ref
nocp-vio0-msch0@ffa87400 vdd-log
nocp-vio0-msch1@ffa8f400 vop@ff8f0000
nocp-vio1-msch0@ffa87800 vop@ff900000
nocp-vio1-msch1@ffa8f800 voppwm@ff8f01a0
opp-table0 voppwm@ff9001a0
opp-table1 vpu_service@ff650000
opp-table2 watchdog@ff848000
opp-table3 wireless-bluetooth
pcie-phy wireless-wlan
pcie@f8000000 xgpio_beep
phy@ff7c0000 xgpio_fan
phy@ff800000 xgpio_hdmiin_spken
pinctrl xgpio_pwr4g
pmu-clock-controller@ff750000 xin24m
pmu_a53 xin32k
pmu_a72
[root@rk3399:/]#
接着进入xgpio_beep文件夹,可以发现beep节点中定义的属性以及它的子节点,如下所示。
[root@rk3399:/sys/firmware/devicetree/base/xgpio_beep]# pwd
/proc/device-tree/xgpio_beep
[root@rk3399:/sys/firmware/devicetree/base/xgpio_beep]# ls
compatible gpio name pinctrl-0 pinctrl-names status
[root@rk3399:/sys/firmware/devicetree/base/xgpio_beep]#
在节点属性中多了一个name,我们在led节点中并没有定义name属性,保存节点名。
这里的属性是一个文件,而子节点是一个文件夹,我们再次进入“xgpio_beep”文件夹。 里面有compatible name reg status四个属性文件。 我们可以使用“cat”命令查看这些属性文件,如下所示。
[root@rk3399:/sys/firmware/devicetree/base/xgpio_beep]# cat name
xgpio_beep
至此,设备树加载成功。
04. 设备树插件的编译和加载
Linux4.4以后引入了动态设备树(Dynamic DeviceTree)。设备树插件被动态的加载到系统中,供被内核识别。 编译设备树插件的时候无需重新编译整个设备树插件,只需要编译我们修改的部分即可。
注意设备树插件和设备树不是互相替代的关系,而是互补的关系。设备树插件可以在主设备树定型的情况下, 再对主设备树未描述的功能进行动态的拓展。 比如A板的设备树没有开启串口1的功能,但B板需要开启串口1的功能,那么可以直接沿用A板的设备树, 并用设备树插件拓展出串口1,满足B板的需求。
4.1 在内核编译设备树插件
设备树插件与设备树一样都是使用DTC工具编译,只不过设备树编译为.dtb。而设备树插件需要编译为.dtbo。 我们可以使用DTC编译命令编译生成.dtbo,但是这样比较繁琐、容易出错。
我们将设备树插件dtbo的编译工作也放在了内核编译时来完成, 当然我们单独编译设备树的时候也是可以编译出设备树插件dtbo的。
我们在内核中提供了大量的设备树插件,有些开发板许多外设硬件描述都是以dtbo插件的形式提供的。 这样使用起来非常灵活。
deng@local:~/code/x3399/kernel/arch/arm64/boot/dts/rockchip$ pwd
/home/deng/code/x3399/kernel/arch/arm64/boot/dts/rockchip
deng@local:~/code/x3399/kernel/arch/arm64/boot/dts/rockchip$ ls Makefile
Makefile
deng@local:~/code/x3399/kernel/arch/arm64/boot/dts/rockchip$
当大家尝试写设备树插件的时候,可以将自己的设备树插件添加到: arch/arm/boot/dts/overlays
目录下, 并修改 arch/arm/boot/dts/overlays/Makefile
文件, 添加编译选项,形如imx-rgb-led.dtbo的添加形式。 将imx-rgb-led.dtbo追加到imx-uart8.dtbo后面即可。
添加好后,可以执行设备树的编译命令,设备树插件的编译也会同步完成。
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- npi_v7_defconfig
make ARCH=arm -j4 CROSS_COMPILE=arm-linux-gnueabihf- dtbs
可以看到,编译输出时,dtbo文件也有对应的打印信息。
4.2 内核编译设备树插件过程
编译设备树插件和编译设备树类似,这里介绍内核中的dtc工具编译编译设备树插件的过程。
内核中将xxx.dts 编译为 xxx.dtbo的过程示例,仅供参考:
内核构建目录/scripts/dtc/dtc -I dts -O dtb -o xxx.dtbo xxx.dts
例如,将rgb-led-overlay.dts编译为rgb.dtbo
../ebf_linux_kernel/build_image/build/scripts/dtc/dtc -I dts -O dtb -o rgb.dtbo rgb-led-overlay.dts
编译好的设备树插件为rgb.dtbo。
当然和编译设备树一样,设备树插件的编译也涉及到依赖关系,所以编译过程也比较复杂。 不仅仅是使用一条命令就可以完成编译的。