一、专业术语:DTB, DTBO, DTC, DTO,DTS, FDT。
它们之间的关系可以描述为:
DTS 是用于描述 FDT 的文件;
DTS 经过 DTC 编译后可生成 DTB/DTBO;
DTB 和 DTBO 通过 DTO 操作可合并成一个新的 DTB;
通常情况下很多用户习惯把“DTO“这个词的动作含义用“DTBO“来替代,下文中我们避开这个概念混用,
明确:DTO 是一个动词概念,代表的是操作;DTBO 是一个名词概念,指的是用于叠加的次 dtb。
本章节更多知识可参考:https://source.android.google.cn/devices/architecture/dto
二、Multi DTB
RK3588平台默认支持multi dtb,即多dtb兼容方式,功能启用需配置 CONFIG_ROCKCHIP_HWID_DTB=y ,代码实现在uboot(u-boot/arch/arm/mach-rockchip/resource_hwid.c),对应的function:static int hwid_adc_find_dtb(const char *file_name),实现的逻辑是通过获取kernel-5.10/scripts/mkmultidtb.py 脚本中配置的DTBS列表,和实际从主板对应saradc通道读取的电压值(或gpio方式)进行匹配,加载对应的dtb。
DTB命名用户需要将ADC/GPIO 的硬件唯一值信息体现在 dtb 文件名里。
命名规则如下:
ADC 作为 HW_ID DTB:文件名以“.dtb”结尾;
HW_ID 格式: #[controller]_ch[channel]=[adcval],称为一个完整单元
[controller]: dts 里面 ADC 控制器的节点名字。
[channel]: ADC 通道。
[adcval]: ADC 的中心值,实际有效范围是:adcval±30。
每个完整单元必须使用小写字母,内部不能有空格;
多个单元之间通过#进行分隔,最多支持 10 个单元;
例如mkmultidtb.py中增加 RK3588-xxx-all (xxx代表对应产品型号名)把当前所有的板型对应的dts汇总:
DTBS['RK3588-xxx-all'] = OrderedDict([('rk3588-xxx-hvt', #_saradc_ch5=1365#_saradc_ch3=10'), ('rk3588-xxx-evt', '#_saradc_ch5=1365#_saradc_ch3=682')])
然后通过build.sh脚本调用./scripts/mkmultidtb.py 传入对应的DTBS对应board名称作为参数,例如上面定义的“RK3588-xxx-all”,mkmultidtb.py脚本会将上面2份dtb打包进resource.img,而resource.img又进一步打包到boot.img。
dtb是由dts编译而来,编译变量在device/rockchip/rk3588/xxx/BoardConfig.mk中指定,这里我加了一个变量 BOARD_BUILD_DTS_ALL_IN_ONE 来决定是否启用多multi dtb方式,然后通过PRODUCT_KERNEL_BOARD 指定mkmultidtb.py 中定义的DTBS列表对应board名称,即如上的 'RK3588-xxx-all'。
# Build all dts in one img and loading by HWID BOARD_BUILD_DTS_ALL_IN_ONE := true # $(warning "BOARD_BUILD_DTS_ALL_IN_ONE : $(BOARD_BUILD_DTS_ALL_IN_ONE)") ifeq ($(strip $(BOARD_BUILD_DTS_ALL_IN_ONE)), true) PRODUCT_KERNEL_DTS := rk3588-xxx-hvt rk3588-xxx-evt PRODUCT_KERNEL_BOARD := RK3588-xxx-all else PRODUCT_KERNEL_DTS := rk3588-xxx-hvt endif
执行编译、打包的脚本是build.sh,通过读取BoardConfig.mk设定的变量决定编译哪些dts,打包哪些dtb:
if [ "$BUILD_DTS_ALL_IN_ONE" = "" ] ; then BUILD_DTS_ALL_IN_ONE=`get_build_var BOARD_BUILD_DTS_ALL_IN_ONE` echo "build_var BOARD_BUILD_DTS_ALL_IN_ONE : $BUILD_DTS_ALL_IN_ONE" fi if [ "$KERNEL_DTS" = "" ] ; then KERNEL_DTS=`get_build_var PRODUCT_KERNEL_DTS` echo "build_var PRODUCT_KERNEL_DTS : $KERNEL_DTS" fi
...... # build kernel getbuildtimes "build kernel begin" a if [ "$BUILD_KERNEL" = true ] ; then echo "Start build kernel" if [ "$BUILD_DTS_ALL_IN_ONE" = true ] ; then # Build all dts in one img and loading by HWID DTBS_BOARD=`get_build_var PRODUCT_KERNEL_BOARD` echo "build_var PRODUCT_KERNEL_BOARD : $DTBS_BOARD" cd $LOCAL_KERNEL_PATH && make clean && make $ADDON_ARGS ARCH=$KERNEL_ARCH $KERNEL_DEFCONFIG && build_multidtb && ./scripts/mkmultidtb.py $DTBS_BOARD && cd - else cd $LOCAL_KERNEL_PATH && make clean && make $ADDON_ARGS ARCH=$KERNEL_ARCH $KERNEL_DEFCONFIG && build_multidtb && ./scripts/mkmultidtb.py RK3588-$TARGET_PRODUCT && cd - fi
另外GPIO作为 HW_ID DTB:文件名以“.dtb”结尾;
HW_ID 格式:#gpio[pin]=[level],称为一个完整单元
[pin]: GPIO 脚,如 0a2 表示 gpio0a2
[level]: GPIO 引脚电平。
每个完整单元必须使用小写字母,内部不能有空格;
多个单元之间通过#进行分隔,最多支持 10 个单元;
例如:
rk3326-evb-lp3-v10#gpio0a2=0#gpio0c3=1.dtb
如果用GPIO作为硬件识别,必须在rkxx-u-boot.dtsi中保留对应的pinctrl和gpio节点;ADC默认已使能。
例如:gpio0和gpio1作为识别:
... &pinctrl { u-boot,dm-spl; // 追加该属性,让该节点被保留在U-Boot DTB中。下同。 }; &gpio0 { u-boot,dm-spl; }; &gpio1 { u-boot,dm-spl; }; ...
加载结果:
Android 12.0, Build 2022.6, v2 Found DTB in boot part DTB: rk3588-xxx-evt#_saradc_ch5=20#_saradc_ch3=0.dtb // 打印匹配的DTB,否则使用默认的"rkkernel.dtb" HASH(c): OK ANDROID: fdt overlay OK
注:rk-kernel.dtb:rk 默认的 dtb,不体现在上述列表中,所有 dtb 都没匹配成功时默认被使用,打包脚本会使用 DTBS 的第一个 dtb 作为默认的 dtb。
上面提到dts编译dtb后是打包到resource.img,进一步打包到boot.img, 所以boot分区大小也决定了到底能存储多少个dtb,而且resource.img还包含了图片资源,如开机logo等。
分区大小可以通过如下指令结合查看:
ls -l /dev/block/by-name cat /proc/partitions
三、DTBO机制
如果需要兼容的dtb太多,boot分区又不能增大情况,可以考虑使用dtbo机制达到兼容,实现方式是以一个主dtb作为基础,所有板型共用部分,然后差异部分通过dtbo叠加到主dtb,进一步合成对应板型匹配的dtb,这样就减少了dtb的重复内容和占用大小。
DTBO默认未使用,如需开启配置如下:
CONFIG_CMD_DTIMG=y
CONFIG_OF_LIBFDT_OVERLAY=y
代码中加载dtbo逻辑由 board_select_fdt_index()函数的实现,这是一个_weak 函数,用户可以根据实际情况重新实现它。函数作用是在多份 DTBO 中获取用于执行 DTO 操作的那份 DTBO(返回 index 索引,最小从0 开始),默认的 weak 函数返回的 index 为 0。具体查阅:u-boot/common/androidbootloader.c 。
/* * Default return index 0. */ __weak int board_select_fdt_index(ulong dt_table_hdr) { /* * User can use "dt_for_each_entry(entry, hdr, idx)" to iterate * over all dt entry of DT image and pick up which they want. * * Example: * struct dt_table_entry *entry; * int index; * * dt_for_each_entry(entry, dt_table_hdr, index) { * * .... (use entry) * } * * return index; */ return 0; }
需要注意DTO 操作使用的 DTB 和 DTBO 的编译跟普通的 DTB 编译有区别,语法上有特殊区别:使用 dtc 编译.dts 时,必须添加选项-@以在生成的.dtbo 中添加_symbols_节点。_symbols_节点包含带标签的所有节点的列表,DTO 库可使用这个列表作为参考。如下示例:
编译主.dts 的示例命令:
dtc -@ -O dtb -o my_main_dt.dtb my_main_dt.dts
编译叠加层 DT .dts 的示例命令
dtc -@ -O dtb -o my_overlay_dt.dtbo my_overlay_dt.dts
DTO语法参考:https://source.android.google.cn/docs/core/architecture/dto/syntax
DTO 执行完成后,在 U-Boot 的开机信息中可以看到结果:
// 成功时的打印 ANDROID: fdt overlay OK // 失败时的打印 ANDROID: fdt overlay failed, ret=-19
通常引起失败的原因一般都是因为主/次设备书 blob 的内容存在不兼容引起,所以用户需要对它们的生成语法和兼容性要比较清楚。
DTO 执行成功后在给 kernel 的 cmdline 里追加如下信息,表明当前使用哪份 DTBO 进行 DTO 操作:
androidboot.dtbo_idx=1 // idx从0开始,这里表示选取idx=1的那份DTBO进行DTO操作
DTO 执行成功后可以在U-Boot命令行使用 fdt 命令查看DTB内容,确认改动是否生效。
标签:KERNEL,DTO,RK3588,dtb,板型,BOARD,Android12,DTS,DTB From: https://www.cnblogs.com/blogs-of-lxl/p/17008925.html