天天看點

U-Boot移植過程概要記錄

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,在上面的步驟中有兩個地方需要說明:

在設定分頻系數的代碼如下:

U-Boot移植過程概要記錄

對于S3C2440,通過查詢硬體手冊可知 default FCLK 是12MHz;

U-Boot移植過程概要記錄

分頻系數設定在SDRAM初始化之前,

初始化SDRAM的時候對SDRAM控制器的時鐘相關寄存器使用下面的值:

U-Boot移植過程概要記錄

即使用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

并修改如下内容:

U-Boot移植過程概要記錄
U-Boot移植過程概要記錄

為了能夠編譯通過,将CONFIG_CMD_NAND 和 CONFIG_YAFFS2宏注釋掉,暫時先關閉Nandflash;

修改源碼目錄下面的boards.cfg檔案,添加内容如下:

U-Boot移植過程概要記錄

修改完畢之後執行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  */
           

經過以上修改之後的執行結果如下圖:

U-Boot移植過程概要記錄

已經有序列槽資訊輸出了,隻不過是亂碼,還需要進一步修改;

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的宏,代碼如下圖:

U-Boot移植過程概要記錄

在 include/configs/smdk2440.h中添加CONFIG_S3C2440宏定義,注釋CONFIG_S3C2410宏定義;

U-Boot移植過程概要記錄

為了編譯通過還需要修改如下兩個宏定義:

U-Boot移植過程概要記錄
U-Boot移植過程概要記錄

修改完畢之後,執行make distclean 然後重新配置編譯,燒寫到Norflash之後,效果如下圖:

U-Boot移植過程概要記錄

序列槽已經能夠正常運作;

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已經啟動成功,可以輸入u-boot的各種指令,可以讀寫Norflash;

9),用u-boot的loadb指令和minicom的kermit模式發送檔案,将uImage裝載到SDRAM中,然後啟動核心,在u-boot指令行輸入loadb 30000000,然後用minicom發送檔案:

發送過程如下圖:

U-Boot移植過程概要記錄

傳送完畢之後,執行bootm 30000000指令,可以看到核心啟動成功,如下圖:

U-Boot移植過程概要記錄
U-Boot移植過程概要記錄

核心啟動成功!

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上,運作結果如下圖:

U-Boot移植過程概要記錄

從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核心;
           

運作結果如下圖:

U-Boot移植過程概要記錄
U-Boot移植過程概要記錄

從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确定的位址上;

上述兩種方案,這篇部落格就不詳細說明了,後續部落格再詳細說明;

繼續閱讀