S3C6410之uboot回炉再造(3)lowlevle_init.S
这一篇粗略讲一下lowlevel_init.S内部的模块。
1、_TEXT_BASE
1 #include <config.h> 2 #include <version.h> 3 4 #include <asm/arch/s3c6400.h> 5 6 #ifdef CONFIG_SERIAL1 7 #define ELFIN_UART_CONSOLE_BASE (ELFIN_UART_BASE + ELFIN_UART0_OFFSET) 8 #elif defined(CONFIG_SERIAL2) 9 #define ELFIN_UART_CONSOLE_BASE (ELFIN_UART_BASE + ELFIN_UART1_OFFSET) 10 #else 11 #define ELFIN_UART_CONSOLE_BASE (ELFIN_UART_BASE + ELFIN_UART2_OFFSET) 12 #endif 13 14 _TEXT_BASE: 15 .word TEXT_BASE 16
来看一下最后两句的的反汇编
00000000 <_TEXT_BASE>: 0: 57e00000 .word 0x57e00000
看到这里的 0x57e00000 有种似曾相识的感觉,但是这段代码的特殊位置决定了这个地址是无效的。
回想一下上一篇的地址无关性,就能明白了。
2、点亮LED
17 .globl lowlevel_init 18 lowlevel_init: 19 mov r12, lr 20 21 /* LED on only #8 */ 22 ldr r0, =ELFIN_GPIO_BASE 23 ldr r1, =0x55540000 24 str r1, [r0, #GPNCON_OFFSET] 25 26 ldr r1, =0x55555555 27 str r1, [r0, #GPNPUD_OFFSET] 28 29 ldr r1, =0xf000 30 str r1, [r0, #GPNDAT_OFFSET] 31
3、关闭看门狗
32 /* Disable Watchdog */ 33 ldr r0, =0x7e000000 @0x7e004000 34 orr r0, r0, #0x4000 35 mov r1, #0 36 str r1, [r0] 37
从这里看到,在 start.S 中取出的关闭看门狗原来是移动到了这里执行。
这样做的好处是,让每个单独部分的代码信息显得更紧凑一些。
4、读一次外部中断,然后清除中断信号
38 /* External interrupt pending clear */ 39 ldr r0, =(ELFIN_GPIO_BASE+EINTPEND_OFFSET) /*EINTPEND*/ 40 ldr r1, [r0] 41 str r1, [r0] 42 43 ldr r0, =ELFIN_VIC0_BASE_ADDR @0x71200000 44 ldr r1, =ELFIN_VIC1_BASE_ADDR @0x71300000 45
5、将所有中断设置为IRQ
51 /* Set all interrupts as IRQ */ 52 mov r3, #0x0 53 str r3, [r0, #oINTMOD] 54 str r3, [r1, #oINTMOD] 55
6、等待中断清除
56 /* Pending Interrupt Clear */ 57 mov r3, #0x0 58 str r3, [r0, #oVECTADDR] 59 str r3, [r1, #oVECTADDR] 60
这里的作用应该等效于禁用中断。
7、初始化系统时钟
61 /* init system clock */ 62 bl system_clock_init 63 //具体实现代码在118行
8、初始化UART串口和NAND flash
64 #ifndef CONFIG_NAND_SPL 65 /* for UART */ 66 bl uart_asm_init 67 #endif 68 69 #ifdef CONFIG_BOOT_NAND 70 /* simple init for NAND */ 71 bl nand_asm_init 72 #endif 73
9、内存控制的初始化
74 /* Memory subsystem address 0x7e00f120 */ 75 ldr r0, =ELFIN_MEM_SYS_CFG 76 77 /* Xm0CSn2 = NFCON CS0, Xm0CSn3 = NFCON CS1 */ 78 mov r1, #S3C64XX_MEM_SYS_CFG_NAND 79 str r1, [r0] 80 81 bl mem_ctrl_asm_init 82
10、测试将要使用的功能
83 /* Wakeup support. Don't know if it's going to be used, untested. */ 84 ldr r0, =(ELFIN_CLOCK_POWER_BASE + RST_STAT_OFFSET) 85 ldr r1, [r0] 86 bic r1, r1, #0xfffffff7 87 cmp r1, #0x8 88 beq wakeup_reset 89 90 1: 91 mov lr, r12 92 mov pc, lr 93 94 wakeup_reset: 95 96 /* Clear wakeup status register */ 97 ldr r0, =(ELFIN_CLOCK_POWER_BASE + WAKEUP_STAT_OFFSET) 98 ldr r1, [r0] 99 str r1, [r0] 100 101 /* LED test */ 102 ldr r0, =ELFIN_GPIO_BASE 103 ldr r1, =0x3000 104 str r1, [r0, #GPNDAT_OFFSET] 105 106 /* Load return address and jump to kernel */ 107 ldr r0, =(ELFIN_CLOCK_POWER_BASE + INF_REG0_OFFSET) 108 /* r1 = physical address of s3c6400_cpu_resume function */ 109 ldr r1, [r0] 110 /* Jump to kernel (sleep-s3c6400.S) */ 111 mov pc, r1 112 nop 113 nop
11、时钟初始化的执行代码
114 /* 115 * system_clock_init: Initialize core clock and bus clock. 116 * void system_clock_init(void) 117 */ 118 system_clock_init: 119 ldr r0, =ELFIN_CLOCK_POWER_BASE /* 0x7e00f000 */ 120 121 #ifdef CONFIG_SYNC_MODE 122 ldr r1, [r0, #OTHERS_OFFSET] 123 mov r2, #0x40 124 orr r1, r1, r2 125 str r1, [r0, #OTHERS_OFFSET] 126 127 nop 128 nop 129 nop 130 nop 131 nop 132 133 ldr r2, =0x80 134 orr r1, r1, r2 135 str r1, [r0, #OTHERS_OFFSET] 136 137 check_syncack: 138 ldr r1, [r0, #OTHERS_OFFSET] 139 ldr r2, =0xf00 140 and r1, r1, r2 141 cmp r1, #0xf00 142 bne check_syncack 143 #else /* ASYNC Mode */ 144 nop 145 nop 146 nop 147 nop 148 nop 149 150 /* 151 * This was unconditional in original Samsung sources, but it doesn't 152 * seem to make much sense on S3C6400. 153 */ 154 #ifndef CONFIG_S3C6400 155 ldr r1, [r0, #OTHERS_OFFSET] 156 bic r1, r1, #0xC0 157 orr r1, r1, #0x40 158 str r1, [r0, #OTHERS_OFFSET] 159 160 wait_for_async: 161 ldr r1, [r0, #OTHERS_OFFSET] 162 and r1, r1, #0xf00 163 cmp r1, #0x0 164 bne wait_for_async 165 #endif 166 167 ldr r1, [r0, #OTHERS_OFFSET] 168 bic r1, r1, #0x40 169 str r1, [r0, #OTHERS_OFFSET] 170 #endif 171 172 mov r1, #0xff00 173 orr r1, r1, #0xff 174 str r1, [r0, #APLL_LOCK_OFFSET] 175 str r1, [r0, #MPLL_LOCK_OFFSET] 176 177 /* Set Clock Divider */ 178 ldr r1, [r0, #CLK_DIV0_OFFSET] 179 bic r1, r1, #0x30000 180 bic r1, r1, #0xff00 181 bic r1, r1, #0xff 182 ldr r2, =CLK_DIV_VAL 183 orr r1, r1, r2 184 str r1, [r0, #CLK_DIV0_OFFSET] 185 186 ldr r1, =APLL_VAL 187 str r1, [r0, #APLL_CON_OFFSET] 188 ldr r1, =MPLL_VAL 189 str r1, [r0, #MPLL_CON_OFFSET] 190 191 /* FOUT of EPLL is 96MHz */ 192 ldr r1, =0x200203 193 str r1, [r0, #EPLL_CON0_OFFSET] 194 ldr r1, =0x0 195 str r1, [r0, #EPLL_CON1_OFFSET] 196 197 /* APLL, MPLL, EPLL select to Fout */ 198 ldr r1, [r0, #CLK_SRC_OFFSET] 199 orr r1, r1, #0x7 200 str r1, [r0, #CLK_SRC_OFFSET] 201 202 /* wait at least 200us to stablize all clock */ 203 mov r1, #0x10000 204 1: subs r1, r1, #1 205 bne 1b 206 207 /* Synchronization for VIC port */ 208 #if defined(CONFIG_SYNC_MODE) 209 ldr r1, [r0, #OTHERS_OFFSET] 210 orr r1, r1, #0x20 211 str r1, [r0, #OTHERS_OFFSET] 212 #elif !defined(CONFIG_S3C6400) 213 /* According to 661558um_S3C6400X_rev10.pdf 0x20 is reserved */ 214 ldr r1, [r0, #OTHERS_OFFSET] 215 bic r1, r1, #0x20 216 str r1, [r0, #OTHERS_OFFSET] 217 #endif 218 mov pc, lr 219 220
12、串口初始化的执行代码
221 #ifndef CONFIG_NAND_SPL 222 /* 223 * uart_asm_init: Initialize UART's pins 224 */ 225 uart_asm_init: 226 /* set GPIO to enable UART */ 227 ldr r0, =ELFIN_GPIO_BASE 228 ldr r1, =0x220022 229 str r1, [r0, #GPACON_OFFSET] 230 mov pc, lr 231 #endif 232
13、NAND flash的初始化
后面有详细分析,这里不重复粘贴了。
14、MMU的初始化
同上。
这篇比较偷懒,但是想把主要精力放在start.S的分析上。
其实这也是分析方法的一种,看代码的时候先了解模块的代码区,若想深入了解某个模块的实现,则再深入到每一行代码中理解。
总结一下 lowlevl_init 的总体实现内容:
1、点亮LED;
2、关闭看门狗;
3、禁用中断;
4、初始化时钟;
5、初始化串口;
6、初始化NAND FLASH;
7、初始化MMU(根据宏声明而定)。
这里补上两段代码的分析:
NAND flash的初始化 与 MMU 的初始化,因为在 kernel 的启动过程中还会有类似的代码段,现在分析了,到后面分析 kernel 初始化的时候可以做一个对比。
1、首先是 NAND FLASH 的初始化
233 #ifdef CONFIG_BOOT_NAND 234 /* 235 * NAND Interface init for SMDK6400 236 */ 237 nand_asm_init: 238 ldr r0, =ELFIN_NAND_BASE 239 ldr r1, [r0, #NFCONF_OFFSET] 240 orr r1, r1, #0x70 241 orr r1, r1, #0x7700 242 str r1, [r0, #NFCONF_OFFSET] 243 244 ldr r1, [r0, #NFCONT_OFFSET] 245 orr r1, r1, #0x07 246 str r1, [r0, #NFCONT_OFFSET] 247 248 mov pc, lr 249 #endif 250
1)确认成立条件 CONFIG_BOOT_NAND
这个条件声明在/include/configs/smdk6400.h中,所以下面的执行代码是有效的。
接着深入分析
238 ldr r0, =ELFIN_NAND_BASE //在/include/asm-arm/arch-s3c64xx/s3c6400.h中声明了 #define ELFIN_NAND_BASE 0x70200000 //这里采用的是伪汇编指令,即将其值放入 r0 中
2)进入s3c6410 UM中检索 ELFIN_NAND_BASE 相关地址信息找到table 2-3 中有相应的地址信息。
Address Description
0x7020_0000 ~ 0x702F_FFFF NFCON SFR
接着在UM中检索0x7020_0000 可以跳转到8.11节。
看了之后发现,这一小篇内容描述的是 NAND FALSH 控制寄存器地图。
其中 base 就是我们的 0x7020_0000。
从UM中,我们先看到 0x7020_0000 代表的意思:
NFCONF (Nand Flash CONFiguration)寄存器功能为 Nand Flash Configuration register。
复位值为 0xX000_100X
相对应的位的信息为
//不同关系的作用位用 ',' 隔开,总体以 4 bit 来分隔, 含同一组作用位的用 '|' 隔开。 [31, 30, 29 28 | 27 26, 25, 24 | 23, 22 21 20 | 19 18 17 16 ] [15, 14 13 12, | 11, 10 9 8, | 7, 6 5 4, | 3, 2, 1, 0] //按作用位组来介绍 //1、 X, 0, 0 0 ---> X 31 - Reserved, 30 - [0:系统时钟 > 66MHZ; 1:系统时钟 < 66MHZ], 29 28 - Reserved //2、 0 0, 0, 0 ---> 0 27 26 - Reserved, 25 - ECC校验长度 [0:512 byte; 1:24 byte ], 24 - 联合23 //3、 0, 0 0 0 ---> 0 24 23 - ECC类型 [00: 1 bit; 10: 4 bit; 01: 8 bit /*注意4 bit 和 8 bit 的类型码*/] 22 : 15 - Reserved //4、 0 0 0 0 ---> 0 //5、 0, 0 0 1 ---> 1 14 13 12 - CLE & ALE 持续时间设定 //6、 0, 0 0 0 ---> 0 11 - Reserved, 10 9 8 - TWRPH0 持续时间设定 //7、 0, 0 0 0 ---> 0 7 - Reserved, 6 5 4 - TWRPH1 持续时间设定 //8、 X, 0, X, 0 ---> X 4 2 1 - Reserved, 3 - Reserved 但必须为1
3)接着看下面的指令
239 ldr r1, [r0, #NFCONF_OFFSET] //先找到 NFCONF_OFFSET 的定义 //目录为 /include/asm-arm/arch-s3c64xx/s3c6400.h, 则此处 r1 = 0xX000_100X //在UM中找到相应项目 240 orr r1, r1, #0x70 // r1 = 0xX020_007X 241 orr r1, r1, #0x7700 // r1 = 0xX020_777X 242 str r1, [r0, #NFCONF_OFFSET] //送回NFCONF寄存器 //对应上面的表可以知道,改变的为 CLE & ALE, TWRPH0/1 的持续时间而已(时间增加了) 243
4)然后是剩下的指令
244 ldr r1, [r0, #NFCONT_OFFSET] //这里是NFCONT_OFFSET了,注意区分 245 orr r1, r1, #0x07 // r1 = 0x0001_00C6 | 0x07 // r1 = 0x0001_00C7
//NFCONT 寄存器的最后一位功能为 [0:禁用NAND Flash 1:使用NADN flash]
//所以这里的功能就很清晰了,就是初始化 NAND flash
246 str r1, [r0, #NFCONT_OFFSET] //回送 247 248 mov pc, lr //程序返回 249 #endif 250
2、MMU的初始化代码
251 #ifdef CONFIG_ENABLE_MMU 252 /* 253 * MMU Table for SMDK6400 254 */ 255 256 /* form a first-level section entry */ 257 .macro FL_SECTION_ENTRY base,ap,d,c,b 258 .word (\base << 20) | (\ap << 10) | \ 259 (\d << 5) | (1<<4) | (\c << 3) | (\b << 2) | (1<<1) 260 .endm 261 262 .section .mmudata, "a" 263 .align 14 264 /* the following alignment creates the mmu table at address 0x4000. */ 265 .globl mmu_table 266 mmu_table: 267 .set __base, 0 268 /* 1:1 mapping for debugging */ 269 .rept 0xA00 270 FL_SECTION_ENTRY __base, 3, 0, 0, 0 271 .set __base, __base + 1 272 .endr 273 274 /* access is not allowed. */ 275 .rept 0xC00 - 0xA00 276 .word 0x00000000 277 .endr 278 279 /* 128MB for SDRAM 0xC0000000 -> 0x50000000 */ 280 .set __base, 0x500 281 .rept 0xC80 - 0xC00 282 FL_SECTION_ENTRY __base, 3, 0, 1, 1 283 .set __base, __base + 1 284 .endr 285 286 /* access is not allowed. */ 287 .rept 0x1000 - 0xc80 288 .word 0x00000000 289 .endr 290 #endif
1)确认成立条件
#if !defined(CONFIG_NAND_SPL) && (TEXT_BASE >= 0xc0000000) #define CONFIG_ENABLE_MMU #endif
此处的TEXT_BASE是 0x0000_0000显然是不成立的。
但是我们还是继续往下分析。
2).macro
252 /* 253 * MMU Table for SMDK6400 254 */ 255 256 /* form a first-level section entry */ 257 .macro FL_SECTION_ENTRY base, ap, d, c, b 258 .word (\base << 20) | (\ap << 10) | \ 259 (\d << 5) | (1<<4) | (\c << 3) | (\b << 2) | (1<<1) 260 .endm //结束宏定义 261
先来看看伪指令的格式
MACRO{$label} macroname {$parameter1} {$parameter2} ... //label宏展开时可以替换的符号 //macroname 宏名 //parameter n 宏指令的参数
这里宏的名字为 FL_SECTION_ENTRY,其实可以看成C语言的 #define
3)制作映射表
262 .section .mmudata, "a" 263 .align 14 //按照 2^14 = 16384 对齐,即 0x4000 264 /* the following alignment creates the mmu table at address 0x4000. */ 265 .globl mmu_table 266 mmu_table: 267 .set __base, 0 //赋值为 0 268 /* 1:1 mapping for debugging */ 269 .rept 0xA00 //重复次数 0xA00 = 2560 270 FL_SECTION_ENTRY __base, 3, 0, 0, 0 //代入公式得 FL_SECTION_ENTRY(起始) = (0b11 << 10) | (0b1 << 4) | (0b1 << 1) = 0x0000_4000(对齐后) 271 .set __base, __base + 1 //每一次 __base + 1 之后, FL_SECTION_ENTRY + 0x0010_0000 ,即 1MB //所以此次制表范围是 2560MB //从 0x0000_4000 ~ 0x9FF0_4000 272 .endr 273
不可访问的区域,全部置为 0
274 /* access is not allowed. */ 275 .rept 0xC00 - 0xA00 //重复次数 0x200 = 512 276 .word 0x00000000 //从0x9FF0_4000 ~ 0xBFF0_4000 277 .endr 278
映射SDRAM
279 /* 128MB for SDRAM 0xC0000000 -> 0x50000000 */ //这里为128MB,在实际使用中应该为256MB 280 .set __base, 0x500 281 .rept 0xC80 - 0xC00 //重复次数 0x80 = 128 //为256MB时应更改为 0xD00 - 0xC00 282 FL_SECTION_ENTRY __base, 3, 0, 1, 1 //代入公式得 FL_SECTION_ENTRY(起始) = 0xBFF0_4000 //此次制表范围 128MB //从 0xBFF0_4000 ~ 0xC7F0_4000 283 .set __base, __base + 1 284 .endr 285
不可访问区域,置为0
286 /* access is not allowed. */ 287 .rept 0x1000 - 0xc80 //重复次数 0x380 = 896 288 .word 0x00000000 //从0xC7F04000 ~ 0xFFF0_4000 289 .endr
290#endif
分析方法就是这样,虽然计算的地址可能有错,但是大概意思已经讲清楚了。
今天就写到这里了。