S5PV210-uboot解析(二)-start.S解析-lowlevel_init函数分析

时间:2021-05-14 19:27:22
lowlevel_init函数分析

lowlevel_init函数是uboot启动阶段调用的第一个函数,主要功能就是初始化S5PV210的各个硬件部分。

#include <config.h>
#include <version.h>
 
#include <s5pc110.h>
#include "smdkc110_val.h"

包含头文件,主要是s5pc110.h文件,里面包含了板子的硬件寄存器的地址。

lowlevel_init:
     push    {lr}

压栈,保存现场,否则函数返回时返回地址就丢了。
 
     /* check reset status  */
      
     ldr r0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET) //0xE0100000+0xa000
     ldr r1, [r0]
     bic r1, r1, #0xfff6ffff
     cmp r1, #0x10000
     beq wakeup_reset_pre
     cmp r1, #0x80000
     beq wakeup_reset_from_didle

检查复位状态,查询数据手册(section 02_system 4.10.3 RESET CONTROL REGISTER)。位清除后保留了bit16和bit19,bit16置1为睡眠模式,跳转到 wakeup_reset_pre,bit19置1,ARM将从DEEP-IDLE模式重置,因此跳转到 wakeup_reset_from_didle
 
     /* IO Retention release */
     ldr r0, =(ELFIN_CLOCK_POWER_BASE + OTHERS_OFFSET) //0xE0100000+0xe000
     ldr r1, [r0]
     ldr r2, =IO_RET_REL //((1 << 31) | (1 << 29) | (1 << 28))
     orr r1, r1, r2
     str r1, [r0]

恢复I/O状态,查数据手册这3位分别对应GPIO、MMC、UART,都是禁止使能。但从描述上来看会自动清零复位。 If you want to disable RELEASE_RET_GPIO, set to 1. After RELEASE_RET_GPIO becomes OFF, this bit will be cleared to 0. 和主线的启动代码关系不大。
 
     /* Disable Watchdog */
     ldr r0, =ELFIN_WATCHDOG_BASE   /* 0xE2700000 */
     mov r1, #0
     str r1, [r0]

关看门狗,给WTCON寄存器写0,主要是bit5禁止看门狗定时器。
 
     /* SRAM(2MB) init for SMDKC110 */
     /* GPJ1 SROM_ADDR_16to21 */
     ldr r0, =ELFIN_GPIO_BASE //0xE0200000
      
     ldr r1, [r0, #GPJ1CON_OFFSET] //0x260
     bic r1, r1, #0xFFFFFF
     ldr r2, =0x444444
     orr r1, r1, r2
     str r1, [r0, #GPJ1CON_OFFSET]

给GPJ1CON寄存器写了6个0100,都是设置SROM_ADDR_16to22。
 
     ldr r1, [r0, #GPJ1PUD_OFFSET] //0x268
     ldr r2, =0x3ff
     bic r1, r1, r2
     str r1, [r0, #GPJ1PUD_OFFSET]

设置GPJ1PUD寄存器,全部为Pull-up/down disable,禁止上拉或下拉。
 
     /* GPJ4 SROM_ADDR_16to21 */
     ldr r1, [r0, #GPJ4CON_OFFSET] //0x2c0
     bic r1, r1, #(0xf<<16)
     ldr r2, =(0x4<<16)
     orr r1, r1, r2
     str r1, [r0, #GPJ4CON_OFFSET]

设置GPJ4CON,bit[19:16]为0001,OUTPUT。
 
     ldr r1, [r0, #GPJ4PUD_OFFSET] //0x2c8
     ldr r2, =(0x3<<8)
     bic r1, r1, r2
     str r1, [r0, #GPJ4PUD_OFFSET]

设置GPJ4PUD的bit[9:8]为00,Pull-up/down disable。
 
 
     /* CS0 - 16bit sram, enable nBE, Byte base address */
     ldr r0, =ELFIN_SROM_BASE   /* 0xE8000000 */
     mov r1, #0x1
     str r1, [r0]

设置SROM的数据位宽,Memory Bank0为16bit。有点无意义,数据手册上写bank0只能用16bit,且不能更改。
 
     /* PS_HOLD pin(GPH0_0) set to high */
     ldr r0, =(ELFIN_CLOCK_POWER_BASE + PS_HOLD_CONTROL_OFFSET)) //0xE0100000+0xe81c
     ldr r1, [r0]
     orr r1, r1, #0x300   
     orr r1, r1, #0x1 
     str r1, [r0]

拉高PS_HOLD,将bit1置1使能PS_HOLD输出,bit8置1设置高电平,bit9置1设置对外输出。
从关看门狗后面的代码大部分都是和GPIO相关的,和主线启动并没有太多关联,主要和启动有关的是供电锁存。

     /* 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   */
 
判断当前代码执行位置,r1存放当前代码地址,r2存放存放_TEXT_BASE链接地址,经过位清除后只比较两个地址的bit12~bit23,如果相等说明当前地址在DDR中,就跳过下面的SDRAM初始化过程。b 1f意思是跳转到标号"1"的位置,f表示向前(下)寻找(forward),向后(上)寻找则是b(backward)。

     /* init system clock */
     bl system_clock_init
 
     /* Memory initialize */
     bl mem_ctrl_asm_init

system_clock_init函数就在lowlevel_init.S文件中,是用来初始化时钟的。里面初始化时钟的操作都很固定,时钟的配置都在configs/x210_sd.h中,如果要更改时钟配置,只需要更改头文件中的配置值即可。
mem_ctrl_asm_init函数在cpu/s5pc11x/s5pc110/cpu_init.S文件中,用来初始化DDR内存的。
      
1:
     /* for UART */
     bl uart_asm_init
 
初始化串口,函数实现在当前文件中,结束函数返回调用时地址之前有这样一个操作,打印了一个‘O’,之后在lowlevel_init函数结束前也有一个类似的操作。主要目的是打印调试信息。
ldr r1, = 0x4f4f4f4f
str r1, [r0, #UTXH_OFFSET]        @ 'O'
 
mov pc, lr

     bl tzpc_init

设置TZPC,找到函数入口,注释说明是 Setting TZPC[TrustZone Protection Controller]。查阅资料得知TrustZone是与安全有关的技术,与启动关联不大。
 
#if defined(CONFIG_ONENAND)
    bl onenandcon_init
#endif
 
#if defined(CONFIG_NAND)
     /* simple init for NAND */
     bl nand_asm_init
#endif

这两段代码无关,我用的板子是iNand,没有定义这两个宏。
 
     /* check reset status  */
      
     ldr r0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET) //0xE0100000+0xa000
     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]

不清楚,数据手册上找不到0xE010C300地址的寄存器,上网查询资料也几乎没有,google了一点资料,应该是Adaptive Body Biasing,与电压相关,和启动关联不大。
 
     /* Print 'K' */
     ldr r0, =ELFIN_UART_CONSOLE_BASE //0xE2900000+0x0000
     ldr r1, =0x4b4b4b4b
     str r1, [r0, #UTXH_OFFSET] //0x20

往串口发送0x4b,对应ASCII码就是'K',和前面初始化UART的函数最后发的'O'组成"OK"。相当于调试信息,当启动uboot时看到"OK"说明lowlevel_init函数运行成功。

     pop {pc}

弹栈,返回调用时的地址。后面还有些内容,比如lowlevel_init函数内部调用的函数的实现、TTB(转换表)等内容。
从启动方面,lowlevel_init主要做了 检查复位状态、IO恢复、关看门狗、开发板供电锁存、时钟初始化、DDR初始化、串口初始化并打印'O'、打印'K'。 值得关注的是 关看门狗、开发板供电锁存、时钟初始化、DDR初始化、打印"OK"。