资料为网上摘抄
板子是国嵌的 GQ2440
u-boot-2011.06虽然支持一些常见的开发板,但如果u-boot-2011.06不支持我们手上的开发板,那就要找到与我们的开发板最接近的u-boot-2011.06所支持的开发板,在这个开发板的基础上进行移植。u-boot-2011.06所支持的smdk2410开发板是最接近我的开发板的,所以我就在该此基础上进行修改、移植。
在移植之前,我们还需要安装、配置eldk,用于编译u-boot。下面我们就先介绍一下eldk的安装与配置:
1.下载eldk
在ftp://ftp.denx.de/pub/eldk/选择任一版本的eldk并下载,我选择的是eldk4.2版本的arm-2008-11-24.iso文件。把该文件下载到/home/zhaocj/download/目录下。
2.在root权限下安装eldk
创建挂载点:
mkdir /mnt/dvdrom
挂载光驱:
mount –o loop /home/zhaocj/download/arm-2008-11-24.iso/mnt/dvdrom
安装eldk:
cd /mnt/dvdrom
./install –d /opt/eldk4.2/
这样eldk就安装到了/opt/eldk4.2目录下。
3.设置环境变量
cd ~
gedit .bashrc
在最后添加如下内容:
export CROSS_COMPILE=arm-linux-
export PATH=/opt/eldk4.2/bin: /opt/eldk4.2/usr/bin:$PATH
保存并退出,然后执行:
source .bashrc
重启电脑就完成了eldk的安装。
下面就介绍u-boot-2011.06的移植,在这里我把我的开发板命名为gq2440:
1.下载u-boot
在ftp://ftp.denx.de/pub/u-boot/下载目前最新的u-boot版本(即u-boot-2011.06.tar.bz2)到我的用户主目录下。
2.解压u-boot
cd ~
把u-boot-2011.06解压到当前目录下
tar –xvjf u-boot-2011.06.tar.bz2
cd u-boot-2011.06
3.在u-boot中添加我的开发板信息
emacs boards.cfg
打开boards.cfg文件,在第70行(smdk2410 arm arm920t – samsung s3c24x0)的下面添加一行,内容如下:
gq2440 arm arm920t – samsung s3c24x0
保存并退出。
4.通过复制的方式添加我的开发板头文件
cp include/configs/smdk2410.h include/configs/gq2440.h
5.通过复制的方式添加我的开发板文件夹
cp –r board/samsung/smdk2410 board/samsung/gq2440
更改文件名
mv board/samsung/gq2440/smdk2410.c board/samsung/gq2440/gq2440.c
修改Makefile文件
emacs board/samsung/gq2440/Makefile
打开Makefile文件,在第28行中,把COBJS :=smdk2410.o改为:
COBJS:=gq2440.o
保存并退出。
6.编译
make distclean
make gq2440_config
make
如果上述过程没有出错的话,会在u-boot的目录下出现新的u-boot.bin文件。
2 NorFlash启动移植方法
.修改宏定义
打开include/configs/gq2440.h文件
第38行“#define CONFIG_S3C2410 ”改为:
38:#define CONFIG_S3C2440
目的是告之系统本开发板使用的是S3C2440处理器。
第131行“#define CONFIG_SYS_PROMPT "SMDK2410 # "”改为:
131:#define CONFIG_SYS_PROMPT "gq2440# "
目的是使命令行提示符显示为“gq2440# ”。
第185行“#define CONFIG_FLASH_CFI_LEGACY”改为:
185:#define CONFIG_SYS_CFI_FLASH_CONFIG_REGS {0xFFFF}
因为在u-boot-2011.06中,使用的是CFI的NorFlash接口,所以不需要定义CONFIG_FLASH_CFI_LEGACY,而是要定义CONFIG_SYS_CFI_FLASH_CONFIG_REGS。
第191行“#define CONFIG_SYS_MAX_FLASH_SECT (19)”改为:
191:#define CONFIG_SYS_MAX_FLASH_SECT (35)
因为本开发板使用的NorFlash是EN29LV160AB,它有35个扇区。
第212行“#define CONFIG_NAND_S3C2410”改为:
212:#define CONFIG_NAND_S3C2440
第213行“#define CONFIG_SYS_S3C2410_NAND_HWECC”改为:
213:#define CONFIG_SYS_S3C2440_NAND_HWECC
2.修改系统时钟频率
打开arch/arm/cpu/arm920t/start.s文件
第164行“# if defined(CONFIG_S3C2410)”改为:
164:# if defined(CONFIG_S3C2440)
第165行“ldr r1, =0x3ff”改为:
165:ldr r1, =0x7fff
目的是屏蔽所有子中断。
第173行“mov r1, #3”改为:
173:mov r1, #5
目的是使FCLK:HCLK:PCLK= 1:4:8。
打开board/samsung/gq2440/gq2440.c文件
第42行至第44行的内容改为:
42:#define M_MDIV 92
43:#define M_PDIV 1
44:#define M_SDIV 1
本开发板的输入晶振频率为12MHz,通过上面的修改,则MPLL频率为400MHz。
第54行至第56行的内容改为:
54:#define U_M_MDIV 56
55:#define U_M_PDIV 2
56:#define U_M_SDIV 2
通过上面的修改,则UPLL频率为48MHz。
3.修改内存SDRAM时序
打开board/samsung/gq2440/lowlevel_init.s文件
第54行至第126行的内容改为:
54:#define B1_BWSCON (DW16)
55:#define B2_BWSCON (DW16)
56:#define B3_BWSCON (DW16)
57:#define B4_BWSCON (DW32)
58:#define B5_BWSCON (DW16)
59:#define B6_BWSCON (DW32)
60:#define B7_BWSCON (DW32)
61:
62:
63:#define B0_Tacs 0x3
64:#define B0_Tcos 0x3
65:#define B0_Tacc 0x7
66:#define B0_Tcoh 0x3
67:#define B0_Tah 0x3
68:#define B0_Tacp 0x1
69:#define B0_PMC 0x0
70:
71:
72:#define B1_Tacs 0x1
73:#define B1_Tcos 0x1
74:#define B1_Tacc 0x6
75:#define B1_Tcoh 0x1
76:#define B1_Tah 0x1
77:#define B1_Tacp 0x0
78:#define B1_PMC 0x0
79:
80:#define B2_Tacs 0x1
81:#define B2_Tcos 0x1
82:#define B2_Tacc 0x6
83:#define B2_Tcoh 0x1
84:#define B2_Tah 0x1
85:#define B2_Tacp 0x0
86:#define B2_PMC 0x0
87:
88:#define B3_Tacs 0x1
89:#define B3_Tcos 0x1
90:#define B3_Tacc 0x6
91:#define B3_Tcoh 0x1
92:#define B3_Tah 0x1
93:#define B3_Tacp 0x0
94:#define B3_PMC 0x0
95:
96:#define B4_Tacs 0x1
97:#define B4_Tcos 0x1
98:#define B4_Tacc 0x6
99:#define B4_Tcoh 0x1
100:#define B4_Tah 0x1
101:#define B4_Tacp 0x0
102:#define B4_PMC 0x0
103:
104:#define B5_Tacs 0x1
105:#define B5_Tcos 0x1
106:#define B5_Tacc 0x6
107:#define B5_Tcoh 0x1
108:#define B5_Tah 0x1
109:#define B5_Tacp 0x0
110:#define B5_PMC 0x0
111:
112:#define B6_MT 0x3
113:#define B6_Trcd 0x1
114:#define B6_SCAN 0x1
115:
116:#define B7_MT 0x3
117:#define B7_Trcd 0x1
118:#define B7_SCAN 0x1
119:
120:
121:#define REFEN 0x1
122:#define TREFMD 0x0
123:#define Trp 0x1
124:#define Trc 0x1
125:#define Tchr 0x2
126:#define REFCNT 1268
4.修改NandFlash相关内容。
尽管本次移植没有涉及到NandFlash,但为了能够编译成功,必须修改下面有关NandFlash的内容。
进入u-boot-2011.06目录,执行下列命令:
cp drivers/mtd/nand/s3c2410_nand.c drivers/mtd/nand/s3c2440_nand.c
打开drivers/mtd/nand/s3c2440_nand.c文件,把该文件内的所有有关“2410”的地方一律改为“2440”。
再打开drivers/mtd/nand/Makefile文件
第48行“COBJS-$(CONFIG_NAND_S3C2410) += s3c2410_nand.o”改为
48:COBJS-$(CONFIG_NAND_S3C2440) += s3c2440_nand.o
编译u-boot,把编译生成的新的u-boot.bin烧写到NorFlash中。开发板上电启动后,则在超级终端(或类似的软件)中显示的结果为:
U-Boot 2011.06 (Jul 30 2011 - 23:40:44)
DRAM: 64 MiB
Flash: 2 MiB
NAND: raise: Signal # 8 caught
raise: Signal # 8 caught
raise: Signal # 8 caught
raise: Signal # 8 caught
raise: Signal # 8 caught
raise: Signal # 8 caught
No NAND device found!!!
0 MiB
*** Warning - bad CRC, using defaultenvironment
In: serial
Out: serial
Err: serial
Net: CS8900-0
gq2440 #
从上述显示的内容可以看出本开发板的一些基本的信息,如DRAM为64M,NorFlash为2M,由于我没有设置NandFlash,因此系统没有检测到NandFlash,命令提示符也已经修改为“gq2440 # ”。
在提示符下输入flinfo,则会把NorFlash的更详细的信息显示出来:
Bank # 1: CFI conformant flash (16 x16) Size: 2 MB in 35 Sectors
AMDStandard command set, Manufacturer ID: 0x1C,Device ID: 0x2249
Erase timeout: 16384 ms, write timeout: 1 ms
Sector Start Addresses:
00000000 RO 00004000 RO 00006000 RO 00008000 RO 00010000 RO
00020000 RO 00030000 RO 00040000 RO 00050000 RO 00060000 RO
00070000 RO 00080000 00090000 000A0000 000B0000
000C0000 000D0000 000E0000 000F0000 00100000
00110000 00120000 00130000 00140000 00150000
00160000 00170000 00180000 00190000 001A0000
001B0000 001C0000 001D0000 001E0000 001F0000
3 支持NandFlash读写
我们把drivers/mtd/nand/s3c2410_nand.c文件复制为s3c2440_nand.c文件,并把该文件内的所有有关“2410”的地方一律改为“2440”。这么修改仅仅是能够让系统编译成功,并没有真正实现NandFlash的读写。在这里,我们就来介绍如何让u-boot支持NandFlash的读写。
由于s3c2410与s3c2440的NandFlash控制器不一样,因此s3c2440_nand.c文件并不能直接应用,需要进行适当的修改,而主要修改的内容就是s3c2440的相关寄存器。
首先重新定义要用到的寄存器,把原文中第27行至第37行之间的宏定义去掉,改为下面的形式:
#define S3C2440_NFCONT_SECCL (1<<6)
#define S3C2440_NFCONT_MECCL (1<<5)
#define S3C2440_NFCONT_INITECC (1<<4)
#define S3C2440_NFCONT_nCE (1<<1)
#define S3C2440_NFCONT_MODE (1<<0)
#define S3C2440_NFCONF_TACLS(x) ((x)<<12)
#define S3C2440_NFCONF_TWRPH0(x) ((x)<<8)
#define S3C2440_NFCONF_TWRPH1(x) ((x)<<4)
#define S3C2440_ADDR_NALE 0x08
#define S3C2440_ADDR_NCLE 0x0C
然后就是修改s3c2440_hwcontrol函数和board_nand_init函数,其他函数不变。
board_nand_init函数主要是用于对NandFlash的初始化,对它修改的内容是对寄存器NFCONF和寄存器NFCONT的修改,如下所示为修改后的board_nand_init函数,其中红色标注的地方为修改的地方:
int board_nand_init(struct nand_chip *nand)
{
u_int32_t cfg;
u_int8_t tacls, twrph0, twrph1;
struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();
struct s3c2440_nand *nand_reg = s3c2440_get_base_nand();
debugX(1,"board_nand_init()\n");
writel(readl(&clk_power->clkcon) |(1 << 4), &clk_power->clkcon);
#ifdefined(CONFIG_S3C24XX_CUSTOM_NAND_TIMING)
tacls = CONFIG_S3C24XX_TACLS;
twrph0 = CONFIG_S3C24XX_TWRPH0;
twrph1 = CONFIG_S3C24XX_TWRPH1;
#else
tacls = 2;
twrph0 = 3;
twrph1 = 1;
#endif
cfg = S3C2440_NFCONF_TACLS(tacls - 1);
cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);
cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);
writel(cfg,&nand_reg->nfconf);
cfg = S3C2440_NFCONT_SECCL;
cfg |= S3C2440_NFCONT_MECCL;
cfg |= S3C2440_NFCONT_MODE;
writel(cfg,&nand_reg->nfcont);
nand->IO_ADDR_R = (void*)&nand_reg->nfdata;
nand->IO_ADDR_W = (void*)&nand_reg->nfdata;
nand->select_chip = NULL;
#ifdefCONFIG_NAND_SPL
nand->read_buf = nand_read_buf;
#endif
nand->cmd_ctrl = s3c2440_hwcontrol;
nand->dev_ready = s3c2440_dev_ready;
#ifdefCONFIG_S3C2440_NAND_HWECC
nand->ecc.hwctl = s3c2440_nand_enable_hwecc;
nand->ecc.calculate = s3c2440_nand_calculate_ecc;
nand->ecc.correct = s3c2440_nand_correct_data;
nand->ecc.mode = NAND_ECC_HW;
nand->ecc.size =CONFIG_SYS_NAND_ECCSIZE;
nand->ecc.bytes =CONFIG_SYS_NAND_ECCBYTES;
#else
nand->ecc.mode = NAND_ECC_SOFT;
#endif
#ifdef CONFIG_S3C2440_NAND_BBT
nand->options = NAND_USE_FLASH_BBT;
#else
nand->options = 0;
#endif
debugX(1, "end ofnand_init\n");
return 0;
}
最后修改s3c2440_hwcontrol函数,该函数是用于对NandFlash的写命令和写地址操作:
static void s3c2440_hwcontrol(structmtd_info *mtd, int cmd, unsigned int ctrl)
{
struct nand_chip *chip =mtd->priv;
struct s3c2440_nand *nand = s3c2440_get_base_nand();
debugX(1,"hwcontrol(): 0xx 0xx\n", cmd, ctrl);
if (ctrl &NAND_CTRL_CHANGE) {
ulong IO_ADDR_W = (ulong)nand;
if (!(ctrl &NAND_CLE))
IO_ADDR_W|= S3C2440_ADDR_NCLE;
if (!(ctrl &NAND_ALE))
IO_ADDR_W|= S3C2440_ADDR_NALE;
if(cmd ==NAND_CMD_NONE)
IO_ADDR_W = &nand->nfdata;
chip->IO_ADDR_W= (void *)IO_ADDR_W;
if (ctrl &NAND_NCE)
writel(readl(&nand->nfconf)& ~S3C2440_NFCONT_nCE,
&nand->nfconf);
else
writel(readl(&nand->nfconf)| S3C2440_NFCONT_nCE,
&nand->nfconf);
}
if (cmd !=NAND_CMD_NONE)
writeb(cmd, chip->IO_ADDR_W);
}
在这个函数中,除了修改寄存器的值以及设置写命令和写地址的IO端口外,我们还增加了if(cmd == NAND_CMD_NONE)判断语句。如果不加这个判断语句,向NandFlash内写数据是写不进去的,尽管系统不会提示任何错误,并显示“OK”,但其实数据是没有被写入的,因此一定要加上这条语句。这是因为在写完命令和地址后,一定还要把IO端口的地址重新设置为寄存器NFDATA。
需要说明的是,由于系统没有定义CONFIG_S3C2410_NAND_HWECC,因此我们暂时先不对s3c2440_nand_enable_hwecc函数、s3c2440_nand_calculate_ecc函数和s3c2440_nand_correct_data函数进行修改。
我们把编译好的u-boot.bin文件烧写到norflash中,利用NandFlash的相关命令进行验证:
U-Boot2011.06 (Aug 10 2011 - 23:16:25)
DRAM: 64 MiB
Flash: 2MiB
NAND: 256 MiB
***Warning - bad CRC, using default environment
In: serial
Out: serial
Err: serial
Net: CS8900-0
gq2440# nand info
Device 0:nand0, sector size 128 KiB
gq2440# nand device 0
gq2440# nand erase 0x100000 0x300000
NANDerase: device 0 offset 0x100000, size 0x300000
Erasingat 0x3e0000 -- 100% complete.
OK
gq2440# nand write 0 0x100000 0x300000
NANDwrite: device 0 offset 0x100000, size 0x300000
3145728 bytes written: OK
gq2440# nand read 0x30004000 0x100000 0x300000
NANDread: device 0 offset 0x100000, size 0x300000
3145728 bytes read: OK
gq2440# md.b 0
00000000:13 00 00 ea 14 f0 9f e5 14 f0 9fe5 14 f0 9f e5 ................
00000010:14 f0 9f e5 14 f0 9fe5 14 f0 9f e5 14 f0 9fe5 ................
00000020:e0 01 00 00 40 02 00 00 a0 0200 00 00 03 00 00 ....@...........
00000030:60 03 00 00 c0 03 00 00 20 04 0000 ef be ad de `....... .......
gq2440# md.b 0x30004000
30004000:13 00 00 ea 14 f0 9f e5 14 f0 9fe5 14 f0 9f e5 ................
30004010:14 f0 9f e5 14 f0 9fe5 14 f0 9f e5 14 f0 9fe5 ................
30004020:e0 01 00 00 40 02 00 00 a0 0200 00 00 03 00 00 ....@...........
30004030:60 03 00 00 c0 03 00 00 20 04 0000 ef be ad de `....... .......
上电以后NAND显示为256MiB,说明系统能够正确识别出NandFlash。然后我们把SDRAM中的数据写入到NandFlash中,再读取NandFlash中的这段数据,通过与原SDRAM的数据做比较,可以看出这两段数据的内容是一致的,因此我们能够断定移植的u-boot可以对NandFlash进行正确的读和写操作。
另外,如果你想更详细地了解开发板上NandFlash的情况,可以把drivers/mtd/nand/nand_base.c文件中第2676行中的MTDDEBUG改为printf,再把该行的MTD_DEBUG_LEVEL0,去掉,则在上电后,会显示如下内容:
U-Boot2011.06 (Aug 10 2011 - 23:55:48)
DRAM: 64 MiB
Flash: 2MiB
NAND: NAND device: ManufacturerID: 0xec, Chip ID: 0xda (Samsung NAND 256MiB 3,3V 8-bit)
256 MiB
***Warning - bad CRC, using default environment
In: serial
Out: serial
Err: serial
Net: CS8900-0
4 解决raise: Signal # 8 caught
其实把这个bug去掉也很简单,就是把time.c(在arch/arm/arm920t/s3c24x0目录下)这个文件中的四个全局变量用gd这个数据结构中的4个相关成员代替就可以了,具体的就是:
timer_load_val用gd->timer_rate_hz替代;
timer_clk用gd->tbl替代;
timestamp用gd->timer_reset_value替代;
lastdec用gd->lastinc替代。
下面我们就列出time.c这个文件具体需要修改的地方:
去掉第38行和第39行关于timer_load_val和timer_clk这两个变量的声明,并加上下面代码:
38:DECLARE_GLOBAL_DATA_PTR;
去掉第49行和第50行关于timestamp和lastdec这两个变量的声明;
去掉第60行至第68行语句(if (timer_load_val == 0)的判断内容),改为:
60:gd->timer_rate_hz = get_PCLK() /(2*16*100);
61:gd->tbl = get_PCLK() / (2 * 16);
剩下需要修改的内容就是具体的变量替换,其中每条语句前面的行号为源文件的行号:
70:gd->lastinc = gd->timer_rate_hz;
71:writel(gd->timer_rate_hz,&timers->tcntb4);
78:gd->timer_reset_value = 0;
99:gd->timer_reset_value = t;
108:tmo *= (gd->timer_rate_hz * 100);
118:gd->lastinc = READ_TIMER();
119:gd->timer_reset_value = 0;
126:return tmr / (gd->tbl / CONFIG_SYS_HZ);
137:tmo *= (gd->timer_rate_hz * 100);
140:tmo = usec * (gd->timer_rate_hz * 100);
160:if (gd->lastinc >= now) {
162:gd->timer_reset_value += gd->lastinc -now;
165:gd->timer_reset_value += gd->lastinc + gd->timer_rate_hz- now;
167:gd->lastinc = now;
169:return gd->timer_reset_value;
181:tbclk = gd->timer_rate_hz * 100;
通过上述的修改,我们再上电启动后,就不会再有raise: Signal # 8 caught了。
5 硬件ECC
,我们很好地完成了u-boot对NandFlash的读写,但这个读写进行的是软件ECC,即用软件编程的方法实现ECC。我们知道S3C2440的NandFlash控制器是支持硬件ECC的,因此在这里我们就来讲解如何实现硬件ECC。
NandFlash的每一页分为main区和spare区,S3C2440的NandFlash控制器支持这两个区的硬件ECC,但为了兼容u-boot-2011.06,我们只实现main区的硬件ECC。
为了实现硬件ECC,首先需要在include/configs/gq2440.h文件内定义宏CONFIG_S3C2440_NAND_HWECC,这样在drivers/mtd/nand/s3c2440_nand.c文件内就定义了硬件ECC所需要的三个函数:s3c2440_nand_enable_hwecc函数、s3c2440_nand_calculate_ecc函数和s3c2440_nand_correct_data函数,而且在board_nand_init函数内,又把这三个函数分别赋给了相对应的结构体的三个成员,这样在进行NandFlash读写时,就会调用这三个函数,从而实现了硬件ECC。s3c2440_nand_enable_hwecc函数负责使能硬件ECC,s3c2440_nand_calculate_ecc函数负责计算ECC(当然这种计算是由硬件来完成的),s3c2440_nand_correct_data函数负责进行ECC的校验(同样地,这种校验也是由硬件自动完成的)。
为了理解u-boot是如何进行硬件ECC的,我们先来简要地分析一下相关的函数。NandFlash是以页为最小单位进行读写操作的,支持硬件ECC的读操作最终是由nand_read_page_hwecc函数(在drivers/mtd/nand目录下)来完成的,支持硬件ECC的写操作最终是由nand_write_page_hwecc函数(在drivers/mtd/nand目录下)来完成的。nand_read_page_hwecc函数的流程为先读取main区数据,同时通过调用s3c2440_nand_calculate_ecc函数来得到硬件ECC;再读取spare区数据;然后提取出储存在spare区内的main区ECC;最后通过调用s3c2440_nand_correct_data函数来对刚刚读取的main区数据进行校验。nand_write_page_hwecc函数的流程比较简单,它先写入main区数据,同时通过调用s3c2440_nand_calculate_ecc函数来得到硬件ECC;然后就是把硬件ECC写入到spare区内。
无论是nand_write_page_hwecc函数,还是nand_write_page_hwecc函数,内部都有一个这样的for循环体:
for(i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
…… ……
}
其中三个主要变量的定义为:
eccsize= chip->ecc.size;
eccbytes= chip->ecc.bytes;
eccsteps= chip->ecc.steps;
下面我们就来介绍一下这个循环的作用:不同的CPU的NandFlash控制器一次所能完成的硬件ECC的字节数是不一样的,例如有些CPU一次只能完成512字节的硬件ECC,但如果开发板上的NandFlash每页有2048个字节,那该怎么办呢?这时就要用到一个循环体,通过循环多次来得到一页的硬件ECC。例如上面这种情况,就要循环4次(2048÷512=4),才能得到这个页内数据完整的硬件ECC。另外每一次硬件ECC,不同的CPU所生成的ECC字节数也是不同的,有的是3个字节,有的是4个字节。
那么,上面那三个变量的含义就分别为:
ecc.size:每一次硬件ECC所检验的字节个数
ecc.bytes:每一次硬件ECC所生成的字节个数
ecc.steps:每一页需要进行硬件ECC的次数
对于S3C2440来说,一次硬件ECC可以检验2048个字节,并且生成4个字节的ECC,因此ecc.size应该为2048,ecc.bytes应该为4。而ecc.steps是通过计算得到的,即系统上电后能够获知NandFlash的每页的大小,用这个值除以ecc.size就等于ecc.steps。所以对于这三个参数,只需事先定义好前两个参数即可。而这两个参数是在drivers/mtd/nand/s3c2440_nand.c文件中的board_nand_init函数内被定义赋值的,即:
nand->ecc.size = 2048;
nand->ecc.bytes = 4;
u-boot-2011.06对S3C2440的NandFlash控制器的寄存器定义得不完整,而且有错误,因此我们还需要对此进行修改。删除arch/arm/include/asm/arch-s3c24x0/s3c24x0.h文件内的第167行至第178行内容,添加进下面的内容:
struct s3c2440_nand {
u32 nfconf;
u32 nfcont;
u32 nfcmd;
u32 nfaddr;
u32 nfdata;
u32 nfmeccd0;
u32 nfmeccd1;
u32 nfseccd;
u32 nfstat;
u32 nfestat0;
u32 nfestat1;
u32 nfmecc0;
u32 nfmecc1;
u32 nfsecc;
u32 nfsblk;
u32 nfeblk;
};
最后,我们对s3c2440_nand_enable_hwecc函数、s3c2440_nand_calculate_ecc函数和s3c2440_nand_correct_data函数进行修改。
void s3c2440_nand_enable_hwecc(structmtd_info *mtd, int mode)
{
struct s3c2440_nand *nand = s3c2440_get_base_nand();
debugX(1,"s3c2440_nand_enable_hwecc(%p,%d)\n", mtd, mode);
writel(readl(&nand->nfcont)| S3C2440_NFCONT_INITECC& ~S3C2440_NFCONT_MECCL,&nand->nfcont);
}
该函数的任务就是初始化ECC(即复位ECC),并解锁main区ECC。
static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, constu_char *dat,
u_char *ecc_code)
{
struct s3c2440_nand *nand = s3c2440_get_base_nand();
u32 mecc0;
writel(readl(&nand->nfcont)| S3C2440_NFCONT_MECCL,&nand->nfcont);
mecc0= readl(&nand->nfmecc0);
ecc_code[0]= mecc0 & 0xff;
ecc_code[1] = (mecc0 >> 8) &0xff;
ecc_code[2] = (mecc0 >> 16) &0xff;
ecc_code[3] =(mecc0 >> 24) & 0xff;
debugX(1,"s3c2440_nand_calculate_hwecc(%p,):0xx 0xx 0xx 0xx\n",
mtd , ecc_code[0], ecc_code[1], ecc_code[2], ecc_code[3]);
return 0;
}
该函数首先锁定main区ECC,然后读取寄存器NFMECC0,该寄存器存放着由硬件生成的main区ECC,最后把4个1字节的ECC存放到ecc_code数组内。
static int s3c2440_nand_correct_data(struct mtd_info *mtd, u_char*dat,
u_char *read_ecc, u_char *calc_ecc)
{
struct s3c2440_nand *nand = s3c2440_get_base_nand();
u32 meccdata0, meccdata1, estat0, err_byte_addr;
int ret = -1;
u8 repaired;
meccdata0= (read_ecc[1] << 16) | read_ecc[0];
meccdata1= (read_ecc[3] << 16) | read_ecc[2];
writel(meccdata0,&nand->nfmeccd0);
writel(meccdata1,&nand->nfmeccd1);
estat0= readl(&nand->nfestat0);
switch(estat0 & 0x3) {
case 0:
ret= 0;
break;
case 1:
err_byte_addr= (estat0 >> 7) & 0x7ff;
repaired= dat[err_byte_addr] ^ (1 << ((estat0 >> 4) & 0x7));
printf("S3C NAND: 1 bit error detected at byte%ld. "
"Correcting from 0xx to0xx...OK\n",
err_byte_addr, dat[err_byte_addr],repaired);
dat[err_byte_addr]= repaired;
ret= 1;
break;
case 2:
case 3:
printf("S3C NAND: ECC uncorrectable errordetected. "
"Not correctable.\n");
ret= -1;
break;
}
return ret;
}
该函数首先把read_ecc数组内的ECC存入寄存器NFMECCD0和寄存器NFMECCD1中,这样系统就会自动校验数据,并把状态放入寄存器NFESTAT0中,然后读取该寄存器的后4位,当为0时表示校验正确;当为1时表示发生了1位错误(该类错误可以校正),我们把它校正过来;当为2和3时表示发生其他类型的错误,这类错误是无法校正的。
通过以上内容的修改,我们就实现了NandFlash的硬件ECC。
ECC足以保证所读数据的正确性,并在有些情况下还可以修正错误,但它不能保证所写数据的正确性。为了保证所写数据的正确性,u-boot还可以通过在include/configs/gq2440.h文件内定义宏CONFIG_MTD_NAND_VERIFY_WRITE来实现把所写的数据再读取一遍,然后与被写入的数据之间进行比较来判断所写数据的正确性,这一过程是在drivers/mtd/nand/nand_base.c文件的nand_write_page函数内调用实现的。
6 DM9000移植
实现了UBOOT中关于DM9000的部分,当然都是拿来主意,对于网卡我是相当陌生的。
首先,在include/configs/fl2440.h中去掉原先CS8900网卡的定义,再定义各种关于DM9000网卡的宏:
- /*#define CONFIG_CS8900*/ /* we have a CS8900 on-board */
- /*#define CONFIG_CS8900_BASE 0x19000300 */
- /*#define CONFIG_CS8900_BUS16*/ /* the Linux driver does accesses as shorts */
- #define CONFIG_DRIVER_DM9000 1
- #define CONFIG_DM9000_BASE 0x20000300
- #define DM9000_IO CONFIG_DM9000_BASE
- #define DM9000_DATA (CONFIG_DM9000_BASE+4) /* the cmd pin is addr2*/
- #define CONFIG_ETHADDR a8:00:3E:26:0A:5B
- #define CONFIG_NETMASK 255.255.255.0
- #define CONFIG_IPADDR 192.168.1.11
- #define CONFIG_SERVERIP 192.168.1.234
- #define CONFIG_NET_MULTI
- /*
- #define CONFIG_NETMASK 255.255.255.0
- #define CONFIG_IPADDR 10.0.0.110
- #define CONFIG_SERVERIP 10.0.0.1
- */
DM9000在FL2440的硬件连接原理图:

图中可以看出DM9000连接到NGCS4,看下地址空间可以知道NGCS4的基址是0x20000000,所以网卡基址是0x20000300,后面这个300据他们说是DM9000内部寄存器是这样定义的,就现实来说我的网卡在这个基址上工作良好,所以应该是对的。
CMD连接的是ADDR2,查DM9000手册上CMD为1时发送的是数据信息,所以,DM9000的数据地址是0x20000304,这个很容易理解。
修改fl2440.c中的board_eth_init函数:
- #ifdef CONFIG_CMD_NET
- int board_eth_init(bd_t *bis)
- {
- return dm9000_initialize(bis);
- }
- #endif
修改drivers/net/dm9000x.c,将下面这段注释掉,至于原因,我也不清楚,不管。
- i = 0;
- while (!(dm9000_phy_read(1) & 0x20)) { /* autonegation complete bit */
- udelay(1000);
- i++;
- if (i == 10000) {
- printf("could not establish link\n");
- return 0;
- }
- }
- /* see what we've got */
- lnk = dm9000_phy_read(17) >> 12;
- printf("operating at ");
- switch (lnk) {
- case 1:
- printf("10M half duplex ");
- break;
- case 2:
- printf("10M full duplex ");
- break;
- case 4:
- printf("100M half duplex ");
- break;
- case 8:
- printf("100M full duplex ");
- break;
- default:
- printf("unknown: %d ", lnk);
- break;
- }
- printf("mode\n");
注释这个函数中的内容,不然网卡会自动断开:
- static void dm9000_halt(struct eth_device *netdev)
- {
- #if 0
- DM9000_DBG("%s\n", __func__);
- /* RESET devie */
- dm9000_phy_write(0, 0x8000); /* PHY RESET */
- DM9000_iow(DM9000_GPR, 0x01); /* Power-Down PHY */
- DM9000_iow(DM9000_IMR, 0x80); /* Disable all interrupt */
- DM9000_iow(DM9000_RCR, 0x00); /* Disable RX */
- #endif
- }
编译好后,网卡就起来了。
7 修改机器码
u-boot-2011.06\board\samsung\gq2440\gq2440.c
/* arch number of SMDK2410-Board */
gd->bd->bi_arch_number = 1999;
/* adress of boot parameters */
gd->bd->bi_boot_params = 0x30000100;
完成!
浙公网安备 33010602011771号