參考文件:
1、TI.Reference_Manual_1.pdf
http://pan.baidu.com/s/1c1BJNtm
2、TI_AM335X.pdf
http://pan.baidu.com/s/1geNOYI3
芯片到uboot啟動流程 :ROM → MLO(SPL)→ uboot.img。
rom為芯片內部的固化的程序,用戶不能修改。內部rom程序流程:
Booting的方式可以通過引腳SYSBOOT[15...0]來配置。SYSBoot[15...0]=LCD_DATA[15...0]引腳,這些引腳的狀態會被上電復位后獲取。
AM335x 中bootloader被分成了 3 個部分:
第一級 bootloader:引導加載程序,板子上電后會自動執行這些代碼,如選擇哪種方式啟動(NAND,SDcard,UART。。。),然后跳轉轉到第二級 bootloader。這些代碼應該是存放在 176KB 的 ROM 中。
第二級 bootloader:MLO(SPL),用以硬件初始化:關閉看門狗,關閉中斷,設置 CPU 時鍾頻率、速度等操作。然后會跳轉到第三級bootloader。MLO文件應該會被映射到 64 KB的 Internal SRAM 中。
第三級 bootloader:uboot.img,C代碼的入口。
其中第一級 bootloader 是板子固化的,第二級和第三級是通過編譯 uboot 所得的。
u-boot-SPL編譯
也就是說spl的編譯是編譯uboot的一部分,和uboot.bin走的是兩條編譯流程,這個要重點注意。
正常來說,會先編譯主體uboot,也就是uboot.bin.再編譯uboot-spl,也就是uboot-spl.bin,雖然編譯命令是一起的,但是編譯流程是分開的。
在uboot的頂層目錄Makefile中有:
spl/u-boot-spl.bin: spl/u-boot-spl
@:
spl/u-boot-spl: tools prepare $(if $(CONFIG_OF_SEPARATE),dts/dt.dtb)
$(Q)$(MAKE) obj=spl -f $(srctree)/scripts/Makefile.spl all
指出編譯u-boot-spl.bin的鏈接是在u-boot的頂層目錄下的scripts/make.spl里定義的,且要編譯u-boot-spl.bin首先要定義CONFIG_SPL(在u-boot的頂層目錄下的configs目錄對應的AM335X_evm_defconfig有定義)
在文件scripts/makedile.spl中:
spl/u-boot-spl.bin依賴如下
||
spl/u-boot-spl-nodtb.bin
||
spl/u-boot-spl
||
u-boot-spl-init, u-boot-spl-main, spl/u-boot-spl.ld 最后通過cmd_u-boot-spl來生成spl/u-boot-spl。
重點定義:
1、CONFIG_SPL:configs/AM335x_evm_defconfig,用於指定是否需要編譯SPL,也就是是否需要編譯出uboot-spl.bin文件
2、CONFIG_SPL_TEXT_BASE 定義在板子的對應的config文件中
3、CONFIG_SPL_BUILD
-
在編譯spl過程中,會配置
u-boot-2016.03/scripts/Makefile.spl中定義了如下KBUILD_CPPFLAGS += -DCONFIG_SPL_BUILD
也就是說在編譯uboot-spl.bin的過程中,CONFIG_SPL_BUILD這個宏是被定義的。 更加具體參考: -
http://www.cnblogs.com/leaven/p/6296140.html。
U-boot-spl代碼流程
通過u-boot-spl編譯的腳本(Makefile.spl)知u-boot-spl入口:u-boot-2016.03/arch/arm/cpu/armv7/u-boot-spl.lds
ENTRY(_start)
所以uboot-spl的代碼入口函數是_start
對應於路徑project-X/u-boot/arch/arm/lib/vector.S的_start,后續就是從這個函數開始分析。
uboot-spl需要做的事情
CPU初始剛上電的狀態。需要小心的設置好很多狀態,包括cpu狀態、中斷狀態、MMU狀態等等。
在armv7架構的uboot-spl,主要需要做如下事情
- 關閉中斷,svc模式
- 禁用MMU、TLB
- 芯片級、板級的一些初始化操作
- IO初始化
- 時鍾
- 內存
- 選項,串口初始化
- 選項,nand flash初始化
- 其他額外的操作
- 加載u-boot.img,跳轉到u-boot.img.
上述工作,也就是uboot-spl代碼流程的核心。
代碼流程
1、代碼整體流程
代碼整體流程如下,以下列出來的就是spl核心函數。
_start———–>reset————–>關閉中斷
………………………………|
………………………………———->cpu_init_cp15———–>關閉MMU,TLB
………………………………|
………………………………———->cpu_init_crit————->lowlevel_init————->CPU級初始化
………………………………|
………………………………———->_main————–>board_init_f_alloc_reserve & board_init_f_init_reserve & board_init_f———->board_init_r加載u-boot.img,跳轉到u-boot.img.
board_init_f,board_init_r執行時已經是C語言環境了。在這里需要結束掉SPL的工作,跳轉到u-boot.img中。
2、_start
上述已經說明了_start是整個spl的入口,其代碼如下:
arch/arm/lib/vector.S
_start:
#ifdef CONFIG_SYS_DV_NOR_BOOT_CFG .word CONFIG_SYS_DV_NOR_BOOT_CFG #endif b reset
會跳轉到reset中。
注意,spl的流程在reset中就應該被結束,也就是說在reset中,就應該轉到到BL2,也就是uboot中了。
后面看reset的實現。
3、reset
建議先參考[kernel 啟動流程] (第二章)第一階段之——設置SVC、關閉中斷,了解一下為什么要設置SVC、關閉中斷以及如何操作。目的:svc模式主要用於軟件中斷和OS操作系統。若是中斷未關閉,CPU在初始化階段有可能產生中斷,但是中斷處理函數還未就緒(未對中斷進行處理),容易使CPU halt停止工作。
代碼如下:
arch/arm/cpu/armv7/start.S
.globl reset
.globl save_boot_params_ret
reset:
/* Allow the board to save important registers */ b save_boot_params save_boot_params_ret: /* * disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode, * except if in HYP mode already */ mrs r0, cpsr and r1, r0, #0x1f @ mask mode bits teq r1, #0x1a @ test for HYP mode bicne r0, r0, #0x1f @ clear all mode bits orrne r0, r0, #0x13 @ set SVC mode orr r0, r0, #0xc0 @ disable FIQ and IRQ msr cpsr,r0 @@ 以上通過設置CPSR寄存器里設置CPU為SVC模式,禁止中斷 @@ 具體操作可以參考《[kernel 啟動流程] (第二章)第一階段之——設置SVC、關閉中斷》的分析 /* the mask ROM code should have PLL and others stable */ #ifndef CONFIG_SKIP_LOWLEVEL_INIT bl cpu_init_cp15 @@ 調用cpu_init_cp15,初始化協處理器CP15,從而禁用MMU和TLB。 @@ 后面會有一小節進行分析 bl cpu_init_crit @@ 調用cpu_init_crit,進行一些關鍵的初始化動作,也就是平台級和板級的初始化 @@ 后面會有一小節進行分析 #endif bl _main @@ 跳轉到主函數,也就是要加載BL2以及跳轉到BL2的主體部分
4、cpu_init_cp15
建議先參考[kernel 啟動流程] (第六章)第一階段之——打開MMU兩篇文章的分析。
cpu_init_cp15主要用於對cp15協處理器進行初始化,其主要目的就是關閉其MMU和TLB。
代碼如下(去掉無關部分的代碼):
arch/arm/cpu/armv7/start.S
ENTRY(cpu_init_cp15)
/* * Invalidate L1 I/D */ mov r0, #0 @ set up for MCR mcr p15, 0, r0, c8, c7, 0 @ invalidate TLBs mcr p15, 0, r0, c7, c5, 0 @ invalidate icache mcr p15, 0, r0, c7, c5, 6 @ invalidate BP array mcr p15, 0, r0, c7, c10, 4 @ DSB mcr p15, 0, r0, c7, c5, 4 @ ISB @@ 這里只需要知道是對CP15處理器的部分寄存器清零即可。 @@ 將協處理器的c7\c8清零等等,各個寄存器的含義請參考《ARM的CP15協處理器的寄存器》 /* * disable MMU stuff and caches */ mrc p15, 0, r0, c1, c0, 0 bic r0, r0, #0x00002000 @ clear bits 13 (--V-) bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM) orr r0, r0, #0x00000002 @ set bit 1 (--A-) Align orr r0, r0, #0x00000800 @ set bit 11 (Z---) BTB #ifdef CONFIG_SYS_ICACHE_OFF bic r0, r0, #0x00001000 @ clear bit 12 (I) I-cache #else orr r0, r0, #0x00001000 @ set bit 12 (I) I-cache #endif mcr p15, 0, r0, c1, c0, 0 @@ 通過上述的文章的介紹,我們可以知道cp15的c1寄存器就是MMU控制器 @@ 上述對MMU的一些位進行清零和置位,達到關閉MMU和cache的目的,具體的話去看一下上述文章吧。 ENDPROC(cpu_init_cp15)
5、cpu_init_crit
cpu_init_crit,進行一些關鍵的初始化動作,也就是平台級和板級的初始化。其代碼核心就是lowlevel_init,如下
arch/arm/cpu/armv7/start.S
ENTRY(cpu_init_crit)
/* * Jump to board specific initialization... * The Mask ROM will have already initialized * basic memory. Go here to bump up clock rate and handle * wake up conditions. */ b lowlevel_init @ go setup pll,mux,memory ENDPROC(cpu_init_crit)
所以說lowlevel_init就是這個函數的核心。
lowlevel_init一般是由板級代碼自己實現的。但是對於某些平台來說,也可以使用通用的lowlevel_init,其定義在arch/arm/cpu/armv7/lowlevel_init.S中
以Am335x為例,在移植過程中,就需要在lowlevel_init.S里加入一些簡單的板級初始化,例如在lowlevle_init.s------->s_init中:
arch/arm/cpu/armv7/am33xx/board.c
void s_init(void) { /* * The ROM will only have set up sufficient pinmux to allow for the * first 4KiB NOR to be read, we must finish doing what we know of * the NOR mux in this space in order to continue. */ #ifdef CONFIG_NOR_BOOT enable_norboot_pin_mux(); #endif watchdog_disable(); //arch/arm/cpu/armv7/am33xx/board.c set_uart_mux_conf(); // xx/board/ti/am335x/board.c setup_clocks_for_console(); // arch/arm/cpu/armv7/am335x/Clock_am33xx.c uart_soft_reset(); // arch/arm/cpu/armv7/am33xx/board.c #if defined(CONFIG_SPL_AM33XX_ENABLE_RTC32K_OSC) // include/configs/ti_am335x_common.h /* Enable RTC32K clock */ rtc32k_enable(); // arch/arm/cpu/armv7/am33xx/board.c #endif }
(其實只要實現了lowlevel_init了就好,沒必要說在哪里是實現,但是通常規范都是創建了lowlevel_init.S來專門實現lowlevel_init函數)。
6、_main
spl的main的主要目標是調用board_init_f進行先前的板級初始化動作,板級初始化之后調用board_init_r主要設計為,加載u-boot.img到DDR上並且跳轉到u-boot.img中。DDR在板級初始化中完成--board_init_f。
由於board_init_f是以C語言的方式實現,所以需要先構造C語言環境。
注意:uboot-spl和uboot的代碼是通用的,其區別就是通過CONFIG_SPL_BUILD宏來進行區分的。
所以以下代碼中,我們只列出spl相關的部分,也就是被CONFIG_SPL_BUILD包含的部分。
arch/arm/lib/crt0.S
ENTRY(_main) /* * Set up initial C runtime environment and call board_init_f(0). 因為后面是C語言環境,首先是設置堆棧 */ #if defined(CONFIG_SPL_BUILD) && defined(CONFIG_SPL_STACK) ldr sp, =(CONFIG_SPL_STACK) //設置堆棧為后邊調用board_init_f做准備 #else ldr sp, =(CONFIG_SYS_INIT_SP_ADDR) #endif #if defined(CONFIG_CPU_V7M) /* v7M forbids using SP as BIC destination */ mov r3, sp bic r3, r3, #7 mov sp, r3 #else bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ #endif mov r0, sp bl board_init_f_alloc_reserve //2. 為gd_t結構體保留空間理論上有些函數可以用了,例如:preloader_console_init()初始化串口,common/init/board_init.c mov sp, r0 /* set up gd here, outside any C code */ mov r9, r0 bl board_init_f_init_reserve //3. 初始化gd_t(清零) common/init/board_init.c //gd_t的地址存在r9寄存器中,結構體中存放的是全局參數 mov r0, #0 bl board_init_f #if ! defined(CONFIG_SPL_BUILD) /* * Set up intermediate environment (new sp and gd) and call * relocate_code(addr_moni). Trick here is that we'll return * 'here' but relocated. */ ldr sp, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp */ #if defined(CONFIG_CPU_V7M) /* v7M forbids using SP as BIC destination */ mov r3, sp bic r3, r3, #7 mov sp, r3 #else bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ #endif ldr r9, [r9, #GD_BD] /* r9 = gd->bd */ sub r9, r9, #GD_SIZE /* new GD is below bd */ adr lr, here ldr r0, [r9, #GD_RELOC_OFF] /* r0 = gd->reloc_off */ add lr, lr, r0 #if defined(CONFIG_CPU_V7M) orr lr, #1 /* As required by Thumb-only */ #endif ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */ b relocate_code here: /* * now relocate vectors */ bl relocate_vectors /* Set up final (full) environment */ bl c_runtime_cpu_setup /* we still call old routine here */ #endif #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_FRAMEWORK) # ifdef CONFIG_SPL_BUILD /* Use a DRAM stack for the rest of SPL, if requested */ bl spl_relocate_stack_gd cmp r0, #0 movne sp, r0 movne r9, r0 # endif ldr r0, =__bss_start /* this is auto-relocated! */ #ifdef CONFIG_USE_ARCH_MEMSET ldr r3, =__bss_end /* this is auto-relocated! */ mov r1, #0x00000000 /* prepare zero to clear BSS */ subs r2, r3, r0 /* r2 = memset len */ bl memset #else ldr r1, =__bss_end /* this is auto-relocated! */ mov r2, #0x00000000 /* prepare zero to clear BSS */ clbss_l:cmp r0, r1 /* while not at end of BSS */ #if defined(CONFIG_CPU_V7M) itt lo #endif strlo r2, [r0] /* clear 32-bit BSS word */ addlo r0, r0, #4 /* move to next */ blo clbss_l #endif #if ! defined(CONFIG_SPL_BUILD) bl coloured_LED_init bl red_led_on #endif /* call board_init_r(gd_t *id, ulong dest_addr) */ mov r0, r9 /* gd_t */ ldr r1, [r9, #GD_RELOCADDR] /* dest_addr */ /* call board_init_r */ #if defined(CONFIG_SYS_THUMB_BUILD) ldr lr, =board_init_r /* this is auto-relocated! */ bx lr #else ldr pc, =board_init_r /* this is auto-relocated! */ #endif /* we should not return here. */ #endif ENDPROC(_main)
代碼拆分如下:
(1)因為后面是C語言環境,首先是設置堆棧
ldr sp, =(CONFIG_SPL_STACK)
@@ 設置堆棧為CONFIG_SPL_STACK
bic sp, sp, #7 /* 8-byte alignment for ABI compliance */ @@ 堆棧是8字節對齊,2^7bit=2^3byte=8byte mov r0, sp @@ 把堆棧地址存放到r0寄存器中
關於CONFIG_SPL_STACK,我們通過前面的文章
我們已經知道am335x的BL1(spl)是運行在RAM的,並且RAM的地址空間是0x402F0400-0x402FFFFF,RAM前面的部分放的是BL1的代碼部分,所以把RAM最后的空間用來當作堆棧。
所以CONFIG_SPL_STACK定義如下:
include/configs/ti_am335x_common.h
--->ti_armv7_common.h
---->ti_armv7_keystone2.h
#define CONFIG_SPL_STACK (0x402F0400+32*1024+32*1024+8*1024-4)//自己計算結果可能不正確
- 1
- 1
注意:上述還不是最終的堆棧地址,只是暫時的堆棧地址!!!
(2)為GD分配空間
bl board_init_f_alloc_reserve
@@ 把堆棧的前面一部分空間分配給GD使用
mov sp, r0
@@ 重新設置堆棧指針SP
/* set up gd here, outside any C code */ mov r9, r0 @@ 保存GD的地址到r9寄存器中
注意:雖然sp的地址和GD的地址是一樣的,但是堆棧是向下增長的,而GD則是占用該地址后面的部分,所以不會有沖突的問題。
關於GD,也就是struct global_data,可以簡單的理解為uboot的全局變量都放在了這里,比較重要,所以后續有會寫篇文章說明一下global_data。這里只需要知道在開始C語言環境的時候需要先為這個結構體分配空間。
board_init_f_alloc_reserve實現如下
common/init/board_init.c
ulong board_init_f_alloc_reserve(ulong top)
{
/* Reserve early malloc arena */ /* LAST : reserve GD (rounded up to a multiple of 16 bytes) */ top = rounddown(top-sizeof(struct global_data), 16); // 現將top(也就是r0寄存器,前面說過存放了暫時的指針地址),減去sizeof(struct global_data),也就是預留出一部分空間給sizeof(struct global_data)使用。 // rounddown表示向下16個字節對其 return top; // 到這里,top就存放了GD的地址,也是SP的地址 //把top返回,注意,返回后,其實還是存放在了r0寄存器中。 }
還有一點,其實GD在spl中沒什么使用,主要是用在uboot中,但在uboot中的時候還需要另外分配空間,在講述uboot流程的時候會說明。
(3)初始化GD空間
前面說了,此時r0寄存器存放了GD的地址。
bl board_init_f_init_reserve
board_init_f_init_reserve實現如下
common/init/board_init.c
編譯SPL的時候_USE_MEMCPY宏沒有打開,所以我們去掉了_USE_MEMCPY的無關部分。
void board_init_f_init_reserve(ulong base) { struct global_data *gd_ptr; int *ptr; /* * clear GD entirely and set it up. * Use gd_ptr, as gd may not be properly set yet. */ gd_ptr = (struct global_data *)base; // 從r0獲取GD的地址 /* zero the area */ for (ptr = (int *)gd_ptr; ptr < (int *)(gd_ptr + 1); ) *ptr++ = 0; // 對GD的空間進行清零 }
(4)跳轉到板級前期的初始化函數中
u-boot-2016.03/arch/arm/cpu/armv7/Am33xx/board.c
如下代碼
bl board_init_f
board_init_f需要由板級代碼自己實現。
u-boot-2016.03/arch/arm/cpu/armv7/Am33xx/board.c
1 void board_init_f(ulong dummy) 2 { 3 board_early_init_f(); 4 preloader_console_init(); //初始化uart,使用Puts函數 5 sdram_init(); 6 }
u-boot-2016.03/arch/arm/cpu/armv7/Am33xx/board.c
1 int board_early_init_f(void) 2 { 3 prcm_init(); //系統外設時鍾初始化使能,SPI,I2C,uart,gpio..... 4 set_mux_conf_regs(); //系統外設引功能配置,spi,I2C,uart,gpio. 5 return 0; 6 }
u-boot-2016.03/arch/arm/cpu/armv7/Am33xx/clock.c
1 void prcm_init() 2 { 3 enable_basic_clocks(); 4 scale_vcores(); 5 setup_dplls(); 6 }
u-boot-2016.03/board/ti/am335x/board.c
void set_mux_conf_regs(void) { __maybe_unused struct am335x_baseboard_id header; if (read_eeprom(&header) < 0) puts("Could not get board ID.\n"); enable_board_pin_mux(&header); puts("enable_board_pin.\n"); }
u-boot-2016.03/board/ti/am335x/MUX.c
void enable_board_pin_mux(struct am335x_baseboard_id *header) { /* Do board-specific muxes. */ if (board_is_bone(header)) { /* Beaglebone pinmux */ configure_module_pin_mux(mii1_pin_mux); configure_module_pin_mux(mmc0_pin_mux); #if defined(CONFIG_NAND) configure_module_pin_mux(nand_pin_mux); #elif defined(CONFIG_NOR) configure_module_pin_mux(bone_norcape_pin_mux); #else configure_module_pin_mux(mmc1_pin_mux); #endif } else if (board_is_gp_evm(header)) { /* General Purpose EVM */ unsigned short profile = detect_daughter_board_profile(); configure_module_pin_mux(rgmii1_pin_mux); configure_module_pin_mux(mmc0_pin_mux); /* In profile #2 i2c1 and spi0 conflict. */ if (profile & ~PROFILE_2) configure_module_pin_mux(i2c1_pin_mux); /* Profiles 2 & 3 don't have NAND */ #ifdef CONFIG_NAND if (profile & ~(PROFILE_2 | PROFILE_3)) configure_module_pin_mux(nand_pin_mux); #endif else if (profile == PROFILE_2) { configure_module_pin_mux(mmc1_pin_mux); configure_module_pin_mux(spi0_pin_mux); } } else if (board_is_idk(header)) { /* Industrial Motor Control (IDK) */ configure_module_pin_mux(mii1_pin_mux); configure_module_pin_mux(mmc0_no_cd_pin_mux); } else if (board_is_evm_sk(header)) { /* Starter Kit EVM */ configure_module_pin_mux(i2c1_pin_mux); // 配置I2C1對應的引腳為功能引腳,u-boot-2016.03/board/ti/am335x/mux.c configure_module_pin_mux(gpio0_7_pin_mux); configure_module_pin_mux(rgmii1_pin_mux); configure_module_pin_mux(mmc0_pin_mux_sk_evm); } else if (board_is_bone_lt(header)) { /* Beaglebone LT pinmux */ configure_module_pin_mux(mii1_pin_mux); configure_module_pin_mux(mmc0_pin_mux); #if defined(CONFIG_NAND) && defined(CONFIG_EMMC_BOOT) configure_module_pin_mux(nand_pin_mux); #elif defined(CONFIG_NOR) && defined(CONFIG_EMMC_BOOT) configure_module_pin_mux(bone_norcape_pin_mux); #else configure_module_pin_mux(mmc1_pin_mux); #endif } else { puts("Unknown board, cannot configure pinmux."); hang(); } }
也就是說
void board_init_f(ulong dummy)
{
1、board_early_init_f------>prmc_init(u-boot-2016.03/arch/arm/cpu/armv7/Am33xx/clock.c)------->enable_basic_clocks(u-boot- 使能CPU各外設包括gpio時鍾 2016.03/arch/arm/cpu/armv7/Am33xx/clock_am3xx.c)
------>set_mux_conf_regs(u-boot-2016.03/board/ti/am335x/board.c)------>enable_board_pin_mux(&header)配置板級各外設引腳功能(UART,MMC,SPI,)u-boot-2016.03/board/ti/am335x/Mux.c
2、sdram_init()--------------》(u-boot-2016.03/board/ti/am335x/board.c)
--------->未完待續
}
SDRAM初始化,只有SDRAM初始化完成了,才能為下一步的u-boot運行提供運行環境,這一部分也是要根據板上的SDRAM芯片型號來初始化的。
u-boot-2016.03/board/ti/am335x/board.c
void sdram_init(void) { __maybe_unused struct am335x_baseboard_id header; if (read_eeprom(&header) < 0) puts("Could not get board ID.\n"); if (board_is_evm_sk(&header)) { /* * EVM SK 1.2A and later use gpio0_7 to enable DDR3. * This is safe enough to do on older revs. */ gpio_request(GPIO_DDR_VTT_EN, "ddr_vtt_en"); //請求GPIO0_7腳是否有效,AM335x共有GPIO=32*4.如果GPIO_DDR_VTT_EN的數值在0~127之間,返回0 gpio_direction_output(GPIO_DDR_VTT_EN, 1);//使GPIO的第8腳,GPIO0_7輸出1,使能SDRAM芯片的電源 } if (board_is_evm_sk(&header)) config_ddr(303, &ioregs_evmsk, &ddr3_data, &ddr3_cmd_ctrl_data, &ddr3_emif_reg_data, 0); else if (board_is_bone_lt(&header)) config_ddr(400, &ioregs_bonelt, &ddr3_beagleblack_data, &ddr3_beagleblack_cmd_ctrl_data, &ddr3_beagleblack_emif_reg_data, 0); else if (board_is_evm_15_or_later(&header)) config_ddr(303, &ioregs_evm15, &ddr3_evm_data, &ddr3_evm_cmd_ctrl_data, &ddr3_evm_emif_reg_data, 0); else config_ddr(266, &ioregs, &ddr2_data, &ddr2_cmd_ctrl_data, &ddr2_emif_reg_data, 0); debug(">>spl:SDRAM_init()\n"); puts(">>spl:SDRAM_init()\n"); }
板級的初級初始化之后board_init_f()會為后續的調用board_init_r()函數提供更多的堆棧的空間(放在了SDRAM中)
bl spl_relocate_stack_gd
u-boot-2016.03/common/spl/spl.c
ulong spl_relocate_stack_gd(void) { #ifdef CONFIG_SPL_STACK_R gd_t *new_gd; ulong ptr = CONFIG_SPL_STACK_R_ADDR; #ifdef CONFIG_SPL_SYS_MALLOC_SIMPLE if (CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN) { if (!(gd->flags & GD_FLG_SPL_INIT)) panic_str("spl_init must be called before heap reloc"); ptr -= CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN; gd->malloc_base = ptr; gd->malloc_limit = CONFIG_SPL_STACK_R_MALLOC_SIMPLE_LEN; gd->malloc_ptr = 0; } #endif /* Get stack position: use 8-byte alignment for ABI compliance */ ptr = CONFIG_SPL_STACK_R_ADDR - roundup(sizeof(gd_t),16); new_gd = (gd_t *)ptr; memcpy(new_gd, (void *)gd, sizeof(gd_t)); #if !defined(CONFIG_ARM) gd = new_gd; #endif return ptr; #else return 0; #endif }
堆棧空間分配好之后就可以用board_init_r了。
u-boot-2016.03/common/spl/spl.c
board_init_r { 1、經行memory的malloc池的初始化 2、timer_init() 初始化時鍾 arch/arm/cpu/armv7/omap_common 3、spl_board_init() arch/arm/cpu/armv7/omap_common/boot-common.c 4、jump_to_image_no_args(&spl_image) // 跳轉u-boot入口地址 entry_point,entry_point是由u-boot.img頭部信息提供的
5、SPL結束其生命,將控制權交給u-boot/Linux
}
arch/arm/cpu/armv7/omap_common/boot-common.c
void spl_board_init(void) { /* * Save the boot parameters passed from romcode. * We cannot delay the saving further than this, * to prevent overwrites. */ save_omap_boot_params(); // 這里講boot_device的參數傳遞給spl_boot_list(自己理解),確定以什么方式加載u-boot.img(uart or spi or mmc or nand....) /* Prepare console output */ // preloader_console_init(); #if defined(CONFIG_SPL_NAND_SUPPORT) || defined(CONFIG_SPL_ONENAND_SUPPORT) gpmc_init(); #endif #ifdef CONFIG_SPL_I2C_SUPPORT i2c_init(CONFIG_SYS_OMAP24_I2C_SPEED, CONFIG_SYS_OMAP24_I2C_SLAVE); #endif #if defined(CONFIG_AM33XX) && defined(CONFIG_SPL_MUSB_NEW_SUPPORT) arch_misc_init(); #endif #if defined(CONFIG_HW_WATCHDOG) hw_watchdog_init(); #endif #ifdef CONFIG_AM33XX am33xx_spl_board_init(); #endif }
更加詳細的啟動順序,及相關內存地址分配參考:
鏈接: https://pan.baidu.com/s/1hseK4cO 密碼: m9ci