Chinaunix首页 | 论坛 | 博客
  • 博客访问: 819746
  • 博文数量: 117
  • 博客积分: 2583
  • 博客等级: 少校
  • 技术积分: 1953
  • 用 户 组: 普通用户
  • 注册时间: 2008-12-06 22:58
个人简介

Coder

文章分类
文章存档

2013年(1)

2012年(10)

2011年(12)

2010年(77)

2009年(13)

2008年(4)

分类: 嵌入式

2010-02-23 08:40:40

第三步、nand flash识别与操作

 

提供对于Nand的支持,仅仅是提供对于nand操作的支持。

首先,要说明一下CFG_NAND_LEGACY的使用。在u-boot/drivers/mtd/下有两个目录,分别是nandnand_legacy。在nand目录下的是nand的初始化函数和nand的操作读写函数,是移植的linuxmtd构架。此目录下的文件,只有在定义了CFG_CMD_NAND宏和没有定义CFG_NAND_LEGACY宏的情况下才会被编译。在nand_leagcy目录下的文件也是是实现nand相关操作命令,如read,write等命令的功能,但不是使用linuxmtd构架。此目录下的文件,只有在定义了CFG_CMD_NAND和定义了CFG_NAND_LEGACY宏的情况下才会定义。此目录下的文件u-boot组织已不推荐使用。事实上,此版中,S3C2410构架已不支持对nand_leagcy,因此,在移植中,是用的不定义CFG_NAND_LEGACY的方式,即非nand_leagcy方式。此版的u-boot已自带board_nand_init(),此函数在drivers/mtd/nand/s3c2410_nand.c中实现。并且此版已不支持定义CFG_NAND_LEGACY,如定义此宏,则编译是会报 #error "U-Boot legacy NAND support not available

for S3C2410"的错误。

 

我们追踪关于Nand的启动初始化代码的执行流,我们从lib_arm目录下的board.c文件开始。启动代码的汇编部分结束,便会执行该文件中的start_armboot函数。在start_armboot函数中,我们看到这样的语句:

#if defined(CONFIG_CMD_NAND)
    puts ("NAND:  ");
    nand_init();        /* go init the NAND */
#endif

也就是说,如果定义了宏CONFIG_CMD_NAND,将会在启动的时候执行初始化Nand的代码,也即提供对于Nand的支持。我们现在配置文件include/configs/mini2440.h  文件中添加这个宏,编译,然后,会报错,我们再根据手册,来定义其他的宏:

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

然后接着追踪执行过程。
nand_init
函数在drivers/mtd/nand/nand.c文件中。
nand_init函数则主要执行相同文件中的nand_init_chip函数。
nand_init_chip函数中调用drivers/mtd/nand/s3c2410_nand.c文件中的board_nand_init函数,由该函数来完成针对特定的板子的nand控制器的初始化。这个文件也就是针对特定板子相关的nand部分代码,移植工作主要修改的也就是这个文件了。根据手册,仔细修改这个文件中的几个函数。注意,S3C2410_ADDR_NALE是用来设置命令寄存器的,而S3C2410_ADDR_NCLE则是用来设置地址寄存器的。同时还要根据手册正确定义结构体struct s3c2410_nand。完整文件为:

#include

#include
#include 3c2410.h>
#include
#define NF_BASE   0x4E000000
#define S3C2410_NFCONT_EN          (1<<0)
#define S3C2410_NFCONT_INITECC     (1<<4)
#define S3C2410_NFCONF_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)

#define S3C2410_ADDR_NALE 0x08
#define S3C2410_ADDR_NCLE 0x0C

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;
    struct s3c2410_nand *nand = s3c2410_get_base_nand();

    debugX(1, "hwcontrol(): 0x%02x 0x%02x\n", cmd, ctrl);

    if (ctrl & NAND_CTRL_CHANGE) {
        IO_ADDR_W = (ulong)nand;

        if (!(ctrl & NAND_CLE))
            IO_ADDR_W |= S3C2410_ADDR_NCLE;
        if (!(ctrl & NAND_ALE))
            IO_ADDR_W |= S3C2410_ADDR_NALE;

        if (ctrl & NAND_NCE)
            writel(readl(&nand->NFCONT) & ~S3C2410_NFCONF_nFCE,
                   &nand->NFCONT);
        else
            writel(readl(&nand->NFCONT) | S3C2410_NFCONF_nFCE,
                   &nand->NFCONT);
    }

    if (cmd != NAND_CMD_NONE)
      writeb(cmd, (void *)IO_ADDR_W);
}

static int s3c2410_dev_ready(struct mtd_info *mtd)
{
    struct s3c2410_nand *nand = s3c2410_get_base_nand();
    debugX(1, "dev_ready\n");
    return readl(&nand->NFSTAT) & 0x01;
}

#ifdef CONFIG_S3C2410_NAND_HWECC
void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
{
    struct s3c2410_nand *nand = s3c2410_get_base_nand();
    debugX(1, "s3c2410_nand_enable_hwecc(%p, %d)\n", mtd, mode);
    writel(readl(&nand->NFCONT) | S3C2410_NFCONT_INITECC, &nand->NFCONT);
}

static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
                      u_char *ecc_code)
{
    ecc_code[0] = NFECC0;
    ecc_code[1] = NFECC1;
    ecc_code[2] = NFECC2;
    debugX(1, "s3c2410_nand_calculate_hwecc(%p,): 0x%02x 0x%02x 0x%02x\n",
           mtd , ecc_code[0], ecc_code[1], ecc_code[2]);

    return 0;
}

static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
                     u_char *read_ecc, u_char *calc_ecc)
{
    if (read_ecc[0] == calc_ecc[0] &&
        read_ecc[1] == calc_ecc[1] &&
        read_ecc[2] == calc_ecc[2])
        return 0;

    printf("s3c2410_nand_correct_data: not implemented\n");
    return -1;
}
#endif

int board_nand_init(struct nand_chip *nand)
{
    u_int32_t cfg;
    u_int8_t tacls, twrph0, twrph1;
    struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();
    struct s3c2410_nand *nand_reg = s3c2410_get_base_nand();

    debugX(1, "board_nand_init()\n");

    writel(readl(&clk_power->CLKCON) | (1 << 4), &clk_power->CLKCON);
    /* initialize hardware */
    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);
    writel(cfg, &nand_reg->NFCONF);

    cfg = (0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(0<<6)|(0<<5)|(1<<4)|(0<<1)|(1<<0);
    writel(cfg, &nand_reg->NFCONT);

    /* initialize nand_chip data structure */
    nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)&nand_reg->NFDATA;

    /* read_buf and write_buf are default */
    /* read_byte and write_byte are default */

    /* hwcontrol always must be implemented */
    nand->cmd_ctrl = s3c2410_hwcontrol;

    nand->dev_ready = s3c2410_dev_ready;

#ifdef CONFIG_S3C2410_NAND_HWECC
    nand->ecc.hwctl = s3c2410_nand_enable_hwecc;
    nand->ecc.calculate = s3c2410_nand_calculate_ecc;
    nand->ecc.correct = s3c2410_nand_correct_data;
    nand->ecc.mode = NAND_ECC_HW3_512;
#else
    nand->ecc.mode = NAND_ECC_SOFT;
#endif

#ifdef CONFIG_S3C2410_NAND_BBT
    nand->options = NAND_USE_FLASH_BBT;
#else
    nand->options = 0;
#endif

    debugX(1, "end of nand_init\n");

    return 0;
}

改写完毕后,u-boot可以识别出nand flash芯片是64MB的,但还不能识别是什么芯片。

/driver/mtd/nand/nand_base.c中的nand_get_flash_type函数结尾,修改MTDDEBUG语句,改为printf,再编译,可以正常显示芯片了。

几点注意说明

1CONFIG_MTD_NAND_VERIFY_WRITE

u-boot自带的nand-flash驱动(不定义nand_leagcy),是基于mtd驱动的。在默认情况下,不进行写入正确与否的校验。要定义CONFIG_MTD_NAND_VERIFY_WRITE宏才能进行写入校验。关于ECC校验,mtd驱动默认是用sotf_ecc的。

2#define CONFIG_NAND_S3C2410

查看drivers/mtd/nandMakefile文件能看到:

COBJS-$(CONFIG_NAND_S3C2410) += s3c2410_nand.o

include $(TOPDIR)/config.mk

也就是说这个Makefile会包含顶层目录内的config.mk文件,而在顶层目录内的config.mk文件又能看到:

sinclude $(OBJTREE)/include/autoconf.mk

这个文件是在编译的时候根据配置文件自动生成的,所以,为了使用s3c2410_nand.c文件,需要定义这个宏。

自带的S3C2410s3c2410_hwcontrol函数有错。在此函数中,把chip->IO_ADDR_W

改写了,导致在写数据时出现错误。将此错误修正后,nand write才可以正常工作。修正方法是使用一全局变量替代chip->IO_ADDR_W

接下来,在rat2440.h中加入#define CONFIG_ENV_IS_IN_NAND 1注掉原来的#define CONFIG_ENV_IS_IN_FLASH 1,加入#define CONFIG_ENV_OFFSET 0x30000 注掉原来的#define CFG_ENV_OFFSET 0x30000。编译。saveenv功能也正常了。
至此,nand-flash驱动移植完成。

测试,nand write 0x30000000 0x40000 0x40000时,成功。用nand read也成功读出。此处要说明的,如果用vivi烧写信息到nand中,再用u-boot读取,会报错,应该是ECC校验不是由同一软件产生所致。

阅读(3488) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~