MTK6735 pre-loader源代码分析
参考:http://blog.chinaunix.net/uid-28458801-id-3487199.html
一.简介
因为bootloader的一部分和系统有关,所以MTK为了不同的应用将它分为两部分的bootloader:
(1)第1部分bootloader,也就是MTK内部(in-house)的pre-loader,这部分依赖平台,这部分有BootROM来加载到内部的ISRAM中执行。
(2)第2部分bootloader,也就是u-boot,这部分依赖操作系统,由pre-loader加载到外部DRAM中执行。负责引导linux操作系统和Android框架,但是从Android 4.1(jelly bean)开始,MTK采用little kernel来替代U-boot。
正常启动的主要工作如下:
(1)设备上电后,Boot ROM开始运行。
(2)Boot ROM初始化软件堆栈(softwarestack)、通信端口和可引导存储设备(比如NAND/EMMC)。
(3)Boot ROM从存储器中加载pre-loader到内部SRAM(ISRAM)中,因为这时候还没有初始化外部的DRAM。
(4)Boot ROM跳转到pre-loader的入口处并执行。
(5)Pre-loader初始化DRAM和加载U-Boot到RAM中。
(6)Pre-loader跳转到U-Boot中并执行,然后U-Boot做一些初始化,比如显示的初始化等。
(7)U-Boot从存储器中加载引导镜像(boot image),包括linux内核和ramdisk(最小文件系统)
(8)U-Boot跳转到linux内核并执行。
二. Pre-loader的过程(procedure)和流程(flow)
三. Pre-loader启动过程的分析
3.1 resethandler()
这是pre-loader的入口函数,在bootable/bootloader/preloader/platform/mt6735/src/init/init.s中定义,下面类分下此函数的代码:
- resethandler :
- MOV r0, #0
- MOV r1, #0
- MOV r2, #0
- MOV r3, #0
- MOV r4, #0
- MOV r5, #0
- MOV r6, #0
- MOV r7, #0
- MOV r8, #0
- MOV r9, #0
- MOV r10, #0
- MOV r11, #0
- MOV r12, #0
- MOV sp, #0
- MOV lr, #0
r0~r12是通用寄存器,可保存数据和地址,r13(sp)、r14(lr)和r15(pc)是ARM处理器为特殊的任务或是专门的功能指定的寄存器。
(1)r13通常用作堆栈指针(sp)
指向当前处理器模式的堆栈的栈顶,RM处理器针对不同的模式,共有 6 个堆栈指针(SP),其中用户模式和系统模式共用一个SP,每种异常模式都有各自专用的R13寄存器(SP)。它们通常指向各模式所对应的专用堆栈,也就是ARM处理器允许用户程序有六个不同的堆栈空间。这些堆栈指针分别为R13、R13_svc、R13_abt、R13_und、R13_irq、R13_fiq。
(2)r14链接寄存器(lr),保存调用子程序的返回地址。
(3)r15是程序计数器(pc),其内容是处理器要取的下一条指令的地址。
这里是把这些寄存器的内容清零。
- /* set the cpu toSVC32 mode */
- MRS r0,cpsr
- BIC r0,r0,#0x1f
- ORR r0,r0,#0xd3
- MSR cpsr,r0
- /* disable interrupt */
- MRS r0, cpsr
- MOV r1, #INT_BIT
- ORR r0, r0, r1
- MSR cpsr_cxsf, r0
最后1行表示把r0寄存器的值写入cpsr寄存器对应的4个控制域
c 控制域屏蔽 psr[7..0]
x 扩展域屏蔽 psr[15..8]
s 状态域屏蔽psr[23..16]
f 标志域屏蔽psr[31..24]
注意:区域名必须为小写字母
(1)MRS和MSR指令
MRS: Move to Register from Stateregister
MSR: Move to State register fromRegister
http://blog.csdn.net/mr_raptor/article/details/6556172
(2)设置CPU为管理模式和屏蔽中断
Cpsr (Current Program Status Register)是当前程序状态寄存器
图4
设置cpsr[7:0]=d3,表示pre-loader禁用中断请求(interrupt request)和快速中断请求(fast interrupt request);T=0表示ARM状态。M4~M0=1011表示管理模式。
- /* enable I+Z bits */
- MRC p15, 0, ip, c1, c0, 0 /*read SCTLR*/
- ORR ip, ip, #0x1800 /* I+Z bits */
- p15, 0, ip, c1, c0, 0 /*write SCTLR*/
第1行代码是通过设置系统控制寄存器(system control register)的I和Z位为1来分别使能Instructioncaching(指令高速缓存)与Program flowprediction(程序流预测)
(1)MRC和MCR是协处理器命令,这里的C指coprocessor
MRC{<cond>}p15,<opcode_1>, <Rd>, <CRn>, <CRm>{,<opcode_2>}
<cond>:为指令执行的条件码。当<cond>忽略时指令为无条件执行
p15:指协处理器CP15
<opcode_1>:操作码1
<Rd>:ARM处理器的寄存器,对于MRC命令来说是目的寄存器。
<CRn>:协处理器寄存器,可为C0,C1,…,C15,CP15的首要寄存器(primary coprocessor)
<CRm>:CP15的次要(辅助/操作)寄存器(secondary/operational coprocessor)
<opcode_2>:操作码2
http://blog.chinaunix.net/uid-24517893-id-253685.html
(2)MRC p15, 0, ip, c1,c0, 0
这里我们是怎么知道读取SCTLR寄存器的值,然后放入到ARM处理器寄存器ip(ip是什么寄存器?),我们先来看《DDI0388F_cortex_a9_r2p2_trm.pdf》下面相关部分:
图5
因为CRn=c1,所以我们来看CP15 c1寄存器部分,如下:
图6
这就是当CRn=c1时我们可以CP15的寄存器,有SCTLR、ACTLR、CPACR等等。可知MRC p15, 0, ip, c1, c0, 0就是读取系统控制寄存器的值(SystemControl Register),如果要知道系统控制寄存器的具体内容就要详细看其介绍。
- ORR ip, ip, #0x1800
- MCR p15, 0, ip, c1,c0, 0
由此可见是要设置SCTLR[12:11]=0b11,我们给出这两位的说明:
图7
到此我们就可以理解上面代码的意义了。
三. Main()
bootable/bootloader/preloader/platform/mt6735/src/core/main.c
这里要特别注意就是不能在调用bldr_pre_process()之前调用串口输出信息的函数,比如print(),否则无法启动,而且还无法再次烧录。上面这些函数都是以bldr开头,这是bootloader的简称。
main
mtk_uart_init(UART_SRC_CLK_FRQ, CFG_LOG_BAUDRATE); //初始化UART, 设置波特率为921600
bldr_pre_process(); //3.2.1
3.1 bldr_pre_process()
/* essential hardware initialization. e.g. timer, pll, uart... */
platform_pre_init();
g_boot_mode = NORMAL_BOOT;
/* hardware initialization */
platform_init();
3.1.1 platform_pre_init()
此函数主要是做一些基本的硬件初始化,包括定时器、pll、串口等
platform_pre_init();
/* init timer */
mtk_timer_init();
/* init pll */
mt_pll_init();
/* init uart baudrate when pll on */
mtk_uart_init(UART_SRC_CLK_FRQ, CFG_LOG_BAUDRATE);
/*GPIO init*/
mt_gpio_init();
mt_gpio_set_default(); //mtk GPIO默认配置
clk_buf_all_on(); //打开clk buf
#if (CFG_USB_UART_SWITCH) //如果设置了这个宏
if (is_uart_cable_inserted()) //插入了uart
set_to_uart_mode(); //切换到uart 模式
//retry 3 times for pmic wrapper init,提高给其他模块调用的API,这里使能一些状态寄存器
pwrap_init_preloader();
//i2c hw init,初始化I2C的引脚,SDA,SCL
i2c_hw_init();
pmic_ret = pmic_init(); //另外单独分析,proload电压管理
mt_pll_post_init(); //初始化pll
//enable long press reboot function ,设置长按重启
PMIC_enable_long_press_reboot();
platform_core_handler
for (i = NR_CPUS - 1; i > 0; --i)
spm_mtcmos_ctrl_cpu(i, STA_POWER_DOWN, 0); //关闭非启动cpu
ptp_init //初始化
1)mtk_timer_init()
MT6577有7个GPT(General-Purpose Timer,通用计时/定时器),其中包括5个32位定时器和1个64位定时器。每个定时器有4种工作模式,分别是ONE-SHOT(一次使用的)、REPEAT(重复使用)、KEEP-GO(继续使用)和FREERUN(自有运行的)。每个定时器工作的时钟源可以是RTC时钟(32.768kHz)或是系统时钟(13MHz)。
图11
此函数通过主要内容如下:
1) 设置PERI_GLOBALCON_PDN0(C1000010,peripheral power-down 0 register for AP side)寄存器的GPT_PDN=1来给GPT上电。
2) 清空和停止GPT4定时器
通过设置GPT4_CON(C1002040)定制器来实现,代码如下:
- *GPT4_CON = 0x0; //disable
- *GPT4_CON = 0x2; //clear counter
*GPT4_CON = 0x0; //disable *GPT4_CON = 0x2; //clear counter
3) 使能GPT4定时器
- //enable REN Bitfor GPT count error on free run mode
- *GPT4_CLK =((GPT4_SYS_CLK)|GPT4_REN_CLK);
- *GPT4_CON =(GPT4_EN|GPT4_FREERUN);
//enable REN Bitfor GPT count error on free run mode *GPT4_CLK =((GPT4_SYS_CLK)|GPT4_REN_CLK); *GPT4_CON =(GPT4_EN|GPT4_FREERUN);
4) 复位GPT4定时器
(2)platform_chip_ver()
图12
通过读取对应寄存器的值来获取chip ID、hardware version和software version。
这里我们读出CHIP_SUBID=0X00008A00,相当于CHIP_6577_E1,根据MTK给出的说明:在上电后CPU 0和1可能没有明确复位,需要手动复位,这通过设置RST_CTL0(MCUSYS复位控制寄存器0)的SW_CPU_RST位来实现:
图13
(3)mt6577_pll_init()
这部分内容比较多,后续作为单独一部分来介绍。
(4)mtk_uart_init(UART_SRC_CLK_FRQ,CFG_LOG_BAUDRATE)
使用默认的时钟源和921600波特率来初始化UART1。
(5)初始化PMIC的I2C接口和初始化PMIC(MT6329)
(6)根据DDR的类型,如果为DDR2或是DDR3则重新初始化PLL,那就需要重新初始化串口和I2C;如果为DDR1则不需要了。
3.1.2 platform_init()
当检测到按下power按键或是USB/充电线插入,pre-loader调用rtc_bbpu_power_on()函数来锁存RTC的PWBB来保持设备的一直供电,这样就算是松开power按键设备也不会关机。
(2)platform_emergency_download()
如果同时按下下面3个按键:
- #define KPD_DL_KEY1 8 /* KEY_POWER */
- #defineKPD_DL_KEY2 9 /* KEY_VOLUMEUP */
- #defineKPD_DL_KEY3 0 /* KEY_VOLUMEDOWN */
#define KPD_DL_KEY1 8 /* KEY_POWER */ #defineKPD_DL_KEY2 9 /* KEY_VOLUMEUP */ #defineKPD_DL_KEY3 0 /* KEY_VOLUMEDOWN */
这3个除了power按键,其他2个应该是可以自定义的。
Emergency DownloadMode紧急下载模式,它就是一个刷机模式,这里是同时按下这3个按键后进入紧急下载模式,但是不知道为什么就关机了,串口输出信息如下:
- ……………
- [PreLoader_mt6577_detect_powerkey]Press
- power key ispressed
- [PLFM] Power keyboot!
- platform_init()g_boot_reason=0
- Entermt6577_kpd_gpio_set!
- [PreLoader_mt6577_detect_powerkey]Press
- power key ispressed
- download keys arepressed
- [PLFM] emergencydownload mode(timeout: 300s).
- mtk_arch_reset atpre-loader!
- €€€€€€€€
3.1.4 uart_handshake_init()
META mode:MobileEngineering Test Architecture
if(mode == NORMAL_BOOT) //如果是NORMAL_BOOT
(1)切换到META端口
mtk_serial_set_current_uart(CFG_UART_META);
(2)判断META和log端口是否一致,如果一致,就采用META的波特率来初始化META端口。并且同时关闭log,这样log信息会保持在log缓冲区中。
if (CFG_UART_META == CFG_UART_LOG) {
/* to prevent sync error with PC */
gpt_busy_wait_us(160);
/* init to meta baudrate */
mtk_uart_init(UART_SRC_CLK_FRQ, CFG_META_BAUDRATE);
/* disable log so that log message will be kept in log buffer */
log_buf_ctrl(0);
log_ctrl(0);
}
(3)通过META端口发送ready给下载工具。
uart_send((u8*)HSHK_COM_READY, strlen(HSHK_COM_READY));
(4)切换回log端口。
3.1.7 part_dump()
打印分区信息,包括分区名和分区占用的大小。
3.1.8 sec_lib_init()
安全库的初始化
3.2 bldr_handshake()
通过USB或是UART和PC机运行的下载工具握手。
3.3 宇龙的ABOOT
// add begin by wangping@yulong.com 20150710
#ifdef YL_DOUBLE_ABOOT
if (NULL == (bootdev = blkdev_get(CFG_BOOT_DEV))) {
print("%s can't find boot device(%d)\n", MOD, CFG_BOOT_DEV);
/* FIXME, should change to global error code */
goto error;
}
recovery_flag = CFG_UBOOT_MEMADDR; // 閲囩敤DRAM鍦板潃锛屼笉閲囩敤SRAM鍦板潃锛堝爢鏍堬級闃叉鍫嗘爤婧㈠嚭
part = part_get("yl_params");
src = part->start_sect * bootdev->blksz;
blkdev_read(bootdev, src, 512 * 4, (char*)recovery_flag, part->part_id);
print("MutiBootloader=%x\n ", *((char*)recovery_flag + 0x6c5));
boot_recovery = *((char*)recovery_flag + 0x6c5);
// boot_recovery = 1; // for test
#endif
// add end.
3.4.sec_boot_check();
/* security check */
3.5.device_APC_dom_setup
3.8 bldr_post_process()
preloader部分对平台部分的后处理
platform_post_init
跳转到uboot起始地址处并执行。此时preloader的工作结束,转入到uboot阶段。