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

📄 nand_base.c

📁 基于linux-2.6.28的mtd驱动
💻 C
📖 第 1 页 / 共 5 页
字号:
	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:		if (chip->dev_ready)			break;		udelay(chip->chip_delay);		chip->cmd_ctrl(mtd, NAND_CMD_STATUS,			       NAND_CTRL_CLE | NAND_CTRL_CHANGE);		chip->cmd_ctrl(mtd,			       NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);		while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) ;		return;		/* This applies to read commands */	default:		/*		 * If we don't have access to the busy pin, we apply the given		 * command delay		 */		if (!chip->dev_ready) {			udelay(chip->chip_delay);			return;		}	}	/* Apply this short delay always to ensure that we do wait tWB in	 * any case on any machine. */	ndelay(100);	nand_wait_ready(mtd);}/** * nand_command_lp - [DEFAULT] Send command to NAND large page device * @mtd:	MTD device structure * @command:	the command to be sent * @column:	the column address for this command, -1 if none * @page_addr:	the page address for this command, -1 if none * * Send command to NAND device. This is the version for the new large page * devices We dont have the separate regions as we have in the small page * devices.  We must emulate NAND_CMD_READOOB to keep the code compatible. */static void nand_command_lp(struct mtd_info *mtd, unsigned int command,			    int column, int page_addr){	register struct nand_chip *chip = mtd->priv;	/* Emulate NAND_CMD_READOOB */	if (command == NAND_CMD_READOOB) {		column += mtd->writesize;		command = NAND_CMD_READ0;	}	/* Command latch cycle */	chip->cmd_ctrl(mtd, command & 0xff,		       NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);	if (column != -1 || page_addr != -1) {		int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;		/* Serially input address */		if (column != -1) {			/* Adjust columns for 16 bit buswidth */			if (chip->options & NAND_BUSWIDTH_16)				column >>= 1;			chip->cmd_ctrl(mtd, column, ctrl);			ctrl &= ~NAND_CTRL_CHANGE;			chip->cmd_ctrl(mtd, column >> 8, ctrl);		}		if (page_addr != -1) {			chip->cmd_ctrl(mtd, page_addr, ctrl);			chip->cmd_ctrl(mtd, page_addr >> 8,				       NAND_NCE | NAND_ALE);			/* One more address cycle for devices > 128MiB */			if (chip->chipsize > (128 << 20))				chip->cmd_ctrl(mtd, page_addr >> 16,					       NAND_NCE | NAND_ALE);		}	}	chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);	/*	 * program and erase have their own busy handlers	 * status, sequential in, and deplete1 need no delay	 */	switch (command) {	case NAND_CMD_CACHEDPROG:	case NAND_CMD_PAGEPROG:	case NAND_CMD_ERASE1:	case NAND_CMD_ERASE2:	case NAND_CMD_SEQIN:	case NAND_CMD_RNDIN:	case NAND_CMD_STATUS:	case NAND_CMD_DEPLETE1:		return;		/*		 * read error status commands require only a short delay		 */	case NAND_CMD_STATUS_ERROR:	case NAND_CMD_STATUS_ERROR0:	case NAND_CMD_STATUS_ERROR1:	case NAND_CMD_STATUS_ERROR2:	case NAND_CMD_STATUS_ERROR3:		udelay(chip->chip_delay);		return;	case NAND_CMD_RESET:		if (chip->dev_ready)			break;		udelay(chip->chip_delay);		chip->cmd_ctrl(mtd, NAND_CMD_STATUS,			       NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);		chip->cmd_ctrl(mtd, NAND_CMD_NONE,			       NAND_NCE | NAND_CTRL_CHANGE);		while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) ;		return;	case NAND_CMD_RNDOUT:		/* No ready / busy check necessary */		chip->cmd_ctrl(mtd, NAND_CMD_RNDOUTSTART,			       NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);		chip->cmd_ctrl(mtd, NAND_CMD_NONE,			       NAND_NCE | NAND_CTRL_CHANGE);		return;	case NAND_CMD_READ0:		chip->cmd_ctrl(mtd, NAND_CMD_READSTART,			       NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);		chip->cmd_ctrl(mtd, NAND_CMD_NONE,			       NAND_NCE | NAND_CTRL_CHANGE);		/* This applies to read commands */	default:		/*		 * If we don't have access to the busy pin, we apply the given		 * command delay		 */		if (!chip->dev_ready) {			udelay(chip->chip_delay);			return;		}	}	/* Apply this short delay always to ensure that we do wait tWB in	 * any case on any machine. */	ndelay(100);	nand_wait_ready(mtd);}/** * nand_get_device - [GENERIC] Get chip for selected access * @chip:	the nand chip descriptor * @mtd:	MTD device structure * @new_state:	the state which is requested * * Get the device and lock it for exclusive access */static intnand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state){	spinlock_t *lock = &chip->controller->lock;	wait_queue_head_t *wq = &chip->controller->wq;	DECLARE_WAITQUEUE(wait, current); retry:	spin_lock(lock);	/* Hardware controller shared among independend devices */	/* Hardware controller shared among independend devices */	if (!chip->controller->active)		chip->controller->active = chip;	if (chip->controller->active == chip && chip->state == FL_READY) {		chip->state = new_state;		spin_unlock(lock);		return 0;	}	if (new_state == FL_PM_SUSPENDED) {		spin_unlock(lock);		return (chip->state == FL_PM_SUSPENDED) ? 0 : -EAGAIN;	}	set_current_state(TASK_UNINTERRUPTIBLE);	add_wait_queue(wq, &wait);	spin_unlock(lock);	schedule();	remove_wait_queue(wq, &wait);	goto retry;}/** * nand_wait - [DEFAULT]  wait until the command is done * @mtd:	MTD device structure * @chip:	NAND chip structure * * 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(struct mtd_info *mtd, struct nand_chip *chip){	unsigned long timeo = jiffies;	int status, state = chip->state;	if (state == FL_ERASING)		timeo += (HZ * 400) / 1000;	else		timeo += (HZ * 20) / 1000;	led_trigger_event(nand_led_trigger, LED_FULL);	/* Apply this short delay always to ensure that we do wait tWB in	 * any case on any machine. */	ndelay(100);	if ((state == FL_ERASING) && (chip->options & NAND_IS_AND))		chip->cmdfunc(mtd, NAND_CMD_STATUS_MULTI, -1, -1);	else		chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);	while (time_before(jiffies, timeo)) {		if (chip->dev_ready) {			if (chip->dev_ready(mtd))				break;		} else {			if (chip->read_byte(mtd) & NAND_STATUS_READY)				break;		}		cond_resched();	}	led_trigger_event(nand_led_trigger, LED_OFF);	status = (int)chip->read_byte(mtd);	return status;}/** * nand_read_page_raw - [Intern] read raw page data without ecc * @mtd:	mtd info structure * @chip:	nand chip info structure * @buf:	buffer to store read data */static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,			      uint8_t *buf){	chip->read_buf(mtd, buf, mtd->writesize);	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);	return 0;}/** * nand_read_page_swecc - [REPLACABLE] software ecc based page read function * @mtd:	mtd info structure * @chip:	nand chip info structure * @buf:	buffer to store read data */static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,				uint8_t *buf){	int i, eccsize = chip->ecc.size;	int eccbytes = chip->ecc.bytes;	int eccsteps = chip->ecc.steps;	uint8_t *p = buf;	uint8_t *ecc_calc = chip->buffers->ecccalc;	uint8_t *ecc_code = chip->buffers->ecccode;	uint32_t *eccpos = chip->ecc.layout->eccpos;	chip->ecc.read_page_raw(mtd, chip, buf);	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)		chip->ecc.calculate(mtd, p, &ecc_calc[i]);	for (i = 0; i < chip->ecc.total; i++)		ecc_code[i] = chip->oob_poi[eccpos[i]];	eccsteps = chip->ecc.steps;	p = buf;	for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {		int stat;		stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);		if (stat < 0)			mtd->ecc_stats.failed++;		else			mtd->ecc_stats.corrected += stat;	}	return 0;}/** * nand_read_subpage - [REPLACABLE] software ecc based sub-page read function * @mtd:	mtd info structure * @chip:	nand chip info structure * @data_offs:	offset of requested data within the page * @readlen:	data length * @bufpoi:	buffer to store read data */static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi){	int start_step, end_step, num_steps;	uint32_t *eccpos = chip->ecc.layout->eccpos;	uint8_t *p;	int data_col_addr, i, gaps = 0;	int datafrag_len, eccfrag_len, aligned_len, aligned_pos;	int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;	/* Column address wihin the page aligned to ECC size (256bytes). */	start_step = data_offs / chip->ecc.size;	end_step = (data_offs + readlen - 1) / chip->ecc.size;	num_steps = end_step - start_step + 1;	/* Data size aligned to ECC ecc.size*/	datafrag_len = num_steps * chip->ecc.size;	eccfrag_len = num_steps * chip->ecc.bytes;	data_col_addr = start_step * chip->ecc.size;	/* If we read not a page aligned data */	if (data_col_addr != 0)		chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1);	p = bufpoi + data_col_addr;	chip->read_buf(mtd, p, datafrag_len);	/* Calculate  ECC */	for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size)		chip->ecc.calculate(mtd, p, &chip->buffers->ecccalc[i]);	/* The performance is faster if to position offsets	   according to ecc.pos. Let make sure here that	   there are no gaps in ecc positions */	for (i = 0; i < eccfrag_len - 1; i++) {		if (eccpos[i + start_step * chip->ecc.bytes] + 1 !=			eccpos[i + start_step * chip->ecc.bytes + 1]) {			gaps = 1;			break;		}	}	if (gaps) {		chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);		chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);	} else {		/* send the command to read the particular ecc bytes */		/* take care about buswidth alignment in read_buf */		aligned_pos = eccpos[start_step * chip->ecc.bytes] & ~(busw - 1);		aligned_len = eccfrag_len;		if (eccpos[start_step * chip->ecc.bytes] & (busw - 1))			aligned_len++;		if (eccpos[(start_step + num_steps) * chip->ecc.bytes] & (busw - 1))			aligned_len++;		chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize + aligned_pos, -1);		chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len);	}	for (i = 0; i < eccfrag_len; i++)		chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + start_step * chip->ecc.bytes]];	p = bufpoi + data_col_addr;	for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) {		int stat;		stat = chip->ecc.correct(mtd, p, &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]);		if (stat == -1)			mtd->ecc_stats.failed++;		else			mtd->ecc_stats.corrected += stat;	}	return 0;}/** * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function * @mtd:	mtd info structure * @chip:	nand chip info structure * @buf:	buffer to store read data * * Not for syndrome calculating ecc controllers which need a special oob layout */static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,				uint8_t *buf){	int i, eccsize = chip->ecc.size;	int eccbytes = chip->ecc.bytes;	int eccsteps = chip->ecc.steps;	uint8_t *p = buf;	uint8_t *ecc_calc = chip->buffers->ecccalc;	uint8_t *ecc_code = chip->buffers->ecccode;	uint32_t *eccpos = chip->ecc.layout->eccpos;	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {		chip->ecc.hwctl(mtd, NAND_ECC_READ);		chip->read_buf(mtd, p, eccsize);		chip->ecc.calculate(mtd, p, &ecc_calc[i]);	}	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);	for (i = 0; i < chip->ecc.total; i++)		ecc_code[i] = chip->oob_poi[eccpos[i]];	eccsteps = chip->ecc.steps;	p = buf;	for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {		int stat;		stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);		if (stat < 0)			mtd->ecc_stats.failed++;		else			mtd->ecc_stats.corrected += stat;	}	return 0;}/** * nand_read_page_syndrome - [REPLACABLE] hardware ecc syndrom based page read * @mtd:	mtd info structure * @chip:	nand chip info structure * @buf:	buffer to store read data * * The hw generator calculates the error syndrome automatically. Therefor * we need a special oob layout and handling. */static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,				   uint8_t *buf){	int i, eccsize = chip->ecc.size;	int eccbytes = chip->ecc.bytes;	int eccsteps = chip->ecc.steps;	uint8_t *p = buf;	uint8_t *oob = chip->oob_poi;	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {		int stat;		chip->ecc.hwctl(mtd, NAND_ECC_READ);		chip->read_buf(mtd, p, eccsize);		if (chip->ecc.prepad) {			chip->read_buf(mtd, oob, chip->ecc.prepad);			oob += chip->ecc.prepad;		}		chip->ecc.hwctl(mtd, NAND_ECC_READSYN);		chip->read_buf(mtd, oob, eccbytes);		stat = chip->ecc.correct(mtd, p, oob, NULL);		if (stat < 0)			mtd->ecc_stats.failed++;		else			mtd->ecc_stats.corrected += stat;		oob += eccbytes;		if (chip->ecc.postpad) {			chip->read_buf(mtd, oob, chip->ecc.postpad);			oob += chip->ecc.postpad;		}	}	/* Calculate remaining oob bytes */	i = mtd->oobsize - (oob - chip->oob_poi);	if (i)		chip->read_buf(mtd, oob, i);	return 0;}/** * nand_transfer_oob - [Internal] Transfer oob to client buffer * @chip:	nand chip structure * @oob:	oob destination address * @ops:	oob ops structure * @len:	size of oob to transfer */static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,				  struct mtd_oob_ops *ops, size_t len){	switch(ops->mode) {	case MTD_OOB_PLACE:	case MTD_OOB_RAW:		memcpy(oob, chip->oob_poi + ops->ooboffs, len);		return oob + len;	case MTD_OOB_AUTO: {		struct nand_oobfree *free = chip->ecc.layout->oobfree;		uint32_t boffs = 0, roffs = ops->ooboffs;		size_t bytes = 0;		for(; free->length && len; free++, len -= bytes) {			/* Read request not from offset 0 ? */			if (unlikely(roffs)) {				if (roffs >= free->length) {

⌨️ 快捷键说明

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