http://home.eeworld.com.cn/my/space.php?uid=135723&do=blog&id=25347这是另外一个网友的一致帖子解释比较详细
1 支持S3C2440,S3C2410未知;
2 NAND flash支持,包括读写NAND flash,从NAND flash启动,saveenv 在NAND flash;
3 支持tftp的使用,也就支持DM9000网卡,8900CS未知;
4 支持yaffs映像的烧写,该版本已经支持yaffs2的文件系统,在fs目录下已经有yaffs2目录;
5 支持串口xmodem协议。
第0阶段:建立自己的开发板配置文件
1 打开u-boot主目录下的makefile,找到smdk2410_config,在其下,仿照它的格式加入如下语句:
smdk2440_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t smdk2440 NULL s3c24x0
各项的意思如下:
arm: CPU的架构(ARCH)
arm920t: CPU的类型(CPU),其对应于cpu/arm920t子目录。
smdk2440: 开发板的型号(BOARD),对应于board/smdk2440目录。
NULL: 开发者/或经销商(vender)。
s3c24x0: 片上系统(SOC)。
此步是为了加入自已的开发板,也可以在现有的开发板基础上修改。
2 修改CROSS_COMPILE,添加这行:CROSS_COMPILE = arm-linux- ,如下:
...
CROSS_COMPILE = arm-linux-
ifndef CROSS_COMPILE
ifeq ($(HOSTARCH),$(ARCH))
...
3 在/board子目录中建立自己的开发板smdk2440目录,结构为/board/smdk2440。
如果开发者/经销商(vender)不为NULL,则目录结构为/board/verder_name/smdk2440,否则编译会出错。
然后,将smdk2410目录下的文件考入此目录中,并将其中的smdk2410.c改名为smdk2440.c。同时还得
修改board/smdk2410/Makefile文件。
...
COBJS := smdk2440.o flash.o
...
4 在include/configs/中建立配置头文件
将smdk2410.h 复制一份在相同目录下。并改名为smdk2440.h
5 回到u-boot主目录,make smdk2440_config,再make,编译生成u-boot.bin成功。
第1阶段:支持2440
u-boot-2008.10 依然没有提供对S3C2440的支持,本阶段任务是加入S3C2440相关的代码,使得u-boot可
以在s3c2440上正常工作,注意涉及以下几个关键文件的修改,为了方便叙述,对于其他功能支持的修改
在这样一起描述。
1): /cpu/arm920t/start.s 改动逐行讲解,没有讲到的保持不变
删除下面这行:
#include <status_led.h> /*这是针对AT91RM9200DK开发板的,删掉。*/
增加变量定义:
...
.globl _bss_end
_bss_end:
.word _end
.globl PreLoadedONRAM
PreLoadedONRAM:
.word 0
...
说明:这个变量用来在通过ViVi 或OpenOCD等工具将U-boot直接下到内存运行后,能在U-boot运行出现菜单
和引导kernel前将U-boot停下来。这个变量在这里定义为 0,如果将U-boot直接下到内存中运行,这个值会
在后面被修改为1,在 Main.c 文件的 main_loop()函数中,如果检测到这个值为1,就会打印一段向导,然
后跳出这个函数,进行死循环。
继续删除下面的几行:这是针对AT91RM9200DK开发板的
bl coloured_LED_init
bl red_LED_on
#if defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK)||defined(CONFIG_AT91RM9200DF)
/*
* relocate exception table
*/
ldr r0, =_start
ldr r1, =0x0
mov r2, #16
copyex:
subs r2, r2, #1
ldr r3, [r0], #4
str r3, [r1], #4
bne copyex
#endif
接下来一段修改为:S3C2440的中断寄存器有改变,设置中断寄存器
/* turn off the watchdog */
#if defined(CONFIG_S3C2400)
# define pWTCON 0x15300000
# define INTMSK 0x14400008 /* Interupt-Controller base addresses */
# define CLKDIVN 0x14800014 /* clock divisor register */
#elif defined(CONFIG_S3C2410)
# define pWTCON 0x53000000
# define INTMOD 0X4A000004 /* add for S3C2440 */
# define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
# define INTSUBMSK 0x4A00001C
# define CLKDIVN 0x4C000014 /* clock divisor register */
#endif
继续,关开门狗和中断
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410) /* 新加,因为上面去掉了这行 */
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
/*
* mask all IRQs by setting all bits in the INTMR - default
*/
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0]
# if defined(CONFIG_S3C2410)
ldr r1, =0x3ff
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
#if 0 /* 将时钟的修改去掉了,在后面的函数中修改 */
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
#endif
#endif /* CONFIG_S3C2400 || CONFIG_S3C2410 */
继续,CPU初始化
...
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
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 */
blne cpu_init_crit
#endif
说明:这个地方要先判断U-boot是否在运行地址中,如果是就不进行CPU的初始化。
删除 #ifndef CONFIG_AT91RM9200 以及其对应的 #endif,将下面设置堆栈的一段剪切来这里:
/* Set up the stack */
stack_setup:
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
sub r0, r0, #CONFIG_SYS_MALLOC_LEN /* malloc area */
sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE /* bdinfo */
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 /* leave 3 words for abort-stack */
设置好堆栈后,可以用C代码来设置时钟了。添加代码如下:这段C代码后面给出。
...
bl clock_init
...
到这里为止,完成的工作有:
1:CPU进入管理模式;
2:关看门狗和中断;
3:CPU的初始化
4:堆栈设置
5:时钟的初始化
至此,初始化工作告一段落,要搬运代码到内存中了(U-boot的运行地址),代码如下:
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate: /* relocate U-Boot to RAM */
...
如果代码已经在运行地址了,则不应该进行代码搬运,而直接跳到后面的代码运行,由于我们
已经在前面设置了堆栈,设置堆栈的label到这段代码的前面去了,所以将跳转改为下面的
label即可。
...
cmp r0, r1 /* don't reloc during debug */
beq clear_bss /* 原来这里为 stack_setup */
...
由于已经设置好了堆栈,可以采用直接读取NAND Flash的C语言代码来搬运代码,从而达到能从
NAND Flash启动的目的。原来的汇编代码只能从NOR Flash搬运代码。修改的代码如下:
...
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
#if 1
bl CopyCode2Ram /* r0: source, r1: dest, r2: size */
#else /* 下面的这部分代码被注释掉了 */
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
添加对 PreLoadedONRAM 的修改,如果直接在内存中运行,则将值修改为1:
...
clbss_l:str r2, [r0] /* clear loop... */
add r0, r0, #4
cmp r0, r1
ble clbss_l
SetLoadFlag:
/* Set a global flag, PreLoadedONRAM */
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 */
ldr r2, =PreLoadedONRAM
mov r3, #1
streq r3, [r2]
...
删除 cpu_init_crit 函数中对下级初始化的编译开关,如下:
...
mov ip, lr
bl lowlevel_init
mov lr, ip
...
到这里,对Start.S的修改结束。
2):board/smdk2440/lowlevel_init.s
修改BWSCON,mini2440 BANK0接NOR Flash,BANK4接DM9000,BANK6接RAM,对于mini2440,
只要修改B4_BWSCON即可,为了配合其他板,这里统一修改如下:
...
#define B1_BWSCON (DW16)
#define B2_BWSCON (DW16)
#define B3_BWSCON (DW16 + UBLB) /* cs8900 */
#define B4_BWSCON (DW16 + WAIT + UBLB) /* DM9000 */
#define B5_BWSCON (DW8)
#define B6_BWSCON (DW32)
#define B7_BWSCON (DW32)
...
然后修改 REFRESH 的刷新周期:
...
#define REFCNT 0x4f4 /* period=7.8125us, HCLK=100Mhz, (2048+1-7.8125*100) */
...
lowlevel_init.s 修改完成。
3):board/smdk2440/smdk2440.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
#endif
#define USB_CLOCK 1
#if USB_CLOCK==0
#define U_M_MDIV 0xA1
#define U_M_PDIV 0x3
#define U_M_SDIV 0x1
#elif USB_CLOCK==1
#define U_M_MDIV 0x48
#define U_M_PDIV 0x3
#define U_M_SDIV 0x2
#endif
static inline void delay (unsigned long loops)
{
__asm__ volatile ("1:/n"
"subs %0, %1, #1/n"
"bne 1b":"=r" (loops):"0" (loops));
}
继续删除下面一段:
/* to reduce PLL lock time, adjust the LOCKTIME register */
clk_power->LOCKTIME = 0xFFFFFF;
/* configure MPLL */
clk_power->MPLLCON = ((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV);
/* some delay between MPLL and UPLL */
delay (4000);
/* configure UPLL */
clk_power->UPLLCON = ((U_M_MDIV << 12) + (U_M_PDIV << 4) + U_M_SDIV);
/* some delay between MPLL and UPLL */
delay (8000);
将GPIO设置下面的一段修改如下,支持2410和2440,同时不开启dcache.
...
/* support both of S3C2410 and S3C2440 */
if (isS3C2410)
{
/* arch number of SMDK2410-Board */
gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;
}
else
{
/* arch number of SMDK2440-Board */
gd->bd->bi_arch_number = MACH_TYPE_S3C2440; /* 782; */
}
/* adress of boot parameters */
gd->bd->bi_boot_params = 0x30000100;
icache_enable();
#if 0
dcache_enable();
#endif
在最后添加下面的函数:
ulong board_flash_get_legacy(ulong base, int banknum, flash_info_t *info)
{
info->portwidth = CONFIG_SYS_FLASH_CFI_WIDTH;
info->chipwidth = CONFIG_SYS_FLASH_CFI_WIDTH;
info->interface = FLASH_CFI_X16;
return 1;
}
smdk2440.c修改完成。
4):smdk2440目录修改:
前面在Start.S中调用的C函数 clock_init 和 CopyCode2RAM 在新添加的文件 boot_init.c中,
把这个文件放到这个目录,同时修改本目录下的makefile和u-boot.lds脚本。
makefile修改:
...
COBJS := smdk2440.o flash.o
...
修改为:
...
COBJS := smdk2440.o boot_init.o
...
去掉flash.o,因为使用CFI接口的NOR Flash。添加boot_init.o 。
u-boot.lds修改:添加boot_init.o到text段的前面,修改后如下:
...
cpu/arm920t/start.o (.text)
board/smdk2440/boot_init.o (.text)
...
5):在 /include/configs/smdk2440.h 增加对CFI接口NOR Flash的支持,修改为:
/*-----------------------------------------------------------------------
* Physical Memory Map
*/
#define CONFIG_NR_DRAM_BANKS 1 /* we have 1 bank of DRAM */
#define PHYS_SDRAM_1 0x30000000 /* SDRAM Bank #1 */
#define PHYS_SDRAM_1_SIZE 0x04000000 /* 64 MB */
#define PHYS_FLASH_1 0x00000000 /* Flash Bank #1 */
#define CONFIG_SYS_FLASH_BASE PHYS_FLASH_1
#define CONFIG_SYS_MONITOR_BASE PHYS_FLASH_1 /* new add */
/*-----------------------------------------------------------------------
* FLASH and environment organization
*/
#if 0
#define CONFIG_AMD_LV400 1 /* uncomment this if you have a LV400 flash */
#define CONFIG_AMD_LV800 1 /* uncomment this if you have a LV800 flash */
#endif
#define CONFIG_SYS_MAX_FLASH_BANKS 1 /* max number of memory banks */
#ifdef CONFIG_AMD_LV800
#define PHYS_FLASH_SIZE 0x00100000 /* 1MB */
#define CONFIG_SYS_MAX_FLASH_SECT (19) /* max number of sectors on one chip */
#define CONFIG_ENV_ADDR (CONFIG_SYS_FLASH_BASE + 0x0F0000) /* addr of environment */
#endif
#ifdef CONFIG_AMD_LV400
#define PHYS_FLASH_SIZE 0x00080000 /* 512KB */
#define CONFIG_SYS_MAX_FLASH_SECT (11) /* max number of sectors on one chip */
#define CONFIG_ENV_ADDR (CONFIG_SYS_FLASH_BASE + 0x070000) /* addr of environment */
#endif
#define CONFIG_SYS_FLASH_CFI 1
#define CONFIG_FLASH_CFI_DRIVER 1
#define CONFIG_SYS_FLASH_USE_BUFFER_WRITE 1
#define CONFIG_FLASH_CFI_LEGACY 1
#ifdef CONFIG_FLASH_CFI_DRIVER
#define CONFIG_SYS_MAX_FLASH_SECT 512 /* max number of sectors on one chip */
#define CONFIG_ENV_ADDR (CONFIG_SYS_FLASH_BASE + 0x1E0000) /* addr of environment,2M NOR Flash */
#define CONFIG_SYS_FLASH_CFI_WIDTH 0x02 /* FLASH_CFI_16BIT */
#endif
6):在 include/s3c24x0.h 中添加 isS3C2410 和 S3C2440_NAND 宏定义:
#define rGSTATUS1 (*(volatile unsigned *)0x560000B0)
#define isS3C2410 ((rGSTATUS1 & 0xffff0000) == 0x32410000)
到这里是可以编译完成的,下面进行时钟修改。
7):修改 cpu/arm920t/s3c24x0/speed.c 修改根据时钟寄存器来计算时钟的方法。修改后为:
#include <common.h>
#if defined(CONFIG_S3C2400) || defined (CONFIG_S3C2410) || defined (CONFIG_TRAB)
#if defined(CONFIG_S3C2400)
#include <s3c2400.h>
#elif defined(CONFIG_S3C2410)
#include <s3c2410.h>
#endif
#define MPLL 0
#define UPLL 1
/* ------------------------------------------------------------------------- */
/* NOTE: This describes the proper use of this file.
*
* CONFIG_SYS_CLK_FREQ should be defined as the input frequency of the PLL.
*
* get_FCLK(), get_HCLK(), get_PCLK() and get_UCLK() return the clock of
* the specified bus in HZ.
*/
/* ------------------------------------------------------------------------- */
static ulong get_PLLCLK(int pllreg)
{
S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
ulong r, m, p, s;
if (pllreg == MPLL)
r = clk_power->MPLLCON;
else if (pllreg == UPLL)
r = clk_power->UPLLCON;
else
hang();
m = ((r & 0xFF000) >> 12) + 8;
p = ((r & 0x003F0) >> 4) + 2;
s = r & 0x3;
/* support both of S3C2410 and S3C2440 */
if (isS3C2410)
return((CONFIG_SYS_CLK_FREQ * m) / (p << s));
else
return((CONFIG_SYS_CLK_FREQ * m * 2) / (p << s)); /* S3C2440 */
}
/* return FCLK frequency */
ulong get_FCLK(void)
{
return(get_PLLCLK(MPLL));
}
/* for s3c2440 */
#define S3C2440_CLKDIVN_PDIVN (1<<0)
#define S3C2440_CLKDIVN_HDIVN_MASK (3<<1)
#define S3C2440_CLKDIVN_HDIVN_1 (0<<1)
#define S3C2440_CLKDIVN_HDIVN_2 (1<<1)
#define S3C2440_CLKDIVN_HDIVN_4_8 (2<<1)
#define S3C2440_CLKDIVN_HDIVN_3_6 (3<<1)
#define S3C2440_CLKDIVN_UCLK (1<<3)
#define S3C2440_CAMDIVN_CAMCLK_MASK (0xf<<0)
#define S3C2440_CAMDIVN_CAMCLK_SEL (1<<4)
#define S3C2440_CAMDIVN_HCLK3_HALF (1<<8)
#define S3C2440_CAMDIVN_HCLK4_HALF (1<<9)
#define S3C2440_CAMDIVN_DVSEN (1<<12)
/* return HCLK frequency */
ulong get_HCLK(void)
{
S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
unsigned long clkdiv;
unsigned long camdiv;
int hdiv = 1;
/* support both of S3C2410 and S3C2440 */
if (isS3C2410)
return((clk_power->CLKDIVN & 0x2) ? get_FCLK()/2 : get_FCLK());
else
{
clkdiv = clk_power->CLKDIVN;
camdiv = clk_power->CAMDIVN;
/* work out clock scalings */
switch (clkdiv & S3C2440_CLKDIVN_HDIVN_MASK) {
case S3C2440_CLKDIVN_HDIVN_1:
hdiv = 1;
break;
case S3C2440_CLKDIVN_HDIVN_2:
hdiv = 2;
break;
case S3C2440_CLKDIVN_HDIVN_4_8:
hdiv = (camdiv & S3C2440_CAMDIVN_HCLK4_HALF) ? 8 : 4;
break;
case S3C2440_CLKDIVN_HDIVN_3_6:
hdiv = (camdiv & S3C2440_CAMDIVN_HCLK3_HALF) ? 6 : 3;
break;
}
return get_FCLK() / hdiv;
}
}
/* return PCLK frequency */
ulong get_PCLK(void)
{
S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
unsigned long clkdiv;
unsigned long camdiv;
int hdiv = 1;
/* support both of S3C2410 and S3C2440 */
if (isS3C2410)
return((clk_power->CLKDIVN & 0x1) ? get_HCLK()/2 : get_HCLK());
else
{
clkdiv = clk_power->CLKDIVN;
camdiv = clk_power->CAMDIVN;
/* work out clock scalings */
switch (clkdiv & S3C2440_CLKDIVN_HDIVN_MASK) {
case S3C2440_CLKDIVN_HDIVN_1:
hdiv = 1;
break;
case S3C2440_CLKDIVN_HDIVN_2:
hdiv = 2;
break;
case S3C2440_CLKDIVN_HDIVN_4_8:
hdiv = (camdiv & S3C2440_CAMDIVN_HCLK4_HALF) ? 8 : 4;
break;
case S3C2440_CLKDIVN_HDIVN_3_6:
hdiv = (camdiv & S3C2440_CAMDIVN_HCLK3_HALF) ? 6 : 3;
break;
}
return get_FCLK() / hdiv / ((clkdiv & S3C2440_CLKDIVN_PDIVN)? 2:1);
}
}
/* return UCLK frequency */
ulong get_UCLK(void)
{
return(get_PLLCLK(UPLL));
}
#endif /* defined(CONFIG_S3C2400) || defined (CONFIG_S3C2410) || defined (CONFIG_TRAB) */
然后修改 include/s3c24x0.h 中时钟寄存器的定义为:
...
typedef struct {
S3C24X0_REG32 LOCKTIME;
S3C24X0_REG32 MPLLCON;
S3C24X0_REG32 UPLLCON;
S3C24X0_REG32 CLKCON;
S3C24X0_REG32 CLKSLOW;
S3C24X0_REG32 CLKDIVN;
S3C24X0_REG32 CAMDIVN; /* for s3c2440 */
} /*__attribute__((__packed__))*/ S3C24X0_CLOCK_POWER;
到此,第一阶段完成,完成对s3c2440的支持。编译完后,下载到板子验证能运行,能从NAND和NOR
Flash启动,cpu运行在400M Hz,并且能引导kernel。bootloader的功能基本达到。后面的改进都是
增强u-boot的功能。
第2阶段:u-boot移植dm9000的网卡驱动
u-boot自带网卡驱动,所以只要做些设置即可。
在 include/configs/smdk2440.h 中注释掉CS8900网口,添加DM9000网口驱动配置,如下:
...
#if 0
#define CONFIG_DRIVER_CS8900 1 /* we have a CS8900 on-board */
#define CS8900_BASE 0x19000300
#define CS8900_BUS16 1 /* the Linux driver does accesses as shorts */
#endif
#if !defined(CONFIG_DRIVER_CS8900)
#define CONFIG_DRIVER_DM9000 1
#define CONFIG_DM9000_USE_16BIT 1
#define CONFIG_DM9000_BASE 0x20000000
#define DM9000_IO 0x20000000
#define DM9000_DATA 0x20000004
#endif
...
将相关IP设置的注释去掉,并修改IP设置,顺便修改下启动参数的宏设置,如下:
...
#define CONFIG_BOOTDELAY 3
#define CONFIG_BOOTARGS "noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0"
#define CONFIG_ETHADDR 08:00:3e:26:0a:5b
#define CONFIG_NETMASK 255.255.255.0
#define CONFIG_IPADDR 192.168.0.88
#define CONFIG_SERVERIP 192.168.0.160
/*#define CONFIG_BOOTFILE "elinos-lart" */
#define CONFIG_BOOTCOMMAND "nand read 0x30007FC0 kernel; bootm 0x30007FC0"
...
在网卡驱动中,drivers/net/dm9000.c,有一段程序试图连接网卡的MII接口,而实际MII接口并未使用
,会有十秒的等待时间,然后报错,可以将此段程序注释掉。
第3阶段:移植nand flash驱动
首先,要说明一下 CFG_NAND_LEGACY 的使用。在u-boot的 drivers/mtd/下有两个目录,分别是nand和
nand_legacy。在nand目录下的是nand的初始化函数和nand的操作读写函数,是使用linux的mtd构架。
此目录下的文件,只有在定义了CFG_CMD_NAND宏和没有定义CFG_NAND_LEGACY宏的情况下才会被编译。
在nand_leagcy目录下的文件也是是实现nand相关操作命令,如read,write等命令的功能,但不是使用
linux的mtd构架。此目录下的文件,只有在定义了CFG_CMD_NAND和定义了CFG_NAND_LEGACY宏的情况下
才会定义。此目录下的文件u-boot已不推荐使用。在本移植过程中采用不定义CFG_NAND_LEGACY的方式。
1):在/include/configs/smdk2440.h 添加命令支持,如下:
...
#define CONFIG_CMD_ELF
#define CONFIG_CMD_NAND /* 添加NAND falsh命令 */
...
同时添加nand flash参数设置。如下:
/*-----------------------------------------------------------------------
* NAND flash settings
*/
#define CONFIG_SYS_NAND_BASE 0 /* NAND控制器基地址*/
#define CONFIG_SYS_MAX_NAND_DEVICE 1 /* 最大nand flash设备数 */
#define NAND_MAX_CHIPS 1 /* nand flash芯片数*/
#define SECTORSIZE 512 /* 1页的大小 */
#define ADDR_COLUMN 1 /* Column地址为1个字节 */
#define ADDR_PAGE 3 /* 页块地址为3个字节 */
#define ADDR_COLUMN_PAGE 4 /* 总地址为4字节 */
#define NAND_ChipID_UNKNOWN 0x00 /* 未知芯片的ID号 */
#define NAND_MAX_FLOORS 1 /* nand 最大层数 */
#define CONFIG_MTD_NAND_VERIFY_WRITE 1 /* 进行写入校验 */
...
2):Nand 的初始化顺序为:
lib_arm/board.c中start_armboot() -> drivers/mtd/nand/nand.c中nand_init() ->
nand_init_chip() -> cpu/arm920t/s3c24x0/nand.c中board_nand_init()完成Nand Flash的初始化。
drivers/mtd/nand/nand.c中nand_init() -> drivers/mtd/nand/nand_base.c中nand_scan() ->
nand_scan_ident() -> nand_set_defaults()
nand_scan_ident() -> nand_get_flash_type()
drivers/mtd/nand/nand_base.c中nand_scan() -> nand_scan_tail()
nand_scan_ident() :Nand Flash设备扫描。
nand_scan_tail() :在nand_scan()中调用,用于填充所有的初始化函数指针和扫描坏块表是否适当。
说明:此版的u-boot已自带board_nand_init(),此函数在/cpu/arm920t/s3c24x0/nand.c中实
现。分析nand.c可以发现如果定义宏CFG_NAND_LEGACY,则直接报 #error "U-Boot legacy
NAND support not available for S3C2410"的错误。
因为S3C2410和S3C2440在FLASH控制器上,差别较大,驱动代码需要改写。改写主要在
/cpu/arm920t/s3c24x0/nand.c中进行。实话实说,这个nand.c实现的比较糟糕,依葫芦画瓢,为
S3C2440写个驱动。
/cpu/arm920t/s3c24x0/nand.c修改过程:
先加入S3C2440 NAND flash控制器的地址定义,修改后如下:
...
#if !defined(CONFIG_S3C2440)
#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 NFECC0 __REGb(NF_BASE + 0x14)
#define NFECC1 __REGb(NF_BASE + 0x15)
#define NFECC2 __REGb(NF_BASE + 0x16)
#else
#define NF_BASE 0x4e000000
#define NFCONF __REGi(NF_BASE + 0x0)
#define NFCONT __REGi(NF_BASE + 0x4)
#define NFCMD __REGb(NF_BASE + 0x8)
#define NFADDR __REGb(NF_BASE + 0xc)
#define NFDATA __REGb(NF_BASE + 0x10)
#define NFMECCD0 __REGi(NF_BASE + 0x14)
#define NFMECCD1 __REGi(NF_BASE + 0x18)
#define NFSECCD __REGi(NF_BASE + 0x1C)
#define NFSTAT __REGb(NF_BASE + 0x20)
#define NFSTAT0 __REGi(NF_BASE + 0x24)
#define NFSTAT1 __REGi(NF_BASE + 0x28)
#define NFMECC0 __REGi(NF_BASE + 0x2C)
#define NFMECC1 __REGi(NF_BASE + 0x30)
#define NFSECC __REGi(NF_BASE + 0x34)
#define NFSBLK __REGi(NF_BASE + 0x38)
#define NFEBLK __REGi(NF_BASE + 0x3c)
#define S3C2440_NFCONT_nCE (1<<1)
#define S3C2440_ADDR_NALE 0x0c
#define S3C2440_ADDR_NCLE 0x08
#endif
...
u-boot.2008.10自带的S3C2410的s3c2410_hwcontrol函数有错。在此函数中,把chip->IO_ADDR_W值
改写了,导致在写数据时出现错误。解决方法是使用一全局变量代替 chip->IO_ADDR_W。
在 s3c2410_hwcontrol 函数上一行定义这个全局变量,然后修改 s3c2410_hwcontrol 函数,让它
支持 S3C2440,修改后如下:
...
static void s3c2410_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
struct nand_chip *chip = mtd->priv;
DEBUGN("hwcontrol(): 0x%02x 0x%02x/n", cmd, ctrl);
#if !defined(CONFIG_S3C2440)
if (ctrl & NAND_CTRL_CHANGE) {
/* ulong IO_ADDR_W = NF_BASE; */
IO_ADDR_W = NF_BASE;
if (!(ctrl & NAND_CLE))
IO_ADDR_W |= S3C2410_ADDR_NCLE;
if (!(ctrl & NAND_ALE))
IO_ADDR_W |= S3C2410_ADDR_NALE;
/* chip->IO_ADDR_W = (void *)IO_ADDR_W; */ /* BUG , delete */
if (ctrl & NAND_NCE)
NFCONF &= ~S3C2410_NFCONF_nFCE;
else
NFCONF |= S3C2410_NFCONF_nFCE;
}
if (cmd != NAND_CMD_NONE)
/* writeb(cmd, chip->IO_ADDR_W);*/
writeb(cmd,(void *)IO_ADDR_W);
#else
if (ctrl & NAND_CTRL_CHANGE) {
IO_ADDR_W = NF_BASE;
if (!(ctrl & NAND_CLE))
IO_ADDR_W |= S3C2440_ADDR_NALE;
if (!(ctrl & NAND_ALE))
IO_ADDR_W |= S3C2440_ADDR_NCLE;
/* chip->IO_ADDR_W = (void *)IO_ADDR_W; */
if (ctrl & NAND_NCE)
NFCONT &= ~S3C2440_NFCONT_nCE;
else
NFCONT |= S3C2440_NFCONT_nCE;
}
if (cmd != NAND_CMD_NONE)
writeb(cmd,(void *)IO_ADDR_W);
#endif
}
...
board_nand_init 修改后如下:
...
clk_power->CLKCON |= (1 << 4);
#if !defined(CONFIG_S3C2440)
DEBUGN("CONFIG_S3C2410/n");
/* 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 data structure */
nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)0x4e00000c;
/* read_buf and write_buf are default */
/* read_byte and write_byte are default */
/* hwcontrol always must be implemented */
nand->cmd_ctrl = s3c2410_hwcontrol;
nand->dev_ready = s3c2410_dev_ready;
#else
DEBUGN("CONFIG_S3C2440/n");
twrph0 = 4; twrph1 = 2; tacls = 0;
cfg = (tacls<<12)|(twrph0<<8)|(twrph1<<4);
NFCONF = cfg;
cfg = (1<<6)|(1<<4)|(0<<1)|(1<<0);
NFCONT = cfg;
/* initialize nand_chip data structure */
nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)0x4e000010;
/* read_buf and write_buf are default */
/* read_byte and write_byte are default */
/* hwcontrol always must be implemented */
nand->cmd_ctrl = s3c2410_hwcontrol;
nand->dev_ready = s3c2410_dev_ready;
#endif
...
为了显示芯片型号,将
drivers/mtd/nand/nand_base.c中的 nand_get_flash_type 函数结尾,修改 MTDDEBUG 为 printf.
3):在Nand Flash中保存 u-boot 参数,(saveenv 功能)
/include/configs/smdk2440.h 修改如下:
...
#define CONFIG_CMD_NAND
#define CONFIG_CMD_ENV /* 添加saveenv命令的支持 */
...
...
/* #define CONFIG_ENV_IS_IN_FLASH 1 */
#define CONFIG_ENV_IS_IN_NAND 1
#define CONFIG_ENV_OFFSET 0x40000 /* 参数在Nand Flash中的起始位置,为256K */
#define CONFIG_ENV_SIZE 0x20000 /* Total Size of Environment Sector,128K */
...
为支持S3C2440的NAND Flash功能添加:
#define CONFIG_S3C2440 1 /* support S3C2440 */
到这里,支持读写nand jffs2文件系统,可以保存环境信息到nand中。
第4阶段:u-boot支持烧写yaffs映像文件
1):在/common/cmd_nand.c中do_nand函数中,加入代码,实现对nand write.yaffs命令的支持。在
对jffs2操作的下面加入,如下:
...
if (!s || !strcmp(s, ".jffs2") ||
!strcmp(s, ".e") || !strcmp(s, ".i")) {
...
}
else if(s != NULL && !strcmp(s, ".yaffs"))
{
if(read)
{
printf("nand read.yaffs is not provide!");
}
else
{
nand->writeoob = 1;
ret = nand_write_skip_bad(nand,off,&size,(u_char *)addr);
nand->writeoob = 0;
}
}
...
在 nand 的命令中加入对 nand write.yaffs 的描述,加入如下3行:
...
U_BOOT_CMD(nand, 5, 1, do_nand,
...
" to/from memory address 'addr', skipping bad blocks./n"
"nand write.yaffs - addr off|partition size/n"
" write 'size' bytes starting at offset 'off'/n"
" to/from yaffs image in memory address 'addr', skipping bad blocks./n"
"nand erase [clean] [off size] - erase 'size' bytes from/n"
...
2):在include/linux/mtd/mtd.h的 mtd_info 结构体定义中加入两个变量如下:
...
struct mtd_info {
u_char writeoob;
u_char skipfirstblock;
...
3):在drivers/mtd/nand/nand_util.c的nand_write_skip_bad函数中加两段程序,一段是为了计算正常数
据的长度,一段是为了在写入一段数据后,数据指针能正常跳到下一段数据,修改后如下:
...
int nand_write_skip_bad(nand_info_t *nand, size_t offset, size_t *length,
u_char *buffer)
{
int rval;
size_t left_to_write = *length;
size_t len_incl_bad;
u_char *p_buffer = buffer;
if(nand->writeoob==1)
{
size_t oobsize = nand->oobsize;
size_t datasize = nand->writesize;
int datapages = 0;
if (((*length)%(nand->oobsize+nand->writesize)) != 0) {
printf ("Attempt to write error length data!/n");
return -EINVAL;
}
datapages = *length/(datasize+oobsize);
*length = datapages*datasize;
left_to_write = *length;
nand->skipfirstblock=1;
}
/* Reject writes, which are not page aligned */
if ((offset & (nand->writesize - 1)) != 0 ||
(*length & (nand->writesize - 1)) != 0) {
printf ("Attempt to write non page aligned data/n");
return -EINVAL;
}
len_incl_bad = get_len_incl_bad (nand, offset, *length);
if ((offset + len_incl_bad) >= nand->size) {
printf ("Attempt to write outside the flash area/n");
return -EINVAL;
}
/*if (len_incl_bad == *length) {
rval = nand_write (nand, offset, length, buffer);
if (rval != 0) {
printf ("NAND write to offset %x failed %d/n",
offset, rval);
return rval;
}
}*/
while (left_to_write > 0) {
size_t block_offset = offset & (nand->erasesize - 1);
size_t write_size;
if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) {
printf ("Skip bad block 0x%08x/n",
offset & ~(nand->erasesize - 1));
offset += nand->erasesize - block_offset;
continue;
}
if(nand->skipfirstblock==1)
{
nand->skipfirstblock=0;
printf ("Skip the first good block 0x%08x/n",
offset & ~(nand->erasesize - 1));
offset += nand->erasesize - block_offset;
continue;
}
if (left_to_write < (nand->erasesize - block_offset))
write_size = left_to_write;
else
write_size = nand->erasesize - block_offset;
printf("/rWriting at 0x%x -- ",offset);
rval = nand_write (nand, offset, &write_size, p_buffer);
if (rval != 0) {
printf ("NAND write to offset %x failed %d/n",
offset, rval);
*length -= left_to_write;
return rval;
}
left_to_write -= write_size;
printf("%ld%% is complete.",100-(left_to_write/(*length/100)));
offset += write_size;
if(nand->writeoob==1)
{
p_buffer += write_size+(write_size/nand->writesize*nand->oobsize);
}
else
{
p_buffer += write_size;
}
}
return 0;
}
...
4):在/drivers/mtd/nand/nand_base.c的nand_write函数中,加入一段把正常数据与oob数据分离的
代码,再加入页写时的模式设置为MTD_OOB_RAW,在页写时,不进行ECC的校验,ECC的校验在yaffs的
oob数据中已自带了,不能重写。此模式下,写入正常数据后,会把oob的数据写入nand的oob区。修
改后如下:
...
static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const uint8_t *buf)
{
struct nand_chip *chip = mtd->priv;
int ret;
int oldopsmode = 0;
if(mtd->writeoob==1)
{
size_t oobsize = mtd->oobsize;
size_t datasize = mtd->writesize;
int i = 0;
uint8_t oobtemp[16];
int datapages = 0;
datapages = len/(datasize);
for(i=0;i<(datapages);i++)
{
memcpy(oobtemp,buf+datasize*(i+1),oobsize);
memmove(buf+datasize*(i+1),buf+datasize*(i+1)+oobsize,
(datapages-(i+1))*(datasize)+(datapages-1)*oobsize);
memcpy(buf+(datapages)*(datasize+oobsize)-oobsize,oobtemp,oobsize);
}
}
/* Do not allow reads past end of device */
if ((to + len) > mtd->size)
return -EINVAL;
if (!len)
{printf("Write data length is %d ",len);return 0;}
nand_get_device(chip, mtd, FL_WRITING);
chip->ops.len = len;
chip->ops.datbuf = (uint8_t *)buf;
if(mtd->writeoob!=1)
{
chip->ops.oobbuf = NULL;
}
else
{
chip->ops.oobbuf = (uint8_t *)(buf+len);
chip->ops.ooblen = mtd->oobsize;
oldopsmode = chip->ops.mode;
chip->ops.mode = MTD_OOB_RAW;
}
ret = nand_do_write_ops(mtd, to, &chip->ops);
*retlen = chip->ops.retlen;
nand_release_device(mtd);
chip->ops.mode = oldopsmode;
return ret;
}
...
在这个阶段,支持NAND 写yaffs格式的文件系统,但是不支持读,通常读yaffs在u-boot中用不着。
第5阶段:u-boot支持串口xmodem协议,即增加一个命令loadx
1):依照loady的实现,修改 common/cmd_load.c。首先使用U_BOOT_CMD宏来增加loadx命令:
U_BOOT_CMD(
loadx, 3, 0, do_load_serial_bin,
"load binary file over serial line (xmodem mode)/n",
"[ off ] [ baud ]/n"
" - load binary file over serial line"
" with offset 'off' and baudrate 'baud'/n"
);
2):在do_load_serial_bin函数中增加对loadx命令的处理分支。也是依照loady来实现,如下:
if (strcmp(argv[0],"loady")==0) {
...
}else if(strcmp(argv[0],"loadx")==0) {
printf ("## Readx for binary (xmodem) download "
"to 0x%08lX at %d bps.../n",
offset,
load_baudrate);
addr = load_serial_xmodem (offset);
}else {
...
3):定义load_serial_xmodem函数,来实现xmodem数据传输。它是依照load_serial_ymodem实现
的一个新函数。
...
#if defined(CONFIG_CMD_LOADB)
static ulong load_serial_xmodem (ulong offset); //添加声明
static ulong load_serial_ymodem (ulong offset);
#endif
...
复制 load_serial_ymodem 函数为 load_serial_xmodem,稍作修改,将里面含有 ymodem 的字样
改为 xmodem 即可,一共 6 处。
第6阶段:补充
1):在 /include/configs/smdk2440.h 增加启动参数宏、支持JFFS2命令、默认MTD分区信息
#define CONFIG_CMDLINE_TAG 1 /* enable passing of ATAGs */
#define CONFIG_SETUP_MEMORY_TAGS 1 /* menory tags */
#define CONFIG_JFFS2_CMDLINE 1
#define CONFIG_JFFS2_NAND 1
#define MTDIDS_DEFAULT "nand0=nandflash0"
#define MTDPARTS_DEFAULT "mtdparts=nandflash0:256k@0(bootloader)," /
"128k(params)," /
"2m(kernel)," /
"-(root)"
2):在 common/main.c 的 main_loop 函数中添加MTD分区处理:
...
#ifdef CONFIG_AUTO_COMPLETE
install_auto_complete();
#endif
#ifdef CONFIG_JFFS2_CMDLINE
extern int mtdparts_init(void);
if (!getenv("mtdparts"))
{
run_command("mtdparts default", 0);
}
else
{
mtdparts_init();
}
#endif
3):PreLoadedONRAM变量的使用
在 include/asm-arm/u-boot-arm.h中添加:
extern ulong PreLoadedONRAM;
在 common/main.c 的 main_loop 函数中添加:
...
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
s = getenv ("bootdelay");
bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
debug ("### main_loop entered: bootdelay=%d/n/n", bootdelay);
# ifdef CONFIG_BOOT_RETRY_TIME
init_cmd_timeout ();
# endif /* CONFIG_BOOT_RETRY_TIME */
if (PreLoadedONRAM) {
printf("Now can write files to flash:/n");
printf("1. use Vivi, OpenOCD, nfs or tftp downlload files to RAM /n");
printf("2. In u-boot, use the flash commands to program the image to flash/n");
goto PROMPT;
}
...
在下面注释的上面添加运行菜单函数,在注释下面添加标签:
...
run_command("menu", 0);
/*
* Main Loop for Monitor Command Processing
*/
PROMPT: