然后我们分析一下nand flash的读写等函数。
既然是命令那自然要看到U_BOOT_CMD宏,这个宏分析的很多就不分析了。在cmd_nand.c文件中。nand的命令执行函数是do_nand。当然我们没有定义CFG_NAND_LEGACY,要看这个分支。do_nand函数也没有什么好分析的,摘取几个命令的处理分析下。
1。nand bad命令
列出函数调用次序先,
do_nand
nand_block_isbad//include/nand.h
nand_block_isbad//在Nand_base.c中, info->block_isbad函数指针指向
nand_block_checkbad//在Nand_base.c
nand_block_bad() //在Nand_base.c中,nand_chip,this->block_bad函数指针指向,
nand_isbad_bbt
//在Nand_bbt.c中
如下。
点击(此处)折叠或打开
- nand = &nand_info[nand_curr_device];
- if (strcmp(cmd,
"bad")
== 0)
{
- printf("\nDevice %d bad blocks:\n", nand_curr_device);
- for (off
= 0; off
< nand->size; off
+= nand->erasesize)
//按块循环
- if (nand_block_isbad(nand, off))
//(1)
- printf(" %08x\n", off);
- return 0;
- }
这个函数的定义在include/nand.h中,它调用nand_info[]变量中的block_isbad函数指针指向的函数;这个指针在初始化时已经被分配,这里是Nand_base.c文件中的nand_block_isbad函数。这里有个小问题,那有两个都被编译的nand_block_isbad函数的定义,那到底调用的是哪个呢。答案是nand.h中的,因为Nand_base.c中的是被定义成static的函数,只能在本文件中使用。
点击(此处)折叠或打开
- static int nand_block_isbad
(struct mtd_info *mtd, loff_t ofs)
- {
- /* Check
for invalid offset
*/
- if (ofs
> mtd->size)
- return -EINVAL;
- return nand_block_checkbad (mtd, ofs, 1, 0);
- }
这个函数又会调用nand_block_checkbad 函数
点击(此处)折叠或打开
- static int nand_block_checkbad
(struct mtd_info *mtd, loff_t ofs,
int getchip,
int allowbbt)
- {
- struct nand_chip *this
= mtd->priv;
- if (!this->bbt)
//如果nand_chip结构体变量中的bbt(坏块标记表)表指针是空的
- return this->block_bad(mtd, ofs, getchip);
- /* Return info from the table
*/
- return nand_isbad_bbt (mtd, ofs, allowbbt);
- }
block_bad函数指针被指向nand_block_bad,分析它,
此函数将从芯片读取坏块标记
点击(此处)折叠或打开
- static int nand_block_bad(struct mtd_info
*mtd, loff_t ofs,
int getchip)
- {
- int page, chipnr, res
= 0;
- struct nand_chip *this
= mtd->priv;
- u16 bad;
- page = (int)(ofs
>> this->page_shift)
& this->pagemask;
//(1)
- if (getchip)
{ //选中芯片
- chipnr = (int)(ofs
>> this->chip_shift);
- /* Grab the lock
and see if the device
is available */
- nand_get_device (this, mtd, FL_READING);
- /*
Select the NAND device */
- this->select_chip(mtd, chipnr);
- }
- if (this->options
& NAND_BUSWIDTH_16)
{
- this->cmdfunc
(mtd, NAND_CMD_READOOB, this->badblockpos
& 0xFE, page);
- bad = cpu_to_le16(this->read_word(mtd));
- if (this->badblockpos
& 0x1)
- bad >>= 1;
- if ((bad
& 0xFF)
!= 0xff)
- res = 1;
- } else
{
- this->cmdfunc
(mtd, NAND_CMD_READOOB, this->badblockpos, page);
//(2)
- if (this->read_byte(mtd)
!= 0xff)
//(3)
- res = 1;
- }
- if (getchip)
{
- /* Deselect
and wake up anyone waiting
on the device */
- nand_release_device(mtd);
- }
- return res;
- }
(1)从偏移地址获取页号。page_shift是page页位数(就是一页的大小的数值用二进制表示最高位的序号)。将偏移地址右移页位数,则低位就是页的号码,有相当于除页大小。然后在与上pagemask,就是页大小(主要是将高位置0,其实这里与不与感觉都无所谓,高位本来就是0)
(2)主要就是这一句,cmdfunc()函数,发送读取oob区命令。this->badblockpos在nand_scan函数中设置了大页0,小页5。
(3)读出的位是否是0xff,如果不是就是坏块。
...................................
再看下如果有bbt表,nand_block_checkbad函数将调用nand_isbad_bbt。bbt表在初始化时scan_bbt函数已经建立。所以nand bad命令在这个uboot中都是通过查bbt表完成的。
点击(此处)折叠或打开
- int nand_isbad_bbt
(struct mtd_info *mtd, loff_t offs,
int allowbbt)
- {
- struct nand_chip *this
= mtd->priv;
- int block;
- uint8_t res;
- /*
Get block number * 2
*/
- block = (int)
(offs >>
(this->bbt_erase_shift
- 1));
//(1)
- res = (this->bbt[block
>> 3]
>>
(block & 0x06))
& 0x03;
//(2)
- DEBUG (MTD_DEBUG_LEVEL2,
"nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n",
- (unsigned int)offs, res, block
>> 1);
- switch ((int)res)
{
- case 0x00: return 0;
- case 0x01: return 1;
- case 0x02: return allowbbt
? 0 : 1;
- }
- return 1;
- }
nand bad命令处理暂时分析到这里
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
分析nand read 命令:
nand read命令的调用顺序为:
do_nand //cmd_nand.c
nand_read_opts
///driver/mtd/nand/nand_util.c
nand_read //nand_base.c ,meminfo->read指针指向
nand_read_ecc //nand_base.c
sep4020_nand_read_buf//cpu/sep4020/nand_flash.c
这里的代码大多照搬了内核的mtd层代码,而仅仅对于uboot不需要这么复杂,一些操作觉得不合理,有很多无用又费周折的操作。
do_nand函数中read相关部分:
点击(此处)折叠或打开
- if (strncmp(cmd,
"read", 4)
== 0
|| strncmp(cmd,
"write", 5)
== 0)
{
- int read;
- if (argc
< 4)
- goto usage;
- addr = (ulong)simple_strtoul(argv[2],
NULL, 16);
- read = strncmp(cmd,
"read", 4)
== 0;
/* 1
= read, 0 = write
*/
- printf("\nNAND %s: ", read
? "read"
: "write");
- if (arg_off_size(argc
- 3, argv
+ 3, nand,
&off,
&size) != 0)
- return 1;
- 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;
//addr是内存地址,nand读出来的数据最终将存入这里
- opts.length = size;
//读取的大小
- opts.offset = off;
//flash地址
- opts.quiet = quiet;
- ret = nand_read_opts(nand,
&opts);
//读数据操作,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;
- ret = nand_write_opts(nand,
&opts);
- }
- } else
if (s !=
NULL &&
!strcmp(s,
".yaffs"))
{
- 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.forceyaffs
= 1;
*/
- opts.noecc = 1;
- opts.writeoob
= 1;
- opts.blockalign
= 1;
- opts.quiet = quiet;
- opts.skipfirstblk
= 1;
- ret = nand_write_opts(nand,
&opts);
- }
- } else
if (s !=
NULL &&
!strcmp(s,
".oob"))
{
- /* read out-of-band data
*/
- if (read)
- ret = nand->read_oob(nand, off, size,
&size,
- (u_char *) addr);
- else
- ret = nand->write_oob(nand, off, size,
&size,
- (u_char *) addr);
- } else
{
- if (read)
- ret = nand_read(nand, off,
&size,
(u_char *)addr);
- else
- ret = nand_write(nand, off,
&size,
(u_char *)addr);
- }
- printf(" %d bytes %s: %s\n", size,
- read ? "read"
: "written", ret
? "ERROR"
: "OK");
- return ret == 0
? 0 : 1;
- }
nand_read_opts在/driver/mtd/nand/nand_util.c,参照代码中原本的英文注释,代码量大也就不做详细分析了
点击(此处)折叠或打开
- /**
- * nand_read_opts:
- read image from NAND flash with support
for various options
- *
- * @param meminfo NAND device
to erase
- * @param opts read options
(@see struct nand_read_options)
- * @return 0 in
case of success
- *
- */
- int nand_read_opts(nand_info_t
*meminfo,
const nand_read_options_t *opts)
- {
- int imglen = opts->length;
- int pagelen;
- int baderaseblock;
- int blockstart
= -1;
- int percent_complete
= -1;
- loff_t offs;
- size_t readlen;
- ulong mtdoffset = opts->offset;
- u_char *buffer
= opts->buffer;
- int result;
- /* make sure device page sizes are valid
*/
- if (!(meminfo->oobsize
== 16
&& meminfo->oobblock
== 512)
- &&
!(meminfo->oobsize
== 8
&& meminfo->oobblock
== 256)
- &&
!(meminfo->oobsize
== 64
&& meminfo->oobblock
== 2048))
{
- printf("Unknown flash (not normal NAND)\n");
- return -1;
- }
- pagelen = meminfo->oobblock
- + ((opts->readoob
!= 0)
? meminfo->oobsize
: 0);
- /* check,
if length is
not larger than device
*/
- if (((imglen
/ pagelen)
* meminfo->oobblock)
- > (meminfo->size
- opts->offset))
{
- printf("Image %d bytes, NAND page %d bytes, "
- "OOB area %u bytes, device size %u bytes\n",
- imglen, pagelen, meminfo->oobblock, meminfo->size);
- printf("Input block is larger than device\n");
- return -1;
- }
- 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 &
(~meminfo->erasesize+1)))
{
- blockstart = mtdoffset
& (~meminfo->erasesize+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");
- return -1;
- }
- 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
- + meminfo->erasesize;
- }
- offs += meminfo->erasesize;
- } while
(offs < blockstart
+ meminfo->erasesize);
- }
- /* read page data
to memory buffer */
- result = meminfo->read(meminfo,
//读2048字节(不包含oob的一页),
- mtdoffset, //nand flash地址
- meminfo->oobblock,
//页大小(2048),即需要读取的字节数
- &readlen,
- (unsigned char
*) &data_buf);
- if (result
!= 0)
{
- printf("reading NAND page at offset 0x%lx failed\n",
- mtdoffset);
- return -1;
- }
- if (imglen
< readlen)
{
- readlen = imglen;
- }
- memcpy(buffer, data_buf, readlen);
- buffer += readlen;
- imglen -= readlen;
- //上面是读页有效数据(2048),这里读oob数据。
- if (opts->readoob)
{
- result = meminfo->read_oob(meminfo,
- mtdoffset,
- meminfo->oobsize,
- &readlen,
- (unsigned char
*)
- &oob_buf);
- if (result
!= 0)
{
- printf("\nMTD readoob failure: %d\n",
- result);
- return -1;
- }
- if (imglen
< readlen)
{
- readlen = imglen;
- }
- memcpy(buffer, oob_buf, readlen);
- buffer += readlen;
- 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)
{
- if (!opts->quiet)
- printf("\rReading data from 0x%x "
- "-- %3d%% complete.",
- mtdoffset, percent);
- percent_complete = percent;
- }
- }
- mtdoffset += meminfo->oobblock;
- }
- if (!opts->quiet)
- printf("\n");
- if (imglen
> 0)
{
- printf("Could not read entire image due to bad blocks\n");
- return -1;
- }
- /* return happy
*/
- return 0;
- }
上面meminfo->read指向的函数是,nand_read在nand_base.c文件中。
点击(此处)折叠或打开
- static int nand_read
(struct mtd_info *mtd, loff_t from, size_t
len, size_t
* retlen, u_char
* buf)
- {
- return nand_read_ecc (mtd, from,
len, retlen, buf,
NULL,
NULL);
- }
nand_read_ecc函数在nand_base.c中,函数如下
点击(此处)折叠或打开
- /**
- * nand_read_ecc
- [MTD Interface] Read data with ECC
- * @mtd: MTD device structure
- * @from: offset
to read from
- * @len: number of bytes
to read
- * @retlen: pointer
to variable to store the number of read bytes
- * @buf: the databuffer
to put data
- * @oob_buf: filesystem supplied oob data buffer
- * @oobsel: oob selection structure
- *
- * NAND read with 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;
- u_char *data_poi,
*oob_data = oob_buf;
- u_char ecc_calc[32];
- u_char ecc_code[32];
- int eccmode, eccsteps;
- unsigned *oob_config;
- int datidx;
- int blockcheck
= (1 <<
(this->phys_erase_shift
- this->page_shift))
- 1;
- int eccbytes;
- int compareecc
= 1;
- int oobreadlen;
- DEBUG (MTD_DEBUG_LEVEL3,
"nand_read_ecc: from = 0x%08x, len = %i\n",
(unsigned int) from,
(int)
len);
- /*
Do not allow reads past
end of device */
- if ((from
+ len)
> mtd->size)
{
- DEBUG (MTD_DEBUG_LEVEL0,
"nand_read_ecc: Attempt read beyond end of device\n");
- *retlen = 0;
- return -EINVAL;
- }
- /* Grab the lock
and see if the device
is available */
- nand_get_device (this, mtd
,FL_READING);
- /* use userspace supplied oobinfo,
if zero */
- if (oobsel
==
NULL)
- oobsel = &mtd->oobinfo;
- /* Autoplace of oob data
? Use the default placement scheme
*/
- if (oobsel->useecc
== MTD_NANDECC_AUTOPLACE)
- oobsel = this->autooob;
- eccmode = oobsel->useecc
? this->eccmode
: NAND_ECC_NONE;
- oob_config = oobsel->eccpos;
- /*
Select the NAND device */
- chipnr = (int)(from
>> this->chip_shift);
- this->select_chip(mtd, chipnr);
- /* First we calculate the starting page
*/
- realpage = (int)
(from >> this->page_shift);
- page = realpage
& this->pagemask;
- /*
Get raw starting column */
- col = from &
(mtd->oobblock
- 1);
- end = mtd->oobblock;
- ecc = this->eccsize;
- eccbytes = this->eccbytes;
- if ((eccmode
== NAND_ECC_NONE)
||
(this->options
& NAND_HWECC_SYNDROME))
- compareecc = 0;
- oobreadlen = mtd->oobsize;
- if (this->options
& NAND_HWECC_SYNDROME)
- oobreadlen -= oobsel->eccbytes;
- /*
Loop until all data read
*/
- while (read
< len)
{
- int aligned =
(!col
&& (len
- read)
>= end);
- /*
- * If the read
is not page aligned, we have
to read into data buffer
- * due to ecc,
else we read into return buffer direct
- */
- if (aligned)
- data_poi = &buf[read];
- else
- data_poi = this->data_buf;
- /* Check,
if we have this page
in the buffer
- *
- * FIXME: Make it work when we must provide oob data too,
- * check the usage of data_buf oob field
- */
- if (realpage
== this->pagebuf
&&
!oob_buf) {
- /* aligned read
? */
- if (aligned)
- memcpy (data_poi, this->data_buf,
end);
- goto readdata;
- }
- /* Check,
if we must send the read command
*/
- if (sndcmd)
{ //板级读命令发送,其实这里主要设置了nandflash的地址。
- this->cmdfunc
(mtd, NAND_CMD_READ0, 0x00, page);
- sndcmd = 0;
- }
- /*
get oob area,
if we have no oob buffer from fs-driver
*/
- if (!oob_buf
|| oobsel->useecc
== MTD_NANDECC_AUTOPLACE
||
- oobsel->useecc
== MTD_NANDECC_AUTOPL_USR)
- oob_data = &this->data_buf[end];
- eccsteps = this->eccsteps;
- switch (eccmode)
{
- case NAND_ECC_NONE:
{ /* No ECC, Read
in a page */
- /* XXX U-BOOT XXX
*/
- #if 0
- static unsigned long lastwhinge = 0;
- if ((lastwhinge
/ HZ)
!= (jiffies
/ HZ))
{
- printk (KERN_WARNING
"Reading data from NAND FLASH without ECC is not recommended\n");
- lastwhinge = jiffies;
- }
- #else
- puts("Reading data from NAND FLASH without ECC is not recommended\n");
- #endif
- this->read_buf(mtd, data_poi,
end);
- break;
- }
- case NAND_ECC_SOFT:
/* Software ECC 3/256: Read
in a page + oob data
*/
- this->read_buf(mtd, data_poi,
end);
//读取数据
- for (i
= 0, datidx
= 0; eccsteps; eccsteps--, i+=3,
datidx += ecc)
- this->calculate_ecc(mtd,
&data_poi[datidx],
&ecc_calc[i]);
- break;
- default:
- for (i
= 0, datidx
= 0; eccsteps; eccsteps--, i+=eccbytes,
datidx += ecc)
{
- this->enable_hwecc(mtd, NAND_ECC_READ);
- this->read_buf(mtd,
&data_poi[datidx], ecc);
- /* HW ecc with syndrome calculation must read the
- * syndrome from flash immidiately after the data
*/
- if (!compareecc)
{
- /* Some hw ecc generators need
to know when the
- * syndrome is read from flash
*/
- this->enable_hwecc(mtd, NAND_ECC_READSYN);
- this->read_buf(mtd,
&oob_data[i], eccbytes);
- /* We calc
error correction directly, it checks the hw
- * generator for an
error, reads back the syndrome
and
- * does the error correction
on the fly */
- if (this->correct_data(mtd,
&data_poi[datidx],
&oob_data[i],
&ecc_code[i])
==
-1) {
- DEBUG (MTD_DEBUG_LEVEL0,
"nand_read_ecc: "
- "Failed ECC read, page 0x%08x on chip %d\n", page, chipnr);
- ecc_failed++;
- }
- } else
{
- this->calculate_ecc(mtd,
&data_poi[datidx],
&ecc_calc[i]);
- }
- }
- break;
- }
- /* read oobdata
*/
- this->read_buf(mtd,
&oob_data[mtd->oobsize
- oobreadlen], oobreadlen);
- /* Skip ECC check,
if not requested
(ECC_NONE or HW_ECC with syndromes)
*/
- if (!compareecc)
- goto readoob;
- /* Pick the ECC bytes out of the oob data
*/
- for (j
= 0; j < oobsel->eccbytes; j++)
- ecc_code[j]
= oob_data[oob_config[j]];
- /* correct data,
if neccecary */
- for (i
= 0, j = 0, datidx
= 0; i
< this->eccsteps; i++, datidx
+= ecc)
{
- ecc_status = this->correct_data(mtd,
&data_poi[datidx],
&ecc_code[j],
&ecc_calc[j]);
- /*
Get next chunk of ecc bytes
*/
- j += eccbytes;
- /* Check,
if we have a fs supplied oob-buffer,
- * This is the legacy mode. Used by YAFFS1
- * Should go away some
day
- */
- if (oob_buf
&& oobsel->useecc
== MTD_NANDECC_PLACE)
{
- int *p
= (int
*)(&oob_data[mtd->oobsize]);
- p[i]
= ecc_status;
- }
- if (ecc_status
==
-1) {
- DEBUG (MTD_DEBUG_LEVEL0,
"nand_read_ecc: "
"Failed ECC read, page 0x%08x\n", page);
- ecc_failed++;
- }
- }
- readoob:
- /* check,
if we have a fs supplied oob-buffer
*/
- if (oob_buf)
{
- /* without autoplace. Legacy mode used by YAFFS1
*/
- switch(oobsel->useecc)
{
- case MTD_NANDECC_AUTOPLACE:
- case MTD_NANDECC_AUTOPL_USR:
- /* Walk through the autoplace chunks
*/
- for (i
= 0, j = 0; j
< mtd->oobavail; i++)
{
- int from = oobsel->oobfree[i][0];
- int num = oobsel->oobfree[i][1];
- memcpy(&oob_buf[oob+j],
&oob_data[from], num);
- j+= num;
- }
- oob += mtd->oobavail;
- break;
- case MTD_NANDECC_PLACE:
- /* YAFFS1 legacy mode
*/
- oob_data += this->eccsteps
* sizeof (int);
- default:
- oob_data += mtd->oobsize;
- }
- }
- readdata:
- /* Partial page read, transfer data into fs buffer
*/
- if (!aligned)
{
- for (j
= col; j <
end && read
< len; j++)
- buf[read++]
= data_poi[j];
- this->pagebuf
= realpage;
- } else
- read += mtd->oobblock;
- /* Apply delay
or wait for ready/busy pin
- * Do this before the AUTOINCR check, so no problems
- * arise if a chip which does auto increment
- * is marked as NOAUTOINCR by the board driver.
- */
- if (!this->dev_ready)
- udelay (this->chip_delay);
- else
- while (!this->dev_ready(mtd));
- if (read
==
len)
- break;
- /*
For subsequent reads align to page boundary.
*/
- col = 0;
- /* Increment page address
*/
- realpage++;
- page = realpage
& this->pagemask;
- /* Check,
if we cross a chip boundary
*/
- if (!page)
{
- chipnr++;
- this->select_chip(mtd,
-1);
- this->select_chip(mtd, chipnr);
- }
- /* Check,
if the chip supports auto page increment
- * or
if we have hit a block boundary.
- */
- if (!NAND_CANAUTOINCR(this)
||
!(page & blockcheck))
- sndcmd = 1;
- }
- /* Deselect
and wake up anyone waiting
on the device */
- nand_release_device(mtd);
- /*
- * Return success,
if no ECC failures,
else -EBADMSG
- * fs driver will take care of that, because
- * retlen == desired
len and result
==
-EBADMSG
- */
- *retlen = read;
- return ecc_failed ?
-EBADMSG : 0;
- }
this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page)函数指针指向的函数为sep4020_nand_command函数
点击(此处)折叠或打开
- static void sep4020_nand_command (struct mtd_info
*mtd, unsigned command,
int column,
int page_addr)
- {
- register struct nand_chip *this
= mtd->priv;
- if(command
== NAND_CMD_READOOB)
//(1)
- {
- column += mtd->oobblock;
- command = NAND_CMD_READ0;
- }
- //column是坏块在oob中的位置,加上oobblock(就是页大小pagesiz,不知道为什么起这个名字oobblock),这样就是
- //地址中的列地址。command命令赋值NAND_CMD_READ0(0),读命令。
- this->hwcontrol(mtd, NAND_CTL_SETCLE);
- //命令引脚使能
- switch(command)
- {
- case NAND_CMD_READ0:
- *(volatile unsigned long*)EMI_NAND_COM_RAW
= 0x40003000;
- //这个寄存器[7:0]命令的第一个字节00
[15:8]是命令的第二个字节30
.
- //最高位是使能位(暂不开启),30位是字节表示1字节还是2字节命令。4=0100
- break;
- case NAND_CMD_SEQIN:
- *(volatile unsigned long*)EMI_NAND_COM_RAW
= 0x40001080;
- // 80,10 写flash
- break;
- default:
- this->write_byte(mtd,command);
- break;
- }
- this->hwcontrol(mtd,NAND_CTL_CLRCLE);
- //命令引脚无效
- if (command
== NAND_CMD_READID)
- {
- EMI_NAND_COM |= 0x80000000;
//使能EMI_NAND_COM
- this->hwcontrol(mtd, NAND_READ_ID);
- return;
- }
- if (command
== NAND_CMD_STATUS)
- {
- EMI_NAND_COM |= 0x80000000;
//使能EMI_NAND_COM
- this->hwcontrol(mtd, NAND_READ_STATUS);
- }
- if (command
== NAND_CMD_RESET)
- {
- EMI_NAND_COM |= 0x80000000;
- this->hwcontrol(mtd, NAND_CTL_CLRALE);
- }
- /*
Set ALE and clear CLE
to start address cycle */
- if (column
!= -1
|| page_addr !=
-1)
{
- this->hwcontrol(mtd, NAND_CTL_SETALE);
//这里这个函数其实没什么用。
- EMI_NAND_ADDR1 = page_addr<<16;
//page_addr是页号。128M,2Kflash一共就64K页
- EMI_NAND_ADDR2 = page_addr>>16;
//对于一共总数64K的页,这个值等于0
- this->hwcontrol(mtd, NAND_CTL_CLRALE);
- }
- //
- }
分析sep4020_hwcontrol函数。此函数之所以存在,应该是为了和MCU通过引脚直接控制或其他MCU的nand flash的代码结构保持兼容,此处此函数的主要作用是将IO_ADDR_W替换成对应的寄存器地址
点击(此处)折叠或打开
- static void sep4020_hwcontrol(struct mtd_info
*mtd,
int cmd)
- {
- struct nand_chip *this
= mtd->priv;
- switch (cmd)
{
- case NAND_CTL_SETNCE:
- case NAND_CTL_CLRNCE:
- break;
- //对于nCE位的操作都不予理睬
- case NAND_CTL_SETCLE:
- this->IO_ADDR_W
= (void __iomem
*) EMI_NAND_COM_RAW;
- break;
- //IO_ADDR_W是nand flash的数据寄存器地址。是_iomem类型变量(这是个空的宏定义,
- //但这样可以让人很容易知道这是个寄存器变量。),这里的作用是将EMI_NAND_COM_RAW即nand flash
- //内存的地址赋值给IO_ADDR_W,这样后面的操作,在使用IO_ADDR_W时就是使用EMI_NAND_COM_RAW。
- case NAND_CTL_SETALE:
- this->IO_ADDR_W
= (void __iomem
*) EMI_NAND_ADDR1_RAW;
- break;
- case NAND_READ_ID:
- this->IO_ADDR_R
= (void __iomem
*) EMI_NAND_ID_RAW;
- break;
- case NAND_READ_STATUS:
- this->IO_ADDR_R
= (void __iomem
*) EMI_NAND_STA_RAW;
- break;
- /* NAND_CTL_CLRCLE:
*/
- /* NAND_CTL_CLRALE:
*/
- default:
- this->IO_ADDR_W
= (void __iomem
*) EMI_NAND_DATA_RAW;
- this->IO_ADDR_R
= (void __iomem
*) EMI_NAND_DATA_RAW;
- //在一些命令使能和地址使能后,将IO_ADDR_W还原成EMI_NAND_DATA_RAW nand flash数据寄存器地址
- break;
- }
- }
this->read_buf(mtd, data_poi, end);read_buf指向的函数为sep4020_nand_read_buf,
点击(此处)折叠或打开
- static void sep4020_nand_read_buf(struct mtd_info
*mtd, u_char
*buf,
int len)
- {
- int i;
- struct nand_chip *this
= mtd->priv;
- //配置DMAC用于nand的传输
- DMAC_C0CONTROL =
((2112>>2)<<14)
+ (1<<13)
+ (2<<9)
+(2<<6)
+ (3<<3)
+ 3;
- DMAC_C0SRCADDR = EMI_NAND_DATA_RAW
;
- DMAC_C0DESTADD = vaddr;
//vaddr在board_nand_init函数中,使用malloc分配的一块2112大的内存空间
- DMAC_C0CONFIGURATION = 0x31d
;
- EMI_NAND_COM = 0xc0003000;
//nand命令控制器,00 30读命令,且最高位使能nand控制器,开始读数据。
- while(1)
- {
- if ((EMI_NAND_IDLE
& 0x01)
!= 0)
- break;
- }
- if(len
== 2048
|| len
== 2112)
//如果要读取的长度是1页或包含oob的1页。则从vaddr开始复制len长度的数据
- {
- memcpy(buf,vaddr,len);
- }
- else if(len
== 64)
//如果读取的长度是64,则是要只读取oob区域,则从vaddr+2048地址处开始复制。
- {
- memcpy(buf,vaddr+2048,len);
- }
- }
nand read命令大致就是这样一个流程。本来想只是写写uboot关于nand的处理,和这个sep4020 nand控制器的特点。没想到这个版本的uboot就是nand驱动和内核的差不多,代码量太多。可能也是自己不熟悉这块,陆陆续续写了几天,感觉写的效率很低,写的想吐。于是草草结尾。之后看看其他版本的uboot的nand相关,不知道还是不是这样了。