Nand flash驱动的编写与移植
发布时间:2020-12-15 18:22:40 所属栏目:百科 来源:网络整理
导读:1 Nand flash工作原理 ??? S3C2410板的Nand?Flash支持由两部分组成:Nand?Flash控制器(集成在S3C2410?CPU)和Nand?Flash存储 芯片(K9F1208U0B)两大部分组成。当要访问Nand Flash中的数据时,必须通过Nand Flash控制器发送命 令才能完成。所以,Nand Flash相当于
1 Nand flash工作原理
??? S3C2410板的Nand?Flash支持由两部分组成:Nand?Flash控制器(集成在S3C2410?CPU)和Nand?Flash存储 芯片(K9F1208U0B)两大部分组成。当要访问Nand Flash中的数据时,必须通过Nand Flash控制器发送命 令才能完成。所以,Nand Flash相当于S3C2410的一个外设,而不位于它的内存地址区. 1.1 Nand flash芯片工作原理 ???? Nand?flash芯片型号为Samsung?K9F1208U0B,数据存储容量为64MB,采用块页式存储管理。8个I/O 引脚充当数据、地址、命令的复用端口。 1.1.1 芯片内部存储布局及存储操作特点 ??? 一片Nand?flash为一个设备(device),?其数据存储分层为: 1设备(Device)?=?4096?块(Blocks) 1块(Block)?=?32页/行(Pages/rows)???;页与行是相同的意思,叫法不一样 1块(Page)?=?528字节(Bytes)?=?数据块大小(512Bytes)?+?OOB块大小(16Bytes) ??? 在每一页中,最后16个字节(又称OOB)用于Nand?Flash命令执行完后设置状态用,剩余512个字节又 分为前半部分和后半部分。可以通过Nand?Flash命令00h/01h/50h分别对前半部、后半部、OOB进行定位通过 Nand?Flash内置的指针指向各自的首地址。 存储操作特点: 1.?擦除操作的最小单位是块。 2.?Nand?Flash芯片每一位(bit)只能从1变为0,而不能从0变为1,所以在对其进行写入操作之前要一定将相应 块擦除(擦除即是将相应块得位全部变为1). 3.?OOB部分的第六字节(即517字节)标志是否是坏块,如果不是坏块该值为FF,否则为坏块。 4.?除OOB第六字节外,通常至少把OOB的前3个字节存放Nand?Flash硬件ECC码(关于硬件ECC码请参看 Nandflash?控制器一节). 1.1.2 重要芯片引脚功能 ??? I/O0-I/O7:复用引脚。可以通过它向nand?flash芯片输入数据、地址、nand?flash命令以及输出数据和操作 状态信息。 ??? CLE(Command?Latch?Enable):??命令锁存允许 ??? ALE(Address?Lactch?Enable):?地址锁存允许 ??? -CE:?芯片选择???? ??? -RE:?读允许 ??? -WE:?写允许 ??? -WP:?在写或擦除期间,提供写保护 ??? R/-B:?读/忙输出 1.1.3 寻址方式 ?? Samsung?K9F1208U0B?Nand?Flash?片内寻址采用26位地址形式。从第0位开始分四次通过I/O0-I/O7进行 传送,并进行片内寻址。具体含义如下: ?? 0-7位:字节在上半部、下半部及OOB内的偏移地址 ?? 8位:值为0代表对一页内前256个字节进行寻址 ???????? 值为1代表对一页内后256个字节进行寻址 ?? 9-13位:对页进行寻址?? 14-25位:对块进行寻址 ?? 当传送地址时,从位0开始 1.1.4 Nand flash主要内设命令详细介绍 Nand?Flash命令执行是通过将命令字送到Nand?Flash控制器的命令寄存器来执行。 Nand?Flash的命令是分周期执行的,每条命令都有一个或多个执行周期,每个执行周期都有相映代码表示该周 期将要执行的动作。 主要命令有:Read?1、Read?2、Read?ID、Reset、Page?Program、Block?Erase、Read?Status。 详细介绍如下: 1.?Read?1: 功能:表示将要读取Nand?flash存储空间中一个页的前半部分,并且将内置指针定位到前半部分的第一个字节。 命令代码:00h 2.?Read?2: 功能:表示将要读取Nand?flash存储空间中一个页的后半部分,并且将内置指针定位到后半部分的第一个字节。 命令代码:01h 3.?Read?ID: 功能:读取Nand?flash芯片的ID号 命令代码:90h 4.?Reset: 功能:重启芯片。 命令代码:FFh 5.?Page?Program: 功能:对页进行编程命令,?用于写操作。 命令代码:首先写入00h(A区)/01h(B区)/05h(C区),?表示写入那个区;?再写入80h开始编程模式(写入模式),接 下来写入地址和数据;?最后写入10h表示编程结束. 6.?Block?Erase 功能:块擦除命令。 命令代码:首先写入60h进入擦写模式,然后输入块地址;?接下来写入D0h,?表示擦写结束. 7.?Read?Status 功能:读取内部状态寄存器值命令。 命令代码:70h 1.2 Nand Flash 控制器工作原理 ?? 对Nand?Flash存储芯片进行操作,?必须通过Nand?Flash控制器的专用寄存器才能完成。所以,不能对Nand? Flash进行总线操作。而Nand?Flash的写操作也必须块方式进行。对Nand?Flash的读操作可以按字节读取。 1.2.1 Nand Flash控制器特性 1.?支持对Nand?Flash芯片的读、检验、编程控制 2.?如果支持从Nand?Flash启动,?在每次重启后自动将前Nand?Flash的前4KB数据搬运到ARM的内部RAM中 3.?支持ECC校验 1.2.2 Nand Flash控制器工作原理 ?? Nand?Flash控制器在其专用寄存器区(SFR)地址空间中映射有属于自己的特殊功能寄存器,就是通过将Nand? Flash芯片的内设命令写到其特殊功能寄存器中,从而实现对Nand?flash芯片读、检验和编程控制的。特殊功能 寄存器有:NFCONF、NFCMD、NFADDR、NFDATA、NFSTAT、NFECC。寄存详细说明见下一节。 1.3 Nand flash 控制器中特殊功能寄存器详细介绍 1.?配置寄存器(NFCONF)功能:用于对Nand?Flash控制器的配置状态进行控制。 在地址空间中地址:0x4E000000,其中: Bit15:Nand?Flash控制器使能位,置0代表禁止Nand?Flash控制器,置1代表激活Nand?Flash控制器; 要想访问Nand?Flash芯片上存储空间,必须激活Nand?Flash控制器。在复位后该位自动置0,因此在初始化时 必须将该位置为1。 Bit12:初始化ECC位,置1为初始化ECC;置0为不初始化ECC。 Bit11:Nand?Flash芯片存储空间使能位,置0代表可以对存储空间进行操作;置1代表禁止对存储空 间进行操作。在复位后,该位自动为1。 Bit10-8:TACLS位。根据此设定CLE&ALE的周期。TACLS的值范围在0-7之间。 Bit6-4、2-0分别为:TWRPH0、TWRPH1位。设定写操作的访问周期。其值在0-7之间。 2.?命令寄存器(NFCMD) 功能:用于存放Nand?flash芯片内设的操作命令。 在地址空间中地址:0x4E000004,其中: Bit0-7:存放具体Nand?flash芯片内设的命令值。其余位保留以后用。 3.?地址寄存器(NFADDR) 功能:用于存放用于对Nand?flash芯片存储单元寻址的地址值。 在地址空间中地址:0x4E000008,其中: Bit0-7:用于存放地址值。因为本款Nand?flash芯片只有I/O0-7的地址/数据复用引脚且地址是四周 期每次8位送入的,所以这里只用到8位。其余位保留待用。 4.?数据寄存器(NFDATA) 功能:Nand?flash芯片所有内设命令执行后都会将其值放到该寄存器中。同时,读出、写入Nand?flash 存储空间的值也是放到该寄存器。 在地址空间中地址:0x4E00000C,其中: Bit0-7:用于存放需要读出和写入的数据。其余位保留代用。 5.?状态寄存器(NFSTAT) 功能:用于检测Nand?flash芯片上次对其存储空间的操作是否完成。 在地址空间中地址:0x4E000010,其中: Bit0:置0表示Nand?flash芯片正忙于上次对存储空间的操作;置1表示Nand?flash芯片准备好接收新 的对存储空间操作的请求。 6.?ECC校验寄存器(NFECC) 功能:ECC校验寄存器 在地址空间中地址:0x4E000014,其中: ??????? Bit0-Bit7:?ECC0 ??????? Bit8-Bit15:?ECC1 ??????? Bit16-Bit23:?ECC2 1.4 Nand Flash 控制器中的硬件ECC介绍 1.4.1 ECC产生方法 ?? ECC是用于对存储器之间传送数据正确进行校验的一种算法,分硬件ECC和软件ECC算法两种,在 S3C2410的Nand?Flash?控制器中实现了由硬件电路(ECC?生成器)实现的硬件ECC。1.4.2 ECC生成器工作过程 ?? 当写入数据到Nand?flash存储空间时, ECC生成器会在写入数据完毕后自动生成ECC码,将其放入到 ECC0-ECC2。当读出数据时Nand?Flash?同样会在读数据完毕后,自动生成ECC码将其放到ECC0-ECC2当 中。 1.4.3 ECC的运用 ??? 当写入数据时,可以在每页写完数据后将产生的ECC码放入到OOB指定的位置(Byte?6)去,这样就完成了 ECC码的存储。这样当读出该页数据时,将所需数据以及整个OOB读出,然后将指定位置的ECC码与读出数 据后在ECC0-ECC1的实际产生的ECC码进行对比,如果相等则读出正确,若不相等则读取错误需要进行重 读。 2 在ADS下flash烧写程序 2.1 ADS下flash烧写程序原理及结构 ??? 基本原理:在windows环境下借助ADS仿真器将在SDRAM中的一段存储区域中的数据写到Nand?flash存 储空间中。烧写程序在纵向上分三层完成: ??? 第一层:?主烧写函数(完成将在SDRAM中的一段存储区域中的数据写到Nand?flash存储空间中); ??? 第二层:?为第一层主烧写函数提供支持的对Nand?flash进行操作的页读、写,块擦除等函数; ??? 第三层:为第二层提供具体Nand?flash控制器中对特殊功能寄存器进行操作的核心函数,该层也是真正的 将数据能够在SDRAM和Nand?flash之间实现传送的函数。 ??? 下面对其三层进行分述: 2.2 第三层实现说明 2.1.1 特殊功能寄存器定义 #define?rNFCONF??????(*(volatile?unsigned?int?*)0x4e000000)? #define?rNFCMD???????(*(volatile?unsigned?char?*)0x4e000004)???????????? #define?rNFADDR?????(*(volatile?unsigned?char?*)0x4e000008)???????????? #define?rNFDATA?????(*(volatile?unsigned?char?*)0x4e00000c)??????????? #define?rNFSTAT??????(*(volatile?unsigned?int?*)0x4e000010)?????? #define?rNFECC????????(*(volatile?unsigned?int?*)0x4e000014)????? #define?rNFECC0?????(*(volatile?unsigned?char??*)0x4e000014) #define?rNFECC1?????(*(volatile?unsigned?char?*)0x4e000015) #define?rNFECC2?????(*(volatile?unsigned?char?*)0x4e000016) 2.1.2 操作的函数实现 1.?发送命令 #define?NF_CMD(cmd) ??????{rNFCMD=cmd;} 2.?写入地址 #define?NF_ADDR(addr) ??????{rNFADDR=addr;} 3.?Nand?Flash芯片选中 #define?NF_nFCE_L() ??????{rNFCONF&=~(1<<11);} 4.?Nand?Flash芯片不选中 #define?NF_nFCE_H() ??????{rNFCONF|=(1<<11);} 5.?初始化ECC #define?NF_RSTECC() ??????{rNFCONF|=(1<<12);} 6.?读数据#define?NF_RDDATA()? ??????(rNFDATA) 7.?写数据 #define?NF_WRDATA(data)?{rNFDATA=data;} 8.?获取Nand?Flash芯片状态 #define?NF_WAITRB()?????????{while(!(rNFSTAT&(1<<0)));} 0/假:?表示Nand?Flash芯片忙状态 1/真:表示Nand?Flash已经准备好 2.3 第二层实现说明 2.3.1 Nand Flash 初始化 void?NF_Init(void) { ??? /*?设置Nand?Flash配置寄存器,?每一位的取值见1.3节 */ ??? rNFCONF=(1<<15)|(1<<14)|(1<<13)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0); ??? /*?复位外部Nand?Flash芯片 */ ? ??? NF_Reset(); } 2.3.2 Nand flash复位 static?void?NF_Reset(void) ??? int?i; ???? ??? NF_nFCE_L();??????????/*?片选Nand?Flash芯片*/ ??? NF_CMD(0xFF); /*?复位命令????????? */ ??? for(i=0;i<10;i++);?????/*?等待tWB?=?100ns.?????*/ ??? NF_WAITRB();???????/*?wait?200~500us;??????????*/ ??? NF_nFCE_H();?????????/*?取消Nand?Flash?选中*/ 2.3.3 获取Nand flash ID 返回值为Nand?flash芯片的ID号 unsigned?short??NF_CheckId(void) ??? unsigned?short?id; ??? NF_nFCE_L();????????????????/*?片选Nand?Flash芯片*/ ??? NF_CMD(0x90);?????????????/*?发送读ID命令到Nand?Flash芯片 */ ??? NF_ADDR(0x0);?????????????/*?指定地址0x0,芯片手册要求??? */ ??? for(i=0;i<10;i++);?????????????/*?等待tWB?=?100ns.?????????????*/ ??? id=NF_RDDATA()<<8;??/*?厂商ID(K9S1208V:0xec)???*/ ??? id|=NF_RDDATA(); ???????/*?设备ID(K9S1208V:0x76)??*/ ??? NF_nFCE_H();?????????????????/*?取消Nand?Flash?选中*/ ??? return?id; }2.3.4 Nand flash写入 以页为单位写入. 参数说明:block??块号 ????????? page???页号 ????????? buffer??指向内存中待写入Nand?flash中的数据起始位置 返回值:?????0:写错误 ???????? 1:写成功 static?int?NF_WritePage(unsigned?int?block,?unsigned?int?page,?unsigned?char?*buffer) ??? unsigned?int?blockPage?=?(block<<5)+page; ??? unsigned?char?*bufPt?=?buffer; ??? NF_RSTECC();????/*?初始化 ECC??????????????*/ ??? NF_nFCE_L();?????/*?片选Nand?Flash芯片*/ ??? NF_CMD(0x0);????/*?从A区开始写????? */ ??? NF_CMD(0x80);??/*?写第一条命令????? */ ??? NF_ADDR(0);?????/*??A0~A7?位(Column?Address)???*/ ??? NF_ADDR(blockPage&0xff); ???/*?A9-A16,?(Page?Address)?*/ ??? NF_ADDR((blockPage>>8)&0xff);???/*?A17-A24,51); font-family:Arial; font-size:14px; line-height:26px">??? NF_ADDR((blockPage>>16)&0xff);??/*?A25,??(Page?Address)?*/ ??? for(i=0;i<512;i++) ??? { NF_WRDATA(*bufPt++); /*?写一个页512字节到Nand?Flash芯片 */ ??? }?? ??? /* ??? *?OOB一共16?Bytes,?每一个字节存放什么由程序员自己定义,?通常, ??? *?我们在Byte0-Byte2存ECC检验码.?Byte6?存放坏块标志. ??? */ ??? seBuf[0]=rNFECC0;??/*?读取ECC检验码0?*/ ??? seBuf[1]=rNFECC1;??/*?读取ECC检验码1?*/ ??? seBuf[2]=rNFECC2;??/*?读取ECC检验码2?*/ ??? seBuf[5]=0xff; ?/*?非坏块标志?????? */ ??? for(i=0;i<16;i++) NF_WRDATA(seBuf[i]); /*?写该页的OOB数据块? */ ??? NF_CMD(0x10);???/*?结束写命令 */ ??? /*?等待Nand?Flash处于准备状态 */??? ??? for(i=0;i<10;i++);??? ??? NF_WAITRB();???? ? ??? /*?发送读状态命令给Nand?Flash??*/ ??? NF_CMD(0x70); ??? for(i=0;i<3;i++);???? ??? if?(NF_RDDATA()&0x1) ??? {????/*如果写有错,?则标示为坏块??? */ ???? NF_nFCE_H();??/*?取消Nand?Flash?选中*/ NF_MarkBadBlock(block); return?0; ??? }?else?{?/*?正常退出 */ ???? NF_nFCE_H();?/*?取消Nand?Flash?选中*/ ???? return?1; ??? } 2.3.5 Nand flash读取 参数说明:block:块号 ????????? page:页号 ????????? buffer:指向将要读取到内存中的起始位置 返回值:1:读成功 ??????? 0:读失败 static?int?NF_ReadPage(unsigned?int?block,51); font-family:Arial; font-size:14px; line-height:26px">??? unsigned?int?blockPage; ??? unsigned?char?ecc0,?ecc1,?ecc2; ??? unsigned?char?*bufPt=buffer; ??? unsigned?char?se[16];??? ??? page=page&0x1f; ??? blockPage=(block<<5)+page; ??? NF_RSTECC();????/*?初始化 ECC??????????????*/???? ??? NF_nFCE_L();??????/*?片选Nand?Flash芯片*/? ??? NF_CMD(0x00);???/*?从A区开始读????? */ ??? NF_ADDR(0);??????/*??A0~A7?位(Column?Address)???*/ ??? NF_ADDR(blockPage&0xff); ????/*?A9-A16,51); font-family:Arial; font-size:14px; line-height:26px">??? NF_ADDR((blockPage>>8)&0xff);????/*?A17-A24,51); font-family:Arial; font-size:14px; line-height:26px">??? /*?等待Nand?Flash处于再准备状态 */?? ??? for(i=0;i<10;i++);? ??? NF_WAITRB();????/*等待 tR(max?12us)?*/ ??? /*?读整个页,?512字节????????????? */ ???? *bufPt++=NF_RDDATA(); ??? /*?读取ECC码 */ ??? ecc0=rNFECC0; ??? ecc1=rNFECC1; ??? ecc2=rNFECC2; ??????? /*?读取该页的OOB块 */ ???? se[i]=NF_RDDATA();? ??? NF_nFCE_H();????/*?取消Nand?Flash?选中*/ ??? /*?校验ECC码,?并返回 */ ??? if(ecc0==se[0]?&&?ecc1==se[1]?&&?ecc2==se[2]) ??return?1; ??? else ???? ?return?0;?? 2.3.6 Nand flash标记坏块 如果是坏块,?通过写OOB块的Byte6把该块标记为坏块。 参数说明:block块号 返回值:1:ok,成功完成标记。 ??????? 0:表示写OOB块正确. static?int?NF_MarkBadBlock(unsigned?int?block) ??? unsigned?int?blockPage=(block<<5); ??? seBuf[0]=0xff; ??? seBuf[1]=0xff;???? ??? seBuf[2]=0xff;???? ??? seBuf[5]=0x44;???/*?设置坏块标记 */ ??? NF_nFCE_L();???/*?片选Nand?Flash芯片*/? ??? NF_CMD(0x50);???/*?从C区开始写??? */? ??? NF_CMD(0x80);???/*?发送编程命令,?让Nand?Flash处理写状态 */ ??? NF_ADDR(0x0);??/*??A0~A7?位(Column?Address)???*/ ??? /*?写OOB数据块 */ NF_WRDATA(seBuf[i]); ??? NF_CMD(0x10);????/*?结束写命令 */ ??? /*?等待NandFlash准备好 */ ??? for(i=0;i<10;i++);??/*?tWB?=?100ns.??*/ ??? NF_WAITRB();???? /*读NandFlash的写状态 */?? ??? NF_CMD(0x70);???? ??? for(i=0;i<3;i++);??/*?twhr=60ns??*/ ??? if?(NF_RDDATA()&0x1)? ???? return?0; ???? }?else?{ ??? return?1; 2.3.7 Nand Flash检查坏块 检查指定块是否是坏块. 返回值:1:指定块是坏块 ??????? 0:指定块不是坏块。 static?int?NF_IsBadBlock(U32?block) ??? U8?data; ???????? ??? blockPage=(block<<5); ??? NF_nFCE_L();?????????/*?片选Nand?Flash芯片*/? ??? NF_CMD(0x50);??????/*?Read?OOB数据块?? */ ??? NF_ADDR(517&0xf);??/*?A0~A7?位(Column?Address)???*/ ??? NF_ADDR(blockPage&0xff);?/*?A9-A16,51); font-family:Arial; font-size:14px; line-height:26px">??? for(i=0;i<10;i++); /*?wait?tWB(100ns)??*/ ??? NF_WAITRB(); ??? /*?读取读出值 */? ??? data=NF_RDDATA(); ??? NF_nFCE_H();???/*?取消Nand?Flash?选中*/ ??? /*?如果data不为0xff时,?表示该块是坏块 */ ??? if(data?!=?0xff) ???? return?0;???? 2.3.8 擦除指定块中数据 参数说明:block?块号 返回值:0:擦除错误。(若是坏块直接返回0;若擦除出现错误则标记为坏块然后返回0)??1:成功擦除。 static?int?NF_EraseBlock(unsigned?int?block) ??? /*?如果该块是坏块,?则返回 */ ??? if(NF_IsBadBlock(block)) ??????? return?0; ??? NF_nFCE_L();??????/*?片选Nand?Flash芯片*/ ??? NF_CMD(0x60);???/*?设置擦写模式????? */ ??? NF_CMD(0xd0);???/*?发送擦写命令,?开始擦写 */ ??? for(i=0;i<10;i++);?/*?tWB(100ns)?*/ ??? NF_WAITRB();? ??? /*?读取操作状态???????? */ ??? NF_CMD(0x70);? NF_MarkBadBlock(block);?/*?标记为坏块 */ ??? }??else??{ ???????? return?1; 2.4? 第一层的实现 2.4.1?NandFlash烧写主函数说明 参数说明: block?块号 ?????????? srcAddress?SDRAM中数据起始地址 ?????????? fileSize?要烧写的数据长度 返回值:?无 void?K9S1208_Program(unsigned?int?block,?unsigned?int?srcAddress,?unsigned?int?fileSize) ????? int??i; ???? int??programError=0; ???? U32??blockIndex; ???? U8??*srcPt,??*saveSrcPt; ???? srcPt=(U8?*)srcAddress;?/*?文件起始地址 */ ????? blockIndex?=?block;?/*?块号 */???? while(1) ???? { ?????? ??????????saveSrcPt=srcPt; ??????????? /*?如果当前块是坏块,??跳过当前块 */ ??????????if(NF_IsBadBlock(blockIndex)) ?????? ??????????{ ??? ????????????????????blockIndex++;???/*?到下一个块 */ ????????????????? continue; ?????????} ??????????? /*?在写之前,?必须先擦除,?如果擦除不成功,?跳过当前块 */ ??????????? if(!NF_EraseBlock(blockIndex)) ?????????{ ?????????????????? blockIndex++;??/*?到下一个块 */ ?? ?????????????????????continue; ??????????? /*?写一个块,?一块有32页 */ ?????????for(i=0;i<32;i++) ????????????????? /*?写入一个页,?如果出错,?停止写当前块 */ ????????????????? if(!NF_WritePage(blockIndex,i,srcPt)) ???? ???????????????????{ ?????????????????????? programError=1; ?????????????????????? break; ????????????????? } ????????????????? /*?如果操作正常,?文件的写位置加上1页偏移,到下一页的起始位置 */ ?????srcPt+=512; ????????????????? /*?如果写地址没有超过文件长度,?继续;?超出则终止写 */ ????????????????? if((U32)srcPt>=(srcAddress+fileSize)) ???break; ?????????? } ????????/*?如果写一个块时,?其中某一页写失败,?则把写地址恢复写该块之前,?并跳过当前块 */ ??????????? if(programError==1) ??????????? { ????????????????? blockIndex++; ????????????????? srcPt=saveSrcPt; ????????????????? programError=0; ??????????? } ??????????? /*?如果写地址没有超过文件长度,51); font-family:Arial; font-size:14px; line-height:26px">??????????? if((U32)srcPt?>=?(srcAddress+fileSize)) ????????????????? break; ??????????? /*?如果正常写成功,?继续写下一个块 */ ??????????? blockIndex++; ????? } }3 在U-BOOT对Nand Flash的支持 3.1 U-BOOT对从Nand Flash启动的支持 3.1.1 从Nand Flash启动U-BOOT的基本原理 1.?前4K的问题 ??? 如果S3C2410被配置成从Nand?Flash启动(配置由硬件工程师在电路板设置),?S3C2410的Nand?Flash控制器 有一个特殊的功能,?在S3C2410上电后,?Nand?Flash控制器会自动的把Nand?Flash上的前4K数据搬移到4K内部 RAM中,?并把0x00000000设置内部RAM的起始地址,?CPU从内部RAM的0x00000000位置开始启动。这个过 程不需要程序干涉。 ??? 程序员需要完成的工作,是把最核心的启动程序放在Nand?Flash的前4K中。 2.??启动程序的安排 ???? 由于Nand?Flash控制器从Nand?Flash中搬移到内部RAM的代码是有限的,所以,?在启动代码的前4K里,我 们必须完成S3C2410的核心配置以及把启动代码(UBOOT)剩余部分搬到RAM中运行。以UBOOT为例,?前4K 完成的主要工作,?见第四部分的2.2节。 3.1.2 支持Nand Flash启动代码说明 ??? 首先在include/configs/crane2410.h中加入CONFIG_S3C2410_NAND_BOOT,?如下: #define?CONFIG_S3C2410_NAND_BOOT?????1 支持从Nand?Flash中启动. 1.?执行Nand?Flash初始化 下面代码在cpu/arm920t/start.S中 #ifdef?CONFIG_S3C2410_NAND_BOOT copy_myself: mov?r10,?lr ldr?sp,?DW_STACK_START???@安装栈的起始地址 mov?fp,?#0?????????????????????????????????@初始化帧指针寄存器 bl?nand_reset?????????????????????????????@跳到复位C函数去执行 ... DW_STACK_START: .word??????STACK_BASE+STACK_SIZE-4 2.?nand_reset?C代码 下面代码被加在/board/crane2410/crane2410.c中 void?nand_reset(void) ??????? int?i; /*?设置Nand?Flash控制器 */ rNFCONF=(1<<15)|(1<<14)|(1<<13)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0); ??????? /*?给Nand?Flash芯片发送复位命令 */ ??????? NF_nFCE_L(); ??????? NF_CMD(0xFF); ??????? for(i=0;?i<10;?i++); ??????? NF_WAITRB();??????? NF_nFCE_H(); 3.??从Nand?Flash中把UBOOT拷贝到RAM @read?U-BOOT?from?Nand?Flash?to?RAM ldr???r0,?=UBOOT_RAM_BASE???@?设置第1个参数:?UBOOT在RAM中的起始地址 mov???r1,?#0x0????????????????????????????????@?设置第2个参数:Nand?Flash的起始地址 mov???r2,?#0x20000????????????????????????@?设置第3个参数:?UBOOT的长度(128KB) bl????nand_read_whole????????????????????@?调用nand_read_whole(),?该函数在board/crane2410/crane2410.c中 tst???r0,?#0x0???????????????????????????????????@?如果函数的返回值为0,表示执行成功. beq???ok_nand_read????????????????????????@?执行内存比较 4.?从Nand?Flash中把数据读入到RAM中 int?nand_read_whole(unsigned?char?*buf,?unsigned?long?start_addr,?int?size) int?i,?j; /*?如果起始地址和长度不是512字节(1页)的倍数,?则返回错误代码 */ if?((start_addr?&?NAND_BLOCK_MASK)?||?(size?&?NAND_BLOCK_MASK))?{ return?-1; /*?激活Nand?Flash?*/ NF_nFCE_L(); for(i=0;?i<10;?i++); i?=?start_addr; while(i?<?start_addr?+?size)?{ /*?读A区 */ rNFCMD?=?0; /*?写入读取地址 */ rNFADDR?=?i?&?0xff; rNFADDR?=?(i?>>?9)?&?0xff; rNFADDR?=?(i?>>?17)?&?0xff; rNFADDR?=?(i?>>?25)?&?0xff; NF_WAITRB(); ??????????????? /*?读出一页(512字节)?*/ for(j=0;?j?<?NAND_SECTOR_SIZE;?j++,?i++)?{ *buf?=?(rNFDATA?&?0xff); buf++; /*?停止驱动Nand?Flash?*/ NF_nFCE_H(); }5.??校查搬移后的数据 把RAM中的前4K与内部中前4K进行比较,?如果完全相同,?则表示搬移成功. ok_nand_read: mov?r0,?#0x00000000??????????????????@内部RAM的起始地址 ldr?r1,?=UBOOT_RAM_BASE???@UBOOT在RAM中的起始地址 mov?r2,?#0x400????????????????????????????@比较1024次,?每次4字节,?4?bytes?*?1024?=?4K-bytes go_next:?????@?比较1024次,?每次4个字节 ldr???r3,?[r0],?#4 ldr???r4,?[r1],51); font-family:Arial; font-size:14px; line-height:26px">teq???r3,?r4 bne??notmatch subs?r2,?r2,51); font-family:Arial; font-size:14px; line-height:26px">beq??done_nand_read bne??go_next notmatch: 1:b?????1b done_nand_read: mov?pc,?r10 3.2 U-BOOT对Nand Flash命令的支持 ??? 在U-BOOT下对Nand?Flash的支持主要是在命令行下实现对nand?flash的操作。对nand?flash实现的命令 为:nand?info、nand?device、nand?read、nand?write、nand?erease、nand?bad。 ??? 用到的主要数据结构有:struct?nand_flash_dev、struct?nand_chip。前者包括主要的芯片型号、存储容量、 设备ID、I/O总线宽度等信息;后者是具体对nand?flash进行操作时用到的信息。 3.2.1 主要数据结构介绍 1.?struct?nand_flash_dev数据结构 该数据结构在include/linux/mtd/nand.h中定义,在include/linux/mtd/nand_ids.h中赋初值。 struct?nand_flash_dev?{ char?*name; ?????????????/*?芯片名称 */ int?manufacture_id;??????????/*?厂商ID?????*/ int?model_id;????????????????????/*?模式ID?????*/ int?chipshift;?????????????????????/*??Nand?Flash地址位数 */ char?page256;???????????????????/*?表明是否时256字节一页。1:是;0:否。*/ char?pageadrlen; ?????????????/*?完成一次地址传送需要往NFADDR中传送几次。*/ unsigned?long?erasesize;??/*?一次块擦除可以擦除多少字节 */ int?bus16;??????????????????????????/*?地址线是否是16位,1:是;0:否 */ }; 2.?struct?nand_chip数据结构 该数据结构在include/linux/mtd/nand.h中定义.?该结构体定义出一个Nand?Flash设备数组: struct?nand_chip?nand_dev_desc[CFG_MAX_NAND_DEVICE]; 该数组在nand_probe()中对其进行初始化. struct?nand_chip?{ int? page_shift;???????????/*??Page?地址位数???????? */ u_char? *data_buf;????????????/*?本次读出的一页数据??? */ u_char? *data_cache;????????/*?读出的一页数据??????? */ int??????????cache_page;?????????/*?上次操作的页号??????? */u_char? ecc_code_buf[6];?/*?ECC校验码??????????? */ u_char? reserved[2]; char?ChipID; ???????????????/*?芯片ID号????????????? */ struct?Nand?*chips;????????????/*?Nand?Flash芯片列表,?表示支持几个芯片为一个设备*/ int?chipshift; char*?chips_name;????????????/*?Nand?Flash芯片名称???? */ unsigned?long?erasesize;???/*?块擦写的大小?????????? */ unsigned?long?mfr;???????????/*?厂商ID?????????????????????????????????*/ unsigned?long?id;??????????????/*?模式ID????????????????????????????????*/ char*?name;??????????????????????/*?设备名称??????????????? */ int?numchips;???????????????????/*?有几块Nand?Flash芯片?? */ char?page256;??????????????????/*?一页是256字节,?还是512字节 */ char?pageadrlen;??????????????/*?页地址的长度?????????? */ unsigned?long?IO_ADDR;??/*?用于对nand?flash进行寻址的地址值存放处 */ unsigned?long?totlen;????/*?Nand?Flash总共大小?????? */ uint?oobblock;??????????????/*?一页的大小。本款nand?flash为512?????????????????*/ uint?oobsize;?? ?????????/*?spare?array大小。本款nand?flash为16????????????*/ uint?eccsize;?????????????????/*?ECC?大小????????????????? */???????????? int?bus16;?????????????????????/*?地址线是否是16位,1:是;0:否??????? */ 3.2.2 支持的命令函数说明 1.?nand?info/nand?device 功能:显示当前nand?flash芯片信息。 函数调用关系如下(按先后顺序): static?void?nand_print(struct?nand_chip?*nand)?; 2.?nand?erase 功能:擦除指定块上的数据。 int?nand_erase(struct?nand_chip*?nand,?size_t?ofs,?size_t?len,?int?clean); 3.?nand?bad 功能:显示坏块。 static?void?nand_print_bad(struct?nand_chip*?nand); int?check_block?(struct?nand_chip?*nand,?unsigned?long?pos); 4.?nand?read 功能:读取nand?flash信息到SDRAM。 int?nand_rw?(struct?nand_chip*?nand,?int?cmd,size_t?start,??size_t?*?retlen,?u_char?*?buf); static?int?nand_read_ecc(struct?nand_chip?*nand,?size_t?start,? ????????????????????? size_t?*?retlen,?u_char?*buf,?u_char?*ecc_code); static?void?NanD_ReadBuf?(struct?nand_chip?*nand,?u_char?*?data_buf,?int?cntr); READ_NAND(adr); 5.?nand?write 功能:从SDRAM写数据到nand?flash中。 函数调用关系如下(按先后顺序):int?nand_rw?(struct?nand_chip*?nand,?size_t?*?retlen,51); font-family:Arial; font-size:14px; line-height:26px">static?int?nand_write_ecc?(struct?nand_chip*?nand,?size_t?to,51); font-family:Arial; font-size:14px; line-height:26px">?????????????????????? size_t?*?retlen,?const?u_char?*?buf,?u_char?*?ecc_code); static?int?nand_write_page?(struct?nand_chip?*nand,?int?page,?int?col,?int?last,51); font-family:Arial; font-size:14px; line-height:26px">WRITE_NAND(d?,?adr); 3.2.3 U-BOOT支持Nand Flash命令移植说明 1.?设置配置选项 ? 在CONFIG_COMMANDS中,?打开CFG_CMD_NAND选项. ? #define?CONFIG_COMMANDS? (CONFIG_CMD_DFL ?|? CFG_CMD_CACHE ?|? CFG_CMD_NAND ?|? /*CFG_CMD_EEPROM?|*/? /*CFG_CMD_I2C ?|*/? /*CFG_CMD_USB ?|*/? CFG_CMD_PING?????|? CFG_CMD_REGINFO??|? CFG_CMD_DATE ?|? CFG_CMD_ELF) #if?(CONFIG_COMMANDS?&?CFG_CMD_NAND) #define?CFG_NAND_BASE???????????????????0x4E000000????/*?Nand?Flash控制器在SFR区中起始寄存器地址 */ #define?CFG_MAX_NAND_DEVICE???1??????????????????????/*?支持的最在Nand?Flash数据 */ #define?SECTORSIZE?????????????????????????????512?????????????????/*?1页的大小 */ #define?NAND_SECTOR_SIZE??????????????SECTORSIZE #define?NAND_BLOCK_MASK????????????(NAND_SECTOR_SIZE?–?1)??/*?页掩码 */ #define?ADDR_COLUMN??????????????????????1????/*?一个字节的Column地址 */ #define?ADDR_PAGE?????????????????????????????3???/*?3字节的页块地址,?A9-A25*/ #define?ADDR_COLUMN_PAGE?????????4???/*??总共4字节的页块地址?? */ #define?NAND_ChipID_UNKNOWN???0x00??/*?未知芯片的ID号 */ #define?NAND_MAX_FLOORS????????????1??????? #define?NAND_MAX_CHIPS????????????????1 /*?Nand?Flash命令层底层接口函数 */ #define?WRITE_NAND_COMMAND(d,?adr)?do?{rNFCMD?=?d;}?while(0) #define?WRITE_NAND_ADDRESS(d,?adr)?do?{rNFADDR?=?d;}?while(0) #define?WRITE_NAND(d,?adr)?????????do?{rNFDATA?=?d;}?while(0) #define?READ_NAND(adr)?????????????(rNFDATA) #define?NAND_WAIT_READY(nand)??????{while(!(rNFSTAT&(1<<0)));} #define?NAND_DISABLE_CE(nand)?{rNFCONF?|=?(1<<11);} #define?NAND_ENABLE_CE(nand)?{rNFCONF?&=?~(1<<11);} /*?下面一组操作对Nand?Flash无效 */ #define?NAND_CTL_CLRALE(nandptr) #define?NAND_CTL_SETALE(nandptr) #define?NAND_CTL_CLRCLE(nandptr) #define?NAND_CTL_SETCLE(nandptr)/*?允许Nand?Flash写校验 */ #define?CONFIG_MTD_NAND_VERIFY_WRITE?1 #endif????/*?CONFIG_COMMANDS?&?CFG_CMD_NAND*/??? 2.?加入自己的Nand?Flash芯片型号 ??? 在include/linux/mtd/?nand_ids.h中的对如下结构体赋值进行修改: ?? static?struct?nand_flash_dev?nand_flash_ids[]?= {? ?????? ...... ????? {"Samsung?K9F1208U0B",????NAND_MFR_SAMSUNG,?0x76,?26,?0,?4,?0x4000,?0},51); font-family:Arial; font-size:14px; line-height:26px">?????? ....... ?? } ?? 这样对于该款Nand?Flash芯片的操作才能正确执行。 3.?编写自己的Nand?Flash初始化函数 在board/crane2410/crane2410.c中加入nand_init()函数.? void?nand_init(void) ?????? /*?初始化Nand?Flash控制器,?以及Nand?Flash?芯片 */ nand_reset();?? ?????? /*?调用nand_probe()来检测芯片类型 */ printf?("%4lu?MBn",?nand_probe(CFG_NAND_BASE)?>>?20); 该函数在启动时被start_armboot()调用. 4 在Linux对Nand Flash的支持 4.1 Linux下Nand Flash调用关系 4.1.1 Nand Flash设备添加时数据结构包含关系 ??????? struct mtd_partition????????? partition_info[] ??? --> struct s3c2410_nand_set?????? nandset ??? --> struct s3c2410_plat form_nand? superlpplatfrom? ??? --> struct platform_device???????? s3c_device_nand ?在该数据结构的name字段的初始化值"s3c2410-nand",必须与Nand Flash设备驱动注册时 ??????? struct device_driver结构中的name字段相同,因为platfrom bus是依靠名字来匹配的. ??? --> struct platform_device???????? *smdk2410_devices[] 4.1.2 Nand Flash设备注册时数据结构包含关系 ??? struct device_driver s3c2410_n and_driver? ??? -->struct device *dev ?????? 该数据构由系统分配. ??? -->struct platform_device *pdev ??? -->struct s3c2410_plat form_nand *plat ??? -->struct s3c2410_nand_set nset ??? -->struct mtd_partition 4.1.3? 当发生系统调用时数据结构调用关系 struct mtd_info它的*priv指向chip ??? -->struct nand_chip ? 它的*priv指向nmtd ??? -->struct s3c2410_nand_mtd 它是s3c2410_nand_info的一个字段 ??? -->s3c2410_nand_info 它被设为Nand Flash设备驱动的私有数据结构,在Nand Flash设备驱动注册时分配空间. ??? -->struct device 4.2 Linux下Nand Flash驱动主要数据结构说明 4.2.1 s3c2410专有数据结构 1.?s3c2410_nand_set struct s3c2410_nand_set { ??????? int???????????????????? n r_chips;????? /* 芯片的数目 */ ??????? int???????????????????? n r_partitions; /* 分区的数目 */ ??????? char??????????????????? *n ame;???????? /* 集合名称?? */ ??????? int????????????????????? nr_map;??????? /* 可选,底层逻辑到物理的芯片数目 */ ??????? struct mtd_partition??? partitions;??? /* 分区列表?? */ 2.?s3c2410_platform_and struct s3c2410_platform_nand { ??????? /* timing information for controller,all times in nanoseconds */ ??????? int???? tacls;? /* 从CLE/ALE有效到 nWE/nOE的时间 */ ??????? int???? twrph0; /* nWE/nOE的有效时间 */ ??????? int???? twrph1; /* 从释放CLE/ALE到nWE/nOE不活动的时间 */ ??????? int???? nr_sets; /* 集合数目 */ ??????? struct s3c2410_nand_set sets; /* 集合列表 */ ?/* 根据芯片编号选择有效集合 */ ??????? void (*select_chip)(struct s3c2410_nand_set,int chip); 3.??s3c2410_nand_mtd 在drivers/mtd/nand/s3c2410.c中,51); font-family:Arial; font-size:14px; line-height:26px">struct s3c2410_nand_mtd { ??????? struct mtd_info???????????????? mt d;??? /* MTD 信息 */ ??????? struct nand_chip??????????????? ch ip;?? /* nand flash 芯片信息 */ ??????? struct s3c2410_nand_set???????? set;??? /* nand flash 集合???? */ ??????? struct s3c2410_nand_info??????? *info;? /* nand flash 信息???? */ ??????? int???????????????????????????? scan _res; };? 4.?s3c2410_nand_info struct s3c2410_nand_info { ??????? /* mtd info */ ??????? struct nand_hw_control????????? co ntroller; /* 硬件控制器 */ ??????? struct s3c2410_nand_mtd???????? *mt ds;????? /* MTD 设备表 */ ??????? struct s3c2410_platform_nand??? platform;?? /* Nand 设备的平台 */??????? /* device info */ ??????? struct device?????????????????? *device;??? /*? 设备指针 */ ??????? struct resource???????????????? *area;????? /*? 资源指针 */ ??????? struct clk????????????????????? *clk;?????? /* N and Flash 时钟 */ ??????? void __iomem??????????????????? *reg s;????? /* 寄存器基地址(map后的逻辑地址) */ ??????? int???????????????????????????? mt d_count;? /* MTD的数目 */ ??????? unsigned char?????????????????? is_s3c2440; 5.?struct?clk 在arch/arm/mach-s3c2410/clock.h中 struct clk { ??????? struct list_head????? list ;???? /* clock 列表结点 */ ??????? struct module??????? *o wner;??? /* 所属模块?????? */? ??????? struct clk?????????? *paren t;?? /* 父结点???????? */ ??????? const char?????????? *n ame;???? /* 名称?????????? */ ??????? int?????????????????? id;?????? /*? 编号?????????? */ ??????? atomic_t????????????? used;???? /*? 使用者计数???? */ ??????? unsigned long???????? rate;???? /* 时钟速率?????? */ ??????? unsigned long???????? ctrlbit;? /* 控制位???????? */ ??????? int???????????????? (*en able)(struct clk *,int enable); /* Clock打开方法 */ 4.2.2 Linux 通用数据结构说明 1.?device_driver include/linux/device.h struct device_driver { ??????? const char????????????? * n ame; /* 驱动名称? */ ??????? struct bus_type???????? * b us;? /* 总线类型? */ ??????? struct completion?????? unloaded;? /* 卸载事件通知机制 */ ??????? struct kobject????????? kobj; /* sys中的对象 */ ??????? struct klist??????????? klist _devices; /* 设备列表 */ ??????? struct klist_node?????? knode_bus; /* 总线结点列表 */ ??????? struct module?????????? * o wner;/* 所有者? */ ??????? /* 设备驱动通用方法 */ ??????? int???? (*probe)??????? (struct device * dev) ; /* 探测设备 */ ??????? int???? (*remove)?????? (struct device * dev) ; /* 移除设备 */ ??????? void??? (*shutdown)???? (struct device * dev) ; /* 关闭设备 */ ??????? /* 挂起设备???????? */ ??????? int???? (*suspend)????? (struct device * dev,pm_messag e_t state,u32 level) ; ??????? int???? (*resume)??????? (struct device * dev,u32 level) ; /* 恢复 */ 2.?platform_device struct platform_device { ??????? const char????? * name;? /* 名称 */ ??????? u32???????????? id;????? /*? 设备编号,-1表示不支持同类多个设备 */ ??????? struct device?? dev;???? /* 设备 */ ??????? u32???????????? n um_resources; /* 资源数 */ ??????? struct resource * resource; /* 资源列表 */}; 3. resource struct resource { ??????? const char name;?????????? /* 资源名称 */? ??????? unsigned long start,end;? /* 开始位置和结束位置 */ ??????? unsigned long flags;?????? /* 资源类型 */ ??????? /* 资源在资源树中的父亲,兄弟和孩子 */ ??????? struct resource *parent,*sibling,*child; 4.?device struct device { ??????? struct klist??????????? klist _children;? /* 在设备列表中的孩子列表 */ ??????? struct klist_node?????? knode_parent;??? /* 兄弟结点 */ ??????? struct klist_node?????? knode_driver;??? /* 驱动结点 */ ??????? struct klist_node?????? knode_bus;?????? /* 总线结点 */ ??????? struct device??????????? parent;????????? /* 父亲???? */ ??????? struct kobject kobj;???????????????????? /* sys结点 */ ??????? char??? bus_id[BUS_ID_SIZE];??????????? ??????? struct semaphore??????? sem;??? /* 同步驱动的信号量? */ ??????? struct bus_type * bus;????????? /* 总线类型????????? */ ??????? struct device_driver *driver;?? /*? 设备驱动????????? */ ??????? void??????????? *driver_dat a;?? /* 驱动的私有数据??? */ ??????? void??????????? *plat form_data; /* 平台指定的数据,为device核心驱动保留 */ ??????? void??????????? *firmw are_data; /* 固件指定的数据,51); font-family:Arial; font-size:14px; line-height:26px">??????? struct dev_pm_info? power;????? /* 设备电源管理信息 */ ??????? u64???????????? *dma_mask;????? /* DMA掩码????????? */ ??????? u64???????????? co herent_dma_mask; ??????? struct list_head dma_pools;???? /* DMA缓冲池??????? */ ??????? struct dma_coherent_mem *dma_mem; /* 连续DMA内存的起始位置 */ ??????? void??? (*release) (struct device * dev) ;? /* 释放设置方法 */ 5. nand_hw_control include/linux/mtd/nand.h struct nand_hw_control { ??????? spinlock_t?????? lock;????? /* 自旋锁,用于硬件控制 */ ??????? struct nand_chip *active;?? /* 正在处理MTD设备??? */ ??????? wait_queue_head_t wq;?????? /* 等待队列??????????? */ 6. nand_chip struct nand_chip { ??????? void? __iomem?? *IO_ADDR_R; /* 读地址 */ ??????? void? __iomem?? *IO_ADDR_W; /* 写地址 */ /* 字节操作?? */???????? ??????? u_char????????? (*read_b yte)(struct mtd_info *mtd);? /* 读一个字节 */???????? ??????? void??????????? (*w rite_byte)(struct mtd_info *mtd,u_char byte); /* 写一个字节 */?????? ??????? /* 双字节操作 */ ??????? u16???????????? (*read_w ord)(struct mtd_info mtd);? /* 读一个字?? */ ??????? void??????????? (*w rite_word)(struct mtd_info *mtd,u16 word); /* 写一个字 */??????? ??????? /* buffer操作 */ ??????? void??????????? (*w rite_buf)(struct mtd_info *mtd,const u_char *buf,int len); ??????? void??????????? (*read_b uf)(struct mtd_info *mtd,u_char *buf,51); font-family:Arial; font-size:14px; line-height:26px">??????? int???????????? (*verify_b uf)(struct mtd_info *mtd,51); font-family:Arial; font-size:14px; line-height:26px">??????? /* 选择一个操作芯片 */???????? ??????? void??????????? (*select _chip)(struct mtd_info *mtd,51); font-family:Arial; font-size:14px; line-height:26px">??????? /* 坏块检查操作 */ ??????? int???????????? (*b lock_bad)(struct mtd_info *mtd,loff_t ofs,int getchip); ??????? /* 坏块标记操作 */ ??????? int???????????? (*b lock_markbad)(struct mtd_info *mtd,loff_t ofs); ??????? /* 硬件控制操作 */ ??????? void??????????? (*h wcontrol)(struct mtd_info *mtd,int cmd); ??????? /* 设备准备操作 */ ??????? int???????????? (*dev_ready) (struct mtd_info *mtd); ??????? /* 命令发送操作 */ ??????? void??????????? (*cmdfun c)(struct mtd_info *mtd,unsigned command,int column,int page_addr); ??????? /* 等待命令完成 */ ??????? int???????????? (*w aitfunc)(struct mtd_info *mtd,struct nand_chip *this,int state); ??????? /* 计算ECC码操作 */ ??????? int???????????? (*calculat e_ecc)(struct mtd_info *mtd,const u_char *dat,u_char *ecc_code); ??????? /* 数据纠错操作? */ ??????? int???????????? (*co rrect_data)(struct mtd_info *mtd,u_char *dat,u_char *read_ecc,51); font-family:Arial; font-size:14px; line-height:26px">u_char *calc_ecc) ; ??????? /* 开启硬件ECC */ ??????? void??????????? (*en able_hwecc)(struct mtd_info *mtd,int mode); ??????? /* 擦除操作???? */ ??????? void??????????? (*erase_cmd) (struct mtd_info *mtd,int page); ??????? /* 检查坏块表?? */ ??????? int???????????? (*scan _bbt)(struct mtd_info *mtd);???????? ??????? int???????????? eccmo de;??? /* ECC模式????? */???????? ??????? int???????????? eccsize;??? /* E CC 计算时使用的字节数 */ ??????? int???????????? eccb ytes;?? /* ECC 码的字节数 */ ??????? int???????????? eccst eps;?? /* ECC 码计算的步骤数 */ ??????? int???????????? ch ip_delay; /* 芯片的延迟时间 */ ??????? spinlock_t????? chip_lock;? /* 芯片访问的自旋锁 */ ??????? wait_queue_head_t wq;?????? /* 芯片访问的等待队列 */ ??????? nand_state_t??? state;????? /* Nand Flash状态 */ ??????? int???????????? pag e_shift; /* 页右移的位数,即column地址位数 */ ??????? int???????????? ph ys_erase_shift; /* 块右移的位数,即column和页一共的地址位数 */ ??????? int???????????? b bt_erase_shift; /* 坏块页表的位数 */ ??????? int???????????? ch ip_shift; /* 该芯片总共的地址位数 */ ??????? u_char????????? *dat a_buf;? /* 数据缓冲区? */ ??????? u_char????????? *o ob_buf;?? /* oob缓冲区? */ ??????? int???????????? o obdirty;?? /* oob缓冲区是否需要重新初始化 */ ??????? u_char????????? *dat a_poi;? /* 数据缓冲区指针 */ ??????? unsigned int??? options;??? /* 芯片专有选项 */ ??????? int???????????? b adblockpos;/* 坏块标示字节在OOB中的位置 */ ??????? int???????????? n umchips;?? /* 芯片的个数?? */??????? unsigned long?? chipsize;?? /* 在多个芯片组中,一个芯片的大小 */ ??????? int???????????? pag emask;?? /* 每个芯片页数的屏蔽字,通过它取出每个芯片包含多少个页 */ ??????? int???????????? pag ebuf;??? /* 在页缓冲区中的页号 */ ??????? struct nand_oobinfo???? *autooob; /* oob信息 */ ??????? uint8_t???????? *bbt;?????? /* 坏块页表 */ ??????? struct nand_bbt_descr?? *bbt_td; /* 坏块表描述 */ ??????? struct nand_bbt_descr?? *bbt_md; /* 坏块表镜像描述 */ ??????? struct nand_bbt_descr?? *badblock_pattern; /* 坏块检测模板 */ ??????? struct nand_hw_control? *controller; /* 硬件控制 */ ??????? void??????????? *priv; /* 私有数据结构 */ ??????? /* 进行附加错误检查 */ ??????? int???????????? (*errst at)(struct mtd_info *mtd,int state,51); font-family:Arial; font-size:14px; line-height:26px">status,51); font-family:Arial; font-size:14px; line-height:26px">7. mtd_info include/linux/mtd/mtd.h struct mtd_info { ??????? u_char type;????? /* 设备类型???????? */ ??????? u_int32_t flags;? /* 设备标志位组???? */ ??????? u_int32_t size;?? /* 总共设备的大小?? */ ??????? u_int32_t erasesize;? /* 擦除块的大小 */ ??????? u_int32_t oobblock; /* OOB块的大小,如:512个字节有一个OOB */ ??????? u_int32_t oobsize;? /* OOB数据的大小,如:一个OOB块有16个字节 */ ??????? u_int32_t ecctype;? /* ECC校验的类型 */ ??????? u_int32_t eccsize;? /* E CC码的大小?? */ ??????? char *name;???????? /* 设备名称?????? */ ??????? int index;????????? /* 设备编号?????? */ ??????? /* oobinfo信息,它可以通过 MEMSETOOBINFO ioctl命令来设置 */ ??????? struct nand_oobinfo oobinfo; ??????? u_int32_t oobavail;? /* OOB区的有效字节数,为文件系统提供 */ ??????? /* 数据擦除边界信息???????? */ ??????? int numeraseregions; ??????? struct mtd_erase_region_info *eraseregions; ??????? u_int32_t bank_size; /* 保留 */ ??????? /* 擦除操作 */ ??????? int (*erase) (struct mtd_info *mtd,struct erase_info *instr); ??????? /* 指向某个执行代码位置 */ ??????? int (*point) (struct mtd_info *mtd,loff_t from,51); font-family:Arial; font-size:14px; line-height:26px">?????????????????????? size_t len,size_t *retlen,u_char **mtdbuf); ??????? /* 取消指向 */ ??????? void (*unpoint) (struct mtd_info *mtd,u_char * addr,lo ff_t from,size_t len); ?/* 读/写操作 */ ??????? int (*read) (struct mtd_info *mtd,size_t len,u_char *buf); ??????? int (*write) (struct mtd_info *mtd,loff_t to,51); font-family:Arial; font-size:14px; line-height:26px">?????????????????????? size_t *retlen,const u_char *buf); ?/* 带ECC码的读/写操作 */ ??????? int (*read_ecc) (struct mtd_info *mtd,51); font-family:Arial; font-size:14px; line-height:26px">????????????????????????? u_char *buf,u_char *eccbuf,struct nand_oobinfo *oobsel); ??????? int (*write_ecc) (struct mtd_info *mtd,51); font-family:Arial; font-size:14px; line-height:26px">?????????????????????????? const u_char *buf,struct nand_oobinfo *oobsel);??????? /* 带OOB码的读/写操作 */ ??????? int (*read_oob) (struct mtd_info *mtd,51); font-family:Arial; font-size:14px; line-height:26px">????????????????????????? u_char *buf); ??????? int (*write_oob) (struct mtd_info *mtd,51); font-family:Arial; font-size:14px; line-height:26px">?????????????????????????? const u_char *buf); ??????? /* 提供访问保护寄存器区的方法 */ ??????? int (*get_fact_prot_info) (struct mtd_info *mtd,struct otp_info *buf,51); font-family:Arial; font-size:14px; line-height:26px">??????? int (*read_fact_prot_reg) (struct mtd_info *mtd,51); font-family:Arial; font-size:14px; line-height:26px">??????????????????????????????????? size_t *retlen,51); font-family:Arial; font-size:14px; line-height:26px">??????? int (*get_user_prot_info) (struct mtd_info *mtd,51); font-family:Arial; font-size:14px; line-height:26px">??????? int (*read_user_pro t_reg) (struct mtd_info *mtd,51); font-family:Arial; font-size:14px; line-height:26px">??????? int (*write_user_prot_reg) (struct mtd_info *mtd,51); font-family:Arial; font-size:14px; line-height:26px">???????????????????????????????????? size_t *retlen,51); font-family:Arial; font-size:14px; line-height:26px">??????? int (*lock_user_prot_reg) (struct mtd_info *mtd,51); font-family:Arial; font-size:14px; line-height:26px">??????? /* 提供readv和writev方法??????? */ ??????? int (*readv) (struct mtd_info *mtd,struct kvec *vecs,un signed long count,51); font-family:Arial; font-size:14px; line-height:26px">?????????????????????? loff_t from,size_t *retlen); ??????? int (*readv_ecc) (struct mtd_info *mtd,51); font-family:Arial; font-size:14px; line-height:26px">?????????????????????????? loff_t from,51); font-family:Arial; font-size:14px; line-height:26px">?????????????????????????? struct nand_oobinfo *oobsel); ??????? int (*writev) (struct mtd_info *mtd,const struct kvec *vecs,51); font-family:Arial; font-size:14px; line-height:26px">??????????????????????? unsigned long count,51); font-family:Arial; font-size:14px; line-height:26px">??????? int (*writev_ecc) (struct mtd_info *mtd,51); font-family:Arial; font-size:14px; line-height:26px">??????????????????????????? unsigned long count,51); font-family:Arial; font-size:14px; line-height:26px">??????????????????????????? u_char *eccbuf,51); font-family:Arial; font-size:14px; line-height:26px">??????? /* 同步操作 */ ??????? void (*sync) (struct mtd_info *mtd); ??????? /* 芯片级支持的加/解锁操作 */ ??????? int (*lock) (struct mtd_info *mtd,51); font-family:Arial; font-size:14px; line-height:26px">??????? int (*unlock) (struct mtd_info *mtd,51); font-family:Arial; font-size:14px; line-height:26px">??????? /* 电源管理操作??????????? */ ??????? int (*suspend) (struct mtd_info *mtd); ??????? void (*resume) (struct mtd_info *mtd); ??????? /* 坏块管理操作??????????? */ ??????? int (*block_isbad) (struct mtd_info *mtd,51); font-family:Arial; font-size:14px; line-height:26px">??????? int (*block_markbad) (struct mtd_info *mtd,51); font-family:Arial; font-size:14px; line-height:26px">?/* 重启前的通知事件??????? */ ??????? struct notifier_block reboot_notifier; ??????? void *priv; /* 私有数据结构 */ ??????? struct module *owner; /* 模块所有者 */ ??????? int usecount; /* 使用次数 */ 4.3 Linux下Nand Flash驱动说明4.3.1 注册driver_register 通过module_init(s3c2410_nand_init);注册Nand?Flash驱动.?在 s3c2410_nand_init?()中通过driver_register()注册 s3c2410_nand_driver驱动程序,如下所示: static struct device_driver s3c2410_n and_driver = { ??????? .name?????????? = "s3c2410-n and",51); font-family:Arial; font-size:14px; line-height:26px">??????? .bus??????????? = &plat form_bus_type,/* 在drivers/base/platform.c中定义 */ ??????? .probe????????? = s3c2410_n and_probe,51); font-family:Arial; font-size:14px; line-height:26px">??????? .remove???????? = s3c2410_n and_remove,51); font-family:Arial; font-size:14px; line-height:26px">4.3.2 探测设备probe 在注册的Nand Flash驱动程序中,probe方法为s3c2410_nand_probe(). s3c2410_nand_probe()再调用 s3c24xx_nand_probe().?在该函数中,?把*info作为Nand?Flash驱动的私有数据结构,?并通过dev_set_drvdata(dev,51); font-family:Arial; font-size:14px; line-height:26px">info)把*info保存在*device的*driver_data字段中.然后通过clk_get(dev,"nand")获取Nand Flash的时钟资 源,? clk_use(in fo->clk)增加时钟资源的使用计数,clk_enable(info->clk)开启资源.填写*info的其它字段,51); font-family:Arial; font-size:14px; line-height:26px">其中包括: ? 1.?通过request_mem_region()为Nand?Flash寄存器区申请I/O内存地址空间区,并通过ioremap()把它映射到虚 拟地址空间. ? 2.??调用s3c2410_nand_inithw()初始化Nand?Flash控制器. ? 3.??为mtd设备分配设备信息的存储空间. ? 4.??对当前mtd设备,调用s3c2410_nand_init_chip()进行初始化. ? 5.??对当前mtd设备,?调用nand_scan()检测Nand?Flash芯片,?nand_scan()函数在drivers/mtd/nand/nand_base.c中 定义.该函数的作用是初始化struct?nand_chip中一些方法,?并从Nand?Flash中读取芯片ID,?并初始化struct? mtd_info中的方法. ? 6.??对当前mtd设备,加入其分区信息. ? 7.?如果还有更多mtd设备,到4执行. 4.3.3 初始化Nand Flash控制器 s3c2410_nand_inithw()函数会初始化Nand?Flash控制器,?通过设置Nand?Flash控制寄存器(S3C2410_NFCONF)来 完成,?这里最重要的是根据S3C2410的PCLK计算出tacls,?twrph0以及twrph1值. 4.3.4 移除设备 s3c2410_nand_remove()当设备被移除时,被device核心驱动调用.它完成的主要工作如下: ? 1.?把*device的*driver_data字段置空. ? 2.?释放mtd设备信息. ? 3.?释放clk资源. ? 4.?通过iounmap()取消映地址空间. ? 5.?释放申请的I/O内存资源. ? 6.?释放设备私有数据*info的空间. 4.3.5 Nand Flash芯片初始化 s3c2410_nand_init_chip()初始化struct?nand_chip中的一些主要字段以及方法.其中主要包括的方法有: ? 1.?s3c2410_nand_hwcontrol();?硬件控制 ? 2.?s3c2410_nand_devready();?设备是否准备好 ? 3.?s3c2410_nand_write_buf();?写一个buffer到nand?flash ? 4.?s3c2410_nand_read_buf();?读一个buffer到nand?flash ? 5.?s3c2410_nand_select_chip();?选择操作芯片 如果支持ECC硬件校验,还设置如下方法:? 1.?s3c2410_nand_correct_data();?通过ECC码校正数据 ? 2.?s3c2410_nand_enable_hwecc();?开启硬件ECC检查 ? 3.?s3c2410_nand_calculate_ecc();?计算ECC码 4.3.6? 读Nand? Flash ??? 当对nand?flash的设备文件(nand?flash在/dev下对应的文件)执行系统调用read(),或在某个文件系统中对该 设备进行读操作时.?会调用struct mtd_info中的read方法,他们缺省调用函数为nand_read(),在 drivers/mtd/nand/nand_base.c中定义.nand_read()调用nand_do_read_ecc(),执行读操作. 在 nand_do_read_ecc()函数中,主要完成如下几项工作: 1. 会调用在nand flash驱动中对struct nand_chip重载的select_chip方法,即 s3c2410_nand_select_chip()选择要操作的MTD芯片. 2. 会调用在struct nand_chip中系统缺省的方法cmdfunc发送读命令到nand flash. 3. 会调用在nand flash驱动中对struct nand_chip重载的read_buf(),即s3c2410_nand_read_buf() 从Nand Flash的控制器的数据寄存器中读出数据. 4. 如果有必要的话,会调用在nand flash驱动中对struct nand_chip重载的 enable_hwecc,correct_data以及calculate_ecc方法,进行数据ECC校验。 4.3.7? 写Nand Flash 当对nand?flash的设备文件(nand?flash在/dev下对应的文件)执行系统调用write(),或在某个文件系统中对该设备 进行读操作时,?会调用struct mtd_info中write方法,他们缺省调用函数为nand_write(),这两个函数在 drivers/mtd/nand/nand_base.c中定义. nand_write()调用nand_write_ecc(),执行写操作.在 nand_do_write_ecc()函数中,51); font-family:Arial; font-size:14px; line-height:26px">2. 调用nand_write_page()写一个页. 3. 在nand_write_page()中,会调用在struct nand_chip中系统缺省的方法cmdfunc发送写命令 到nand flash. 4. 在nand_write_page()中,51); font-family:Arial; font-size:14px; line-height:26px">write_buf(),即s3c2410_nand_write_buf()从Nand Flash的控制器的数据寄存器中写入数据. 5. 在nand_write_page()中,会调用在nand flash驱动中对struct nand_chip重载waitfunc方法,51); font-family:Arial; font-size:14px; line-height:26px">该方法调用系统缺省函数nand_wait(),该方法获取操作状态,并等待nand flash操作完成.等 待操作完成,是调用nand flash驱动中对struct nand_chip中重载的dev_ready方法,即? s3c2410_nand_devready()函数. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |