#include "s3c6410_reg.h"
#define TACLS 0
#define TWRPH0 1
#define TWRPH1 0
#define PAGE_SIZE 2048 // =2^11 =0x800 =0b1000 0000 0000
#define BLOCK_SIZE ((PAGE_SIZE) * 64) // =2^17 =0x20000 =0b10 0000 0000 0000 0000
#define check_rb do {while (!(NFSTAT & 1)) {} } while (0)
struct oob_data {
unsigned char badblock_flag;
unsigned char reserved;
unsigned char mainecc[4];
unsigned char spareecc[2];
};
static inline void nandchip_enable()
{
NFCONT &= ~(1 << 1);
}
static inline void nandchip_disable()
{
NFCONT |= 1 << 1;
}
static inline void unlock_mainecc()
{
NFCONT &= ~(1 << 7);
}
static inline void unlock_spareecc()
{
NFCONT &= ~(1 << 6);
}
static inline void lock_mainecc()
{
NFCONT |= 1 << 7;
}
static inline void lock_spareecc()
{
NFCONT |= 1 << 6;
}
static inline void init_mainecc()
{
NFCONT |= 1 << 5;
}
static inline void init_spareecc()
{
NFCONT |= 1 << 4;
}
void nand_init()
{
GPOCON = GPOCON & ~3 | 2;
GPPCON = GPPCON & ~(0x3ff << 6) | (2 << 6 | 2 << 8 | 2 << 10 | 2 << 12 | 2 << 14);
//关闭nand控制器
NFCONT &= ~1;
NFCONF = NFCONF & ~(3 << 23 | 7 << 12 | 7 << 8 | 7 << 4 | 1)
| (TACLS << 12 | TWRPH0 << 8 | TWRPH1 << 4 | 1 << 2);
//打开nand控制器
NFCONT |= 1;
//reset nand chip
NFCMMD = 0xff;
check_rb;
}
static inline void __send_addr(unsigned long addr)
{
NFADDR = addr & 0xff;
NFADDR = (addr >> 8) & 0x7;
NFADDR = (addr >> 11) & 0xff;
NFADDR = (addr >> 19) & 0xff;
NFADDR = (addr >> 27) & 0xff;
}
static inline void __send_addr_block(unsigned long addr)
{
NFADDR = (addr >> 11) & 0xff;
NFADDR = (addr >> 19) & 0xff;
NFADDR = (addr >> 27) & 0xff;
}
/* 通过已经设置的坏块标志来检测本页所在块是否为坏块
* 检测块首页的oob第一个字节是否为0xff
* return: 0:正常的块,-1: 坏块
*/
static int check_badblock(unsigned long dst)
{
dst &= ~(BLOCK_SIZE - 1);
dst += PAGE_SIZE - 1;
NFCMMD = 0;
__send_addr(dst);
NFCMMD = 0x30;
check_rb;
unsigned char tmp = 0;
tmp = NFDATA8;
tmp = NFDATA8;
if (tmp == 0xff) {
return 0;
} else {
return -1;
}
}
/*
* 从nandflash的src地址中读取size个字节的数据到dst位置的内存中
* @dst: 目标内存地址
* @src: nandflash芯片中的源地址,只接受页首地址
* @size:读取的字节数
*/
int nand_read(unsigned long dst, unsigned long src,
unsigned long size)
{
nandchip_enable();
//将src地址修改成页首地址
src &= ~(PAGE_SIZE - 1); //0b111 1111 1111
//找到第一个非坏块
while (check_badblock(src) < 0) {
print_str("skip a bad block\n");
src += BLOCK_SIZE;
}
unsigned long i = 0;
unsigned long j = 0;
for (; i < size; i += PAGE_SIZE, dst += PAGE_SIZE) {
//如果当前是块首页,则进行坏块判断
if (!((src + i) & (PAGE_SIZE - 1))) {
while (check_badblock(src + i) < 0) {
print_str("skip a bad block\n");
src += BLOCK_SIZE;
}
}
NFCMMD = 0;
__send_addr(src + i);
NFCMMD = 0x30;
check_rb;
for (j = 0; j < PAGE_SIZE; j += 4) {
*(volatile unsigned long*)(dst + j) = NFDATA;
}
}
nandchip_disable();
return 0;
}
int nand_write(unsigned long dst, unsigned long src,
unsigned long size)
{
nandchip_enable();
//将src地址修改成页首地址
src &= ~(PAGE_SIZE - 1);
//找到第一个非坏块
while (check_badblock(src) < 0) {
print_str("skip a bad block\n");
src += BLOCK_SIZE;
}
unsigned long i = 0;
unsigned long j = 0;
for (; i < size; i += PAGE_SIZE, dst += PAGE_SIZE) {
//如果当前是块首页,则进行坏块判断
if (!((src + i) & (PAGE_SIZE - 1))) {
while (check_badblock(src + i) < 0) {
print_str("skip a bad block\n");
src += BLOCK_SIZE;
}
}
NFCMMD = 0x80;
__send_addr(src + i);
for (j = 0; j < PAGE_SIZE; j += 4) {
NFDATA = *(volatile unsigned long*)(dst + j);
}
NFCMMD = 0x10;
check_rb;
NFCMMD = 0x70;
if (NFDATA8 & 1) {
print_str("write page error\n");
return -1;
}
}
nandchip_disable();
return 0;
}
int nand_erase(unsigned long dst, unsigned long size)
{
nandchip_enable();
dst &= ~(BLOCK_SIZE - 1);
unsigned long i = 0;
for (; i < size; i += BLOCK_SIZE) {
/* while (check_badblock(dst + i) < 0) {
* print_str("skip a bad block\n");
* dst += BLOCK_SIZE;
* }*/
NFCMMD = 0x60;
__send_addr_block(dst + i);
NFCMMD = 0xd0;
check_rb;
NFCMMD = 0x70;
if (NFDATA8 & 1) {
print_str("erase block error\n");
return -1;
}
}
nandchip_disable();
return 0;
}
int nand_read_ecc(unsigned long dst, unsigned long src,
unsigned long size)
{
nandchip_enable();
//将src地址修改成页首地址
src &= ~(PAGE_SIZE - 1);
unsigned long i = 0;
unsigned long j = 0;
for (; i < size; i += PAGE_SIZE, dst += PAGE_SIZE) {
unlock_mainecc();
init_mainecc();
NFCMMD = 0;
__send_addr(src + i);
NFCMMD = 0x30;
check_rb;
for (j = 0; j < PAGE_SIZE; j += 4) {
*(volatile unsigned long*)(dst + j) = NFDATA;
}
#if 1
lock_mainecc(); //计算出来的ECC码存放于NFMECC0中
//读取oob中的ecc值,并计算此时读取oob区的spareecc校验码,但有两个字节的偏差
unlock_spareecc();
init_spareecc();
struct oob_data ob;
ob.badblock_flag = NFDATA8;
ob.reserved = NFDATA8;
//读取存放于oob区原来(写入使计算出的)的mainecc校验码
ob.mainecc[0] = NFDATA8;
ob.mainecc[1] = NFDATA8;
ob.mainecc[2] = NFDATA8;
ob.mainecc[3] = NFDATA8;
lock_spareecc();
ob.spareecc[0] = NFDATA8;
ob.spareecc[1] = NFDATA8;
//将保存在oob中的ecc值(在ob中刚读到的)与当前读取操作得出的ecc值(刚计算的保存在NFMECC里)进行比对
NFMECCD0 = ob.mainecc[0] | ob.mainecc[1] << 16;
NFMECCD1 = ob.mainecc[2] | ob.mainecc[3] << 16;
NFSECCD = ob.spareecc[0] | ob.spareecc[1] << 16;
switch (NFECCERR0 & 3) {
case 0:
print_str("main ecc ok\n");
break;
case 1:
print_str("main ecc 1-bit error\n");
break;
case 2:
print_str("main ecc uncorrectable error\n");
break;
case 3:
print_str("main ecc area error\n");
break;
}
switch ((NFECCERR0 >> 2) & 3) {
case 0:
print_str("spare ecc ok\n");
break;
case 1:
print_str("spare ecc 1-bit error\n");
break;
case 2:
print_str("spare ecc uncorrectable error\n");
break;
case 3:
print_str("spare ecc area error\n");
break;
}
#endif
}
nandchip_disable();
return 0;
}
int nand_write_ecc(unsigned long dst, unsigned long src,
unsigned long size)
{
nandchip_enable();
//将src地址修改成页首地址
src &= ~(PAGE_SIZE - 1);
unsigned long i = 0;
unsigned long j = 0;
for (; i < size; i += PAGE_SIZE, dst += PAGE_SIZE) {
//读取本页oob区的数据,为了保证前两个字节不被修改
NFCMMD = 0;
__send_addr(dst + PAGE_SIZE - 1);
NFCMMD = 0x30;
check_rb;
unsigned char tmp = NFDATA8;
struct oob_data od;
unsigned long *p = (unsigned long *)(&od);
*p = NFDATA;
//*(p + 1) = NFDATA;
unlock_mainecc();
init_mainecc();
NFCMMD = 0x80;
__send_addr(src + i);
for (j = 0; j < PAGE_SIZE; j += 4) {
NFDATA = *(volatile unsigned long*)(dst + j);
}
lock_mainecc();
unlock_spareecc();
init_spareecc();
NFDATA8 = od.badblock_flag;
NFDATA8 = od.reserved;
NFDATA = NFMECC0;
lock_spareecc();
NFDATA8 = NFSECC & 0xff;
NFDATA8 = (NFSECC >> 8) & 0xff;
NFCMMD = 0x10;
check_rb;
NFCMMD = 0x70;
if (NFDATA8 & 1) {
print_str("write page error\n");
return -1;
}
}
nandchip_disable();
return 0;
}