1,移植環境:
u-boot版本:u-boot-2012.04.01(包含了S3C2410 , 但是未包含S3C2440)
硬體環境: S3C2440平台
2,過程:
1),首先解壓縮 tar -xvf u-boot-2012.04.01.tar.bz2;
然後編譯make smdk2410_config && make ,之後得到u-boot.bin,通過openjtag燒入Norflash,從Norflash啟動開發闆,通過minicom序列槽檢視沒有任何反應;
2),u-boot-2012.04.01,第一階段啟動過程分析:
->設定CPU到管理模式
->關閉Watchdog
->屏蔽所有中斷
->禁止MMU和Caches及初始化SDRAM
->設定棧,在board_init_f函數中初始化:劃分記憶體,設定時鐘,初始化序列槽,初始化控制台等重要的初始化;
->在relocate_code代碼中:重新設定棧,重定位代碼;
->處理.rel.dyn(通過編譯選項PIE生成的代碼段)代碼段相關的連結位址到新位址,即就是處理位置無關代碼;
->清bss段;
->對S3C24X0不支援從Nandflash啟動,是以進入board_init_r函數:初始化gd_t結構體執行個體,設定malloc記憶體空間等,擷取flash大小,進入main_loop函數;
->在main_loop函數中通過bootcmd指令進入u-boot啟動第二階段啟動核心;
3),對于S3C2440,在上面的步驟中有兩個地方需要說明:
在設定分頻系數的代碼如下:
對于S3C2440,通過查詢硬體手冊可知 default FCLK 是12MHz;
分頻系數設定在SDRAM初始化之前,
初始化SDRAM的時候對SDRAM控制器的時鐘相關寄存器使用下面的值:
即使用HCLK=60Mhz,但是在SDRAM之前并未設定MPLL,而是在board_init_f->board_early_init_f函數設定時鐘的時候才設定時鐘控制器的MPLL;
通過上述時鐘頻率的分析,可知,S3C2440初始化SDRAM的時候 HCLK并不是60MHz,是以此處需要調整,是以這樣初始化的SDRAM在使用的時候會有問題;
4),添加S3C2440單闆目錄:
cp -rd board/samsung/smdk2410/ board/samsung/smdk2440/
修改其中的smdk2410.c 為smdk2440.c
修改board/samsung/smdk2440/Makefile中的2410為2440
cp include/configs/smdk2410.h include/configs/smdk2440.h
并修改如下内容:
為了能夠編譯通過,将CONFIG_CMD_NAND 和 CONFIG_YAFFS2宏注釋掉,暫時先關閉Nandflash;
修改源碼目錄下面的boards.cfg檔案,添加内容如下:
修改完畢之後執行make smdk2440_config && make 開始編譯,編譯通過得到u-boot.bin暫時還是無法運作;
5),修改上述3)中問題;
修改政策:将時鐘控制器的初始化全部放在SDRAM初始化之前(根據硬體手冊設定MPLL,設定分頻系數,設定總線異步模式);然後初始化SDRAM(根據硬體手冊從新設定SDRAM控制器寄存器的值)
start.S 和 smdk2440.c 中修改時鐘控制器部分,lowlevel_init.S修改SDRAM控制器部分;
arch/arm/cpu/arm920t/start.S
/* modify by gh begin */
/* FCLK:HCLK:PCLK = 1:4:8 */
ldr r0, =CLKDIVN
mov r1, #0x5
str r1, [r0]
/* set BUS mode */
mrc p15,,r0,c1,c0,
orr r0,r0,#0xc0000000
mcr p15,,r0,c1,c0,
/* set MPLL FCLK=400MHz */
ldr r0,=
ldr r1,=
str r1,[r0]
/* modify by gh end */
board/samsung/smdk2440/smdk2440.c : serial_init_dev方法
/* modify by gh begin; */
/* to reduce PLL lock time, adjust the LOCKTIME register */
//writel(0xFFFFFF, &clk_power->locktime);
/* configure MPLL */
//writel((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV,
// &clk_power->mpllcon);
/* some delay between MPLL and UPLL */
//pll_delay(4000);
/* modify by gh end; */
board/samsung/smdk2440/lowlevel_init.S中的記憶體控制器初始化值:
/* modify by gh begin */
SMRDATA:
.long
.long
.long
.long
.long
.long
.long
.long
.long
.long
.long
.long
.long
/* modify by gh end */
經過以上修改之後的執行結果如下圖:
已經有序列槽資訊輸出了,隻不過是亂碼,還需要進一步修改;
6),修改序列槽輸出:
序列槽初始化方法的調用路徑:
board_init_f->serial_init->serial_init_dev->_serial_setbrg
serial_init_dev方法的修改在上述3)問題的修改中已經修改完畢,檢視_serial_setbrg這個方法,在這個方法中用PCLK時鐘和波特率計算UBRDIV寄存器的[10:0]為的值,在擷取PCLK時鐘的get_PCLK方法中最終會調用到get_HCLK方法,在這個方法中已經支援S3C2440隻不過未定義CONFIG_S3C2440的宏,代碼如下圖:
在 include/configs/smdk2440.h中添加CONFIG_S3C2440宏定義,注釋CONFIG_S3C2410宏定義;
為了編譯通過還需要修改如下兩個宏定義:
修改完畢之後,執行make distclean 然後重新配置編譯,燒寫到Norflash之後,效果如下圖:
序列槽已經能夠正常運作;
7),修改Norflash資訊擷取:
1> Norflash支援XIP技術,是以從Norflash啟動時,Norflash不需要初始化;
2> Norflash支援兩種規範JEDEC,CFI(Common Flash Interface);
JEDEC規範是從Norflash中查尋到廠商ID和裝置ID,然後用這兩個ID在u-boot中記錄的晶片資訊數組中查詢Norflash的其他硬體資訊,例如:大小,sector的種類的數量,位寬等;
CFI規範将所有的硬體資訊存儲在Norflash晶片内,u-boot可以從Norflash讀取所有的硬體資訊;
從上述序列槽輸出截圖中的錯誤資訊可以定位到,如下代碼問題:
arch/arm/lib/board.c中的方法 board_init_r(gd_t *id, ulong dest_addr);
void board_init_r(gd_t *id, ulong dest_addr)
{
flash_size = flash_init();
if (flash_size > ) {
. . . . .
} else {//由于flash_size=0,列印錯誤資訊;
puts(failed);
hang();
}
進一步跟蹤代碼; flash_init(cfi_flash.c)->flash_detect_legacy(cfi_flash.c);就會看到
u-boot預設使用JEDEC規範,而u-boot的硬體資訊數組:
static const struct amd_flash_info jedec_table[]; drivers/mtd/jedec_flash.c
中并沒有我用的Norflash硬體資訊:MX29LV160DBTI-70G;是以,有兩種該法支援Norflash;
1> 在jedec_table數組中添加MX29LV160DBTI-70G的硬體資訊(硬體資訊查詢晶片資料手冊);
2> 在include/configs/smdk2440.h中去掉CONFIG_FLASH_CFI_LEGACY宏定義,讓u-boot使用CFI規範,然後在根據錯誤資訊修改最大sector的數量,修改如下:
/* modify by gh begin */
//#define CONFIG_SYS_MAX_FLASH_SECT (19)
#define CONFIG_SYS_MAX_FLASH_SECT (35)
/* modify by gh end */
/* modify by gh begin */
//#define CONFIG_FLASH_CFI_LEGACY
/* modify by gh end */
我按照第二種修改方法修改完畢之後,燒寫重新開機開發闆,結果如下圖:
u-boot已經啟動成功,可以輸入u-boot的各種指令,可以讀寫Norflash;
9),用u-boot的loadb指令和minicom的kermit模式發送檔案,将uImage裝載到SDRAM中,然後啟動核心,在u-boot指令行輸入loadb 30000000,然後用minicom發送檔案:
發送過程如下圖:
傳送完畢之後,執行bootm 30000000指令,可以看到核心啟動成功,如下圖:
核心啟動成功!
10)支援Nandflash;
Nandflash初始化序列如下:
start.S -> board_init_r(arch/arm/lib/board.c) ->
nand_init(drivers/mtd/nand/nand.c) -> nand_init_chip(drivers/mtd/nand/nand.c) ->
board_nand_init(drivers/mtd/nand/s3c2440_nand.c) 和 nand_scan(drivers/mtd/nand/nand_base.c)
對上面調用序列分析,通過和S3C2440 晶片手冊 和 Nandflash晶片手冊中的操作過程比對,可以發現需要修改一下幾點:
修改include/configs/smdk2440.h
/* modify by gh begin; */
#define CONFIG_CMD_NAND //去掉之前的注釋;
#define CONFIG_S3C24XX_CUSTOM_NAND_TIMING
#define CONFIG_S3C24XX_TACLS 0
#define CONFIG_S3C24XX_TWRPH0 1
#define CONFIG_S3C24XX_TWRPH1 0
/* modify by gh end; */
// modify by gh begin
#ifndef CONFIG_S3C2440
#define CONFIG_NAND_S3C2410
#define CONFIG_SYS_S3C2410_NAND_HWECC
#else
#define CONFIG_NAND_S3C2440 //如果是S3C2440則添加這個宏;
#endif
// modify by gh end
建立drivers/mtd/nand/s3c2440_nand.c
修改drivers/mtd/nand/Makefile
添加如下代碼:
# modify by gh begin
COBJS-$(CONFIG_NAND_S3C2440) += s3c2440_nand.o
# modify by gh end
修改drivers/mtd/nand/s3c2440_nand.c
首先把S3C2410對于的宏定義修改為S3C2440,然後根據晶片手冊(S3C2440晶片手冊,Nandflash晶片手冊)調整宏定義的值
/* modify by gh begin */
#define S3C2440_NFCONF_EN (1<<0)
/* modify by gh end */
/* modify by gh begin */
#define S3C2440_NFCONF_nFCE (1<<1)
#define S3C2440_NFCONF_TACLS(x) ((x)<<12)
#define S3C2440_NFCONF_TWRPH0(x) ((x)<<8)
#define S3C2440_NFCONF_TWRPH1(x) ((x)<<4)
#define S3C2440_ADDR_NALE 0xC
#define S3C2440_ADDR_NCLE 8
/* modify by gh end */
static void s3c2410_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
... ...
if (ctrl & NAND_CTRL_CHANGE) {
ulong IO_ADDR_W = (ulong)nand;
/* modify by gh begin */
if (ctrl & NAND_CLE)
IO_ADDR_W |= S3C2440_ADDR_NCLE;
if (ctrl & NAND_ALE)
IO_ADDR_W |= S3C2440_ADDR_NALE;
/* modify by gh end; */
chip->IO_ADDR_W = (void *)IO_ADDR_W;
/* modify by gh begin */
if (ctrl & NAND_NCE)
writel(readl(&nand->nfcont) & ~S3C2440_NFCONF_nFCE,
&nand->nfcont);
else
writel(readl(&nand->nfcont) | S3C2440_NFCONF_nFCE,
&nand->nfcont);
/* modify by gh end; */
}
... ...
}
int board_nand_init(struct nand_chip *nand)
{
... ...
#if defined(CONFIG_S3C24XX_CUSTOM_NAND_TIMING)
tacls = CONFIG_S3C24XX_TACLS;
twrph0 = CONFIG_S3C24XX_TWRPH0;
twrph1 = CONFIG_S3C24XX_TWRPH1;
#else
tacls = ;
twrph0 = ;
twrph1 = ;
#endif
/* modify by gh begin */
writel((<<|<<|),&nand_reg->nfcont);
cfg = S3C2440_NFCONF_TACLS(tacls);
cfg |= S3C2440_NFCONF_TWRPH0(twrph0);
cfg |= S3C2440_NFCONF_TWRPH1(twrph1);
writel(cfg, &nand_reg->nfconf);
/* modify by gh end; */
... ...
}
修改drivers/mtd/nand/nand_base.c
static void nand_select_chip(struct mtd_info *mtd, int chipnr)
{
struct nand_chip *chip = mtd->priv;
switch (chipnr) {
case -:
chip->cmd_ctrl(mtd, NAND_CMD_NONE, | NAND_CTRL_CHANGE);
break;
case :
/* add by gh begin */
chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
/* add by gh end; */
break;
default:
BUG();
}
}
修改完畢編譯,燒寫到Norflash上,運作結果如下圖:
從Nandflash的kernel分區(0x60000,2M)加載uImage到SDRAM,然後bootm 30000000運作:
nand read x30000000 x60000 x200000
bootm
也可以在u-boot指令行執行:
set bootcmd "nand read 0x30000000 0x60000 0x200000;bootm 30000000"
save //将bootcmd參數儲存到u-boot的環境變量中;
reset //重新開機之後直接進入Linux核心;
運作結果如下圖:
從Nandflash加載Linux核心,啟動成功!
由于Norflash空間不夠大,放不下核心,是以,通常将Linux核心和u-boot都放在Nandflash的分區上,然後直接從Nandflash啟動,這個有兩種方案:
1> u-boot本身的SPL;
2> 去掉直接u-boot的編譯時的PIE選項和重定位代碼時對連結位址的動态處理,這樣可以減少u-boot體積,使得重定位代碼的位置盡量位于SRAM的4k空間内,手工安排u-boot連結位址,将u-boot裝載到SDRAM确定的位址上;
上述兩種方案,這篇部落格就不詳細說明了,後續部落格再詳細說明;