近来学习了一哈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 #endiflowlevel_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]
同时有三种时钟: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 */
ldr r1, [r0, #OTHERS_OFFSET] bic r1, r1, #0x40 str r1, [r0, #OTHERS_OFFSET]
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
#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和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
关于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