下面这部分主要介绍一下,关于硬件的设计和规范,是如何映射到具体的软件实现的,看了这部分内容之后,你对如何根据硬件的规范去用软件代码实现对应的功能,就有了大概的了解了,然后去实现对应的某硬件的驱动,就有了大概的脉络了。
关于硬件部分的细节,前面其实已经介绍过了,但是为了方便说明,此处还是以读操作为例去讲解硬件到软件是如何映射的。
再次贴出上面的那个图:
对于上面的从1到6,每个阶段所表示的含义,再简单解释一下:
上面的是内容,说的是硬件是如何设计的,而这硬件的设计,即硬件的逻辑时序是如何规定的,对应的软件实现,也就要如何实现。不过可以看出的是,其中很多步骤,比如步骤1和步骤4,那都是固定的,而且,即使其中的步骤2和步骤3,即使是不同厂家和不同的Nand Flash芯片,除了要写入的列地址和行地址可能不同之外,也都是逻辑一样的,同样地,步骤5和6,也都是一样的,唯一不同的,是每家不同的Nand Flash控制器不同,所以具体到步骤6的时候,去读出数据的方式不同,所以,那一部分肯定是你要实现Nand Flash驱动的时候要自己实现的,而对应的其他几个公有的步骤呢,就有了Linux的MTD层帮你实现好了,所以,下面就来介绍一下,关于读取一个Nand Flash的页Page,Linux的MTD层,是如何具体的帮你实现的:
关于Nand Flash的读操作,即读取一页的数据,这样的读数据的操作,很明显,是从上层文件系统传递过来的,其细节我们在此忽略,但是要知道,上层读取数据的请求,传递到了MTD这一层,其入口是哪个函数,然后我们才能继续往下面分析细节。
关于下面所要的介绍的代码,如果没有明确指出,都是位于此文件:
代码位置:\drivers\mtd\nand\nand_base.c
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) {
......
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
......
ret = chip->ecc.read_page(mtd, chip, bufpoi,
buf += bytes;
......
readlen - = bytes;
if (! readlen)
break;
/ * For subsequent reads align to page boundary. */
col = 0;
/ * Increment page address */
realpage++;
page = realpage & chip->pagemask;
......
}
......
}
............
return mtd->ecc_stats.corrected - stats.corrected ? - EUCLEAN : 0;
}
对于上述
中的函数cmdfunc,一般来说可以不用自己的驱动中实现,而直接使用MTD层提供的已有的函数,nand_command_lp,其细节如下:
static void nand_command_lp(struct mtd_info *mtd, unsigned int command, int column, int page_addr)
{
......
/ * Command latch cycle */
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;
chip->cmd_ctrl(mtd, column, ctrl);
ctrl &= ~NAND_CTRL_CHANGE;
chip->cmd_ctrl(mtd, column >> 8, ctrl);
}
if (page_addr ! = - 1) {
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);
}
}
chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
/ *
* program and erase have their own busy handlers
* status, sequential in, and deplete1 need no delay
*/
switch (command) {
......
case NAND_CMD_READ0:
chip->cmd_ctrl(mtd, NAND_CMD_READSTART, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
/ * This applies to read commands */
default:
......
}
/ * Apply this short delay always to ensure that we do wait tWB in
* any case on any machine. */
/* */
ndelay(100);
nand_wait_ready(mtd);
}
对于之前的
的函数read_page,一般来说也可以不用自己的驱动中实现,而直接使用MTD层提供的已有的函数,nand_read_page_hwecc,该函数所要实现的功能,正是上面余下没介绍的6,即一点点的读出我们要的数据:
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);
chip->read_buf(mtd, p, eccsize);
chip->ecc.calculate(mtd, p, &ecc_calc[i]);
}
......
}
上面的read_buf,就是真正的去读取数据的函数了,由于不同的Nand Flash controller控制器所实现的方式不同,所以这个函数必须在你的Nand Flash驱动中实现,即MTD层,能帮我们实现的都实现了,不能实现的,那肯定要你的驱动自己实现。
对于我们这里的s3c2410的例子来说,就是s3c2410_nand_read_buf:
文件位置:\drivers\mtd\nand\s3c2410.c
static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
struct nand_chip *this = mtd->priv;
readsb(this->IO_ADDR_R, buf, len);
}
可以看出,此处的实现相当地的简单,就是读取对应的IO的地址,然后就可以把数据读出来就可以了。
不过,要注意的是,并不是所有的驱动都是这么简单,具体情况则是不同的Nand Flash控制器对应不同实现方法。
至此,关于整个的Nand Flash的读取一页的数据的操作,是如何将硬件的逻辑时序图,映射到对应的软件的实现的,就已经介绍完了。而看懂了这个过程,你才会更加明白,原来MTD层,已经帮助我们实现了很多很多通用的操作所对应的软件部分,而只需要我们实现剩下那些和具体硬件相关的操作的函数,就可以了,可以说大大减轻了驱动开发者的工作量。
因为,如果没了MTD层,那么上面那么多的函数,几乎都要我们自己实现,单单是代码量,就很庞大,而且再加上写完代码后的驱动测试功能是否正常,使得整个驱动开发,变得难的多得多。






