/******************************************************************************
Copyright (C), 2001-2011, DCN Co., Ltd.
******************************************************************************
File Name : nand.c
Version : Initial Draft
Author : oucaijun
Created : 2014/5/9
Last Modified :
Description : K9F2G08 nandflash 底层读写、控制驱动程序
当前的程序可以对nand进行随机读写,随机读写的时候数据正确,但没有正确实现ecc算法。
当前程序可以实现对nand普通的页读写,并实现ecc算法。
因随机读写ecc不正确,因此不能和普通的带ecc的读写方式配套使用。
页内地址: 2048 = 2 ^ 11 , 使用A0-A11表示页内地址 见K9F2G08手册
Function List :
CheckBadBlk
CheckNandflash
EraseBlock
getBlockNum
getPageNum
InitNandCfg
MarkBadBlk
MY_ReadPage
MY_WritePage
nandNew
NF_Reset
RandomRead
RandomWrite
ReadBlock
ReadChipId
ReadPage
ReadPageAll
ReadStatus
WaitNFBusy
WriteBlock
WritePage
History :
1.Date : 2014/5/9
Author : ocj
Modification: Created file
******************************************************************************/
/*----------------------------------------------*
* routines' implementations *
*----------------------------------------------*/
#include "Common.h"
#include <string.h>
#include "nand.h"
#include "nfarea.h"
/*----------------------------------------------*
* macros *
*----------------------------------------------*/
/*----------------------------------------------*
* external routine prototypes *
*----------------------------------------------*/
/*----------------------------------------------*
* internal routine prototypes *
*----------------------------------------------*/
/*----------------------------------------------*
* project-wide global variables *
*----------------------------------------------*/
/*----------------------------------------------*
* module-wide global variables *
*----------------------------------------------*/
/*----------------------------------------------*
*external variables *
*----------------------------------------------*/
/*----------------------------------------------*
* constants *
*----------------------------------------------*/
#define NF_WAIT_RB() {while(!(rNFSTAT &(1<<0)));} //this bit is ReadOnly
#define NF_CLEAR_RB() {rNFSTAT |=(1<<2);} //清除RnB信号 //this bit is RW
#define NF_DETECT_RB() {while(!(rNFSTAT &(1<<2))) ;}
/*
R/B: When low, it indicates that a program, erase or
random read operation is in process
and returns to high state upon completion.
*/
void NF_Reset(void)
{
NFChipEn();
NF_CLEAR_RB();
WrNFCmd(RESET_CMD);
NF_DETECT_RB();
NFChipDs();
}
/*------------------------------------------------------------/
函数名称: InitNandCfg
功能描述: 配置flash
传 参: 无
返 回 值: 无
-------------------------------------------------------------*/
static void InitNandCfg(void)
{
rNFCONF = (TACLS<<)|(TWRPH0<<)|(TWRPH1<<)|(<<);//初始化时序参数
rNFCONT = (<<)|(<<)|(<<)|(<<)|(<<)|(<<)|(<<)|(<<)|(<<)|(<<);
//非锁定,屏蔽nandflash中断,初始化ECC及锁定main区和spare区ECC,使能nandflash片选及控制器
NF_Reset();
}
/*------------------------------------------------------------/
函数名称: WaitNFBusy
功能描述: Read Status Register , I/O = RdNFDat();
RdNFDat() until(I/O 6 = 1 or R/B = 1 ) ;
if(I/O 0 = 0 ) program/erase/ success ;
传 参: 无
返 回 值: I/O bit0 , 表示了命令的执行状况。若为0,表示success
-------------------------------------------------------------*/
static U32 WaitNFBusy(void) //
{
U8 stat;
WrNFCmd(QUERYCMD);
do
{
stat = RdNFDat();
}
while (!(stat&0x40));
WrNFCmd(READCMD0);
return stat&; //注意0为操作成功
}
/*------------------------------------------------------------/
函数名称: ReadChipId
功能描述: 读flash ID
传 参: 无
返 回 值: static U32 id
-------------------------------------------------------------*/
U32 ReadChipId(void)
{
U8 MID , PID , cyc3,cyc4,cyc5 ;
NFChipEn();
WrNFCmd(RdIDCMD);
WrNFAddr();
while(NFIsBusy());
MID = RdNFDat();
PID = RdNFDat();
cyc3 = RdNFDat();
cyc4 = RdNFDat();
cyc5 = RdNFDat();
NFChipDs();
return (MID<<)|(PID<<)|(cyc3<<)|(cyc4);
}
/*------------------------------------------------------------/
函数名称: ReadStatus
功能描述: 读FLASH状态
传 参: 无
返 回 值: static U16 stat
-------------------------------------------------------------*/
static U16 ReadStatus(void)
{
U16 stat;
NFChipEn();
WrNFCmd(QUERYCMD);
stat = RdNFDat();
NFChipDs();
return stat;
}
//CheckNandflash(int info) ret =0,没有flash或者不支持的flash。
int CheckNandflash(int info)
{
U32 i;
int have_nandflash ;
InitNandCfg();
i = ReadChipId();
if(info)
debugk("NAND ID is 0x%04x \n", i);
if((i==0xecda) || (i==0xadda)) {
have_nandflash = ;
}
else if(i==0xecf1) {
have_nandflash = ;
}
else {
have_nandflash = ;
debugk("unsupported nandflash id \n");
}
if(info)
debugk("Nand flash status = %x\n", ReadStatus());
return have_nandflash ;
}
/*------------------------------------------------------------/
函数名称: EraseBlock
功能描述: 擦除 FLASH
传 参: U32 addr
返 回 值: U32 ~stat
-------------------------------------------------------------*/
U32 EraseBlock(U32 addr)// if( stat & 0x1 == 1),erase success
{
U8 stat;
addr &= ~0x3f; // page_addr 的低六位清零
//addr(page_addr) : row_addr A12-A28 not need cloumn addr A0-A11
//64*2048(row_addr : one nand 64*2048 pages) * 2048(cloumn_addr : one page 2048 bytes)
NFChipEn();
WrNFCmd(ERASECMD0);
WrNFAddr(addr); //行地址A18~A19
WrNFAddr(addr>>);//行地址A20~A27
WrNFAddr(addr>>);//行地址A28
WrNFCmd(ERASECMD1);
stat = WaitNFBusy();
NFChipDs();
return ~stat; //最后一位等于0,success
}
/*------------------------------------------------------------/
函数名称: ReadPage
功能描述:
传 参: U32 addr, U8 *buf
返 回 值: 无
-------------------------------------------------------------*/
void ReadPage(U32 addr, U8 *buf)
{
U16 i;
NFChipEn();
WrNFCmd(READCMD0);
WrNFAddr();
WrNFAddr();
WrNFAddr(addr);
WrNFAddr(addr>>);
WrNFAddr(addr>>);
WrNFCmd(READCMD1);
InitEcc();
WaitNFBusy();
for(i=; i<; i++)
buf[i] = RdNFDat();
NFChipDs();
}
void ReadPageAll(U32 addr, U8 *buf)
{
U16 i;
NFChipEn();
WrNFCmd(READCMD0);
WrNFAddr();
WrNFAddr();
WrNFAddr(addr);
WrNFAddr(addr>>);
WrNFAddr(addr>>);
WrNFCmd(READCMD1);
InitEcc();
WaitNFBusy();
for(i=; i<+; i++)
buf[i] = RdNFDat();
NFChipDs();
}
/*------------------------------------------------------------/
函数名称: WritePage
功能描述:
传 参: U32 addr, U8 *buf
返 回 值: ret若为0xff,表示success
-------------------------------------------------------------*/
U32 WritePage(U32 addr, U8 *buf)
{
U32 i, mecc;
U8 stat, tmp[];
NFChipEn();
WrNFCmd(PROGCMD0);
WrNFAddr();
WrNFAddr();
WrNFAddr(addr);
WrNFAddr(addr>>);
WrNFAddr(addr>>);
InitEcc(); //reset mecc and secc
MEccUnlock();
for(i=; i<; i++)
WrNFDat(buf[i]);
MEccLock();
//因为K9F2G08U0A是8位IO口,因此S3C2440共产生4个字节的main区ECC码和2个字节的spare区ECC码
//在这里我们规定,在每一页的spare区的第0个地址到第3个地址存储main区ECC,第4个地址和第5个地址存储spare区ECC。
//在下次读取这一页数据的时候,同样我们也计算ECC校验码,然后与spare区中的ECC校验码比较,如果一致则说明读取的数据正确,如果不一致则不正确
//读取rNFMECC0 -- Main的ECC校验码
//for 8 bit nand flash, only use NFMECC0
mecc = RdNFMEcc() ;
tmp[] = mecc&0xff;
tmp[] = (mecc>>)&0xff;
tmp[] = (mecc>>)&0xff;
tmp[] = (mecc>>)&0xff;
WrNFDat(0xff);//2048,坏块标志
SEccUnlock();
WrNFDat(tmp[]);//ECC校验码
WrNFDat(tmp[]);
WrNFDat(tmp[]);
WrNFDat(tmp[]);
SEccLock();
WrNFCmd(PROGCMD1);
stat = WaitNFBusy();//stat若为0,表示success
NFChipDs();
return ~stat;////ret若为0xff,表示success
}
// RandomWrite 随机写
// 不直接改写spare区域
// addr_in_page 0-2047
U32 RandomWrite(int page_num, int addr_in_page ,U8 *buf,int len)
{
U8 stat ;
U32 mecc , secc ;
if( len+addr_in_page> ){
debugs("RandomWrite :do not allow to write spare_section directly!\n");
}
if(buf==NULL){
debugs("RandomWrite :wrong data pointer!\n");
}
//cs
NFChipEn();
WrNFCmd(PROGCMD0);
WrNFAddr();
WrNFAddr();
WrNFAddr(page_num&0xff);
WrNFAddr((page_num>>) &0xff);
WrNFAddr((page_num>>) &0xff);
WrNFCmd(RANDOM_PROGRAMCMD); //85h
WrNFAddr( (addr_in_page&0xff) ); //页内A0~A7
WrNFAddr( ((addr_in_page>>)&0x0f) ); //页内地址A8~A11
while(len--){
WrNFDat(*buf++);
}
WrNFCmd(PROGCMD1);//
stat = WaitNFBusy();
//ncs
NFChipDs();
return ~stat;
}
//MY_WritePage 写块 ,并将ecc写入spare区域 。
//完全同 WritePage
U32 MY_WritePage(U32 addr, U8 *buf)
{
U32 mecc ,secc ;
int i ,ret ;
U8 stat;
InitEcc(); //复位ECC
MEccUnlock(); //解锁main区的ECC
NFChipEn(); //打开nandflash片选
// NF_CLEAR_RB(); //清RnB信号 没有用这个判断busy而是直接读的query_nand ,没必要。
WrNFCmd(PROGCMD0);//页写命令周期1
//写入5个地址周期
WrNFAddr(0x00); //列地址A0~A7
WrNFAddr(0x00); //列地址A8~A11 列地址就是页内的地址,不能超过2048,要注意传入的值的有效性。
WrNFAddr((addr) & 0xff); //行地址A12~A19
WrNFAddr((addr >> ) & 0xff); //行地址A20~A27
WrNFAddr((addr >> ) & 0xff); //行地址A28
for (i = ; i < ; i++)//写入一页数据
{
WrNFDat( buf[i] );
}
MEccLock(); //锁定main区的ECC值
//读取main区的ECC校验码
//for 8 bit nand flash, only use NFMECC0
mecc = rNFMECC0 ;
SEccUnlock();//解锁spare区的ECC
WrNFDat(0xff);//先写入0XFF非坏块标志到2048
WrNFDat32(mecc);//把main区的ECC值写入到spare区的前2-5字节地址内,即第2049~2052地址
SEccLock();//锁定spare区的ECC值
secc = rNFSECC ;//读取spare区的ECC校验码
WrNFDat8(secc & 0xff);//写入secc校验码到spare区第6-7个字节
WrNFDat8((secc>>) & 0xff);
WrNFCmd(PROGCMD1);
WrNFCmd(QUERYCMD);
do {
stat = RdNFDat();
} while(!(stat & 0x40));
NFChipDs();
if (stat & 0x1) {//fail
debugs("fail to write nand!\n");
}
return ~(stat & 0x1); //if ok,ret = 0xff
}
/*------------------------------------------------------------/
函数名称: ReadBlock
功能描述:
传 参: U32 addr, U8 *buf
返 回 值: 无
-------------------------------------------------------------*/
void ReadBlock(U32 addr, U8 buf[N_PAGES_NAND_BLOCK][N_BYTES_NAND_PAGE]) //addr :pages ,64的整数倍
{
int i;
int start ;
start = addr ;
for(i=; i<; i++) {
ReadPage( start+i,buf[i] );
}
}
/*------------------------------------------------------------/
函数名称: WriteBlock
功能描述:
传 参: U32 addr, U8 *buf
返 回 值: 无
-------------------------------------------------------------*/
void WriteBlock(U32 addr, U8 buf[][])
{
int i ;
int start = addr ;
for(i=; i<; i++) {
WritePage( start+i ,buf[i] );
}
}
int getPageNum(u32 addr ,u32 PageNum[])
{
PageNum[] = addr/ ;
return PageNum[] ;
}
int getBlockNum(u32 addr ,u32 BlockNum[])
{
BlockNum[] = addr// ;
return BlockNum[] ;
}
////////////////////////////////////////////
////////////////////////////////////////////
//CheckBadBlk
//addr : pageaddr{A12--A28}
//ret == 0 : badblk
//在该块的第一页 第2048bytes查询是否为non-FFh,是则为badblk 。
int CheckBadBlk(U32 addr)
{
U8 dat;
addr &= ~0x3f;
/*
Samsung makes sure that either the 1st or 2nd page of
everyinitial invalid block has non-FFh data
at the column address of 2048.
*/
NFChipEn();
WrNFCmd(READCMD0);
WrNFAddr(); //
WrNFAddr(); // A11=1 该地址值为2048
WrNFAddr(addr & 0xff);
WrNFAddr((addr>>) & 0xff);
WrNFAddr((addr>>) & 0xff);//in a page [offset == 2048] : read the 2048th bytes of page(addr) .
WrNFCmd(READCMD1);
WaitNFBusy();
dat = RdNFDat();
NFChipDs();
if(dat!=0xff) {
debugs("Blk % 4d is NG! page_addr=%d\n" ,addr>> , addr );
}
return (dat != 0xff);
}
//MarkBadBlk 标记坏块
//addr : pageaddr{A12--A28}
//在该块的第一页 第2048bytes 写入non-FFh。
void MarkBadBlk(U32 addr)
{
addr &= ~0x3f;
NFChipEn();
WrNFCmd(PROGCMD0);
//mark offset 2048
WrNFAddr(); //
WrNFAddr(); // A11=1 2048
WrNFAddr(addr & 0xff);
WrNFAddr((addr>>) & 0xff);
WrNFAddr((addr>>) & 0xff);//in a page [offset == 2048] : read the 2048th bytes of page(addr) .
WrNFDat(); //mark with 0 == “badblk”
WrNFCmd(PROGCMD1);
WaitNFBusy(); //needn't check return status
NFChipDs();
}
//MY_ReadPage
//ret ==0 ok
//ret ==1 ecc error
int MY_ReadPage(U32 addr, U8 *buf)
{
int i ;
U32 mecc ,secc ;
char ch;
InitEcc();
MEccUnlock();
NFChipEn();
NF_CLEAR_RB();
WrNFCmd(READCMD0);
WrNFAddr();
WrNFAddr();
WrNFAddr(addr&0xff);
WrNFAddr((addr>>)&0xff);
WrNFAddr((addr>>)&0xff);
WrNFCmd(READCMD1);
NF_DETECT_RB();
for(i=; i<; i++) {
buf[i] = RdNFDat();
}
MEccLock();
SEccUnlock();
ch = RdNFDat();//跳过0XFF坏块标志 2048
//PREFERENCE :DATASHEET--ECC MODULE FEATURES
//读spare区的前4个地址内容,即第 2049 - 2052地址,这4个字节为main区的ECC
//把读取到的main区的ECC校验码放入NFMECCD0/1的相应位置内
mecc = RdNFDat32();
rNFMECCD0 = ((mecc&0xff00)<<) | (mecc&0xff) ;
rNFMECCD1 = ((mecc&0xff000000)>>) | ((mecc&0xff0000)>>) ;
SEccLock();//这之前计算的secc仅仅包括bit2048 2049 2050 2051 2052 的计算结果????? 应该是吧
secc = RdNFDat32();
//继续读spare区的4个地址内容,即第 2052--2055地址
//把读取到的spare区的ECC校验码放入NFSECCD的相应位置内
rNFSECCD = ((secc&0xff00)<<) | (secc&0xff);
NFChipDs();
if((rNFESTAT0&0xf)==0x0) { //查看ECC状态寄存器
return ;
} else {
debugs("rNFESTAT0 = %0xh \n" ,rNFESTAT0 );
return ;
}
}
//
int RandomRead(U32 page_number, U32 addr_in_page , U8 *bufr ,int len )
{
if( len+addr_in_page>+ ){
debugs("RandomRead error : read out of spare_section!\n");
return ;
}
if(bufr==NULL){
debugs("RandomRead error : wrong data pointer!\n");
return ;
}
NFChipEn(); //打开Nand Flash片选
NF_CLEAR_RB(); //清RnB信号
WrNFCmd(READCMD0); //页读命令周期1
//写入5个地址周期
WrNFAddr(0x00); //列地址A0~A7
WrNFAddr(0x00); //列地址A8~A11
WrNFAddr((page_number) & 0xff); //行地址A12~A19
WrNFAddr((page_number >> ) & 0xff); //行地址A20~A27
WrNFAddr((page_number >> ) & 0xff); //行地址A28
WrNFCmd(READCMD1); //页读命令周期2
NF_DETECT_RB(); //等待RnB信号变高,即不忙
WrNFCmd(RANDOM_READCMD0); //随意读命令周期1
//页内地址
WrNFAddr( addr_in_page&0xff ); //列地址A0~A7
WrNFAddr( (addr_in_page>>)&0x0f ); //列地址A8~A11
WrNFCmd(RANDOM_READCMD1); //随意读命令周期2
while(len--){
*bufr++ = RdNFDat(); //读取数据
}
NFChipDs();
return ;
}
//http://www.cnblogs.com/idle_man/archive/2010/12/23/1915303.html
#define getnum(i) {\
mygetstring(string) ;\
i = myatoi(string ) ;\
}
void nandNew(void)
{
int ret , n ,i , j ,k;
U32 nID;
U8 MID , PID , cyc3,cyc4,cyc5 ;
char dat ;
char ch ;
U8 uch;
char string[] = {};
char bufw[] = { , , };
char bufr[+] = {};
memset(bufw , 0xaa , );
while() {
debugs("\n------------------------------------------------\n");
debugs("nand menu\n");
debugs("input num to select:\n");
debugs("now will operate block %d,its addr is %x\n",AppParaBpageS,AppParaBaseNF);//page num = 34816
debugs("0 sysHardwareReset\n");
debugs("1 nand EraseBlock\n");
debugs("2 nand MY_WritePage caculate ecc\n");
debugs("3 nand ReadPageAll\n");
debugs("4 nand my_cop\n");
debugs("5 nand CheckBadBlk\n");
debugs("6 nand MY_ReadPage check ecc\n");
debugs("------------------------------------------------\n");
ch = mygetc();
switch ( ch - '' )
{
case :
sysHardwareReset();
break;
case :
ret = EraseBlock(AppParaBpageS );
if(ret&0x1 == ) {
debugs("EraseBlock ok!\n");
} else {
debugs("EraseBlock fail!\n");
}
break;
case :
ret = MY_WritePage(AppParaBpageS, bufw);
if(ret&0x1 == ) {
debugs("WritePage ok!\n");
} else {
debugs("WritePage fail!\n");
}
break;
case :
if(){
int j ;
int i = ;
char buf[] = {,,,,,,,,};
buf[] = ;
buf[] = ;
buf[] = ;
memset(bufr, , sizeof(bufr));
ReadPageAll(i , bufr);
ret = memcmp(buf , bufr+ , );
debugs("ret of memcmp %x\n" , ret);
arryprintfs("ReadPage+0" ,bufr , );
arryprintfs("ReadPage+2048" ,bufr+ , );
}
break;
case :
if(){
int time = ;
int nPage = , nAddr = ;
char rbufw[] = {, , , , , , , , ,};
ret = RandomWrite(nPage , nAddr, rbufw , );
if(ret&0x1 == ) {
debugs("WritePage ok!\n");
} else {
debugs("WritePage fail!\n");
}
for(time = ; time< + ; time++){
ret = 0x11;
ret = RandomRead(nPage ,nAddr , bufr , +);
debugs("RamdomRead(page %d, in_page_addr%d ) ,ret = 0x%x\n" ,nPage , nAddr, ret);
arryprintfs("RamdomRead bufr" ,bufr ,+ ) ;
nAddr++;
}
}
/*
----------------prints---------------------------
WritePage ok!
RamdomRead(page 34816, in_page_addr1 ) ,uch = 0x1
RamdomRead(page 34816, in_page_addr2 ) ,uch = 0x2
RamdomRead(page 34816, in_page_addr3 ) ,uch = 0x3
RamdomRead(page 34816, in_page_addr4 ) ,uch = 0x4
RamdomRead(page 34816, in_page_addr5 ) ,uch = 0x5
RamdomRead(page 34816, in_page_addr6 ) ,uch = 0x6
RamdomRead(page 34816, in_page_addr7 ) ,uch = 0x7
RamdomRead(page 34816, in_page_addr8 ) ,uch = 0x8
RamdomRead(page 34816, in_page_addr9 ) ,uch = 0x9
RamdomRead(page 34816, in_page_addr10 ) ,uch = 0xa
----------------prints---------------------------
*/
break;
case : //CheckBadBlk from blk0~blk2047
for(ret= ; ret<; ret++) {
CheckBadBlk(ret<<); //ret<<6 : page_addr
}
break;
case : //
if(){
memset(bufr, , sizeof(bufr));
ret = MY_ReadPage(, bufr);
debugs("MY_ReadPage ret==%d\n" , ret);
arryprintfs("ReadPage+0" ,bufr , );
}
break;
case : //
if(){
T_ecc() ;
}
break;
}
}
}