您的位置:首页 > 运维架构 > Linux

嵌入式Linux引导过程之1.3——Xloader的sys_init

2013-07-22 16:28 295 查看
上一篇文章对XLOADER_ENTRY进行了分析,看到其中调用的第一个标号就是sys_init,本文就对这个标号对应的代码段进行粗略的分析,这里我也还有好多没有搞明白的,就先留着,日后慢慢明白,先把自己目前能够看明白的东西记下来。

另外,需要说明的是,像sys_init以及后续还要讲的ddr_init之类的代码是与体系结构高度相关的,由于我的文章针对的是SPEArPlus开发板的,因此,上面提到的所有寄存器的地址以及功能说明要需要参考相应的user manual文档。

与本文代码对应的文档是SPEArPlus 600 PRELIMINARY USER MANUAL。

好, 下面就开始看代码。sys_init代码段位于Xloader目录下的pll/spear_pll.S代码中,该代码段的主要功能是对系统的时钟频率以及 工作方式进行初始化,并初始化外设时钟等,主要对GMAC Ethernet、UART1、UART2以及SMI(Serial Flash Controller)等外设进行了初始化。

对代码的整个说明以注释的方式跟源文件一起贴在这里,其中还有很多不明白的地方,希望以后能够慢慢明白:

1 /* PRESERVE8 */

2

3

4

5 /*;----------------------------------------------------------------------------------- */

6 /*

7 INCLUDE include/mmu946T.s

8 INCLUDE include/arm.equ

9 */

10 #include "splus_pll.h"

11

12

13 #define MISC_BASE 0xFCA80000

14 #define PHY_CTR_REG 0xA4

15 #define PERIPHCLK_CFG 0x28

16 #define PERIPH1_CLKEN 0x2C

17 #define PERIPH1_RST 0x38

18 #define AMBA_CLK_CFG 0x24

19 #define GMAC_SYNTH_CLK 0x68

20 #define GMAC_CTR_REG 0xA8

21 #define PLL1_FREQ 0xa600010f

22 #define PLL1_CNTL_REG 0x00000008

23 #define PLL1_FRQ_REG 0x0000000C

24

25 #define TXCLK_SYNTH 0x00000008 /*enable synth. clock */

26 #define PLL2_FREQ 0x8500010f

27 #define PLL2_CNTL_REG 0x00000014

28 #define PLL2_FRQ_REG 0x00000018

29

30

31 #define SYSCTL_BASE 0xFCA00000

32 #define SCCTRL 0x00000000

33 #define SCPLLCTRL 0x00000014

34 #define PLLTIM 0x1FFFFFF

35

36 #define SMI_BASE 0xFC000000

37 #define SMI_CR1 0x00

38

39 /* CONTROL REG 1 */

40 #define BANK_EN 0x0000000F /* enables all banks */

41 #define DSEL_TIME 0x00000050 /* Deselect time 5+1 SMI_CK periods */

42 #define PRESCAL3 0x00000300 /* AHB_CK prescaling value */

43 #define PRESCAL5 0x00000500 /* AHB_CK prescaling value */

44 #define PRESCALA 0x00000A00 /* AHB_CK prescaling value */

45 #define PRESCALF 0x00000F00 /* AHB_CK prescaling value */

46 #define PRESCAL9 0x00000900 /* AHB_CK prescaling value */

47 #define SW_MODE 0x10000000 /* enables SW Mode */

48 #define WB_MODE 0x20000000 /* Write Burst Mode */

49 #define FAST_MODE 0x00008000 /* Fast Mode */

50

51

52 #define ARM1_WE 0x00000001

53 #define ARM1 0x00000002

54 #define ARM2 0x00000004

55 #define UART1 0x00000008

56 #define UART2 0x00000010

57 #define SSP1 0x00000020

58 #define SSP2 0x00000040

59 #define I2C 0x00000080

60 #define JPEG 0x00000100

61 #define FSMC 0x00000200

62 #define FIRDA 0x00000400

63 #define GPT4 0x00000800

64 #define GPT5 0x00001000

65 #define GPIO4 0x00002000

66 #define SSP3 0x00004000

67 #define AD 0x00008000

68 #define GPT3 0x00010000

69 #define RTC 0x00020000

70 #define GPIO3 0x00040000

71 #define DMA 0x00080000

72 #define ROM 0x00100000

73 #define SMI 0x00200000

74 #define CLCD 0x00400000

75 #define GMAC 0x00800000

76 #define USBDEV 0x01000000

77 #define USBHOST1 0x02000000

78 #define USBHOST2 0x04000000

79 #define DDR_CTRL 0x08000000

80 #define RAM_WRAPPER 0x10000000

81 #define DDR_CORE 0x20000000

82

83 #define PLL_MODE_NON_DITHERED_M_MASK 0xFF000000

84 #define PLL_MODE_NON_DITHERED_M_SHIFT 24

85 #define PLL_VALUE_M_MASK 0xFFFF0000

86 #define PLL_VALUE_M_SHIFT 16

87 #define PLL_VALUE_P_MASK 0x00000700

88 #define PLL_VALUE_P_SHIFT 8

89 #define PLL_VALUE_N_MASK 0x0000000F

90 #define PLL_VALUE_N_SHIFT 0

91 #define SYNTH_XMASK 0XFFFF0000

92 #define SYNTH_YMASK 0X0000FFFF

93

94

95 /* AREA INIT, CODE, READONLY

96

97 ;-----------------------------------------------------------------------------------

98

99 ; ************************************************

100 ; * Routine to initialize the system controller.

101 ; ************************************************

102 */

103

104

105 .global sys_init

106 sys_init:

107

108 /* SYSTEM PLL INIT */

109

/* 0xFC880010是WdogRIS寄存器的地址。

* 该寄存器是一个只读的寄存器,当读出来的内容最低位为1时,

* 表示由于看门狗计数器到达了0而引发了一次中断,否则该位为0。

* 此处一直到114行代码断的作用是判断是否是由于看门狗引发的

* 中断而导致了sys_init的调用,如果是,则直接跳转到

* normal_mode标号处执行代码,而不需要对时钟进行初始化了。

*/

110 LDR R1, =0xFC880010

111 LDR R3,[R1, #0x0 ] /* 读取WdogRIS寄存器的内容 */

112 AND R3,R3,#0x1 /* 只取读取内容的最低位,该位就表明了当前看门狗的状态 */

113 CMP R3,#0x1 /* 看看最低位是否为1,如果是,则表明此时是由看门狗引发的中断

114 BEQ normal_mode * 不需要重新进行时钟初始化,直接跳转到normal_mode就行。

*/

115

/* 对系统工作模式进行设定,将系统设置为SLOW模式

* 其中,SYSCTL_BASE==0xFCA00000

* SCCTRL==0x00000000

*/

116 /* setting SYSCTL to SLOW mode */

117 LDR R1, =SYSCTL_BASE /* System Controler registers的基地址 */

118 LDR R3,[R1, #SCCTRL ] /* 读入SCCTRL寄存器的内容 */

119 ORR R2,R3,#0x00000002 /* 将SCCTRL寄存器的低三位设置成'b01x(x表示任意值),

* 当系统reset后,初始时SCCTRL的低三位为'b001。

* 当SCCTRL低三位的值为'b01x时,系统将被设置成SLOW模式。

*/

120 STR R2,[R1, #SCCTRL] /* 保存设置之后的值到SCCTRL寄存器 */

121

122 /* setting 100us pll timer */

123 LDR R4, =PLLTIM /* PLLTIM==0x01FFFFFF */

124 MOVS R2,R4,LSL #0x3 /* 由于SCPLLCTRL寄存器的[27:3]位是保存PllTime的,

* 因此这里将PLLTIM左移三位,用来设置SCPLLCTRL中的PllTime。

*/

125 STR R2,[R1, #SCPLLCTRL] /* 将移位之后的PLLTIM的值保存到SCPLLCTRL寄存器 */

126

127 /*

128 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

129 ; programming PLL1

130 ; LDR R1,=MISC_BASE

131 ; LDR R2,=PLL1_FREQ

132 ; STR R2,[R1, #PLL1_FRQ_REG]

133

134

135 /* ; programming PLL1 */

136 LDR R1,=MISC_BASE /* MISC_BASE==0xFCA80000,是各种寄存器的基地址 */

137

138 /* ; program M value */

139 LDR R2,[R1, #PLL1_FRQ_REG] /* ; read the PLL1_Frequency register*/

/* 距MISC_BASE偏移PLL1_FRQ_REG==0x0000000C处

* 是PLL1_FRQ寄存器。此处,首先加载PLL1_FRQ寄存器的值

*/

140 LDR R7,=PLL_MODE_NON_DITHERED_M_MASK /* PLL_MODE_NON_DITHERED_M_MASK==0xFF000000 */

141 MVN R7,R7 /* 将R7的值按位反转,也就是R7==0x00FFFFFF */

142 AND R2,R2,R7 /*; Mask the M value */ /* 将从PLL1_FRQ寄存器中读出的值的高8位清零,

* 这高8位要替换成自己定义的值

*/

143 LDR R4,=CONFIG_SYS_PLL1_M_VALUE /* CONFIG_SYS_PLL1_M_VALUE==166==0xA6,

* 这个值定义在spear_pll.h文件中,当我们修改该头文件中的

* CONFIG_SYS_PLL1_M_VALUE的时候,

* 就会在初始化的时候改变PLL1_FRQ寄存器中的高8位的值。

*/

144 MOV R4,R4, lsl #PLL_MODE_NON_DITHERED_M_SHIFT /* ; Shift and set the M value */

/* PLL_MODE_NON_DITHERED_M_SHIFT==24

* 这里是将CONFIG_SYS_PLL1_M_VALUE的值左移24位,

* 使其有效位位于32位字中的高8位。

*/

145 ORR R2,R2,R4 /* 将PLL1_FRQ寄存器中读出的值的高8位设置成spear_pll.h中定义的值 */

146 STR R2,[R1, #PLL1_FRQ_REG] /* ; load the M value*/ /* 将设置保存到PLL1_FRQ寄存器中 */

147

/* 148-155行所做的事情与139-146行所做的事情类似

* 只不过此处设置的是P值,而不是M值

*/

148 LDR R2,[R1, #PLL1_FRQ_REG]

149 LDR R7,=PLL_VALUE_P_MASK

150 MVN R7,R7

151 AND R2,R2,R7

152 LDR R4,=CONFIG_SYS_PLL1_P_VALUE

153 MOV R4,R4, lsl #PLL_VALUE_P_SHIFT

154 ORR R2,R2,R4

155 STR R2,[R1, #PLL1_FRQ_REG]

156

/* 157-164行所做的事情与139-146行所做的事情类似

* 只不过此处设置的是N值,而不是M值

*/

157 LDR R2,[R1, #PLL1_FRQ_REG]

158 LDR R7,=PLL_VALUE_N_MASK

159 MVN R7,R7

160 AND R2,R2,R7

161 LDR R4,=CONFIG_SYS_PLL1_N_VALUE

162 MOV R4,R4, lsl #PLL_VALUE_N_SHIFT

163 ORR R2,R2,R4

164 STR R2,[R1, #PLL1_FRQ_REG]

165

166

/* 从这里开始设置PLL1_CTR寄存器, PLL1_CNTL_REG==0x00000008

* 我猜应该是先diable再enable使得之前对pll的设置生效。

*/

167 /*; power down : pll1 ctrl programming */

168 LDR R2,=0x1c0a /* PLL1_CTR寄存器的第2位清零,disable pll */

169 STR R2,[R1, #PLL1_CNTL_REG]

170 /*;enable pll1 */

171 LDR R2,=0x1c0e /* PLL1_CTR寄存器的第2位置位,enable pll */

172 STR R2,[R1, #PLL1_CNTL_REG]

173 /* ;strobe */ /* 这一段没明白什么意思... */

174 LDR R2,=0x1c06

175 STR R2,[R1, #PLL1_CNTL_REG]

176 LDR R2,=0x1c0e

177 STR R2,[R1, #PLL1_CNTL_REG]

178 /*;wait for lock */

/* PLL1_CTR寄存器的最低位是一个只读位,当它为0的时候

* 表示pll处于unlock状态,当它为1的时候表示pll处于lock状态

* 此处,是在等待,知道pll处于lock状态。

*/

179 pll1_lock_1:

180 LDR R2,[R1,#PLL1_CNTL_REG] /* 读取PLL1_CTR寄存器的值 */

181 AND R2,R2,#0x1 /* 屏蔽除最低位之外的所有位 */

182 CMP R2,#0x1 /* 看看最低位是否为1,为1则表明已经处于lock状态了 */

183 BNE pll1_lock_1 /* 否则继续等待,知道最低位为1 */

/* 以下代码所做的工作类似与上面的代码

* 只不过上面的代码是对PLL1进行初始化,

* 而下面的代码是对PLL2进行初始化。原因可能是SPEArPlus这个开发板具有双ARM核。

*/

184 /*

185 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

186 ; programming PLL2

187 */

188 LDR R1,=MISC_BASE

189 LDR R2,=PLL2_FREQ

190 STR R2,[R1, #PLL2_FRQ_REG]

191

192 /*

193 ; power down : PLL2 ctrl programming

194 */

195 LDR R2,=0x1c0a

196 STR R2,[R1, #PLL2_CNTL_REG]

197 /* ;enable pll1*/

198 LDR R2,=0x1c0e

199 STR R2,[R1, #PLL2_CNTL_REG]

200 /*;strobe */

201 LDR R2,=0x1c06

202 STR R2,[R1, #PLL2_CNTL_REG]

203 LDR R2,=0x1c0e

204 STR R2,[R1, #PLL2_CNTL_REG]

205 /*;turn from int to ext. div */

206 /*; LDR R2,=0x1d0f */

207 /*; STR R2,[R1, #PLL2_CNTL_REG]*/

208

209 /*;wait for lock */

210 pll2_lock_1:

211 LDR R2,[R1,#PLL2_CNTL_REG]

212 AND R2,R2,#0x1

213 CMP R2,#0x1

214 BNE pll2_lock_1

215 /*

216 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

217 ;GMAC Tx Clock programming

218 ;MISCCntl->PERIPH1_CLKEN &= ~ PERIPH_GMAC;

219

220 ;MISCCntl->GMAC_SYNTH_CLK = (SYNTH_XMASK & (0x2<<16)) | (SYNTH_YMASK & 0x3); //375*(X/Y)=125

221 ;MISCCntl->GMAC_CTR_REG = TXCLK_SYNTH;

222

223 ;MISCCntl->PERIPH1_CLKEN |= PERIPH_GMAC;

224 ;MISCCntl->PERIPH1_RST |= PERIPH_GMAC;

225 ;MISCCntl->PERIPH1_RST &= ~PERIPH_GMAC;

226

227

228

229 ;MISCCntl->PERIPH1_CLKEN &= ~ PERIPH_GMAC;

230 */

/* PERIPH1_CLKEN==0x2C,此偏移对应的寄存器是PERIP1_CLK_ENB

* 该寄存器的功能是设置外设时钟的使能

* 这里先关闭gmac的时钟,然后对gmac进行相关设置之后再打开gmac的时钟

*/

231 LDR R1, =MISC_BASE

232 LDR R2, [R1, #PERIPH1_CLKEN] /*;// read the peripheral-1 clock enable register */

233 BIC R2, R2, #GMAC /* GMAC==0x00800000,也就是将PERIP1_CLK_ENB寄存器中的gmac_clken位清零

* disable gmac ethernet的时钟

*/

234 STR R2, [R1, #PERIPH1_CLKEN] /*;// set the enable value */ /* 保存设置到寄存器 */

235

236 /*;MISCCntl->GMAC_SYNTH_CLK = (SYNTH_XMASK & (0x2<<16)) | (SYNTH_YMASK & 0x3); //375*(X/Y)=125 */

/* GMAC_SYNTH_CLK==0x68,R1+GMAC_SYNTH_CLK的地址对应与GMAC_CLK_SYNT寄存器。

* 对GMAC_CLK_SYNT寄存器进行设置,将其设置为0x00020003

*/

237 LDR R2, =SYNTH_XMASK /* SYNTH_XMASK==0xFFFF0000 */

238 MOV R3, #0x02

239 MOV R3, R3, lsl #16

240 AND R2, R2, R3 /* R2 = 0xFFFF0000 & 0x00020000 */

241 LDR R3, =SYNTH_YMASK /* SYNTH_YMASK==0x0000FFFF */

242 AND R3, R3, #0x03

243 ORR R2, R2, R3 /* R2 = 0x00020000 | 0x00000003 */

244 STR R2, [R1, #GMAC_SYNTH_CLK] /* ;// read the peripheral-1 clock enable register */

245

246 /*;MISCCntl->GMAC_CTR_REG = TXCLK_SYNTH; */

/* GMAC_CTR_REG==0xA8,对应于GMAC_CFG_CTR寄存器 */

247 LDR R2, =0x00

248 STR R2, [R1, #GMAC_CTR_REG] /*;// read the peripheral-1 clock enable register */

249

250 /*;MISCCntl->PERIPH1_CLKEN |= PERIPH_GMAC; */

/* 重新将gmac对应的外设时钟使能 */

251 LDR R2, [R1, #PERIPH1_CLKEN] /* ;// read the peripheral-1 clock enable register*/

252 ORR R2, R2, #GMAC

253 STR R2, [R1, #PERIPH1_CLKEN] /* ;// set the enable value*/

254

255 /*;MISCCntl->PERIPH1_RST |= PERIPH_GMAC; */

/* PERIPH1_RST==0x38,对应于PERIP1_SOF_RST寄存器

* 此处将PERIPH1_RST寄存器中的GMAC位置1是令gmac ethernet执行reset

* 使得以上对gmac的设置生效

*/

256 LDR R2, [R1, #PERIPH1_RST] /* ;// read the peripheral-1 clock enable register*/

257 ORR R2, R2, #GMAC

258 STR R2, [R1, #PERIPH1_RST] /* ;// set the enable value */

259

260 /* ;MISCCntl->PERIPH1_RST &= ~PERIPH_GMAC;*/

/* 将PERIP1_SOF_RST寄存器中的GMAC复位,即reset完之后关闭reset功能 */

261 LDR R2, [R1, #PERIPH1_RST] /*;// read the peripheral-1 clock enable register */

262 BIC R2, R2, #GMAC

263 STR R2, [R1, #PERIPH1_RST] /*;// set the enable value */

264

265

266

267

268

269

270

271 /* ; enable plltimeen */

/* 设置PERIP_CLK_CFG寄存器的值为0x82,很奇怪手册里面没有对该寄存器的具体说明 */

272 LDR R2,=0x82

273 STR R2,[R1, #PERIPHCLK_CFG] /* PERIPHCLK_CFG==0x28 */

274

275 /* ; set pclkdiv & hclkdiv */

/* 设置CORE_CLK_CFG寄存器的值为0x555 */

276 LDR R1, =MISC_BASE

277 LDR R2,=0x555

278 STR R2,[R1, #AMBA_CLK_CFG] /* AMBA_CLK_CFG==0x24 */

279

280 /* ; SMI init */

281 LDR R1, =SMI_BASE /* SMI_BASE==0xFC000000 */

282 LDR R2, =BANK_EN /* BANK_EN==0x0000000F,低4位每一位代表一个bank

* 系统初始化的时候只有bank0是enable的,以支持

* 从外部memory来引导系统

*/

283 ORR R2, R2, #DSEL_TIME /* DSEL_TIME==0x00000050 Deselect time 5+1 SMI_CK periods */

284 ORR R2, R2, #PRESCALF /* PRESCALF==0x00000F00 AHB_CK prescaling value */

285 STR R2, [R1, #SMI_CR1] /* 保存设置到寄存器SMI_CR1 */

286

/* 又来一遍一样的?不明白。。。 */

287 LDR R2, =BANK_EN

288 ORR R2, R2, #DSEL_TIME

289 ORR R2, R2, #PRESCALF

290 STR R2, [R1, #SMI_CR1]

291

292

293 /*; setting SYSCTL to NORMAL mode */

294 LDR R1, =SYSCTL_BASE /* SYSCTL_BASE==0xFCA00000,是所有系统控制寄存器的基地址 */

295 LDR R3,[R1, #SCCTRL ] /* 读取SCCTRL寄存器的值保存在R3中 */

296 MOV R2,#0x00000004

297 STR R2,[R1, #SCCTRL] /* 将SCCTRL寄存器的值设置成0x00000004

* SCCTRL寄存器中最低3位为'b1xx(x表示任意值)时为NORMAL模式

*/

298

299 /* ; wait for normal mode */

300 LDR R1, =SYSCTL_BASE

/* SCCTRL寄存器的[6:3]位为ModeStatus位,当ModeStatus为'b0100时,

* 表示系统处于NORMAL模式。

* 下面的代码中,0x20=='b0(0100)000,括号中的4位即与ModeStatus对应。

* 当从SCCTRL寄存器读出的内容经过掩码后与0x20相同,则说明已经为NORMAL模式了

* 否则说明还没有切换到NORMAL模式,继续等待,知道成功切换到NORMAL模式。

*/

301 loop_normal:

302 LDR R2,[R1, #SCCTRL]

303 AND R2,R2,#0x20

304 CMP R2,#0x20

305 BNE loop_normal

306

/* 下面的代码块看似想要使能UART1、UART2和SMI的时钟

* 可是,感觉又好像没有必要,因为315行这一句就使能了所有外设的时钟了

* 有点诡异。。。没搞明白为什么要这么做

*/

307 /*; enable UART1, UART2, SMI clocks */

308 LDR R1, =MISC_BASE

309 LDR R2, [R1, #PERIPH1_CLKEN] /*;// read the peripheral-1 clock enable register */

310 /* ; enable UART1, UART2 & SMI clocks */

311 ORR R2, R2, #UART1 /* PERIP1_CLK_ENB第3位置1,使能UART1 clock */

312 ORR R2, R2, #UART2 /* PERIP1_CLK_ENB第4位置1,使能UART2 clock */

313 ORR R2, R2, #SMI /* PERIP1_CLK_ENB第21位置1,使能Serial Flash clock */

314 LDR R3, =0xFFFFFFF8

315 ORR R2,R2,R3 /* ???这个比较诡异,看着好像是使能所有的外设时钟??? */

316 STR R2, [R1, #PERIPH1_CLKEN] /*;// set the enable value */

317

/* 这部分跟上面那部分代码有的一拼,感觉是想要disable UART1、UART1和SMI的reset,

* 可是323这一句就清除了所有外设的reset了

* 所以还是看不明白为什么要这么做。。。

*/

318 /* ; remove reset of UART1, UART2, SMI peripherals */

319 LDR R2, [R1, #PERIPH1_RST]

320 BIC R2, R2, #UART1

321 BIC R2, R2, #UART2

322 BIC R2, R2, #SMI

323 MOV R2,#0x00

324 STR R2, [R1, #PERIPH1_RST]

325

326 /*; remove reset on all IPs */

327 /*; LDR R1,=MISC_BASE */

328 /*@; LDR R2,=0x0 */

329 /*; STR R2,[R1, #PERIPH1_RST_REG] */

330

/* 这个标号是在Watchdog引发中断以后,用于直接返回的,在整个sys_init的开头就进行了判断

* 另外,当不是由Watchdog引发中断时,程序走到这里也将返回,在我们上下文里,将返回XLOADER_ENTRY

*/

331 normal_mode:

332 MOV PC, R14

333
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: