嵌入式Linux開發(fā)環(huán)境的搭建之:U-Boot移植
簡單地說,Bootloader就是在操作系統(tǒng)內(nèi)核運行之前運行的一段程序,它類似于PC機中的BIOS程序。通過這段程序,可以完成硬件設(shè)備的初始化,并建立內(nèi)存空間的映射關(guān)系,從而將系統(tǒng)的軟硬件環(huán)境帶到一個合適的狀態(tài),為最終加載系統(tǒng)內(nèi)核做好準(zhǔn)備。
通常,Bootloader比較依賴于硬件平臺,特別是在嵌入式系統(tǒng)中,更為如此。因此,在嵌入式世界里建立一個通用的Bootloader是一件比較困難的事情。盡管如此,仍然可以對Bootloader歸納出一些通用的概念來指導(dǎo)面向用戶定制的Bootloader設(shè)計與實現(xiàn)。
(1)Bootloader所支持的CPU和嵌入式開發(fā)板。
每種不同的CPU體系結(jié)構(gòu)都有不同的Bootloader。有些Bootloader也支持多種體系結(jié)構(gòu)的CPU,如后面要介紹的U-Boot支持ARM、MIPS、PowerPC等眾多體系結(jié)構(gòu)。除了依賴于CPU的體系結(jié)構(gòu)外,Bootloader實際上也依賴于具體的嵌入式板級設(shè)備的配置。
(2)Bootloader的存儲位置。
系統(tǒng)加電或復(fù)位后,所有的CPU通常都從某個由CPU制造商預(yù)先安排的地址上取指令。而基于CPU構(gòu)建的嵌入式系統(tǒng)通常都有某種類型的固態(tài)存儲設(shè)備(比如ROM、EEPROM或Flash等)被映射到這個預(yù)先安排的地址上。因此在系統(tǒng)加電后,CPU將首先執(zhí)行Bootloader程序。
(3)Bootloader的啟動過程分為單階段和多階段兩種。通常多階段的Bootloader能提供更為復(fù)雜的功能,以及更好的可移植性。
(4)Bootloader的操作模式。大多數(shù)Bootloader都包含兩種不同的操作模式:“啟動加載”模式和“下載”模式,這種區(qū)別僅對于開發(fā)人員才有意義。
n 啟動加載模式:這種模式也稱為“自主”模式。也就是Bootloader從目標(biāo)機上的某個固態(tài)存儲設(shè)備上將操作系統(tǒng)加載到RAM中運行,整個過程并沒有用戶的介入。這種模式是嵌入式產(chǎn)品發(fā)布時的通用模式。
n 下載模式:在這種模式下,目標(biāo)機上的Bootloader將通過串口連接或網(wǎng)絡(luò)連接等通信手段從主機(Host)下載文件,比如:下載內(nèi)核映像和根文件系統(tǒng)映像等。從主機下載的文件通常首先被Bootloader保存到目標(biāo)機的RAM中,然后再被Bootloader寫入到目標(biāo)機上的Flash類固態(tài)存儲設(shè)備中。Bootloader的這種模式在系統(tǒng)更新時使用。工作于這種模式下的Bootloader通常都會向它的終端用戶提供一個簡單的命令行接口。
(5)Bootloader與主機之間進行文件傳輸所用的通信設(shè)備及協(xié)議,最常見的情況就是,目標(biāo)機上的Bootloader通過串口與主機之間進行文件傳輸,傳輸協(xié)議通常是xmodem/ymodem/zmodem等。但是,串口傳輸?shù)乃俣仁怯邢薜?,因此通過以太網(wǎng)連接并借助TFTP等協(xié)議來下載文件是個更好的選擇。
2.Bootloader啟動流程Bootloader的啟動流程一般分為兩個階段:stage1和stage2,下面分別對這兩個階段進行講解。
(1)Bootloader的stage1。
在stage1中Bootloader主要完成以下工作。
n 基本的硬件初始化,包括屏蔽所有的中斷、設(shè)置CPU的速度和時鐘頻率、RAM初始化、初始化外圍設(shè)備、關(guān)閉CPU內(nèi)部指令和數(shù)據(jù)cache等。
n 為加載stage2準(zhǔn)備RAM空間,通常為了獲得更快的執(zhí)行速度,通常把stage2加載到RAM空間中來執(zhí)行,因此必須為加載Bootloader的stage2準(zhǔn)備好一段可用的RAM空間。
n 復(fù)制stage2到RAM中,在這里要確定兩點:①stage2的可執(zhí)行映像在固態(tài)存儲設(shè)備的存放起始地址和終止地址;②RAM空間的起始地址。
n 設(shè)置堆棧指針sp,這是為執(zhí)行stage2的C語言代碼做好準(zhǔn)備。
(2)Bootloader的stage2。
在stage2中Bootloader主要完成以下工作。
n 用匯編語言跳轉(zhuǎn)到main入口函數(shù)。
由于stage2的代碼通常用C語言來實現(xiàn),目的是實現(xiàn)更復(fù)雜的功能和取得更好的代碼可讀性和可移植性。但是與普通C語言應(yīng)用程序不同的是,在編譯和鏈接Bootloader這樣的程序時,不能使用glibc庫中的任何支持函數(shù)。
n 初始化本階段要使用到的硬件設(shè)備,包括初始化串口、初始化計時器等。在初始化這些設(shè)備之前可以輸出一些打印信息。
n 檢測系統(tǒng)的內(nèi)存映射,所謂內(nèi)存映射就是指在整個4GB物理地址空間中指出哪些地址范圍被分配用來尋址系統(tǒng)的內(nèi)存。
n 加載內(nèi)核映像和根文件系統(tǒng)映像,這里包括規(guī)劃內(nèi)存占用的布局和從Flash上復(fù)制數(shù)據(jù)。
n 設(shè)置內(nèi)核的啟動參數(shù)。
5.2.2U-Boot概述1.U-Boot簡介U-Boot(UniversalBootloader)是遵循GPL條款的開放源碼項目。它是從FADSROM、8xxROM、PPCBOOT逐步發(fā)展演化而來。其源碼目錄、編譯形式與Linux內(nèi)核很相似,事實上,不少U-Boot源碼就是相應(yīng)的Linux內(nèi)核源程序的簡化,尤其是一些設(shè)備的驅(qū)動程序,這從U-Boot源碼的注釋中能體現(xiàn)這一點。但是U-Boot不僅僅支持嵌入式Linux系統(tǒng)的引導(dǎo),而且還支持NetBSD、VxWorks、QNX、RTEMS、ARTOS、LynxOS等嵌入式操作系統(tǒng)。其目前要支持的目標(biāo)操作系統(tǒng)是OpenBSD、NetBSD、FreeBSD、4.4BSD、Linux、SVR4、Esix、Solaris、Irix、SCO、Dell、NCR、VxWorks、LynxOS、pSOS、QNX、RTEMS、ARTOS。這是U-Boot中Universal的一層含義,另外一層含義則是U-Boot除了支持PowerPC系列的處理器外,還能支持MIPS、x86、ARM、NIOS、XScale等諸多常用系列的處理器。這兩個特點正是U-Boot項目的開發(fā)目標(biāo),即支持盡可能多的嵌入式處理器和嵌入式操作系統(tǒng)。就目前為止,U-Boot對PowerPC系列處理器支持最為豐富,對Linux的支持最完善。
2.U-Boot特點U-Boot的特點如下。
n 開放源碼;
n 支持多種嵌入式操作系統(tǒng)內(nèi)核,如Linux、NetBSD、VxWorks、QNX、RTEMS、ARTOS、LynxOS;
n 支持多個處理器系列,如PowerPC、ARM、x86、MIPS、XScale;
n 較高的可靠性和穩(wěn)定性;
n 高度靈活的功能設(shè)置,適合U-Boot調(diào)試、操作系統(tǒng)不同引導(dǎo)要求和產(chǎn)品發(fā)布等;
n 豐富的設(shè)備驅(qū)動源碼,如串口、以太網(wǎng)、SDRAM、Flash、LCD、NVRAM、EEPROM、RTC、鍵盤等;
n 較為豐富的開發(fā)調(diào)試文檔與強大的網(wǎng)絡(luò)技術(shù)支持。
3.U-Boot主要功能U-Boot可支持的主要功能列表。
n 系統(tǒng)引導(dǎo):支持NFS掛載、RAMDISK(壓縮或非壓縮)形式的根文件系統(tǒng)。支持NFS掛載,并從Flash中引導(dǎo)壓縮或非壓縮系統(tǒng)內(nèi)核。
n 基本輔助功能:強大的操作系統(tǒng)接口功能;可靈活設(shè)置、傳遞多個關(guān)鍵參數(shù)給操作系統(tǒng),適合系統(tǒng)在不同開發(fā)階段的調(diào)試要求與產(chǎn)品發(fā)布,尤其對Linux支持最為強勁;支持目標(biāo)板環(huán)境參數(shù)多種存儲方式,如Flash、NVRAM、EEPROM;CRC32校驗,可校驗Flash中內(nèi)核、RAMDISK映像文件是否完好。
n 設(shè)備驅(qū)動:串口、SDRAM、Flash、以太網(wǎng)、LCD、NVRAM、EEPROM、鍵盤、USB、PCMCIA、PCI、RTC等驅(qū)動支持。
n 上電自檢功能:SDRAM、Flash大小自動檢測;SDRAM故障檢測;CPU型號。
n 特殊功能:XIP內(nèi)核引導(dǎo)。
5.2.3U-Boot源碼導(dǎo)讀1.U-Boot源碼結(jié)構(gòu)U-Boot源碼結(jié)構(gòu)如圖5.27所示。
圖5.27U-Boot源碼結(jié)構(gòu)
n board:和一些已有開發(fā)板有關(guān)的代碼,比如makefile和U-Boot.lds等都和具體開發(fā)板的硬件和地址分配有關(guān)。
n common:與體系結(jié)構(gòu)無關(guān)的代碼,用來實現(xiàn)各種命令的C程序。
n cpu:包含CPU相關(guān)代碼,其中的子目錄都是以U-BOOT所支持的CPU為名,比如有子目錄arm926ejs、mips、mpc8260和nios等,每個特定的子目錄中都包括cpu.c和interrupt.c,start.S等。其中cpu.c初始化CPU、設(shè)置指令Cache和數(shù)據(jù)Cache等;interrupt.c設(shè)置系統(tǒng)的各種中斷和異常,比如快速中斷、開關(guān)中斷、時鐘中斷、軟件中斷、預(yù)取中止和未定義指令等;匯編代碼文件start.S是U-BOOT啟動時執(zhí)行的第一個文件,它主要是設(shè)置系統(tǒng)堆棧和工作方式,為進入C程序奠定基礎(chǔ)。
n disk:disk驅(qū)動的分區(qū)相關(guān)代碼。
n doc:文檔。
n drivers:通用設(shè)備驅(qū)動程序,比如各種網(wǎng)卡、支持CFI的Flash、串口和USB總線等。
n fs:支持文件系統(tǒng)的文件,U-BOOT現(xiàn)在支持cramfs、fat、fdos、jffs2和registerfs等。
n include:頭文件,還有對各種硬件平臺支持的匯編文件,系統(tǒng)的配置文件和對文件系統(tǒng)支持的文件。
n net:與網(wǎng)絡(luò)有關(guān)的代碼,BOOTP協(xié)議、TFTP協(xié)議、RARP協(xié)議和NFS文件系統(tǒng)的實現(xiàn)。
n lib_arm:與ARM體系結(jié)構(gòu)相關(guān)的代碼。
n tools:創(chuàng)建S-Record格式文件和U-BOOTimages的工具。
2.U-Boot重要代碼(1)cpu/arm920t/start.S
這是U-Boot的起始位置。在這個文件中設(shè)置了處理器的狀態(tài)、初始化中斷向量和內(nèi)存時序等,從Flash中跳轉(zhuǎn)到定位好的內(nèi)存位置執(zhí)行。
.globl_start(起始位置:中斷向量設(shè)置)
_start:breset
ldrpc,_undefined_instruction
ldrpc,_software_interrupt
ldrpc,_prefetch_abort
ldrpc,_data_abort
ldrpc,_not_used
ldrpc,_irq
ldrpc,_fiq
_undefined_instruction:.wordundefined_instruction
_software_interrupt:.wordsoftware_interrupt
_prefetch_abort:.wordprefetch_abort
_data_abort:.worddata_abort
_not_used:.wordnot_used
_irq:.wordirq
_fiq:.wordfiq
_TEXT_BASE:(代碼段起始位置)
.wordTEXT_BASE
.globl_armboot_start
_armboot_start:
.word_start
/*
*Thesearedefinedintheboard-specificlinkerscript.
*/
.globl_bss_start(BSS段起始位置)
_bss_start:
.word__bss_start
.globl_bss_end
_bss_end:
.word_end
reset:(執(zhí)行入口)
/*
*setthecputoSVC32mode;使處理器進入特權(quán)模式
*/
mrsr0,cpsr
bicr0,r0,#0x1f
orrr0,r0,#0xd3
msrcpsr,r0
relocate:(代碼的重置)/*relocateU-BoottoRAM*/
adrr0,_start/*r0<-currentpositionofcode*/
ldrr1,_TEXT_BASE/*testifwerunfromflashorRAM*/
cmpr0,r1/*don'trelocduringdebug*/
beqstack_setup
ldrr2,_armboot_start
ldrr3,_bss_start
subr2,r3,r2/*r2<-sizeofarmboot*/
addr2,r0,r2/*r2<-sourceendaddress*/
copy_loop:(拷貝過程)
ldmiar0!,{r3-r10}/*copyfromsourceaddress[r0]*/
stmiar1!,{r3-r10}/*copytotargetaddress[r1]*/
cmpr0,r2/*untilsourceendaddreee[r2]*/
blecopy_loop
/*Setupthestack;設(shè)置堆棧*/
stack_setup:
ldrr0,_TEXT_BASE/*upper128KiB:relocateduboot*/
subr0,r0,#CFG_MALLOC_LEN/*mallocarea*/
subr0,r0,#CFG_GBL_DATA_SIZE/*bdinfo*/
clear_bss:(清空BSS段)
ldrr0,_bss_start/*findstartofbsssegment*/
ldrr1,_bss_end/*stophere*/
movr2,#0x00000000/*clear*/
clbss_l:strr2,[r0]/*clearloop...*/
addr0,r0,#4
cmpr0,r1
bneclbss_l
ldrpc,_start_armboot
_start_armboot:.wordstart_armboot
(2)interrupts.c
這個文件是處理中斷的,如打開和關(guān)閉中斷等。
#ifdefCONFIG_USE_IRQ
/*enableIRQinterrupts;中斷使能函數(shù)*/
voidenable_interrupts(void)
{
unsignedlongtemp;
__asm____volatile__("mrs%0,cpsrn"
"bic%0,%0,#0x80n"
"msrcpsr_c,%0"
:"=r"(temp)
:
:"memory");
}
/*
*disableIRQ/FIQinterrupts;中斷屏蔽函數(shù)
*returnstrueifinterruptshadbeenenabledbeforewedisabledthem
*/
intdisable_interrupts(void)
{
unsignedlongold,temp;
__asm____volatile__("mrs%0,cpsrn"
"orr%1,%0,#0xc0n"
"msrcpsr_c,%1"
:"=r"(old),"=r"(temp)
:
:"memory");
return(old&0x80)==0;
}
#endif
voidshow_regs(structpt_regs*regs)
{
unsignedlongflags;
constchar*processor_modes[]={
"USER_26","FIQ_26","IRQ_26","SVC_26",
"UK4_26","UK5_26","UK6_26","UK7_26",
"UK8_26","UK9_26","UK10_26","UK11_26",
"UK12_26","UK13_26","UK14_26","UK15_26",
"USER_32","FIQ_32","IRQ_32","SVC_32",
"UK4_32","UK5_32","UK6_32","ABT_32",
"UK8_32","UK9_32","UK10_32","UND_32",
"UK12_32","UK13_32","UK14_32","SYS_32",
};
…
}
/*在U-Boot啟動模式下,在原則上要禁止中斷處理,所以如果發(fā)生中斷,當(dāng)作出錯處理*/
voiddo_fiq(structpt_regs*pt_regs)
{
printf("fastinterruptrequestn");
show_regs(pt_regs);
bad_mode();
}
voiddo_irq(structpt_regs*pt_regs)
{
printf("interruptrequestn");
show_regs(pt_regs);
bad_mode();
}
(3)cpu.c
這個文件是對處理器進行操作,如下所示:
intcpu_init(void)
{
/*
*setupupstacksifnecessary;設(shè)置需要的堆棧
*/
#ifdefCONFIG_USE_IRQ
DECLARE_GLOBAL_DATA_PTR;
IRQ_STACK_START=_armboot_start-CFG_MALLOC_LEN-CFG_GBL_DATA_SIZE-4;
FIQ_STACK_START=IRQ_STACK_START-CONFIG_STACKSIZE_IRQ;
#endif
return0;
}
intcleanup_before_linux(void)/*準(zhǔn)備加載linux*/
{
/*
*thisfunctioniscalledjustbeforewecalllinux
*itpreparestheprocessorforlinux
*
*weturnoffcachesetc...
*/
unsignedlongi;
disable_interrupts();
/*turnoffI/D-cache:關(guān)閉cache*/
asm("mrcp15,0,%0,c1,c0,0":"=r"(i));
i&=~(C1_DC|C1_IC);
asm("mcrp15,0,%0,c1,c0,0"::"r"(i));
/*flushI/D-cache*/
i=0;
asm("mcrp15,0,%0,c7,c7,0"::"r"(i));
return(0);
}
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
.=0x00000000;
.=ALIGN(4);
.text:
{
cpu/arm920t/start.o(.text)
*(.text)
}
.=ALIGN(4);
.rodata:{*(.rodata)}
.=ALIGN(4);
.data:{*(.data)}
.=ALIGN(4);
.got:{*(.got)}
__u_boot_cmd_start=.;
.u_boot_cmd:{*(.u_boot_cmd)}
__u_boot_cmd_end=.;
.=ALIGN(4);
__bss_start=.;
.bss:{*(.bss)}
_end=.;
}
(4)memsetup.S
這個文件是用于配置開發(fā)板參數(shù)的,如下所示:
/*memsetup.c*/
/*memorycontrolconfiguration*/
/*maker0relativethecurrentlocationsothatit*/
/*readsSMRDATAoutofFLASHratherthanmemory!*/
ldrr0,=SMRDATA
ldrr1,_TEXT_BASE
subr0,r0,r1
ldrr1,=BWSCON /*BusWidthStatusController*/
addr2,r0,#52
0:
ldrr3,[r0],#4
strr3,[r1],#4
cmpr2,r0
bne0b
/*everythingisfinenow*/
movpc,lr
.ltorg
5.2.4U-Boot移植主要步驟(1)建立自己的開發(fā)板類型。
閱讀makefile文件,在makefile文件中添加兩行,如下所示:
fs2410_config:unconfig
@./mkconfig$(@:_config=)armarm920tfs2410
其中“arm”為表示處理器體系結(jié)構(gòu)的種類,“arm920t”表示處理器體系結(jié)構(gòu)的名稱,“fs2410”為主板名稱。
在board目錄中建立fs2410目錄,并將smdk2410目錄中的內(nèi)容(cp–asmdk2410/*fs2410)復(fù)制到該目錄中。
n 在include/configs/目錄下將smdk2410.h復(fù)制到(cpsmdk2410.hfs2410.h)。
n 修改ARM編譯器的目錄名及前綴(都要改成以“fs2410”開頭)。
n 完成之后,可以測試配置。
$makefs2410_config;make
(2)修改程序鏈接地址。
在board/s3c2410中有一個config.mk文件,它是用于設(shè)置程序鏈接的起始地址,因為會在U-Boot中增加功能,所以留下6MB的空間,修改33F80000為33A00000。
為了以后能用U-Boot的“go”命令執(zhí)行修改過的用loadb或tftp下載的U-Boot,需要在board/s3c2410的memsetup.S中標(biāo)記符”0:”上加入5句:
movr3,pc
ldrr4,=0x3FFF0000
andr3,r3,r4(以上3句得到實際代碼啟動的內(nèi)存地址)
aadr0,r0,r3(用go命令調(diào)試u-boot時,啟動地址在RAM)
addr2,r2,r3(把初始化內(nèi)存信息的地址,加上實際啟動地址)
(3)將中斷禁止的部分應(yīng)該改為如下所示(/cpu/arm920t/start.S):
#ifdefined(CONFIG_S3C2410)
ldrr1,=0x7ff
ldrr0,=INTSUBMSK
strr1,[r0]
#endif
(4)因為在fs2410開發(fā)板啟動時是直接從NandFlash加載代碼,所以啟動代碼應(yīng)該改成如下所示(/cpu/arm920t/start.S):
#ifdefCONFIG_S3C2410_NAND_BOOT@START
@resetNAND
movr1,#NAND_CTL_BASE
ldrr2,=0xf830@initialvalue
strr2,[r1,#oNFCONF]
ldrr2,[r1,#oNFCONF]
bicr2,r2,#0x800@enablechip
strr2,[r1,#oNFCONF]
movr2,#0xff@RESETcommand
strbr2,[r1,#oNFCMD]
movr3,#0@wait
nand1:
addr3,r3,#0x1
cmpr3,#0xa
bltnand1
nand2:
ldrr2,[r1,#oNFSTAT]@waitready
tstr2,#0x1
beqnand2
ldrr2,[r1,#oNFCONF]
orrr2,r2,#0x800@disablechip
strr2,[r1,#oNFCONF]
@getreadtocallCfunctions(fornand_read())
ldrsp,DW_STACK_START@setupstackpointer
movfp,#0@nopreviousframe,sofp=0
@copyU-BoottoRAM
ldrr0,=TEXT_BASE
movr1,#0x0
movr2,#0x20000
blnand_read_ll
tstr0,#0x0
beqok_nand_read
bad_nand_read:
loop2:bloop2@infiniteloop
ok_nand_read:
@verify
movr0,#0
ldrr1,=TEXT_BASE
movr2,#0x400@4bytes*1024=4K-bytes
go_next:
ldrr3,[r0],#4
ldrr4,[r1],#4
teqr3,r4
bnenotmatch
subsr2,r2,#4
beqstack_setup
bnego_next
notmatch:
loop3:bloop3@infiniteloop
#endif@CONFIG_S3C2410_NAND_BOOT@END
在“_start_armboot:.wordstart_armboot”后加入:
.align2
DW_STACK_START:.wordSTACK_BASE+STACK_SIZE-4
(5)修改內(nèi)存配置(board/fs2410/lowlevel_init.S)。
#defineBWSCON0x48000000
#definePLD_BASE0x2C000000
#defineSDRAM_REG0x2C000106
/*BWSCON*/
#defineDW8 (0x0)
#defineDW16(0x1)
#defineDW32(0x2)
#defineWAIT(0x1<<2)
#defineUBLB(0x1<<3)
/*BANKSIZE*/
#defineBURST_EN(0x1<<7)
#defineB1_BWSCON(DW16+WAIT)
#defineB2_BWSCON(DW32)
#defineB3_BWSCON(DW32)
#defineB4_BWSCON(DW16+WAIT+UBLB)
#defineB5_BWSCON(DW8+UBLB)
#defineB6_BWSCON(DW32)
#defineB7_BWSCON(DW32)
/*BANK0CON*/
#defineB0_Tacs0x0/*0clk*/
#defineB0_Tcos0x1/*1clk*/
#defineB0_Tacc0x7/*14clk*/
#defineB0_Tcoh0x0/*0clk*/
#defineB0_Tah0x0/*0clk*/
#defineB0_Tacp0x0/*pagemodeisnotused*/
#defineB0_PMC0x0/*pagemodedisabled*/
/*BANK1CON*/
#defineB1_Tacs0x0/*0clk*/
#defineB1_Tcos0x1/*1clk*/
#defineB1_Tacc0x7/*14clk*/
#defineB1_Tcoh0x0/*0clk*/
#defineB1_Tah0x0/*0clk*/
#defineB1_Tacp0x0/*pagemodeisnotused*/
#defineB1_PMC0x0/*pagemodedisabled*/
……
/*REFRESHparameter*/
#defineREFEN0x1/*Refreshenable*/
#defineTREFMD0x0/*CBR(CASbeforeRAS)/Autorefresh*/
#defineTrp0x0/*2clk*/
#defineTrc0x3/*7clk*/
#defineTchr0x2/*3clk*/
#defineREFCNT1113/*period=15.6us,HCLK=60Mhz,(2048+1-15.6*60)*/
......
.word((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))
.word((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))
.word((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)
.word0x32
.word0x30
.word0x30
(6)加入NandFlash讀函數(shù)(創(chuàng)建board/fs2410/nand_read.c文件)。
#include<config.h>
#define__REGb(x)(*(volatileunsignedchar*)(x))
#define__REGi(x)(*(volatileunsignedint*)(x))
#defineNF_BASE0x4e000000
#defineNFCONF__REGi(NF_BASE+0x0)
#defineNFCMD__REGb(NF_BASE+0x4)
#defineNFADDR__REGb(NF_BASE+0x8)
#defineNFDATA__REGb(NF_BASE+0xc)
#defineNFSTAT__REGb(NF_BASE+0x10)
#defineBUSY1
inlinevoidwait_idle(void)
{
Inti;
while(!(NFSTAT&BUSY))
{
for(i=0;i<10;i++);
}
}
/*lowlevelnandreadfunction*/
intnand_read_ll(unsignedchar*buf,unsignedlongstart_addr,intsize)
{
inti,j;
if((start_addr&NAND_BLOCK_MASK)||(size&NAND_BLOCK_MASK))
{
return-1;/*invalidalignment*/
}
/*chipEnable*/
NFCONF&=~0x800;
for(i=0;i<10;i++);
for(i=start_addr;i<(start_addr+size);)
{
/*READ0*/
NFCMD=0;
/*WriteAddress*/
NFADDR=i&0xff;
NFADDR=(i>>9)&0xff;
NFADDR=(i>>17)&0xff;
NFADDR=(i>>25)&0xff;
wait_idle();
for(j=0;j<NAND_SECTOR_SIZE;j++,i++)
{
*buf=(NFDATA&0xff);
buf++;
}
}
/*chipDisable*/
NFCONF|=0x800;/*chipdisable*/
return0;
}
修改board/fs2410/makefile文件,以增加nand_read()函數(shù)。
OBJS:=fs2410.oflash.onand_read.o
(7)加入NandFlash的初始化函數(shù)(board/fs2410/fs2410.c)。
#if(CONFIG_COMMANDS&CFG_CMD_NAND)
typedefenum
{
NFCE_LOW,
NFCE_HIGH
}NFCE_STATE;
staticinlinevoidNF_Conf(u16conf)
{
S3C2410_NAND*constnand=S3C2410_GetBase_NAND();
nand->NFCONF=conf;
}
staticinlinevoidNF_Cmd(u8cmd)
{
S3C2410_NAND*constnand=S3C2410_GetBase_NAND();
nand->NFCMD=cmd;
}
staticinlinevoidNF_CmdW(u8cmd)
{
NF_Cmd(cmd);
udelay(1);
}
staticinlinevoidNF_Addr(u8addr)
{
S3C2410_NAND*constnand=S3C2410_GetBase_NAND();
nand->NFADDR=addr;
}
staticinlinevoidNF_SetCE(NFCE_STATEs)
{
S3C2410_NAND*constnand=S3C2410_GetBase_NAND();
switch(s)
{
caseNFCE_LOW:
nand->NFCONF&=~(1<<11);
break;
caseNFCE_HIGH:
nand->NFCONF|=(1<<11);
break;
}
}
staticinlinevoidNF_WaitRB(void)
{
S3C2410_NAND*constnand=S3C2410_GetBase_NAND();
while(!(nand->NFSTAT&(1<<0)));
}
staticinlinevoidNF_Write(u8data)
{
S3C2410_NAND*constnand=S3C2410_GetBase_NAND();
nand->NFDATA=data;
}
staticinlineu8NF_Read(void)
{
S3C2410_NAND*constnand=S3C2410_GetBase_NAND();
return(nand->NFDATA);
}
staticinlinevoidNF_Init_ECC(void)
{
S3C2410_NAND*constnand=S3C2410_GetBase_NAND();
nand->NFCONF|=(1<<12);
}
staticinlineu32NF_Read_ECC(void)
{
S3C2410_NAND*constnand=S3C2410_GetBase_NAND();
return(nand->NFECC);
}
#endif
/*
*NANDflashinitialization.
*/
#if(CONFIG_COMMANDS&CFG_CMD_NAND)
externulongnand_probe(ulongphysadr);
staticinlinevoidNF_Reset(void)
{
inti;
NF_SetCE(NFCE_LOW);
NF_Cmd(0xFF);/*resetcommand*/
for(i=0;i<10;i++);/*tWB=100ns.*/
NF_WaitRB();/*wait200~500us;*/
NF_SetCE(NFCE_HIGH);
}
staticinlinevoidNF_Init(void)
{
#defineTACLS0
#defineTWRPH04
#defineTWRPH12
NF_Conf((1<<15)|(0<<14)|(0<<13)|(1<<12)
|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0));
/*1111,1xxx,rxxx,rxxx*/
/*En512B4stepECCRnFCE=HtACLStWRPH0tWRPH1*/
NF_Reset();
}
voidnand_init(void)
{
S3C2410_NAND*constnand=S3C2410_GetBase_NAND();
NF_Init();
#ifdefDEBUG
printf("NANDflashprobingat0x%.8lXn",(ulong)nand);
#endif
printf("%4luMBn",nand_probe((ulong)nand)>>20);
}
#endif
(8)修改GPIO配置(board/fs2410/fs2410.c)。
/*setuptheI/Oports*/
gpio->GPACON=0x007FFFFF;
gpio->GPBCON=0x002AAAAA;
gpio->GPBUP=0x000002BF;
gpio->GPCCON=0xAAAAAAAA;
gpio->GPCUP=0x0000FFFF;
gpio->GPDCON=0xAAAAAAAA;
gpio->GPDUP=0x0000FFFF;
gpio->GPECON=0xAAAAAAAA;
gpio->GPEUP=0x000037F7;
gpio->GPFCON=0x00000000;
gpio->GPFUP=0x00000000;
gpio->GPGCON=0xFFEAFF5A;
gpio->GPGUP=0x0000F0DC;
gpio->GPHCON=0x0018AAAA;
gpio->GPHDAT=0x000001FF;
gpio->GPHUP=0x00000656
(9)提供nandflash相關(guān)宏定義(include/configs/fs2410.h),具體參考源碼。
(10)加入NandFlash設(shè)備(include/linux/mtd/nand_ids.h)
staticstructnand_flash_devnand_flash_ids[]=
{
......
{"SamsungKM29N16000",NAND_MFR_SAMSUNG,0x64,21,1,2,0x1000,0},
{"SamsungK9F1208U0M",NAND_MFR_SAMSUNG,0x76,26,0,3,0x4000,0},
{"Samsungunknown4Mb",NAND_MFR_SAMSUNG,0x6b,22,0,2,0x2000,0},
......
{NULL,}
};
(11)設(shè)置NandFlash環(huán)境(common/env_nand.c)
intnand_legacy_rw(structnand_chip*nand,intcmd,
size_tstart,size_tlen,
size_t*retlen,u_char*buf);
externstructnand_chipnand_dev_desc[CFG_MAX_NAND_DEVICE];
externintnand_legacy_erase(structnand_chip*nand,
size_tofs,size_tlen,intclean);
/*infoforNANDchips,definedindrivers/nand/nand.c*/
externnand_info_tnand_info[CFG_MAX_NAND_DEVICE];
......
#else/*!CFG_ENV_OFFSET_REDUND*/
intsaveenv(void)
{
ulongtotal;
intret=0;
puts("ErasingNand...");
if(nand_legacy_erase(nand_dev_desc+0,
CFG_ENV_OFFSET,CFG_ENV_SIZE,0))
{
return1;
}
puts("WritingtoNand...");
total=CFG_ENV_SIZE;
ret=nand_legacy_rw(nand_dev_desc+0,0x00|0x02,CFG_ENV_OFFSET,
CFG_ENV_SIZE,&total,(u_char*)env_ptr);
if(ret||total!=CFG_ENV_SIZE)
{
return1;
}
puts("donen");
returnret;
......
#else/*!CFG_ENV_OFFSET_REDUND*/
voidenv_relocate_spec(void)
{
#if!defined(ENV_IS_EMBEDDED)
ulongtotal;
intret;
total=CFG_ENV_SIZE;
ret=nand_legacy_rw(nand_dev_desc+0,0x01|0x02,CFG_ENV_OFFSET,
CFG_ENV_SIZE,&total,(u_char*)env_ptr);