文件:u-boot/board/samsung/mini6410/lowlevel_init.S
#include <config.h>
#include <version.h>
#include <s3c6410.h>
#include "mini6410_val.h"
_TEXT_BASE:
.word TEXT_BASE @每个lds里面的模块,都可以定义一个TEXT_BASE。
.globl lowlevel_init
lowlevel_init:
mov r12, lr @保存PC值到r12
/* LED on only #8 */
ldr r0, =ELFIN_GPIO_BASE @GPIO的基地址=0x7f008000
ldr r1, =0x55540000
str r1, [r0, #GPNCON_OFFSET] @设置GPN15到GPN9(GPN15-GPN13为boot中断?,GPN12为IR红外接口,GPN11/GPN9为外部GPIO口,GPN10为SDIO口使用)为输出管脚;
@GPN8到GPN0(GPN0-GPN5为mini6410的K1到K6的6个按键,GPN7为DM9000网络芯片的INT中断引脚,GPN6为外部GPIO口,GPN8为USB OTG中断接口)为输入管脚。
ldr r1, =0x55555555
str r1, [r0, #GPNPUD_OFFSET] @全部下拉使能
ldr r1, =0xf000
str r1, [r0, #GPNDAT_OFFSET] @设置GPN15-GPN12为1,GPN11-GPN9为1,GPN8-GPN0为输入管脚,设置无效,外部是什么信号,数据就是什么。
ldr r0, =ELFIN_GPIO_BASE
ldr r1, =0x1
str r1, [r0, #GPECON_OFFSET] @设置GPE4-GPE1(mini6410保留给GPIO)为输入,GPE0(mini6410为LCD的夜晚背光on或off)为输出。
ldr r1, =0x0
str r1, [r0, #GPEDAT_OFFSET] @设置GPE0输出0。
ldr r0, =ELFIN_GPIO_BASE
ldr r1, =0x2A5AAAAA
str r1, [r0, #GPPCON_OFFSET] @设置GPP14(mini6410为GPIO)为输入,GPP13-GPP11(GPP13,GPP12在mini6410为GPIO,GPP11为WiFi_PD),GPP8-GPP0(mini6410为GPIO,GPP7-GPP2为Flash,GPP1和GPP0为mem0暂时未用)为MEM0_XXX,GPP10-GPP9(mini6410下GPP9为GPIO,GPP10为WiFi_IO)为输出。
ldr r1, =0x0
str r1, [r0, #GPPDAT_OFFSET] @设置所有输出为0。
ldr r1, =0x55555555
str r1, [r0, #MEM1DRVCON_OFFSET] @设置存储器端口1(mini6410为RAM)芯片管脚的电平为7毫安或10mA。
/* Disable Watchdog */
ldr r0, =0x7e000000 @0x7e004000
orr r0, r0, #0x4000
mov r1, #0
str r1, [r0] @禁止看门狗定时器、复位功能、中断停止等
@ External interrupt pending clear
ldr r0, =(ELFIN_GPIO_BASE+EINTPEND_OFFSET) /*EINTPEND*/
ldr r1, [r0]
str r1, [r0] @读一次外部中断引脚,读完之后中断信号就都被清除,上面已经禁止了中断,所以读完之后就不会再产生中断了。
ldr r0, =ELFIN_VIC0_BASE_ADDR @0x71200000 中断控制器0
ldr r1, =ELFIN_VIC1_BASE_ADDR @0x71300000 中断控制器1
@ Disable all interrupts (VIC0 and VIC1)
mvn r3, #0x0 @0取反,为全1
str r3, [r0, #oINTMSK] @中断使能清除寄存器全部设置1,清除所有中断使能
str r3, [r1, #oINTMSK]
@ Set all interrupts as IRQ
mov r3, #0x0
str r3, [r0, #oINTMOD] @所有中断设置为IRQ中断。
str r3, [r1, #oINTMOD]
@ Pending Interrupt Clear
mov r3, #0x0
str r3, [r0, #oVECTADDR] @设置矢量地址寄存器(当前中断的矢量地址)为全0。
str r3, [r1, #oVECTADDR]
/* init system clock */
bl system_clock_init @初始化系统时钟。
/* for UART */
bl uart_asm_init @初始化UART串口。
#if defined(CONFIG_NAND)
/* simple init for NAND */
bl nand_asm_init @NAND FLASH初始化。
#endif
#if 0
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 */
#endif
bl mem_ctrl_asm_init @内存控制初始化(参考cpu_init.S)。
#if 1
ldr r0, =(ELFIN_CLOCK_POWER_BASE+RST_STAT_OFFSET) @RESET状态,0x7E00F904
ldr r1, [r0]
bic r1, r1, #0xfffffff7
cmp r1, #0x8
beq wakeup_reset @SLEEP模式唤醒导致的reset。
#endif
1:
ldr r0, =ELFIN_UART_BASE
ldr r1, =0x4b4b4b4b
str r1, [r0, #UTXH_OFFSET] @UART发送缓冲区设置为'K'。
mov lr, r12
mov pc, lr @初始化完成,返回。
#if 1
wakeup_reset:
/*Clear wakeup status register*/
ldr r0, =(ELFIN_CLOCK_POWER_BASE+WAKEUP_STAT_OFFSET)
ldr r1, [r0]
str r1, [r0] @写一次唤醒状态寄存器,可以清除掉这些状态。应该写1,这里为什么只写了一下呢?
/*LED test*/
ldr r0, =ELFIN_GPIO_BASE
ldr r1, =0x3000
str r1, [r0, #GPNDAT_OFFSET] @设置GPN14和GPN13的值为1,跟mini6410实际不一致,mini6410的LED在GPK4,5,6,7。
/*Load return address and jump to kernel*/
ldr r0, =(ELFIN_CLOCK_POWER_BASE+INF_REG0_OFFSET) @0x7E00FA00
ldr r1, [r0] /* r1 = physical address of s3c6400_cpu_resume function*/ @s3c6400_cpu_resume函数的物理地址保存在这里。
mov pc, r1 /*Jump to kernel (sleep-s3c6400.S)*/ @跳到s3c6400_cpu_resume函数执行。s3c6400_cpu_resume找不到?
nop
nop
#endif
/*
* system_clock_init: Initialize core clock and bus clock.
* void system_clock_init(void)
*/
system_clock_init:
ldr r0, =ELFIN_CLOCK_POWER_BASE @0x7e00f000
#ifdef CONFIG_SYNC_MODE @同步模式选择,内核和总线不同步时,需用异步模式。
ldr r1, [r0, #OTHERS_OFFSET]
mov r2, #0x40
orr r1, r1, r2
str r1, [r0, #OTHERS_OFFSET] @设置SYNCMUX的选择为1,就是DOUTAPLL,就是跟ARM内核用同一个锁相环。
nop @等待系统稳定。
nop
nop
nop
nop
ldr r2, =0x80
orr r1, r1, r2
str r1, [r0, #OTHERS_OFFSET] @设置同步模式位7为1,变成同步模式。
check_syncack:
ldr r1, [r0, #OTHERS_OFFSET]
ldr r2, =0xf00
and r1, r1, r2
cmp r1, #0xf00 @比较0xf00和(OTHERS的同步确认位11,10,9,8的值和0x0f00相与)的值,不等则循环等待。四位全1表示同步完成。
bne check_syncack
#else /* ASYNC Mode */
nop
nop
nop
nop
nop
ldr r1, [r0, #OTHERS_OFFSET]
bic r1, r1, #0xC0
orr r1, r1, #0x40
str r1, [r0, #OTHERS_OFFSET] @设置为异步模式,SYNCMUX的选择为1,就是DOUTAPLL,下面又设置成0,为什么要这样做呢?
wait_for_async:
ldr r1, [r0, #OTHERS_OFFSET]
and r1, r1, #0xf00
cmp r1, #0x0
bne wait_for_async @同步确认四位为全0,表示异步模式稳定下来。
ldr r1, [r0, #OTHERS_OFFSET]
bic r1, r1, #0x40
str r1, [r0, #OTHERS_OFFSET] @设置SYNCMUX的选择为0,就是MOUTPLL。
#endif
mov r1, #0xff00
orr r1, r1, #0xff
str r1, [r0, #APLL_LOCK_OFFSET]
str r1, [r0, #MPLL_LOCK_OFFSET]
str r1, [r0, #EPLL_LOCK_OFFSET] @设置时钟输出稳定时间为最大
/* CLKUART(=66.5Mhz) = CLKUART_input(532/2=266Mhz) / (UART_RATIO(3)+1) */
/* CLKUART(=50Mhz) = CLKUART_input(400/2=200Mhz) / (UART_RATIO(3)+1) */
/* Now, When you use UART CLK SRC by EXT_UCLK1, We support 532MHz & 400MHz value */
#if defined(CONFIG_CLKSRC_CLKUART)
ldr r1, [r0, #CLK_DIV2_OFFSET]
bic r1, r1, #0x70000
orr r1, r1, #0x30000
str r1, [r0, #CLK_DIV2_OFFSET] @设置UART_RATIO为3,即四分频。
#endif
ldr r1, [r0, #CLK_DIV0_OFFSET] /*Set Clock Divider*/
bic r1, r1, #0x30000
bic r1, r1, #0xff00
bic r1, r1, #0xff
ldr r2, =CLK_DIV_VAL
orr r1, r1, r2
str r1, [r0, #CLK_DIV0_OFFSET] @设置时钟分频,PCLK_RATIO,HCLK2_RATIO,HCLK_RATIO,MPLL_RATIO,APLL_RATIO。
ldr r1, =APLL_VAL
str r1, [r0, #APLL_CON_OFFSET] @设置APLL_MDIV,APLL_PDIV,APLL_SDIV。
ldr r1, =MPLL_VAL
str r1, [r0, #MPLL_CON_OFFSET] @设置MPLL_MDIV,MPLL_PDIV,MPLL_SDIV。
ldr r1, =0x80200203 /* FOUT of EPLL is 96MHz */
str r1, [r0, #EPLL_CON0_OFFSET]
ldr r1, =0x0
str r1, [r0, #EPLL_CON1_OFFSET] @设置EPLL_MDIV,EPLL_PDIV,EPLL_SDIV,EPLL_KDIV。
ldr r1, [r0, #CLK_SRC_OFFSET] /* APLL, MPLL, EPLL select to Fout */
#if defined(CONFIG_CLKSRC_CLKUART)
ldr r2, =0x2007
#else
ldr r2, =0x7
#endif
orr r1, r1, r2
str r1, [r0, #CLK_SRC_OFFSET] @设置APLL,MPLL,EPLL时钟源为FOUT,UART为DOUT(同步模式设置为DOUT,异步模式设置为MOUT)。
/* wait at least 200us to stablize all clock */
mov r1, #0x10000
1: subs r1, r1, #1
bne 1b @等待所有时钟稳定。
#if 0
mrc p15, 0, r0, c1, c0, 0
orr r0, r0, #0xc0000000 /* clock setting in MMU */
mcr p15, 0, r0, c1, c0, 0
#endif
#ifdef CONFIG_SYNC_MODE /* Synchronization for VIC port */
ldr r1, [r0, #OTHERS_OFFSET]
orr r1, r1, #0x20
str r1, [r0, #OTHERS_OFFSET] @手册上为保留,这里是为了兼容老的?
#else
ldr r1, [r0, #OTHERS_OFFSET]
bic r1, r1, #0x20
str r1, [r0, #OTHERS_OFFSET]
#endif
mov pc, lr
/*
* uart_asm_init: Initialize UART in asm mode, 115200bps fixed.
* void uart_asm_init(void)
*/
uart_asm_init:
/* set GPIO to enable UART */
@ GPIO setting for UART
ldr r0, =ELFIN_GPIO_BASE
ldr r1, =0x22222222
str r1, [r0, #GPACON_OFFSET] @设置GPA0-GPA7口为UART用
ldr r1, =0x2222
str r1, [r0, #GPBCON_OFFSET] @设置GPB0-GPB3为UART用
ldr r0, =ELFIN_UART_CONSOLE_BASE @0x7F005000
mov r1, #0x0
str r1, [r0, #UFCON_OFFSET] @正常模式,无校验,1bit停止位,5bit数据位。
str r1, [r0, #UMCON_OFFSET] @FIFO为63字节,没有流量控制,没有中断,RTS不使能。
mov r1, #0x3 @was 0.
str r1, [r0, #ULCON_OFFSET] @设置为8bit数据位
#if defined(CONFIG_CLKSRC_CLKUART)
ldr r1, =0xe45 /* UARTCLK SRC = 11 => EXT_UCLK1*/
#else
ldr r1, =0x245 /* UARTCLK SRC = x0 => PCLK */
#endif
str r1, [r0, #UCON_OFFSET] @设置接收超时中断不使能,使能接收错误中断,非环回模式,正常传输,中断方式发送,中断方式接收。电平方式的接收、发送中断,时钟为UCLK1或PCLK。
#if defined(CONFIG_UART_50)
ldr r1, =0x1A
#elif defined(CONFIG_UART_66)
ldr r1, =0x22
#else
ldr r1, =0x1A
#endif
str r1, [r0, #UBRDIV_OFFSET] @设置波特率整数部分:(66000000/(115200X16))-1=34.81, 34的16进制为0x22。
#if defined(CONFIG_UART_50)
ldr r1, =0x3
#elif defined(CONFIG_UART_66)
ldr r1, =0x1FFF
#else
ldr r1, =0x3
#endif
str r1, [r0, #UDIVSLOT_OFFSET] @设置波特率小数部分:(UDIVSLOT中1的个数)/16=0.81,则UDIVSLOT中1的个数=12.92,取整为13个,而0x1fff正好有13个二进制1和3个二进制0。
ldr r1, =0x4f4f4f4f
str r1, [r0, #UTXH_OFFSET] @'O',发送缓冲寄存器里面填入'O'字符。
mov pc, lr
/*
* Nand Interface Init for SMDK6400 */
nand_asm_init:
ldr r0, =ELFIN_NAND_BASE @0x70200000
ldr r1, [r0, #NFCONF_OFFSET]
orr r1, r1, #0x70
orr r1, r1, #0x7700
str r1, [r0, #NFCONF_OFFSET] @设置TACLS(信号清除),TWRPH0(读写阶段0),TWRPH1(读写阶段1)(NAND时序图上有)的持续时间。
ldr r1, [r0, #NFCONT_OFFSET]
orr r1, r1, #0x03
str r1, [r0, #NFCONT_OFFSET] @为了老芯片兼容,s3c6410x低四位为保留。
mov pc, lr
#ifdef CONFIG_ENABLE_MMU
/*
* MMU Table for SMDK6400
*/
/* form a first-level section entry */
.macro FL_SECTION_ENTRY base,ap,d,c,b @定义一个可以生成描述符的宏,按照section方式映射,每个section必须是1M,占20位地址,这里共256M物理内存,映射到整个地址空间4G,所以共有4096(0x1000)个描述符。虚拟地址的高12位(4096)作为描述符位置,低20位作为section里面的地址。从下面映射表看出来,虚拟地址0x00000000-0xa0000000跟对应的物理地址是一致的,虚拟地址0xa0000000-0xc0000000和0xc8000000-0xffffffff没有物理地址对应,虚拟地址0xc0000000-0xc8000000跟物理地址0x50000000-0x58000000相对应,所以TEXT_BASE基地址没有MMU时就是物理地址0x57e00000,有MMU时对应的虚拟地址就是0xc7e00000。
.word (\base << 20) | (\ap << 10) | \
(\d << 5) | (1<<4) | (\c << 3) | (\b << 2) | (1<<1) @描述符的最低两位00为无效,01为二级页表方式(由高地址12位的一级页表(TTB的高18位指定的虚拟基地址)位置取出对应二级页表(coarse page table)的基地址,由中间地址8位的二级页表取出对应4KB页表(small page)的基地址,最后由低12为地址的页表取出最终的内存值),10为一级页表方式(如果bit18为0,则为section,bit18为1,则为supersection),11保留。
.endm
.section .mmudata, "a" @定义MMU数据段,在lds文件里面用到了,“a”表示这是一个需要鉴权的段
.align 14
// the following alignment creates the mmu table at address 0x4000. @对齐到0x4000(16KB)的整数倍位置,因为TTB寄存器只保存MMU表的高18位地址(16KB对齐位置),所以必须14位对齐。
.globl mmu_table @在start.S中使能MMU的时候用到了。
mmu_table:
.set __base,0 @前0xa00(2560)个描述符定义,ap为3表示读写允许。c,b比特为00表示共享,互斥读写。d为0表示这些描述符都属于domain0(domain用来做权限控制)。
// 1:1 mapping for debugging
.rept 0xA00
FL_SECTION_ENTRY __base,3,0,0,0
.set __base,__base+1
.endr
// access is not allowed.
.rept 0xC00 - 0xA00 @从0xa00-0xc00共512个描述符,ap为0表示不允许访问。
.word 0x00000000
.endr
// 128MB for SDRAM 0xC0000000 -> 0x50000000
.set __base, 0x500 @从0xc00-0xc80共128个描述符(因为每个描述符代表1M,就是128MB)定义,cb比特为11表示内存输入输出都为回写模式。
.rept 0xC80 - 0xC00
FL_SECTION_ENTRY __base,3,0,1,1
.set __base,__base+1
.endr
// access is not allowed.
.rept 0x1000 - 0xc80 @从0xc80-0x1000共896个描述符,不允许访问。
.word 0x00000000
.endr
#endif