移植u-boot-1.3.4到GT2440(第二版2.0)
作者:guolele
注释:图片太多,传麻烦,有兴趣到网上去找这文章吧,我已经上传了
第二版主要是对于第一版的一些错误改正,之前nand flash的移植失败,特在此处使用另一种nand flash移植方法.另外在这里还介绍一下uboot支持yaffs烧写功能的移植(新版里已经支持了),还介绍一下yaffs2文件系统的制作与使用。
硬件配置
1、 GT2440\其它开发板
2、 其中nand flash为2Kb一页(具体和512byte的有什么区别,参考nand元件手册,或者上网搜一下,对于uboot,主要是读nand时的写地址时序的不一样)
3、 串行线
4、 J-link\j-tag(笔者用的是jlink,主要用于uboot的调试,因为你移植马上能用的可能性不大,所以需要调试)
软件配置:
1、u-boot-1.3.4
2、- j-link或者H-jtag
一、从nandflash 启动uboot的原理
Uboot 源码是不支持从nand中启动的,但是2410 2440是支持的,而且对于闪存,nand比较大容量,比较便宜,所以使用nand启动uboot是比较需要的。那么它启动的原理是什么?
其实从nand flash 控制器有一个特殊功能,会自动把nand flash前4K内容复制到4K SRAM中(注意,是只有4k的SRAM而不是SDRAM,超过怎么办,所以要复制到SDRAM中,如下图)中,并把0x00000000设置成内存起始地址,cpu从这个地址开始运行。这个过程不需要程序干涉。在配置NAND启动模式之后,S3C2440上电会先将NAND中的0x0 - 0x1000共4096字节的数据拷贝到位于Bank0中的Boot Internal SRAM上
这4K的内容,主要是保存的uboot的部分功能(拷贝功能),执行后,再把nand里的内容拷贝到SDRAM中,原因有下:
1、 SDRAM运行速度快
2、 实际的uboot代码永远大于4Kb的空间,,所以要开辟一个新空间给uboot运行
|
二、uboot的运行流程
首先先大概分析一下uboot,这样有利于明白,移植的每一步是需要做些什么?
在讲uboot启动前,先讲一下arm启动流程,arm启动是先运行芯片厂家固化的boot block,这段程序是引导块,芯片厂家将boot block地址重映射到片内存储器空间最高处,接近接近2G(0x8000 0000靠MMU映射)。运行完这段程序后,就会运行0x0 地址。看回上面的2440存储映射图,假如是nor的话,就会直接运行Nor 里的内容,如果是nand的话,nand控制器先拷4k到0x0000 0000里的SRAM,然后0x4000000后的内容会被重映射到0x0000 0000的映射,这样就代表前4k直接操作的是bootinternal的SRAM,而不是nand的前4k,可能是因为SRAM速度快.
上个很好的图:
现在看回uboot启动流程:
像网上说的,uboot编译时首先编译的是u-boot-1.3.4\board\你的开发板文件夹\u-boot.lds,看一个2410的例子:
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 = .;
}
(1) 从ENTRY(_start)可以看出u-boot的入口函数是_start,这个没错
(2) 从. = 0x00000000也许可以看出_start的地址是0x00000000,事实并不是这样的,
.text为代码段,可以看出cpu/arm920t/start.o 在代码段的最前面,所以会先执行start.o 中的代码, 如何设置从0x33f80000开始呢?~这是链接的时候指定的
在根目录下面的config.mk中有下面一句
LDFLAGS +=-Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)
关键就是其中的-Ttext $(TEXT_BASE),这句指明了代码段的起始地址
而TEXT_BASE在 board/smdk2440/config.mk中定义 TEXT_BASE = 0x33F8 0000
为什么是0x33F80000呢?~
这是将NAND中Uboot拷贝到RAM中的起始地址,所以在代码拷贝到RAM之前不能使用绝对地址(链接后都会加上_TEXT_BASE)来寻址数据,只能用相对地址(相对于当前PC)(adr r0, _start可以确定程序在哪里运行,因为adr是基于pc当前值的,假如在内存中0x33f8000就应该是0x33f8000+_start,假如是在flash中,就应该是0+_start )
ARM汇编中,常有两种跳转方法:b跳转指令、ldr指令向PC赋值。 (参考《[NAND]UBOOT从NAND FLASH启动分析》)
要特别注意这两条指令的意思:
(a) b step:b跳转指令是相对跳转,依赖当前PC的值,偏移量是通过该
指令本身的 bit[23:0]算出来的,这使得使用b指令的程序不依赖于要跳到的代
码的位置,只看指令本身。具体是将这24位左移两位加上PC再赋给PC寄存器,得到目标地址
(b) ldr pc, =step :该指令是一个伪指令编译后会生成以下代码:
ldr pc, 0x30008000
<0x30008000 > step
是从内存中的某个位置(step)读出数据并赋给PC,同样依赖当前PC的值,但
是偏移量是step的连接地址(运行时的地址),所以可以用它实现从Flash到RAM的程
序跳转。
(c)此外,有必要回味一下adr伪指令,U-boot中那段relocate代码就是通过adr实
现当前程序是在RAM中还是flash中:
relocate: /* 把U-Boot
重新定位到RAM*/
adr r0, _start /* r0是代码的当
前位置 */
/* adr伪指令,汇编器自动通过当前PC的值算出这条指令中“_start"的值,执行到_
start时PC的值放到r0中:
当此段在flash中执行时r0 = _start = 0;当此段在RAM中执行时_start = _TEXT_B
ASE(在board/smdk2410/config.mk中指定的值为0x33F80000,即u-boot在把代码拷贝到RAM中去 执行的代码段的开始) */
ldr r1, _TEXT_BASE /* 测试判断是从Flash启
动,还是RAM */
/* 此句执行的结果r1始终是0x33F80000,因为此值是链接指定的 */
cmp r0, r1 /* 比较r0和r1,
调试的时候不要执行重定位 */(引用《u-boot.lds解析》)
所以说实际链接时,都是以_TEXT_BASE为基地址,但是运行时会根据现在代码所处的位置去复制uboot代码到内存(因为在复制到内存之前都是用相对地址的),如果在内存,就直接接着运行即可.具体请参考u-boot根目录下的config.mk.
顺着这个config.lds文件,就开始执行u-boot-1.3.4\cpu\arm920t\start.S
它里面大概执行流程是
(1) CPU为SVC模式
(2) 定义中断向量表
(3) 关闭中断 (因为uboot不需要使用中断)
(4) 底层初始化:两个函数cpu_init_crit和lowlevel_init
cpu_init_crit是禁止MMU和CACHE,为什么?因为如果你不禁止,有些数据会残留在cache上,会造成脏数据(《具体也可以参考ARM体系结构与编程》)
lowlevel_init主要是定义部分寄存器以及初始化SDRAM
(5) 然后把代码拷贝到内存中(没修改时为下面代码,修改后使用自己移植的)
#ifndefCONFIG_SKIP_RELOCATE_UBOOT
relocate: /* relocate U-Boot to RAM */
adr r0,_start /* r0 <- currentposition of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't relocduring 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 */
(6) 初始化堆栈(其实就是在内存中开辟一个区域,用于保存数据,其实是栈,经典映射图如下图)
(7) 调用C语言函数入口,start_armboot
到此第一阶段基本完成
注意b 跳转跟 ldr 跳转是不一样的
b跳转,依赖当前PC值,偏移量通过指令bit[23:0]算出
Ldr pc,=label
Ldr 伪指令,将内存值赋给PC,也是依赖于当前PC值,但是偏移量是label的运行时的地址
|
|
第二阶段是start_armboot
其实这个函数是定义在u-boot-1.3.4\lib_arm\board.c
他的作用是初始化,这初始化跟第一阶段的不一样,第一阶段的初始化主要是比较低层的,面现在已经建立起C的运行环境,所以可以初始化一点其它函数
这里主要初始化的是
主要定义在init_fnc_t*init_sequence[]这个指针数组里(注,参考《ARM79出口-u-boot移植手册》)
1、 cpu_init 定义在cpu\arm920t里,并没有做实质性的工作,因为笔者现在还没有定义CONFIG_USE_IRQ所以,代码来到这,直接return0
2、 board_init 定义在开发板.c里,笔者这是GT2440.c。它主要的工作是初始化系统时钟,引脚IO设置,使能数据cache和指令cache,考虑到板子可能会发生意外中断,还初始化了中断,下一步就初始化中断
3、 interrupt_init 这实际上是定时器的中断初始化。定义在cpu\arm920t\s3c24x0\Interrupts.c里
4、 env_init 环境初始化。在u-boot-1.3.4\common\env_nand.c里
5、 init_baudrate 用于初始化波特率
6、 serial_init 用于初始化串口,定义在u-boot-1.3.4\cpu\arm920t\s3c24x0\serial.c里
7、 console_init_f 作用是告诉编译器,笔者目前是使用串口的,
8、 display_banner 串口初始化完成,所以就可以打印信息,成功后,就可以不用点灯大法,可以使用串口输出来调试了
9、 print_cpuinfo 可选
10、 checkboard 可选,后面还有几个选的,就不说了
初始化完这些函数后,就进入其它函数
flash_init()。定义在…\board\gt2440(开发板目录,以后就不打了)\flash.c,它是负责显示你的nor flash有多大,然后用后面的函数display_flash_config显示它。当你打开它,会发现这里兼容AMD的芯片,如果你的不是AMD的就要修改flash ID号了
如何修改?先在这作简单介绍
就是在gt2440.h原本定义#define CONFIG_AMD_LV800 1屏蔽掉
然后定义自己的
#define CONFIG_SST_VF1601 1
后面再修改FLASH_BANK_SIZE、CFG_MAX_FLASH_SECT
但是由于sstnor操作是不相同的,所以就需要修改board/gt2440/flash.c
mem_malloc_init() 初始化内存,非常关键,要把这块区域清零,以便后续来使用它。
后面几个就是什么LCD、VFD之类的先不理它
到了nand_init()了,这里的话,涉及到后面讲的是使用在gt2440.c里定义的nand_init()还是uboot自带的,就要靠几个宏定义,现在先放一放,知道这里是初始化nand的就可以了。
然后是什么打印环境变量,开中断,还有一个有关网卡的函数,笔者使用的是cs8900所以直接用它自带的,如果读者的是dm9000或者其它,就应该自己写了后替换它,推荐使用宏。最后面,一系列之后,到了main_loop()。Uboot执行完成
三、 UBOOT移植
1、注意问题
了解了大概uboot的运行过程,现在开始移植,移植前,说明几个问题
由于uboot-1.3.4较于较早的版本,就是Makefile有一定的变化,使得对于24x0处理器从nand启动会有问题,就是有人说的无法运行过lowlevel_init。这个问题产生的原因是因为编译器将笔者自己添加的用于nandboot的子函数放到了4K之后(为什么说是4K,uboot从nand启动原理,前面说过),解决办法:
1、顶层Makefile文件中:
#__LIBS := $(subst $(obj),,$(LIBS)) $(subst$(obj),,$(LIBBOARD))
__LIBS := $(subst $(obj),,$(LIBBOARD)) $(subst $(obj),,$(LIBS))
这样修改之后查看map文件可以发现,lowlevel_init函数链接到了前面.
2、在不修改Makefile的情况下,可以通过修改目标板下的链接文件u-boot.lds来使lowlevel_init放在4K之内: cpu/arm920t/start.o (.text)board/net2410e/lowlevel_init.o (.text) 把之放到start.o的后面.
这相当于修改链接脚本,未做验证,笔者使用第一种方法!
3、 开始移植/*其中,蓝色为修改内容,红色为添加内容,参考《移植u-boot-1.3.4到s3c2440》*/
下载源码,网址:
U-Boot软件包下载网站:http://sourceforge.net/project/u-boot
笔者更喜欢从这里下载源码:ftp://ftp.denx.de/pub/u-boot/
1) 解压uboot
#tar –xjvf u-boot-1.3.4.tar.gz2
2) 修改Makefile
#cd u-boot-1.3.4
#vi Makefile或者直接在图形界面打开Makefile,可能这种方法更好用
修改内容如下:
__LIBS := $(subst$(obj),,$(LIBS)) $(subst $(obj),,$(LIBBOARD))
改为:
__LIBS := $(subst $(obj),,$(LIBBOARD)) $(subst$(obj),,$(LIBS))
添加开发板信息
sbc2410x_config: unconfig @$(MKCONFIG)$(@:_config=) arm arm920t sbc2410x NULL s3c24x0
gt2440_config : unconfig @$(MKCONFIG) $(@:_config=) armarm920t gt2440 NULL s3c24x0
/*
各项的意思如下:
gt2440_config : 这个名字是将来你配置板子时候用到的名字,参见make gt2440_config命令。
arm: CPU的架构(ARCH)
arm920t: CPU的类型(CPU),其对应于cpu/arm920t子目录。
gt2440: 开发板的型号(BOARD),对应于board/ gt2440目录。
NULL: 开发者/或经销商(vender)。s3c24x0: 片上系统(SOC)。
*/
3) 在/board/下建立自己的开发板目录
由于笔者开发者那填了NULL,所以直接创建,如果有填,则要再建一个子目录,因为笔者是以sbc2410x这板为基础移植的,所以还要将它的内容复制到开发板文件夹上。
[root@localhost u-boot-1.3.4]#mkdir /board/gt2440
[root@localhost u-boot-1.3.4]#cp –arfsbc2410x/* gt2440/
[root@localhost u-boot-1.3.4]#cd gt2440
[root@localhost u-boot-1.3.4]#mv sbc2410x.cgt2440.c
[root@localhost u-boot-1.3.4]#ls可以看到下面这些文件
config.mk flash.c lowlevel_init.s Makefile GT2440.c u-boot.lds
[root@localhost u-boot-1.3.4]#vi Makefile
COBJS:= gt2440.o flash.o
4) 在include/configs/中建立开发板所需要的配置头文件
[root@localhost u-boot-1.3.4]#cp../../include/configs/sbc2410x.h ../../include/configs/gt2440.h
5) 测试交叉编译能否成功
这里提醒一下,移植uboot是需要交叉编译有浮点运算功能的,安装arm-linux时,应该选用有浮点运算的,而不是最新的,因为最新的未必是最好的,uboot也是一样,新的只是移植简单了,对于交叉编译工具,笔者选用的是arm-linux-gcc-3.4.1
安装步骤:限于篇幅,详情可看笔者的一篇日志http://hi.baidu.com/guolele1990/blog/item/38789835cf1cd2d2a2cc2b5c.html
(1) 配置
[root@localhost u-boot-1.3.4]#makegt2440_config
Configure for gt2440 board
(2) 测试编译
[root@localhost u-boot-1.3.4]#make
编译信息最后两行
arm-linux-objcopy --gap-fill=0xff -O srec u-bootu-boot.srec
arm-linux-objcopy --gap-fill=0xff -O binaryu-boot u-boot.bin
6) 开始针对自己的开发板移植
1. 修改/cpu/arm920t/start.S
1.1 修改寄存器地址定义
#ifdefined(CONFIG_S3C2400)||defined(CONFIG_S3C2410)||defined(CONFIG_S3C2440)
/*turn offthe watchdog*/
#if defined(CONFIG_S3C2400)
# define pWTCON 0x15300000
# define INTMSK 0x14400008 /* Interupt-Controller base addresses */
# define CLKDIVN 0x14800014 /* clockdivisor register */
#else
# define pWTCON 0x53000000 /*该地址用来屏蔽看门狗*/
# define INTMSK 0x4A000008 /* Interupt-Controller base addresses 该地址用来屏蔽中断*/
# define INTSUBMSK 0x4A00001C /*该地址用来屏蔽子中断*/
# define CLKDIVN 0x4C000014 /* clockdivisor register 该地址用来决定FCLK、HCLK、PCLK的比例*/
#define CLK_CTL_BASE 0x4c000000 /* 从S3C2440A.pdf中可以看出该寄存器是存放Mpll和Upll的P254 */
#if defined(CONFIG_S3C2440)
#define MDIV_405 0x7f << 12 /* 参见P255表,同时要知道本开发板的Fin是12MHz,需要的Fclk(也就是Mpll)是405MHz*/
#define PSDIV_405 0x21 /* 同上,同时设定PDIV和SDIV的值,PDIV和SDIV参见S3C2440A.pdf*/
#endif
#endif
1.2 修改中断禁止部分
# ifdefined(CONFIG_S3C2410)
//ldr r1,0x3ff
ldr r1, =0x7ff //根据2410芯片手册,INTSUBMSK有11位可用,
//vivi也是0x7ff,不知为什么U-Boot一直没改过来。但是由于芯片复位默认
//所有的终端都是被屏蔽的,所以这个不影响工
ldr r0,=INTSUBMSK
str r1, [r0]
# endif
# if defined(CONFIG_S3C2440)
ldr r1, =0x7fff //根据2440芯片手册,INTSUBMSK有15位可用
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
1.3 修改时钟设置
/*时钟控制逻辑单元能够产生s3c2440需要的时钟信号,包括CPU使用的主频FCLK,AHB总线使用的HCLK,APB总线设备使用的PCLK,2440里面的两个锁相环(PLL),其中一个对应FCLK、HCLK、PCLK,另外一个对应UCLK(48MHz)*/
/* FCLK:HCLK:PCLK = 1:4:8 */
ldr r0, =CLKDIVN
mov r1, #5
str r1, [r0]
/*协处理器操作
CP15 —系统控制协处理器 (the system control coprocessor)他通过协处理器指令MCR和MRC提供具体的寄存器来配置和控制caches、MMU、保护系统、配置时钟模式(在bootloader时钟初始化用到),详情可看《ARM体系结构与编程》或者http://hi.baidu.com/guolele1990/blog/item/57a96f3d26bc33ea838b136a.html笔者的一个浅谈,也是参考那本书的
*/
mcr p15,0,r1,c1,c0,0 /*读协处理器p15 c1寄存器的值到r1*/
orr r1,r1,#0xc0000000
mcr p15,0,r1,c1,c0,0 /*把r1的内容写到c1中去,禁止MMU、禁止地址对齐检查功能、禁止caches、禁止写入缓冲、异常中断处理程序进入32位地址模式、禁止26位地址异常检查、选择时期中止模型、选择little-endian。。。。。。详情可以看《ARM体系结构与编程》的5.2节*/
#ifdefined(CONFIG_S3C2440)
/*now, CPU clock is 405.00 Mhz qljt*/
mov r1, #CLK_CTL_BASE
mov r2, #MDIV_405 /* mpll_405mhz */
add r2, r2, #PSDIV_405 /* mpll_405mhz */
str r2, [r1,#0x04] /* MPLLCON实际上是设置寄存器CLK_CTL_BASE+0x04=0x4c000004的值 */
#endif
#endif /*CONFIG_S3C2400 || CONFIG_S3C2410|| CONFIG_S3C2440 */
1.4 将Flash启动改成从NANDFlash启动(这里网上还有一个小程序,是判别现在是nor还是nand启动的,原理可参考http://blog.chinaunix.net/u1/34474/showart.php?id=2085212,
其实就是利用start.S里的魔方数以及nand和nor的写特性,然后可以同时烧进nor和nand中,主要区别在于,nor可以直接运行uboot。笔者这里没加)
将u-boot重定向语句段屏蔽
@#if ndef CONFIG_AT91RM9200
#if 0
#ifndefCONFIG_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 ofarmboot */
add r2, r0, r2 /* r2 <- source endaddress */
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 */
#endif/*#if 0*/
#endif /*CONFIG_AT91RM9200 */
然后添加(参考VIVI):
#ifdef CONFIG_S3C2440_NAND_BOOT
/*往下四段内容都是针对S3C2440的关于NAND-FLASH的寄存器的设置*/
mov r1, #NAND_CTL_BASE
ldr r2, =((7<<12)|(7<<8)|(7<<4)|(0<<0) ) /*总线宽为8
Bit,设定TWRPH1,TWRPH0(nWE高低电平持续时间)*/
str r2, [r1, #oNFCONF] /*这些宏是在include/configs/GT2440.h中被定义的*/
ldr r2, [r1, #oNFCONF]
ldr r2, =((1<<4)|(0<<1)|(1<<0) ) @ Active low CE Control
str r2, [r1, #oNFCONT]
ldr r2, [r1, #oNFCONT]
ldr r2, =(0x6) @ RnB Clear
str r2, [r1, #oNFSTAT]
ldr r2, [r1, #oNFSTAT]
mov r2, #0xff @ RESET command
strb r2, [r1, #oNFCMD]
/*delay一段时间*/
mov r3,#0 @ wait
nand1:
add r3, r3, #0x1
cmp r3, #0xa
blt nand1
/*等待nand-flash的复位完毕信号*/
nand2:
ldr r2, [r1, #oNFSTAT] @ wait ready
tst r2, #0x4
beq nand2
ldr r2, [r1, #oNFCONT]
orr r2, r2,#0x2 @ Flash Memory ChipDisable /*在这里先Display fansh CE先,在C函数中再enable*/
str r2, [r1, #oNFCONT]
/*下面这段用来初始化栈指针sp和帧指针fp,至于它们的定义和作用参考文件夹” 栈指针sp和帧指针fp”里面的内容
记住它们都是与函数调用时候相关的。简单来讲就是子函数被调用以后是通过指针的相对位置来查找调用参数和局部变量的,但是由于sp经常变化,所以需要fp来协助。*/
@ get ready to call C functions (for nand_read())
ldr sp,DW_STACK_START @ setup stack pointer /*sp 是指堆栈指针*/
mov fp,#0 @ no previous frame, so fp=0
@ copy U-Boot toRAM /*注意:现在是运行在4k 的SRAM里,现在要把nand flash的内容拷贝到SDRAM中!vivi里面应该是有一段是针对gpio的程序,也许使用来debug用的信号灯,这里省略了*/
/*TEXT_BASE 是uboot自己的入口地址,在u-boot-1.3.4-board/gt2440的config.mk中定义
有趣的是外国人的逆向思维很厉害,它们很灵活地把它放在SDRAM的最后0x80000地方,也就是0x33F80000
*/
ldr r0, =TEXT_BASE /*r0 : 把u-boot复制到ram的那个位置*/
mov r1, #0x0 /*r1 : 从falsh的那个位置开始复制*/
mov r2, #0x20000 /*r2 : 复制多大的内容*/
bl nand_read_ll /*跳到执行uboot复制的程序入口,这个函数从哪里来?自己在后面写的,也可以参考vivi的*/
tst r0, #0x0 /*这里特别注意r0的值是指nand_read_ll 执行完以后的返回值,而不是上面ldr r0, =TEXT_BASE 的值,可以查看uboot反汇编文件,跟踪到nand_read_ll可得mov r0,#0*/
beq ok_nand_read
bad_nand_read: /*如果读nand_read失败的话,那么死循环*/
loop2: b loop2 @ infinite loop
ok_nand_read:
@ verify
/*下面这段程序的作用就是用开始执行的4Kbytes程序跟笔者复制到SRAM中的uboot的前4K程序进行比较,从而校验,现在运行在SRAM里*/
mov r0, #0
ldr r1, =TEXT_BASE
mov r2, #0x400 @ 4 bytes * 1024 = 4K-bytes
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_S3C2440_NAND_BOOT
1.5 跳转到C函数之前,即跳出start.S进入第二阶段之前,加个led控制,点灯大法,具体参照读者的硬件配置
ble clbss_l
ldr pc,_start_armboot
#ifdefined(CONFIG_S3C2440)
/*LED1 onu-boot stage 1 is ok!,gt2440.h*/
mov r1, #GPIO_CTL_BASE
add r1, r1, #oGPIO_B
ldr r2,=0x3D57FC
str r2, [r1, #oGPIO_CON]
ldr r2, =0x7ff
str r2, [r1, #oGPIO_UP]
ldr r2, =0x7fce
str r2, [r1, #oGPIO_DAT]
#endif/*s3c2440*/
_start_armboot: .word start_armboot
1.6在“_start_armboot: .word start_armboot”后加入
#ifdefined(CONFIG_S3C2440_NAND_BOOT)
.align 2 /*四字节对齐*/
DW_STACK_START:.wordSTACK_BASE+STACK_SIZE-4/*这里的STACK_BASE和 STACK_SIZE 将在gt2440.h中定义*/
#endif
2. 修改include/configs/gt2440.h文件,因为参考的s3c2410与s3c2440的nandflash控制器寄存器不同,其他相同的可以不改
/*
* Nandflash Boot
*/
#define CONFIG_S3C2440_NAND_BOOT 1
#define STACK_BASE 0x33f00000
#define STACK_SIZE 0x8000
/* NAND Flash Controller */
#define NAND_CTL_BASE 0x4E000000
/* Offset */
#define oNFCONF 0x00 /*这些宏是在start.S中被调用的*/
#define oNFCONT 0x04
#define oNFCMD 0x08
#define oNFADDR 0x0c
#define oNFDATA 0x10
#define oNFSTAT 0x20
#define oNFECC 0x2c
/* GPIO */
#define GPIO_CTL_BASE 0x56000000
#define oGPIO_F 0x50
#define oGPIO_CON 0x0 /* R/W,Configures the pins of the port */
#define oGPIO_DAT 0x4 /*R/W, Data register for port */
#define oGPIO_UP 0x8 /*R/W, Pull-up disable register */
#endif /* __CONFIG_H */
3. 在board/gt2440中加入NAND FLASH读函数文件同(参考的是《GT2440之 U-boot使用及移植详细手册》,可实现不同大小的nand读操作)
/*
* nand_read.c: Simple NAND read functions forbooting from NAND
*
* This is used by cpu/arm920/start.S assemblercode,
* and the board-specific linker script must makesure this
* file is linked within the first 4kB of NANDflash.
*
* Taken from GPLv2 licensed vivi bootloader,
* Copyright (C) 2002 MIZI Research, Inc.
*
* Author: Hwang, Chideok<[email protected]>
* Date : $Date: 2004/02/04 10:37:37 $
*
* u-boot integration and bad-block skipping(C) 2006 by OpenMoko, Inc
* Author: Harald Welte<[email protected]>
*/
#include<common.h>
#include<linux/mtd/nand.h>
#define__REGb(x) (*(volatile unsigned char *)(x))
#define__REGw(x) (*(volatile unsigned short *)(x))
#define__REGi(x) (*(volatile unsigned int *)(x))
#defineNF_BASE 0x4e000000
#ifdefined(CONFIG_S3C2410)
#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)
#defineNFSTAT_BUSY 1
#definenand_select() (NFCONF &= ~0x800)
#definenand_deselect() (NFCONF |= 0x800)
#definenand_clear_RnB() do {} while (0)
#elifdefined(CONFIG_S3C2440) || defined(CONFIG_S3C2442)
#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)
#defineNFDATA16 __REGw(NF_BASE + 0x10)
#defineNFSTAT __REGb(NF_BASE + 0x20)
#defineNFSTAT_BUSY 1
#definenand_select() (NFCONT &= ~(1 << 1))
#definenand_deselect() (NFCONT |= (1 << 1))
#definenand_clear_RnB() (NFSTAT |= (1 << 2))
#endif
static inlinevoid nand_wait(void)
{
int i;
while (!(NFSTAT & NFSTAT_BUSY))
for (i=0; i<10; i++);
}
structboot_nand_t { /*用来贮存芯片的参数*/
int page_size;
int block_size;
int bad_block_offset;
// unsignedlong size;
};
#if 0
#ifdefined(CONFIG_S3C2410) || defined(CONFIG_GT2440)
/*configuration for 2410 with 512byte sized flash */
#defineNAND_PAGE_SIZE 512
#defineBAD_BLOCK_OFFSET 5
#defineNAND_BLOCK_MASK (NAND_PAGE_SIZE - 1)
#defineNAND_BLOCK_SIZE 0x4000
#else
/*configuration for 2440 with 2048byte sized flash */
#defineNAND_5_ADDR_CYCLE
#defineNAND_PAGE_SIZE 2048
#defineBAD_BLOCK_OFFSET NAND_PAGE_SIZE
#defineNAND_BLOCK_MASK (NAND_PAGE_SIZE - 1)
#defineNAND_BLOCK_SIZE (NAND_PAGE_SIZE * 64)
#endif /*defined(CONFIG_S3C2410) || defined(CONFIG_GT2440)*/
/* compile timefailure in case of an invalid configuration */
#ifdefined(CONFIG_S3C2410) && (NAND_PAGE_SIZE != 512)
#error"S3C2410 does not support nand page size != 512"
#endif /*defined(CONFIG_S3C2410) && (NAND_PAGE_SIZE != 512)*/
#endif /*IF 0*/
static intis_bad_block(struct boot_nand_t * nand, unsigned long i)
{
unsigned char data;
unsigned long page_num;
nand_clear_RnB();
/*page_size的不同主要是读的时候写地址不一样,2K的为5次,2次Colmn 3次row address
而512的是1次Colmn和3次row address具体怎么安排,可看芯片手册
还有一些宏,例如NAND_CMD_READ0定义在include/linux/mtd/nand.h里*/
if (nand->page_size == 512) {
NFCMD = NAND_CMD_READOOB; /* 0x50 */
NFADDR = nand->bad_block_offset & 0xf;
NFADDR = (i >> 9) & 0xff;
NFADDR = (i >> 17) & 0xff;
NFADDR = (i >> 25) & 0xff;
} else if (nand->page_size == 2048) {
page_num = i >> 11; /* addr / 2048 */
NFCMD = NAND_CMD_READ0;
NFADDR = nand->bad_block_offset &0xff;
NFADDR = (nand->bad_block_offset >>8) & 0xff;
NFADDR = page_num & 0xff;
NFADDR = (page_num >> 8) & 0xff;
NFADDR = (page_num >> 16) & 0xff;
NFCMD = NAND_CMD_READSTART;
} else {
return -1;
}
nand_wait();
data = (NFDATA & 0xff);
if (data != 0xff)
return 1;
return 0;
}
static intnand_read_page_ll(struct boot_nand_t * nand, unsigned char *buf, unsigned longaddr)
{
unsigned short *ptr16 = (unsigned short *)buf;
unsigned int i, page_num;
nand_clear_RnB();
NFCMD = NAND_CMD_READ0;
if (nand->page_size == 512) {
/* Write Address */
NFADDR = addr & 0xff;
NFADDR = (addr >> 9) & 0xff;
NFADDR = (addr >> 17) & 0xff;
NFADDR = (addr >> 25) & 0xff;
} else if (nand->page_size == 2048) {
page_num = addr >> 11; /* addr / 2048*/
/* Write Address */
NFADDR = 0;
NFADDR = 0;
NFADDR = page_num & 0xff;
NFADDR = (page_num >> 8) & 0xff;
NFADDR = (page_num >> 16) & 0xff;
NFCMD = NAND_CMD_READSTART; /*0x30*/
}
else
{
return -1;
}
nand_wait();
#ifdefined(CONFIG_S3C2410)
for (i = 0; i < nand->page_size; i++) {
*buf = (NFDATA & 0xff); /*8位线宽*/
buf++;
}
#elifdefined(CONFIG_S3C2440) || defined(CONFIG_S3C2442)
for (i = 0; i <(nand->page_size>>1); i++) {
*ptr16 = NFDATA16; /*16位线宽*/
ptr16++;
}
#endif /* defined(CONFIG_S3C2410)*/
return nand->page_size;
}
static unsignedshort nand_read_id()
{
unsigned short res = 0;
NFCMD = NAND_CMD_READID;
NFADDR = 0;
res = NFDATA;
res = (res << 8) | NFDATA;
return res;
}
extern unsignedint dynpart_size[];
/* low levelnand read function */
intnand_read_ll(unsigned char *buf, unsigned long start_addr, int size)
{
int i, j;
unsigned short nand_id;
struct boot_nand_t nand;
/* chip Enable */
nand_select();
nand_clear_RnB();
for (i = 0; i < 10; i++)
;
nand_id = nand_read_id();
if (0) { /* dirty little hack to detect ifnand id is misread */
unsigned short * nid = (unsigned short*)0x31fffff0;
*nid = nand_id;
}
if (nand_id == 0xec76 || /* Samsung K91208 */
nand_id == 0xad76 ) { /*HynixHY27US08121A*/
nand.page_size = 512;
nand.block_size = 16 * 1024;
nand.bad_block_offset = 5;
// nand.size = 0x4000000;
} else if (nand_id == 0xecf1 || /* SamsungK9F1G08U0B */
nand_id == 0xecda || /* Samsung K9F2G08U0B*/
nand_id == 0xecd3 ) { /* Samsung K9K8G08*/
nand.page_size = 2048;
nand.block_size = 128 * 1024;
nand.bad_block_offset = nand.page_size;
// nand.size = 0x8000000;
} else {
return -1; // hang
}
if ((start_addr & (nand.block_size-1)) ||(size & ((nand.block_size-1))))
return-1; /* invalid alignment */
for (i=start_addr; i < (start_addr +size);) {
#ifndefCONFIG_S3C2410_NAND_SKIP_BAD /*不检查坏块*/
if (i & (nand.block_size-1)== 0) {
if (is_bad_block(&nand, i) ||
is_bad_block(&nand, i +nand.page_size)) {
/* Bad block */
i += nand.block_size;
size += nand.block_size;
continue;
}
}
#endif
j = nand_read_page_ll(&nand, buf, i);
i += j;
buf += j;
}
/* chip Disable */
nand_deselect();
return 0;
}
/*===========================================================
到这里,应该是可以编译通过的,否则就是编辑的时候出现了错误
===========================================================*/
4. 修改board/gt2440/lowlevel_init.S文件
由于这个是底层的初始化文件,所以要根据读者的开发板情况来配置或者修改/board/gt2440/lowlevel_init.S文件
/* REFRESH parameter 下面这6个配置都可以参考s3c2440A datasheet P210的REFRESH寄存器 */
#define REFEN 0x1 /* Refresh enable */
#define TREFMD 0x0 /* CBR(CAS before RAS)/Auto refresh */
#define Trp 0x01 /* 3clk 这个值可以参考本版子上的SDRAM的datasheet*/
#defineTrc 0x3 /* 也就是SDRAM datasheet里面的Tsrc 7clk 本来这个地方是Trc,但从lowlevel_init.S里面的调用来看,应该是s3c2440的寄存器REFRESH的Tsrc才对,好多地方都没有改过来,只是个名字而已,不影响结果
注意:如果这里改了,那么下面这句中的Trc也要改为相应的
Tsrc:
.word((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)*/
#define Tchr 0x2 /* 3clk,这个从lowlevel_init.S里面的调用来看是属于REFRESH的保留位,不知道为什么还要给他赋值*/
#defineREFCNT 1259 /*这个值的算法参考s3c2440A datasheet P210的Refresh Counter
Refresh count = 2^11 + 1- HCLK * period */
/*下面不厌其烦地解析一下lowlevel_init.S这个原文件*/
#define BWSCON 0x48000000
……
#define Tchr 0x2 /* 3clk */
#define REFCNT 0x0459
/**************************************/
/*1.要知道上面这些配置的最终会被用到下面SMRDATA 这个数据池里面,所以必须要明白SMRDATA 这个数据池是用
来干什么的,SMRDATA 后面每一个.word 后面放置的数据都是将要写入BWSCON 开始的寄存器的,总共有13个.word ,它们后面放置的值将会分别写入0x48000000、0x48000004、0x48000008…一直到0x48000030共13个寄存器。 */
/*2.上面那些配置的值是怎样决定的呢,详细请参考s3c2440A和你所用SDRAM的datasheet。细心找总是能找到的。*/
/*3.而上面的那些配置值最终是通过下面lowlevel_init后面的这段函数写到寄存器里面的,下面对该段函数逐一分析:*/
_TEXT_BASE:
.word TEXT_BASE
.globl lowlevel_init
lowlevel_init:
/* memory controlconfiguration */
/* make r0 relative thecurrent location so that it */
/* reads SMRDATA out of FLASHrather than memory ! */
ldr r0, =SMRDATA
ldr r1, _TEXT_BASE
sub r0, r0, r1 /*其实明白了前三条语句这段程序就不难懂了,归根到底就是为什么将SMRDATA 的值减去_TEXT_BASE的值?
原因如下:
当笔者编译出uboot的时候,编译器会给uboot这程序定一个初始地址作为基地址,其它就以它为基准,这个地址就是在config.mk里指定的TEXT_BASE,笔者的定为0x33f80000。
笔者使用的是从nandflashboot的方式,目前程序仍然在4K-bytes ‘Steppingstone’(即开始的4K SRAM)上面运行。
现在代码还没有搬运到内存中,什么时候搬?就是下一段代码要做的。所以如果调用这个label的话,找不到数据的,因为那个位置没东西,那数据在哪?在flash中!!那怎么找?其实flash是安排在0x0地址中的,所以真正的数据也是在相对于0x0的位置,那怎么找?就是用你相对于text_base的位置减去text_base的不就可以吗?事实笔者也是这样做的。*/
ldr r1, =BWSCON /* Bus Width StatusController */
add r2, r0, #13*4 /*总共13个寄存器*/
0:
ldr r3, [r0], #4
str r3, [r1], #4
cmp r2, r0
bne 0b
/* everything is fine now */
mov pc, lr
.ltorg /*数据缓冲池,上网可以查得资料*/
/* the literal pools origin */
SMRDATA:
……
.word((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)
.word 0xb2
.word 0x30 /*需要注意的是CASLatency的值在这里直接配置*/
.word 0x30
/***********************************************************
到这里,应该是可以编译通过的,否则就是编辑的时候出现了错误
************************************************************/
5. 修改board/gt2440/gt2440.c
这个文件主要板级初始化的任务(如:GPIO初始化,lcd、网卡初始化类,PLL),修改的地方主要包括:修改PLL、增加LCD初始化函数(暂时没加)、修改GPIO设置、如果网上不是CS8900还要添加网卡芯片的初始化函数。
这里主要修改GPIO设置和PLL设置
#elif FCLK_SPEED==1 /* Fout =405MHz */
//#define M_MDIV 0x5c
//#define M_PDIV 0x4
//#define M_SDIV 0x0
#define M_MDIV 0x7f
#define M_PDIV 0x2
#define M_SDIV 0x1
#elif USB_CLOCK==1
//#define U_M_MDIV 0x48
//#define U_M_PDIV 0x3
#define U_M_MDIV 0x38
#define U_M_PDIV 0x2
#define U_M_SDIV 0x2
/*对于PLL时钟问题,笔者把参考《ARM79出口-u-boot移植手册》,将UPLLCON的配置和MPLLCON的配置顺序颠倒一下(不颠倒也是可以工作的,但是S3c2440 datasheet文档中明确规定,笔者就按它说的去做吧)*/
/* configure UPLL */
clk_power->UPLLCON = ((U_M_MDIV <<12) + (U_M_PDIV << 4) +
U_M_SDIV);
/* some delaybetween MPLL and UPLL */
delay(0xffff);
delay(0xffff);
delay(0xffff);
/*configure MPLL */
clk_power->MPLLCON = ((M_MDIV << 12
/*some delay between MPLL and UPLL */
delay(0xffff);
delay(0xffff);
delay(0xffff);
......
/* set up the I/O ports */
gpio->GPACON = 0x007FFFFF;
// gpio->GPBCON = 0x00044556;
gpio->GPBCON =0x3D57FC; /*for LED*/
......
/* arch number of S3C2440-Board */
gd->bd->bi_arch_number = MACH_TYPE_S3C2440;
/* adress of boot parameters */
gd->bd->bi_boot_params =0x30000100;
icache_enable();
dcache_enable();
gpio->GPBDAT = 0x7f8e; /*for LED*/
//int board_init (void)设置完成后,LED1和LED2会亮起!
return 0;
}
这里说明一下,gt2440.c完成的板级初始化函数在哪里调用。其实它是被定义在
init_fnc_t *init_sequence[]
这个初始化函数指针集里的,很熟悉吧?就是在前面介绍流程board.c里面,它从汇编跳到这执行start_armboot()函数,在函数里一一执行。
/*===========================================================
到这里,应该是可以编译通过的,否则就是编辑的时候出现了错误
===========================================================*/
6. 实现NAND flash 的读写,再次修改/include/configs/gt2440.h
这里主要是定义了两个宏和屏蔽了原来的宏,修改
/*
* High Level Configuration Options
* (easy to change)
*/
#defineCONFIG_ARM920T 1 /* This isan ARM920T Core */
//#define CONFIG_S3C2410 1 /*in a SAMSUNG S3C2410 SoC */
//#define CONFIG_SBC2410X 1 /* on a friendly-arm SBC-2410X Board */
#define CONFIG_S3C2440 1 /* 在前面很多地方调用到CONFIG_S3C2440 ,他是在这里定义 */
#define CONFIG_GT2440 1 /* 针对一些本开发板配置的宏控制*/
......
/***********************************************************
* Command definition
***********************************************************/
#define CONFIG_CMD_DHCP
#define CONFIG_CMD_ELF
#define CONFIG_CMD_PING
#define CONFIG_CMD_NAND /*这个宏和CFG_CMD_LEGACY控制调用nand_init()的地方(是在nand.c还是gt2440.c)问题*/
#define CONFIG_CMD_NET /*在board.c里得到调用,判断是否使用net*/
#define CONFIG_CMD_ENV /*环境变量相关的宏*/
#define CFG_LONGHELP
/* undef to save memory */
#define CFG_PROMPT "[GT2440]#" /*这个就 是你启动开发板后命令行显示的内容了*/
/*Monitor Command Prompt */
#define CFG_CBSIZE 256
/* Console I/O Buffer Size */
......
#define CFG_LOAD_ADDR 0x30008000 /*以后linux kernel就要放在这里执行 */
/* default load address */
......
//#define CFG_ENV_IS_IN_FLASH 1 /这里的flash应该是指nor了/
#define CFG_ENV_IS_IN_NAND 1 /*定义这个宏的目的是为了调用nand flash类型的saveenv,因为还有其它类型存储器的saveenv,在u-boot中查看saveenv的定义,有多少中定义就有多少种*/
/*在linux对nand flash分区的时候,给u-boot分配384k的空间(0~0x60000)
其中 u-boot.bin [0x0~0x40000] 占256K
而 u-boot的参数 [0x40000~0x60000] 占128k
*/
#defineCFG_ENV_OFFSET 0x40000 /*这个宏是表示环境变量在nandflash里的偏移
这里要注意,这个宏必须是nand flash block size的整数倍,例如这里我的是64*2048 = 0x20000,所以这宏必须为这个的整数倍,否则操作是会提示:N AND 256MiB 3,3V 8-bit: MTD Erase failure: -22 */
#define CFG_ENV_SIZE 0x20000 /*环境变量的长度*/
/*注意:网上很多地方都有关于CONFIG_CMD_NAND 、CFG_NAND_LEGACY、drivers/mtd/nand/nand.c中的nand_init()函数以及board/gt2440/GT2440.c中的nand_init()函数这四个东西的关系,他们就是一些宏,在不同的文件中有宏关系,从而实现调用不同的nand_init(),有兴趣可以跟踪一下,附录有他们调用的关系,这里笔者定义了CFG_NAND_LEGACY和CONFIG_CMD_NAND,所以调用的是gt2440.c的nand_init()函数,现在有没有?当然没有,要自己写,下一步骤就是修改这个*/
/*----------------------------------------------------------------------
* NAND flash settings
*/
#if defined (CONFIG_CMD_NAND)
#define CFG_NAND_BASE 0x4E000000 /*这个宏在drivers/mtd/nand/nand.c中被调用,它是NAND控制寄存器的基地址*/
/* NandFlash控制器在SFR区起始寄存器地址 */
#define CFG_MAX_NAND_DEVICE 1
#define LARGE_NAND_FLASH 1 //原因是笔者这使用的是大容量nand为2K每页
#ifndef LARGE_NAND_FLASH
/* 支持的最大Nand Flash数据 */
#define SECTORSIZE 512
/* 1页的大小 */
#define NAND_SECTOR_SIZE SECTORSIZE
#define NAND_BLOCK_MASK (SECTORSIZE-1) /*本flash一个block的大小-1*/
/* 页掩码 */
#defineADDR_COLUMN 1 /*意思是你所用的nandflash的Column地址占多少个字节*/
/* 一个字节的Column地址 */
#define ADDR_PAGE 3 /*意思是你所用的nandflash的(row)page地址占多少个字节*/
/* 3字节的页块地址!!!!!*/
#define ADDR_COLUMN_PAGE 4 /*意思是你所用的nandflash的column地址+page地址共占多少个字节*/
/* 总共4字节的页块地址!!!!! */
#else
/* 支持的最大Nand Flash数据 */
#defineSECTORSIZE 2048
/* 1页的大小 */
#define NAND_SECTOR_SIZE SECTORSIZE
#define NAND_BLOCK_MASK (SECTORSIZE-1) /*本flash一个block的大小-1*/
/* 页掩码 */
#define ADDR_COLUMN 2
/* 一个字节的Column地址 */
#define ADDR_PAGE 3
#define ADDR_COLUMN_PAGE 5
#endif
#defineNAND_ChipID_UNKNOWN 0x00
/* 未知芯片的ID号 */
#define NAND_MAX_FLOORS 1 /*怎样算一floor*/
#define NAND_MAX_CHIPS 1
/* Nand Flash命令层底层接口函数 */
#define rNFCONF (*(volatile unsigned int *)0x4e000000)
#define rNFCONT(*(volatile unsigned int *)0x4e000004)
#define rNFCMD(*(volatile unsigned char *)0x4e000008)
#define rNFADDR (*(volatile unsigned char *)0x4e00000c)
#define rNFDATA (*(volatile unsigned char *)0x4e000010)
#define rNFSTAT (*(volatile unsigned int *)0x4e000020)
#define rNFECC (*(volatile unsigned int *)0x4e00002c)
/*下面部分内容是修改的*/
/* Nand Flash命令层底层接口函数 ,函数哪里来?自己定义呗?所以下一步骤还要写这些函数*/
/*
#define NAND_WAIT_READY(nand) NF_WaitRB()
#define NAND_DISABLE_CE(nand) NF_SetCE(NFCE_HIGH)
#define NAND_ENABLE_CE(nand) NF_SetCE(NFCE_LOW)
#define WRITE_NAND_COMMAND(d, adr) NF_Cmd(d)
#define WRITE_NAND_COMMANDW(d, adr) NF_CmdW(d)
#define WRITE_NAND_ADDRESS(d, adr) NF_Addr(d)
#define WRITE_NAND(d, adr) NF_Write(d)
#define READ_NAND(adr) NF_Read()
*/ /*由于我们这次的移植是使用cpu/arm920t/s3c24x0/nand.c,这文件里也有定义需要的函数,不过是以NFADDR这种格式出现的,不过宏没多大关系,这样移植性也好点*/
#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 WRITE_NAND_COMMAND(d, adr) {rNFCMD = d;}
#define WRITE_NAND_COMMANDW(d, adr) NF_CmdW(d)
# if defined(CONFIG_S3C2440)
#define NAND_DISABLE_CE(nand) {rNFCONT |= (1<<1);}
#define NAND_ENABLE_CE(nand) {rNFCONT &= ~(1<<1);}
#endif
# if defined(CONFIG_S3C2410)
#define NAND_DISABLE_CE(nand) {rNFCONF |= (1<<11);}
#define NAND_ENABLE_CE(nand) {rNFCONF &= ~(1<<11);}
#endif
/* 允许Nand Flash写校验打开下面宏定义*/
#defineCONFIG_MTD_NAND_VERIFY_WRITE 1
......
#endif /* __CONFIG_H */
7. 修改cpu/arm920t/s3c24x0/Nand.c
#define NF_BASE 0x4e000000
#ifdefined(CONFIG_S3C2410)
#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)
#defineNFECC0 __REGb(NF_BASE + 0x14)
#defineNFECC1 __REGb(NF_BASE + 0x15)
#defineNFECC2 __REGb(NF_BASE + 0x16)
#define S3C2410_NFCONF_EN (1<<15)
#defineS3C2410_NFCONF_512BYTE (1<<14)
#defineS3C2410_NFCONF_4STEP (1<<13)
#defineS3C2410_NFCONF_INITECC (1<<12)
#defineS3C2410_NFCONF_nFCE (1<<11)
#defineS3C2410_NFCONF_TACLS(x) ((x)<<8)
#define S3C2410_NFCONF_TWRPH0(x) ((x)<<4)
#defineS3C2410_NFCONF_TWRPH1(x) ((x)<<0)
#elifdefined(CONFIG_S3C2440)
#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)
#defineNFMECCD0 __REGi(NF_BASE + 0x14)
#defineNFMECCD1 __REGi(NF_BASE + 0x18)
#defineNFSECCD __REGi(NF_BASE + 0x1C)
#defineNFSTAT __REGb(NF_BASE + 0x20)
#defineNFSTAT0 __REGi(NF_BASE + 0x24)
#define NFSTAT1 __REGi(NF_BASE + 0x28)
#defineNFMECC0 __REGi(NF_BASE + 0x2C)
#defineNFMECC1 __REGi(NF_BASE + 0x30)
#defineNFSECC __REGi(NF_BASE + 0x34)
#defineNFSBLK __REGi(NF_BASE + 0x38)
#defineNFEBLK __REGi(NF_BASE + 0x3c)
#defineS3C2440_NFCONT_nCE (1<<1)
#defineS3C2440_ADDR_NALE 0x0c
#defineS3C2440_ADDR_NCLE 0x08
#endif
static void s3c2410_hwcontrol(struct mtd_info *mtd, int cmd)
{
struct nand_chip *chip =mtd->priv;
DEBUGN("hwcontrol():0x%02x: ", cmd);
#if defined(CONFIG_S3C2410)
switch (cmd) {
case NAND_CTL_SETNCE:
NFCONF &=~S3C2410_NFCONF_nFCE;
DEBUGN("NFCONF=0x%08x\n", NFCONF);
break;
case NAND_CTL_CLRNCE:
NFCONF |= S3C2410_NFCONF_nFCE;
DEBUGN("NFCONF=0x%08x\n", NFCONF);
break;
case NAND_CTL_SETALE:
chip->IO_ADDR_W = NF_BASE +0x8;
DEBUGN("SETALE\n");
break;
case NAND_CTL_SETCLE:
chip->IO_ADDR_W = NF_BASE +0x4;
DEBUGN("SETCLE\n");
break;
default:
chip->IO_ADDR_W = NF_BASE +0xc;
break;
}
#elif defined(CONFIG_S3C2440)
switch (cmd) {
case NAND_CTL_SETNCE:
NFCONF &=~S3C2440_NFCONT_nCE;
DEBUGN("NFCONF=0x%08x\n", NFCONF);
break;
case NAND_CTL_CLRNCE:
NFCONF |= S3C2440_NFCONT_nCE;
DEBUGN("NFCONF=0x%08x\n", NFCONF);
break;
case NAND_CTL_SETALE:
chip->IO_ADDR_W = NF_BASE +S3C2440_ADDR_NALE;
DEBUGN("SETALE\n");
break;
case NAND_CTL_SETCLE:
chip->IO_ADDR_W = NF_BASE +S3C2440_ADDR_NCLE;
DEBUGN("SETCLE\n");
break;
default:
chip->IO_ADDR_W = NF_BASE +0x10; //注意是0x10
break;
}
#endif
return;
}
intboard_nand_init(struct nand_chip *nand)
{
u_int32_t cfg;
u_int8_t tacls, twrph0, twrph1;
S3C24X0_CLOCK_POWER * const clk_power =S3C24X0_GetBase_CLOCK_POWER();
DEBUGN("board_nand_init()\n");
clk_power->CLKCON |= (1 << 4);
#if defined(CONFIG_S3C2410)
/* initialize hardware */
twrph0 = 3; twrph1 = 0; tacls = 0;
cfg = S3C2410_NFCONF_EN;
cfg |= S3C2410_NFCONF_TACLS(tacls- 1);
cfg |=S3C2410_NFCONF_TWRPH0(twrph0 - 1);
cfg |=S3C2410_NFCONF_TWRPH1(twrph1 - 1);
NFCONF = cfg;
/* initialize nand_chip datastructure */
nand->IO_ADDR_R = nand->IO_ADDR_W= 0x4e00000c;
/* read_buf and write_buf aredefault */
/* read_byte and write_byte aredefault */
/* hwcontrol always must beimplemented */
nand->hwcontrol =s3c2410_hwcontrol;
nand->dev_ready =s3c2410_dev_ready;
#ifdef CONFIG_S3C2410_NAND_HWECC
nand->enable_hwecc =s3c2410_nand_enable_hwecc;
nand->calculate_ecc =s3c2410_nand_calculate_ecc;
nand->correct_data =s3c2410_nand_correct_data;
nand->eccmode =NAND_ECC_HW3_512;
#else
nand->eccmode = NAND_ECC_SOFT;
//nand->eccmode = NAND_ECC_NONE; /*这个ECC先去掉,否则你使用nand write命令和nand read会boot 不起内核*/
#endif
#ifdef CONFIG_S3C2410_NAND_BBT
nand->options =NAND_USE_FLASH_BBT;
#else
nand->options = 0;
#endif
#elif defined(CONFIG_S3C2440)
twrph0 = 6; twrph1 = 2; tacls =0;
cfg = (tacls<<12)|(twrph0<<8)|(twrph1<<4);
NFCONF = cfg;
cfg =(1<<6)|(1<<4)|(0<<1)|(1<<0);
NFCONT = cfg;
/* initialize nand_chip data structure */
nand->IO_ADDR_R =nand->IO_ADDR_W = (void *)0x4e000010;
/* read_buf and write_buf aredefault */
/* read_byte and write_byte aredefault */
/* hwcontrol always must beimplemented */
nand->hwcontrol =s3c2410_hwcontrol;
nand->dev_ready =s3c2410_dev_ready;
#ifdef CONFIG_S3C2440_NAND_HWECC
nand->enable_hwecc =s3c2410_nand_enable_hwecc;
nand->calculate_ecc = s3c2410_nand_calculate_ecc;
nand->correct_data =s3c2410_nand_correct_data;
nand->eccmode =NAND_ECC_HW3_512;
#else
nand->eccmode = NAND_ECC_SOFT;
//nand->eccmode = NAND_ECC_NONE; /*这个ECC先去掉,否则你使用nand write命令和nand read会boot 不起内核*/
#endif
#ifdef CONFIG_S3C2440_NAND_BBT
nand->options =NAND_USE_FLASH_BBT;
#else
nand->options = 0;
#endif
#endif
DEBUGN("end of nand_init\n");
return 0;
}
#else
error "U-Boot legacy NAND support notavailable for S3C2410"
#endif
#endif
/*到这里,编译是不能通过的,原因上一节中CONFIG_S3C2410这个宏定义被注释掉,下面要用CONFIG_S3C2440这个宏打开CONFIG_S3C2410所打开的内容*/
8. 在s3c2440与s3c2410能够的文件中添加“CONFIG_S3C2440”,使得原来s3c2410的代码可以编译进来
(1)/include/common.h文件的第492行:/*一些公用的常用函数,例如get_fclk()*/
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410) ||defined(CONFIG_LH7A40X) || defined(CONFIG_S3C2440)
(2)/include/s3c24x0.h:文件的第85、95、99、110、148、404行:/*一些关于S3C2440寄存器的结构体*/
#if defined(CONFIG_S3C2410) || defined (CONFIG_S3C2440)
(3)/cpu/arm920t/s3c24x0/interrupts.c文件的第33行:/*主要把一些头文件包含进去*/
#if defined(CONFIG_S3C2400) || defined (CONFIG_S3C2410) || defined(CONFIG_TRAB) ||defined (CONFIG_S3C2440)
第38行:
#elif defined(CONFIG_S3C2410) || defined (CONFIG_S3C2440)
(4)/cpu/arm920t/s3c24x0/serial.c文件的第22行:/*主要把一些头文件包含进去*/
#if defined(CONFIG_S3C2400) || defined (CONFIG_S3C2410) || defined (CONFIG_TRAB) || defined(CONFIG_S3C2440)
第26行:
#elif defined(CONFIG_S3C2410) || defined (CONFIG_S3C2440)
(5)/cpu/arm920t/s3c24x0/speed.c文件的第33行:
#if defined(CONFIG_S3C2400) || defined (CONFIG_S3C2410) || defined(CONFIG_TRAB) || defined (CONFIG_S3C2440)
第37行:
#elif defined(CONFIG_S3C2410) || defined (CONFIG_S3C2440)
顺便修改源代码,以匹配s3c2440:
static ulong get_PLLCLK(int pllreg)
{
......
m = ((r & 0xFF000) >> 12) + 8;
p = ((r & 0x003F0) >> 4) + 2;
s = r & 0x3;
/*这两个PLL的算法参见S3C2440datasheet的254页*/
#if defined(CONFIG_S3C2440)
if (pllreg == MPLL)
return((CONFIG_SYS_CLK_FREQ * m * 2) / (p << s)); /*CONFIG_SYS_CLK_FREQ 在qljt2440.h中定义*/
else if (pllreg == UPLL)
#endif
return((CONFIG_SYS_CLK_FREQ * m) / (p << s));
}
......
/* return FCLK frequency */
ulong get_FCLK(void)
{
return(get_PLLCLK(MPLL));
}
/* return HCLK frequency */
ulong get_HCLK(void)
{
S3C24X0_CLOCK_POWER * const clk_power =S3C24X0_GetBase_CLOCK_POWER();
/*看看s3c2410与s3c2440的datasheet就知道s3c2440的HCLK可选择的值多很*/
if (clk_power->CLKDIVN & 0x6)
{
if ((clk_power->CLKDIVN &0x6)==2) return(get_FCLK()/2);
if ((clk_power->CLKDIVN &0x6)==6) return((clk_power->CAMDIVN & 0x100) ? get_FCLK()/6 :get_FCLK()/3); /*注意这里的CAMDIVN还没有被定义,在/include/s3c24x0.h中定义 所以下一个大步骤就去定义*/
if ((clk_power->CLKDIVN &0x6)==4) return((clk_power->CAMDIVN & 0x200) ? get_FCLK()/8 :get_FCLK()/4);
return(get_FCLK());
}
else {
return(get_FCLK());
}
// return((clk_power->CLKDIVN & 0x2) ?get_FCLK()/2 : get_FCLK());
}
......
(6)/cpu/arm920t/s3c24x0/usb_ohci.c文件的第45行:
#elif defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)
(7)drivers/rtc/s3c24x0_rtc.c文件的第35行:
#elif defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)
(8)在文件中添加“defined(CONFIG_GT2440)”,使得原来SBC2410X开发板的代码可以编译进来,
/cpu/arm920t/s3c24x0/interrupts.c文件的第181行:
#elif defined(CONFIG_SBC2410X) || \
defined(CONFIG_SMDK2410) || \
defined(CONFIG_VCMA9) || defined(CONFIG_GT2440)
tbclk = CFG_HZ; /*对于CFG_HZ 的值,结合uboot的说明和s3c2440datasheet就比较容易理解*/
#else
(9)/cpu/arm920t/s3c24x0/usb.c文件的第31行:
#elif defined(CONFIG_S3C2410) ||defined (CONFIG_S3C2440)
(10)/cpu/arm920t/s3c24x0/i2c.c文件的第35行:
#elif defined(CONFIG_S3C2410) ||defined(CONFIG_S3C2440)
第66、85、142、150、174行:
将“#ifdef CONFIG_S3C2410”改为
#if defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)
(11)drivers/usb/usb_ohci.c文件的第68行附近:
#if defined(CONFIG_ARM920T) || \
defined(CONFIG_S3C2400) || \
defined(CONFIG_S3C2410)|| \
defined(CONFIG_S3C2440)|| \
defined(CONFIG_440EP)|| \
defined(CONFIG_PCI_OHCI) || \
defined(CONFIG_MPC5200)
9. 在/include/s3c24x0.h中加入2440 的NAND FLASH 寄存器定义和CAMDIVN定义:
typedefstruct {
S3C24X0_REG32 LOCKTIME;
S3C24X0_REG32 MPLLCON;
S3C24X0_REG32 UPLLCON;
S3C24X0_REG32 CLKCON;
S3C24X0_REG32 CLKSLOW;
S3C24X0_REG32 CLKDIVN;
S3C24X0_REG32 CAMDIVN;
}S3C24X0_CLOCK_POWER;
......
#ifdefined(CONFIG_S3C2410) //2410 的NAND FLASH 寄存器
typedefstruct {
S3C24X0_REG32 NFCONF;
S3C24X0_REG32 NFCMD;
S3C24X0_REG32 NFADDR;
S3C24X0_REG32 NFDATA;
S3C24X0_REG32 NFSTAT;
S3C24X0_REG32 NFECC;
}S3C2410_NAND;
#endif
#if defined(CONFIG_S3C2440)
typedef struct{
S3C24X0_REG32 NFCONF;
S3C24X0_REG32 NFCONT;
S3C24X0_REG32 NFCMD;
S3C24X0_REG32 NFADDR;
S3C24X0_REG32 NFDATA;
S3C24X0_REG32 NFMECC0;
S3C24X0_REG32 NFMECC1;
S3C24X0_REG32 NFSECC;
S3C24X0_REG32 NFSTAT;
S3C24X0_REG32 NFESTAT0;
S3C24X0_REG32 NFESTAT1;
S3C24X0_REG32 NFECC;
} S3C2410_NAND;
#endif
10. 修改/lib_arm中的board.c。
#include<common.h>
#include <command.h>
#include <malloc.h>
#include <devices.h>
#include <version.h>
#include <net.h>
#include <s3c2410.h>
/*===========================================================
到这里,应该是可以编译通过的,否则就是编辑的时候出现了错误
===========================================================*/
11. 为了在uboot里显示你现在的nand的大小,修改include/linux/mtd/nand_ids.h的结构体nand_flash_ids /*这个结构体是为了在初始化nand时,用读到的id和这里面匹配,如果有就输出该nand的信息*/
注意:根据朋友小子 提醒,其实不用添加这一步的,而是在drivers/mtd/nand/nand_ids.h里已经有定义了,可以去看一下,这里也不删这段了,就当是告诉这结构体每一位的作用
static struct nand_flash_dev nand_flash_ids[] = {
....../*结构体nand_flash_dev 在doc2000.h中定义*/
/*厂家 型号,生产商编号,本模块的编号,总共容纳地址的位数,存储页字节数是否为256 ,地址需要多少字节数减一(行列地址总共) ,擦除1个block的大小,是否为16位总线*/
{"Samsung KM29N16000",NAND_MFR_SAMSUNG, 0x64, 21,1, 2, 0x1000, 0},
{"Samsung K9F2G08U0B", NAND_MFR_SAMSUNG, 0xDA, 28, 0, 4, 0x10000, 0},
{"Samsung unknown 4Mb", NAND_MFR_SAMSUNG, 0x6b, 22, 0, 2, 0x2000, 0},
......
};
其中"SamsungK9F2G08U0B"是nand的名字,无关紧要
NAND_MFR_SAMSUNG是该nand的makercode 三星的是0xec
0xDA是本模块的编号,也就是device code,笔者的为0xda
28是你的芯片总共容纳的地址位数,也就是有效的地址位数,这个地方控制你显示的大小的。
0, 即是否为256字节一页,笔者的为2048一页,所以为0
0x10000为擦除1个block的大小,简单来说就是一个block的大小,本flash为1block= 64*1page = 64*2048 = 0x20000
1为是否16位总线,笔者的为8位
12. 修改lib_arm中的board.c添加几个debug信息,即点几个灯
#include<common.h>
#include <command.h>
#include <malloc.h>
#include <devices.h>
#include <version.h>
#include <net.h>
......
static intdisplay_banner (void)
{
S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();
gpio->GPFBDAT =0x7f8e;
//在串口初始化和console初始化完成,串口输出信息之前,LED1、LED2、LED3会亮起!
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, _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);
}
......
voidstart_armboot (void)
{
init_fnc_t **init_fnc_ptr;
char *s;
#ifndefCFG_NO_FLASH
ulong size;
#endif
#ifdefined(CONFIG_VFD) || defined(CONFIG_LCD)
unsigned long addr;
#endif
S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();
......
gpio->GPFDAT = 0x7f0e;
//在进入命令提示符之前,四个LED会同时亮起!
/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;) {
main_loop ();
}
/* NOTREACHED - no way out of command loop except booting */
}
/*到这里,应该是可以编译通过的,否则就是编辑的时候出现了错误*/
15. 修改nor flash写入功能的代码,因为AMD与SST写时序有差别,所以要修改
在board/gt2440/flash.c里
//#define CMD_ERASE_CONFIRM 0x00000030
#define CMD_ERASE_CONFIRM 0x00000050
#if defined(CONFIG_SST_VF1601)
#define MEM_FLASH_ADDR1 (*(volatileu16 *)(CFG_FLASH_BASE + (0x00005555 << 1)))
#define MEM_FLASH_ADDR2 (*(volatileu16 *)(CFG_FLASH_BASE + (0x00002AAA << 1)))
#else
#define MEM_FLASH_ADDR1 (*(volatileu16 *)(CFG_FLASH_BASE + (0x00000555 << 1)))
#define MEM_FLASH_ADDR2 (*(volatileu16 *)(CFG_FLASH_BASE + (0x000002AA << 1)))
#endif
#elif defined(CONFIG_AMD_LV800)
(AMD_MANUFACT& FLASH_VENDMASK) |
(AMD_ID_LV800B& FLASH_TYPEMASK);
#elif defined(CONFIG_SST_VF1601)
(SST_MANUFACT& FLASH_VENDMASK) |
(SST_ID_xF1601& FLASH_TYPEMASK);
#else
#error "Unknown flash configured"
#ifndef CONFIG_SST_VF1601
……
flash_info[i].start[j] =
flashbase+ (j - 3) * MAIN_SECT_SIZE;
}
#else
flash_info[i].start[j]=
flashbase+ (j) * MAIN_SECT_SIZE;
#endif
void flash_print_info (flash_info_t * info)
case (SST_MANUFACT & FLASH_VENDMASK):
printf("SST:");
break;
……
case (SST_ID_xF1601 & FLASH_TYPEMASK):
printf("1xSST39VF1601(2MB)\n");
break;
int flash_erase (flash_info_t * info, ints_first, int s_last)
……
#ifdef CONFIG_SST_VF1601
if((info->flash_id & FLASH_VENDMASK) !=
(SST_MANUFACT& FLASH_VENDMASK)) {
returnERR_UNKNOWN_FLASH_VENDOR;
}
#else
if ((info->flash_id & FLASH_VENDMASK)!=
(AMD_MANUFACT & FLASH_VENDMASK)) {
returnERR_UNKNOWN_FLASH_VENDOR;
}
#endif
int flash_erase (flash_info_t * info, ints_first, int s_last)
#if 0
/*wait until flash is ready */
chip= 0;
do{
result= *addr;
/*check timeout */
if(get_timer_masked () >
CFG_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;
}
printf("ok.\n");
}else { /* it was protected */
printf("protected!\n");
}
}
#endif
/**********************************************/
/*wait until flash is ready*/
while(1){
unsignedshort i;
i= *((volatile unsigned short *)addr)& 0x40;
if(i!= (*((volatile unsigned short *)addr) & 0x40))
continue;
if((*((volatileunsigned short *)addr)) & 0x80)
break;
}
printf("ok.\n");
}else { /* it was protected */
printf ("protected!\n");
}
}
static int write_hword (flash_info_t * info, ulong dest,ushort data)
……
// MEM_FLASH_ADDR1= CMD_UNLOCK_BYPASS;
// *addr= CMD_PROGRAM;
MEM_FLASH_ADDR1 = CMD_PROGRAM;
#if 0
/* wait until flash is ready */
chip= 0;
do{
……
*addr= CMD_READ_ARRAY;
if(chip == ERR || *addr != data)
rc= ERR_PROG_ERROR;
#endif
/*waituntil flash is ready*/
while(1){
unsignedshort i = *(volatile unsigned short *)addr & 0x40;
if(i !=(*(volatile unsigned short *)addr & 0x40)) //D6 == D6
continue;
if((*(volatile unsigned short *)addr & 0x80) == (data & 0x80)){
rc = ERR_OK;
break; //D7 == D7
}
在include/configs/gt2440.h加上:
#define CONFIG_SST_VF1601 1
#ifdef CONFIG_AMD_LV400
#define PHYS_FLASH_SIZE 0x00080000/* 512KB */
#define CFG_MAX_FLASH_SECT (11) /* max number ofsectors on one chip */
#define CFG_ENV_ADDR (CFG_FLASH_BASE+ 0x070000) /* addr of environment */
#endif
#ifdef CONFIG_SST_VF1601
#define PHYS_FLASH_SIZE 0x00200000/* 2M */
#define CFG_MAX_FLASH_SECT (512) /* max number ofsectors on one chip */
#define CFG_ENV_ADDR (CFG_NAND_BASE + CFG_ENV_OFFSET) /* addrof environment */
#endif
13、支持yaffs根文件系统的烧写(可选)
因为在开发过程中,烧写yaffs根文件系统还是有需要的,但是1.3.4又不支持yaffs的烧写,所以就要手动移植。Yaffs根文件系统主要特点是烧写时第一块是不烧内容的,也就跳过第一块,所以在移植时会在相应的结构体里加入skipfirstblk这个成员。
1.首先在common/cmd_nand.c的496行附近加入
"nandread.yaffs - addr off|partitionsize\n"
"nandwrite.yaffs - addr off|partition size- read/write `size' bytes starting\n"
这两行主要是起提示作用的。
在349附近加入
#ifdefined(ENABLE_CMD_NAND_YAFFS)
} else if ( s != NULL&&
(!strcmp(s, ".yaffs") ||!strcmp(s, ".yaffs1"))){
if (read) {
/* read */
nand_read_options_t opts;
memset(&opts, 0, sizeof(opts));
opts.buffer= (u_char*) addr;
opts.length= size;
opts.offset= off;
opts.readoob= 1;
opts.quiet = quiet;
ret =nand_read_opts(nand, &opts);
} else {
/* write */
nand_write_options_topts;
memset(&opts, 0, sizeof(opts));
opts.buffer= (u_char*) addr;
opts.length= size;
opts.offset= off;
//opts.noecc = 1;
opts.pad =0;
opts.writeoob = 1;
opts.blockalign = 1;
opts.quiet = quiet;
opts.autoplace = 1;
if (s[6] =='1')
opts.forceyaffs = 1;
#ifdefined(ENABLE_CMD_NAND_YAFFS_SKIPFB)
opts.skipfirstblk = 1;
#endif
ret =nand_write_opts(nand, &opts);
}
#endif
/*add to useyaffs*/
2.在include/command.h
加入
#defineENABLE_CMD_NAND_YAFFS 1
#define ENABLE_CMD_NAND_YAFFS_SKIPFB 1
#defineCFG_NAND_YAFFS1_NEW_OOB_LAYOUT 0
3.在include/nand.h里的nand_write_options结构体加入
u_char *buffer; /*memory block containing image to write */
ulong length; /*number of bytes to write */
ulong offset; /*start address in NAND */
int quiet; /*don't display progress messages */
int autoplace; /*if true use auto oob layout */
int forcejffs2; /*force jffs2 oob layout */
int forceyaffs; /*force yaffs oob layout */
int noecc; /*write without ecc */
int writeoob; /*image contains oob data */
int pad; /* pad topage size */
int blockalign; /*1|2|4 set multiple of eraseblocks
*to align to */
int skipfirstblk;//add to use yaffs
};
14、yaffs2根文件系统制作
在说制作时,先要说说制作的工具,首先,内核要支持yaffs根文件系统,就要去打补丁,先到yaffs官网上下载
其中的mkyaffs2image这个工具是有点不正常的,不支持2K的nand,要上网去搜补丁,主要是制作根文件映像时,oob区域不一样导致。
附录:
1.在u-boot-1.3.2前(不含u-boot-1.3.2)nand_init函数的调用关系,它的调用是被“CONFIG_COMMANDS&
CFG_CMD_NAND”和“CFG_NAND_LEGACY”控制的,1:表示该值为真,0:表示该值为假
CONFIG_COMMANDS& CFG_CMD_NAND |
CFG_NAND_LEGACY |
/drivers/mtd/nand/nand.c中的 nand_init()函数 |
/board/qljt/qljt2440/qljt2440.c中的nand_init()函数 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
1 |
0 |
1 |
1 |
1 |
1 |
0 |
1 |
2.在u-boot-1.3.2后(含u-boot-1.3.2)nand_init函数的调用关系,它的调用是被“CONFIG_CMD_NAND”和“CFG_NAND_LEGACY”控制的,1:表示该值为真,0:表示该值为假
CONFIG_CMD_NAND |
CFG_NAND_LEGACY |
/drivers/mtd/nand/nand.c中的 nand_init()函数 |
/board/qljt/qljt2440/qljt2440.c中的nand_init()函数 |
0 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
1 |
0 |
1 |
1 |
1 |
1 |
0 |
1 |
三、调试uboot
调试uboot有很多方法,其中主要分两大类,一是用jlink或者jtag然后使用调试工具,如GDB,AXD以及RVD之类的,二是直接下载到内存中去运行
两种方法应该场合不一样,后一种是推荐使用,但是前提是要有网络,而前一种方法因为可以实现单步源码级调试,有很多场合也非常有用,下面介绍一下
1、 使用jlink和AXD
首先,打开AXD后,按Alt+L打开命令窗
对于命令窗,说明:
如果板子没有网口,在调试U-Boot和uclinux时就没法用gdb调试。
这时只能利用串口和JTAG口进行调试,linux下可以用BDI这个玩意调试,可是BDI非常昂贵,不适合大众需求。
我总结了下,根据我的调试经验,可以用ADS调试linux的程序。
打开AXD,按下 Alt + L ;或者点SystemViews下Command Line Interface,就可以打开一个命令行:
输入help查看帮助文件。
比较有用的命令如:
LoadBinary = 将一个文件导入RAM
LoadSymbols = 导入符号表
SetPC = 设置PC寄存器
Run = 开始运行
OB + 文件名 = 按照批处理文件运行
所有的命令在GUI里面也是有的,可以利用批处理文件(OB命令)来免去敲命令和点菜单的麻烦,
以调试u-boot为例,写一个批处理文件放在D盘,文件名为u-boot.txt,内容如下:
loadbinary Y:\u-boot-1.1.4\u-boot.bin 0x33f80000
loadsymbols Y:\u-boot-1.1.4\u-boot
setpc 0x33f80000
run
打开AXD,按下ALT+L,键盘输入:obd:\u-boot.txt
那么AXD会自动运行批处理文件内的命令,自动载入u-boot的二进制代码,自动载入符号表,设置指针为0x00100000,并开始运行。
在调试时,最后自己一步步输入命令做
loadbinary简写lb,只能将二进制文件加载到ram中,不能将地址指定到flash中
在loadsymbols之后,等待进度条显示加载完成,将setpc (pc)指到u-boot.bin的加载位置,即可单步调试。
这时ads会提示寻找u-boot的入口文件start.s地址,此时需要手动指定start.s的位置,通常这时可以在linux下建立smaba服务共享,将整个u-boot的文件夹映射成windows下一个盘符,这样就可以指定目录地址了。
在进入c语言代码中后,ads还可能出现提示指定board.c、string.c等其他文件的地址,这都需要手动做。
此后就可以用ads进行c语言级的u-boot源码调试了!
这种方法应该没问题的,因为我实验了很多次了,都成功了,这可是偶找了近一个月才弄清楚的方法..........
同样,这种方法不知道是否可以用在uclinux上调试,正在进行试验。
参考地址:http://www.mcuzone.com/artical_index.html下的使用AXD在9261上调试u-boot。
按照上面说的,就可以调试到,其中软件会叫你指定一些文件,指定完就可以持到源文件,在源文件上调试
二、出现的问题
****************************************
在nor flashs模式下调试
************************************
1、调试前如果没加上Init script,会出现raise an exption,cause:The processor was reset.
所以参考开发板的配套资料
在命令行打入(我的GT2440是)
setmem 0x53000000,0x00000000,32
setmem 0x4a000008,0xffffffff,32
setmem 0x4a00001c,0x000007ff,32
setmem 0x53000000,0x00000000,32
setmem 0x56000050,0x000055aa,32
setmem 0x4c000014,0x00000007,32
setmem 0x4c000000,0x00ffffff,32
setmem 0x4c000004,0x00061012,32
setmem 0x4c000008,0x00040042,32
setmem 0x48000000,0x22111120,32
setmem 0x48000004,0x00002f50,32
setmem 0x48000008,0x00000700,32
setmem 0x4800000c,0x00000700,32
setmem 0x48000010,0x00000700,32
setmem 0x48000014,0x00000700,32
setmem 0x48000018,0x0007fffc,32
setmem 0x4800001c,0x00018005,32
setmem 0x48000020,0x00018005,32
setmem 0x48000024,0x008e0459,32
setmem 0x48000028,0x00000032,32
setmem 0x4800002c,0x00000030,32
setmem 0x48000030,0x00000030,32
——————————
用于初始化内存
——————————
后面我把它写到一个startrun.ini里,然后用ob ...就行了
运行到bl lowlevel_init运行不下去,有两种可能
第一种是lowlevel_init 没有运行在前4K内存中
解决办法
方法一:
顶层Makefile文件中:
#__LIBS := $(subst $(obj),,$(LIBS)) $(subst$(obj),,$(LIBBOARD))
__LIBS := $(subst $(obj),,$(LIBBOARD)) $(subst $(obj),,$(LIBS))
这样修改之后查看map文件可以发现,lowlevel_init函数链接到了前面.
方法二:
在不修改Makefile的情况下,可以通过修改目标板下的链接文件u-boot.lds来使lowlevel_init放在4K之内: cpu/arm920t/start.o (.text)board/net2410e/lowlevel_init.o (.text) 把之放到start.o的后面.
这相当于修改链接脚本,未做验证,我使用第一种方法!
第二种问题是解决第一种后,还出现
这个问题解决办法:暂未研究
--------------------------------------------------------------------
为方便使用,同时,也是方便u-boot的调试。因此,我要将此u-boot代码再做一修改,使其可以在内存中运行。这样,可以用
开发板自带的vivi将其下载到内存中,再在内存中运行u-boot。要想它在内存中运行,方法很简单,将/root/u-boot-
1.3.1/cpu/arm920t中的start.s中
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif
此段代码中的bl cpu_init_crit注释掉,即不进行CPU的初始化工作(此工作,当前在板子上运行的vivi已完成,故不能再次进行)
,即改为
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
@bl cpu_init_crit
#endif
修改/board/hugerat/rat2440/config.mk中text_base 值为0x33000000
使用vivi命令load ram 0x33000000 0x17ea8 x将u-boot.bin装入内存。再用go 0x33000000命令,即可。
******************************************************
直接在nand flash模式下调试
---------------------------------------------------------
uboot直接加载到SDRAM是不能运行的,必须屏蔽掉uboot中的CPU及RAM初始化代码才行,
因为从Norflash启动时已经初始化过了。
**********************************
其实最简单的方法就是在你创建的头文件中定义 CONFIG_SKIP_LOWLEVEL_INIT
其实就是跳过底层初始化
因为执行lowlevel_init会初始化SDRAM,但是这工作在NORflash上已经执行,如果又执行,则将清空
内存版u-boot的制作简单地说,就是注释掉不需要运行的相关代码,主要是:
1)内存,flash的初始化
2)ARM的内存重映射
3)相关时钟初始化
***********************************
2、 用tftp下载在内存里调试
出自于:http://hi.baidu.com/guolele1990
首先,手头上要有的软硬件:
1、移植好的uboot(具有tftp和go功能),可选用开发板配套的,这里我使用的是自己移植的(比较有成功感)
2、tftp就要有网线,这里这调试局限,要有网络,如果没有,可以使用AXD调试,详情请看另一文章
3、串口线当然要,超级终端也是必须
开始:
1、下载移植好的uboot(不是调试的,是成功的)
可下载到nor也可以nand
2、下载要调试的uboot到内存中,需要了解几个信息
(1)内存的大小:SDRAM:64M(开发板不同而不同)
了解到内存是从30000000开始的,那64M就是3000,0000~3400,0000,这里有个问题,你uboot从nor或者nand运行时,再经过一系列拷贝之类,就在内存中运行,在哪里运行?就是_TEXT_BASE=33f80000的位置后512K中运行,所以移植如果超过512就要注意,这时可以修改board\开发板文件\config.mk那时指定,当然现在不是这问题。现在是什么问题呢?现在的问题是如果我还是把uboot用tftp下载到33f80000里可不可以,解决这问题前,还有一个问题,我为什么下载到33f80000?我们先看一段代码start.S里:
***************** CHECK_CODE_POSITION******************************************/
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 duringdebug */
beq stack_setup
/***************** CHECK_CODE_POSITION******************************************/
这段代码是检测你现在是否运行在RAM中,它怎么知道?就是看你开始的代码是不是为text_base的值,如果你移植的不是,那当然会再执行下面的nor或者flash拷贝了
解决上面这问题,重新回到为什么不下载到33f80000?我们知道,它现在运行在33f80000,如果你再tftp进去,那不是把现在的执行中的代码都清除了?有什么后果?死机!!!
所以不能下载到33f80000里,那么应该下载到哪里?我应该是要下载到TEXT_BASE指定的地址里的,所以我们要执行这样的操作:修改TEXT_BASE的值!!!
修改\board\开发板目录\config.mk
将TEXT_BASE改成33000000这个虽然顺便改,但是不能改到别的什么堆啊什么栈空间里,这样程序也执行不下去,最好的就是修改到后面,面不影响原来的就可以。
(2)还有一个问题,就是底层初始化,底层初始化会将内存清空一次的,所以不应该让它执行,所以在开发板头文件中应该定义:CONFIG_SKIP_LOWLEVEL_INIT或者在start.S中定义也可以
要解决的问题解决了,现在是下载
先打开开发板,执行移植好的uboot,然后不tftp下载uboot到指定内存中33000000,然后执行
go 33000000
就可以运行了!!!
Uboot移植到此结束,希望对大家有帮助,后面可能还会加其它功能,那只是后话。。。。
提供一个移植uboot也做得非常好的网址,http://www.cublog.cn/u3/101649/showart_2276917.html
Guolele1990