UBOOT1.2.0分析及移植
格式弄的太乱了,不过还是希望对大家有所帮助 :D,下面是参考资料,这些都是移植uboot必看的资料。
参考资料:
(1)http://blog.chinaunix.net/u1/34474/showart.php?id=363269
这位高手的blog是每位想进去嵌入式行业的同学必看的。
(2)<<smdk2410+uboo1.2.0..>>pdf文档,百度上可以搜到的,非常感谢原作者的分享
工作环境:
uboot1.2.0
Ubuntu8.10
cross_compile3.3.2
1 绪论
描述当前嵌入式产品的发展现状;
描述sangsun公司的产品,简单介绍s3c2410,以及板载配置。
2 bootloader的介绍,不同种类优劣性比较
3 论文出发点:目前的论文主要是针对Uboot的编译与移植,侧重点在如何配置适合自己开发板的参数,并将它移植到开发板。但并没有对uboot的工作流程进行清晰的分析,如果能够从本质上掌握整个uboot的工作原理,开发嵌入式产品时的周期也将缩短。因此对uboot的分析是必要的。
4 uboot目录结构。u-boot的源码顶层目录说明
目 录 特 性 解 释 说 明
board 平台依赖 存放电路板相关的目录文件,
例如:RPXlite(mpc8xx)、
smdk2410(arm920t)、
sc520_cdp(x86) 等目录
cpu 平台依赖 存放CPU相关的目录文件
例如:mpc8xx、ppc4xx、
arm720t、arm920t、 xscale、i386等目录
lib_ppc 平台依赖 存放对PowerPC体系结构通用的文件,
主要用于实现PowerPC平台通用的函数
lib_arm 平台依赖 存放对ARM体系结构通用的文件,
主要用于实现ARM平台通用的函数
lib_i386 平台依赖 存放对X86体系结构通用的文件,
主要用于实现X86平台通用的函数
include 通用 头文件和开发板配置文件,
所有开发板的配置文件都在configs目录下
common 通用 通用的多功能函数实现
lib_generic 通用 通用库函数的实现
net 通用 存放网络的程序
fs 通用 存放文件系统的程序
post 通用 存放上电自检程序
drivers 通用 通用的设备驱动程序,主要有以太网接口的驱动
disk 通用 硬盘接口程序
rtc 通用 RTC的驱动程序
dtt 通用 数字温度测量器或者传感器的驱动
examples 应用例程 一些独立运行的应用程序的例子,例如helloworld
tools 工具 存放制作S-Record或者u-boot格式的映像等工具,
例如mkimage
doc 文档 开发使用文档
5 简单介绍一下makefile,开始着手于源码的Makefile分析。在整个uboot目录中,makefile可以分为三类,
(1)顶层makefile
(2)结构makefile
(3)子目录makefile
正式分析Makefile。makefile内容比较容易理解。
Uboot的makefile总共有50几页,但其中重要的内容折合起来也就十二三页。
6正式开始分析uboot源码。
根据/board/smdk2410目录下的u-boot.lds可知,函数入口处在/cpu/arm920t/start.S中的_start处,且从0x00000000地址处开始。
因此首先应该分析start.S文件,这是第一个执行的汇编文件,非常的重要。
(1) cpu/arm920t/start.S
uboot启动也分stage1和stage2两个阶段。Uboot加载流程。2410上电,nandflash前4kb的内容以硬件的方式被拷贝到SRAM中,SRAM是cpu核中与nandflash配合的芯片从而实现nandflash的自启动系统。在SRAM中的部分uboot代码执行并在第2阶段执行之前,将nandflash中的uboot的全部代码拷贝到SDRAM中运行。然后引导内核与文件系统。
由上可以看出,这4kb以内代码必须要能够完成一些初始化工作。其实这就是我们平时所说的Uboot运行两阶段中的第一阶段。这部分短小的代码用汇编语言编写而成,也就是我们的start.S了。下面是总结的一张流程图
.globl _start
![](http://blogimg.chinaunix.net/blog/upfile2/090420233913.jpg)
1 _start: b reset 运行以后,先跳转到reset标签处
。。。。。。
-
reset:
从reset主要完成以下的功能。
-
首先切换到svc超级用户模式。在系统复位和软件中断时可以进入这种模式。
-
关闭看门狗。看门狗可以用作16位的定时器来请求中断服务。并在固定时钟内产生复位信号所以要关闭。
-
屏蔽所有的中断。通过设置INTMSK,INTSUBMSK寄存器来实现。
-
设置时钟。2410的时钟一般采用默认的,不用另外设置。
3 #ifndef CONFIG_SKIP_LOWLEVEL_INIT 跳转到cpu_init_crit处
bl cpu_init_crit
#endif 接下来跳转到cpu_init_crit处。Bl指令是带返回的。PC值将指向下一条指令。
cpu_init_crit:从注释中可以了解到。它主要来设置一些重要的寄存器和内存时钟的设置。
MCR.MRC指令统称为协处理器寄存器传送指令。
MCR{cond} coproc,opcode1,Rd,CRn,CRm,{,opcode2}
Coproc:指令操作的协处理器名。标准名为pn,n为0~15
mcr p15, 0, r0, c7, c7, 0例如这条指令就是将寄存器r0中的值送到协处理器p15中c7中去。
mrc即是将协处理中的寄存器的内容送到外部的寄存器中。
在重定位之前,我们必须设置内存时序,因为内存时钟是依赖于开发版的。在lowlevel_init.S文件里可以得到解释。lowlevel_init.S主要定义了各Bank的内钟等等
之后mov ip, lr //保存当前的lr。即是当前的pc值,因为再下一条指令又要进行跳转了。。
bl lowlevel_init//同样,这是条带返回的跳转。指向下一条指令。
mov lr, ip
mov pc, lr
因为lowevel_init被声明为.globl,可以全局使用,虽然不在start.S文件中定义。因此可以直接跳转到/board/smdk2410/lowlevel.S中来执行这个命令了。
它主要完成内存控制的配置,设置当前的PC为nandflash的起始位置。
下面是Lowlevel_init中的部分代码
BWSCON:BUS WIDTH&WAIT CONTROL REGISTER 0x48000000
可以来选择哪个BANK
ldr r0, =SMRDATA //SMRDATA的地址传到r0中。
ldr r1, _TEXT_BASE //通过将_TEXT_BASE中的值加载到r1中
sub r0, r0, r1 //r0=r0-r1??
ldr r1, =BWSCON /* Bus Width Status Controller */
add r2, r0, #13*4//SMRDATA共13条指令。
0:
ldr r3, [r0], #4//将SMDDATA中的值循环。主要是涉及了BAND0~7的时钟啊,内存刷新什么的
str r3, [r1], #4
cmp r2, r0
bne 0b
/* everything is fine now */
mov pc, lr//返回到cpu_init_crit
这样返回到了cup_init_crit中,
.....
mov ip,lr //ip中保存了reset中调用cpu_init_crit的下一条指令地址
bl lowlevel_init
mov lr, ip //Lowlevel_init返回后,执行这条指令。将ip传给lr
mov pc, l //再传给pc,这样就回到了reset的下一条指令处
#endif /* CONFIG_SKIP_LOWLEVEL_INIT */
返回到reset处吧~~#endif等着我们,到这里reset就执行完成了。执行完成以后返回到原命令处。继续执行接下去的命令。
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
定义各种异常模式。每种异常模式都有对应的pc来存储异常指令地址。接着继续执行这条指令以后的汇编代码,具体还是要结合start.S源码看的。
另外在/board/smdk2410/config,mk目录下存储着这样的信息。
# SMDK2410 has 1 bank of 64 MB DRAM
#
# 3000'0000 to 3400'0000
#
# Linux-Kernel is expected to be at 3000'8000, entry 3000'8000
# optionally with a ramdisk at 3080'0000
#
# we load ourself to 33F8'0000
#
# download area is 3300'0000
#
TEXT_BASE = 0x33F80000
定义了TEXT_BASE的地址。另外UBOOT_RAM_BASE _armboot_start 都是这个地址
Start.S继续向下执行,到达relocate代码段
relocate那段代码只针对从norflash中启动的设备有效,可要可不要,因此要是设备支持从nandflash启动,需要添加能将uboot拷贝到RAM中的代码。start.S本身是在SRAM中运行的,添加的这部分拷贝代码就是用来将剩余的内容全部拷贝到RAM中去了。
我们这里只是分析阶段。具体怎么在这里添加拷贝代码,参考第二部分 正式移植
在添加的拷贝代码中,执行了一条跳转到nand_read_ll的指令。此函数在board/smdk2410/nand_read,c中定义了。但是uboot的源码包中没有这个函数,我们可以从vivi中获取这个函数~通过这个函数来将uboot读取到RAM中。
执行完这个函数以后,接着从copy_myself返回!!!检验一下是否将uboot已全部读取到TEXT_BASE中,如果成功,则继续执行下一条关键指令!!
即ldr pc,_start_armboot 将这个函数的地址值赋给PC,直接实现跳转!
_start_armboot: .word start_armboot !
开始跳转到/lib_arm/board.c中定义的start_armboot函数中执行了。到现在为止就是第2阶段正式开始了。Start.S文件分析也到此结束!!
(2)board.c也非常重要,并且start_armboot()函数定义lib_arm/board.c文件中。
board.c是个非常重要的文件。包括了CS8900网卡驱动函数的声明、内存函数初始化的函数mem_malloc_init、nandflash初始化、波特率初始化函数、输出一些硬件信息....start_armboot本身主要完成网络的设置等,并最终进入到main_loop()函数。即循环等待。下面先介绍几个重要的数据结构。
1初始化函数序列init_sequence[]
init_sequence[]数组保存着基本的初始化函数指针。这些函数名称和实现的程序文件在下列注释中。
init_fnc_t *init_sequence[] = {
cpu_init, /* 基本的处理器相关配置 -- cpu/arm920t/cpu.c */
board_init, /* 基本的板级相关配置 -- board/smdk2410/smdk2410.c */
interrupt_init, /* 初始化例外处理 -- cpu/arm920t/s3c24x0/interrupt.c */
env_init, /* 初始化环境变量 -- common/env_flash.c */
init_baudrate, /* 初始化波特率设置 -- lib_arm/board.c */
serial_init, /* 串口通讯设置 -- cpu/arm920t/s3c24x0/serial.c */
console_init_f, /* 控制台初始化阶段1 -- common/console.c */
display_banner, /* 打印u-boot信息 -- lib_arm/board.c */
dram_init, /* 配置可用的RAM -- board/smdk2410/smdk2410.c */
display_dram_config, /* 显示RAM的配置大小 -- lib_arm/board.c */
NULL,
};
2 全局数据结构
typedef struct global_data {全局数据
bd_t *bd; //板子数据指针
unsigned long flags;//指示标志,如设备已经初始化等。
unsigned long baudrate;//波特率定义
unsigned long have_console; /* serial_init() was called 串口初始化*/
unsigned long reloc_off; /* Relocation Offset */
unsigned long env_addr; /* Address of Environment struct 环境参数地址*/
unsigned long env_valid; /* Checksum of Environment valid? 环境参数CRC检验有效标志*/
unsigned long fb_base; /* base address of frame buffer 帧地址*/
#ifdef CONFIG_VFD
unsigned char vfd_type; /* display type */
#endif
#if 0
unsigned long cpu_clk; /* CPU clock in Hz! */
unsigned long bus_clk;
unsigned long ram_size; /* RAM size */
unsigned long reset_status; /* reset status register at boot */
#endif
void **jt; /* jump table */
} gd_t; include/asm-arm/Global_data.h中定义。全局数据变量指针,它保存了U-boot运行时需要的全局数据。
3 开发板数据结构
typedef struct bd_info {
int bi_baudrate; /* serial console baudrate */
unsigned long bi_ip_addr; /* IP Address */
unsigned char bi_enetaddr[6]; /* Ethernet adress即网卡地址 */
struct environment_s *bi_env;
ulong bi_arch_number; /* unique id for this board */
ulong bi_boot_params; /* where this board expects params启动参数 */
struct /* RAM configuration */
{
ulong start;
ulong size;
} bi_dram[CONFIG_NR_DRAM_BANKS];
#ifdef CONFIG_HAS_ETH1
/* second onboard ethernet port */
unsigned char bi_enet1addr[6];
#endif
} bd_t; include/asm-arm/u-boot.h中定义。描述开发板的数据结构。
4 voidstart_armboot (void)函数分析
{
//全局数据变量指针gd占用r8。
DECLARE_GLOBAL_DATA_PTR;
/* 给全局数据变量gd安排空间*/
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t));
memset ((void*)gd, 0, sizeof (gd_t));
/* 给板子数据变量gd->bd安排空间*/
gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));
memset (gd->bd, 0, sizeof (bd_t));
monitor_flash_len = _bss_start - _armboot_start;//取u-boot的长度。
/* 顺序执行init_sequence数组中的初始化函数 */
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {
if ((*init_fnc_ptr)() != 0) {
hang ();
}
}
/*配置可用的Flash */
size = flash_init ();
……
/* 初始化堆空间 */
mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);
/* 重新定位环境变量, */
env_relocate ();
/* 从环境变量中获取IP地址 */
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
/* 以太网接口MAC 地址 */
……
devices_init (); /* 设备初始化 */
jumptable_init (); //跳转表初始化
console_init_r (); /* 完整地初始化控制台设备 */
enable_interrupts (); /* 使能中断处理 */
/* 通过环境变量初始化 */
if ((s = getenv ("loadaddr")) != NULL) {
load_addr = simple_strtoul (s, NULL, 16);
}
/* main_loop()循环不断执行 */
for (;;) {
main_loop (); /* 主循环函数处理执行用户命令 -- common/main.c */
}
}
在U-boot-arm.h中定义了这个变量。
extern ulong _armboot_start; /* code start */
extern ulong _bss_start; /* code + data end == BSS start */
extern ulong _bss_end; /* BSS end */
extern ulong IRQ_STACK_START; /* top of IRQ stack */
extern ulong FIQ_STACK_START; /* top of FIQ stack */
分析完start_armboot函数后,下面再分析几个比较重要的文件。
(1) include/configs/smdk2410.h
主要是定义了一些宏。用来选择处理器,设置时钟,定义内存池的大小。设置网卡CS8900,还有串口与时钟的设置。
一些命令的定义。主要是CONFIG_COMMANDS宏中。看名称就能猜到是定义命令的意思了。包括CFG_CMD_CACHE命令等等,如果要设置nandflash启动,那么必须要使CFG_CMD_NAND、CFG_CMD_PING、CFG_CMD_NET三个选项有效。接下去的程序就定义了一些关于IP地址,网关的设置。以及关于内存起始地址CFG_MEMTEST_START,CFG_MEMTEST_END的定义。还有一些栈的设置。包含了许多宏地址的定义。因此这个文件很重要!!最后一些就包括了FLASH的环境设置。要进行修改。将SDRAM的起始地址0x30000000 与flash的bank1 0x00000000 联系起来。下表是一些比较重要的宏定义。
表1
PHY_FLASH_1 | 0X00000000 |
CFG_FLASH_BASE | PHY_FLASH_1 |
CFG_PROMPT | “SMDK2410#” 开发板的用户名称 |
CFG_MEMTEST_START | 0X30000000 |
CFG_MEMTEST_END | 0X33F00000 |
CFG_LOAD_ADDR | 0X33000000默认的加载地址 |
PHYS_SDRAM_1 | OX30000000 SDRAM bank1 |
PHYS_SDRAM_1_SIZE | 0X04000000 |
CFG_ENV_IS_IN_FLASH | 1 |
CFG_ENV_SIZE | 0X10000 64K |
这个文件的主要功能就分析完了。我对源码做了比较好的分析,这里就不一一列举了。利用source insight进行查看。
(2)/include/Cmd_confdefs.h 这个文件包含了全部命令的宏定义,好像是定义宏的地址~~~~在smdk2410.h定义部分命令后,则必须将这个头文件include 进来。
(3)/include/s3c2410.h与/include/s3c24x0.h
首先先分析s3c24x0.h文件。这个文件中定义了所有的硬件资源寄存器。包括内存控制,USB控制,中断、DMA等等。主要是通过这些结构体定义的对象与s3c2410.h中定义的SFR基地址结合,定义这个某个寄存器有哪些状态寄存器。
该文件中。主要定义了特殊寄存的基地址。SFR的范围0x48000000~0x5A000000
如s3c24x0.h中的部分内容
typedef struct {
S3C24X0_REG32 BWSCON;
S3C24X0_REG32 BANKCON[8];
S3C24X0_REG32 REFRESH;
S3C24X0_REG32 BANKSIZE;
S3C24X0_REG32 MRSRB6;
S3C24X0_REG32 MRSRB7;
} /*__attribute__((__packed__))*/ S3C24X0_MEMCTL
定义一个s3c24x0_MEMCTL对象,这个结构体中都是内存的相关寄存器。
下面是s3c2410.h中的部分代码。利用24x0中的对象来获取基地址。两者结合~
static inline S3C24X0_MEMCTL * const S3C24X0_GetBase_MEMCTL(void)
{
return (S3C24X0_MEMCTL * const)S3C24X0_MEMCTL_BASE;
先定义S3C24X0_MEMCTL结构体对象, 再在s3c2410.h中定义一个const函数,表示这个函数的返回值是不变的。
(4)/include/common.h、 /common/cmd_nand.c、 /common/command.c common目录下包括一些通用的函数、如网络、硬盘、串口、nandflash等等.
这个文件主要定义了uboot的所有命令的形式。
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}每个命令就是一个cmd_tbl_t的对象。即每个命令都有一个结构体来描述。包括命令名字、参数的最大个数、重复数、命令执行函数、用法、帮助。从控制台中输入的命令是又command.c完成的,而find_cmd函数用来匹配具体的某个命令。
7内存分布情况从高到低分配。
对于smdk2410,RAM范围从0x30000000~0x34000000. u-boot占用高端内存区的1M大小。从前面的分析可知内存分配大致如下:
![](http://blogimg.chinaunix.net/blog/upfile2/090420234229.jpg)
内存图的分析:在start.S中
gd和bd共占128个字节的大小。在给malloc区和bd数据结构分配后,如果定义了IRQ,则给栈分配IRQ+FIQ占8K大小的区。CONFIG_STACKSIZE_IRQ=4K。如果没有定义,则至少保存12个字节。在smdk2410.h有定义。从0x33f0000到0x34000000的1M地址,是留给uboot使用的。下面是从代码中总结出的信息,不知道对不对。需要进一步验证。DW_STACK_START,建立堆栈,栈起点0x33f00000,大小为0x8000 32K大小的栈?UBOOT_RAM_BASE 与_TEXT_BASE相同0x33f80000 _arm_boot_start也为0x33f80000.uboot源码分析暂到此结束。其实还是有许多不明白的地方。以后发现什么新的了再添加吧
8 分析到此告一段落,下面进入uboot的具体移植
(1)创建自己的工作目录。在board目录下创建自己的工作目录,将smdk2410目录下的内容全部拷贝到tc2410目录下。
cd board
mkdir tc2410
cp -fr board/smdk2410 board/tc2410
cp include/configs/smdk2410.h include/configs/tc2410.h
修改tc2410目录下相关的文件名以及makefile,。在这个目录下需要添加nand_read.c文件,可以从vivi里面拷贝过来,nand_read.c文件主要实现了start.S文件中的nand_read_ll读写函数,用来将uboot拷贝到内存里去。
需要修改的地方:mv smdk2410.c tc2410.cCOBJS := tc2410.o flash.o nand_read.o
修改顶层makefile,添加自己的信息:
tc2410_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t tc2410 null s3c24x0
(2)修改start.S
禁止中断部分也要修改
# if defined(CONFIG_S3C2410)
ldr r1, =0x7ff //根据2410芯片手册,INTSUBMSK有11位可用,
//vivi也是0x7ff,不知为什么U-Boot一直没改过来。
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
将以下U-Boot的重定向语句替换:这段relocate代码只对从norflash中启动的设备有效,因此可以去掉
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate: /* relocate U-Boot to RAM */
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug */
beq stack_setup
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
add r2, r0, r2 /* r2 <- source end address */
copy_loop:
ldmia r0!, {r3-r10} /* copy from source address [r0] */
stmia r1!, {r3-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end addreee [r2] */
ble copy_loop
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */
替换成:
#ifdef CONFIG_S3C2410_NAND_BOOT
@ reset NAND
mov r1, #NAND_CTL_BASE
ldr r2, =0xf830 @ initial value
str r2, [r1, #oNFCONF]
ldr r2, [r1, #oNFCONF]
bic r2, r2, #0x800 @ enable chip
str r2, [r1, #oNFCONF]
mov r2, #0xff @ RESET command
strb r2, [r1, #oNFCMD]
mov r3, #0 @ wait
nand1:
add r3, r3, #0x1
cmp r3, #0xa
blt nand1
nand2:
ldr r2, [r1, #oNFSTAT] @ wait ready
tst r2, #0x1
beq nand2
ldr r2, [r1, #oNFCONF]
orr r2, r2, #0x800 @ disable chip
str r2, [r1, #oNFCONF]
@ get read to call C functions (for nand_read())
ldr sp, DW_STACK_START @ setup stack pointer
mov fp, #0 @ no previous frame, so fp=0
@ copy U-Boot to RAM
ldr r0, =TEXT_BASE
mov r1, #0x0
mov r2, #0x20000
bl nand_read_ll 这个函数在nand_read.c中实现
tst r0, #0x0
beq ok_nand_read
bad_nand_read:
loop2: b loop2 @ infinite loop
ok_nand_read:
@ verify
mov r0, #0
ldr r1, =TEXT_BASE
mov r2, #0x400 @ 4 bytes * 1024 = 4K-bytes,这句话的意思在start.S中已注释。
go_next:
ldr r3, [r0], #4
ldr r4, [r1], #4
teq r3, r4
bne notmatch
subs r2, r2, #4
beq stack_setup
bne go_next
notmatch:
loop3: b loop3 @ infinite loop
#endif @ CONFIG_S3C2410_NAND_BOOT
在 “ _start_armboot: .word start_armboot ” 后加入:
.align 2
DW_STACK_START: .word STACK_BASE+STACK_SIZE-4
start.S文件修改到此结束。
(3)在include/configs/tc2410.h头文件中添加nandflash的初始化信息,这个文件与板子密切相关,需要修改的地方也很多。
具体添加信息/*//添加的内容
//#define CFG_ENV_IS_IN_FLASH 1
#define CFG_ENV_IS_IN_NAND 1
#define CFG_ENV_OFFSET 0x020000
#define CFG_NAND_BASE 0x4E000000
#define CMD_SAVEENV
#define CFG_NAND_LEGACY
#define CFG_ENV_SIZE 0x10000 /* Total Size of Environment Sector */
#define CFG_MONITOR_BASE PHYS_SDRAM_1
#if (CONFIG_COMMANDS & CFG_CMD_NAND)
#define CFG_NAND_BASE 0x4E000000
/* NandFlash控制器在SFR区起始寄存器地址 */
#define CFG_MAX_NAND_DEVICE 1
/* 支持的最在Nand Flash数据 */
#define SECTORSIZE 512
/* 1页的大小 */
#define NAND_SECTOR_SIZE SECTORSIZE
#define NAND_BLOCK_MASK 511
/* 页掩码 */
#define ADDR_COLUMN 1
/* 一个字节的Column地址 */
#define ADDR_PAGE 3
/* 3字节的页块地址!!!!!*/
#define ADDR_COLUMN_PAGE 4
/* 总共4字节的页块地址!!!!! */
#define NAND_ChipID_UNKNOWN 0x00
/* 未知芯片的ID号 */
#define NAND_MAX_FLOORS 1
#define NAND_MAX_CHIPS 1
/* Nand Flash命令层底层接口函数 */
#define WRITE_NAND_COMMAND(d, adr) {rNFCMD = d;}
#define WRITE_NAND_ADDRESS(d, adr) {rNFADDR = d;}
#define WRITE_NAND(d, adr) {rNFDATA = d;}
#define READ_NAND(adr) (rNFDATA)
#define NAND_WAIT_READY(nand) {while(!(rNFSTAT&(1<<0)));}
#define NAND_DISABLE_CE(nand) {rNFCONF |= (1<<11);}
#define NAND_ENABLE_CE(nand) {rNFCONF &= ~(1<<11);}
/* the following functions are NOP's because S3C24X0 handles this in hardware 一定要加上 */
#define NAND_CTL_CLRALE(nandptr)
#define NAND_CTL_SETALE(nandptr)
#define NAND_CTL_CLRCLE(nandptr)
#define NAND_CTL_SETCLE(nandptr)
/* 允许Nand Flash写校验 */
#define CONFIG_MTD_NAND_VERIFY_WRITE 1
//添加的内容
/*
* Nandflash Boot
*/
#define CONFIG_S3C2410_NAND_BOOT 1
#define STACK_BASE 0x33f00000
#define STACK_SIZE 0x8000
#define UBOOT_RAM_BASE 0x33f80000
/* NAND Flash Controller */
#define NAND_CTL_BASE 0x4E000000
#define bINT_CTL(Nb) __REG(INT_CTL_BASE + (Nb))
/* Offset */
#define oNFCONF 0x00
#define oNFCMD 0x04
#define oNFADDR 0x08
#define oNFDATA 0x0c
#define oNFSTAT 0x10
#define oNFECC 0x14
#define rNFCONF (*(volatile unsigned int *)0x4e000000)
#define rNFCMD (*(volatile unsigned char *)0x4e000004)
#define rNFADDR (*(volatile unsigned char *)0x4e000008)
#define rNFDATA (*(volatile unsigned char *)0x4e00000c)
#define rNFSTAT (*(volatile unsigned int *)0x4e000010)
#define rNFECC (*(volatile unsigned int *)0x4e000014)
#define rNFECC0 (*(volatile unsigned char *)0x4e000014)
#define rNFECC1 (*(volatile unsigned char *)0x4e000015)
#define rNFECC2 (*(volatile unsigned char *)0x4e000016)
#endif /* CONFIG_COMMANDS & CFG_CMD_NAND*/
#endif /*__CONFIG_H*/
(4)我使用的是勤研开发板,在vivi里面并没有找到关于内存区配置的信息。lowlevel.init文件不知道如何修改。看见网上很多人说根据开发板的内存区情况配置,我找了半天没找到,郁闷至极。。。不过没有影响我成功移植,:-)
(5)在include/linux/mtd/nand_ids.h的结构体nand_flash_ids加入nandflash的具体信息。
static struct nand_flash_dev nand_flash_ids[] = {
......
{"Samsung KM29N16000",NAND_MFR_SAMSUNG, 0x64, 21, 1, 2, 0x1000, 0},
{"Samsung K9F1208U0B", NAND_MFR_SAMSUNG, 0x76, 26, 0, 3, 0x4000, 0},
{"Samsung unknown 4Mb", NAND_MFR_SAMSUNG, 0x6b, 22, 0, 2, 0x2000, 0},
......
{NULL,}
};
(6)修改/lib_arm中的board.c。
static int display_banner (void)
{
printf ("\n\n%s\n\n", version_string);
debug ("U-Boot code: %08lX -> %08lX BSS: -> %08lX\n",
_armboot_start, _bss_start, _bss_end);
printf ("U-Boot code: %08lX -> %08lX BSS: -> %08lX\n",输出_armboot_start地址信息
_armboot_start, _bss_start, _bss_end);
#ifdef CONFIG_MODEM_SUPPORT
debug ("Modem Support enabled\n");
#endif
#ifdef CONFIG_USE_IRQ
debug ("IRQ Stack: %08lx\n", IRQ_STACK_START);
debug ("FIQ Stack: %08lx\n", FIQ_STACK_START);
#endif
return (0);
}
(7)修改common/env_nand.c,具体nandflash的擦写需要使用nand_legacy_erase等函数实现,而不是nand-erase()函数
......
#ifdef CONFIG_INFERNO
#error CONFIG_INFERNO not supported yet
#endif
int nand_legacy_rw (struct nand_chip* nand, int cmd,
size_t start, size_t len,
size_t * retlen, u_char * buf);
extern struct nand_chip nand_dev_desc[CFG_MAX_NAND_DEVICE];
extern int nand_legacy_erase(struct nand_chip *nand, size_t ofs, size_t len, int clean);
/* info for NAND chips, defined in drivers/nand/nand.c */
extern nand_info_t nand_info[CFG_MAX_NAND_DEVICE];
......
#else /* ! CFG_ENV_OFFSET_REDUND */
int saveenv(void)
{
ulong total;
int ret = 0;
puts ("Erasing Nand...");
//if (nand_erase(&nand_info[0], CFG_ENV_OFFSET, CFG_ENV_SIZE))
if (nand_legacy_erase(nand_dev_desc + 0, CFG_ENV_OFFSET, CFG_ENV_SIZE, 0))
return 1;
puts ("Writing to Nand... ");
total = CFG_ENV_SIZE;
//ret = nand_write(&nand_info[0], CFG_ENV_OFFSET, &total, (u_char*)env_ptr);
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)
return 1;
puts ("done\n");
return ret;
......
#else /* ! CFG_ENV_OFFSET_REDUND */
/*
* The legacy NAND code saved the environment in the first NAND device i.e.,
* nand_dev_desc + 0. This is also the behaviour using the new NAND code.
*/
void env_relocate_spec (void)
{
#if !defined(ENV_IS_EMBEDDED)
ulong total;
int ret;
total = CFG_ENV_SIZE;
//ret = nand_read(&nand_info[0], CFG_ENV_OFFSET, &total, (u_char*)env_ptr);
ret = nand_legacy_rw(nand_dev_desc + 0, 0x01 | 0x02, CFG_ENV_OFFSET, CFG_ENV_SIZE, &total, (u_char*)env_ptr);
(8)在/board/tc2410/tc2410.c文件的末尾添加对Nand Flash 的初始化函数(在后面Nand Flash的操作都要用到)
u-boot运行至第二阶段进入start_armboot()函数。其中nand_init()函数是对nand flash的最初初始化函数。Nand_init()函数在两个文件中实现。其调用与CFG_NAND_LEGACY宏有关,如果没有定义这个宏,系统调用 drivers/nnd/nand.c中的nand_init();否则调用board/smdk2410/smdk2410.c中的nand_init()函数。这里我选择第二种方式。
#if (CONFIG_COMMANDS & CFG_CMD_NAND)
typedef enum {
NFCE_LOW,
NFCE_HIGH
} NFCE_STATE;
static inline void NF_Conf(u16 conf)
{
S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
nand->NFCONF = conf;
}
static inline void NF_Cmd(u8 cmd)
{
S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
nand->NFCMD = cmd;
}
static inline void NF_CmdW(u8 cmd)
{
NF_Cmd(cmd);
udelay(1);
}
static inline void NF_Addr(u8 addr)
{
S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
nand->NFADDR = addr;
}
static inline void NF_SetCE(NFCE_STATE s)
{
S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
switch (s) {
case NFCE_LOW:
nand->NFCONF &= ~(1<<11);
break;
case NFCE_HIGH:
nand->NFCONF |= (1<<11);
break;
}
}
static inline void NF_WaitRB(void)
{
S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
while (!(nand->NFSTAT & (1<<0)));
}
static inline void NF_Write(u8 data)
{
S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
nand->NFDATA = data;
}
static inline u8 NF_Read(void)
{
S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
return(nand->NFDATA);
}
static inline void NF_Init_ECC(void)
{
S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
nand->NFCONF |= (1<<12);
}
static inline u32 NF_Read_ECC(void)
{
S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
return(nand->NFECC);
}
#endif
/*
* NAND flash initialization.
*/
#if (CONFIG_COMMANDS & CFG_CMD_NAND)
extern ulong nand_probe(ulong physadr);
static inline void NF_Reset(void)
{
int i;
NF_SetCE(NFCE_LOW);
NF_Cmd(0xFF); /* reset command */
for(i = 0; i < 10; i++); /* tWB = 100ns. */
NF_WaitRB(); /* wait 200~500us; */
NF_SetCE(NFCE_HIGH);
}
static inline void NF_Init(void)
{
#if 0 /* a little bit too optimistic */
#define TACLS 0
#define TWRPH0 3
#define TWRPH1 0
#else
#define TACLS 0
#define TWRPH0 4
#define TWRPH1 2
#endif
NF_Conf((1<<15)|(0<<14)|(0<<13)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0));
/*nand->NFCONF = (1<<15)|(1<<14)|(1<<13)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0); */
/* 1 1 1 1, 1 xxx, r xxx, r xxx */
/* En 512B 4step ECCR nFCE=H tACLS tWRPH0 tWRPH1 */
NF_Reset();
}
void
nand_init(void)
{
S3C2410_NAND * const nand = S3C2410_GetBase_NAND();
NF_Init();
#ifdef DEBUG
printf("NAND flash probing at 0x%.8lX\n", (ulong)nand);
#endif
printf ("%4lu MB\n", nand_probe((ulong)nand) >> 20);
}
#endif
(9)移植的大部分问题是解决了,但是将Uboot烧进去后,可能还是会碰到问题。<<smdk2410+开发板u-boot-1>>这本pdf资料描述的问题我全都碰到了。呵呵,不过根据书上所提出的方法,我们都可以一一解决。
a 比如你如果没打开tc2410.h文件中的CFG_CMD_PING选项,使用ping 192.168.1.X命令时,可能ping不通。
b 不能保存环境变量,
Nand flash saveenv命令实现的函数 common/env_nand.c主要由以下几个宏决定:
CFG_CMD_ENV
CFG_CMD_NAND
CFG_ENV_IS_IN_NAND ---> 此宏决定调用 common/env_nand.c中的saveenv()函数,
如果定义CFG_ENV_IS_IN_FLASH宏,则调用 common/env_flash.c中的saveenv()函数
CFG_ENV_OFFSET 0X20000
#define CFG_NAND_BASE 0X4E000000
#define CMD_SAVEENV
#define CFG_NAND_LEGACY
#define CFG_ENV_SIZE 0x10000 //64kB
这些也是要在tc2410.h中添加的信息。
c tftp下载时出现TTTTTTTTTT然后超时的提示信息。我被这个问题困扰了很长时间,我也按照<<smdk2410+开发板u-boot-1>>这本pdf资料修改,将U_BOOT_CMD(
bootm, CFG_MAXARGS, 1, do_bootm,
"bootm - boot application image from memory\n",
"[addr [arg ...]]\n - boot application image stored in memory\n"
"\tpassing arguments 'arg ...'; when booting a Linux kernel,\n"
"\t'arg' can be the address of an initrd image\n"
/*#ifdef CONFIG_OF_FLAT_TREE
"\tWhen booting a Linux kernel which requires a flat device-tree\n"
"\ta third argument is required which is the address of the
"\tdevice-tree blob. To boot that kernel without an initrd image,\
"\tuse a '-' for the second argument. If you do not pass a third\
"\ta bd_info struct will be passed instead\n"
endif */
Ifdef注释掉以后,然后再将config.mk中的Os和O2都改做O.
终端再输入:make distclean
make tc2410_config
make CROSS_COMPILE=arm-linux-
生成了uboot镜像,共110.4K。烧到板子里去后,使用tftp下载内核,搞定!!!终于把uboot搞定了,断断续续的大概花了1个多月的时间,参考了网上众多高手的心得以及很多不错的资料,总算勉强的弄出来Uboot了,但还是有很多问题没有弄清楚呵呵,不过还是挺有成就感的。
疑问:
1 可以在include/configs/smdk2410.h中添加nandflash的宏定义
2 board.c里面的start_armboot()函数需要调用nand_init,即nandflash的初始化。在drive/nand/nand.c中有这样一段代码
#if (CONFIG_COMMANDS & CFG_CMD_NAND) && !defined(CFG_NAND_LEGACY)
意思就是 如果没有定义CFG_NAND_LEGACY那么则调用nand.c文件中的nand_init()函数。这个是默认的初始化函数。但还有个board_nand_init()函数没有实现。为了安全起见,我们可以定义CFG_NAND_LEGACY,从而可以调用board/smdk2410/smdk2410.c中的nand_init()函数.这样我们需要在smdk2410.c中添加这个初始化函数。在nand_legacy.c中添加这个nand_init()与在smdk2410.c中添加有什么区别??
board/smdk2410/smdk2410.c文件里只有board_init(),dram_init()函数,没有nand_init()函数。那这个函数去哪里实现。
3 nand_read_ll用来将代码拷贝到RAM中去,这个函数可以从vivi中拷贝过来。