淘先锋技术网

首页 1 2 3 4 5 6 7

 

引用

expowinzaxmini2440 u-boot-2009.03 移植最详细攻略

 

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> 

增加变量定义:
...
.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)
 
 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的中断寄存器有改变,设置中断寄存器
 
#if defined(CONFIG_S3C2400)
#  define pWTCON  0x15300000
#  define INTMSK  0x14400008 
#  define CLKDIVN 0x14800014 
#elif defined(CONFIG_S3C2410)
#  define pWTCON  0x53000000
# define INTMOD     0X4A000004 
#  define INTMSK  0x4A000008 
#  define INTSUBMSK 0x4A00001C
#  define CLKDIVN 0x4C000014 
#endif

继续,关开门狗和中断
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
 ldr     r0, =pWTCON
 mov     r1, #0x0
 str     r1, [r0]

 
 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
 
 
 ldr r0, =CLKDIVN
 mov r1, #3
 str r1, [r0]
#endif
#endif 

继续,CPU初始化
...
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
 adr r0, _start     
 ldr r1, _TEXT_BASE  
 cmp     r0, r1       
 blne cpu_init_crit
#endif

说明:这个地方要先判断U-boot是否在运行地址中,如果是就不进行CPU的初始化。

删除 #ifndef CONFIG_AT91RM9200 以及其对应的 #endif,将下面设置堆栈的一段剪切来这里:

 
stack_setup:
 ldr r0, _TEXT_BASE  
 sub r0, r0, #CONFIG_SYS_MALLOC_LEN 
 sub r0, r0, #CONFIG_SYS_GBL_DATA_SIZE
#ifdef CONFIG_USE_IRQ
 sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
 sub sp, r0, #12  

设置好堆栈后,可以用C代码来设置时钟了。添加代码如下:这段C代码后面给出。
...
    bl clock_init
...

到这里为止,完成的工作有:
1:CPU进入管理模式;
2:关看门狗和中断;
3:CPU的初始化
4:堆栈设置
5:时钟的初始化

至此,初始化工作告一段落,要搬运代码到内存中了(U-boot的运行地址),代码如下:

#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate:    
...

如果代码已经在运行地址了,则不应该进行代码搬运,而直接跳到后面的代码运行,由于我们
已经在前面设置了堆栈,设置堆栈的label到这段代码的前面去了,所以将跳转改为下面的
label即可。
...
  cmp     r0, r1                 
 beq     clear_bss              
...

由于已经设置好了堆栈,可以采用直接读取NAND Flash的C语言代码来搬运代码,从而达到能从
NAND Flash启动的目的。原来的汇编代码只能从NOR Flash搬运代码。修改的代码如下:
...
 ldr r2, _armboot_start
 ldr r3, _bss_start
 sub r2, r3, r2  
#if 1
 bl  CopyCode2Ram 
#else
 add r2, r0, r2  

copy_loop:
 ldmia r0!, {r3-r10}  
 stmia r1!, {r3-r10}  
 cmp r0, r2   
 ble copy_loop
#endif

添加对 PreLoadedONRAM 的修改,如果直接在内存中运行,则将值修改为1:
...
clbss_l:str r2, [r0]  
 add r0, r0, #4
 cmp r0, r1
 ble clbss_l

SetLoadFlag:
 
 adr r0, _start  
 ldr r1, _TEXT_BASE  
 cmp     r0, r1                 
 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)
#define B4_BWSCON    (DW16 + WAIT + UBLB)
#define B5_BWSCON    (DW8)
#define B6_BWSCON  (DW32)
#define B7_BWSCON  (DW32)
...

然后修改 REFRESH 的刷新周期:
...
#define REFCNT    0x4f4 
...

lowlevel_init.s 修改完成。

3):board/smdk2440/smdk2440.c 去掉对时钟的修改

删除下面的一段:
#define FCLK_SPEED 1

#if FCLK_SPEED==0  
#define M_MDIV 0xC3
#define M_PDIV 0x4
#define M_SDIV 0x1
#elif FCLK_SPEED==1  
#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));
}

继续删除下面一段:
 
 clk_power->LOCKTIME = 0xFFFFFF;

 
 clk_power->MPLLCON = ((M_MDIV << 12) + (M_PDIV << 4) + M_SDIV);

 
 delay (4000);

 
 clk_power->UPLLCON = ((U_M_MDIV << 12) + (U_M_PDIV << 4) + U_M_SDIV);

 
 delay (8000);

将GPIO设置下面的一段修改如下,支持2410和2440,同时不开启dcache.

...
   
    if (isS3C2410)
    {
 
 gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;
    }
    else
    {
       
        gd->bd->bi_arch_number = MACH_TYPE_S3C2440;
    }

 
 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的支持,修改为:

#define CONFIG_NR_DRAM_BANKS 1   
#define PHYS_SDRAM_1  0x30000000
#define PHYS_SDRAM_1_SIZE 0x04000000

#define PHYS_FLASH_1  0x00000000
#define CONFIG_SYS_FLASH_BASE  PHYS_FLASH_1
#define CONFIG_SYS_MONITOR_BASE  PHYS_FLASH_1 


#if 0
#define CONFIG_AMD_LV400 1 
#define CONFIG_AMD_LV800 1 
#endif

#define CONFIG_SYS_MAX_FLASH_BANKS 1 
#ifdef CONFIG_AMD_LV800
#define PHYS_FLASH_SIZE  0x00100000
#define CONFIG_SYS_MAX_FLASH_SECT (19) 
#define CONFIG_ENV_ADDR  (CONFIG_SYS_FLASH_BASE + 0x0F0000)
#endif
#ifdef CONFIG_AMD_LV400
#define PHYS_FLASH_SIZE  0x00080000
#define CONFIG_SYS_MAX_FLASH_SECT (11) 
#define CONFIG_ENV_ADDR  (CONFIG_SYS_FLASH_BASE + 0x070000)
#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  
#define CONFIG_ENV_ADDR  (CONFIG_SYS_FLASH_BASE + 0x1E0000) 
#define CONFIG_SYS_FLASH_CFI_WIDTH      0x02 
#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



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;

   
    if (isS3C2410)
        return((CONFIG_SYS_CLK_FREQ * m) / (p << s));
    else
        return((CONFIG_SYS_CLK_FREQ * m * 2) / (p << s));  
}


ulong get_FCLK(void)
{
    return(get_PLLCLK(MPLL));
}


#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)


ulong get_HCLK(void)
{
    S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
    unsigned long clkdiv;
    unsigned long camdiv;
    int hdiv = 1;

   
    if (isS3C2410)
        return((clk_power->CLKDIVN & 0x2) ? get_FCLK()/2 : get_FCLK());
    else
    {
        clkdiv = clk_power->CLKDIVN;
        camdiv = clk_power->CAMDIVN;

       

        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;
    }
}


ulong get_PCLK(void)
{
    S3C24X0_CLOCK_POWER * const clk_power = S3C24X0_GetBase_CLOCK_POWER();
    unsigned long clkdiv;
    unsigned long camdiv;
    int hdiv = 1;

   
    if (isS3C2410)
        return((clk_power->CLKDIVN & 0x1) ? get_HCLK()/2 : get_HCLK());
    else
    {  
        clkdiv = clk_power->CLKDIVN;
        camdiv = clk_power->CAMDIVN;

       

        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);
    }       
}


ulong get_UCLK(void)
{
    return(get_PLLCLK(UPLL));
}

#endif

然后修改 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;   
} 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 
#define CS8900_BASE  0x19000300
#define CS8900_BUS16  1
#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_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 flash参数设置。如下:

#define CONFIG_SYS_NAND_BASE          0     
#define CONFIG_SYS_MAX_NAND_DEVICE    1     
#define NAND_MAX_CHIPS         1      
#define SECTORSIZE             512        
#define ADDR_COLUMN            1  
#define ADDR_PAGE              3     
#define ADDR_COLUMN_PAGE       4 
#define NAND_ChipID_UNKNOWN    0x00 
#define NAND_MAX_FLOORS        1    
#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) {
  
  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;

  

  if (ctrl & NAND_NCE)
   NFCONF &= ~S3C2410_NFCONF_nFCE;
  else
   NFCONF |= S3C2410_NFCONF_nFCE;
 }

 if (cmd != NAND_CMD_NONE)
  
  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;

  

  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");
 
 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;

 
 nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)0x4e00000c;

 
 

 
 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;
 
   
 nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)0x4e000010;

 
 

 
 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      
...

...

#define CONFIG_ENV_IS_IN_NAND        1
#define CONFIG_ENV_OFFSET      0x40000 
#define CONFIG_ENV_SIZE        0x20000  
...

为支持S3C2440的NAND Flash功能添加:
#define CONFIG_S3C2440      1  

到这里,支持读写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;
 }

 
 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;
 }

 

 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);
  }

 }

 
 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  
#define CONFIG_SETUP_MEMORY_TAGS 1

#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 

 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);

PROMPT:
...