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

  分析方法就是这样,虽然计算的地址可能有错,但是大概意思已经讲清楚了。

  今天就写到这里了。

 

 

  

 

  

 

 

  

 

 

 

 

 

你可能感兴趣的:(init)