之前做过u-boot-1.1.6在AT91RM9200的移植,虽然2410和9200这两款ARM9芯片都是ARM920T核的,但还是有不少区别的,特别是启动方式(当然前者是工业级后者是民用级不必说)。at91rm9200内部本身有128k的片内rom,其固化了一个bootloader和uploader, 用来支持程序的下载和引导,而且其内部固化的程序提供了很多内部服务接口(Internel Service)供我们来使用,例如Xmodem;而S3C2410启动是把nandflash的前4K代码自动搬到Steppintstone中去运行,而Steppingstone使用的物理地是从0号位置开始的。(当然如果是Nor Flash启动,两者是相同的)
1.开发环境
宿主机:FC11
交叉编译器:
之前编译的arm-linux-gcc-3.4.5-softfloat,请参见:
http://blog.csdn.net/shevsten/archive/2007/07/14/1691197.aspx
开发板: GEC2410 NAND Flash:64M K9F1208, NOR Flash:2M SST39VF1601 SDRAM 64M, CS8900A
2.下载u-boot-1.1.6源码
u-boot的源码可以从以下网址下载:
ftp://ftp.denx.de/pub/u-boot/
解压放到指定目录:
# tar -xvjf u-boot-1.1.6.tar.bz2
3.建立自己GEC2410开发板的配置
复制smdk2410的开发板配置,然后在做相应修改,由于Uboot1.1.6对SMDK2410板的NAND Flash初始化部分没有写,主要移植内容为Nand Flash部分.
复制smdk2410目录及对应头文件,并改名为gec2410:
1)# cp –r board/smdk2410 board/gec2410
2)# cp include/configs/smdk2410.h include/configs/gec2410.h
4.修改顶层Makefile
(1)增加gec2410配置
找到Line 1879:
smdk2410_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0
在其后面添加:
gec2410_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t gec2410 NULL s3c24x0
各项的意思如下:
arm: CPU的架构(ARCH)
arm920t: CPU的类型(CPU),其对应于cpu/arm920t子目录。
fs2410: 开发板的型号(BOARD),对应于board/fs2410目录。
NULL: 开发者/或经销商(vender)。
s3c24x0: 片上系统(SOC)。
这样在执行make gec2410_config命令后就把环境变量设置好了
(2)设置编译器路径(Line 127)
ifeq ($(ARCH),arm)
CROSS_COMPILE = /home/GEC2410/toolchain/arm-softfloat-linux-gnu/bin/arm-softfloat-linux-gnu-
5.修改u-boot的命令行提示符
include/configs/gec2410.h第111行:
修改:
# define CFG_PROMPT "SMDK2410 #"
为:
# define CFG_PROMPT "GEC2410 #"
这是u-boot的命令行提示符。
6.修改board/gec2410/Makefile
将:
COBJS := smdk2410.o flash.o
改为:
COBJS := gec2410.o flash.o
gec2410下的smdk2410.c改成gec2410.c;
7.修改board/gec2410/lowlevel_init.S文件
参考GEC2410开发板自带BIOS里的配置参数,主要是SDRAM时序参数的配置.
/* BWSCON */ #define DW8 (0x0) #define DW16 (0x1) #define DW32 (0x2) #define WAIT (0x1<<2) #define UBLB (0x1<<3) //#define B1_BWSCON (DW32) #define B1_BWSCON (DW16) #define B2_BWSCON (DW16) #define B3_BWSCON (DW16 + WAIT + UBLB) #define B4_BWSCON (DW16) #define B5_BWSCON (DW16) #define B6_BWSCON (DW32) #define B7_BWSCON (DW32) /* BANK0CON */ #define B0_Tacs 0x3 /*0x0 0clk */ #define B0_Tcos 0x3 /*0x0 0clk */ #define B0_Tacc 0x7 /* 14clk */ #define B0_Tcoh 0x3 /*0x0 0clk */ #define B0_Tah 0x3 /*0x0 0clk */ #define B0_Tacp 0x1 #define B0_PMC 0x0 /* normal */ /* BANK1CON */ #define B1_Tacs 0x3 /* 4clk */ /*0x0 0clk */ #define B1_Tcos 0x3 /* 4clk */ /*0x0 0clk */ #define B1_Tacc 0x7 /* 14clk */ #define B1_Tcoh 0x3 /* 4clk */ /*0x0 0clk */ #define B1_Tah 0x3 /* 4clk */ /*0x0 0clk */ #define B1_Tacp 0x3 #define B1_PMC 0x0 #define B2_Tacs 0x0 #define B2_Tcos 0x0 #define B2_Tacc 0x7 #define B2_Tcoh 0x0 #define B2_Tah 0x0 #define B2_Tacp 0x0 #define B2_PMC 0x0 #define B3_Tacs 0x0 /* 0clk */ #define B3_Tcos 0x3 /* 4clk */ #define B3_Tacc 0x7 /* 14clk */ #define B3_Tcoh 0x1 /* 1clk */ #define B3_Tah 0x0 /* 0clk */ #define B3_Tacp 0x3 /* 6clk */ #define B3_PMC 0x0 /* normal */
这时候可以测试下编译是否成功
#make gec2410_config
#make
u-boot-bin目录下会出现u-boot.bin
8.board/fs2410加入NAND Flash读函数,建立nand_read.c(从开发板提供的u-boot-1.1.4中复制board/gec2410/nand_read.c,实际上就是从vivi中拷贝过来的)代码比较简单,实际上就一个读NAND的函数,代码如下:
具体对NAND的操作可参考CE下NAND驱动分析:
http://blog.csdn.net/shevsten/archive/2010/04/27/5533400.aspx
/* * nand_read.c: Simple NAND read functions for booting from NAND * * Copyright (C) 2002 MIZI Research, Inc. * * Author: Hwang, Chideok <[email protected]> * Date : $Date: 2002/08/14 10:26:47 $ * * $Revision: 1.6 $ * $Id: param.c,v 1.9 2002/07/11 06:17:20 nandy Exp * * */ #include <config.h> #define __REGb(x) (*(volatile unsigned char *)(x)) #define __REGi(x) (*(volatile unsigned int *)(x)) #define NF_BASE 0x4e000000 #define NFCONF __REGi(NF_BASE + 0x0) #define NFCMD __REGb(NF_BASE + 0x4) #define NFADDR __REGb(NF_BASE + 0x8) #define NFDATA __REGb(NF_BASE + 0xc) #define NFSTAT __REGb(NF_BASE + 0x10) #define BUSY 1 inline void wait_idle(void) { int i; while(!(NFSTAT & BUSY)) for(i=0; i<10; i++); } #define NAND_SECTOR_SIZE 512 #define NAND_BLOCK_MASK (NAND_SECTOR_SIZE - 1) /* low level nand read function */ int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size) { int i, j; if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)) { return -1; /* invalid alignment */ } /* chip Enable */ NFCONF &= ~0x800; for(i=0; i<10; i++); for(i=start_addr; i < (start_addr + size);) { /* READ0 */ NFCMD = 0; /* Write Address */ NFADDR = i & 0xff; NFADDR = (i >> 9) & 0xff; NFADDR = (i >> 17) & 0xff; NFADDR = (i >> 25) & 0xff; wait_idle(); for(j=0; j < NAND_SECTOR_SIZE; j++, i++) { *buf = (NFDATA & 0xff); buf++; } } /* chip Disable */ NFCONF |= 0x800; /* chip disable */ return 0; }
接着修改board/gec2410/makefile,增加对nand_read.c的编译
第28行:
COBJS := gec2410.o flash.o nand_read.o
9.修改cpu/arm920t/start.S文件
2410从Nand Flash启动时,NAND FLASH的前4KB(地址为0x00000000,OM[1:0]=0)将被装载到SDRAM中被称为Setppingstone的地址中,然后开始执行这段代码.启动以后,这4KB的空间可以做其他用途,在start.S加入搬运代码.
主要工作开始初始化NAND控制器,然后调用nand_read.c中的nand_read_ll函数来复制u-boot自身代码到RAM中,最后跳转到RAM中运行.汇编代码如下:
#ifdef CONFIG_S3C2410_NAND_BOOT bl copy_myself @ jump to ram ldr r1, =on_the_ram add pc, r1, #0 nop nop 1: b 1b @ infinite loop on_the_ram: #endif /* Set up the stack */ stack_setup: #.............. ###################################### ldr pc, _start_armboot _start_armboot: .word start_armboot #ifdef CONFIG_S3C2410_NAND_BOOT copy_myself: mov r10, lr @ 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 1:add r3, r3, #0x1 cmp r3, #0xa blt 1b 2:ldr r2, [r1, #oNFSTAT] @ wait ready tst r2, #0x1 beq 2b 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 vivi to RAM ldr r0, =UBOOT_RAM_BASE mov r1, #0x0 mov r2, #0x20000 bl nand_read_ll tst r0, #0x0 beq ok_nand_read #ifdef CONFIG_DEBUG_LL bad_nand_read: ldr r0, STR_FAIL ldr r1, SerBase bl PrintWord 1:b 1b @ infinite loop #endif ok_nand_read: #ifdef CONFIG_DEBUG_LL ldr r0, STR_OK ldr r1, SerBase bl PrintWord #endif @ verify mov r0, #0 ldr r1, =UBOOT_RAM_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 done_nand_read bne go_next notmatch: #ifdef CONFIG_DEBUG_LL sub r0, r0, #4 ldr r1, SerBase bl PrintHexWord ldr r0, STR_FAIL ldr r1, SerBase mov r2, #0 mov r3, r2 mov r4, r2 mov r5, r2 mov r6, r2 mov r7, r2 mov r8, r2 mov r9, r2 clear_loop: stmia r0!, {r2-r9} subs r1, r1, #(8 * 4) bne clear_loop mov pc, lr #endif 1:b 1b done_nand_read: #ifdef CONFIG_DEBUG_LL ldr r0, STR_OK ldr r1, SerBase bl PrintWord #endif mov pc, r10 @ clear memory @ r0: start address @ r1: length mem_clear: mov r2, #0 mov r3, r2 mov r4, r2 mov r5, r2 mov r6, r2 mov r7, r2 mov r8, r2 mov r9, r2 clear_loop: stmia r0!, {r2-r9} subs r1, r1, #(8 * 4) bne clear_loop mov pc, lr #endif @ CONFIG_S3C2410_NAND_BOOT
注意bl copy_myself即第一个#ifdef CONFIG_S3C2410_NAND_BOOT的内容必须放在Set up the stack之前,否则会发生tftp Retry count exceeded; starting again的错误.(具体为net.c中 if (memcmp(ether, NetEtherNullAddr, 6) == 0) NetEtherNullAddr初始值为{0,0,0,0,0,0},而调试发现变成了{ff,ff,ff,ff,ff,ff},从而导致ether server的MAC地址无法获得,发送的UDP包目标机MAC地址缺失,从而无法连接,原因可能是堆栈区设置好后被uboot代码覆盖,因此,堆栈设置应该在uboot复制到ram后进行)
10.在gec2410.h中定义:
主要是NAND控制器地址,寄存器偏移量,uboot RAM地址等.
其中CONFIG_S3C2410_NAND_BOOT定义从Nand启动,如果不定义CONFIG_S3C2410_NAND_BOOT则u-boot从nor flash启动.
/*----------------------------------------------------------------------- * NAND FLASH BOOT */ #define CONFIG_S3C2410_NAND_BOOT 1 #define STACK_BASE 0x33f00000 #define STACK_SIZE 0x8000 #define UBOOT_RAM_BASE 0x30100000 #define NAND_CTL_BASE 0x4e000000 #define bINT_CTL(Nb) _REG(INT_CTL_BASE+(Nb)) #define oNFCONF 0x00 #define oNFCMD 0x04 #define oNFADDR 0x08 #define oNFDATA 0x0c #define oNFSTAT 0x10 #define oNFECC 0x14 /*--------------------------------------------------------------------*/ #define NAND_MAX_CHIPS 1
11.编写NAND闪存初始化函数
u-boot运行至第二阶段进入start_armboot()函数(位于Lib_arm/board.c
)。其中nand_init()函数是对nand flash 的最初初始化函数。其调用与 CFG_NAND_LEGACY 宏有关,如果没定义CFG_NAND_LEGACY 这个宏,就按照start_armboot()调用drivers/nand/nand.c 中的nand_init 函数(该函数在 1.1.6 已经被实现), 但还有个 board_nand_init()函数没实现,需自己添加。如果定义了CFG_NAND_LEGACY,就不使用默认的nand_init,而调用自己写的nand_init 函数了,这里我们选择第二种方式。
在common/cmd_nand.c最后添加:
void nand_init(void) { int i; unsigned long j; rNFCONF=(1<<15)|(1<<14)|(1<<13)|(1<<12)|(1<<11)|(0<<8)|(3<<4)|(0<<0); rNFCONF=rNFCONF&~(1<<11); rNFCMD=0xff; for(i=0;i<10;i++); while(!(rNFSTAT&(1<<0))); rNFCONF=rNFCONF|(1<<11); j=nand_probe(0x4e000000); printf("nand flash : %4lu MB/n",j>>20); }
该函数首先初始化NAND控制器,然后调用nand_probe获取NAND信息.
nand_probe实现在/src/drivers/nand_legacy/nand_legacy.c
有这样一段代码:
for (i=0; i<CFG_MAX_NAND_DEVICE; i++) { if (nand_dev_desc[i].ChipID == NAND_ChipID_UNKNOWN) { nand = &nand_dev_desc[i]; break; } } if (!nand) return (0);
生成的u-boot.bin烧写后运行,NAND的信息无法得到,显示的0MB,同样不能saveenv.
检查发现上面的循环中的if并未进入,nand指针一直为NULL,就直接返回0了.原因是该文件上面定义的
struct nand_chip nand_dev_desc[CFG_MAX_NAND_DEVICE] = {{0}};
而if (nand_dev_desc[i].ChipID == NAND_ChipID_UNKNOWN) 判断时则会为否.因此在for循环之前增加:
nand_dev_desc[0].ChipID = 0x00;
使if判断成功,nand才能成功被赋值.
12. 修改include/configs/gec2410.h,在上次修改的基础上加上如下代码,定义NAND 闪存命令层的底
接口函数等:
#define CFG_NAND_LEGACY 1 #define CFG_ENV_IS_IN_NAND 1 #define CFG_NAND_BASE 0x4E000000 #define CMD_SAVEENV #define CFG_ENV_SIZE 0x10000 /* Total Size of Environment Sector */ #define CFG_ENV_OFFSET 0x030000 /* nand flash sector 24 */ #define CFG_MONITOR_BASE PHYS_SDRAM_1 /* Nand Flash Settings */ #define rNFCONF (*(volatile unsigned *)0x4E000000) #define rNFCMD (*(volatile unsigned *)0x4E000004) #define rNFADDR (*(volatile unsigned *)0x4E000008) #define rNFDATA (*(volatile unsigned *)0x4E00000C) #define rNFSTAT (*(volatile unsigned *)0x4E000010) #define rNFECC (*(volatile unsigned *)0x4E000014) #define CFG_MAX_NAND_DEVICE 1 /* Max number of NAND devices */ #define SECTORSIZE 512 #define ADDR_COLUMN 1 #define ADDR_PAGE 2 #define ADDR_COLUMN_PAGE 3 #define NAND_ChipID_UNKNOWN 0x00 #define NAND_MAX_FLOORS 1 #define NAND_MAX_CHIPS 1 #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);} #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) /* 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) #define CONFIG_MTD_NAND_VERIFY_WRITE 1
13. 修改common/env_nand.c
make后出现/env_nand.c:206 undefined reference to 'nand_info'等等问题
原来nand flash 真正的擦除和读写函数使用的是drivers/nand_legacy/目录下面的读写、擦除函数
在env_nand.c中添加如下声明:
int nand_legacy_erase(struct nand_chip* nand, size_t ofs,size_t len, int clean); int nand_legacy_rw(struct nand_chip* nand, int cmd,size_t start, size_t len,size_t * retlen, u_char * buf);
修改saveenv和env_relocate_spec函数:
...... ...... #else /* ! CFG_ENV_OFFSET_REDUND */ int saveenv(void) /* 2008-6-26 by weij */ { 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); 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; } #endif /* 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); if (ret || total != CFG_ENV_SIZE) return use_default(); if (crc32(0, env_ptr->data, ENV_SIZE) != env_ptr->crc) return use_default(); #endif /* ! ENV_IS_EMBEDDED */ } #endif /* CFG_ENV_OFFSET_REDUND */ ...... ......
14.U-BOOT给linux内核传递合适参数的定义
再次修改include/configs/gec2410.h:
/* allow to overwrite serial and ethaddr */ #define CONFIG_ENV_OVERWRITE #define CONFIG_BAUDRATE 115200 /************************************************************/ /* enable passing of ATAGs */ #define CONFIG_CMDLINE_TAG 1 #define CONFIG_SETUP_MEMORY_TAGS 1 #define CONFIG_INITRD_TAG 1
15.修改UBOOT的2410CPU频率
smdk2410的U-BOOT原来运行频率是202.8M,而GEC2410的BIOS里面是200M,所以不修改频率可能会出点问题.
修改board/gec2410/gec2410.c文件如下:
#define FCLK_SPEED 1 #if FCLK_SPEED==0 /* Fout = 203MHz, Fin = 12MHz for Audio */ #define M_MDIV 0xC3 #define M_PDIV 0x4 #define M_SDIV 0x1 #elif FCLK_SPEED==1 /* Fout = 202.8MHz */ //#define M_MDIV 0xA1 //#define M_PDIV 0x3 //#define M_SDIV 0x1 #define M_MDIV 0x5c /* Fout = 200MHz */ #define M_PDIV 0x4 #define M_SDIV 0x0 #endif
使用H-JTAG烧写到板子上后重启,出现如下信息:
U-Boot 1.1.6 (May 12 2010 - 13:48:06)
DRAM: 64 MB
Flash: 512 kB
NAND: nand flash : 64 MB
In: serial
Out: serial
Err: serial
GEC2410#
saveenv,tftp均成功:
GEC2410#saveenv
Saving Environment to NAND...
Erasing Nand...Writing to Nand... done
GEC2410#
GEC2410#tftp 30008000 zImage
TFTP from server 192.168.1.15; our IP address is 192.168.1.5
Filename 'zImage'.
Load address: 0x30008000
Loading: #################################################################
#################################################################
#################################################################
##############################
done
Bytes transferred = 1147452 (11823c hex)
接下来就是移植linux内核了.