lowlevel_init 里面实现了cpu 相关硬件初始化:检查复位状态、IO恢复、关看门狗、开发板供电锁存、时钟初始化、DDR初始化、串口初始化并打印'O'、tzpc初始化、打印'K'。lowlevel_init 函数真正的地方,是在uboot/board/samsumg/x210/lowlevel_init.S中。
目录
检查复位状态:
#include <config.h>
#include <version.h>
#include <s5pc110.h>
#include "smdkc110_val.h"
_TEXT_BASE:
.word TEXT_BASE
.globl lowlevel_init
lowlevel_init:
push {lr} //先将lr压栈保存,保存当前链接寄存器地址,等跳转回start.s时继续执行使用
/* check reset status */
ldr r0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET)
ldr r1, [r0]
bic r1, r1, #0xfff6ffff
cmp r1, #0x10000
beq wakeup_reset_pre
cmp r1, #0x80000
beq wakeup_reset_from_didle
- lowlevel_init:直接跳到这边执行。
- 现在复杂cpu有多种复位状态。譬如直接冷上电、热启动、睡眠(低功耗)状态下的唤醒等,这些情况都属于复位。所以我们在复位代码中要去检测复位状态,来判断到底是哪种情况。以上代码就是来检查其复位的状态。
- 判断哪种复位的意义在于:冷上电时DDR是需要初始化才能用的;而热启动或者低功耗状态下的复位则不需要再次初始化DDR。
IO 状态的恢复:
/* IO Retention release */
ldr r0, =(ELFIN_CLOCK_POWER_BASE + OTHERS_OFFSET)
ldr r1, [r0]
ldr r2, =IO_RET_REL
orr r1, r1, r2
str r1, [r0]
- IO复位:在进入低功耗之前记录 IO 的值,以便来保存IO 恢复。(这部分跟复位的部分代码跟主线无关,可以不看)
关看门狗:
/* Disable Watchdog */
ldr r0, =ELFIN_WATCHDOG_BASE /* 0xE2700000 */
mov r1, #0
str r1, [r0]
- 在嵌入式系统中,不可避免的会碰到系统运行时出错的问题,有时候为了使系统能够自动的进行复位,就引入了看门狗的概念,实际上它就是一个计数器,到了一定的值后就会复位cpu,在程序中我们需要在计数器增加到这个值之前对这个计数器做一个复位清零的工作,俗称喂狗,使程序继续运行。
- 在系统初始化的时候,由于我们并没有进行喂狗的工作,为了防止看门狗一直复位cpu,因此我们需要先将其关闭。
接下来是一些SRAM SROM相关GPIO设置,与主线启动代码无关,可以不用管:
/* SRAM(2MB) init for SMDKC110 */
/* GPJ1 SROM_ADDR_16to21 */
ldr r0, =ELFIN_GPIO_BASE
ldr r1, [r0, #GPJ1CON_OFFSET]
bic r1, r1, #0xFFFFFF
ldr r2, =0x444444
orr r1, r1, r2
str r1, [r0, #GPJ1CON_OFFSET]
ldr r1, [r0, #GPJ1PUD_OFFSET]
ldr r2, =0x3ff
bic r1, r1, r2
str r1, [r0, #GPJ1PUD_OFFSET]
/* GPJ4 SROM_ADDR_16to21 */
ldr r1, [r0, #GPJ4CON_OFFSET]
bic r1, r1, #(0xf<<16)
ldr r2, =(0x4<<16)
orr r1, r1, r2
str r1, [r0, #GPJ4CON_OFFSET]
ldr r1, [r0, #GPJ4PUD_OFFSET]
ldr r2, =(0x3<<8)
bic r1, r1, r2
str r1, [r0, #GPJ4PUD_OFFSET]
/* CS0 - 16bit sram, enable nBE, Byte base address */
ldr r0, =ELFIN_SROM_BASE /* 0xE8000000 */
mov r1, #0x1
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]
- 供电锁存:如果是软开关,这个开关是一个不会自锁的按钮,当按下时给芯片通电,弹起时芯片断电,想要给芯片持续供电,需要软件给供电锁存。
初始化时钟、初始化DDR动态内存:
/* 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
bic r1, pc, r0 /* 将pc的值中的某些bit位清0,剩下一些特殊的bit位赋值给r1(r0中为1的那些位清零)相等于:r1 = pc & ~(ff000fff) */
ldr r2, _TEXT_BASE /* 加载链接地址到r2,然后将r2的相应位清0剩下特定位。 */
bic r2, r2, r0 /* r0 <- current base addr of code */
cmp r1, r2 /* compare r0, r1 */
beq 1f /* r0 == r1 then skip sdram init */
/* init system clock */
bl system_clock_init
/* Memory initialize */
bl mem_ctrl_asm_init
1:
/* for UART */
bl uart_asm_init
bl tzpc_init
- cmp r1, r2 :判定当前代码执行的位置在 SRAM 中还是在 DDR 中。为什么要做这个判定?原因1:BL1(uboot的前一部分)在SRAM中有一份,在 DDR 中也有一份,因此如果是冷启动那么当前代码应该是在 SRAM 中运行的BL1,如果是低功耗状态的复位这时候应该就是在 DDR 中运行的。原因2:我们判定当前运行代码的地址是有用的,可以指导后面代码的运行。譬如在 lowlevel_init.S 中判定当前代码的运行地址,就是为了确定要不要执行时钟初始化和初始化 DDR 的代码。如果当前代码是在 SRAM 中,说明冷启动,那么时钟和 DDR 都需要初始化;如果当前代码是在 DDR 中,那么说明是热启动则时钟和 DDR 都不用再次初始化。也就是说如果 r1 和 r2 相等就说明是在DDR运行,就要跳过时钟初始化和初始化DDR。如果 r1 和 r2 不相等就说明是在 SRAM 运行,就要进行 时钟初始化 和 初始化DDR。
- beq 1f: 如果相等就跳转到 1, 1 是标号, f 是向后找, b 是往前找。
- bl system_clock_init :初始化时钟,system_clock_init:函数就在本文件的 205 - 385行。在include\configs\x210_sd.h中300行到428行,都是和时钟相关的配置值。这些宏定义就决定了210的时钟配置是多少。也就是说代码在lowlevel_init.S中都写好了,但是代码的设置值都被宏定义在x210_sd.h中了。因此,如果移植时需要更改CPU的时钟设置,根本不需要动代码,只需要在x210_sd.h中更改配置值即可。
- bl mem_ctrl_asm_init: 初始化DDR动态内存:借鉴以下别人的 ddr 的解析:https://blog.csdn.net/wangdapao12138/article/details/79828923
串口初始化、tzpc初始化、打印 'K':
/* for UART */
bl uart_asm_init
bl tzpc_init
/* Print 'K' */
ldr r0, =ELFIN_UART_CONSOLE_BASE
ldr r1, =0x4b4b4b4b
str r1, [r0, #UTXH_OFFSET]
pop {pc}
- uart_asm_init:串口初始化,初始化完了后通过串口发送了一个'O'。
- tzpc_init:可信任区域的初始化。trust zone初始化
- pop {pc}:这边是函数的返回,lowlevel_init 已经完成。但是在函数返回前通过串口打印'K'。lowlevel_init.S 执行完如果没错那么就会串口打印出"OK"字样。这应该是我们uboot中看到的最早的输出信息。
lowlevel_init.S 中 lowlevel_init 完整代码:
lowlevel_init:
push {lr}
/* check reset status */
ldr r0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET)
ldr r1, [r0]
bic r1, r1, #0xfff6ffff
cmp r1, #0x10000
beq wakeup_reset_pre
cmp r1, #0x80000
beq wakeup_reset_from_didle
/* IO Retention release */
ldr r0, =(ELFIN_CLOCK_POWER_BASE + OTHERS_OFFSET)
ldr r1, [r0]
ldr r2, =IO_RET_REL
orr r1, r1, r2
str r1, [r0]
/* Disable Watchdog */
ldr r0, =ELFIN_WATCHDOG_BASE /* 0xE2700000 */
mov r1, #0
str r1, [r0]
/* SRAM(2MB) init for SMDKC110 */
/* GPJ1 SROM_ADDR_16to21 */
ldr r0, =ELFIN_GPIO_BASE
ldr r1, [r0, #GPJ1CON_OFFSET]
bic r1, r1, #0xFFFFFF
ldr r2, =0x444444
orr r1, r1, r2
str r1, [r0, #GPJ1CON_OFFSET]
ldr r1, [r0, #GPJ1PUD_OFFSET]
ldr r2, =0x3ff
bic r1, r1, r2
str r1, [r0, #GPJ1PUD_OFFSET]
/* GPJ4 SROM_ADDR_16to21 */
ldr r1, [r0, #GPJ4CON_OFFSET]
bic r1, r1, #(0xf<<16)
ldr r2, =(0x4<<16)
orr r1, r1, r2
str r1, [r0, #GPJ4CON_OFFSET]
ldr r1, [r0, #GPJ4PUD_OFFSET]
ldr r2, =(0x3<<8)
bic r1, r1, r2
str r1, [r0, #GPJ4PUD_OFFSET]
/* CS0 - 16bit sram, enable nBE, Byte base address */
ldr r0, =ELFIN_SROM_BASE /* 0xE8000000 */
mov r1, #0x1
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]
/* 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
bic r1, pc, r0 /* r0 <- current base addr of code */
ldr r2, _TEXT_BASE /* 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 */
/* init system clock */
bl system_clock_init
/* Memory initialize */
bl mem_ctrl_asm_init
1:
/* for UART */
bl uart_asm_init
bl tzpc_init
#if defined(CONFIG_ONENAND)
bl onenandcon_init
#endif
#if defined(CONFIG_NAND)
/* simple init for NAND */
bl nand_asm_init
#endif
/* check reset status */
ldr r0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET)
ldr r1, [r0]
bic r1, r1, #0xfffeffff
cmp r1, #0x10000
beq wakeup_reset_pre
/* ABB disable */
ldr r0, =0xE010C300
orr r1, r1, #(0x1<<23)
str r1, [r0]
/* Print 'K' */
ldr r0, =ELFIN_UART_CONSOLE_BASE
ldr r1, =0x4b4b4b4b
str r1, [r0, #UTXH_OFFSET]
pop {pc}