天天看點

嵌入式Linux引導過程之1.3——Xloader的sys_init

上一篇文章對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

繼續閱讀