【内核驱动】设备树编译和加载

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。

当然和编译设备树一样,设备树插件的编译也涉及到依赖关系,所以编译过程也比较复杂。 不仅仅是使用一条命令就可以完成编译的。

05. 讨论

06. 附录

猜你喜欢

转载自blog.csdn.net/dengjin20104042056/article/details/132873437