本节中实现uboot对Nand flash的操作
对比了S3C2410和S3C2440的Nand控制寄存器,他们之间的差别还是比较大,uboot源代码中支持2410 nand,我们需要复制文件drivers/mtd/nand/s3c2410_nand.c
为s3c2440_nand.c
修改drivers/mtd/nand/Makefile
加入obj-$(CONFIG_NAND_S3C2440) += s3c2440_nand.o
CONFIG_NAND_S3C2410
定义在include/configs/smdk2410.h
中
接下来修改include/configs/smdk2440.h
将
#ifdef CONFIG_CMD_NAND
#define CONFIG_NAND_S3C2410
#define CONFIG_SYS_S3C2410_NAND_HWECC
#define CONFIG_SYS_MAX_NAND_DEVICE 1
#define CONFIG_SYS_NAND_BASE 0x4E000000
#endif
修改为
#ifdef CONFIG_CMD_NAND
#define CONFIG_NAND_S3C2440
#define CONFIG_SYS_S3C2440_NAND_HWECC
#define CONFIG_SYS_MAX_NAND_DEVICE 1
#define CONFIG_SYS_NAND_BASE 0x4E000000
#endif
之后编译uboot,发现可以正常编译出u-boot.bin
,但是在最后报错
原因时在uboot编译时,检查有关Kconfig中是否有关新定义的宏是否有对应的配置项。
打开drivers/mtd/nand/Kconfig
,看到并没有CONFIG_NAND_S3C2410
的配置。
使用grep
命令查找uboot源代码,可以看到在scripts/config_whitelist.txt
中有这个宏,从文件名可以推断出如果宏定义在此文件中,Kconfig中不定义对应项,也能正常编译通过。因此在此文件中哦你工加入
CONFIG_NAND_S3C2440
CONFIG_SYS_S3C2440_NAND_HWECC
drivers/mtd/nand/s3c2440_nand.c
修改寄存器的配置项
#define S3C2440_NFCONT_EN (1<<0)
#define S3C2440_NFCONT_INITECC (1<<4)
#define S3C2440_NFCONT_nFCE (1<<1)
#define S3C2440_NFCONF_TACLS(x) ((x)<<12)
#define S3C2440_NFCONF_TWRPH0(x) ((x)<<8)
#define S3C2440_NFCONF_TWRPH1(x) ((x)<<4)
#define S3C2440_ADDR_NALE 0x0C
#define S3C2440_ADDR_NCLE 0x08
修改s3c24x0_hwcontrol
static void s3c24x0_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
struct s3c24x0_nand *nand = s3c24x0_get_base_nand();
debug("hwcontrol(): 0x%02x 0x%02x\n", cmd, ctrl);
if (ctrl & NAND_CTRL_CHANGE) {
if (ctrl & NAND_NCE)
{
writel(readl(&nand->nfcont) & ~S3C2440_NFCONT_nFCE,
&nand->nfcont);
}
else
{
writel(readl(&nand->nfcont) | S3C2440_NFCONT_nFCE,
&nand->nfcont);
}
}
if (ctrl & NAND_CLE)
{
writeb(cmd,&nand->nfcmd);
}
else if (ctrl & NAND_ALE)
{
writeb(cmd,&nand->nfaddr);
}
}
这个函数里面已经有了对nand控制器使能引脚的操作,修改下地址和命令之间的区分就可以了
修改board_nand_init
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 s3c24x0_nand *nand_reg = s3c24x0_get_base_nand();
debug("board_nand_init()\n");
writel(readl(&clk_power->clkcon) | (1 << 4), &clk_power->clkcon);
/* initialize hardware */
#if defined(CONFIG_S3C24XX_CUSTOM_NAND_TIMING)
tacls = CONFIG_S3C24XX_TACLS;
twrph0 = CONFIG_S3C24XX_TWRPH0;
twrph1 = CONFIG_S3C24XX_TWRPH1;
#else
tacls = 0;
twrph0 = 1;
twrph1 = 0;
#endif
cfg = 0;
cfg |= S3C2440_NFCONF_TACLS(tacls - 1);
cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);
cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);
writel(cfg, &nand_reg->nfconf);
//enable NAND controller
cfg = S3C2440_NFCONT_EN | S3C2440_NFCONT_INITECC | S3C2440_NFCONT_nFCE;
writel(cfg, &nand_reg->nfcont);
/* initialize nand_chip data structure */
nand->IO_ADDR_R = (void *)&nand_reg->nfdata;
nand->IO_ADDR_W = (void *)&nand_reg->nfdata;
//nand->select_chip = s3c24x0_select_chip;
nand->select_chip = NULL;
/* read_buf and write_buf are default */
/* read_byte and write_byte are default */
#ifdef CONFIG_NAND_SPL
nand->read_buf = nand_read_buf;
#endif
/* hwcontrol always must be implemented */
nand->cmd_ctrl = s3c24x0_hwcontrol;
nand->dev_ready = s3c24x0_dev_ready;
#ifdef CONFIG_S3C2410_NAND_HWECC
nand->ecc.hwctl = s3c24x0_nand_enable_hwecc;
nand->ecc.calculate = s3c24x0_nand_calculate_ecc;
nand->ecc.correct = s3c24x0_nand_correct_data;
nand->ecc.mode = NAND_ECC_HW;
nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;
nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;
nand->ecc.strength = 1;
#else
nand->ecc.mode = NAND_ECC_SOFT;
#endif
#ifdef CONFIG_S3C2410_NAND_BBT
nand->bbt_options |= NAND_BBT_USE_FLASH;
#endif
debug("end of nand_init\n");
return 0;
}
之后,nand flash就可以正常操作了
在uboot自带的源代码中,s3c24x0_hwcontrol
函数的以下代码块实现对其的控制
if (ctrl & NAND_CTRL_CHANGE) {
if (ctrl & NAND_NCE)
{
writel(readl(&nand->nfcont) & ~S3C2440_NFCONT_nFCE,
&nand->nfcont);
}
else
{
writel(readl(&nand->nfcont) | S3C2440_NFCONT_nFCE,
&nand->nfcont);
}
}
也可以不用这部分代码,删掉后加入自己写的控制函数
void s3c24x0_select_chip(struct mtd_info *mtd, int chip)
{
struct s3c24x0_nand *nand = s3c24x0_get_base_nand();
if(chip == 0)
{
writel(readl(&nand->nfcont) & ~S3C2440_NFCONT_nFCE,
&nand->nfcont);
}
else if(chip == -1)
{
writel(readl(&nand->nfcont) | S3C2440_NFCONT_nFCE,
&nand->nfcont);
}
}
在board_nand_init
中对其注册
nand->select_chip = s3c24x0_select_chip;
这样也可以正常使用
烧写新编译的uboot.bin后,可以用如下方式进行测试
md.b 30000000
nand erase 0 4
nand write 30000000 0 4
nand read 30008000 0 4
md.b 30008000
查看新读出的数据前四个字节是否与之前读出30000000地址的数据相同