1:客户需求
使用相同的固件rom包,在设备启动过程中,根据硬件设计的一个或多个gpio引脚的高低电平来选择使用不同的设备树配置。
2:修改思路
在qcom平台上,存在有几十个设备树文件, 这些设备树中都会包含多组 board-id, plateform-id, pmic-id 等信息, 用于与之比较的是在lk中读取到的设备信息。
现在新建立一个新的设备树文件,就要保证 在匹配过程中,能够有条件让我们添加的设备树被正确的识别并匹配到。
所以修改的思路是,在lk读取设备信息之后, 根据gpio的状态 去主动的修改 这些用于匹配的设备信息中的某些内容,用于匹配到新添加的设备树。
3:过程分析
简析lk驱动过程中,有关于设备树匹配的部分,这有助于理解 设备树识别,匹配的规则,方便我们有效的完成这个修改方案。
从lk开始的部分 Kmain函数开始:
/* called from crt0.S */
void kmain(void)
|->platform_early_init(void)
| ->board_init();
| ->platform_detect();
| ->wirite struct board_data
| struct board_data {
| uint32_t platform;
| uint32_t foundry_id;
| uint32_t platform_version;
| uint32_t platform_hw;
| uint32_t platform_subtype;
| uint32_t target;
| uint32_t baseband;
| struct board_pmic_data pmic_info[MAX_PMIC_DEVICES];
| uint32_t platform_hlos_subtype;
|
|
|->bootstrap2
| ->apps_init();
| ->aboot_init();
| ->normal_boot:
| boot_linux_from_mmc();
| ->dev_tree_appended
| ->...
| while (((uintptr_t)dtb + sizeof(struct fdt_header)) < (uintptr_t)kernel_end) {
| fdt_check_header
| fdt_check_header_ext
| dev_tree_compatible(dtb, dtb_size, dt_entry_queue);
| }
| platform_dt_match_best
| ->best_match_dt_entry
| ...
};
以上的while循环中,会把镜像中打包的每个设备设备树文件取出,进行检测及解析最后进行匹配的工作,将匹配上的设备树加入到链表中,供后面再比较选择。
分析下dev_tree_compatible:
dev_tree_compatible
->root_offset = fdt_path_offset(dtb, "/");
prop = fdt_getprop(dtb, root_offset, "model", &len);
pmic_prop = (const char *)fdt_getprop(dtb, root_offset, "qcom,pmic-id", &len_pmic_id);
board_prop = (const char *)fdt_getprop(dtb, root_offset, "qcom,board-id", &len_board_id);
->
......
/* Extract board data from DTB */
for(i = 0 ; i < board_data_count; i++) {
board_data[i].variant_id = fdt32_to_cpu(((struct board_id *)board_prop)->variant_id);
board_data[i].platform_subtype = fdt32_to_cpu(((struct board_id *)board_prop)->platform_subtype);
if (board_data[i].platform_subtype == 0)
board_data[i].platform_subtype =
fdt32_to_cpu(((struct board_id *)board_prop)->variant_id) >> 0x18;
len_board_id -= sizeof(struct board_id);
board_prop += sizeof(struct board_id);
}
/* Extract platform data from DTB */
for(i = 0 ; i < msm_data_count; i++) {
platform_data[i].platform_id = fdt32_to_cpu(((struct plat_id *)plat_prop)->platform_id);
platform_data[i].soc_rev = fdt32_to_cpu(((struct plat_id *)plat_prop)->soc_rev);
len_plat_id -= sizeof(struct plat_id);
plat_prop += sizeof(struct plat_id);
}
if (dtb_ver == DEV_TREE_VERSION_V3 && pmic_prop) {
/* Extract pmic data from DTB */
for(i = 0 ; i < pmic_data_count; i++) {
pmic_data[i].pmic_version[0]= fdt32_to_cpu(((struct pmic_id *)pmic_prop)->pmic_version[0]);
pmic_data[i].pmic_version[1]= fdt32_to_cpu(((struct pmic_id *)pmic_prop)->pmic_version[1]);
pmic_data[i].pmic_version[2]= fdt32_to_cpu(((struct pmic_id *)pmic_prop)->pmic_version[2]);
pmic_data[i].pmic_version[3]= fdt32_to_cpu(((struct pmic_id *)pmic_prop)->pmic_version[3]);
len_pmic_id -= sizeof(struct pmic_id);
pmic_prop += sizeof(struct pmic_id);
}
......
platform_dt_absolute_match(&(dt_entry_array[i]), dtb_list))
}
}
以上是简单的挑选出了对每个设备树提取数据的过程,最后会通过platform_dt_absolute_match与之前在platform_detect 读取到的 内容做比较。
-> platform_dt_absolute_match(&(dt_entry_array[i]), dtb_list))
if(
(cur_dt_msm_id == (board_platform_id() & 0x0000ffff)) &&
(cur_dt_hw_platform == board_hardware_id()) &&
(cur_dt_hw_subtype == board_hardware_subtype()) &&
(cur_dt_hlos_ddr == (target_get_hlos_subtype() & 0x700)) &&
(cur_dt_entry->soc_rev <= board_soc_version()) &&
((cur_dt_entry->variant_id & 0x00ffff00) <= (board_target_id() & 0x00ffff00)) &&
((cur_dt_entry->pmic_rev[0] & 0x00ffff00) <= (board_pmic_target(0) & 0x00ffff00)) &&
((cur_dt_entry->pmic_rev[1] & 0x00ffff00) <= (board_pmic_target(1) & 0x00ffff00)) &&
((cur_dt_entry->pmic_rev[2] & 0x00ffff00) <= (board_pmic_target(2) & 0x00ffff00)) &&
((cur_dt_entry->pmic_rev[3] & 0x00ffff00) <= (board_pmic_target(3) & 0x00ffff00)))
{
dt_node_tmp = dt_entry_list_init();
memcpy((char*)dt_node_tmp->dt_entry_m,(char*)cur_dt_entry, sizeof(struct dt_entry));
dprintf(SPEW, "Add DTB entry %u/%08x/0x%08x/%x/%x/%x/%x/%x/%x/%x\n",
dt_node_tmp->dt_entry_m->platform_id, dt_node_tmp->dt_entry_m->variant_id,
dt_node_tmp->dt_entry_m->board_hw_subtype, dt_node_tmp->dt_entry_m->soc_rev,
dt_node_tmp->dt_entry_m->pmic_rev[0], dt_node_tmp->dt_entry_m->pmic_rev[1],
dt_node_tmp->dt_entry_m->pmic_rev[2], dt_node_tmp->dt_entry_m->pmic_rev[3],
dt_node_tmp->dt_entry_m->offset, dt_node_tmp->dt_entry_m->size);
insert_dt_entry_in_queue(dt_list, dt_node_tmp);
return 1;
}
return 0;
这里可以看出在筛选阶段,需要 board_platform_id,board_hardware_id,board_hardware_subtype,target_get_hlos_subtype,board_soc_version,board_target_id,board_pmic_target 都能满足对应的条件才能够完成匹配
从实际打印的log中来看,其实在设备上,这个阶段已经将设备树筛选完了, 下面截取一些,未被选中的 设备树在匹配过程中的判断信息:
[8150] [8150] Found an appended flattened device tree (Qualcomm Technologies, Inc. MSM8909-PM8909 MTP - 245 8 256 0x0)
[8160] [8160] cur_dt_msm_id000000f5, (board_platform_id() & 0x0000ffff)000000f5
[8170] [8170] cur_dt_hw_platform00000008, board_hardware_id00000008
[8180] [8180] cur_dt_hw_subtype00000000, board_hardware_subtype00000001
[8180] [8180] Device tree's msm_id doesn't match the board: <245 8 0 0x100> != <245 8 1 0x20000>
[8190] [8190] Found an appended flattened device tree (Qualcomm Technologies, Inc. MSM8909-PM8909 MTP - 245 8 256 0x0)
[8200] [8200] cur_dt_msm_id000000f5, (board_platform_id() & 0x0000ffff)000000f5
[8210] [8210] cur_dt_hw_platform00000008, board_hardware_id00000008
[8210] [8210] cur_dt_hw_subtype00000000, board_hardware_subtype00000001
[8220] [8220] Device tree's msm_id doesn't match the board: <245 8 0 0x100> != <245 8 1 0x20000>
这里可以发现,MSM8909-PM8909 MTP 这个设备树未匹配成功,从我添加的log中可以发现,是cur_dt_hw_subtype 不等于 board_hardware_subtype 导致了,匹配失败
这里也可以确定,如果想要匹配成功,board_hardware_subtype 就需要为 0 ,下面给出 在platform_detect 阶段读到内容log,与正确匹配时的log##
->platform_detect
[6300] [6300] sc20 boardinfo as fllow:
baseband:0
platform:246
foundry_id:0
platform_hlos_subtype:0
platform_hw:8
platform_subtype:0
platform_version:20000
target:10008
[6320] [6320] sc20 pmicinfo :3 as fllow:
[6320] [6320] pmic0-----
[6320] [6320]
pmic_target:1010d
pmic_type:1000d
pmic_version:10001
[10680] [10680] Found an appended flattened device tree (Qualcomm Technologies, Inc. MSM8909-PM8909 1GB MTP ST - 246 8 0 0x0)
[10690] [10690] cur_dt_msm_id000000f6, (board_platform_id() & 0x0000ffff)000000f6
[10700] [10700] cur_dt_hw_platform00000008, board_hardware_id00000008
[10700] [10700] cur_dt_hw_subtype00000000, board_hardware_subtype00000000
[10710] [10710] cur_dt_hlos_ddr00000000, (target_get_hlos_subtype() & 0x700)00000000
[10720] [10720] cur_dt_entry->soc_rev00000000, board_soc_version00020000
[10720] [10720] (cur_dt_entry->variant_id & 0x00ffff00)00000000, (board_target_id() & 0x00ffff00)00010000
[10730] [10730] (cur_dt_entry->pmic_rev[0] & 0x00ffff00)00010000, (board_pmic_target(0) & 0x00ffff00)00010100
[10740] [10740] (cur_dt_entry->pmic_rev[1] & 0x00ffff00)00000000, (board_pmic_target(1) & 0x00ffff00)00000000
[10750] [10750] (cur_dt_entry->pmic_rev[2] & 0x00ffff00)00000000, (board_pmic_target(2) & 0x00ffff00)00000000
[10760] [10760] (cur_dt_entry->pmic_rev[3] & 0x00ffff00)00000000, (board_pmic_target(3) & 0x00ffff00)00000000
[10780] [10780] Add DTB entry 246/00000008/0x00000000/0/1000d/0/0/0/90a9c834/29307
[10780] [10780] Device tree exact match the board: <246 8 0 0x0> == <246 8 0 0x20000>
最后在对所有的设备树文件处理完成之后,退出循环,通过platform_dt_match_best 将链表中的设备树进行多伦比较,将最终的设备树确定下来
best_match_dt_entry = platform_dt_match_best(dt_entry_queue);
static struct dt_entry *platform_dt_match_best(struct dt_entry_node *dt_list){
if (!platform_dt_absolute_compat_match(dt_list, DTB_FOUNDRY))
return NULL;
if (!platform_dt_absolute_compat_match(dt_list, DTB_PMIC_MODEL))
return NULL;
if (!platform_dt_absolute_compat_match(dt_list, DTB_PANEL_TYPE))
return NULL;
if (!platform_dt_absolute_compat_match(dt_list, DTB_BOOT_DEVICE))
return NULL;
if (!update_dtb_entry_node(dt_list, DTB_SOC))
return NULL;
if (!update_dtb_entry_node(dt_list, DTB_MAJOR_MINOR))
return NULL;
if (!update_dtb_entry_node(dt_list, DTB_PMIC0))
......
}
if (best_match_dt_entry){
bestmatch_tag = (void *)best_match_dt_entry->offset;
bestmatch_tag_size = best_match_dt_entry->size;
dprintf(INFO, "Best match DTB tags %u/%08x/0x%08x/%x/%x/%x/%x/%x/%x/%x\n",
best_match_dt_entry->platform_id, best_match_dt_entry->variant_id,
best_match_dt_entry->board_hw_subtype, best_match_dt_entry->soc_rev,
best_match_dt_entry->pmic_rev[0], best_match_dt_entry->pmic_rev[1],
best_match_dt_entry->pmic_rev[2], best_match_dt_entry->pmic_rev[3],
best_match_dt_entry->offset, best_match_dt_entry->size);
dprintf(INFO, "Using pmic info 0x%0x/0x%x/0x%x/0x%0x for device 0x%0x/0x%x/0x%x/0x%0x\n",
best_match_dt_entry->pmic_rev[0], best_match_dt_entry->pmic_rev[1],
best_match_dt_entry->pmic_rev[2], best_match_dt_entry->pmic_rev[3],
board_pmic_target(0), board_pmic_target(1),
board_pmic_target(2), board_pmic_target(3));
}
最后打印出来的 即是在开机log中最常见的加载到的设备树的基本信息
[15880] [15880] Best match DTB tags 246/00000008/0x00000000/0/1000d/0/0/0/90a9c834/29307
[15890] [15890] Using pmic info 0x1000d/0x0/0x0/0x0 for device 0x1010d/0x0/0x0/0x0
4:实际修改
了解了以上流程之后,再来区别 如何select设备树就很简单了,下面给出一个实例。
在追溯代码的过程中,发现,这些id虽然可以自行修改,但是其中有些id在lk启动流程中会参加多次判断 比如, board-id 为8,意味着 platform=HW_PLATFORM_MTP, msm-id 为 245 意味着,平台是MSM8909,这些宏在启动过程中会参加一些逻辑的判断,比如panel_select 为了不涉及到更多地方的修改,这里选择修改board_hardware_subtype (即platform_subtype),根据gpio的状态,如果为高电平,就将此id修改为1. 在platform_detect()中添加:
gpio_tlmm_config(TLMM_BOOT_SELECT_GPIO, 0, 0, 3, 0, 0);
udelay(5000);
board.platform_subtype = gpio_status(TLMM_BOOT_SELECT_GPIO);
根据上述最终在msm8909平台上最终生效的是 msm8909-1gb-mtp.dts(根据自己实际项目决定)配置来看,可以复制一份 msm8909-1gb-mtp.dts 为 msm8909-1gb-mtp-st.dts(makefile要添加好) 将其中的id修改为 qcom,board-id= <8 1> 然后将引用的“主”设备树文件msm8909-mtp.dtsi(这里的主是指最常修改的那个,最终都会合并成一个文件编译成dtb文件),复制并修改为msm8909-mtp-st.dtsi 这样当TLMM_BOOT_SELECT_GPIO(自行定义一个gpio) 为高时,使用的就是新建的设备树文件中的内容,反之还是原设备树文件生效。