五、硬件时序到软件代码的演变过程对nand_base.c部分代码的分析
该文件位于</linux2.6.35/dricer/mtd/nand/nand_base.c>
还是把那个读NAND的硬件时序图给贴上,如下图:
???
①:此阶段,是读命令第一个周期,发送的命令为0x00。
②:此阶段,依次发送列地址,关于这些行地址,列地址等是如何计算出来的,后面的内容
会有详细解释。?
③:此阶段是发送对应的行地址?
④:此阶段是发送读命令第二周期 2nd cycle所对应的命令,0x30?
⑤:此阶段是等待时间,等待 Nand Flash硬件上准备好对应的数据,以便后续读出。?
⑥:此阶段,就是一点点地把所需要的数据读出来。?
MTD 读取数据的入口是 nand_read,然后调用 nand_do_read_ops,此函数主体如下:
static int nand_do_read_ops(struct mtd_info *mtd,loff_t from,
?? ??? ??? ???? struct mtd_oob_ops *ops)
{
??? /***此处省略部分代码**/
??? 。。。。。。。。。。。。。。
??? while(1) {
?????????? /******省略****/
????????? .。。。。。。。。。。。。。。。
?? ??? ???? if (likely(sndcmd)) {/*#define NAND_CMD_READ0 0*/
??????????????? /*1)***读取数据前肯定要先发送对应的读页命令******/
?? ??? ??? ???? chip->cmdfunc(mtd,NAND_CMD_READ0,0x00,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,bufpoi,
?? ??? ??? ??? ??? ??? ??? ?? 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层的自定义代码就行。。。
nand_command_lp的分析
static void nand_command_lp(struct mtd_info *mtd,unsigned int command,
?? ??? ??? ???? int column,int page_addr)
{
?? ?register struct nand_chip *chip = mtd->priv;
?? ?/* 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);
?? ?if (column != -1 || page_addr != -1) {
?? ??? ?int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;
?? ??? ?/* Serially input address */
?? ??? ?if (column != -1) {
?? ??? ??? ?/* Adjust columns for 16 bit buswidth */
?? ??? ??? ?if (chip->options & NAND_BUSWIDTH_16)
?? ??? ??? ??? ?column >>= 1;
????????? /* 发送两个column列地址,对应着上述步骤中的② */
?? ??? ???? chip->cmd_ctrl(mtd,column,ctrl);/*发送列地址1*/
?? ??? ??? ?ctrl &= ~NAND_CTRL_CHANGE;
?? ??? ??? ?chip->cmd_ctrl(mtd,column >> 8,ctrl);/*发送列地址2*/
?? ??? ?}
?? ??? ?if (page_addr != -1) {
????????? /* 接下来是发送三个Row,行地址,对应着上述步骤中的② */
/* 此处是对应着④中的tWB的等待时间*/
??? ndelay(100);
/* 接下来就是要等待一定的时间,使得Nand Flash硬件上准备好数据,以供你之
后读取,即对应着步骤⑤ */?
?? ?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 page)
{
?? 。。。。。。。。。。。。。。。。。。。。。。
?? ?for (i = 0; eccsteps; eccsteps--,i += eccbytes,p += eccsize) {
?? ??? ?chip->ecc.hwctl(mtd,NAND_ECC_READ);
???????? /**这个最重要了这才是真正的从NAND的缓冲区中把数据给读出来****/
?? ???? chip->read_buf(mtd,p,eccsize);
?? ??? ?chip->ecc.calculate(mtd,&ecc_calc[i]);
?? ?}
?
? 。。。。。。。。。。
?? ?return 0;
}
上面的 read_buf,就是真正的去读取数据的函数了,由于不同的Nand Flash controller 控制器所实现的方式不同,所以这个函数必须在你的 Nand Flash驱动中实现,即MTD 层,能帮我们实现的都实现了,不能实现的,那肯定是我们自己的事情了。。。接下来的工作是什么?MTD原始设备和硬件驱动层的交互了.这个才是我们要去真正实现的。。