第三步、nand flash识别与操作
提供对于Nand的支持,仅仅是提供对于nand操作的支持。
首先,要说明一下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组织已不推荐使用。事实上,此版中,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/nand的Makefile文件能看到:
COBJS-$(CONFIG_NAND_S3C2410) += s3c2410_nand.o
include
$(TOPDIR)/config.mk
也就是说这个Makefile会包含顶层目录内的config.mk文件,而在顶层目录内的config.mk文件又能看到:
sinclude
$(OBJTREE)/include/autoconf.mk
这个文件是在编译的时候根据配置文件自动生成的,所以,为了使用s3c2410_nand.c文件,需要定义这个宏。
自带的S3C2410的s3c2410_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校验不是由同一软件产生所致。