/*
* $Id: nand_cp.c,v 1.1 2013/6/6 22:46:46 $
*
* (C) kangear
*
* 这个程序适用于tiny210v2(NandFlash:K9GAG08U0F)
* 这个Flash的页大小为8k+512大小;是MLC类型的!
*
* 由于是MLC类型的,所以很容易出错,我就在这里边加了ECC 16bit校验。这个校验只
* 实现了读校验,且还是读与开发板配套的Superboot写到NandFlash中的校验值。由于
* 到目前并没有发现Superboot对oob区的校验,所以在读的时候,也没有办法进行oob区
* 校验。
*
* 程序完善了oob区的校验会更完整,目前没有做的原因上边已经说明。
* 由于没有时行oob区进行校验,在读取Main区的校验码时就会有出错的可能,错误的校验
* 会造成错误的修正,不过根据规律已经解决了除第一位就是错误校验码引起修正外的其它
* 错误判断。关于如果512Byte中唯一一位就是错误的校验码引起的伪翻转只能通过屏蔽典型
* Byte的不进行校验来进行减少出现误判断。对于这些问题,如果能进行oob区的校验,都会
* 迎刃而解了。(由于oob区校验没有办法做实现,所以NF_ReadOob只是一个框架)
*
* 2013-6-9添加了nandll_read_n_byte,改善了读的速度。256k只需要3秒钟就可以
* copy完
* 2013-6-25进行的优化,去掉无关的注释
*/
/*
* Standard NAND flash commands
*/
#define NAND_CMD_READ0 0x00
#define NAND_CMD_READ1 0x30
#define NAND_CMD_RNDOUT0 0x05
#define NAND_CMD_RNDOUT1 0xE0
#define NAND_CMD_PAGEPROG 0x10
#define NAND_CMD_READOOB 0x50
#define NAND_CMD_ERASE1 0x60
#define NAND_CMD_STATUS 0x70
#define NAND_CMD_STATUS_MULTI 0x71
#define NAND_CMD_SEQIN 0x80
#define NAND_CMD_RNDIN 0x85
#define NAND_CMD_READID 0x90
#define NAND_CMD_PARAM 0xec
#define NAND_CMD_ERASE2 0xd0
#define NAND_CMD_RESET 0xff
/* Extended commands for large page devices */
#define NAND_CMD_READSTART 0x30
#define NAND_CMD_RNDOUTSTART 0xE0
#define NAND_CMD_CACHEDPROG 0x15
#define NAND_DISABLE_CE() (NFCONT_REG |= (1 << 1))
#define NAND_ENABLE_CE() (NFCONT_REG &= ~(1 << 1))
#define NF_TRANSRnB() do { while(!(NFSTAT_REG & (1 << 0))); } while(0)
typedef unsigned char uchar;
typedef volatile unsigned long vu_long;
typedef volatile unsigned short vu_short;
typedef volatile unsigned char vu_char;
typedef unsigned char unchar;
typedef unsigned short ushort;
typedef unsigned int uint;
typedef unsigned long ulong;
#define __REG(x) (*(vu_long *)(x))
#define __REGl(x) (*(vu_long *)(x))
#define __REGw(x) (*(vu_short *)(x))
#define __REGb(x) (*(vu_char *)(x))
#define __REG2(x,y) (*(vu_long *)((x) + (y)))
#include "s5pv210.h"
//#define COPY_BL2_SIZE 882046
//#define CONFIG_SYS_TEXT_BASE 0x22000000
#ifdef DEBUG
#define debug(fmt,args...) printf (fmt ,##args)
#define debugX(level,fmt,args...) if (DEBUG>=level) printf(fmt,##args);
#else
#define debug(fmt,args...)
#define debugX(level,fmt,args...)
#define puthex(args)
#define put32bits(args)
#endif /* DEBUG */
#define NAND_CONTROL_ENABLE() (NFCONT_REG |= (1 << 0))
/*
* address format
* 17 16 9 8 0
* --------------------------------------------
* | block(12bit) | page(5bit) | offset(9bit) |
* --------------------------------------------
*/
#define NFECCCONF_REG __REG(ELFIN_NAND_ECC_BASE+NFECCCONF_OFFSET)
#define NFECCCONT_REG __REG(ELFIN_NAND_ECC_BASE+NFECCCONT_OFFSET)
#define NFECCSTAT_REG __REG(ELFIN_NAND_ECC_BASE+NFECCSTAT_OFFSET)
#define NFECCSECSTAT_REG __REG(ELFIN_NAND_ECC_BASE+NFECCSECSTAT_OFFSET)
#define NFECCCONECC0_REG __REG(ELFIN_NAND_ECC_BASE+NFECCCONECC0_OFFSET)
#define NFECCCONECC1_REG __REG(ELFIN_NAND_ECC_BASE+NFECCCONECC1_OFFSET)
#define NFECCCONECC2_REG __REG(ELFIN_NAND_ECC_BASE+NFECCCONECC2_OFFSET)
#define NFECCCONECC3_REG __REG(ELFIN_NAND_ECC_BASE+NFECCCONECC3_OFFSET)
#define NFECCCONECC4_REG __REG(ELFIN_NAND_ECC_BASE+NFECCCONECC4_OFFSET)
#define NFECCCONECC5_REG __REG(ELFIN_NAND_ECC_BASE+NFECCCONECC5_OFFSET)
#define NFECCCONECC6_REG __REG(ELFIN_NAND_ECC_BASE+NFECCCONECC6_OFFSET)
#define NFECCERL0_REG __REG(ELFIN_NAND_ECC_BASE+NFECCERL0_OFFSET)
#define NFECCERL1_REG __REG(ELFIN_NAND_ECC_BASE+NFECCERL1_OFFSET)
#define NFECCERL2_REG __REG(ELFIN_NAND_ECC_BASE+NFECCERL2_OFFSET)
#define NFECCERL3_REG __REG(ELFIN_NAND_ECC_BASE+NFECCERL3_OFFSET)
#define NFECCERL4_REG __REG(ELFIN_NAND_ECC_BASE+NFECCERL4_OFFSET)
#define NFECCERL5_REG __REG(ELFIN_NAND_ECC_BASE+NFECCERL5_OFFSET)
#define NFECCERL6_REG __REG(ELFIN_NAND_ECC_BASE+NFECCERL6_OFFSET)
#define NFECCERL7_REG __REG(ELFIN_NAND_ECC_BASE+NFECCERL7_OFFSET)
#define NFECCERP0_REG __REG(ELFIN_NAND_ECC_BASE+NFECCERP0_OFFSET)
#define NFECCERP1_REG __REG(ELFIN_NAND_ECC_BASE+NFECCERP1_OFFSET)
#define NFECCERP2_REG __REG(ELFIN_NAND_ECC_BASE+NFECCERP2_OFFSET)
#define NFECCERP3_REG __REG(ELFIN_NAND_ECC_BASE+NFECCERP3_OFFSET)
#define NFSTAT_ECCDECDONE (1<<24)
#define NF_RSTECC() {NFECCCONT_REG |= (1<<2);}
#define NF_MECC_UnLock() {NFCONT_REG &= ~(1<<7);}
#define NF_MECC_Lock() {NFCONT_REG |= (1<<7);}
#define NF_SECC_UnLock() {NFCONT_REG &= ~(1<<6);}
#define NF_SECC_Lock() {NFCONT_REG |= (1<<6);}
static void nandll_read_n_byte (uchar* buf, ulong addr, ulong add, ulong size)
{
int i = 0;
NAND_ENABLE_CE();
NFCMD_REG = NAND_CMD_READ0;
/* Write 5 Address */
NFADDR_REG = 0; //col:A0 ~ A7
NFADDR_REG = 0; //col:A8 ~ A11
NFADDR_REG = (addr) & 0xff;
NFADDR_REG = (addr >> 8) & 0xff;
NFADDR_REG = (addr >> 16) & 0xff;
NFCMD_REG = NAND_CMD_READ1;
NF_TRANSRnB();
/* for compatibility(2460). u32 cannot be used. by scsuh */
for(i=0; i < size; i++)
{
NFCMD_REG = NAND_CMD_RNDOUT0;
NFADDR_REG = (char)((add+i) & 0xff); // col:A0~A7
NFADDR_REG = (char)(((add+i) >> 8) & 0x3f); // col:A8~A11
NFCMD_REG = NAND_CMD_RNDOUT1;
*buf++ = NFDATA8_REG;
}
NAND_DISABLE_CE();
}
static void nandll_read_16bit_ecc (uchar* buf, ulong addr, uchar num)
{
int base = 8192+36;
int ecc_size = 26;
int off = base + num*28;
nandll_read_n_byte(buf, addr, off, ecc_size);
}
static void nandll_read_512byte (uchar *buf, ulong addr, uchar num)
{
int base = 0;
int size = 512;
int off = base + num*size;
nandll_read_n_byte(buf, addr, off, size);
}
/*
* 判断一个数组前n项是否为递增
*/
int isInc(uint* a, int len)
{
if( len > 0 )
{
if( len == 1 )
{
return 1;
}
else
{
return (a[len-2] <= a[len-1]) && isInc(a, len - 1);
}
}
else
{
return -1;
}
}
/*
* 修复Main区的反转位
*/
int fixEcc(uchar* buf, int num, int flag)
{
uint subst[16];
uchar pattern[16];
int i = 0;
// 数组赋值为0
for(i=0; i<16; i++)
{
subst[i]=pattern[i]=0;
}
{
subst[0] = (NFECCERL0_REG>>0) & 0x1ff;
pattern[0] = (NFECCERP0_REG>>0) & 0xff;
subst[1] = (NFECCERL0_REG>>16) & 0x1ff;
pattern[1] = (NFECCERP0_REG>>8) & 0xff;
subst[2] = (NFECCERL1_REG>>0) & 0x1ff;
pattern[2] = (NFECCERP0_REG>>16) & 0xff;
subst[3] = (NFECCERL1_REG>>16) & 0x1ff;
pattern[3] = (NFECCERP0_REG>>24) & 0xff;
subst[4] = (NFECCERL2_REG>>0) & 0x1ff;
pattern[4] = (NFECCERP1_REG>>0) & 0xff;
subst[5] = (NFECCERL2_REG>>16) & 0x1ff;
pattern[5] = (NFECCERP1_REG>>8) & 0xff;
subst[6] = (NFECCERL3_REG>>0) & 0x1ff;
pattern[6] = (NFECCERP1_REG>>16) & 0xff;
subst[7] = (NFECCERL3_REG>>16) & 0x1ff;
pattern[7] = (NFECCERP1_REG>>24) & 0xff;
subst[8] = (NFECCERL4_REG>>0) & 0x1ff;
pattern[8] = (NFECCERP2_REG>>0) & 0xff;
subst[9] = (NFECCERL4_REG>>16) & 0x1ff;
pattern[9] = (NFECCERP2_REG>>8) & 0xff;
subst[10] = (NFECCERL5_REG>>0) & 0x1ff;
pattern[10] = (NFECCERP2_REG>>16) & 0xff;
subst[11] = (NFECCERL5_REG>>16) & 0x1ff;
pattern[11] = (NFECCERP2_REG>>24) & 0xff;
subst[12] = (NFECCERL6_REG>>0) & 0x1ff;
pattern[12] = (NFECCERP3_REG>>0) & 0xff;
subst[13] = (NFECCERL6_REG>>16) & 0x1ff;
pattern[13] = (NFECCERP3_REG>>8) & 0xff;
subst[14] = (NFECCERL7_REG>>0) & 0x1ff;
pattern[14] = (NFECCERP3_REG>>16) & 0xff;
subst[15] = (NFECCERL7_REG>>16) & 0x1ff;
pattern[15] = (NFECCERP3_REG>>24) & 0xff;
}
// 解决最后一个误判断
if(!isInc(subst, num))
num --; //如果不是递增说明最后是一个是误判断,所以减少一次修正。
for(i=0; i<num; i++)
buf[subst[i]] ^= pattern[i];
return 0;
}
/*
* 读512Byte并进行ECC校验
*/
uchar nand_read_512_ecc(uchar *buf, unsigned int addr, uchar num)
{
uint r