NAND FLASH学习笔记之MTD下nand flash驱动(六)
六、驱动层之Flash读操作 MTD对NAND芯片的读写 主要分三部分: A、struct mtd_info中的读写函数,如read,write_oob等,这是MTD原始设备层与FLASH硬件层之间的接口; B、struct nand_ecc_ctrl中的读写函数,如read_page_raw,write_page等,主要用来做一些与ecc有关的操作; C、struct nand_chip中的读写函数,如read_buf,cmdfunc等,与具体的NANDcontroller相关,就是这部分函数与硬件交互,通常需要我们自己来实现。 注: nand_chip中的读写函数虽然与具体的NAND controller相关,但是MTD也为我们提供了默认的读写函数,如果NAND controller比较通用(使用PIO模式),那么对NAND芯片的读写与MTD提供的这些函数一致,就不必自己实现这些函数。 ? 上面三部分读写函数相互配合完成对NAND芯片的读写,具体流程如下: 首先,MTD上层需要读写NAND芯片时,会调用struct mtd_info中的读写函数,接着struct mtd_info中的读写函数就会调用struct nand_chip或struct nand_ecc_ctrl中的读写函数,最后,若调用的是struct nand_ecc_ctrl中的读写函数,那么它又会接着调用struct nand_chip中的读写函数。 ? 以读为例: MTD上层会调用struct mtd_info中的读page函数,即nand_read函数。 接着nand_read函数会调用struct nand_chip中cmdfunc函数,这个cmdfunc函数与具体的NAND controller相关,它的作用是使NAND controller向NAND芯片发出读命令,NAND芯片收到命令后,就会做好准备等待NAND controller下一步的读取。接着nand_read函数又会调用struct nand_ecc_ctrl中的read_page函数,而read_page函数又会调用struct nand_chip中read_buf函数,从而真正把NAND芯片中的数据读取到buffer中(所以这个read_buf的意思其实应该是read into buffer,另外,这个buffer是struct mtd_info中的nand_read函数传下来的)。 read_buf函数返回后,read_page函数就会对buffer中的数据做一些处理,比如校验ecc,以及若数据有错,就根据ecc对数据修正之类的,最后read_page函数返回到nand_read函数中。 对NAND芯片的其它操作,如写,擦除等,都与读操作类似 ? ? JZ4780之NAND FLASH读函数调用流程: ???????? ?mtd上层选中并调用mtd_info中的读函数 ??->nand_read(mtd_info) ?? ->nand_do_read_ops ?? ??????????? (1)->chip->cmdfunc(mtd,NAND_CMD_READ0,0x00,page); ?? ??????????? (2)->chip->ecc.read_page() ????????????????? (1) ->??? read_buf()(read into buffer) ????????????????? (2) -> 调用一系列函数进行相关的ecc校验 ? 问题:nand_chip(nand flash的描述符)的读写操作是怎么和MTD的读写操作联系起来的呢? 1)probe->scan_tail; ???????? ??在填充MTD的时候,使用mtd->read = nand_read;这里和mtd挂钩。 ? ? 2)在nand_read实现中又调用了nand_do_read_ops(mtd,from,&chip->ops);这里和nand_chip联系起来了。 以读为例对代码进行分析如下:
读分析 MTD 读取数据的入口是 nand_read,然后调用 nand_do_read_ops,此函数主体如下: 《一》nand_read代码如下: static int nand_read(struct mtd_info *mtd,loff_t from,size_t len,size_t *retlen,uint8_t *buf) { struct mtd_oob_ops ops; int ret; nand_get_device(mtd,FL_READING); ops.len = len; ops.datbuf = buf; ops.oobbuf = NULL; ops.mode = MTD_OPS_PLACE_OOB; ret = nand_do_read_ops(mtd,&ops); *retlen = ops.retlen; nand_release_device(mtd); return ret; } 《二》nand_do_read_ops代码如下: static int nand_do_read_ops(struct mtd_info *mtd,struct mtd_oob_ops *ops) { /******省略****/ 。。。。。。。。。。。。。。 while(1) { /******省略****/ .。。。。。。。。。。。。。。。 if (likely(sndcmd)) {/*#define NAND_CMD_READ0 0*/ /*1)***读取数据前肯定要先发送对应的读页命令******/ chip->cmdfunc(mtd,page); sndcmd = 0; } /* Now read the page into the buffer */ if (unlikely(ops->mode == MTD_OOB_RAW)) ret = chip->ecc.read_page_raw(mtd,chip,bufpoi,page); else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob) ret = chip->ecc.read_subpage(mtd,col,bytes,bufpoi); else /******执行到这里read_page函数读取对应的数据了******/ ret = chip->ecc.read_page(mtd,page); if (ret < 0) break; /* Transfer not aligned data */ if (!aligned) { if (!NAND_SUBPAGE_READ(chip) && !oob) chip->pagebuf = realpage; memcpy(buf,chip->buffers->databuf + col,bytes); } buf += bytes; 。。。。。。。。。。。。。。。。。。 if (mtd->ecc_stats.failed - stats.failed) return -EBADMSG; return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; } 上面这些代码都不需要我们去实现的,使用MTD层的自定义代码就行。下面将要分析chip->cmdfunc,我们从probe函数中可以知道 /* step3. replace NAND command function with large page version */ if (mtd->writesize > 512) chip->cmdfunc = jz4780_nand_command_lp; jz4780_nand_command_lp的分析 static void jz4780_nand_command_lp(struct mtd_info *mtd,unsigned int command,int column,int page_addr) { register struct nand_chip *chip = mtd->priv; struct jz4780_nand *nand; nand_flash_if_t *nand_if; nand_flash_info_t *nand_info; nand = mtd_to_jz4780_nand(mtd); nand_if = nand->nand_flash_if_table[nand->curr_nand_flash_if]; nand_if->curr_command = command; nand_info = nand->curr_nand_flash_info; /* Emulate NAND_CMD_READOOB */ if (command == NAND_CMD_READOOB) { column += mtd->writesize; command = NAND_CMD_READ0; } /* Command latch cycle */ /* 此处就是就是发送读命令的第一个周期1st Cycle的命令,即0x00, 对应着上述步骤中的① */ chip->cmd_ctrl(mtd,command & 0xff,NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); jz4780_nand_delay_after_command(nand,nand_info,command); if (column != -1 || page_addr != -1) { int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE; /* Serially input address */ /* 发送两个column列地址,对应着上述步骤中的② */ if (column != -1) { chip->cmd_ctrl(mtd,column,ctrl); ctrl &= ~NAND_CTRL_CHANGE; chip->cmd_ctrl(mtd,column >> 8,ctrl); } if (page_addr != -1) { /* 接下来是发送三个Row,行地址,对应着上述步骤中的② */ chip->cmd_ctrl(mtd,page_addr,ctrl); chip->cmd_ctrl(mtd,page_addr >> 8,NAND_NCE | NAND_ALE); /* One more address cycle for devices > 128MiB */ if (chip->chipsize > (128 << 20)) chip->cmd_ctrl(mtd,page_addr >> 16,NAND_NCE | NAND_ALE); } } jz4780_nand_delay_after_address(nand,command); chip->cmd_ctrl(mtd,NAND_CMD_NONE,NAND_NCE | NAND_CTRL_CHANGE); switch (command) { 。。。。。。。。。。。。。。。 /******省略****/ .。。。。。。。。。。。。。。。 /* 接下来发送读命令的第二个周期2nd Cycle的命令,即0x30,对应着 上述步骤中的④ */ case NAND_CMD_READ0: chip->cmd_ctrl(mtd,NAND_CMD_READSTART,NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); chip->cmd_ctrl(mtd,NAND_NCE | NAND_CTRL_CHANGE); /* This applies to read commands */ default: /* * If we don't have access to the busy pin,we apply the given * command delay. */ if (!chip->dev_ready) { nand->udelay(chip->chip_delay); return; } } /* * Apply this short delay always to ensure that we do wait tWB in * any case on any machine. */ /* 此处是对应着④中的tWB的等待时间*/ nand->ndelay(100); /* 接下来就是要等待一定的时间,使得Nand Flash硬件上准备好数据,以供你之后读取,即对应着步骤⑤ */ nand->nand_wait_ready(mtd); } /*还有一个步骤没有实现那就是步骤⑥了一点一点的把数据读出来*/ nand_read_page_hwecc分析 static int nand_read_page_hwecc(struct mtd_info *mtd,struct nand_chip *chip,uint8_t *buf,int oob_required,int page) { int i,eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; uint8_t *p = buf; uint8_t *ecc_calc = chip->buffers->ecccalc; uint8_t *ecc_code = chip->buffers->ecccode; uint32_t *eccpos = chip->ecc.layout->eccpos; unsigned int max_bitflips = 0; for (i = 0; eccsteps; eccsteps--,i += eccbytes,p += eccsize) { chip->ecc.hwctl(mtd,NAND_ECC_READ); chip->read_buf(mtd,p,eccsize);//这个函数必须有我们来实现 chip->ecc.calculate(mtd,&ecc_calc[i]); } chip->read_buf(mtd,chip->oob_poi,mtd->oobsize); for (i = 0; i < chip->ecc.total; i++) ecc_code[i] = chip->oob_poi[eccpos[i]]; eccsteps = chip->ecc.steps; p = buf; for (i = 0 ; eccsteps; eccsteps--,p += eccsize) { int stat; stat = chip->ecc.correct(mtd,&ecc_code[i],&ecc_calc[i]); if (stat < 0) { mtd->ecc_stats.failed++; } else { mtd->ecc_stats.corrected += stat; max_bitflips = max_t(unsigned int,max_bitflips,stat); } } return max_bitflips; } 上面的 read_buf,就是真正的去读取数据的函数了,由于不同的Nand Flash controller 控制器所实现的方式不同,所以这个函数必须在你的 Nand Flash驱动中实现,即MTD 层,能帮我们实现的都实现了,不能实现的,那肯定是我们自己的事情了。。。 个人观点,有问题请斧正!! ? ? 转载请注明出处:http://blog.csdn.net/wang_zheng_kai (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |