这节继续前文分析lowlevel_init
函数。那么这个函数究竟在哪里呢?让我们再看一眼前面定义的链接器脚本中的.text
段。
.text:
{
cpu/s5pc11x/start.o (.text)
cpu/s5pc11x/s5pc110/cpu_init.o (.text)
board/samsung/x210/lowlevel_init.o (.text)
cpu/s5pc11x/onenand_cp.o (.text)
cpu/s5pc11x/nand_cp.o (.text)
cpu/s5pc11x/movi.o (.text)
common/secure_boot.o (.text)
common/ace_sha1.o (.text)
cpu/s5pc11x/pmic.o (.text)
*(.text)
}
这里很清楚的指示了lowlevel_init
可能出现在lowlevel_init.o
对应的文件中(也可以在cpu_init.o
对应的文件中查找,然而根本没找到),我们打开board/samsung/x210/lowlevel_init.S
中文件就能发现我们的目标函数,下面只讲一些重点部分。
关闭看门狗
ldr r0, =ELFIN_WATCHDOG_BASE /* 0xE2700000 */
mov r1, #0
str r1, [r0]
开发板置锁
/* PS_HOLD pin(GPH0_0) set to high */
ldr r0, =(ELFIN_CLOCK_POWER_BASE + PS_HOLD_CONTROL_OFFSET)
ldr r1, [r0]
orr r1, r1, #0x300
orr r1, r1, #0x1
str r1, [r0]
- 所谓置锁就是把信号锁存住,让它一直维持在高或低
- 下面是开发板电源模块线路设计,在输入电源与开发板电源之间接了一个稳压芯片
MP1482
,这个芯片有一个EN(Enable)引脚,这个引脚可以让稳压芯片输出(为高电平时)或关闭输出(为低电平时)。
-
EN
引脚有一个VDD_IN
电源输入,但这个电源输入被
所阻挡,因此EN
引脚是否有电压取决于该硬件是否导电,而它是否通路又取决于它下面的1
这个点 - 当
POWER
按键被按下后,1
点被接通,则EN
引脚产生电压。当按键放开后电压消失 - 当
EINT0
引脚为POWER_LOCK
高电平时,1
点也能被接通。EN
也会产生电压 -
EINT1
引脚是用来做中断,提供给CPU用来唤醒的 - 总结如下
- 根据代码中的注释,我们在数据手册里搜索
PS_HOLD
,找到了如下的定义
- 从上面的数据手册可以看到只有第0,8,9位有意义,当第0为设置为1时才能使用PS_HOLD_CONTROL功能,第9位定义了信号方向,它是往外输出所以设置为1,而我们要设置为高电平,所以第8位也为1
- 所以我们要实现置锁功能就将第0,8,9位均设置为1即可,下面对照代码来看
-
ldr r0, =(ELFIN_CLOCK_POWER_BASE + PS_HOLD_CONTROL_OFFSET)
将地址写入r0
寄存器 -
ldr r1, [r0]
用寄存器间接寻址读出r0
中的值存入r1
中 -
orr r1, r1, #0x300
将r1中的值与0x300进行位或运算,结果存入r1中,0x300其实就是11 0000 0000,因此就是把第八位与第九位设置为1,其余值保持不变 -
orr r1, r1, #0x1
第上面的功能相同,结果就是再把第0位设置为1,这样就把第0,8,9位全都设置为1了 -
str r1, [r0]
再写回去
- 这样就完成了开发板置锁功能。当程序执行完成这段代码之后就始终保持有电状态,如果将这段代码注销,则必须按住电源键才能使开发板有电
判断当前代码执行位置
/* when we already run in ram, we don't need to relocate U-Boot.
* and actually, memory controller must be configured before U-Boot
* is running in ram.
*/
ldr r0, =0xff000fff
/*将pc的值中的某些比特位清零,剩下一些特殊的比特位赋值给r1(r0中为1的那些位清零),相当于 r1 = pc & ~(0xff000fff)。*/
bic r1, pc, r0 /* r0 <- current base addr of code */
ldr r2, _TEXT_BASE /* 加载链接地址到r2,然后将r2的相应位清0剩下特定位 r1 <- original base addr in ram */
bic r2, r2, r0 /* r0 <- current base addr of code */
cmp r1, r2 /* compare r0, r1 */
beq 1f /* r0 == r1 then skip sdram init */
- 这几行代码的作用就是判定当前代码执行的位置在SRAM中还是在DDR中
- BL1(uboot的前一部分)在SRAM中有一份,在DDR中也有一份,因此如果是冷启动那么当前代码应该是在SRAM中运行的BL1,如果是低功耗状态的复位这时候应该就是在DDR中运行的。
- 我们判定当前运行代码的地址来指导后面代码的运行。譬如在lowlevel_init.S中,判定当前代码的运行地址就是为了确定要不要执行时钟初始化和初始化DDR的代码。如果当前代码是在SRAM中,说明是冷启动,那么时钟和DDR都需要初始化;如果当前代码是在DDR中,那么说明是热启动则时钟和DDR都不用再次初始化。
- 通过
cmp
判断,如果相等则是在DDR中,不相等则是在SRAM中 -
beq
指令表示如果相等就跳转,1代表标号,f代表向下找,b表示向前找(如果有b的话)