⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 nand.c

📁 Boot code for ADM5120 with serial console for Edimax router.
💻 C
📖 第 1 页 / 共 2 页
字号:
/*****************************************************************************;;   (C) Unpublished Work of ADMtek Incorporated.  All Rights Reserved.;;       THIS WORK IS AN UNPUBLISHED WORK AND CONTAINS CONFIDENTIAL,;       PROPRIETARY AND TRADESECRET INFORMATION OF ADMTEK INCORPORATED.;       ACCESS TO THIS WORK IS RESTRICTED TO (I) ADMTEK EMPLOYEES WHO HAVE A;       NEED TO KNOW TO PERFORM TASKS WITHIN THE SCOPE OF THEIR ASSIGNMENTS;       AND (II) ENTITIES OTHER THAN ADMTEK WHO HAVE ENTERED INTO APPROPRIATE;       LICENSE AGREEMENTS.  NO PART OF THIS WORK MAY BE USED, PRACTICED,;       PERFORMED, COPIED, DISTRIBUTED, REVISED, MODIFIED, TRANSLATED,;       ABBRIDGED, CONDENSED, EXPANDED, COLLECTED, COMPILED, LINKED, RECAST,;       TRANSFORMED OR ADAPTED WITHOUT THE PRIOR WRITTEN CONSENT OF ADMTEK.;       ANY USE OR EXPLOITATION OF THIS WORK WITHOUT AUTHORIZATION COULD;       SUBJECT THE PERPERTRATOR TO CRIMINAL AND CIVIL LIABILITY.;;------------------------------------------------------------------------------;;    Project : AM5120;    Creator : Jeanson Hung;    File    : nand.c;    Abstract:;;Modification History:;	10/8/2003,	ProChao		- support the bootloader to be compatible with the;							  NAND flash driver in application (Linux kernel);;;*****************************************************************************/#include <ctype.h>#include <mips.h>#include <mips4kc.h>#include <adm5120.h>/*#include <buart.h>*/#include "nand.h"#include "ftl-port.h"			//porting by ProChao, 10/8/2003//---------------------------------------------------------volatile UINT8 *base = (UINT8 *) NAND_REG_BASE;static struct nand_oobinfo oobinfo_buf = {	// init'ed by ProChao	1,	{0, 1, 2, 3, 4, 5}};static u_char oob_databuf[NAND_PAGE_SIZE + NAND_PAGE_OOB_SIZE];// adds the verifying block that can be used before reading/writing(erasing) blockstatic void nand_hwcontrol(int cmd){	switch (cmd) {		case NAND_CTL_SETCLE:			*(base + NAND_SET_CLE) = 0x01;			break;		case NAND_CTL_CLRCLE:			*(base + NAND_CLR_CLE) = 0x01;			break;		case NAND_CTL_SETALE:			*(base + NAND_SET_ALE) = 0x01;			break;		case NAND_CTL_CLRALE:			*(base + NAND_CLR_ALE) = 0x01;			break;		case NAND_CTL_SETNCE:			*(base + NAND_SET_CEn) = 0x01;			break;		case NAND_CTL_CLRNCE:			*(base + NAND_CLR_CEn) = 0x01;		default:			break;	}}//static UINT8 nand_read_byte(void){	return *base;				// readb( base);}static void nand_write_byte(UINT8 byte){	*base = byte;	//writeb( byte, base);}//static void nand_select_chip(int chip){	switch (chip) {		case -1:			nand_hwcontrol(NAND_CTL_CLRNCE);			break;		case 0:			nand_hwcontrol(NAND_CTL_SETNCE);			break;		default:				// bugs, should not be here			break;	}}static void nand_write_buf(const u_char * buf, int len){	int i;	for (i = 0; i < len; i++)		*base = buf[i];}static void nand_read_buf(u_char * buf, int len){	int i;	for (i = 0; i < len; i++)		buf[i] = *base;}static int nand_verify_buf(const u_char * buf, int len){	int i;	for (i = 0; i < len; i++)		if (buf[i] != *base)			return i;	//	return 0;}// send command to the NAND devicestatic void nand_command(unsigned command, int column, int page_addr){	UINT32 j;	int readcmd;	/* Begin command latch cycle */	nand_hwcontrol(NAND_CTL_SETCLE);	/* Write out the command to the device. */	if (command == NAND_CMD_SEQIN) {		if (column >= NAND_PAGE_SIZE) {	/* OOB area */			column -= NAND_PAGE_SIZE;			readcmd = NAND_CMD_READOOB;		} else if (column < 256) {	/* First 256 bytes --> READ0 */			readcmd = NAND_CMD_READ0;		} else {				// 2nd 256 bytes			column -= 256;			readcmd = NAND_CMD_READ1;		}		nand_write_byte(readcmd);	}	nand_write_byte(command);	/* Set ALE and clear CLE to start address cycle */	nand_hwcontrol(NAND_CTL_CLRCLE);	if (column != -1 || page_addr != -1) {		nand_hwcontrol(NAND_CTL_SETALE);		/* Serially input address */		if (column != -1)			nand_write_byte(column);		if (page_addr != -1) {			nand_write_byte((unsigned char) (page_addr & 0xff));			nand_write_byte((unsigned char) ((page_addr >> 8) & 0xff));			/* One more address cycle for higher density devices */			if (NAND_FLASH_SIZE & 0x0c000000)	// 32MB by ProChao, 10/8/2003				nand_write_byte((unsigned char) ((page_addr >> 16) & 0x0f));		}		/* Latch in address */		nand_hwcontrol(NAND_CTL_CLRALE);	}	/* program and erase have their own busy handlers	 * status and sequential in needs no delay	 */	switch (command) {		case NAND_CMD_PAGEPROG:		case NAND_CMD_ERASE1:		case NAND_CMD_ERASE2:		case NAND_CMD_SEQIN:		case NAND_CMD_STATUS:			return;		case NAND_CMD_RESET:			nand_hwcontrol(NAND_CTL_SETCLE);			nand_write_byte(NAND_CMD_STATUS);			nand_hwcontrol(NAND_CTL_CLRCLE);			while (!(nand_read_byte() & 0x40));			return;			/* This applies to read commands */		default:				/* If we don't have access to the busy pin, we apply the given								 * command delay */			// wait 1us */			for (j = 0; j < 1000; j++);	}}/* Appropriate chip should already be selected */static int nand_block_bad(unsigned long page){	UINT8 bad, status, status1;	nand_command(NAND_CMD_READOOB, NAND_BADBLOCK_POS, page);	bad = nand_read_byte();	if (bad != 0xff) {			// might be programmed alrady with ECC info, so checking other status info		status = nand_read_byte();	// readout next 2 octets		status1 = nand_read_byte();		if (status != status1)	// these 2 octets should be programmed same			return 1;			// this block is treated as the bad one		//		switch (status) {			case SECTOR_FREE:	// available for use			case SECTOR_USED:	// of course, for being used				break;			case SECTOR_IGNORED:			case SECTOR_DELETED:			default:				return 1;		// be treated as not-usable		}	}	return 0;}/* * Wait for command done. This applies to erase and program only * Erase can take up to 400ms and program up to 20ms according to * general NAND and SmartMedia specs **/static int nand_wait(int state){	int i, j, status;	int timeo;					// HZ=100 ?	if (state == FL_ERASING)		timeo = 50;	else		timeo = 3;//  spin_lock_bh (&this->chip_lock);	nand_command(NAND_CMD_STATUS, -1, -1);	while (timeo--) {			//delay for a while before reading		for (i = 0; i < 10; i++)	//10us			for (j = 0; j < 1000; j++);		//		if (nand_read_byte() & 0x40)	//status ready			break;	}	status = (int) nand_read_byte();	return status;}/* *	Nand_page_program function is used for write and writev ! *	This function will always program a full page of data *	If you call it with a non page aligned buffer, you're lost :) */static int nand_write_page(u_char * data_poi, int page, u_char * oob_buf, struct nand_oobinfo *oobsel){	int i, status;	u_char ecc_code[6], *oob_data;	/* force SOFT_ECC given by NAND driver, Jeanson */	int eccmode = NAND_ECC_SOFT;	//this->eccmode;	int *oob_config = oobsel->eccpos;	/* pad oob area, if we have no oob buffer from fs-driver */	if (!oob_buf) {		oob_data = &oob_databuf[NAND_PAGE_SIZE];	//this->data_buf[ mtd->oobblock];		for (i = 0; i < NAND_PAGE_OOB_SIZE; i++)			oob_data[i] = 0xff;	} else		oob_data = oob_buf;	/* Send command to begin auto page programming */	nand_command(NAND_CMD_SEQIN, 0x00, page);	/* Pull high SPn, Jeanson */	*(base + NAND_CLR_SPn_REG) = 1;	/* Write out complete page of data, take care of eccmode */	switch (eccmode) {			/* No ecc and software ecc 3/256, write all */		case NAND_ECC_NONE://          printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n");			nand_write_buf(data_poi, NAND_PAGE_SIZE);			break;		case NAND_ECC_SOFT:			nand_calculate_ecc(&data_poi[0], &ecc_code[0]);			for (i = 0; i < 3; i++)				oob_data[oob_config[i]] = ecc_code[i];			/* Calculate and write the second ECC for 512 Byte page size */			// must be 512-byte page size			nand_calculate_ecc(&data_poi[256], &ecc_code[3]);			for (i = 3; i < 6; i++)				oob_data[oob_config[i]] = ecc_code[i];			//			nand_write_buf(data_poi, NAND_PAGE_SIZE);			break;		default://          printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);//          BUG();			break;	}	/* Pull low SPn, Jeanson */	*(base + NAND_SET_SPn_REG) = 1;	/* Write out OOB data */	nand_write_buf(oob_data, NAND_PAGE_OOB_SIZE);	/* Send command to actually program the data */	nand_command(NAND_CMD_PAGEPROG, -1, -1);	/* call wait ready function */	status = nand_wait(FL_WRITING);	/* See if device thinks it succeeded */	if (status & 0x01) {//      DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page);		return -EIO;	}//#ifdef CONFIG_MTD_NAND_VERIFY_WRITE	/* The NAND device assumes that it is always writing to	 * a cleanly erased page. Hence, it performs its internal	 * write verification only on bits that transitioned from	 * 1 to 0. The device does NOT verify the whole page on a	 * byte by byte basis. It is possible that the page was	 * not completely erased or the page is becoming unusable	 * due to wear. The read with ECC would catch the error	 * later when the ECC page check fails, but we would rather	 * catch it early in the page write stage. Better to write	 * no data than invalid data.	 */	/* Send command to read back the page */	nand_command(NAND_CMD_READ0, 0, page);	/* Loop through and verify the data */	if (nand_verify_buf(data_poi, NAND_PAGE_SIZE)) {//      DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);		return -EIO;	}	/* check, if we have a fs-supplied oob-buffer */	if (oob_buf) {		if (nand_verify_buf(oob_data, NAND_PAGE_OOB_SIZE)) {//          DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);			return -EIO;		}	} else {					// soft ECC used		int ecc_bytes = 0;		ecc_bytes = (NAND_PAGE_SIZE == 512) ? 6 : 3;		nand_read_buf(oob_data, NAND_PAGE_OOB_SIZE);		for (i = 0; i < ecc_bytes; i++) {			if (oob_data[oob_config[i]] != ecc_code[i]) {//              DEBUG (MTD_DEBUG_LEVEL0, "%s: Failed ECC write "//                     "verify, page 0x%08x, " "%6i bytes were succesful\n", __FUNCTION__, page, i);				return -EIO;			}		}	}	/*	 * Terminate the read command. This is faster than sending a reset command or	 * applying a 20us delay before issuing the next programm sequence.	 * This is not a problem for all chips, but I have found a bunch of them.	 */	nand_select_chip(-1);	nand_select_chip(0);//#endif	return 0;}// now, read_ecc(), read_oob(), write_ecc(), and write_oob()/* NAND read with ECC */static int nand_read_ecc(loff_t from, size_t len, size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel){	int i, j, col, page, end, ecc;	int erase_state = 0;	int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0;//  struct  nand_chip *this = mtd->priv;	u_char *data_poi, *oob_data = oob_buf;	u_char ecc_calc[6];	u_char ecc_code[6];	int eccmode;	int *oob_config;	// use chip default if zero	if (oobsel == NULL)		oobsel = &oobinfo_buf;	// ???? how to initial this	/* force ECC_SOFT given by NAND driver, by Jeanson */	eccmode = NAND_ECC_SOFT;	//this->eccmode;	oob_config = oobsel->eccpos;	/* Do not allow reads past end of device */	if ((from + len) > NAND_FLASH_SIZE) {//      DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: Attempt read beyond end of device\n");		*retlen = 0;		return -EINVAL;	}	/* Grab the lock and see if the device is available */	//( FL_READING, &erase_state);  // under bootloader, should be ready always	/* Select the NAND device */	nand_select_chip(0);	/* First we calculate the starting page */	page = from >> ADD2SHIFT;	//9, this->page_shift;	/* Get raw starting column */	col = from & (NAND_PAGE_SIZE - 1);	end = NAND_PAGE_SIZE;		//512 bytes	ecc = NAND_PAGE_SIZE;		//512 bytes	/* Send the read command */	nand_command(NAND_CMD_READ0, 0x00, page);	/* Pull high SPn, Jeanson */	*(base + NAND_CLR_SPn_REG) = 1;	/* Loop until all data read */	while (read < len) {		/* If we have consequent page reads, apply delay or wait for ready/busy pin */		if (read) {				// wait chip_delay(10) us */			for (i = 0; i < 10; i++)				for (j = 0; j < 1000; j++);		}		/* If the read is not page aligned, we have to read into data buffer		 * due to ecc, else we read into return buffer direct		 */		if (!col && (len - read) >= end)			data_poi = &buf[read];		else			data_poi = oob_databuf;		/* get oob area, if we have no oob buffer from fs-driver */		if (!oob_buf) {			oob_data = &oob_databuf[end];			oob = 0;		}		/* Send the read command */		// if this page is the 1st page of current block		if ((page % NAND_BLK_PER_PAGE) == 0)	// ProChao, 10/13/2003			nand_command(NAND_CMD_READ0, 0x00, page);		//		j = 0;		switch (eccmode) {			case NAND_ECC_NONE:	/* No ECC, Read in a page */				nand_read_buf(data_poi, end);				break;			case NAND_ECC_SOFT:	/* Software ECC 3/256: Read in a page + oob data */				nand_read_buf(data_poi, end);				nand_calculate_ecc(&data_poi[0], &ecc_calc[0]);				//if (mtd->oobblock == 512) // must be 512 bytes				nand_calculate_ecc(&data_poi[256], &ecc_calc[3]);				break;#if 0							// commented out by ProChao, 10/8/2003			case NAND_ECC_HW3_256:	/* Hardware ECC 3 byte /256 byte data: Read in first 256 byte, get ecc, */				this->enable_hwecc(mtd, NAND_ECC_READ);				this->read_buf(mtd, data_poi, ecc);				this->calculate_ecc(mtd, &data_poi[0], &ecc_calc[0]);	/* read from hardware */				if (mtd->oobblock == 512) {	/* read second, if pagesize = 512 */					this->enable_hwecc(mtd, NAND_ECC_READ);					this->read_buf(mtd, &data_poi[ecc], end - ecc);					this->calculate_ecc(mtd, &data_poi[256], &ecc_calc[3]);	/* read from hardware */				}				break;			case NAND_ECC_HW3_512:			case NAND_ECC_HW6_512:	/* Hardware ECC 3/6 byte / 512 byte data : Read in a page  */				this->enable_hwecc(mtd, NAND_ECC_READ);				this->read_buf(mtd, data_poi, end);				this->calculate_ecc(mtd, &data_poi[0], &ecc_calc[0]);	/* read from hardware */				break;#endif			default://              printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);//              BUG();				break;		}		/* Pull low SPn, Jeanson */		*(base + NAND_SET_SPn_REG) = 1;		/* read oobdata */		for (j = 0; j < NAND_PAGE_OOB_SIZE; j++)			oob_data[oob + j] = nand_read_byte();		// if this page is the 1st page of current block		if ((page % NAND_BLK_PER_PAGE) == 0)	// ProChao, 10/13/2003		{						// yes, this page is the 1st page, using the oob_data to check block validity			if (oob_data[5] != SECTOR_FREE) {	// this block might be used normal (marked FREE or USED)				if ((oob_data[6] != oob_data[7]) || (oob_data[6] != SECTOR_USED) && (oob_data[6] != SECTOR_FREE)) {					page += NAND_BLK_PER_PAGE;	// move forward to next block					col = 0;					/* Send the read command */					nand_command(NAND_CMD_READ0, 0x00, page);					//					continue;				}			}		}		/* Skip ECC, if not active */		if (eccmode == NAND_ECC_NONE)			goto readdata;		/* Pick the ECC bytes out of the oob data */		for (j = 0; j < 6; j++)			ecc_code[j] = oob_data[oob + oob_config[j]];		/* correct data, if neccecary */		ecc_status = nand_correct_data(&data_poi[0], &ecc_code[0], &ecc_calc[0]);		/* check, if we have a fs supplied oob-buffer */		if (oob_buf) {			oob += NAND_PAGE_OOB_SIZE;			*((int *) &oob_data[oob]) = ecc_status;			oob += sizeof(int);		}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -