近来学习了一哈uboot,本来想下载最新的uboot来学习哈,但是手上就只有ok6410-A的板子,看了哈uboot最新代码貌似不支持s3c6410了,因此就作罢,转而学习uboot-2012.10的老代码了。。学啥不是学呢,哈哈
lowlevel_init.s的代码不多,主要看了一哈ok6410的时钟初始化部分
#include <config.h>
#include <version.h>
#include <asm/arch/s3c6410.h>
#ifdef CONFIG_SERIAL1
#define ELFIN_UART_CONSOLE_BASE (ELFIN_UART_BASE + ELFIN_UART0_OFFSET)
#elif defined(CONFIG_SERIAL2)
#define ELFIN_UART_CONSOLE_BASE (ELFIN_UART_BASE + ELFIN_UART1_OFFSET)
#else
#define ELFIN_UART_CONSOLE_BASE (ELFIN_UART_BASE + ELFIN_UART2_OFFSET)
#endif
_TEXT_BASE:
.word CONFIG_SYS_TEXT_BASE
.globl lowlevel_init
lowlevel_init:
mov r12, lr
/* LED on only #8 */
ldr r0, =ELFIN_GPIO_BASE
ldr r1, =0x55540000
str r1, [r0, #GPNCON_OFFSET]
ldr r1, =0x55555555
str r1, [r0, #GPNPUD_OFFSET]
ldr r1, =0xf000
str r1, [r0, #GPNDAT_OFFSET]
/* 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
ldr r1, =ELFIN_VIC1_BASE_ADDR @0x71300000
/* Disable all interrupts (VIC0 and VIC1) */
mvn r3, #0x0
str r3, [r0, #oINTMSK]
str r3, [r1, #oINTMSK]
/* Set all interrupts as IRQ */
mov r3, #0x0
str r3, [r0, #oINTMOD]
str r3, [r1, #oINTMOD]
/* Pending Interrupt Clear */
mov r3, #0x0
str r3, [r0, #oVECTADDR]
str r3, [r1, #oVECTADDR]
/* init system clock */
bl system_clock_init
#ifndef CONFIG_NAND_SPL
/* for UART */
bl uart_asm_init
#endif
#ifdef CONFIG_BOOT_NAND
/* simple init for NAND */
bl nand_asm_init
#endif
/* Memory subsystem address 0x7e00f120 */
ldr r0, =ELFIN_MEM_SYS_CFG
/* Xm0CSn2 = NFCON CS0, Xm0CSn3 = NFCON CS1 */
mov r1, #S3C64XX_MEM_SYS_CFG_NAND
str r1, [r0]
bl mem_ctrl_asm_init
/* Wakeup support. Don't know if it's going to be used, untested. */
ldr r0, =(ELFIN_CLOCK_POWER_BASE + RST_STAT_OFFSET)
ldr r1, [r0]
bic r1, r1, #0xfffffff7
cmp r1, #0x8
beq wakeup_reset
1:
mov lr, r12
mov pc, lr
wakeup_reset:
/* Clear wakeup status register */
ldr r0, =(ELFIN_CLOCK_POWER_BASE + WAKEUP_STAT_OFFSET)
ldr r1, [r0]
str r1, [r0]
/* LED test */
ldr r0, =ELFIN_GPIO_BASE
ldr r1, =0x3000
str r1, [r0, #GPNDAT_OFFSET]
/* Load return address and jump to kernel */
ldr r0, =(ELFIN_CLOCK_POWER_BASE + INF_REG0_OFFSET)
/* r1 = physical address of s3c6400_cpu_resume function */
ldr r1, [r0]
/* Jump to kernel (sleep-s3c6400.S) */
mov pc, r1
nop
nop
/*
* 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]
nop
nop
nop
nop
nop
ldr r2, =0x80
orr r1, r1, r2
str r1, [r0, #OTHERS_OFFSET]
check_syncack:
ldr r1, [r0, #OTHERS_OFFSET]
ldr r2, =0xf00
and r1, r1, r2
cmp r1, #0xf00
bne check_syncack
#else /* ASYNC Mode */
nop
nop
nop
nop
nop
/*
* This was unconditional in original Samsung sources, but it doesn't
* seem to make much sense on S3C6400.
*/
#ifndef CONFIG_S3C6400
ldr r1, [r0, #OTHERS_OFFSET]
bic r1, r1, #0xC0
orr r1, r1, #0x40
str r1, [r0, #OTHERS_OFFSET]
wait_for_async:
ldr r1, [r0, #OTHERS_OFFSET]
and r1, r1, #0xf00
cmp r1, #0x0
bne wait_for_async
#endif
ldr r1, [r0, #OTHERS_OFFSET]
bic r1, r1, #0x40
str r1, [r0, #OTHERS_OFFSET]
#endif
mov r1, #0xff00
orr r1, r1, #0xff
str r1, [r0, #APLL_LOCK_OFFSET]
str r1, [r0, #MPLL_LOCK_OFFSET]
/* Set Clock Divider */
ldr r1, [r0, #CLK_DIV0_OFFSET]
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]
ldr r1, =APLL_VAL
str r1, [r0, #APLL_CON_OFFSET]
ldr r1, =MPLL_VAL
str r1, [r0, #MPLL_CON_OFFSET]
/* FOUT of EPLL is 96MHz */
ldr r1, =0x200203
str r1, [r0, #EPLL_CON0_OFFSET]
ldr r1, =0x0
str r1, [r0, #EPLL_CON1_OFFSET]
/* APLL, MPLL, EPLL select to Fout */
ldr r1, [r0, #CLK_SRC_OFFSET]
orr r1, r1, #0x7
str r1, [r0, #CLK_SRC_OFFSET]
/* wait at least 200us to stablize all clock */
mov r1, #0x10000
1: subs r1, r1, #1
bne 1b
/* Synchronization for VIC port */
#if defined(CONFIG_SYNC_MODE)
ldr r1, [r0, #OTHERS_OFFSET]
orr r1, r1, #0x20
str r1, [r0, #OTHERS_OFFSET]
#elif !defined(CONFIG_S3C6400)
/* According to 661558um_S3C6400X_rev10.pdf 0x20 is reserved */
ldr r1, [r0, #OTHERS_OFFSET]
bic r1, r1, #0x20
str r1, [r0, #OTHERS_OFFSET]
#endif
mov pc, lr
#ifndef CONFIG_NAND_SPL
/*
* uart_asm_init: Initialize UART's pins
*/
uart_asm_init:
/* set GPIO to enable UART */
ldr r0, =ELFIN_GPIO_BASE
ldr r1, =0x220022
str r1, [r0, #GPACON_OFFSET]
mov pc, lr
#endif
#ifdef CONFIG_BOOT_NAND
/*
* NAND Interface init for SMDK6400
*/
nand_asm_init:
ldr r0, =ELFIN_NAND_BASE
ldr r1, [r0, #NFCONF_OFFSET]
orr r1, r1, #0x70
orr r1, r1, #0x7700
str r1, [r0, #NFCONF_OFFSET]
ldr r1, [r0, #NFCONT_OFFSET]
orr r1, r1, #0x07
str r1, [r0, #NFCONT_OFFSET]
mov pc, lr
#endif
#ifdef CONFIG_ENABLE_MMU
/*
* MMU Table for SMDK6400
*/
/* form a first-level section entry */
.macro FL_SECTION_ENTRY base,ap,d,c,b
.word (\base << 20) | (\ap << 10) | \
(\d << 5) | (1<<4) | (\c << 3) | (\b << 2) | (1<<1)
.endm
.section .mmudata, "a"
.align 14
/* the following alignment creates the mmu table at address 0x4000. */
.globl mmu_table
mmu_table:
.set __base, 0
/* 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
.word 0x00000000
.endr
/* 128MB for SDRAM 0xC0000000 -> 0x50000000 */
.set __base, 0x500
.rept 0xC80 - 0xC00
FL_SECTION_ENTRY __base, 3, 0, 1, 1
.set __base, __base + 1
.endr
/* access is not allowed. */
.rept 0x1000 - 0xc80
.word 0x00000000
.endr
#endif
lowlevel_init由start中调用,进来之后是LED初始化,这边要跟ok6410的板子相匹配
是占用了GPM的0~3口,关于GPM的介绍如下
GPMCON是配置该端口用做什么,比如输入口、输出口等
我们需要配置GPM0-3为输出口
GPMDAT不用多说是该端口的数值,GPLPDN用来配置该端口的上拉/下拉电阻,查看ok6410的原理图
我们不需要上拉/下拉电阻,因此GPMLPUD配置成00即可,而且GPMDAT=0时,LED灯点亮,我们可以将led的初始化改成下面这样
lowlevel_init:
/*保存lr到r12中*/
mov r12, lr
/* LED on only #8 */
ldr r0, =ELFIN_GPIO_BASE
ldr r1, =0x00001111
str r1, [r0, #GPMCO_OFFSET]
ldr r1, =0x00000000
str r1, [r0, #GPMPUD_OFFSET]
ldr r1, =0x00000000
str r1, [r0, #GPMDAT_OFFSET]
/* 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
ldr r1, =ELFIN_VIC1_BASE_ADDR @0x71300000
/* Disable all interrupts (VIC0 and VIC1) */
mvn r3, #0x0
str r3, [r0, #oINTMSK]
str r3, [r1, #oINTMSK]
/* Set all interrupts as IRQ */
mov r3, #0x0
str r3, [r0, #oINTMOD]
str r3, [r1, #oINTMOD]
/* Pending Interrupt Clear */
mov r3, #0x0
str r3, [r0, #oVECTADDR]
str r3, [r1, #oVECTADDR]
/* init system clock */
bl system_clock_init
关于关闭看门狗不晓得为啥不这么写
/* Disable Watchdog */
ldr r0, =0x7e004000 @0x7e004000
mov r1, #0
str r1, [r0]
之后跳转到了system_clock_init系统时钟初始化中,s3c6410的时钟有几个需要注意的地方,有3个PLL:APLL、MPLL、EPLL
同时有三种时钟:FCLK、HCLK、PCLK,
FCLK是ARM的核心时钟最大为667MHz,一般uboot采用的是533MHz
HCLK是HAB总线的时钟,最大是133MHz
PCLK是APB总线的时钟,最大为66MHz
关于s3c6410的时钟产生,看下面这个图就可以了。
对比代码一步步看时钟的产生,在时钟配置时首先判断了是否是同步模式CONFIG_SYNC_MODE该定义在sdmk6410.h中是没有定义的
/*#define CONFIG_CLK_667_133_66*/
#define CONFIG_CLK_533_133_66
/*
#define CONFIG_CLK_400_100_50
#define CONFIG_CLK_400_133_66
#define CONFIG_SYNC_MODE
*/
采用异步模式,并且FCLK、HCLK、PCLK分别配置为533MHz、133MHz、66MHz,直接看异步模式CONFIG_S3C6410该定义在sdmk6410中已经有定义了,只看下面代码即可
ldr r1, [r0, #OTHERS_OFFSET]
bic r1, r1, #0x40
str r1, [r0, #OTHERS_OFFSET]
将OTHERS寄存器中第六位清0,OTHERS[6]=0,从上图中可以知道系统时钟将由MPLL产生,及MPLK和PCLK由MPLL产生
mov r1, #0xff00
orr r1, r1, #0xff
str r1, [r0, #APLL_LOCK_OFFSET]
str r1, [r0, #MPLL_LOCK_OFFSET]
设置锁周期,看文档即可,下面是各个时钟的配置
/* Set Clock Divider */
ldr r1, [r0, #CLK_DIV0_OFFSET]
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]
ldr r1, =APLL_VAL
str r1, [r0, #APLL_CON_OFFSET]
ldr r1, =MPLL_VAL
str r1, [r0, #MPLL_CON_OFFSET]
/* FOUT of EPLL is 96MHz */
ldr r1, =0x200203
str r1, [r0, #EPLL_CON0_OFFSET]
ldr r1, =0x0
str r1, [r0, #EPLL_CON1_OFFSET]
/* APLL, MPLL, EPLL select to Fout */
ldr r1, [r0, #CLK_SRC_OFFSET]
orr r1, r1, #0x7
str r1, [r0, #CLK_SRC_OFFSET]
/* wait at least 200us to stablize all clock */
mov r1, #0x10000
1: subs r1, r1, #1
bne 1b
第一步设置时钟分频,DIV0
CLK_DIV_VAL的值在s3c6410.h中定义
#define STARTUP_PCLKDIV 3
#define STARTUP_HCLKX2DIV 1
#define STARTUP_HCLKDIV 1
#define STARTUP_MPLLDIV 1
#define STARTUP_APLLDIV 0
#define CLK_DIV_VAL ((STARTUP_PCLKDIV << 12) | (STARTUP_HCLKX2DIV << 9) | \
(STARTUP_HCLKDIV << 8) | (STARTUP_MPLLDIV<<4) | STARTUP_APLLDIV)
对应着下图中各个时钟的配置
PCLK为HCLKX2的4分频,HCLKX2为HCLKX2in的2分频,HCLK为HCLKX2的2分频,DOUTmpll为 mpll的2分频,ARMCLK为DOUTapll的1分频。
#elif defined(CONFIG_CLK_533_133_66)
#define STARTUP_AMDIV 533
#define STARTUP_MDIV 533
#define STARTUP_PDIV 6
#define STARTUP_SDIV 1
#define APLL_VAL ((1 << 31) | (STARTUP_AMDIV << 16) | \
(STARTUP_PDIV << 8) | STARTUP_SDIV)
#define MPLL_VAL ((1 << 31) | (STARTUP_MDIV << 16) | \
(STARTUP_PDIV << 8) | STARTUP_SDIV)
接下来分别设置APLL和MPLL的值,根据宏定义一级手册中APLL_CON/MPLL_CON的说明,
手册中给出了建议的时钟以及各自的配置数据
根据代码我们可以看出使能了APLL和MPLL的时钟控制,并且APLL和MPLL输出的都是533MHz的时钟,
/* FOUT of EPLL is 96MHz */
ldr r1, =0x80200102
str r1, [r0, #EPLL_CON0_OFFSET]
ldr r1, =0x0
str r1, [r0, #EPLL_CON1_OFFSET]
/* APLL, MPLL, EPLL select to Fout */
ldr r1, [r0, #CLK_SRC_OFFSET]
orr r1, r1, #0x7
str r1, [r0, #CLK_SRC_OFFSET]
/* wait at least 200us to stablize all clock */
mov r1, #0x10000
1: subs r1, r1, #1
bne 1b
配置EPLL的数据输出96MHz的时钟,最后在CLK_SRC中设置输出三种时钟,并等待时钟稳定
关于MISC_CON的配置在文档也有说MISC_CON[19]应该为0
结合时钟图和配置数据可以知晓:FCLK=533MHz,HCLKX2=266MHz,HCLK=133MHz,PCLK=66MHz等,各个时钟对应的外设都可以通过各自的GATE来配置是否需要产生所需的时钟,GATE寄存器有HCLK_GATE、PCLK_GATE、SCLK_GATE可以查看手册来帮助理解。
在等待时钟稳定之后又有一个配置OTHERS寄存器的代码
/* Synchronization for VIC port */
#if defined(CONFIG_SYNC_MODE)
ldr r1, [r0, #OTHERS_OFFSET]
orr r1, r1, #0x20
str r1, [r0, #OTHERS_OFFSET]
#elif !defined(CONFIG_S3C6410)
/* According to 661558um_S3C6400X_rev10.pdf 0x20 is reserved */
ldr r1, [r0, #OTHERS_OFFSET]
bic r1, r1, #0x20
str r1, [r0, #OTHERS_OFFSET]
#endif
mov pc, lr
清掉OTHERS[5]
手册上说OTHERS[5:3]应该是0x03不能改变,但是OTHERS的复位值却是0x801E
不晓得为啥这么设计,还得手动清掉OTHERS[5]。。。
之后返回到上方进行UART的初始化,UART初始化很简单
#ifndef CONFIG_NAND_SPL
/*
* uart_asm_init: Initialize UART's pins
*/
uart_asm_init:
/* set GPIO to enable UART */
ldr r0, =ELFIN_GPIO_BASE
ldr r1, =0x220022
str r1, [r0, #GPACON_OFFSET]
mov pc, lr
#endif
GPA端口是可以用做UART口的,这里设置了UART0、UART1