.块设备驱动之nandflash——简单程序分析

来源:互联网 发布:亚马逊运营书籍知乎 编辑:程序博客网 时间:2024/05/17 01:08
本节里面我们实现一个简单的nandflash驱动程序,我们先来看代码:
/* 参考  * drivers\mtd\nand\s3c2410.c * drivers\mtd\nand\at91_nand.c */#include <linux/module.h>#include <linux/types.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/ioport.h>#include <linux/platform_device.h>#include <linux/delay.h>#include <linux/err.h>#include <linux/slab.h>#include <linux/clk.h> #include <linux/mtd/mtd.h>#include <linux/mtd/nand.h>#include <linux/mtd/nand_ecc.h>#include <linux/mtd/partitions.h> #include <asm/io.h> #include <asm/arch/regs-nand.h>#include <asm/arch/nand.h>struct s3c_nand_regs {unsigned long nfconf  ;unsigned long nfcont  ;unsigned long nfcmd   ;unsigned long nfaddr  ;unsigned long nfdata  ;unsigned long nfeccd0 ;unsigned long nfeccd1 ;unsigned long nfeccd  ;unsigned long nfstat  ;unsigned long nfestat0;unsigned long nfestat1;unsigned long nfmecc0 ;unsigned long nfmecc1 ;unsigned long nfsecc  ;unsigned long nfsblk  ;unsigned long nfeblk  ;};static struct nand_chip *s3c_nand;static struct mtd_info *s3c_mtd;static struct s3c_nand_regs *s3c_nand_regs;static void s3c2440_select_chip(struct mtd_info *mtd, int chipnr){if (chipnr == -1){/* 取消选中: NFCONT[1]设为1 */s3c_nand_regs->nfcont |= (1<<1); }else{/* 选中: NFCONT[1]设为0 */s3c_nand_regs->nfcont &= ~(1<<1);}}static void s3c2440_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl){if (ctrl & NAND_CLE){/* 发命令: NFCMMD=dat */s3c_nand_regs->nfcmd = dat;}else{/* 发地址: NFADDR=dat */s3c_nand_regs->nfaddr = dat;}}static int s3c2440_dev_ready(struct mtd_info *mtd){return (s3c_nand_regs->nfstat & (1<<0));}static int s3c_nand_init(void){struct clk *clk;/* 1. 分配一个nand_chip结构体 */s3c_nand = kzalloc(sizeof(struct nand_chip), GFP_KERNEL);s3c_nand_regs = ioremap(0x4E000000, sizeof(struct s3c_nand_regs));/* 2. 设置nand_chip *//* 设置nand_chip是给nand_scan函数使用的, 如果不知道怎么设置, 先看nand_scan怎么使用 * 它应该提供:选中,发命令,发地址,发数据,读数据,判断状态的功能*/s3c_nand->select_chip = s3c2440_select_chip;s3c_nand->cmd_ctrl    = s3c2440_cmd_ctrl;s3c_nand->IO_ADDR_R   = &s3c_nand_regs->nfdata;s3c_nand->IO_ADDR_W   = &s3c_nand_regs->nfdata;s3c_nand->dev_ready   = s3c2440_dev_ready;s3c_nand->ecc.mode    = NAND_ECC_SOFT;/* 3. 硬件相关的设置: 根据NAND FLASH的手册设置时间参数 *//* 使能NAND FLASH控制器的时钟 */clk = clk_get(NULL, "nand");clk_enable(clk);              /* CLKCON'bit[4] *//* HCLK=100MHz* TACLS:  发出CLE/ALE之后多长时间才发出nWE信号, 从NAND手册可知CLE/ALE与nWE可以同时发出,所以TACLS=0* TWRPH0: nWE的脉冲宽度, HCLK x ( TWRPH0 + 1 ), 从NAND手册可知它要>=12ns, 所以TWRPH0>=1* TWRPH1: nWE变为高电平后多长时间CLE/ALE才能变为低电平, 从NAND手册可知它要>=5ns, 所以TWRPH1>=0*/#define TACLS    0#define TWRPH0   1#define TWRPH1   0s3c_nand_regs->nfconf = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);/* NFCONT: * BIT1-设为1, 取消片选 * BIT0-设为1, 使能NAND FLASH控制器*/s3c_nand_regs->nfcont = (1<<1) | (1<<0);/* 4. 使用: nand_scan */s3c_mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);s3c_mtd->owner = THIS_MODULE;s3c_mtd->priv  = s3c_nand;nand_scan(s3c_mtd, 1);  /* 识别NAND FLASH, 构造mtd_info *//* 5. add_mtd_partitions */return 0;}static void s3c_nand_exit(void){kfree(s3c_mtd);iounmap(s3c_nand_regs);kfree(s3c_nand);}module_init(s3c_nand_init);module_exit(s3c_nand_exit);MODULE_LICENSE("GPL");


分析:首先我们先来看一下nandflash驱动的编写步骤:
(1)分配一个nand_chip
(2)设置这个nand_chip
(3)硬件相关的设置
(4)使用nand_scan和add_mtd_partitions
那么下面我们就根据这个步骤来一步一步分析这个程序:
我们先分配一个nand_chip,对这个结构体的设置是一个比较纠结的过程,但是韦东山老师告诉我们一个好办法,nand_chip设置了是给 nand_scan用的,那么我们只要看看它是怎么用这个结构体的不就可以学着设置了吗?果然是好主要!
我们从 nand_scan出发,一步一步往下分析:
(1)我们发现有这么个函数:select_chip(mtd, 0)
我们到函数:nand_set_defaults(chip, busw);里面去看默认的设置,发现对 chip->select_chip 这个函数的设置比较坑爹,所以我们必须自己完成这个函数。这个函数的作用就是选通芯片,或者不选通芯片,这由 NFCONT 的第一位来决定,为0是表示选通,1时表示不选通, 还是比较容易设置的。
(2)接下来又出现了函数:chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);我们到nand_set_defaults(chip, busw);里面去看它的默认设置,然后发现chip->cmdfunc函数最终会调用:chip->cmd_ctrl(mtd, readcmd, ctrl);这个函数,那么我们需要对chip->cmd_ctrl(mtd, readcmd, ctrl);进行实现。这个函数的作用就是根据ctrl来判断传递的是地址、命令还是数据。也很容易实现。
(3)接下来是:maf_id = chip->read_byte(mtd);我们还是去看默认设置,发现这么一句:readb(chip->IO_ADDR_R);里面的参数当然就是读的地址了,所以我们还必须要设置读地址。同样写地址也要设这的,方法一样的。
(4)我们还需要获取的是nandflash的状态,因为我们需要判断nandflash是不是忙。
(5)设置校验码:关于校验码我们还得好好说道一下呢!我们下来看一下nandflash的组织图:
块设备驱动之nandflash——简单程序分析 - 小白 - 小白的博客
我们看到一页里面有2k加64b,其实存储真正的数据的是2k,64b是用作ecc校验的。它的原理如下:
当我们写nandflash时,会根据写入的数据计算出来ecc码,并将ecc码写入64b的空间里面。当我们读数据时,会根据读出来的数据从新计算出来ecc校验码,然后跟存放在64b空间里面的ecc校验码相比较,如果一致说明没有发生错误,否则是发生了错误。
 
(6)我们还需要一个struct mtd_info结构体,并使其私有数据指向我们上面所设置的struct nand_chip结构体。
(7)接下来就是硬件相关的设置了,首先我们需要设置时钟。我们直接用内核函数先获得时钟,然后使能时钟。除此之外我们还需要设置一些延时参数:
先贴两张图:
这两张是s3c2440上的:
块设备驱动之nandflash——简单程序分析 - 小白 - 小白的博客
 
块设备驱动之nandflash——简单程序分析 - 小白 - 小白的博客
 

这两张是nandflash芯片资料上面的:
块设备驱动之nandflash——简单程序分析 - 小白 - 小白的博客
 
块设备驱动之nandflash——简单程序分析 - 小白 - 小白的博客
我们先来说说我们要设置的三个时间参数的含义吧:
TACLS:  发出CLE/ALE之后多长时间才发出nWE信号
TWRPH0: nWE的脉冲宽度, HCLK x ( TWRPH0 + 1 )
TWRPH1: nWE变为高电平后多长时间CLE/ALE才能变为低电平

比较两幅时序图我们发下如下对应关系(HCLK=100Mhz):
TACLS*HCLK=tCLS-tWP=12-12=0,所以TACLS=0
(TWRPH0+1)*HCLK>=12ns,所以TWRPH0>=1
(TWRPH1+1)*HCLK>=5ns, 所以TWRPH1>=0
 于是可以设置:s3c_nand_regs->nfconf = (0<<12) | (1<<8) | (0<<4);
(8)对于NFCONT,我们要先使能控制寄存器,但是要先取消片选。
原创粉丝点击