[转]ARM9 2410移植之Nand flash 驱动的编写与移植

<p></p>
<p>1 Nand flash 工作原理 <br><br>
S3C2410 板的Nand Flash 支持由两部分组成:Nand Flash 控制器(集成在S3C2410 CPU)和Nand Flash 存储 <br><br>
芯片(K9F1208U0B)两大部分组成。当要访问Nand Flash中的数据时,必须通过Nand Flash控制器发送命 <br><br>
令才能完成。所以, Nand Flash相当于S3C2410的一个外设,而不位于它的内存地址区. <br><br>
1.1 Nand flash 芯片工作原理 <br><br>
Nand flash 芯片型号为Samsung K9F1208U0B,数据存储容量为64MB ,采用块页式存储管理。8 个I/O <br><br>
引脚充当数据、地址、命令的复用端口。 <br><br>
1.1.1 芯片内部存储布局及存储操作特点 <br><br>
一片Nand flash 为一个设备(device), 其数据存储分层为: <br><br>
1设备(Device) = 4096 块(Blocks) <br><br>
1块(Block) = 32 页/ (Pages/rows) ;页与 是相同的意思,叫法不一样 <br><br>
1块(Page) = 528 字节(Bytes) = 数据块大小(512Bytes) + OOB 块大小(16Bytes) <br><br>
在每一页中,最后16 个字节 (又称OOB)用于Nand Flash 命令执行完后设置状态用,剩余512 个字节又 <br><br>
分为前半部分和后半部分。可以通过Nand Flash 命令00h/01h/50h 分别对前半部、后半部、OOB 进行定位通过 <br><br>
Nand Flash 内置的指针指向各自的首地址。 <br><br>
存储操作特点: <br><br>
1. 擦除操作的最小单位是块。 <br><br>
2. Nand Flash 芯片每一位(bit)只能从1 变为0 ,而不能从0 变为 1,所以在对其进行写入操作之前要一定将相应 <br><br>
块擦除(擦除即是将相应块得位全部变为 1). <br><br>
3. OOB 部分的第六字节(即517 字节)标志是否是坏块,如果不是坏块该值为FF ,否则为坏块。 <br><br>
4. 除OOB 第六字节外,通常至少把OOB 的前3 个字节存放Nand Flash 硬件ECC 码(关于硬件ECC 码请参看 <br><br>
Nandflash 控制器一节). <br><br>
1.1.2 重要芯片引脚功能 <br><br>
I/O0-I/O7:复用引脚。可以通过它向nand flash 芯片输入数据、地址、nand flash 命令以及输出数据和操作 <br><br>
状态信息。 <br><br>
CLE(Command Latch Enable): 命令锁存允许 <br><br>
ALE(Address Lactch Enable): 地址锁存允许 <br><br>
-CE: 芯片选择 <br><br>
-RE: 读允许 <br><br>
-WE: 写允许 <br><br>
-WP: 在写或擦除期间,提供写保护 <br><br>
R/-B: 读/忙输出 <br><br>
1.1.3 寻址方式 <br><br>
Samsung K9F1208U0B Nand Flash 片内寻址采用26 位地址形式。从第0 位开始分四次通过I/O0 -I/O7 进 <br><br>
传送,并进行片内寻址。具体含义如下: <br><br>
0 -7 位:字节在上半部、下半部及OOB 内的偏移地址 <br><br>
8 位:值为0 代表对一页内前256 个字节进行寻址 <br><br>
值为1 代表对一页内后256 个字节进行寻址 <br><br>
9-13位:对页进行寻址 <br><br>
14-25 位:对块进行寻址 <br><br>
当传送地址时,从位0 开始 <br><br>
1.1.4 Nand flash主要内设命令详细介绍 <br><br>
Nand Flash 命令执 是通过将命令字送到Nand Flash 控制器的命令寄存器来执 。 <br><br>
Nand Flash 的命令是分周期执行的,每条命令都有一个或多个执 周期,每个执 周期都有相映代码表示该周 <br><br>
期将要执行的动作。 <br><br>
主要命令有:Read 1 、Read 2 、Read ID 、Reset 、Page Program 、Block Erase 、Read Status 。 <br><br>
详细介绍如下: <br><br>
1. Read 1: <br><br>
功能:表示将要读取Nand flash 存储空间中一个页的前半部分,并且将内置指针定位到前半部分的第一个字节。 <br><br>
命令代码:00h <br><br>
2. Read 2: <br><br>
功能:表示将要读取Nand flash 存储空间中一个页的后半部分,并且将内置指针定位到后半部分的第一个字节。 <br><br>
命令代码:01h <br><br>
3. Read ID: <br><br>
功能:读取Nand flash 芯片的ID 号 <br><br>
命令代码:90h <br><br>
4. Reset: <br><br>
功能:重启芯片。 <br><br>
命令代码:FFh <br><br>
5. Page Program: <br><br>
功能:对页进行编程命令, 用于写操作。 <br><br>
命令代码:首先写入00h(A 区)/01h(B 区)/05h(C 区), 表示写入那个区; 再写入80h 开始编程模式(写入模式) ,接 <br><br>
下来写入地址和数据; 最后写入 10h 表示编程结束. <br><br>
6. Block Erase <br><br>
功能:块擦除命令。 <br><br>
命令代码:首先写入60h 进入擦写模式,然后输入块地址; 接下来写入D0h, 表示擦写结束. <br><br>
7. Read Status <br><br>
功能:读取内部状态寄存器值命令。 <br><br>
命令代码:70h <br><br>
1.2 Nand Flash 控制器工作原理 <br><br>
对Nand Flash 存储芯片进行操作, 必须通过Nand Flash 控制器的专用寄存器才能完成。所以,不能对Nand <br><br>
Flash 进 总线操作。而Nand Flash 的写操作也必须块方式进 。对Nand Flash 的读操作可以按字节读取。 <br><br>
1.2.1 Nand Flash控制器特性 <br><br>
1. 支持对Nand Flash 芯片的读、检验、编程控制 <br><br>
2. 如果支持从Nand Flash 启动, 在每次重启后自动将前Nand Flash 的前4KB 数据搬运到ARM 的内部RAM 中 <br><br>
3. 支持ECC 校验 <br><br>
1.2.2 Nand Flash控制器工作原理 <br><br>
Nand Flash 控制器在其专用寄存器区(SFR)地址空间中映射有属于自己的特殊功能寄存器,就是通过将Nand <br><br>
Flash 芯片的内设命令写到其特殊功能寄存器中,从而实现对Nand flash 芯片读、检验和编程控制的。特殊功能 <br><br>
寄存器有:NFCONF 、NFCMD 、NFADDR 、NFDATA 、NFSTAT 、NFECC 。寄存详细说明见下一节。 <br><br>
1.3 Nand flash 控制器中特殊功能寄存器详细介绍 <br><br>
1. 配置寄存器(NFCONF) <br><br>
功能:用于对Nand Flash 控制器的配置状态进行控制。 <br><br>
在地址空间中地址:0x4E000000,其中: <br><br>
Bit15:Nand Flash 控制器使能位,置0 代表禁止Nand Flash 控制器,置 1 代表激活Nand Flash 控制器; <br><br>
要想访问Nand Flash 芯片上存储空间,必须激活Nand Flash 控制器。在复位后该位自动置0 ,因此在初始化时 <br><br>
必须将该位置为 1 。 <br><br>
Bit12:初始化ECC 位,置1 为初始化ECC;置0 为不初始化ECC 。 <br><br>
Bit11:Nand Flash 芯片存储空间使能位,置0 代表可以对存储空间进行操作;置 1 代表禁止对存储空 <br><br>
间进行操作。在复位后,该位自动为 1 。 <br><br>
Bit10-8:TACLS 位。根据此设定CLE&ALE 的周期。TACLS 的值范围在0 -7 之间。 <br><br>
Bit6-4 、2-0 分别为:TWRPH0 、TWRPH1 位。设定写操作的访问周期。其值在0 -7 之间。 <br><br>
2. 命令寄存器(NFCMD) <br><br>
功能:用于存放Nand flash 芯片内设的操作命令。 <br><br>
在地址空间中地址:0x4E000004 ,其中: <br><br>
Bit0 -7:存放具体Nand flash 芯片内设的命令值。其余位保留以后用。 <br><br>
3. 地址寄存器(NFADDR) <br><br>
功能:用于存放用于对Nand flash 芯片存储单元寻址的地址值。 <br><br>
在地址空间中地址:0x4E000008 ,其中: <br><br>
Bit0 -7:用于存放地址值。因为本款Nand flash 芯片只有I/O0 -7 的地址/数据复用引脚且地址是四周 <br><br>
期每次8 位送入的,所以这里只用到8 位。其余位保留待用。 <br><br>
4. 数据寄存器(NFDATA) <br><br>
功能:Nand flash 芯片所有内设命令执行后都会将其值放到该寄存器中。同时,读出、写入Nand flash <br><br>
存储空间的值也是放到该寄存器。 <br><br>
在地址空间中地址:0x4E00000C ,其中: <br><br>
Bit0 -7:用于存放需要读出和写入的数据。其余位保留代用。 <br><br>
5. 状态寄存器(NFSTAT) <br><br>
功能:用于检测Nand flash 芯片上次对其存储空间的操作是否完成。 <br><br>
在地址空间中地址:0x4E000010 ,其中: <br><br>
Bit0:置0 表示Nand flash 芯片正忙于上次对存储空间的操作;置 1 表示Nand flash 芯片准备好接收新 <br><br>
的对存储空间操作的请求。 <br><br>
6. ECC 校验寄存器(NFECC) <br><br>
功能:ECC 校验寄存器 <br><br>
在地址空间中地址:0x4E000014 ,其中: <br><br>
Bit0-Bit7: ECC0 <br><br>
Bit8-Bit15: ECC1 <br><br>
Bit16-Bit23: ECC2 <br><br>
1.4 Nand Flash 控制器中的硬件 ECC 介绍 <br><br>
1.4.1 ECC产生方法 <br><br>
ECC 是用于对存储器之间传送数据正确进行校验的一种算法,分硬件ECC 和软件ECC 算法两种,在 <br><br>
S3C2410 的Nand Flash 控制器中实现了由硬件电路 (ECC 生成器)实现的硬件ECC 。 <br><br>
1.4.2 ECC生成器工作过程 <br><br>
当写入数据到Nand flash 存储空间时, ECC 生成器会在写入数据完毕后自动生成ECC 码,将其放入到 <br><br>
ECC0 -ECC2 。当读出数据时Nand Flash 同样会在读数据完毕后,自动生成ECC 码将其放到ECC0 -ECC2 当 <br><br>
中。 <br><br>
1.4.3 ECC 的运用 <br><br>
当写入数据时,可以在每页写完数据后将产生的ECC 码放入到OOB 指定的位置(Byte 6)去,这样就完成了 <br><br>
ECC 码的存储。这样当读出该页数据时,将所需数据以及整个OOB 读出,然后将指定位置的ECC 码与读出数 <br><br>
据后在ECC0 -ECC1 的实际产生的ECC 码进 对比,如果相等则读出正确,若不相等则读取错误需要进行重 <br><br>
读。 <br><br>
2 在ADS 下 flash烧写程序 <br><br>
2.1 ADS 下flash烧写程序原理及结构 <br><br>
基本原理:在windows 环境下借助ADS 仿真器将在SDRAM 中的一段存储区域中的数据写到Nand flash 存 <br><br>
储空间中。烧写程序在纵向上分三层完成: <br><br>
第一层: 主烧写函数 (完成将在SDRAM 中的一段存储区域中的数据写到Nand flash 存储空间中); <br><br>
第二层: 为第一层主烧写函数提供支持的对Nand flash 进行操作的页读、写,块擦除等函数; <br><br>
第三层:为第二层提供具体Nand flash 控制器中对特殊功能寄存器进行操作的核心函数,该层也是真正的 <br><br>
将数据能够在SDRAM 和Nand flash 之间实现传送的函数。 <br><br>
下面对其三层进行分述: <br><br>
2.2 第三层实现说明 <br><br>
2.1.1 特殊功能寄存器定义 <br><br>
#define rNFCONF (*(volatile unsigned int *)0x4e000000) <br><br>
#define rNFCMD (*(volatile unsigned char *)0x4e000004) <br><br>
#define rNFADDR (*(volatile unsigned char *)0x4e000008) <br><br>
#define rNFDATA (*(volatile unsigned char *)0x4e00000c) <br><br>
#define rNFSTAT (*(volatile unsigned int *)0x4e000010) <br><br>
#define rNFECC (*(volatile unsigned int *)0x4e000014) <br><br>
#define rNFECC0 (*(volatile unsigned char *)0x4e000014) <br><br>
#define rNFECC1 (*(volatile unsigned char *)0x4e000015) <br><br>
#define rNFECC2 (*(volatile unsigned char *)0x4e000016) <br><br>
2.1.2 操作的函数实现 <br><br>
1. 发送命令 <br><br>
#define NF_CMD(cmd) {rNFCMD=cmd;} <br><br>
2. 写入地址 <br><br>
#define NF_ADDR(addr) {rNFADDR=addr;} <br><br>
3. Nand Flash 芯片选中 <br><br>
#define NF_nFCE_L() {rNFCONF&=~(1<<11);} <br><br>
4. Nand Flash 芯片不选中 <br><br>
#define NF_nFCE_H() {rNFCONF|=(1<<11);} <br><br>
5. 初始化ECC <br><br>
#define NF_RSTECC() {rNFCONF|=(1<<12);} <br><br>
6. 读数据 <br><br>
#define NF_RDDATA() (rNFDATA) <br><br>
7. 写数据 <br><br>
#define NF_WRDATA(data) {rNFDATA=data;} <br><br>
8. 获取Nand Flash 芯片状态 <br><br>
#define NF_WAITRB() {while(!(rNFSTAT&(1<<0)));} <br><br>
0/假: 表示Nand Flash 芯片忙状态 <br><br>
1/真:表示Nand Flash 已经准备好 <br><br>
2.3 第二层实现说明 <br><br>
2.3.1 Nand Flash 初始化 <br><br>
void NF_Init(void) <br><br>
{ /* 设置Nand Flash 配置寄存器, 每一位的取值见1.3 节 */ <br><br>


rNFCONF=(1<<15)|(1<<14)|(1<<13)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0);
<br><br>
/* 复位外部Nand Flash 芯片 */ <br><br>
NF_Reset(); <br><br>
} <br><br>
2.3.2 Nand flash复位 <br><br>
static void NF_Reset(void) <br><br>
{ <br><br>
int i; <br><br><br><br>
NF_nFCE_L(); /* 片选Nand Flash 芯片*/ <br><br>
NF_CMD(0xFF); /* 复位命令 */ <br><br>
for(i=0;i<10;i++); /* 等待tWB = 100ns. */ <br><br>
NF_WAITRB(); /* wait 200~500us; */ <br><br>
NF_nFCE_H(); /* 取消Nand Flash 选中*/ <br><br>
} <br><br>
2.3.3 获取 Nand flash ID <br><br>
返回值为Nand flash 芯片的ID 号 <br><br>
unsigned short NF_CheckId(void) <br><br>
{ <br><br>
int i; <br><br>
unsigned short id; <br><br><br><br>
NF_nFCE_L(); /* 片选Nand Flash 芯片*/ <br><br>
NF_CMD(0x90); /* 发送读ID 命令到Nand Flash 芯片 */ <br><br>
NF_ADDR(0x0); /* 指定地址0x0 ,芯片手册要求 */ <br><br>
for(i=0;i<10;i++); /* 等待tWB = 100ns. */ <br><br>
id=NF_RDDATA()<<8; /* 厂商ID(K9S1208V:0xec) */ <br><br>
id|=NF_RDDATA(); /* 设备ID(K9S1208V:0x76) */ <br><br>
NF_nFCE_H(); /* 取消Nand Flash 选中*/ <br><br>
return id; <br><br>
} <br><br>
2.3.4 Nand flash 写入 <br><br>
以页为单位写入. <br><br>
参数说明:block 块号 <br><br>
page 页号 <br><br>
buffer 指向内存中待写入Nand flash 中的数据起始位置 <br><br>
返回值: 0 :写错误 <br><br>
1:写成功 <br><br>
static int NF_WritePage(unsigned int block, unsigned int page, unsigned char *buffer) <br><br>
{ <br><br>
int i; <br><br>
unsigned int blockPage = (block<<5)+page; <br><br>
unsigned char *bufPt = buffer; <br><br>
NF_RSTECC(); /* 初始化 ECC */ <br><br>
NF_nFCE_L(); /* 片选Nand Flash 芯片*/ <br><br>
NF_CMD(0x0); /* 从A 区开始写 */ <br><br>
NF_CMD(0x80); /* 写第一条命令 */ <br><br>
NF_ADDR(0); /* A0~A7 位(Column Address) */ <br><br>
NF_ADDR(blockPage&0xff); /* A9-A16, (Page Address) */ <br><br>
NF_ADDR((blockPage>>8)&0xff); /* A17-A24, (Page Address) */ <br><br>
NF_ADDR((blockPage>>16)&0xff); /* A25, (Page Address) */ <br><br>
for(i=0;i<512;i++) <br><br>
{ <br><br>
NF_WRDATA(*bufPt++); /* 写一个页512 字节到Nand Flash 芯片 */ <br><br>
} <br><br><br><br>
/* <br><br>
* OOB 一共16 Bytes, 每一个字节存放什么由程序员自己定义, 通常, <br><br>
* 我们在Byte0-Byte2 存ECC 检验码. Byte6 存放坏块标志. <br><br>
*/ <br><br>
seBuf[0]=rNFECC0; /* 读取ECC 检验码0 */ <br><br>
seBuf[1]=rNFECC1; /* 读取ECC 检验码 1 */ <br><br>
seBuf[2]=rNFECC2; /* 读取ECC 检验码2 */ <br><br>
seBuf[5]=0xff; /* 非坏块标志 */ <br><br><br><br>
for(i=0;i<16;i++) <br><br>
{ <br><br>
NF_WRDATA(seBuf[i]); /* 写该页的OOB 数据块 */ <br><br>
} <br><br>
NF_CMD(0x10); /* 结束写命令 */ <br><br>
/* 等待Nand Flash 处于准备状态 */ <br><br>
for(i=0;i<10;i++); <br><br>
NF_WAITRB(); <br><br><br><br>
/* 发送读状态命令 Nand Flash */ <br><br>
NF_CMD(0x70); <br><br>
for(i=0;i<3;i++); <br><br><br><br>
if (NF_RDDATA()&0x1) <br><br>
{ /*如果写有错, 则标示为坏块 */ <br><br>
NF_nFCE_H(); /* 取消Nand Flash 选中*/ <br><br>
NF_MarkBadBlock(block); <br><br>
return 0; <br><br>
} else { /* 正常退出 */ <br><br>
NF_nFCE_H(); /* 取消Nand Flash 选中*/ <br><br>
return 1; <br><br>
} <br><br>
} <br><br>
2.3.5 Nand flash读取 <br><br>
参数说明:block:块号 <br><br>
page:页号 <br><br>
buffer:指向将要读取到内存中的起始位置 <br><br>
返回值:1:读成功 <br><br>
0:读失败 <br><br>
static int NF_ReadPage(unsigned int block, unsigned int page, unsigned char *buffer) <br><br>
{ <br><br>
int i; <br><br>
unsigned int blockPage; <br><br>
unsigned char ecc0, ecc1, ecc2; <br><br>
unsigned char *bufPt=buffer; <br><br>
unsigned char se[16]; <br><br><br><br>
page=page&0x1f; <br><br>
blockPage=(block<<5)+page; <br><br>
NF_RSTECC(); /* 初始化 ECC */ <br><br>
NF_nFCE_L(); /* 片选Nand Flash 芯片*/ <br><br>
NF_CMD(0x00); /* 从A 区开始读 */ <br><br>
NF_ADDR(0); /* A0~A7 位(Column Address) */ <br><br>
NF_ADDR(blockPage&0xff); /* A9-A16, (Page Address) */ <br><br>
NF_ADDR((blockPage>>8)&0xff); /* A17-A24, (Page Address) */ <br><br>
NF_ADDR((blockPage>>16)&0xff); /* A25, (Page Address) */ <br><br><br><br>
/* 等待Nand Flash 处于再准备状态 */ <br><br>
for(i=0;i<10;i++); <br><br>
NF_WAITRB(); /*等待 tR(max 12us) */ <br><br>
/* 读整个页, 512 字节 */ <br><br>
for(i=0;i<512;i++) <br><br>
{ <br><br>
*bufPt++=NF_RDDATA(); <br><br>
} <br><br><br><br>
/* 读取ECC 码 */ <br><br>
ecc0=rNFECC0; <br><br>
ecc1=rNFECC1; <br><br>
ecc2=rNFECC2; <br><br><br><br>
/* 读取该页的OOB 块 */ <br><br>
for(i=0;i<16;i++) <br><br>
{ <br><br>
se[i]=NF_RDDATA(); <br><br>
} <br><br><br><br>
NF_nFCE_H(); /* 取消Nand Flash 选中*/ <br><br><br><br>
/* 校验ECC 码, 并返回 */ <br><br>
if(ecc0==se[0] && ecc1==se[1] && ecc2==se[2]) <br><br>
return 1; <br><br>
else <br><br>
return 0; <br><br>
} <br><br>
2.3.6 Nand flash标记坏块 <br><br>
如果是坏块, 通过写OOB 块的Byte6 把该块标记为坏块。 <br><br>
参数说明:block 块号 <br><br>
返回值:1:ok ,成功完成标记。 <br><br>
0:表示写OOB 块正确. <br><br>
static int NF_MarkBadBlock(unsigned int block) <br><br>
{ <br><br>
int i; <br><br>
unsigned int blockPage=(block<<5); <br><br><br><br>
seBuf[0]=0xff; <br><br>
seBuf[1]=0xff; <br><br>
seBuf[2]=0xff; <br><br>
seBuf[5]=0x44; /* 设置坏块标记 */ <br><br><br><br>
NF_nFCE_L(); /* 片选Nand Flash 芯片*/ <br><br>
NF_CMD(0x50); /* 从C 区开始写 */ <br><br>
NF_CMD(0x80); /* 发送编程命令, 让Nand Flash 处理写状态 */ <br><br>
NF_ADDR(0x0); /* A0~A7 位(Column Address) */ <br><br>
NF_ADDR(blockPage&0xff); /* A9-A16, (Page Address) */ <br><br>
NF_ADDR((blockPage>>8)&0xff); /* A17-A24, (Page Address) */ <br><br>
NF_ADDR((blockPage>>16)&0xff); /* A25, (Page Address) */ <br><br><br><br>
/* 写OOB 数据块 */ <br><br>
for(i=0;i<16;i++) <br><br>
{ <br><br>
NF_WRDATA(seBuf[i]); <br><br>
} <br><br>
NF_CMD(0x10); /* 结束写命令 */ <br><br><br><br>
/* 等待NandFlash 准备好 */ <br><br>
for(i=0;i<10;i++); /* tWB = 100ns. */ <br><br>
NF_WAITRB(); <br><br>
/*读NandFlash 的写状态 */ <br><br>
NF_CMD(0x70); <br><br>
for(i=0;i<3;i++); /* twhr=60ns */ <br><br>
if (NF_RDDATA()&0x1) <br><br>
{ <br><br>
NF_nFCE_H(); /* 取消Nand Flash 选中*/ <br><br>
return 0; <br><br>
} else { <br><br>
NF_nFCE_H(); /* 取消Nand Flash 选中*/ <br><br>
} <br><br>
return 1; <br><br>
} <br><br>
2.3.7 Nand Flash检查坏块 <br><br>
检查指定块是否是坏块. <br><br>
参数说明:block:块号 <br><br>
返回值:1:指定块是坏块 <br><br>
0:指定块不是坏块。 <br><br>
static int NF_IsBadBlock(U32 block) <br><br>
{ <br><br>
int i; <br><br>
unsigned int blockPage; <br><br>
U8 data; <br><br><br><br>
blockPage=(block<<5); <br><br>
NF_nFCE_L(); /* 片选Nand Flash 芯片*/ <br><br>
NF_CMD(0x50); /* Read OOB 数据块 */ <br><br>
NF_ADDR(517&0xf); /* A0~A7 位(Column Address) */ <br><br>
NF_ADDR(blockPage&0xff); /* A9-A16, (Page Address) */ <br><br>
NF_ADDR((blockPage>>8)&0xff); /* A17-A24, (Page Address) */ <br><br>
NF_ADDR((blockPage>>16)&0xff); /* A25, (Page Address) */ <br><br><br><br>
/* 等待NandFlash 准备好 */ <br><br>
for(i=0;i<10;i++); /* wait tWB(100ns) */ <br><br>
NF_WAITRB(); <br><br>
/* 读取读出值 */ <br><br>
data=NF_RDDATA(); <br><br>
NF_nFCE_H(); /* 取消Nand Flash 选中*/ <br><br>
/* 如果data 不为0xff 时, 表示该块是坏块 */ <br><br>
if(data != 0xff) <br><br>
return 1; <br><br>
else <br><br>
return 0; <br><br>
} <br><br>
2.3.8 擦除指定块中数据 <br><br>
参数说明:block 块号 <br><br>
返回值:0:擦除错误。(若是坏块直接返回0;若擦除出现错误则标记为坏块然后返回0) <br><br>
1 :成功擦除。 <br><br>
static int NF_EraseBlock(unsigned int block) <br><br>
{ <br><br>
unsigned int blockPage=(block<<5); <br><br>
int i; <br><br><br><br>
/* 如果该块是坏块, 则返回 */ <br><br>
if(NF_IsBadBlock(block)) <br><br>
return 0; <br><br>
NF_nFCE_L(); /* 片选Nand Flash 芯片*/ <br><br>
NF_CMD(0x60); /* 设置擦写模式 */ <br><br>
NF_ADDR(blockPage&0xff); /* A9-A16, (Page Address) , 是基于块擦*/ <br><br>
NF_ADDR((blockPage>>8)&0xff); /* A17-A24, (Page Address) */ <br><br>
NF_ADDR((blockPage>>16)&0xff); /* A25, (Page Address) */ <br><br>
NF_CMD(0xd0); /* 发送擦写命令, 开始擦写 */ <br><br>
/* 等待NandFlash 准备好 */ <br><br>
for(i=0;i<10;i++); /* tWB(100ns) */ <br><br>
NF_WAITRB(); <br><br>
/* 读取操作状态 */ <br><br>
NF_CMD(0x70); <br><br>
if (NF_RDDATA()&0x1) <br><br>
{ <br><br>
NF_nFCE_H(); /* 取消Nand Flash 选中*/ <br><br>
NF_MarkBadBlock(block); /* 标记为坏块 */ <br><br>
return 0; <br><br>
} else { <br><br>
NF_nFCE_H(); /* 取消Nand Flash 选中*/ <br><br>
return 1; <br><br>
} <br><br>
} <br><br>
2.4 第一层的实现 <br><br>
2.4.1 NandFlash 烧写主函数说明 <br><br>
参数说明: block 块号 <br><br>
srcAddress SDRAM 中数据起始地址 <br><br>
fileSize 要烧写的数据长度 <br><br>
返回值: 无 <br><br>
void K9S1208_Program(unsigned int block, unsigned int srcAddress, unsigned int fileSize) <br><br>
{ <br><br>
int i; <br><br>
int programError=0; <br><br>
U32 blockIndex; <br><br>
U8 *srcPt, *saveSrcPt; <br><br>
srcPt=(U8 *)srcAddress; /* 文件起始地址 */ <br><br>
blockIndex = block; /* 块号 */ <br><br>
while(1) <br><br>
{ <br><br>
saveSrcPt=srcPt; <br><br>
/* 如果当前块是坏块, 跳过当前块 */ <br><br>
if(NF_IsBadBlock(blockIndex)) <br><br>
{ <br><br>
blockIndex++; /* 到下一个块 */ <br><br>
continue; <br><br>
} <br><br>
/* 在写之前, 必须先擦除, 如果擦除不成功, 跳过当前块 */ <br><br>
if(!NF_EraseBlock(blockIndex)) <br><br>
{ <br><br>
blockIndex++; /* 到下一个块 */ <br><br>
continue; <br><br>
} <br><br><br><br>
/* 写一个块, 一块有32 页 */ <br><br>
for(i=0;i<32;i++) <br><br>
{ <br><br>
/* 写入一个页, 如果出错, 停止写当前块 */ <br><br>
if(!NF_WritePage(blockIndex,i,srcPt)) <br><br>
{ <br><br>
programError=1; <br><br>
break; <br><br>
} <br><br>
/* 如果操作正常, 文件的写位置加上1 页偏移,到下一页的起始位置 */ <br><br>
srcPt+=512; <br><br>
/* 如果写地址没有超过文件长度, 继续; 超出则终止写 */ <br><br>
if((U32)srcPt>=(srcAddress+fileSize)) <br><br>
break; <br><br>
} <br><br><br><br>
/* 如果写一个块时, 其中某一页写失败, 则把写地址恢复写该块之前, 并跳过当前块 */ <br><br>
if(programError==1) <br><br>
{ <br><br>
blockIndex++; <br><br>
srcPt=saveSrcPt; <br><br>
programError=0; <br><br>
continue; <br><br>
} <br><br>
/* 如果写地址没有超过文件长度, 继续; 超出则终止写 */ <br><br>
if((U32)srcPt >= (srcAddress+fileSize)) <br><br>
break; <br><br>
/* 如果正常写成功, 继续写下一个块 */ <br><br>
blockIndex++; <br><br>
} <br><br>
} <br><br>
3 在 U-BOOT对Nand Flash 的支持 <br><br>
3.1 U-BOOT 对从Nand Flash启动的支持 <br><br>
3.1.1 从Nand Flash启动 U-BOOT的基本原理 <br><br>
1. 前4K 的问题 <br><br>
如果S3C2410 被配置成从Nand Flash 启动(配置由硬件工程师在电路板设置), S3C2410 的Nand Flash 控制器 <br><br>
有一个特殊的功能, 在S3C2410 上电后, Nand Flash 控制器会自动的把Nand Flash 上的前4K 数据搬移到4K 内部 <br><br>
RAM 中, 并把0x00000000 设置内部RAM 的起始地址, CPU 从内部RAM 的0x00000000 位置开始启动。这个过 <br><br>
程不需要程序干涉。 <br><br>
程序员需要完成的工作,是把最核心的启动程序放在Nand Flash 的前4K 中。 <br><br>
2. 启动程序的安排 <br><br>
由于Nand Flash 控制器从Nand Flash 中搬移到内部RAM 的代码是有限的,所以, 在启动代码的前4K 里,我 <br><br>
们必须完成S3C2410 的核心配置以及把启动代码(UBOOT)剩余部分搬到RAM 中运 。以UBOOT 为例, 前4K <br><br>
完成的主要工作, 见第四部分的2.2 节。 <br><br>
3.1.2 支持 Nand Flash启动代码说明 <br><br>
首先在include/configs/crane2410.h 中加入CONFIG_S3C2410_NAND_BOOT, 如下: <br><br>
#define CONFIG_S3C2410_NAND_BOOT 1 <br><br>
支持从Nand Flash 中启动. <br><br>
1. 执 Nand Flash 初始化 <br><br>
下面代码在cpu/arm920t/start.S 中 <br><br>
#ifdef CONFIG_S3C2410_NAND_BOOT <br><br>
copy_myself: <br><br>
mov r10, lr <br><br>
ldr sp, DW_STACK_START @安装栈的起始地址 <br><br>
mov fp, #0 @初始化帧指针寄存器 <br><br>
bl nand_reset @跳到复位C 函数去执 <br><br>
... <br><br>
DW_STACK_START: <br><br>
.word STACK_BASE+STACK_SIZE-4 <br><br>
2. nand_reset C 代码 <br><br>
下面代码被加在/board/crane2410/crane2410.c 中 <br><br>
void nand_reset(void) <br><br>
{ <br><br>
int i; <br><br>
/* 设置Nand Flash 控制器 */ <br><br>


rNFCONF=(1<<15)|(1<<14)|(1<<13)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0);
<br><br>
/* Nand Flash 芯片发送复位命令 */ <br><br>
NF_nFCE_L(); <br><br>
NF_CMD(0xFF); <br><br>
for(i=0; i<10; i++); <br><br>
NF_WAITRB(); <br><br>
NF_nFCE_H(); <br><br>
} <br><br><br><br>
3. 从Nand Flash 中把UBOOT 拷贝到RAM <br><br>
@read U-BOOT from Nand Flash to RAM <br><br>
ldr r0, =UBOOT_RAM_BASE @ 设置第 1 个参数: UBOOT 在RAM 中的起始地址 <br><br>
mov r1, #0x0 @ 设置第2 个参数:Nand Flash 的起始地址 <br><br>
mov r2, #0x20000 @ 设置第3 个参数: UBOOT 的长度(128KB) <br><br>
bl nand_read_whole @ 调用nand_read_whole(), 该函数在board/crane2410/crane2410.c 中 <br><br>
tst r0, #0x0 @ 如果函数的返回值为0,表示执行成功. <br><br>
beq ok_nand_read @ 执行内存比较 <br><br>
4. 从Nand Flash 中把数据读入到RAM 中 <br><br>
int nand_read_whole(unsigned char *buf, unsigned long start_addr, int size) <br><br>
{ <br><br>
int i, j; <br><br>
/* 如果起始地址和长度不是512 字节(1 页)的倍数, 则返回错误代码 */ <br><br>
if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)) { <br><br>
return -1; <br><br>
} <br><br>
/* 激活Nand Flash */ <br><br>
NF_nFCE_L(); <br><br>
for(i=0; i<10; i++); <br><br>
i = start_addr; <br><br>
while(i < start_addr + size) { <br><br>
/* 读A 区 */ <br><br>
rNFCMD = 0; <br><br><br><br>
/* 写入读取地址 */ <br><br>
rNFADDR = i & 0xff; <br><br>
rNFADDR = (i >> 9) & 0xff; <br><br>
rNFADDR = (i >> 17) & 0xff; <br><br>
rNFADDR = (i >> 25) & 0xff; <br><br><br><br>
NF_WAITRB(); <br><br>
/* 读出一页(512 字节) */ <br><br>
for(j=0; j < NAND_SECTOR_SIZE; j++, i++) { <br><br>
*buf = (rNFDATA & 0xff); <br><br>
buf++; <br><br>
} <br><br>
} <br><br>
/* 停止驱动Nand Flash */ <br><br>
NF_nFCE_H(); <br><br>
return 0; <br><br>
} <br><br>
5. 校查搬移后的数据 <br><br>
把RAM 中的前4K 与内部中前4K 进 比较, 如果完全相同, 则表示搬移成功. <br><br>
ok_nand_read: <br><br>
mov r0, #0x00000000 @内部RAM 的起始地址 <br><br>
ldr r1, =UBOOT_RAM_BASE @UBOOT 在RAM 中的起始地址 <br><br>
mov r2, #0x400 @比较1024 次, 每次4 字节, 4 bytes * 1024 = 4K-bytes <br><br>
go_next: @ 比较1024 次, 每次4 个字节 <br><br>
ldr r3, [r0], #4 <br><br>
ldr r4, [r1], #4 <br><br>
teq r3, r4 <br><br>
bne notmatch <br><br>
subs r2, r2, #4 <br><br>
beq done_nand_read <br><br>
bne go_next <br><br>
notmatch: <br><br>
1:b 1b <br><br>
done_nand_read: <br><br>
mov pc, r10 <br><br>
3.2 U-BOOT 对Nand Flash 命令的支持 <br><br>
在U-BOOT 下对Nand Flash 的支持主要是在命令行下实现对nand flash 的操作。对nand flash 实现的命令 <br><br>
为:nand info 、nand device 、nand read 、nand write 、nand erease 、nand bad 。 <br><br>
用到的主要数据结构有:struct nand_flash_dev 、struct nand_chip 。前者包括主要的芯片型号、存储容量、 <br><br>
设备ID 、I/O 总线宽度等信息;后者是具体对nand flash 进行操作时用到的信息。 <br><br>
3.2.1 主要数据结构介绍 <br><br>
1. struct nand_flash_dev 数据结构 <br><br>
该数据结构在include/linux/mtd/nand.h 中定义,在include/linux/mtd/nand_ids.h 中赋初值。 <br><br>
struct nand_flash_dev { <br><br>
char *name; /* 芯片名称 */ <br><br>
int manufacture_id; /* 厂商ID */ <br><br>
int model_id; /* 模式ID */ <br><br>
int chipshift; /* Nand Flash 地址位数 */ <br><br>
char page256; /* 表明是否时256 字节一页。1:是;0:否。*/ <br><br>
char pageadrlen; /* 完成一次地址传送需要 NFADDR 中传送几次。*/ <br><br>
unsigned long erasesize; /* 一次块擦除可以擦除多少字节 */ <br><br>
int bus16; /* 地址线是否是16位,1:是;0:否 */ <br><br>
}; <br><br>
2. struct nand_chip 数据结构 <br><br>
该数据结构在include/linux/mtd/nand.h 中定义. 该结构体定义出一个Nand Flash 设备数组: <br><br>
struct nand_chip nand_dev_desc[CFG_MAX_NAND_DEVICE]; <br><br>
该数组在nand_probe()中对其进行初始化. <br><br>
struct nand_chip { <br><br>
int page_shift; /* Page 地址位数 */ <br><br>
u_char *data_buf; /* 本次读出的一页数据 */ <br><br>
u_char *data_cache; /* 读出的一页数据 */ <br><br>
int cache_page; /* 上次操作的页号 */ <br><br>
u_char ecc_code_buf[6]; /* ECC 校验码 */ <br><br>
u_char reserved[2]; <br><br>
char ChipID; /* 芯片ID 号 */ <br><br>
struct Nand *chips; /* Nand Flash 芯片列表, 表示支持几个芯片为一个设备*/ <br><br>
int chipshift; <br><br>
char* chips_name; /* Nand Flash 芯片名称 */ <br><br>
unsigned long erasesize; /* 块擦写的大小 */ <br><br>
unsigned long mfr; /* 厂商ID */ <br><br>
unsigned long id; /* 模式ID */ <br><br>
char* name; /* 设备名称 */ <br><br>
int numchips; /* 有几块Nand Flash 芯片 */ <br><br>
char page256; /* 一页是256 字节, 还是512 字节 */ <br><br>
char pageadrlen; /* 页地址的长度 */ <br><br>
unsigned long IO_ADDR; /* 用于对nand flash 进行寻址的地址值存放处 */ <br><br>
unsigned long totlen; /* Nand Flash 总共大小 */ <br><br>
uint oobblock; /* 一页的大小。本款nand flash 为512 */ <br><br>
uint oobsize; /* spare array 大小。本款nand flash 为16 */ <br><br>
uint eccsize; /* ECC 大小 */ <br><br>
int bus16; /* 地址线是否是16位,1:是;0:否 */ <br><br>
}; <br><br>
3.2.2 支持的命令函数说明 <br><br>
1. nand info/nand device <br><br>
功能:显示当前nand flash 芯片信息。 <br><br>
函数调用关系如下(按先后顺序): <br><br>
static void nand_print(struct nand_chip *nand) ; <br><br>
2. nand erase <br><br>
功能:擦除指定块上的数据。 <br><br>
函数调用关系如下(按先后顺序): <br><br>
int nand_erase(struct nand_chip* nand, size_t ofs, size_t len, int clean); <br><br>
3. nand bad <br><br>
功能:显示坏块。 <br><br>
函数调用关系如下(按先后顺序): <br><br>
static void nand_print_bad(struct nand_chip* nand); <br><br>
int check_block (struct nand_chip *nand, unsigned long pos); <br><br>
4. nand read <br><br>
功能:读取nand flash 信息到SDRAM 。 <br><br>
函数调用关系如下(按先后顺序): <br><br>
int nand_rw (struct nand_chip* nand, int cmd,size_t start, size_t len, size_t * retlen, u_char * buf); <br><br>
static int nand_read_ecc(struct nand_chip *nand, size_t start, size_t len, <br><br>
size_t * retlen, u_char *buf, u_char *ecc_code); <br><br>
static void NanD_ReadBuf (struct nand_chip *nand, u_char * data_buf, int cntr); <br><br>
READ_NAND(adr); <br><br>
5. nand write <br><br>
功能:从SDRAM 写数据到nand flash 中。 <br><br>
函数调用关系如下(按先后顺序): <br><br>
int nand_rw (struct nand_chip* nand, int cmd,size_t start, size_t len, size_t * retlen, u_char * buf); <br><br>
static int nand_write_ecc (struct nand_chip* nand, size_t to, size_t len, <br><br>
size_t * retlen, const u_char * buf, u_char * ecc_code); <br><br>
static int nand_write_page (struct nand_chip *nand, int page, int col, int last, u_char * ecc_code); <br><br>
WRITE_NAND(d , adr); <br><br>
3.2.3 U-BOOT支持 Nand Flash命令移植说明 <br><br>
1. 设置配置选项 <br><br>
在CONFIG_COMMANDS 中, 打开CFG_CMD_NAND 选项. <br><br>
#define CONFIG_COMMANDS / <br><br>
(CONFIG_CMD_DFL | / <br><br>
CFG_CMD_CACHE | / <br><br>
CFG_CMD_NAND | / <br><br>
/*CFG_CMD_EEPROM |*/ / <br><br>
/*CFG_CMD_I2C |*/ / <br><br>
/*CFG_CMD_USB |*/ / <br><br>
CFG_CMD_PING | / <br><br>
CFG_CMD_REGINFO | / <br><br>
CFG_CMD_DATE | / <br><br>
CFG_CMD_ELF) <br><br>
#if (CONFIG_COMMANDS & CFG_CMD_NAND) <br><br>
#define CFG_NAND_BASE 0x4E000000 /* Nand Flash 控制器在SFR 区中起始寄存器地址 */ <br><br>
#define CFG_MAX_NAND_DEVICE 1 /* 支持的最在Nand Flash 数据 */ <br><br>
#define SECTORSIZE 512 /* 1 页的大小 */ <br><br>
#define NAND_SECTOR_SIZE SECTORSIZE <br><br>
#define NAND_BLOCK_MASK (NAND_SECTOR_SIZE – 1) /* 页掩码 */ <br><br>
#define ADDR_COLUMN 1 /* 一个字节的Column 地址 */ <br><br>
#define ADDR_PAGE 3 /* 3 字节的页块地址, A9-A25*/ <br><br>
#define ADDR_COLUMN_PAGE 4 /* 总共4 字节的页块地址 */ <br><br>
#define NAND_ChipID_UNKNOWN 0x00 /* 未知芯片的ID 号 */ <br><br>
#define NAND_MAX_FLOORS 1 <br><br>
#define NAND_MAX_CHIPS 1 <br><br>
/* Nand Flash 命令层底层接口函数 */ <br><br>
#define WRITE_NAND_COMMAND(d, adr) do {rNFCMD = d; } while(0) <br><br>
#define WRITE_NAND_ADDRESS(d, adr) do {rNFADDR = d;} while(0) <br><br>
#define WRITE_NAND(d, adr) do {rNFDATA = d;} while(0) <br><br>
#define READ_NAND(adr) (rNFDATA) <br><br>
#define NAND_WAIT_READY(nand) {while(!(rNFSTAT&(1<<0)));} <br><br>
#define NAND_DISABLE_CE(nand) {rNFCONF |= (1<<11);} <br><br>
#define NAND_ENABLE_CE(nand) {rNFCONF &= ~(1<<11);} <br><br>
/* 下面一组操作对Nand Flash 无效 */ <br><br>
#define NAND_CTL_CLRALE(nandptr) <br><br>
#define NAND_CTL_SETALE(nandptr) <br><br>
#define NAND_CTL_CLRCLE(nandptr) <br><br>
#define NAND_CTL_SETCLE(nandptr) <br><br>
/* 允许Nand Flash 写校验 */ <br><br>
#define CONFIG_MTD_NAND_VERIFY_WRITE 1 <br><br>
#endif /* CONFIG_COMMANDS & CFG_CMD_NAND*/ <br><br>
2. 加入自己的Nand Flash 芯片型号 <br><br>
在include/linux/mtd/ nand_ids.h 中的对如下结构体赋值进行修改: <br><br>
static struct nand_flash_dev nand_flash_ids[] = { <br><br>
...... <br><br>
{"Samsung K9F1208U0B", NAND_MFR_SAMSUNG, 0x76, 26, 0, 4, 0x4000, 0}, <br><br>
....... <br><br>
} <br><br>
这样对于该款Nand Flash 芯片的操作才能正确执 。 <br><br>
3. 编写自己的Nand Flash 初始化函数 <br><br>
在board/crane2410/crane2410.c 中加入nand_init()函数. <br><br>
void nand_init(void) <br><br>
{ <br><br>
/* 初始化Nand Flash 控制器, 以及Nand Flash 芯片 */ <br><br>
nand_reset(); <br><br>
/* 调用nand_probe()来检测芯片类型 */ <br><br>
printf ("%4lu MB/n", nand_probe(CFG_NAND_BASE) >> 20); <br><br>
} <br><br>
该函数在启动时被start_armboot()调用. <br><br>
4 在 Linux对Nand Flash 的支持 <br><br>
4.1 Linux 下Nand Flash 调用关系 <br><br>
4.1.1 Nand Flash设备添加时数据结构包含关系 <br><br>
struct mtd_partition partition_info[] <br><br>
--> struct s3c2410_nand_set nandset <br><br>
--> struct s3c2410_platform_nand superlpplatfrom <br><br>
--> struct platform_device s3c_device_nand <br><br>
在该数据结构的name字段的初始化值"s3c2410-nand",必须与Nand Flash设备驱动注册时 <br><br>
struct device_driver结构中的name字段相同,因为platfrom bus是依靠名字来匹配的. <br><br>
--> struct platform_device *smdk2410_devices[] <br><br>
4.1.2 Nand Flash设备注册时数据结构包含关系 <br><br>
struct device_driver s3c2410_nand_driver <br><br>
-->struct device *dev <br><br>
该数据构由系统分配. <br><br>
-->struct platform_device *pdev <br><br>
-->struct s3c2410_platform_nand *plat <br><br>
-->struct s3c2410_nand_set nset <br><br>
-->struct mtd_partition <br><br>
4.1.3 当发生系统调用时数据结构调用关系 <br><br>
struct mtd_info <br><br>
它的*priv指向chip <br><br>
-->struct nand_chip <br><br>
它的*priv指向nmtd <br><br>
-->struct s3c2410_nand_mtd <br><br>
它是s3c2410_nand_info 的一个字段 <br><br>
-->s3c2410_nand_info <br><br>
它被设为Nand Flash设备驱动的私有数据结构,在Nand Flash设备驱动注册时分配空间. <br><br>
-->struct device <br><br><br><br>
4.2 Linux 下Nand Flash 驱动主要数据结构说明 <br><br>
4.2.1 s3c2410专有数据结构 <br><br>
1. s3c2410_nand_set <br><br>
struct s3c2410_nand_set { <br><br>
int nr_chips; /* 芯片的数目 */ <br><br>
int nr_partitions; /* 分区的数目 */ <br><br>
char *name; /* 集合名称 */ <br><br>
int nr_map; /* 可选, 底层逻辑到物理的芯片数目 */ <br><br>
struct mtd_partition partitions; /* 分区列表 */ <br><br>
}; <br><br>
2. s3c2410_platform_and <br><br>
struct s3c2410_platform_nand { <br><br>
/* timing information for controller, all times in nanoseconds */ <br><br>
int tacls; /* 从CLE/ALE有效到 nWE/nOE 的时间 */ <br><br>
int twrph0; /* nWE/nOE 的有效时间 */ <br><br>
int twrph1; /* 从释放CLE/ALE 到nWE/nOE不活动的时间 */ <br><br>
int nr_sets; /* 集合数目 */ <br><br>
struct s3c2410_nand_set sets; /* 集合列表 */ <br><br>
/* 根据芯片编号选择有效集合 */ <br><br>
void (*select_chip)(struct s3c2410_nand_set , int chip); <br><br>
}; <br><br>
3. s3c2410_nand_mtd <br><br>
在drivers/mtd/nand/s3c2410.c 中, <br><br>
struct s3c2410_nand_mtd { <br><br>
struct mtd_info mtd; /* MTD 信息 */ <br><br>
struct nand_chip chip; /* nand flash 芯片信息 */ <br><br>
struct s3c2410_nand_set set; /* nand flash 集合 */ <br><br>
struct s3c2410_nand_info *info; /* nand flash 信息 */ <br><br>
int scan_res; <br><br>
}; <br><br>
4. s3c2410_nand_info <br><br>
struct s3c2410_nand_info { <br><br>
/* mtd info */ <br><br>
struct nand_hw_control controller; /* 硬件控制器 */ <br><br>
struct s3c2410_nand_mtd *mtds; /* MTD 设备表 */ <br><br>
struct s3c2410_platform_nand platform; /* Nand 设备的平台 */ <br><br>
/* device info */ <br><br>
struct device *device; /* 设备指针 */ <br><br>
struct resource *area; /* 资源指针 */ <br><br>
struct clk *clk; /* Nand Flash 时钟 */ <br><br>
void __iomem *regs; /* 寄存器基地址(map后的逻辑地址) */ <br><br>
int mtd_count; /* MTD的数目 */ <br><br>
unsigned char is_s3c2440; <br><br>
}; <br><br>
5. struct clk <br><br>
在arch/arm/mach-s3c2410/clock.h 中 <br><br>
struct clk { <br><br>
struct list_head list; /* clock 列表结点 */ <br><br>
struct module *owner; /* 所属模块 */ <br><br>
struct clk *parent; /* 父结点 */ <br><br>
const char *name; /* 名称 */ <br><br>
int id; /* 编号 */ <br><br>
atomic_t used; /* 使用者计数 */ <br><br>
unsigned long rate; /* 时钟速率 */ <br><br>
unsigned long ctrlbit; /* 控制位 */ <br><br>
int (*enable)(struct clk *, int enable); /* Clock打开方法 */ <br><br>
}; <br><br>
4.2.2 Linux 通用数据结构说明 <br><br>
1. device_driver <br><br>
include/linux/device.h <br><br>
struct device_driver { <br><br>
const char * name; /* 驱动名称 */ <br><br>
struct bus_type * bus; /* 总线类型 */ <br><br>
struct completion unloaded; /* 卸载事件通知 制 */ <br><br>
struct kobject kobj; /* sys中的对象 */ <br><br>
struct klist klist_devices; /* 设备列表 */ <br><br>
struct klist_node knode_bus; /* 总线结点列表 */ <br><br>
struct module * owner;/* 所有者 */ <br><br>
/* 设备驱动通用方法 */ <br><br>
int (*probe) (struct device * dev); /* 探测设备 */ <br><br>
int (*remove) (struct device * dev); /* 移除设备 */ <br><br>
void (*shutdown) (struct device * dev); /* 关闭设备 */ <br><br>
/* 挂起设备 */ <br><br>
int (*suspend) (struct device * dev, pm_message_t state, u32 level); <br><br>
int (*resume) (struct device * dev, u32 level); /* 恢复 */ <br><br>
}; <br><br><br>
2. platform_device <br><br>
include/linux/device.h <br><br>
struct platform_device { <br><br>
const char * name; /* 名称 */ <br><br>
u32 id; /* 设备编号, -1表示不支持同类多个设备 */ <br><br>
struct device dev; /* 设备 */ <br><br>
u32 num_resources; /* 资源数 */ <br><br>
struct resource * resource; /* 资源列表 */ <br><br>
}; <br><br>
3. resource <br><br>
struct resource { <br><br>
const char name; /* 资源名称 */ <br><br>
unsigned long start, end; /* 开始位置和结束位置 */ <br><br>
unsigned long flags; /* 资源类型 */ <br><br>
/* 资源在资源树中的父亲,兄弟和孩子 */ <br><br>
struct resource *parent, *sibling, *child; <br><br>
}; <br><br>
4. device <br><br>
include/linux/device.h <br><br>
struct device { <br><br>
struct klist klist_children; /* 在设备列表中的孩子列表 */ <br><br>
struct klist_node knode_parent; /* 兄弟结点 */ <br><br>
struct klist_node knode_driver; /* 驱动结点 */ <br><br>
struct klist_node knode_bus; /* 总线结点 */ <br><br>
struct device parent; /* 父亲 */ <br><br>
struct kobject kobj; /* sys结点 */ <br><br>
char bus_id[BUS_ID_SIZE]; <br><br>
struct semaphore sem; /* 同步驱动的信号量 */ <br><br>
struct bus_type * bus; /* 总线类型 */ <br><br>
struct device_driver *driver; /* 设备驱动 */ <br><br>
void *driver_data; /* 驱动的私有数据 */ <br><br>
void *platform_data; /* 平台指定的数据,为device核心驱动保留 */ <br><br>
void *firmware_data; /* 固件指定的数据,为device核心驱动保留 */ <br><br>
struct dev_pm_info power; /* 设备电源管理信息 */ <br><br>
u64 *dma_mask; /* DMA掩码 */ <br><br>
u64 coherent_dma_mask; <br><br>
struct list_head dma_pools; /* DMA缓冲池 */ <br><br>
struct dma_coherent_mem *dma_mem; /* 连续DMA 内存的起始位置 */ <br><br>
void (*release)(struct device * dev); /* 释放设置方法 */ <br><br>
}; <br><br>
5. nand_hw_control <br><br>
include/linux/mtd/nand.h <br><br>
struct nand_hw_control { <br><br>
spinlock_t lock; /* 自旋锁,用于硬件控制 */ <br><br>
struct nand_chip *active; /* 正在处理MTD设备 */ <br><br>
wait_queue_head_t wq; /* 等待队列 */ <br><br>
}; <br><br>
6. nand_chip <br><br>
include/linux/mtd/nand.h <br><br>
struct nand_chip { <br><br>
void __iomem *IO_ADDR_R; /* 读地址 */ <br><br>
void __iomem *IO_ADDR_W; /* 写地址 */ <br><br>
/* 字节操作 */ <br><br>
u_char (*read_byte)(struct mtd_info *mtd); /* 读一个字节 */ <br><br>
void (*write_byte)(struct mtd_info *mtd, u_char byte); /* 写一个字节 */ <br><br>
/* 双字节操作 */ <br><br>
u16 (*read_word)(struct mtd_info mtd); /* 读一个字 */ <br><br>
void (*write_word)(struct mtd_info *mtd, u16 word); /* 写一个字 */ <br><br>
/* buffer操作 */ <br><br>
void (*write_buf)(struct mtd_info *mtd, const u_char *buf, int len); <br><br>
void (*read_buf)(struct mtd_info *mtd, u_char *buf, int len); <br><br>
int (*verify_buf)(struct mtd_info *mtd, const u_char *buf, int len); <br><br>
/* 选择一个操作芯片 */ <br><br>
void (*select_chip)(struct mtd_info *mtd, int chip); <br><br>
/* 坏块检查操作 */ <br><br>
int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip); <br><br>
/* 坏块标记操作 */ <br><br>
int (*block_markbad)(struct mtd_info *mtd, loff_t ofs); <br><br>
/* 硬件控制操作 */ <br><br>
void (*hwcontrol)(struct mtd_info *mtd, int cmd); <br><br>
/* 设备准备操作 */ <br><br>
int (*dev_ready)(struct mtd_info *mtd); <br><br>
/* 命令发送操作 */ <br><br>
void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int <br><br>
page_addr); <br><br>
/* 等待命令完成 */ <br><br>
int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this, int state); <br><br>
/* 计算ECC码操作 */ <br><br>
int (*calculate_ecc)(struct mtd_info *mtd, const u_char *dat, u_char <br><br>
*ecc_code); <br><br>
/* 数据纠错操作 */ <br><br>
int (*correct_data)(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, <br><br>
u_char *calc_ecc); <br><br>
/* 开启硬件ECC */ <br><br>
void (*enable_hwecc)(struct mtd_info *mtd, int mode); <br><br>
/* 擦除操作 */ <br><br>
void (*erase_cmd)(struct mtd_info *mtd, int page); <br><br>
/* 检查坏块表 */ <br><br>
int (*scan_bbt)(struct mtd_info *mtd); <br><br>
int eccmode; /* ECC模式 */ <br><br>
int eccsize; /* ECC 计算时使用的字节数 */ <br><br>
int eccbytes; /* ECC 码的字节数 */ <br><br>
int eccsteps; /* ECC 码计算的步骤数 */ <br><br>
int chip_delay; /* 芯片的延迟时间 */ <br><br>
spinlock_t chip_lock; /* 芯片访问的自旋锁 */ <br><br>
wait_queue_head_t wq; /* 芯片访问的等待队列 */ <br><br>
nand_state_t state; /* Nand Flash状态 */ <br><br>
int page_shift; /* 页右移的位数,即column地址位数 */ <br><br>
int phys_erase_shift; /* 块右移的位数, 即column和页一共的地址位数 */ <br><br>
int bbt_erase_shift; /* 坏块页表的位数 */ <br><br>
int chip_shift; /* 该芯片总共的地址位数 */ <br><br>
u_char *data_buf; /* 数据缓冲区 */ <br><br>
u_char *oob_buf; /* oob 缓冲区 */ <br><br>
int oobdirty; /* oob 缓冲区是否需要重新初始化 */ <br><br>
u_char *data_poi; /* 数据缓冲区指针 */ <br><br>
unsigned int options; /* 芯片专有选项 */ <br><br>
int badblockpos;/* 坏块标示字节在OOB 中的位置 */ <br><br>
int numchips; /* 芯片的个数 */ <br><br>
unsigned long chipsize; /* 在多个芯片组中, 一个芯片的大小 */ <br><br>
int pagemask; /* 每个芯片页数的屏蔽字, 通过它取出每个芯片包含多少个页 */ <br><br>
int pagebuf; /* 在页缓冲区中的页号 */ <br><br>
struct nand_oobinfo *autooob; /* oob信息 */ <br><br>
uint8_t *bbt; /* 坏块页表 */ <br><br>
struct nand_bbt_descr *bbt_td; /* 坏块表描述 */ <br><br>
struct nand_bbt_descr *bbt_md; /* 坏块表镜像描述 */ <br><br>
struct nand_bbt_descr *badblock_pattern; /* 坏块检测模板 */ <br><br>
struct nand_hw_control *controller; /* 硬件控制 */ <br><br>
void *priv; /* 私有数据结构 */ <br><br>
/* 进行附加错误检查 */ <br><br>
int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int <br><br>
status, int page); <br><br>
}; <br><br>
7. mtd_info <br><br>
include/linux/mtd/mtd.h <br><br>
struct mtd_info { <br><br>
u_char type; /* 设备类型 */ <br><br>
u_int32_t flags; /* 设备标志位组 */ <br><br>
u_int32_t size; /* 总共设备的大小 */ <br><br>
u_int32_t erasesize; /* 擦除块的大小 */ <br><br>
u_int32_t oobblock; /* OOB块的大小,如:512个字节有一个OOB */ <br><br>
u_int32_t oobsize; /* OOB数据的大小,如:一个OOB块有16个字节 */ <br><br>
u_int32_t ecctype; /* ECC校验的类型 */ <br><br>
u_int32_t eccsize; /* ECC码的大小 */ <br><br>
char *name; /* 设备名称 */ <br><br>
int index; /* 设备编号 */ <br><br>
/* oobinfo信息,它可以通过 MEMSETOOBINFO ioctl命令来设置 */ <br><br>
struct nand_oobinfo oobinfo; <br><br>
u_int32_t oobavail; /* OOB区的有效字节数,为文件系统提供 */ <br><br>
/* 数据擦除边界信息 */ <br><br>
int numeraseregions; <br><br>
struct mtd_erase_region_info *eraseregions; <br><br>
u_int32_t bank_size; /* 保留 */ <br><br>
/* 擦除操作 */ <br><br>
int (*erase) (struct mtd_info *mtd, struct erase_info *instr); <br><br>
/* 指向某个执行代码位置 */ <br><br>
int (*point) (struct mtd_info *mtd, loff_t from, <br><br>
size_t len, size_t *retlen, u_char **mtdbuf); <br><br>
/* 取消指向 */ <br><br>
void (*unpoint) (struct mtd_info *mtd, u_char * addr, loff_t from, size_t len); <br><br>
/* 读/写操作 */ <br><br>
int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); <br><br>
int (*write) (struct mtd_info *mtd, loff_t to, size_t len, <br><br>
size_t *retlen, const u_char *buf); <br><br>
/* 带ECC码的读/写操作 */ <br><br>
int (*read_ecc) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, <br><br>
u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel); <br><br>
int (*write_ecc) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, <br><br>
const u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel); <br><br>
/* 带OOB码的读/写操作 */ <br><br>
int (*read_oob) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, <br><br>
u_char *buf); <br><br>
int (*write_oob) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, <br><br>
const u_char *buf); <br><br>
/* 提供访问保护寄存器区的方法 */ <br><br>
int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len); <br><br>
int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, <br><br>
size_t *retlen, u_char *buf); <br><br>
int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len); <br><br>
int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, <br><br>
size_t *retlen, u_char *buf); <br><br>
int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, <br><br>
size_t *retlen, u_char *buf); <br><br>
int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len); <br><br>
/* 提供readv和writev方法 */ <br><br>
int (*readv) (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, <br><br>
loff_t from, size_t *retlen); <br><br>
int (*readv_ecc) (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, <br><br>
loff_t from, size_t *retlen, u_char *eccbuf, <br><br>
struct nand_oobinfo *oobsel); <br><br>
int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, <br><br>
unsigned long count, loff_t to, size_t *retlen); <br><br>
int (*writev_ecc) (struct mtd_info *mtd, const struct kvec *vecs, <br><br>
unsigned long count, loff_t to, size_t *retlen, <br><br>
u_char *eccbuf, struct nand_oobinfo *oobsel); <br><br>
/* 同步操作 */ <br><br>
void (*sync) (struct mtd_info *mtd); <br><br>
/* 芯片级支持的加/解锁操作 */ <br><br>
int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len); <br><br>
int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len); <br><br>
/* 电源管理操作 */ <br><br>
int (*suspend) (struct mtd_info *mtd); <br><br>
void (*resume) (struct mtd_info *mtd); <br><br>
/* 坏块管理操作 */ <br><br>
int (*block_isbad) (struct mtd_info *mtd, loff_t ofs); <br><br>
int (*block_markbad) (struct mtd_info *mtd, loff_t ofs); <br><br>
/* 重启前的通知事件 */ <br><br>
struct notifier_block reboot_notifier; <br><br>
void *priv; /* 私有数据结构 */ <br><br>
struct module *owner; /* 模块所有者 */ <br><br>
int usecount; /* 使用次数 */ <br><br>
}; <br><br>
4.3 Linux 下Nand Flash 驱动说明 <br><br>
4.3.1 注册driver_register <br><br>
通过module_init(s3c2410_nand_init);注册Nand Flash 驱动. 在 s3c2410_nand_init ()中通过driver_register()注册 <br><br>
s3c2410_nand_driver驱动程序,如下所示: <br><br>
static struct device_driver s3c2410_nand_driver = { <br><br>
.name = "s3c2410-nand", <br><br>
.bus = &platform_bus_type, /* 在drivers/base/platform.c 中定义 */ <br><br>
.probe = s3c2410_nand_probe, <br><br>
.remove = s3c2410_nand_remove, <br><br>
}; <br><br>
4.3.2 探测设备probe <br><br>
在注册的Nand Flash驱动程序中, probe 方法为s3c2410_nand_probe(). s3c2410_nand_probe()再调用 <br><br>
s3c24xx_nand_probe(). 在该函数中, 把*info 作为Nand Flash 驱动的私有数据结构, 并通过dev_set_drvdata(dev, <br><br>
info)把*info 保存在*device 的*driver_data 字段中.然后通过clk_get(dev, "nand")获取Nand Flash的时钟资 <br><br>
源, clk_use(info->clk)增加时钟资源的使用计数, clk_enable(info->clk)开启资源.填写*info 的其它字段, <br><br>
其中包括: <br><br>
1. 通过request_mem_region()为Nand Flash 寄存器区申请I/O 内存地址空间区,并通过ioremap()把它映射到虚 <br><br>
拟地址空间. <br><br>
2. 调用s3c2410_nand_inithw()初始化Nand Flash 控制器.</p>
<p>3. 为mtd 设备分配设备信息的存储空间. <br><br>
4. 对当前mtd 设备,调用s3c2410_nand_init_chip()进行初始化. <br><br>
5. 对当前mtd 设备, 调用nand_scan()检测Nand Flash 芯片, nand_scan()函数在drivers/mtd/nand/nand_base.c 中 <br><br>
定义.该函数的作用是初始化struct nand_chip 中一些方法, 并从Nand Flash 中读取芯片ID, 并初始化struct <br><br>
mtd_info 中的方法. <br><br>
6. 对当前mtd 设备,加入其分区信息. <br><br>
7. 如果还有更多mtd 设备,到4 执 . <br><br>
4.3.3 初始化 Nand Flash控制器 <br><br>
s3c2410_nand_inithw()函数会初始化Nand Flash 控制器, 通过设置Nand Flash 控制寄存器(S3C2410_NFCONF)来 <br><br>
完成, 这里最重要的是根据S3C2410 的PCLK 计算出tacls, twrph0 以及twrph1 值. <br><br>
4.3.4 移除设备 <br><br>
s3c2410_nand_remove()当设备被移除时,被device 核心驱动调用.它完成的主要工作如下: <br><br>
1. 把*device 的*driver_data 字段置空. <br><br>
2. 释放mtd 设备信息. <br><br>
3. 释放clk 资源. <br><br>
4. 通过iounmap()取消映地址空间. <br><br>
5. 释放申请的I/O 内存资源. <br><br>
6. 释放设备私有数据*info 的空间. <br><br>
4.3.5 Nand Flash芯片初始化 <br><br>
s3c2410_nand_init_chip()初始化struct nand_chip 中的一些主要字段以及方法.其中主要包括的方法有: <br><br>
1. s3c2410_nand_hwcontrol(); 硬件控制 <br><br>
2. s3c2410_nand_devready(); 设备是否准备好 <br><br>
3. s3c2410_nand_write_buf(); 写一个buffer 到nand flash <br><br>
4. s3c2410_nand_read_buf(); 读一个buffer 到nand flash <br><br>
5. s3c2410_nand_select_chip(); 选择操作芯片 <br><br>
如果支持ECC 硬件校验,还设置如下方法: <br><br>
1. s3c2410_nand_correct_data(); 通过ECC 码校正数据 <br><br>
2. s3c2410_nand_enable_hwecc(); 开启硬件ECC 检查 <br><br>
3. s3c2410_nand_calculate_ecc(); 计算ECC 码 <br><br>
4.3.6 读Nand Flash <br><br>
当对nand flash 的设备文件(nand flash 在/dev 下对应的文件)执行系统调用read(),或在某个文件系统中对该 <br><br>
设备进 读操作时. 会调用struct mtd_info 中的read方法,他们缺省调用函数为nand_read(),在 <br><br>
drivers/mtd/nand/nand_base.c 中定义.nand_read()调用nand_do_read_ecc(),执行读操作. 在 <br><br>
nand_do_read_ecc()函数中,主要完成如下几项工作: <br><br>
1. 会调用在nand flash驱动中对struct nand_chip重载的select_chip方法,即 <br><br>
s3c2410_nand_select_chip()选择要操作的MTD 芯片. <br><br>
2. 会调用在struct nand_chip 中系统缺省的方法cmdfunc发送读命令到nand flash. <br><br>
3. 会调用在nand flash驱动中对struct nand_chip重载的read_buf(),即s3c2410_nand_read_buf() <br><br>
从Nand Flash的控制器的数据寄存器中读出数据. <br><br>
4. 如果有必要的话,会调用在nand flash驱动中对struct nand_chip重载的 <br><br>
enable_hwecc,correct_data 以及calculate_ecc方法,进行数据ECC校验。 <br><br>
4.3.7 写 Nand Flash <br><br>
当对nand flash 的设备文件(nand flash 在/dev 下对应的文件)执行系统调用write(),或在某个文件系统中对该设备 <br><br>
进 读操作时, 会调用struct mtd_info 中write方法,他们缺省调用函数为nand_write(),这两个函数在 <br><br>
drivers/mtd/nand/nand_base.c 中定义. nand_write()调用nand_write_ecc(),执行写操作.在 <br><br>
nand_do_write_ecc()函数中,主要完成如下几项工作: <br><br>
1. 会调用在nand flash驱动中对struct nand_chip重载的select_chip方法,即 <br><br>
s3c2410_nand_select_chip()选择要操作的MTD 芯片. <br><br>
2. 调用nand_write_page()写一个页. <br><br>
3. 在nand_write_page()中,会调用在struct nand_chip 中系统缺省的方法cmdfunc发送写命令 <br><br>
到nand flash. <br><br>
4. 在nand_write_page()中,会调用在nand flash驱动中对struct nand_chip重载的 <br><br>
write_buf(),即s3c2410_nand_write_buf()从Nand Flash的控制器的数据寄存器中写入数据. <br><br>
5. 在nand_write_page()中,会调用在nand flash驱动中对struct nand_chip重载waitfunc方法, <br><br>
该方法调用系统缺省函数nand_wait(),该方法获取操作状态,并等待nand flash操作完成.等 <br><br>
待操作完成,是调用nand flash驱动中对struct nand_chip 中重载的dev_read 方法,即 <br><br>
s3c2410_nand_devready()函数.</p>
<p>
作者介绍:本文由尚观科技老师和同学生(刘勇,孙贺,聂强,聂大鹏 ,牛须乐,孙磊)共同创作</p>
<p><span style="color: #000099;"><strong>原文地址</strong>
</span>
<a href="http://www.uplinux.com/shizi/wenxian/109.html" target="_blank">http://www.uplinux.com/shizi/wenxian/109.html</a>
</p>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值