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

📄 yynf_nand.c

📁 nand flash驱动
💻 C
📖 第 1 页 / 共 3 页
字号:
	/* Grab the lock and see if the device is available */	nand_get_chip (this, mtd, FL_WRITING, NULL);	/* Select the NAND device */	nand_select ();	/* Check the WP bit */	this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);	if (!(readb (this->IO_ADDR_R) & 0x80)) {		DEBUG (MTD_DEBUG_LEVEL0, "nand_writev: Device is write protected!!!\n");		ret = -EIO;		goto out;	}	/* Loop until all iovecs' data has been written */	cnt = col;	len = 0;		while (count) {		/* 		 *  Check, if we write from offset 0 and if the tuple		 *  gives us not enough data for a full page write. Then we		 *  can use the iov direct, else we have to copy into		 *  data_buf.				 */		if (!cnt && (vecs->iov_len - len) >= mtd->oobblock) {			cnt = mtd->oobblock;			this->data_poi = (u_char *) vecs->iov_base;			this->data_poi += len;			len += mtd->oobblock; 			/* Check, if we have to switch to the next tuple */			if (len >= (int) vecs->iov_len) {				vecs++;				len = 0;				count--;			}		} else {			/*			 * Read data out of each tuple until we have a full page			 * to write or we've read all the tuples.		 	*/			while ((cnt < mtd->oobblock) && count) {				if (vecs->iov_base != NULL && vecs->iov_len) {					this->data_buf[cnt++] = ((u_char *) vecs->iov_base)[len++];				}				/* Check, if we have to switch to the next tuple */				if (len >= (int) vecs->iov_len) {					vecs++;					len = 0;					count--;				}			}				this->data_poi = this->data_buf;			}				/* We use the same function for write and writev !) */		ret = nand_write_page (mtd, this, page, col, cnt, NULL, oobsel);		if (ret)			goto out;		/* Update written bytes count */		written += (cnt - col);		/* Reset written byte counter and column */		col = cnt = 0;		/* Increment page address */		page++;	}out:	/* De-select the NAND device */	nand_deselect ();	/* Wake up anyone waiting on the device */	spin_lock_bh (&this->chip_lock);	this->state = FL_READY;	wake_up (&this->wq);	spin_unlock_bh (&this->chip_lock);	*retlen = written;	return ret;}/* * NAND erase a block */static int nand_erase (struct mtd_info *mtd, struct erase_info *instr){	int page, len, status, pages_per_block, ret;	struct nand_chip *this = mtd->priv;	DECLARE_WAITQUEUE (wait, current);	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);	/* Calculate pages in each block */	pages_per_block = mtd->erasesize / mtd->oobblock;	/* Select the NAND device */	nand_select ();	/* Check the WP bit */	this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);	if (!(readb (this->IO_ADDR_R) & 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 ! */		this->cmdfunc (mtd, NAND_CMD_READOOB, NAND_BADBLOCK_POS, page);		if (readb (this->IO_ADDR_R) != 0xff) {			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;		}		/* Send commands to erase a page */		this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page);		this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1);		spin_unlock_bh (&this->chip_lock);		status = this->waitfunc (mtd, this, FL_ERASING);		/* Get spinlock, in case we exit */		spin_lock_bh (&this->chip_lock);		/* 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;		}				/* Check, if we were interupted */		if (this->state == FL_ERASING) {			/* Increment page address and decrement length */			len -= mtd->erasesize;			page += pages_per_block;		}		/* Release the spin lock */		spin_unlock_bh (&this->chip_lock);erase_retry:		spin_lock_bh (&this->chip_lock);		/* Check the state and sleep if it changed */		if (this->state == FL_ERASING || this->state == FL_READY) {			/* Select the NAND device again, if we were interrupted */			this->state = FL_ERASING;			nand_select ();			continue;		} else {			set_current_state (TASK_UNINTERRUPTIBLE);			add_wait_queue (&this->wq, &wait);			spin_unlock_bh (&this->chip_lock);			schedule ();			remove_wait_queue (&this->wq, &wait);			goto erase_retry;		}	}	instr->state = MTD_ERASE_DONE;erase_exit:	/* De-select the NAND device */	nand_deselect ();	spin_unlock_bh (&this->chip_lock);	ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;;	/* Do call back function */	if (!ret && instr->callback)		instr->callback (instr);	/* The device is ready */	spin_lock_bh (&this->chip_lock);	this->state = FL_READY;	spin_unlock_bh (&this->chip_lock);	/* Return more or less happy */	return ret;}/* * NAND sync */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);}/* * Scan for the NAND device */static int yuyin_nand_scan (struct mtd_info *mtd){	int i, nand_maf_id, nand_dev_id;	struct nand_chip *this = mtd->priv;	/* 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;	/* Select the device */	nand_select ();	/* Send the command for reading device ID */	this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);	/* Read manufacturer and device IDs */	nand_maf_id = readb (this->IO_ADDR_R);	nand_dev_id = readb (this->IO_ADDR_R);	/* 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 && !mtd->size) {			mtd->name = nand_flash_ids[i].name;			mtd->erasesize = nand_flash_ids[i].erasesize;			mtd->size = (1 << nand_flash_ids[i].chipshift);			mtd->eccsize = 256;			if (nand_flash_ids[i].page256) {				mtd->oobblock = 256;				mtd->oobsize = 8;				this->page_shift = 8;			} else {				mtd->oobblock = 512;				mtd->oobsize = 16;				this->page_shift = 9;			}			/* 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: Manufacture ID:"				" 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, 				nand_manuf_ids[i].name , mtd->name);			break;		}	}	/* 	 * 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: 		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: 		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();		}		/* Initialize state, waitqueue and spinlock */	this->state = FL_READY;	init_waitqueue_head (&this->wq);	spin_lock_init (&this->chip_lock);	/* De-select the device */	nand_deselect ();	/* Print warning message for no device */	if (!mtd->size) {		printk (KERN_WARNING "No NAND device found!!!\n");		return 1;	}	/* Fill in remaining MTD driver data */	mtd->type = MTD_NANDFLASH;	mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC;	mtd->module = THIS_MODULE;	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;	/* Return happy */	return 0;}/* * MTD structure for YUYINBOARD NAND FLASH 1 */static struct mtd_info *yuyinnf1_mtd = NULL;/* * Define partitions for flash device */const static struct mtd_partition partition_info[] = {	{ name: "YuyinP1",	  offset: 0,	  size: 64*1024*1024 }};#define NUM_PARTITIONS 1/*  *	hardware specific access to control-lines*/void yynf1_hwcontrol(int cmd){    switch(cmd){	case NAND_CTL_SETCLE: (*(volatile unsigned short *) (0xfa000032)) =  1; break;	case NAND_CTL_CLRCLE: (*(volatile unsigned short *) (0xfa000032)) = 0; break;	case NAND_CTL_SETALE: (*(volatile unsigned short *) (0xfa000030)) =  1; break;	case NAND_CTL_CLRALE: (*(volatile unsigned short *) (0xfa000030)) =  0; break;	case NAND_CTL_SETNCE: (*(volatile unsigned short *) (0xfa000034)) =  1; break;	case NAND_CTL_CLRNCE: (*(volatile unsigned short *) (0xfa000034)) =  0; break;    }}/* * Main initialization routine */int __init yynf1_init (void){	struct nand_chip *this;	/* Allocate memory for MTD device structure and private data */	yuyinnf1_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),				GFP_KERNEL);	if (!yuyinnf1_mtd) {		printk ("Unable to allocate NAND MTD device structure.\n");		return -ENOMEM;	}	/* Get pointer to private data */	this = (struct nand_chip *) (&yuyinnf1_mtd[1]);	/* Initialize structures */	memset((char *) yuyinnf1_mtd, 0, sizeof(struct mtd_info));	memset((char *) this, 0, sizeof(struct nand_chip));	/* Link the private data with the MTD structure */	yuyinnf1_mtd->priv = this;	/* Set address of NAND IO lines */	this->IO_ADDR_R = 0xfa000036;	this->IO_ADDR_W = 0xfa000036;	/* Set address of hardware control function */	this->hwcontrol = yynf1_hwcontrol;	/* 15 us command delay time */	this->chip_delay = 15;	/* Scan to find existence of the device */	if (yuyin_nand_scan (yuyinnf1_mtd)) {		kfree (yuyinnf1_mtd);		return -ENXIO;	}	/* Allocate memory for internal data buffer */	this->data_buf = kmalloc (sizeof(u_char) * (yuyinnf1_mtd->oobblock + yuyinnf1_mtd->oobsize), GFP_KERNEL);	if (!this->data_buf) {		printk ("Unable to allocate NAND data buffer.\n");		kfree (yuyinnf1_mtd);		return -ENOMEM;	}	/* Allocate memory for internal data buffer */	this->data_cache = kmalloc (sizeof(u_char) * (yuyinnf1_mtd->oobblock + yuyinnf1_mtd->oobsize), GFP_KERNEL);	if (!this->data_cache) {		printk ("Unable to allocate NAND data cache for.\n");		kfree (this->data_buf);		kfree (yuyinnf1_mtd);		return -ENOMEM;	}	this->cache_page = -1;	/* Register the partitions */	add_mtd_partitions(yuyinnf1_mtd, partition_info, NUM_PARTITIONS);	/* Return happy */	return 0;}module_init(yynf1_init);

⌨️ 快捷键说明

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