u-boot-2009.08在mini2440上的移植

(一)建立mini2440工程环境

移植环境

1,主机环境:debian 7.0 ,4G内存。

2,编译编译环境:arm-linux-gcc v4.3.2

3,开发板:mini2440,2M nor flash,64M nand flash。

5,u-boot版本:u-boot-2009.08

6,参考文章:

http://blogold.chinaunix.net/u3/101649/showart.php?id=2105215

http://blog.chinaunix.net/space.php?uid=23787856&do=blog&id=115382

http://blogimg.chinaunix.net/blog/upfile2/100811115954.pdf

下载并解压源文件

1,下载u-boot-2009.08,可以从官方网站下载u-boot-2009.08

2,#tar -jxvf u-boot-2009.08.tar.bz2    //解压源码

移植步骤

1.1,了解u-boot目录结构和启动流程,请参考U-Boot启动过程分析

【1】目录结构


【2】启动流程

1.2,建立mini2440开发板文件并编译测试

在项目根目录u-boot-2009.08上单击右键->浏览文件夹

【1】定位到board/samsung,将目录smdk2410复制并粘贴到当前目录下,将其重命名为mini2440。

【2】打开mini2440目录,将smdk2410.c重命名为mini2440.c,

【3】用gedit打开当前目录下的Makefile(在Makefile上单击右键->使用“文本编辑器”打开),定位到28行,修改后代码如下所示(修改部分用灰色背景颜色标出):

include $(TOPDIR)/config.mk
LIB = $(obj)lib$(BOARD).a
COBJS := mini2440.o flash.o 
SOBJS := lowlevel_init.o
然后保存。

【4】在根目录下定位到include/configs,将smdk2410.h复制并粘贴到当前目录下,将其重命名成mini2440.h。

【5】用gedit打开根目录下的Makefile文件,然后搜索smdk2410,定位到2997行,找到下列语句

smdk2410_config : unconfig
 @$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 samsung s3c24x0

然后将其复制并粘贴到其下面,并修改成如下语句

mini2440_config : unconfig
 @$(MKCONFIG) $(@:_config=) arm arm920t mini2440 samsung s3c24x0

然后保存。
*说明:

       arm    :CPU的架构(ARCH)
       arm920t:CPU的类型
       mini2440 :对应在board目录下建立新的开发板项目的目录
       samsung:新开发板项目目录的上级目录,如直接在board下建立新的开发板项目的目录,则这里就为NULL
       s3c24x0:CPU型号
*注意:编译选项格式的第二行要用Tab键开始,否则编译会出错。

【6】编译测试

打开终端,进入到u-boot-2009.08根目录下执行

[root@localhost u-boot-2009.08]# make distclean
[root@localhost u-boot-2009.08]# make mini2440_config
Configuring for mini2440 board...
[root@localhost u-boot-2009.08]# make

编译通过。

【7】加入调试选项,启用DEBUG宏,有两种办法办法

<1>参考文章Makefile,如何传递宏定义DEBUG和Debug版和Release版的程序

在编译时,直接在make 后面传入参数 -d ,表示Debug模式,输出有关文件和检测时间的详细信息,如

make -d

可以输出源程序中定义的DEBUG宏定义的调试信息

<2>用gedit打开根目录下的config.mk文件,然后搜索“DDEBUG”,找到如下语句

DBGFLAGS= -g # -DDEBUG

将注释掉的DDEBUG选项打开,修改后下面语句

DBGFLAGS= -g  -DDEBUG

但是此种办法需要在调试完成时需要在将其注释掉。

1.3,根据启动流程修改或添加基本的u-boot源码,使其能够在内存中启动

【1】增加对S3C2440一些寄存器的支持,添加中断禁止部分和时钟设置部分

用gedit打开cpu/arm920t/start.S,定位到134行附近,如下代码

#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
 /* turn off the watchdog */

由于2410和2440的寄存器及地址大部分是一致的,所以这里就直接在2410的基础上再加上对2440的支持即可,修改后代码如下:

#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)
 /* turn off the watchdog */

 ... ...

# if defined(CONFIG_S3C2410)
    ldr  r1, =0x3ff
    ldr  r0, =INTSUBMSK
    str  r1, [r0]
# endif

# if defined(CONFIG_S3C2440)//添加s3c2440的中断禁止部分
    ldr  r1, =0x7fff        //根据2440芯片手册,INTSUBMSK寄存器有15位可用   
    ldr  r0, =INTSUBMSK
    str  r1, [r0]
# endif

# if defined(CONFIG_S3C2440)   //添加s3c2440的时钟部分
#define MPLLCON   0x4C000004   //系统主频配置寄存器基地址
#define UPLLCON   0x4C000008   //USB时钟频率配置寄存器基地址 
    ldr  r0, =CLKDIVN          //设置分频系数FCLK:HCLK:PCLK = 1:4:8
    mov  r1, #5
    str  r1, [r0]
    ldr  r0, =MPLLCON  //设置系统主频为405MHz  
    ldr  r1, =0x7F021  //这个值参考芯片手册“PLL VALUE SELECTION TABLE”部分
    str  r1, [r0]
    ldr  r0, =UPLLCON  //设置USB时钟频率为48MHz  
    ldr  r1, =0x38022  //这个值参考芯片手册“PLL VALUE SELECTION TABLE”部分
    str  r1, [r0]
# else //其他开发板的时钟部分
  /* FCLK:HCLK:PCLK = 1:2:4 */
 /* default FCLK is 202.8 MHz ! */
   ldr r0, =CLKDIVN
   mov r1, #3
   str r1, [r0]

   ldr  r0, =MPLLCON  //设置系统主频为202.8MHz
   ldr  r1, =0xa1031  //这个值参考芯片手册“PLL VALUE SELECTION TABLE”部分
   str  r1, [r0]
#endif
#endif /* CONFIG_S3C2400 || CONFIG_S3C2410 || CONFIG_S3C2440 */



【2】S3C2440的时钟部分除了在start.S中添加外,还要分别在board/samsung/mini2440/mini2440.c和cpu/arm920t/s3c24x0/speed.c中修改或添加部分代码。

(1)用gedit打开board/samsung/mini2440/mini2440.c,定位到33行,修改或添加如下内容:

//设置主频和USB时钟频率参数与start.S中的一致

#define FCLK_SPEED  2       //设置默认等于2

#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
#elif FCLK_SPEED==2        /* Fout = 405MHz */
#define M_MDIV    0x7F     //这三个值根据S3C2440芯片手册“PLL VALUE SELECTION TABLE”部分进行设置
#define M_PDIV    0x2
#define M_SDIV    0x1
#endif

#define USB_CLOCK 2        //设置默认等于2

#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
#elif USB_CLOCK==2         /* Fout = 48MHz */
#define U_M_MDIV    0x38   //这三个值根据S3C2440芯片手册“PLL VALUE SELECTION TABLE”部分进行设置
#define U_M_PDIV    0x2
#define U_M_SDIV    0x2
#endif

(2)用gedit打开cpu/arm920t/s3c24x0/speed.c,定位到69行加入如下代码

   m = ((r & 0xFF000) >> 12) + 8;
    p = ((r & 0x003F0) >> 4) + 2;
    s = r & 0x3;

//根据设置的分频系数FCLK:HCLK:PCLK = 1:4:8修改获取时钟频率的函数
#if defined(CONFIG_S3C2440)
    if(pllreg == MPLL)//参考S3C2440芯片手册上的公式:PLL=(2 * m * Fin)/(p * 2s)
        return((CONFIG_SYS_CLK_FREQ * m * 2) / (p << s));
    //else if (pllreg == UPLL) //warning: control reaches end of non-void function
#endif

    return((CONFIG_SYS_CLK_FREQ * m) / (p << s));

为什么要再返回时加一个判断呢?因为在2440中MPLL的时钟为UPLL时钟的2倍,在s3c2440的数据手册里的227页这样写到MPLL和UPLL的计算方法
MPLL Control Register
Mpll = (2 * m * Fin) / (p * 2s)
m = (MDIV + 8), p = (PDIV + 2), s = SDIV
UPLL Control Register
Upll = (m * Fin) / (p * 2s)
m = (MDIV + 8), p = (PDIV + 2), s = SDIV
这个就是修改此函数的缘由。

由于S3C2410和S3C2440的设置方法也不一样,所以get_HCLK函数也需要修改:

/* return HCLK frequency */
ulong get_HCLK(void)
{
    S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
#if defined(CONFIG_S3C2440)
 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);
    if ((clk_power->CLKDIVN & 0x6)==4) return((clk_power->CAMDIVN & 0x200) ? get_FCLK()/8 : get_FCLK()/4);
    return(get_FCLK());
    }
 else return(get_FCLK());
#else
    return((clk_power->CLKDIVN & 0x2) ? get_FCLK()/2 : get_FCLK());
#endif

 }

这里用到了将在include/s3c24x0.h文件里所添加的CAMDIVN 项,因为这一项的值决定了我们的时钟配置。
这样修改的原因是在s3c2440的数据手册的231页有这样一段话:
CLOCK DIVIDER CONTROL (CLKDIVN) REGISTER
Register Address R/W Description Reset Value
CLKDIVN 0x4C000014 R/W Clock divider control register 0x00000000
CLKDIVN               Bit            Description
DIVN_UPLL           [3]            UCLK select register(UCLK must be 48MHz for USB)
0:UCLK = UPLL clock
1:UCLK = UPLL clock / 2
Set to 0, when UPLL clock is set as 48Mhz
Set to 1. when UPLL clock is set as 96Mhz.
HDIVN                  [2:1]       00 : HCLK = FCLK/1.
01 : HCLK = FCLK/2.
10 : HCLK = FCLK/4 when CAMDIVN[9] = 0.
HCLK= FCLK/8 when CAMDIVN[9] = 1.
11 : HCLK = FCLK/3 when CAMDIVN[8] = 0.
HCLK = FCLK/6 when CAMDIVN[8] = 1.
PDIVN [0]                              0: PCLK has the clock same as the HCLK/1.
1: PCLK has the clock same as the HCLK/2.
我们到底应该返回FCLK的几分之一在这里就有秒数,其中必须根据HDIVN 的值与CAMDIVN的值来判断。

【3】加入LED进度指示,增加控制台显示信息

作用是显示代码进度,对 Debug 有帮助。

(1)代码在跳转到第二阶段代码start_armboot 函数前会亮起一个LED 灯,打开cpu/arm920t/start.S,定位到240行附近,修改如下:

clbss_l: str r2, [r0]  /* clear loop...                    */
       add r0, r0, #4
       cmp r0, r1
       ble clbss_l

#if defined(CONFIG_MINI2440_LED)
//根据mini2440原理图可知LED分别由S3C2440的PB5、6、7、8口来控制,

//以下是PB端口寄存器基地址(查2440的DataSheet得知)
#define GPBCON 0x56000010
#define GPBDAT 0x56000014
#define GPBUP  0x56000018 
    //以下对寄存器的操作参照S3C2440的DataSheet进行操作
    ldr r0, =GPBUP
    ldr r1, =0x7FF    //即:二进制11111111111,关闭PB口上拉
    str r1, [r0]

    ldr r0, =GPBCON   //配置PB5、6、7、8为输出口,对应PBCON寄存器的第10-17位
    ldr r1, =0x154FD  //即:二进制010101010011111101
    str r1, [r0]

    ldr r0, =GPBDAT
    ldr r1, =0x1C0    //即:二进制111000000,PB5设为低电平,6、7、8为高电平
    str r1, [r0]

#endif
//此段代码使u-boot启动后,开发板上的LED1被点亮,而LED2、LED3、LED4不亮

    ldr pc, _start_armboot

_start_armboot: .word start_armboot
#define STACK_BASE 0x33f00000
#define STACK_SIZE 0x10000
.align 2
           DW_STACK_START: .word STACK_BASE+STACK_SIZE-4


(2)在完成board_init函数初始化时的点亮第二个LED,修改如下:

打开board/samsung/mini2440/mini2440.c,定位到100行附近,修改如下:

/* set up the I/O ports */
 gpio->GPACON = 0x007FFFFF;
#if defined(CONFIG_MINI2440) 
 gpio->GPBCON = 0x00295551;
#else
 gpio->GPBCON = 0x00044556;
#endif
 gpio->GPBUP = 0x000007FF;
 gpio->GPCCON = 0xAAAAAAAA;
 gpio->GPCUP = 0xFFFFFFFF;
 gpio->GPDCON = 0xAAAAAAAA;
 gpio->GPDUP = 0xFFFFFFFF;
 gpio->GPECON = 0xAAAAAAAA;
 gpio->GPEUP = 0x0000FFFF;
 gpio->GPFCON = 0x000055AA;
 gpio->GPFUP = 0x000000FF;
 gpio->GPGCON = 0xFF95FFBA;
 gpio->GPGUP = 0x0000FFFF;
 gpio->GPHCON = 0x0016FAAA;
 gpio->GPHUP = 0x000007FF;

 gpio->EXTINT0=0x22222222;
 gpio->EXTINT1=0x22222222;
 gpio->EXTINT2=0x22222222;

 /* arch number of SMDK2410-Board */
 gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;

 /* adress of boot parameters */
 gd->bd->bi_boot_params = 0x30000100;

 icache_enable();
 dcache_enable();
#if defined(CONFIG_MINI2440_LED)
 gpio->GPBDAT = 0x00000181;//GPB0=buzzer 
#endif

int dram_init (void)
{
 gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
 gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;

 return 0;
}

(3)在初始化console后点亮一个LED3,进入命令行之前点亮LED4

打开/lib_arm/board.c,定位到52行,修改如下:

#include <nand.h>
#include <onenand_uboot.h>
#include <mmc.h>
#include <s3c2410.h>    //aniu12

#ifdef CONFIG_DRIVER_SMC91111

定位到121行,注释掉下面代码:

#if 0
/************************************************************************
 * Coloured LED functionality
 ************************************************************************
 * May be supplied by boards if desired
 */
void inline __coloured_LED_init (void) {}
//void inline coloured_LED_init (void) __attribute__((weak, alias("__coloured_LED_init")));
void inline __red_LED_on (void) {}
//void inline red_LED_on (void) __attribute__((weak, alias("__red_LED_on")));
void inline __red_LED_off(void) {}
//void inline red_LED_off(void)      __attribute__((weak, alias("__red_LED_off")));
void inline __green_LED_on(void) {}
//void inline green_LED_on(void) __attribute__((weak, alias("__green_LED_on")));
void inline __green_LED_off(void) {}
//void inline green_LED_off(void)__attribute__((weak, alias("__green_LED_off")));
void inline __yellow_LED_on(void) {}
//void inline yellow_LED_on(void)__attribute__((weak, alias("__yellow_LED_on")));
void inline __yellow_LED_off(void) {}
//void inline yellow_LED_off(void)__attribute__((weak, alias("__yellow_LED_off")));
void inline __blue_LED_on(void) {}
//void inline blue_LED_on(void)__attribute__((weak, alias("__blue_LED_on")));
void inline __blue_LED_off(void) {}
//void inline blue_LED_off(void)__attribute__((weak, alias("__blue_LED_off")));
#endif

定位到171行附近:修改如下:

static int display_banner (void)
{
#if defined(CONFIG_MINI2440_LED)  
 S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();
  gpio->GPBDAT = 0x101; // 
#endif 
 printf ("\n\n%s\n\n", version_string);
 printf (" modified by singleboy (singleboy@163.com)\n"); //display on serial console
 printf (" Love Linux forever!!\n\n");
 debug ("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);
}

定位到314行附近:修改如下:

#if defined(CONFIG_VFD) || defined(CONFIG_LCD)
 unsigned long addr;
#endif
#if defined(CONFIG_MINI2440_LED)  
  S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();
#endif
 /* Pointer is writable since we allocated a register for it */
 gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));
 /* compiler optimization barrier needed for GCC >= 3.4 */
 __asm__ __volatile__("": : :"memory");
定位到479行附近,修改如下:

#if defined(CONFIG_RESET_PHY_R)
 debug ("Reset Ethernet PHY\n");
 reset_phy();
#endif
#endif
#if defined(CONFIG_MINI2440_LED)  
  gpio->GPBDAT = 0x0; //
#endif
 /* main_loop() can return to retry autoboot, if so just run it again. */
 for (;;) {
  main_loop ();
 }

【4】如果这时进行编译,是不能通过的,对于S3C2440,很多代码是借用S3C2410 的,所以要在所有条件编译中有CONFIG_S3C2410 的地方添CONFIG_S3C2440,这样这些代码才会编译进来。一个简单的方法就是在代码中搜索出所有的CONFIG_S3C2410,并根据实际情况修改。但要注意不是所有的2410的寄存器都和2440兼容,还必须根据两个芯片的不同来分布做出修改,比如PLL的操作代码和NAND Flash Controller的操作代码。现分别修改如下:

(1)打开include/common.h,定位到496行,修改结果如下:

#ifdef CONFIG_4xx
ulong get_OPB_freq (void);
ulong get_PCI_freq (void);
#endif
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410) ||  defined(CONFIG_S3C2440) || \
 defined(CONFIG_LH7A40X) || defined(CONFIG_S3C6400)
ulong get_FCLK (void);
ulong get_HCLK (void);

(2)打开include/s3c24x0.h文件,分别定位到85、95、99、110、148、404行,将“#ifdef CONFIG_S3C2410”改为

#if defined(CONFIG_S3C2410) || defined (CONFIG_S3C2440) //#ifdef CONFIG_S3C2410

然后在下面结构中加入2440 的NAND FLASH 的数据结构成员CAMDIVN定义(第128行附近):

/* CLOCK & POWER MANAGEMENT (see S3C2400 manual chapter 6) */
/*                          (see S3C2410 manual chapter 7) */
typedef struct {
 S3C24X0_REG32 LOCKTIME;
 S3C24X0_REG32 MPLLCON;
 S3C24X0_REG32 UPLLCON;
 S3C24X0_REG32 CLKCON;
 S3C24X0_REG32 CLKSLOW;
 S3C24X0_REG32 CLKDIVN;
#if defined (CONFIG_S3C2440)
  S3C24X0_REG32 CAMDIVN;
#endif
} /*__attribute__((__packed__))*/ S3C24X0_CLOCK_POWER;

这个结构是用来封装时钟寄存器的,我们要在其中增加一项CAMDIVN寄存器是2410所没有的,而2440在配置时钟的时候又必须用到,看名字我们就知道是用来配置CAMERA时钟的,也就是配置摄像头的时钟的。

加入Nand flash寄存器定义(第160行附近):

#if defined(CONFIG_S3C2410)
/* NAND FLASH (see S3C2410 manual chapter 6) */
typedef struct {
 S3C24X0_REG32 NFCONF;
 S3C24X0_REG32 NFCMD;
 S3C24X0_REG32 NFADDR;
 S3C24X0_REG32 NFDATA;
 S3C24X0_REG32 NFSTAT;
 S3C24X0_REG32 NFECC;
} /*__attribute__((__packed__))*/ S3C2410_NAND;
#endif
#if defined (CONFIG_S3C2440)
/* NAND FLASH (see S3C2440 manual chapter 6) */
typedef struct {
        S3C24X0_REG32   NFCONF;
        S3C24X0_REG32   NFCONT;
        S3C24X0_REG32   NFCMD;
        S3C24X0_REG32   NFADDR;
        S3C24X0_REG32   NFDATA;
        S3C24X0_REG32   NFMECCD0;
        S3C24X0_REG32   NFMECCD1;
        S3C24X0_REG32   NFSECCD;
        S3C24X0_REG32   NFSTAT;
        S3C24X0_REG32   NFESTAT0;
        S3C24X0_REG32   NFESTAT1;
        S3C24X0_REG32   NFMECC0;
        S3C24X0_REG32   NFMECC1;
        S3C24X0_REG32   NFSECC;
        S3C24X0_REG32   NFSBLK;
        S3C24X0_REG32   NFEBLK;
} /*__attribute__((__packed__))*/ S3C2410_NAND;
#endif

定位到343行附近,加入下面代码:

#endif /*  __BIG_ENDIAN */
 //S3C24X0_USB_DEV_FIFOS fifo[5];
 //S3C24X0_USB_DEV_DMAS dma[5];
 S3C24X0_REG32   res17[8];
 S3C24X0_USB_DEV_FIFOS   fifo[5];
 S3C24X0_REG32   res18[11];
 S3C24X0_USB_DEV_DMAS    ep1;
 S3C24X0_USB_DEV_DMAS    ep2;
 S3C24X0_REG8    res19[16];
 S3C24X0_USB_DEV_DMAS    ep3;
 S3C24X0_USB_DEV_DMAS    ep4;
} /*__attribute__((__packed__))*/ S3C24X0_USB_DEVICE;

定位到486行附近,加入下面代码:

 S3C24X0_REG32 GSTATUS3;
 S3C24X0_REG32 GSTATUS4;
#if defined (CONFIG_S3C2440)
 S3C24X0_REG32 res9[3];
 S3C24X0_REG32 MSLCON;
 S3C24X0_REG32 GPJCON;
 S3C24X0_REG32 GPJDAT;
 S3C24X0_REG32 GPJUP;
#endif
#endif
} /*__attribute__((__packed__))*/ S3C24X0_GPIO;

定位到686行附近,修改如下:

S3C24X0_REG32 SDIDSTA;
 S3C24X0_REG32 SDIFSTA;
#if defined(CONFIG_S3C2410) 
#if 0 
#ifdef __BIG_ENDIAN
 S3C24X0_REG8 res[3];
 S3C24X0_REG8 SDIDAT;
#else
 S3C24X0_REG8 SDIDAT;
 S3C24X0_REG8 res[3];
#endif
#endif 
 S3C24X0_REG32 SDIDAT;
 S3C24X0_REG32 SDIIMSK;
#elif defined(CONFIG_S3C2440)
 S3C24X0_REG32 SDIIMSK;
 S3C24X0_REG32 SDIDAT;
#endif
} /*__attribute__((__packed__))*/ S3C2410_SDI;

(3)打开cpu/arm920t/s3c24x0/interrupts.c文件,定位到第36行作如下修改:

#if defined(CONFIG_S3C2400)
#include <s3c2400.h>
#elif defined(CONFIG_S3C2410) || defined (CONFIG_S3C2440)
#include <s3c2410.h>
#endif

(4)打开cpu/arm920t/s3c24x0/timer.c文件分别定位到33行和37行,修改如下:

#include <common.h>
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440) || defined(CONFIG_TRAB)
#if defined(CONFIG_S3C2400)
#include <s3c2400.h>
#elif defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)
#include <s3c2410.h>
#endif
在180行添加:
#if defined(CONFIG_SMDK2400) || defined(CONFIG_TRAB)
 tbclk = timer_load_val * 100;
#elif defined(CONFIG_SBC2410X) || \
      defined(CONFIG_SMDK2410) || \
      defined(CONFIG_MINI2440) || \
      defined(CONFIG_VCMA9)
 tbclk = CONFIG_SYS_HZ;
#else
# error "tbclk not configured"
#endif

(5)打开cpu/arm920t/s3c24x0/speed.c,分别定位到33行和37行,修改如下:

#include <common.h>
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440) || defined(CONFIG_TRAB)
#if defined(CONFIG_S3C2400)
#include <s3c2400.h>
#elif defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)
#include <s3c2410.h>
#endif

(6)打开/cpu/arm920t/s3c24x0/usb.c文件,定位到第27、31行:
#if defined(CONFIG_USB_OHCI_NEW) && defined(CONFIG_SYS_USB_OHCI_CPU_INIT)
# if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)
#if defined(CONFIG_S3C2400)
# include <s3c2400.h>
#elif defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)
# include <s3c2410.h>
#endif

(7)打开drivers/serial/serial_s3c24x0.c,定位到24行,修改如下:

#include <common.h>
#if defined(CONFIG_S3C2400) || defined(CONFIG_TRAB)
#include <s3c2400.h>
#elif defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)
#include <s3c2410.h>
#endif

(8)打开drivers/rtc/s3c24x0_rtc.c文件,定位到第35行:

#if defined(CONFIG_S3C2400)
#include <s3c2400.h>
#elif defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)
#include <s3c2410.h>
#endif

(9)打开/drivers/i2c/s3c24x0_i2c.c文件,定位到32行:

#include <common.h>
#if defined(CONFIG_S3C2400)
#include <s3c2400.h>
#elif defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440)
#include <s3c2410.h>
#endif
#include <i2c.h>
再分别定位到第63、82、139、147、171行:
#if defined(CONFIG_S3C2410) || defined (CONFIG_S3C2440) //#ifdef CONFIG_S3C2410
 return (gpio->GPEDAT & 0x8000) >> 15;
#endif
... ...
#if defined(CONFIG_S3C2410) || defined (CONFIG_S3C2440) //#ifdef CONFIG_S3C2410
 gpio->GPEDAT = (gpio->GPEDAT & ~0x4000) | (x&1) << 14;
#endif
... ...
#if defined(CONFIG_S3C2410) || defined (CONFIG_S3C2440) //#ifdef CONFIG_S3C2410
  /* set I2CSDA and I2CSCL (GPE15, GPE14) to GPIO */
  gpio->GPECON = (gpio->GPECON & ~0xF0000000) | 0x10000000;
#endif
... ...
  /* restore pin functions */
#if defined(CONFIG_S3C2410) || defined (CONFIG_S3C2440) //#ifdef CONFIG_S3C2410
  gpio->GPECON = old_gpecon;
#endif

【5】在mini2440.h头文件中加入宏定义

用gedit打开include/configs/mini2440.h头文件,在其中添加CONFIG_S3C2440宏,修改后代码如下所示

#define CONFIG_ARM920T  1 /* This is an ARM920T Core */
//#define CONFIG_S3C2410  1 /* in a SAMSUNG S3C2410 SoC     */
//#define CONFIG_SMDK2410  1 /* on a SAMSUNG SMDK2410 Board  */
#define CONFIG_S3C2440  1 /* in a SAMSUNG S3C2440 SoC     */
#define CONFIG_MINI2440  1 /* on a SAMSUNG MINI2440 Board  */
#define CONFIG_MINI2440_LED 1  /* mini2440 led on/off */

/* input clock of PLL */
#define CONFIG_SYS_CLK_FREQ 12000000/* the SMDK2410 has 12MHz input clock */

【6】为了能够让u-boot.bin下载到内存中运行,需要注释掉CPU和DRAM初始化部分

用gedit打开cpu/arm920t/start.S,定位到如下代码

#ifndef CONFIG_SKIP_LOWLEVEL_INIT
 bl cpu_init_crit
#endif

将其注释掉,屏蔽u-boot对CPU、RAM的初始化

//#ifndef CONFIG_SKIP_LOWLEVEL_INIT
 //bl cpu_init_crit
//#endif

1.3重新编译,运行测试

[root@localhost u-boot-2009.08]# make clean
Generating include/autoconf.mk
[root@localhost u-boot-2009.08]# make

... ...

In file included from speed.c:38:
/root/u-boot-test/u-boot-2009.08/include/s3c2410.h:96: error: expected '=', ',', ';', 'asm' or '__attribute__' before '*' token
make[1]: *** [speed.o] 错误 1
make[1]: Leaving directory `/root/u-boot-test/u-boot-2009.08/cpu/arm920t/s3c24x0'
make: *** [cpu/arm920t/s3c24x0/libs3c24x0.a] 错误 2
错误发生在/include/s3c2410.h:96行S3C2410_NAND结构定义,将其误写成S3C2440_NAND了,进行更正后重新编译。
编译完成之后,利用mini2440自带的supervivi将根目录u-boot-2009.08下的u-boot.bin文件下载到DRAM中运行测试。确认开发板与主机之间的网线和串口线已经正确连接,给开发板上电,在suppervivi启动完成后,在与开发板相连的终端中执行如下命令:

##### FriendlyARM BIOS 2.0 for 2440 #####
[x] format NAND FLASH for Linux
[v] Download vivi
[k] Download linux kernel
[y] Download root_yaffs image
[a] Absolute User Application
[n] Download Nboot for WinCE
[l] Download WinCE boot-logo
[w] Download WinCE NK.bin
[d] Download & Run
[z] Download zImage into RAM
[g] Boot linux from RAM
[f] Format the nand flash
[b] Boot the system
[s] Set the boot parameters
[u] Backup NAND Flash to HOST through USB(upload)
[r] Restore NAND Flash from HOST through USB
[q] Goto shell of vivi
Enter your selection: d
Clear the free memory
USB host is connected. Waiting a download.

Now, Downloading [ADDRESS:31000000h,TOTAL:99806]
RECEIVED FILE SIZE:   99806 (97KB/S, 1S)
Downloaded file at 0x30000000, size = 99796 bytes


U-Boot 2009.08 ( 5鏈?04 2011 - 17:04:11)

DRAM:  64 MB
Flash: 512 kB
*** Warning - bad CRC, using default environment

In:    serial
Out:   serial
Err:   serial
SMDK2410 #

到此可以看到u-boot的版本和编译时间-Boot 2009.08 ( 4鏈?22 2011 - 17:04:11),以及还未修改的提示符SMDK2410 #。同时还可以看到开发板上的第一个LED灯是亮的,其它三个是灭的。有关Warning ...相关的告警信息,将在下一节解决。

接下来将进入u-boot的第二阶段,为u-boot-2009.08增加norflash支持。

(二)增加nor flash功能 

         通常,在嵌入式bootloader中,有两种方式来引导启动内核:从Nor Flash启动和从Nand Flash启动。u-boot中默认是从Nor Flash启动,再从上一节测试运行的结果的中看,还发现几个问题:第一,我开发板的Nor Flash是2M的,而这里显示的是512kB;第二,出现Warning - bad CRC, using default environment的警告信息。不是u-boot默认是从Nor Flash启动的吗?为什么会有这些错误信息呢?这是因为我们还没有添加对我们自己的Nor Flash的支持,u-boot默认的是其他型号的Nor Flash,而我们的Nor Flash的型号是SST39VF1601。另外怎样将命令行提示符前面的SMDK2410变成我自己定义的呢?下面我们一一来解决这些问题。

2.1,修改u-boot源码,使其完全支持Nor Flash。

【1】添加对我们mini2440开发板上2M的Nor Flash(型号为SST39VF1601)的支持

在虽然 S3C2440 和S3C2410 对于Nor Flash 的链接都是一样的,但是SBC2410 使用的AMD 的Nor Flash 芯片,而mini2440 使用的SST 的Nor Flash。这两款芯片在写入时所使用的块大小、时序和指令代码有差别,所以必须根据芯片的数据手册进行修改。主要的差别请看数据手册的对比:
SST39VF1601:


Am29LV160:


在u-boot中对Nor Flash的操作分别有初始化、擦除和写入,所以我们主要修改与硬件密切相关的三个函数flash_init、flash_erase、write_hword。

用gedit打开board/samsung/mini2440/flash.c,定位到31行附近,修改如下:

#define FLASH_BANK_SIZE PHYS_FLASH_SIZE
//#define MAIN_SECT_SIZE  0x10000 /* 64 KB */
#define MAIN_SECT_SIZE     0x1000  //定义为4k,刚好是一个扇区的大小

flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS];

... ...
Nor Flash硬件连接


由原理图可知,一般NorFlash放在Bank0.所以CONFIG_SYS_BASE=0,但是开启mmu后baseaddr=地址0映射到的新地址。0x555<<1的原因是LADDR1与A0连接。也就是0x555表示片内第0x555个word(16bit)。

#define CMD_UNLOCK_BYPASS 0x00000020

#if defined(CONFIG_SST_VF1601)
#define MEM_FLASH_ADDR1  (*(volatile u16 *)(CONFIG_SYS_FLASH_BASE + (0x000005555 << 1)))
#define MEM_FLASH_ADDR2  (*(volatile u16 *)(CONFIG_SYS_FLASH_BASE + (0x000002AAA << 1)))
#else
#define MEM_FLASH_ADDR1  (*(volatile u16 *)(CONFIG_SYS_FLASH_BASE + (0x00000555 << 1)))
#define MEM_FLASH_ADDR2  (*(volatile u16 *)(CONFIG_SYS_FLASH_BASE + (0x000002AA << 1)))
#endif

#define BIT_ERASE_DONE  0x00000080
在flash_init 函数中修改或添加后代码如下:
ulong flash_init (void)
{
 int i, j;
 ulong size = 0;

for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) {
  ulong flashbase = 0;

... ...

#elif defined(CONFIG_AMD_LV800)
            (AMD_MANUFACT & FLASH_VENDMASK) |
            (AMD_ID_LV800B & FLASH_TYPEMASK);
#elif defined(CONFIG_SST_39VF1601)   
            (SST_MANUFACT & FLASH_VENDMASK) |
            (SST_ID_xF1601 & FLASH_TYPEMASK);
#else
#error "Unknown flash configured"
#endif

... ...

for (j = 0; j < flash_info[i].sector_count; j++) {
#ifndef CONFIG_SST_VF1601
   if (j <= 3) {
    /* 1st one is 16 KB */

... ...
#else
    flash_info[i].start[j] = flashbase + (j) * MAIN_SECT_SIZE;
#endif
 }
  size += flash_info[i].size;
 }

  ... ...

    return size;
}
在flash_print_info 函数中修改或添加后代码如下:
void flash_print_info (flash_info_t * info)
{
     int i;

    switch (info->flash_id & FLASH_VENDMASK) {
              case (AMD_MANUFACT & FLASH_VENDMASK):
              printf ("AMD: ");
              break;
              case (SST_MANUFACT & FLASH_VENDMASK):
              printf ("SST: ");
              break;

              default:
              printf ("Unknown Vendor ");
              break;
    }

    switch (info->flash_id & FLASH_TYPEMASK) {
               case (AMD_ID_LV400B & FLASH_TYPEMASK):
               printf ("1x Amd29LV400BB (4Mbit)\n");
               break;
               case (AMD_ID_LV800B & FLASH_TYPEMASK):
               printf ("1x Amd29LV800BB (8Mbit)\n");
               break;
               case (SST_ID_xF1601 & FLASH_TYPEMASK):
               printf ("1x SST39VF1601 (16Mbit)\n");
               break;
               default:
               printf ("Unknown Chip Type\n");
               goto Done;
               break;
    }
   ... ...
    Done:;
}
在flash_erase函数中修改或添加后代码如下:
int flash_erase (flash_info_t * info, int s_first, int s_last)
{
    ushort result;
    int iflag, cflag, prot, sect;
    int rc = ERR_OK;
    int chip;

     .... ....

    if ((s_first < 0) || (s_first > s_last)) {
    return ERR_INVAL;
    }

 if ((s_first < 0) || (s_first > s_last)) {
  return ERR_INVAL;
 }

#ifdef CONFIG_SST_VF1601
 if ((info->flash_id & FLASH_VENDMASK) !=
     (SST_MANUFACT & FLASH_VENDMASK)) {
  return ERR_UNKNOWN_FLASH_VENDOR;
 }
#else
 if ((info->flash_id & FLASH_VENDMASK) !=
     (AMD_MANUFACT & FLASH_VENDMASK)) {
  return ERR_UNKNOWN_FLASH_VENDOR;
 }
#endif
 prot = 0;

... ...

   if (info->protect[sect] == 0) { /* not protected */


   vu_short *addr = (vu_short *) (info->start[sect]);

   MEM_FLASH_ADDR1 = CMD_UNLOCK1;
   MEM_FLASH_ADDR2 = CMD_UNLOCK2;
   MEM_FLASH_ADDR1 = CMD_ERASE_SETUP;

   MEM_FLASH_ADDR1 = CMD_UNLOCK1;
   MEM_FLASH_ADDR2 = CMD_UNLOCK2;
   *addr = CMD_ERASE_CONFIRM;

   /* wait until flash is ready */

#if 0
  chip = 0;
   do {

... ...

    printf ("ok.\n");
   } else { /* it was protected */
       printf ("protected!\n");
   }
}
#endif
   while(1){
          unsigned short i;
          i = *((volatile unsigned short *)addr) & 0x40;
          if(i != (*((volatile unsigned short *)addr) & 0x40))
               continue;
          if((*((volatile unsigned short *)addr)) & 0x80)
               break;
    }
    printf ("ok.\n");
   } else { /* it was protected */
   printf ("protected!\n");
  }
}
在write_hword函数中修改或添加后代码如下:

static int write_hword (flash_info_t * info, ulong dest, ushort data)
{
    vu_short *addr = (vu_short *) dest;
    ushort result;
    int rc = ERR_OK;
    int cflag, iflag;
    int chip;

    ... ...

    MEM_FLASH_ADDR1 = CMD_UNLOCK1;
    MEM_FLASH_ADDR2 = CMD_UNLOCK2;
    //MEM_FLASH_ADDR1 = CMD_UNLOCK_BYPASS;
    MEM_FLASH_ADDR1 = CMD_PROGRAM; 
    //*addr = CMD_PROGRAM;
    *addr = data;

    /* arm simple, non interrupt dependent timer */
    reset_timer_masked ();

    /* wait until flash is ready */

#if 0 
 chip = 0;
   do {
   result = *addr;

... ...

    } while (!chip);

    *addr = CMD_READ_ARRAY;

    if (chip == ERR || *addr != data)
        rc = ERR_PROG_ERROR;
#endif
  while(1){
         unsigned short 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
          }
        }
   if (iflag)
     enable_interrupts ();

   if (cflag)
     icache_enable ();

    return rc;
}

修改完后,保存。

【2】修改lowlevel_init.S 文件

为了匹配mini2440 的存储器配置(总线上连接的Nor Flash 和SDRAM),需要修改lowlevel_init.S文件。这个所连接的Nor Flash位数有关。至于SDRAM的参数,可以从芯片手册查到。据说有人将其64MB的内存升到了128MB,其参数就是在这里修改的,有需要可以看MINI2440: Auto probe for SDRAM size 。以下的64MB 内存的参数修改:

打开/board/samsung/mini2440/lowlevel_init.S,定位到123行,修改如下:

/* REFRESH parameter */
#define REFEN   0x1 /* Refresh enable */
#define TREFMD   0x0 /* CBR(CAS before RAS)/Auto refresh */
//#define Trp   0x0 /* 2clk */
#define Trc    0x3 /* 7clk */
#define Tchr   0x2 /* 3clk */
#if defined(CONFIG_S3C2440)
#define Trp    0x2 /* 4clk */
#define REFCNT   1012
#else
#define Trp    0x0 /* 2clk */
#define REFCNT   1113 /* period=15.6us, HCLK=60Mhz, (2048+1-15.6*60) */
#endif
/**************************************/

在这个 lowlevel_init.S 有一个小bug,使得无法使用OpenJTAG 下载到内存中直接运行,修正如下:

lowlevel_init:
 /* memory control configuration */
 /* make r0 relative the current location so that it */
 /* reads SMRDATA out of FLASH rather than memory ! */
 ldr     r0, =SMRDATA
 //ldr r1, _TEXT_BASE
 ldr r1, =lowlevel_init
 sub r0, r0, r1
 adr r3, lowlevel_init /* r3 <- current position of code */
 add r0, r0, r3
 ldr r1, =BWSCON /* Bus Width Status Controller */
 add     r2, r0, #13*4

修改完后,保存。

【3】修改开发板终端中命令行提示符

用gedit打开include/configs/mini2440.h头文件,定位到116行,其中的 "SMDK2410 # " 就是开发板在终端中显示的提示符,现在将其改为 "[u-boot@MINI2440]# " ,如下所示

/*
 * Miscellaneous configurable options
 */
#define CONFIG_SYS_LONGHELP    /* undef to save memory  */
#define CONFIG_SYS_PROMPT  "[u-boot@MINI2440]# " /* Monitor Command Prompt */

【4】修改Nor Flash参数的部分定义

然后定位到157行附近,注释掉下面两个类型的Nor Flash设置,因为不是我们所使用的型号,然后加入新的定义,如下所示

 宏 CFG_ENV_ADDR定义了存放uboot变量的地址,换算一下为1MB-64KB=960KB地方,而实际uboot编译出来的大小仅为120KB 左右,由此可以得出,即使从新烧录了新编译的uboot到Nor flash内,也不会影响先前设定使用的uboot变量。

/*-----------------------------------------------------------------------
 * FLASH and environment organization
 */
#if 0 //注释掉下面两个类型的Nor Flash设置,因为不是我们所使用的型号
#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_SST_VF1601        1        //添加mini2440开发板Nor Flash设置

 ... ...

#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
#ifdef CONFIG_SST_VF1601
#define PHYS_FLASH_SIZE  0x00200000 /* 2MB */
#define CONFIG_SYS_MAX_FLASH_SECT (32) /* max number of sectors on one chip */
#define CONFIG_ENV_ADDR  (CONFIG_SYS_FLASH_BASE + CONFIG_ENV_OFFSET) /* addr of environment */
#endif
修改完后,保存。

【5】u-boot编译时间

u-boot的编译时间在/Makefile的384行附近:

$(VERSION_FILE):
  @( printf '#define U_BOOT_VERSION "U-Boot %s%s"\n' "$(U_BOOT_VERSION)" \
   '$(shell $(TOPDIR)/tools/setlocalversion $(TOPDIR))' ) > $@.tmp
  @cmp -s $@ $@.tmp && rm -f $@.tmp || mv -f $@.tmp $@

$(TIMESTAMP_FILE):
  @date +'#define U_BOOT_DATE "%b %d %C%y"' > $@
  @date +'#define U_BOOT_TIME "%T"' >> $@

而显示在终端或者LCD上的版本+编译时间的宏在/lib_arm/board.c的74行附近被引用,

打开/lib_arm/board.c的74行,定位到74行,修改如下:

#ifndef CONFIG_IDENT_STRING
#define CONFIG_IDENT_STRING ""
#endif

/*const char version_string[] = \
  U_BOOT_VERSION" (" U_BOOT_DATE " - " U_BOOT_TIME ")"CONFIG_IDENT_STRING; */
const char version_string[] = U_BOOT_VERSION;
这样可以屏蔽掉编译时间的显示。

2.2,重新编译,运行测试

[root@localhost u-boot-2009.08]# make clean
Generating include/autoconf.mk
[root@localhost u-boot-2009.08]# make
... ...

arm-linux-objcopy -O srec u-boot u-boot.srec
arm-linux-objcopy --gap-fill=0xff -O binary u-boot u-boot.bin
[root@localhost u-boot-2009.08]# 
编译完成后,将根目录u-boot-2009.08下的u-boot.bin文件复制到/tftpboot目录下。在与开发板相连的终端中执行如下命令:

##### FriendlyARM BIOS 2.0 for 2440 #####
[x] format NAND FLASH for Linux
[v] Download vivi
[k] Download linux kernel
[y] Download root_yaffs image
[a] Absolute User Application
[n] Download Nboot for WinCE
[l] Download WinCE boot-logo
[w] Download WinCE NK.bin
[d] Download & Run
[z] Download zImage into RAM
[g] Boot linux from RAM
[f] Format the nand flash
[b] Boot the system
[s] Set the boot parameters
[u] Backup NAND Flash to HOST through USB(upload)
[r] Restore NAND Flash from HOST through USB
[q] Goto shell of vivi
Enter your selection: d
Clear the free memory
USB host is connected. Waiting a download.

Now, Downloading [ADDRESS:31000000h,TOTAL:99674]
RECEIVED FILE SIZE:   99674 (97KB/S, 1S)
Downloaded file at 0x30000000, size = 99664 bytes


U-Boot 2009.08 ( 5鏈?05 2011 - 22:39:33)

DRAM:  64 MB
Flash:  2 MB
In:    serial
Out:   serial
Err:   serial
[u-boot@MINI2440]#

从运行结果看,Nor Flash的大小可以正确检测到了,命令行前面的名字也由原来的SMDK2410改成自己定义的[u-boot@MINI2440]# 了,但是有会出现bad CRC的警告信息,其实这并不是什么问题,只是还没有将环境变量设置到Nor Flash中,我们执行一下u-boot的:saveenv命令就可以了。如下所示:

[u-boot@MINI2440]# saveenv
Saving Environment to Flash...
Un-Protected 16 sectors
Erasing Flash...Erasing sector 64 ... ok.
Erasing sector 65 ... ok.
Erasing sector 66 ... ok.
Erasing sector 67 ... ok.
Erasing sector 68 ... ok.
Erasing sector 69 ... ok.
Erasing sector 70 ... ok.
Erasing sector 71 ... ok.
Erasing sector 72 ... ok.
Erasing sector 73 ... ok.
Erasing sector 74 ... ok.
Erasing sector 75 ... ok.
Erasing sector 76 ... ok.
Erasing sector 77 ... ok.
Erasing sector 78 ... ok.
Erasing sector 79 ... ok.
Erased 16 sectors
Writing to Flash... done
Protected 16 sectors
[u-boot@MINI2440]#

因为DRAM运行的是现在的u-boot,其下载命令还未实现,需要给开发板重新上电,再重新下载u-boot.bin到DRAM中运行

Enter your selection: d
Clear the free memory
USB host is connected. Waiting a download.

Now, Downloading [ADDRESS:31000000h,TOTAL:99682]
RECEIVED FILE SIZE:   99682 (97KB/S, 1S)
Downloaded file at 0x30000000, size = 99672 bytes


U-Boot 2009.08 ( 5鏈?05 2011 - 22:50:40)

DRAM:  64 MB
Flash:  2 MB
In:    serial
Out:   serial
Err:   serial
[u-boot@MINI2440]#

可以观察到不会出现警告信息了,这时候u-boot已经对我们开发板上的Nor Flash完全支持了。

接下来将进入u-boot的第三阶段,为u-boot-2009.08增加nand flash支持。

问题记录:env_flash.c:67: error: 'CONFIG_ENV_ADDR' undeclared here (not in a function)出现这个错误。无法排错。

解决:在mini2440.h里: #define CONFIG_ENV_ADDR         (CONFIG_SYS_FLASH_BASE + CONFIG_ENV_OFFSET),没有对CONFIG_ENV_OFFSET进行定义,导致这个问题,我按照《ARM嵌入式系统移植实战开发》中给的:#define CONFIG_ENV_ADDR         (CONFIG_SYS_FLASH_BASE + 0x80000)后make没有问题,

(三)增加nand flash功能  

         目前u-boot中还没有对2440上Nand Flash的支持,也就是说要想u-boot从Nand Flash上启动得自己去实现了。在做u-boot移植的时候,多数人使用的是Nand flash启动或Nar Flash启动。这样u-boot就只能在Nand flash或Nor flash。那么我们如何让我们的u-boot在Nand flash或Nor flash都能使用。

3.1,Nand flash或Nor flash双启动原理

首先,看一下我们熟悉的u-boot启动的时候执行的一段程序,这段程序一般存放在Nand flash中或Nor flash中。我们所说的Nand flash启动或Nor flash启动主要是涉及到一段搬移代码。这段搬移代码的功能是u-boot自己把自己搬移到内存中执行。如下是Nor flash启动中的这段搬移代码(这里以s3c2410为例)

  relocate:                        /* relocate U-Boot to RAM */
  adr r0, _start        /* r0 <- current position of code */
  ldr r1, _TEXT_BASE        /* test if we run from flash or RAM */
  cmp r0, r1                        /* don't reloc during debug */
  beq stack_setup
  ldr r2, _armboot_start
  ldr r3, _bss_start
  sub r2, r3, r2                /* r2 <- size of armboot */
  add r2, r0, r2                /* r2 <- source end address */
  copy_loop:
  ldmia r0!, {r3-r10}                /* copy from source address [r0] */
  stmia r1!, {r3-r10}                /* copy to target address [r1] */
  cmp r0, r2                /* until source end addreee [r2] */
  ble copy_loop
  #endif        /* CONFIG_SKIP_RELOCATE_UBOOT */
  #endif

上面这段代码就是把u-boot搬移到内存。而不同的启动方式区别也就在这段代码上,如果我们这里是Nand flash启动的话我们也需要写相同功能的代码,不同的是对于Nand的操作和Nor的操作是完全不同的,选择Nor flash启动是将Nor flash映射到片选0上也就是0x0地址而选择Nand flash启动则是将CPU的片内RAM(4K)映射到0地址,通过Nand flash控制器操作Nand flash。我们这里讨论如何实现Nand 和 Nor双启动。下面我们看看这两种启动的映射关系。


同时我们可以看到: 总线宽度和等待控制寄存器:


在系统重启时会扫描BWSCON的状态,而BWSCON的其他位的初始状态都是0,只有DW0(BWSCON[2:1])的值由OM[1:0]来决定,通过上面的2个图我们可以发现,我们可以通过判断BWSCON的第2位、第3位   {DW0(BWSCON[2:1])}的值,判断是Nor flash启动还是Nand Flash启动。可以启动代码之前添加如下代码,来判断是Nor flash启动还是Nand Flash启动。

 # define BWSCON   0x48000000 
        ldr r0,=BWSCON
        ldr r0,[r0]
        and r0,r0,#6
        cmp  r0, #0  
        bne  relocate

 ///
     //nand_boot
  //Nand 搬移代码
  relocate:
       //nor_boot
      //Nor 搬移代码
有了上面这段代码,就可以实现双启动了,只要再适当的添加对应的功能我们的uboot就完成了

 3.2,为u-boot增加对nand flash支持实际操作

【1】修改代码重定向部分,增加Nor flash 和Nand flash 双启动

修改cpu/arm920t/start.S文件,为使u-boot从Nand Flash启动,需要将下面注释掉的CPU和DRAM初始化部分还原。

用gedit打开cpu/arm920t/start.S,定位到199行附近,找到如下代码

//#ifndef CONFIG_SKIP_LOWLEVEL_INIT
 //bl cpu_init_crit
//#endif

将注释掉的这段代码恢复,修改如下:

#ifndef CONFIG_SKIP_LOWLEVEL_INIT
 bl cpu_init_crit
#endif

Tekkaman Ninja从2009.08 开始就在启动时增加了启动时检测自身是否已经在SDRAM中(通过OpenJTAG载入),以及芯片是Norboot还是Nandboot的机制,来决定代码重定向的方式,使得编译出的bin文件可以同时烧入Nand Flash和Nor flash,以及被OpenJTAG载入进行调试。至于这部分的原理,在Tekkaman Ninja的博客文章《在U-boot下实现自动识别启动Flash的原理(针对S3C24x0)》中有详细叙述。这部分代码修改后结果如下:

(1)判断当前代码位置,如果在内存,直接跳到stack_setup

//#ifndef CONFIG_SKIP_RELOCATE_UBOOT
//relocate: /* relocate U-Boot to RAM */
/***************** Check the code position begain*******************************/
 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
/****************** Check the code position end ********************************/

(2)如果不是在代码当前位置不再内存中,就判断启动的Flash:Nand 或者Nor
/***************** Check the boot flash begain **********************************/
# define BWSCON  0x48000000 
        ldr r0,=BWSCON
        ldr r0,[r0]
        ands r0,r0,#6
        cmp  r0, #0  
        bne  relocate


 /* recovery  */
 ldr r0, =(0xdeadbeef)
 ldr r1, =( (4<<28)|(3<<4)|(3<<2) )
 str r0, [r1]
/***************** check the boot flash end ************************************/

(3)如果判断是在Nand Flash中启动的话,那么nand  Flash搬移代码如下:
定义u-boot在nand flash中存放的长度为#define LENGTH_UBOOT 0x60000,可以方便修改u-boot因为裁剪和增添大小的改变而占的长度。

// copy U-Boot to RAM  form   Nand Flash

/***************** NAND BOOT start *************************************************/

#define LENGTH_UBOOT 0x60000
#define NAND_CTL_BASE 0x4E000000

#ifdef CONFIG_S3C2440
/* Offset */
#define oNFCONF 0x00
#define oNFCONT 0x04
#define oNFCMD 0x08
#define oNFSTAT 0x20

 @ reset NAND
 mov r1, #NAND_CTL_BASE
 ldr r2, =( (7<<12)|(7<<8)|(7<<4)|(0<<0) )
 str r2, [r1, #oNFCONF]
 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]

 mov r3, #0 @ wait
nand1:
 add r3, r3, #0x1
 cmp r3, #0xa
 blt nand1

nand2:
 ldr r2, [r1, #oNFSTAT] @ wait ready
 tst r2, #0x4
 beq nand2


 ldr r2, [r1, #oNFCONT]
 orr r2, r2, #0x2 @ Flash Memory Chip Disable
 str r2, [r1, #oNFCONT]

 @ get read to call C functions (for nand_read())
 ldr sp, DW_STACK_START @ setup stack pointer
 mov fp, #0 @ no previous frame, so fp=0

 @ copy U-Boot to RAM
 ldr r0, =TEXT_BASE
 mov r1, #0x0
 mov r2, #LENGTH_UBOOT
 bl nand_read_ll
 tst r0, #0x0
 beq ok_nand_read

bad_nand_read:
loop2:
 b loop2 @ infinite loop
ok_nand_read:
 @ verify
 mov r0, #0
 ldr r1, =TEXT_BASE
 mov r2, #0x400 @ 4 bytes * 1024 = 4K-bytes
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

#ifdef CONFIG_S3C2410

/* Offset */
#define oNFCONF 0x00
#define oNFCMD 0x04
#define oNFSTAT 0x10

 @ reset NAND
 mov r1, #NAND_CTL_BASE
 ldr r2, =0xf830 @ initial value
 str r2, [r1, #oNFCONF]
 ldr r2, [r1, #oNFCONF]
 bic r2, r2, #0x800 @ enable chip
 str r2, [r1, #oNFCONF]
 mov r2, #0xff  @ RESET command
 strb r2, [r1, #oNFCMD]


 mov r3, #0 @ wait
nand1:
 add r3, r3, #0x1
 cmp r3, #0xa
 blt nand1

nand2:
 ldr r2, [r1, #oNFSTAT] @ wait ready
 tst r2, #0x1
 beq nand2

 ldr r2, [r1, #oNFCONF]
 orr r2, r2, #0x800 @ disable chip
 str r2, [r1, #oNFCONF]

 @ get read to call C functions (for nand_read())
 ldr sp, DW_STACK_START @ setup stack pointer
 mov fp, #0 @ no previous frame, so fp=0

 @ copy U-Boot to RAM
 ldr r0, =TEXT_BASE
 mov r1, #0x0
 mov r2, #LENGTH_UBOOT
 bl nand_read_ll
 tst r0, #0x0
 beq ok_nand_read

bad_nand_read:
loop2:
 b loop2 @ infinite loop


ok_nand_read:
 @ verify
 mov r0, #0
 ldr r1, =TEXT_BASE
 mov r2, #0x400 @ 4 bytes * 1024 = 4K-bytes
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
/***************** NAND BOOT end *************************************************/


(4)如果判断是在Nor  Flash中启动的话,那么nor  Flash搬移代码如下:

// copy U-Boot to RAM  form   Nor  Flash

/***************** NOR BOOT start *************************************************/
relocate:    /* relocate U-Boot to RAM     */
      /*********** CHECK_FOR_MAGIC_NUMBER***************/
 ldr r1, =(0xdeadbeef)
 cmp r0, r1
 bne loop3
      /*********** CHECK_FOR_MAGIC_NUMBER***************/
 adr r0, _start  /* r0 <- current position of code   */
 ldr r1, _TEXT_BASE  /* test if we run from flash or RAM */
 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 */ 
/***************** NOR BOOT end********************************************/


 /* 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 

... ...

【2】在上面添加的代码中有一个跳转:bl nand_read_ll ,它跳入是新增的C 语言文件board/samsung/mini2440/nand_read.c中的函数,这个文件原本是用vivi 的代码,后来经过openmoko 的修改,并支持不同的Nand Flash 芯片,我又这里多加了几个个芯片ID以支持所有mini2440 的Nand Flash。代码如下:

/*
 * nand_read.c: Simple NAND read functions for booting from NAND
 *
 * This is used by cpu/arm920/start.S assembler code,
 * and the board-specific linker script must make sure this
 * file is linked within the first 4kB of NAND flash.
 *
 * Taken from GPLv2 licensed vivi bootloader,
 * Copyright (C) 2002 MIZI Research, Inc.
 *
 * Author: Hwang, Chideok <hwang@mizi.com>
 * Date  : $Date: 2004/02/04 10:37:37 $
 *
 * u-boot integration and bad-block skipping (C) 2006 by OpenMoko, Inc.
 * Author: Harald Welte <laforge@openmoko.org>
 */

#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))
#define NF_BASE  0x4e000000
#if defined(CONFIG_S3C2410)
#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 NFSTAT_BUSY 1
#define nand_select() (NFCONF &= ~0x800)
#define nand_deselect() (NFCONF |= 0x800)
#define nand_clear_RnB() do {} while (0)
#elif defined(CONFIG_S3C2440) || defined(CONFIG_S3C2442)
#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 NFDATA16 __REGw(NF_BASE + 0x10)
#define NFSTAT  __REGb(NF_BASE + 0x20)
#define NFSTAT_BUSY 1
#define nand_select() (NFCONT &= ~(1 << 1))
#define nand_deselect() (NFCONT |= (1 << 1))
#define nand_clear_RnB() (NFSTAT |= (1 << 2))
#endif

static inline void nand_wait(void)
{
 int i;

 while (!(NFSTAT & NFSTAT_BUSY))
  for (i=0; i<10; i++);
}

struct boot_nand_t {
 int page_size;
 int block_size;
 int bad_block_offset;
// unsigned long size;
};

#if 0
#if defined(CONFIG_S3C2410) || defined(CONFIG_MINI2440)
/* configuration for 2410 with 512byte sized flash */
#define NAND_PAGE_SIZE  512
#define BAD_BLOCK_OFFSET 5
#define NAND_BLOCK_MASK  (NAND_PAGE_SIZE - 1)
#define NAND_BLOCK_SIZE  0x4000
#else
/* configuration for 2440 with 2048byte sized flash */
#define NAND_5_ADDR_CYCLE
#define NAND_PAGE_SIZE  2048
#define BAD_BLOCK_OFFSET NAND_PAGE_SIZE
#define NAND_BLOCK_MASK  (NAND_PAGE_SIZE - 1)
#define NAND_BLOCK_SIZE  (NAND_PAGE_SIZE * 64)
#endif

/* compile time failure in case of an invalid configuration */
#if defined(CONFIG_S3C2410) && (NAND_PAGE_SIZE != 512)
#error "S3C2410 does not support nand page size != 512"
#endif
#endif

static int is_bad_block(struct boot_nand_t * nand, unsigned long i)
{
 unsigned char data;
 unsigned long page_num;

 nand_clear_RnB();
 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 int nand_read_page_ll(struct boot_nand_t * nand, unsigned char *buf, unsigned long addr)
{
 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;
 } else {
  return -1;
 }
 nand_wait();

#if defined(CONFIG_S3C2410)
 for (i = 0; i < nand->page_size; i++) {
  *buf = (NFDATA & 0xff);
  buf++;
 }
#elif defined(CONFIG_S3C2440) || defined(CONFIG_S3C2442)
 for (i = 0; i < (nand->page_size>>1); i++) {
  *ptr16 = NFDATA16;
  ptr16++;
 }
#endif

 return nand->page_size;
}

static unsigned short nand_read_id(void)
{
 unsigned short res = 0;
 NFCMD = NAND_CMD_READID;
 NFADDR = 0;
 res = NFDATA;
 res = (res << 8) | NFDATA;
 return res;
}

extern unsigned int dynpart_size[];

/* low level nand read function */
int nand_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 if nand id is misread */
  unsigned short * nid = (unsigned short *)0x31fffff0;
  *nid = nand_id;
 }

       if (nand_id == 0xec76   /* Samsung K91208 */
           ||nand_id == 0xad76 ) { /*Hynix HY27US08121A*/
  nand.page_size = 512;
  nand.block_size = 16 * 1024;
  nand.bad_block_offset = 5;
 // nand.size = 0x4000000;
 } else if (nand_id == 0xecf1  /* Samsung K9F1G08U0B */
     ||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);) {
#ifdef CONFIG_S3C2410_NAND_SKIP_BAD
 //if (i & (nand.block_size-1)== 0) {//warning: suggest parentheses around comparison in operand of '&'
 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;
}

然后保存为nand_read.c,还要记得路径是在board/samsung/mini2440/目录下。

注意:上面这段代码中对Nand进行寻址的部分,这跟具体的Nand Flash的寻址方式有关。根据我们开发板上的Nand Flash(K9F1G08U0C)数据手册得知,片内寻址是采用27位地址形式。从第0位开始分四次通过I/O0-I/O7进行传送,并进行片内寻址。具体含义和结构图如下(相关概念参考Nand数据手册):

0-11位:Y-Buffers偏移地址

12-27位:X-Buffers偏移地址


【3】在board/samsung/mini2440/Makefile中添加nand_read.c的编译选项,使他编译到u-boot中,修改后代码如下:
 
include $(TOPDIR)/config.mk
LIB = $(obj)lib$(BOARD).a
COBJS :=  nand_read.o mini2440.o flash.o
SOBJS := lowlevel_init.o
【4】在mini2440.h头文件中加入Nand要用到的宏和寄存器的定义。
用gedit打开include/configs/mini2440.h头文件,定位到文件末尾处加入下列代码:
/*-----------------------------------------------------------------------
 * NAND flash settings
 */

#define CONFIG_S3C2410_NAND_SKIP_BAD 1

【5】在cpu/arm920t/u-boot.lds中添加下面两行代码,主要目的是防止编译器把我们自己添加的用于nandboot的子函数放到4K之后,否则是无法启动的,修改后代码如下:
 .text :
 {
     cpu/arm920t/start.o (.text)
     board/samsung/mini2440/lowlevel_init.o (.text)
     board/samsung/mini2440/nand_read.o (.text)
     *(.text)
 }
 

3.3,重新编译
  
编译成功后生成u-boot.bin文件。下载时先将mini2440开发板调到Nor启动档,利用supervivi的a命令将u-boot.bin下载到开发板的Nand Flash中,再把开发板调到Nand启动档,打开电源就从Nand Flash启动了,启动结果如下:
... ...
Enter your selection: ?
U-Boot 2009.08 ( 5鏈?06 2011 - 14:17:44)
DRAM:  64 MB
Flash:  2 MB
*** Warning - bad CRC, using default environment
In:    serial
Out:   serial
Err:   serial
[u-boot@MINI2440]#nand info
Unknown command nand' - try 'help'
[u-boot@MINI2440]#

从上面的运行结果看,显然现在的Nand还不能做任何事情,而且也没有显示有关Nand的任何信息,所以只能说明上面的这些步骤只是完成了Nand移植的Stage1部分。下面我们来添加我们开发板上的Nand Flash(K9F1G08U0B)的Stage2部分的有关操作支持。
3.4,在U-boot 启动的第一阶段,初始化了Nand Flash 控制器。但到第二阶段start_armboot函数还是会再次初始化Nand Flash 控制器。因为第二阶段和第一阶段的代码基本是独立的,第一阶段的代码基本只起到代码重定位的作用,到了第二阶段才是真正U-boot 的开始,以前的初始化过程还会重做一遍,比如始化Nand Flash 控制器、CPU 频率等。因为 S3C2440 和S3C2410 之间的很大差别就是:S3C2410 的Nand Flash 控制器只支持512B+16B 的Nand Flash,而S3C2440 还支持2KB+64B 的大容量Nand Flash。所以在Nand Flash 控制器上寄存器和控制流程上的差别很明显,底层驱动代码的修改也是必须的。具体的差别还是需要对比芯片数据手册的,下面我们结合代码来分析一下u-boot在第二阶段的执行流程:
<1>.lib_arm/board.c文件中的start_armboot函数调用了drivers/mtd/nand/nand.c文件中的nand_init函数,如下:
  #if defined(CONFIG_CMD_NAND) //可以看到CONFIG_CMD_NAND宏决定了Nand的初始化
      puts ("NAND: ");
      nand_init();
  #endif
<2>.nand_init调用了同文件下的nand_init_chip函数;
<3>.nand_init_chip函数调用drivers/mtd/nand/s3c2410_nand.c文件下的board_nand_init函数,然后再调用drivers/mtd/nand/nand_base.c函数中的nand_scan函数;
<4>.nand_scan函数调用了同文件下的nand_scan_ident函数等。

【1】Nand Flash 相关代码的修改
打开/drivers/mtd/nand/s3c2410_nand.c,定位到37行,修改如下:
#define __REGb(x) (*(volatile unsigned char *)(x))
#define __REGi(x) (*(volatile unsigned int *)(x))
#define NF_BASE  0x4e000000
#if defined(CONFIG_S3C2410)
#define NFCONF  __REGi(NF_BASE + 0x0)
#define NFCMD  __REGb(NF_BASE + 0x4)
... ...
#define S3C2410_ADDR_NALE 4
#define S3C2410_ADDR_NCLE 8
#endif
#if defined(CONFIG_S3C2440)
#define S3C2410_ADDR_NALE 0x08
#define S3C2410_ADDR_NCLE 0x0c
#define NFCONF  __REGi(NF_BASE + 0x0)
#define NFCONT    __REGb(NF_BASE + 0x4)   
#define NFCMD  __REGb(NF_BASE + 0x8)
#define NFADDR  __REGb(NF_BASE + 0xc)
#define NFDATA  __REGb(NF_BASE + 0x10)
#define NFMECCD0   __REGb(NF_BASE + 0x14)
#define NFMECCD1   __REGb(NF_BASE + 0x18)
#define NFSECCD   __REGb(NF_BASE + 0x1c)
#define NFSTAT  __REGb(NF_BASE + 0x20)
#define NFESTAT0 __REGb(NF_BASE + 0x24)
#define NFESTAT1 __REGb(NF_BASE + 0x28)
#define NFMECC0  __REGb(NF_BASE + 0x2c)
#define NFMECC1  __REGb(NF_BASE + 0x30)
#define NFSECC  __REGb(NF_BASE + 0x34)
#define NFSBLK  __REGb(NF_BASE + 0x38)
#define NFEBLK  __REGb(NF_BASE + 0x3c)
#define NFECC0  __REGb(NF_BASE + 0x2c)
#define NFECC1  __REGb(NF_BASE + 0x2d)
#define NFECC2  __REGb(NF_BASE + 0x2e)
#define S3C2410_NFCONT_EN          (1<<0)
#define S3C2410_NFCONT_INITECC     (1<<4)
#define S3C2410_NFCONT_nFCE        (1<<1)
#define S3C2410_NFCONT_MAINECCLOCK (1<<5)
#define S3C2410_NFCONF_TACLS(x)    ((x)<<12)
#define S3C2410_NFCONF_TWRPH0(x)   ((x)<<8)
#define S3C2410_NFCONF_TWRPH1(x)   ((x)<<4)
#endif
ulong IO_ADDR_W = NF_BASE;  
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 (ctrl & NAND_CTRL_CHANGE) {
  //ulong IO_ADDR_W = NF_BASE;//应注释掉这个局部变量否则找不到ID "No NAND device found!!!" 
   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;
#if defined(CONFIG_S3C2410)
  if (ctrl & NAND_NCE)
   NFCONF &= ~S3C2410_NFCONF_nFCE;
  else
   NFCONF |= S3C2410_NFCONF_nFCE;
#endif
#if defined(CONFIG_S3C2440)
  if (ctrl & NAND_NCE)
   NFCONT &= ~S3C2410_NFCONT_nFCE;
  else
   NFCONT |= S3C2410_NFCONT_nFCE;
#endif
 }
 if (cmd != NAND_CMD_NONE)
  //writeb(cmd, chip->IO_ADDR_W);
  writeb(cmd, (void *)IO_ADDR_W);
}
... ...
#ifdef CONFIG_S3C2410_NAND_HWECC
void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
{
 DEBUGN("s3c2410_nand_enable_hwecc(%p, %d)\n", mtd, mode);
#if defined(CONFIG_S3C2410)
 NFCONF |= S3C2410_NFCONF_INITECC;
#endif
#if defined(CONFIG_S3C2440)
 NFCONT |= S3C2410_NFCONT_INITECC;
#endif
}
... ...
#endif
int board_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);
 /* initialize hardware */
#if defined(CONFIG_S3C2410)
  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;
#endif
#if defined(CONFIG_S3C2440)
  twrph0 = 4; twrph1 = 2; tacls = 0;
  cfg = 0;
  cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
  cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
  cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
  NFCONF = cfg;
  NFCONT = (0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(0<<6)|(0<<5)|(1<<4)|(0<<1)|(1<<0);
  /* initialize nand_chip data structure */
  nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)0x4e000010;
#endif
 /* read_buf and write_buf are default */
 /* read_byte and write_byte are default */


【2】在mini2440.h里添加nand_flash相关宏定义
打开include/configs/mini2440.h,定位到98行附近,加入下列代码:
/*
 * Command line configuration.
 */
#include <config_cmd_default.h>
#define CONFIG_CMD_CACHE
#define CONFIG_CMD_DATE
#define CONFIG_CMD_ELF
#define CONFIG_CMD_NAND
#define CONFIG_CMD_JFFS2  /* JFFS2 Support*/
... ...
在文件末尾处加入下列代码:
/*-----------------------------------------------------------------------
 * NAND flash settings
 */
#if defined(CONFIG_CMD_NAND)
#define CONFIG_NAND_S3C2410
#define CONFIG_SYS_NAND_BASE 0x4E000000 
#define CONFIG_SYS_MAX_NAND_DEVICE 1 /* Max number of NAND devices  */
#define SECTORSIZE 512
#define SECTORSIZE_2K 2048
#define NAND_SECTOR_SIZE SECTORSIZE
#define NAND_SECTOR_SIZE_2K SECTORSIZE_2K
#define NAND_BLOCK_MASK 511
#define NAND_BLOCK_MASK_2K 2047
#define NAND_MAX_CHIPS 1
#define CONFIG_MTD_NAND_VERIFY_WRITE 
#define CONFIG_SYS_64BIT_VSPRINTF  /* needed for nand_util.c */
#endif /* CONFIG_CMD_NAND */
#define CONFIG_S3C2410_NAND_SKIP_BAD 1
 
3.5,重新编译,按上面3.3中方法下载,运行结果如下:
 
Enter your selection: ?
U-Boot 2009.08 ( 5鏈?06 2011 - 19:52:47)
DRAM:  64 MB
Flash:  2 MB
NAND:  128 MiB
*** Warning - bad CRC, using default environment
In:    serial
Out:   serial
Err:   serial
[u-boot@MINI2440]# nand info
Device 0: NAND 128MiB 3,3V 8-bit, sector size 128 KiB
[u-boot@MINI2440]# saveenv
Saving Environment to Flash...
Un-Protected 16 sectors
Erasing Flash...Erasing sector 64 ...
从上面输出信息可以看到保存环境变量并没有成功,而且它是将把环境变量保存到Flash即Nor flash中,显然这不正确,我们是要保存到Nand中。原来,u-boot在默认的情况下把环境变量都是保存到Nor Flash中的,所以我们要修改代码,让它保存到Nand中,如下:
打开include/configs/mini2440.h,定位到193行附近,注释掉下列代码:

/* timeout values are in ticks */
#define CONFIG_SYS_FLASH_ERASE_TOUT (5*CONFIG_SYS_HZ) /* Timeout for Flash Erase */
#define CONFIG_SYS_FLASH_WRITE_TOUT (5*CONFIG_SYS_HZ) /* Timeout for Flash Write */

//#define CONFIG_ENV_IS_IN_FLASH 1
//#define CONFIG_ENV_SIZE  0x20000 /* Total Size of Environment Sector */

然后加入下列代码:

/*-----------------------------------------------------------------------
 * NAND flash settings
 */
#define CONFIG_ENV_IS_IN_NAND  1
#define CONFIG_ENV_OFFSET      0x40000 //将环境变量保存到nand中的0x40000位置必,须在块的起始位置
#define CONFIG_ENV_SIZE        0x20000 /*必须为块大小的整数倍 , 否则会提示下面的信息,将擦除整个块*/
#if defined(CONFIG_CMD_NAND)
 
Warning: Erase size 0x00010000 smaller than one erase block 0x00020000
         Erasing 0x00020000 instead
NAND 128MiB 3,3V 8-bit: MTD Erase failure: -22
Writing to Nand... FAILED!
 
然后保存,重新编译、拨到nor 档下载,在拨到nand 档上电运行:
Enter your selection: ?
U-Boot 2009.08 ( 5鏈?06 2011 - 22:30:25)
DRAM:  64 MB
Flash:  2 MB
NAND:  128 MiB
*** Warning - bad CRC or NAND, using default environment
In:    serial
Out:   serial
Err:   serial
[u-boot@MINI2440]# saveenv
Saving Environment to NAND...
Erasing Nand...
Erasing at 0x4000000000002 --   0% complete.
Writing to Nand... done
[u-boot@MINI2440]#
可以看到环境变量保存成功,将开发板重新上电后不会再有bad CRC告警,nand flash 移植成功。


接下来将进入u-boot的第四阶段,为u-boot-2009.08增加网卡DM9000驱动支持。


(四)---增加DM9000驱动和命令自动补全功能  

        u-boot-2009.08版本已经对CS8900、RTL8019和DM9000X等网卡有比较完善的代码支持(代码在drivers/net/目录下),而且在S3C24XX系列中默认对CS8900网卡进行配置使用。而mini2440开发板使用的则是DM9000网卡芯片,所以只需在开发板上添加对DM9000的支持即可。还有一点,以前的 U-boot 对于网络延时部分有问题,需要修改许多地方。但是现在的U-boot 网络 部分已经基本不需要怎么修改了,只有在DM9000 的驱动和NFS 的TIMEOUT 参数上需要稍微修改一下。

4.1,DM9000驱动代码修改

【1】修改static int dm9000_init函数中部分代码,如果不修改这一部分,在使用网卡的时候会报“could not establish link”的错误。

打开/drivers/net/dm9000x.c,定位到377行,修改如下:

 /* Activate DM9000 */
 /* RX enable */
 DM9000_iow(DM9000_RCR, RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN);
 /* Enable TX/RX interrupt mask */
 DM9000_iow(DM9000_IMR, IMR_PAR);

 #if 0 //default to link MII interface
 i = 0;
 while (!(phy_read(1) & 0x20)) { /* autonegation complete bit */
  udelay(1000);
  i++;
  if (i == 1650) { 
   //printf("could not establish link\n");
   //return 0;
   break;
  }
 }
#endif

【2】对于NFS,增加了延时,否则会出现“*** ERROR: Cannot mount”的错误。

打开/net/nfs.c,定位到36行,修改如下:

#if defined(CONFIG_CMD_NET) && defined(CONFIG_CMD_NFS)

#define HASHES_PER_LINE 65 /* Number of "loading" hashes per line */
#define NFS_RETRY_COUNT 30
#define NFS_TIMEOUT (CONFIG_SYS_HZ/1000*2000UL) //2000UL

【3】添加网卡芯片(DM9000)的初始化函数

打开board/samsung/mini2440/mini2440.c,定位到194行附近,文件末尾处,修改如下:

int dram_init (void)
{
 gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
 gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;

 return 0;
}

extern int dm9000_initialize(bd_t *bis);//implicit declaration of function 'dm9000_initialize'
#ifdef CONFIG_DRIVER_DM9000
int board_eth_init(bd_t *bis)
{
 return dm9000_initialize(bis);
}
#endif

【4】添加串口 Xmodem 传输协议(可不修改)

对于使用串口传输数据到内存的操作,有可能会用到Xmodem协议。但是原本的kermit协议传输就挺好用的,速度也比较快,所以可添加此功能。

打开/common/cmd_load.c,定位到37行,修改如下:

#if defined(CONFIG_CMD_LOADB)
#if defined(ENABLE_CMD_LOADB_X)
static ulong load_serial_xmodem (ulong offset);
#endif
static ulong load_serial_ymodem (ulong offset);
#endif

然后再定位到480行附近,修改如下:

 if (load_baudrate != current_baudrate) {
  printf ("## Switch baudrate to %d bps and press ENTER ...\n",
   load_baudrate);
  udelay(50000);
  gd->baudrate = load_baudrate;
  serial_setbrg ();
  udelay(50000);
  for (;;) {
   if (getc() == '\r')
    break;
  }
 }
#if defined(ENABLE_CMD_LOADB_X)
 if (strcmp(argv[0],"loadx")==0) {
  printf ("## Ready for binary (xmodem) download "
   "to 0x%08lX at %d bps...\n",
   offset,
   load_baudrate);

  addr = load_serial_xmodem (offset);

 } else if (strcmp(argv[0],"loady")==0) {
#else
 if (strcmp(argv[0],"loady")==0) {
#endif
  printf ("## Ready for binary (ymodem) download "
   "to 0x%08lX at %d bps...\n",
   offset,
   load_baudrate);

  addr = load_serial_ymodem (offset);

再定位到998行附近,修改如下:

static int getcxmodem(void) {
 if (tstc())
  return (getc());
 return -1;
}
#if defined(ENABLE_CMD_LOADB_X)
static ulong load_serial_xmodem (ulong offset)
{
 int size;
 char buf[32];
 int err;
 int res;
 connection_info_t info;
 char xmodemBuf[1024];
 ulong store_addr = ~0;
 ulong addr = 0;

 size = 0;
 info.mode = xyzModem_xmodem;
 res = xyzModem_stream_open (&info, &err);
 if (!res) {

  while ((res =
   xyzModem_stream_read (xmodemBuf, 1024, &err)) > 0) {
   store_addr = addr + offset;
   size += res;
   addr += res;
#ifndef CFG_NO_FLASH
   if (addr2info (store_addr)) {
    int rc;

    rc = flash_write ((char *) xmodemBuf,
    store_addr, res);
    if (rc != 0) {
    flash_perror (rc);
    return (~0);
    }
   } else
#endif
   {
    memcpy ((char *) (store_addr), xmodemBuf,
     res);
   }

  }
 } else {
  printf ("%s\n", xyzModem_error (err));
 }

 xyzModem_stream_close (&err);
 xyzModem_stream_terminate (false, &getcxmodem);


 flush_cache (offset, size);

 printf ("## Total Size      = 0x%08x = %d Bytes\n", size, size);
 sprintf (buf, "%X", size);
 setenv ("filesize", buf);

 return offset;
}
#endif
static ulong load_serial_ymodem (ulong offset)

再定位到1169行,修改如下:

#if defined(CONFIG_CMD_LOADB)
U_BOOT_CMD(
 loadb, 3, 0, do_load_serial_bin,
 "load binary file over serial line (kermit mode)",
 "[ off ] [ baud ]\n"
 "    - load binary file over serial line"
 " with offset 'off' and baudrate 'baud'"
);
#if defined(ENABLE_CMD_LOADB_X)
U_BOOT_CMD(
 loadx, 3, 0,    do_load_serial_bin,
 "load binary file over serial line (xmodem mode)",
 "[ off ] [ baud ]\n"
 "    - load binary file over serial line"
 " with offset 'off' and baudrate 'baud'"
);
#endif

U_BOOT_CMD(
 loady, 3, 0, do_load_serial_bin,
 "load binary file over serial line (ymodem mode)",
 "[ off ] [ baud ]\n"
 "    - load binary file over serial line"
 " with offset 'off' and baudrate 'baud'"
);

【5】修改配置文件,在mini2440.h中加入相关定义

打开/include/configs/mini2440.h,定位到60行附近,修改如下:

/*
 * Hardware drivers
 */
#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 
#define CONFIG_NET_MULTI  1
#define CONFIG_DRIVER_DM9000 1
#define CONFIG_DM9000_BASE 0x20000300 //网卡片选地址
#define DM9000_IO CONFIG_DM9000_BASE
#define DM9000_DATA (CONFIG_DM9000_BASE+4) //网卡数据地址
#define CONFIG_DM9000_NO_SROM  1
//#define CONFIG_DM9000_USE_16BIT
#undef CONFIG_DM9000_DEBUG

注意:
u-boot-2009.08 可以自动检测DM9000网卡的位数,根据开发板原理图可知网卡的数据位为16位,并且网卡位
于CPU的BANK4上,所以只需在 board/samsung/mini2440/lowlevel_init.S中设置 #define B4_BWSCON (DW16) 即
可,不需要此处的 #define CONFIG_DM9000_USE_16BIT 1

给u-boot加上ping命令,用来测试网络通不通

/*
 * Command line configuration.
 */
#include <config_cmd_default.h>

#define CONFIG_CMD_CACHE
#define CONFIG_CMD_DATE
#define CONFIG_CMD_ELF
#define CONFIG_CMD_NAND
#define CONFIG_CMD_JFFS2  /* JFFS2 Support*/
#define CONFIG_CMD_PING /*ping command support*/

恢复被注释掉的网卡MAC地址和修改你合适的开发板IP地址以及内核启动参数:

#define CONFIG_BOOTDELAY 3
#define CONFIG_ETHADDR 08:00:3e:26:0a:5b 
#define CONFIG_NETMASK     255.255.255.0
#define CONFIG_IPADDR  10.1.0.129
#define CONFIG_SERVERIP  10.1.0.128
#define CONFIG_GATEWAYIP 10.1.0.1
#define CONFIG_OVERWRITE_ETHADDR_ONCE
/*#define CONFIG_BOOTFILE "elinos-lart" */

定位到139行附近,加入使能串口传输数据到内存的操作:

#define ENABLE_CMD_LOADB_X    1 //使能串口传输数据到内存的操作

#if defined(CONFIG_CMD_KGDB)
#define CONFIG_KGDB_BAUDRATE 115200  /* speed to run kgdb serial port */
/* what's this ? it's not used anywhere */
#define CONFIG_KGDB_SER_INDEX 1  /* which serial port to use */
#endif

【6】为u-boot增加命令自动补全和历史记录功能

此功能如同shell里面自动补全和历史记录功能,用起来非常方便。

打开/include/configs/mini2440.h,定位到109附近,加入宏定义:

#define CONFIG_CMD_ASKENV
#define CONFIG_CMD_CACHE
#define CONFIG_CMD_DATE
#define CONFIG_CMD_ELF
#define CONFIG_AUTO_COMPLETE  /*command auto complete*/
#define CONFIG_CMDLINE_EDITING /*command history record*/
#define CONFIG_CMD_NAND
#define CONFIG_CMD_JFFS2  /* JFFS2 Support*/
#define CONFIG_CMD_PING  /*ping command support*/

4.2,重新编译u-boot,下载到Nand中从Nand启动,查看启动信息和环境变量并使用ping命令测试网卡,操作如下:

Enter your selection: a
USB host is connected. Waiting a download.

Now, Downloading [ADDRESS:30000000h,TOTAL:154934]
RECEIVED FILE SIZE:  154934 (151KB/S, 1S)
Downloaded file at 0x30000000, size = 154924 bytes
Write to flash ok: skipped size = 0x0, size = 0x25d2c

... ...

nand 方式上电重启后:

U-Boot 2009.08 ( 5鏈?09 2011 - 15:01:04)

DRAM:  64 MB
Flash:  2 MB
NAND:  128 MiB
In:    serial
Out:   serial
Err:   serial
Net:   dm9000
[u-boot@MINI2440]#

显示下环境变量:

[u-boot@MINI2440]# printenv
bootdelay=3
baudrate=115200
netmask=255.255.255.0
stdin=serial
stdout=serial
stderr=serial
ipaddr=10.1.129
serverip=10.1.0.128
ethact=dm9000

Environment size: 141/131068 bytes

ping测试:
[u-boot@MINI2440]# ping 10.1.0.128
dm9000 i/o: 0x20000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 00:00:00:00:00:00
operating at 100M full duplex mode
*** ERROR: `ethaddr' not set
dm9000 i/o: 0x20000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 00:00:00:00:00:00
operating at 100M full duplex mode
ping failed; host 10.1.0.128 is not alive

需要设定IP地址和MAC地址

[u-boot@MINI2440]# setenv ipaddr 10.1.0.129
[u-boot@MINI2440]# setenv serverip 10.1.0.128
[u-boot@MINI2440]# setenv setenv ethaddr 12:34:56:78:9A:BC
[u-boot@MINI2440]# saveenv
Saving Environment to NAND...
Erasing Nand...
Erasing at 0x4000000000002 --   0% complete.
Writing to Nand... done
[u-boot@MINI2440]#

然后再进行ping测试:

[u-boot@MINI2440]# ping 10.1.0.128
dm9000 i/o: 0x20000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 12:34:56:78:9a:bc
operating at 100M full duplex mode
Using dm9000 device
ping failed; host 10.1.0.128 is not alive
[u-boot@MINI2440]#

可以看到,启动信息里面显示了Net:dm9000,printenv查看的环境变量也和include/configs/mini2440.h中设置的一致。但是现在有个问题就是ping不能通过。
经过一段时间在网上搜索,原来有很多人都碰到了这种情况。出现问题的地方可能是DM9000网卡驱动中关闭网卡的地方,如是就试着修改代码如下:

打开drivers/net/dm9000x.c ,定位到456行附近,屏蔽掉dm9000_halt函数中的内容:

/*
  Stop the interface.
  The interface is stopped when it is brought.
*/
static void dm9000_halt(struct eth_device *netdev)
{
#if 0 
 DM9000_DBG("%s\n", __func__);

 /* RESET devie */
 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 
}

定位到607行,屏蔽掉phy_write(int reg, u16 value)定义,修改如下:

/*
   Write a word to phyxcer
*/
#if 0
static void
phy_write(int reg, u16 value)
{

 /* Fill the phyxcer register into REG_0C */
 DM9000_iow(DM9000_EPAR, DM9000_PHY | reg);

 /* Fill the written data into REG_0D & REG_0E */
 DM9000_iow(DM9000_EPDRL, (value & 0xff));
 DM9000_iow(DM9000_EPDRH, ((value >> 8) & 0xff));
 DM9000_iow(DM9000_EPCR, 0xa); /* Issue phyxcer write command */
 udelay(500);   /* Wait write complete */
 DM9000_iow(DM9000_EPCR, 0x0); /* Clear phyxcer write command */
 DM9000_DBG("phy_write(reg:0x%x, value:0x%x)\n", reg, value);
}
#endif

定位到114行,将其函数声明注释掉:

/* function declaration ------------------------------------- */
static int dm9000_probe(void);
static u16 phy_read(int);
//static void phy_write(int, u16);
static u8 DM9000_ior(int);
static void DM9000_iow(int reg, u8 value);

重新编译下载,nand启动,运行结果:

[u-boot@MINI2440]# ping 10.1.0.128
dm9000 i/o: 0x20000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 00:00:00:00:00:00
operating at unknown: 0 mode
*** ERROR: `ethaddr' not set
dm9000 i/o: 0x20000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 00:00:00:00:00:00
operating at unknown: 0 mode
ping failed; host 10.1.0.128 is not alive

[u-boot@MINI2440]# setenv gatewayip 10.1.0.1
[u-boot@MINI2440]# setenv ethaddr 12:34:56:78:9a:bc //MAC地址,随便设
[u-boot@MINI2440]# ping 10.1.0.128
dm9000 i/o: 0x20000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 12:34:56:78:9a:bc
operating at unknown: 0 mode
Using dm9000 device
host 10.1.0.128 is alive
[u-boot@MINI2440]# saveenv
Saving Environment to NAND...
Erasing Nand...
Erasing at 0x4000000000002 --   0% complete.
Writing to Nand... done

[u-boot@MINI2440]# ping 10.1.0.128
dm9000 i/o: 0x20000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 12:34:56:78:9a:bc
operating at unknown: 0 mode
Using dm9000 device
host 10.1.0.128 is alive
[u-boot@MINI2440]#

结果,只是第一次ping不通,以后都是可以ping通的(据网友们说这是正常的)。

4.3,tftp功能测试

首先需要将友善官方移植好的有关mini2440的内核文件zImage_T35复制到linux 宿主机的/tftpboot目录下,因为u-boot默认的此目录,然后执行:

[u-boot@MINI2440]# tftp 0x30008000 zImage_T35
dm9000 i/o: 0x20000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 12:34:56:78:9a:bc
operating at unknown: 0 mode
Using dm9000 device
TFTP from server 10.1.0.128; our IP address is 10.1.0.129
Filename 'zImage_T35'.
Load address: 0x30008000
Loading: T ################################################T #################
         #############T T ######################T ##############################
T T
         ########
done
Bytes transferred = 2022348 (1edbcc hex)
[u-boot@MINI2440]#

至此DM9000网卡驱动移植成功。但是还发现一个问题:"这里之前还是"operating at 100M full duplex mode",而现在怎么是"operating at unknown: 0 mode"?原来是dm9000的phy_read(int reg)函数延时出了问题,现操作如下:

打开/drivers/net/dm9000x.c,定位到595行附近,修改如下:

/*
   Read a word from phyxcer
*/
static u16
phy_read(int reg)
{
 u16 val;

 /* Fill the phyxcer register into REG_0C */
 DM9000_iow(DM9000_EPAR, DM9000_PHY | reg);
 DM9000_iow(DM9000_EPCR, 0xc); /* Issue phyxcer read command */
 udelay(1000); //udelay(100);   /* Wait read complete */
 DM9000_iow(DM9000_EPCR, 0x0); /* Clear phyxcer read command */
 val = (DM9000_ior(DM9000_EPDRH) << 8) | DM9000_ior(DM9000_EPDRL);

 /* The read data keeps on REG_0D & REG_0E */
 DM9000_DBG("phy_read(0x%x): 0x%x\n", reg, val);
 return val;
}
重新编译下载后:

[u-boot@MINI2440]# ping 10.1.0.128
dm9000 i/o: 0x20000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 12:34:56:78:9a:bc
operating at unknown: 15 mode
Using dm9000 device
ping failed; host 10.1.0.128 is not alive
[u-boot@MINI2440]# ping 10.1.0.128
dm9000 i/o: 0x20000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 12:34:56:78:9a:bc
operating at 100M full duplex mode
Using dm9000 device
host 10.1.0.128 is alive
[u-boot@MINI2440]#
可以看到"operating at 100M full duplex mode"这样的信息了

上面还有一个问题,就是问什么第一次ping不通呢?经过尝试,操作如下:

打开/drivers/net/dm9000x.c,定位到377行,修改如下:

 /* Activate DM9000 */
 /* RX enable */
 DM9000_iow(DM9000_RCR, RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN);
 /* Enable TX/RX interrupt mask */
 DM9000_iow(DM9000_IMR, IMR_PAR);

 #if 1 //internet delay loop
 i = 0;
 while (!(phy_read(1) & 0x20)) { /* autonegation complete bit */
  udelay(1000);
  i++;
  if (i == 3000) { 
   printf("could not establish link\n");
   return 0;
   //break;
  }
 }
#endif

 

修改后重新编译下载:

[u-boot@MINI2440]# ping 10.1.0.128
dm9000 i/o: 0x20000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 12:34:56:78:9a:bc
operating at 100M full duplex mode
Using dm9000 device
host 10.1.0.128 is alive
[u-boot@MINI2440]#

OK! 第一次ping不通的问题解决了!
 

接下来将进入u-boot的第五阶段,为u-boot-2009.08增加yaffs2文件系统。


(五)增加yaffs2文件系

5.1,添加Yaffs2镜像烧写功能
由于现在很多使用Nand Flash 的系统,在Linux 下都用Yaffs2作为存储数据的文件系统,甚至是根文件系统。所以在BootLoader 下能够烧写Yaffs2 映像文件变得很必要。对于Yaffs2 映像烧写的支持其实就是在烧写时,写入数据的同时,将镜像文件中的oob数据也写入到Nand Flash 的Spare 区。这和Yaffs 文件系统原理以及Nand Flash 的结构有关,注意:这里对Nand的操作是基于MTD架构方式。

通常一个Nnad Flash存储设备由若干块组成,1个块由若干页组成。一般128MB以下容量的Nand Flash芯片,一页大小为528B,被依次分为2个256B的主数据区和16B的额外空间;128MB以上容量的Nand Flash芯片,一页大小通常为2KB。由于Nand Flash出现位反转的概率较大,一般在读写时需要使用ECC进行错误检验和恢复。

Yaffs/yaffs2文件系统的设计充分考虑到Nand Flash以页为存取单位等的特点,将文件组织成固定大小的段(Chunk)。以528B的页为例,Yaffs/yaffs2文件系统使用前512B存储 数据和16B的额外空间存放数据的ECC和文件系统的组织信息等(称为OOB数据)。通过OOB数据,不但能实现错误检测和坏块处理,同时还可以避免加载 时对整个存储介质的扫描,加快了文件系统的加载速度。以下是Yaffs/yaffs2文件系统页的结构说明:


     Yaffs页结构说明
==============================================
   字节                   用途
==============================================
 0 - 511                存储数据(分为两个半部)
512 - 515               系统信息
   516                  数据状态字
   517                  块状态字
518 - 519               系统信息
520 - 522               后半部256字节的ECC
523 - 524               系统信息
525 - 527               前半部256字节的ECC
==============================================

好了,在了解Nand Flash组成和Yaffs/yaffs2文件系统结构后,我们再回到u-boot中。目前,在u-boot中已经有对Cramfs、Jffs2等文件系统的读写支持,但与带有数据校验等功能的OOB区的Yaffs/Yaffs2文件系统相比,他们是将所有文件数据简单的以线性表形式组织的。所以,我们只要在此基础上通过修改u-boot的Nand Flash读写命令,增加处理00B区域数据的功能,即可以实现对Yaffs/Yaffs2文件系统的读写支持。

实现u-boot对Yaffs或者Yaffs2文件系统的读写支持步骤如下:
【1】下面是需要修改的 4 个文件的补丁:
(1)打开/common/cmd_nand.c,定位到392行,在nand操作的do_nand函数中,修改如下:
   if (read)
    ret = nand_read_skip_bad(nand, off, &size,
        (u_char *)addr);
   else
    ret = nand_write_skip_bad(nand, off, &size,
         (u_char *)addr);

}
#if defined(CONFIG_MTD_NAND_YAFFS2)
    else if (s != NULL && (!strcmp(s, ".yaffs2")))
    {
         nand->rw_oob = 1;
         nand->skipfirstblk = 1;
         ret = nand_write_skip_bad(nand,off,&size,(u_char *)addr);
         nand->skipfirstblk = 0;
         nand->rw_oob = 0;
    }

#endif 
  else if (!strcmp(s, ".oob")) {
   /* out-of-band data */
   mtd_oob_ops_t ops = {
    .oobbuf = (u8 *)addr,
    .ooblen = size,
    .mode = MTD_OOB_RAW
   };
... ...

定位到509行附近,在Nand操作的命令集列表中添加Yaffs2对Nand的读写命令,修改如下
U_BOOT_CMD(nand, CONFIG_SYS_MAXARGS, 1, do_nand,
 "NAND sub-system",
 "info - show available NAND devices\n"
 "nand device [dev] - show or set current device\n"
 "nand read - addr off|partition size\n"
 "nand write - addr off|partition size\n"
 "    read/write 'size' bytes starting at offset 'off'\n"
 "    to/from memory address 'addr', skipping bad blocks.\n"
 "nand erase [clean] [off size] - erase 'size' bytes from\n"
 "    offset 'off' (entire device if not specified)\n"
#if defined(CONFIG_MTD_NAND_YAFFS2)
 "nand write[.yaffs2] - addr off|partition size - write `size' byte yaffs image\n"
 " starting at offset off' from memory address addr' (.yaffs2 for 512+16 NAND)\n"
#endif
 "nand bad - show bad blocks\n"
 "nand dump[.oob] off - dump page\n"
 "nand scrub - really clean NAND erasing bad blocks (UNSAFE)\n"
 "nand markbad off [...] - mark bad block(s) at offset (UNSAFE)\n"
 "nand biterr off - make a bit error at offset (UNSAFE)"
#ifdef CONFIG_CMD_NAND_LOCK_UNLOCK
 "\n"
 "nand lock [tight] [status]\n"
 "    bring nand to lock state or display locked pages\n"
 "nand unlock [offset] [size] - unlock section"
#endif
);

注意:这里只添加了yaffs2的写命令,u-boot下载用的是写功能。


(2)打开include/linux/mtd/mtd.h头文件,定位到131行,在mtd_info结构体中添加上面用到rw_oob和skipfirstblk数据成员,修改如下:

u_int32_t writesize;
#if defined(CONFIG_MTD_NAND_YAFFS2)
  /*Thanks for hugerat's code*/
 u_char rw_oob;
 u_char skipfirstblk;
#endif
 u_int32_t oobsize;   /* Amount of OOB data per block (e.g. 16) */
 u_int32_t oobavail;  /* Available OOB bytes per block */

(3)在第一步关联的nand_write_skip_bad函数中添加对Nand OOB的相关操作,修改如下:
打开/drivers/mtd/nand/nand_util.c,定位到483行,在nand_write_skip_bad函数中添加代码,修改如下:
int nand_write_skip_bad(nand_info_t *nand, loff_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 defined(CONFIG_MTD_NAND_YAFFS2)
 if(nand->rw_oob==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;
 }
#endif

 /* 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 !defined(CONFIG_MTD_NAND_YAFFS2)//add yaffs2 file system support
 if (len_incl_bad == *length) {
  rval = nand_write (nand, offset, length, buffer);
  if (rval != 0)
   printf ("NAND write to offset %llx failed %d\n",
    offset, rval);
  return rval;
 }
#endif
 while (left_to_write > 0) {
  size_t block_offset = offset & (nand->erasesize - 1);
  size_t write_size;
  WATCHDOG_RESET ();
  if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) {
   printf ("Skip bad block 0x%08llx\n",
    offset & ~(nand->erasesize - 1));
   offset += nand->erasesize - block_offset;
   continue;
  }
#if defined(CONFIG_MTD_NAND_YAFFS2) //add yaffs2 file system support
  if(nand->skipfirstblk==1)
  {
   nand->skipfirstblk=0;
   printf ("Skip the first good block %llx\n", offset & ~(nand->erasesize - 1));
   offset += nand->erasesize - block_offset;
   continue;
  }
#endif

  if (left_to_write < (nand->erasesize - block_offset))
   write_size = left_to_write;
  else
   write_size = nand->erasesize - block_offset;

  printf("\rWriting at 0x%llx -- ",offset); //add yaffs2 file system support
  rval = nand_write (nand, offset, &write_size, p_buffer);
  if (rval != 0) {
   printf ("NAND write to offset %llx failed %d\n",
    offset, rval);
   *length -= left_to_write;
   return rval;
  }
  left_to_write -= write_size;
  printf("%d%% is complete.",100-(left_to_write/(*length/100)));/*Thanks for hugerat's code*/
  offset        += write_size;
#if defined(CONFIG_MTD_NAND_YAFFS2)
  /*Thanks for hugerat's code*/
  if(nand->rw_oob==1) {
   p_buffer += write_size+(write_size/nand->writesize*nand->oobsize);
  } else {
   p_buffer += write_size;
  }
#else 
  p_buffer      += write_size;
#endif
 }
 return 0;
}

(4)在第三步的nand_write_skip_bad函数中我们看到又对nand_write函数进行了访问,所以这一步是到nand_write函数中添加对yaffs2的支持。

打开/drivers/mtd/nand/nand_base.c,定位到1961行,修改如下:
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;
#if defined(CONFIG_MTD_NAND_YAFFS2)
 /*Thanks for hugerat's code!*/
   int oldopsmode = 0;
   if(mtd->rw_oob==1)    
     {
      int i = 0;
      int datapages = 0;

      size_t oobsize = mtd->oobsize;
      size_t datasize = mtd->writesize;

      uint8_t oobtemp[oobsize];
      datapages = len / (datasize);

      for(i = 0; i < (datapages); i++)    
          {
          memcpy((void *)oobtemp, (void *)(buf + datasize * (i + 1)), oobsize);
          memmove((void *)(buf + datasize * (i + 1)), 
            (void *)(buf + datasize * (i + 1) + oobsize), 
            (datapages - (i + 1)) * (datasize) + (datapages - 1) * oobsize);
          memcpy((void *)(buf+(datapages) * (datasize + oobsize) - oobsize), 
            (void *)(oobtemp), oobsize);
          }
   
}
#endif 

/* Do not allow reads past end of device */
 if ((to + len) > mtd->size)
  return -EINVAL;
 if (!len)
  return 0;
 nand_get_device(chip, mtd, FL_WRITING);
 chip->ops.len = len;
 chip->ops.datbuf = (uint8_t *)buf;
 //chip->ops.oobbuf = NULL;
#if defined(CONFIG_MTD_NAND_YAFFS2)//add yaffs2 file system support
 /*Thanks for hugerat's code!*/
 if(mtd->rw_oob!=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; 
 }
#else
 chip->ops.oobbuf = NULL;
#endif
 ret = nand_do_write_ops(mtd, to, &chip->ops);
 *retlen = chip->ops.retlen;
  nand_release_device(mtd);
#if defined(CONFIG_MTD_NAND_YAFFS2)//add yaffs2 file system support
 /*Thanks for hugerat's code!*/
 chip->ops.mode = oldopsmode; 
#endif
 return ret;
}

【2】在mini2440.h里添加yaffs2相关宏定义
打开include/configs/mini2440.h,定位到98行附近,加入下列代码:
/*
 * Command line configuration.
 */
#include <config_cmd_default.h>
#define CONFIG_CMD_CACHE
#define CONFIG_CMD_DATE
#define CONFIG_CMD_ELF
#define CONFIG_CMD_NAND
#define CONFIG_CMD_JFFS2  /* JFFS2 Support*/
... ...
在文件末尾处加入下列代码:
/*-----------------------------------------------------------------------
 * NAND flash settings
 */
#if defined(CONFIG_CMD_NAND)
#define CONFIG_NAND_S3C2410
#define CONFIG_SYS_NAND_BASE 0x4E000000 
#define CONFIG_SYS_MAX_NAND_DEVICE 1 /* Max number of NAND devices  */
#define SECTORSIZE 512
#define SECTORSIZE_2K 2048
#define NAND_SECTOR_SIZE SECTORSIZE
#define NAND_SECTOR_SIZE_2K SECTORSIZE_2K
#define NAND_BLOCK_MASK 511
#define NAND_BLOCK_MASK_2K 2047
#define NAND_MAX_CHIPS 1
#define CONFIG_MTD_NAND_VERIFY_WRITE 
#define CONFIG_SYS_64BIT_VSPRINTF  /* needed for nand_util.c */
#endif /* CONFIG_CMD_NAND */
 
#define CONFIG_JFFS2_NAND 1
//#undef CONFIG_JFFS2_CMDLINE
#define CONFIG_JFFS2_DEV "nand0"
#define CONFIG_JFFS2_PART_SIZE 0x480000
#define CONFIG_JFFS2_PART_OFFSET 0x80000
#define CONFIG_JFFS2_CMDLINE 1
#define MTDIDS_DEFAULT "nand0=nandflash0"
#define MTDPARTS_DEFAULT "mtdparts=nandflash0:384k(bootloader)," \
           "128k(params)," \
           "5m(kernel)," \
           "-(root)"
#define CONFIG_MTD_NAND_YAFFS2   1
#define ENABLE_CMD_LOADB_X 1
 

5.2, 烧录yaffs2文件系统测试

重新编译后下载,nand方式启动,在u-boot的命令行输入nand help查看nand的命令,可以看到多了一个nand write[.yaffs2]的命令:

[u-boot@MINI2440]# nand help
nand - NAND sub-system

Usage:
nand info - show available NAND devices
nand device [dev] - show or set current device
nand read - addr off|partition size
nand write - addr off|partition size
    read/write 'size' bytes starting at offset 'off'
    to/from memory address 'addr', skipping bad blocks.
nand erase [clean] [off size] - erase 'size' bytes from
    offset 'off' (entire device if not specified)
nand write[.yaffs2] - addr off|partition size - write `size' byte yaffs image
 starting at offset off' from memory address addr' (.yaffs2 for 512+16 NAND)
nand bad - show bad blocks
nand dump[.oob] off - dump page
nand scrub - really clean NAND erasing bad blocks (UNSAFE)
nand markbad off [...] - mark bad block(s) at offset (UNSAFE)
nand biterr off - make a bit error at offset (UNSAFE)
[u-boot@MINI2440]#

这个就是用来下载yaffs2文件系统到nand中的命令了。首先需要将友善官方提供的有关mini2440的yaffs2文件系统文件root_qtopia-128M.img复制到linux宿主机的/tftpboot目录下,然后执行:

tf[u-boot@MINI2440]# tp 0x30000000 root_qtopia-128M.img
Filename 'root_qtopia-128M.img'.
Load address: 0x30000000
Loading: T ##########T T #####################################################T
##T T
         #T T T T ######
Retry count exceeded; starting again
dm9000 i/o: 0x20000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 12:34:56:78:9a:bc
operating at 100M full duplex mode 
Using dm9000 device
TFTP from server 10.1.0.128; our IP address is 10.1.0.129
Filename 'root_qtopia-128M.img'.
Load address: 0x30000000
Loading: T ###T ###T T ########

一直无休止循环,经网上搜索得知Uboot 端 tftp 程序传过来的Timeout参数不符合服务器端定义引起的,原文链接地址

Retry count exceeded; starting again 解决方法:tftp客户端传过来的timeout是7810,而服务器端定义的范围在1-255秒之间,不是服务器的问题,而是uboot中tftp参数设置的问题,参见TFTP Unsupported option(s) requested 问题详细分析及解决
打开/net/net.c,定位到104行附近,修改如下:

#if defined(CONFIG_CMD_NET)

DECLARE_GLOBAL_DATA_PTR;

#ifndef CONFIG_ARP_TIMEOUT
# define ARP_TIMEOUT  10000UL*CONFIG_SYS_HZ/1000 //5000UL /* Milliseconds before trying ARP again */
#else
# define ARP_TIMEOUT  CONFIG_ARP_TIMEOUT
#endif

定位到573行附近,修改如下:

#ifndef CONFIG_NET_MULTI
 //NetSetTimeout (10000UL, startAgainTimeout);
 NetSetTimeout (10000UL*CONFIG_SYS_HZ/1000, startAgainTimeout);
 NetSetHandler (startAgainHandler);
#else /* !CONFIG_NET_MULTI*/

定位到585行附近,修改如下:

eth_init (gd->bd);
 if (NetRestartWrap) {
  NetRestartWrap = 0;
  if (NetDevExists && !once) {
   //NetSetTimeout (10000UL, startAgainTimeout);
   NetSetTimeout (10000UL*CONFIG_SYS_HZ/1000, startAgainTimeout);
   NetSetHandler (startAgainHandler);
  } else {
   NetState = NETLOOP_FAIL;
  }

定位到779行附近,修改如下:

#define CDP_SYSOBJECT_TLV  0x0015
#define CDP_MANAGEMENT_ADDRESS_TLV 0x0016

#define CDP_TIMEOUT   (250UL*CONFIG_SYS_HZ/1000) //250UL /* one packet every 250ms */

static int CDPSeq;
static int CDPOK;

打开/net/tftp.c,定位到16行,修改如下:

#define TIMEOUT  60000UL //5000UL /* Millisecs to timeout for lost pkt */

然后重新编译后下载测试:

[u-boot@MINI2440]# tftp 0x30000000 root_qtopia-128M.img
dm9000 i/o: 0x20000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 12:34:56:78:9a:bc
operating at 100M full duplex mode
Using dm9000 device
TFTP from server 10.1.0.128; our IP address is 10.1.0.129
Filename 'root_qtopia-128M.img'.
Load address: 0x30000000
Loading: T T T T T T T T T T
Retry count exceeded; starting again
dm9000 i/o: 0x20000300, id: 0x90000a46
DM9000: running in 16 bit mode
MAC: 12:34:56:78:9a:bc
operating at 100M full duplex mode 
Using dm9000 device
TFTP from server 10.1.0.128; our IP address is 10.1.0.129
Filename 'root_qtopia-128M.img'.
Load address: 0x30000000
Loading: #################################################################
         ################################################################# 

         ... ...
         #################################################################
         ####################
done
Bytes transferred = 58487616 (37c7340 hex)
[u-boot@MINI2440]#

在yaffs2文件系统镜像下载到内存后:

[u-boot@MINI2440]# nand erase 0 0x40000

NAND erase: device 0 offset 0x0, size 0x40000
Erasing at 0x2000000000004 --   0% complete.
OK
[u-boot@MINI2440]# nand write 0x30000000 0 0x40000

NAND write: device 0 offset 0x0, size 0x40000
Writing at 0x2000000020000 -- 100% is complete. 262144 bytes written: OK
[u-boot@MINI2440]#

 可以看到,yaffs2文件系统烧录成功。下一步需要加载内核来挂载yaffs2文件系统。

 

接下来将进入u-boot的第六阶段,为u-boot-2009.08 增加引导内核功能。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值