mtd/drivers/mtd/nand nand_base.c,1.86,1.87

gleixner at infradead.org gleixner at infradead.org
Wed May 26 09:35:33 EDT 2004


Update of /home/cvs/mtd/drivers/mtd/nand
In directory phoenix.infradead.org:/tmp/cvs-serv11150

Modified Files:
	nand_base.c 
Log Message:
Add bad block table support. Signed-off-by: Thomas Gleixner <tglx at linutronix.de>

Index: nand_base.c
===================================================================
RCS file: /home/cvs/mtd/drivers/mtd/nand/nand_base.c,v
retrieving revision 1.86
retrieving revision 1.87
diff -u -r1.86 -r1.87
--- nand_base.c	19 May 2004 20:17:40 -0000	1.86
+++ nand_base.c	26 May 2004 13:35:30 -0000	1.87
@@ -48,6 +48,7 @@
 #include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
+#include <linux/slab.h>
 #include <linux/types.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
@@ -57,6 +58,10 @@
 #include <linux/bitops.h>
 #include <asm/io.h>
 
+#ifdef CONFIG_MTD_PARTITIONS
+#include <linux/mtd/partitions.h>
+#endif
+
 /* Define default oob placement schemes for large and small page devices */
 static struct nand_oobinfo nand_oob_8 = {
 	.useecc = MTD_NANDECC_AUTOPLACE,
@@ -268,7 +273,6 @@
 	u16 bad;
 
 	if (getchip) {
-		/* Shift to get page */
 		page = ((int) ofs) >> this->page_shift;
 		chipnr = (int)((unsigned long) ofs / this->chipsize);
 
@@ -309,10 +313,18 @@
 	struct nand_chip *this = mtd->priv;
 	u_char buf[2] = {0, 0};
 	size_t	retlen;
+	int block;
 	
-	ofs += mtd->oobsize + (this->badblockpos & ~0x01);
-	
+	/* Get block number */
+	block = ((int) ofs) >> this->erase_shift;
+	this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
+
+	/* Do we have a flash based bad block table ? */
+	if (this->options & NAND_USE_FLASH_BBT)
+		return nand_update_bbt (mtd);
+		
 	/* We write two bytes, so we dont have to mess with 16 bit access */
+	ofs += mtd->oobsize + (this->badblockpos & ~0x01);
 	return nand_write_oob (mtd, ofs , 2, &retlen, buf);
 }
 
@@ -327,6 +339,29 @@
 	return (this->read_byte(mtd) & 0x80) ? 0 : 1; 
 }
 
+/* Check, if the block is already scanned in the bad block table.
+ * If not, call the scan function and store the result in 
+ * the table.
+ *
+ * @mtd:	MTD device structure
+ * @ofs:	offset from device start
+ * @getchip:	0, if the chip is already selected
+ *
+ */
+static int nand_block_checkbad (struct mtd_info *mtd, loff_t ofs, int getchip)
+{
+	struct nand_chip *this = mtd->priv;
+	int block;
+	
+	if (!this->bbt)
+		return this->block_bad(mtd, ofs, getchip);
+	
+	/* Return info from the table */
+	/* Get block number */
+	block = (int) (ofs >> (this->erase_shift - 1));
+	return this->bbt[block >> 3] & (1 << (block & 0x06)) ? 1 : 0;
+}
+
 /*
  * Send command to NAND device
  */
@@ -1088,16 +1123,14 @@
 	 */
 	i = 0;
 	while (i < len) {
-		int thislen = (mtd->oobsize - col) & (mtd->oobsize - 1);
-		if (!thislen)
-			thislen = mtd->oobsize;
+		int thislen = mtd->oobsize - col;
 		thislen = min_t(int, thislen, len);
 		this->read_buf(mtd, &buf[i], thislen);
 		i += thislen;
-		col += thislen;
 		/* Read more ? */
 		if (i < len) {
 			page++;
+			col = 0;
 			/* Apply delay or wait for ready/busy pin 
 			 * Do this before the AUTOINCR check, so no problems
 			 * arise if a chip which does auto increment
@@ -1133,6 +1166,64 @@
 	return 0;
 }
 
+/* 
+ * Read raw data including oob into buffer
+ *
+ * @mtd:	MTD device structure
+ * @buf:	temporary buffer
+ * @from:	offset to read from
+ * @len:	number of bytes to read
+ */
+int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len)
+{
+	struct nand_chip *this = mtd->priv;
+	int page = (int) (from >> this->page_shift);
+	int chipshift = ffs(this->chipsize) - 1;
+	int chip = (int) (from >> chipshift);
+	int sndcmd = 1;
+	int erase_state = 0;
+	int cnt = 0;
+	int pagesize = mtd->oobblock + mtd->oobsize;
+	int blockcheck = (mtd->erasesize >> this->page_shift) - 1;
+
+	/* Do not allow reads past end of device */
+	if ((from + len) > mtd->size) {
+		DEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: Attempt read beyond end of device\n");
+		return -EINVAL;
+	}
+
+	/* Grab the lock and see if the device is available */
+	nand_get_chip (this, mtd , FL_READING, &erase_state);
+
+	this->select_chip (mtd, chip);
+	
+	while (len) {
+		if (sndcmd)
+			this->cmdfunc (mtd, NAND_CMD_READ0, 0, page & this->pagemask);
+		sndcmd = 0;	
+
+		this->read_buf (mtd, &buf[cnt], pagesize);
+
+		len -= pagesize;
+		cnt += pagesize;
+		page++;
+		
+		if (!this->dev_ready) 
+			udelay (this->chip_delay);
+		else
+			while (!this->dev_ready(mtd));	
+			
+		/* Check, if the chip supports auto page increment */ 
+		if (!NAND_CANAUTOINCR(this) || !(page & blockcheck))
+			sndcmd = 1;
+	}
+
+	/* Deselect and wake up anyone waiting on the device */
+	nand_release_chip(mtd);
+	return 0;
+}
+
+
 /* Prepare the out of band buffer 
  *
  * Return:
@@ -1642,6 +1733,7 @@
  */
 static void single_erase_cmd (struct mtd_info *mtd, int page)
 {
+	struct nand_chip *this = mtd->priv;
 	/* Send commands to erase a block */
 	this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page);
 	this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1);
@@ -1653,6 +1745,7 @@
  */
 static void multi_erase_cmd (struct mtd_info *mtd, int page)
 {
+	struct nand_chip *this = mtd->priv;
 	/* Send commands to erase a block */
 	this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++);
 	this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++);
@@ -1721,7 +1814,7 @@
 
 	while (len) {
 		/* Check if we have a bad block, we do not erase bad blocks ! */
-		if (this->block_bad(mtd, (loff_t) page, 0)) {
+		if (nand_block_checkbad(mtd, (loff_t) page << this->page_shift, 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;
@@ -1758,7 +1851,7 @@
 
 erase_exit:
 
-	ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;;
+	ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
 	/* Do call back function */
 	if (!ret && instr->callback)
 		instr->callback (instr);
@@ -1817,6 +1910,7 @@
 	spin_unlock_bh (&this->chip_lock);
 }
 
+
 /*
  * Check whether the block at the given offset is bad
  *
@@ -1825,13 +1919,11 @@
  */
 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);
+	
+	return nand_block_checkbad (mtd, ofs, 1);
 }
 
 /*
@@ -1902,6 +1994,8 @@
 		this->read_buf = busw ? nand_read_buf16 : nand_read_buf;
 	if (!this->verify_buf)
 		this->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;
+	if (!this->scan_bbt)
+		this->scan_bbt = nand_default_bbt;
 
 	/* Select the device */
 	this->select_chip(mtd, 0);
@@ -1972,8 +2066,9 @@
 		this->badblockpos = mtd->oobblock > 512 ? 
 			NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
 
-		/* Get chip options */
-		this->options = nand_flash_ids[i].options;
+		/* Get chip options, preserve non chip based options */
+		this->options &= ~NAND_CHIPOPTIONS_MSK;
+		this->options |= nand_flash_ids[i].options & NAND_CHIPOPTIONS_MSK;
 		/* Check if this is a not a samsung device. Do not clear the options
 		 * for chips which are not having an extended id.
 		 */	
@@ -1987,7 +2082,7 @@
 			this->erase_cmd = single_erase_cmd;
 
 		/* Do not replace user supplied command function ! */
-		if (this->pagesize > 512 && this->cmdfunc == nand_command)
+		if (mtd->oobblock > 512 && this->cmdfunc == nand_command)
 			this->cmdfunc = nand_command_lp;
 				
 		/* Try to identify manufacturer */
@@ -2021,9 +2116,29 @@
 	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;
+	/* Allocate buffers, if neccecary */
+	if (!this->oob_buf) {
+		size_t len;
+		len = mtd->oobsize << (this->erase_shift - this->page_shift);
+		this->oob_buf = kmalloc (len, GFP_KERNEL);
+		if (!this->oob_buf) {
+			printk (KERN_ERR "nand_scan(): Cannot allocate oob_buf\n");
+			return -ENOMEM;
+		}
+		this->options |= NAND_OOBBUF_ALLOC;
+	}
+	
+	if (!this->data_buf) {
+		size_t len;
+		len = mtd->oobblock + mtd->oobsize;
+		this->data_buf = kmalloc (len, GFP_KERNEL);
+		if (!this->data_buf) {
+			if (this->options & NAND_OOBBUF_ALLOC)
+				kfree (this->oob_buf);
+			printk (KERN_ERR "nand_scan(): Cannot allocate data_buf\n");
+			return -ENOMEM;
+		}
+		this->options |= NAND_DATABUF_ALLOC;
 	}
 
 	/* Store the number of chips and calc total size for mtd */
@@ -2164,11 +2279,38 @@
 
 	mtd->owner = THIS_MODULE;
 
-	/* Return happy */
-	return 0;
+	/* Build bad block table */
+	return this->scan_bbt (mtd);
+}
+
+/* Free resources held by the NAND device 
+ *
+ * @mtd:	MTD device structure
+*/
+void nand_release (struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+
+#ifdef CONFIG_MTD_PARTITIONS
+	/* Unregister partitions */
+	del_mtd_partitions (mtd);
+#endif
+	/* Unregister the device */
+	del_mtd_device (mtd);
+
+	/* Free bad block table memory, if allocated */
+	if (this->bbt)
+		kfree (this->bbt);
+	/* Buffer allocated by nand_scan ? */
+	if (this->options & NAND_OOBBUF_ALLOC)
+		kfree (this->oob_buf);
+	/* Buffer allocated by nand_scan ? */
+	if (this->options & NAND_DATABUF_ALLOC)
+		kfree (this->data_buf);
 }
 
 EXPORT_SYMBOL (nand_scan);
+EXPORT_SYMBOL (nand_release);
 
 MODULE_LICENSE ("GPL");
 MODULE_AUTHOR ("Steven J. Hill <sjhill at realitydiluted.com>, Thomas Gleixner <tglx at linutronix.de>");





More information about the linux-mtd-cvs mailing list