您的位置:首页 > 运维架构 > Linux

如何编写linux下nand flash驱动

2010-04-30 11:49 246 查看
http://usr.cc/html/99/n-599.html

如何编写linux下nand flash驱动-1

发布时间: 2009-7-31 11:19 |
发布作者: dean |
|
查看: 59次



1.


硬件特性:



[b]Flash


[/b]
的硬件实现机制】


Flash

全名叫做
Flash Memory

,属于非易失性存储设备

(Non-volatile Memory Device)

,与此相对应的是易失性存储设备
(Volatile Memory Device)

。关于什么是非易失性
/

易失性,从名字中就可以看出,非易失性就是不容易丢失,数据存储在这类设备中,即使断电了,也不会丢失,这类设备,除了
Flash

,还有其他比较常见的入硬盘,
ROM

等,与此相对的,易失性就是断电了,数据就丢失了,比如大家常用的内存

,不论是以前的
SDRAM


DDR SDRAM

,还是现在的
DDR2


DDR3

等,都是断电后,数据就没了。

Flash

的内部存储是
MOSFET

,里面有个悬浮门
(Floating Gate)

,是真正存储数据的单元。


Flash

之前,紫外线可擦除
(uv-erasable)


EPROM

,就已经采用用
Floating Gate

存储数据这一技术

了。





1

.

典型的


Flash

内存单元的物理结构


数据在
Flash

内存单元中是以电荷
(electrical charge)

形式存储的。存储电荷的多少,取决于图中的外部门(
external gate

)所被施加的电压,其控制了是向存储单元中冲入电荷还是使其释放电荷。而数据的表示,以所存储的电荷的电压是否超过一个特定的阈值
Vth

来表示。


SLC



MLC


的实现机制】


Nand

Flash

按照内部存储数据单元的电压的不同层次,也就是单个内存单元中,是存储
1

位数据,还是多位数据,可以分为
SLC


MLC



1.


SLC



Single Level Cell:



单个存储单元,只存储一位数据,表示成
1


0.

就是上面介绍的,对于数据的表示,单个存储单元中内部所存储电荷的电压,和某个特定的阈值电压
Vth

,相比,如果大于此
Vth

值,就是表示
1

,反之,小于
Vth

,就表示
0.

对于
nand Flash

的数据的写入
1

,就是控制
External Gate

去充电,使得存储的电荷够多,超过阈值
Vth

,就表示
1

了。而对于写入
0

,就是将其放电,电荷减少到小于
Vth

,就表示
0

了。

关于为何
Nand Flash

不能从
0

变成
1

,我的理解是,物理上来说,是可以实现每一位的,从
0

变成
1

的,但是实际上,对于实际的物理实现,出于效率的考虑,如果对于,每一个存储单元都能单独控制,即,
0

变成
1

就是,对每一个存储单元单独去充电,所需要的硬件实现就很复杂和昂贵,同时,所进行对块擦除的操作,也就无法实现之前的,一闪而过的速度了,也就失去了
Flash

的众多特性了。

2.


MLC



Multi Level Cell






SLC

相对应,就是单个存储单元,可以存储多个位,比如
2

位,
4

位等。其实现机制,说起来比较简单,就是,通过控制内部电荷的多少,分成多个阈值,通过控制里面的电荷多少,而达到我们所需要的存储成不同的数据。比如,假设输入电压是
Vin


4V

(实际没有这样的电压,此处只是为了举例方便),那么,可以设计


2


2

次方=
4

个阈值,
1/4


Vin


1V


2/4


Vin


2V


3/4


Vin


3V


Vin


4V

,分别表示
2

位数据
00


01


10


11

,对于写入数据,就是充电,通过控制内部的电荷的多少,对应表示不同的数据。

对于读取,则是通过对应的内部的电流(与
Vth

成反比),然后通过一系列解码电路完成读取,解析

出所存储的数据。这些具体的物理实现,都是有足够精确的设备和技术,才能实现精确的数据写入和读出的。

单个存储单元可以存储
2

位数据的,称作
2


2

次方=
4 Level Cell

,而不是
2 Level Cell

,这点,之前差点搞晕了。。。,同理,对于新出的单个存储单元可以存储
4

位数据的,称作
2


4

次方=
16 Level Cell



【关于如何识别
SLC


还是
MLC





Nand Flash

设计中,有个命令

叫做
Read ID

,读取
ID

,意思是读取芯片的
ID

,就像大家的身份证一样,这里读取的
ID

中,是读取好几个字节,一般最少是
4

个,新的芯片,支持
5

个甚至更多,从这些字节中,可以解析出很多相关的信息,比如此
Nand Flash

内部是几个芯片(
chip

)所组成的,每个
chip

包含了几片(
Plane

),每一片中的页大小,块大小,等等。在这些信息中,其中有一个,就是识别此
flash


SLC

还是
MLC

。下面这个就是最常见的
Nand Flash


datasheet

中所规定的,第
3

个字节,
3rd byte

,所表示的信息,其中就有
SLC/MLC

的识别信息:

Description


I/O7


I/O6


I/O5 I/O4


I/O3 I/O2


I/O1 I/O0


Internal



Chip Number



1



2



4



8



0

0



0
1



1
0



1
1



Cell Type



2 Level Cell



4 Level Cell



8 Level Cell



16 Level Cell



0
0



0
1



1
0



1

1



Number of



Simultaneously



Programmed Pages



1



2



4



8



0

0



0

1



1
0



1
1



Interleave Program



Between multiple chips



Not Support



Support



0



1



Cache Program



Not Support



Support



0



1



http://usr.cc/html/00/n-600.html

如何编写linux下nand flash驱动-2

发布时间: 2009-7-31 11:20 |
发布作者: dean |
|
查看: 49次




[b]Nand


Flash

[/b]
引脚
(Pin)


的说明】








3.Nand Flash


引脚功能说明


上图是常见的
Nand Flash

所拥有的引脚(
Pin

)所对应的功能,简单翻译如下:

1.

I/O0 ~ I/O7

:用于输入地址
/

数据
/

命令

,输出数据

2.

CLE


Command Latch Enable

,命令锁存使能,在输入命令之前,要先在模式寄存器中,设置

CLE

使能

3.

ALE


Address Latch Enable

,地址锁存使能,在输入地址之前,要先在模式寄存器中,设置
ALE

使能

4.

CE#


Chip Enable

,芯片使能,在操作
Nand Flash

之前,要先选中此芯片,才能操作

5.

RE#


Read Enable

,读使能,在读取数据之前,要先使
CE

#有效。

6.

WE#


Write Enable

,写使能
,

在写取数据之前,要先使
WE

#有效。

7.

WP#


Write Protect

,写保护

8.

R/B#:Ready/Busy Output,

就绪
/


,

主要用于在发送完编程
/

擦除命令后
,

检测这些操作是否完成
,


,

表示编程
/

擦除操作仍在进行中
,

就绪表示操作完成
.

9.

Vcc


Power

,电源

10.

Vss


Ground

,接地

11.

N.C


Non-Connection,

未定义,未连接。

[

小常识
]

在数据手册

中,你常会看到,对于一个引脚定义,有些字母上面带一横杠的,那是说明此引脚
/

信号是低电平有效,比如你上面看到的
RE

头上有个横线,就是说明,此
RE

是低电平有效,此外,为了书写方便,在字母后面加“#”,也是表示低电平有效,比如我上面写的
CE

#;如果字母头上啥都没有,就是默认的高电平有效,比如上面的
CLE

,就是高电平有效。

【为何需要
ALE



CLE





突然想明白了,
Nand Flash


,

为何设计

这么多的命令
,

把整个系统

搞这么复杂的原因了
:

比如命令锁存使能
(Command Latch Enable,CLE)


地址锁存使能
(Address Latch Enable


ALE)

,那是因为,
Nand Flash


8


I/O

,而且是复用的,也就是,可以传数据,也可以传地址,也可以传命令,为了区分你当前传入的到底是啥,所以,先要用发一个
CLE

(或
ALE

)命令,告诉
nand Flash

的控制器一声,我下面要传的是命令(或地址),这样,里面才能根据传入的内容,进行对应的动作。否则
,nand flash

内部
,

怎么知道你传入的是数据
,

还是地址
,

还是命令啊
,

也就无法实现正确的操作了
.


Nand Flash


只有
8



I/O


引脚的好处】


1.

减少外围引脚:相对于并口
(Parellel)


Nor Flash


48


52

个引脚来说,的确是大大减小了引脚数目,这样封装后的芯片体积,就小很多。现在芯片在向体积更小,功能更强,功耗更低发展,减小芯片体积,就是很大的优势。同时,减少芯片接口,也意味着使用

此芯片的相关的外围电路会更简化,避免了繁琐的硬件连线。

2.

提高系统的可扩展性,因为没有像其他设备

一样用物理大小对应的完全数目的
addr

引脚,在芯片内部换了芯片的大小等的改动,对于用全部的地址
addr

的引脚,那么就会引起这些引脚数目的增加,比如容量扩大一倍,地址空间
/

寻址空间扩大一倍,所以,地址线数目
/addr

引脚数目,就要多加一个,而对于统一用
8


I/O

的引脚的
Nand Flash

,由于对外提供的都是统一的
8

个引脚,内部的芯片大小的变化或者其他的变化,对于外部使用者
(

比如编写

nand flash

驱动

的人
)

来说,不需要关心,只是保证新的芯片,还是遵循同样的接口,同样的时序,同样的命令,就可以了。这样就提高了系统的扩展性。


Nand flash


的一些典型
(typical)


特性】


1.

页擦除时间是
200us

,有些慢的有
800us



2.

块擦除时间是
1.5ms.

3.

页数据读取到数据寄存器的时间一般是
20us



4.

串行访问(
Serial access

)读取一个数据的时间是
25ns

,而一些旧的
nand flash


30ns

,甚至是
50ns



5.

输入输出端口是地址和数据以及命令一起
multiplex

复用的。

以前老的
Nand Flash

,编程
/

擦除时间比较短,比如
K9G8G08U0M

,才
5K

次,而后来很多
6.nand flash

的编程
/

擦除的寿命,最多允许的次数,以前的
nand flash

多数是
10K

次,也就是
1

万次,而现在很多新的
nand flash

技术

提高了,比如,
Micron


MT29F1GxxABB


Numonyx


NAND04G-B2D/NAND08G-BxC

,都可以达到
100K

,也就是
10

万次的编程
/

擦除。和之前常见的
Nor Flash

达到同样的使用寿命了。

7.48

引脚的
TSOP1

封装


52

引脚的
ULGA

封装


Nand Flash


中的特殊硬件结构】


由于
nand flash

相对其他常见设备来说,比较特殊,所以,特殊的设备,也有特殊的设计,所以,有些特殊的硬件特性,就有比较解释一下:

1.

页寄存器(
Page Register

):由于
Nand Flash

读取和编程操作来说,一般最小单位是页,所以,
nand flash

在硬件设计时候,就考虑到这一特性,对于每一片,都有一个对应的区域,专门用于存放,将要写入到物理存储单元中去的或者刚从存储单元中读取出来的,一页的数据,这个数据缓存区,本质上就是一个
buffer

,但是只是名字叫法不同,
datasheet

里面叫做
Page Register

,此处翻译为
页寄存器,实际理解为页缓存,更为恰当些。而正是因为有些人不了解此内部结构,才容易产生之前遇到的某人的误解,以为内存

里面的数据,通过
Nand Flash


FIFO

,写入到
Nand Flash

里面去,就以为立刻实现了实际数据写入到物理存储单元中了。而实际上,只是写到了这个页缓存中,只有等你发了对应的编程第二阶段的确认命令
0x10

之后,实际的编程动作才开始,才开始把页缓存中的数据,一点点写到物理存储单元中去。

所以,简单总结一下就是,对于数据的流向,实际是经过了如下步骤:




4 Nand Flash


读写时的数据流向



Nand Flash


中的坏块
(Bad Block)





Nand Flash

中,一个块中含有
1

个或多个位是坏的,就成为其为坏块。

坏块的稳定性是无法保证的,也就是说,不能保证你写入的数据是对的,或者写入对了,读出来也不一定对的。而正常的块,肯定是写入读出都是正常的。

坏块有两种:


1

)一种是出厂的时候,也就是,你买到的新的,还没用过的
Nand Flash

,就可以包含了坏块。此类出厂时就有的坏块,被称作
factory (masked)bad block


initial bad/invalid block

,在出厂之前,就会做对应的标记,标为坏块。

具体标记的地方是,对于现在常见的页大小为
2K


Nand Flash

,是块中第一个页的
oob

起始位置(关于什么是页和
oob

,下面会有详细解释)的第
1

个字节(旧的小页面,
pagesize


512B

甚至
256B


nand flash

,坏块标记是第
6

个字节),如果不是
0xFF

,就说明是坏块。相对应的是,所有正常的块,好的块,里面所有数据都是
0xFF

的。


2

)第二类叫做在使用过程中产生的,由于使用过程时间长了,在擦块除的时候,出错了,说明此块坏了,也要在程序运行过程中,发现,并且标记成坏块的。具体标记的位置,和上面一样。这类块叫做
worn-out bad block



对于坏块的管理

,在
Linux

系统中,叫做坏块管理(
BBM


Bad Block Managment

),对应的会有一个表去记录好块,坏块的信息,以及坏块是出厂就有的,还是后来使用产生的,这个表叫做坏块表(
BBT


Bad Block Table

)。在
Linux

内核

MTD

架构下的
Nand Flash

驱动,和
Uboot


Nand Flash

驱动中,在加载完驱动之后,如果你没有加入参数

主动要求跳过坏块扫描的话,那么都会去主动扫描坏块,建立必要的
BBT

的,以备后面坏块管理所使用。

而关于好块和坏块,
Nand Flash

在出厂的时候,会做出保证:

1.

关于好的,可以使用的块的数目达到一定的数目,比如三星的
K9G8G08U0M

,整个
flash

一共有
4096

个块,出厂的时候,保证好的块至少大于
3996

个,也就是意思是,你新买到这个型号的
nand flash

,最坏的可能,

3096


3996


100

个坏块。不过,事实上,现在出厂时的坏块,比较少,绝大多数,都是使用时间长了,在使用过程中出现的。

2.

保证第一个块是好的,并且一般相对来说比较耐用。做此保证的主要原因是,很多
Nand Flash

坏块管理方法中,就是将第一个块,用来存储上面提到的
BBT

,否则,都是出错几率一样的块,那么也就不太好管理了,连放
BBT

的地方,都不好找了,
^_^



一般来说,不同型号的
Nand Flash

的数据手册中,也会提到,自己的这个
nand flash

,最多允许多少个坏块。就比如上面提到的,三星的
K9G8G08U0M

,最多有
100

个坏块。

对于坏块的标记,本质上,也只是对应的
flash

上的某些字节的数据是非
0xFF

而已,所以,只要是数据,就是可以读取和写入的。也就意味着,可以写入其他值,也就把这个坏块标记信息破坏了。对于出厂时的坏块,一般是不建议将标记好的信息擦除掉的。

uboot

中有个命令是“
nand scrub

”就可以将块中所有的内容都擦除了,包括坏块标记,不论是出厂时的,还是后来使用过程中出现而新标记的。一般来说,不建议用这个。不过,我倒是经常用,其实也没啥大碍,呵呵。

最好用“
nand erase

”只擦除好的块,对于已经标记坏块的块,不擦除。


nand Flash


中页的访问顺序】


在一个块内,对每一个页进行编程的话,必须是顺序的,而不能是随机的。比如,一个块中有
128

个页,那么你只能先对
page0

编程,再对
page1

编程,。。。。,而不能随机的,比如先对
page3

,再
page1


page2.


page0


page4


.

。。。

【片选无关
(


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

,这段时间的取消片选,可以降低很少的系统功耗,但是多次的操作,就可以在很大程度上降低整体的功耗了。

总结起来简单解释就是:由于某些外部应用的频率比较低,而
http://usr.cc/html/01/n-601.html

如何编写linux下nand flash驱动-3

发布时间: 2009-7-31 11:20 |
发布作者: dean |
|
查看: 48次



【读(
read


)操作过程[b]详解


[/b]

以最简单的
read

操作为例,解释如何理解时序图,以及将时序图

中的要求,转化为代码。

解释时序图之前,让我们先要搞清楚,我们要做的事情:那就是,要从
nand

flash



某个页里面,读取我们要的数据。

要实现此功能,会涉及到几部分的知识,至少很容易想到的就是:需要用到哪些命令

,怎么发这些命令,怎么计算所需要的地址,怎么读取我们要的数据等等。

下面,就一步步的解释,需要做什么,以及如何去做:

1.


需要[b]使用

何种命令
[/b]

首先,是要了解,对于读取数据,要用什么命令。

下面是
datasheet

中的命令集合:







5.Nand Flash K9K8G08U0A


的命令集合


很容易看出,我们要读取数据,要用到
Read

命令,该命令需要
2

个周期,第一个周期发
0x00

,第二个周期发
0x30



2.


发送命令前的准备工作以及时序图各个信号的具体含义


知道了用何命令后,再去了解如何发送这些命令。

[

小常识
]

在开始解释前,多罗嗦一下


使能


这个词,以便有些读者和我以前一样,在听这类虽然对于某些专业人士说是属于最基本的词汇了,但是对于初次接触,或者接触不多的人来说,听多了,容易被搞得一头雾水:使能(
Enable

),是指使其(某个信号)有效,使其生效的意思,“使其”“能够”怎么怎么样。。。。比如,上面图中的
CLE

线号,是高电平有效,如果此时将其设为高电平,我们就叫做,将
CLE

使能,也就是使其生效的意思。







6.Nand Flash


数据读取操作的时序图


注:此图来自三星的型号
K9K8G08U0A


nand flash

的数据手册

(datasheet)



我们来一起看看,我在图
6

中的特意标注的①边上的黄色竖线。

黄色竖线所处的时刻,是在发送读操作的第一个周期的命令
0x00

之前的那一刻。

让我们看看,在那一刻,其所穿过好几行都对应什么值,以及进一步理解,为何要那个值。


1

)黄色竖线穿过的第一行,是
CLE

。还记得前面介绍命令所存使能(
CLE

)那个引脚吧?
CLE

,将
CLE


1

,就说明你将要通过
I/O

复用端口发送进入
Nand Flash

的,是命令,而不是地址或者其他类型的数据。只有这样将
CLE


1

,使其有效,才能去通知了内部硬件逻辑,你接下来将收到的是命令,内部硬件逻辑,才会将受到的命令,放到命令寄存器中,才能实现后面正确的操作,否则,不去将
CLE


1

使其有效,硬件会无所适从,不知道你传入的到底是数据还是命令了。


2

)而第二行,是
CE#

,那一刻的值是
0

。这个道理很简单,你既然要向
Nand Flash

发命令,那么先要选中它,所以,要保证
CE#

为低电平,使其有效,也就是片选有效。


3

)第三行是
WE#

,意思是写使能。因为接下来是往
nand Flash

里面写命令,所以,要使得
WE#

有效,所以设为低电平。


4

)第四行,是
ALE

是低电平,而
ALE

是高电平有效,此时意思就是使其无效。而对应地,前面介绍的,使
CLE

有效,因为将要数据的是命令,而不是地址。如果在其他某些场合,比如接下来的要输入地址的时候,就要使其有效,而使
CLE

无效了。


5

)第五行,
RE#

,此时是高电平,无效。可以看到,知道后面低
6

阶段,才变成低电平,才有效,因为那时候,要发生读取命令,去读取数据。


6

)第六行,就是我们重点要介绍的,复用的输入输出
I/O

端口了,此刻,还没有输入数据,接下来,在不同的阶段,会输入或输出不同的数据
/

地址。


7

)第七行,
R/B#,

高电平,表示
R


Ready


/

就绪,因为到了后面的第
5

阶段,硬件内部,在第四阶段,接受了外界的读取命令后,把该页的数据一点点送到页寄存器中,这段时间,属于系统

在忙着干活,属于忙的阶段,所以,
R/B#

才变成低,表示
Busy

忙的状态的。

介绍了时刻①的各个信号的值,以及为何是这个值之后,相信,后面的各个时刻,对应的不同信号的各个值,大家就会自己慢慢分析了,也就容易理解具体的操作顺序和原理

了。

3.


如何计算出,我们要传入的地址


在介绍具体读取数据的详细流程之前,还要做一件事,那就是,先要搞懂我们要访问的地址,以及这些地址,如何分解后,一点点传入进去,使得硬件能识别才行。

此处还是以
K9K8G08U0A

为例,此
nand flash

,一共有
8192

个块,每个块内有
64

页,每个页是
2K+64 Bytes

,假设,我们要访问其中的第
7000

个块中的第
25

页中的
1208

字节处的地址,此时,我们就要先把具体的地址算出来:

物理地址
=

块大小×块号
+

页大小×页号
+

页内地址
=7000

×
128K+64

×
2K+1208=0x36B204B8,

接下来,我们就看看,怎么才能把这个实际的物理地址,转化为
nand Flash

所要求的格式。

在解释地址组成之前,先要来看看其
datasheet

中关于地址周期的介绍:




7 Nand Flash


的地址周期组成


结合图
7

和图
5

中的
2


3

阶段,我们可以看出,此
nand flash

地址周期共有
5

个,
2

个列
(Column)

周期,
3

个行(
Row

)周期。而对于对应地,我们可以看出,实际上,列地址
A0~A10

,就是页内地址,地址范围是从
0


2047

,而对出的
A11

,理论上可以表示
2048


4095

,但是实际上,我们最多也只用到了
2048


2011

,用于表示页内的
oob

区域,其大小是
64

字节。

对应地,
A12


A30

,称作页号,页的号码,可以定位到具体是哪一个页。而其中,
A18


A30

,表示对应的块号,即属于哪个块。

简单解释完了地址组成,那么就很容易分析上面例子中的地址了:

0x36B204B8 = 0011
0110 10110010 0000
01001011 1000

,分别分配到
5

个地址周期就是:

1st

周期,
A7


A0


1011 1000

=0x B8

2nd

周期,
A11


A8


0000 0100 = 0x04

3rd

周期,
A19


A12


0010 0000 = 0x20



4th

周期,
A27


A20


0110 1011 = 0x6B

5th

周期,
A30


A28


0000 0011 = 0x03

注意,与图
7

中对应的,
*L

,意思是地电平,由于未用到那些位,
datasheet

中强制要求设为
0

,所以,才有上面的
2nd

周期中的高
4

位是
0000.

其他的
A30

之后的位也是类似原理,都是
0



因此,接下来要介绍的,我们要访问第
7000

个块中的第
25

页中的
1208

字节处的话,所要传入的地址就是分
5

个周期,分别传入两个列地址的:
0xB8


0x04

,然后再传
3

个行地址的:
0x20


0x6B


0x03

,这样硬件才能识别。

4.


读操作过程的解释


准备工作终于完了,下面就可以开始解释说明,对于读操作的,上面图中标出来的,
1-6

个阶段,具体是什么含义。

(1)


操作准备阶段:此处是读(
Read

)操作,所以,先发一个图
5

中读命令的第一个阶段的
0x00,

表示,让硬件先准备一下,接下来的操作是读。

(2)


发送两个周期的列地址。也就是页内地址,表示,我要从一个页的什么位置开始读取数据。

(3)


接下来再传入三个行地址。对应的也就是页号。

(4)


然后再发一个读操作的第二个周期的命令
0x30

。接下来,就是硬件内部自己的事情了。

(5)


Nand Flash

内部硬件逻辑,负责去按照你的要求,根据传入的地址,找到哪个块中的哪个页,然后把整个这一页的数据,都一点点搬运到页缓存中去。而在此期间,你所能做的事,也就只需要去读取状态寄存器,看看对应的位的值,也就是
R/B#

那一位,是
1

还是
0


0

的话,就表示,系统是
busy

,仍在


忙“(着读取数据),如果是
1

,就说系统活干完了,忙清了,已经把整个页的数据都搬运到页缓存里去了,你可以接下来读取你要的数据了。

对于这里。估计有人会问了,这一个页一共
2048+64

字节,如果我传入的页内地址,就像上面给的
1208

一类的值,只是想读取
1028


2011

这部分数据,而不是页开始的
0

地址整个页的数据,那么内部硬件却读取整个页的数据出来,岂不是很浪费吗?答案是,的确很浪费,效率看起来不高,但是实际就是这么做的,而且本身读取整个页的数据,相对时间并不长,而且读出来之后,内部数据指针会定位到你刚才所制定的
1208

的那个位置。

(6)


接下来,就是你“窃取“系统忙了半天之后的劳动成果的时候了,呵呵。通过先去
Nand Flash

的控制器中的数据寄存器中写入你要读取多少个字节
(byte)/


(word)

,然后就可以去
Nand Flash

的控制器的
FIFO

中,一点点读取你要的数据了。

至此,整个
Nand Flash

的读操作就完成了。

对于其他操作,可以根据我上面的分析,一点点自己去看
datasheet

,根据里面的时序图去分析具体的操作过程,然后对照代码,会更加清楚具体是如何实现的。


Flash


的类型】


Flash

的类型主要分两种,
nand flash


nor flash



除了网上最流行的这个解释之外:

NAND


NOR
的比较

再多说几句:

1.nor

的成本相对高,具体读写数据时候,不容易出错。总体上,比较适合应用于存储少量的代码。

2.Nand flash

相对成本低。使用中数据读写容易出错,所以一般都需要有对应的软件或者硬件的数据校验算法,统称为
ECC

。由于相对来说,容量大,价格便宜,因此适合用来存储大量的数据。其在嵌入式

系统中的作用,相当于
PC

上的硬盘,用于存储大量数据。

所以,一个常见的应用组合就是,用小容量的
Nor Flash

存储启动代码,比如
uboot

,系统启动后
,

初始化对应的硬件,包括
SDRAM

等,然后将
Nand Flash

上的
Linux

内核

读取到内存

中,做好该做的事情后,就跳转到
SDRAM

中去执行内核了,然后内核解压(如果是压缩内核的话,否则就直接运行了)后,开始运行,在
Linux

内核启动最后,去
Nand Flash

上,挂载根文件

,比如
jffs2


yaffs2

等,挂载完成,运行初始化脚本

,启动
consle

交互,才运行你通过
console

和内核交互。至此完成整个系统启动过程。


Nor Flash

就分别存放的是
Uboot


Nand Flash

存放的是
Linux

的内核镜像和根文件系统

,以及余下的空间分成一个数据区。

Nor flash

,有类似于
dram

之类的地址总线,因此可以直接和
CPU

相连,
CPU

可以直接通过地址总线对
nor flash

进行访问,而
nand flash

没有这类的总线,只有
IO

接口,只能通过
IO

接口发送命令和地址,对
nand flash

内部数据进行访问。相比之下,
nor flash

就像是并行访问,
nand flash

就是串行访问,所以相对来说,前者的速度更快些。

但是由于物理制程
/

制造方面的原因,导致
nor


nand

在一些具体操作方面的特性不同:

NOR


NAND


(备注)


http://usr.cc/html/02/n-602.html

如何编写linux下nand flash驱动-4

发布时间: 2009-7-31 11:21 |
发布作者: dean |
|
查看: 147次



2.


软件方面


如果想要在
[b]Linux


[/b]
下[b]编写

Nand

Flash

[/b]
[b]驱动

,那么就先要搞清楚
Linux

[/b]
下,关于此部分的整个框架。弄明白,[b]系统

是如何管理

你的
nand flash

[/b]
的,以及,系统都帮你做了那些准备工作,而剩下的,驱动底层实现部分,你要去实现哪些功能,才能使得硬件正常工作起来。


【[b]内存

技术

设备


MTD

[/b]

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

设备来管理,根据这些设备的特点,上层实现了常见的操作函数封装,底层具体的内部实现,就需要驱动设计者自己来实现了。具体的内部硬件设备的读
/


/

擦除函数,那就是你必须实现的了。

HARD drives


MTD device


连续的扇区

连续的可擦除块

扇区都很小
(512B,1024B)

可擦除块比较大
(32KB,128KB)

主要通过两个操作对其维护操作:读扇区,写扇区

主要通过三个操作对其维护操作:从擦除块中读,写入擦除块,擦写可擦除块

坏快被重新映射,并且被硬件隐藏起来了(至少是在如今常见的
LBA

硬盘设备中是如此)

坏的可擦除块没有被隐藏,软件中要处理对应的坏块问题。

HDD

扇区没有擦写寿命超出的问题。

可擦除块是有擦除次数限制的,大概是
104
-105


.



4.MTD


设备和硬盘设备之间的区别


多说一句,关于
MTD

更多的内容,感兴趣的,去附录中的
MTD

的主页去看。

关于
mtd

设备驱动,感兴趣的可以去参考

MTD

原始设备与

FLASH

硬件驱动的对话



MTD

原始设备与

FLASH

硬件驱动的对话

-





那里,算是比较详细地介绍了整个流程,方便大家理解整个
mtd

框架和
nand flash

驱动。


Nand flash


驱动工作[b]原理


[/b]

在介绍具体如何写
Nand Flash

驱动之前,我们先要了解,大概的,整个系统,和
Nand Flash

相关的部分的驱动工作流程,这样,对于后面的驱动实现,才能更加清楚机制,才更容易实现,否则就是,即使写完了代码,也还是没搞懂系统是如何工作的了。

让我们以最常见的,
Linux

内核

中已经有的三星的
Nand Flash

驱动,来解释
Nand Flash

驱动具体流程和原理。

此处是参考
2.6.29

版本的
Linux

源码中的
/drivers/mtd/nand/s3c2410.c

,以
2410

为例。

1.


nand flash

驱动加载后,第一步,就是去调用对应的
init

函数,
s3c2410_nand_init,

去将在
nand flash

驱动注册到
Linux

驱动框架中。

2.

驱动本身,真正开始,是从
probe

函数,
s3c2410_nand_probe->s3c24xx_nand_probe,


probe

过程中,去用
clk_enable

打开
nand flash

控制器的
clock

时钟,用
request_mem_region

去申请驱动所需要的一些内存等相关资源。然后,在
s3c2410_nand_inithw

中,去初始化硬件相关的部分,主要是关于时钟频率的计算,以及启用
nand flash

控制器,使得硬件初始化好了,后面才能正常工作。

3.

需要多解释一下的,是这部分代码:


for (setno = 0; setno < nr_sets; setno++, nmtd++) {




pr_debug("initialising set %d (%p, info %p)/n", setno, nmtd, info);



/*


调用
init chip


去挂载你的
nand


驱动的底层函数到
nand flash


的结构体中,以及[b]设置

对应的
ecc mode

[/b]
,挂载
ecc


相关的函数
*/





s3c2410_nand_init_chip(info, nmtd, sets);



/* scan_ident


,扫描
nand


设备,设置
nand flash


的默认函数,获得物理设备的具体型号以及对应各个特性[b]参数

,这部分算出来的一些值,对于
nand flash

[/b]
来说,是最主要的参数,比如
nand falsh


的芯片的大小,块大小,页大小等。
*/




nmtd->scan_res = nand_scan_ident(&nmtd->mtd,





(sets) ? sets->nr_chips : 1);




if (nmtd->scan_res == 0) {




s3c2410_nand_update_chip(info, nmtd);



/* scan tail


,从名字就可以看出来,是扫描的后一阶段,此时,经过前面的
scan_ident


,我们已经获得对应
nand flash


的硬件的各个参数,然后就可以在
scan tail


中,根据这些参数,去设置其他一些重要参数,尤其是
ecc



layout


,即
ecc


是如何在
oob


中摆放的,最后,再去进行一些初始化操作,主要是根据你的驱动,如果没有实现一些函数的话,那么就用系统默认的。
*/




nand_scan_tail(&nmtd->mtd);



/* add partion


,根据你的
nand flash


的分区设置,去分区
*/




s3c2410_nand_add_partition(info, nmtd, sets);




}




if (sets != NULL)




sets++;




}



4.

等所有的参数都计算好了,函数都挂载完毕,系统就可以正常工作了。

上层访问你的
nand falsh

中的数据的时候,通过
MTD

层,一层层调用,最后调用到你所实现的那些底层访问硬件数据
/

缓存的函数中。



Linux




nand flash


驱动编写步骤简介】


关于上面提到的,在
nand_scan_tail

的时候,系统会根据你的驱动,如果没有实现一些函数的话,那么就用系统默认的。如果实现了自己的函数,就用你的。

估计很多人就会问了,那么到底我要实现哪些函数呢,而又有哪些是可以不实现,用系统默认的就可以了呢。

此问题的,就是我们下面要介绍的,也就是,你要实现的,你的驱动最少要做哪些工作,才能使整个
nand flash

工作起来。

1.


对于驱动框架部分


其实,要了解,关于驱动框架部分,你所要做的事情的话,只要看看三星的整个
nand flash

驱动中的这个结构体,就差不多了:

static struct platform_driver s3c2410_nand_driver = {




.probe
= s3c2410_nand_probe,




.remove
= s3c2410_nand_remove,




.suspend= s3c24xx_nand_suspend,




.resume
= s3c24xx_nand_resume,




.driver
= {




.name
= "s3c2410-nand",




.owner
= THIS_MODULE,




},



};



对于上面这个结构体,没多少要解释的。从名字,就能看出来:


1


probe

就是系统“探测”,就是前面解释的整个过程,这个过程中的多数步骤,都是和你自己的
nand flash

相关的,尤其是那些硬件初始化部分,是你必须要自己实现的。


2


remove

,就是和
probe

对应的,“反初始化”相关的动作。主要是释放系统相关资源和关闭硬件的时钟等常见操作了。

(3)suspend


resume

,对于很多没用到电源管理的情况下,至少对于我们刚开始写基本的驱动的时候,可以不用关心,放个空函数即可。

2.


对于
nand flash


底层操作实现部分


而对于底层硬件操作的有些函数,总体上说,都可以在上面提到的
s3c2410_nand_init_chip

中找到:

static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,





struct s3c2410_nand_mtd *nmtd,





struct s3c2410_nand_set *set)



{




struct nand_chip *chip = &nmtd->chip;




void __iomem *regs = info->regs;




chip->write_buf
=
s3c2410_nand_write_buf
;




chip->read_buf
=
s3c2410_nand_read_buf
;




chip->select_chip
=
s3c2410_nand_select_chip
;




chip->chip_delay
= 50;




chip->priv

= nmtd;




chip->options
= 0;




chip->controller
= &info->controller;




switch (info->cpu_type) {




case TYPE_S3C2410:



/* nand flash


控制器中,一般都有对应的数据寄存器,用于给你往里面写数据,表示将要读取或写入多少个字节
(byte,u8)/



(word,u32)


,所以,此处,你要给出地址,以便后面的操作所使用
*/




chip->IO_ADDR_W = regs + S3C2410_NFDATA;




info->sel_reg
= regs + S3C2410_NFCONF;




info->sel_bit
= S3C2410_NFCONF_nFCE;




chip->cmd_ctrl
=
s3c2410_nand_hwcontrol
;




chip->dev_ready =
s3c2410_nand_devready
;




break;



。。。。。。




}




chip->IO_ADDR_R = chip->IO_ADDR_W;




nmtd->info

= info;




nmtd->mtd.priv

= chip;




nmtd->mtd.owner
= THIS_MODULE;




nmtd->set

= set;




if (hardware_ecc) {




chip->ecc.calculate =
s3c2410_nand_calculate_ecc
;




chip->ecc.correct
=
s3c2410_nand_correct_data
;



/*


此处,多数情况下,你所用的
Nand Flash


的控制器,都是支持硬件
ECC


的,所以,此处设置硬件
ECC(HW_ECC)


,也是充分利用硬件的特性,而如果此处不用硬件去做的
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: