U-Boot 的移植入门(2)——nand flash识别与操作

本文详细介绍了U-Boot环境下NAND Flash驱动的移植步骤,包括必要的宏定义、配置选项及具体函数实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第三步、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 <common.h>

#include <nand.h>
#include <s3c2410.h>
#include <asm/io.h>
#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 ,再编译,可以正常显示芯片了。

几点注意说明

1 CONFIG_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 校验不是由同一软件产生所致。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值