s3c2440 u-boot-1.1.6
移植过u-boot-1.1.6的都知道:nand的硬件相关的操作是在/cpu/arm/s3c24x0/目录下新建一个nand_flash.c 。但我们在调试模式使用的nand read等命令的源码位于与硬件无关的/common/cmd_nand.c里面呀???那么它们之间到底是怎么联系起来的呀???
其实就算没有看过源码也能猜到,这个关系应该如下:
其实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的底层到顶层的调用过程。