从命令说起,在u-boot输入下列命令:

nand write 40008000 0 20000
命令的意思是将内存0x40008000开始的部分写入nand,从nand地址0开始写,写入长度是0x200000

回车之后,代码如何运行呢?命令的输入,执行之前都已经分析过了,初始化过程也分析了

请参阅:

http://blog.csdn.net/andy_wsj/article/details/9335755

http://blog.csdn.net/andy_wsj/article/details/9339247

http://blog.csdn.net/andy_wsj/article/details/8614905

执行这条命令,将调用\u-boot-sunxi-sunxi\common\cmd_nand.c内的函数do_nand。

int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])

nand write 40008000 0 20000在参数argv中,而且

argv[0] = "nand"

argv[1] = "write"

argv[2] = "40008000"

argv[3] = "0"

argv[4] = "20000"

argc = 5 参数的个数

分析一下do_nand函数的片段,篇幅关系,只保留写操作部分:

nt do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char * const argv[])

{


int i, ret = 0;


ulong addr;


loff_t off, size;


char *cmd, *s;


nand_info_t *nand;

#ifdef CONFIG_SYS_NAND_QUIET


int quiet = CONFIG_SYS_NAND_QUIET;

#else


int quiet = 0;

#endif


const char *quiet_str = getenv("quiet");


int dev = nand_curr_device;                 //当前NAND芯片,如果板上有多个芯片,则不能直接赋值,大部分板子都是一个NAND


int repeat = flag & CMD_FLAG_REPEAT;

/* at least two arguments please */


if (argc < 2)


goto usage;

if (quiet_str)


quiet = simple_strtoul(quiet_str, NULL, 0) != 0;

cmd = argv[1];   //cmd就指向命令“write”,

........判断是什么命令,多余判断删除了..............

/* The following commands operate on the current device, unless


* overridden by a partition specifier.  Note that if somehow the


* current device is invalid, it will have to be changed to a valid


* one before these commands can run, even if a partition specifier


* for another device is to be used.


*/


if (dev < 0 || dev >= CONFIG_SYS_MAX_NAND_DEVICE ||  //判断芯片是否存在或是否定义


   !nand_info[dev].name) {


puts("\nno devices available\n");


return 1;


}


nand = &nand_info[dev];   //获取定义的nand芯片信息

  

  ................

  


if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) {  //nand读写操作


size_t rwsize;


ulong pagecount = 1;


int read;


int raw;

if (argc < 4)  


goto usage;

addr = (ulong)simple_strtoul(argv[2], NULL, 16);  //将argv[2] = "40008000"转换成16进制,0x40008000

read = strncmp(cmd, "read", 4) == 0; /* 1 = read, 0 = write */  //判断读写操作类型


printf("\nNAND %s: ", read ? "read" : "write");

nand = &nand_info[dev];

s = strchr(cmd, '.');   //看看是否带有扩展命令,如write.raw, write.jffs2等等,输入是“write”,结果s = NULL;

if (s && !strcmp(s, ".raw")) {


      ......省略.....


      


} else {  //执行这里,计算地址偏移量,长度


if (arg_off_size(argc - 3, argv + 3, &dev, 


&off, &size) != 0)


return 1;

rwsize = size;


}

if (!s || !strcmp(s, ".jffs2") ||      //实际执行这里


   !strcmp(s, ".e") || !strcmp(s, ".i")) {


if (read)


ret = nand_read_skip_bad(nand, off, &rwsize,


(u_char *)addr);


else


ret = nand_write_skip_bad(nand, off, &rwsize,   //执行函数nand_write_skip_bad


 (u_char *)addr, 0);

} else if (......省略.....) {


......省略.....


......省略.....

} else {


printf("Unknown nand command suffix '%s'.\n", s);


return 1;


}

printf(" %zu bytes %s: %s\n", rwsize,


      read ? "read" : "written", ret ? "ERROR" : "OK");

return ret == 0 ? 0 : 1;


}

..........

return 0;

}

来看看函数nand_write_skip_bad,在文件\u-boot-sunxi-sunxi\drivers\mtd\nand\nand_util.c内:

经过do_nand处理,可知参数就是输入命令的内容:

offset   为  0

*length  为 0x200000

buffer   指向0x40008000

int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,


u_char *buffer, int flags)

{


int rval = 0, blocksize;


size_t left_to_write = *length;


u_char *p_buffer = buffer;


int need_skip;

#ifdef CONFIG_CMD_NAND_YAFFS


if (flags & WITH_YAFFS_OOB) {


if (flags & ~WITH_YAFFS_OOB)


return -EINVAL;

int pages;


pages = nand->erasesize / nand->writesize;


blocksize = (pages * nand->oobsize) + nand->erasesize;


if (*length % (nand->writesize + nand->oobsize)) {


printf ("Attempt to write incomplete page"


" in yaffs mode\n");


return -EINVAL;


}


} else

#endif


{


blocksize = nand->erasesize;  //执行这里,nand的刷新都是以块为单位的,所以blocksize就是刷新的长度,对于cubieboard上的nand芯片,是1M+80K


}

/*


* nand_write() handles unaligned, partial page writes.


*


* We allow length to be unaligned, for convenience in


* using the $filesize variable.


*


* However, starting at an unaligned offset makes the


* semantics of bad block skipping ambiguous (really,


* you should only start a block skipping access at a


* partition boundary).  So don't try to handle that.


*/


if ((offset & (nand->writesize - 1)) != 0) {    //输入的偏移量要以块长度对齐


printf ("Attempt to write non page aligned data\n");


*length = 0;


return -EINVAL;


}

need_skip = check_skip_len(nand, offset, *length);  //判断是否需要越过坏块,这里需要坏块读取操作,nand驱动的一个功能


if (need_skip < 0) {


printf ("Attempt to write outside the flash area\n");


*length = 0;


return -EINVAL;


}

if (!need_skip && !(flags & WITH_DROP_FFS)) {        //不需要,即写的部分没有坏块


rval = nand_write (nand, offset, length, buffer);  //直接写


if (rval == 0)


return 0;

*length = 0;


printf ("NAND write to offset %llx failed %d\n",


offset, rval);


return rval;


}

while (left_to_write > 0) {  // 剩下要写的字节数,开始就是命令输入的0x200000


size_t block_offset = offset & (nand->erasesize - 1);


size_t write_size, truncated_write_size;

WATCHDOG_RESET ();

if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) { //从开始的位置往后找坏块,直到找到一个可写的为止


printf ("Skip bad block 0x%08llx\n",


offset & ~(nand->erasesize - 1));


offset += nand->erasesize - block_offset;


continue;


}

if (left_to_write < (blocksize - block_offset))  //找到可写的块,判断写入的数据是不是小于一块,对于cubieboard,是1M


write_size = left_to_write;                    //由于输入的是0x200000即2M,因此需要写两次


else


write_size = blocksize - block_offset;

#ifdef CONFIG_CMD_NAND_YAFFS

.......

#endif


{


truncated_write_size = write_size;

#ifdef CONFIG_CMD_NAND_TRIMFFS

 .......

#endif

rval = nand_write(nand, offset, &truncated_write_size,  //调用nand_write,写入数据


p_buffer);


offset += write_size;         //偏移量往后移动


p_buffer += write_size;       //数据指针往后移动


}

if (rval != 0) {


printf ("NAND write to offset %llx failed %d\n",


offset, rval);


*length -= left_to_write;


return rval;


}

left_to_write -= write_size;   //剩下的字节数,循环写的条件


}

return 0;

}

无论如何写,有没有坏块,最后都使用函数nand_write,接下来再看看这个函数

在文件在文件\u-boot-sunxi-sunxi\drivers\mtd\nand\nand_base.c内:

这个函数就是写的准备,这已经执行到驱动代码的逻辑层了

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 writes past end of device */ 不能超过最大长度


if ((to + len) > mtd->size)


return -EINVAL;


if (!len)


return 0;

nand_get_device(chip, mtd, FL_WRITING);  //获取设备,就是获取需要写的那个nand芯片的数据

chip->ops.len = len;                     //写入的长度,按输入命令,第一次时这个就是一个块的长度


chip->ops.datbuf = (uint8_t *)buf;       //数据所在的位置,第一次就是输入的内存地址0x40008000处


chip->ops.oobbuf = NULL;

ret = nand_do_write_ops(mtd, to, &chip->ops);   //执行写操作

*retlen = chip->ops.retlen;

nand_release_device(mtd);

return ret;

}

再看看nand_do_write_ops函数,就在这个文件nand_base.c内,nand_write函数的上面:

到了这里,其实已经接近硬件操作了,如果要写一个nand驱动,实现写操作,

看看这个函数,就是知道需要实现的几个操作了。下面对几个关键的地方进行标记,说明写驱动需要实现的功能

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;

uint32_t oobwritelen = ops->ooblen;


uint32_t oobmaxlen = ops->mode == MTD_OOB_AUTO ?


mtd->oobavail : mtd->oobsize;

uint8_t *oob = ops->oobbuf;


uint8_t *buf = ops->datbuf;


int ret, subpage;

ops->retlen = 0;


if (!writelen)


return 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);          //芯片片选,由于各种CPU的片选方式或寄存器不同,或者板子电路不同,所以用户必须自己实现这个函数

/* Check, if it is write protected */


if (nand_check_wp(mtd)) {


printk (KERN_NOTICE "nand_do_write_ops: Device is write protected\n");


return -EIO;


}

realpage = (int)(to >> chip->page_shift);


page = realpage & chip->pagemask;


blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;

/* Invalidate the page cache, when we write to the cached page */


if (to <= (chip->pagebuf << chip->page_shift) &&


   (chip->pagebuf << chip->page_shift) < (to + ops->len))


chip->pagebuf = -1;

/* If we're not given explicit OOB data, let it be 0xFF */


if (likely(!oob))


memset(chip->oob_poi, 0xff, mtd->oobsize);

/* Don't allow multipage oob writes with offset */


if (oob && ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen))


return -EINVAL;

while (1) {    输入的长度是块,只能一页一页的写,所以要循环写


WATCHDOG_RESET();

int bytes = mtd->writesize;


int cached = writelen > bytes && page != blockmask;


uint8_t *wbuf = buf;

/* Partial page write ? */


if (unlikely(column || writelen < (mtd->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)) {


size_t len = min(oobwritelen, oobmaxlen);


oob = nand_fill_oob(chip, oob, len, ops);


oobwritelen -= len;


}

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;

}

再看看通用的页写函数

在函数int nand_scan_tail(struct mtd_info *mtd)内有两句代码:

......


if (!chip->write_page)


chip->write_page = nand_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);   //命令函数也有默认版本,对单次写地址的芯片如2440,6410,可以使用默认函数,但是不适合A10

                                                    //A10的地址是两个寄存器,每个32位,理论可以支持64位的地址宽度

                                                    //这里执行nand命令NAND_CMD_SEQIN,值是0x80


if (unlikely(raw))                                //观察调用的地方,可以看出 raw = 2    ===>  ops->mode == MTD_OOB_RAW 


chip->ecc.write_page_raw(mtd, chip, buf);       //ecc模块也有默认实现


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);   ////这里执行nand命令NAND_CMD_PAGEPROG,值是0x10


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;

}

再看看默认的chip->ecc.write_page_raw函数干了什么事情

在函数int nand_scan_tail(struct mtd_info *mtd)内有两句代码:

......


if (!chip->ecc.write_page_raw)


chip->ecc.write_page_raw = nand_write_page_raw;

......

如果用户没初始化页写函数,则使用默认函数nand_write_page_raw,这就是需要分析的函数

这个函数将数据写入,写入什么位置呢?还要看看它调用的函数chip->write_buf

static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,


const uint8_t *buf)

{


chip->write_buf(mtd, buf, mtd->writesize); 


chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);

}

再看看默认的chip->write_buf函数

在函数int nand_scan_tail(struct mtd_info *mtd)内有两句代码:

......


if (!chip->write_buf)


chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;

......

如果用户没初始化页写函数,8位操作则使用默认函数nand_write_buf,16位操作则使用默认函数nand_write_buf16,

cubieboard使用的nand芯片是8位的,就看看nand_write_buf函数

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 < len; i++)


writeb(buf[i], chip->IO_ADDR_W);

}

将数据写入寄存器chip->IO_ADDR_W,即写到nand缓存

从这里可以看出,上面的写操作过程就是:

命令0x80-->写数据-->命令0x10-->等待完成

查看cubieboard上面nand芯片K9GBG08U0A的数据手册,页写操作的过程真好相同,因此这个驱动可以使用

使用的前提就是需要实现一下几个函数:

片选函数:    chip->select_chip

命令操作函数:chip->cmdfunc

chip->waitfunc调用的两个函数:

芯片就绪函数:chip->dev_ready

字节读取函数:chip->read_byte

到这里,我都没有分析数据结构,只描述了调用流程

观察各个函数,贯穿整个过程的数据结构有两个

struct mtd_info

struct nand_chip

这两个数据结构在初始化分析时已经讲过了

u-boot的nand驱动写过程分析的更多相关文章

  1. 基于MTD的NAND驱动开发、K9F1G08 2K page、Yaffs2 Files System

    转载:http://hi.baidu.com/cui1206/item/1d4119e376132513585dd886 基于MTD的NAND驱动(linux-2.6.22.10内核),目前已可以在该 ...

  2. MTD中的nand驱动初步分析---面向u-boot

    之前提到nand驱动的初始化分析,有一个结构体 struct mtd_info始终贯穿这些代码 再来分析一下这个结构体的基本功能,如何初始化,如何使用 一.分析过程 看看结构体的出现和使用方式 第一次 ...

  3. MTD下的Nand驱动

    目录 MTD下的Nand驱动 引入 平台设备资源文件 关键数据结构 平台框架 s3c24xx_nand_probe nand_scan s3c2410_nand_add_partition add_m ...

  4. NAND驱动

    NAND FLASH是一个存储芯片 那么: 这样的操作很合理"读地址A的数据,把数据B写到地址A" 问1. 原理图上NAND FLASH和S3C2440之间只有数据线,      ...

  5. nand烧写分析/内核在启动过程中式如何将这个文件映射成/目录及各子目录的?

    我用的是ramdisk.image.gz,烧写在flash的0x10140000处 我不太明白内核在启动过程中式如何将这个文件映射成/目录及各子目录的? 如果ramdisk.image.gz在flas ...

  6. LPC1788 nand驱动

    Lpc 1788自带有emc接口用于驱动nandflash,norflash,sdram设备,对于nandflash驱动因为配置简单,时序也简单 首先,针对nandflash而言应当在系统中有三个地址 ...

  7. Linux-Nand Flash驱动(分析MTD层并制作NAND驱动)

    1.本节使用的nand flash型号为K9F2G08U0M,它的命令如下: 1.1我们以上图的read id(读ID)为例,它的时序图如下: 首先需要使能CE片选 1)使能CLE 2)发送0X90命 ...

  8. 24.Linux-Nand Flash驱动(分析MTD层并制作NAND驱动)

    1.本节使用的nand flash型号为K9F2G08U0M,它的命令如下: 1.1我们以上图的read id(读ID)为例,它的时序图如下: 首先需要使能CE片选 1)使能CLE 2)发送0X90命 ...

  9. ESP8266乐鑫版本的(支持云端升级 (Boot 模式)烧写方法,(V1.5.4官方介绍如下)(BOOT模式)

    硬件平台: nodeMCU devkit核心板,带ch340g,应该是仿造的,官方是cp2102驱动,安信可科技有连接https://wiki.ai-thinker.com/esp8266/board ...

随机推荐

  1. C#委托好处知多少

    1.性能 性能是泛型的一个主要优点. 直接上例子,通过实例可以让我们很好的理解这一点. Stopwatch stopwatch = new Stopwatch(); stopwatch.Start() ...

  2. cocos2d-x游戏开发系列教程-超级玛丽01-前言

    前言 上次用象棋演示了cocos2dx的基本用法,但是对cocos2dx并没有作深入的讨论,这次以超级马里奥的源代码为线索,我们一起来学习超级马里奥的实现,并以一些篇幅来详细讲述遇到的具体问题和具体的 ...

  3. 基于visual Studio2013解决算法导论之027hash表

     题目 hash表,用链表来解决冲突问题 解决代码及点评 /* 哈希表 链接法解决冲突问题 */ #include <iostream> using namespace std; s ...

  4. 使用Maven构建和部署J2EE应用程序的EAR文件

    这篇文章.主要是技术上的整理,用来mark一下,用的时候參考. 一.新建项目 新建一个空的Maven Project项目 二.放入依赖 注:ear部署时假设里面有entity,会错误发生.所以不要把e ...

  5. BZOJ 3685: 普通van Emde Boas树( 线段树 )

    建颗权值线段树就行了...连离散化都不用... 没加读入优化就TLE, 加了就A掉了...而且还快了接近1/4.... ---------------------------------------- ...

  6. php 上传文件代码

    通过 PHP,能够把文件上传到server.里面加入一些图片的推断,假设不加推断文件的类型就能够上传随意格式的文件. 为了站点的安全,肯定不让上传php文件,假设有人进入你的后台,上传了一个php文件 ...

  7. Java 螺纹第三版 第三章数据同步 读书笔记

    多线程间共享数据问题 一.Synchronizedkeyword      atomic一词与"原子"无关,它以前被觉得是物质的最小的单元,不能再被拆解成更小的部分.      当 ...

  8. 机器时代的中国字幕(Automata.2014.720p.WEB-DL.DD5.1.H264-RARBG.srt)

    看字幕.再也看不下去.自己翻译的位 评价的探讨 1 00:01:58,452 --> 00:02:02,088 人工增雨 期限为32分钟16第二 2 00:02:02,089 --> 00 ...

  9. ModelConvertHelper(将DataTable转换成List<model>)

      public class ModelConvertHelper<T> where T : new() {      public static IList<T> Conve ...

  10. 三家DirectUI的商业公司

    目前正在研究DirectUI技术,分享一点心得给大家.关于DirectUI技术的介绍我在这里就不说了,可以上Google查一下,非常丰富.目前使用DirectUI技术开发的软件产品原来原丰富,比如QQ ...