如何编写Linux下linux spi nand flashh驱动

 专题推荐
 ? ? ? ? ? ? ? ? ? ?
 最近更新
?&?&?&?&?&?&?&?&?&?&?&?&
 频道精选
 嵌入式开发频道导航JavaScript is disabled on your browser. Please enable JavaScript to enjoy all the features of this site.
1.2.15.2. 读(Read)操作过程详解
下面以最简单的read操作为例,解释如何理解时序图,以及将时序图中的要求,转化为代码。解释时序图之前,让我们先要搞清楚,我们要做的事情:从Nand Flash的某个页Page里面,读取我们要的数据。要实现此功能,会涉及到几部分的知识,即使我们不太懂Nand Flash的细节,但是通过前面的基本知识的介绍,那么以我们的常识,至少很容易想到的就是,需要用到哪些命令,怎么发这些命令,怎么计算所需要的地址,怎么读取我们要的数据等等。下面就一步步的解释,需要做什么,以及如何去做:1.2.15.2.1. 需要使用何种命令
首先,是要了解,对于读取数据,要用什么命令:根据前面关于Nand Flash的命令集合介绍,我们知道,要读取数据,要用到Read命令,该命令需要2个周期,第一个周期发0x00,第二个周期发0x30。1.2.15.2.2. 发送命令前的准备工作以及时序图各个信号的具体含义
知道了用何命令后,再去了解如何发送这些命令。提示在开始解释前,关于”使能”这个词要罗嗦一下,以防有些读者和我以前一样,在听这类词语的时候,属于初次接触,或者接触不多的,就很容易被搞得一头雾水的(虽然该词汇对于某些专业人士说是属于最基本的词汇了,囧)。使能(Enable),是指使其(某个信号)有效,使其生效的意思,“使其”“能够”怎么怎么样。。。。比如,上面图中的CLE线号,是高电平有效,如果此时将其设为高电平,我们就叫做,将CLE使能,也就是使其生效的意思。图 1.7. Nand Flash数据读取操作的时序图注意此图来自三星的型号K9K8G08U0A的Nand Flash的数据手册(datasheet)。我们来一起看看,我在图6中的特意标注的①边上的黄色竖线。黄色竖线所处的时刻,是在发送读操作的第一个周期的命令0x00之前的那一刻。让我们看看,在那一刻,其所穿过好几行都对应什么值,以及进一步理解,为何要那个值。黄色竖线穿过的第一行,是CLE。还记得前面介绍命令所存使能(CLE)那个引脚吧?CLE,将CLE置1,就说明你将要通过I/O复用端口发送进入Nand Flash的,是命令,而不是地址或者其他类型的数据。只有这样将CLE置1,使其有效,才能去通知了内部硬件逻辑,你接下来将收到的是命令,内部硬件逻辑,才会将受到的命令,放到命令寄存器中,才能实现后面正确的操作,否则,不去将CLE置1使其有效,硬件会无所适从,不知道你传入的到底是数据还是命令了。而第二行,是CE#,那一刻的值是0。这个道理很简单,你既然要向Nand Flash发命令,那么先要选中它,所以,要保证CE#为低电平,使其有效,也就是片选有效。第三行是WE#,意思是写使能。因为接下来是往Nand Flash里面写命令,所以,要使得WE#有效,所以设为低电平。第四行,是ALE是低电平,而ALE是高电平有效,此时意思就是使其无效。而对应地,前面介绍的,使CLE有效,因为将要数据的是命令(此时是发送图示所示的读命令第二周期的0x30),而不是地址。如果在其他某些场合,比如接下来的要输入地址的时候,就要使其有效,而使CLE无效了。第五行,RE#,此时是高电平,无效。可以看到,知道后面低6阶段,才变成低电平,才有效,因为那时候,要发生读取命令,去读取数据。第六行,就是我们重点要介绍的,复用的输入输出I/O端口了,此刻,还没有输入数据,接下来,在不同的阶段,会输入或输出不同的数据/地址。第七行,R/B#,高电平,表示R(Ready)/就绪,因为到了后面的第5阶段,硬件内部,在第四阶段,接受了外界的读取命令后,把该页的数据一点点送到页寄存器中,这段时间,属于系统在忙着干活,属于忙的阶段,所以,R/B#才变成低,表示Busy忙的状态的。介绍了时刻①的各个信号的值,以及为何是这个值之后,相信,后面的各个时刻,对应的不同信号的各个值,大家就会自己慢慢分析了,也就容易理解具体的操作顺序和原理了。1.2.15.2.3. 如何计算出我们要传入的行地址和列地址
在介绍具体读取数据的详细流程之前,还要做一件事,那就是,先要搞懂我们要访问的地址,以及这些地址,如何分解后,一点点传入进去,使得硬件能识别才行。此处还是以K9K8G08U0A为例,此Nand Flash,一共有8192个块,每个块内有64页,每个页是2K+64 Bytes。假设,我们要访问其中的第7000个块中的第64页中的1208字节处的地址,此时,我们就要先把具体的地址算出来:物理地址=块大小×块号 + 页大小×页号 + 页内地址=128K×7000 + 2K×64 + 1208=0x36B204B8接下来,我们就看看,怎么才能把这个实际的物理地址,转化为Nand Flash所要求的格式。在解释地址组成之前,先要来看看其datasheet中关于地址周期的介绍:图 1.8. Nand Flash的地址周期组成结合中的2,3阶段,我们可以看出,此Nand Flash地址周期共有5个,2个列(Column)周期,3个行(Row)周期。对应地,列地址A0~A10,就是页内地址,地址范围是从0到2047。
细心的读者可能注意到了,为何此处多出来个A11呢?这样从A0到A11,一共就是12位,可以表示的范围就是0~212,即0~4096了。实际上,由于我们访问页内地址,可能会访问到oob的位置,即这64个字节的范围内,所以,此处实际上只用到了,用于表示页内的oob区域,其大小是64字节。对应地,A12~A30,称作页号,页的号码,可以定位到具体是哪一个页。
A18~A30,表示对应的块号,即属于哪个块。简单解释完了地址组成,那么就很容易分析上面例子中的地址了。注意,下面这样的方法,是错误的:0x36B204B8 = 11 10 0000 0100 ,分别分配到5个地址周期就是:注意
与中对应的,*L,意思是地电平,由于未用到那些位,datasheet中强制要求设为0,所以,才有上面的2nd周期中的高4位是0000.其他的A30之后的位也是类似原理,都是0。
而至于上述计算方法为何是错误的,那是因为上面计算过程中,把第11位的值,本来是属于页号的位A11,给算成页内地址里面的值了。应该是这样计算,才是对的:0x36B204B8 = 11 10 0000 0100 那有人会问了,上面表11中,不是明明写的A0到A30,其中包括A11,不是正好对应着此处地址中的bit0到bit30吗?其实,我开始也是犯了同样的错误,误以为我们要传入的地址的每一位,就是对应着表11中的A0到A30呢,实际上,表11中的A11,是比较特殊的,只有当我们访问页内地址处于oob的位置,即属于的时候,A11才会其效果,才会用A0-A11用来表示对应的某个属于的某个值,属于oob的某个位置。而我们此处的页内地址为2108,还没有超过2047呢,所以A11肯定是0。这么解释,显得很绕,很难看懂。换种方式来解释,就容易听懂了:说白了,我们就是要访问第7000个块中的第64页中的1208字节处,对应着页内地址=1208=0x4B8页号=块数×页数/块 + 块内的页号 = 7000×(128K/2K) + 64= 7000×64 + 64= 448064=0x6D640也就是,我们要访问0x6D640页内的0x4B8地址,这样很好理解吧,^_^。然后对应的:页内地址=0x4B8分成两个对应的列地址,就变成0x4B8 :列地址1=0xB8,列地址2=0x04页号=0x6D640,分成三个行号就是:0x6D640:行号1=0x40,行号2=0xD6,行号3=0x06再回头看看上面的计算方法,最开始计算出来的:列地址1=0xB8列地址2=0x04行号1=0x20行号2=0x6B行号3=0x03是错误的。而第二次计算正确的:列地址1=0xB8列地址2=0x04行号1=0x40行号2=0xD6行号3=0x06才是对的,也和我们此处自己手动计算,是一致的。第一次之所以计算错,就是错误的把行地址的最低一位A11,放到列地址中的最高位了。至此,才算把如何手动计算行地址和列地址,解释明白和正确了。对应的,Linux的源码\drivers\mtd\nand\nand_base.c中,也是这样处理的:
static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
int column, int page_addr)
/* Serially input address */
if (column != -1) {
chip-&cmd_ctrl(mtd, column, ctrl); /* 发送Col Addr 1 */
ctrl &= ~NAND_CTRL_CHANGE;
chip-&cmd_ctrl(mtd, column && 8, ctrl); /* 发送Col Addr 2 */
if (page_addr != -1) {
chip-&cmd_ctrl(mtd, page_addr, ctrl); /* 发送Row Addr 1 */
chip-&cmd_ctrl(mtd, page_addr && 8, /* 发送Row Addr 2 */
NAND_NCE | NAND_ALE);
/* One more address cycle for devices & 128MiB */
if (chip-&chipsize & (128 && 20))
chip-&cmd_ctrl(mtd, page_addr && 16, /* 发送Row Addr 3 */
NAND_NCE | NAND_ALE);
column,即页内地址,多数情况下,都是0,即使不是0,也可以直接通过将传入的地址除于页地址所得的余数,就是列地址了。 page_addr即页号,也很简单,就是通过要访问的地址,除于页大小,即可得到。因此,我们要访问第7000个块中的第64页中的1208字节处的话,所要传入的地址就是分5个周期:分别传入两个列地址的:列地址1=0xB8列地址2=0x04然后再传3个行地址的:行号1=0x40行号2=0xD6行号3=0x06这样硬件才能识别。而接下来的内容,也就是介绍硬件是如何处理这些输入的。1.2.15.2.4. 读操作过程的解释
准备工作终于完了,下面就可以开始解释说明,对于读操作的,上面图中标出来的,1-6个阶段,具体是什么含义。操作准备阶段:此处是读(Read)操作,所以,先发一个图5中读命令的第一个阶段的0x00,表示,让硬件先准备一下,接下来的操作是读。发送两个周期的列地址。也就是页内地址,表示,我要从一个页的什么位置开始读取数据。接下来再传入三个行地址。对应的也就是页号。然后再发一个读操作的第二个周期的命令0x30。接下来,就是硬件内部自己的事情了。Nand Flash内部硬件逻辑,负责去按照你的要求,根据传入的地址,找到哪个块中的哪个页,然后把整个这一页的数据,都一点点搬运到页缓存中去。而在此期间,你所能做的事,也就只需要去读取状态寄存器,看看对应的位的值,也就是R/B#那一位,是1还是0,0的话,就表示,系统是busy,仍在”忙“(着读取数据),如果是1,就说系统活干完了,忙清了,已经把整个页的数据都搬运到页缓存里去了,你可以接下来读取你要的数据了。对于这里。估计有人会问了,这一个页一共2048+64字节,如果我传入的页内地址,就像上面给的1208一类的值,只是想读取这部分数据,而不是页开始的0地址整个页的数据,那么内部硬件却读取整个页的数据出来,岂不是很浪费吗?答案是,的确很浪费,效率看起来不高,但是实际就是这么做的,而且本身读取整个页的数据,相对时间并不长,而且读出来之后,内部数据指针会定位到你刚才所制定的1208的那个位置。接下来,就是你“窃取“系统忙了半天之后的劳动成果的时候了,呵呵。通过先去Nand Flash的控制器中的数据寄存器中写入你要读取多少个字节(byte)/字(word),然后就可以去Nand Flash的控制器的FIFO中,一点点读取你要的数据了。至此,整个Nand Flash的读操作就完成了。对于其他操作,可以根据我上面的分析,一点点自己去看datasheet,根据里面的时序图去分析具体的操作过程,然后对照代码,会更加清楚具体是如何实现的。Nand Flash驱动程序编写_Linux编程_Linux公社-Linux系统门户网站
你好,游客
Nand Flash驱动程序编写
来源:Linux社区&
作者:veryStrong
NAND FLASH是一个存储芯片
那么: 这样的操作很合理"读地址A的数据,把数据B写到地址A"
问1. 原理图上NAND FLASH和S3C2440之间只有数据线,
怎么传输地址?
答1.在DATA0~DATA7上既传输数据,又传输地址
当ALE为高电平时传输的是地址,
问2. 从NAND FLASH芯片手册可知,要操作NAND FLASH需要先发出命令
怎么传入命令?
答2.在DATA0~DATA7上既传输数据,又传输地址,也传输命令
当ALE为高电平时传输的是地址,
当CLE为高电平时传输的是命令
当ALE和CLE都为低电平时传输的是数据
问3. 数据线既接到NAND FLASH,也接到NOR FLASH,还接到SDRAM、DM9000等等
那么怎么避免干扰?
答3. 这些设备,要访问之必须"选中",
没有选中的芯片不会工作,相当于没接一样
问4. 假设烧写NAND FLASH,把命令、地址、数据发给它之后,
NAND FLASH肯定不可能瞬间完成烧写的,
怎么判断烧写完成?
答4. 通过状态引脚RnB来判断:它为高电平表示就绪,它为低电平表示正忙
问5. 怎么操作NAND FLASH呢?
答5. 根据NAND FLASH的芯片手册,一般的过程是:
发出数据/读数据
& & & & & NAND FLASH& & & & & & & & & & & S3C2440
发命令& & 选中芯片& & & & & & & & &
& & & & & CLE设为高电平& & & & & & & & &
NFCMMD=命令值& &
& & & & & 在DATA0~DATA7上输出命令值
& & & & & 发出一个写脉冲
& & & & & &
发地址& & 选中芯片& & & & & & & & & & & & NFADDR=地址值
& & & & & ALE设为高电平
& & & & & 在DATA0~DATA7上输出地址值
& & & & & 发出一个写脉冲
发数据& & 选中芯片& & & & & & & & & & & & NFDATA=数据值
& & & & & ALE,CLE设为低电平
& & & & & 在DATA0~DATA7上输出数据值
& & & & & 发出一个写脉冲
读数据& & 选中芯片& & & & & & & & & & & & val=NFDATA
& & & & & 发出读脉冲
& & & & & 读DATA0~DATA7的数据
用UBOOT来体验NAND FLASH的操作:
& & & & & & & & & & & & & & &
S3C2440& & & & & & & &
选中& & & & & & & & & & & & &
NFCONT的bit1设为0&
md.l 0x4E; mw.l 0x4E
发出命令0x90& & & & & & & & &
NFCMMD=0x90& & & &
mw.b 0x4Ex90
发出地址0x00& & & & & & & & &
NFADDR=0x00& & & &
mw.b 0x4E0
读数据得到0xEC& & & & & & & &
val=NFDATA& & & & & md.b 0x4E
读数据得到device code& & & & & val=NFDATA& & & & & md.b 0x4E
& & & & & 0xda
退出读ID的状态& & & & & & & &
NFCMMD=0xff& & & &
mw.b 0x4Exff
2. 读内容: 读0地址的数据
使用UBOOT命令:
nand dump 0
& & & & 17 00 00 ea 14 f0 9f e5& 14 f0 9f e5 14 f0 9f e5
& & & & & & & & & & & & & & &
S3C2440& & & & & & & &
选中& & & & & & & & & & & & &
NFCONT的bit1设为0&
md.l 0x4E; mw.l 0x4E
发出命令0x00& & & & & & & & &
NFCMMD=0x00& & & &
mw.b 0x4Ex00
发出地址0x00& & & & & & & & &
NFADDR=0x00& & & &
mw.b 0x4E0
发出地址0x00& & & & & & & & &
NFADDR=0x00& & & &
mw.b 0x4E0
发出地址0x00& & & & & & & & &
NFADDR=0x00& & & &
mw.b 0x4E0
发出地址0x00& & & & & & & & &
NFADDR=0x00& & & &
mw.b 0x4E0
发出地址0x00& & & & & & & & &
NFADDR=0x00& & & &
mw.b 0x4E0
发出命令0x30& & & & & & & & &
NFCMMD=0x30& & & &
mw.b 0x4Ex30
读数据得到0x17& & & & & & & &
val=NFDATA& & & & & md.b 0x4E
读数据得到0x00& & & & & & & &
val=NFDATA& & & & & md.b 0x4E
读数据得到0x00& & & & & & & &
val=NFDATA& & & & & md.b 0x4E
读数据得到0xea& & & & & & & &
val=NFDATA& & & & & md.b 0x4E
退出读状态& & & & & & & & & &
NFCMMD=0xff& & & &
mw.b 0x4Exff
NAND FLASH驱动程序层次
看内核启动信息
S3C24XX NAND Driver, (c) 2004 Simtec Electronics
s3c2440-nand s3c2440-nand: Tacls=3, 30ns Twrph0=7 70ns, Twrph1=3 30ns
NAND device: Manufacturer ID: 0xec, Chip ID: 0xda (Samsung NAND 256MiB 3,3V 8-bit)
Scanning device for bad blocks
Bad eraseblock 256 at 0x
Bad eraseblock 257 at 0x
Bad eraseblock 319 at 0x027e0000
Bad eraseblock 606 at 0x04bc0000
Bad eraseblock 608 at 0x04c00000
Creating 4 MTD partitions on "NAND 256MiB 3,3V 8-bit":
0xx : "bootloader"
0xx : "params"
0xx : "kernel"
0xx : "root"
搜"S3C24XX NAND Driver"
S3c2410.c (drivers\mtd\nand)
s3c2410_nand_inithw
s3c2410_nand_init_chip
nand_scan& // drivers/mtd/nand/nand_base.c 根据nand_chip的底层操作函数识别NAND FLASH,构造mtd_info
& & nand_scan_ident
& & & & nand_set_defaults
& & & & & & if (!chip-&select_chip)
& & & & & & & & chip-&select_chip = nand_select_ // 默认值不适用
& & & & & & if (chip-&cmdfunc == NULL)
& & & & & & & & chip-&cmdfunc = nand_
& & & & & & & & & & & & & & & & & & chip-&cmd_ctrl(mtd, command, ctrl);
& & & & & & if (!chip-&read_byte)
& & & & & & & & chip-&read_byte = nand_read_
& & & & & & & & & & & & & & & & & & readb(chip-&IO_ADDR_R);
& & & & & & if (chip-&waitfunc == NULL)
& & & & & & & & chip-&waitfunc = nand_
& & & & & & & & & & & & & & & & & & chip-&dev_ready
& & & & nand_get_flash_type
& & & & & & chip-&select_chip(mtd, 0);
& & & & & & chip-&cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
& & & & & & *maf_id = chip-&read_byte(mtd);
& & & & & & dev_id = chip-&read_byte(mtd);
& & nand_scan_tail
& & & & & & mtd-&erase = nand_
& & & & & & mtd-&read = nand_
& & & & & & mtd-&write = nand_
& & & & & &
& & & & & &
& & & & & &
s3c2410_nand_add_partition
& & add_mtd_partitions
& & & & add_mtd_device
& & & & & & list_for_each(this, &mtd_notifiers) { // 问. mtd_notifiers在哪设置
& & & & & & & & & & & & & & & & & & & & & & & & & // 答. drivers/mtd/mtdchar.c,mtd_blkdev.c调用register_mtd_user
& & & & & & & & struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
& & & & & & & & not-&add(mtd);
& & & & & & & & // mtd_notify_add& 和 blktrans_notify_add
& & & & & & & & 先看字符设备的mtd_notify_add
& & & & & & & & & & & & class_device_create
& & & & & & & & & & & & class_device_create
& & & & & & & & 再看块设备的blktrans_notify_add
& & & & & & & & & & list_for_each(this, &blktrans_majors) { // 问. blktrans_majors在哪设置
& & & & & & & & & & & & & & & & & & & & & & & & & & & & & & // 答. drivers\mtd\mdblock.c或mtdblock_ro.c&
register_mtd_blktrans
& & & & & & & & & & & & struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);& & & & & & &
& & & & & & & & & & & & tr-&add_mtd(tr, mtd);
& & & & & & & & & & & & & & & & mtdblock_add_mtd (drivers\mtd\mdblock.c)
& & & & & & & & & & & & & & & & & & add_mtd_blktrans_dev
& & & & & & & & & & & & & & & & & & & & alloc_disk
& & & & & & & & & & & & & & & & & & & & gd-&queue = tr-&blkcore_priv-& // tr-&blkcore_priv-&rq = blk_init_queue(mtd_blktrans_request, &tr-&blkcore_priv-&queue_lock);
& & & & & & & & & & & & & & & & & & & & add_disk& & & & & &
1. make menuconfig去掉内核自带的NAND FLASH驱动
-& Device Drivers
& -& Memory Technology Device (MTD) support
& & -& NAND Device Support
NAND Flash support for S3C0 SoC
2. make uImage
使用新内核启动, 并且使用NFS作为根文件系统
3. insmod s3c_nand.ko
4. 格式化 (参考下面编译工具)
flash_eraseall& /dev/mtd3& // yaffs
mount -t yaffs /dev/mtdblock3 /mnt
6. 在/mnt目录下建文件&
编译工具:
1. tar xjf mtd-utils-05.07.23.tar.bz2
2. cd mtd-utils-05.07.23/util
修改Makefile:
#CROSS=arm-linux-
CROSS=arm-linux-
4. cp flash_erase flash_eraseall /work/nfs_root/first_fs/bin/&br&&br&&br&
复制代码struct nand_chip {/*8 位NAND 芯片的读写地址*/void __iomem& & *IO_ADDR_R;void __iomem& & *IO_ADDR_W;
uint8_t& & (*read_byte)(struct mtd_info *mtd);u16& & (*read_word)(struct mtd_info *mtd);
void& & (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);void& & (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);/*读取数据并验证*/int& & (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);/*选定芯片*/void& & (*select_chip)(struct mtd_info *mtd, int chip);/*检查坏块*/int& & (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);/*mark(标记)bad block*/int& & (*block_markbad)(struct mtd_info *mtd, loff_t ofs);/*发送命令/地址*/void& & (*cmd_ctrl)(struct mtd_info *mtd, int dat,unsigned int ctrl);/*读取芯片状态*/int& & (*dev_ready)(struct mtd_info *mtd);/*发送命令*/void& & (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);/*等待就绪*/int& & (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);/*擦除命令函数*/void& & (*erase_cmd)(struct mtd_info *mtd, int page);/*扫描坏块表*/int& & (*scan_bbt)(struct mtd_info *mtd);int& & (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);/*高级页面写功能*/int& & (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,const uint8_t *buf, int page, int cached, int raw);
int& & chip_struct nand_ecc_struct nand_buffers *...................};
struct nand_ecc_ctrl {nand_ecc_modes_t& &int& &int& &int& &int& &int& &int& &struct nand_ecclayout& & *
/*hwctl函数:*这个函数用来控制硬件产生ecc *其实它主要的工作就是控制NAND controller 向 NAND 芯片发出NAND_ECC_READ 、NAND_ECC_WRITE 和NAND_ECC_READSYN *等命令,与struct nand_chip 结构体中的cmdfunc 类似*/void& & (*hwctl)(struct mtd_info *mtd, int mode);
/*根据data 计算ecc 值*/int& & (*calculate)(struct mtd_info *mtd,const uint8_t *dat,uint8_t *ecc_code);/*根据ecc 值,判断读写数据时是否有错误发生,若有错,则立即试着纠正,纠正失败则返回错误*/int& & (*correct)(struct mtd_info *mtd, uint8_t *dat,uint8_t *read_ecc,uint8_t *calc_ecc);
/*read_page_raw write_page_raw函数*从NAND 芯片中读取一个page 的原始数据和向NAND 芯片写入一个page 的原始数据,所谓的原始数据,即不对读写的数据做ecc处理 *该读写什么值就读写什么值。另外,这两个函数会读写整个page 中的所有内容,即不但会读写一个page 中MAIN部分,还会读写OOB 部分。*/int& & (*read_page_raw)(struct mtd_info *mtd,struct nand_chip *chip,uint8_t *buf);void& & (*write_page_raw)(struct mtd_info *mtd,struct nand_chip *chip,const uint8_t *buf);
/**read_page 和write_page 在读写过程中会加入ecc 的计算,校验,和纠正等处理。*/int& & (*read_page)(struct mtd_info *mtd,struct nand_chip *chip,uint8_t *buf);void& & (*write_page)(struct mtd_info *mtd,struct nand_chip *chip,const uint8_t *buf);
/**读写oob 中的内容,不包括MAIN 部分。*/int& & (*read_oob)(struct mtd_info *mtd,struct nand_chip *chip,int page,int sndcmd);int& & (*write_oob)(struct mtd_info *mtd,struct nand_chip *chip,int page);};其实,以上提到的这几个read_xxx 和write_xxx 函数,最终都会调用struct nand_chip 中的read_buf 和write_buf 这两个函数,所以如果没有特殊需求的话,我认为不必自己实现,使用MTD 提供的default 的函数即可。
--------------------------------------------------------
#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_static struct s3c_nand_regs *s3c_nand_
static struct mtd_partition s3c_nand_parts[] = {& & [0] = {& & & & .name&
= "bootloader",& & & & .size&
= 0x,& & & & .offset& & = 0,& & },& & [1] = {& & & & .name&
= "params",& & & & .offset = MTDPART_OFS_APPEND,& & & & .size&
= 0x,& & },& & [2] = {& & & & .name&
= "kernel",& & & & .offset = MTDPART_OFS_APPEND,& & & & .size&
= 0x,& & },& & [3] = {& & & & .name&
= "root",& & & & .offset = MTDPART_OFS_APPEND,& & & & .size&
= MTDPART_SIZ_FULL,& & }};
/*判断忙*/static int s3c_dev_ready(struct mtd_info *mtd){& & /*返回"NFSTAT的bit[0]";*/& & return (s3c_nand_regs-&nfstat & (1&&0));}
static void s3c_cmd_ctrl(struct mtd_info *mtd, int dat,unsigned int ctrl){
& & if (ctrl & NAND_CLE)& & {& & & & /* 发命令: NFCMMD=dat */& & & & s3c_nand_regs-&nfcmd =& & & & //writeb(cmd, host-&io_base + (1 && host-&board-&cle));/*命令*/& & }& & else& & {& & & & /* 发地址: NFADDR=dat */& & & & s3c_nand_regs-&nfaddr =& & & & //writeb(cmd, host-&io_base + (1 && host-&board-&ale));/*地址*/& & }}
static void s3c_select_chip(struct mtd_info *mtd, int chip){& & if(chip ==-1)& & {& & & & /*表示取消选中 NFCONT[1]设为1& */& & & & s3c_nand_regs-&nfcont |=(1&&1);& & & & }& & else& & {& & & & s3c_nand_regs-&nfcont &=~(1&&1);& & & & /*选中芯片 NFCONT[1]设为0 */& & }& & }
static int s3c_nand_init(void){
& & struct clk *& & & & /*1.分配一个nand_chip结构体*/& & s3c_nand_chip =& kzalloc(sizeof(struct nand_chip), GFP_KERNEL);& & & & s3c_nand_regs = ioremap(0x4E000000, sizeof(struct s3c_nand_regs));& & & & /*2.设置nandc_chip结构体*/& & s3c_nand_chip-&IO_ADDR_R = &s3c_nand_regs-&& & s3c_nand_chip-&IO_ADDR_W = &s3c_nand_regs-&& & s3c_nand_chip-&cmd_ctrl = s3c_cmd_& & s3c_nand_chip-&dev_ready = s3c_dev_& & s3c_nand_chip-&select_chip = s3c_select_& & s3c_nand_chip-&ecc.mode& & = NAND_ECC_SOFT;& & //s3c_nand_chip-&chip_delay = 20;
& & /* 使能NAND FLASH控制器的时钟 */& & clk = clk_get(NULL, "nand");& & clk_enable(clk);& & & & & & & /* CLKCON'bit[4] */& &
& & /*初始化nand控制器 设置寄存器*/#define TACLS&
0#define TWRPH0& 3#define TWRPH1& 0& & /* 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& &
*/& & s3c_nand_regs-&nfconf=(TACLS&&12)|(TWRPH0&&8)|(TWRPH1&&4);
& & /* NFCONT: & &
* BIT1-设为1, 取消片选 & &
* BIT0-设为1, 使能NAND FLASH控制器& &
* BIT4-设为0, 未初始化hardware ECC& &
*/& & s3c_nand_regs-&nfcont = (1&&1) | (1&&0);& & & & /*分配一个mtd_info结构体*/& & s3c_mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL);
& & /*设置s3c_mtd结构体*/& & s3c_mtd-&owner = THIS_MODULE;& & s3c_mtd-&priv = s3c_nand_
& & /* 识别NAND FLASH, 构造mtd_info */& & nand_scan(s3c_mtd, 1);
& & /*设置分区表*/& & add_mtd_partitions(s3c_mtd, s3c_nand_parts, 4);& & & & add_mtd_device(s3c_mtd);& & return 0;}
static void s3c_nand_exit(void){& & kfree(s3c_mtd);& & kfree(s3c_nand_chip);& & iounmap(s3c_nand_regs);}
module_init(s3c_nand_init);module_exit(s3c_nand_exit);MODULE_LICENSE("GPL");
本文永久更新链接地址:
相关资讯 & & &
& (01月29日)
& (06/17/:49)
& (06/17/:48)
& (10/16/:55)
& (06/17/:56)
& (03/30/:28)
   同意评论声明
   发表
尊重网上道德,遵守中华人民共和国的各项有关法律法规
承担一切因您的行为而直接或间接导致的民事或刑事法律责任
本站管理人员有权保留或删除其管辖留言中的任意内容
本站有权在网站内转载或引用您的评论
参与本评论即表明您已经阅读并接受上述条款

我要回帖

更多关于 linux spi nand flash 的文章

 

随机推荐