s3c2440_uboot移植(五)支持Nand操作

1. 让uboot 支持操作Nand Flash

1.1 uboot 的不足


在第三篇中,我们给uboot移植了Nand 启动的代码,并且成功启动了。不过uboot
程序仍然无法正常去操作Nand Flash,原因是我们并没有配置uboot 中关于Nand Flash操作的相关代码。

1.2 添加对Nand Flash 的操作

1.2.1 添加宏定义和s3c2440_nand.c 文件

  1. 要让uboot 支持Nand Flash 操作,我们首先需要给include/configs
    /s3c2440.h,添加CONFIG_CMD_NAND 的宏定义
    添加CMD_NAND配置

  2. 拷贝driver/mtd/nand/s3c2410_nand.c 为driver/mtd/nand/s3c2440_nand.
    c,仿造s3c2410_nand 来写我们的s3c2440_nand 文件。并且在driver/
    mtd/nand/Makefile 中添加s3c2440_nand.c
    ,将其编译进内核:
    
修改Makefile
    从图中我们可知,我们还需要往s3c2440.h 中添加宏定义CONFIG_NAND_S3C2440

完成上面两步操作后,我们就成功将Nand 的代码添加到我们的uboot 中了。

1.2.2 修改s3c2440_nand.c 使其适配我们的jz2440

我们使用的是s3c2410 的nand 代码,其并不适配我们的s3c2440 芯片。所以我们需要基于s3c2410 的代码上进行修改。

添加宏定义

为了方便进行操作,我们仿造s3c2410 定义一些s3c2440 芯片操作Nand Flash 控制器需要用到宏定义:

// 与时序设置有关,在设置时序时使用到了
#define S3C2440_NFCONF_TACLS(x)    ((x)<<12)
#define S3C2440_NFCONF_TWRPH0(x)   ((x)<<8)
#define S3C2440_NFCONF_TWRPH1(x)   ((x)<<4)

// 控制有关
#define S3C2440_NFCONT_nFCE		    (1<<1)
#define S3C2440_NFCONT_ENABLE	    (1)
#define S3C2440_NFCONT_INITECC	    (1<<4)
#define S3C2440_NFCONT_MAINECCLock  (1<<5)
#define S3C2440_NFCONT_SpareECCLock (1<<6)

// 寄存器地址
#define S3C2440_ADDR_NCLE			0x8			// 命令寄存器的偏移地址
#define S3C2440_ADDR_NALE			0xC			// 地址寄存器的偏移地址
修改时序

board_nand_init时序设置

在board_nand_init 中设置上面的时序,Nand Flash 芯片为K9F2G08U0C,其时序分析如下:
board_nand_init时序设置
从上面的表格数据来看:(HCLK时钟,100MHz,其工作频率为10ns每次)

  1. 我们CLS/ALS都需要2clk的时间,但仔细看时序图其实tWP 与tCLS 和tALS 的终止时序是同一点。并且刚好它们都为12ns,因此它们是可以同时发出的。所以我们的tACLS 可以取0个周期
  2. tWRPH0 为tWP/tRE(需要12ns)因此最少需要2clk,总共20ns。
  3. tWRPH1 为tWH/tRE(需要10ns)因此可以设置为1clk
  4. tWC/tRC 的nWE/nRE 信号的一次周期需要25ns,并且tWC/tRC 等于tWRPH0 + tWRPH1 正好等于30ns(>= 25ns),也满足读写信号的时序要求。
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 s3c2440_nand *nand_reg = s3c2440_get_base_nand();

	debug("board_nand_init()\n");

	// 使能Nand Flash控制器,开启Nand Flash模块
	writel(readl(&clk_power->clkcon) | (1 << 4), &clk_power->clkcon);

#if defined(CONFIG_S3C24XX_CUSTOM_NAND_TIMING)
	tacls  = CONFIG_S3C24XX_TACLS;
	twrph0 = CONFIG_S3C24XX_TWRPH0;
	twrph1 = CONFIG_S3C24XX_TWRPH1;
#else
	tacls  = 0; // 0ns
	twrph0 = 2; // 20ns
	twrph1 = 1; // 10ns
#endif
	// S3C2440_NFCONT_ENABLE: 开启Nand Flash控制器
    // S3C2440_NFCONT_INITECC: 使能ECC
    // S3C2440_NFCONT_nFCE: 禁止片选
	cfg = S3C2440_NFCONT_ENABLE | S3C2440_NFCONT_INITECC | S3C2440_NFCONT_nFCE;
	writel(cfg, &nand_reg->nfcont);
	
	/* 设置时序 */ 
	cfg = 0;
	cfg |= S3C2440_NFCONF_TACLS(tacls);
	cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);
	cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);
	writel(cfg, &nand_reg->nfconf);


	/* initialize nand_chip data structure */
	nand->IO_ADDR_R = (void *)&nand_reg->nfdata;	// 设置读寄存器的地址
	nand->IO_ADDR_W = (void *)&nand_reg->nfdata;	// 设置写寄存器的地址

	nand->select_chip = s3c2440_nand_select;		// 片选函数

	/* read_buf and write_buf are default */
	/* read_byte and write_byte are default */
#ifdef CONFIG_NAND_SPL
	nand->read_buf = s3c2440_nand_read_buf;
#endif

	/* hwcontrol always must be implemented */
	nand->cmd_ctrl = s3c2440_hwcontrol;		// 必须实现的
	nand->dev_ready = s3c2440_dev_ready;	// 必须实现的

#ifdef CONFIG_S3C2440_NAND_HWECC
	nand->ecc.hwctl 	= s3c2440_nand_enable_hwecc;
	nand->ecc.calculate = s3c2440_nand_calculate_ecc;
	nand->ecc.correct 	= s3c2440_nand_correct_data;
	nand->ecc.mode 		= NAND_ECC_HW;
	nand->ecc.size 		= CONFIG_SYS_NAND_ECCSIZE;
	nand->ecc.bytes 	= CONFIG_SYS_NAND_ECCBYTES;
#else
	nand->ecc.mode 		= NAND_ECC_SOFT;
#endif

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

	debug("end of nand_init\n");

	return 0;
}

上面我们实现了s3c2440_hwcontrol、s3c2440_dev_ready,以及ECC操作需
要的相关函数,这里我们先忽略ECC操作,先完成前两个函数。

实现芯片相关的函数
s3c2440_hwcontrol 实现

根据芯片手册,我们基于原理s3c2410_hwcontrol 的代码进行修改,修改使能芯片的操作。

static void s3c2440_hwcontrol(struct mtd_info *mtd, int dat, unsigned int ctrl)
{
	struct nand_chip *chip = mtd->priv;
	struct s3c2440_nand *nand = s3c2440_get_base_nand();
	
	debug("hwcontrol(): 0x%02x 0x%02x\n", dat, ctrl);

	if (ctrl & NAND_CLE) {
		writeb(dat, &nand->nfcmd);
	}
	else if (ctrl & NAND_ALE) {
		writeb(dat, &nand->nfaddr);
	}
}
s3c2440_dev_ready 实现
static int s3c2440_dev_ready(struct mtd_info *mtd)
{
	struct s3c2440_nand *nand = s3c2440_get_base_nand();
	debug("dev_ready\n");
	return readl(&nand->nfstat) & 0x01;
}
s3c2440_nand_select 实现

​ 这里模仿nand_select_chip 的实现。

static void s3c2440_nand_select(struct mtd_info *mtd, int chipnr)
{
	struct nand_chip *chip = mtd->priv;
	struct s3c2440_nand *nand = s3c2440_get_base_nand();
	struct nand_chip *chip = mtd->priv;

	switch (chipnr) {
	case -1:
		writel(readl(&nand->nfconf) | S3C2440_NFCONT_nFCE, &nand->nfcont);
		break;
	case 0:
		writel(readl(&nand->nfconf) & ~S3C2440_NFCONT_nFCE, &nand->nfcont);
		break;
	default:
		BUG();
	}
}

1.3 重新烧写

修改完了s3c2440_nand.c 文件,我们重新编译程序并进行烧写。如下图,我们的uboot 已经成功识别出Nand Flash 的大小,为256 MiB。
在这里插入图片描述

2. 总结

为uboot 添加Nand Flash 的支持,我们需要完成以下几件事:

  1. 我们从board.c 的board_init_r 函数开始分析,发现Nand Flash 的初始化入口函数是nand_init 函数,于是我们从nand_init 函数开始进行追踪;

  2. nand_init 会根据配置文件(s3c2440.h)中指定Nand Flash 芯片的个数,对每一个芯片分别调用nand_init_chip 函数,分别对每一个块芯片的nand_chip 结构体(对象)进行初始化,设置相关的操纵函数;

  3. nand_init_chip 会调用board_nand_init,进而进入我们自己创建的s3c2440_nand.c 文件中的board_nand_init 函数,进行芯片(与开发板相关的)初始化工作,由我们自己指定相关需要重新实现的操纵函数
    其中s3c2440_hwcontrol、s3c2440_dev_ready 是必须重新实现的操纵函数。除此以外,对于我们的s3c2440 芯片来说还需要设置Nand Flash 控制器操作的时序;

  4. **board_nand_init 函数返回后,会执行nand_scan 函数,该函数会对我们的Nand Flash 芯片进行扫描,去读芯片型号、厂家信息等,一些列固化在Nand Flash 芯片的信息。**并且对于nand_chip 中没有在board_nand_init 函数中被指定的操纵函数,会在nand_set_defaults 函数内被指定一个默认的函数。

其执行的流程大致如下:

在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值