www.pudn.com > usbboot-v1.2.rar.zip > nandflash_4740.c, change:2008-03-01,size:14019b


#include "nandflash.h"
#include "jz4740.h"
#include "usb_boot.h"

#define __nand_enable()		(REG_EMC_NFCSR |= EMC_NFCSR_NFE1 | EMC_NFCSR_NFCE1)
#define __nand_disable()	(REG_EMC_NFCSR &= ~(EMC_NFCSR_NFCE1))
#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_ready()		((REG_GPIO_PXPIN(2) & 0x40000000) ? 1 : 0)
#define __nand_ecc()		(REG_EMC_NFECC & 0x00ffffff)
#define __nand_cmd(n)		(REG8(cmdport) = (n))
#define __nand_addr(n)		(REG8(addrport) = (n))
#define __nand_data8()		REG8(dataport)
#define __nand_data16()		REG16(dataport)

#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
#define ECC_SIZE        36

static volatile unsigned char *gpio_base = (volatile unsigned char *)0xb0010000;
static volatile unsigned char *emc_base = (volatile unsigned char *)0xb3010000;
static volatile unsigned char *addrport = (volatile unsigned char *)0xb8010000;
static volatile unsigned char *dataport = (volatile unsigned char *)0xb8000000;
static volatile unsigned char *cmdport = (volatile unsigned char *)0xb8008000;

static int bus = 8, row = 2, pagesize = 2048, oobsize = 64, ppb = 128;
static int bad_block_pos,bad_block_page,force_erase,ecc_pos;

static u8 data_buf[2048] = {0};
static u8 oob_buf[128] = {0};

static inline void __nand_sync(void)
{
	unsigned int timeout = 2000;
	while ((REG_GPIO_PXPIN(2) & 0x40000000) && timeout--);
	while (!(REG_GPIO_PXPIN(2) & 0x40000000));
}

static int read_oob(void *buf, u32 size, u32 pg);
static int nand_data_write8(char *buf, int count);
static int nand_data_write16(char *buf, int count);
static int nand_data_read8(char *buf, int count);
static int nand_data_read16(char *buf, int count);
static int (*write_proc)(char *, int) = NULL;
static int (*read_proc)(char *, int) = NULL;

static nand_init_gpio(void)
{
	//modify this fun to a specifical borad
	//this fun init those gpio use by all flash chip
	//select the gpio function related to flash chip
	__gpio_as_nand();
}

inline void nand_enable_4740(unsigned int csn)
{
	//modify this fun to a specifical borad
	//this fun to enable the chip select pin csn
	//the choosn chip can work after this fun
	//dprintf("\n Enable chip select :%d",csn);
	__nand_enable();
}

inline void nand_disable_4740(unsigned int csn)
{
	//modify this fun to a specifical borad
	//this fun to enable the chip select pin csn
	//the choosn chip can not work after this fun
	//dprintf("\n Disable chip select :%d",csn);
	__nand_disable();
}

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 nand_init_4740(int bus_width, int row_cycle, int page_size, int page_per_block,
		   int bbpage,int bbpos,int force,int ep)
{
	bus = bus_width;
	row = row_cycle;
	pagesize = page_size;
	oobsize = pagesize / 32;
	ppb = page_per_block;
	bad_block_pos = bbpos;
	bad_block_page = bbpage;
	force_erase = force;
	ecc_pos = ep;

	/* Initialize NAND Flash Pins */
	nand_init_gpio();
//	REG_EMC_SMCR1 = 0x077f7700;      //optimize speed???
	REG_EMC_SMCR1 = 0x04444400;      //optimize speed???
//	REG_EMC_SMCR1 = 0x022e2200;      //optimize speed???

	if (bus == 8) {
		write_proc = nand_data_write8;
		read_proc = nand_data_read8;
	} else {
		write_proc = nand_data_write16;
		read_proc = nand_data_read16;
	}
	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(void *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;
}

static int nand_check_block(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;
}


/*
 * Read data <pagenum> pages from <startpage> page.
 * Don't skip bad block.
 * Don't use HW ECC.
 */
int nand_read_raw_4740(void *buf, u32 startpage, u32 pagenum,int option)
{
	u32 cnt, j;
	u32 cur_page, rowaddr;
	u8 *tmpbuf;

	tmpbuf = (u8 *)buf;

	cur_page = startpage;
	cnt = 0;
	while (cnt < pagenum) {
		//__nand_sync();
		if ((cur_page % ppb) == 0) {
			if (nand_check_block(cur_page / ppb)) {
				cur_page += ppb;   // Bad block, set to next block 
				continue;
			}
		}

		__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;

		if (option != NO_OOB)
		{
			read_oob(tmpbuf, oobsize, cur_page);
			tmpbuf += oobsize;
		}

		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 */
			//__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) ;

		cur += ppb;
	}

	return 0;
}

static int read_oob(void *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;
}

void rs_correct(unsigned char *buf, int idx, int mask)
{
	int i, j;
	unsigned short d, d1, dm;

	i = (idx * 9) >> 3;
	j = (idx * 9) & 0x7;

	i = (j == 0) ? (i - 1) : i;
	j = (j == 0) ? 7 : (j - 1);

	d = (buf[i] << 8) | buf[i - 1];

	d1 = (d >> j) & 0x1ff;
	d1 ^= mask;

	dm = ~(0x1ff << j);
	d = (d & dm) | (d1 << j);

	buf[i - 1] = d & 0xff;
	buf[i] = (d >> 8) & 0xff;
}

 /*
 * Read data <pagenum> pages from <startpage> page.
 * Skip bad block if detected.
 * HW ECC is used.
 */
int nand_read_4740(void *buf, u32 startpage, u32 pagenum,int option)
{
	u32 j, k;
	u32 cur_page, cur_blk, cnt, rowaddr, ecccnt;
	u8 *tmpbuf,*p;
	ecccnt = pagesize / ECC_BLOCK;
	
	cur_page = startpage;
	cnt = 0;

	while (cnt < pagenum) {
		/* If this is the first page of the block, check for bad. */
		if ((cur_page % ppb) == 0) {
			cur_blk = cur_page / ppb;
			if (nand_check_block(cur_blk)) {
				cur_page += ppb;   // Bad block, set to next block 
				continue;
			}
		}
	
		/* 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 *)data_buf;

		for (j = 0; j < ecccnt; j++) {
			volatile u8 *paraddr = (volatile u8 *)EMC_NFPAR0;
			u32 stat;
			REG_EMC_NFINTS = 0x0;
			__nand_ecc_rs_decoding();
			read_proc(tmpbuf, ECC_BLOCK);
			for (k = 0; k < PAR_SIZE; k++) {
				*paraddr++ = oob_buf[ecc_pos + j*PAR_SIZE + k];
			}
			REG_EMC_NFECR |= EMC_NFECR_PRDY;
			__nand_ecc_decode_sync();
			__nand_ecc_disable();
			/* Check decoding */
			stat = REG_EMC_NFINTS;
			if (stat & EMC_NFINTS_ERR) {
				if (stat & EMC_NFINTS_UNCOR) {
					serial_puts("\nUncorrectable 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;
		}

		switch (option)
		{
		case	OOB_ECC:
			tmpbuf = (u8 *)((u32)buf + cnt * (pagesize + oobsize));
			p = (u8 *)data_buf;
			for (j = 0; j < pagesize; j++)
				tmpbuf[j] = p[j];
			for (j = 0; j < oobsize; j++)
				tmpbuf[pagesize+j] = oob_buf[j];
			break;
		case	OOB_NO_ECC:
			tmpbuf = (u8 *)((u32)buf + cnt * (pagesize + oobsize));
			p = (u8 *)data_buf;
			for (j = 0; j < pagesize + oobsize; j++)
				tmpbuf[j] = p[j];
			for (j = 0; j < ECC_SIZE; j++)
				tmpbuf[pagesize + ecc_pos + j] = 0xff;
			break;
		case	NO_OOB:
			tmpbuf = (u8 *)((u32)buf + cnt * pagesize);
			p = (u8 *)data_buf;
			for (j = 0; j < pagesize; j++)
				tmpbuf[j] = p[j];
			break;
		}

		cur_page++;
		cnt++;
	}
	return 0;
}

int nand_program_4740(void *context, int spage, int pages, int option)
{
	u32 i, j, cur, rowaddr;
	u8 *tmpbuf;
	u32 ecccnt;
	u8 ecc_buf[64];

	tmpbuf = (u8 *)context;
	ecccnt = pagesize / ECC_BLOCK;

	i = 0;
	cur = spage;

	while (i < pages) {
		if ((cur % ppb) == 0) {
			if (nand_check_block(cur / ppb)) {
				cur += ppb;   // Bad block, set to next block 
				continue;
			}
		}

		__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;
		}

		for ( j = 0 ; j < oobsize ; j ++)
		{
			if (tmpbuf[j + pagesize ]!=0xff)
				break;
		}

		if ( j == oobsize ) 
		{
			tmpbuf += pagesize + oobsize ;
			i ++;
			cur ++;
			continue;
		}
		switch (option)
		{
		case OOB_ECC:
			write_proc(tmpbuf, pagesize);  //write data
			tmpbuf += pagesize;
			write_proc((u8 *)tmpbuf, oobsize); //write oob
			tmpbuf += oobsize;

			break;
		case OOB_NO_ECC:          
			for (j = 0; j < ecccnt; j++) {
				volatile u8 *paraddr = (volatile u8 *)EMC_NFPAR0;
				int k;

				REG_EMC_NFINTS = 0x0;				
				__nand_ecc_rs_encoding();
				write_proc(tmpbuf, ECC_BLOCK);
				__nand_ecc_encode_sync();
				__nand_ecc_disable();

				/* Read PAR values */
				for (k = 0; k < PAR_SIZE; k++) {
					ecc_buf[j*PAR_SIZE+k] = *paraddr++;
				}
				
				tmpbuf += ECC_BLOCK;
			}
			for (j = 0; j < oobsize; j++) {
				oob_buf[j] = tmpbuf[j];
			}
			
			for (j = 0; j < ecccnt*PAR_SIZE; j++) 
				oob_buf[ecc_pos + j] = ecc_buf[j];
			write_proc((u8 *)oob_buf, oobsize);
			tmpbuf += oobsize;

			break;
		case NO_OOB:              //bin image
			/* write out data */
			for (j = 0; j < ecccnt; j++) {
				volatile u8 *paraddr = (volatile u8 *)EMC_NFPAR0;
				int k;

				REG_EMC_NFINTS = 0x0;				
				__nand_ecc_rs_encoding();
				write_proc(tmpbuf, ECC_BLOCK);
				__nand_ecc_encode_sync();
				__nand_ecc_disable();

				/* Read PAR values */
				for (k = 0; k < PAR_SIZE; k++) {
					ecc_buf[j*PAR_SIZE+k] = *paraddr++;
				}
				
				tmpbuf += ECC_BLOCK;
			}
			
			for (j = 0; j < oobsize; j++) {
				oob_buf[j] = 0xff;
			}
			oob_buf[2] = 0;
			oob_buf[3] = 0;
			oob_buf[4] = 0;

			for (j = 0; j < ecccnt*PAR_SIZE; j++) {
				oob_buf[ecc_pos + j] = ecc_buf[j];
			}
			write_proc((u8 *)oob_buf, oobsize);
			break;
		}

		/* send program confirm command */
		__nand_cmd(CMD_PGPROG);
		__nand_sync();

		__nand_cmd(CMD_READ_STATUS);

		if (__nand_data8() & 0x01)  /* page program error */
			;
		i ++;
		cur ++;
	}
	return 0;
}

static int nand_data_write8(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(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(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(char *buf, int count)
{
	int i;
	u16 *p = (u16 *)buf;
	for (i=0;i<count/2;i++)
		*p++ = __nand_data16();
	return 0;
}