i.mx8系列芯片是不能原生支持dtb overlay的,需要修改uboot来支持,整个操作过程需要改不少代码,还需要对linux设备树有一个清晰的理解,现在以i.mx8mn为例分析一下如何通过修改uboot来支持dtb overlay。
参阅 : https://source.android.com/docs/core/architecture/dto?hl=zh-cn
一、首先要了解dtb overlay的运行机制。设备树的存在是为了告知内核以及驱动程序所使用的硬件型号和属性,内核和驱动程序根据设备树提供的参数去驱动硬件。对于内核来说,驱动代码和设备树一起屏蔽了硬件,当我们需要更换硬件的时候,只需要修改设备树和驱动即可,不需要修改内核和应用层代码。设备树由芯片厂商提供,下游厂商根据自己的产品修改设备树。假如某个厂商同一个设备有多种型号,它们之间区别很小,仅仅更换了个别传感器,这种情况下,为了满足所有型号的设备,就必须给每个型号的设备都写一套dts文件,而他们所烧写的image都是不同的。这样可能会造成管理困难。如果使用dtb overlay,就不需要每个型号都编一套image了,只需要在runtime的时候load 对应的dtb去配置参数就可以了。
二、如何写dtb overlay
dtb overlay会由uboot在运行时附加在base dtb上,然后uboot会把新生成的dtb load到ram中。所以dtb overlay不仅要遵循自身的格式,还要结合base dtb。
例如,在AOSP中:(省略了部分无关代码)
xiachen@sz187:/home/ssd-3/xiachen/android_12.0.0_2.0.0/vendor/nxp-opensource/kernel_imx/arch/arm64/boot/dts/freescale$ vim imx8mn.dtsi
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright 2019 NXP
*/
#include <dt-bindings/clock/imx8mn-clock.h>
...
/ {
interrupt-parent = <&gic>;
#address-cells = <2>;
#size-cells = <2>;
aliases {
ethernet0 = &fec1;
...
csi0 = &mipi_csi_1;
};
cpus {
#address-cells = <1>;
#size-cells = <0>;
idle-states {
entry-method = "psci";
...
在根节点中存在着interrupt-parent、#address-cells、#size-cells、cpus等节点,在i.mx8mn evk board的/proc/device-tree下可以找到。
编写两份dtb overlay,在主节点下面增加自己的节点
[my_overlay_dt-a.dts]
/dts-v1/;
/plugin/;
/ {
board_id = <0x000029a>;
board_rev = <0x0000000>;
};
&{/} {
maximus = "Maximus_Sun";
handsome {
first_name = "Maximus";
second_name = "Sun";
};
};
[my_overlay_dt-b.dts]
/dts-v1/;
/plugin/;
/ {
board_id = <0x000029b>;
board_rev = <0x0000000>;
};
&{/} {
xiachen = "Xiachen_Sun";
handsome {
first_name = "Xiachen";
second_name = "Sun";
};
};
具体编写dtb overlay语法请参阅博客开头的链接
三、编译
需要提前安装dtc(device tree compiler)
命令如下:(-@参数必不可少,它会给dts文件添加/symbols节点,它的具体作用可以看我的这篇文章:***)
dtc -@ -O dtb -o my_overlay_dt-a.dtb my_overlay_dt-a.dts
dtc -@ -O dtb -o my_overlay_dt-b.dtb my_overlay_dt-b.dts
maximus@maximus-OptiPlex-7090:~/dtbo$ ls
dtbo.img dtc-1.6.1.tar.gz my_overlay_dt-a.dtb my_overlay_dt-b.dtb
dtc-1.6.1 imx8mn-evk.dtb my_overlay_dt-a.dts my_overlay_dt-b.dts
编译生成的文件my_overlay_dt-a.dtb my_overlay_dt-b.dtb
四、打包
需要提前安装mkdtimg工具,这个工具会把设备树打包成一个dtbo文件,mkdtimg会在dtbo文件的开头插入dt_table_header,将所有输入的dtb文件组织起来。下面给出mkdtimg的基础命令。
创建:
mkdtimg create <image_filename> (<global-option>...) \
<ftb1_filename> (<entry1_option>...) \
<ftb2_filename> (<entry2_option>...) \
...
ftbX_filename 会在映像中生成一个 dt_table_entry。entryX_option 是分配给 dt_table_entry 的值。这些值可以是以下任一值:
--id=<number|path>
--rev=<number|path>
--custom0=<number|path>
--custom1=<number|path>
--custom2=<number|path>
--custom3=<number|path>
dump:
mkdtimg dump <dtbo_filename>
现在用nxp提供的imx8mn-evk.dtb与之前生成的两个dtb overlay文件打包成一个dtbo文件,imx8mn-evk.dtb在路径vendor/nxp-opensource/kernel_imx/arch/arm64/boot/dts/freescale中:
maximus@maximus-OptiPlex-7090:~/dtbo$ mkdtimg create dtbo.img imx8mn-evk.dtb --id=0 my_overlay_dt-a.dtb --id=1 my_overlay_dt-b.dtb --id=2
create image file: dtbo.img...
Total 3 entries.
maximus@maximus-OptiPlex-7090:~/dtbo$
这里仅仅给出了--id的值,因为uboot会根据id在dtbo中寻找对应的dtb overlay,并叠加到主dtb上去。这里可以dump一下看看重点关注每一个fdt的id
maximus@maximus-OptiPlex-7090:~/dtbo$ mkdtimg dump dtbo.img
dt_table_header:
magic = d7b7ab1e
total_size = 80732
header_size = 32
dt_entry_size = 32
dt_entry_count = 3
dt_entries_offset = 32
page_size = 2048
version = 0
dt_table_entry[0]:
dt_size = 80000
dt_offset = 128
id = 00000000
rev = 00000000
custom[0] = 00000000
custom[1] = 00000000
custom[2] = 00000000
custom[3] = 00000000
(FDT)size = 80000
(FDT)compatible = fsl,imx8mn-evk
dt_table_entry[1]:
dt_size = 302
dt_offset = 80128
id = 00000001
rev = 00000000
custom[0] = 00000000
custom[1] = 00000000
custom[2] = 00000000
custom[3] = 00000000
(FDT)size = 302
(FDT)compatible = (unknown)
dt_table_entry[2]:
dt_size = 302
dt_offset = 80430
id = 00000002
rev = 00000000
custom[0] = 00000000
custom[1] = 00000000
custom[2] = 00000000
custom[3] = 00000000
(FDT)size = 302
(FDT)compatible = (unknown)
maximus@maximus-OptiPlex-7090:~/dtbo$
其实到这里生成的dtbo还不完整,为了保证平台的安全性能,官方的编译脚本还会有签名的操作,这里刷trusty的image,可以省略这一步操作。
5、修改uboot
首先要给uboot添加这么一个功能,可以参考下面的patch修改: