Uboot20之Start.S中lowlevel_init

时间:2018.4.5  作者:Tom   工作:HWE 说明:如需转载,请注明出处。已注明转载

bl    lowlevel_init    /* go setup pll,mux,memory */ 17.调用lowlevel_init函数初始化pll memory等与板子相关的内容 函数位于board目录下

_TEXT_BASE:

    .word    TEXT_BASE

内核加载的地址

    .globl lowlevel_init

lowlevel_init:

    push    {lr}

lr入栈,主要原因是在被调用的函数内还有再次调用函数,而BL只会将返回地址存储到LR中,但是我们只有一个LR,所以在第二层调用函数前要先将LR入栈,否则函数返回时第一层的返回地址就丢了。

    /* check reset status */

    ldr    r0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET)

    ldr    r1, [r0]

读取复位状态。

    bic    r1, r1, #0xfff6ffff

保存16和19位,其他清零,将结果保存至r1中。

    cmp    r1, #0x10000

与第16-19位比较,如果与立即数0x 1相等则属于睡眠模式唤醒。

    beq    wakeup_reset_pre

    cmp    r1, #0x80000

与第16-19位比较,如果与立即数0x 8相等则属于空闲模式。

    beq    wakeup_reset_from_didle

1)复杂CPU允许多种复位情况。譬如直接冷上电、热启动、睡眠(低功耗)状态下的唤醒等,这些情况都属于复位。所以我们在复位代码中要去检测复位状态,来判断到底是哪种情况。

2)判断哪种复位的意义在于:冷上电时DDR是需要初始化才能用的;而热启动或者低功耗状态下的复位则不需要再次初始化DDR。

 

    /* IO Retention release */

    ldr    r0, =(ELFIN_CLOCK_POWER_BASE + OTHERS_OFFSET)

#define ELFIN_CLOCK_POWER_BASE        0xE0100000 (regs.h)

#define OTHERS_OFFSET                0xe000

ELFIN_CLOCK_POWER_BASE + OTHERS_OFFSET = 0xE010E000

    ldr    r1, [r0]

    ldr    r2, =IO_RET_REL

#define IO_RET_REL            ((1 << 31) | (1 << 29) | (1 << 28))

r2 的最高四位都设成1.

    orr    r1, r1, r2

或一下,放入r1。

    str    r1, [r0]

r1放入r0。

这一段代码的作用是IO保持,也就是说一般在休眠状态下,IO是不能用的。

    /* Disable Watchdog */

    ldr    r0, =ELFIN_WATCHDOG_BASE    /* 0xE2700000 */

    mov    r1, #0

    str    r1, [r0]

就是把第一位写0.

WATCHDOG对于没有底层开发经验的开发人员来说,可能比较陌生,但是它在系统起到非常重要的作用,相当于系统警察,当系统发生严重错误(如程序进入死循环等)不能 恢复的时候,WATCHDOG能够让系统重启。WATCHDOG的应用主要是在嵌入式操作系统中,避免了系统在无人干预时长时间挂起的情况。

那么其是如何实现此功能的呢?那么就要简单解释一下其实现原理了:

watchdog 硬件的逻辑就是,其硬件上有个记录超时功能,然后要求用户需要每隔一段时间(此时间可以根据自己需求而配置)去对其进行一定操作,比如往里面写一些固定的值,俗称"喂狗",那么狗发现超时了,即过了这么长时间你没有给狗喂食,那么狗就认为你系统是死机了,出问题了,我就帮你重启系统。说白了就是弄个看家狗dog,你要定期给其喂食,如果超时不喂食,那么狗就认为你,他的主人,你的系统,死机了,就帮你 reset 重启系统。

为何在要系统初始化的时候关闭 watchdog:

了解了watchdog的原理后,此问题就很容易理解了。如果不禁用watchdog,那么就要单独写程序去定期"喂狗",那多麻烦,多无聊啊。毕竟咱此处只是去用uboot初始化必要的硬件资源和系统资源而已,完全用不到这个watchdog的机制。需要用到,那也是你linux内核跑起来了,是你系统关心的事情,和我uboot没啥关系的,所以肯定此处要去关闭watchdog(的reset功能)了。

    /* 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]

上面这段代码是用来初始化外置的SRAM总线的GPIO状态。

此时我们没有用到外置SRAM,与主线启动代码无关,不用管

    /* PS_HOLD pin(GPH0_0) set to high */

    ldr    r0, =(ELFIN_CLOCK_POWER_BASE + PS_HOLD_CONTROL_OFFSET)

#define ELFIN_CLOCK_POWER_BASE        0xE0100000

#define PS_HOLD_CONTROL_OFFSET        0xe81c

结果就是:0xe010 e81c

    ldr    r1, [r0]

    orr    r1, r1, #0x300    

将第8位和第9位设置为1。

    orr    r1, r1, #0x1    

将第0位设置为1,使能。

    str    r1, [r0]

开发板供电锁存。

读---》改-----》写

 

总结:在前100行,lowlevel_init.S中并没有做太多有意义的事情(除了关看门狗、供电锁存外),然后下面从110行才开始进行有意义的操作。

    /* 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 */

这句代码的意义是:将pc的值中的某些bit位清0,剩下一些特殊的bit位赋值给r1(r0中为1的那些位清零)相等于:r1 = pc & ~(ff000fff)

    ldr    r2, _TEXT_BASE        /* r1 <- original base addr in ram */

    bic    r2, r2, r0        /* r0 <- current base addr of code */

加载链接地址到r2,然后将r2的相应位清0剩下特定位。

    cmp r1, r2 /* compare r0, r1 */

最后比较r1和r2.

    beq 1f            /* r0 == r1 then skip sdram init */ 跳转到1:

这几行代码的作用就是判定当前代码执行的位置在SRAM中还是在DDR中。为什么要做这个判定?

原因1:BL1(uboot的前一部分)在SRAM中有一份,在DDR中也有一份,因此如果是冷启动那么当前代码应该是在SRAM中运行的BL1,如果是低功耗状态的复位这时候应该就是在DDR中运行的。

原因2:我们判定当前运行代码的地址是有用的,可以指导后面代码的运行。譬如在lowlevel_init.S中判定当前代码的运行地址,就是为了确定要不要执行时钟初始化和初始化DDR的代码。如果当前代码是在SRAM中,说明冷启动,那么时钟和DDR都需要初始化;如果当前代码是在DDR中,那么说明是热启动则时钟和DDR都不用再次初始化。

/* init system clock */

    bl system_clock_init


    /* Memory initialize */

    bl mem_ctrl_asm_init

1)该函数用来初始化DDR

2)函数位置在uboot/cpu/s5pc11x/s5pc110/cpu_init.S文件中。

3)该函数和裸机中初始化DDR代码是一样的。实际上裸机中初始化DDR的代码就是从这里抄的。配置值也可以从这里抄,但是当时我自己根据理解+抄袭整出来的一份。

4)配置值中其他配置值参考裸机中的解释即可明白,有一个和裸机中讲的不一样。DMC0_MEMCONFIG_0,在裸机中配置值为0x20E01323;在uboot中配置为0x30F01313.这个配置不同就导致结果不同。

在 裸机中DMC0的256MB内存地址范围是0x20000000-0x2FFFFFFF;

在uboot中DMC0的256MB内存地址范围为0x30000000-0x3FFFFFFF。

5)之前在裸机中时配置为2开头的地址,当时并没有说可以配置为3开头。从分析九鼎移植的uboot可以看出:DMC0上允许的地址范围是20000000-3FFFFFFF(一共是512MB),而我们实际只接了256MB物理内存,SoC允许我们给这256MB挑选地址范围。

6)总结一下:在uboot中,可用的物理地址范围为:0x30000000-0x4FFFFFFF。一共512MB,其中30000000-3FFFFFFF为DMC0,40000000-4FFFFFFF为DMC1。

7)我们需要的内存配置值在x210_sd.h的438行到468行之间。分析的时候要注意条件编译的条件,配置头文件中考虑了不同时钟配置下的内存配置值,这个的主要目的是让不同时钟需求的客户都能找到合适自己的内存配置值。

8)在uboot中DMC0和DMC1都工作了,所以在裸机中只要把uboot中的配置值和配置代码全部移植过去,应该是能够让DMC0和DMC1都工作的。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值