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

📄 nand.c

📁 linux下的MTD设备驱动源代码,配合jffs2 yaffss2文件系统.
💻 C
📖 第 1 页 / 共 4 页
字号:
				goto out;			page++;		}		this->data_poi = bufstart;		ret = nand_verify_pages (mtd, this, startpage, numpages, oobbuf, oobsel, chipnr, 0);		if (ret)			goto out;					written += mtd->oobblock * numpages;		/* All done ? */		if (!count)			break;		startpage = page & this->pagemask;		/* Check, if we cross a chip boundary */		if (!startpage) {			chipnr++;			this->select_chip(mtd, -1);			this->select_chip(mtd, chipnr);		}	}	ret = 0;out:	/* Deselect and wake up anyone waiting on the device */	nand_release_chip(mtd);	*retlen = written;	return ret;}/* * NAND erase a block *  * @mtd:	MTD device structure * @instr:	erase instruction */static int nand_erase (struct mtd_info *mtd, struct erase_info *instr){	int page, len, status, pages_per_block, ret, chipnr;	struct nand_chip *this = mtd->priv;	DEBUG (MTD_DEBUG_LEVEL3,	       "nand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len);	/* Start address must align on block boundary */	if (instr->addr & (mtd->erasesize - 1)) {		DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n");		return -EINVAL;	}	/* Length must align on block boundary */	if (instr->len & (mtd->erasesize - 1)) {		DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Length not block aligned\n");		return -EINVAL;	}	/* Do not allow erase past end of device */	if ((instr->len + instr->addr) > mtd->size) {		DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Erase past end of device\n");		return -EINVAL;	}	/* Grab the lock and see if the device is available */	nand_get_chip (this, mtd, FL_ERASING, NULL);	/* Shift to get first page */	page = (int) (instr->addr >> this->page_shift);	chipnr = (int)((unsigned long)instr->addr / this->chipsize);	/* Calculate pages in each block */	pages_per_block = mtd->erasesize / mtd->oobblock;	/* Select the NAND device */	this->select_chip(mtd, chipnr);	/* Check the WP bit */	this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);	if (!(this->read_byte(mtd) & 0x80)) {		DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Device is write protected!!!\n");		instr->state = MTD_ERASE_FAILED;		goto erase_exit;	}	/* Loop through the pages */	len = instr->len;	instr->state = MTD_ERASING;	while (len) {		/* Check if we have a bad block, we do not erase bad blocks ! */		if (this->block_bad(mtd, (loff_t) page, 0)) {			printk (KERN_WARNING "nand_erase: attempt to erase a bad block at page 0x%08x\n", page);			instr->state = MTD_ERASE_FAILED;			goto erase_exit;		}				/* Invalidate the page cache, if we erase the block which contains 		   the current cached page */		if (page >= this->pagebuf && this->pagebuf < (page + pages_per_block))  			this->pagebuf = -1;		/* Send commands to erase a page */		this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page & this->pagemask);		this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1);		status = this->waitfunc (mtd, this, FL_ERASING);		/* See if block erase succeeded */		if (status & 0x01) {			DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page);			instr->state = MTD_ERASE_FAILED;			goto erase_exit;		}				/* Increment page address and decrement length */		len -= mtd->erasesize;		page += pages_per_block;		/* Check, if we cross a chip boundary */		if (len && !(page & this->pagemask)) {			chipnr++;			this->select_chip(mtd, -1);			this->select_chip(mtd, chipnr);		}	}	instr->state = MTD_ERASE_DONE;erase_exit:	ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;;	/* Do call back function */	if (!ret && instr->callback)		instr->callback (instr);	/* Deselect and wake up anyone waiting on the device */	nand_release_chip(mtd);	/* Return more or less happy */	return ret;}/* * NAND sync * * @mtd:	MTD device structure */static void nand_sync (struct mtd_info *mtd){	struct nand_chip *this = mtd->priv;	DECLARE_WAITQUEUE (wait, current);	DEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n");retry:	/* Grab the spinlock */	spin_lock_bh (&this->chip_lock);	/* See what's going on */	switch (this->state) {	case FL_READY:	case FL_SYNCING:		this->state = FL_SYNCING;		spin_unlock_bh (&this->chip_lock);		break;	default:		/* Not an idle state */		add_wait_queue (&this->wq, &wait);		spin_unlock_bh (&this->chip_lock);		schedule ();		remove_wait_queue (&this->wq, &wait);		goto retry;	}	/* Lock the device */	spin_lock_bh (&this->chip_lock);	/* Set the device to be ready again */	if (this->state == FL_SYNCING) {		this->state = FL_READY;		wake_up (&this->wq);	}	/* Unlock the device */	spin_unlock_bh (&this->chip_lock);}/* * Check whether the block at the given offset is bad * * @mtd:	MTD device structure * @ofs:	offset relative to mtd start */static int nand_block_isbad (struct mtd_info *mtd, loff_t ofs){	struct nand_chip *this = mtd->priv;		/* Check for invalid offset */	if (ofs > mtd->size) 		return -EINVAL;	return this->block_bad(mtd, ofs, 1);}/* * Mark the block at the given offset as bad * * @mtd:	MTD device structure * @ofs:	offset relative to mtd start */static int nand_block_markbad (struct mtd_info *mtd, loff_t ofs){	struct nand_chip *this = mtd->priv;			/* Check for invalid offset */	if (ofs > mtd->size) 		return -EINVAL;	return this->block_markbad(mtd, ofs);}/* * Scan for the NAND device * * This fills out all the not initialized function pointers * with the defaults. * The flash ID is read and the mtd/chip structures are * filled with the appropriate values. * * @mtd:	MTD device structure * @maxchips:	Number of chips to scan for */int nand_scan (struct mtd_info *mtd, int maxchips){	int i, nand_maf_id, nand_dev_id, busw;	struct nand_chip *this = mtd->priv;	/* Get buswidth to select the correct functions*/	busw = this->options & NAND_BUSWIDTH_16;	/* check for proper chip_delay setup, set 20us if not */	if (!this->chip_delay)		this->chip_delay = 20;	/* check, if a user supplied command function given */	if (this->cmdfunc == NULL)		this->cmdfunc = nand_command;	/* check, if a user supplied wait function given */	if (this->waitfunc == NULL)		this->waitfunc = nand_wait;	if (!this->select_chip)		this->select_chip = nand_select_chip;	if (!this->write_byte)		this->write_byte = busw ? nand_write_byte16 : nand_write_byte;	if (!this->read_byte)		this->read_byte = busw ? nand_read_byte16 : nand_read_byte;	if (!this->write_word)		this->write_word = nand_write_word;	if (!this->read_word)		this->read_word = nand_read_word;	if (!this->block_bad)		this->block_bad = nand_block_bad;	if (!this->block_markbad)		this->block_markbad = nand_default_block_markbad;	if (!this->write_buf)		this->write_buf = busw ? nand_write_buf16 : nand_write_buf;	if (!this->read_buf)		this->read_buf = busw ? nand_read_buf16 : nand_read_buf;	if (!this->verify_buf)		this->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;	/* Select the device */	this->select_chip(mtd, 0);	/* Send the command for reading device ID */	this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);	/* Read manufacturer and device IDs */	nand_maf_id = this->read_byte(mtd);	nand_dev_id = this->read_byte(mtd);	/* Print and store flash device information */	for (i = 0; nand_flash_ids[i].name != NULL; i++) {						if (nand_dev_id != nand_flash_ids[i].id) 			continue;					mtd->name = nand_flash_ids[i].name;		this->chipsize = nand_flash_ids[i].chipsize << 20;				/* New devices have all the information in additional id bytes */		if (!nand_flash_ids[i].pagesize) {			int extid;			/* The 3rd id byte contains non relevant data ATM */			extid = this->read_byte(mtd);			/* The 4th id byte is the important one */			extid = this->read_byte(mtd);			/* Calc pagesize */			mtd->oobblock = 1024 << (extid & 0x3);			extid >>= 2;			/* Calc oobsize */			mtd->oobsize = (8 << (extid & 0x03)) * (mtd->oobblock / 512);			extid >>= 2;			/* Calc blocksize. Blocksize is multiples of 64KiB */			mtd->erasesize = (64 * 1024)  << (extid & 0x03);			extid >>= 2;			/* Get buswidth information */			busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;					/* Do not replace user supplied command function ! */			if (this->cmdfunc == nand_command)				this->cmdfunc = nand_command_lp;		} else {			/* Old devices have this data hardcoded in the			 * device id table */			mtd->erasesize = nand_flash_ids[i].erasesize;			mtd->oobblock = nand_flash_ids[i].pagesize;			mtd->oobsize = mtd->oobblock / 32;			busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16;		}		/* Check, if buswidth is correct. Hardware drivers should set		 * this correct ! */		if (busw != (this->options & NAND_BUSWIDTH_16)) {			printk (KERN_INFO "NAND device: Manufacturer ID:"				" 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, 				nand_manuf_ids[i].name , mtd->name);			printk (KERN_WARNING 				"NAND bus width %d instead %d bit\n", 					(this->options & NAND_BUSWIDTH_16) ? 16 : 8,					busw ? 16 : 8);			this->select_chip(mtd, -1);			return 1;			}				/* Calculate the address shift from the page size */			this->page_shift = ffs(mtd->oobblock) - 1;		/* Set the bad block position */		this->badblockpos = mtd->oobblock > 512 ? 			NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;		/* Get chip options */		this->options = nand_flash_ids[i].options;		/* Check if this is a not a samsung device */			if (nand_maf_id != NAND_MFR_SAMSUNG)			this->options &= ~NAND_SAMSUNG_LP_OPTIONS;				/* Try to identify manufacturer */		for (i = 0; nand_manuf_ids[i].id != 0x0; i++) {			if (nand_manuf_ids[i].id == nand_maf_id)				break;		}		printk (KERN_INFO "NAND device: Manufacturer ID:"			" 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, 			nand_manuf_ids[i].name , mtd->name);		break;	}	if (!mtd->name) {		printk (KERN_WARNING "No NAND device found!!!\n");		this->select_chip(mtd, -1);		return 1;	}	for (i=1; i < maxchips; i++) {		this->select_chip(mtd, i);		/* Send the command for reading device ID */		this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);		/* Read manufacturer and device IDs */		if (nand_maf_id != this->read_byte(mtd) ||		    nand_dev_id != this->read_byte(mtd))			break;	}	if (i > 1)		printk(KERN_INFO "%d NAND chips detected\n", i);		if (!this->oob_buf || !this->data_buf) {		printk (KERN_WARNING "nand_scan(): Buffers not set. Scan aborted\n"); 		return 1;	}	/* Store the number of chips and calc total size for mtd */	this->numchips = i;	mtd->size = i * this->chipsize;	/* Convert chipsize to number of pages per chip -1. */	this->pagemask = (this->chipsize >> this->page_shift) - 1;	/* Preset the internal oob buffer */	memset(this->oob_buf, 0xff, mtd->oobsize * (mtd->erasesize / mtd->oobblock));		/* If no default placement scheme is given, select an	 * appropriate one */	if (!this->autooob) {		/* Select the appropriate default oob placement scheme for		 * placement agnostic filesystems */		switch (mtd->oobsize) { 		case 8:			this->autooob = &nand_oob_8;			break;		case 16:			this->autooob = &nand_oob_16;			break;		case 64:			this->autooob = &nand_oob_64;			break;		default:			printk (KERN_WARNING "No oob scheme defined for oobsize %d\n",				mtd->oobsize);			BUG();		}	}		/* The number of bytes available for the filesystem to place fs dependend	 * oob data */	if (this->options & NAND_BUSWIDTH_16) {		mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 2);		if (this->autooob->eccbytes & 0x01)			mtd->oobavail--;	} else		mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 1);	/* 	 * check ECC mode, default to software	 * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize	 * fallback to software ECC 	*/	this->eccsize = 256;	/* set default eccsize */		switch (this->eccmode) {	case NAND_ECC_HW3_512: 	case NAND_ECC_HW6_512: 		if (mtd->oobblock == 256) {			printk (KERN_WARNING "512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC \n");			this->eccmode = NAND_ECC_SOFT;			this->calculate_ecc = nand_calculate_ecc;			this->correct_data = nand_correct_data;			break;				} else 			this->eccsize = 512; /* set eccsize to 512 and fall through for function check */	case NAND_ECC_HW3_256:		if (this->calculate_ecc && this->correct_data && this->enable_hwecc)			break;		printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n");		BUG();		case NAND_ECC_NONE: 		printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n");		this->eccmode = NAND_ECC_NONE;		break;	case NAND_ECC_SOFT:			this->calculate_ecc = nand_calculate_ecc;		this->correct_data = nand_correct_data;		break;	default:		printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);		BUG();		}			mtd->eccsize = this->eccsize;		/* Set the number of read / write steps for one page to ensure ECC generation */	switch (this->eccmode) {	case NAND_ECC_HW3_512:	case NAND_ECC_HW6_512:		this->eccsteps = mtd->oobblock / 512;		break;	case NAND_ECC_HW3_256:	case NAND_ECC_SOFT:			this->eccsteps = mtd->oobblock / 256;		break;			case NAND_ECC_NONE: 		this->eccsteps = 1;		break;	}		/* Initialize state, waitqueue and spinlock */	this->state = FL_READY;	init_waitqueue_head (&this->wq);	spin_lock_init (&this->chip_lock);	/* De-select the device */	this->select_chip(mtd, -1);	/* Invalidate the pagebuffer reference */	this->pagebuf = -1;	/* Fill in remaining MTD driver data */	mtd->type = MTD_NANDFLASH;	mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC;	mtd->ecctype = MTD_ECC_SW;	mtd->erase = nand_erase;	mtd->point = NULL;	mtd->unpoint = NULL;	mtd->read = nand_read;	mtd->write = nand_write;	mtd->read_ecc = nand_read_ecc;	mtd->write_ecc = nand_write_ecc;	mtd->read_oob = nand_read_oob;	mtd->write_oob = nand_write_oob;	mtd->readv = NULL;	mtd->writev = nand_writev;	mtd->writev_ecc = nand_writev_ecc;	mtd->sync = nand_sync;	mtd->lock = NULL;	mtd->unlock = NULL;	mtd->suspend = NULL;	mtd->resume = NULL;	mtd->block_isbad = nand_block_isbad;	mtd->block_markbad = nand_block_markbad;	mtd->owner = THIS_MODULE;	/* Return happy */	return 0;}EXPORT_SYMBOL (nand_scan);MODULE_LICENSE ("GPL");MODULE_AUTHOR ("Steven J. Hill <sjhill@realitydiluted.com>, Thomas Gleixner <tglx@linutronix.de>");MODULE_DESCRIPTION ("Generic NAND flash driver code");

⌨️ 快捷键说明

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