您的位置:首页 > 其它

Uboot移植

2013-12-23 19:19 176 查看
一、移植环境

主机:VMWare--Fedora9
开发板:Mini2440--64MBNand,Kernel:2.6.30.4
编译器:arm-linux-gcc-4.3.2.tgz
u-boot:u-boot-2009.08.tar.bz2

二、移植步骤

本次移植的功能特点包括:

支持NandFlash读写
支持从Nor/NandFlash启动
支持CS8900或者DM9000网卡
支持Yaffs文件系统
支持USB下载(还未实现)

1.了解u-boot主要的目录结构和启动流程,如下图。




u-boot的stage1代码通常放在cpu/xxxx/start.S文件中,他用汇编语言写成;
u-boot的stage2代码通常放在lib_xxxx/board.c文件中,他用C语言写成。
各个部分的流程图如下:






2.建立自己的开发板项目并测试编译。
目前u-boot对很多CPU直接支持,可以查看board目录的一些子目录,如:board/samsung/目录下就是对三星一些ARM处理器的支持,有smdk2400、smdk2410和smdk6400,但没有2440,所以我们就在这里建立自己的开发板项目。

1)因2440和2410的资源差不多,主频和外设有点差别,所以我们就在board/samsung/下建立自己开发板的项目,取名叫my2440
#tar-jxvfu-boot-2009.08.tar.bz2//解压源码

#cdu-boot-2009.08/board/samsung/
//进入目录

#mkdirmy2440//创建my2440文件夹

2)因2440和2410的资源差不多,所以就以2410项目的代码作为模板,以后再修改

#cp-rfsmdk2410/*my2440///将2410下所有的代码复制到2440下

#cdmy2440//进入my2440目录

#mvsmdk2410.cmy2440.c//将my2440下的smdk2410.c改名为my2440.c

#cd../../..///回到u-boot根目录
#cpinclude/configs/smdk2410.hinclude/configs/my2440.h//建立2440头文件

#geditboard/samsung/my2440/Makefile//修改my2440下Makefile的编译项,如下:

COBJS:=my2440.oflash.o//因在my2440下我们将smdk2410.c改名为my2440.c

3)修改u-boot跟目录下的Makefile文件。查找到smdk2410_config的地方,在他下面按照smdk2410_config的格式建立my2440_config的编译选项,另外还要指定交叉编译器

#geditMakefile

CROSS_COMPILE?=arm-linux-//指定交叉编译器为arm-linux-gcc


smdk2410_config:unconfig//2410编译选项格式

@$(MKCONFIG)$(@:_config=)armarm920tsmdk2410samsungs3c24x0



my2440_config:unconfig//2440编译选项格式

@$(MKCONFIG)$(@:_config=)armarm920tmy2440samsungs3c24x0


*说明:arm:CPU的架构(ARCH)

arm920t:CPU的类型

my2440:对应在board目录下建立新的开发板项目的目录

samsung:新开发板项目目录的上级目录,如直接在board下建立新的开发板项目的目录,则这里就为NULL

s3c24x0:CPU型号

*注意:编译选项格式的第二行要用Tab键开始,否则编译会出错

4)测试编译新建的my2440开发板项目

#makemy2440_config//如果出现Configuringformy2440board...则表示设置正确

#make//编译后在根目录下会出现u-boot.bin文件,则u-boot移植的第一步就算完成了

到此为止,u-boot对自己的my2440开发板还没有任何用处,以上的移植只是搭建了一个my2440开发板u-boot的框架,要使其功能实现,还要根据my2440开发板的具体资源情况来对u-boot源码进行修改。

3.根据u-boot启动流程图的步骤来分析或者修改添加u-boot源码,使之适合my2440开发板(注:修改或添加的地方都用红色表示)。

1)my2440开发板u-boot的stage1入口点分析。

一般在嵌入式系统软件开发中,在所有源码文件编译完成之后,链接器要读取一个链接分配文件,在该文件中定义了程序的入口点,代码段、数据段等分配情况等。那么我们的my2440开发板u-boot的这个链接文件就是cpu/arm920t/u-boot.lds,打开该文件部分代码如下:



知道了程序的入口点是_start,那么我们就打开my2440开发板u-boot第一个要运行的程序cpu/arm920t/start.S(即u-boot的stage1部分),查找到_start的位置如下:



从这个汇编代码可以看到程序又跳转到start_code处开始执行,那么再查找到start_code处的代码如下:

/*

*theactualstartcode

*/

start_code:

/*

*setthecputoSVC32mode

*/

mrsr0,cpsr

bicr0,r0,#0x1f

orrr0,r0,#0xd3

msrcpsr,r0

blcoloured_LED_init//此处两行是对AT91RM9200DK开发板上的LED进行初始化的

blred_LED_on

由此可以看到,start_code处才是u-boot启动代码的真正开始处。以上就是u-boot的stage1入口的过程。

2)my2440开发板u-boot的stage1阶段的硬件设备初始化。

由于在u-boot启动代码处有两行是AT91RM9200DK的LED初始代码,但我们my2440上的LED资源与该开发板的不一致,所以我们要删除或屏蔽该处代码,再加上my2440的LED驱动代码(注:添加my2440LED功能只是用于表示u-boot运行的状态,给调试带来方便,可将该段代码放到任何你想调试的地方),代码如下:

/*blcoloured_LED_init
//这两行是AT91RM9200DK开发板的LED初始化,注释掉

blred_LED_on*/


#ifdefined(CONFIG_S3C2440)//区别与其他开发板

//根据mini2440原理图可知LED分别由S3C2440的PB5、6、7、8口来控制,以下是PB端口寄存器基地址(查2440的DataSheet得知)

#defineGPBCON0x56000010
#defineGPBDAT0x56000014

#defineGPBUP0x56000018

//以下对寄存器的操作参照S3C2440的DataSheet进行操作

ldrr0,=GPBUP

ldrr1,=0x7FF//即:二进制11111111111,关闭PB口上拉

strr1,[r0]

ldrr0,=GPBCON//配置PB5、6、7、8为输出口,对应PBCON寄存器的第10-17位

ldrr1,=0x154FD//即:二进制010101010011111101

strr1,[r0]

ldrr0,=GPBDAT

ldrr1,=0x1C0//即:二进制111000000,PB5设为低电平,6、7、8为高电平

strr1,[r0]
#endif

//此段代码使u-boot启动后,点亮开发板上的LED1,LED2、LED3、LED4不亮

在include/configs/my2440.h头文件中添加CONFIG_S3C2440宏

3)在u-boot中添加对S3C2440一些寄存器的支持、添加中断禁止部分和时钟设置部分。

由于2410和2440的寄存器及地址大部分是一致的,所以这里就直接在2410的基础上再加上对2440的支持即可,代码如下:



S3C2440的时钟部分除了在start.S中添加外,还要分别在board/samsung/my2440/my2440.c和cpu/arm920t/s3c24x0/speed.c中修改或添加部分代码,如下:

#geditboard/samsung/my2440/my2440.c
//设置主频和USB时钟频率参数与start.S中的一致

#defineFCLK_SPEED
2//设置默认等于2,即下面红色代码部分有效

#ifFCLK_SPEED==0/*Fout=203MHz,Fin=12MHzforAudio*/

#defineM_MDIV0xC3

#defineM_PDIV0x4

#defineM_SDIV0x1

#elifFCLK_SPEED==1/*Fout=202.8MHz*/

#defineM_MDIV0xA1

#defineM_PDIV0x3

#defineM_SDIV0x1

#elifFCLK_SPEED==2/*
Fout=405MHz*/

#defineM_MDIV0x7F
//这三个值根据S3C2440芯片手册“PLLVALUESELECTIONTABLE”部分进行设置

#defineM_PDIV0x2

#defineM_SDIV0x1#endif

#defineUSB_CLOCK
2//设置默认等于2,即下面红色代码部分有效

#ifUSB_CLOCK==0

#defineU_M_MDIV0xA1

#defineU_M_PDIV0x3

#defineU_M_SDIV0x1

#elifUSB_CLOCK==1

#defineU_M_MDIV0x48

#defineU_M_PDIV0x3

#defineU_M_SDIV0x2

#elifUSB_CLOCK==2/*
Fout=48MHz*/

#defineU_M_MDIV0x38
//这三个值根据S3C2440芯片手册“PLLVALUESELECTIONTABLE”部分进行设置

#defineU_M_PDIV0x2

#defineU_M_SDIV0x2#endif

#geditcpu/arm920t/start.S

#ifdefined(CONFIG_S3C2400)||
defined(CONFIG_S3C2410)||defined(CONFIG_S3C2440)

/*turnoffthewatchdog
*/

#ifdefined(CONFIG_S3C2400)

#definepWTCON0x15300000

#defineINTMSK0x14400008/*Interupt-Controllerbaseaddresses*/

#defineCLKDIVN0x14800014/*clockdivisorregister*/

#else//下面2410和2440的寄存器地址是一致的

#definepWTCON0x53000000

#defineINTMSK0x4A000008/*Interupt-Controllerbaseaddresses*/

#defineINTSUBMSK0x4A00001C

#defineCLKDIVN0x4C000014/*clockdivisorregister*/

#endif

ldrr0,=pWTCON

movr1,#0x0

strr1,[r0]

/*

*maskallIRQsbysettingallbitsintheINTMR
-default

*/

movr1,#0xffffffff

ldrr0,=INTMSK

strr1,[r0]

#ifdefined(CONFIG_S3C2410)

ldrr1,=0x3ff

ldrr0,=INTSUBMSK

strr1,[r0]

#endif

#ifdefined(CONFIG_S3C2440)//添加s3c2440的中断禁止部分

ldrr1,=0x7fff//根据2440芯片手册,INTSUBMSK寄存器有15位可用

ldrr0,=INTSUBMSK

strr1,[r0]

#endif

#if
defined(CONFIG_S3C2440)//添加s3c2440的时钟部分#
endif#endif/*CONFIG_S3C2400||CONFIG_S3C2410||
CONFIG_S3C2440*/

#defineMPLLCON0x4C000004//系统主频配置寄存器基地址
#defineUPLLCON0x4C000008//USB时钟频率配置寄存器基地址

ldrr0,=CLKDIVN//设置分频系数FCLK:HCLK:PCLK
=1:4:8

movr1,#5

strr1,[r0]

ldrr0,=MPLLCON//设置系统主频为405MHz
ldrr1,=0x7F021//这个值参考芯片手册“PLLVALUESELECTIONTABLE”部分
strr1,[r0]

ldrr0,=UPLLCON//设置USB时钟频率为48MHz

ldrr1,=0x38022//这个值参考芯片手册“PLLVALUESELECTIONTABLE”部分
strr1,[r0]
#else//其他开发板的时钟部分,这里就不用管了,我们现在是做2440的
/*FCLK:HCLK:PCLK=
1:2:4*/

/*defaultFCLKis120MHz!
*/
ldrr0,=CLKDIVN

movr1,#3

strr1,[r0]

#geditcpu/arm920t/s3c24x0/speed.c
//根据设置的分频系数FCLK:HCLK:PCLK=1:4:8修改获取时钟频率的函数

staticulongget_PLLCLK(intpllreg)

{

S3C24X0_CLOCK_POWER*
constclk_power=S3C24X0_GetBase_CLOCK_POWER();

ulongr,m,p,s;

if(pllreg==MPLL)

r=clk_power->MPLLCON;

elseif(pllreg
==UPLL)

r=clk_power->UPLLCON;

else

hang();

m=((r&0xFF000)
>>12)+8;

p=((r&0x003F0)
>>4)+2;

s=r&0x3;

#ifdefined(CONFIG_S3C2440)

if(pllreg==
MPLL)

{
//参考S3C2440芯片手册上的公式:PLL=(2*m*Fin)/(p*2s)

return((CONFIG_SYS_CLK_FREQ*
m*2)/
(p<<
s));

}#endif

return((CONFIG_SYS_CLK_FREQ*m)
/(p
<<s));

}

/*returnHCLKfrequency*/

ulongget_HCLK(void)

{

S3C24X0_CLOCK_POWER*
constclk_power=S3C24X0_GetBase_CLOCK_POWER();

#ifdefined(CONFIG_S3C2440)

return(get_FCLK()/4);#endif

return((clk_power->CLKDIVN&0x2)
?get_FCLK()/2:get_FCLK());

}

好了!修改完毕后我们再重新编译u-boot,然后再下载到RAM中运行测试。结果终端有输出信息并且出现类似Shell的命令行,这说明这一部分移植完成。示意图如下:



#geditinclude/configs/my2440.h

#defineCONFIG_ARM920T1/*
ThisisanARM920TCore*/

#defineCONFIG_S3C24101/*inaSAMSUNGS3C2410SoC*/

#defineCONFIG_SMDK24101/*onaSAMSUNGSMDK2410Board*/

#defineCONFIG_S3C24401/*inaSAMSUNGS3C2440SoC*/

现在编译u-boot,在根目录下会生成一个u-boot.bin文件。然后我们利用mini2440原有的supervivi把u-boot.bin下载到RAM中运行测试(注意:我们使用supervivi进行下载时已经对CPU、RAM进行了初始化,所以我们在u-boot中要屏蔽掉对CPU、RAM的初始化),如下:


/*#ifndefCONFIG_SKIP_LOWLEVEL_INIT//在start.S文件中屏蔽u-boot对CPU、RAM的初始化

blcpu_init_crit#endif*/

#makemy2440_config

#make

下载运行后可以看到开发板上的LED灯第一了亮了,其他三个熄灭,测试结果符合上面的要求。终端运行结果如下:



#geditcpu/arm920t/start.S

.globl_start

_start:bstart_code//将程序的执行跳转到start_code处

#geditcpu/arm920t/u-boot.lds

OUTPUT_FORMAT("elf32-littlearm","elf32-littlearm","elf32-littlearm")

OUTPUT_ARCH(arm)//定义生成文件的目标平台是arm

ENTRY(_start)//定义程序的入口点是_start

SECTIONS

{

//其他一些代码段、数据段等分配

.=0x00000000;

.=ALIGN(4);

.text:

{

cpu/arm920t/start.o(.text)

*(.text)

}

..................

..................

}




(2)

一、移植环境

主机:VMWare--Fedora9
开发板:Mini2440--64MBNand,Kernel:2.6.30.4
编译器:arm-linux-gcc-4.3.2.tgz
u-boot:u-boot-2009.08.tar.bz2

二、移植步骤

4)准备进入u-boot的第二阶段(在u-boot中添加对我们开发板上NorFlash的支持)

通常,在嵌入式bootloader中,有两种方式来引导启动内核:从NorFlash启动和从NandFlash启动。u-boot中默认是从NorFlash启动,再从上一节这个运行结果图中看,还发现几个问题:第一,我开发板的NorFlash是2M的,而这里显示的是512kB;第二,出现Warning-badCRC,usingdefaultenvironment的警告信息。不是u-boot默认是从NorFlash启动的吗?为什么会有这些错误信息呢?这是因为我们还没有添加对我们自己的Nor
Flash的支持,u-boot默认的是其他型号的NorFlash,而我们的NorFlash的型号是SST39VF1601。另外怎样将命令行提示符前面的SMDK2410变成我自己定义的呢?

下面我们一一来解决这些问题,让u-boot完全对我们NorFlash的支持。首先我们修改头文件代码如下:

#geditinclude/configs/my2440.h
//修改命令行前的名字和NorFlash参数部分的定义

#defineCONFIG_SYS_PROMPT"[MY2440]#"//将命令行前的名字改成[MY2440]


/*-----------------------------------------------------------------------

*FLASHandenvironmentorganization

*/

#if0//注释掉下面两个类型的Nor
Flash设置,因为不是我们所使用的型号

#defineCONFIG_AMD_LV4001/*uncommentthisifyouhaveaLV400flash*/

#defineCONFIG_AMD_LV8001/*uncommentthisifyouhaveaLV800flash*/#endif

#defineCONFIG_SYS_MAX_FLASH_BANKS1/*
maxnumberofmemorybanks*/

#ifdefCONFIG_AMD_LV800

#definePHYS_FLASH_SIZE0x00100000/*1MB*/

#defineCONFIG_SYS_MAX_FLASH_SECT
(19)/*maxnumberofsectorsononechip*/

#defineCONFIG_ENV_ADDR(CONFIG_SYS_FLASH_BASE
+0x0F0000)
/*addrofenvironment*/#endif

#ifdefCONFIG_AMD_LV400

#definePHYS_FLASH_SIZE0x00080000/*512KB*/

#defineCONFIG_SYS_MAX_FLASH_SECT(11)/*max
numberofsectorsononechip*/

#defineCONFIG_ENV_ADDR(CONFIG_SYS_FLASH_BASE+0x070000)/*
addrofenvironment*/#endif

#defineCONFIG_SST_39VF16011//添加mini2440开发板Nor
Flash设置

#definePHYS_FLASH_SIZE0x200000//我们开发板的Nor
Flash是2M

#defineCONFIG_SYS_MAX_FLASH_SECT(512)//根据SST39VF1601的芯片手册描述,对其进行操作有两种方式:块方式和扇区方式。现采用扇区方式(sector),1
sector=2Kword=4Kbyte,所以2M的NorFlash共有512个sector

#defineCONFIG_ENV_ADDR(CONFIG_SYS_FLASH_BASE+
0x040000)//暂设置环境变量的首地址为0x040000(即:256Kb)

然后添加对我们mini2440开发板上2M的NorFlash(型号为SST39VF1601)的支持。在u-boot中对NorFlash的操作分别有初始化、擦除和写入,所以我们主要修改与硬件密切相关的三个函数flash_init、flash_erase、write_hword,修改代码如下:

#geditboard/samsung/my2440/flash.c

//修改定义部分如下:

//#defineMAIN_SECT_SIZE0x10000

#defineMAIN_SECT_SIZE0x1000//定义为4k,刚好是一个扇区的大小

//#defineMEM_FLASH_ADDR1(*(volatileu16*)(CONFIG_SYS_FLASH_BASE+(0x00000555<<1)))

//#defineMEM_FLASH_ADDR2(*(volatileu16*)(CONFIG_SYS_FLASH_BASE+(0x000002AA<<1)))

#defineMEM_FLASH_ADDR1(*(volatile
u16*)(CONFIG_SYS_FLASH_BASE+
(0x00005555<<
1)))//这两个参数看SST39VF1601手册

#defineMEM_FLASH_ADDR2(*(volatileu16
*)(CONFIG_SYS_FLASH_BASE+(0x00002AAA<<
1)))


//修改flash_init函数如下:

#elifdefined(CONFIG_AMD_LV800)

(AMD_MANUFACT
&FLASH_VENDMASK)
|

(AMD_ID_LV800B
&FLASH_TYPEMASK);

#elifdefined(CONFIG_SST_39VF1601)//在CONFIG_AMD_LV800后面添加CONFIG_SST_39VF1601

(SST_MANUFACT&
FLASH_VENDMASK)|

(SST_ID_xF1601&
FLASH_TYPEMASK);



for(j
=0;j<flash_info[i].sector_count;j++){

//if(j<=3){

///*1stoneis16KB*/

//if(j==0){

//flash_info[i].start[j]=flashbase+0;

//}

///*2ndand3rdareboth8KB*/

//if((j==1)||(j==2)){

//flash_info[i].start[j]=flashbase+0x4000+(j-1)
*0x2000;

//}

///*4th32KB*/

//if(j==3){

//flash_info[i].start[j]=flashbase+0x8000;

//}

//}else{

//flash_info[i].start[j]=flashbase+(j-3)*MAIN_SECT_SIZE;

//}

flash_info[i].start[j]=
flashbase+j
*MAIN_SECT_SIZE;

}


//修改flash_print_info函数如下:

case(AMD_MANUFACT
&FLASH_VENDMASK):

printf
("AMD:");

break;

case(SST_MANUFACT&
FLASH_VENDMASK)://添加SST39VF1601的

printf
("SST:");

break;

case(AMD_ID_LV800B&FLASH_TYPEMASK):

printf
("1xAmd29LV800BB(8Mbit)\n");

break;

case(SST_ID_xF1601&
FLASH_TYPEMASK)://添加SST39VF1601的

printf("1xSST39VF1610
(16Mbit)\n");

break;


//修改flash_erase函数如下:

//if((info->flash_id&FLASH_VENDMASK)!=

//(AMD_MANUFACT&FLASH_VENDMASK)){

//returnERR_UNKNOWN_FLASH_VENDOR;

//}

if((info->flash_id&
FLASH_VENDMASK)!=

(SST_MANUFACT&FLASH_VENDMASK)){

returnERR_UNKNOWN_FLASH_VENDOR;

}

///*waituntilflashisready*/

//chip=0;

//do{

//result=*addr;

///*checktimeout*/

//if(get_timer_masked()>

//CONFIG_SYS_FLASH_ERASE_TOUT){

//MEM_FLASH_ADDR1=CMD_READ_ARRAY;

//chip=TMO;

//break;

//}

//if(!chip

//&&(result&0xFFFF)&BIT_ERASE_DONE)

//chip=READY;

//if(!chip

//&&(result&0xFFFF)&BIT_PROGRAM_ERROR)

//chip=ERR;

//}while(!chip);

//MEM_FLASH_ADDR1=CMD_READ_ARRAY;

//if(chip==ERR){

//rc=ERR_PROG_ERROR;

//gotooutahere;

//}

//if(chip==TMO){

//rc=ERR_TIMOUT;

//gotooutahere;

//}

while(1)

{

if((*addr&
0x40)!=(*addr&
0x40))

continue;

if(*addr&
0x80)

{

rc=ERR_OK;

break;

}

}


//修改write_hword函数如下:

MEM_FLASH_ADDR1=CMD_UNLOCK1;

MEM_FLASH_ADDR2=CMD_UNLOCK2;

//MEM_FLASH_ADDR1=CMD_UNLOCK_BYPASS;

MEM_FLASH_ADDR1=CMD_PROGRAM;

//*addr=CMD_PROGRAM;

*addr=data;

///*waituntilflashisready*/

//chip=0;

//do{

//result=*addr;

///*checktimeout*/

//if(get_timer_masked()>CONFIG_SYS_FLASH_ERASE_TOUT){

//chip=ERR|TMO;

//break;

//}

//if(!chip&&((result&0x80)==(data&0x80)))

//chip=READY;

//if(!chip&&((result&0xFFFF)&BIT_PROGRAM_ERROR)){

//result=*addr;

//if((result&0x80)==(data&0x80))

//chip=READY;

//else

//chip=ERR;

//}

//}while(!chip);

//*addr=CMD_READ_ARRAY;

//if(chip==ERR||*addr!=data)

//rc=ERR_PROG_ERROR;

while(1)

{

if((*addr&
0x40)!=(*addr&
0x40))

continue;

if
((*addr&
0x80)==(data&
0x80))

{

rc=ERR_OK;

break;

}

}

修改完后重新编译u-boot,下载到RAM中运行结果如下图:



从运行结果图看,NorFlash的大小可以正确检测到了,命令行前面的名字也由原来的SMDK2410改成我自己定义的[MY2440]了,但是还会出现badCRC的警告信息,其实这并不是什么问题,只是还没有将环境变量设置到NorFlash中,我们执行一下u-boot的:saveenv命令就可以了。如下图:



再重新下载u-boot.bin文件到RAM中运行,可以观察到不会出现警告信息了,这时候u-boot已经对我们开发板上的NorFlash完全支持了。如下:



(3)

一、移植环境

主机:VMWare--Fedora9
开发板:Mini2440--64MBNand,Kernel:2.6.30.4
编译器:arm-linux-gcc-4.3.2.tgz
u-boot:u-boot-2009.08.tar.bz2

二、移植步骤

5)准备进入u-boot的第二阶段(在u-boot中添加对我们开发板上NandFlash的支持)[b]。[/b]目前u-boot中还没有对2440上NandFlash的支持,也就是说要想u-boot从NandFlash上启动得自己去实现了。

首先,在include/configs/my2440.h头文件中定义Nand要用到的宏和寄存器,如下:

#geditinclude/configs/my2440.h//在文件末尾加入以下NandFlash相关定义

/*

*Nandflashregisterandenvionmentvariables

*/

#defineCONFIG_S3C2440_NAND_BOOT1

#defineNAND_CTL_BASE0x4E000000//NandFlash配置寄存器基地址,查2440手册可得知

#defineSTACK_BASE0x33F00000//定义堆栈的地址

#defineSTACK_SIZE0x8000//堆栈的长度大小

#defineoNFCONF0x00//相对Nand配置寄存器基地址的偏移量,还是配置寄存器的基地址

#defineoNFCONT0x04//相对Nand配置寄存器基地址的偏移量,可得到控制寄存器的基地址(0x4E000004)

#defineoNFADDR0x0c//相对Nand配置寄存器基地址的偏移量,可得到地址寄存器的基地址(0x4E00000c)

#defineoNFDATA0x10//相对Nand配置寄存器基地址的偏移量,可得到数据寄存器的基地址(0x4E000010)

#defineoNFCMD0x08//相对Nand配置寄存器基地址的偏移量,可得到指令寄存器的基地址(0x4E000008)

#defineoNFSTAT0x20//相对Nand配置寄存器基地址的偏移量,可得到状态寄存器的基地址(0x4E000020)

#defineoNFECC0x2c//相对Nand配置寄存器基地址的偏移量,可得到ECC寄存器的基地址(0x4E00002c)

其次,修改cpu/arm920t/start.S这个文件,使u-boot从NandFlash启动,在上一节中提过,u-boot默认是从NorFlash启动的。修改部分如下:

#geditcpu/arm920t/start.S

//注意:在上一篇NorFlash启动中,我们为了把u-boot用supervivi下载到内存中运行而屏蔽掉这段有关CPU初始化的代码。而现在我们要把u-boot下载到NandFlash中,从NandFlash启动,所以现在要恢复这段代码。

#ifndefCONFIG_SKIP_LOWLEVEL_INIT

blcpu_init_crit#endif


#if0//屏蔽掉u-boot中的从NorFlash启动部分

#ifndefCONFIG_SKIP_RELOCATE_UBOOT

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#endif/*CONFIG_SKIP_RELOCATE_UBOOT*/#endif


//下面添加2440中u-boot从NandFlash启动

#ifdefCONFIG_S3C2440_NAND_BOOT

movr1,#NAND_CTL_BASE//复位NandFlash

ldrr2,=((7<<12)|(7<<8)|(7<<4)|(0<<0))

strr2,[r1,#oNFCONF]//设置配置寄存器的初始值,参考s3c2440手册

ldrr2,[r1,#oNFCONF]

ldrr2,=((1<<4)|(0<<1)|(1<<0))

strr2,[r1,#oNFCONT]//设置控制寄存器

ldrr2,[r1,#oNFCONT]

ldrr2,=(0x6)//RnBClear

strr2,[r1,#oNFSTAT]

ldrr2,[r1,#oNFSTAT]

movr2,#0xff//复位command

strbr2,[r1,#oNFCMD]

movr3,#0//等待


nand1:

addr3,r3,#0x1

cmpr3,#0xa

bltnand1

nand2:

ldrr2,[r1,#oNFSTAT]//等待就绪

tstr2,#0x4

beqnand2

ldrr2,[r1,#oNFCONT]

orrr2,r2,#0x2//取消片选

strr2,[r1,#oNFCONT]

//getreadtocallCfunctions(fornand_read())

ldrsp,DW_STACK_START//为C代码准备堆栈,DW_STACK_START定义在下面

movfp,#0

//copyU-BoottoRAM

ldrr0,=TEXT_BASE//传递给C代码的第一个参数:u-boot在RAM中的起始地址

movr1,#0x0//传递给C代码的第二个参数:NandFlash的起始地址

movr2,#0x30000//传递给C代码的第三个参数:u-boot的长度大小(128k)

blnand_read_ll//此处调用C代码中读Nand的函数,现在还没有要自己编写实现

tstr0,#0x0

beqok_nand_read

bad_nand_read:

loop2:bloop2//infiniteloop

ok_nand_read:

//检查搬移后的数据,如果前4k完全相同,表示搬移成功

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_S3C2440_NAND_BOOT


_start_armboot:.wordstart_armboot//在这一句的下面加上DW_STACK_START的定义

.align2

DW_STACK_START:.wordSTACK_BASE+STACK_SIZE-4

再次,在board/samsung/my2440/目录下新建一个nand_read.c文件,在该文件中来实现上面汇编中要调用的nand_read_ll函数,代码如下:

然后,在board/samsung/my2440/Makefile中添加nand_read.c的编译选项,使他编译到u-boot中,如下:

COBJS:=my2440.oflash.onand_read.o

还有一个重要的地方要修改,在cpu/arm920t/u-boot.lds中,这个u-boot启动连接脚本文件决定了u-boot运行的入口地址,以及各个段的存储位置,这也是链接定位的作用。添加下面两行代码的主要目的是防止编译器把我们自己添加的用于nandboot的子函数放到4K之后,否则是无法启动的。如下:

.text:

{

cpu/arm920t/start.o(.text)

board/samsung/my2440/lowlevel_init.o(.text)

board/samsung/my2440/nand_read.o(.text)

*(.text)

}

最后编译u-boot,生成u-boot.bin文件。然后先将mini2440开发板调到Nor启动档,利用supervivi的a命令将u-boot.bin下载到开发板的NandFlash中,再把开发板调到Nand启动档,打开电源就从NandFlash启动了,启动结果图如下:



从上面的运行图看,显然现在的Nand还不能做任何事情,而且也没有显示有关Nand的任何信息,所以只能说明上面的这些步骤只是完成了Nand移植的Stage1部分。下面我们来添加我们开发板上的NandFlash(K9F1208U0C)的Stage2部分的有关操作支持。

6)现在进入u-boot的第二阶段(添加NandFlash(K9F1208U0C)的有关操作支持)。

在上一节中我们说过,通常在嵌入式bootloader中,有两种方式来引导启动内核:从NorFlash启动和从NandFlash启动,但不管是从Nor启动或者从Nand启动,进入第二阶段以后,两者的执行流程是相同的。

当u-boot的start.S运行到“_start_armboot:.wordstart_armboot”时,就会调用lib_arm/board.c中的start_armboot函数,至此u-boot正式进入第二阶段。此时注意:以前较早的u-boot版本进入第二阶段后,对NandFlash的支持有新旧两套代码,新代码在drivers/nand目录下,旧代码在drivers/nand_legacy目录下,CFG_NAND_LEGACY宏决定了使用哪套代码,如果定义了该宏就使用旧代码,否则使用新代码。但是现在的u-boot-2009.08版本对Nand的初始化、读写实现是基于最近的Linux内核的MTD架构,删除了以前传统的执行方法,使移植没有以前那样复杂了,实现Nand的操作和基本命令都直接在drivers/mtd/nand目录下(在doc/README.nand中讲得很清楚)。下面我们结合代码来分析一下u-boot在第二阶段的执行流程:



因为2440和2410对nand控制器的操作有很大的不同,所以s3c2410_nand.c下对nand操作的函数就是我们做移植需要实现的部分了,他与具体的NandFlash硬件密切相关。为了区别与2410,这里我们就重新建立一个s3c2440_nand.c文件,在这里面来实现对nand的操作,代码如下:


最后,重新编译u-boot并使用supervivi的a命令下载到NandFlash中,把开发板调到Nand档从Nand启动,启动结果图如下:



从上图可以看出,现在u-boot已经对我们开发板上64M的NandFlash完全支持了。Nand相关的基本命令也都可以正常使用了。

补充内容:

从以上的启动信息看,有一个警告信息“***Warning-badCRCorNAND,usingdefaultenvironment”,我们知道,这是因为我们还没有将u-boot的环境变量保存nand中的缘故,那现在我们就用u-boot的saveenv命令来保存环境变量,如下:



从上图可以看到保存环境变量并没有成功,而且从信息看他将把环境变量保存到Flash中,显然这不正确,我们是要保存到Nand中。原来,u-boot在默认的情况下把环境变量都是保存到NorFlash中的,所以我们要修改代码,让他保存到Nand中,如下:

#geditinclude/configs/my2440.h

//注释掉环境变量保存到Flash的宏(注意:如果你要使用上一篇中的从Nor启动的saveenv命令,则要恢复这些Flash宏定义)

//#defineCONFIG_ENV_IS_IN_FLASH1

//#defineCONFIG_ENV_SIZE0x10000/*TotalSizeofEnvironmentSector*/


//添加环境变量保存到Nand的宏(注意:如果你要使用上一篇中的从Nor启动的saveenv命令,则不要这些Nand宏定义)

#defineCONFIG_ENV_IS_IN_NAND1

#defineCONFIG_ENV_OFFSET0x30000//将环境变量保存到nand中的0x30000位置

#defineCONFIG_ENV_SIZE0x10000/*TotalSizeofEnvironmentSector*/

重新编译u-boot,下载到nand中,启动开发板再来保存环境变量,如下:



可以看到,现在成功保存到Nand中了,为了验证,我们重新启动开发板,那条警告信息现在没有了,如下:




#geditdrivers/mtd/nand/s3c2440_nand.c//新建s3c2440_nand.c文件

#include<common.h>
#if0

#defineDEBUGNprintf

#else

#defineDEBUGN(x,args...){}#endif
#include<nand.h>

#include<s3c2410.h>

#include<asm/io.h>

#define__REGb(x)(*(volatileunsignedchar*)(x))

#define__REGi(x)(*(volatileunsignedint*)(x))

#defineNF_BASE0x4e000000//Nand配置寄存器基地址

#defineNFCONF__REGi(NF_BASE+0x0)//偏移后还是得到配置寄存器基地址

#defineNFCONT__REGi(NF_BASE+0x4)//偏移后得到Nand控制寄存器基地址

#defineNFCMD__REGb(NF_BASE+0x8)//偏移后得到Nand指令寄存器基地址

#defineNFADDR__REGb(NF_BASE+0xc)//偏移后得到Nand地址寄存器基地址

#defineNFDATA__REGb(NF_BASE+0x10)//偏移后得到Nand数据寄存器基地址

#defineNFMECCD0__REGi(NF_BASE+0x14)//偏移后得到Nand主数据区域ECC0寄存器基地址

#defineNFMECCD1__REGi(NF_BASE+0x18)//偏移后得到Nand主数据区域ECC1寄存器基地址

#defineNFSECCD__REGi(NF_BASE+0x1C)//偏移后得到Nand空闲区域ECC寄存器基地址

#defineNFSTAT__REGb(NF_BASE+0x20)//偏移后得到Nand状态寄存器基地址

#defineNFSTAT0__REGi(NF_BASE+0x24)//偏移后得到NandECC0状态寄存器基地址

#defineNFSTAT1__REGi(NF_BASE+0x28)//偏移后得到NandECC1状态寄存器基地址

#defineNFMECC0__REGi(NF_BASE+0x2C)//偏移后得到Nand主数据区域ECC0状态寄存器基地址

#defineNFMECC1__REGi(NF_BASE+0x30)//偏移后得到Nand主数据区域ECC1状态寄存器基地址

#defineNFSECC__REGi(NF_BASE+0x34)//偏移后得到Nand空闲区域ECC状态寄存器基地址

#defineNFSBLK__REGi(NF_BASE+0x38)//偏移后得到Nand块开始地址

#defineNFEBLK__REGi(NF_BASE+0x3c)//偏移后得到Nand块结束地址

#defineS3C2440_NFCONT_nCE(1<<1)

#defineS3C2440_ADDR_NALE0x0c

#defineS3C2440_ADDR_NCLE0x08

ulongIO_ADDR_W=NF_BASE;

staticvoids3c2440_hwcontrol(structmtd_info*mtd,intcmd,unsignedintctrl)

{

structnand_chip*chip=mtd->priv;
DEBUGN("hwcontrol():0x%02x0x%02x\n",cmd,ctrl);
if(ctrl&NAND_CTRL_CHANGE){

IO_ADDR_W=NF_BASE;
if(!(ctrl&NAND_CLE))//要写的是地址

IO_ADDR_W|=S3C2440_ADDR_NALE;

if(!(ctrl&NAND_ALE))//要写的是命令

IO_ADDR_W|=S3C2440_ADDR_NCLE;
if(ctrl&NAND_NCE)

NFCONT&=~S3C2440_NFCONT_nCE;//使能nandflash

else

NFCONT|=S3C2440_NFCONT_nCE;//禁止nandflash

}
if(cmd!=NAND_CMD_NONE)

writeb(cmd,(void*)IO_ADDR_W);

}

staticints3c2440_dev_ready(structmtd_info*mtd)

{

DEBUGN("dev_ready\n");

return(NFSTAT&0x01);

}

intboard_nand_init(structnand_chip*nand)

{

u_int32_tcfg;

u_int8_ttacls,twrph0,twrph1;

S3C24X0_CLOCK_POWER*constclk_power=S3C24X0_GetBase_CLOCK_POWER();

DEBUGN("board_nand_init()\n");

clk_power->CLKCON|=(1<<4);

twrph0=4;twrph1=2;tacls=0;

cfg=(tacls<<12)|(twrph0<<8)|(twrph1<<4);

NFCONF=cfg;

cfg=(1<<6)|(1<<4)|(0<<1)|(1<<0);

NFCONT=cfg;

/*initializenand_chipdatastructure*/

nand->IO_ADDR_R=nand->IO_ADDR_W=(void*)0x4e000010;

/*read_bufandwrite_bufaredefault*/

/*read_byteandwrite_bytearedefault*/
/*hwcontrolalwaysmustbeimplemented*/

nand->cmd_ctrl=s3c2440_hwcontrol;

nand->dev_ready=s3c2440_dev_ready;

return0;

}
其次,在开发板配置文件include/configs/my2440.h文件中定义支持Nand操作的相关宏,如下:
#geditinclude/configs/my2440.h

/*Commandlineconfiguration.*/

#defineCONFIG_CMD_NAND

#defineCONFIG_CMDLINE_EDITING
#ifdefCONFIG_CMDLINE_EDITING

#undefCONFIG_AUTO_COMPLETE

#else

#defineCONFIG_AUTO_COMPLETE#endif

/*NANDflashsettings*/

#ifdefined(CONFIG_CMD_NAND)

#defineCONFIG_SYS_NAND_BASE0x4E000000//Nand配置寄存器基地址

#defineCONFIG_SYS_MAX_NAND_DEVICE1

#defineCONFIG_MTD_NAND_VERIFY_WRITE1

//#defineNAND_SAMSUNG_LP_OPTIONS1//注意:我们这里是64M的NandFlash,所以不用,如果是128M的大块NandFlash,则需加上#endif
然后,在drivers/mtd/nand/Makefile文件中添加s3c2440_nand.c的编译项,如下:
#geditdrivers/mtd/nand/Makefile

COBJS-y+=s3c2440_nand.o

COBJS-$(CONFIG_NAND_S3C2440)+=s3c2440_nand.o

1.lib_arm/board.c文件中的start_armboot函数调用了drivers/mtd/nand/nand.c文件中的nand_init函数,如下:

#ifdefined(CONFIG_CMD_NAND)//可以看到CONFIG_CMD_NAND宏决定了Nand的初始化

puts("NAND:");

nand_init();

#endif

2.nand_init调用了同文件下的nand_init_chip函数;

3.nand_init_chip函数调用drivers/mtd/nand/s3c2410_nand.c文件下的board_nand_init函数,然后再调用drivers/mtd/nand/nand_base.c函数中的nand_scan函数;

4.nand_scan函数调用了同文件下的nand_scan_ident函数等。

#geditboard/samsung/my2440/nand_read.c//新建一个nand_read.c文件,记得保存

#include<config.h>



#defineNF_BASE0x4E000000
//NandFlash配置寄存器基地址

#define__REGb(x)(*(volatile
unsignedchar*)(x))

#define__REGi(x)(*(volatile
unsignedint*)(x))

#defineNFCONF__REGi(NF_BASE+0x0
)//通过偏移量还是得到配置寄存器基地址

#defineNFCONT__REGi(NF_BASE+0x4)
//通过偏移量得到控制寄存器基地址

#defineNFCMD__REGb(NF_BASE+0x8)
//通过偏移量得到指令寄存器基地址

#defineNFADDR__REGb(NF_BASE+0xC)
//通过偏移量得到地址寄存器基地址

#defineNFDATA__REGb(NF_BASE+0x10)//通过偏移量得到数据寄存器基地址

#defineNFSTAT__REGb(NF_BASE+0x20)//通过偏移量得到状态寄存器基地址

#defineNAND_CHIP_ENABLE(NFCONT&=~(1<<1))//Nand片选使能

#defineNAND_CHIP_DISABLE(NFCONT|=
(1<<1))//取消Nand片选

#defineNAND_CLEAR_RB(NFSTAT|=(1<<2))

#defineNAND_DETECT_RB{while(!(NFSTAT&(1<<2)));}

#defineNAND_SECTOR_SIZE512

#defineNAND_BLOCK_MASK(NAND_SECTOR_SIZE-1)

/*lowlevelnandreadfunction*/

intnand_read_ll(unsignedchar*buf,
unsignedlongstart_addr,intsize)

{

inti,j;

if
((start_addr&NAND_BLOCK_MASK)||(size
&NAND_BLOCK_MASK))

{

return-1;//地址或长度不对齐

}

NAND_CHIP_ENABLE;
//选中Nand片选

for(i=start_addr;
i<
(start_addr+size);)

{

//发出READ0指令
NAND_CLEAR_RB;

NFCMD=0;

//对Nand进行寻址

NFADDR=i&0xFF;
NFADDR=(i>>
9)&0xFF;

NFADDR=(i>>
17)&0xFF;

NFADDR=(i>>
25)&0xFF;

NAND_DETECT_RB;

for(j=0;
j<NAND_SECTOR_SIZE;j++,
i++)

{

*buf=(NFDATA
&0xFF);

buf++;

}

}

NAND_CHIP_DISABLE;
//取消片选信号

return0;

}
注意:上面这段代码中对Nand进行寻址的部分,这跟具体的NandFlash的寻址方式有关。根据我们开发板上的NandFlash(K9F1208U0C)数据手册得知,片内寻址是采用26位地址形式。从第0位开始分四次通过I/O0-I/O7进行传送,并进行片内寻址。具体含义和结构图如下(相关概念参考Nand数据手册):





0-7位:字节在上半部、下半部及OOB内的偏移地址

8位:值为0代表对一页内前256个字节进行寻址,值为1代表对一页内后256个字节进行寻址

9-13位:对页进行寻址

14-25位:对块进行寻址


(4)

一、移植环境

主机:VMWare--Fedora9
开发板:Mini2440--64MBNand,Kernel:2.6.30.4
编译器:arm-linux-gcc-4.3.2.tgz
u-boot:u-boot-2009.08.tar.bz2

二、移植步骤

在这一篇中,我们首先让开发板对CS8900或者DM9000X网卡的支持,然后再分析实现u-boot怎样来引导Linux内核启动。因为测试u-boot引导内核我们要用到网络下载功能。

7)u-boot对CS8900或者DM9000X网卡的支持。

u-boot-2009.08版本已经对CS8900和DM9000X网卡有比较完善的代码支持(代码在drivers/net/目录下),而且在S3C24XX系列中默认对CS8900网卡进行配置使用。只是在个别地方要根据开发板的具体网卡片选进行设置,就可以对S3C24XX系列中CS8900网卡的支持使用。代码如下:



现在修改对我们开发板上DM9000X网卡的支持。

首先,我们看看drivers/net/目录下有关DM9000的代码,发现dm9000x.h中对CONFIG_DRIVER_DM9000宏的依赖,dm9000x.c中对CONFIG_DM9000_BASE宏、DM9000_IO宏、DM9000_DATA等宏的依赖,所以我们修改代码如下:
#geditinclude/configs/my2440.h


/**Hardwaredrivers*/屏蔽掉u-boot默认对CS8900网卡的支持

//#defineCONFIG_DRIVER_CS89001/*wehaveaCS8900on-board*/

//#defineCS8900_BASE0x19000300

//#defineCS8900_BUS161/*theLinuxdriverdoesaccessesasshorts*/



//添加u-boot对DM9000X网卡的支持

#defineCONFIG_DRIVER_DM90001

#defineCONFIG_NET_MULTI1

#defineCONFIG_DM9000_NO_SROM1

#defineCONFIG_DM9000_BASE0x20000300//网卡片选地址

#defineDM9000_IOCONFIG_DM9000_BASE

#defineDM9000_DATA(CONFIG_DM9000_BASE+4)
//网卡数据地址

//#defineCONFIG_DM9000_USE_16BIT1

注意:

u-boot-2009.08可以自动检测DM9000网卡的位数,根据开发板原理图可知网卡的数据位为16位,并且网卡位于CPU的BANK4上,所以只需在board/samsung/my2440/lowlevel_init.S中设置#defineB4_BWSCON(DW16)即可,不需要此处的#defineCONFIG_DM9000_USE_16BIT1



//给u-boot加上ping命令,用来测试网络通不通

#defineCONFIG_CMD_PING



//恢复被注释掉的网卡MAC地址和修改你合适的开发板IP地址

#defineCONFIG_ETHADDR08:00:3e:26:0a:5b//开发板MAC地址

#defineCONFIG_NETMASK255.255.255.0

#defineCONFIG_IPADDR192.168.1.105//开发板IP地址

#defineCONFIG_SERVERIP192.168.1.103//Linux主机IP地址

添加板载DM9000网卡初始化代码,如下:

#geditboard/samsung/my2440/my2440.c


#include<net.h>

#include<netdev.h>



#ifdefCONFIG_DRIVER_DM9000

intboard_eth_init(bd_t*bis)

{

returndm9000_initialize(bis);

}#endif

修改MD9000网卡驱动代码,如下:

#geditdrivers/net/dm9000x.c


#if0//屏蔽掉dm9000_init函数中的这一部分,不然使用网卡的时候会报“could
notestablishlink”的错误

i=0;

while(!(phy_read(1)&
0x20)){/*autonegationcompletebit*/

udelay(1000);

i++;

if(i==10000){

printf("couldnotestablishlink");

return0;

}

}#endif


然后重新编译u-boot,下载到Nand中从Nand启动,查看启动信息和环境变量并使用ping命令测试网卡,操作如下:





可以看到,启动信息里面显示了Net:dm9000,printenv查看的环境变量也和include/configs/my2440.h中设置的一致。但是现在有个问题就是ping不能通过。

经过一段时间在网上搜索,原来有很多人都碰到了这种情况。出现问题的地方可能是DM9000网卡驱动中关闭网卡的地方,如是就试着修改代码如下:
结果,只是第一次ping不通,以后都是可以ping通的(据网友们说这是正常的),如下图:




好了,现在只剩下一个问题了,就是使用tftp进行下载。关于tftp服务器在Linux中的安装和配置,这里我就不讲了,在网上搜一下很多的。然而,在tftp下载时又遇到了问题,总是出现传送不完整又重新传送的现象,不断的循环,如下图:




困惑好久的tftp问题现在终于搞定啦,心情真是爽啊!!首先分析上面图中的现象,在下载过程中断断续续就说明是可以下载的,只是由于某种原因使网络出现超时从而重新下载,那我想出现这种情况的可能性有两种:1、u-boot中对网络的延时设置;2、就是我的物理网络结构。首先针对第一种,我修改了net/net.c中对网络延时的设置,结果还是不行。接着就试试第二种情况,因为之前我的网络是通过路由器来管理的,主机和开发板也是通过路由器来连接的,所以现在我就改用一条交叉网线直接把主机和开发板连接起来,一试,果然可以啦,哈哈哈哈....。至此,网络部分的移植总算完成了。





8)实现u-boot引导Linux内核启动。

在前面几节中,我们讲了u-boot对NorFlash和NandFlash的启动支持,那现在我们就再来探讨一下u-boot怎样来引导Linux内核的启动。

①、机器码的确定
通常,在u-boot和kernel中都会有一个机器码(即:MACH_TYPE),只有这两个机器码一致时才能引导内核,否则就会出现如下mach的错误信息:




首先,确定u-boot中的MACH_TYPE。在u-boot的include/asm-arm/mach-types.h文件中针对不同的CPU定义了非常多的MACH_TYPE,可以找到下面这个定义:

②、准备能被u-boot直接引导的内核uImage
通常,kernel的启动需要u-boot提供一些参数信息,比如ramdisk在RAM中的地址。经过编译后的u-boot在根目录下的tools目录中,会有个叫做mkimage的工具,他可以给zImage添加一个header,也就是说使得通常我们编译的内核zImage添加一个数据头信息部分,我们把添加头后的image通常叫uImage,uImage是可以被u-boot直接引导的内核镜像。

mkimage工具的使用介绍如下:

使用:中括号括起来的是可选的


mkimage[-x]-Aarch-Oos-Ttype-Ccomp-aaddr-eep-nname-ddata_file[:data_file...]image



选项:

-A:setarchitectureto'arch'//用于指定CPU类型,比如ARM

-O:setoperatingsystemto'os'//用于指定操作系统,比如Linux

-T:setimagetypeto'type'//用于指定image类型,比如Kernel

-C:setcompressiontype'comp'//指定压缩类型

-a:setloadaddressto'addr'(hex)//指定image的载入地址

-e:setentrypointto'ep'(hex)//内核的入口地址,一般为image的载入地址+0x40(信息头的大小)

-n:setimagenameto'name'//image在头结构中的命名

-d:useimagedatafrom'datafile'//无头信息的image文件名

-x:setXIP(executeinplace)//设置执行位置


先将u-boot下的tools中的mkimage复制到主机的/usr/local/bin目录下,这样就可以在主机的任何目录下使用该工具了。现在我们进入kernel生成目录(一般是arch/arm/boot目录),然后执行如下命令,就会在该目录下生成一个uImage.img的镜像文件,把他复制到tftp目录下,这就是我们所说的uImage。

mkimage
-n'linux-2.6.30.4'
-Aarm-Olinux
-Tkernel-Cnone
-a0x30008000-e0x30008000
-dzImageuImage.img


③、NandFlash的分区。我们查看内核在arch/arm/plat-s3c24xx/common-smdk.c中的分区情况如下:

起始地址结束地址


uboot:0x000000000x00030000

param:0x000300000x00040000//注意这个环境变量的地址范围要与上一节补充内容中配置的CONFIG_ENV_OFFSET一致

kernel:0x000500000x00200000

root:0x002500000x03dac000


④、设置修改u-boot的启动参数,在u-boot命令行下输入:

//设置启动参数,意思是将nand中0x50000-0x00200000(和kernel分区一致)的内容读到内存0x31000000中,然后用bootm命令来执行


setbootcmd'nandread0x310000000x500000x00200000;bootm0x31000000'


saveenv
//保存设置


⑤、把uImage.img用tftp下载到内存中,然后再固化到NandFlash中,操作和执行图如下:

tftp0x30000000uImage.img//将uImage.img下载到内存0x30000000处

nanderase0x500000x200000//擦除nand的0x50000-0x200000的内容

nandwrite0x300000000x500000x200000//将内存0x30000000处的内容写入到nand的0x50000处





最后,我们重新启动开发板,可以看到,内核被u-boot成功引导起来了,如图:






#defineMACH_TYPE_SMDK24401008//针对2440的MACH_TYPE码的值定义为1008


那么我们就修改u-boot的MACH_TYPE代码引用部分,确定u-boot的MACH_TYPE。如下:

#geditboard/samsung/my2440/my2440.c//修改board_init函数


/*archnumberofSMDK2410-Board*/

//gd->bd->bi_arch_number=MACH_TYPE_SMDK2410;

改为:

gd->bd->bi_arch_number=MACH_TYPE_SMDK2440;


其次,确定kernel中的MACH_TYPE。在kernel的arch/arm/tools/mach-types文件中也针对不同的CPU定义了非常多的MACH_TYPE,也可以找到下面这个定义:

smdk2440MACH_SMDK2440SMDK24401008


那么我们就修改kernel的MACH_TYPE代码引用部分,确定kernel的MACH_TYPE。如下:

#geditarch/arm/mach-s3c2440/mach-smdk2440.c//修改文件最后面


//MACHINE_START(S3C2440,"SMDK2440")

改为:

MACHINE_START(SMDK2440,"SMDK2440")


#geditarch/arm/kernel/head.S//在ENTRY(stext)下添加如下代码(红色部分)


ENTRY(stext)


movr0,#0

movr1,#0x3f0//上面的MACH_TYPE值1008换成十六进制就是0x3f0

ldrr2,=0x30000100



msrcpsr_c,#PSR_F_BIT|PSR_I_BIT|SVC_MODE


.......


分别重新编译u-boot和kernel。u-boot下载后,记得要saveenv;kernel用tftp下载到内存后使用go命令来测试引导内核,结果可以引导了,如下:



#geditdrivers/net/dm9000x.c//屏蔽掉dm9000_halt函数中的内容


/*

Stoptheinterface.

Theinterfaceisstoppedwhenitisbrought.

*/

staticvoiddm9000_halt(structeth_device*netdev)

{

//DM9000_DBG("%sn",__func__);

///*RESETdevie*/

//phy_write(0,0x8000);/*PHYRESET*/

//DM9000_iow(DM9000_GPR,0x01);/*Power-DownPHY*/

//DM9000_iow(DM9000_IMR,0x80);/*Disableallinterrupt*/

//DM9000_iow(DM9000_RCR,0x00);/*DisableRX*/

}


#geditinclude/configs/my2440.h


/*

*Hardwaredrivers

*/

#defineCONFIG_DRIVER_CS89001/*wehaveaCS8900on-board*/

#defineCS8900_BASE0x19000300//注意:对不同的开发板就是要修改这个片选地址参数,这个参数值就看开发板上网卡的片选引脚是接到ARM芯片存储控制器的哪个Bank上

#defineCS8900_BUS161/*theLinuxdriverdoesaccessesasshorts*/


(6)

一、移植环境

主机:VMWare--Fedora9
开发板:Mini2440--64MBNand,Kernel:2.6.30.4
编译器:arm-linux-gcc-4.3.2.tgz
u-boot:u-boot-2009.08.tar.bz2

二、移植步骤

10)u-boot利用tftp服务下载内核和利用nfs服务挂载nfs文件系统。

知识点:

tftp服务的安装与配置及测试;
nfs服务的安装与配置及测试;
u-boot到kernel的参数传递(重点)。

我们知道使用tftp下载内核和使用nfs挂载文件系统的好处是,当我们重新编译内核或文件系统后不用重新把这些镜像文件再烧录到flash上,而是把这些镜像文件放到开发主机的tftp或nfs服务的主目录下,通过网络来加载他们,不用频繁的往flash上烧,这样一可以保护flash的使用寿命,二可以方便的调试内核或文件系统,提高开发效率。可见,让u-boot实现这个功能是一件很有意义的事情。

实现这样的功能很简单,网上也有很多资料。但有很多细节的东西如果稍不注意就导致失败,这里就结合本人实现的过程进行讲述和一些问题的分析。

tftp服务的安装与配置及测试

要使用tftp服务及测试它要安装两个软件包,一个就是tftp服务器,另外一个就是tftp客户端,这里安装客户端只是用于在主机本地测试tftp服务器是否正常运行的,来确保u-boot能够访问tftp服务(u-boot中已有tftp客户端的功能,其实在前面几篇中都已经使用了tftp下载内核或文件系统到开发板上,如果那里都做到了,这里就可以直接跳过)。

首先使用rpm命令查看你的主机上是否已经安装了tftp服务器和客户端,如果没有安装就去下载这两个软件包进行安装或者可以使用yum命令进行在线安装,yum会自动的去搜索适合你主机平台的最新软件包进行下载安装,如果主机已经安装了,则会提示软件包已经安装了最新的版本。如下图所示:



配置tftp服务器,主要是配置tftp的主目录及访问权限。因tftp服务依赖于xinetd服务,所以一般tftp服务安装好后其配置文件一般会在/etc/xinetd.d/目录下:



nfs服务的安装与配置及测试

以root的身份在控制台输入setup,在系统服务选项中选中nfs服务,如下图:





配置NFS服务器的共享主目录,也要注意权限问题:

u-boot到kernel的参数传递

我们知道,在kernel配置选项Bootoptions中有一个Defaultkernelcommandstring参数项,而在u-boot参数中也有一个bootargs参数项,他们都是供内核启动用的,那他们又有什么区别呢,内核启动时到底是用哪一个呢?两种参数项分别如下图所示(kernel中的参数指定是从开发板Flash分区上挂载文件系统,u-boot中的参数指定的是从NFS挂载文件系统):






实际上,内核中的参数项是内核默认提供的,在内核配置时去指定,而u-boot提供的则在u-boot启动时传递到内核中取代内核提供的参数。所以当u-boot没有提供bootargs参数时,内核启动就是用内核配置时指定的参数,当u-boot提供了bootargs参数时就使用u-boot的参数。

那么,u-boot是如果将参数信息传递到内核中的呢?而内核又是怎么接收u-boot传递过来的参数呢?这就涉及到一点点ARM寄存器的知识了。

我们知道,ARM有7种工作模式和37个寄存器(31个通用寄存器和6个状态寄存器),如下图:



ARM工作模式之间的转换就是利用这些寄存器进行,而u-boot参数的传递也利用了三个通用寄存器R0、R1和R2。关于ARM工作模式和寄存器在这里就不做讲叙了,以后再讲,这里你就理解成u-boot在启动的时候把参数存放到这三个寄存器中,到内核启动时再把寄存器中的参数取出,当然,他们并不是就这样简单的操作。下面我们看代码一一分析。

首先,我们来分析一下u-boot是怎样处理和发送要传递的参数,而u-boot要传递的参数又有哪些呢?除了我们最容易知道的bootargs(即内核commandline)参数项外,要传递的参数还有MACH_TYPE(即我们所说的机器码)、系统根设备信息(标志,页面大小)、内存信息(起始地址,大小)、RAMDISK信息(起始地址,大小)、压缩的RAMDISK根文件系统信息(起始地址,大小)。由此可见要传递的参数很多,这时候,u-boot就提供一种叫做参数链表(taggedlist)的方式把这些参数组织起来,链表结构体定义在:include/asm-arm/setup.h中,而实现链表的组织在lib_arm/bootm.c中:






我们可以看到,链表的组织是由一系列函数实现,u-boot规定,链表必须以ATAG_CORE标记开始,以ATAG_NONE标记结束,中间就是一些参数标记项,这点从代码中可以体现出来。那么在这些函数中有一个bd的参数是至关重要的,它是一个bd_info类型的结构体,定义在include/asm-arm/u-boot.h中,而这个结构体又被一个global_data类型的结构体所引用,定义在include/asm-arm/global_data.h中,如下:





那么,那个bd参数到底是做什么用的呢?从定义中可以得知,bd记录了机器码、u-boot参数链表在内存中的地址等信息,那又问,它在什么地方进行记录的呢?它就在我们自己开发板初始化代码中记录的,如:board/samsung/my2440/my2440.c中



注意:bd_t被gd_t所引用,而在global_data.h中我们可以看到,u-boot定义了一个gd_t的全局指针变量*gd,所以在这里就可以直接使用gd来设置bd了。

好了,我们还是接着分析这个参数链表是如何被传递的,组织参数链表的系列函数在一个叫do_bootm_linux的函数中被调用的,还是定义在lib_arm/bootm.c中



从这个函数中我们可以看到,要使参数传递生效必须需要CONFIG_SETUP_MEMORY_TAGS和CONFIG_CMDLINE_TAG这两个宏的支持,所以需要在include/configs/my2440.h中定义它们。原来我就是没定义它们,在使用NFS挂载文件系统时就出现问题。同时,theKernel这个函数指针是u-boot参数传递的至关点,我们知道,函数在内存中执行的时候其实就是一个地址,而在代码中首先将这个函数指针指向kernel的入口地址,最后还将0、机器码和u-boot参数项在内存中的地址带给这个入口地址,故执行这个入口地址的时候即kernel启动的时候可以有这三个参数进行接收。那么,这个入口地址(kernel启动地址或者说kernel入口地址)是怎么来的是谁指定的,又是多少呢?看代码,是从一个bootm_headers_t类型的结构体的成员ep取得的,而这个结构体是从调用do_bootm_linux的地方传递过来的。bootm_headers_t定义在include/image.h中,do_bootm_linux在common/cmd_bootm.c中被调用,如下:






从代码中可以清楚的看到对bootm_headers_t的成员ep进行了赋值,但是还是不够直观这个入口地址到底是多少?只知道是使用image_get_ep函数从bootm_headers_t中的legacy_hdr_os_copy上取得的,那它在什么地方被赋值的呢?原来在image_set_ep函数中,定义在tools/mkimage.c中,如下:




我们再想想,这个mkimage.c是做什么用的?原来是用它来***u-boot格式的内核——uImage,还记得怎样使用mkimage来***uImage吧,在“u-boot-2009.08在2440上的移植详解(四)”中讲到,如下:
mkimage[-x]-A
arch-Oos-Ttype
-Ccomp-aaddr
-eep-nname
-ddata_file[:data_file...]
image

选项:

-A:setarchitectureto
'arch'//用于指定CPU类型,比如ARM

-O:setoperatingsystemto'os'
//用于指定操作系统,比如Linux

-T:setimagetypeto
'type'//用于指定image类型,比如Kernel

-C:setcompressiontype
'comp'//指定压缩类型

-a:setloadaddressto
'addr'(hex)//指定image的载入地址

-e:setentrypointto
'ep'(hex)//内核的入口地址,一般为image的载入地址+0x40(信息头的大小)

-n:setimagenameto
'name'//image在头结构中的命名

-d:useimagedatafrom
'datafile'//无头信息的image文件名

-x:setXIP(executeinplace)//设置执行位置

例如:

mkimage-n'linux-2.6.30.4'-Aarm
-Olinux-Tkernel
-Cnone-a0x30008000
-e0x30008000-dzImageuImage.img

呵呵,相信此时的你拨云见日,茅塞顿开了吧!这个入口地址就是0x30008000,这也正是为什么u-boot一定要使用uImage的格式来启动内核的原因之一。注意:这里有个kernel入口地址0x30008000,在上面还提到一个u-boot参数链表在内存中的地址0x30000100,试想如果这里指定的kernel入口地址覆盖了参数链表的地址会怎么样?

好了,把上面每个步骤从下往上看就可以知道u-boot参数项在u-boot端的传递的整个流程了,那么,接下来再分析u-boot参数项在kernel端是怎样接收的。

kernel启动的流程如下图所示:




在文件arch/arm/boot/compressed/head.S中,start是zImage的起始点,部分代码如下:
start:

......

.word0x016f2818@Magicnumberstohelptheloader

.wordstart@absoluteload/runzImageaddress

.word_edata@zImageendaddress

1:movr7,r1@savearchitectureID

movr8,r2@saveatagspointer

......

wont_overwrite:movr0,r4

movr3,r7

bldecompress_kernel

bcall_kernel

......

call_kernel:blcache_clean_flush

blcache_off

movr0,#0@mustbezero

movr1,r7@restorearchitecturenumber

movr2,r8@restoreatagspointer

movpc,r4@callkernel

......

首先,将u-boot传递过来的r1(机器码)、r2(参数链表在内在中的物理地址)分别保存到ARM寄存器r7、r8中,再将r7作为参数传递给解压函数decompress_kernel(),在这个解压函数中再将r7传递给全局变量__machine_arch_type,然后在跳转到vmlinux入口之前再将r7、r8还原到r1、r2中。

在arch/arm/kernel/head.S文件中,内核vmlinux入口的部分代码如下:

ENTRY(stext)

setmodePSR_F_BIT|PSR_I_BIT
|SVC_MODE,r9@ensuresvcmode@
andirqsdisabled

mrcp15,0,r9,c0,c0@getprocessorid

bl__lookup_processor_type@r5=procinfor9=cpuid

movsr10,r5@invalidprocessor(r5=0)?

beq__error_p@yes,error'p'

bl__lookup_machine_type@r5=machinfo

movsr8,r5@invalidmachine(r5=0)?

beq__error_a@yes,error'a'

bl__vet_atags

bl__create_page_tables

......

首先从ARM特殊寄存器(CP15)中获得ARM内核的类型,从处理器内核描述符(proc_info_list)表(__proc_info_begin—__proc_info_end)中查询有无此ARM内核的类型,如果无就出错退出。处理器内核描述符定义在include/asm-arm/procinfo.h中,具体的函数实现在arch/arm/mm/proc-xxx.S中,在编译连接过程中将各种处理器内核描述符组合成表。接着从机器描述(machine_desc)表(__mach_info_begin—__mach_info_end)中查询有无r1寄存器指定的机器码,如果没有就出错退出,所以这也说明了为什么在u-boot中指定的机器码一定要与内核中指定的一致,否则内核就无法启动。机器编号mach_type_xxx在arch/arm/tools/mach-types文件中说明,每个机器描述符中包括一个唯一的机器编号,机器描述符的定义在
include/asm-arm/mach/arch.h中,具体实现在arch/arm/mach-xxxx文件夹中,在编译连接过程中将基于同一种处理器的不同机器描述符组合成表。例如,S3C2440处理器的机器码为1008的机器描述符如下所示:
MACHINE_START(SMDK2440,"SMDK2440")

/*Maintainer:BenDooks<ben@fluff.org>*/

.phys_io
=S3C2410_PA_UART,

.io_pg_offst=(((u32)S3C24XX_VA_UART)>>
18)&0xfffc,

.boot_params=S3C2410_SDRAM_PA+0x100,//注意:这个地址就是与u-boot中参数链表在内存中的物理地址相对应

.init_irq=s3c24xx_init_irq,

.map_io=smdk2440_map_io,

.init_machine=smdk2440_machine_init,

.timer
=&s3c24xx_timer,

MACHINE_END


最后就打开MMU,并跳转到init/main.c的start_kernel()初始化系统。函数start_kernel()的部分代码如下:

asmlinkagevoid__initstart_kernel(void)

{

......

setup_arch(&command_line);

......

}

函数setup_arch在arch/arm/kernel/setup.c中实现,部分代码如下:

void__initsetup_arch(char**cmdline_p)

{

......

setup_processor();

mdesc=setup_machine(machine_arch_type);

......

parse_tags(tags);

......

}

setup_processor()函数从处理器内核描述符表中找到匹配的描述符,并初始化一些处理器

变量。setup_machine()用机器编号(在解压函数decompress_kernel中被赋值)作为参数返回机器描述符。从机器描述符中获得内核参数的物理地址,赋值给tags变量。然后调用parse_tags()函数分析内核参数链表,把各个参数值传递给全局变量。这样内核就收到了u-boot传递的参数。


tftp下载内核和nfs挂载文件系统

好了,上面tftp服务和nfs服务都已经准备好了,u-boot到kernel的参数传递也没问题了,接下来就设置一下u-boot环境变量中的参数项和kernel的配置选项使之能使用tftp自动下载kernal和通过网络自动挂载nfs文件系统。u-boot环境变量设置如下:



bootcmd参数项就是使用tftp把主机tftp主目录下的uImage下载到开发板SDRAM中的0x31000000位置,接着使用bootm命令执行引导内核启动。

而bootargs参数项就是内核启动的命令行参数,u-boot就是把这个参数项传递给了内核,通过nfs挂载文件系统。这里一定要注意serverip和ipaddr的设置(即服务器IP或者开发主机IP和开发板的IP)。另外要注意,内核要能使用nfs也要配置相应的选项,如下:

Filesystems--->

NetworkFileSystems--->

<*>NFSfilesystemsupport##必选

[*]ProvideNFSv3clientsupport##可选

[*]RootfilesystemonNFS##必选

Networking--->

[*]Networkingsupport

Networkingoptions--->

[*]IP:kernellevelautoconfiguration##必选


运行结果如下:

a.tftp下载内核,并引导内核启动:




b.u-boot传递的命令行参数被内核所接收:



c.内核通过nfs挂载文件系统:



d.查看挂载的nfs文件系统,发现完全与主机nfs服务器主目录中的文件系统一致,说明成功!







[root@localhosthome]#vi/etc/exports//如果没有这个文件就创建它,添加下面一行配置信息,注意格式一定要正确,否则导致服务不正常

/home/filesystem*(rw,no_root_squash,sync)

注释:“/home/filesystem”是NFS服务器的主目录,注意目录的权限

“*”表示所有的IP都可以访问NFS主目录

“rw”表示可读可写

”no_root_squash“表示登入到NFS主机的用户如果是ROOT用户,他就拥有ROOT的权限

“sync”表示同步


[root@localhosthome]#servicenfsrestart//重新启动NFS服务,使配置文件生效

测试NFS服务是否正常。将事先准备好的文件系统放到NFS主目录下,如下:

[root@localhosthome]#ls/home/filesystem/

bindevhomelibmntrootsum100tmpvar

debugetchostnamelinuxrcprocsbinsysusr

[root@localhosthome]#

//在主机本地测试NFS服务,将NFS主目录下的文件系统挂载到/mnt目录下,192.168.1.101是主机的IP

[root@localhosthome]#mount-onolock-tnfs192.168.1.101:/home/filesystem/mnt

可以看到/mnt目录下的内容和NFS主目录/home/filesystem下的内容完全一致,说明NFS服务正常:





[root@localhosthome]#
vi/etc/xinetd.d/tftp

servicetftp

{

disable=
no

socket_type=dgram

protocol=udp

wait
=yes

user=root

server=
/usr/sbin/in.tftpd

server_args=
-s/home/tftp-root-c//主要是修改这里,指定tftp服务器的主目录,-c选项是指可以创建文件

per_source=11

cps=1002

flags=IPv4

}


创建刚才指定的tftp服务器主目录,也要注意主目录的可读可写的权限:

[root@localhosthome]#mkdir
/home/tftp-root

[root@localhosthome]#chmod777/home/tftp-root

启动和测试tftp服务:

[root@localhosthome]#service
xinetdrestart//重启xinetd服务就会启动其下的所有服务,也包括tftp服务

[root@localhosthome]#service
iptablesstop//关闭防火墙

[root@localhosthome]#tftp主机IP地址

tftp>get要下载的文件

tftp>put要上传的文件

tftp>q

[root@localhosthome]#







(7)


一、移植环境

主机:VMWare--Fedora9
开发板:Mini2440--64MBNand,Kernel:2.6.30.4
编译器:arm-linux-gcc-4.3.2.tgz
u-boot:u-boot-2009.08.tar.bz2

二、移植步骤

9)实现u-boot对yaffs/yaffs2文件系统下载的支持。

注意:此篇对Nand的操作是基于MTD架构方式,在“u-boot-2009.08在2440上的移植详解(三)”中讲到过。

通常一个NnadFlash存储设备由若干块组成,1个块由若干页组成。一般128MB以下容量的NandFlash芯片,一页大小为528B,被依次分为2个256B的主数据区和16B的额外空间;128MB以上容量的NandFlash芯片,一页大小通常为2KB。由于NandFlash出现位反转的概率较大,一般在读写时需要使用ECC进行错误检验和恢复。

Yaffs/yaffs2文件系统的设计充分考虑到NandFlash以页为存取单位等的特点,将文件组织成固定大小的段(Chunk)。以528B的页为例,Yaffs/yaffs2文件系统使用前512B存储数据和16B的额外空间存放数据的ECC和文件系统的组织信息等(称为OOB数据)。通过OOB数据,不但能实现错误检测和坏块处理,同时还可以避免加载时对整个存储介质的扫描,加快了文件系统的加载速度。以下是Yaffs/yaffs2文件系统页的结构说明:
Yaffs页结构说明

==============================================

字节用途

==============================================

0-511存储数据(分为两个半部)

512-515系统信息

516数据状态字

517块状态字

518-519系统信息

520-522后半部256字节的ECC

523-524系统信息

525-527前半部256字节的ECC

==============================================


好了,在了解NandFlash组成和Yaffs/yaffs2文件系统结构后,我们再回到u-boot中。目前,在u-boot中已经有对Cramfs、Jffs2等文件系统的读写支持,但与带有数据校验等功能的OOB区的Yaffs/Yaffs2文件系统相比,他们是将所有文件数据简单的以线性表形式组织的。所以,我们只要在此基础上通过修改u-boot的NandFlash读写命令,增加处理00B区域数据的功能,即可以实现对Yaffs/Yaffs2文件系统的读写支持。

实现对Yaffs或者Yaffs2文件系统的读写支持步骤如下:
①、在include/configs/my2440.h头文件中定义一个管理对Yaffs2支持的宏和开启u-boot中对NandFlash默认分区的宏,如下:

#geditinclude/configs/my2440.h//添加到文件末尾即可


#defineCONFIG_MTD_NAND_YAFFS21//定义一个管理对Yaffs2支持的宏



//开启NandFlash默认分区,注意此处的分区要和你的内核中的分区保持一致

#defineMTDIDS_DEFAULT
"nand0=nandflash0"

#defineMTDPARTS_DEFAULT"mtdparts=nandflash0:192k(bootloader),"\

"64k(params),"\

"2m(kernel),"\

"-(root)"


②、在原来对Nand操作的命令集列表中添加Yaffs2对Nand的写命令,如下:



接着,在该文件中对nand操作的do_nand函数中添加yaffs2对nand的操作,如下:

if(strncmp(cmd,"read",4)==0||strncmp(cmd,"write",5)==0)

{

intread;

if(argc<4)

gotousage;

addr=(ulong)simple_strtoul(argv[2],NULL,16);

read=strncmp(cmd,"read",4)==0;/*1=read,0=write*/

printf("\nNAND%s:",read?"read":"write");

if(arg_off_size(argc-3,argv+3,nand,&off,&size)!=0)

return1;

s=strchr(cmd,'.');

if(!s||!strcmp(s,".jffs2")||!strcmp(s,".e")||!strcmp(s,".i"))

{

if(read)

ret=nand_read_skip_bad(nand,off,&size,(u_char*)addr);

else

ret=nand_write_skip_bad(nand,off,&size,(u_char*)addr);

}

//添加yaffs2相关操作,注意该处又关联到nand_write_skip_bad函数

#ifdefined(CONFIG_MTD_NAND_YAFFS2)

elseif(s!=NULL&&(!strcmp(s,".yaffs2")))

{

nand->rw_oob=1;

nand->skipfirstblk=1;

ret=nand_write_skip_bad(nand,off,&size,(u_char*)addr);

nand->skipfirstblk=0;

nand->rw_oob=0;

}#endif

elseif(!strcmp(s,".oob"))

{

/*out-of-banddata*/

mtd_oob_ops_tops=

{

.oobbuf=(u8*)addr,

.ooblen=size,

.mode=MTD_OOB_RAW

};

if(read)

ret=nand->read_oob(nand,off,&ops);

else

ret=nand->write_oob(nand,off,&ops);

}

else

{

printf("Unknownnandcommandsuffix'%s'.\n",s);

return1;

}

printf("%zubytes%s:%s\n",size,read?"read":"written",ret?"ERROR":"OK");

returnret==0?0:1;

}

③、在include/linux/mtd/mtd.h头文件的mtd_info结构体中添加上面用到rw_oob和skipfirstblk数据成员,如下:
#geditinclude/linux/mtd/mtd.h//在mtd_info结构体中添加


#ifdefined(CONFIG_MTD_NAND_YAFFS2)

u_charrw_oob;

u_charskipfirstblk;#endif


④、在第二步关联的nand_write_skip_bad函数中添加对NandOOB的相关操作,如下:
#geditdrivers/mtd/nand/nand_util.c//在nand_write_skip_bad函数中添加


intnand_write_skip_bad(nand_info_t*nand,loff_toffset,size_t*length,u_char*buffer)

{

intrval;

size_tleft_to_write=*length;

size_tlen_incl_bad;

u_char*p_buffer=buffer;

#ifdefined(CONFIG_MTD_NAND_YAFFS2)
//addyaffs2filesystemsupport

if(nand->rw_oob==1)

{

size_toobsize=nand->oobsize;

size_tdatasize=nand->writesize;

intdatapages=0;

if(((*length)%(nand->oobsize+nand->writesize))!=0)

{

printf("Attempttowriteerrorlengthdata!\n");

return-EINVAL;

}

datapages=*length/(datasize+oobsize);

*length=datapages*datasize;

left_to_write=*length;

}#endif

/*Rejectwrites,whicharenotpagealigned*/

if((offset&(nand->writesize-1))!=0||

(*length&(nand->writesize-1))!=0){

printf("Attempttowritenonpagealigneddata\n");

return-EINVAL;

}

len_incl_bad=get_len_incl_bad(nand,offset,*length);

if((offset+len_incl_bad)>=nand->size){

printf("Attempttowriteoutsidetheflasharea\n");

return-EINVAL;

}

#if!defined(CONFIG_MTD_NAND_YAFFS2)
//addyaffs2filesystemsupport

if(len_incl_bad==*length){

rval=nand_write(nand,offset,length,buffer);

if(rval!=0)

printf("NANDwritetooffset%llxfailed%d\n",

offset,rval);

returnrval;

}#endif

while(left_to_write>0){

size_tblock_offset=offset&(nand->erasesize-1);

size_twrite_size;

WATCHDOG_RESET();

if(nand_block_isbad(nand,offset&~(nand->erasesize-1))){

printf("Skipbadblock0x%08llx\n",

offset&~(nand->erasesize-1));

offset+=nand->erasesize-block_offset;

continue;

}

#ifdefined(CONFIG_MTD_NAND_YAFFS2)
//addyaffs2filesystemsupport

if(nand->skipfirstblk==1)

{

nand->skipfirstblk=0;

printf("Skipthefirstgoodblock%llx\n",offset&~(nand->erasesize-1));

offset+=nand->erasesize-block_offset;

continue;

}#endif

if(left_to_write<(nand->erasesize-block_offset))

write_size=left_to_write;

else

write_size=nand->erasesize-block_offset;

printf("\rWritingat0x%llx--",offset);//addyaffs2filesystemsupport

rval=nand_write(nand,offset,&write_size,p_buffer);

if(rval!=0){

printf("NANDwritetooffset%llxfailed%d\n",

offset,rval);

*length-=left_to_write;

returnrval;

}

left_to_write-=write_size;

printf("%d%%iscomplete.",100-(left_to_write/(*length/100)));

offset+=write_size;

#ifdefined(CONFIG_MTD_NAND_YAFFS2)
//addyaffs2filesystemsupport

if(nand->rw_oob==1)

{

p_buffer+=write_size+(write_size/nand->writesize*nand->oobsize);

}

else

{

p_buffer+=write_size;

}

#else

p_buffer+=write_size;#endif

}

return0;

}

⑤、在第四步nand_write_skip_bad函数中我们看到又对nand_write函数进行了访问,所以这一步是到nand_write函数中添加对yaffs2的支持,如下:
#geditdrivers/mtd/nand/nand_base.c//在nand_write函数中添加


staticintnand_write(structmtd_info*mtd,loff_tto,size_tlen,size_t*retlen,constuint8_t*buf)

{

structnand_chip*chip=mtd->priv;

intret;




#ifdefined(CONFIG_MTD_NAND_YAFFS2)//addyaffs2filesystemsupport


intoldopsmode=0;


if(mtd->rw_oob==1)

{

inti=0;

intdatapages=0;


size_toobsize=mtd->oobsize;

size_tdatasize=mtd->writesize;


uint8_toobtemp[oobsize];

datapages=len/(datasize);


for(i=0;i<(datapages);i++)

{

memcpy((void*)oobtemp,(void*)(buf+datasize*(i+1)),oobsize);

memmove((void*)(buf+datasize*(i+1)),(void*)(buf+datasize*(i+1)+oobsize),(datapages-(i+1))*(datasize)+(datapages-1)*oobsize);

memcpy((void*)(buf+(datapages)*(datasize+oobsize)-oobsize),(void*)(oobtemp),oobsize);

}

}#endif




/*Donotallowreadspastendofdevice*/

if((to+len)>mtd->size)

return-EINVAL;

if(!len)

return0;


nand_get_device(chip,mtd,FL_WRITING);


chip->ops.len=len;

chip->ops.datbuf=(uint8_t*)buf;




#ifdefined(CONFIG_MTD_NAND_YAFFS2)//addyaffs2filesystemsupport


if(mtd->rw_oob!=1)

{

chip->ops.oobbuf=NULL;

}

else

{

chip->ops.oobbuf=(uint8_t*)(buf+len);

chip->ops.ooblen=mtd->oobsize;

oldopsmode=chip->ops.mode;

chip->ops.mode=MTD_OOB_RAW;

}

#else

chip->ops.oobbuf=NULL;#endif




ret=nand_do_write_ops(mtd,to,&chip->ops);


*retlen=chip->ops.retlen;


nand_release_device(mtd);




#ifdefined(CONFIG_MTD_NAND_YAFFS2)//addyaffs2filesystemsupport


chip->ops.mode=oldopsmode;#endif




returnret;

}


OK,对yaffs2支持的代码已修改完毕,重新编译u-boot并下载到nand中,启动开发板,在u-boot的命令行输入:nandhelp查看nand的命令,可以看到多了一个nandwrite[.yaffs2]的命令,这个就是用来下载yaffs2文件系统到nand中的命令了。


⑥、使用nandwrite[.yaffs2]命令把事前***好的yaffs2文件系统下载到NandFlash中(yaffs2文件系统的***请参考:Linux-2.6.30.4在2440上的移植之文件系统),下载操作步骤和效果图如下:



⑦、结合u-boot和内核来测试启动下载的yaffs2文件系统

设置u-boot启动参数bootargs,注意:这一长串参数要与内核配置里面的Bootoptions-->Defaultkernelcommandstring的设置要一致。特别是mtdblock3要根据内核具体的分区来设,在上一篇中讲到了内核中Nand的分区情况,u-boot属于mtdblock0,param属于mtdblock1,kernel属于mtdblock2,root就属于mtdblock3,所以这里要设置成root=/dev/mtdblock3,否则文件系统无法启动成功,会出现一些什么I/O之类的错误



好了,最后重启开发板,内核引导成功,yaffs2文件系统也挂载成功,效果图如下:



tftp0x30000000root-2.6.30.4.bin//用tftp将yaffs2文件系统下载到内存的0x30000000位置

nanderase0x2500000x3dac000//擦除Nand的文件系统分区

nandwrite.yaffs20x300000000x2500000x658170//将内存中的yaffs2文件系统写入Nand的文件系统分区,注意这里的0x658170是yaffs2文件系统的实际大小(可以在tftp传送完后可以看到),要写正确,否则会形成假坏块


#geditcommon/cmd_nand.c//在U_BOOT_CMD中添加


U_BOOT_CMD(nand,CONFIG_SYS_MAXARGS,1,do_nand,

"NANDsub-system",

"info-showavailableNANDdevices\n"

"nanddevice[dev]-showorsetcurrentdevice\n"

"nandread-addroff|partitionsize\n"

"nandwrite-addroff|partitionsize\n"

"read/write'size'bytesstartingatoffset'off'\n"

"to/frommemoryaddress'addr',skippingbadblocks.\n"

//注意:这里只添加了yaffs2的写命令,因为我们只用u-boot下载(即写)功能,所以我们没有添加yaffs2读的命令

#ifdefined(CONFIG_MTD_NAND_YAFFS2)

"nandwrite[.yaffs2]-addroff|partitionsize-write`size'byteyaffsimage\n"

"startingatoffsetoff'frommemoryaddressaddr'(.yaffs2for512+16NAND)\n"#endif

"nanderase[clean][offsize]-erase'size'bytesfrom\n"

"offset'off'(entiredeviceifnotspecified)\n"

"nandbad-showbadblocks\n"

"nanddump[.oob]off-dumppage\n"

"nandscrub-reallycleanNANDerasingbadblocks(UNSAFE)\n"

"nandmarkbadoff[...]-markbadblock(s)atoffset(UNSAFE)\n"

"nandbiterroff-makeabiterroratoffset(UNSAFE)"

#ifdefCONFIG_CMD_NAND_LOCK_UNLOCK

"\n"

"nandlock[tight][status]\n"

"bringnandtolockstateordisplaylockedpages\n"

"nandunlock[offset][size]-unlocksection"#endif

);

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: