mirror of
git://projects.qi-hardware.com/xburst-tools.git
synced 2024-11-23 13:06:16 +02:00
735 lines
14 KiB
C
735 lines
14 KiB
C
|
/*
|
||
|
* Common NAND Flash operations for JZ4740.
|
||
|
*
|
||
|
* This software is free.
|
||
|
*/
|
||
|
|
||
|
#include "jz4740.h"
|
||
|
#include "include.h"
|
||
|
|
||
|
extern struct nand_oobinfo oob_64[];
|
||
|
|
||
|
#define __nand_enable() (REG_EMC_NFCSR |= EMC_NFCSR_NFE1 | EMC_NFCSR_NFCE1)
|
||
|
#define __nand_disable() (REG_EMC_NFCSR &= ~(EMC_NFCSR_NFCE1|EMC_NFCSR_NFE1 ))
|
||
|
#define __nand_ecc_rs_encoding() (REG_EMC_NFECR = EMC_NFECR_ECCE | EMC_NFECR_ERST | EMC_NFECR_RS | EMC_NFECR_RS_ENCODING)
|
||
|
#define __nand_ecc_rs_decoding() (REG_EMC_NFECR = EMC_NFECR_ECCE | EMC_NFECR_ERST | EMC_NFECR_RS | EMC_NFECR_RS_DECODING)
|
||
|
#define __nand_ecc_disable() (REG_EMC_NFECR &= ~EMC_NFECR_ECCE)
|
||
|
#define __nand_ecc_encode_sync() while (!(REG_EMC_NFINTS & EMC_NFINTS_ENCF))
|
||
|
#define __nand_ecc_decode_sync() while (!(REG_EMC_NFINTS & EMC_NFINTS_DECF))
|
||
|
#define __nand_ecc_enable() (REG_EMC_NFECR = EMC_NFECR_ECCE | EMC_NFECR_ERST )
|
||
|
#define __nand_ecc_disable() (REG_EMC_NFECR &= ~EMC_NFECR_ECCE)
|
||
|
|
||
|
#define __nand_select_hm_ecc() (REG_EMC_NFECR &= ~EMC_NFECR_RS )
|
||
|
#define __nand_select_rs_ecc() (REG_EMC_NFECR |= EMC_NFECR_RS)
|
||
|
|
||
|
#define __nand_read_hm_ecc() (REG_EMC_NFECC & 0x00ffffff)
|
||
|
|
||
|
#define __nand_ecc() (REG_EMC_NFECC & 0x00ffffff)
|
||
|
#define __nand_cmd(n) (REG8(cmdport+csn) = (n))
|
||
|
#define __nand_addr(n) (REG8(addrport+csn) = (n))
|
||
|
#define __nand_data8() REG8(dataport+csn)
|
||
|
#define __nand_data16() REG16(dataport+csn)
|
||
|
|
||
|
#define CMD_READA 0x00
|
||
|
#define CMD_READB 0x01
|
||
|
#define CMD_READC 0x50
|
||
|
#define CMD_ERASE_SETUP 0x60
|
||
|
#define CMD_ERASE 0xD0
|
||
|
#define CMD_READ_STATUS 0x70
|
||
|
#define CMD_CONFIRM 0x30
|
||
|
#define CMD_SEQIN 0x80
|
||
|
#define CMD_PGPROG 0x10
|
||
|
#define CMD_READID 0x90
|
||
|
|
||
|
#define OOB_BAD_OFF 0x00
|
||
|
#define OOB_ECC_OFF 0x04
|
||
|
|
||
|
#define OP_ERASE 0
|
||
|
#define OP_WRITE 1
|
||
|
#define OP_READ 2
|
||
|
|
||
|
#define ECC_BLOCK 512
|
||
|
#define ECC_POS 6
|
||
|
#define PAR_SIZE 9
|
||
|
|
||
|
static volatile unsigned char *gpio_base;
|
||
|
static volatile unsigned char *emc_base;
|
||
|
static volatile unsigned char *addrport;
|
||
|
static volatile unsigned char *dataport;
|
||
|
static volatile unsigned char *cmdport;
|
||
|
unsigned int EMC_BASE;
|
||
|
unsigned int GPIO_BASE;
|
||
|
|
||
|
static int bus = 8, row = 2, pagesize = 512, oobsize = 16, ppb = 32;
|
||
|
static u32 bad_block_pos = 0,bad_block_page=0, csn = 0;
|
||
|
static u8 badbuf[2048 + 64] = {0};
|
||
|
static u8 data_buf[2048] = {0};
|
||
|
static u8 oob_buf[128] = {0};
|
||
|
static struct nand_oobinfo *oob_pos;
|
||
|
static np_data *np;
|
||
|
|
||
|
static inline void __nand_sync(void)
|
||
|
{
|
||
|
unsigned int timeout = 1000;
|
||
|
while ((REG_GPIO_PXPIN(2) & 0x40000000) && timeout--);
|
||
|
while (!(REG_GPIO_PXPIN(2) & 0x40000000));
|
||
|
}
|
||
|
|
||
|
static int read_oob(u8 *buf, u32 size, u32 pg);
|
||
|
static int nand_data_write8(unsigned char *buf, int count);
|
||
|
static int nand_data_write16(unsigned char *buf, int count);
|
||
|
static int nand_data_read8(unsigned char *buf, int count);
|
||
|
static int nand_data_read16(unsigned char *buf, int count);
|
||
|
|
||
|
static int (*write_proc)(unsigned char *, int) = 0;
|
||
|
static int (*read_proc)(unsigned char *, int) = 0;
|
||
|
|
||
|
extern void dumpbuf(u8 *p, int count);
|
||
|
|
||
|
unsigned int nand_query_4740(void)
|
||
|
{
|
||
|
u16 vid, did;
|
||
|
|
||
|
__nand_sync();
|
||
|
__nand_cmd(CMD_READID);
|
||
|
__nand_addr(0);
|
||
|
|
||
|
vid = __nand_data8();
|
||
|
did = __nand_data8();
|
||
|
|
||
|
return (vid << 16) | did;
|
||
|
}
|
||
|
|
||
|
int chip_select_4740(u8 cs)
|
||
|
{
|
||
|
csn = (u32)cs << 15; // modify this number for your board
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int nand_init_4740(np_data *npp)
|
||
|
{
|
||
|
bus = npp->bw;
|
||
|
row = npp->rc;
|
||
|
pagesize = npp->ps;
|
||
|
oobsize = npp->os;
|
||
|
ppb = npp->ppb;
|
||
|
bad_block_pos = npp->bbp;
|
||
|
bad_block_page = npp->bba;
|
||
|
gpio_base = (u8 *)npp->gpio_map;
|
||
|
emc_base = (u8 *)npp->base_map;
|
||
|
dataport = (u8 *)npp->port_map;
|
||
|
addrport = (u8 *)((u32)dataport + npp->ap_offset);
|
||
|
cmdport = (u8 *)((u32)dataport + npp->cp_offset);
|
||
|
|
||
|
EMC_BASE = (u32)emc_base;
|
||
|
GPIO_BASE = (u32)gpio_base;
|
||
|
|
||
|
/* Initialize NAND Flash Pins */
|
||
|
// __gpio_as_nand();
|
||
|
// __nand_enable();
|
||
|
|
||
|
chip_select_4740(npp->cs);
|
||
|
if (bus == 8) {
|
||
|
write_proc = nand_data_write8;
|
||
|
read_proc = nand_data_read8;
|
||
|
} else {
|
||
|
write_proc = nand_data_write16;
|
||
|
read_proc = nand_data_read16;
|
||
|
}
|
||
|
|
||
|
oob_pos = &oob_64[npp->ep];
|
||
|
// REG_EMC_SMCR1 = 0x0fff7700;
|
||
|
np = npp;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int nand_fini_4740(void)
|
||
|
{
|
||
|
__nand_disable();
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Read oob <pagenum> pages from <startpage> page.
|
||
|
* Don't skip bad block.
|
||
|
* Don't use HW ECC.
|
||
|
*/
|
||
|
int nand_read_oob_4740(u8 *buf, u32 startpage, u32 pagenum)
|
||
|
{
|
||
|
u32 cnt, cur_page;
|
||
|
u8 *tmpbuf;
|
||
|
|
||
|
tmpbuf = (u8 *)buf;
|
||
|
|
||
|
cur_page = startpage;
|
||
|
cnt = 0;
|
||
|
while (cnt < pagenum) {
|
||
|
read_oob((void *)tmpbuf, oobsize, cur_page);
|
||
|
|
||
|
tmpbuf += oobsize;
|
||
|
cur_page++;
|
||
|
cnt++;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int nand_check_block_4740(u32 block)
|
||
|
{
|
||
|
u32 pg;
|
||
|
|
||
|
pg = block * ppb + bad_block_page;
|
||
|
read_oob(oob_buf, oobsize, pg);
|
||
|
if (oob_buf[bad_block_pos] != 0xff)
|
||
|
return -1;
|
||
|
read_oob(oob_buf, oobsize, pg + 1);
|
||
|
if (oob_buf[bad_block_pos] != 0xff)
|
||
|
return -1;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Mark a block bad.
|
||
|
*/
|
||
|
void nand_block_markbad_4740(u32 block)
|
||
|
{
|
||
|
u32 i, rowaddr;
|
||
|
|
||
|
for (i = 0; i < pagesize + oobsize; i++)
|
||
|
badbuf[i] = 0x00;
|
||
|
badbuf[pagesize + bad_block_pos] = 0; /* bad block flag */
|
||
|
|
||
|
rowaddr = block * ppb + bad_block_page;
|
||
|
//bad block ID locate No.bad_block_page page
|
||
|
|
||
|
__nand_cmd(CMD_READA);
|
||
|
__nand_cmd(CMD_SEQIN);
|
||
|
|
||
|
__nand_addr(0);
|
||
|
if (pagesize == 2048)
|
||
|
__nand_addr(0);
|
||
|
for (i = 0; i < row; i++) {
|
||
|
__nand_addr(rowaddr & 0xff);
|
||
|
rowaddr >>= 8;
|
||
|
}
|
||
|
|
||
|
write_proc((unsigned char *)badbuf, pagesize + oobsize);
|
||
|
__nand_cmd(CMD_PGPROG);
|
||
|
__nand_sync();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Read data <pagenum> pages from <startpage> page.
|
||
|
* Don't skip bad block.
|
||
|
* Don't use HW ECC.
|
||
|
*/
|
||
|
int nand_read_raw_4740(u8 *buf, u32 startpage, u32 pagenum)
|
||
|
{
|
||
|
u32 cnt, j;
|
||
|
u32 cur_page, rowaddr;
|
||
|
u8 *tmpbuf;
|
||
|
|
||
|
tmpbuf = (u8 *)buf;
|
||
|
|
||
|
cur_page = startpage;
|
||
|
cnt = 0;
|
||
|
while (cnt < pagenum) {
|
||
|
__nand_sync();
|
||
|
__nand_cmd(CMD_READA);
|
||
|
__nand_addr(0);
|
||
|
if (pagesize == 2048)
|
||
|
__nand_addr(0);
|
||
|
|
||
|
rowaddr = cur_page;
|
||
|
for (j = 0; j < row; j++) {
|
||
|
__nand_addr(rowaddr & 0xff);
|
||
|
rowaddr >>= 8;
|
||
|
}
|
||
|
|
||
|
if (pagesize == 2048)
|
||
|
__nand_cmd(CMD_CONFIRM);
|
||
|
|
||
|
__nand_sync();
|
||
|
read_proc(tmpbuf, pagesize);
|
||
|
|
||
|
tmpbuf += pagesize;
|
||
|
cur_page++;
|
||
|
cnt++;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
int nand_erase_4740(int blk_num, int sblk, int force)
|
||
|
{
|
||
|
int i, j;
|
||
|
u32 cur, rowaddr;
|
||
|
|
||
|
cur = sblk * ppb;
|
||
|
for (i = 0; i < blk_num; i++) {
|
||
|
rowaddr = cur;
|
||
|
|
||
|
if (!force) { /* if set, erase anything */
|
||
|
/* test Badflag. */
|
||
|
__nand_sync();
|
||
|
|
||
|
__nand_cmd(CMD_READA);
|
||
|
|
||
|
__nand_addr(0);
|
||
|
if (pagesize == 2048)
|
||
|
__nand_addr(0);
|
||
|
for (j=0;j<row;j++) {
|
||
|
__nand_addr(rowaddr & 0xff);
|
||
|
rowaddr >>= 8;
|
||
|
}
|
||
|
|
||
|
if (pagesize == 2048)
|
||
|
__nand_cmd(CMD_CONFIRM);
|
||
|
|
||
|
__nand_sync();
|
||
|
|
||
|
read_proc((u8 *)data_buf, pagesize);
|
||
|
read_proc((u8 *)oob_buf, oobsize);
|
||
|
|
||
|
if (oob_buf[0] != 0xff) { /* Bad block, skip */
|
||
|
cur += ppb;
|
||
|
continue;
|
||
|
}
|
||
|
rowaddr = cur;
|
||
|
}
|
||
|
|
||
|
__nand_cmd(CMD_ERASE_SETUP);
|
||
|
|
||
|
for (j = 0; j < row; j++) {
|
||
|
__nand_addr(rowaddr & 0xff);
|
||
|
rowaddr >>= 8;
|
||
|
}
|
||
|
__nand_cmd(CMD_ERASE);
|
||
|
|
||
|
__nand_sync();
|
||
|
|
||
|
__nand_cmd(CMD_READ_STATUS);
|
||
|
|
||
|
if (__nand_data8() & 0x01)
|
||
|
{
|
||
|
/* Erase Error, mark it as bad block */
|
||
|
nand_block_markbad(cur);
|
||
|
|
||
|
} else ;
|
||
|
|
||
|
cur += ppb;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int read_oob(u8 *buf, u32 size, u32 pg)
|
||
|
{
|
||
|
u32 i, coladdr, rowaddr;
|
||
|
|
||
|
if (pagesize == 512)
|
||
|
coladdr = 0;
|
||
|
else
|
||
|
coladdr = pagesize;
|
||
|
|
||
|
if (pagesize == 512)
|
||
|
/* Send READOOB command */
|
||
|
__nand_cmd(CMD_READC);
|
||
|
else
|
||
|
/* Send READ0 command */
|
||
|
__nand_cmd(CMD_READA);
|
||
|
|
||
|
/* Send column address */
|
||
|
__nand_addr(coladdr & 0xff);
|
||
|
if (pagesize != 512)
|
||
|
__nand_addr(coladdr >> 8);
|
||
|
|
||
|
/* Send page address */
|
||
|
rowaddr = pg;
|
||
|
for (i = 0; i < row; i++) {
|
||
|
__nand_addr(rowaddr & 0xff);
|
||
|
rowaddr >>= 8;
|
||
|
}
|
||
|
|
||
|
/* Send READSTART command for 2048 ps NAND */
|
||
|
if (pagesize != 512)
|
||
|
__nand_cmd(CMD_CONFIRM);
|
||
|
|
||
|
/* Wait for device ready */
|
||
|
__nand_sync();
|
||
|
|
||
|
/* Read oob data */
|
||
|
read_proc(buf, size);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Correct 1~9-bit errors in 512-bytes data */
|
||
|
static void rs_correct(unsigned char *dat, int idx, int mask)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
idx--;
|
||
|
|
||
|
i = idx + (idx >> 3);
|
||
|
if (i >= 512)
|
||
|
return;
|
||
|
|
||
|
mask <<= (idx & 0x7);
|
||
|
|
||
|
dat[i] ^= mask & 0xff;
|
||
|
if (i < 511)
|
||
|
dat[i+1] ^= (mask >> 8) & 0xff;
|
||
|
}
|
||
|
|
||
|
static int nand_hm_correct_data(u8 *dat, u8 *oob_s, u8 *calc_ecc,u8 p)
|
||
|
{
|
||
|
u8 a, b, c, d1, d2, d3, add, bit, i;
|
||
|
u8 *e1,*e2,*e3;
|
||
|
|
||
|
e1 = &oob_s[oob_pos->eccpos[p+0]];
|
||
|
e2 = &oob_s[oob_pos->eccpos[p+1]];
|
||
|
e3 = &oob_s[oob_pos->eccpos[p+2]];
|
||
|
// printf("read ecc :%x %x %x %d %d\n",*e1,*e2,*e3,
|
||
|
// oob_pos->eccpos[p+0],oob_pos->eccpos[p+1]);
|
||
|
|
||
|
d1 = calc_ecc[0] ^ *e1;
|
||
|
d2 = calc_ecc[1] ^ *e2;
|
||
|
d3 = calc_ecc[2] ^ *e3;
|
||
|
|
||
|
if ((d1 | d2 | d3) == 0) {
|
||
|
/* No errors */
|
||
|
return 0;
|
||
|
}
|
||
|
else {
|
||
|
a = (d1 ^ (d1 >> 1)) & 0x55;
|
||
|
b = (d2 ^ (d2 >> 1)) & 0x55;
|
||
|
c = (d3 ^ (d3 >> 1)) & 0x54;
|
||
|
|
||
|
/* Found and will correct single bit error in the data */
|
||
|
if ((a == 0x55) && (b == 0x55) && (c == 0x54)) {
|
||
|
c = 0x80;
|
||
|
add = 0;
|
||
|
a = 0x80;
|
||
|
for (i=0; i<4; i++) {
|
||
|
if (d1 & c)
|
||
|
add |= a;
|
||
|
c >>= 2;
|
||
|
a >>= 1;
|
||
|
}
|
||
|
c = 0x80;
|
||
|
for (i=0; i<4; i++) {
|
||
|
if (d2 & c)
|
||
|
add |= a;
|
||
|
c >>= 2;
|
||
|
a >>= 1;
|
||
|
}
|
||
|
bit = 0;
|
||
|
b = 0x04;
|
||
|
c = 0x80;
|
||
|
for (i=0; i<3; i++) {
|
||
|
if (d3 & c)
|
||
|
bit |= b;
|
||
|
c >>= 2;
|
||
|
b >>= 1;
|
||
|
}
|
||
|
b = 0x01;
|
||
|
a = dat[add];
|
||
|
a ^= (b << bit);
|
||
|
dat[add] = a;
|
||
|
return 0;
|
||
|
}
|
||
|
else {
|
||
|
i = 0;
|
||
|
while (d1) {
|
||
|
if (d1 & 0x01)
|
||
|
++i;
|
||
|
d1 >>= 1;
|
||
|
}
|
||
|
while (d2) {
|
||
|
if (d2 & 0x01)
|
||
|
++i;
|
||
|
d2 >>= 1;
|
||
|
}
|
||
|
while (d3) {
|
||
|
if (d3 & 0x01)
|
||
|
++i;
|
||
|
d3 >>= 1;
|
||
|
}
|
||
|
if (i == 1) {
|
||
|
/* ECC Code Error Correction */
|
||
|
*e1 = calc_ecc[0];
|
||
|
*e2 = calc_ecc[1];
|
||
|
*e3 = calc_ecc[2];
|
||
|
return 0;
|
||
|
}
|
||
|
else {
|
||
|
/* Uncorrectable Error */
|
||
|
// printf("uncorrectable ECC error\n");
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Should never happen */
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Read data <pagenum> pages from <startpage> page.
|
||
|
* HW ECC is used.
|
||
|
*/
|
||
|
int nand_read_4740_hm(u8 *buf, u32 startpage, u32 pagenum)
|
||
|
{
|
||
|
u32 j, calc_ecc;
|
||
|
u32 cur_page, cnt, rowaddr, ecccnt;
|
||
|
u8 *tmpbuf;
|
||
|
ecccnt = pagesize / 256;
|
||
|
|
||
|
cur_page = startpage;
|
||
|
cnt = 0;
|
||
|
while (cnt < pagenum) {
|
||
|
/* read oob first */
|
||
|
read_oob(oob_buf, oobsize, cur_page);
|
||
|
|
||
|
__nand_sync();
|
||
|
__nand_cmd(CMD_READA);
|
||
|
|
||
|
__nand_addr(0);
|
||
|
if (pagesize == 2048)
|
||
|
__nand_addr(0);
|
||
|
|
||
|
rowaddr = cur_page;
|
||
|
for (j = 0; j < row; j++) {
|
||
|
__nand_addr(rowaddr & 0xff);
|
||
|
rowaddr >>= 8;
|
||
|
}
|
||
|
|
||
|
if (pagesize == 2048)
|
||
|
__nand_cmd(CMD_CONFIRM);
|
||
|
__nand_sync();
|
||
|
tmpbuf = (u8 *)((u32)buf + cnt * ( pagesize+oobsize));
|
||
|
|
||
|
for (j = 0; j < ecccnt ; j++)
|
||
|
{
|
||
|
__nand_ecc_enable();
|
||
|
__nand_select_hm_ecc();
|
||
|
read_proc(tmpbuf, 256);
|
||
|
__nand_ecc_disable();
|
||
|
calc_ecc = __nand_read_hm_ecc();
|
||
|
if (oob_pos->eccname == LINUXHM)
|
||
|
calc_ecc = ~calc_ecc | 0x00030000;
|
||
|
|
||
|
nand_hm_correct_data(tmpbuf,oob_buf,(u8*)&calc_ecc,j*3);
|
||
|
tmpbuf += 256;
|
||
|
}
|
||
|
|
||
|
for (j = 0; j < oobsize; j++)
|
||
|
tmpbuf[j] = oob_buf[j];
|
||
|
|
||
|
cur_page++;
|
||
|
cnt++;
|
||
|
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Read data <pagenum> pages from <startpage> page.
|
||
|
* HW ECC is used.
|
||
|
*/
|
||
|
int nand_read_4740_rs(u8 *buf, u32 startpage, u32 pagenum)
|
||
|
{
|
||
|
u32 j, k;
|
||
|
u32 cur_page, cnt, rowaddr, ecccnt;
|
||
|
u8 *tmpbuf;
|
||
|
ecccnt = pagesize / ECC_BLOCK;
|
||
|
|
||
|
cur_page = startpage;
|
||
|
cnt = 0;
|
||
|
while (cnt < pagenum) {
|
||
|
/* read oob first */
|
||
|
read_oob(oob_buf, oobsize, cur_page);
|
||
|
|
||
|
__nand_sync();
|
||
|
__nand_cmd(CMD_READA);
|
||
|
|
||
|
__nand_addr(0);
|
||
|
if (pagesize == 2048)
|
||
|
__nand_addr(0);
|
||
|
|
||
|
rowaddr = cur_page;
|
||
|
for (j = 0; j < row; j++) {
|
||
|
__nand_addr(rowaddr & 0xff);
|
||
|
rowaddr >>= 8;
|
||
|
}
|
||
|
|
||
|
if (pagesize == 2048)
|
||
|
__nand_cmd(CMD_CONFIRM);
|
||
|
__nand_sync();
|
||
|
tmpbuf = (u8 *)((u32)buf + cnt * ( pagesize+oobsize));
|
||
|
|
||
|
for (j = 0; j < ecccnt ; j++) {
|
||
|
volatile u8 *paraddr = (volatile u8 *)EMC_NFPAR0;
|
||
|
u32 stat;
|
||
|
|
||
|
/* Read data */
|
||
|
REG_EMC_NFINTS = 0x0;
|
||
|
__nand_ecc_rs_decoding();
|
||
|
read_proc(tmpbuf, ECC_BLOCK);
|
||
|
|
||
|
/* Set PAR values */
|
||
|
for (k = 0; k < PAR_SIZE; k++) {
|
||
|
*paraddr++ = oob_buf[oob_pos->eccpos[j*PAR_SIZE + k]];
|
||
|
}
|
||
|
|
||
|
/* Set PRDY */
|
||
|
REG_EMC_NFECR |= EMC_NFECR_PRDY;
|
||
|
|
||
|
/* Wait for completion */
|
||
|
__nand_ecc_decode_sync();
|
||
|
__nand_ecc_disable();
|
||
|
|
||
|
/* Check decoding */
|
||
|
stat = REG_EMC_NFINTS;
|
||
|
|
||
|
if (stat & EMC_NFINTS_ERR) {
|
||
|
// printf("Error occured!\n");
|
||
|
if (stat & EMC_NFINTS_UNCOR) {
|
||
|
int t;
|
||
|
for (t = 0; t < oob_pos->eccbytes; t++)
|
||
|
if (oob_buf[oob_pos->eccpos[t]] != 0xff) break;
|
||
|
if (t < oob_pos->eccbytes-1) {
|
||
|
// printf("Uncorrectable error occurred\n");
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
u32 errcnt = (stat & EMC_NFINTS_ERRCNT_MASK) >> EMC_NFINTS_ERRCNT_BIT;
|
||
|
switch (errcnt) {
|
||
|
case 4:
|
||
|
rs_correct(tmpbuf, (REG_EMC_NFERR3 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT, (REG_EMC_NFERR3 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT);
|
||
|
case 3:
|
||
|
rs_correct(tmpbuf, (REG_EMC_NFERR2 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT, (REG_EMC_NFERR2 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT);
|
||
|
case 2:
|
||
|
rs_correct(tmpbuf, (REG_EMC_NFERR1 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT, (REG_EMC_NFERR1 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT);
|
||
|
case 1:
|
||
|
rs_correct(tmpbuf, (REG_EMC_NFERR0 & EMC_NFERR_INDEX_MASK) >> EMC_NFERR_INDEX_BIT, (REG_EMC_NFERR0 & EMC_NFERR_MASK_MASK) >> EMC_NFERR_MASK_BIT);
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
/* increment pointer */
|
||
|
tmpbuf += ECC_BLOCK ;
|
||
|
}
|
||
|
|
||
|
for (j = 0; j < oobsize; j++)
|
||
|
tmpbuf[j] = oob_buf[j];
|
||
|
|
||
|
cur_page++;
|
||
|
cnt++;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int nand_program_4740(u8 *context, int spage, int pages)
|
||
|
{
|
||
|
u32 i, j, cur, rowaddr;
|
||
|
u8 *tmpbuf;
|
||
|
|
||
|
tmpbuf = (u8 *)context;
|
||
|
i = 0;
|
||
|
cur = spage;
|
||
|
|
||
|
while (i < pages) {
|
||
|
|
||
|
for (j=0;j<np->os;j++)
|
||
|
{
|
||
|
if (tmpbuf[j+np->ps]!=0xff)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (j==np->os)
|
||
|
{
|
||
|
tmpbuf += np->ps+np->os;
|
||
|
i ++;
|
||
|
cur ++;
|
||
|
continue;
|
||
|
}
|
||
|
if (pagesize != 2048)
|
||
|
__nand_cmd(CMD_READA);
|
||
|
|
||
|
__nand_cmd(CMD_SEQIN);
|
||
|
|
||
|
__nand_addr(0);
|
||
|
if (pagesize == 2048)
|
||
|
__nand_addr(0);
|
||
|
rowaddr = cur;
|
||
|
for (j = 0; j < row; j++) {
|
||
|
__nand_addr(rowaddr & 0xff);
|
||
|
rowaddr >>= 8;
|
||
|
}
|
||
|
|
||
|
write_proc(tmpbuf, np->ps+np->os);
|
||
|
tmpbuf += np->ps+np->os;
|
||
|
|
||
|
/* send program confirm command */
|
||
|
__nand_cmd(CMD_PGPROG);
|
||
|
__nand_sync();
|
||
|
|
||
|
__nand_cmd(CMD_READ_STATUS);
|
||
|
// __nand_sync();
|
||
|
|
||
|
if (__nand_data8() & 0x01) { /* page program error */
|
||
|
return -1;
|
||
|
} else ;
|
||
|
|
||
|
i ++;
|
||
|
cur ++;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int nand_data_write8(unsigned char *buf, int count)
|
||
|
{
|
||
|
int i;
|
||
|
u8 *p = (u8 *)buf;
|
||
|
for (i=0;i<count;i++)
|
||
|
__nand_data8() = *p++;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int nand_data_write16(unsigned char *buf, int count)
|
||
|
{
|
||
|
int i;
|
||
|
u16 *p = (u16 *)buf;
|
||
|
for (i=0;i<count/2;i++)
|
||
|
__nand_data16() = *p++;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int nand_data_read8(unsigned char *buf, int count)
|
||
|
{
|
||
|
int i;
|
||
|
u8 *p = (u8 *)buf;
|
||
|
for (i=0;i<count;i++)
|
||
|
*p++ = __nand_data8();
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int nand_data_read16(unsigned char *buf, int count)
|
||
|
{
|
||
|
int i;
|
||
|
u16 *p = (u16 *)buf;
|
||
|
for (i=0;i<count/2;i++)
|
||
|
*p++ = __nand_data16();
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|