RK3588的Uboot启动流程分析--Uboot第一阶段(2) 续接上文在gd中配置好设备树的地址后运行int arch_cpu_init(void)其目录在arch/arm/mach-rockchip/rk3588下。writel(QOS_PRIORITY_LEVEL(4, 4), DMAC0_PRIORITY_REG); /* * set VOP M0 and VOP M1 to priority 0x303,then * Peri VOP/MCU ISP/VICAP other * Note: VOP priority can only be modified during the u-boot stage, * as VOP default power down, and power up after trust. */ writel(QOS_PRIORITY_LEVEL(3, 3), VOP_M0_PRIORITY_REG); writel(QOS_PRIORITY_LEVEL(3, 3), VOP_M1_PRIORITY_REG); /* * set SATA,USB,GMAC to priority 0x404 */ writel(QOS_PRIORITY_LEVEL(4, 4), MMU600PHP_TBU_PRIORITY_REG); writel(QOS_PRIORITY_LEVEL(4, 4), MMU600PHP_TCU_PRIORITY_REG); /* * Set SATA FBSCP and PORTS_IMPL for kernel drivers */ writel(SATA_FBS_ENABLE, SATA0_BASE_ADDR SATA_PORT_CMD); writel(1, SATA0_BASE_ADDR SATA_PI); writel(SATA_FBS_ENABLE, SATA1_BASE_ADDR SATA_PORT_CMD); writel(1, SATA1_BASE_ADDR SATA_PI); writel(SATA_FBS_ENABLE, SATA2_BASE_ADDR SATA_PORT_CMD); writel(1, SATA2_BASE_ADDR SATA_PI); #endif /* Select usb otg0 phy status to 0 that make rockusb can work at high-speed */ writel(0x00080008, USBGRF_BASE USB_GRF_USB3OTG0_CON1);上述为运行的核心代码首先writel(QOS_PRIORITY_LEVEL(4, 4), DMAC0_PRIORITY_REG);将DMAC0与DMAC1/2三者的优先级统一根据下面的表格可以清晰的查看到它们的优先级。此处的目的是为了统一DMA优先级的接口。writel(QOS_PRIORITY_LEVEL(3, 3), VOP_M0_PRIORITY_REG);writel(QOS_PRIORITY_LEVEL(3, 3), VOP_M1_PRIORITY_REG);设置Q 默认QOS的情况下VOP低于MCU上述代码将VOP的优先级拉高到和MCU一样高原因显示控制器VOP需要从内存中实时、周期性地读取图像数据并发送到屏幕。如果这个数据流被其他突发的大流量传输比如持续的文件拷贝所阻塞屏幕就会出现卡顿等现象。将 VOP 的优先级设为最高3级就是为了保证显示数据流的绝对优先确保用户界面始终流畅。除此之外由于后续的安全固件会使用到VOP现实设备因此无法等到内核启动在配置VOP的正确优先级。writel(SATA_FBS_ENABLE, SATA0_BASE_ADDR SATA_PORT_CMD);writel(1, SATA0_BASE_ADDR SATA_PI);writel(SATA_FBS_ENABLE, SATA1_BASE_ADDR SATA_PORT_CMD);writel(1, SATA1_BASE_ADDR SATA_PI);writel(SATA_FBS_ENABLE, SATA2_BASE_ADDR SATA_PORT_CMD);writel(1, SATA2_BASE_ADDR SATA_PI);降低SATA、USB、网卡等外设的优先级它们通常会产生大量、持续的数据流。如果它们的优先级过高会长时间占用总线导致 CPU 无法及时响应其他任务。给予它们次高优先级4级既能保证其数据吞吐量又不会过度抢占总线资源。writel(0x00080008, USBGRF_BASE USB_GRF_USB3OTG0_CON1);重新复位USB OTG使其进入到USB高速状态。上述的优先级主要是针对的DDR调度器的优先级进行配置至于什么是DDR调度器后续会专门出一张来进行说明。2.static int initf_dm(void)扫描和初始化部分外设。static int initf_dm(void) { #if defined(CONFIG_DM) CONFIG_VAL(SYS_MALLOC_F_LEN) int ret; bootstage_start(BOOTSTATE_ID_ACCUM_DM_F, dm_f); ret dm_init_and_scan(true); bootstage_accum(BOOTSTATE_ID_ACCUM_DM_F); if (ret) return ret; #endif #ifdef CONFIG_TIMER_EARLY ret dm_timer_init(); if (ret) return ret; #endif return 0; }此处扫描和初始化外部的设备属于Uboot的早期初始化这是针对在Uboot第一阶段中需要使用部分外设而配置的如初始化终端的串口虽然在RK3588中已经在更早期TPL-SPL阶段就已经将调试串口进行了初始化因此如果想在终端进行调试从start.s的内容也可以使用串口。如何设定这些早起要初始化的外设呢1.dm_init_and_scan(true);当传入的参数为true时就代表本次扫描和初始化的外设是早期开启的外设2.在我们使用的设备树文件中也就是.dts文件中一个设备节点内部包含dm-pre-reloc这个属性的设备节点则说明它是早期Uboot第一阶段被扫描和初始化的外设通过上述两部就可以在Uboot中添加自己想要使用的外设了前提是还要有相应外设的资源和驱动。int dm_init_and_scan(bool pre_reloc_only) { int ret; ret dm_init(IS_ENABLED(CONFIG_OF_LIVE)); if (ret) { debug(dm_init() failed: %d\n, ret); return ret; } ret dm_scan_platdata(pre_reloc_only); if (ret) { debug(dm_scan_platdata() failed: %d\n, ret); return ret; } if (CONFIG_IS_ENABLED(OF_CONTROL) !CONFIG_IS_ENABLED(OF_PLATDATA)) { ret dm_extended_scan_fdt(gd-fdt_blob, pre_reloc_only); if (ret) { debug(dm_extended_scan_dt() failed: %d\n, ret); return ret; } } ret dm_scan_other(pre_reloc_only); if (ret) return ret; return 0; }从dm_init_and_scan可以看出有四种不同的扫描和初始化设备树的方式1.dm_init从设备树中扫描和初始化2.dm_scan_platdata从平台数据中获取设备的信息3.dm_extended_scan_fdt从拓展设备树中也就是之前提到的fdtdec_setup中配置的gd-fdt_blob的设备树地址中进行获取4.最后一个dm_scan_other其他设备扫描。1.dm_init 该函数的作用是从设备树中获取具有dm-pre-reloc属性的设备节点并将其初始化。int dm_init(bool of_live) { int ret; if (gd-dm_root) { dm_warn(Virtual root driver already exists!\n); return -EINVAL; } INIT_LIST_HEAD(DM_UCLASS_ROOT_NON_CONST); #if defined(CONFIG_NEEDS_MANUAL_RELOC) fix_drivers(); fix_uclass(); fix_devices(); #endif ret device_bind_by_name(NULL, false, root_info, DM_ROOT_NON_CONST); if (ret) return ret; #if CONFIG_IS_ENABLED(OF_CONTROL) # if CONFIG_IS_ENABLED(OF_LIVE) if (of_live) DM_ROOT_NON_CONST-node np_to_ofnode(gd-of_root); else #endif DM_ROOT_NON_CONST-node offset_to_ofnode(0); #endif ret device_probe(DM_ROOT_NON_CONST); if (ret) return ret; return 0; }在该dm_init中首先判断了gd-dm_root是否为空如果不为空说明已经完成了这些设备初始化则不需要再一次初始化。ret device_bind_by_name(NULL, false, root_info, DM_ROOT_NON_CONST);从root_info的设备实例化信息中通过name这个属性从设备树中查找找到后创建struct device这个设备实例。其中DM_ROOT_NON_CONST (((gd_t *)gd)-dm_root)用于存储当存初始化到的实例设备。ret device_probe(DM_ROOT_NON_CONST);执行匹配到的设备的probe函数将他们进行初始化。2.dm_scan_platdata平台数据主要是一些开发板的固定设备信息通过一个struct结构体来定义设备的寄存器地址、IRQ 号等。主要存储在u-boot.lds中的 .u_boot_list : {KEEP(*(SORT(.u_boot_list*)));}这一段中后续的初始化流程可以查看我的主页如果没有我很快会持续的更新希望大家能够点点关注。