一、介绍
Nand flash K9GAG08U0D (2G Byte)
在u-boot的shell里面执行如下命令: 把 rootfs.yaffs从SD卡的第一个分区读取出来,并写到nand flash中去.
SMDK6401> fatload mmc 0:1 50008000 rootfs.yaffs
SMDK6401> nand erase 600000 $(filesize)
SMDK6401> nand write.yaffs2 50008000 600000 $(filesize)
这儿分析一下最后一条命令:将数据写入到yaffs2分区的过程
二、 过程分析
1.1 u-boot/common/cmd_nand.c中
int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
{
if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) {
addr = (ulong)simple_strtoul(argv[2], NULL, 16);
read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */
if (arg_off_size(argc - 3, argv + 3, nand, &off, &size) != 0)
return 1;
s = strchr(cmd, '.');
if (!read && s != NULL && + (!strcmp(s, ".yaffs2") || !strcmp(s, ".yaffs1")))
{
nand_write_options_t opts;
memset(&opts, 0, sizeof(opts));
opts.buffer = (u_char*) addr; // addr=0x50008000内存
opts.length = size; // length是文件长度
opts.offset = off; // offset 是要写到nand flash的地址0x600000
opts.pad = 0;
opts.blockalign = 1;
opts.quiet = quiet;
opts.writeoob = 1;
opts.autoplace = 1;
ret = nand_write_opts(nand, &opts);
}
}
argv[0] argv[1] argv[2] argv[3] argv[4]
nand write.yaffs2 50008000 600000 $(filesize)
addr off size=0x420000
1.2 在文件driver/nand/nand_utils.c中
int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts)
{
ulong mtdoffset = opts->offset; // mtdoffset=nand_flash中的偏移0x600000
ulong erasesize_blockalign;
u_char *buffer = opts->buffer; // buffer=(u_char*)0x500080
imglen = opts->length; // imglen是rootfs.yaffs2这个文件的长度
while (imglen && (mtdoffset size)) {
//下面这个 while判断要写入的块是不是坏块,如果是坏块继续查找直到找到一个好块
while (blockstart != (mtdoffset & (~erasesize_blockalign+1))) {
blockstart = mtdoffset & (~erasesize_blockalign+1);
offs = blockstart;
baderaseblock = 0;
do {
int ret = meminfo->block_isbad(meminfo, offs); //判断是不是块坏
if (ret
printf("Bad block check failedn");
goto restoreoob;
}
if (ret == 1) { //ret=1是坏块
baderaseblock = 1; //这个地方还要设个标志,直接do_something不就得了?
if (!opts->quiet)
printf("rBad block at 0x%lx "
"in erase block from "
"0x%x will be skippedn",
(long) offs,
blockstart);
}
if (baderaseblock) {
mtdoffset = blockstart + erasesize_blockalign; //如果ret=1是坏块,要写入的起始位置指向下一个块
}
offs += erasesize_blockalign
/ opts->blockalign;
} while (offs
}
readlen = meminfo->writesize;
memcpy(data_buf, buffer, readlen); //初始时:buffer=(u_char*)0x50008000
buffer += readlen; //meminfo->writesize= 4096
if (opts->writeoob) {
memcpy(oob_buf, buffer, meminfo->oobsize); //buffer=(data+oob)*n, oob紧跟data
buffer += meminfo->oobsize; //meminfo->oobsize = 128
oob_ops.mode = MTD_OOB_AUTO;
oob_ops.len = meminfo->writesize; // 每一次写的大小为writesize=4k
oob_ops.ooboffs = 0;
oob_ops.ooblen = meminfo->oobsize;
oob_ops.oobbuf = (unsigned char *)&oob_buf;
oob_ops.datbuf = (unsigned char *)&data_buf;
result = meminfo->write_oob(meminfo, mtdoffset, &oob_ops); //如果没有坏块的话: mtdoffset=nand_flash中的偏移0x600000
imglen -= meminfo->oobsize;
}
imglen -= readlen; // mtd->writesize=4096
mtdoffset += meminfo->writesize; // mtdoffset指向下一个page,是page_align
}
}
1.3 在driver/nand/nand_base.c中
int nand_scan_tail(struct mtd_info *mtd)
{
mtd->write_oob = nand_write_oob; //初始化
}
//初始化时,所以 meminfo->write_oob(meminfo, mtdoffset, &oob_ops),就是调用nand_write_oob
static int nand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
{
struct nand_chip *chip = mtd->priv;
nand_get_device(chip, mtd, FL_WRITING);
if (!ops->datbuf) //ops->databuf不为空,要调用下面那个
ret = nand_do_write_oob(mtd, to, ops);
else
ret = nand_do_write_ops(mtd, to, ops); //调用这个,这就是nand flash写的过程,可参考下面的图
}
1.4 在driver/nand/nand_base.c中
/**
* nand_do_write_ops - [Internal] NAND write with ECC
* @mtd: MTD device structure
* @to: offset to write to
* @ops: oob operations description structure
*
* NAND write with ECC
*/
static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops)
{
int chipnr, realpage, page, blockmask, column;
struct nand_chip *chip = mtd->priv;
uint32_t writelen = ops->len;
uint8_t *oob = ops->oobbuf;
uint8_t *buf = ops->datbuf;
int ret, subpage;
ops->retlen = 0;
column = to & (mtd->writesize - 1);
subpage = column || (writelen & (mtd->writesize - 1));
if (subpage && oob)
return -EINVAL;
chipnr = (int)(to >> chip->chip_shift);
chip->select_chip(mtd, chipnr); //写过程第1步,选中芯片
realpage = (int)(to >> chip->page_shift);
page = realpage & chip->pagemask;
blockmask = (1
/* Invalidate the page cache, when we write to the cached page */
if (to <= (chip->pagebuf
(chip->pagebuf
chip->pagebuf = -1;
while(1) {
int bytes = mtd->writesize;
int cached = writelen > bytes && page != blockmask;
uint8_t *wbuf = buf;
if (unlikely(column || writelen writesize - 1))) {
cached = 0;
bytes = min_t(int, bytes - column, (int) writelen);
chip->pagebuf = -1;
memset(chip->buffers->databuf, 0xff, mtd->writesize);
memcpy(&chip->buffers->databuf[column], buf, bytes);
wbuf = chip->buffers->databuf;
}
if (unlikely(oob))
oob = nand_fill_oob(chip, oob, ops);
ret = chip->write_page(mtd, chip, wbuf, page, cached, (ops->mode == MTD_OOB_RAW));
if (ret)
break;
writelen -= bytes;
if (!writelen)
break;
column = 0;
buf += bytes;
realpage++;
page = realpage & chip->pagemask;
/* Check, if we cross a chip boundary */
if (!page) {
chipnr++;
chip->select_chip(mtd, -1);
chip->select_chip(mtd, chipnr);
}
}
ops->retlen = ops->len - writelen;
if (unlikely(oob))
ops->oobretlen = ops->ooblen;
return ret;
}
1.5 在driver/nand/nand_base.c中
static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, struct mtd_oob_ops *ops)
{
size_t len = ops->ooblen;
int i=0;
switch(ops->mode) {
case MTD_OOB_AUTO:
{
struct nand_oobfree *free = chip->ecc.layout->oobfree;
uint32_t boffs = 0, woffs = ops->ooboffs;
size_t bytes = 0;
// free->length=22, len=128, woffs=0
for(; free->length && len; free++, len -= bytes)
{ // bytes=22, boffs=2
bytes = min_t(size_t, len, free->length);
boffs = free->offset;
memcpy(chip->oob_poi + boffs, oob, bytes); //将rootfs.yaffs2中紧跟main区的22个字节copy到oob_poi[2-24]处
oob += bytes; //这块数据是yaffs2文件系统的信息
}
return oob;
}
}
return NULL;
}
nand_scan_tail
{
if (!chip->write_page)
chip->write_page = nand_write_page;
}
static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf, int page, int cached, int raw)
{
int status;
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page); //写过程第2-3步,发命令0x80,发地址
if (unlikely(raw))
chip->ecc.write_page_raw(mtd, chip, buf);
else //exec
chip->ecc.write_page(mtd, chip, buf);
cached = 0;
if (!cached || !(chip->options & NAND_CACHEPRG)) {
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1,-1); //写过程第5步,写命令0x10
status = chip->waitfunc(mtd, chip); //写过程第6步,等侍结束
if ((status & NAND_STATUS_FAIL) && (chip->errstat))
status = chip->errstat(mtd, chip, FL_WRITING, status,page);
if (status & NAND_STATUS_FAIL)
return -EIO;
}
return 0;
}
1.7 在cpu/s3c64xx/nand.c中
board_nand_init()
{
#if defined(CONFIG_NAND_BL1_8BIT_ECC) && (defined(CONFIG_S3C6410) || defined(CONFIG_S3C6430))
nand->ecc.write_page = s3c_nand_write_page_8bit;
nand->ecc.size = 512;
nand->ecc.bytes = 13;
nand->ecc.layout = &s3c_nand_oob_mlc_128_8bit;
#endif
}
void s3c_nand_write_page_8bit(struct mtd_info *mtd, struct nand_chip *chip, const uint8_t *buf)
{
int i, eccsize = 512;
int eccbytes = 13;
int eccsteps = mtd->writesize / eccsize;
uint8_t *ecc_calc = chip->buffers->ecccalc;
uint8_t *p = buf;
//下面这个for代码,写的不明了,
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { //意思是: 每512字节生成13字节的ECC,4K生成13*(4K/512)=104字节的ECC
s3c_nand_enable_hwecc_8bit(mtd, NAND_ECC_WRITE); //使能硬件ECC
chip->write_buf(mtd, p, eccsize); //写过程第4步,写数据
s3c_nand_calculate_ecc_8bit(mtd, p, &ecc_calc[i]); //每写512字节到main区,就生成13字节的ECC,依次填充到ecc_calc[0-13*8=104]处
}
for (i = 0; i writesize / eccsize); i++)
chip->oob_poi[i+24] = ecc_calc[i]; //将硬件生成的ECC值填入oob的[24-128]处, ECC_size=8*13=104, (24+104=128)
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize); //写过程第4步,写OOB(OOB数据紧随main区数据写入的,单独写OOB是写不了的 )
}
1.8 在driver/nand/nand_base.c中
static void nand_set_defaults(struct nand_chip *chip, int busw)
{
if (!chip->write_buf)
chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;
}
static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
{
int i;
struct nand_chip *chip = mtd->priv;
for (i = 0; i
writeb(buf[i], chip->IO_ADDR_W);
}
1.9 上述写过程如下图所示:
1.10 OOB数据如下图所示
『本文转载自网络,版权归原作者所有,如有侵权请联系删除』