打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
【详解三】Nand Flash 编写驱动之前要了解的知识

1.2.16. Nand Flash的一些高级特性

1.2.16.1. Nand Flash的Unique ID

1.2.16.1.1. 什么是Unique ID唯一性标识

Unique ID,翻译为中文就是,独一无二的ID,唯一性标识。

很明显,这个Unique ID是为了用来识别某些东西的,每一个东西都拥有一个独一无二的标识信息。

在Nand Flash里面的Unique ID,主要是某个ID信息,保证每个Nand Flash都是独一无二的。主要用于其它的使用Nand Flash的用户,根据此unique id去做加密等应用,实现某些安全方面的应用。

简而言之,就是用Nand Flash的Unique ID来实现安全相关的应用,比如加密,版权保护等等。

1.2.16.1.2. 不同Nand Flash厂商的对Unique ID的不同的实现方法

此处,继续解释之前,还要再次赘述一下:

目前Nand Flash的厂家有samsung,Toshiba,Intel, Hynix,Micron,Numonyx,Phison ,SanDisk,Sony,Spansion等。

由于前面所说的Nand Flash的规范之争,即Toshiba & Samsung和Intel + 其它厂商(Hynix,Micron,Numonyx,Phison ,SanDisk,Sony,Spansion等)之争,导致对于Unique ID这么个小的功能点(feature),不同的方面,弄出了不同的实现(做法)。

下面就来解释一下各个厂家关于Unique ID的实现方法,以及如何读取对应的Unique ID:

1.2.16.1.2.1. Toshiba东芝的Nand的Unique ID

网上找到一个datasheet:

Toshiba TH58NS512DC

http://datasheet.elcodis.com/pdf/11/23/112371/th58ns512dc-to51y.pdf

中有提到:

P1:“The TH58NS512DC is a SmartMediaTM with ID and each device has 128 bit unique ID number embedded in the device. This unique ID number is applicable to image files, music files, electronic books, and so on where copyright protection is required.”即每个Toshiba的TH58NS512DC中,都有一个128 bit=16 byte的Unique ID,可用于图片,音乐,电子书等应用中的版权保护。

P24:

图 1.9. Toshiba的Unique ID

1.2.16.1.2.2. 读取Toshiba的Nand的Unique ID

从上面可以看出,Toshiba的Nand中,关于Unique ID,是需要先通过普通的0x90,即Read ID的命令,去读取Nand的ID,找到第三个字节(3rd byte),然后判断其是否是0xA5,如果是0xA5,然后才能确定此Nand里面是有Unique ID的,然后才有去读取Unique ID这一说。

而关于如何独缺Unique ID,则需要和自己去联系Toshiba,和其签订NDA协议后,才可得知读取Nand的Unique ID的方法。

1.2.16.1.3. Samsung三星的Nand的Unique ID

网上找到的某款三星的Nand的datasheet:

Samsung K9F5608U0B

http://hitmen.c02.at/files/docs/psp/ds_k9f5608u0b_rev13.pdf

6. Unique ID for Copyright Protection is available

- The device includes one block sized OTP (One Time Programmable), which can be used to increase system security or to provide identification capabilities. Detailed information can be obtained by contact with Samsung

即,Samsung的Nand的Unique ID,也和Toshiba的用途类似,也主要是用于版权保护,但是其实现却不同。

Samsung的Unique ID的实现,是专门在Nand 里面配备了一个OTP的Block,而此Nand芯片的Block大小是16KB。

而关于如何操作此OTP,即如何写入数据和读取数据,此处未说明。

欲知详情,请联系三星。

不过个人理解,应该和普通的block的操作类似,即普通的block,包含很多page,每个page的操作,有对应的page read,用对应的page read命令来读取此特殊的OTP的block里面的数据。

而此OTP的block里面的数据是什么,完全取决于自己最开始往里面写入了什么数据。说白了就是,你根据自己需求,在你的产品出厂的时候,写入对应的数据,比如该款产品的SN序列号等数据,然后自己在用page read读取出相应数据后,自己解析,得到自己要的信息,用于自己的用途,比如版权保护等。

1.2.16.1.3.1. 读取Samsung的Nand的Unique ID

如前所述:

关于如何操作此OTP的block,即如何写入数据和读取数据,此处未说明。

即想要知道如何读取Samsung的Nand的Unique ID,请自己去问三星。

1.2.16.1.4. 遵循ONFI规范的厂商的Nand的Unique ID

主要指的是Intel英特尔,Hynix海力士,Micron美光,Numonyx恒亿,Spansion飞索等公司。

对应的Nand 的Unique ID的相关定义,ONFI的规范中都有,现简要摘录如下:

ONFI 2.2

http://onfi.org/wp-content/uploads/2009/02/ONFI%202_2%20Gold.pdf

ONFI规范中,在“5.7.1. Parameter Page Data Structure Definition”中,如图:

图 1.10. ONFI的参数页数据结构定义

定义了一个page的数据,用于存储对应的Nand的各种参数,其中,有一个第8个字节的bit5==1的时候,表示支持“Read Unique ID”的命令,即说明此Nand芯片支持此命令,如果byte8的bit5==0,那么说明不支持,也就没法去读Unique ID了。

1.2.16.1.4.1. 读取遵循ONFI的厂商的Nand的Unique ID

如果经过上述判断,此符合ONFI的Nand Flash支持Read Unique ID命令,次此时就可以通过该命令来读取对应的Nand Flash的Unique ID了。

此Read Unique ID的详细解释为:

“ 5.8. Read Unique ID Definition

The Read Unique ID function is used to retrieve the 16 byte unique ID (UID) for the device. The unique ID when combined with the device manufacturer shall be unique.

The UID data may be stored within the Flash array. To allow the host to determine if the UID is without bit errors, the UID is returned with its complement, as shown in Table 47. If the XOR of the UID and its bit-wise complement is all ones, then the UID is valid.

即用Read Unique ID命令来读取128bit=16字节的Unique ID,但是呢,为了用于防止写入的Unique ID有误,因此在16字节后面又添了个对应的补码,即每位取反的结果,这样前16字节的Unique ID和后16字节的Unique ID的补码,构成了32字节,算作一组,如下图所示:

图 1.11. ONFI中Unique ID的结构


To accommodate robust retrieval of the UID in the case of bit errors, sixteen copies of the UID and the corresponding complement shall be stored by the target. For example, reading bytes 32-63 returns to the host another copy of the UID and its complement. Read Status Enhanced shall not be used during execution of the Read Unique ID command

Figure 57 defines the Read Unique ID behavior. The host may use any timing mode supported by the target in order to retrieve the UID data.

而为了进一步防止出错,将上面32字节算一组,重复了16次,将这16个32字节的数据,存在Nand Flash里面,然后用Read Unique ID命令去读取出来,取得其中某个32字节即可,然后判断前16字节和后16字节取反,如果结果所有位都是1,那么结果即为16个0xFF,那么说明Unique ID是正确的。否则说明有误。

Read Unique ID的命令的详细格式如下图所示:

图 1.12. ONFI中Read Unique ID命令的时序图

即先发送0xED命令,再发送0x00地址,然后等待Nand Flash的busy状态结束,就可以读取出来的那16组的32个字节了,然后用上面办法去判断,找到前16字节和后16字节异或得到结果全部16字节都为0xFF,即说明得到了正确的Unique ID。至此,符合ONFI规范的Unique ID,就读取出来了。

1.2.16.2. 片选无关(CE don’t-care)技术

很多Nand flash支持一个叫做CE don’t-care的技术,字面意思就是,不关心是否片选。

对此也许有人会问了,如果不片选,那还能对其操作吗?答案就是,这个技术,主要用在当时是不需要选中芯片,但是芯片内部却仍可以继续操作的这些情况:在某些应用,比如录音,音频播放等应用中,外部使用的微秒(us)级的时钟周期,此处假设是比较少的2us,在进行读取一页或者对页编程时,是对Nand Flash操作,这样的串行(Serial Access)访问的周期都是20/30/50ns,都是纳秒(ns)级的,此处假设是50ns,当你已经发了对应的读或写的命令之后,接下来只是需要Nand Flash内部去自己操作,将数据读取除了或写入进去到内部的数据寄存器中而已,此处,如果可以把片选取消,CE#是低电平有效,取消片选就是拉高电平,这样会在下一个外部命令发送过来之前,即微秒量级的时间里面,即2us-50ns≈2us,这段时间的取消片选,可以降低很少的系统功耗,但是多次的操作,就可以在很大程度上降低整体的功耗了。

总的来说就是:由于某些外部应用所需要的访问Nand Flash的频率比较低,而Nand Flash内部操作速度比较快,所以在针对Nand Flash的读或写操作的大部分时间里面,都是在等待外部命令的输入,同时却选中芯片,产生了多余的功耗,此“不关心片选”技术,就是在Nand Flash的内部的相对快速的操作(读或写)完成之后,就取消片选,以节省系统功耗。待下次外部命令/数据/地址输入来的时候,再选中芯片,即可正常继续操作了。这样,整体上,就可以大大降低系统功耗了。

提示
  1. 如果想要操作硬件Nand Flash芯片,先要将对应的CE#(低有效)片选信号拉低,选中该芯片,然后才能做接下来的读写操作所要做的发命令,发数据等动作。

  2. Nand Flash的片选与否,功耗差别会有很大。如果数据没有记错的话,我之前遇到我们系统里面的Nand Flash的片选,大概有5个mA的电流输出呢,也许你对5mA没太多概念,给你说个数据你就知道了:当时为了针对MP3播放功耗进行优化,整个系统优化之后的待机功耗,也才10个mA左右的,所以节省5mA已经算是很不错的功耗优化了。

1.2.16.3. 带EDC的拷回操作以及Sector的定义(Copy-Back Operation with EDC & Sector Definition for EDC)

Copy-Back功能,简单的说就是,将一个页的数据,拷贝到另一个页。

如果没有Copy-Back功能,那么正常的做法就是,先要将那个页的数据拷贝出来放到内存的数据buffer中,读出来之后,再用写命令将这页的数据,写到新的页里面。

而Copy-Back功能的好处在于,不需要用到外部的存储空间,不需要读出来放到外部的buffer里面,而是可以直接读取数据到内部的页寄存器(page register)然后写到新的页里面去。

而且,为了保证数据的正确,要硬件支持EDC(Error Detection Code)的,否则,在数据的拷贝过程中,可能会出现错误,并且拷贝次数多了,可能会累积更多错误。

而对于错误检测来说,硬件一般支持的是512字节数据,对应有16字节用来存放校验产生的ECC数值,而这512字节一般叫做一个扇区。对于2K+64字节大小的页来说,按照512字节分,分别叫做A,B,C,D区,而后面的64字节的oob区域,按照16字节一个区,分别叫做E,F,G,H区,对应存放A,B,C,D数据区的ECC的值。

Copy-Back编程的主要作用在于,去掉了数据串行读取出来,再串行写入进去的时间,所以,而这部分操作,是比较耗时的,所以此技术可以提高编程效率,提高系统整体性能。

1.2.16.4. 多片同时编程(Simultaneously Program Multi Plane)

对于有些新出的Nand Flash,支持同时对多个片进行编程,比如上面提到的三星的K9K8G08U0A,内部包含4片(Plane),分别叫做Plane0,Plane1,Plane2,Plane3。.由于硬件上,对于每一个Plane,都有对应的大小是2048+64=2112字节的页寄存器(Page Register),使得同时支持多个Plane编程成为可能。 K9K8G08U0A支持同时对2个Plane进行编程。

不过要注意的是,只能对Plane0和Plane1或者Plane2和Plane3,同时编程,而不支持Plane0和Plane2同时编程。

1.2.16.5. 交错页编程(Interleave Page Program)

多片同时编程,是针对一个chip里面的多个Plane来说的,

而此处的交错页编程,是指对多个chip而言的。

可以先对一个chip,假设叫chip1,里面的一页进行编程,然后此时,chip1内部就开始将数据一点点写到页里面,就出于忙的状态了,而此时可以利用这个时间,对出于就绪状态的chip2,也进行页编程,发送对应的命令后,chip2内部也就开始慢慢的写数据到存储单元里面去了,也出于忙的状态了。此时,再去检查chip1,如果编程完成了,就可以开始下一页的编程了,然后发完命令后,就让其内部慢慢的编程吧,再去检查chip2,如果也是编程完了,也就可以进行接下来的其他页的编程了。如此,交互操作chip1和chip2,就可以有效地利用时间,使得整体编程效率提高近2倍,大大提高Nand Flash的编程/擦写速度了。

1.2.16.6. 随机输出页内数据(Random Data Output In a Page)

在介绍此特性之前,先要说说,与Random Data Output In a Page相对应的是,普通的,正常的,sequential data output in a page。

正常情况下,我们读取数据,都是先发读命令,然后等待数据从存储单元到内部的页数据寄存器中后,我们通过不断地将RE#(Read Enale,低电平有效)置低,然后从我们开始传入的列的起始地址,一点点读出我们要的数据,直到页的末尾,当然有可能还没到页地址的末尾,就不再读了。所谓的顺序(sequential)读取也就是,根据你之前发送的列地址的起始地址开始,每读一个字节的数据出来,内部的数据指针就加1,移到下个字节的地址,然后你再读下一个字节数据,就可以读出来你要的数据了,直到读取全部的数据出来为止。

而此处的随机(random)读取,就是在你正常的顺序读取的过程中,先发一个随机读取的开始命令0x05命令,再传入你要将内部那个数据指针定位到具体什么地址,也就是2个cycle的列地址,然后再发随机读取结束命令0xE0,然后,内部那个数据地址指针,就会移动到你所制定的位置了,你接下来再读取的数据,就是从那个制定地址开始的数据了。

而Nand Flash数据手册里面也说了,这样的随机读取,你可以多次操作,没限制的。

请注意,上面你所传入的地址,都是列地址,也就是页内陆址,也就是说,对于页大小为2K的Nand Flash来说,所传入的地址,应该是小于2048+64=2112的。

不过,实际在Nand Flash的使用中,好像这种用法很少的。绝大多数,都是顺序读取数据。

1.3. 软件方面

如果想要在Linux下编写Nand Flash驱动,那么就先要搞清楚Linux下,关于此部分的整个框架。弄明白,系统是如何管理你的Nand Flash的,以及,系统都帮你做了那些准备工作,而剩下的,驱动底层实现部分,你要去实现哪些功能,才能使得硬件正常工作起来。

1.3.1. Nand Flash相关规范 – ONFI和LBA

在介绍Nand Flash的软件细节方面之前,先来介绍一下Nand Flash的两个相关的规范:ONFI和LBA。

1.3.1.1. ONFI是什么

ONFI规范,即Open Nand Flash Interface specification。

ONFI是Intel主导的,其他一些厂家(Hynix,Micron,Numonyx,Phison ,SanDisk,Sony,Spansion等)参与制定的,统一了Nand Flash的操作接口。

所谓操作接口,就是那些对Nand Flash操作的命令等内容。

而所谓统一,意思是之前那些Nand Flash的操作命令等,都是各自为政,虽然大多数常见的Nand Flash的操作,比如page read的命令是0x00,0x30,page write的命令是0x80,0x10等,但是有些命令相关的内容,很特别且很重要的一个例子就是,每个厂家的Nand Flash的read id的命令,虽然都是0x90,但是读取出来的几个字节的含义,每个厂家定义的都不太一样。

因此,才有统一Nand Flash的操作接口这一说。

ONFI规范,官网可以下载的到:

http://onfi.org/specifications/

比如:

ONFI 2.2 Spec

http://onfi.org/wp-content/uploads/2009/02/ONFI%202_2%20Gold.pdf

ONFI规范中定义的Nand Flash的命令集合为:

图 1.13. ONFI中的Nand Flash的命令集合

可以看到,其中常见的一些命令,比如

  1. page read(0x00,0x30)

  2. page write(0x80,0x10)

  3. block erase(0x60,0xD0)

  4. Reset(0xFF)

等等命令,都是和普通的Nand Flash的命令是一样的,而额外多出一些命令,比如Read Unique ID(0xED)等命令,是之前某些Nand Flash命令所不具有的。

如此,定义了Nand Flash的操作的命令的集合以及发送对应命令所遵循的时序等内容。

1.3.1.1.1. ONFI Block Abstracted NAND

ONFI还定义了另外一个规范:

ONFI Block Abstracted Nand Specification

http://onfi.org/wp-content/uploads/2009/02/BA_NAND_rev_1_1_Gold.pdf

即ONFI LBA Nand,简单说就是,逻辑块寻址的Nand。其含义和Toshiba的LBA,基本没有太多区别。

1.3.1.1.2. ONFI的好处

ONFI规范定义了之后,每家厂商的Nand Flash,只要符合这个ONFI规范,然后上层Nand Flash的软件,就可以统一只用一种了,换句话说,我写了一份Nand Flash的驱动后,就可以操作所有和ONFI兼容的Nand Flash了,整个Nand Flash的兼容性,上层软件的兼容性,互操作性,就大大提高了。

而且,同样的,由于任何规范在定义的时候,都会考虑到兼容性和扩展性,ONFI也不例外。针对符合ONFI规范的,写好的软件,除了可以操作多家与ONFI兼容的Nand Flash之外,而对于以后出现的新的技术,新制程的Nand Flash,只要符合ONFI规范,也同样可以支持,可以在旧的软件下工作,而不需要由于Nand Flash的更新换代,而更改上层软件和驱动,这个优势,由于对于将Nand Flash芯片集成到自己系统中的相关开发人员来说,是个好消息。

1.3.1.2. LBA规范是什么

LBA Nand Flash,Logical Block Address,逻辑块寻址的Nand Flash,是Nand Flash大厂之一的Toshiba,自己独立设计出来的新一代的Nand Flash的规范。

之所以叫做逻辑块寻址,是相对于之前常见的,普通的Nand Flash的物理块的寻址来说的。常见的Nand Flash,如果要读取和写入数据,所用的对应的地址是对应的:block地址+block内的Page地址+Page内的偏移量 = 绝对的物理地址,

此物理块寻址,相对来说有个缺点,那就是,由于之前提到的Nand Flash会出现使用过程中出现坏块,所以,遇到这样的坏块,首先坏块管理要去将此坏块标记,然后将坏块的数据拷贝到另一个好的block中,再继续访问新的block。

而且数据读写过程中,还要有对应的ECC校验,很多情况下,也都是软件来实现这部分的工作,即使是硬件的ECC校验,也要写少量的软件,去操作对应寄存器,读取ECC校验的结果,当然别忘了,还有对应的负载平衡等工作。

如此的这类的坏块管理工作,对于软件来说,很是繁重,而且整个系统实现起来也不是很容易,所以,才催生了一个想法,是否可以把ECC校验,负载平衡,坏块管理,全部都放到硬件实现上,而对于软件来说,我都不关心,只关心有多少个Block供我使用,用于数据读写。

针对于此需求,Toshiba推出了LBA逻辑块寻址的Nand Flash,在Nand Flash存储芯片之外,加了对应一个硬件控制权Controller,实现了上述的坏块管理,ECC校验,负载平衡等工作,这样使得人家想要用你LBA的Nand Flash的人,去开发对应的软件来驱动LBA Nand Flash工作,相对要做的事情,就少了很多,相对来说就是减轻了软件系统集成方面的工作,提高了开发效率,缩短了产品上市周期。

LBA Nand,最早放出对应的样片(sample)是在2006年8月。

网上找到一个LBA Nand Flash的简介:

http://www.toshiba-components.com/prpdf/5678E.pdf

现早已经量产,偶在之前开发过程中,就用过其某款LBA的Nand Flash。

目前网上还找不到免费的LBA的规范。除非你搞开发,和Toshiba签订NDA协议后,才可以拿到对应的specification。

关于Toshiba LBA Nand规范,在此多说一点(参考附录中:lba-core.c):

LBA Nand分为PNP,VFP和MDP三种分区:

  1. PNP主要用于存放Uboot等启动代码

  2. VFP主要用于存放uImage等内核代码

  3. MDP主要用于存放用户的数据,以及rootfs等内容

1.3.1.3. 为何会有ONFI和LBA

在解释为何会有ONFI和LBA之前,先来个背景介绍:

目前Nand Flash的厂家有Samsung,Toshiba,Intel, Hynix,Micron,Numonyx,Phison ,SanDisk,Sony,Spansion等。

1.3.1.3.1. 技术层面的解释

ONFI的出现,上面已经解释过了,就是为了统一Nand Flash的接口,使得软件兼容性更好;

而LBA的出现,是为了减轻软件方面对Nand Flash的各种管理工作。上面这些解释,其实只是技术上解释。

1.3.1.3.2. 现实层面的解释

而现实方面是,ONFI是Intel主导的,其他一些Nand Flash厂商参与制定出来的一套Nand Flash的规范,但是却没有得到Nand Flash的两个大厂家的认可,一个是以第一厂商自居samsung,另一个是Nand Flash技术引导者的Toshiba。所以,可以算是在Nand Flash领域里,老三带着一帮小的,定了一个规范,但是老大和老二却不买账。因此,技术上的Nand的老大Toshiba联手产量上的老大,自己去推出了另外一套规范LBA。

这可以称得上是典型的规范之争吧。

1.3.1.4. ONFI和LBA的区别和联系

总的来说,ONFI在对于旧的Nand Flash的兼容上,都是相对类似的。

1.3.1.4.1. ONFI和LBA的区别

总的来说:

ONFI规范,更注重对于Nand Flash的操作接口方面的定义 + ONFI LBA Nand的定义,而Toshiba LBA规范,主要侧重于LBA的Nand的定义。

1.3.1.4.2. ONFI和LBA的联系

ONFI Block Abstracted NAND Specification,基本上和Toshiba的LBA,没太多区别,只是Toshiba的LBA规范中,又多了些其他模式和应用类别。

总的来说,可以这么划分:

ONFI = Nand Flash操作接口的统一 + ONFI的LBA Nand

Toshiba LBA = 等价于ONFI的LBA Nand + 多种模式和对应的不同应用

1.3.2. 内存技术设备,MTD(Memory Technology Device)

MTD,是Linux的存储设备中的一个子系统。其设计此系统的目的是,对于内存类的设备,提供一个抽象层,一个接口,使得对于硬件驱动设计者来说,可以尽量少的去关心存储格式,比如FTL,FFS2等,而只需要去提供最简单的底层硬件设备的读/写/擦除函数就可以了。而对于数据对于上层使用者来说是如何表示的,硬件驱动设计者可以不关心,而MTD存储设备子系统都帮你做好了。

对于MTD子系统的好处,简单解释就是,他帮助你实现了,很多对于以前或者其他系统来说,本来也是你驱动设计者要去实现的很多功能。换句话说,有了MTD,使得你设计Nand Flash的驱动,所要做的事情,要少很多很多,因为大部分工作,都由MTD帮你做好了。

当然,这个好处的一个“副作用”就是,使得我们不了解的人去理解整个Linux驱动架构,以及MTD,变得更加复杂。但是,总的说,觉得是利远远大于弊,否则,就不仅需要你理解,而且还是做更多的工作,实现更多的功能了。

此外,还有一个重要的原因,那就是,前面提到的Nand Flash和普通硬盘等设备的特殊性:

有限的通过出复用来实现输入输出命令和地址/数据等的IO接口,最小单位是页而不是常见的bit,写前需擦除等,导致了这类设备,不能像平常对待硬盘等操作一样去操作,只能采取一些特殊方法,这就诞生了MTD设备的统一抽象层。

MTD,将Nand Flash,nor flash和其他类型的flash等设备,统一抽象成MTD设备来管理,根据这些设备的特点,上层实现了常见的操作函数封装,底层具体的内部实现,就需要驱动设计者自己来实现了。具体的内部硬件设备的读/写/擦除函数,那就是你必须实现的了。

表 1.4. MTD设备和硬盘设备之间的区别

HARD drivesMTD device
连续的扇区连续的可擦除块
扇区都很小(512B,1024B)可擦除块比较大 (32KB,128KB)
主要通过两个操作对其维护操作:读扇区,写扇区主要通过三个操作对其维护操作:从擦除块中读,写入擦除块,擦写可擦除块
坏快被重新映射,并且被硬件隐藏起来了(至少是在如今常见的LBA硬盘设备中是如此)坏的可擦除块没有被隐藏,软件中要处理对应的坏块问题
HDD扇区没有擦写寿命超出的问题可擦除块是有擦除次数限制的,大概是104-105次

多说一句,关于MTD更多的内容,感兴趣的,去附录中的MTD主页去看。

关于mtd设备驱动,感兴趣的可以去参考附录中MTD设备的文章,该文章是比较详细地介绍了整个MTD框架和流程,方便大家理解整个mtd框架和Nand Flash驱动。

1.3.2.1. Linux MTD中检测不同类型Nand Flash的ID部分的代码

关于nand flash,由于各个厂家的read id读出的内容的定义,都不同,导致,对于读出的id,分别要用不同的解析方法,下面这段代码,是我之前写的,本来打算自己写信去推荐到Linux MTD内核源码的,不过后来由于没搞懂具体申请流程,就放弃了。不过,后来,看到Linux的MTD部分更新了,加了和下面类似的做法。

此处只是为了记录下来,也算给感兴趣的人一个参考吧。

文件:\linux-2.6.28.4\drivers\mtd\nand\nand_base.c

/*
 * Get the flash and manufacturer id and lookup if the type is supported
 */
static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
                          struct nand_chip *chip,
                          int busw, int *maf_id)
{
... ...
    chip->chipsize = (uint64_t)type->chipsize << 20;

    /* 针对不同的MLC和SLC的nand flash,添加了不同的解析其ID的方法 */
    /* Newer devices have all the information in additional id bytes */
    if (!type->pagesize) {
        int erase_bits, page_base, block_base, old_50nm, new_40nm;
        uint8_t id3rd, id4th, id5th, id6th, id7th;

        /* The 3rd id byte holds MLC / multichip data */
        chip->cellinfo = id3rd = chip->read_byte(mtd);
        /* The 4th id byte is the important one */
        id4th = chip->read_byte(mtd);
        id5th = chip->read_byte(mtd);
        id6th = chip->read_byte(mtd);
        id7th = chip->read_byte(mtd);
        /* printk(KERN_INFO " (ID:%02x %02x %02x %02x %02x %02x %02x) ",
            id1st, id2nd, id3rd, id4th, id5th, id6th, id7th); */

        if (nand_is_mlc(chip->cellinfo)) {
            /*
             * MLC:
             * 50nm has 5 bytes ID, further read ID will periodically output
             * 40nm has 6 bytes ID
             */

            /*
             * the 4th byte is not the same meaning for different manufature
            */
            if (NAND_MFR_SAMSUNG == *maf_id) {
                /* samsung MLC chip has several type ID meanings:
                (1)50nm serials, such as K9GAG08U0M
                (2)40nm serials, such as K9LBG08UXD
                */

                /* old 50nm chip will periodically output if read further ID */
                old_50nm = (id1st == id6th) && (id2nd == id7th);
                /* is 40nm or newer */
                new_40nm = id6th & 0x07;
                if ((!old_50nm) && new_40nm) {
                    /*
                     * Samsang
                     * follow algorithm accordding to datasheets of:
                     * K9LBG08UXD_1.3 (40nm), 
                     * ID(hex): EC D7 D5 29 38 41
                     * this algorithm is suitable for new chip than 50nm
                     * such as K9GAG08u0D, 
                     * ID(hex): EC D5 94 29 B4 41
                     */

                    int bit236;

                    /* Calc pagesize, bit0,bit1: page size */
                    page_base = (1 << 11);  /* 2KB */
                    mtd->writesize = page_base * (1 << (id4th & BIT01));
                    block_base = (1 << 17); /* 128 KB */
                    /* Calc block size, bit4,bit5,bit7: block size */
                    erase_bits = (id4th >> 4) & BIT01; /* get bit4,bit5 */
                    erase_bits |= (id4th >> 5) & BIT(2); /* get bit7 and combine them */
                    mtd->erasesize = block_base * (1 << erase_bits);
                    /* Calc oobsize, bit2,bit3,bit6: oob size */
                    bit236 = (id4th >> 2) & BIT01; /* get bit2,bit3 */
                    bit236 |= (id4th >> 4) & BIT(2); /* get bit6 and combine them */
                    switch (bit236) {
                    case 0x01:
                        mtd->oobsize = 128;
                        break;
                    case 0x02:
                        mtd->oobsize = 218;
                        break;
                    default:
                        /* others reserved */
                        break;
                    }
                }
                else {
                    /*
                     * Samsang
                     * follow algorithm accordding to datasheets of:
                     * K9GAG08U0M (50nm)
                     * this algorithm is suitable for old 50nm chip
                     */

                    goto slc_algorithm;
                }
            }
            else if (NAND_MFR_TOSHIBA == *maf_id) {
                /*
                 * Toshiba
                 * follow algorithm guess from ID of TC58NVG3D1DTG00:
                 * Toshiba MLC TC58NVG3D1DTG00 1GB 8bit 1chip
                 * 4K+218 512K+27K 3.3V, (ID:98 D3 94 BA 64 13 42)
                 */
                int bit23;

                /* Calc pagesize, bit0,bit1: page size */
                page_base = (1 << 10);  /* 1KB */
                mtd->writesize = page_base * (1 << (id4th & BIT01));
                block_base = (1 << 16); /* 64 KB */
                /* Calc block size, bit4,bit5: block size */
                erase_bits = (id4th >> 4) & BIT01; /* get bit4,bit5 */
                mtd->erasesize = block_base * (1 << erase_bits);
                /* Calc oobsize, use spare/redundant area bit */
                bit23 = (id4th >> 2) & BIT01; /* get bit2,bit3 */
                switch (bit23) {
                case 0x01:
                    mtd->oobsize = 128;
                    break;
                case 0x02:
                    mtd->oobsize = 218;
                    break;
                default:
                    /* others reserved */
                    break;
                }
                /* Get buswidth information: x8 or x16 */
                busw = ((id4th >> 6) & BIT(0)) ? NAND_BUSWIDTH_16 : 0;
            }
            else if (NAND_MFR_MICRON == *maf_id) {
                /*
                 * Micron
                 * follow algorithm accordding to datasheets of:
                 * 29F32G08CBAAA
                 */
                int spare_area_size_bit;

                /* Calc pagesize, bit0,bit1: page size */
                page_base = (1 << 10);  /* 1KB */
                mtd->writesize = page_base * (1 << (id4th & 0x03));
                block_base = (1 << 16); /* 64 KB */
                /* Calc block size, bit4,bit5: block size */
                erase_bits = (id4th >> 4) & BIT01; /* get bit4,bit5 */
                mtd->erasesize = block_base * (1 << erase_bits);
                /* Calc oobsize, use spare/redundant area bit */
                spare_area_size_bit = (id4th >> 2) & BIT(0);
                if (spare_area_size_bit) /* special oob */
                    mtd->oobsize = 218;
                else /* normal */
                    mtd->oobsize = mtd->writesize / NAND_PAGE_OOB_RATIO;
                /* Get buswidth information: x8 or x16 */
                busw = ((id4th >> 6) & BIT(0)) ? NAND_BUSWIDTH_16 : 0;
            }
            else {
                /*
                 * Others
                 * FIXME: update follow algrithm,
                 * according to different manufacture's chip's datasheet
                 */

                goto slc_algorithm;
            }       
        }
        else {
            /*
             * SLC, only has 4 bytes ID, further read will output periodically, such as:
             * Hynix : HY27UG084G2M, only has 4 byte ID,
             * following read ID is periodically same as the 1st ~ 4th byte,
             * for HY27UG084G2M is : 0xAD 0xDC 0x80 0x15 0xAD 0xDC 0x80 0x15 ..... 
            */
slc_algorithm:
            /* Calc pagesize, bit0,bit1: page size */
            page_base = (1 << 10);  /* 1KB */
            mtd->writesize = page_base * (1 << (id4th & BIT01));
            block_base = (1 << 16); /* 64 KB */
            /* Calc block size, bit4,bit5: block size */
            erase_bits = (id4th >> 4) & BIT01; /* get bit4,bit5 */
            mtd->erasesize = block_base * (1 << erase_bits);
            /* Calc oobsize, use fixed ratio */
            mtd->oobsize = mtd->writesize / NAND_PAGE_OOB_RATIO;
            /* Get buswidth information: x8 or x16 */
            busw = ((id4th >> 6) & BIT(0)) ? NAND_BUSWIDTH_16 : 0;
        }
    } else {
        /*
         * Old devices have chip data hardcoded in the device id table
         */
        mtd->erasesize = type->erasesize;
        mtd->writesize = type->pagesize;
        mtd->oobsize = mtd->writesize / NAND_PAGE_OOB_RATIO;
        busw = type->options & NAND_BUSWIDTH_16;
    }
    
    /*
    * 以上内容,主要是更加不同厂家的nand flash的datasheet,一点点总结出来的算法。
    * 最新的Linux的MTD部分,已经添加了类似如上部分的代码。此处贴出来,仅供参考。
    */
    
    /*
     * Check, if buswidth is correct. Hardware drivers should set
     * chip correct !
     */
    if (busw != (chip->options & NAND_BUSWIDTH_16)) {
        printk(KERN_INFO "NAND device: Manufacturer ID:"
               " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,
               dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
        printk(KERN_WARNING "NAND bus width %d instead %d bit\n",
               (chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
               busw ? 16 : 8);
        return ERR_PTR(-EINVAL);
    }
... ...
}
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
MTD(3)-- nandflash的erase,read,write接口函数代码分析
【简介】如何编写linux下nand flash驱动
【已解决】Uboot中nand erase出错:NAND 256MiB 3,3V 8
嵌入式测试手册——基于NXP iMX6ULL开发板(2)
NAND FLASH学习笔记之nand flash基础(一)
为什么Micron的NAND Flash能达到200MB/s的读取速度
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服