分析了一下Uboot中Nandflash的驱动,u-boot-2009.08使用的是和Linux内核一样的MTD(内存技术设备)架构。在Uboot下对Nand的支持体现在命令行下实现对nand flash的操作,为:nand info,nand device,nand read,nand write,nand erease,nand bad。用到的主要数据结构有:struct nand_flash_dev,struct nand_chip。前者包括主要的芯片型号,存储容量,设备ID,I/O总线宽度等信息;后者是具体对nand flash进行操作时用到的信息。 
对Nandflash驱动代码的分析: 

1)lib_arm/board.c中start_armboot()函数中调用了nand_init()。 
2)nand_init()函数定义在drivers/mtd/nand/Nand.c文件中,调用了同文件下的nand_init_chip()函数和board_nand_select_device()函数,并累加Nandflash的总大小。 
3)nand_init_chip()函数初始化了IO_ADDR_R和IO_ADDR_W,调用board_nand_init()和nand_scan()。(在drivers/mtd/nand/S3c2410_nand.c中) 
4)board_nand_init()函数在drivers/mtd/nand/S3c2410_nand.c中,初始化NFCONF配置寄存器。主要对struct nand_chip结构体的函数指针赋值,让他们指向自己为nand驱动编写的一些函数,该数据结构在include/linux/mtd/nand.h中定义。 
5)nand_scan()函数在drivers/mtd/nand/Nand_base.c文件中定义,并调用了nand_scan_ident()和nand_scan_tail()函数。 
6)nand_scan_ident()调用了同文件下的nand_set_defaults()和nand_get_flash_type(),nand_set_defaults()函数对struct nand_chip结构体的函数指针进行了赋值。在此函数中cmdfunc映射到了nand_command,nand_get_flash_type()读取了厂商和设备ID,并对struct nand_chip结构体的变量和mtd_info进行初始化操作。 
7)nand_scan_tail()进行了ECC的设置和剩下的MTD驱动函数的初始化。 
8)nand_select_device()函数用来打开或关闭nand芯片,-1是打开,0是关闭。chip->cmd_ctrl映射到hwcontrol(在drivers/mtd/nand/S3c2410_nand.c的board_nand_init()函数中)。 
9)返回nand_init(),这样nand的初始化就完成了。 
移植Nandflash驱动,主要是修改S3c2410_nand.c。 
看一个Read操作的例子:

以common/env_nand.c里读取Nandflash的环境变量为例, 
env_nand.c中调用了nand_read(&nand_info[0], offset, &len, char_ptr); 
nand_read在include/nand.h中定义。 
static inline int nand_read(nand_info_t *info, loff_t ofs, size_t *len, u_char *buf) 
{ 
    return info->read(info, ofs, *len, (size_t *)len, buf); 
} 
nand_read又调用info->read,即mtd_info的read,mtd_info与nand_info_t同名。 
mtd_info的read函数在drivers/nand/nand_base.c中的nand_scan_tail函数中被映射到nand_read(),在nand_read()中调用nand_do_read_ops()。这是最后一层,通过调用nand_chip中的函数完成带ECC的读操作。
现在进行移植: 
修改include/configs/TE2440II.h如下 
/* 
* 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_CMDLINE_EDITING 
#ifdef CONFIG_CMDLINE_EDITING 
#undef CONFIG_AUTO_COMPLETE 
#else 
#define CONFIG_AUTO_COMLETE 
#endif 
/* 
* NAND flash setting 
*/ 
#if defined(CONFIG_CMD_NAND) 
#define CONFIG_SYS_NAND_BASE 0x4e000000 
#define CONFIG_SYS_MAX_NAND_DEVICE 1 
#define CONFIG_MTD_NAND_VERIFY_WRITE 1 
#define NAND_SAMSUNG_LP_OPTIONS 1 
#define CONFIG_NAND_S3C2440 1 
#endif 
环境变量那部分修改: 
//#define CONFIG_ENV_IS_IN_FLASH    1 
#define CONFIG_ENV_IS_IN_NAND 1         /*环境变量的保存位置*/ 
在drivers/mtd/nand/Makefile文件中添加: 
COBJS-y += s3c2440_nand.o 
COBJS-$(CONFIG_NAND_S3C2440) += s3c2440_nand.o 
我们最重要的是修改s3c2410_nand.c,把它复制重命名为s3c2440_nand.c,因为s3c2410和s3c2440在nand控制器方面有很多不同,所以要进行移植。 
修改s3c2440_nand.c为: 
#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 
ulong IO_ADDR_W = NF_BASE;
static void s3c2440_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) { 
        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; //使能nand flash 
            //DEBUGN("NFCONT is 0x%x ",NFCONT); 
            //DEBUGN("nand Enable "); 
            } 
        else 
            {NFCONT |= S3C2440_NFCONT_nCE;  //禁止nand flash 
            //DEBUGN("nand disable "); 
            } 
    } 
if (cmd != NAND_CMD_NONE) 
        writeb(cmd,(void *)IO_ADDR_W); 
} 
static int s3c2440_dev_ready(struct mtd_info *mtd) 
{ 
    DEBUGN("dev_ready\n"); 
    return (NFSTAT & 0x01); 
} 
/******************************************************************************************/
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); 
DEBUGN("CONFIG_S3C2440\n"); 
        twrph0 = 4; twrph1 = 2; tacls = 0; 
    cfg = (tacls<<12)|(twrph0<<8)|(twrph1<<4); 
    NFCONF = cfg; 
    //DEBUGN("cfg is %x\n",cfg); 
    //DEBUGN("NFCONF is %lx\n",NFCONF); 
    cfg = (1<<6)|(1<<4)|(0<<1)|(1<<0); 
    NFCONT = cfg; 
    //DEBUGN("cfg is %lx\n",cfg); 
    //DEBUGN("NFCONT is %x\n",NFCONT); 
/* initialize nand_chip data structure */ 
    nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)0x4e000010; 
/* read_buf and write_buf are default */ 
    /* read_byte and write_byte are default */ 
/* hwcontrol always must be implemented */ 
    nand->cmd_ctrl = s3c2440_hwcontrol; 
    nand->dev_ready = s3c2440_dev_ready; 
    return 0; 
} 
s3c2440_hwcontrol函数主要控制是写命令还是写地址。 
我遇到的问题,由于board_nand_init()函数我没有添加return 0,导致后面的判断没法进行,结果读出的NAND为0MiB,所以移植一定要仔细阅读源码,还有个问题就是uboot的打印信息中出现NAND_ECC_NONE selected by board driver. This is not recommended!!,这根ECC的校验模式有关,有人说“据说vivi或uboot通过软件算法产生的ecc校验码于S3C2410 NAND Flash 控制器产生的ecc校验码不一致”,所以我就没改什么,我把那个打印信息注释掉了。


![B[$N1[V$7}3$]M2JC6YL4WA B[$N1[V$7}3$]M2JC6YL4WA](/uploads/allimg/200608/192R25502_0.gif)