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

📄 smc_core.c

📁 比较好的VIVI开发源码
💻 C
📖 第 1 页 / 共 2 页
字号:
/*
 * vivi/drivers/mtd/nand/smc_core.c: 
 *
 * Based on linux/drivers/mtd/nand/smc.c
 *
 * $Id: smc_core.c,v 1.3 2002/08/10 07:47:08 nandy Exp $
 *
 *
 * 棱淬:
 *   恐 this->oobblock捞扼绊 沁阑鳖? thsi->oobblcok_ofs捞扼绊 窍搁
 *   粱歹 疙犬窍瘤 臼阑鳖?
 */

#include "config.h"
#include "mtd/mtd.h"
#include "mtd/nand.h"
#include "mtd/nand_ids.h"
#include "mtd/nand_ecc.h"
#include "time.h"
#include "printk.h"
#include <types.h>
#include <errno.h>

/*
 * Macros for low-level register control
 */
#define nand_select()	this->hwcontrol(NAND_CTL_SETNCE); \
			nand_command(mtd, NAND_CMD_RESET, -1, -1); \
			udelay(10);
#define nand_deselect()	this->hwcontrol(NAND_CTL_CLRNCE);

static inline void 
sm_swap(u_char *x, u_char *y) {
	u_char tmp;
	tmp = *x;
	*x = *y;
	*y = tmp;
}

/* define if you'll be using < 2M SMC device */
#undef USE_256BYTE_NAND_FLASH

/*
 * Send command to NAND device
 */
static void
nand_command(struct mtd_info *mtd, unsigned command, int column, int page_addr)
{
	register struct nand_chip *this = mtd->priv;

	/* Begin command latch cycle */
	this->hwcontrol(NAND_CTL_SETCLE);

	this->hwcontrol(NAND_CTL_DAT_OUT);

	/*
	 * Write out the command to the device.
	 */
	if (command != NAND_CMD_SEQIN)
		this->write_cmd(command);
	else {
		if (mtd->oobblock == 256 && column >= 256) {
			column -= 256;
			this->write_cmd(NAND_CMD_RESET);
			this->write_cmd(NAND_CMD_READOOB);
			this->write_cmd(NAND_CMD_SEQIN);
		} else if (mtd->oobblock == 512 && column >= 256) {
			if (column < 512) {
				column -= 256;
				this->write_cmd(NAND_CMD_READ1);
				this->write_cmd(NAND_CMD_SEQIN);
			} else {
				column -= 512;
				this->write_cmd(NAND_CMD_READOOB);
				this->write_cmd(NAND_CMD_SEQIN);
			}
		} else {
			this->write_cmd(NAND_CMD_READ0);
			this->write_cmd(NAND_CMD_SEQIN);
		}
	}

	/* Set ALE and clear CLE to start address cycle */
	this->hwcontrol(NAND_CTL_CLRCLE);
	this->hwcontrol(NAND_CTL_SETALE);

	/* Serially input address */
	if (column != -1)
		this->write_addr(column);
	if (page_addr != -1) {
		this->write_addr((u_char)(page_addr & 0xff));
		this->write_addr((u_char)((page_addr >> 8) & 0xff));
		/* One more address cycle for higher density devices */
		if (mtd->size & 0x0c000000) {
			this->write_addr((u_char)((page_addr >> 16) & 0xff));
		}
	}

	/* Latch in address */
	this->hwcontrol(NAND_CTL_CLRALE);
	
	this->hwcontrol(NAND_CTL_DAT_IN);
	/* Pause for 15us */
	udelay(15);
 }

/*
 * NAND read
 */
static int
nand_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
{
	int j, col, page, state;
	struct nand_chip *this = mtd->priv;

	DEBUG(MTD_DEBUG_LEVEL3,
		__FUNCTION__"(): from = 0x%08lx, len = %i\n",
		(unsigned int)from, (int)len);

	/* Do not allow reads past end of device */
	if ((from + len) > mtd->size) {
		DEBUG(MTD_DEBUG_LEVEL0,
			__FUNCTION__"(): Attempt read beyond end of device\n");
		*retlen = 0;
		return -EINVAL;
	}

	/* First we calculate the starting page */
	page = from >> this->page_shift;

	/* Get raw starting column */
	col = from & (mtd->oobblock - 1);

	/* State machine for devices having pages larger than 256 bytes */
	state = (col < mtd->eccsize) ? 0 : 1;

	/* Calculate column address within ECC block context */
	col = (col >= mtd->eccsize) ? (col - mtd->eccsize) : col;

	/* Initialize return value */
	*retlen = 0;

	/* Select the NAND device */
	nand_select();

	/* Loop until all data read */
	while (*retlen < len) {
		/* Send the read command */
		if (!state)
			nand_command(mtd, NAND_CMD_READ0, col, page);
		else
			nand_command(mtd, NAND_CMD_READ1, col, page);

		this->wait_for_ready();

		/* Read the data directly into the return buffer */
		if ((*retlen + (mtd->eccsize - col)) >= len) {
			while (*retlen < len)
				buf[(*retlen)++] = this->read_data();
			/* We're done */
			continue;
		} else {
			for (j = col; j < mtd->eccsize; j++)
				buf[(*retlen)++] = this->read_data();
		}

		/*
		 * If the amount of data to be read is greater than
		 * (256 - col), then all subsequent reads will take
		 * place on page or half-page (in the case of 512 byte
		 * page devices) aligned boundaries and the column
		 * address will be zero. Setting the column address to
		 * zero after the first read allows us to simplify
		 * the reading of data and the if/else statements above.
		 */
		if (col)
			col = 0x00;

		/* Increment page address */
		if ((mtd->oobblock == 256) || state)
			page++;

		/* Toggle state machine */
		if (mtd->oobblock == 512)
			state = state ? 0 : 1;
	}

	/* De-select the NAND device */
	nand_deselect();

	/* Return happy */
	return 0;	
}

/*
 * if mtd->oobblock == 512
 */
inline int
smc_read_ecc_512(struct mtd_info *mtd, u_char *ecc_code)
{
	struct nand_chip *this = mtd->priv;
	u_char ecc_calc[3];
	int j, ret;

	/* Read in a block big enough for ECC */
	for (j = 0; j < (mtd->oobblock + mtd->oobsize); j++)
		this->data_buf[j] = this->read_data();

	for (j = 0; j < mtd->oobsize; j++)
		ecc_code[j] = this->data_buf[(mtd->oobblock + j)];

	nand_calculate_ecc(&this->data_buf[0], &ecc_calc[0]);
	sm_swap(&ecc_calc[0], &ecc_calc[1]);
	DEBUG(MTD_DEBUG_LEVEL3,
		__FUNCTION__"(): ECC [%02x%02x%02x : %02x%02x%02x]\n",
		ecc_code[SMC_OOB_ECC1], ecc_code[SMC_OOB_ECC1+1],
		ecc_code[SMC_OOB_ECC1+2], ecc_calc[0], ecc_calc[1], ecc_calc[2]);
	ret = nand_correct_data(&this->data_buf[0],
				&(ecc_code[SMC_OOB_ECC1]), &ecc_calc[0]);
	if (ret == -1)
		return ret;

	nand_calculate_ecc(&this->data_buf[mtd->eccsize], &ecc_calc[0]);
	sm_swap(&ecc_calc[0], &ecc_calc[1]);
	DEBUG(MTD_DEBUG_LEVEL3,
		__FUNCTION__"(): ECC [%02x%02x%02x : %02x%02x%02x]\n",
		ecc_code[SMC_OOB_ECC2], ecc_code[SMC_OOB_ECC2+1],
		ecc_code[SMC_OOB_ECC2+2], ecc_calc[0], ecc_calc[1], ecc_calc[2]);
	ret = nand_correct_data(&this->data_buf[mtd->eccsize],
				&(ecc_code[SMC_OOB_ECC2]), &ecc_calc[0]);
	if (ret == -1)
		return ret;

	return 0;
}

/*
 * NAND read with ECC
 */
static int
nand_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen,
              u_char *buf, u_char *ecc_code)
{
	int j, offset, page;
	struct nand_chip *this = mtd->priv;
	int ret;

	DEBUG(MTD_DEBUG_LEVEL3,
		__FUNCTION__ "(): from = 0x%08x, len = %i\n", (unsigned int)from,
		(int)len);

	/* Do not allow reads past end of device */
	if ((from + len) > mtd->size) {
		DEBUG(MTD_DEBUG_LEVEL0,
			__FUNCTION__ "(): Attempt read beyond end of device\n");
		*retlen = 0;
		return -EINVAL;
	}

	/* Select the NAND device */
	nand_select();

	/* Initialize return value */
	*retlen = 0;

#ifdef USE_256BYTE_NAND_FLASH
	/* First we calculate the starting page */
	page = from >> this->page_shift;

	/* Get raw starting column */
	offset = from & (mtd->oobblock - 1);

	/* if the length of Page is 256bytes,
	 * 2 Pages must be taken for 1 Sector and as a result,
	 * higher level 8 bytes of information
	 * among the above 16 byte information must coincide with
	 * Spare(or OOB) of Even-Page while lowel level 8 bytes
	 * coincide with Spar(or OOB) of Odd-page.
	 *   i.e, [0 block][oob][1 block][oob]
	 *        [2 block][oob][3 block][oob]...
	 */
	if (mtd->oobblock == 256) {
		u_char ecc_calc[3];
		int oob_offset;

		/* Loop until all data read */
		while (*retlen < len) {
			nand_command(mtd, NAND_CMD_READ0, 0x00, page);

			this->wait_for_ready();

			/* Read in a block big enough for ECC */
			for (j = 0; j < mtd->eccsize; j++)
				this->data_buf[j] = this->read_data();

			if (!(page & 0x1)) {	/* page is odd! */
				nand_command(mtd, NAND_CMD_READOOB,
					     SMC_OOB256_ECC1, page + 1);
				oob_offset = SMC_OOB_ECC1;
			} else {
				nand_command(mtd, NAND_CMD_READOOB,
					     SMC_OOB256_ECC2, page);
				oob_offset = SMC_OOB_ECC2;
			}

			this->wait_for_ready();

			for (j = 0; j < 3; j++)
				ecc_code[oob_offset + j] = this->read_data();
			nand_calculate_ecc(&this->data_buf[0], &ecc_calc[0]);
			sm_swap(&ecc_calc[0], &ecc_calc[1]);
			ret = nand_correct_data(&this->data_buf[0], 
						&(ecc_code[oob_offset]), 
						&ecc_calc[0]);
			if (ret == -1)
				goto nand_read_ecc_err;

			/* Read the data from ECC data buffer into return buffer */
			if ((*retlen + (mtd->eccsize - offset)) > = len) {
				while (*retlen < len)
					buf[(*retlen)++] = this->data_buf[offset++];
				/* We're done */
				continue;
			} else {
				for (j = offset; j < mtd->eccsize; j++)
					buf[(*retlen)++] = this->data_buf[j];
			}

			/*
			 * If the amount of data to be read is greater than
			 * (256 - offset), then all subsequent reads will take
			 * place on page or half-page (in the case of 512 byte
			 * page devices) aligned boundaries and the column
			 * address will be zero. Setting the column address to
			 * to zero after the first read allows us to simplify
			 * the reading of data and the if/else statements above.
			 */
			if (offset)
				offset = 0x00;

			/* Increment page address */
			page++;
		}
	} 
	else 
#endif	/* USE_256BYTE_NAND_FLASH */
	{ /* mtd->oobblock == 512 */
		size_t last, next, len2;

		last = from + len;
		for (next = from; from < last; ) {
			page = from >> this->page_shift;
			offset = from & (mtd->oobblock - 1);
			len2 = mtd->oobblock - offset;
			next += len2;
			nand_command(mtd, NAND_CMD_READ0, 0x00, page);

			this->wait_for_ready();

			ret = smc_read_ecc_512(mtd, ecc_code);
			if (ret == -1)
				goto nand_read_ecc_err;

			if (next >= last)
				if ((last & (mtd->oobblock - 1)) != 0)
					len2 = (last & (mtd->oobblock - 1)) - offset;

			for (j = 0; j < len2; j++)
				buf[(*retlen) + j] = this->data_buf[offset + j];
			*retlen += len2;
			from = next;
		}
	}

	ret = 0;

	/* De-select the NAND device */
	nand_deselect();

	return ret;

nand_read_ecc_err:
	DEBUG(MTD_DEBUG_LEVEL0,
		__FUNCTION__"(): Failed ECC read, page 0x%08lx\n", page);
	return -EIO;
}

/*
 * NAND read out-of-band
 */
static int
nand_read_oob(struct mtd_info *mtd, loff_t from, size_t len, 
              size_t *retlen, u_char *buf)
{
	int i, offset, page;
	struct nand_chip *this = mtd->priv;

	DEBUG(MTD_DEBUG_LEVEL3,
		__FUNCTION__ "(): from = 0x%08x, len = %i\n", (unsigned int)from,
		(int)len);

	/* Shift to get page */
	page = ((int)from) >> this->page_shift;

	/* Mask to get column */
	offset = from & 0x0f;

	/* Initialize return length value */
	*retlen = 0;

	/* Do not allow read past end of page */
	if ((offset + len) > mtd->oobsize) {
		DEBUG(MTD_DEBUG_LEVEL0,
			__FUNCTION__"(): Attempt read past end of page " \
			"0x%08lx, column %i, length %i\n", page, offset, len);
		return -EINVAL;
	}

	/* Select the NAND device */
	nand_select();

	/* Send the read command */
	nand_command(mtd, NAND_CMD_READOOB, offset, page);

	this->wait_for_ready();

	/* Read the data */
	for (i = 0; i < len; i++)
		buf[i] = this->read_data();

	/* De-select the NAND device */
	nand_deselect();

	/* Return happy */
	*retlen = len;
	return 0;
}

/*
 * NAND write
 */
static int
nand_write(struct mtd_info *mtd, loff_t to, size_t len, 
           size_t *retlen, const u_char *buf)
{
	int i, page, col, cnt, status;
	struct nand_chip *this = mtd->priv;

	DEBUG(MTD_DEBUG_LEVEL3,
		__FUNCTION__"(): to = 0x%08x, len = %i\n", (unsigned int)to,
		(int) len);

	/* Do not allow write past end of page */
	if ((to + len) > mtd->size) {
		DEBUG(MTD_DEBUG_LEVEL0,
			__FUNCTION__"(): Attempted write past end of device\n");
		return -EINVAL;
	}

	/* Shift to get page */
	page = ((int)to) >> this->page_shift;

	/* Get the starting column */
	col = to & (mtd->oobblock - 1);

	/* Initialize return length value */
	*retlen = 0;

	/* Select the NAND device */
	nand_select();

	/* Check the WP bit */
	nand_command(mtd, NAND_CMD_STATUS, -1, -1);

	this->wait_for_ready();

	if (!(this->read_data() & SMC_STAT_NOT_WP)) {
		DEBUG(MTD_DEBUG_LEVEL0,
			__FUNCTION__"(): Device is write protected!!!\n");
		i = -EPERM;
		goto nand_write_exit;
	}

	/* Loop until all data is written */
	while (*retlen < len) {
		/* Write data into buffer */
		if ((col + len) >= mtd->oobblock)
			for (i = col, cnt = 0; i < mtd->oobblock; i++, cnt++)
				this->data_buf[i] = buf[(*retlen + cnt)];
		else
			for (i = col, cnt = 0; cnt < (len - *retlen); i++, cnt++)
				this->data_buf[i] = buf[(*retlen + cnt)];
		/* Write ones for partial page programming */
		for (i = mtd->oobblock; i < (mtd->oobblock + mtd->oobsize); i++)
			this->data_buf[i] = 0xff;
		
		/* Write pre-padding bytes into buffer */
		for (i = 0; i < col; i++)
			this->data_buf[i] = 0xff;

		/* Write post-padding bytes into buffer */
		if ((col + (len - *retlen)) < mtd->oobblock) {
			for (i = (col + cnt); i < mtd->oobblock; i++)
				this->data_buf[i] = 0xff;
		}

		/* Send command to begin auto page programming */
		nand_command(mtd, NAND_CMD_SEQIN, 0x00, page);

		/* Write out complete page of data */
		this->hwcontrol(NAND_CTL_DAT_OUT);
		for (i = 0; i < (mtd->oobblock + mtd->oobsize); i++)
			this->write_data(this->data_buf[i]);
		this->hwcontrol(NAND_CTL_DAT_IN);

		/* Send command to actually program the data */
		nand_command(mtd, NAND_CMD_PAGEPROG, -1, -1);

		this->wait_for_ready();

		/*
		 * Wait for program operation to complete. This could
		 * take up to 3000us (3ms) on some devices. so we try
		 * and exit as quickly as possible.
		 */
		status = 0;
		for (i = 0; i < 24; i++) {
			/* Delay for 125us */
			udelay(125);

			/* Check for status */
			nand_command(mtd, NAND_CMD_STATUS, -1, -1);
			status = (int)this->read_data();
			if (status & SMC_STAT_READY)
				break;
		}

		/* See if device thinks it succeeded */
		if (status & SMC_STAT_WRITE_ERR) {
			DEBUG(MTD_DEBUG_LEVEL0,
				__FUNCTION__"(): Failed write, page 0x%08x, " \
				"%6i bytes were sucesful\n", page, *retlen);
			i = -EIO;
			goto nand_write_exit;
		}

⌨️ 快捷键说明

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