本文通过对三星smdkv210开发板的u-boot源码进行分析,了解u-boot烧写yaffs的方法,源码可以通过谷歌找到。
首先了解mtd的部分重要成员:
我的nand是2K,每512字节产生13字节的ecccode,所以ecc.steps = 2048 / 512= 4,
由chip->ecc.steps = mtd->writesize / chip->ecc.size;可知writesize表示页大小,ecc.size表示每次计算ecc的单元大小,这是由cpu决定的,比如s5pv210可以每读写512字节就生成ecc。erasesize肯定是块大小了。
先默认
mtd->writesize = 2048,ecc.size = 512。oobsize = 64。erasesize = 128*2048。
mtd_oob_ops用来指示烧写内容的相关参数,注意mtd_oob_ops本身并没有操作函数。
-
struct nand_write_options {
-
u_char *buffer; /* memory block containing image to write */
-
ulong length; /* number of bytes to write */
-
ulong offset; /* start address in NAND */
-
int quiet; /* don't display progress messages */
-
int autoplace; /* if true use auto oob layout */
-
int forcejffs2; /* force jffs2 oob layout */
-
int forceyaffs; /* force yaffs oob layout */
-
int noecc; /* write without ecc */是否进行ECC?
-
int writeoob; /* image contains oob data */指明所烧写的内容是否包含OOB
-
int pad; /* pad to page size */指明填充至页大小(不写OOB)
-
int blockalign; /* 1|2|4 set multiple of eraseblocks
-
* to align to */
-
int flag; //hxs add
-
};
-
普通分区烧写:
-
s = strchr(cmd, '.');
-
if (s != NULL &&
-
(!strcmp(s, ".jffs2") || !strcmp(s, ".e") || !strcmp(s, ".i"))) {
-
if (read) {
-
/* read */
-
nand_read_options_t opts;
-
memset(&opts, 0, sizeof(opts));
-
opts.buffer = (u_char*) addr;
-
opts.length = size;
-
opts.offset = off;
-
opts.quiet = quiet;
-
ret = nand_read_opts(nand, &opts);
-
} else {
-
/* write */
-
nand_write_options_t opts;
-
memset(&opts, 0, sizeof(opts));
-
opts.buffer = (u_char*) addr;
-
opts.length = size;
-
opts.offset = off;
-
/* opts.forcejffs2 = 1; */
-
opts.pad = 1;
-
opts.blockalign = 1;
-
opts.quiet = quiet;
-
opts.flag = flag; //HXS ADD
-
ret = nand_write_opts(nand, &opts);
-
}
-
-
tftp烧写yaffs分区:
-
-
#ifdef CFG_NAND_YAFFS_WRITE
-
} else if (!read && s != NULL && + (!strcmp(s, ".yaffs") || !strcmp(s, ".yaffs1"))) {
-
nand_write_options_t opts;
-
memset(&opts, 0, sizeof(opts));
-
opts.buffer = (u_char*) addr;
-
opts.length = size;
-
opts.offset = off;
-
opts.pad = 0;
-
opts.blockalign = 1;
-
opts.quiet = quiet;
-
opts.writeoob = 1;
-
opts.autoplace = 1;
-
-
opts.flag = flag; //HXS ADD
-
/* jsgood */
-
/* if (s[6] == '1')
-
opts.forceyaffs = 1; */
-
-
ret = nand_write_opts(nand, &opts);
-
#endif
-
-
sd卡烧写yaffs:
-
#if (defined(CONFIG_TQ210_YAFFS) && defined(CONFIG_OEM_SDREAD))
-
} else if (/*!s || */!strcmp(s, ".OEMyaffs")) {
-
unsigned int Dir = 0;
-
DirFunRet FileDirFileRet = FindDirFile(argv[4]);
-
//printf("size:0x%x\n",FileDirFileRet.FileSize);
-
if (FileDirFileRet.FileSize == 0)
-
{
-
return 1;
-
}
-
else
-
{
-
Dir = FileDirFileRet.StartCluster;
-
size = FileDirFileRet.FileSize;
-
//if((size & (nand->oobsize+nand->writesize -1)) != 0){
-
// size = (size/(nand->oobsize+nand->writesize) + 1) * (nand->oobsize+nand->writesize);
-
//}
-
// printf ("cmd_sizeof(nand_info_t)= 0x%x\n",sizeof(nand_info_t));
-
// printf ("sizof(loff_t)= 0x%x\n",sizeof(ulong));
-
-
// printf("size:0x%x,&size=0x%x,addr:0x%x,\n",size,&size,addr);
-
}
-
nand_write_options_t opts;
-
memset(&opts, 0, sizeof(opts));
-
opts.buffer = (u_char*) addr;
-
opts.length = size;
-
opts.offset = off;
-
opts.pad = 0;
-
opts.blockalign = 1;
-
opts.quiet = quiet;
-
opts.writeoob = 1;
-
opts.autoplace = 1;
-
-
/* jsgood */
-
/* if (s[6] == '1')
-
opts.forceyaffs = 1; */
-
-
ret = nand_OEM_write_opts(nand, &opts,Dir);
普通烧写时的nand_write_opts:
-
/**
-
* nand_write_opts: - write image to NAND flash with support for various options
-
*
-
* @param meminfo NAND device to erase
-
* @param opts write options (@see nand_write_options)
-
* @return 0 in case of success
-
*
-
* This code is ported from nandwrite.c from Linux mtd utils by
-
* Steven J. Hill and Thomas Gleixner.
-
*/
-
opts.pad = 1;
-
opts.blockalign = 1;
-
opts.quiet = quiet;
-
opts.flag = flag; //HXS ADD
-
int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts)
-
{
-
int imglen = 0;
-
int pagelen;
-
int baderaseblock;
-
int blockstart = -1;
-
loff_t offs;
-
int readlen;
-
int oobinfochanged = 0;
-
int percent_complete = -1;
-
-
/* org: struct nand_oobinfo old_oobinfo; */
-
struct nand_ecclayout old_oobinfo;
-
-
ulong mtdoffset = opts->offset;
-
ulong erasesize_blockalign;
-
u_char *buffer = opts->buffer;
-
size_t written;
-
int result;
-
-
/* jsgood */
-
struct mtd_oob_ops oob_ops;
-
-
/* set erasesize to specified number of blocks - to match
-
* jffs2 (virtual) block size */
-
if (opts->blockalign == 0) {
-
erasesize_blockalign = meminfo->erasesize;
-
} else {
-
erasesize_blockalign = meminfo->erasesize * opts->blockalign;
-
}
-
-
/* read the current oob info */
-
/* org: memcpy(&old_oobinfo, &meminfo->oobinfo, sizeof(old_oobinfo)); */
-
memcpy(&old_oobinfo, meminfo->ecclayout, sizeof(struct nand_ecclayout));
-
-
/* get image length */
-
imglen = opts->length;
-
pagelen = meminfo->writesize
-
+ ((opts->writeoob != 0) ? meminfo->oobsize : 0);
-
-
/* check, if file is pagealigned */
-
if ((!opts->pad) && ((imglen % pagelen) != 0)) {
-
printf("Input block length is not page aligned\n");
-
goto restoreoob;
-
}
-
-
/* check, if length fits into device */
-
if (((imglen / pagelen) * meminfo->writesize)
-
> (meminfo->size - opts->offset)) {
-
printf("Image %d bytes, NAND page %d bytes, "
-
"OOB area %u bytes, device size %u bytes\n",
-
imglen, pagelen, meminfo->writesize, meminfo->size);
-
printf("Input block does not fit into device\n");
-
goto restoreoob;
-
}
-
-
if (!opts->quiet)
-
printf("\n");
-
-
/* get data from input and write to the device */
-
while (imglen && (mtdoffset < meminfo->size)) {
-
-
WATCHDOG_RESET ();
-
-
/*
-
* new eraseblock, check for bad block(s). Stay in the
-
* loop to be sure if the offset changes because of
-
* a bad block, that the next block that will be
-
* written to is also checked. Thus avoiding errors if
-
* the block(s) after the skipped block(s) is also bad
-
* (number of blocks depending on the blockalign
-
*/
-
while (blockstart != (mtdoffset & (~erasesize_blockalign+1))) {
-
blockstart = mtdoffset & (~erasesize_blockalign+1);
-
offs = blockstart;
-
baderaseblock = 0;
-
-
/* check all the blocks in an erase block for
-
* bad blocks */
-
do {
-
int ret = meminfo->block_isbad(meminfo, offs);
-
-
if (ret < 0) {
-
printf("Bad block check failed\n");
-
goto restoreoob;
-
}
-
if (ret == 1) {
-
baderaseblock = 1;
-
if (!opts->quiet)
-
printf("\rBad block at 0x%lx "
-
"in erase block from "
-
"0x%x will be skipped\n",
-
(long) offs,
-
blockstart);
-
}
-
-
if (baderaseblock) {
-
mtdoffset = blockstart
-
+ erasesize_blockalign;
-
}
-
offs += erasesize_blockalign
-
/ opts->blockalign;
-
} while (offs < blockstart + erasesize_blockalign);
-
}
-
-
readlen = meminfo->writesize;
-
if (opts->pad && (imglen < readlen)) {
-
readlen = imglen;
-
memset(data_buf + readlen, 0xff,
-
meminfo->writesize - readlen);
-
}
-
-
/* read page data from input memory buffer */
-
memcpy(data_buf, buffer, readlen);
-
buffer += readlen;
-
-
/* This is yaffs2 writing if opts->writeoob == 1,
-
* and the other case is jffs2 writing in S3C NAND by jsgood.
-
*/
-
if (opts->writeoob) {
-
/* read OOB data from input memory block, exit
-
* on failure */
-
memcpy(oob_buf, buffer, meminfo->oobsize);
-
buffer += meminfo->oobsize;
-
-
/* write OOB data first, as ecc will be placed
-
* in there*/
-
/* org: result = meminfo->write_oob(meminfo,
-
mtdoffset,
-
meminfo->oobsize,
-
&written,
-
(unsigned char *)
-
&oob_buf); */
-
oob_ops.mode = MTD_OOB_AUTO;
-
oob_ops.len = meminfo->writesize;
-
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);
-
-
if (result != 0) {
-
printf("\nMTD writeoob failure: %d\n",
-
result);
-
goto restoreoob;
-
}
-
imglen -= meminfo->oobsize;
-
} else {
-
/* write out the page data */
-
result = meminfo->write(meminfo,
-
mtdoffset,
-
meminfo->writesize,
-
&written,
-
(unsigned char *) &data_buf);
-
-
if (result != 0) {
-
printf("writing NAND page at offset 0x%lx failed\n",
-
mtdoffset);
-
goto restoreoob;
-
}
-
}
-
-
imglen -= readlen;
-
-
if (!opts->quiet) {
-
unsigned long long n = (unsigned long long)
-
(opts->length-imglen) * 100;
-
int percent;
-
-
do_div(n, opts->length);
-
percent = (int)n;
-
-
/* output progress message only at whole percent
-
* steps to reduce the number of messages printed
-
* on (slow) serial consoles
-
*/
-
if (percent != percent_complete) {
-
printf("\rWriting data at 0x%lx "
-
"-- %3d%% complete.",
-
mtdoffset, percent);
-
percent_complete = percent;
-
}
-
}
-
-
mtdoffset += meminfo->writesize;
-
}
-
-
if (!opts->quiet)
-
printf("\n");
-
-
restoreoob:
-
if (oobinfochanged) {
-
/* org: memcpy(&meminfo->oobinfo, &old_oobinfo,
-
sizeof(meminfo->oobinfo)); */
-
memcpy(meminfo->ecclayout, &old_oobinfo,
-
sizeof(struct nand_ecclayout));
-
}
-
-
if (imglen > 0) {
-
printf("Data did not fit into device, due to bad blocks\n");
-
return -1;
-
}
-
-
/* return happy */
-
return 0;
-
}
可见最终调用了
meminfo->write来烧写数据:
/* write out the page data */
result = meminfo->write(meminfo,
mtdoffset,
meminfo->writesize,
&written,
(unsigned char *) &data_buf);
上一篇中我们已经分析过
meminfo->write的调用过程,即mtd->write > nand_write > nand_do_write_ops > chip->write_page > s3c_nand_write_page_8bit(mtd, chip, buf);
而不管是tftp还是从sd卡烧写yaffs镜像时,都会标记opts.writeoob = 1; 因此会调用meminfo->write_oob(meminfo, mtdoffset, &oob_ops);
meminfo->write_oob就是nand_write_oob:
-
/**
-
* nand_write - [MTD Interface] NAND write with ECC
-
* @mtd: MTD device structure
-
* @to: offset to write to
-
* @len: number of bytes to write
-
* @retlen: pointer to variable to store the number of written bytes
-
* @buf: the data to write
-
*
-
* NAND write with ECC
-
*/
-
static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
-
size_t *retlen, const uint8_t *buf)
-
{
-
struct nand_chip *chip = mtd->priv;
-
int ret;
-
-
/* Do not allow reads past end of device */
-
if ((to + len) > mtd->size)
-
return -EINVAL;
-
if (!len)
-
return 0;
-
-
nand_get_device(chip, mtd, FL_WRITING);
-
-
chip->ops.len = len;
-
chip->ops.datbuf = (uint8_t *)buf;
-
chip->ops.oobbuf = NULL;
-
-
ret = nand_do_write_ops(mtd, to, &chip->ops);
-
-
*retlen = chip->ops.retlen;
-
-
nand_release_device(mtd);
-
-
return ret;
-
}
-
-
static int nand_write_oob(struct mtd_info *mtd, loff_t to,
-
struct mtd_oob_ops *ops)
-
{
-
struct nand_chip *chip = mtd->priv;
-
-
/* org: int ret = -ENOTSUPP */
-
int ret = -524;
-
-
ops->retlen = 0;
-
-
/* Do not allow writes past end of device */
-
if (ops->datbuf && (to + ops->len) > mtd->size) {
-
DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
-
"Attempt read beyond end of device\n");
-
return -EINVAL;
-
}
-
-
nand_get_device(chip, mtd, FL_WRITING);
-
-
switch(ops->mode) {
-
case MTD_OOB_PLACE:
-
case MTD_OOB_AUTO:
-
case MTD_OOB_RAW:
-
break;
-
-
default:
-
goto out;
-
}
-
-
if (!ops->datbuf)
-
ret = nand_do_write_oob(mtd, to, ops);
-
else
-
ret = nand_do_write_ops(mtd, to, ops);
-
-
out:
-
nand_release_device(mtd);
-
return ret;
-
}
注意opts->writeoob == 1时,会构建oob_ops结构体,接着从内存的源数据中分离出一页数据中的data和oob,赋给
oob_ops,这个样
oob_ops就包含了data和oob数据:
oob_ops.oobbuf = (unsigned char *)&oob_buf;
oob_ops.datbuf = (unsigned char *)&data_buf;
然后使用 meminfo->write_oob(meminfo, mtdoffset, &oob_ops); 烧写data和oob。
而
opts->writeoob == 0时,不会构建
oob_ops结构体,直接使用
result = meminfo->write(meminfo,mtdoffset,meminfo->writesize,&written,(unsigned char *) &data_buf);
来烧写数据。
nand_write和nand_
write_oob 最后都会执行nand_do_write_ops函数,
如果
nand_write_oob,会执行
if (unlikely(oob))
oob = nand_fill_oob(chip, oob, ops);
将源数据中的oob数据填入nand中的oobfree区域,然后执行
write_page写入数据和ecc。
如果
nand_write,不会填充OOB,而是直接执行chip->write_page(mtd, chip, wbuf, page, cached, (ops->mode == MTD_OOB_RAW)); 烧写数据和ecc。
最后最重要的函数:
-
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);
-
-
/* jsgood: org: #else block only */
-
if ((mtd->writesize == 512 && page < 512) || (mtd->writesize == 2048 && page < 256) ||
-
(mtd->writesize == 4096 && page < 128)) {
-
-
memset(chip->oob_poi, 0xff, mtd->oobsize);
-
s3c_nand_write_page_8bit(mtd, chip, buf);
-
} else
-
{
-
if (unlikely(raw))
-
chip->ecc.write_page_raw(mtd, chip, buf);
-
else
-
chip->ecc.write_page(mtd, chip, buf);
-
}
-
-
/*
-
* Cached progamming disabled for now, Not sure if its worth the
-
* trouble. The speed gain is not very impressive. (2.3->2.6Mib/s)
-
*/
-
cached = 0;
-
-
if (!cached || !(chip->options & NAND_CACHEPRG)) {
-
-
chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
-
status = chip->waitfunc(mtd, chip);
-
/*
-
* See if operation failed and additional status checks are
-
* available
-
*/
-
if ((status & NAND_STATUS_FAIL) && (chip->errstat))
-
status = chip->errstat(mtd, chip, FL_WRITING, status,
-
page);
-
-
if (status & NAND_STATUS_FAIL)
-
return -EIO;
-
} else {
-
chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1);
-
status = chip->waitfunc(mtd, chip);
-
}
-
-
#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
-
/* Send command to read back the data */
-
chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
-
-
if (chip->verify_buf(mtd, buf, mtd->writesize))
-
return -EIO;
-
#endif
-
return 0;
-
}
最关键的就是if ((mtd->writesize == 512 && page < 512) || (mtd->writesize == 2048 && page < 256) ||
(mtd->writesize == 4096 && page < 128)) {
这个判断条件,当写入的页地址未超过uboot分区时,采用8bitecc,若超过则采用1bitecc。
阅读(1438) | 评论(0) | 转发(0) |