u-boot的nand层次分析

s3c2440 u-boot-1.1.6

移植过u-boot-1.1.6的都知道:nand的硬件相关的操作是在/cpu/arm/s3c24x0/目录下新建一个nand_flash.c 。但我们在调试模式使用的nand read等命令的源码位于与硬件无关的/common/cmd_nand.c里面呀???那么它们之间到底是怎么联系起来的呀???

其实就算没有看过源码也能猜到,这个关系应该如下:

       u-boot的nand层次分析_第1张图片

其实nand的初始化是从/lib_arm/borad.c中的start_armboot函数中的nand_init调用开始的。

一、nand硬件操作

/cpu/arm920t/s3c24x0/nand_flash.c(用户根据nand型号自己建立)
#include <common.h>
#if (CONFIG_COMMANDS & CFG_CMD_NAND) && !defined(CFG_NAND_LEGACY)
#include <s3c2410.h>
#include <nand.h>
DECLARE_GLOBAL_DATA_PTR;
#define S3C2440_NFSTAT_READY    (1<<0)
#define S3C2440_NFCONT_nFCE     (1<<1)

/* select chip, for s3c2440 */
static void s3c2440_nand_select_chip(struct mtd_info *mtd, int chip)//nand_flash片选
{
    S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();
    if (chip == -1) {
        s3c2440nand->NFCONT |= S3C2440_NFCONT_nFCE;
    } else {
        s3c2440nand->NFCONT &= ~S3C2440_NFCONT_nFCE;
    }
}
static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd)//根据不同的命令获得寄存器地址
{
    S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();
    struct nand_chip *chip = mtd->priv;

    switch (cmd) {
    case NAND_CTL_SETNCE:
    case NAND_CTL_CLRNCE:  break;

    case NAND_CTL_SETCLE:
        chip->IO_ADDR_W = (void *)&s3c2440nand->NFCMD;break;

    case NAND_CTL_SETALE:
        chip->IO_ADDR_W = (void *)&s3c2440nand->NFADDR; break;

    default:
        chip->IO_ADDR_W = (void *)&s3c2440nand->NFDATA;break;
    }
}
static int s3c2440_nand_devready(struct mtd_info *mtd)//设备是否准备好了
{
    S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();
    return (s3c2440nand->NFSTAT & S3C2440_NFSTAT_READY);
}

static void s3c24x0_nand_inithw(void)//主要是nand_flash控制器的设置,包括奇偶校验,时序等
{
    S3C2410_NAND * const s3c2410nand = S3C2410_GetBase_NAND();
    S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();

#define TACLS   0
#define TWRPH0  4
#define TWRPH1  2
        /* Set flash memory timing */
        s3c2440nand->NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
        /* Initialize ECC, enable chip select, NAND flash controller enable */
        s3c2440nand->NFCONT = (1<<4)|(0<<1)|(1<<0);
}

void board_nand_init(struct nand_chip *chip)//这是供外部调用的接口,主要将操作nand硬件的函数供外面使用
{
    S3C2410_NAND * const s3c2410nand = S3C2410_GetBase_NAND();
    S3C2440_NAND * const s3c2440nand = S3C2440_GetBase_NAND();

    s3c24x0_nand_inithw();
        chip->IO_ADDR_R    = (void *)&s3c2440nand->NFDATA;//nand读地址
        chip->IO_ADDR_W    = (void *)&s3c2440nand->NFDATA;//nand写地址
        chip->hwcontrol    = s3c2440_nand_hwcontrol;//寄存器选择函数,主要用来修改IO_ADDR_R和IO_ADDR_W的值
        chip->dev_ready    = s3c2440_nand_devready;//nand状态查询函数
        chip->select_chip  = s3c2440_nand_select_chip;//nand片选函数
        chip->options      = 0;
    chip->eccmode       = NAND_ECC_SOFT;//软件ecc校验
}
#endif

二、nand操作上层接口

/include/linux/mtd/nand.h
其实nand的硬件操作最终都是以该变量作为接口来访问的,它里面的指针就指向nand的硬件操作函数。从上面board_nand_init初始化来看
其实很多nand的操作都没有实现,这是因为很多的操作可以通过上面的几个来实现。而且也没有必要实现struct nand_chip里面的所有函数。
struct nand_chip {
	void  __iomem	*IO_ADDR_R;
	void  __iomem	*IO_ADDR_W;

	u_char		(*read_byte)(struct mtd_info *mtd);
	void		(*write_byte)(struct mtd_info *mtd, u_char byte);
	u16		(*read_word)(struct mtd_info *mtd);
	void		(*write_word)(struct mtd_info *mtd, u16 word);

	void		(*write_buf)(struct mtd_info *mtd, const u_char *buf, int len);
	void		(*read_buf)(struct mtd_info *mtd, u_char *buf, int len);
	int		(*verify_buf)(struct mtd_info *mtd, const u_char *buf, int len);
	void		(*select_chip)(struct mtd_info *mtd, int chip);
	int		(*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
	int		(*block_markbad)(struct mtd_info *mtd, loff_t ofs);
	void		(*hwcontrol)(struct mtd_info *mtd, int cmd);
	int		(*dev_ready)(struct mtd_info *mtd);
	void		(*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);
	int		(*waitfunc)(struct mtd_info *mtd, struct nand_chip *this, int state);
	int		(*calculate_ecc)(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code);
	int		(*correct_data)(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
	void		(*enable_hwecc)(struct mtd_info *mtd, int mode);
	void		(*erase_cmd)(struct mtd_info *mtd, int page);
	int		(*scan_bbt)(struct mtd_info *mtd);
	int		eccmode;
	int		eccsize;
	int		eccbytes;
	int		eccsteps;
	int		chip_delay;
#if 0
	spinlock_t	chip_lock;
	wait_queue_head_t wq;
	nand_state_t	state;
#endif
	int		page_shift;
	int		phys_erase_shift;
	int		bbt_erase_shift;
	int		chip_shift;
	u_char		*data_buf;
	u_char		*oob_buf;
	int		oobdirty;
	u_char		*data_poi;
	unsigned int	options;
	int		badblockpos;
	int		numchips;
	unsigned long	chipsize;
	int		pagemask;
	int		pagebuf;
	struct nand_oobinfo	*autooob;
	uint8_t		*bbt;
	struct nand_bbt_descr	*bbt_td;
	struct nand_bbt_descr	*bbt_md;
	struct nand_bbt_descr	*badblock_pattern;
	struct nand_hw_control	*controller;
	void		*priv;
};                                                                                                          

三、struct nand_chip其他函数的初始化

/driver/nand/nand_base.c   下面使用到的默认nand操作函数都在该文件下
int nand_scan (struct mtd_info *mtd, int maxchips)
	int i, j, nand_maf_id, nand_dev_id, busw;
	struct nand_chip *this = mtd->priv;//我们初始化过的struct nand_chip 结构体
	busw = this->options & NAND_BUSWIDTH_16;//设置位宽
	if (!this->chip_delay)
		this->chip_delay = 20;//设置延时时间
	if (this->cmdfunc == NULL)         //如果用户没有指定nand的cmdfunc就是用默认的,这是很nand最为重要的函数,需根据实际修改
		this->cmdfunc = nand_command;
	if (this->waitfunc == NULL)        //如果用户没有指定nand的waitfunc就是用默认的
		this->waitfunc = nand_wait;
	if (!this->select_chip)                   //片选函数指定过了
		this->select_chip = nand_select_chip;
	if (!this->write_byte)                  //如果用户没有指定nand的write_byte就是用默认的
		this->write_byte = busw ? nand_write_byte16 : nand_write_byte;
	if (!this->read_byte)                    //如果用户没有指定nand的read_byte就是用默认的
		this->read_byte = busw ? nand_read_byte16 : nand_read_byte;
	if (!this->write_word)                      //如果用户没有指定就是用默认的
		this->write_word = nand_write_word;
	if (!this->read_word)                       //如果用户没有指定就是用默认的
		this->read_word = nand_read_word;
	if (!this->block_bad)                        //如果用户没有指定就是用默认的
		this->block_bad = nand_block_bad;
	if (!this->block_markbad)                  //如果用户没有指定就是用默认的
		this->block_markbad = nand_default_block_markbad;
	if (!this->write_buf)                            //如果用户没有指定就是用默认的
		this->write_buf = busw ? nand_write_buf16 : nand_write_buf;
	if (!this->read_buf)                              //如果用户没有指定就是用默认的
		this->read_buf = busw ? nand_read_buf16 : nand_read_buf;
	if (!this->verify_buf)                           //如果用户没有指定就是用默认的
		this->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;
	if (!this->scan_bbt)                            //如果用户没有指定就是用默认的
		this->scan_bbt = nand_default_bbt;
	/* Select the device */
	this->select_chip(mtd, 0);

	/* Send the command for reading device ID */
	this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);

	/* Read manufacturer and device IDs */
	nand_maf_id = this->read_byte(mtd);
	nand_dev_id = this->read_byte(mtd);
	下面的代码就是我们nand移植好后在串口上打印出NAND 128MiB 3,3V 16-bit这些的源码位置
/driver/nand/nand_base.c主要实现了nand的写操作
static void nand_write_byte16(struct mtd_info *mtd, u_char byte)
{
	struct nand_chip *this = mtd->priv;
	writew(le16_to_cpu((u16) byte), this->IO_ADDR_W);
}
/driver/nand/nand_base.c主要实现nand的读操作
static u_char nand_read_byte16(struct mtd_info *mtd)
{
	struct nand_chip *this = mtd->priv;
	return (u_char) cpu_to_le16(readw(this->IO_ADDR_R));
}
/drivers/nand_base.c 这个要根据实际情况修改
static void nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
	register struct nand_chip *this = mtd->priv;
	this->hwcontrol(mtd, NAND_CTL_SETCLE);//设置chip->IO_ADDR_W地址变为NFCMD寄存器地址
	//1.先写命令
	if (command == NAND_CMD_SEQIN) {//假如是NAND_CMD_SEQIN话,先根据column的值来确定是那个具体的读命令
		int readcmd;
		if (column >= mtd->oobblock) {
			column -= mtd->oobblock;
			readcmd = NAND_CMD_READOOB;
		} else if (column < 256) {
			readcmd = NAND_CMD_READ0;
		} else {
			column -= 256;
			readcmd = NAND_CMD_READ1;
		}
		this->write_byte(mtd, readcmd);//发送出读命令
	}
	this->write_byte(mtd, command);//不是NAND_CMD_SEQIN的话就发送出该命令
                  //2.发地址
	this->hwcontrol(mtd, NAND_CTL_CLRCLE);
	if (column != -1 || page_addr != -1) {//如果column或者page_addr有效
		this->hwcontrol(mtd, NAND_CTL_SETALE);////设置chip->IO_ADDR_W地址变为NFADDR寄存器地址
		/* Serially input address */
		if (column != -1) {
	                                     if (this->options & NAND_BUSWIDTH_16)
			          column >>= 1;
			this->write_byte(mtd, column);//发送地址1
//			udelay(15);		//HJ
		}
		if (page_addr != -1) {
			this->write_byte(mtd, (unsigned char) (page_addr & 0xff));//发送地址2
//			udelay(15);		
			this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));//发送地址3
//			udelay(15);	
			if (this->chipsize > (32 << 20))
			this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));//发送地址4
//   			udelay(15);
		}
		this->hwcontrol(mtd, NAND_CTL_CLRALE);
	}

四、nand操作的上上层

struct nand_chip里面包含了nand的硬件操作的函数指针,但是这个struct nand_chip一般只是作为void *priv,因为顶层的很多操作只是通过struct mtd_info变量来访问的。

/include/linux/mtd/mtd.h  
struct mtd_info 部分内容如下
	int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
	int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);

	int (*read_ecc) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel);
	int (*write_ecc) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel);

	int (*read_oob) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
	int (*write_oob) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);

	int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);

	int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);

	/* This function is not yet implemented */
	int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);

	/* Chip-supported device locking */
	int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len);
	int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len);

	/* Power Management functions */
	int (*suspend) (struct mtd_info *mtd);
	void (*resume) (struct mtd_info *mtd);

	/* Bad block management functions */
	int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
	int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);

	void *priv;
	struct module *owner;
	int usecount;

/drivers/nand/nand.c 
nand_info_t nand_info[CFG_MAX_NAND_DEVICE];//外部可访问
static struct nand_chip nand_chip[CFG_MAX_NAND_DEVICE];//只能文件内部可访问
static void nand_init_chip(struct mtd_info *mtd, struct nand_chip *nand, ulong base_addr)
{
	mtd->priv = nand;//struct nand_chip只是mtd的一部分而已哦
	nand->IO_ADDR_R = nand->IO_ADDR_W = (void  __iomem *)base_addr;
	board_nand_init(nand);
	if (nand_scan(mtd, 1) == 0)
                    {          if (!mtd->name)
		 mtd->name = (char *)default_nand_name;
	  } else
		mtd->name = NULL;

}
/drivers/nand/nand_base.c
int nand_scan (struct mtd_info *mtd, int maxchips)  //在nand_scan函数的后面,在初始化完struct nand_chip结构体后
                   mtd->type = MTD_NANDFLASH;
	mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC;
	mtd->ecctype = MTD_ECC_SW;
	mtd->erase = nand_erase;
	mtd->point = NULL;
	mtd->unpoint = NULL;
	mtd->read = nand_read;
	mtd->write = nand_write;
	mtd->read_ecc = nand_read_ecc;
	mtd->write_ecc = nand_write_ecc;
	mtd->read_oob = nand_read_oob;
	mtd->write_oob = nand_write_oob;
为什么要放在struct nand_chip的后面呀,因为它调用的函数里面都是通过struct nand_chip里面的函数指针完成的
先以nand_read_ecc 部分代码为例,如下:
static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel)
	int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1;
	int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0;
	struct nand_chip *this = mtd->priv;//提取出里面的struct nand_chip结构体
	u_char *data_poi, *oob_data = oob_buf;
	u_char ecc_calc[32];
	u_char ecc_code[32];
	int eccmode, eccsteps
	this->select_chip(mtd, chipnr);//使用struct nand_chip结构体里面的函数完成片选
	realpage = (int) (from >> this->page_shift);
	page = realpage & this->pagemask;//使用struct nand_chip结构体里面的成员
	/* Get raw starting column */
	col = from & (mtd->oobblock - 1);
	end = mtd->oobblock;
	ecc = this->eccsize;//使用struct nand_chip结构体里面的成员
	eccbytes = this->eccbytes;//使用struct nand_chip结构体里面的成员
	if ((eccmode == NAND_ECC_NONE) || (this->options & NAND_HWECC_SYNDROME))     compareecc = 0;
	oobreadlen = mtd->oobsize;
	if (this->options & NAND_HWECC_SYNDROME)//使用struct nand_chip结构体里面的成员
		oobreadlen -= oobsel->eccbytes;
五、nand的顶层操作

nand最终就是供/common/cmd_nand.c中的do_nand函数使用

/common/cmd_nand.c
int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
      nand_info_t *nand;
      nand = &nand_info[nand_curr_device];//获取mtd设备
      if (read)
          ret = nand_read(nand, off, &size, (u_char *)addr);
       else
          ret = nand_write(nand, off, &size, (u_char *)addr);

/include/nand.h
static inline int nand_read(nand_info_t *info, ulong ofs, ulong *len, u_char *buf)
{
	return info->read(info, ofs, *len, (size_t *)len, buf);
}
这就是nand的底层到顶层的调用过程。



你可能感兴趣的:(u-boot,nand)